1use crate::utils::file_utils::{read_file_with_context_sync, write_file_with_context_sync};
7use serde::{Deserialize, Serialize};
8use std::time::Duration;
9
10#[derive(Debug, Clone, Serialize, Deserialize, Default)]
12pub struct ImprovementsConfig {
13 pub similarity: SimilarityConfig,
15
16 pub time_decay: TimeDecayConfig,
18
19 pub patterns: PatternConfig,
21
22 pub cache: CacheConfig,
24
25 pub context: ContextConfig,
27
28 pub fallback: FallbackConfig,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct SimilarityConfig {
35 pub min_similarity_threshold: f32,
37
38 pub high_similarity_threshold: f32,
40
41 pub argument_weight: f32,
43
44 pub return_type_weight: f32,
46
47 pub description_weight: f32,
49
50 pub success_history_weight: f32,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct TimeDecayConfig {
57 pub decay_constant: f32,
60
61 pub half_life_hours: f32,
63
64 pub minimum_score: f32,
66
67 pub recent_window_hours: f32,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct PatternConfig {
74 pub min_sequence_length: usize,
76
77 pub pattern_window_seconds: u64,
79
80 pub confidence_threshold: f32,
82
83 pub max_patterns: usize,
85
86 pub enable_advanced_detection: bool,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct CacheConfig {
93 pub max_entries: usize,
95
96 pub ttl: Duration,
98
99 pub enable_result_cache: bool,
101
102 pub enable_metadata_cache: bool,
104
105 pub enable_pattern_cache: bool,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct ContextConfig {
112 pub max_context_tokens: usize,
114
115 pub truncation_threshold_percent: f32,
117
118 pub enable_compaction: bool,
120
121 pub max_history_entries: usize,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct FallbackConfig {
128 pub max_attempts: usize,
130
131 pub backoff_multiplier: f32,
133
134 pub initial_backoff_ms: u64,
136
137 pub max_backoff_ms: u64,
139
140 pub enable_exponential_backoff: bool,
142}
143
144impl Default for SimilarityConfig {
145 fn default() -> Self {
146 Self {
147 min_similarity_threshold: 0.6,
148 high_similarity_threshold: 0.8,
149 argument_weight: 0.4,
150 return_type_weight: 0.3,
151 description_weight: 0.2,
152 success_history_weight: 0.1,
153 }
154 }
155}
156
157impl Default for TimeDecayConfig {
158 fn default() -> Self {
159 Self {
160 decay_constant: 0.1,
161 half_life_hours: 24.0,
162 minimum_score: 0.1,
163 recent_window_hours: 1.0,
164 }
165 }
166}
167
168impl Default for PatternConfig {
169 fn default() -> Self {
170 Self {
171 min_sequence_length: 3,
172 pattern_window_seconds: 300,
173 confidence_threshold: 0.75,
174 max_patterns: 100,
175 enable_advanced_detection: true,
176 }
177 }
178}
179
180impl Default for CacheConfig {
181 fn default() -> Self {
182 Self {
183 max_entries: 10_000,
184 ttl: Duration::from_secs(3600),
185 enable_result_cache: true,
186 enable_metadata_cache: true,
187 enable_pattern_cache: true,
188 }
189 }
190}
191
192impl Default for ContextConfig {
193 fn default() -> Self {
194 Self {
195 max_context_tokens: 100_000,
196 truncation_threshold_percent: 85.0,
197 enable_compaction: true,
198 max_history_entries: 100,
199 }
200 }
201}
202
203impl Default for FallbackConfig {
204 fn default() -> Self {
205 Self {
206 max_attempts: 3,
207 backoff_multiplier: 2.0,
208 initial_backoff_ms: 100,
209 max_backoff_ms: 5000,
210 enable_exponential_backoff: true,
211 }
212 }
213}
214
215impl ImprovementsConfig {
216 pub fn from_file(path: &str) -> anyhow::Result<Self> {
218 let content =
219 read_file_with_context_sync(std::path::Path::new(path), "improvements config")?;
220 toml::from_str(&content).map_err(|e| anyhow::anyhow!("failed to parse config: {}", e))
221 }
222
223 pub fn to_file(&self, path: &str) -> anyhow::Result<()> {
225 let content = toml::to_string_pretty(self)?;
226 write_file_with_context_sync(std::path::Path::new(path), &content, "improvements config")?;
227 Ok(())
228 }
229
230 pub fn validate(&self) -> Result<(), String> {
232 if !(0.0..=1.0).contains(&self.similarity.min_similarity_threshold) {
234 return Err("min_similarity_threshold must be between 0.0 and 1.0".to_string());
235 }
236 if !(0.0..=1.0).contains(&self.similarity.high_similarity_threshold) {
237 return Err("high_similarity_threshold must be between 0.0 and 1.0".to_string());
238 }
239
240 if self.time_decay.decay_constant <= 0.0 {
242 return Err("decay_constant must be positive".to_string());
243 }
244 if self.time_decay.half_life_hours <= 0.0 {
245 return Err("half_life_hours must be positive".to_string());
246 }
247
248 if self.patterns.min_sequence_length < 2 {
250 return Err("min_sequence_length must be at least 2".to_string());
251 }
252 if !(0.0..=1.0).contains(&self.patterns.confidence_threshold) {
253 return Err("confidence_threshold must be between 0.0 and 1.0".to_string());
254 }
255
256 if self.context.max_context_tokens == 0 {
258 return Err("max_context_tokens must be positive".to_string());
259 }
260 if !(0.0..=100.0).contains(&self.context.truncation_threshold_percent) {
261 return Err("truncation_threshold_percent must be between 0.0 and 100.0".to_string());
262 }
263
264 Ok(())
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn test_default_config_is_valid() {
274 let config = ImprovementsConfig::default();
275 config.validate().unwrap();
276 }
277
278 #[test]
279 fn test_config_validation_similarity() {
280 let mut config = ImprovementsConfig::default();
281 config.similarity.min_similarity_threshold = 1.5;
282 assert!(config.validate().is_err());
283 }
284
285 #[test]
286 fn test_config_validation_decay() {
287 let mut config = ImprovementsConfig::default();
288 config.time_decay.decay_constant = -0.1;
289 assert!(config.validate().is_err());
290 }
291
292 #[test]
293 fn test_config_validation_pattern() {
294 let mut config = ImprovementsConfig::default();
295 config.patterns.min_sequence_length = 1;
296 assert!(config.validate().is_err());
297 }
298
299 #[test]
300 fn test_config_serialization() {
301 let config = ImprovementsConfig::default();
302 let toml_str = toml::to_string_pretty(&config).expect("serialization failed");
303 assert!(toml_str.contains("min_similarity_threshold"));
304 assert!(toml_str.contains("decay_constant"));
305 }
306}