Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 960b7ab3b1 | |||
| 28df1bd2dc | |||
| bb5962b771 | |||
| 6a0024943c |
@@ -761,4 +761,15 @@ class GraphSpec(BaseModel):
|
||||
"GCU nodes must be declared as subagents of a parent node."
|
||||
)
|
||||
|
||||
# All sub_agents references must point to registered nodes
|
||||
all_node_ids = {n.id for n in self.nodes}
|
||||
for node in self.nodes:
|
||||
for sa_id in node.sub_agents or []:
|
||||
if sa_id not in all_node_ids:
|
||||
errors.append(
|
||||
f"Node '{node.id}' references sub_agent '{sa_id}' "
|
||||
"which is not registered in the nodes list. "
|
||||
"Ensure the sub_agent node is included in the agent's nodes."
|
||||
)
|
||||
|
||||
return {"errors": errors, "warnings": warnings}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
Tests for sub_agents reference validation.
|
||||
|
||||
Validates that all node IDs listed in a node's sub_agents field
|
||||
are actually registered in the graph's nodes list.
|
||||
"""
|
||||
|
||||
from framework.graph.edge import GraphSpec
|
||||
from framework.graph.node import NodeSpec
|
||||
|
||||
|
||||
class TestSubAgentRegistrationValidation:
|
||||
"""sub_agents references to unregistered nodes must be rejected."""
|
||||
|
||||
def test_sub_agent_not_registered_fails(self):
|
||||
"""A node referencing a sub_agent that isn't in the nodes list -> error."""
|
||||
graph = GraphSpec(
|
||||
id="g1",
|
||||
goal_id="goal1",
|
||||
entry_node="main",
|
||||
terminal_nodes=["main"],
|
||||
nodes=[
|
||||
NodeSpec(
|
||||
id="main",
|
||||
name="Main",
|
||||
description="Main node",
|
||||
sub_agents=["missing_gcu"],
|
||||
),
|
||||
],
|
||||
edges=[],
|
||||
)
|
||||
|
||||
errors = graph.validate()["errors"]
|
||||
sa_errors = [e for e in errors if "not registered" in e]
|
||||
assert len(sa_errors) == 1
|
||||
assert "'missing_gcu'" in sa_errors[0]
|
||||
assert "'main'" in sa_errors[0]
|
||||
|
||||
def test_sub_agent_registered_passes(self):
|
||||
"""A node referencing a sub_agent that exists in nodes -> no error."""
|
||||
graph = GraphSpec(
|
||||
id="g1",
|
||||
goal_id="goal1",
|
||||
entry_node="main",
|
||||
terminal_nodes=["main"],
|
||||
nodes=[
|
||||
NodeSpec(
|
||||
id="main",
|
||||
name="Main",
|
||||
description="Main node",
|
||||
sub_agents=["helper"],
|
||||
),
|
||||
NodeSpec(
|
||||
id="helper",
|
||||
name="Helper",
|
||||
description="Helper GCU node",
|
||||
node_type="gcu",
|
||||
),
|
||||
],
|
||||
edges=[],
|
||||
)
|
||||
|
||||
errors = graph.validate()["errors"]
|
||||
sa_errors = [e for e in errors if "not registered" in e]
|
||||
assert len(sa_errors) == 0
|
||||
|
||||
def test_multiple_unregistered_sub_agents(self):
|
||||
"""Multiple missing sub_agents -> one error per missing reference."""
|
||||
graph = GraphSpec(
|
||||
id="g1",
|
||||
goal_id="goal1",
|
||||
entry_node="main",
|
||||
terminal_nodes=["main"],
|
||||
nodes=[
|
||||
NodeSpec(
|
||||
id="main",
|
||||
name="Main",
|
||||
description="Main node",
|
||||
sub_agents=["missing_a", "missing_b"],
|
||||
),
|
||||
],
|
||||
edges=[],
|
||||
)
|
||||
|
||||
errors = graph.validate()["errors"]
|
||||
sa_errors = [e for e in errors if "not registered" in e]
|
||||
assert len(sa_errors) == 2
|
||||
|
||||
def test_no_sub_agents_passes(self):
|
||||
"""A node with no sub_agents -> no error from this rule."""
|
||||
graph = GraphSpec(
|
||||
id="g1",
|
||||
goal_id="goal1",
|
||||
entry_node="main",
|
||||
terminal_nodes=["main"],
|
||||
nodes=[
|
||||
NodeSpec(id="main", name="Main", description="Main node"),
|
||||
],
|
||||
edges=[],
|
||||
)
|
||||
|
||||
errors = graph.validate()["errors"]
|
||||
sa_errors = [e for e in errors if "not registered" in e]
|
||||
assert len(sa_errors) == 0
|
||||
@@ -114,7 +114,7 @@ def test_list_agent_tools_groups_by_provider_and_keeps_uncredentialed(monkeypatc
|
||||
google_tools = {t["name"] for t in providers["google"]["tools"]}
|
||||
assert "gmail_list_messages" in google_tools
|
||||
assert "calendar_list_events" in google_tools
|
||||
assert "send_email" in google_tools
|
||||
assert "send_email" not in google_tools # multi-provider tool, only in resend
|
||||
assert providers["google"]["authorization"]
|
||||
|
||||
resend_tools = {t["name"] for t in providers["resend"]["tools"]}
|
||||
|
||||
Reference in New Issue
Block a user