lean_ctx/core/context_ledger/
reinjection.rs1use super::helpers::GWT_IGNITION_Z;
2use super::types::{ContextLedger, LedgerEntry};
3
4#[derive(Debug, Clone)]
5pub struct ReinjectionAction {
6 pub path: String,
7 pub current_mode: String,
8 pub new_mode: String,
9 pub tokens_freed: usize,
10}
11
12#[derive(Debug, Clone)]
13pub struct ReinjectionPlan {
14 pub actions: Vec<ReinjectionAction>,
15 pub total_tokens_freed: usize,
16 pub new_utilization: f64,
17}
18
19impl ContextLedger {
20 pub fn reinjection_plan(
21 &self,
22 intent: &super::super::intent_engine::StructuredIntent,
23 target_utilization: f64,
24 ) -> ReinjectionPlan {
25 let current_util = self.total_tokens_sent as f64 / self.window_size as f64;
26 if current_util <= target_utilization {
27 return ReinjectionPlan {
28 actions: Vec::new(),
29 total_tokens_freed: 0,
30 new_utilization: current_util,
31 };
32 }
33
34 let tokens_to_free =
35 self.total_tokens_sent - (self.window_size as f64 * target_utilization) as usize;
36
37 let target_set: std::collections::HashSet<&str> = intent
38 .targets
39 .iter()
40 .map(std::string::String::as_str)
41 .collect();
42
43 let mut candidates: Vec<(usize, &LedgerEntry)> = self
44 .entries
45 .iter()
46 .enumerate()
47 .filter(|(_, e)| !target_set.iter().any(|t| e.path.contains(t)))
48 .collect();
49
50 candidates.sort_by(|a, b| {
51 let a_phi = a.1.phi.unwrap_or(0.0);
52 let b_phi = b.1.phi.unwrap_or(0.0);
53 a_phi
54 .partial_cmp(&b_phi)
55 .unwrap_or_else(|| a.1.timestamp.cmp(&b.1.timestamp))
56 });
57
58 let mut actions = Vec::new();
59 let mut freed = 0usize;
60
61 for (_, entry) in &candidates {
62 if freed >= tokens_to_free {
63 break;
64 }
65 if let Some((new_mode, new_tokens)) = downgrade_mode(&entry.mode, entry.sent_tokens) {
66 let saving = entry.sent_tokens.saturating_sub(new_tokens);
67 if saving > 0 {
68 actions.push(ReinjectionAction {
69 path: entry.path.clone(),
70 current_mode: entry.mode.clone(),
71 new_mode,
72 tokens_freed: saving,
73 });
74 freed += saving;
75 }
76 }
77 }
78
79 let new_sent = self.total_tokens_sent.saturating_sub(freed);
80 let new_utilization = new_sent as f64 / self.window_size as f64;
81
82 ReinjectionPlan {
83 actions,
84 total_tokens_freed: freed,
85 new_utilization,
86 }
87 }
88}
89
90pub(super) fn downgrade_mode(current_mode: &str, current_tokens: usize) -> Option<(String, usize)> {
91 match current_mode {
92 "full" => Some(("signatures".to_string(), current_tokens / 5)),
93 "aggressive" => Some(("signatures".to_string(), current_tokens / 3)),
94 "signatures" => Some(("map".to_string(), current_tokens / 2)),
95 "map" => Some(("reference".to_string(), current_tokens / 4)),
96 _ => None,
97 }
98}
99
100pub(super) fn ignition_z_threshold() -> f64 {
104 std::env::var("LEAN_CTX_GWT_IGNITION_Z")
105 .ok()
106 .and_then(|v| v.trim().parse::<f64>().ok())
107 .filter(|v| *v > 0.0)
108 .unwrap_or(GWT_IGNITION_Z)
109}
110
111impl Default for ContextLedger {
112 fn default() -> Self {
113 Self::new()
114 }
115}