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 = read_file_with_context_sync(std::path::Path::new(path), "improvements config")?;
219 toml::from_str(&content).map_err(|e| anyhow::anyhow!("failed to parse config: {e}"))
220 }
221
222 pub fn to_file(&self, path: &str) -> anyhow::Result<()> {
224 let content = toml::to_string_pretty(self)?;
225 write_file_with_context_sync(std::path::Path::new(path), &content, "improvements config")?;
226 Ok(())
227 }
228
229 pub fn validate(&self) -> Result<(), String> {
231 if !(0.0..=1.0).contains(&self.similarity.min_similarity_threshold) {
233 return Err("min_similarity_threshold must be between 0.0 and 1.0".to_string());
234 }
235 if !(0.0..=1.0).contains(&self.similarity.high_similarity_threshold) {
236 return Err("high_similarity_threshold must be between 0.0 and 1.0".to_string());
237 }
238
239 if self.time_decay.decay_constant <= 0.0 {
241 return Err("decay_constant must be positive".to_string());
242 }
243 if self.time_decay.half_life_hours <= 0.0 {
244 return Err("half_life_hours must be positive".to_string());
245 }
246
247 if self.patterns.min_sequence_length < 2 {
249 return Err("min_sequence_length must be at least 2".to_string());
250 }
251 if !(0.0..=1.0).contains(&self.patterns.confidence_threshold) {
252 return Err("confidence_threshold must be between 0.0 and 1.0".to_string());
253 }
254
255 if self.context.max_context_tokens == 0 {
257 return Err("max_context_tokens must be positive".to_string());
258 }
259 if !(0.0..=100.0).contains(&self.context.truncation_threshold_percent) {
260 return Err("truncation_threshold_percent must be between 0.0 and 100.0".to_string());
261 }
262
263 Ok(())
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270
271 #[test]
272 fn test_default_config_is_valid() {
273 let config = ImprovementsConfig::default();
274 config.validate().unwrap();
275 }
276
277 #[test]
278 fn test_config_validation_similarity() {
279 let mut config = ImprovementsConfig::default();
280 config.similarity.min_similarity_threshold = 1.5;
281 assert!(config.validate().is_err());
282 }
283
284 #[test]
285 fn test_config_validation_decay() {
286 let mut config = ImprovementsConfig::default();
287 config.time_decay.decay_constant = -0.1;
288 assert!(config.validate().is_err());
289 }
290
291 #[test]
292 fn test_config_validation_pattern() {
293 let mut config = ImprovementsConfig::default();
294 config.patterns.min_sequence_length = 1;
295 assert!(config.validate().is_err());
296 }
297
298 #[test]
299 fn test_config_serialization() {
300 let config = ImprovementsConfig::default();
301 let toml_str = toml::to_string_pretty(&config).expect("serialization failed");
302 assert!(toml_str.contains("min_similarity_threshold"));
303 assert!(toml_str.contains("decay_constant"));
304 }
305}