use ri_agent_graph::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
println!("=== Human-in-the-Loop Interrupt Example ===\n");
let graph = AgentGraph::builder()
.add_node(
"draft",
node!(|state| async move {
println!("[draft] Generating initial draft...");
state
.set(
"document",
"Rust is a systems programming language focused on safety.",
)
.await?;
state.set("status", "drafted").await?;
println!("[draft] Draft complete.");
Ok(())
}),
)
.add_node(
"review",
node!(|state| async move {
println!("[review] Reviewing document...");
let doc: String = state.get("document").await?;
let reviewed = format!("{} [Reviewed: looks good]", doc);
state.set("document", reviewed).await?;
state.set("status", "reviewed").await?;
println!("[review] Review complete.");
Ok(())
}),
)
.add_node(
"finalize",
node!(|state| async move {
println!("[finalize] Finalizing document...");
let doc: String = state.get("document").await?;
let finalized = format!("{} [Final version]", doc);
state.set("document", finalized).await?;
state.set("status", "finalized").await?;
println!("[finalize] Document finalized.");
Ok(())
}),
)
.add_edge("draft", "review")
.add_edge("review", "finalize")
.with_interrupt_before(vec!["review".to_string()])
.build()?;
println!("--- Run 1: Execute from 'draft' (expecting interrupt) ---\n");
let state = AgentState::new();
let result = graph
.execute_with_interrupt("draft", state, GraphConfig::new())
.await;
let resumed_state = match result {
ExecutionResult::Interrupted {
state,
node,
checkpoint_data,
..
} => {
println!("\nExecution interrupted before node '{}'.", node);
let status: String = state.get("status").await?;
let doc: String = state.get("document").await?;
println!(" Current status : {}", status);
println!(" Document so far: {}", doc);
if let Some(ref cp) = checkpoint_data {
println!(" Resume node : {}", cp.resume_node);
}
println!("\n (A human would inspect the draft here and decide to proceed.)\n");
state.set("human_note", "Approved by reviewer.").await?;
state
}
ExecutionResult::Complete(_) => {
println!("ERROR: Expected an interrupt but the graph completed.");
return Ok(());
}
ExecutionResult::Failed { error, .. } => {
eprintln!("ERROR: Graph execution failed: {error}");
return Err(error);
}
};
println!("--- Run 2: Resume from 'review' (completing the workflow) ---\n");
let resume_graph = AgentGraph::builder()
.add_node(
"review",
node!(|state| async move {
println!("[review] Reviewing document...");
let doc: String = state.get("document").await?;
let note: String = state
.get_opt("human_note")
.await?
.unwrap_or_else(|| "(none)".to_string());
println!("[review] Human note: {}", note);
let reviewed = format!("{} [Reviewed: looks good]", doc);
state.set("document", reviewed).await?;
state.set("status", "reviewed").await?;
println!("[review] Review complete.");
Ok(())
}),
)
.add_node(
"finalize",
node!(|state| async move {
println!("[finalize] Finalizing document...");
let doc: String = state.get("document").await?;
let finalized = format!("{} [Final version]", doc);
state.set("document", finalized).await?;
state.set("status", "finalized").await?;
println!("[finalize] Document finalized.");
Ok(())
}),
)
.add_edge("review", "finalize")
.build()?;
let final_state = resume_graph.execute("review", resumed_state).await?;
println!("\n--- Final Results ---\n");
let document: String = final_state.get("document").await?;
let status: String = final_state.get("status").await?;
let human_note: String = final_state.get("human_note").await?;
println!(" Status : {}", status);
println!(" Human note: {}", human_note);
println!(" Document : {}", document);
println!("\nWorkflow completed successfully.");
Ok(())
}