from langgraph.graph import StateGraph, START, END from langchain_core.messages import ( HumanMessage, ) from langgraph.checkpoint.memory import MemorySaver from langchain_core.output_parsers import JsonOutputParser from typing_extensions import TypedDict #get graph visuals from IPython.display import Image, display from langchain_core.runnables.graph import MermaidDrawMethod from pydantic import BaseModel, Field import os from typing_extensions import TypedDict from typing import Optional from dotenv import load_dotenv #getting current location import geocoder import os import requests load_dotenv() GOOGLE_API_KEY=os.getenv('google_api_key') class State(TypedDict): """ A dictionnary representing the state of the agent. """ node_message: str query: str #location data latitude: str longitude: str address: str place_query: str #results from place search places: dict route:str def get_current_location_node(state: State): """ 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 { 'latitude':latitude, 'longitude':longitude, 'address':address, 'node_message':{'latitude':latitude, 'longitude':longitude, 'address':address}} else: return {'node_message':'failed'} def router_node(state=State): route=state.get('route') if route=='look_for_places': return 'to_look_for_places' elif route=='current_loc': return 'to_current_loc' def look_for_places_node(state: State): """ 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. Alaways include the links in the respons, but not longitude or latitude """ try: response=requests.get(f'https://maps.googleapis.com/maps/api/place/textsearch/json?query={state.get("place_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 {'places':places, 'node_message':places} except Exception as e: return {'node_message': e} 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, show_places_found] def agent_node(state:State): class Form(BaseModel): route: str = Field(description= 'return current_loc or look_for_places') place_query: Optional[str] = Field(description= ' if the query is to look for a place return the place_query has to be in this format eg.Spicy%20Vegetarian%20Food%20in%20Sydney%20Australia') parser=JsonOutputParser(pydantic_object=Form) instruction=parser.get_format_instructions() response=llm.invoke([HumanMessage(content=f'based on this query:{state['query']}, return current_loc to get the current location or look_for_places for the route '+'\n\n'+instruction)]) response=parser.parse(response.content) route=response.get('route') place_query=response.get('place_query') return {'route':route, 'place_query': place_query} graph_builder = StateGraph(State) graph_builder.add_node('current_loc', get_current_location_node) graph_builder.add_node('look_for_places',look_for_places_node) graph_builder.add_node('agent',agent_node) graph_builder.add_edge(START,'agent') graph_builder.add_conditional_edges('agent',router_node,{'to_current_loc':'current_loc', 'to_look_for_places':'look_for_places'}) graph_builder.add_edge('current_loc',END) graph_builder.add_edge('look_for_places',END) memory=MemorySaver() graph=graph_builder.compile(checkpointer=memory) return graph def display_graph(self): return display( Image( self.agent.get_graph().draw_mermaid_png( draw_method=MermaidDrawMethod.API, ) ) ) def get_state(self, state_val:str): config = {"configurable": {"thread_id": "1"}} return self.agent.get_state(config).values[state_val] def chat(self,input:str): config = {"configurable": {"thread_id": "1"}} response=self.agent.invoke({'query':input},config) return response.get('node_message') def stream(self,input:str): config = {"configurable": {"thread_id": "1"}} for event in self.agent.stream({'query':input}, config, stream_mode="updates"): print(event)