fix(graph): name the offending node type when a map branch reaches a disallowed node
PR #24 follow-up 2. With validate_before_run off, a user-authored approval/input/end/map node inside a branch reached a message that called itself an internal error. Name the node type and point at the validator setting instead.
This commit is contained in:
1 file changed
+65
-7
+65
-7
@@ -221,13 +221,17 @@ async fn run_chain_step(
|
|||||||
let node = step_ctx.graph.get_node(current).ok_or_else(|| {
|
let node = step_ctx.graph.get_node(current).ok_or_else(|| {
|
||||||
anyhow!("map node '{map_id}': sub-branch [{idx}] routed to unknown node '{current}'")
|
anyhow!("map node '{map_id}': sub-branch [{idx}] routed to unknown node '{current}'")
|
||||||
})?;
|
})?;
|
||||||
if !matches!(
|
let disallowed = match &node.node_type {
|
||||||
node.node_type,
|
NodeType::Approval(_) => Some("an approval node"),
|
||||||
NodeType::Llm(_) | NodeType::Agent(_) | NodeType::Rag(_) | NodeType::Script(_)
|
NodeType::Input(_) => Some("an input node"),
|
||||||
) {
|
NodeType::End(_) => Some("an end node"),
|
||||||
|
NodeType::Map(_) => Some("a map node"),
|
||||||
|
NodeType::Agent(_) | NodeType::Llm(_) | NodeType::Rag(_) | NodeType::Script(_) => None,
|
||||||
|
};
|
||||||
|
if let Some(type_phrase) = disallowed {
|
||||||
bail!(
|
bail!(
|
||||||
"map branch '{current}' has type that cannot run inside a map \
|
"'{current}' is {type_phrase}; approval/input/end/map nodes cannot run inside a \
|
||||||
(validator should have caught this; internal error)"
|
map branch (enable settings.validate_before_run to catch this at load time)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1115,12 +1119,66 @@ nodes:
|
|||||||
let chain = error_chain(run_graph(yaml, &ws).await);
|
let chain = error_chain(run_graph(yaml, &ws).await);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
chain.contains("map branch 'ask' has type that cannot run inside a map"),
|
chain.contains(
|
||||||
|
"'ask' is an approval node; approval/input/end/map nodes cannot run inside a \
|
||||||
|
map branch (enable settings.validate_before_run to catch this at load time)"
|
||||||
|
),
|
||||||
"{chain}"
|
"{chain}"
|
||||||
);
|
);
|
||||||
assert!(chain.contains("failed at node 'ask' (step 2)"), "{chain}");
|
assert!(chain.contains("failed at node 'ask' (step 2)"), "{chain}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn map_chain_pre_check_rejects_end_node() {
|
||||||
|
if !cmd_available("python3") {
|
||||||
|
eprintln!("skipping: python3 not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let ws = TestWorkspace::new();
|
||||||
|
ws.write_py("gate.py", r#"print(json.dumps({}))"#);
|
||||||
|
|
||||||
|
let yaml = r#"
|
||||||
|
name: chain
|
||||||
|
start: fan_out
|
||||||
|
settings:
|
||||||
|
validate_before_run: false
|
||||||
|
initial_state:
|
||||||
|
items: [1]
|
||||||
|
nodes:
|
||||||
|
fan_out:
|
||||||
|
type: map
|
||||||
|
over: "{{items}}"
|
||||||
|
as: item
|
||||||
|
branch: gate
|
||||||
|
collect_into: results
|
||||||
|
next: done
|
||||||
|
gate:
|
||||||
|
type: script
|
||||||
|
script: gate.py
|
||||||
|
next: finish
|
||||||
|
finish:
|
||||||
|
type: end
|
||||||
|
output: "done early"
|
||||||
|
done:
|
||||||
|
type: end
|
||||||
|
output: "{{results}}"
|
||||||
|
"#;
|
||||||
|
let chain = error_chain(run_graph(yaml, &ws).await);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
chain.contains(
|
||||||
|
"'finish' is an end node; approval/input/end/map nodes cannot run inside a \
|
||||||
|
map branch (enable settings.validate_before_run to catch this at load time)"
|
||||||
|
),
|
||||||
|
"{chain}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
chain.contains("map node 'fan_out': sub-branch [0] failed at node 'finish' (step 2)"),
|
||||||
|
"{chain}"
|
||||||
|
);
|
||||||
|
assert_eq!(chain.matches("sub-branch [").count(), 1, "{chain}");
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn map_chain_script_error_routes_to_fallback() {
|
async fn map_chain_script_error_routes_to_fallback() {
|
||||||
if !cmd_available("python3") || !cmd_available("bash") {
|
if !cmd_available("python3") || !cmd_available("bash") {
|
||||||
|
|||||||
Reference in new issue
Block a user