Lifecycle Callbacks
yoagent provides three lifecycle callbacks that let you observe and control the agent loop without modifying its internals.
Callbacks
before_turn
Called before each LLM call. Receives the current message history and the turn number (0-indexed). Return false to abort the loop.
#![allow(unused)] fn main() { let agent = Agent::new(provider) .on_before_turn(|messages, turn| { println!("Turn {} starting with {} messages", turn, messages.len()); turn < 10 // Stop after 10 turns }); }
after_turn
Called after each LLM response and tool execution. Receives the updated message history and the turn's token usage.
#![allow(unused)] fn main() { use std::sync::{Arc, Mutex}; let total_cost = Arc::new(Mutex::new(0u64)); let cost_tracker = total_cost.clone(); let agent = Agent::new(provider) .on_after_turn(move |_messages, usage| { let mut cost = cost_tracker.lock().unwrap(); *cost += usage.input + usage.output; println!("Cumulative tokens: {}", *cost); }); }
on_error
Called when the LLM returns a StopReason::Error. Receives the error message string.
#![allow(unused)] fn main() { let agent = Agent::new(provider) .on_error(|err| { eprintln!("LLM error: {}", err); // Log to monitoring, send alert, etc. }); }
Combining Callbacks
All callbacks are optional and independent:
#![allow(unused)] fn main() { let agent = Agent::new(provider) .on_before_turn(|_msgs, turn| turn < 20) .on_after_turn(|msgs, usage| { println!("Messages: {}, Tokens: {}/{}", msgs.len(), usage.input, usage.output); }) .on_error(|err| eprintln!("Error: {}", err)); }
Using with AgentLoopConfig
For direct loop usage without the Agent wrapper:
#![allow(unused)] fn main() { use std::sync::Arc; use yoagent::agent_loop::AgentLoopConfig; let config = AgentLoopConfig { before_turn: Some(Arc::new(|_msgs, turn| turn < 5)), after_turn: Some(Arc::new(|_msgs, _usage| { /* log */ })), on_error: Some(Arc::new(|err| eprintln!("{}", err))), // ... other fields }; }
Callback Timing
Loop iteration:
1. Inject pending messages (steering/follow-up)
2. Check execution limits
3. before_turn(messages, turn_number) <-- return false to abort
4. Compact context
5. Stream LLM response
6. Check for error/abort → on_error(message) if StopReason::Error
→ after_turn(messages, usage) even on error/abort
7. Execute tool calls
8. Track turn
9. after_turn(messages, usage)
10. Emit TurnEnd event