Skip to main content

lc_callbacks/handlers/
stdout_handler.rs

1// lc-callbacks/src/handlers/stdout_handler.rs
2//! Standard output callback handler for debugging
3
4use async_trait::async_trait;
5use std::io::Write;
6
7use crate::{CallbackHandler, RunTree};
8use lc_schema::Message;
9
10/// Standard output callback handler
11///
12/// Prints trace information to console for debugging.
13pub struct StdOutHandler {
14    verbose: bool,
15}
16
17impl StdOutHandler {
18    pub fn new() -> Self {
19        Self { verbose: true }
20    }
21
22    pub fn with_verbose(mut self, verbose: bool) -> Self {
23        self.verbose = verbose;
24        self
25    }
26}
27
28impl Default for StdOutHandler {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34#[async_trait]
35impl CallbackHandler for StdOutHandler {
36    async fn on_run_start(&self, run: &RunTree) {
37        if self.verbose {
38            println!(
39                "\n{} [{}] START: {}",
40                run.run_type.emoji(),
41                run.run_type.as_str().to_uppercase(),
42                run.name
43            );
44            println!("   ID: {}", run.id);
45            if run.inputs != serde_json::Value::Null {
46                println!(
47                    "   Inputs: {}",
48                    serde_json::to_string_pretty(&run.inputs).unwrap_or_default()
49                );
50            }
51        }
52    }
53
54    async fn on_run_end(&self, run: &RunTree) {
55        if self.verbose {
56            let duration = run
57                .duration_ms()
58                .map(|d| format!("{}ms", d))
59                .unwrap_or_default();
60
61            println!(
62                "\n{} [{}] END: {} ({})",
63                run.run_type.emoji(),
64                run.run_type.as_str().to_uppercase(),
65                run.name,
66                duration
67            );
68
69            if let Some(outputs) = &run.outputs {
70                println!(
71                    "   Outputs: {}",
72                    serde_json::to_string_pretty(outputs).unwrap_or_default()
73                );
74            }
75        }
76    }
77
78    async fn on_run_error(&self, run: &RunTree, error: &str) {
79        if self.verbose {
80            println!(
81                "\n❌ [{}] ERROR: {}",
82                run.run_type.as_str().to_uppercase(),
83                run.name
84            );
85            println!("   Error: {}", error);
86        }
87    }
88
89    async fn on_llm_start(&self, run: &RunTree, messages: &[Message]) {
90        if self.verbose {
91            self.on_run_start(run).await;
92            println!("   Messages: {} message(s)", messages.len());
93        }
94    }
95
96    async fn on_llm_new_token(&self, _run: &RunTree, token: &str) {
97        if self.verbose {
98            print!("{}", token);
99            let _ = std::io::stdout().flush();
100        }
101    }
102
103    async fn on_tool_start(&self, run: &RunTree, tool_name: &str, input: &str) {
104        if self.verbose {
105            self.on_run_start(run).await;
106            println!("   Tool: {}", tool_name);
107            println!("   Input: {}", input);
108        }
109    }
110
111    async fn on_retriever_start(&self, run: &RunTree, query: &str) {
112        if self.verbose {
113            self.on_run_start(run).await;
114            println!("   Query: {}", query);
115        }
116    }
117
118    async fn on_retriever_end(&self, run: &RunTree, documents: &[serde_json::Value]) {
119        if self.verbose {
120            self.on_run_end(run).await;
121            println!("   Documents: {} result(s)", documents.len());
122        }
123    }
124}