use ri_agent_graph::prelude::*;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<()> {
println!("=== Retry Policy Example ===\n");
println!("--- Part 1: Flaky API with automatic retries ---\n");
let graph = AgentGraph::builder()
.add_node_with_retry(
"flaky_api",
node!(|state| async move {
let attempt: u32 = state.get_opt("flaky_attempt").await?.unwrap_or(0);
let attempt = attempt + 1;
state.set("flaky_attempt", attempt).await?;
println!(" [flaky_api] Attempt {} ...", attempt);
if attempt < 3 {
println!(" [flaky_api] Transient failure on attempt {}!", attempt);
return Err(AgentGraphError::ExecutionError(format!(
"API timeout on attempt {}",
attempt
)));
}
println!(" [flaky_api] Success on attempt {}!", attempt);
state.set("flaky_result", "data from API").await?;
Ok(())
}),
RetryPolicy::new()
.with_max_attempts(5)
.with_initial_interval(Duration::from_millis(100))
.with_jitter(false),
)
.add_node(
"process",
node!(|state| async move {
let data: String = state.get("flaky_result").await?;
println!(" [process] Processing result: '{}'", data);
state
.set("final_output", format!("processed: {}", data))
.await?;
Ok(())
}),
)
.add_edge("flaky_api", "process")
.build()?;
let state = AgentState::new();
let result = graph.execute("flaky_api", state).await?;
let attempts: u32 = result.get("flaky_attempt").await?;
let output: String = result.get("final_output").await?;
println!("\n Total attempts: {}", attempts);
println!(" Final output : {}", output);
println!("\n--- Part 2: Selective retry with predicate ---\n");
let picky_graph = AgentGraph::builder()
.add_node_with_retry(
"picky_api",
node!(|state| async move {
let attempt: u32 = state.get_opt("picky_attempt").await?.unwrap_or(0);
let attempt = attempt + 1;
state.set("picky_attempt", attempt).await?;
println!(" [picky_api] Attempt {} ...", attempt);
match attempt {
1 => {
println!(" [picky_api] ExecutionError (retryable) on attempt 1");
Err(AgentGraphError::ExecutionError(
"connection reset".to_string(),
))
}
2 => {
println!(" [picky_api] ExecutionError (retryable) on attempt 2");
Err(AgentGraphError::ExecutionError("read timeout".to_string()))
}
_ => {
println!(" [picky_api] Success on attempt {}!", attempt);
state.set("picky_result", "picky data").await?;
Ok(())
}
}
}),
RetryPolicy::new()
.with_max_attempts(5)
.with_initial_interval(Duration::from_millis(100))
.with_jitter(false)
.with_retry_on(|err| {
matches!(err, AgentGraphError::ExecutionError(_))
}),
)
.build()?;
let state2 = AgentState::new();
let result2 = picky_graph.execute("picky_api", state2).await?;
let picky_attempts: u32 = result2.get("picky_attempt").await?;
let picky_result: String = result2.get("picky_result").await?;
println!("\n Total attempts: {}", picky_attempts);
println!(" Result : {}", picky_result);
println!("\n--- Part 3: Non-retryable error fails immediately ---\n");
let non_retryable_graph = AgentGraph::builder()
.add_node_with_retry(
"strict_api",
node!(|state| async move {
let attempt: u32 = state.get_opt("strict_attempt").await?.unwrap_or(0);
let attempt = attempt + 1;
state.set("strict_attempt", attempt).await?;
println!(" [strict_api] Attempt {} ...", attempt);
println!(
" [strict_api] StateError (non-retryable) on attempt {}",
attempt
);
Err::<(), _>(AgentGraphError::StateError(
"invalid input format".to_string(),
))
}),
RetryPolicy::new()
.with_max_attempts(5)
.with_initial_interval(Duration::from_millis(100))
.with_jitter(false)
.with_retry_on(|err| matches!(err, AgentGraphError::ExecutionError(_))),
)
.build()?;
let state3 = AgentState::new();
let result3 = non_retryable_graph.execute("strict_api", state3).await;
match result3 {
Err(AgentGraphError::StateError(msg)) => {
println!(" Correctly failed without retrying: {}", msg);
}
Err(other) => {
println!(" Unexpected error type: {:?}", other);
}
Ok(s) => {
let attempts: u32 = s.get("strict_attempt").await?;
println!(" Unexpectedly succeeded after {} attempts", attempts);
}
}
println!("\nAll retry examples complete.");
Ok(())
}