feat: add customer phone to agent state on new conversations
This commit is contained in:
@@ -11,6 +11,7 @@ from dataclasses import dataclass
|
||||
@dataclass
|
||||
class AgentConfig:
|
||||
"""Configuración inmutable del agente para fácil testing."""
|
||||
|
||||
system_prompt: str = (
|
||||
"helpful assistant that can call tools when needed."
|
||||
" Always respond with a message."
|
||||
@@ -30,7 +31,7 @@ class Agent:
|
||||
model: Any,
|
||||
config: AgentConfig | None = None,
|
||||
checkpointer: Union[BaseCheckpointSaver, None] = None,
|
||||
tools: list | None = None
|
||||
tools: list | None = None,
|
||||
):
|
||||
"""
|
||||
Inicializa el agente.
|
||||
@@ -48,9 +49,7 @@ class Agent:
|
||||
self._tools = tools or []
|
||||
self._checkpointer = checkpointer or InMemorySaver()
|
||||
|
||||
self._tools_by_name: dict[str, Any] = {
|
||||
tool.name: tool for tool in self._tools
|
||||
}
|
||||
self._tools_by_name: dict[str, Any] = {tool.name: tool for tool in self._tools}
|
||||
self._compiled_agent = self._compile_agent()
|
||||
|
||||
self._pre_llm_hook: Callable | None = None
|
||||
@@ -66,14 +65,17 @@ class Agent:
|
||||
"""Retorna configuración actual."""
|
||||
return self._config
|
||||
|
||||
@property
|
||||
def checkpointer(self):
|
||||
"""Retorna el checkpointer para acceso al estado."""
|
||||
return self._checkpointer
|
||||
|
||||
@property
|
||||
def graph(self) -> StateGraph:
|
||||
"""Expone el grafo para inspección en tests."""
|
||||
return self._build_agent()
|
||||
|
||||
async def ainvoke(
|
||||
self, state: dict, config: RunnableConfig | None = None
|
||||
) -> dict:
|
||||
async def ainvoke(self, state: dict, config: RunnableConfig | None = None) -> dict:
|
||||
"""
|
||||
Versión asíncrona de invoke, necesaria para checkpointers de DB.
|
||||
"""
|
||||
@@ -86,9 +88,7 @@ class Agent:
|
||||
# Llamamos al método ainvoke del grafo compilado
|
||||
return await self._compiled_agent.ainvoke(state, config=config)
|
||||
|
||||
def invoke(
|
||||
self, state: dict, config: RunnableConfig | None = None
|
||||
) -> dict:
|
||||
def invoke(self, state: dict, config: RunnableConfig | None = None) -> dict:
|
||||
"""
|
||||
Ejecuta el agente con el estado inicial.
|
||||
|
||||
@@ -139,12 +139,7 @@ class Agent:
|
||||
|
||||
agent_builder.add_edge(START, "llm_call")
|
||||
agent_builder.add_conditional_edges(
|
||||
"llm_call",
|
||||
self._should_continue,
|
||||
{
|
||||
"tool_node": "tool_node",
|
||||
END: END
|
||||
}
|
||||
"llm_call", self._should_continue, {"tool_node": "tool_node", END: END}
|
||||
)
|
||||
agent_builder.add_edge("tool_node", "llm_call")
|
||||
|
||||
@@ -161,9 +156,9 @@ class Agent:
|
||||
if self._pre_llm_hook:
|
||||
self._pre_llm_hook(state)
|
||||
|
||||
messages = [
|
||||
SystemMessage(content=self._config.system_prompt)
|
||||
] + state["messages"]
|
||||
messages = [SystemMessage(content=self._config.system_prompt)] + state[
|
||||
"messages"
|
||||
]
|
||||
|
||||
model_with_tools = self._model.bind_tools(self._tools)
|
||||
|
||||
@@ -177,10 +172,7 @@ class Agent:
|
||||
f"Max iterations ({self._config.max_iterations}) exceeded"
|
||||
)
|
||||
|
||||
return {
|
||||
"messages": state["messages"] + [response],
|
||||
"llm_calls": new_calls
|
||||
}
|
||||
return {"messages": state["messages"] + [response], "llm_calls": new_calls}
|
||||
|
||||
def _tool_node(self, state: MessagesState) -> dict:
|
||||
"""
|
||||
@@ -200,9 +192,7 @@ class Agent:
|
||||
if self._post_tool_hook:
|
||||
self._post_tool_hook(state, result)
|
||||
|
||||
return {
|
||||
"messages": state["messages"] + tool_results
|
||||
}
|
||||
return {"messages": state["messages"] + tool_results}
|
||||
|
||||
def _execute_tool(self, tool_call: dict) -> ToolMessage:
|
||||
"""
|
||||
@@ -224,9 +214,8 @@ class Agent:
|
||||
observation = tool.invoke(tool_args)
|
||||
|
||||
return ToolMessage(
|
||||
content=str(observation),
|
||||
tool_call_id=tool_id,
|
||||
name=tool_name)
|
||||
content=str(observation), tool_call_id=tool_id, name=tool_name
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return ToolMessage(
|
||||
@@ -235,9 +224,7 @@ class Agent:
|
||||
name=tool_name,
|
||||
)
|
||||
|
||||
def _should_continue(
|
||||
self, state: MessagesState
|
||||
) -> Literal["tool_node", END]:
|
||||
def _should_continue(self, state: MessagesState) -> Literal["tool_node", END]:
|
||||
"""
|
||||
Condición de transición: ¿El LLM solicitó herramientas?
|
||||
"""
|
||||
|
||||
@@ -6,3 +6,4 @@ from typing_extensions import TypedDict, Annotated
|
||||
class MessagesState(TypedDict):
|
||||
messages: Annotated[list[AnyMessage], add_messages]
|
||||
llm_calls: int
|
||||
customer_phone: str
|
||||
|
||||
@@ -22,25 +22,13 @@ async def send_whatsapp_message(request_data: SendMessageScheme):
|
||||
delay = request_data.get("delay", 1200)
|
||||
|
||||
endpoint = f"{instance_url}/message/sendText/{instance_name}"
|
||||
headers = {
|
||||
"apikey": apikey,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
headers = {"apikey": apikey, "Content-Type": "application/json"}
|
||||
|
||||
payload = {
|
||||
"number": jid,
|
||||
"text": text,
|
||||
"delay": delay,
|
||||
"linkPreview": False
|
||||
}
|
||||
payload = {"number": jid, "text": text, "delay": delay, "linkPreview": False}
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
response = await client.post(
|
||||
endpoint,
|
||||
json=payload,
|
||||
headers=headers
|
||||
)
|
||||
response = await client.post(endpoint, json=payload, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
@@ -72,30 +60,34 @@ async def webhook_chat(request: Request, agent=Depends(get_agent)):
|
||||
if not user_message:
|
||||
return {"reply": "No text message provided."}
|
||||
|
||||
config = {
|
||||
"configurable": {
|
||||
"thread_id": thread_id
|
||||
}
|
||||
customer_phone = thread_id.split("@")[0]
|
||||
|
||||
config = {"configurable": {"thread_id": thread_id}}
|
||||
|
||||
initial_state = {
|
||||
"messages": [HumanMessage(content=user_message)],
|
||||
"llm_calls": 0,
|
||||
"customer_phone": "",
|
||||
}
|
||||
|
||||
messages = [HumanMessage(content=user_message)]
|
||||
existing_state = await agent.checkpointer.get(config)
|
||||
if existing_state is None:
|
||||
initial_state["customer_phone"] = customer_phone
|
||||
logger.info(f"Nueva conversación iniciada con cliente: {customer_phone}")
|
||||
|
||||
agent_response = await agent.ainvoke(
|
||||
{"messages": messages}, config=config
|
||||
)
|
||||
agent_response = await agent.ainvoke(initial_state, config=config)
|
||||
agent_response_content = agent_response["messages"][-1].content
|
||||
|
||||
clean_jid = thread_id.split('@')[0]
|
||||
|
||||
await send_whatsapp_message(
|
||||
SendMessageScheme(
|
||||
instance_url=server_url,
|
||||
instance_name=instance_name,
|
||||
apikey=api_key,
|
||||
jid=clean_jid,
|
||||
jid=customer_phone,
|
||||
text=agent_response_content,
|
||||
delay=1200
|
||||
))
|
||||
delay=1200,
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing webhook: {e}")
|
||||
|
||||
Reference in New Issue
Block a user