| | |
| | 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) |
| |
|