Skip to content

Commit 7e245c4

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: Validate that no old orchestrators are used inside Workflow graphs
Adds validation to `validate_graph` to raise an error if any of the deprecated orchestrators (`SequentialAgent`, `ParallelAgent`, `LoopAgent`) are included as nodes in a `Workflow` static graph. Also includes logic to recursively unwrap wrapped nodes (e.g. `_ParallelWorker`) to ensure the underlying agent types are validated. PiperOrigin-RevId: 947349988
1 parent c441ab2 commit 7e245c4

2 files changed

Lines changed: 0 additions & 110 deletions

File tree

src/google/adk/workflow/utils/_graph_validation.py

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -198,43 +198,6 @@ def _validate_chat_agent_wiring(edges: list[Edge]) -> None:
198198
)
199199

200200

201-
def _validate_no_old_orchestrators(nodes: list[BaseNode]) -> None:
202-
"""Validates that deprecated orchestrators are not used in the workflow graph."""
203-
from ...agents.loop_agent import LoopAgent
204-
from ...agents.parallel_agent import ParallelAgent
205-
from ...agents.sequential_agent import SequentialAgent
206-
207-
for node in nodes:
208-
target = node
209-
visited = set()
210-
while True:
211-
if id(target) in visited:
212-
break
213-
visited.add(id(target))
214-
215-
next_target = None
216-
for attr in ("node", "_node", "agent", "_agent"):
217-
val = getattr(target, attr, None)
218-
if isinstance(val, BaseNode):
219-
next_target = val
220-
break
221-
222-
if next_target is not None:
223-
target = next_target
224-
else:
225-
break
226-
227-
if isinstance(target, (SequentialAgent, ParallelAgent, LoopAgent)):
228-
agent_type_name = type(target).__name__
229-
raise ValueError(
230-
f"Graph validation failed. Node '{node.name}' uses deprecated"
231-
f" orchestrator '{agent_type_name}'. Old orchestrators"
232-
" (SequentialAgent, ParallelAgent, LoopAgent) cannot be used inside"
233-
" a Workflow static graph. Please migrate the orchestration logic to"
234-
" Workflow nodes and edges."
235-
)
236-
237-
238201
def _compute_terminal_nodes(
239202
nodes: list[BaseNode], edges: list[Edge]
240203
) -> set[str]:
@@ -256,5 +219,4 @@ def validate_graph(nodes: list[BaseNode], edges: list[Edge]) -> set[str]:
256219
_detect_unconditional_cycles(edges, node_names)
257220
_validate_static_schemas(edges)
258221
_validate_chat_agent_wiring(edges)
259-
_validate_no_old_orchestrators(nodes)
260222
return _compute_terminal_nodes(nodes, edges)

tests/unittests/workflow/utils/test_graph_validation.py

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616

1717
import logging
1818

19-
from google.adk.agents.loop_agent import LoopAgent
20-
from google.adk.agents.parallel_agent import ParallelAgent
21-
from google.adk.agents.sequential_agent import SequentialAgent
22-
from google.adk.workflow import BaseNode
2319
from google.adk.workflow import Edge
2420
from google.adk.workflow import START
2521
from google.adk.workflow._graph import DEFAULT_ROUTE
@@ -392,71 +388,3 @@ def test_chat_agent_wiring_validation_only_runs_on_llm_agent() -> None:
392388
validate_graph(
393389
graph.nodes, graph.edges
394390
) # Should not raise because node_b is a TestingNode, not LlmAgent
395-
396-
397-
@pytest.mark.parametrize(
398-
'agent_class, name, expected_class_name',
399-
[
400-
(SequentialAgent, 'SeqAgent', 'SequentialAgent'),
401-
(ParallelAgent, 'ParAgent', 'ParallelAgent'),
402-
(LoopAgent, 'LoopAgent', 'LoopAgent'),
403-
],
404-
)
405-
def test_old_orchestrators_fail_validation(
406-
agent_class: type[BaseNode],
407-
name: str,
408-
expected_class_name: str,
409-
) -> None:
410-
"""Tests that a graph containing deprecated orchestrators fails validation."""
411-
agent = agent_class(name=name, sub_agents=[])
412-
graph = Graph(
413-
edges=[
414-
Edge(from_node=START, to_node=agent),
415-
],
416-
)
417-
with pytest.raises(
418-
ValueError,
419-
match=(
420-
rf"Graph validation failed\. Node '{name}' uses deprecated"
421-
rf" orchestrator '{expected_class_name}'\."
422-
),
423-
):
424-
validate_graph(graph.nodes, graph.edges)
425-
426-
427-
def test_old_orchestrator_wrapped_in_parallel_worker_fails_validation() -> None:
428-
"""Tests that a graph with old orchestrators in ParallelWorker fails validation."""
429-
from google.adk.agents.sequential_agent import SequentialAgent
430-
from google.adk.workflow._parallel_worker import _ParallelWorker
431-
432-
seq_agent = SequentialAgent(name='SeqAgent', sub_agents=[])
433-
worker_node = _ParallelWorker(node=seq_agent)
434-
graph = Graph(
435-
edges=[
436-
Edge(from_node=START, to_node=worker_node),
437-
],
438-
)
439-
with pytest.raises(
440-
ValueError,
441-
match=(
442-
r"Graph validation failed\. Node 'SeqAgent' uses deprecated"
443-
r" orchestrator 'SequentialAgent'\."
444-
),
445-
):
446-
validate_graph(graph.nodes, graph.edges)
447-
448-
449-
def test_self_referential_wrapped_node_safely_terminates() -> None:
450-
"""Tests that a self-referential wrapped node safely breaks without infinite loop."""
451-
node_a = TestingNode(name='NodeA')
452-
# Mock a self-referential node property
453-
object.__setattr__(node_a, 'node', node_a)
454-
455-
graph = Graph(
456-
edges=[
457-
Edge(from_node=START, to_node=node_a),
458-
],
459-
)
460-
validate_graph(
461-
graph.nodes, graph.edges
462-
) # Should not spin forever and should pass

0 commit comments

Comments
 (0)