294 lines
11 KiB
Python
294 lines
11 KiB
Python
import os
|
|
from dataclasses import dataclass, field
|
|
from time import perf_counter
|
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
|
|
from agno.agent import Agent
|
|
from agno.db.base import SessionType
|
|
from agno.models.openai import OpenAIChat
|
|
|
|
from .agente_kanboard import AgenteBasico
|
|
from .base import AgentDefinition
|
|
from mcp_wrapper import MCPConfigError, initialize_mcp_tools, load_mcp_tools
|
|
from utils.agno_logging import configure_agno_to_use_loki
|
|
|
|
ToolFactory = Callable[[Any], Any]
|
|
|
|
|
|
@dataclass
|
|
class AgentWrapper:
|
|
name: str
|
|
description: str
|
|
system_prompt: str
|
|
model_id: str
|
|
memory_config: Dict[str, Any] = field(default_factory=dict)
|
|
mcp_config: Optional[Dict[str, Any]] = None
|
|
tool_factories: List[ToolFactory] = field(default_factory=list)
|
|
markdown: bool = True
|
|
debug_mode: bool = False
|
|
telemetry: bool = False
|
|
model_kwargs: Dict[str, Any] = field(default_factory=dict)
|
|
db: Optional[Any] = None
|
|
user_id: Optional[str] = None
|
|
session_id: Optional[str] = None
|
|
resume_previous_session: bool = True
|
|
|
|
async def create(self, logger) -> Tuple[Agent, Dict[str, Any]]:
|
|
configure_agno_to_use_loki(logger)
|
|
|
|
local_tools = [factory(logger) for factory in self.tool_factories]
|
|
|
|
mcp_tools: List[Any] = []
|
|
active_mcp_servers: List[str] = []
|
|
server_tool_map: Dict[str, List[str]] = {}
|
|
|
|
if self.mcp_config:
|
|
mcp_config = dict(self.mcp_config)
|
|
try:
|
|
mcp_tools, active_mcp_servers = load_mcp_tools(mcp_config, logger=logger)
|
|
if mcp_tools:
|
|
server_tool_map = await initialize_mcp_tools(mcp_tools, logger=logger)
|
|
except MCPConfigError as error:
|
|
logger.error(
|
|
"🚨 Configuración MCP inválida",
|
|
add_fields={
|
|
"error": str(error),
|
|
"agent_call": {
|
|
"action": "load_mcp_config",
|
|
"agent_name": self.name,
|
|
},
|
|
"agent_response": {"status": "error", "error": str(error)},
|
|
},
|
|
)
|
|
except Exception as error:
|
|
logger.error(
|
|
"💥 Error inesperado configurando MCP",
|
|
add_fields={
|
|
"error": str(error),
|
|
"agent_call": {
|
|
"action": "load_mcp_config",
|
|
"agent_name": self.name,
|
|
},
|
|
"agent_response": {"status": "error", "error": str(error)},
|
|
},
|
|
)
|
|
raise
|
|
|
|
if self.db:
|
|
logger.info(
|
|
"🗄️ Base de datos SQLite configurada para el agente",
|
|
add_fields={
|
|
"agent_call": {
|
|
"action": "configure_memory",
|
|
"agent_name": self.name,
|
|
"db_file": getattr(self.db, "db_file", None),
|
|
},
|
|
"agent_response": {
|
|
"status": "db_ready",
|
|
"resume_previous_session": self.resume_previous_session,
|
|
},
|
|
},
|
|
)
|
|
|
|
if self.resume_previous_session and not self.session_id and self.user_id:
|
|
start = perf_counter()
|
|
try:
|
|
sessions = self.db.get_sessions(user_id=self.user_id, session_type=SessionType.AGENT)
|
|
duration_ms = round((perf_counter() - start) * 1000, 3)
|
|
if sessions:
|
|
self.session_id = getattr(sessions[0], "session_id", None)
|
|
logger.info(
|
|
"📂 Sesión previa recuperada desde memoria persistente",
|
|
add_fields={
|
|
"agent_call": {
|
|
"action": "restore_session",
|
|
"agent_name": self.name,
|
|
"user_id": self.user_id,
|
|
},
|
|
"agent_response": {
|
|
"status": "session_found",
|
|
"session_id": self.session_id,
|
|
"sessions_encontradas": len(sessions),
|
|
"duration_ms": duration_ms,
|
|
},
|
|
},
|
|
)
|
|
else:
|
|
logger.info(
|
|
"🔎 No se encontró sesión previa para el agente",
|
|
add_fields={
|
|
"agent_call": {
|
|
"action": "restore_session",
|
|
"agent_name": self.name,
|
|
"user_id": self.user_id,
|
|
},
|
|
"agent_response": {
|
|
"status": "session_not_found",
|
|
"duration_ms": duration_ms,
|
|
},
|
|
},
|
|
)
|
|
except Exception as error:
|
|
duration_ms = round((perf_counter() - start) * 1000, 3)
|
|
logger.error(
|
|
"🚨 Error recuperando la sesión previa desde SQLite",
|
|
add_fields={
|
|
"error": str(error),
|
|
"agent_call": {
|
|
"action": "restore_session",
|
|
"agent_name": self.name,
|
|
"user_id": self.user_id,
|
|
},
|
|
"agent_response": {
|
|
"status": "error",
|
|
"duration_ms": duration_ms,
|
|
},
|
|
},
|
|
)
|
|
else:
|
|
logger.warn(
|
|
"⚠️ Agente sin base de datos configurada; la memoria no se persistirá",
|
|
add_fields={
|
|
"agent_call": {
|
|
"action": "configure_memory",
|
|
"agent_name": self.name,
|
|
},
|
|
"agent_response": {
|
|
"status": "db_missing",
|
|
"add_history_to_context": self.memory_config.get("add_history_to_context", False),
|
|
},
|
|
},
|
|
)
|
|
|
|
api_key = os.getenv("OPENAI_API_KEY")
|
|
if not api_key:
|
|
logger.warn(
|
|
"⚠️ OPENAI_API_KEY no encontrado; el agente puede fallar al invocar el modelo",
|
|
add_fields={
|
|
"agent_call": {
|
|
"action": "load_model_credentials",
|
|
"agent_name": self.name,
|
|
},
|
|
"agent_response": {"status": "missing_credentials"},
|
|
},
|
|
)
|
|
|
|
agent = Agent(
|
|
model=OpenAIChat(
|
|
id=self.model_id,
|
|
api_key=api_key,
|
|
**self.model_kwargs,
|
|
),
|
|
db=self.db,
|
|
user_id=self.user_id,
|
|
session_id=self.session_id,
|
|
tools=[*local_tools, *mcp_tools],
|
|
markdown=self.markdown,
|
|
name=self.name,
|
|
description=self.description,
|
|
system_message=self.system_prompt,
|
|
debug_mode=self.debug_mode,
|
|
telemetry=self.telemetry,
|
|
**self.memory_config,
|
|
)
|
|
|
|
logger.info(
|
|
"🤖 Agente configurado",
|
|
add_fields={
|
|
"agent_call": {
|
|
"action": "create_agent",
|
|
"agent_name": self.name,
|
|
"local_tools": [tool.__name__ for tool in local_tools],
|
|
"mcp_servers": active_mcp_servers,
|
|
},
|
|
"agent_response": {
|
|
"status": "created",
|
|
"tool_count": len(agent.tools) if agent.tools else 0,
|
|
},
|
|
},
|
|
)
|
|
|
|
logger.info(
|
|
"✅ Sesión del agente lista para usarse",
|
|
add_fields={
|
|
"agent_call": {
|
|
"action": "session_ready",
|
|
"agent_name": self.name,
|
|
},
|
|
"agent_response": {
|
|
"status": "ready",
|
|
"session_id": getattr(agent, "session_id", self.session_id),
|
|
"user_id": self.user_id,
|
|
"db_file": getattr(self.db, "db_file", None) if self.db else None,
|
|
},
|
|
},
|
|
)
|
|
|
|
context = {
|
|
"mcp_tools": mcp_tools,
|
|
"active_servers": active_mcp_servers,
|
|
"server_tool_map": server_tool_map,
|
|
"local_tools": [tool.__name__ for tool in local_tools],
|
|
"agent_name": self.name,
|
|
"db_file": getattr(self.db, "db_file", None) if self.db else None,
|
|
"user_id": self.user_id,
|
|
"session_id": getattr(agent, "session_id", self.session_id),
|
|
"resume_previous_session": self.resume_previous_session,
|
|
}
|
|
self.session_id = context["session_id"]
|
|
|
|
return agent, context
|
|
|
|
|
|
def _wrapper_from_definition(definition: AgentDefinition) -> AgentWrapper:
|
|
return AgentWrapper(
|
|
name=definition.name,
|
|
description=definition.description,
|
|
system_prompt=definition.system_prompt,
|
|
model_id=definition.model_id,
|
|
memory_config=definition.memory_config,
|
|
mcp_config=definition.mcp_config,
|
|
tool_factories=definition.tool_factories,
|
|
markdown=definition.markdown,
|
|
debug_mode=definition.debug_mode,
|
|
telemetry=definition.telemetry,
|
|
model_kwargs=definition.model_kwargs,
|
|
db=definition.db,
|
|
user_id=definition.user_id,
|
|
session_id=definition.session_id,
|
|
resume_previous_session=definition.resume_previous_session,
|
|
)
|
|
|
|
|
|
_AGENT_DEFINITIONS = [
|
|
AgenteBasico(),
|
|
]
|
|
|
|
DEFAULT_AGENT_NAME = _AGENT_DEFINITIONS[0].get_registry_key()
|
|
|
|
AGENT_REGISTRY: Dict[str, AgentWrapper] = {
|
|
agent.get_registry_key(): _wrapper_from_definition(agent.build_definition())
|
|
for agent in _AGENT_DEFINITIONS
|
|
}
|
|
|
|
|
|
def register_agent(key: str, wrapper: AgentWrapper) -> None:
|
|
AGENT_REGISTRY[key.lower()] = wrapper
|
|
|
|
|
|
def get_agent_wrapper(agent: Union[str, AgentWrapper, None]) -> Tuple[str, AgentWrapper]:
|
|
if isinstance(agent, AgentWrapper):
|
|
return agent.name.lower(), agent
|
|
agent_key = (agent or DEFAULT_AGENT_NAME).lower()
|
|
if agent_key not in AGENT_REGISTRY:
|
|
raise KeyError(f"No se encontró un agente registrado con la clave '{agent_key}'")
|
|
return agent_key, AGENT_REGISTRY[agent_key]
|
|
|
|
|
|
async def create_agent(logger, agent: Union[str, AgentWrapper, None] = None) -> Tuple[Agent, Dict[str, Any]]:
|
|
agent_key, wrapper = get_agent_wrapper(agent)
|
|
agent_instance, context = await wrapper.create(logger)
|
|
context.setdefault("agent_name", wrapper.name)
|
|
context.setdefault("agent_key", agent_key)
|
|
return agent_instance, context
|