Skip to main content

lean_ctx_server/tools/
ctx_feedback.rs

1// SPDX-License-Identifier: Apache-2.0
2use lean_ctx::core::llm_feedback::{LlmFeedbackEvent, LlmFeedbackStore};
3
4pub fn record(event: &LlmFeedbackEvent) -> Result<String, String> {
5    LlmFeedbackStore::record(event.clone())?;
6    Ok("feedback recorded".to_string())
7}
8
9pub fn status() -> String {
10    let s = LlmFeedbackStore::status();
11    format!(
12        "ctx_feedback status\n path: {}\n bytes: {}\n retention: max_events={} max_bytes={}",
13        s.path.display(),
14        s.bytes,
15        s.max_events,
16        s.max_bytes
17    )
18}
19
20pub fn report(limit: usize) -> String {
21    let s = LlmFeedbackStore::summarize(limit);
22    if s.total_events == 0 {
23        return "No LLM feedback recorded yet.".to_string();
24    }
25
26    let mut lines = vec![
27        format!("LLM Feedback Report (last {limit} events)"),
28        format!(" total_events: {}", s.total_events),
29        format!(" avg_output_ratio: {:.2}", s.avg_output_ratio),
30        format!(" max_output_tokens: {}", s.max_output_tokens),
31        format!(" max_output_ratio: {:.2}", s.max_output_ratio),
32    ];
33    if let Some(ms) = s.avg_latency_ms {
34        lines.push(format!(" avg_latency_ms: {ms:.0}"));
35    }
36
37    if !s.by_model.is_empty() {
38        lines.push(" by_model:".to_string());
39        for (model, m) in s.by_model.iter().take(10) {
40            let mut row = format!(
41                "  {model}: n={} avg_ratio={:.2} max_out={}",
42                m.events, m.avg_output_ratio, m.max_output_tokens
43            );
44            if let Some(ms) = m.avg_latency_ms {
45                row.push_str(&format!(" avg_ms={ms:.0}"));
46            }
47            lines.push(row);
48        }
49    }
50
51    lines.join("\n")
52}
53
54pub fn json(limit: usize) -> String {
55    let status = LlmFeedbackStore::status();
56    let summary = LlmFeedbackStore::summarize(limit);
57    let recent = LlmFeedbackStore::recent(limit.min(200));
58    serde_json::json!({
59        "status": status,
60        "summary": summary,
61        "recent": recent,
62    })
63    .to_string()
64}
65
66pub fn reset() -> String {
67    match LlmFeedbackStore::reset() {
68        Ok(()) => "LLM feedback has been reset.".to_string(),
69        Err(e) => format!("Error resetting feedback: {e}"),
70    }
71}