3030from ._base_llm_processor import BaseLlmRequestProcessor
3131
3232if typing .TYPE_CHECKING :
33+ from a2a .types import AgentCard
34+
3335 from ...agents import BaseAgent
3436 from ...agents import LlmAgent
37+ from ...agents .remote_a2a_agent import RemoteA2aAgent
3538
3639
3740class _AgentTransferLlmRequestProcessor (BaseLlmRequestProcessor ):
@@ -50,11 +53,11 @@ async def run_async(
5053 if not transfer_targets :
5154 return
5255
53- llm_request . append_instructions ([
54- _build_target_agents_instructions (
55- invocation_context .agent , transfer_targets
56- )
57- ])
56+ # Build instructions asynchronously to support A2A agent card resolution
57+ instructions = await _build_target_agents_instructions (
58+ invocation_context .agent , transfer_targets
59+ )
60+ llm_request . append_instructions ([ instructions ])
5861
5962 transfer_to_agent_tool = FunctionTool (func = transfer_to_agent )
6063 tool_context = ToolContext (invocation_context )
@@ -69,7 +72,102 @@ async def run_async(
6972request_processor = _AgentTransferLlmRequestProcessor ()
7073
7174
75+ def _build_target_agent_info_from_card (
76+ target_agent : RemoteA2aAgent , agent_card : AgentCard
77+ ) -> str :
78+ """Build rich agent info from A2A Agent Card.
79+
80+ Args:
81+ target_agent: The RemoteA2aAgent instance
82+ agent_card: The resolved A2A Agent Card
83+
84+ Returns:
85+ Formatted string with detailed agent information from the card
86+ """
87+ info_parts = [f'Agent name: { target_agent .name } ' ]
88+
89+ # Use card description if available, fallback to agent description
90+ description = agent_card .description or target_agent .description
91+ if description :
92+ info_parts .append (f'Agent description: { description } ' )
93+
94+ # Add skills information from the card
95+ if agent_card .skills :
96+ skills_info = []
97+ for skill in agent_card .skills :
98+ skill_desc = f' - { skill .name } '
99+ if skill .description :
100+ skill_desc += f': { skill .description } '
101+ if skill .tags :
102+ skill_desc += f' (tags: { ", " .join (skill .tags )} )'
103+ skills_info .append (skill_desc )
104+
105+ info_parts .append (f'Agent skills:\n ' + '\n ' .join (skills_info ))
106+
107+ # Add capabilities if available
108+ if agent_card .capabilities :
109+ caps = agent_card .capabilities
110+ cap_info = []
111+ if hasattr (caps , 'streaming' ) and caps .streaming :
112+ cap_info .append ('supports streaming' )
113+ if hasattr (caps , 'multimodal' ) and caps .multimodal :
114+ cap_info .append ('supports multimodal input/output' )
115+
116+ if cap_info :
117+ info_parts .append (f'Capabilities: { ", " .join (cap_info )} ' )
118+
119+ # Add input/output modes
120+ if agent_card .default_input_modes :
121+ info_parts .append (
122+ f'Input modes: { ", " .join (agent_card .default_input_modes )} '
123+ )
124+ if agent_card .default_output_modes :
125+ info_parts .append (
126+ f'Output modes: { ", " .join (agent_card .default_output_modes )} '
127+ )
128+
129+ return '\n ' .join (info_parts )
130+
131+
132+ async def _build_target_agents_info_async (target_agent : BaseAgent ) -> str :
133+ """Build agent info, using A2A Agent Card if available.
134+
135+ Args:
136+ target_agent: The agent to build info for
137+
138+ Returns:
139+ Formatted string with agent information
140+ """
141+ from ...agents .remote_a2a_agent import RemoteA2aAgent
142+
143+ # Check if this is a RemoteA2aAgent and ensure it's resolved
144+ if isinstance (target_agent , RemoteA2aAgent ):
145+ try :
146+ # Ensure the agent card is resolved
147+ await target_agent ._ensure_resolved ()
148+
149+ # If we have an agent card, use it to build rich info
150+ if target_agent ._agent_card :
151+ return _build_target_agent_info_from_card (
152+ target_agent , target_agent ._agent_card
153+ )
154+ except Exception :
155+ # If resolution fails, fall through to default behavior
156+ pass
157+
158+ # Fallback to original behavior for non-A2A agents or if card unavailable
159+ return _build_target_agents_info (target_agent )
160+
161+
72162def _build_target_agents_info (target_agent : BaseAgent ) -> str :
163+ """Build basic agent info (fallback for non-A2A agents).
164+
165+ Args:
166+ target_agent: The agent to build info for
167+
168+ Returns:
169+ Formatted string with basic agent information
170+ """
73171 return f"""
74172Agent name: { target_agent .name }
75173Agent description: { target_agent .description }
@@ -79,9 +177,18 @@ def _build_target_agents_info(target_agent: BaseAgent) -> str:
79177line_break = '\n '
80178
81179
82- def _build_target_agents_instructions (
180+ async def _build_target_agents_instructions (
83181 agent : LlmAgent , target_agents : list [BaseAgent ]
84182) -> str :
183+ """Build instructions for agent transfer with detailed agent information.
184+
185+ Args:
186+ agent: The current agent
187+ target_agents: List of agents that can be transferred to
188+
189+ Returns:
190+ Formatted instructions string with agent transfer information
191+ """
85192 # Build list of available agent names for the NOTE
86193 # target_agents already includes parent agent if applicable, so no need to add it again
87194 available_agent_names = [target_agent .name for target_agent in target_agents ]
@@ -94,12 +201,16 @@ def _build_target_agents_instructions(
94201 f'`{ name } `' for name in available_agent_names
95202 )
96203
204+ # Build agent info asynchronously to support A2A agent card resolution
205+ agent_info_list = []
206+ for target_agent in target_agents :
207+ agent_info = await _build_target_agents_info_async (target_agent )
208+ agent_info_list .append (agent_info )
209+
97210 si = f"""
98211You have a list of other agents to transfer to:
99212
100- { line_break .join ([
101- _build_target_agents_info (target_agent ) for target_agent in target_agents
102- ])}
213+ { line_break .join (agent_info_list )}
103214
104215If you are the best to answer the question according to your description, you
105216can answer it.
0 commit comments