feat(graph): support max_concurrent_jobs at the graph level
Graph agents could only inherit the app-wide job budget; the agent-level header in graph.yaml now accepts max_concurrent_jobs alongside model/temperature, flowing through AgentConfig::from_graph into the run-wide supervisor. Deliberately graph-wide, not per-node: jobs outlive the node that started them.
This commit is contained in:
@@ -36,6 +36,12 @@ top_p: null # Default sampling top-p for `llm` nodes
|
|||||||
reasoning_effort: null # Default reasoning effort for `llm` nodes that don't override it.
|
reasoning_effort: null # Default reasoning effort for `llm` nodes that don't override it.
|
||||||
# Only valid when the model declares reasoning_levels.
|
# Only valid when the model declares reasoning_levels.
|
||||||
|
|
||||||
|
max_concurrent_jobs: 5 # Max background jobs (`job__*` tools) running at once across the
|
||||||
|
# whole graph run. Jobs live in the run-wide supervisor and outlive
|
||||||
|
# the `llm` node that started them, so the budget is graph-wide —
|
||||||
|
# there is no per-node override. Overrides the global setting;
|
||||||
|
# 0 disables background jobs for this graph agent.
|
||||||
|
|
||||||
global_tools: # Tool universe an `llm` node's `tools:` whitelist draws from
|
global_tools: # Tool universe an `llm` node's `tools:` whitelist draws from
|
||||||
- web_search_coyote.sh
|
- web_search_coyote.sh
|
||||||
- fetch_url_via_curl.sh
|
- fetch_url_via_curl.sh
|
||||||
|
|||||||
@@ -854,6 +854,7 @@ impl AgentConfig {
|
|||||||
variables: graph.variables.clone(),
|
variables: graph.variables.clone(),
|
||||||
can_spawn_agents: graph.has_agent_node(),
|
can_spawn_agents: graph.has_agent_node(),
|
||||||
max_concurrent_agents: default_max_concurrent_agents(),
|
max_concurrent_agents: default_max_concurrent_agents(),
|
||||||
|
max_concurrent_jobs: graph.max_concurrent_jobs,
|
||||||
max_agent_depth: default_max_agent_depth(),
|
max_agent_depth: default_max_agent_depth(),
|
||||||
escalation_timeout: default_escalation_timeout(),
|
escalation_timeout: default_escalation_timeout(),
|
||||||
..AgentConfig::default()
|
..AgentConfig::default()
|
||||||
@@ -1328,6 +1329,7 @@ variables:
|
|||||||
model: claude:claude-sonnet-4-6
|
model: claude:claude-sonnet-4-6
|
||||||
temperature: 0.3
|
temperature: 0.3
|
||||||
top_p: 0.8
|
top_p: 0.8
|
||||||
|
max_concurrent_jobs: 2
|
||||||
global_tools:
|
global_tools:
|
||||||
- fetch_pdf.sh
|
- fetch_pdf.sh
|
||||||
mcp_servers:
|
mcp_servers:
|
||||||
@@ -1350,6 +1352,7 @@ variables:
|
|||||||
assert_eq!(config.model_id.as_deref(), Some("claude:claude-sonnet-4-6"));
|
assert_eq!(config.model_id.as_deref(), Some("claude:claude-sonnet-4-6"));
|
||||||
assert_eq!(config.temperature, Some(0.3));
|
assert_eq!(config.temperature, Some(0.3));
|
||||||
assert_eq!(config.top_p, Some(0.8));
|
assert_eq!(config.top_p, Some(0.8));
|
||||||
|
assert_eq!(config.max_concurrent_jobs, Some(2));
|
||||||
assert_eq!(config.global_tools, vec!["fetch_pdf.sh"]);
|
assert_eq!(config.global_tools, vec!["fetch_pdf.sh"]);
|
||||||
assert_eq!(config.mcp_servers, vec!["pubmed-search"]);
|
assert_eq!(config.mcp_servers, vec!["pubmed-search"]);
|
||||||
assert_eq!(config.conversation_starters, vec!["Start here"]);
|
assert_eq!(config.conversation_starters, vec!["Start here"]);
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ pub struct Graph {
|
|||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub reasoning_effort: Option<String>,
|
pub reasoning_effort: Option<String>,
|
||||||
|
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub max_concurrent_jobs: Option<usize>,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub global_tools: Vec<String>,
|
pub global_tools: Vec<String>,
|
||||||
|
|
||||||
@@ -895,6 +898,7 @@ nodes:
|
|||||||
assert!(graph.model.is_none());
|
assert!(graph.model.is_none());
|
||||||
assert!(graph.temperature.is_none());
|
assert!(graph.temperature.is_none());
|
||||||
assert!(graph.top_p.is_none());
|
assert!(graph.top_p.is_none());
|
||||||
|
assert!(graph.max_concurrent_jobs.is_none());
|
||||||
assert!(graph.global_tools.is_empty());
|
assert!(graph.global_tools.is_empty());
|
||||||
assert!(graph.mcp_servers.is_empty());
|
assert!(graph.mcp_servers.is_empty());
|
||||||
assert!(graph.conversation_starters.is_empty());
|
assert!(graph.conversation_starters.is_empty());
|
||||||
|
|||||||
@@ -998,6 +998,7 @@ mod tests {
|
|||||||
temperature: None,
|
temperature: None,
|
||||||
top_p: None,
|
top_p: None,
|
||||||
reasoning_effort: None,
|
reasoning_effort: None,
|
||||||
|
max_concurrent_jobs: None,
|
||||||
global_tools: Vec::new(),
|
global_tools: Vec::new(),
|
||||||
mcp_servers: Vec::new(),
|
mcp_servers: Vec::new(),
|
||||||
skills_enabled: None,
|
skills_enabled: None,
|
||||||
|
|||||||
Reference in New Issue
Block a user