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        println!(
80            "\n❌ [{}] ERROR: {}",
81            run.run_type.as_str().to_uppercase(),
82            run.name
83        );
84        println!("   Error: {}", error);
85    }
86
87    async fn on_llm_start(&self, run: &RunTree, messages: &[Message]) {
88        if self.verbose {
89            self.on_run_start(run).await;
90            println!("   Messages: {} message(s)", messages.len());
91        }
92    }
93
94    async fn on_llm_new_token(&self, _run: &RunTree, token: &str) {
95        print!("{}", token);
96        let _ = std::io::stdout().flush();
97    }
98
99    async fn on_tool_start(&self, run: &RunTree, tool_name: &str, input: &str) {
100        if self.verbose {
101            self.on_run_start(run).await;
102            println!("   Tool: {}", tool_name);
103            println!("   Input: {}", input);
104        }
105    }
106
107    async fn on_retriever_start(&self, run: &RunTree, query: &str) {
108        if self.verbose {
109            self.on_run_start(run).await;
110            println!("   Query: {}", query);
111        }
112    }
113
114    async fn on_retriever_end(&self, run: &RunTree, documents: &[serde_json::Value]) {
115        if self.verbose {
116            self.on_run_end(run).await;
117            println!("   Documents: {} result(s)", documents.len());
118        }
119    }
120}