Skip to main content

lean_ctx/core/ocla/builtin/
response_optimizer.rs

1//! BuiltinResponseOptimizer — response dedup and cache via OCLA trait.
2//!
3//! Wraps `proxy/response_optimizer.rs` behind the canonical trait interface.
4//! Emits ResponseOptimized events to OclaBus. The actual cache and dedup
5//! logic is delegated to the existing optimizer; this provides the trait seam.
6
7use crate::core::ocla::traits::{OclaService, ResponseOptimizer};
8use crate::core::ocla::types::{
9    OclaCapability, OclaCapabilityKind, OclaResult, ResponseOptimizationRequest,
10    ResponseOptimizationResult,
11};
12use crate::core::ocla_bus::{self, OclaEvent};
13
14pub struct BuiltinResponseOptimizer;
15
16impl BuiltinResponseOptimizer {
17    pub fn new() -> Self {
18        Self
19    }
20}
21
22impl Default for BuiltinResponseOptimizer {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl OclaService for BuiltinResponseOptimizer {
29    fn capability(&self) -> OclaCapability {
30        OclaCapability::available(OclaCapabilityKind::ResponseOptimizer)
31    }
32}
33
34impl ResponseOptimizer for BuiltinResponseOptimizer {
35    fn optimize_response(
36        &self,
37        request: ResponseOptimizationRequest,
38    ) -> OclaResult<ResponseOptimizationResult> {
39        let decision = crate::proxy::response_optimizer::optimize_response(&request);
40
41        ocla_bus::emit(OclaEvent::ResponseOptimized {
42            cache_hit: decision.cache_hit,
43            is_duplicate: decision.is_duplicate,
44            tokens_saved: decision.tokens_saved,
45        });
46
47        Ok(ResponseOptimizationResult {
48            response_ref: request.response_ref.clone(),
49            delivered_tokens: delivered_tokens(&request, &decision),
50            recovery_ref: decision
51                .cache_hit
52                .then(|| format!("cache:{:016x}", decision.cache_key)),
53        })
54    }
55}
56
57fn delivered_tokens(
58    request: &ResponseOptimizationRequest,
59    decision: &crate::proxy::response_optimizer::OptimizationDecision,
60) -> u64 {
61    if decision.cache_hit {
62        return 0;
63    }
64
65    let original = request.original_tokens;
66    let target = request.target_tokens.min(original);
67    if decision.is_duplicate {
68        let dedup_factor = target;
69        return original
70            .saturating_mul(dedup_factor)
71            .checked_div(original.max(1))
72            .unwrap_or(dedup_factor);
73    }
74
75    target
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81    use crate::core::ocla::types::OclaRequestContext;
82
83    /// `tag` keys the request to one test. The optimizer keeps a process-wide
84    /// per-session cache, so two tests sharing a session id + response ref see
85    /// each other's entries: whichever ran second got a cache hit and zero
86    /// delivered tokens, which only showed up once tests ran in parallel and
87    /// the order stopped being fixed.
88    fn req(tag: &str, original: u64, target: u64) -> ResponseOptimizationRequest {
89        ResponseOptimizationRequest {
90            context: OclaRequestContext {
91                request_id: "r1".into(),
92                session_id: format!("s1-{tag}"),
93                agent_id: "agent-test".into(),
94                content_ref: "ref:test".into(),
95                tenant_id: None,
96                trace_id: "tr-unit".into(),
97            },
98            response_ref: format!("resp:{tag}"),
99            original_tokens: original,
100            target_tokens: target,
101        }
102    }
103
104    #[test]
105    fn optimization_caps_at_target() {
106        // optimize_response appends a `proxy_response_optimizer` event to the
107        // savings ledger. Without an isolated data dir that write lands in
108        // whatever LEAN_CTX_DATA_DIR currently points at — under parallel tests
109        // that is another test's isolated dir, whose ledger assertions then see
110        // a foreign event.
111        let _iso = crate::core::data_dir::isolated_data_dir();
112        let opt = BuiltinResponseOptimizer::new();
113        let result = opt.optimize_response(req("caps", 1000, 400)).unwrap();
114        assert_eq!(result.delivered_tokens, 400);
115    }
116
117    #[test]
118    fn preserves_response_ref() {
119        // See `optimization_caps_at_target`: keeps this test's ledger write out
120        // of another test's isolated data dir.
121        let _iso = crate::core::data_dir::isolated_data_dir();
122        let opt = BuiltinResponseOptimizer::new();
123        let result = opt.optimize_response(req("preserves", 500, 300)).unwrap();
124        assert_eq!(result.response_ref, "resp:preserves");
125    }
126
127    #[test]
128    fn registry_path_reports_cache_as_zero_delivery() {
129        // See `optimization_caps_at_target`: keeps this test's ledger write out
130        // of another test's isolated data dir.
131        let _iso = crate::core::data_dir::isolated_data_dir();
132        let registry = crate::core::ocla::registry::OclaRegistry::with_builtins();
133        let mut request = req("registry", 1000, 400);
134        request.context.session_id = "registry-response-optimizer".into();
135        request.response_ref = "resp:registry-response-optimizer".into();
136        let first = registry
137            .response_optimizer
138            .optimize_response(request.clone())
139            .unwrap();
140        let cached = registry
141            .response_optimizer
142            .optimize_response(request)
143            .unwrap();
144
145        assert_eq!(first.delivered_tokens, 400);
146        assert_eq!(cached.delivered_tokens, 0);
147    }
148
149    #[test]
150    fn duplicate_delivery_uses_target_ratio() {
151        let request = req("duplicate", 1000, 250);
152        let decision = crate::proxy::response_optimizer::OptimizationDecision {
153            cache_hit: false,
154            is_duplicate: true,
155            cache_key: 0,
156            tokens_saved: 750,
157            source: crate::proxy::response_optimizer::OptimizationSource::Dedup,
158        };
159
160        assert_eq!(delivered_tokens(&request, &decision), 250);
161    }
162}