use littrs::PyValue;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)]
pub enum AgentEvent {
IterationStart {
iteration: usize,
max_iterations: usize,
},
LLMRequest { message_count: usize },
LLMResponse {
content: String,
#[allow(dead_code)]
tokens_used: Option<usize>,
},
Thinking { content: String },
CodeGenerated { code: String },
CodeExecuted {
code: String,
output: String,
success: bool,
},
ToolCall { name: String, args: Vec<PyValue> },
ToolResult { name: String, result: PyValue },
Finish { value: PyValue },
Error { message: String },
}
pub type EventCallback = Arc<dyn Fn(&AgentEvent) + Send + Sync>;
#[derive(Default, Clone)]
pub struct AgentCallbacks {
pub on_iteration_start: Option<EventCallback>,
pub on_llm_request: Option<EventCallback>,
pub on_llm_response: Option<EventCallback>,
pub on_thinking: Option<EventCallback>,
pub on_code_generated: Option<EventCallback>,
pub on_code_executed: Option<EventCallback>,
pub on_tool_call: Option<EventCallback>,
pub on_tool_result: Option<EventCallback>,
pub on_finish: Option<EventCallback>,
pub on_error: Option<EventCallback>,
pub on_event: Option<EventCallback>,
pub(crate) captured_events: Option<Arc<Mutex<Vec<AgentEvent>>>>,
}
impl AgentCallbacks {
pub fn emit(&self, event: &AgentEvent) {
if let Some(ref events) = self.captured_events {
if let Ok(mut events) = events.lock() {
events.push(event.clone());
}
}
let specific = match event {
AgentEvent::IterationStart { .. } => &self.on_iteration_start,
AgentEvent::LLMRequest { .. } => &self.on_llm_request,
AgentEvent::LLMResponse { .. } => &self.on_llm_response,
AgentEvent::Thinking { .. } => &self.on_thinking,
AgentEvent::CodeGenerated { .. } => &self.on_code_generated,
AgentEvent::CodeExecuted { .. } => &self.on_code_executed,
AgentEvent::ToolCall { .. } => &self.on_tool_call,
AgentEvent::ToolResult { .. } => &self.on_tool_result,
AgentEvent::Finish { .. } => &self.on_finish,
AgentEvent::Error { .. } => &self.on_error,
};
if let Some(cb) = specific {
cb(event);
}
if let Some(cb) = &self.on_event {
cb(event);
}
}
}
pub fn verbose_callbacks() -> AgentCallbacks {
AgentCallbacks {
on_iteration_start: Some(Arc::new(|e| {
if let AgentEvent::IterationStart {
iteration,
max_iterations,
} = e
{
eprintln!("[dragen] Iteration {}/{}", iteration, max_iterations);
}
})),
on_llm_response: Some(Arc::new(|e| {
if let AgentEvent::LLMResponse { content, .. } = e {
let preview: String = content.chars().take(100).collect();
let suffix = if content.len() > 100 { "..." } else { "" };
eprintln!("[dragen] LLM: {}{}", preview.replace('\n', "\\n"), suffix);
}
})),
on_code_generated: Some(Arc::new(|e| {
if let AgentEvent::CodeGenerated { code } = e {
let lines: Vec<&str> = code.lines().take(3).collect();
let preview = lines.join("\\n");
let suffix = if code.lines().count() > 3 { "..." } else { "" };
eprintln!("[dragen] Code: {}{}", preview, suffix);
}
})),
on_code_executed: Some(Arc::new(|e| {
if let AgentEvent::CodeExecuted { output, success, .. } = e {
let status = if *success { "✓" } else { "✗" };
let preview: String = output.chars().take(80).collect();
let suffix = if output.len() > 80 { "..." } else { "" };
eprintln!(
"[dragen] {} {}{}",
status,
preview.replace('\n', "\\n"),
suffix
);
}
})),
on_tool_call: Some(Arc::new(|e| {
if let AgentEvent::ToolCall { name, args } = e {
let args_preview: Vec<String> =
args.iter().take(3).map(|a| format!("{:?}", a)).collect();
let suffix = if args.len() > 3 { ", ..." } else { "" };
eprintln!(
"[dragen] Tool: {}({}{})",
name,
args_preview.join(", "),
suffix
);
}
})),
on_finish: Some(Arc::new(|e| {
if let AgentEvent::Finish { value } = e {
eprintln!("[dragen] Finish: {:?}", value);
}
})),
on_error: Some(Arc::new(|e| {
if let AgentEvent::Error { message } = e {
eprintln!("[dragen] Error: {}", message);
}
})),
..Default::default()
}
}