ricecoder_modes/
auto_enable.rs

1use crate::models::ComplexityLevel;
2
3/// Detects task complexity and determines if Think More should be auto-enabled
4#[derive(Debug, Clone)]
5pub struct ComplexityDetector {
6    /// Threshold for auto-enabling Think More
7    threshold: ComplexityLevel,
8}
9
10impl ComplexityDetector {
11    /// Create a new complexity detector with a specific threshold
12    pub fn new(threshold: ComplexityLevel) -> Self {
13        Self { threshold }
14    }
15
16    /// Create a detector with default threshold (Complex)
17    pub fn default_threshold() -> Self {
18        Self {
19            threshold: ComplexityLevel::Complex,
20        }
21    }
22
23    /// Detect complexity from task description
24    pub fn detect_complexity(&self, task_description: &str) -> ComplexityLevel {
25        let complexity_score = self.calculate_complexity_score(task_description);
26        self.score_to_complexity(complexity_score)
27    }
28
29    /// Calculate a complexity score based on various factors
30    pub fn calculate_complexity_score(&self, task_description: &str) -> f32 {
31        let mut score = 0.0;
32
33        // Factor 1: Length of description (longer = more complex)
34        let word_count = task_description.split_whitespace().count() as f32;
35        score += (word_count / 100.0).min(2.0);
36
37        // Factor 2: Presence of complexity keywords
38        let complexity_keywords = [
39            "complex",
40            "difficult",
41            "challenging",
42            "intricate",
43            "sophisticated",
44            "algorithm",
45            "optimization",
46            "performance",
47            "architecture",
48            "design",
49            "refactor",
50            "restructure",
51            "integrate",
52            "coordinate",
53            "orchestrate",
54            "analyze",
55            "debug",
56            "troubleshoot",
57            "investigate",
58            "research",
59            "multiple",
60            "various",
61            "several",
62            "many",
63            "numerous",
64            "concurrent",
65            "parallel",
66            "async",
67            "distributed",
68            "scalable",
69        ];
70
71        for keyword in &complexity_keywords {
72            if task_description.to_lowercase().contains(keyword) {
73                score += 1.0;
74            }
75        }
76
77        // Factor 3: Presence of uncertainty indicators
78        let uncertainty_keywords = [
79            "unclear",
80            "ambiguous",
81            "uncertain",
82            "unknown",
83            "not sure",
84            "might",
85            "could",
86            "possibly",
87            "perhaps",
88            "maybe",
89            "question",
90            "problem",
91            "issue",
92            "bug",
93            "error",
94        ];
95
96        for keyword in &uncertainty_keywords {
97            if task_description.to_lowercase().contains(keyword) {
98                score += 0.5;
99            }
100        }
101
102        // Factor 4: Presence of multiple requirements
103        let requirement_indicators = ["and", "also", "additionally", "furthermore", "moreover"];
104        let requirement_count = requirement_indicators
105            .iter()
106            .filter(|indicator| task_description.to_lowercase().contains(*indicator))
107            .count() as f32;
108        score += (requirement_count / 5.0).min(1.0);
109
110        // Factor 5: Presence of technical depth indicators
111        let technical_keywords = [
112            "algorithm",
113            "data structure",
114            "memory",
115            "performance",
116            "optimization",
117            "concurrency",
118            "threading",
119            "async",
120            "distributed",
121            "microservice",
122            "database",
123            "query",
124            "index",
125            "cache",
126            "transaction",
127        ];
128
129        for keyword in &technical_keywords {
130            if task_description.to_lowercase().contains(keyword) {
131                score += 1.5;
132            }
133        }
134
135        score
136    }
137
138    /// Convert complexity score to complexity level
139    fn score_to_complexity(&self, score: f32) -> ComplexityLevel {
140        match score {
141            s if s < 2.0 => ComplexityLevel::Simple,
142            s if s < 5.0 => ComplexityLevel::Moderate,
143            _ => ComplexityLevel::Complex,
144        }
145    }
146
147    /// Check if Think More should be auto-enabled for a given complexity
148    pub fn should_auto_enable(&self, complexity: ComplexityLevel) -> bool {
149        matches!(
150            (complexity, self.threshold),
151            (ComplexityLevel::Complex, _)
152                | (ComplexityLevel::Moderate, ComplexityLevel::Moderate)
153                | (ComplexityLevel::Moderate, ComplexityLevel::Simple)
154        )
155    }
156
157    /// Set the complexity threshold
158    pub fn set_threshold(&mut self, threshold: ComplexityLevel) {
159        self.threshold = threshold;
160    }
161
162    /// Get the current threshold
163    pub fn get_threshold(&self) -> ComplexityLevel {
164        self.threshold
165    }
166
167    /// Analyze task and return detailed complexity analysis
168    pub fn analyze_task(&self, task_description: &str) -> ComplexityAnalysis {
169        let complexity = self.detect_complexity(task_description);
170        let score = self.calculate_complexity_score(task_description);
171        let should_enable = self.should_auto_enable(complexity);
172
173        ComplexityAnalysis {
174            complexity,
175            score,
176            should_enable_think_more: should_enable,
177            reasoning: self.generate_reasoning(task_description, complexity, score),
178        }
179    }
180
181    /// Generate reasoning for the complexity assessment
182    fn generate_reasoning(
183        &self,
184        task_description: &str,
185        complexity: ComplexityLevel,
186        score: f32,
187    ) -> String {
188        let mut reasoning = String::new();
189
190        reasoning.push_str(&format!("Complexity Level: {:?}\n", complexity));
191        reasoning.push_str(&format!("Complexity Score: {:.2}\n", score));
192
193        // Identify contributing factors
194        let mut factors = Vec::new();
195
196        let word_count = task_description.split_whitespace().count();
197        if word_count > 100 {
198            factors.push(format!("Long description ({} words)", word_count));
199        }
200
201        let complexity_keywords = [
202            "complex",
203            "difficult",
204            "challenging",
205            "intricate",
206            "sophisticated",
207            "algorithm",
208            "optimization",
209            "performance",
210            "architecture",
211            "design",
212        ];
213        let found_keywords: Vec<&str> = complexity_keywords
214            .iter()
215            .filter(|kw| task_description.to_lowercase().contains(*kw))
216            .copied()
217            .collect();
218        if !found_keywords.is_empty() {
219            factors.push(format!(
220                "Complex keywords found: {}",
221                found_keywords.join(", ")
222            ));
223        }
224
225        let technical_keywords = [
226            "algorithm",
227            "data structure",
228            "memory",
229            "performance",
230            "optimization",
231            "concurrency",
232            "threading",
233            "async",
234            "distributed",
235        ];
236        let found_technical: Vec<&str> = technical_keywords
237            .iter()
238            .filter(|kw| task_description.to_lowercase().contains(*kw))
239            .copied()
240            .collect();
241        if !found_technical.is_empty() {
242            factors.push(format!("Technical depth: {}", found_technical.join(", ")));
243        }
244
245        if factors.is_empty() {
246            reasoning.push_str("Factors: Simple task with straightforward requirements\n");
247        } else {
248            reasoning.push_str("Factors:\n");
249            for factor in factors {
250                reasoning.push_str(&format!("  - {}\n", factor));
251            }
252        }
253
254        reasoning
255    }
256}
257
258impl Default for ComplexityDetector {
259    fn default() -> Self {
260        Self::default_threshold()
261    }
262}
263
264/// Detailed analysis of task complexity
265#[derive(Debug, Clone)]
266pub struct ComplexityAnalysis {
267    /// Detected complexity level
268    pub complexity: ComplexityLevel,
269    /// Complexity score (0.0 - infinity)
270    pub score: f32,
271    /// Whether Think More should be auto-enabled
272    pub should_enable_think_more: bool,
273    /// Reasoning for the assessment
274    pub reasoning: String,
275}
276
277#[cfg(test)]
278mod tests {
279    use super::*;
280
281    #[test]
282    fn test_detector_creation() {
283        let detector = ComplexityDetector::new(ComplexityLevel::Complex);
284        assert_eq!(detector.get_threshold(), ComplexityLevel::Complex);
285    }
286
287    #[test]
288    fn test_default_threshold() {
289        let detector = ComplexityDetector::default_threshold();
290        assert_eq!(detector.get_threshold(), ComplexityLevel::Complex);
291    }
292
293    #[test]
294    fn test_detect_simple_task() {
295        let detector = ComplexityDetector::default();
296        let complexity = detector.detect_complexity("Write a simple function");
297        assert_eq!(complexity, ComplexityLevel::Simple);
298    }
299
300    #[test]
301    fn test_detect_moderate_task() {
302        let detector = ComplexityDetector::default();
303        let complexity = detector.detect_complexity(
304            "Implement a sorting algorithm with optimization and performance considerations",
305        );
306        assert!(matches!(
307            complexity,
308            ComplexityLevel::Moderate | ComplexityLevel::Complex
309        ));
310    }
311
312    #[test]
313    fn test_detect_complex_task() {
314        let detector = ComplexityDetector::default();
315        let complexity = detector.detect_complexity(
316            "Design and implement a distributed concurrent system with complex algorithm optimization, \
317             performance tuning, and architectural considerations for scalability"
318        );
319        assert_eq!(complexity, ComplexityLevel::Complex);
320    }
321
322    #[test]
323    fn test_should_auto_enable_complex() {
324        let detector = ComplexityDetector::new(ComplexityLevel::Complex);
325        assert!(detector.should_auto_enable(ComplexityLevel::Complex));
326    }
327
328    #[test]
329    fn test_should_not_auto_enable_simple() {
330        let detector = ComplexityDetector::new(ComplexityLevel::Complex);
331        assert!(!detector.should_auto_enable(ComplexityLevel::Simple));
332    }
333
334    #[test]
335    fn test_should_auto_enable_moderate_with_moderate_threshold() {
336        let detector = ComplexityDetector::new(ComplexityLevel::Moderate);
337        assert!(detector.should_auto_enable(ComplexityLevel::Moderate));
338    }
339
340    #[test]
341    fn test_set_threshold() {
342        let mut detector = ComplexityDetector::new(ComplexityLevel::Complex);
343        detector.set_threshold(ComplexityLevel::Simple);
344        assert_eq!(detector.get_threshold(), ComplexityLevel::Simple);
345    }
346
347    #[test]
348    fn test_analyze_task() {
349        let detector = ComplexityDetector::default();
350        let analysis = detector.analyze_task("Write a complex distributed system");
351        assert!(!analysis.reasoning.is_empty());
352        assert!(analysis.score > 0.0);
353    }
354
355    #[test]
356    fn test_complexity_keywords_detection() {
357        let detector = ComplexityDetector::default();
358        let score1 = detector.calculate_complexity_score("Simple task");
359        let score2 = detector.calculate_complexity_score("Complex algorithm optimization");
360        assert!(score2 > score1);
361    }
362
363    #[test]
364    fn test_technical_keywords_detection() {
365        let detector = ComplexityDetector::default();
366        let score1 = detector.calculate_complexity_score("Write code");
367        let score2 = detector.calculate_complexity_score("Implement concurrent distributed system");
368        assert!(score2 > score1);
369    }
370
371    #[test]
372    fn test_length_factor() {
373        let detector = ComplexityDetector::default();
374        let short = "Do something";
375        let long = "Do something very complex and intricate with many considerations and requirements and factors";
376        let score1 = detector.calculate_complexity_score(short);
377        let score2 = detector.calculate_complexity_score(long);
378        assert!(score2 > score1);
379    }
380
381    #[test]
382    fn test_default_implementation() {
383        let detector = ComplexityDetector::default();
384        assert_eq!(detector.get_threshold(), ComplexityLevel::Complex);
385    }
386
387    #[test]
388    fn test_score_to_complexity_simple() {
389        let detector = ComplexityDetector::default();
390        let complexity = detector.score_to_complexity(1.0);
391        assert_eq!(complexity, ComplexityLevel::Simple);
392    }
393
394    #[test]
395    fn test_score_to_complexity_moderate() {
396        let detector = ComplexityDetector::default();
397        let complexity = detector.score_to_complexity(3.0);
398        assert_eq!(complexity, ComplexityLevel::Moderate);
399    }
400
401    #[test]
402    fn test_score_to_complexity_complex() {
403        let detector = ComplexityDetector::default();
404        let complexity = detector.score_to_complexity(10.0);
405        assert_eq!(complexity, ComplexityLevel::Complex);
406    }
407
408    #[test]
409    fn test_analysis_includes_reasoning() {
410        let detector = ComplexityDetector::default();
411        let analysis = detector.analyze_task("Implement complex algorithm");
412        assert!(analysis.reasoning.contains("Complexity Level"));
413        assert!(analysis.reasoning.contains("Complexity Score"));
414    }
415}