lean_ctx/core/config/
memory.rs1use serde::{Deserialize, Serialize};
4
5use super::Config;
6
7#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
11#[serde(rename_all = "lowercase")]
12pub enum MemoryCleanup {
13 Aggressive,
14 #[default]
15 Shared,
16}
17
18impl MemoryCleanup {
19 pub fn from_env() -> Option<Self> {
20 std::env::var("LEAN_CTX_MEMORY_CLEANUP").ok().and_then(|v| {
21 match v.trim().to_lowercase().as_str() {
22 "aggressive" => Some(Self::Aggressive),
23 "shared" => Some(Self::Shared),
24 _ => None,
25 }
26 })
27 }
28
29 pub fn effective(config: &Config) -> Self {
30 if let Some(env_val) = Self::from_env() {
31 return env_val;
32 }
33 config.memory_cleanup.clone()
34 }
35
36 pub fn idle_ttl_secs(&self) -> u64 {
38 match self {
39 Self::Aggressive => 300,
40 Self::Shared => 3600,
41 }
42 }
43
44 pub fn index_retention_multiplier(&self) -> f64 {
46 match self {
47 Self::Aggressive => 1.0,
48 Self::Shared => 3.0,
49 }
50 }
51}
52
53#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
58#[serde(rename_all = "lowercase")]
59pub enum MemoryProfile {
60 Low,
61 Balanced,
62 #[default]
63 Performance,
64}
65
66impl MemoryProfile {
67 pub fn from_env() -> Option<Self> {
68 std::env::var("LEAN_CTX_MEMORY_PROFILE").ok().and_then(|v| {
69 match v.trim().to_lowercase().as_str() {
70 "low" => Some(Self::Low),
71 "balanced" => Some(Self::Balanced),
72 "performance" => Some(Self::Performance),
73 _ => None,
74 }
75 })
76 }
77
78 pub fn effective(config: &Config) -> Self {
79 if let Some(env_val) = Self::from_env() {
80 return env_val;
81 }
82 config.memory_profile.clone()
83 }
84
85 pub fn bm25_max_cache_mb(&self) -> u64 {
86 match self {
87 Self::Low => 64,
88 Self::Balanced => 128,
89 Self::Performance => 512,
90 }
91 }
92
93 pub fn semantic_cache_enabled(&self) -> bool {
94 !matches!(self, Self::Low)
95 }
96
97 pub fn embeddings_enabled(&self) -> bool {
98 !matches!(self, Self::Low)
99 }
100}
101
102#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
110#[serde(rename_all = "lowercase")]
111pub enum SavingsFooter {
112 Auto,
113 Always,
114 #[default]
115 Never,
116}
117
118impl SavingsFooter {
119 pub fn from_env() -> Option<Self> {
120 std::env::var("LEAN_CTX_SAVINGS_FOOTER").ok().and_then(|v| {
121 match v.trim().to_lowercase().as_str() {
122 "auto" => Some(Self::Auto),
123 "always" => Some(Self::Always),
124 "never" => Some(Self::Never),
125 _ => None,
126 }
127 })
128 }
129
130 pub fn effective() -> Self {
131 if let Some(env_val) = Self::from_env() {
132 return env_val;
133 }
134 let cfg = super::Config::load();
135 cfg.savings_footer.clone()
136 }
137}
138
139#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
145#[serde(rename_all = "lowercase")]
146pub enum CompressionAnnotation {
147 Full,
148 #[default]
149 Quantized,
150 None,
151}
152
153impl CompressionAnnotation {
154 pub fn effective() -> Self {
155 if let Ok(v) = std::env::var("LEAN_CTX_COMPRESSION_ANNOTATION") {
156 return match v.trim().to_lowercase().as_str() {
157 "full" => Self::Full,
158 "quantized" => Self::Quantized,
159 "none" => Self::None,
160 _ => Self::default(),
161 };
162 }
163 let cfg = super::Config::load();
164 cfg.compression_annotation.clone()
165 }
166}
167
168pub struct MemoryGuardConfig {
170 pub max_ram_percent: u8,
171}
172
173impl MemoryGuardConfig {
174 pub fn effective(config: &Config) -> Self {
175 let base_pct = std::env::var("LEAN_CTX_MAX_RAM_PERCENT")
176 .ok()
177 .and_then(|v| v.parse::<u8>().ok())
178 .unwrap_or(config.max_ram_percent)
179 .clamp(1, 50);
180 let profile = MemoryProfile::effective(config);
183 let pct = if profile == MemoryProfile::Low {
184 (base_pct / 2).max(3)
185 } else {
186 base_pct
187 };
188 Self {
189 max_ram_percent: pct,
190 }
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[test]
199 fn savings_footer_defaults_to_never() {
200 assert_eq!(SavingsFooter::default(), SavingsFooter::Never);
201 }
202
203 #[test]
204 fn savings_footer_from_env_accepts_auto() {
205 let _guard = crate::core::data_dir::test_env_lock();
206 crate::test_env::set_var("LEAN_CTX_SAVINGS_FOOTER", "auto");
207 assert_eq!(SavingsFooter::from_env(), Some(SavingsFooter::Auto));
208 crate::test_env::remove_var("LEAN_CTX_SAVINGS_FOOTER");
209 }
210}