Skip to content

Commit 9b141b6

Browse files
committed
fix: multi layer agents schema
1 parent d0b408b commit 9b141b6

4 files changed

Lines changed: 476 additions & 74 deletions

File tree

packages/uipath-openai-agents/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath-openai-agents"
3-
version = "0.0.2"
3+
version = "0.0.3"
44
description = "UiPath OpenAI Agents SDK"
55
readme = "README.md"
66
requires-python = ">=3.11"

packages/uipath-openai-agents/src/uipath_openai_agents/runtime/schema.py

Lines changed: 112 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)