1use crate::runtime::{AgentEvent, ToolUseResult};
7
8pub struct AgentHookContext {
10 pub node_name: String,
12 pub input_message_count: usize,
14}
15
16pub struct AgentHookSnapshot {
18 pub result: ToolUseResult,
20 pub events: Vec<AgentEvent>,
22}
23
24pub trait AgentHook: Send + Sync {
29 fn before_agent(&self, _ctx: &AgentHookContext) {}
31
32 fn after_agent(&self, _snapshot: &AgentHookSnapshot) {}
34}
35
36#[derive(Debug, Clone, Default)]
38pub struct NoOpAgentHook;
39
40impl AgentHook for NoOpAgentHook {}
41
42#[derive(Debug, Clone)]
44pub struct TracingAgentHook;
45
46impl AgentHook for TracingAgentHook {
47 fn before_agent(&self, ctx: &AgentHookContext) {
48 tracing::debug!(
49 node = %ctx.node_name,
50 input_messages = ctx.input_message_count,
51 "agent loop starting"
52 );
53 }
54
55 fn after_agent(&self, snapshot: &AgentHookSnapshot) {
56 tracing::debug!(
57 iterations = snapshot.result.iterations,
58 tool_calls = snapshot.result.tool_calls_executed,
59 stop_reason = ?snapshot.result.stop_reason,
60 "agent loop completed"
61 );
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn test_noop_agent_hook() {
71 let hook = NoOpAgentHook;
72 let ctx = AgentHookContext {
73 node_name: "test".to_string(),
74 input_message_count: 0,
75 };
76 hook.before_agent(&ctx);
77 }
78
79 #[test]
80 fn test_tracing_agent_hook() {
81 let hook = TracingAgentHook;
82 let ctx = AgentHookContext {
83 node_name: "test".to_string(),
84 input_message_count: 5,
85 };
86 hook.before_agent(&ctx);
87 }
88}