feat(agent): update A2A route and enhance agent builder with improved logging and validation

This commit is contained in:
Davidson Gomes 2025-05-05 15:12:48 -03:00
parent 5b7b690b20
commit 5827b44a21
4 changed files with 92 additions and 45 deletions

View File

@ -124,7 +124,7 @@ def get_task_manager(agent_id, db=None, reuse=True, operation_type="query"):
return task_manager
@router.post("/{agent_id}")
@router.post("/{agent_id}/rpc")
async def process_a2a_request(
agent_id: uuid.UUID,
request: Request,

View File

@ -24,7 +24,7 @@ class AgentBuilder:
self.custom_tool_builder = CustomToolBuilder()
self.mcp_service = MCPService()
def _get_current_time(self, city: str = "new york") -> dict:
def get_current_time(self, city: str = "new york") -> dict:
"""Get the current time in a city."""
city_timezones = {
"new york": "America/New_York",
@ -99,7 +99,7 @@ class AgentBuilder:
)
# Combine all tools
all_tools = custom_tools + mcp_tools + [self._get_current_time]
all_tools = custom_tools + mcp_tools + [self.get_current_time]
now = datetime.now()
current_datetime = now.strftime("%d/%m/%Y %H:%M")
@ -116,7 +116,7 @@ class AgentBuilder:
)
# Add get_current_time instructions to prompt
formatted_prompt += "\n\n<get_current_time_instructions>Use the get_current_time tool to get the current time in a city. The tool is available in the tools section of the configuration. Use 'new york' by default if no city is provided.</get_current_time_instructions>\n\n"
formatted_prompt += "<get_current_time_instructions>Use the get_current_time tool to get the current time in a city. The tool is available in the tools section of the configuration. Use 'new york' by default if no city is provided.ALWAYS use the 'get_current_time' tool when you need to use the current date and time in any type of situation, whether in interaction with the user or for calling other tools.</get_current_time_instructions>\n\n"
# Check if load_memory is enabled
# before_model_callback_func = None
@ -144,10 +144,15 @@ class AgentBuilder:
"""Get and create LLM sub-agents."""
sub_agents = []
for sub_agent_id in sub_agent_ids:
agent = get_agent(self.db, sub_agent_id)
sub_agent_id_str = str(sub_agent_id)
agent = get_agent(self.db, sub_agent_id_str)
if agent is None:
raise AgentNotFoundError(f"Agent with ID {sub_agent_id} not found")
logger.error(f"Sub-agent not found: {sub_agent_id_str}")
raise AgentNotFoundError(f"Agent with ID {sub_agent_id_str} not found")
logger.info(f"Sub-agent found: {agent.name} (type: {agent.type})")
if agent.type == "llm":
sub_agent, exit_stack = await self._create_llm_agent(agent)
@ -163,6 +168,10 @@ class AgentBuilder:
raise ValueError(f"Invalid agent type: {agent.type}")
sub_agents.append((sub_agent, exit_stack))
logger.info(f"Sub-agent added: {agent.name}")
logger.info(f"Sub-agents created: {len(sub_agents)}")
logger.info(f"Sub-agents: {str(sub_agents)}")
return sub_agents
@ -224,15 +233,33 @@ class AgentBuilder:
self, root_agent
) -> Tuple[SequentialAgent | ParallelAgent | LoopAgent, Optional[AsyncExitStack]]:
"""Build a composite agent (Sequential, Parallel or Loop) with its sub-agents."""
logger.info(f"Processing sub-agents for agent {root_agent.type}")
logger.info(
f"Processing sub-agents for agent {root_agent.type} (ID: {root_agent.id}, Name: {root_agent.name})"
)
if not root_agent.config.get("sub_agents"):
logger.error(
f"Sub_agents configuration not found or empty for agent {root_agent.name}"
)
raise ValueError(f"Missing sub_agents configuration for {root_agent.name}")
logger.info(
f"Sub-agents IDs to be processed: {root_agent.config.get('sub_agents', [])}"
)
sub_agents_with_stacks = await self._get_sub_agents(
root_agent.config.get("sub_agents", [])
)
logger.info(
f"Sub-agents processed: {len(sub_agents_with_stacks)} of {len(root_agent.config.get('sub_agents', []))}"
)
sub_agents = [agent for agent, _ in sub_agents_with_stacks]
logger.info(f"Extracted sub-agents: {[agent.name for agent in sub_agents]}")
if root_agent.type == "sequential":
logger.info("Creating SequentialAgent")
logger.info(f"Creating SequentialAgent with {len(sub_agents)} sub-agents")
return (
SequentialAgent(
name=root_agent.name,
@ -242,7 +269,7 @@ class AgentBuilder:
None,
)
elif root_agent.type == "parallel":
logger.info("Creating ParallelAgent")
logger.info(f"Creating ParallelAgent with {len(sub_agents)} sub-agents")
return (
ParallelAgent(
name=root_agent.name,
@ -252,7 +279,7 @@ class AgentBuilder:
None,
)
elif root_agent.type == "loop":
logger.info("Creating LoopAgent")
logger.info(f"Creating LoopAgent with {len(sub_agents)} sub-agents")
return (
LoopAgent(
name=root_agent.name,

View File

@ -85,23 +85,31 @@ async def run_agent(
new_message=content,
)
last_response = None
all_responses = []
async for event in events_async:
if event.is_final_response():
if event.content and event.content.parts:
# Assuming text response in the first part
await response_queue.put(event.content.parts[0].text)
elif event.actions and event.actions.escalate:
await response_queue.put(
f"Agent escalated: {event.error_message or 'No specific message.'}"
)
if (
event.content
and event.content.parts
and event.content.parts[0].text
):
current_text = event.content.parts[0].text
last_response = current_text
all_responses.append(current_text)
if event.actions and event.actions.escalate:
escalate_text = f"Agent escalated: {event.error_message or 'No specific message.'}"
await response_queue.put(escalate_text)
execution_completed.set()
break
return
if not execution_completed.is_set():
if last_response:
await response_queue.put(last_response)
else:
await response_queue.put("Finished without specific response")
execution_completed.set()
execution_completed.set()
except Exception as e:
logger.error(f"Error in process_events: {str(e)}")
await response_queue.put(f"Error: {str(e)}")

View File

@ -35,12 +35,26 @@ def _convert_uuid_to_str(obj):
return obj
def validate_sub_agents(db: Session, sub_agents: List[uuid.UUID]) -> bool:
def validate_sub_agents(db: Session, sub_agents: List[Union[uuid.UUID, str]]) -> bool:
"""Validate if all sub-agents exist"""
logger.info(f"Validando sub-agentes: {sub_agents}")
if not sub_agents:
logger.warning("Lista de sub-agentes vazia")
return False
for agent_id in sub_agents:
agent = get_agent(db, agent_id)
# Garantir que o ID esteja no formato correto
agent_id_str = str(agent_id)
logger.info(f"Validando sub-agente com ID: {agent_id_str}")
agent = get_agent(db, agent_id_str)
if not agent:
logger.warning(f"Sub-agente não encontrado: {agent_id_str}")
return False
logger.info(f"Sub-agente válido: {agent.name} (ID: {agent_id_str})")
logger.info(f"Todos os {len(sub_agents)} sub-agentes são válidos")
return True
@ -133,31 +147,29 @@ async def create_agent(db: Session, agent: AgentCreate) -> Agent:
detail=f"Failed to process agent card: {str(e)}",
)
# Additional sub-agent validation (for non-llm and non-a2a types)
elif agent.type != "llm":
if not isinstance(agent.config, dict):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid configuration: must be an object with sub_agents",
)
if not isinstance(agent.config, dict):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid configuration: must be an object with sub_agents",
)
if "sub_agents" not in agent.config:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid configuration: sub_agents is required for sequential, parallel or loop agents",
)
if "sub_agents" not in agent.config:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid configuration: sub_agents is required for sequential, parallel or loop agents",
)
if not agent.config["sub_agents"]:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid configuration: sub_agents cannot be empty",
)
if not agent.config["sub_agents"]:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid configuration: sub_agents cannot be empty",
)
if not validate_sub_agents(db, agent.config["sub_agents"]):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="One or more sub-agents do not exist",
)
if not validate_sub_agents(db, agent.config["sub_agents"]):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="One or more sub-agents do not exist",
)
# Process the configuration before creating the agent
config = agent.config