feat(graph): seed a child graph agent's state from an agent node's inputs map
Agent nodes gain an optional `inputs:` map of templates resolved against
the parent state and overlaid on the child graph agent's initial_state
before its executor starts. A lone {{key}} keeps the state value's JSON
type; mixed text renders to a string; initial_prompt is still owned by the
dispatcher. run_child_agent and run_active_agent_graph keep their
signatures as wrappers over the new *_with_inputs variants, and
run_agent_for_graph rejects inputs aimed at a config-only agent or
containing initial_prompt.
Design: plans/map-subgraph-branches-design.md §3.12 (C18), task T5.
This commit is contained in:
8 files changed
+283
-8
No files matched your search
@@ -240,6 +240,9 @@ pub struct AgentNode {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub timeout: Option<u64>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub inputs: Option<HashMap<String, String>>,
|
||||
|
||||
#[serde(default)]
|
||||
pub teammates: bool,
|
||||
}
|
||||
@@ -634,6 +637,48 @@ nodes:
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_node_inputs_parse_optionally_and_empty_map_is_some() {
|
||||
let yaml = r#"
|
||||
name: g
|
||||
start: with_inputs
|
||||
nodes:
|
||||
with_inputs:
|
||||
id: with_inputs
|
||||
type: agent
|
||||
agent: helper
|
||||
prompt: hi
|
||||
inputs:
|
||||
width: "{{n}}"
|
||||
note: "Repo: {{repo}}"
|
||||
next: without_inputs
|
||||
without_inputs:
|
||||
id: without_inputs
|
||||
type: agent
|
||||
agent: helper
|
||||
prompt: hi
|
||||
next: empty_inputs
|
||||
empty_inputs:
|
||||
id: empty_inputs
|
||||
type: agent
|
||||
agent: helper
|
||||
prompt: hi
|
||||
inputs: {}
|
||||
"#;
|
||||
let graph: Graph = serde_yaml::from_str(yaml).unwrap();
|
||||
let inputs_of = |id: &str| match &graph.get_node(id).unwrap().node_type {
|
||||
NodeType::Agent(n) => n.inputs.clone(),
|
||||
_ => panic!("expected Agent variant"),
|
||||
};
|
||||
|
||||
let with = inputs_of("with_inputs").expect("inputs should parse");
|
||||
assert_eq!(with.len(), 2);
|
||||
assert_eq!(with.get("width").map(String::as_str), Some("{{n}}"));
|
||||
assert_eq!(with.get("note").map(String::as_str), Some("Repo: {{repo}}"));
|
||||
assert_eq!(inputs_of("without_inputs"), None);
|
||||
assert_eq!(inputs_of("empty_inputs"), Some(HashMap::new()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserializes_mcp_tools_at_graph_and_node_level() {
|
||||
let yaml = r#"
|
||||
|
||||
Reference in new issue
Block a user