Spaces:
Running
Running
# flake8: noqa: E501 | |
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/' | |
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. | |
""" | |
# Request URL | |
url = self.base_url + 'Routes/Driving?o=json&wp.0=' + start + '&wp.1=' + end + '&key=' + self.key | |
# GET request | |
r = requests.get(url) | |
# TODO check request status? | |
data = json.loads(r.text) | |
# Extract route information | |
route = data['resourceSets'][0]['resources'][0] | |
# Extract distance in miles | |
distance = route['travelDistance'] | |
return dict(distance=distance) | |
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. | |
""" | |
# Request URL | |
url = self.base_url + 'Routes/Driving?o=json&wp.0=' + start + '&wp.1=' + end + '&key=' + self.key | |
# GET request | |
r = requests.get(url) | |
data = json.loads(r.text) | |
# Extract route information | |
route = data['resourceSets'][0]['resources'][0] | |
itinerary = route['routeLegs'][0]['itineraryItems'] | |
# Extract route text information | |
route_text = [] | |
for item in itinerary: | |
if 'instruction' in item: | |
route_text.append(item['instruction']['text']) | |
return dict(route=route_text) | |
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]) | |
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'] | |
# Build the request query string | |
params = { | |
'query': search_term, | |
'userLocation': f'{latitude},{longitude}', | |
'radius': radius, | |
'key': self.key | |
} | |
# Make the request | |
response = requests.get(url, params=params) | |
# Parse the response | |
response_data = json.loads(response.content) | |
# Get the results | |
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.""" | |
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. | |
""" | |
# Request URL | |
url = self.base_url + 'Routes/Driving?o=json&wp.0=' + start + '&wp.1=' + end + '&key=' + self.key | |
# GET request | |
async with aiohttp.ClientSession() as session: | |
async with session.get(url) as resp: | |
# TODO check request status? | |
data = await resp.json() | |
# Extract route information | |
route = data['resourceSets'][0]['resources'][0] | |
# Extract distance in miles | |
distance = route['travelDistance'] | |
return dict(distance=distance) | |
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. | |
""" | |
# Request URL | |
url = self.base_url + 'Routes/Driving?o=json&wp.0=' + start + '&wp.1=' + end + '&key=' + self.key | |
# GET request | |
async with aiohttp.ClientSession() as session: | |
async with session.get(url) as resp: | |
data = await resp.json() | |
# Extract route information | |
route = data['resourceSets'][0]['resources'][0] | |
itinerary = route['routeLegs'][0]['itineraryItems'] | |
# Extract route text information | |
route_text = [] | |
for item in itinerary: | |
if 'instruction' in item: | |
route_text.append(item['instruction']['text']) | |
return dict(route=route_text) | |
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]) | |
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'] | |
# Build the request query string | |
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) | |