Repository navigation
Expand file tree
/
Copy pathcallbacks.rs
More file actions
130 lines (119 loc) · 4.08 KB
/
Copy pathcallbacks.rs
File metadata and controls
130 lines (119 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Lifecycle callbacks example.
//!
//! Demonstrates:
//! - `on_before_turn` to limit turns
//! - `on_after_turn` to track token usage
//! - `on_error` to log errors
//!
//! Uses MockProvider so no API key is needed.
//! cargo run --example callbacks
use std::sync::{Arc, Mutex};
use yoagent::agent::Agent;
use yoagent::provider::mock::*;
use yoagent::provider::MockProvider;
use yoagent::types::*;
#[tokio::main]
async fn main() {
// Provider: tool call → text response (2-turn conversation)
let provider = MockProvider::new(vec![
MockResponse::ToolCalls(vec![MockToolCall {
provider_metadata: None,
name: "greet".into(),
arguments: serde_json::json!({"name": "World"}),
}]),
MockResponse::Text("I greeted the world for you!".into()),
]);
// Track usage across turns
let token_log: Arc<Mutex<Vec<(usize, u64, u64)>>> = Arc::new(Mutex::new(Vec::new()));
let log_clone = token_log.clone();
// Track errors
let errors: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(Vec::new()));
let errors_clone = errors.clone();
struct GreetTool;
#[async_trait::async_trait]
impl AgentTool for GreetTool {
fn name(&self) -> &str {
"greet"
}
fn label(&self) -> &str {
"Greet"
}
fn description(&self) -> &str {
"Greets someone"
}
fn parameters_schema(&self) -> serde_json::Value {
serde_json::json!({
"type": "object",
"properties": {
"name": {"type": "string"}
}
})
}
async fn execute(
&self,
params: serde_json::Value,
_ctx: ToolContext,
) -> Result<ToolResult, ToolError> {
let name = params["name"].as_str().unwrap_or("stranger");
Ok(ToolResult {
content: vec![Content::Text {
text: format!("Hello, {}!", name),
}],
details: serde_json::Value::Null,
})
}
}
let mut agent = Agent::from_provider(provider, yoagent::provider::ModelConfig::mock())
.with_system_prompt("You are helpful.")
.with_tools(vec![Box::new(GreetTool)])
// Limit to 5 turns (plenty for this example)
.on_before_turn(|messages, turn| {
println!("[before_turn] turn={}, messages={}", turn, messages.len());
turn < 5
})
// Log token usage after each turn
.on_after_turn(move |messages, usage| {
let entry = (messages.len(), usage.input, usage.output);
println!(
"[after_turn] messages={}, tokens: {} in / {} out",
entry.0, entry.1, entry.2
);
log_clone.lock().unwrap().push(entry);
})
// Log any errors
.on_error(move |err| {
println!("[on_error] {}", err);
errors_clone.lock().unwrap().push(err.to_string());
});
println!("=== Running agent with callbacks ===\n");
let mut rx = agent.prompt("Greet the world").await;
while let Some(event) = rx.recv().await {
match event {
AgentEvent::MessageUpdate {
delta: StreamDelta::Text { delta },
..
} => print!("{}", delta),
AgentEvent::ToolExecutionStart { tool_name, .. } => {
println!("\n [tool: {}]", tool_name);
}
AgentEvent::AgentEnd { .. } => println!("\n"),
_ => {}
}
}
// Print summary
let log = token_log.lock().unwrap();
println!("=== Callback Summary ===");
println!("after_turn called {} time(s)", log.len());
for (i, (msgs, input, output)) in log.iter().enumerate() {
println!(
" Turn {}: {} messages, {} input / {} output tokens",
i, msgs, input, output
);
}
let errs = errors.lock().unwrap();
if errs.is_empty() {
println!("No errors recorded.");
} else {
println!("{} error(s): {:?}", errs.len(), *errs);
}
}