Skip to main content

lean_ctx/core/quality_lab/
orchestrator.rs

1use std::sync::atomic::Ordering;
2
3use crate::core::context_kernel::proxy_bridge;
4use crate::core::telemetry::global_metrics;
5
6use super::calibration::{CalibratedCount, compare_calibration};
7use super::fidelity::assess_fidelity;
8
9/// Compression quality and savings measured for one input/output pair.
10#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
11pub struct InputCompressionMetrics {
12    pub modes_tested: usize,
13    pub best_mode: String,
14    pub best_savings_pct: f64,
15    pub avg_savings_pct: f64,
16    pub avg_preservation_score: f64,
17    pub fidelity_class: String,
18    pub quality_gate_passed: bool,
19}
20
21/// Hit rates and token savings across the three cache layers.
22#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
23pub struct CacheEffectivenessMetrics {
24    pub session_cache_hit_rate: f64,
25    pub content_cache_hit_rate: f64,
26    pub response_cache_hit_rate: f64,
27    pub aggregate_hit_rate: f64,
28    pub estimated_token_savings: u64,
29}
30
31/// Cross-family tokenizer calibration summary.
32#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
33pub struct TokenizerCalibrationMetrics {
34    pub families_tested: usize,
35    pub max_cross_family_variance_pct: f64,
36    pub dominant_family: String,
37    pub dominant_accuracy: String,
38}
39
40/// Runtime effective-tokens-per-accepted-outcome summary.
41#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
42pub struct EtpaoSummary {
43    pub current_etpao: Option<f64>,
44    pub savings_rate_pct: f64,
45    pub total_events: u64,
46    pub quality_gate: String,
47}
48
49/// Unified quality lab report aggregating all measurement pillars.
50#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
51pub struct QualityLabReport {
52    pub schema_version: String,
53    pub input_compression: InputCompressionMetrics,
54    pub cache_effectiveness: CacheEffectivenessMetrics,
55    pub tokenizer_calibration: TokenizerCalibrationMetrics,
56    pub etpao: EtpaoSummary,
57    pub overall_quality_grade: QualityGrade,
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
61pub enum QualityGrade {
62    Premium,
63    Good,
64    Acceptable,
65    BelowThreshold,
66}
67
68#[derive(Debug, Clone, Copy)]
69struct CacheCounts {
70    session_hits: u64,
71    session_misses: u64,
72    content_hits: u64,
73    content_misses: u64,
74    response_hits: u64,
75    response_misses: u64,
76    tokens_saved: u64,
77}
78
79pub fn assess_input_compression(
80    original: &str,
81    compressed: &str,
82    ext: &str,
83) -> InputCompressionMetrics {
84    let preservation = crate::core::preservation::measure(original, compressed, ext);
85    let fidelity = assess_fidelity(original, compressed, ext);
86    let input_tokens = crate::core::tokens::count_tokens(original) as u64;
87    let output_tokens = crate::core::tokens::count_tokens(compressed) as u64;
88    let savings_pct = token_savings_pct(input_tokens, output_tokens);
89
90    InputCompressionMetrics {
91        modes_tested: 1,
92        best_mode: "provided".to_string(),
93        best_savings_pct: savings_pct,
94        avg_savings_pct: savings_pct,
95        avg_preservation_score: preservation.overall(),
96        fidelity_class: format!("{:?}", fidelity.class),
97        quality_gate_passed: fidelity.passed_quality_gate,
98    }
99}
100
101pub fn assess_cache_effectiveness() -> CacheEffectivenessMetrics {
102    let counts = telemetry_counts();
103    let session_requests = counts.session_hits.saturating_add(counts.session_misses);
104    let content_requests = counts.content_hits.saturating_add(counts.content_misses);
105    let response_requests = counts.response_hits.saturating_add(counts.response_misses);
106    let total_hits = counts
107        .session_hits
108        .saturating_add(counts.content_hits)
109        .saturating_add(counts.response_hits);
110    let total_requests = session_requests
111        .saturating_add(content_requests)
112        .saturating_add(response_requests);
113
114    CacheEffectivenessMetrics {
115        session_cache_hit_rate: hit_rate(counts.session_hits, session_requests),
116        content_cache_hit_rate: hit_rate(counts.content_hits, content_requests),
117        response_cache_hit_rate: hit_rate(counts.response_hits, response_requests),
118        aggregate_hit_rate: hit_rate(total_hits, total_requests),
119        estimated_token_savings: if total_requests == 0 {
120            0
121        } else {
122            counts.tokens_saved
123        },
124    }
125}
126
127pub fn assess_tokenizer_calibration(sample_text: &str) -> TokenizerCalibrationMetrics {
128    let counts = compare_calibration(sample_text);
129    let mut minimum = u64::MAX;
130    let mut maximum = 0_u64;
131    let mut dominant: Option<CalibratedCount> = None;
132
133    for count in counts.iter().copied() {
134        minimum = minimum.min(count.tokens);
135        if dominant.is_none_or(|current| count.tokens > current.tokens) {
136            dominant = Some(count);
137        }
138        maximum = maximum.max(count.tokens);
139    }
140
141    let variance = if maximum == 0 {
142        0.0
143    } else {
144        maximum.saturating_sub(minimum) as f64 / maximum as f64 * 100.0
145    };
146
147    TokenizerCalibrationMetrics {
148        families_tested: counts.len(),
149        max_cross_family_variance_pct: variance,
150        dominant_family: dominant.map_or_else(
151            || "Unknown".to_string(),
152            |count| format!("{:?}", count.family),
153        ),
154        dominant_accuracy: dominant.map_or_else(
155            || "CharFallback".to_string(),
156            |count| format!("{:?}", count.accuracy),
157        ),
158    }
159}
160
161pub fn compute_quality_grade(report: &QualityLabReport) -> QualityGrade {
162    let input = &report.input_compression;
163    let structural = matches!(input.fidelity_class.as_str(), "Exact" | "Structural");
164    let savings = input.best_savings_pct;
165    let cache = report.cache_effectiveness.aggregate_hit_rate;
166    let etpao = report.etpao.savings_rate_pct;
167
168    if savings >= 80.0 && cache >= 50.0 && structural && etpao >= 50.0 {
169        QualityGrade::Premium
170    } else if savings >= 60.0 && cache >= 30.0 && structural {
171        QualityGrade::Good
172    } else if savings >= 40.0 && structural {
173        QualityGrade::Acceptable
174    } else {
175        QualityGrade::BelowThreshold
176    }
177}
178
179pub fn run_quality_lab(original: &str, compressed: &str, ext: &str) -> QualityLabReport {
180    let input_compression = assess_input_compression(original, compressed, ext);
181    let cache_effectiveness = assess_cache_effectiveness();
182    let tokenizer_calibration = assess_tokenizer_calibration(original);
183    let etpao = assess_etpao(input_compression.best_savings_pct);
184    let mut report = QualityLabReport {
185        schema_version: "lean-ctx.quality-lab/v1".to_string(),
186        input_compression,
187        cache_effectiveness,
188        tokenizer_calibration,
189        etpao,
190        overall_quality_grade: QualityGrade::BelowThreshold,
191    };
192    report.overall_quality_grade = compute_quality_grade(&report);
193    report
194}
195
196pub fn format_quality_report(report: &QualityLabReport) -> String {
197    format!(
198        concat!(
199            "Quality Lab ({})\n",
200            "Input Compression\n",
201            "  savings={:.1}% preservation={:.3} fidelity={} gate={}\n",
202            "Cache Effectiveness\n",
203            "  session={:.1}% content={:.1}% response={:.1}% ",
204            "aggregate={:.1}%\n",
205            "Tokenizer Calibration\n",
206            "  families={} variance={:.1}% dominant={} ({})\n",
207            "ETPAO\n",
208            "  current={} savings={:.1}% events={} gate={}\n",
209            "Overall Grade: {:?}"
210        ),
211        report.schema_version,
212        report.input_compression.best_savings_pct,
213        report.input_compression.avg_preservation_score,
214        report.input_compression.fidelity_class,
215        gate_label(report.input_compression.quality_gate_passed),
216        report.cache_effectiveness.session_cache_hit_rate,
217        report.cache_effectiveness.content_cache_hit_rate,
218        report.cache_effectiveness.response_cache_hit_rate,
219        report.cache_effectiveness.aggregate_hit_rate,
220        report.tokenizer_calibration.families_tested,
221        report.tokenizer_calibration.max_cross_family_variance_pct,
222        report.tokenizer_calibration.dominant_family,
223        report.tokenizer_calibration.dominant_accuracy,
224        format_etpao(report.etpao.current_etpao),
225        report.etpao.savings_rate_pct,
226        report.etpao.total_events,
227        report.etpao.quality_gate,
228        report.overall_quality_grade,
229    )
230}
231
232fn token_savings_pct(input_tokens: u64, output_tokens: u64) -> f64 {
233    if input_tokens == 0 {
234        return 0.0;
235    }
236    let retained = output_tokens as f64 / input_tokens as f64;
237    ((1.0 - retained) * 100.0).clamp(0.0, 100.0)
238}
239
240fn hit_rate(hits: u64, requests: u64) -> f64 {
241    if requests == 0 {
242        0.0
243    } else {
244        hits as f64 / requests as f64 * 100.0
245    }
246}
247
248fn telemetry_counts() -> CacheCounts {
249    let metrics = global_metrics();
250    let aggregate_hits = metrics.cache_hits.load(Ordering::Relaxed);
251    let aggregate_misses = metrics.cache_misses.load(Ordering::Relaxed);
252    let content = crate::core::content_cache::stats();
253    let response = crate::core::ocla::response_cache::global_response_cache().stats();
254    let classified_hits = content.hits.saturating_add(response.hits);
255    let classified_misses = content.misses.saturating_add(response.misses);
256
257    CacheCounts {
258        session_hits: aggregate_hits.saturating_sub(classified_hits),
259        session_misses: aggregate_misses.saturating_sub(classified_misses),
260        content_hits: content.hits,
261        content_misses: content.misses,
262        response_hits: response.hits,
263        response_misses: response.misses,
264        tokens_saved: metrics.tokens_saved.load(Ordering::Relaxed),
265    }
266}
267
268fn assess_etpao(input_savings_pct: f64) -> EtpaoSummary {
269    let summary = proxy_bridge::etpao_summary();
270    let has_data = summary.accepted_outcomes > 0;
271    EtpaoSummary {
272        current_etpao: has_data.then_some(summary.etpao),
273        savings_rate_pct: input_savings_pct,
274        total_events: summary.accepted_outcomes as u64,
275        quality_gate: if has_data && input_savings_pct >= 50.0 {
276            "PASS".to_string()
277        } else if has_data {
278            "BELOW_THRESHOLD".to_string()
279        } else {
280            "NO_DATA".to_string()
281        },
282    }
283}
284
285fn gate_label(passed: bool) -> &'static str {
286    if passed { "PASS" } else { "FAIL" }
287}
288
289fn format_etpao(value: Option<f64>) -> String {
290    value.map_or_else(|| "n/a".to_string(), |current| format!("{current:.1}"))
291}
292
293#[cfg(test)]
294mod tests {
295    use super::{
296        CacheEffectivenessMetrics, EtpaoSummary, InputCompressionMetrics, QualityGrade,
297        QualityLabReport, TokenizerCalibrationMetrics, assess_cache_effectiveness,
298        assess_input_compression, assess_tokenizer_calibration, compute_quality_grade,
299        format_quality_report, run_quality_lab,
300    };
301
302    const ORIGINAL: &str = r"pub fn process(items: &[Item]) -> Result<Vec<Output>, Error> {
303    let mut outputs = Vec::with_capacity(items.len());
304    for item in items {
305        let validated = validate(item)?;
306        outputs.push(transform(validated));
307    }
308    Ok(outputs)
309}";
310
311    const COMPRESSED: &str = r"pub fn process(items: &[Item]) -> Result<Vec<Output>, Error> {
312    let outputs = items.iter().map(validate).map(transform).collect();
313    Ok(outputs)
314}";
315
316    #[test]
317    fn test_input_compression_assessment() {
318        let metrics = assess_input_compression(ORIGINAL, COMPRESSED, "rs");
319        assert_eq!(metrics.modes_tested, 1);
320        assert!((0.0..=100.0).contains(&metrics.best_savings_pct));
321        assert!((0.0..=1.0).contains(&metrics.avg_preservation_score));
322        assert!(!metrics.fidelity_class.is_empty());
323    }
324
325    #[test]
326    fn test_cache_effectiveness_valid_ranges() {
327        let metrics = assess_cache_effectiveness();
328        assert!((0.0..=100.0).contains(&metrics.session_cache_hit_rate));
329        assert!((0.0..=100.0).contains(&metrics.content_cache_hit_rate));
330        assert!((0.0..=100.0).contains(&metrics.response_cache_hit_rate));
331        assert!((0.0..=100.0).contains(&metrics.aggregate_hit_rate));
332    }
333
334    #[test]
335    fn test_tokenizer_calibration_variance() {
336        let metrics = assess_tokenizer_calibration(ORIGINAL);
337        assert!(metrics.families_tested > 1);
338        assert!(metrics.max_cross_family_variance_pct >= 0.0);
339        assert!(!metrics.dominant_family.is_empty());
340    }
341
342    #[test]
343    fn test_premium_grade_thresholds() {
344        let report = report_with(85.0, 55.0, "Structural", 70.0);
345        assert_eq!(compute_quality_grade(&report), QualityGrade::Premium);
346    }
347
348    #[test]
349    fn test_below_threshold_grade() {
350        let report = report_with(25.0, 90.0, "Lossy", 80.0);
351        assert_eq!(compute_quality_grade(&report), QualityGrade::BelowThreshold);
352    }
353
354    #[test]
355    fn test_quality_lab_report_serialization() {
356        let report = report_with(65.0, 35.0, "Exact", 40.0);
357        let json = serde_json::to_string(&report).expect("serialize report");
358        let decoded: QualityLabReport = serde_json::from_str(&json).expect("deserialize report");
359        assert_eq!(decoded.schema_version, report.schema_version);
360        assert_eq!(decoded.overall_quality_grade, report.overall_quality_grade);
361    }
362
363    #[test]
364    fn test_format_report_output() {
365        let output = format_quality_report(&report_with(65.0, 35.0, "Exact", 40.0));
366        assert!(output.contains("Input Compression"));
367        assert!(output.contains("Cache Effectiveness"));
368        assert!(output.contains("Tokenizer Calibration"));
369        assert!(output.contains("ETPAO"));
370        assert!(output.contains("Overall Grade"));
371    }
372
373    #[test]
374    fn test_run_quality_lab_integration() {
375        let report = run_quality_lab(ORIGINAL, COMPRESSED, "rs");
376        assert_eq!(report.schema_version, "lean-ctx.quality-lab/v1");
377        assert!(report.tokenizer_calibration.families_tested > 1);
378        assert!((0.0..=100.0).contains(&report.input_compression.best_savings_pct));
379    }
380
381    fn report_with(savings: f64, cache: f64, fidelity: &str, etpao: f64) -> QualityLabReport {
382        QualityLabReport {
383            schema_version: "lean-ctx.quality-lab/v1".to_string(),
384            input_compression: InputCompressionMetrics {
385                modes_tested: 1,
386                best_mode: "provided".to_string(),
387                best_savings_pct: savings,
388                avg_savings_pct: savings,
389                avg_preservation_score: 1.0,
390                fidelity_class: fidelity.to_string(),
391                quality_gate_passed: true,
392            },
393            cache_effectiveness: CacheEffectivenessMetrics {
394                session_cache_hit_rate: cache,
395                content_cache_hit_rate: cache,
396                response_cache_hit_rate: cache,
397                aggregate_hit_rate: cache,
398                estimated_token_savings: 1_024,
399            },
400            tokenizer_calibration: TokenizerCalibrationMetrics {
401                families_tested: 4,
402                max_cross_family_variance_pct: 8.0,
403                dominant_family: "Llama".to_string(),
404                dominant_accuracy: "ProxyTokenizer".to_string(),
405            },
406            etpao: EtpaoSummary {
407                current_etpao: Some(750.0),
408                savings_rate_pct: etpao,
409                total_events: 12,
410                quality_gate: "PASS".to_string(),
411            },
412            overall_quality_grade: QualityGrade::BelowThreshold,
413        }
414    }
415}