Skip to main content

lean_ctx/core/shadow/
recommendation.rs

1//! LeanCTX treatment measurement and applied-optimization evidence.
2
3use crate::core::{
4    shadow::baseline::accepted,
5    value_gate::{OutcomeSignal, cost_tracker::calculate_cost},
6};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct ShadowTask {
10    pub task_id: String,
11    pub query: String,
12    pub raw_input_tokens: u64,
13    pub compressed_input_tokens: u64,
14    pub output_tokens: u64,
15    pub model_used: String,
16    pub outcome_signals: Vec<OutcomeSignal>,
17    pub duration_ms: u64,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct TreatmentMeasurement {
22    pub task_id: String,
23    pub input_tokens: u64,
24    pub output_tokens: u64,
25    pub model: String,
26    pub total_cost_micros: u64,
27    pub duration_ms: u64,
28    pub outcome_accepted: bool,
29    pub optimizations_applied: Vec<String>,
30}
31
32pub fn measure_treatment(task: &ShadowTask) -> TreatmentMeasurement {
33    let mut optimizations = Vec::new();
34    // Only context changes are LeanCTX optimizations; the model is the host's choice.
35    if task.compressed_input_tokens < task.raw_input_tokens {
36        optimizations.push("compression".into());
37    }
38    TreatmentMeasurement {
39        task_id: task.task_id.clone(),
40        input_tokens: task.compressed_input_tokens,
41        output_tokens: task.output_tokens,
42        model: task.model_used.clone(),
43        total_cost_micros: calculate_cost(
44            task.compressed_input_tokens,
45            task.output_tokens,
46            0,
47            &task.model_used,
48        ),
49        duration_ms: task.duration_ms,
50        outcome_accepted: accepted(&task.task_id, &task.outcome_signals),
51        optimizations_applied: optimizations,
52    }
53}