ricecoder_modes/
ask_mode.rs1use 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#[derive(Debug, Clone)]
20pub struct AskMode {
21 config: ModeConfig,
22}
23
24impl AskMode {
25 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 pub fn with_config(config: ModeConfig) -> Self {
48 Self { config }
49 }
50
51 pub fn answer_question(&self, question: &str) -> Result<String> {
55 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 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 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 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 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 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 let mut response = ModeResponse::new(input.to_string(), self.id().to_string());
186
187 response.add_action(ModeAction::AskQuestion {
189 question: input.to_string(),
190 });
191
192 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 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}