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