ricecoder_modes/
ask_mode.rs

1//! Ask Mode implementation for question answering without file modifications
2
3use async_trait::async_trait;
4use std::time::Instant;
5
6use crate::error::Result;
7use crate::mode::Mode;
8use crate::models::{
9    Capability, ModeAction, ModeConfig, ModeConstraints, ModeContext, ModeResponse, Operation,
10};
11
12/// Ask Mode for question answering and explanations
13///
14/// Ask Mode provides capabilities for:
15/// - Question answering
16/// - Explanations and guidance
17/// - Code examples in responses
18/// - No file modifications or command execution
19#[derive(Debug, Clone)]
20pub struct AskMode {
21    config: ModeConfig,
22}
23
24impl AskMode {
25    /// Create a new Ask Mode instance
26    pub fn new() -> Self {
27        Self {
28            config: ModeConfig {
29                temperature: 0.7,
30                max_tokens: 2048,
31                system_prompt: "You are a helpful assistant. Answer questions clearly and \
32                    provide explanations when needed. Include code examples in responses."
33                    .to_string(),
34                capabilities: vec![Capability::QuestionAnswering, Capability::FreeformChat],
35                constraints: ModeConstraints {
36                    allow_file_operations: false,
37                    allow_command_execution: false,
38                    allow_code_generation: false,
39                    require_specs: false,
40                    auto_think_more_threshold: None,
41                },
42            },
43        }
44    }
45
46    /// Create an Ask Mode with custom configuration
47    pub fn with_config(config: ModeConfig) -> Self {
48        Self { config }
49    }
50
51    /// Answer a question clearly
52    ///
53    /// This method generates a clear answer to the provided question.
54    pub fn answer_question(&self, question: &str) -> Result<String> {
55        // For now, return a placeholder response
56        // In a real implementation, this would call an LLM
57        Ok(format!(
58            "Question: {}\n\nAnswer: This is a placeholder response. \
59            In a real implementation, this would be answered by an LLM.",
60            question
61        ))
62    }
63
64    /// Provide an explanation for a concept
65    ///
66    /// This method generates an explanation for the given concept.
67    pub fn explain_concept(&self, concept: &str) -> Result<String> {
68        Ok(format!(
69            "Explanation of '{}':\n\n\
70            This is a placeholder explanation. \
71            In a real implementation, this would provide a detailed explanation.",
72            concept
73        ))
74    }
75
76    /// Include code examples in a response
77    ///
78    /// This method adds code examples to a response.
79    pub fn include_code_examples(&self, response: &str, language: &str) -> Result<String> {
80        Ok(format!(
81            "{}\n\nCode example ({}):\n```{}\n// Example code\n```",
82            response, language, language
83        ))
84    }
85
86    /// Suggest an approach without executing it
87    ///
88    /// This method provides guidance on how to approach a problem.
89    pub fn suggest_approach(&self, problem: &str) -> Result<String> {
90        Ok(format!(
91            "Problem: {}\n\n\
92            Suggested approach:\n\
93            1. Analyze the problem\n\
94            2. Break it down into smaller parts\n\
95            3. Implement each part\n\
96            4. Test and validate\n\n\
97            Note: This is a general approach. Specific implementation details \
98            would depend on your particular use case.",
99            problem
100        ))
101    }
102
103    /// Validate that an operation is allowed in Ask Mode
104    ///
105    /// This method checks if an operation can be executed in Ask Mode.
106    /// Only question answering operations are allowed.
107    pub fn validate_operation(&self, operation: &Operation) -> Result<()> {
108        match operation {
109            Operation::AnswerQuestion => Ok(()),
110            Operation::GenerateCode => Err(crate::error::ModeError::OperationNotAllowed {
111                mode: self.id().to_string(),
112                operation: operation.to_string(),
113            }),
114            Operation::ModifyFile => Err(crate::error::ModeError::FileOperationBlocked),
115            Operation::ExecuteCommand => Err(crate::error::ModeError::OperationNotAllowed {
116                mode: self.id().to_string(),
117                operation: operation.to_string(),
118            }),
119            Operation::RunTests => Err(crate::error::ModeError::OperationNotAllowed {
120                mode: self.id().to_string(),
121                operation: operation.to_string(),
122            }),
123            Operation::ValidateQuality => Err(crate::error::ModeError::OperationNotAllowed {
124                mode: self.id().to_string(),
125                operation: operation.to_string(),
126            }),
127        }
128    }
129
130    /// Provide clear error message for blocked operations
131    ///
132    /// This method generates a user-friendly error message for blocked operations.
133    pub fn blocked_operation_message(&self, operation: &Operation) -> String {
134        match operation {
135            Operation::ModifyFile => "File operations are not allowed in Ask Mode. \
136                Switch to Code Mode if you need to modify files."
137                .to_string(),
138            Operation::ExecuteCommand => "Command execution is not allowed in Ask Mode. \
139                Switch to Code Mode if you need to execute commands."
140                .to_string(),
141            Operation::GenerateCode => "Code generation is not allowed in Ask Mode. \
142                Switch to Code Mode if you need to generate code."
143                .to_string(),
144            Operation::RunTests => "Test execution is not allowed in Ask Mode. \
145                Switch to Code Mode if you need to run tests."
146                .to_string(),
147            Operation::ValidateQuality => "Quality validation is not allowed in Ask Mode. \
148                Switch to Code Mode if you need to validate code quality."
149                .to_string(),
150            Operation::AnswerQuestion => {
151                "This operation should be allowed in Ask Mode.".to_string()
152            }
153        }
154    }
155}
156
157impl Default for AskMode {
158    fn default() -> Self {
159        Self::new()
160    }
161}
162
163#[async_trait]
164impl Mode for AskMode {
165    fn id(&self) -> &str {
166        "ask"
167    }
168
169    fn name(&self) -> &str {
170        "Ask Mode"
171    }
172
173    fn description(&self) -> &str {
174        "Question answering and explanations without file modifications"
175    }
176
177    fn system_prompt(&self) -> &str {
178        &self.config.system_prompt
179    }
180
181    async fn process(&self, input: &str, context: &ModeContext) -> Result<ModeResponse> {
182        let start = Instant::now();
183
184        // Create response with input as content
185        let mut response = ModeResponse::new(input.to_string(), self.id().to_string());
186
187        // Add question answering action
188        response.add_action(ModeAction::AskQuestion {
189            question: input.to_string(),
190        });
191
192        // Add suggestion to include code examples if appropriate
193        if input.contains("code") || input.contains("example") {
194            response.add_suggestion(
195                "I can provide code examples to illustrate the concept.".to_string(),
196            );
197        }
198
199        // Update metadata
200        response.metadata.duration = start.elapsed();
201        response.metadata.think_more_used = context.think_more_enabled;
202
203        Ok(response)
204    }
205
206    fn capabilities(&self) -> Vec<Capability> {
207        self.config.capabilities.clone()
208    }
209
210    fn config(&self) -> &ModeConfig {
211        &self.config
212    }
213
214    fn can_execute(&self, operation: &Operation) -> bool {
215        matches!(operation, Operation::AnswerQuestion)
216    }
217
218    fn constraints(&self) -> ModeConstraints {
219        self.config.constraints.clone()
220    }
221}
222
223#[cfg(test)]
224mod tests {
225    use super::*;
226
227    #[test]
228    fn test_ask_mode_creation() {
229        let mode = AskMode::new();
230        assert_eq!(mode.id(), "ask");
231        assert_eq!(mode.name(), "Ask Mode");
232    }
233
234    #[test]
235    fn test_ask_mode_capabilities() {
236        let mode = AskMode::new();
237        let capabilities = mode.capabilities();
238        assert!(capabilities.contains(&Capability::QuestionAnswering));
239        assert!(capabilities.contains(&Capability::FreeformChat));
240        assert!(!capabilities.contains(&Capability::CodeGeneration));
241        assert!(!capabilities.contains(&Capability::FileOperations));
242    }
243
244    #[test]
245    fn test_ask_mode_can_execute() {
246        let mode = AskMode::new();
247        assert!(mode.can_execute(&Operation::AnswerQuestion));
248        assert!(!mode.can_execute(&Operation::GenerateCode));
249        assert!(!mode.can_execute(&Operation::ModifyFile));
250        assert!(!mode.can_execute(&Operation::ExecuteCommand));
251        assert!(!mode.can_execute(&Operation::RunTests));
252        assert!(!mode.can_execute(&Operation::ValidateQuality));
253    }
254
255    #[test]
256    fn test_ask_mode_constraints() {
257        let mode = AskMode::new();
258        let constraints = mode.constraints();
259        assert!(!constraints.allow_file_operations);
260        assert!(!constraints.allow_command_execution);
261        assert!(!constraints.allow_code_generation);
262        assert!(!constraints.require_specs);
263        assert_eq!(constraints.auto_think_more_threshold, None);
264    }
265
266    #[test]
267    fn test_ask_mode_system_prompt() {
268        let mode = AskMode::new();
269        let prompt = mode.system_prompt();
270        assert!(prompt.contains("helpful assistant"));
271        assert!(prompt.contains("Answer questions"));
272        assert!(prompt.contains("code examples"));
273    }
274
275    #[tokio::test]
276    async fn test_ask_mode_process() {
277        let mode = AskMode::new();
278        let context = ModeContext::new("test-session".to_string());
279        let response = mode.process("What is Rust?", &context).await.unwrap();
280        assert_eq!(response.content, "What is Rust?");
281        assert_eq!(response.metadata.mode, "ask");
282        assert!(!response.metadata.think_more_used);
283        assert!(!response.actions.is_empty());
284    }
285
286    #[tokio::test]
287    async fn test_ask_mode_process_with_think_more() {
288        let mode = AskMode::new();
289        let mut context = ModeContext::new("test-session".to_string());
290        context.think_more_enabled = true;
291        let response = mode.process("What is Rust?", &context).await.unwrap();
292        assert!(response.metadata.think_more_used);
293    }
294
295    #[tokio::test]
296    async fn test_ask_mode_process_with_code_keyword() {
297        let mode = AskMode::new();
298        let context = ModeContext::new("test-session".to_string());
299        let response = mode
300            .process("Show me a code example", &context)
301            .await
302            .unwrap();
303        assert!(!response.suggestions.is_empty());
304    }
305
306    #[test]
307    fn test_ask_mode_default() {
308        let mode = AskMode::default();
309        assert_eq!(mode.id(), "ask");
310    }
311
312    #[test]
313    fn test_ask_mode_with_custom_config() {
314        let custom_config = ModeConfig {
315            temperature: 0.5,
316            max_tokens: 1024,
317            system_prompt: "Custom prompt".to_string(),
318            capabilities: vec![Capability::QuestionAnswering],
319            constraints: ModeConstraints {
320                allow_file_operations: false,
321                allow_command_execution: false,
322                allow_code_generation: false,
323                require_specs: false,
324                auto_think_more_threshold: None,
325            },
326        };
327        let mode = AskMode::with_config(custom_config);
328        assert_eq!(mode.config().temperature, 0.5);
329        assert_eq!(mode.config().max_tokens, 1024);
330    }
331
332    #[test]
333    fn test_answer_question() {
334        let mode = AskMode::new();
335        let question = "What is Rust?";
336        let result = mode.answer_question(question);
337        assert!(result.is_ok());
338        let answer = result.unwrap();
339        assert!(answer.contains("Question:"));
340        assert!(answer.contains(question));
341    }
342
343    #[test]
344    fn test_explain_concept() {
345        let mode = AskMode::new();
346        let concept = "ownership";
347        let result = mode.explain_concept(concept);
348        assert!(result.is_ok());
349        let explanation = result.unwrap();
350        assert!(explanation.contains("Explanation"));
351        assert!(explanation.contains(concept));
352    }
353
354    #[test]
355    fn test_include_code_examples() {
356        let mode = AskMode::new();
357        let response = "Here's how to do it";
358        let result = mode.include_code_examples(response, "rust");
359        assert!(result.is_ok());
360        let with_examples = result.unwrap();
361        assert!(with_examples.contains("Code example"));
362        assert!(with_examples.contains("rust"));
363        assert!(with_examples.contains("```"));
364    }
365
366    #[test]
367    fn test_suggest_approach() {
368        let mode = AskMode::new();
369        let problem = "How to parse JSON?";
370        let result = mode.suggest_approach(problem);
371        assert!(result.is_ok());
372        let approach = result.unwrap();
373        assert!(approach.contains("Problem:"));
374        assert!(approach.contains("Suggested approach"));
375        assert!(approach.contains("Analyze"));
376    }
377
378    #[test]
379    fn test_validate_operation_allowed() {
380        let mode = AskMode::new();
381        let result = mode.validate_operation(&Operation::AnswerQuestion);
382        assert!(result.is_ok());
383    }
384
385    #[test]
386    fn test_validate_operation_blocked_generate_code() {
387        let mode = AskMode::new();
388        let result = mode.validate_operation(&Operation::GenerateCode);
389        assert!(result.is_err());
390    }
391
392    #[test]
393    fn test_validate_operation_blocked_modify_file() {
394        let mode = AskMode::new();
395        let result = mode.validate_operation(&Operation::ModifyFile);
396        assert!(result.is_err());
397    }
398
399    #[test]
400    fn test_validate_operation_blocked_execute_command() {
401        let mode = AskMode::new();
402        let result = mode.validate_operation(&Operation::ExecuteCommand);
403        assert!(result.is_err());
404    }
405
406    #[test]
407    fn test_validate_operation_blocked_run_tests() {
408        let mode = AskMode::new();
409        let result = mode.validate_operation(&Operation::RunTests);
410        assert!(result.is_err());
411    }
412
413    #[test]
414    fn test_validate_operation_blocked_validate_quality() {
415        let mode = AskMode::new();
416        let result = mode.validate_operation(&Operation::ValidateQuality);
417        assert!(result.is_err());
418    }
419
420    #[test]
421    fn test_blocked_operation_message_modify_file() {
422        let mode = AskMode::new();
423        let message = mode.blocked_operation_message(&Operation::ModifyFile);
424        assert!(message.contains("File operations"));
425        assert!(message.contains("Ask Mode"));
426        assert!(message.contains("Code Mode"));
427    }
428
429    #[test]
430    fn test_blocked_operation_message_execute_command() {
431        let mode = AskMode::new();
432        let message = mode.blocked_operation_message(&Operation::ExecuteCommand);
433        assert!(message.contains("Command execution"));
434        assert!(message.contains("Ask Mode"));
435    }
436
437    #[test]
438    fn test_blocked_operation_message_generate_code() {
439        let mode = AskMode::new();
440        let message = mode.blocked_operation_message(&Operation::GenerateCode);
441        assert!(message.contains("Code generation"));
442        assert!(message.contains("Ask Mode"));
443    }
444
445    #[test]
446    fn test_blocked_operation_message_run_tests() {
447        let mode = AskMode::new();
448        let message = mode.blocked_operation_message(&Operation::RunTests);
449        assert!(message.contains("Test execution"));
450        assert!(message.contains("Ask Mode"));
451    }
452
453    #[test]
454    fn test_blocked_operation_message_validate_quality() {
455        let mode = AskMode::new();
456        let message = mode.blocked_operation_message(&Operation::ValidateQuality);
457        assert!(message.contains("Quality validation"));
458        assert!(message.contains("Ask Mode"));
459    }
460}