lean_ctx/core/context_kernel/
live_dashboard.rs1use std::panic::{AssertUnwindSafe, catch_unwind};
4
5use super::{coverage_class, mcp_bridge, proxy_bridge, receipt_chain, usage_normalizer};
6
7#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
9pub struct DashboardSnapshot {
10 pub proxy_etpao: f64,
12 pub mcp_etpao: f64,
14 pub identity: IdentityView,
16 pub usage: UsageView,
18 pub chain: ChainView,
20 pub coverage: CoverageView,
22}
23
24#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
26pub struct IdentityView {
27 pub total_users: usize,
29 pub total_tokens: usize,
31 pub total_saved: usize,
33 pub acceptance_rate: f64,
35}
36
37#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
39pub struct UsageView {
40 pub total_requests: usize,
42 pub total_tokens: usize,
44 pub total_saved: usize,
46 pub compression_ratio: f64,
48 pub top_model: Option<String>,
50}
51
52#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
54pub struct ChainView {
55 pub total_entries: usize,
57 pub accepted: usize,
59 pub rejected: usize,
61 pub kernel_hit_rate: f64,
63 pub phantom_savings_pct: f64,
65}
66
67#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
69pub struct CoverageView {
70 pub proxy_coverage: String,
72 pub mcp_coverage: String,
74 pub is_fully_addressable: bool,
76}
77
78#[must_use]
80pub fn snapshot() -> DashboardSnapshot {
81 catch_unwind(AssertUnwindSafe(build_snapshot)).unwrap_or_default()
82}
83
84#[must_use]
86pub fn snapshot_json() -> String {
87 serde_json::to_string(&snapshot()).unwrap_or_else(|_| "{}".to_owned())
88}
89
90#[must_use]
92pub fn format_summary() -> String {
93 let value = snapshot();
94 format!(
95 "ETPAO proxy={:.2} mcp={:.2}; requests={}; compression={:.2}; chain={}/{} accepted",
96 value.proxy_etpao,
97 value.mcp_etpao,
98 value.usage.total_requests,
99 value.usage.compression_ratio,
100 value.chain.accepted,
101 value.chain.total_entries,
102 )
103}
104
105fn build_snapshot() -> DashboardSnapshot {
106 let identity = proxy_bridge::identity_summary();
107 let proxy_etpao = proxy_bridge::etpao_summary();
108 let usage = usage_normalizer::session_usage();
109 let compression = usage_normalizer::compression_overview();
110 let chain = receipt_chain::chain_summary();
111 let proxy_coverage = coverage_class::CoverageClass::FullInline;
112 let mcp_coverage = coverage_class::CoverageClass::ContextControlled;
113
114 DashboardSnapshot {
115 proxy_etpao: proxy_etpao.etpao,
116 mcp_etpao: mcp_bridge::mcp_etpao(),
117 identity: IdentityView {
118 total_users: identity.total_users,
119 total_tokens: identity.total_tokens,
120 total_saved: identity.total_savings,
121 acceptance_rate: proxy_etpao.first_pass_rate,
122 },
123 usage: UsageView {
124 total_requests: usage.total_requests,
125 total_tokens: usage.total_tokens,
126 total_saved: usage.total_saved,
127 compression_ratio: compression.avg_compression_ratio,
128 top_model: compression.best_model,
129 },
130 chain: ChainView {
131 total_entries: chain.total_entries,
132 accepted: chain.accepted,
133 rejected: chain.rejected,
134 kernel_hit_rate: receipt_chain::kernel_hit_rate(),
135 phantom_savings_pct: average(chain.total_phantom_savings_pct, chain.total_entries),
136 },
137 coverage: CoverageView {
138 proxy_coverage: coverage_class::coverage_label(proxy_coverage).to_owned(),
139 mcp_coverage: coverage_class::coverage_label(mcp_coverage).to_owned(),
140 is_fully_addressable: coverage_class::is_addressable(proxy_coverage)
141 && coverage_class::is_addressable(mcp_coverage),
142 },
143 }
144}
145
146fn average(total: f64, count: usize) -> f64 {
147 if count == 0 {
148 0.0
149 } else {
150 total / count as f64
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use std::sync::{Mutex, MutexGuard};
157
158 use super::{DashboardSnapshot, format_summary, snapshot, snapshot_json};
159 use crate::core::context_kernel::mcp_bridge::{self, McpCallData};
160 use crate::core::context_kernel::proxy_bridge::{self, ProxyRequestData};
161
162 static TEST_LOCK: Mutex<()> = Mutex::new(());
163
164 fn isolated_test() -> MutexGuard<'static, ()> {
165 let guard = TEST_LOCK
166 .lock()
167 .unwrap_or_else(std::sync::PoisonError::into_inner);
168 proxy_bridge::reset_state();
169 mcp_bridge::reset_mcp_state();
170 crate::core::context_kernel::usage_normalizer::reset_usage();
171 crate::core::context_kernel::receipt_chain::reset_chain();
172 guard
173 }
174
175 #[test]
176 fn empty_snapshot() {
177 let _guard = isolated_test();
178 let value = snapshot();
179
180 assert_eq!(value.proxy_etpao, 0.0);
181 assert_eq!(value.mcp_etpao, 0.0);
182 assert_eq!(value.identity.total_tokens, 0);
183 assert_eq!(value.usage.total_requests, 0);
184 assert_eq!(value.chain.total_entries, 0);
185 }
186
187 #[test]
188 fn snapshot_serializes() {
189 let _guard = isolated_test();
190 let decoded: DashboardSnapshot = serde_json::from_str(&snapshot_json()).unwrap();
191
192 assert_eq!(decoded.proxy_etpao, 0.0);
193 }
194
195 #[test]
196 fn format_summary_non_empty() {
197 let _guard = isolated_test();
198 let summary = format_summary();
199
200 assert!(summary.contains("ETPAO"));
201 assert!(summary.contains("compression"));
202 }
203
204 #[test]
205 fn snapshot_includes_coverage() {
206 let _guard = isolated_test();
207 let coverage = snapshot().coverage;
208
209 assert_eq!(coverage.proxy_coverage, "full_inline");
210 assert_eq!(coverage.mcp_coverage, "context_controlled");
211 assert!(coverage.is_fully_addressable);
212 }
213
214 #[test]
215 fn snapshot_after_activity() {
216 let _guard = isolated_test();
217 let _ = proxy_bridge::process_proxy_request(&ProxyRequestData {
218 input_tokens: 80,
219 output_tokens: 20,
220 request_count: 1,
221 ..ProxyRequestData::default()
222 });
223 mcp_bridge::record_mcp_call(&McpCallData {
224 input_tokens: 40,
225 output_tokens: 10,
226 call_number: 1,
227 ..McpCallData::default()
228 });
229
230 let value = snapshot();
231 assert!(value.proxy_etpao > 0.0);
232 assert!(value.mcp_etpao > 0.0);
233 assert!(value.identity.total_tokens > 0);
234 }
235}