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 = 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    /// Save configuration to TOML file
223    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    /// Validate configuration values
230    pub fn validate(&self) -> Result<(), String> {
231        // Similarity config validation
232        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        // Time decay validation
240        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        // Pattern validation
248        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        // Context validation
256        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}