feat: auto continuation is unavailable in llm nodes; continuation driven loops should be in agent nodes only
This commit is contained in:
4 files changed
+167
-20
No files matched your search
@@ -3,6 +3,7 @@ use super::types::{ConcurrencyCap, Graph, NextTargets, Node, NodeType};
|
||||
use crate::client::{Model, ModelType};
|
||||
use crate::config;
|
||||
use crate::config::{Agent, AppConfig, paths};
|
||||
use crate::function::todo::TODO_FUNCTION_PREFIX;
|
||||
use crate::function::user_interaction::USER_FUNCTION_PREFIX;
|
||||
use crate::rag::{GraphRagConfig, RagData};
|
||||
use anyhow::{Result, bail};
|
||||
@@ -181,6 +182,7 @@ impl GraphValidator {
|
||||
self.validate_fallback_capture(graph, &mut result);
|
||||
self.validate_output_schema_properties(graph, &mut result);
|
||||
self.validate_structured_output_user_tools(graph, &mut result);
|
||||
self.validate_llm_node_todo_tools(graph, &mut result);
|
||||
self.validate_max_concurrency(graph, &mut result);
|
||||
self.validate_max_concurrency_template(graph, &mut result);
|
||||
self.validate_orchestration_limits(graph, &mut result);
|
||||
@@ -483,6 +485,28 @@ impl GraphValidator {
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_llm_node_todo_tools(&self, graph: &Graph, result: &mut ValidationResult) {
|
||||
for (node_id, node) in &graph.nodes {
|
||||
let NodeType::Llm(llm) = &node.node_type else {
|
||||
continue;
|
||||
};
|
||||
let exposes_todo_tools = llm.tools.as_ref().is_some_and(|tools| {
|
||||
tools
|
||||
.iter()
|
||||
.any(|t| t.trim().starts_with(TODO_FUNCTION_PREFIX))
|
||||
});
|
||||
if exposes_todo_tools {
|
||||
result.error(ValidationError::with_node(
|
||||
node_id,
|
||||
"llm node exposes todo tools (`todo__*`): todo tools drive chat turn \
|
||||
loops (auto_continue), which llm nodes don't have. Node continuation \
|
||||
is owned by the node loop, max_iterations, and graph edges. For driven \
|
||||
multi-step work, use an agent node whose agent sets `auto_continue: true`",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_node_references(&self, graph: &Graph, result: &mut ValidationResult) {
|
||||
for (node_id, node) in &graph.nodes {
|
||||
for (target, label) in declared_targets(node) {
|
||||
@@ -4793,6 +4817,68 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn structured_output_node_with_todo_tools_errors() {
|
||||
let mut node = llm_with_output_schema("l", &["verdict"], Some("end"));
|
||||
if let NodeType::Llm(ref mut n) = node.node_type {
|
||||
n.tools = Some(vec!["todo__init".into()]);
|
||||
}
|
||||
let graph = graph_with(vec![("l", node), ("end", end_node("end"))], "l");
|
||||
|
||||
let result = validator().validate(&graph);
|
||||
|
||||
assert!(
|
||||
result
|
||||
.errors
|
||||
.iter()
|
||||
.any(|e| e.node_id.as_deref() == Some("l")
|
||||
&& e.message.contains("todo tools drive chat turn loops")),
|
||||
"expected todo-tool error: {:?}",
|
||||
result.errors
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_structured_node_with_todo_tools_errors() {
|
||||
let mut node = llm_node("l", None, Some("end"));
|
||||
if let NodeType::Llm(ref mut n) = node.node_type {
|
||||
n.tools = Some(vec!["todo__pause".into()]);
|
||||
}
|
||||
let graph = graph_with(vec![("l", node), ("end", end_node("end"))], "l");
|
||||
|
||||
let result = validator().validate(&graph);
|
||||
|
||||
assert!(
|
||||
result
|
||||
.errors
|
||||
.iter()
|
||||
.any(|e| e.node_id.as_deref() == Some("l")
|
||||
&& e.message.contains("todo tools drive chat turn loops")),
|
||||
"expected todo-tool error: {:?}",
|
||||
result.errors
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn llm_node_without_todo_tools_does_not_error() {
|
||||
let mut node = llm_node("l", None, Some("end"));
|
||||
if let NodeType::Llm(ref mut n) = node.node_type {
|
||||
n.tools = Some(vec!["user__select".into()]);
|
||||
}
|
||||
let graph = graph_with(vec![("l", node), ("end", end_node("end"))], "l");
|
||||
|
||||
let result = validator().validate(&graph);
|
||||
|
||||
assert!(
|
||||
!result
|
||||
.errors
|
||||
.iter()
|
||||
.any(|e| e.message.contains("todo tools drive chat turn loops")),
|
||||
"unexpected todo-tool error: {:?}",
|
||||
result.errors
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fallback_with_output_schema_and_empty_state_updates_warns() {
|
||||
let mut node = llm_with_output_schema("l", &["summary"], Some("end"));
|
||||
|
||||
Reference in new issue
Block a user