strands_agents/handlers/
mod.rs

1//! Handlers for formatting and displaying events from the agent.
2
3use std::sync::Arc;
4
5/// Callback handler event data.
6#[derive(Debug, Clone, Default)]
7pub struct CallbackEvent {
8    pub reasoning_text: Option<String>,
9    pub data: Option<String>,
10    pub complete: bool,
11    pub current_tool_use: Option<CurrentToolUse>,
12}
13
14/// Current tool use information.
15#[derive(Debug, Clone, PartialEq)]
16pub struct CurrentToolUse {
17    pub name: String,
18    pub tool_use_id: Option<String>,
19    pub input: Option<serde_json::Value>,
20}
21
22/// Trait for callback handlers.
23pub trait CallbackHandler: Send + Sync {
24    /// Handle a callback event.
25    fn call(&mut self, event: &CallbackEvent);
26}
27
28/// Handler for streaming text output and tool invocations to stdout.
29pub struct PrintingCallbackHandler {
30    tool_count: u32,
31    previous_tool_use: Option<CurrentToolUse>,
32    verbose_tool_use: bool,
33}
34
35impl PrintingCallbackHandler {
36    /// Create a new printing callback handler.
37    pub fn new(verbose_tool_use: bool) -> Self {
38        Self {
39            tool_count: 0,
40            previous_tool_use: None,
41            verbose_tool_use,
42        }
43    }
44}
45
46impl Default for PrintingCallbackHandler {
47    fn default() -> Self {
48        Self::new(true)
49    }
50}
51
52impl CallbackHandler for PrintingCallbackHandler {
53    fn call(&mut self, event: &CallbackEvent) {
54        if let Some(ref reasoning_text) = event.reasoning_text {
55            print!("{}", reasoning_text);
56        }
57
58        if let Some(ref data) = event.data {
59            if event.complete {
60                println!("{}", data);
61            } else {
62                print!("{}", data);
63            }
64        }
65
66        if let Some(ref current_tool_use) = event.current_tool_use {
67            if self.previous_tool_use.as_ref() != Some(current_tool_use) {
68                self.previous_tool_use = Some(current_tool_use.clone());
69                self.tool_count += 1;
70                if self.verbose_tool_use {
71                    println!("\nTool #{}: {}", self.tool_count, current_tool_use.name);
72                }
73            }
74        }
75
76        if event.complete && event.data.is_some() {
77            println!();
78        }
79    }
80}
81
82/// Callback handler that combines multiple callback handlers.
83pub struct CompositeCallbackHandler {
84    handlers: Vec<Arc<std::sync::Mutex<dyn CallbackHandler>>>,
85}
86
87impl CompositeCallbackHandler {
88    /// Create a new composite callback handler.
89    pub fn new() -> Self {
90        Self {
91            handlers: Vec::new(),
92        }
93    }
94
95    /// Add a handler to the composite.
96    pub fn add_handler(&mut self, handler: impl CallbackHandler + 'static) {
97        self.handlers.push(Arc::new(std::sync::Mutex::new(handler)));
98    }
99}
100
101impl Default for CompositeCallbackHandler {
102    fn default() -> Self {
103        Self::new()
104    }
105}
106
107impl CallbackHandler for CompositeCallbackHandler {
108    fn call(&mut self, event: &CallbackEvent) {
109        for handler in &self.handlers {
110            if let Ok(mut h) = handler.lock() {
111                h.call(event);
112            }
113        }
114    }
115}
116
117/// Callback handler that discards all output.
118pub struct NullCallbackHandler;
119
120impl CallbackHandler for NullCallbackHandler {
121    fn call(&mut self, _event: &CallbackEvent) {
122    }
123}
124
125/// Convenience function to create a null callback handler.
126pub fn null_callback_handler() -> NullCallbackHandler {
127    NullCallbackHandler
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn test_printing_callback_handler() {
136        let mut handler = PrintingCallbackHandler::new(true);
137        
138        let event = CallbackEvent {
139            data: Some("Hello".to_string()),
140            complete: false,
141            ..Default::default()
142        };
143        
144        handler.call(&event);
145    }
146
147    #[test]
148    fn test_null_callback_handler() {
149        let mut handler = null_callback_handler();
150        
151        let event = CallbackEvent {
152            data: Some("Should be ignored".to_string()),
153            ..Default::default()
154        };
155        
156        handler.call(&event);
157    }
158
159    #[test]
160    fn test_composite_callback_handler() {
161        let mut composite = CompositeCallbackHandler::new();
162        composite.add_handler(NullCallbackHandler);
163        
164        let event = CallbackEvent::default();
165        composite.call(&event);
166    }
167}
168