@@ -143,8 +143,9 @@ def get_agent_schema(agent: Agent) -> UiPathRuntimeGraph:
143143 """
144144 Extract graph structure from an OpenAI Agent.
145145
146- OpenAI Agents can delegate to other agents through handoffs,
147- creating a hierarchical agent structure.
146+ OpenAI Agents are represented as simple nodes. Tools and handoff agents
147+ are also represented as nodes. This function recursively explores nested
148+ agents to discover all tools and their connections.
148149
149150 Args:
150151 agent: An OpenAI Agent instance
@@ -154,83 +155,95 @@ def get_agent_schema(agent: Agent) -> UiPathRuntimeGraph:
154155 """
155156 nodes : list [UiPathRuntimeNode ] = []
156157 edges : list [UiPathRuntimeEdge ] = []
157-
158- # Start node
159- nodes .append (
160- UiPathRuntimeNode (
161- id = "__start__" ,
162- name = "__start__" ,
163- type = "__start__" ,
164- subgraph = None ,
165- )
166- )
167-
168- # Main agent node (always type "model" since it's an LLM)
169- agent_name = getattr (agent , "name" , "agent" )
170- nodes .append (
171- UiPathRuntimeNode (
172- id = agent_name ,
173- name = agent_name ,
174- type = "model" ,
175- subgraph = None ,
176- )
177- )
178-
179- # Connect start to main agent
180- edges .append (
181- UiPathRuntimeEdge (
182- source = "__start__" ,
183- target = agent_name ,
184- label = "input" ,
158+ visited : set [str ] = set () # Track visited agents to avoid circular references
159+ tool_ids : set [str ] = set () # Track added tool IDs to avoid duplicates
160+
161+ def _add_agent_and_tools (current_agent : Agent ) -> None :
162+ """Recursively add agent, its tools, and nested agents to the graph."""
163+ agent_name = getattr (current_agent , "name" , "agent" )
164+
165+ # Prevent circular references using agent name
166+ if agent_name in visited :
167+ return
168+ visited .add (agent_name )
169+
170+ # Add agent node (first visit always adds the node)
171+ nodes .append (
172+ UiPathRuntimeNode (
173+ id = agent_name ,
174+ name = agent_name ,
175+ type = "model" ,
176+ subgraph = None ,
177+ metadata = None ,
178+ )
185179 )
186- )
187180
188- # Add tool nodes if tools are available
189- tools = getattr (agent , "tools" , None ) or []
190- if tools :
181+ # Process tools (including agent-tools)
182+ tools = getattr (current_agent , "tools" , None ) or []
191183 for tool in tools :
192- # Extract tool name - handle various tool types
193- tool_name = _get_tool_name (tool )
194- if tool_name :
195- nodes .append (
196- UiPathRuntimeNode (
197- id = tool_name ,
198- name = tool_name ,
199- type = "tool" ,
200- subgraph = None ,
184+ if isinstance (tool , Agent ):
185+ tool_agent_name = getattr (tool , "name" , _get_tool_name (tool ))
186+ if tool_agent_name and tool_agent_name not in visited :
187+ # Recursively process agent-tool
188+ _add_agent_and_tools (tool )
189+
190+ # Add edges for agent-tool
191+ edges .append (
192+ UiPathRuntimeEdge (
193+ source = agent_name ,
194+ target = tool_agent_name ,
195+ label = "tool_call" ,
196+ )
201197 )
202- )
203- # Bidirectional edges: agent calls tool, tool returns to agent
204- edges .append (
205- UiPathRuntimeEdge (
206- source = agent_name ,
207- target = tool_name ,
208- label = "tool_call" ,
198+ edges .append (
199+ UiPathRuntimeEdge (
200+ source = tool_agent_name ,
201+ target = agent_name ,
202+ label = "tool_result" ,
203+ )
209204 )
210- )
211- edges .append (
212- UiPathRuntimeEdge (
213- source = tool_name ,
214- target = agent_name ,
215- label = "tool_result" ,
205+ else :
206+ # Regular tool
207+ tool_name = _get_tool_name (tool )
208+ if tool_name :
209+ # Add tool node if not already added
210+ if tool_name not in tool_ids :
211+ nodes .append (
212+ UiPathRuntimeNode (
213+ id = tool_name ,
214+ name = tool_name ,
215+ type = "tool" ,
216+ subgraph = None ,
217+ metadata = None ,
218+ )
219+ )
220+ tool_ids .add (tool_name )
221+
222+ # Add edges for tool
223+ edges .append (
224+ UiPathRuntimeEdge (
225+ source = agent_name ,
226+ target = tool_name ,
227+ label = "tool_call" ,
228+ )
229+ )
230+ edges .append (
231+ UiPathRuntimeEdge (
232+ source = tool_name ,
233+ target = agent_name ,
234+ label = "tool_result" ,
235+ )
216236 )
217- )
218237
219- # Add handoff agents as nodes
220- handoffs = getattr (agent , "handoffs" , None ) or []
221- if handoffs :
238+ # Process handoff agents
239+ handoffs = getattr (current_agent , "handoffs" , None ) or []
222240 for handoff_agent in handoffs :
223241 handoff_name = getattr (handoff_agent , "name" , None )
224- if handoff_name :
225- nodes .append (
226- UiPathRuntimeNode (
227- id = handoff_name ,
228- name = handoff_name ,
229- type = "model" ,
230- subgraph = None , # Handoff agents are peers, not subgraphs
231- )
232- )
233- # Handoff edges
242+ if handoff_name and handoff_name not in visited :
243+ # Recursively process handoff agent
244+ _add_agent_and_tools (handoff_agent )
245+
246+ # Add handoff edges
234247 edges .append (
235248 UiPathRuntimeEdge (
236249 source = agent_name ,
@@ -246,17 +259,44 @@ def get_agent_schema(agent: Agent) -> UiPathRuntimeGraph:
246259 )
247260 )
248261
249- # End node
262+ # Add __start__ node
263+ nodes .append (
264+ UiPathRuntimeNode (
265+ id = "__start__" ,
266+ name = "__start__" ,
267+ type = "__start__" ,
268+ subgraph = None ,
269+ metadata = None ,
270+ )
271+ )
272+
273+ # Recursively build graph starting from main agent
274+ _add_agent_and_tools (agent )
275+
276+ # Get the main agent name
277+ agent_name = getattr (agent , "name" , "agent" )
278+
279+ # Add __end__ node
250280 nodes .append (
251281 UiPathRuntimeNode (
252282 id = "__end__" ,
253283 name = "__end__" ,
254284 type = "__end__" ,
255285 subgraph = None ,
286+ metadata = None ,
287+ )
288+ )
289+
290+ # Connect start to main agent
291+ edges .append (
292+ UiPathRuntimeEdge (
293+ source = "__start__" ,
294+ target = agent_name ,
295+ label = "input" ,
256296 )
257297 )
258298
259- # Connect agent to end
299+ # Connect main agent to end
260300 edges .append (
261301 UiPathRuntimeEdge (
262302 source = agent_name ,
0 commit comments