travel_agent / maps_agent.py
wolf1997's picture
Update maps_agent.py
50ef67d verified
raw
history blame
13.1 kB
from langchain.tools import tool
from langchain.prompts import PromptTemplate
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition,InjectedState
from langchain_core.messages import (
SystemMessage,
HumanMessage,
AIMessage,
ToolMessage,
)
from langgraph.types import Command, interrupt
from langgraph.checkpoint.memory import MemorySaver
from langchain_core.tools.base import InjectedToolCallId
#structuring
import ast
from dataclasses import dataclass
from typing_extensions import TypedDict
from typing import Annotated, Literal
#getting current location
import geocoder
import os
import requests
import json
from dotenv import load_dotenv
from os import listdir
from os.path import isfile, join
load_dotenv()
GOOGLE_API_KEY=os.getenv('google_api_key')
class State(TypedDict):
"""
A dictionnary representing the state of the agent.
"""
messages: Annotated[list, add_messages]
#location data
latitude: str
longitude: str
address: str
#results from place search
places: dict
def get_current_location_node(state: State):
current_location = geocoder.ip("me")
if current_location.latlng:
latitude, longitude = current_location.latlng
address = current_location.address
return {'latitude':latitude, 'longitude':longitude, 'address':address}
else:
return None
@tool
def get_current_location_tool(tool_call_id: Annotated[str, InjectedToolCallId]):
"""
Tool to get the current location of the user.
agrs: none
"""
current_location = geocoder.ip("me")
if current_location.latlng:
latitude, longitude = current_location.latlng
address = current_location.address
return Command(update={'messages':[ToolMessage(F'The current location is: address:{address}, longitude:{longitude},lattitude:{latitude}', tool_call_id=tool_call_id)],
'latitude':latitude,
'longitude':longitude,
'address':address})
else:
return None
@tool
def find_places_near_me(query:str,state: Annotated[dict, InjectedState],tool_call_id: Annotated[str, InjectedToolCallId]):
"""
Use this tool to find locations near me.
args: query - has to be one of the following "car_dealer", "car_rental", "car_repair", "car_wash",
"electric_vehicle_charging_station", "gas_station", "parking", "rest_stop",
"corporate_office", "farm", "ranch", "art_gallery", "art_studio", "auditorium",
"cultural_landmark", "historical_place", "monument", "museum", "performing_arts_theater",
"sculpture", "library", "preschool", "primary_school", "school", "secondary_school",
"university", "adventure_sports_center", "amphitheatre", "amusement_center", "amusement_park",
"aquarium", "banquet_hall", "barbecue_area", "botanical_garden", "bowling_alley", "casino",
"childrens_camp", "comedy_club", "community_center", "concert_hall", "convention_center",
"cultural_center", "cycling_park", "dance_hall", "dog_park", "event_venue", "ferris_wheel",
"garden", "hiking_area", "historical_landmark", "internet_cafe", "karaoke", "marina",
"movie_rental", "movie_theater", "national_park", "night_club", "observation_deck",
"off_roading_area", "opera_house", "park", "philharmonic_hall", "picnic_ground", "planetarium",
"plaza", "roller_coaster", "skateboard_park", "state_park", "tourist_attraction", "video_arcade",
"visitor_center", "water_park", "wedding_venue", "wildlife_park", "wildlife_refuge", "zoo",
"public_bath", "public_bathroom", "stable", "accounting", "atm", "bank", "acai_shop",
"afghani_restaurant", "african_restaurant", "american_restaurant", "asian_restaurant",
"bagel_shop", "bakery", "bar", "bar_and_grill", "barbecue_restaurant", "brazilian_restaurant",
"breakfast_restaurant", "brunch_restaurant", "buffet_restaurant", "cafe", "cafeteria",
"candy_store", "cat_cafe", "chinese_restaurant", "chocolate_factory", "chocolate_shop",
"coffee_shop", "confectionery", "deli", "dessert_restaurant", "dessert_shop", "diner",
"dog_cafe", "donut_shop", "fast_food_restaurant", "fine_dining_restaurant", "food_court",
"french_restaurant", "greek_restaurant", "hamburger_restaurant", "ice_cream_shop", "indian_restaurant",
"indonesian_restaurant", "italian_restaurant", "japanese_restaurant", "juice_shop",
"korean_restaurant", "lebanese_restaurant", "meal_delivery", "meal_takeaway",
"mediterranean_restaurant", "mexican_restaurant", "middle_eastern_restaurant", "pizza_restaurant",
"pub", "ramen_restaurant", "restaurant", "sandwich_shop", "seafood_restaurant", "spanish_restaurant",
"steak_house", "sushi_restaurant", "tea_house", "thai_restaurant", "turkish_restaurant",
"vegan_restaurant", "vegetarian_restaurant", "vietnamese_restaurant", "wine_bar",
"administrative_area_level_1", "administrative_area_level_2", "country", "locality", "postal_code",
"school_district", "city_hall", "courthouse", "embassy", "fire_station", "government_office",
"local_government_office", "neighborhood_police_station", "police", "post_office", "chiropractor",
"dental_clinic", "dentist", "doctor", "drugstore", "hospital", "massage", "medical_lab", "pharmacy",
"physiotherapist", "sauna", "skin_care_clinic", "spa", "tanning_studio", "wellness_center", "yoga_studio",
"apartment_building", "apartment_complex", "condominium_complex", "housing_complex", "bed_and_breakfast",
"budget_japanese_inn", "campground", "camping_cabin", "cottage", "extended_stay_hotel", "farmstay",
"guest_house", "hostel", "hotel", "inn", "japanese_inn", "mobile_home_park", "motel", "private_guest_room",
"resort_hotel", "rv_park", "beach", "church", "hindu_temple", "mosque", "synagogue", "astrologer",
"barber_shop", "beautician", "beauty_salon", "body_art_service", "catering_service", "cemetery",
"child_care_agency", "consultant", "courier_service", "electrician", "florist", "food_delivery", "foot_care",
"funeral_home", "hair_care", "hair_salon", "insurance_agency", "laundry", "lawyer", "locksmith",
"makeup_artist", "moving_company", "nail_salon", "painter", "plumber", "psychic", "real_estate_agency",
"roofing_contractor", "storage", "summer_camp_organizer", "tailor", "telecommunications_service_provider",
"tour_agency", "tourist_information_center", "travel_agency", "veterinary_care", "asian_grocery_store",
"auto_parts_store", "bicycle_store", "book_store", "butcher_shop", "cell_phone_store", "clothing_store",
"convenience_store", "department_store", "discount_store", "electronics_store", "food_store",
"furniture_store", "gift_shop", "grocery_store", "hardware_store", "home_goods_store", "home_improvement_store",
"jewelry_store", "liquor_store", "market", "pet_store", "shoe_store", "shopping_mall", "sporting_goods_store",
"store", "supermarket", "warehouse_store", "wholesaler", "arena", "athletic_field", "fishing_charter",
"fishing_pond", "fitness_center", "golf_course", "gym", "ice_skating_rink", "playground", "ski_resort",
"sports_activity_location", "sports_club", "sports_coaching", "sports_complex", "stadium", "swimming_pool",
"airport", "airstrip", "bus_station", "bus_stop", "ferry_terminal", "heliport", "international_airport",
"light_rail_station", "park_and_ride", "subway_station", "taxi_stand", "train_station", "transit_depot",
"transit_station", "truck_stop"
"""
try:
my_longitude=state['longitude']
my_latitude=state['latitude']
response=requests.get(f'https://maps.googleapis.com/maps/api/place/nearbysearch/json?location={my_latitude}%2C{my_longitude}&radius=500&type={query}&key={GOOGLE_API_KEY}')
data=response.json()
places={}
for place in data['results']:
try:
name=place['name']
rating=place['rating']
id=place['place_id']
response=requests.get(f'https://places.googleapis.com/v1/places/{id}?fields=googleMapsLinks.placeUri&key={GOOGLE_API_KEY}')
data=response.json()
link=data['googleMapsLinks']['placeUri']
places[name]= {'rating':rating,
'google_maps_link':link,
}
except Exception as e:
f'Error: {e}'
return Command(update={'places':places,
'messages':[ToolMessage(f'I found {len(places)} places', tool_call_id=tool_call_id)]})
except:
return Command(update={'messages':[ToolMessage('Could not find places based on the query', tool_call_id=tool_call_id)]})
@tool
def look_for_places(query: str, tool_call_id: Annotated[str, InjectedToolCallId]):
"""
Tool to look for places based on the user query and location.
Use this tool for more complex user queries like sentences, and if the location is specified in the query.
Places includes restaurants, bars, speakeasy, games, anything.
args: query - the query has to be in this format eg.Spicy%20Vegetarian%20Food%20in%20Sydney%20Australia.
"""
try:
response=requests.get(f'https://maps.googleapis.com/maps/api/place/textsearch/json?query={query}?&key={GOOGLE_API_KEY}')
data=response.json()
places={}
for place in data['results']:
try:
name=place['name']
rating=place['rating']
id=place['place_id']
price_level=place['price_level']
address=place['formatted_address']
lattitude=place['geometry']['location']['lat']
longitude=place['geometry']['location']['lng']
response=requests.get(f'https://places.googleapis.com/v1/places/{id}?fields=googleMapsLinks.placeUri&key={GOOGLE_API_KEY}')
data=response.json()
link=data['googleMapsLinks']['placeUri']
places[name]= {'address': address,
'rating':rating,
'Price_level':price_level,
'google_maps_link':link,
'longitude':longitude,
'latitude':lattitude}
except Exception as e:
f'Error: {e}'
return Command(update={'places':places,
'messages':[ToolMessage(f'I found {len(places)} places', tool_call_id=tool_call_id)]})
except Exception as e:
return f'Error: error'
@tool
def show_places_found(state: Annotated[dict, InjectedState]):
"""
Tool to get the places found by previous tool calls and to show/display them.
It has links within that can also be used for directions
always show the links
args: none
"""
return state['places']
class maps_agent:
def __init__(self,llm: any):
self.agent=self._setup(llm)
def _setup(self,llm):
langgraph_tools=[get_current_location_tool,look_for_places, find_places_near_me,show_places_found]
graph_builder = StateGraph(State)
# Modification: tell the LLM which tools it can call
llm_with_tools = llm.bind_tools(langgraph_tools)
tool_node = ToolNode(tools=langgraph_tools)
def chatbot(state: State):
""" travel assistant that answers user questions about their trip.
Depending on the request, leverage which tools to use if necessary."""
return {"messages": [llm_with_tools.invoke(state['messages'])]}
graph_builder.add_node("chatbot", chatbot)
graph_builder.add_node('current_location',get_current_location_node)
graph_builder.add_node("tools", tool_node)
# Any time a tool is called, we return to the chatbot to decide the next step
graph_builder.set_entry_point("current_location")
graph_builder.add_edge('current_location','chatbot')
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_conditional_edges(
"chatbot",
tools_condition,
)
memory=MemorySaver()
graph=graph_builder.compile(checkpointer=memory)
return graph
def get_state(self, state_val:str):
config = {"configurable": {"thread_id": "1"}}
return self.agent.get_state(config).values[state_val]
def stream(self,input:str):
config = {"configurable": {"thread_id": "1"}}
input_message = HumanMessage(content=input)
for event in self.agent.stream({"messages": [input_message]}, config, stream_mode="values"):
event["messages"][-1].pretty_print()
def chat(self,input:str):
config = {"configurable": {"thread_id": "1"}}
response=self.agent.invoke({'messages':HumanMessage(content=str(input))},config)
return response['messages'][-1].content