feat: wired together graph execution and agent graph dispatch

This commit is contained in:
2026-05-14 11:10:45 -06:00
parent cc8b48c355
commit e36af11e98
11 changed files with 270 additions and 12 deletions
+31 -8
View File
@@ -14,8 +14,8 @@ use super::types::{EndNode, Graph, Node, NodeType};
use super::user_interaction::{ApprovalNodeExecutor, InputNodeExecutor};
use super::validator::GraphValidator;
use crate::config::RequestContext;
use crate::utils::AbortSignal;
use anyhow::{Context, Result, bail, anyhow};
use crate::utils::{AbortSignal, dimmed_text};
use anyhow::{Context, Result, anyhow, bail};
use serde_json::Value;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
@@ -74,6 +74,10 @@ impl GraphExecutor {
let mut current = graph.start.clone();
info!("[graph:{}] start at '{}'", graph.name, current);
eprintln!(
"{}",
dimmed_text(&format!("▸ graph: {} (start: {})", graph.name, current))
);
let output = loop {
if abort_signal.aborted() {
@@ -102,14 +106,18 @@ impl GraphExecutor {
);
}
let node = graph.get_node(&current).ok_or_else(|| {
anyhow!("Node '{}' not found in graph '{}'", current, graph.name)
})?;
let node = graph
.get_node(&current)
.ok_or_else(|| anyhow!("Node '{}' not found in graph '{}'", current, graph.name))?;
debug!(
"[graph:{}] entering '{}' (visit {})",
graph.name, current, visits
);
eprintln!(
"{}",
dimmed_text(&format!("{} ({})", current, node_type_label(node)))
);
let next = step(
node,
@@ -134,6 +142,13 @@ impl GraphExecutor {
current,
start.elapsed()
);
eprintln!(
"{}",
dimmed_text(&format!(
"▸ graph done in {:.2}s",
start.elapsed().as_secs_f64()
))
);
break out;
}
}
@@ -148,6 +163,16 @@ enum StepResult {
End(String),
}
fn node_type_label(node: &Node) -> &'static str {
match &node.node_type {
NodeType::Agent(_) => "agent",
NodeType::Script(_) => "script",
NodeType::Approval(_) => "approval",
NodeType::Input(_) => "input",
NodeType::End(_) => "end",
}
}
async fn step(
node: &Node,
state: &mut StateManager,
@@ -179,9 +204,7 @@ async fn step(
}
};
let next = dynamic.or_else(|| node.next.clone()).ok_or_else(|| {
anyhow!(
"script node '{current}' did not emit `_next` and has no static `next`"
)
anyhow!("script node '{current}' did not emit `_next` and has no static `next`")
})?;
Ok(StepResult::Continue(next))
}