feat: support explicit can_spawn_agents, max_concurrent_agents, and max_agent_depth keys in graph.yaml

Graph agents previously derived can_spawn_agents from the presence of an agent node, so a pure-LLM-node graph could never spawn dynamic sub-agents. The three optional graph-level keys now override the derivation/defaults in AgentConfig::from_graph, enabling orchestrator graphs (can_spawn_agents: true with only llm nodes). Explicit false on a graph with agent nodes still runs those nodes; only dynamic spawning from llm nodes is disabled.
This commit is contained in:
Dark-Alex-17 committed 2026-09-09 16:44:23 -06:00
1 parent 7f5b75dbdf
commit 7e84804ed1
3 files changed
+115 -5

No files matched your search

+71 -4
View File
@@ -835,7 +835,7 @@ fn default_max_concurrent_agents() -> usize {
4
}
fn default_max_agent_depth() -> usize {
pub(crate) fn default_max_agent_depth() -> usize {
3
}
@@ -878,10 +878,16 @@ impl AgentConfig {
skill_instructions: graph.skill_instructions.clone(),
conversation_starters: graph.conversation_starters.clone(),
variables: graph.variables.clone(),
can_spawn_agents: graph.has_agent_node(),
max_concurrent_agents: default_max_concurrent_agents(),
can_spawn_agents: graph
.can_spawn_agents
.unwrap_or_else(|| graph.has_agent_node()),
max_concurrent_agents: graph
.max_concurrent_agents
.unwrap_or_else(default_max_concurrent_agents),
max_concurrent_jobs: graph.max_concurrent_jobs,
max_agent_depth: default_max_agent_depth(),
max_agent_depth: graph
.max_agent_depth
.unwrap_or_else(default_max_agent_depth),
escalation_timeout: default_escalation_timeout(),
..AgentConfig::default()
}
@@ -1460,6 +1466,67 @@ variables:
assert_eq!(config.escalation_timeout, default_escalation_timeout());
}
#[test]
fn from_graph_explicit_can_spawn_agents_enables_spawning_without_agent_nodes() {
let yaml = formatdoc! {r#"
name: g
can_spawn_agents: true
start: x
nodes:
x:
id: x
type: end
output: ok
"#};
let graph: Graph = serde_yaml::from_str(&yaml).unwrap();
assert!(AgentConfig::from_graph("d", &graph).can_spawn_agents);
}
#[test]
fn from_graph_explicit_can_spawn_agents_false_wins_over_agent_nodes() {
let yaml = formatdoc! {r#"
name: g
can_spawn_agents: false
start: a
nodes:
a:
id: a
type: agent
agent: helper
prompt: hi
next: e
e:
id: e
type: end
output: done
"#};
let graph: Graph = serde_yaml::from_str(&yaml).unwrap();
assert!(!AgentConfig::from_graph("d", &graph).can_spawn_agents);
}
#[test]
fn from_graph_propagates_explicit_agent_limits() {
let yaml = formatdoc! {r#"
name: g
max_concurrent_agents: 7
max_agent_depth: 3
start: x
nodes:
x:
id: x
type: end
output: ok
"#};
let graph: Graph = serde_yaml::from_str(&yaml).unwrap();
let config = AgentConfig::from_graph("d", &graph);
assert_eq!(config.max_concurrent_agents, 7);
assert_eq!(config.max_agent_depth, 3);
}
#[test]
fn agent_metadata_stub_extracts_description_from_graph_yaml() {
let yaml = r#"
+32
View File
@@ -31,6 +31,15 @@ pub struct Graph {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_concurrent_jobs: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub can_spawn_agents: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_concurrent_agents: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_agent_depth: Option<usize>,
#[serde(default)]
pub global_tools: Vec<String>,
@@ -946,11 +955,34 @@ nodes:
assert!(graph.temperature.is_none());
assert!(graph.top_p.is_none());
assert!(graph.max_concurrent_jobs.is_none());
assert!(graph.can_spawn_agents.is_none());
assert!(graph.max_concurrent_agents.is_none());
assert!(graph.max_agent_depth.is_none());
assert!(graph.global_tools.is_empty());
assert!(graph.mcp_servers.is_empty());
assert!(graph.conversation_starters.is_empty());
}
#[test]
fn graph_parses_agent_orchestration_keys() {
let yaml = r#"
name: g
can_spawn_agents: true
max_concurrent_agents: 6
max_agent_depth: 2
start: x
nodes:
x:
id: x
type: end
output: ok
"#;
let graph: Graph = serde_yaml::from_str(yaml).unwrap();
assert_eq!(graph.can_spawn_agents, Some(true));
assert_eq!(graph.max_concurrent_agents, Some(6));
assert_eq!(graph.max_agent_depth, Some(2));
}
#[test]
fn node_ids_lists_nodes_in_order() {
let yaml = r#"