Skip to main content

vtcode_core/tools/
improvements_config.rs

1//! Configuration management for tool improvements system
2//!
3//! Defines all tunable parameters for similarity scoring, time decay,
4//! pattern detection, and cache behavior.
5
6use crate::utils::file_utils::{read_file_with_context_sync, write_file_with_context_sync};
7use serde::{Deserialize, Serialize};
8use std::time::Duration;
9
10/// Configuration for the improvements system
11#[derive(Debug, Clone, Serialize, Deserialize, Default)]
12pub struct ImprovementsConfig {
13    /// Similarity scoring configuration
14    pub similarity: SimilarityConfig,
15
16    /// Time decay configuration
17    pub time_decay: TimeDecayConfig,
18
19    /// Pattern detection configuration
20    pub patterns: PatternConfig,
21
22    /// Cache configuration
23    pub cache: CacheConfig,
24
25    /// Context management
26    pub context: ContextConfig,
27
28    /// Fallback chain configuration
29    pub fallback: FallbackConfig,
30}
31
32/// Similarity scoring thresholds and weights
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct SimilarityConfig {
35    /// Minimum similarity score (0.0-1.0) to consider tools related
36    pub min_similarity_threshold: f32,
37
38    /// Score considered "high similarity" (0.0-1.0)
39    pub high_similarity_threshold: f32,
40
41    /// Weight for argument similarity (0.0-1.0)
42    pub argument_weight: f32,
43
44    /// Weight for return type similarity (0.0-1.0)
45    pub return_type_weight: f32,
46
47    /// Weight for description similarity (0.0-1.0)
48    pub description_weight: f32,
49
50    /// Weight for recent success (0.0-1.0)
51    pub success_history_weight: f32,
52}
53
54/// Time decay configuration for effectiveness scores
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct TimeDecayConfig {
57    /// Decay constant (lambda in exp(-lambda * age_hours))
58    /// Higher values = faster decay
59    pub decay_constant: f32,
60
61    /// Age at which score drops to 50% (hours)
62    pub half_life_hours: f32,
63
64    /// Minimum score after decay (prevents dropping to zero)
65    pub minimum_score: f32,
66
67    /// Window for considering recent successes (hours)
68    pub recent_window_hours: f32,
69}
70
71/// Pattern detection configuration
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct PatternConfig {
74    /// Minimum tools in sequence to detect pattern
75    pub min_sequence_length: usize,
76
77    /// Time window for detecting patterns (seconds)
78    pub pattern_window_seconds: u64,
79
80    /// Confidence threshold for pattern detection
81    pub confidence_threshold: f32,
82
83    /// Maximum number of patterns to track
84    pub max_patterns: usize,
85
86    /// Enable advanced pattern detection (ML-ready)
87    pub enable_advanced_detection: bool,
88}
89
90/// Cache configuration
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct CacheConfig {
93    /// Maximum cache size (number of entries)
94    pub max_entries: usize,
95
96    /// Cache entry time-to-live
97    pub ttl: Duration,
98
99    /// Enable result caching
100    pub enable_result_cache: bool,
101
102    /// Enable metadata caching
103    pub enable_metadata_cache: bool,
104
105    /// Enable pattern cache
106    pub enable_pattern_cache: bool,
107}
108
109/// Context management configuration
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct ContextConfig {
112    /// Maximum context window size (tokens)
113    pub max_context_tokens: usize,
114
115    /// Threshold for context truncation (% full)
116    pub truncation_threshold_percent: f32,
117
118    /// Enable aggressive context compaction
119    pub enable_compaction: bool,
120
121    /// Maximum history to retain
122    pub max_history_entries: usize,
123}
124
125/// Fallback chain configuration
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct FallbackConfig {
128    /// Maximum fallback attempts
129    pub max_attempts: usize,
130
131    /// Backoff multiplier between retries
132    pub backoff_multiplier: f32,
133
134    /// Initial backoff duration
135    pub initial_backoff_ms: u64,
136
137    /// Maximum backoff duration
138    pub max_backoff_ms: u64,
139
140    /// Enable exponential backoff
141    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    /// Load configuration from TOML file
217    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    /// Save configuration to TOML file
224    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    /// Validate configuration values
231    pub fn validate(&self) -> Result<(), String> {
232        // Similarity config validation
233        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        // Time decay validation
241        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        // Pattern validation
249        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        // Context validation
257        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}