Skip to main content

lean_ctx/core/ocla/builtin/
efficiency_analyzer.rs

1//! BuiltinEfficiencyAnalyzer — computes ETPAO and duplication metrics.
2//!
3//! Wraps `core/mode_predictor.rs` behind the OCLA trait. Computes Effective
4//! Tokens Per Accepted Outcome (ETPAO) from a sample of compression results
5//! paired with acceptance signals.
6
7use crate::core::ocla::traits::{EfficiencyAnalyzer, OclaService};
8use crate::core::ocla::types::{
9    EfficiencyAnalysis, EfficiencySample, OclaCapability, OclaCapabilityKind, OclaResult,
10};
11use crate::core::{io_boundary, tokens};
12use std::path::Path;
13
14pub struct BuiltinEfficiencyAnalyzer;
15
16impl BuiltinEfficiencyAnalyzer {
17    pub fn new() -> Self {
18        Self
19    }
20}
21
22impl Default for BuiltinEfficiencyAnalyzer {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl OclaService for BuiltinEfficiencyAnalyzer {
29    fn capability(&self) -> OclaCapability {
30        OclaCapability::available(OclaCapabilityKind::EfficiencyAnalyzer)
31    }
32}
33
34impl EfficiencyAnalyzer for BuiltinEfficiencyAnalyzer {
35    fn analyze_efficiency(&self, sample: EfficiencySample) -> OclaResult<EfficiencyAnalysis> {
36        let original_tokens = measured_original_tokens(&sample);
37        let etpao = if sample.accepted == Some(true) && sample.delivered_tokens > 0 {
38            Some(sample.delivered_tokens.saturating_mul(1000) / original_tokens.max(1))
39        } else {
40            None
41        };
42
43        let compression_rate = if original_tokens > 0 {
44            let savings = original_tokens.saturating_sub(sample.delivered_tokens);
45            #[allow(clippy::cast_possible_truncation)]
46            let ratio = (savings.saturating_mul(1000) / original_tokens).min(1000) as u16;
47            ratio
48        } else {
49            0
50        };
51
52        #[allow(clippy::cast_possible_truncation)]
53        let cache_hit_rate = sample
54            .cache_hits
55            .saturating_mul(1000)
56            .checked_div(sample.cache_reads)
57            .unwrap_or(0)
58            .min(1000) as u16;
59
60        #[allow(clippy::cast_possible_truncation)]
61        let duplicate_ratio = sample
62            .cache_hits
63            .saturating_mul(1000)
64            .checked_div(sample.cache_reads)
65            .unwrap_or(0)
66            .min(1000) as u16;
67
68        let mut recommendation_refs = Vec::new();
69        if compression_rate < 300 {
70            recommendation_refs.push("increase_compression_aggressiveness".into());
71        }
72        if cache_hit_rate > 800 {
73            recommendation_refs.push("leverage_high_cache_hit_rate".into());
74        }
75        if matches!(etpao, Some(value) if value > 0 && value < 500) {
76            recommendation_refs.push("review_low_etpao_reads".into());
77        }
78
79        Ok(EfficiencyAnalysis {
80            etpao_milli: etpao,
81            duplicate_ratio_milli: duplicate_ratio,
82            compression_rate_milli: compression_rate,
83            cache_hit_rate_milli: cache_hit_rate,
84            recommendation_refs,
85        })
86    }
87}
88
89fn measured_original_tokens(sample: &EfficiencySample) -> u64 {
90    let path = sample
91        .context
92        .content_ref
93        .strip_prefix("file:")
94        .unwrap_or(&sample.context.content_ref);
95    if Path::new(path).is_file()
96        && let Ok(content) = io_boundary::read_file_lossy(path)
97    {
98        return tokens::count_tokens(&content) as u64;
99    }
100    sample.original_tokens
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106    use crate::core::ocla::types::OclaRequestContext;
107
108    fn sample(original: u64, delivered: u64, accepted: Option<bool>) -> EfficiencySample {
109        EfficiencySample {
110            context: OclaRequestContext {
111                request_id: "r1".into(),
112                session_id: "s1".into(),
113                agent_id: "agent-test".into(),
114                content_ref: "ref:test".into(),
115                tenant_id: None,
116                trace_id: "tr-unit".into(),
117            },
118            original_tokens: original,
119            delivered_tokens: delivered,
120            accepted,
121            cache_hits: 0,
122            cache_reads: 0,
123        }
124    }
125
126    #[test]
127    fn etpao_computed_when_accepted() {
128        let analyzer = BuiltinEfficiencyAnalyzer::new();
129        let result = analyzer
130            .analyze_efficiency(sample(1000, 300, Some(true)))
131            .unwrap();
132        assert_eq!(result.etpao_milli, Some(300));
133    }
134
135    #[test]
136    fn etpao_none_when_rejected() {
137        let analyzer = BuiltinEfficiencyAnalyzer::new();
138        let result = analyzer
139            .analyze_efficiency(sample(1000, 300, Some(false)))
140            .unwrap();
141        assert_eq!(result.etpao_milli, None);
142    }
143
144    #[test]
145    fn duplicate_ratio() {
146        let analyzer = BuiltinEfficiencyAnalyzer::new();
147        let mut input = sample(1000, 250, Some(true));
148        input.cache_hits = 3;
149        input.cache_reads = 4;
150
151        let result = analyzer.analyze_efficiency(input).unwrap();
152
153        assert_eq!(result.duplicate_ratio_milli, 750);
154        assert_eq!(result.compression_rate_milli, 750);
155    }
156
157    #[test]
158    fn recommendations_follow_efficiency_thresholds() {
159        let mut input = sample(1000, 800, Some(true));
160        input.cache_hits = 9;
161        input.cache_reads = 10;
162
163        let result = BuiltinEfficiencyAnalyzer::new()
164            .analyze_efficiency(input)
165            .unwrap();
166
167        assert_eq!(
168            result.recommendation_refs,
169            vec![
170                "increase_compression_aggressiveness",
171                "leverage_high_cache_hit_rate",
172            ]
173        );
174
175        let result = BuiltinEfficiencyAnalyzer::new()
176            .analyze_efficiency(sample(1000, 100, Some(true)))
177            .unwrap();
178        assert_eq!(result.recommendation_refs, vec!["review_low_etpao_reads"]);
179    }
180
181    #[test]
182    fn registry_with_builtins_analyzes_efficiency() {
183        let registry = crate::core::ocla::OclaRegistry::with_builtins();
184        let result = registry
185            .efficiency_analyzer
186            .analyze_efficiency(sample(1000, 200, Some(true)))
187            .unwrap();
188
189        assert_eq!(result.etpao_milli, Some(200));
190        assert_eq!(result.compression_rate_milli, 800);
191    }
192
193    #[test]
194    fn cache_hit_rate_uses_observed_reads() {
195        let analyzer = BuiltinEfficiencyAnalyzer::new();
196        let mut input = sample(1000, 400, Some(true));
197        input.cache_hits = 3;
198        input.cache_reads = 4;
199
200        let result = analyzer.analyze_efficiency(input).unwrap();
201
202        assert_eq!(result.cache_hit_rate_milli, 750);
203    }
204
205    #[test]
206    fn original_tokens_are_measured_from_file_content() {
207        let dir = tempfile::tempdir().unwrap();
208        let path = dir.path().join("sample.rs");
209        std::fs::write(&path, "hello world").unwrap();
210        let mut input = sample(1000, 1, Some(true));
211        input.context.content_ref = format!("file:{}", path.display());
212
213        let result = BuiltinEfficiencyAnalyzer::new()
214            .analyze_efficiency(input)
215            .unwrap();
216
217        assert_eq!(result.etpao_milli, Some(500));
218        assert_eq!(result.compression_rate_milli, 500);
219    }
220}