mejorado bot de telegram
This commit is contained in:
+76
-26
@@ -2,12 +2,15 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import html
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional, Set
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
from agno.agent.agent import RunOutput
|
||||||
|
from agno.run.agent import ToolCallStartedEvent
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
from agentes import DEFAULT_AGENT_NAME, create_agent
|
from agentes import DEFAULT_AGENT_NAME, create_agent
|
||||||
@@ -51,11 +54,16 @@ class TelegramKanBot:
|
|||||||
|
|
||||||
self.agent = None
|
self.agent = None
|
||||||
self.context: Dict[str, Any] = {}
|
self.context: Dict[str, Any] = {}
|
||||||
|
self.mcp_tool_names: Set[str] = set()
|
||||||
|
|
||||||
async def setup_agent(self) -> None:
|
async def setup_agent(self) -> None:
|
||||||
agent, context = await create_agent(self.logger, agent=self.agent_key)
|
agent, context = await create_agent(self.logger, agent=self.agent_key)
|
||||||
self.agent = agent
|
self.agent = agent
|
||||||
self.context = context
|
self.context = context
|
||||||
|
server_tool_map = context.get("server_tool_map") or {}
|
||||||
|
self.mcp_tool_names = {
|
||||||
|
tool_name for tools in server_tool_map.values() for tool_name in (tools or [])
|
||||||
|
}
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
"🤖 Agente Kan para Telegram listo",
|
"🤖 Agente Kan para Telegram listo",
|
||||||
add_fields={
|
add_fields={
|
||||||
@@ -186,8 +194,49 @@ class TelegramKanBot:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
typing_task = asyncio.create_task(self._typing_indicator(chat_id))
|
typing_task = asyncio.create_task(self._typing_indicator(chat_id))
|
||||||
|
notified_tool_calls: Set[str] = set()
|
||||||
|
response: Optional[RunOutput] = None
|
||||||
try:
|
try:
|
||||||
response = await self.agent.arun(text)
|
run_result = await self.agent.arun(
|
||||||
|
text,
|
||||||
|
stream=True,
|
||||||
|
stream_events=True,
|
||||||
|
yield_run_response=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(run_result, RunOutput):
|
||||||
|
response = run_result
|
||||||
|
else:
|
||||||
|
async for event in run_result:
|
||||||
|
if isinstance(event, RunOutput):
|
||||||
|
response = event
|
||||||
|
elif isinstance(event, ToolCallStartedEvent):
|
||||||
|
await self._maybe_notify_tool_execution(chat_id, event, notified_tool_calls)
|
||||||
|
if response is None:
|
||||||
|
raise RuntimeError("El agente no devolvió respuesta.")
|
||||||
|
|
||||||
|
content = (response.content or "(respuesta vacía)").strip()
|
||||||
|
if not content:
|
||||||
|
content = "(respuesta vacía)"
|
||||||
|
|
||||||
|
await self._send_message(chat_id, content)
|
||||||
|
|
||||||
|
if response.metrics:
|
||||||
|
self.logger.info(
|
||||||
|
"📊 Métricas de ejecución",
|
||||||
|
add_fields={
|
||||||
|
"agent_call": {"action": "agent_run_metrics"},
|
||||||
|
"agent_response": {
|
||||||
|
"status": "completed",
|
||||||
|
"metrics": {
|
||||||
|
"input_tokens": response.metrics.input_tokens,
|
||||||
|
"output_tokens": response.metrics.output_tokens,
|
||||||
|
"total_tokens": response.metrics.total_tokens,
|
||||||
|
"duration_seconds": response.metrics.duration,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
except Exception as error: # pragma: no cover - manejo defensivo
|
except Exception as error: # pragma: no cover - manejo defensivo
|
||||||
self.logger.exception(
|
self.logger.exception(
|
||||||
error,
|
error,
|
||||||
@@ -203,32 +252,11 @@ class TelegramKanBot:
|
|||||||
with suppress(asyncio.CancelledError):
|
with suppress(asyncio.CancelledError):
|
||||||
await typing_task
|
await typing_task
|
||||||
|
|
||||||
content = (response.content or "(respuesta vacía)").strip()
|
async def _send_message(self, chat_id: int, text: str, *, parse_mode: Optional[str] = None) -> None:
|
||||||
if not content:
|
|
||||||
content = "(respuesta vacía)"
|
|
||||||
|
|
||||||
await self._send_message(chat_id, content)
|
|
||||||
|
|
||||||
if response.metrics:
|
|
||||||
self.logger.info(
|
|
||||||
"📊 Métricas de ejecución",
|
|
||||||
add_fields={
|
|
||||||
"agent_call": {"action": "agent_run_metrics"},
|
|
||||||
"agent_response": {
|
|
||||||
"status": "completed",
|
|
||||||
"metrics": {
|
|
||||||
"input_tokens": response.metrics.input_tokens,
|
|
||||||
"output_tokens": response.metrics.output_tokens,
|
|
||||||
"total_tokens": response.metrics.total_tokens,
|
|
||||||
"duration_seconds": response.metrics.duration,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
async def _send_message(self, chat_id: int, text: str) -> None:
|
|
||||||
for chunk in self._chunk_text(text):
|
for chunk in self._chunk_text(text):
|
||||||
payload = {"chat_id": chat_id, "text": chunk}
|
payload = {"chat_id": chat_id, "text": chunk}
|
||||||
|
if parse_mode:
|
||||||
|
payload["parse_mode"] = parse_mode
|
||||||
try:
|
try:
|
||||||
response = await asyncio.to_thread(
|
response = await asyncio.to_thread(
|
||||||
self.session.post,
|
self.session.post,
|
||||||
@@ -255,6 +283,28 @@ class TelegramKanBot:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def _maybe_notify_tool_execution(
|
||||||
|
self,
|
||||||
|
chat_id: int,
|
||||||
|
event: ToolCallStartedEvent,
|
||||||
|
notified_tool_calls: Set[str],
|
||||||
|
) -> None:
|
||||||
|
if not self.mcp_tool_names:
|
||||||
|
return
|
||||||
|
tool = getattr(event, "tool", None)
|
||||||
|
if not tool or not tool.tool_name:
|
||||||
|
return
|
||||||
|
tool_identifier = tool.tool_call_id or f"{tool.tool_name}:{len(notified_tool_calls)}"
|
||||||
|
if tool_identifier in notified_tool_calls:
|
||||||
|
return
|
||||||
|
if tool.tool_name not in self.mcp_tool_names:
|
||||||
|
return
|
||||||
|
|
||||||
|
notified_tool_calls.add(tool_identifier)
|
||||||
|
|
||||||
|
escaped_name = html.escape(tool.tool_name)
|
||||||
|
await self._send_message(chat_id, f"<i>Ejecutado {escaped_name}</i>", parse_mode="HTML")
|
||||||
|
|
||||||
async def _typing_indicator(self, chat_id: int) -> None:
|
async def _typing_indicator(self, chat_id: int) -> None:
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
|
|||||||
Reference in New Issue
Block a user