lean_ctx/core/context_kernel/
response_evidence.rs1use std::sync::atomic::{AtomicUsize, Ordering};
4
5use super::kernel_config;
6
7static TOTAL_RESPONSES: AtomicUsize = AtomicUsize::new(0);
8static TOTAL_OUTPUT_TOKENS: AtomicUsize = AtomicUsize::new(0);
9static CACHED_RESPONSES: AtomicUsize = AtomicUsize::new(0);
10
11#[derive(Debug, Clone, Default, serde::Serialize)]
13pub struct ResponseSummary {
14 pub total_responses: usize,
16 pub total_output_tokens: usize,
18 pub cached_responses: usize,
20 pub cache_hit_rate: f64,
22}
23
24pub fn record_response(tool_name: &str, output_tokens: usize, was_cached: bool) {
26 if !kernel_config::is_enabled() {
27 return;
28 }
29 let _ = tool_name;
30 TOTAL_RESPONSES.fetch_add(1, Ordering::Relaxed);
31 TOTAL_OUTPUT_TOKENS.fetch_add(output_tokens, Ordering::Relaxed);
32 if was_cached {
33 CACHED_RESPONSES.fetch_add(1, Ordering::Relaxed);
34 }
35}
36
37#[must_use]
39pub fn response_summary() -> ResponseSummary {
40 let total_responses = TOTAL_RESPONSES.load(Ordering::Relaxed);
41 let cached_responses = CACHED_RESPONSES.load(Ordering::Relaxed);
42 ResponseSummary {
43 total_responses,
44 total_output_tokens: TOTAL_OUTPUT_TOKENS.load(Ordering::Relaxed),
45 cached_responses,
46 cache_hit_rate: cached_responses as f64 / total_responses.max(1) as f64,
47 }
48}
49
50pub fn reset() {
52 TOTAL_RESPONSES.store(0, Ordering::Relaxed);
53 TOTAL_OUTPUT_TOKENS.store(0, Ordering::Relaxed);
54 CACHED_RESPONSES.store(0, Ordering::Relaxed);
55}
56
57#[cfg(test)]
58mod tests {
59 use super::{record_response, reset, response_summary};
60 use crate::core::context_kernel::kernel_config;
61
62 fn isolated() -> std::sync::MutexGuard<'static, ()> {
63 let guard = kernel_config::KERNEL_TEST_LOCK
64 .lock()
65 .unwrap_or_else(std::sync::PoisonError::into_inner);
66 kernel_config::reset_features();
67 reset();
68 guard
69 }
70
71 #[test]
72 fn records_response() {
73 let _guard = isolated();
74 for _ in 0..3 {
75 record_response("ctx_read", 1, false);
76 }
77 assert_eq!(response_summary().total_responses, 3);
78 }
79
80 #[test]
81 fn tracks_output_tokens() {
82 let _guard = isolated();
83 record_response("ctx_read", 13, false);
84 record_response("ctx_search", 21, false);
85 assert_eq!(response_summary().total_output_tokens, 34);
86 }
87
88 #[test]
89 fn cache_hit_rate() {
90 let _guard = isolated();
91 for was_cached in [true, false, true, false] {
92 record_response("ctx_read", 1, was_cached);
93 }
94 assert_eq!(response_summary().cache_hit_rate, 0.5);
95 }
96
97 #[test]
98 fn disabled_kernel_noop() {
99 let _guard = isolated();
100 let mut features = kernel_config::features();
101 features.enabled = false;
102 kernel_config::update_features(features);
103 record_response("ctx_read", 10, true);
104 let summary = response_summary();
105 assert_eq!(summary.total_responses, 0);
106 assert_eq!(summary.total_output_tokens, 0);
107 assert_eq!(summary.cached_responses, 0);
108 }
109}