|
|
|
import json |
|
import os |
|
from typing import Optional, Type |
|
|
|
import aiohttp |
|
import requests |
|
|
|
from lagent.actions.base_action import AsyncActionMixin, BaseAction, tool_api |
|
from lagent.actions.parser import BaseParser, JsonParser |
|
|
|
|
|
class BINGMap(BaseAction): |
|
"""BING Map plugin for looking up map information.""" |
|
|
|
def __init__( |
|
self, |
|
key: Optional[str] = None, |
|
description: Optional[dict] = None, |
|
parser: Type[BaseParser] = JsonParser, |
|
) -> None: |
|
super().__init__(description, parser) |
|
key = os.environ.get('BING_MAP_KEY', key) |
|
if key is None: |
|
raise ValueError( |
|
'Please set BING Map API key either in the environment ' |
|
'as BING_MAP_KEY or pass it as `key` parameter.') |
|
self.key = key |
|
self.base_url = 'http://dev.virtualearth.net/REST/V1/' |
|
|
|
@tool_api(explode_return=True) |
|
def get_distance(self, start: str, end: str) -> dict: |
|
"""Get the distance between two locations in km. |
|
|
|
Args: |
|
start (:class:`str`): The start location |
|
end (:class:`str`): The end location |
|
|
|
Returns: |
|
:class:`dict`: distance information |
|
* distance (str): the distance in km. |
|
""" |
|
|
|
url = self.base_url + 'Routes/Driving?o=json&wp.0=' + start + '&wp.1=' + end + '&key=' + self.key |
|
|
|
r = requests.get(url) |
|
|
|
data = json.loads(r.text) |
|
|
|
route = data['resourceSets'][0]['resources'][0] |
|
|
|
distance = route['travelDistance'] |
|
return dict(distance=distance) |
|
|
|
@tool_api(explode_return=True) |
|
def get_route(self, start: str, end: str) -> dict: |
|
"""Get the route between two locations in km. |
|
|
|
Args: |
|
start (:class:`str`): The start location |
|
end (:class:`str`): The end location |
|
|
|
Returns: |
|
:class:`dict`: route information |
|
* route (list): the route, a list of actions. |
|
""" |
|
|
|
url = self.base_url + 'Routes/Driving?o=json&wp.0=' + start + '&wp.1=' + end + '&key=' + self.key |
|
|
|
r = requests.get(url) |
|
data = json.loads(r.text) |
|
|
|
route = data['resourceSets'][0]['resources'][0] |
|
itinerary = route['routeLegs'][0]['itineraryItems'] |
|
|
|
route_text = [] |
|
for item in itinerary: |
|
if 'instruction' in item: |
|
route_text.append(item['instruction']['text']) |
|
return dict(route=route_text) |
|
|
|
@tool_api(explode_return=True) |
|
def get_coordinates(self, location: str) -> dict: |
|
"""Get the coordinates of a location. |
|
|
|
Args: |
|
location (:class:`str`): the location need to get coordinates. |
|
|
|
Returns: |
|
:class:`dict`: coordinates information |
|
* latitude (float): the latitude of the location. |
|
* longitude (float): the longitude of the location. |
|
""" |
|
url = self.base_url + 'Locations' |
|
params = {'query': location, 'key': self.key} |
|
response = requests.get(url, params=params) |
|
json_data = response.json() |
|
coordinates = json_data['resourceSets'][0]['resources'][0]['point'][ |
|
'coordinates'] |
|
return dict(latitude=coordinates[0], longitude=coordinates[1]) |
|
|
|
@tool_api(explode_return=True) |
|
def search_nearby(self, |
|
search_term: str, |
|
places: str = 'unknown', |
|
latitude: float = 0.0, |
|
longitude: float = 0.0, |
|
radius: int = 5000) -> dict: |
|
"""Search for places nearby a location, within a given radius, and return the results into a list. You can use either the places name or the latitude and longitude. |
|
|
|
Args: |
|
search_term (:class:`str`): the place name. |
|
places (:class:`str`): the name of the location. Defaults to ``'unknown'``. |
|
latitude (:class:`float`): the latitude of the location. Defaults to ``0.0``. |
|
longitude (:class:`float`): the longitude of the location. Defaults to ``0.0``. |
|
radius (:class:`int`): radius in meters. Defaults to ``5000``. |
|
|
|
Returns: |
|
:class:`dict`: places information |
|
* places (list): the list of places, each place is a dict with name and address, at most 5 places. |
|
""" |
|
url = self.base_url + 'LocalSearch' |
|
if places != 'unknown': |
|
pos = self.get_coordinates(**{'location': places}) |
|
latitude, longitude = pos[1]['latitude'], pos[1]['longitude'] |
|
|
|
params = { |
|
'query': search_term, |
|
'userLocation': f'{latitude},{longitude}', |
|
'radius': radius, |
|
'key': self.key |
|
} |
|
|
|
response = requests.get(url, params=params) |
|
|
|
response_data = json.loads(response.content) |
|
|
|
results = response_data['resourceSets'][0]['resources'] |
|
addresses = [] |
|
for result in results: |
|
name = result['name'] |
|
address = result['Address']['formattedAddress'] |
|
addresses.append(dict(name=name, address=address)) |
|
if len(addresses) == 5: |
|
break |
|
return dict(place=addresses) |
|
|
|
|
|
class AsyncBINGMap(AsyncActionMixin, BINGMap): |
|
"""BING Map plugin for looking up map information.""" |
|
|
|
@tool_api(explode_return=True) |
|
async def get_distance(self, start: str, end: str) -> dict: |
|
"""Get the distance between two locations in km. |
|
|
|
Args: |
|
start (:class:`str`): The start location |
|
end (:class:`str`): The end location |
|
|
|
Returns: |
|
:class:`dict`: distance information |
|
* distance (str): the distance in km. |
|
""" |
|
|
|
url = self.base_url + 'Routes/Driving?o=json&wp.0=' + start + '&wp.1=' + end + '&key=' + self.key |
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.get(url) as resp: |
|
|
|
data = await resp.json() |
|
|
|
route = data['resourceSets'][0]['resources'][0] |
|
|
|
distance = route['travelDistance'] |
|
return dict(distance=distance) |
|
|
|
@tool_api(explode_return=True) |
|
async def get_route(self, start: str, end: str) -> dict: |
|
"""Get the route between two locations in km. |
|
|
|
Args: |
|
start (:class:`str`): The start location |
|
end (:class:`str`): The end location |
|
|
|
Returns: |
|
:class:`dict`: route information |
|
* route (list): the route, a list of actions. |
|
""" |
|
|
|
url = self.base_url + 'Routes/Driving?o=json&wp.0=' + start + '&wp.1=' + end + '&key=' + self.key |
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.get(url) as resp: |
|
data = await resp.json() |
|
|
|
route = data['resourceSets'][0]['resources'][0] |
|
itinerary = route['routeLegs'][0]['itineraryItems'] |
|
|
|
route_text = [] |
|
for item in itinerary: |
|
if 'instruction' in item: |
|
route_text.append(item['instruction']['text']) |
|
return dict(route=route_text) |
|
|
|
@tool_api(explode_return=True) |
|
async def get_coordinates(self, location: str) -> dict: |
|
"""Get the coordinates of a location. |
|
|
|
Args: |
|
location (:class:`str`): the location need to get coordinates. |
|
|
|
Returns: |
|
:class:`dict`: coordinates information |
|
* latitude (float): the latitude of the location. |
|
* longitude (float): the longitude of the location. |
|
""" |
|
url = self.base_url + 'Locations' |
|
params = {'query': location, 'key': self.key} |
|
async with aiohttp.ClientSession() as session: |
|
async with session.get(url, params=params) as resp: |
|
data = await resp.json() |
|
coordinates = data['resourceSets'][0]['resources'][0]['point'][ |
|
'coordinates'] |
|
return dict(latitude=coordinates[0], longitude=coordinates[1]) |
|
|
|
@tool_api(explode_return=True) |
|
async def search_nearby(self, |
|
search_term: str, |
|
places: str = 'unknown', |
|
latitude: float = 0.0, |
|
longitude: float = 0.0, |
|
radius: int = 5000) -> dict: |
|
"""Search for places nearby a location, within a given radius, and return the results into a list. You can use either the places name or the latitude and longitude. |
|
|
|
Args: |
|
search_term (:class:`str`): the place name. |
|
places (:class:`str`): the name of the location. Defaults to ``'unknown'``. |
|
latitude (:class:`float`): the latitude of the location. Defaults to ``0.0``. |
|
longitude (:class:`float`): the longitude of the location. Defaults to ``0.0``. |
|
radius (:class:`int`): radius in meters. Defaults to ``5000``. |
|
|
|
Returns: |
|
:class:`dict`: places information |
|
* places (list): the list of places, each place is a dict with name and address, at most 5 places. |
|
""" |
|
url = self.base_url + 'LocalSearch' |
|
if places != 'unknown': |
|
pos = self.get_coordinates(**{'location': places}) |
|
latitude, longitude = pos[1]['latitude'], pos[1]['longitude'] |
|
|
|
params = { |
|
'query': search_term, |
|
'userLocation': f'{latitude},{longitude}', |
|
'radius': radius, |
|
'key': self.key |
|
} |
|
async with aiohttp.ClientSession() as session: |
|
async with session.get(url, params=params) as resp: |
|
data = await resp.json() |
|
results = data['resourceSets'][0]['resources'] |
|
addresses = [] |
|
for result in results: |
|
name = result['name'] |
|
address = result['Address']['formattedAddress'] |
|
addresses.append(dict(name=name, address=address)) |
|
if len(addresses) == 5: |
|
break |
|
return dict(place=addresses) |
|
|