from langchain_community.tools.tavily_search import TavilySearchResults from typing import Annotated from typing_extensions import TypedDict from langgraph.graph.message import add_messages from langchain_openai import ChatOpenAI from dotenv import load_dotenv from langgraph.prebuilt import create_react_agent from langchain_core.prompts import ChatPromptTemplate from langgraph.checkpoint.memory import MemorySaver from langchain_tools.agent_tools import ( redact_email, list_calendar_events, create_calendar_event, get_company_info, get_current_date_and_time ) from langchain_community.tools.gmail.utils import ( build_resource_service, get_gmail_credentials, ) from langchain_community.agent_toolkits import GmailToolkit load_dotenv() # Inicialiamos un LLM de OpenaIA llm = ChatOpenAI( model="gpt-4o-mini", temperature=0.9 ) toolkit = GmailToolkit() # Can review scopes here https://developers.google.com/gmail/api/auth/scopes # For instance, readonly scope is 'https://www.googleapis.com/auth/gmail.readonly' credentials = get_gmail_credentials( token_file="token.json", scopes=["https://mail.google.com/"], client_secrets_file="credentials.json", ) api_resource = build_resource_service(credentials=credentials) toolkit = GmailToolkit(api_resource=api_resource) # creamos la lista de herramientas de gmail tools = toolkit.get_tools() search = TavilySearchResults(max_results=2) tools.append(search) tools.append(redact_email) tools.append(list_calendar_events) tools.append(create_calendar_event) tools.append(get_company_info) tools.append(get_current_date_and_time) system_prompt = ChatPromptTemplate.from_messages( [ ("system", "Eres Mariana, el asistente virtual de OneCluster, una empresa de software que ofrece soluciones personalizadas. Asume el tono de J.A.R.V.I.S.: cordial, atento y con tacto en todo momento."), # Instrucciones sobre presentación y tono ("system", "Preséntate como Mariana en el primer mensaje y pregunta el nombre del usuario si no lo tienes registrado."), ("system", "Si el usuario ya ha interactuado antes, usa su nombre sin necesidad de volver a preguntar."), ("system", "Si el primer mensaje del usuario es una solicitud, pregúntale su nombre antes de responder si aún no lo conoces."), # Instrucciones sobre el manejo de solicitudes y tareas ("system", "OneCluster es una empresa de software especializada en desarrollo a medida. Solo responde a preguntas y solicitudes relacionadas con la empresa y sus servicios."), ("system", "Si necesitas información adicional sobre la empresa, usa la función get_company_info."), ("system", "Antes de enviar correos o crear eventos, muestra los detalles al usuario para que los confirme antes de ejecutar la tarea."), # Cómo manejar preguntas fuera del alcance ("system", "Si te preguntan algo no relacionado con los servicios de OneCluster, responde que solo puedes ayudar con temas relacionados con la empresa y sus soluciones."), # Prohibición de revelar herramientas internas ("system", "Evita mencionar o hacer alusión a las herramientas que utilizas internamente. Esa información es confidencial."), # Placeholder para el contenido dinámico de la conversación ("placeholder", "{messages}"), ] ) class State(TypedDict): messages: Annotated[list, add_messages] is_last_step: bool # Cambiar a booleano si es necesario # Creamos el graph con el estado definido graph= create_react_agent( model = llm, tools = tools, state_schema = State, state_modifier = system_prompt, checkpointer = MemorySaver() ) config= {"configurable": {"thread_id": "thread-1", "recursion_limit": 50}} while True: user_input = input("User: ") if user_input.lower() in ["quit", "exit", "q"]: print("Goodbye!") break events = graph.stream( {"messages": [("user", user_input)], "is_last_step": False}, config, stream_mode = "updates") for event in events: if "agent" in event: print(f"\nAsistente: {event["agent"]["messages"][-1].content}\n")