ricecoder_modes/
models.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::path::PathBuf;
4use std::time::{Duration, SystemTime};
5
6/// Represents a capability that a mode can have
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub enum Capability {
9    /// Code generation capability
10    CodeGeneration,
11    /// Code modification capability
12    CodeModification,
13    /// File operations capability
14    FileOperations,
15    /// Command execution capability
16    CommandExecution,
17    /// Test execution capability
18    TestExecution,
19    /// Quality validation capability
20    QualityValidation,
21    /// Question answering capability
22    QuestionAnswering,
23    /// Freeform chat capability
24    FreeformChat,
25    /// Spec conversion capability
26    SpecConversion,
27}
28
29impl std::fmt::Display for Capability {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        match self {
32            Capability::CodeGeneration => write!(f, "CodeGeneration"),
33            Capability::CodeModification => write!(f, "CodeModification"),
34            Capability::FileOperations => write!(f, "FileOperations"),
35            Capability::CommandExecution => write!(f, "CommandExecution"),
36            Capability::TestExecution => write!(f, "TestExecution"),
37            Capability::QualityValidation => write!(f, "QualityValidation"),
38            Capability::QuestionAnswering => write!(f, "QuestionAnswering"),
39            Capability::FreeformChat => write!(f, "FreeformChat"),
40            Capability::SpecConversion => write!(f, "SpecConversion"),
41        }
42    }
43}
44
45/// Represents an operation that can be validated against mode constraints
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
47pub enum Operation {
48    /// Generate code operation
49    GenerateCode,
50    /// Modify file operation
51    ModifyFile,
52    /// Execute command operation
53    ExecuteCommand,
54    /// Run tests operation
55    RunTests,
56    /// Validate quality operation
57    ValidateQuality,
58    /// Answer question operation
59    AnswerQuestion,
60}
61
62impl std::fmt::Display for Operation {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        match self {
65            Operation::GenerateCode => write!(f, "GenerateCode"),
66            Operation::ModifyFile => write!(f, "ModifyFile"),
67            Operation::ExecuteCommand => write!(f, "ExecuteCommand"),
68            Operation::RunTests => write!(f, "RunTests"),
69            Operation::ValidateQuality => write!(f, "ValidateQuality"),
70            Operation::AnswerQuestion => write!(f, "AnswerQuestion"),
71        }
72    }
73}
74
75/// Constraints that apply to a mode
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct ModeConstraints {
78    /// Whether file operations are allowed
79    pub allow_file_operations: bool,
80    /// Whether command execution is allowed
81    pub allow_command_execution: bool,
82    /// Whether code generation is allowed
83    pub allow_code_generation: bool,
84    /// Whether specs are required
85    pub require_specs: bool,
86    /// Complexity threshold for auto-enabling Think More
87    pub auto_think_more_threshold: Option<ComplexityLevel>,
88}
89
90/// Complexity level for auto-enabling Think More
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
92pub enum ComplexityLevel {
93    /// Simple complexity level
94    Simple,
95    /// Moderate complexity level
96    Moderate,
97    /// Complex complexity level
98    Complex,
99}
100
101/// Configuration for Think More extended thinking
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct ThinkMoreConfig {
104    /// Whether Think More is enabled
105    pub enabled: bool,
106    /// Depth of thinking
107    pub depth: ThinkingDepth,
108    /// Timeout for thinking
109    pub timeout: Duration,
110    /// Whether to auto-enable based on complexity
111    pub auto_enable: bool,
112}
113
114impl Default for ThinkMoreConfig {
115    fn default() -> Self {
116        Self {
117            enabled: false,
118            depth: ThinkingDepth::Medium,
119            timeout: Duration::from_secs(30),
120            auto_enable: false,
121        }
122    }
123}
124
125/// Depth of thinking for extended reasoning
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
127pub enum ThinkingDepth {
128    /// Light thinking depth
129    Light,
130    /// Medium thinking depth
131    Medium,
132    /// Deep thinking depth
133    Deep,
134}
135
136/// Configuration for a mode
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct ModeConfig {
139    /// Temperature for response generation
140    pub temperature: f32,
141    /// Maximum tokens for response
142    pub max_tokens: usize,
143    /// System prompt for the mode
144    pub system_prompt: String,
145    /// Capabilities available in this mode
146    pub capabilities: Vec<Capability>,
147    /// Constraints for this mode
148    pub constraints: ModeConstraints,
149}
150
151/// A message in the conversation history
152#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct Message {
154    /// Role of the message sender
155    pub role: MessageRole,
156    /// Content of the message
157    pub content: String,
158    /// Timestamp when the message was created
159    pub timestamp: SystemTime,
160}
161
162/// Role of a message sender
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
164pub enum MessageRole {
165    /// User message
166    User,
167    /// Assistant message
168    Assistant,
169    /// System message
170    System,
171}
172
173/// Context for mode execution
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct ModeContext {
176    /// Session ID
177    pub session_id: String,
178    /// Project path
179    pub project_path: Option<PathBuf>,
180    /// Whether Think More is enabled
181    pub think_more_enabled: bool,
182    /// Think More configuration
183    pub think_more_config: ThinkMoreConfig,
184    /// Conversation history
185    pub conversation_history: Vec<Message>,
186    /// Custom context data
187    pub custom: HashMap<String, serde_json::Value>,
188}
189
190impl ModeContext {
191    /// Create a new mode context
192    pub fn new(session_id: String) -> Self {
193        Self {
194            session_id,
195            project_path: None,
196            think_more_enabled: false,
197            think_more_config: ThinkMoreConfig::default(),
198            conversation_history: Vec::new(),
199            custom: HashMap::new(),
200        }
201    }
202
203    /// Add a message to the conversation history
204    pub fn add_message(&mut self, role: MessageRole, content: String) {
205        self.conversation_history.push(Message {
206            role,
207            content,
208            timestamp: SystemTime::now(),
209        });
210    }
211}
212
213/// An action that a mode can perform
214#[derive(Debug, Clone, Serialize, Deserialize)]
215pub enum ModeAction {
216    /// Generate code from a specification
217    GenerateCode {
218        /// The specification
219        spec: String,
220    },
221    /// Modify a file
222    ModifyFile {
223        /// Path to the file
224        path: PathBuf,
225        /// Diff to apply
226        diff: String,
227    },
228    /// Run a command
229    RunCommand {
230        /// The command to run
231        command: String,
232    },
233    /// Run tests
234    RunTests {
235        /// Paths to test
236        paths: Vec<PathBuf>,
237    },
238    /// Validate quality
239    ValidateQuality {
240        /// Paths to validate
241        paths: Vec<PathBuf>,
242    },
243    /// Ask a question
244    AskQuestion {
245        /// The question
246        question: String,
247    },
248    /// Suggest a mode
249    SuggestMode {
250        /// The mode to suggest
251        mode: String,
252        /// Reason for suggestion
253        reason: String,
254    },
255    /// Display thinking content
256    DisplayThinking {
257        /// The thinking content
258        content: String,
259    },
260}
261
262/// Summary of changes made
263#[derive(Debug, Clone, Default, Serialize, Deserialize)]
264pub struct ChangeSummary {
265    /// Number of files created
266    pub files_created: usize,
267    /// Number of files modified
268    pub files_modified: usize,
269    /// Number of tests run
270    pub tests_run: usize,
271    /// Number of tests passed
272    pub tests_passed: usize,
273    /// Quality issues found
274    pub quality_issues: Vec<String>,
275}
276
277/// Metadata about a response
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct ResponseMetadata {
280    /// The mode that generated the response
281    pub mode: String,
282    /// Whether Think More was used
283    pub think_more_used: bool,
284    /// Thinking content if Think More was used
285    pub thinking_content: Option<String>,
286    /// Number of tokens used
287    pub tokens_used: usize,
288    /// Duration of processing
289    pub duration: Duration,
290    /// Summary of changes made
291    pub changes_summary: Option<ChangeSummary>,
292}
293
294/// Response from a mode
295#[derive(Debug, Clone, Serialize, Deserialize)]
296pub struct ModeResponse {
297    /// Response content
298    pub content: String,
299    /// Actions to perform
300    pub actions: Vec<ModeAction>,
301    /// Suggestions for the user
302    pub suggestions: Vec<String>,
303    /// Response metadata
304    pub metadata: ResponseMetadata,
305    /// Summary of changes
306    pub summary: Option<String>,
307}
308
309impl ModeResponse {
310    /// Create a new mode response
311    pub fn new(content: String, mode: String) -> Self {
312        Self {
313            content,
314            actions: Vec::new(),
315            suggestions: Vec::new(),
316            metadata: ResponseMetadata {
317                mode,
318                think_more_used: false,
319                thinking_content: None,
320                tokens_used: 0,
321                duration: Duration::from_secs(0),
322                changes_summary: None,
323            },
324            summary: None,
325        }
326    }
327
328    /// Add an action to the response
329    pub fn add_action(&mut self, action: ModeAction) {
330        self.actions.push(action);
331    }
332
333    /// Add a suggestion to the response
334    pub fn add_suggestion(&mut self, suggestion: String) {
335        self.suggestions.push(suggestion);
336    }
337}
338
339#[cfg(test)]
340mod tests {
341    use super::*;
342
343    #[test]
344    fn test_mode_context_creation() {
345        let context = ModeContext::new("test-session".to_string());
346        assert_eq!(context.session_id, "test-session");
347        assert!(context.project_path.is_none());
348        assert!(!context.think_more_enabled);
349        assert!(context.conversation_history.is_empty());
350    }
351
352    #[test]
353    fn test_mode_context_add_message() {
354        let mut context = ModeContext::new("test-session".to_string());
355        context.add_message(MessageRole::User, "Hello".to_string());
356        assert_eq!(context.conversation_history.len(), 1);
357        assert_eq!(context.conversation_history[0].role, MessageRole::User);
358        assert_eq!(context.conversation_history[0].content, "Hello");
359    }
360
361    #[test]
362    fn test_mode_response_creation() {
363        let response = ModeResponse::new("Test response".to_string(), "test-mode".to_string());
364        assert_eq!(response.content, "Test response");
365        assert_eq!(response.metadata.mode, "test-mode");
366        assert!(response.actions.is_empty());
367        assert!(response.suggestions.is_empty());
368    }
369
370    #[test]
371    fn test_mode_response_add_action() {
372        let mut response = ModeResponse::new("Test".to_string(), "test-mode".to_string());
373        response.add_action(ModeAction::AskQuestion {
374            question: "What is this?".to_string(),
375        });
376        assert_eq!(response.actions.len(), 1);
377    }
378
379    #[test]
380    fn test_mode_response_add_suggestion() {
381        let mut response = ModeResponse::new("Test".to_string(), "test-mode".to_string());
382        response.add_suggestion("Try this".to_string());
383        assert_eq!(response.suggestions.len(), 1);
384        assert_eq!(response.suggestions[0], "Try this");
385    }
386
387    #[test]
388    fn test_think_more_config_default() {
389        let config = ThinkMoreConfig::default();
390        assert!(!config.enabled);
391        assert_eq!(config.depth, ThinkingDepth::Medium);
392        assert!(!config.auto_enable);
393    }
394
395    #[test]
396    fn test_mode_constraints_creation() {
397        let constraints = ModeConstraints {
398            allow_file_operations: true,
399            allow_command_execution: false,
400            allow_code_generation: true,
401            require_specs: false,
402            auto_think_more_threshold: Some(ComplexityLevel::Complex),
403        };
404        assert!(constraints.allow_file_operations);
405        assert!(!constraints.allow_command_execution);
406        assert!(constraints.allow_code_generation);
407    }
408
409    #[test]
410    fn test_change_summary_default() {
411        let summary = ChangeSummary::default();
412        assert_eq!(summary.files_created, 0);
413        assert_eq!(summary.files_modified, 0);
414        assert_eq!(summary.tests_run, 0);
415        assert_eq!(summary.tests_passed, 0);
416        assert!(summary.quality_issues.is_empty());
417    }
418
419    #[test]
420    fn test_message_creation() {
421        let msg = Message {
422            role: MessageRole::User,
423            content: "Hello".to_string(),
424            timestamp: SystemTime::now(),
425        };
426        assert_eq!(msg.role, MessageRole::User);
427        assert_eq!(msg.content, "Hello");
428    }
429
430    #[test]
431    fn test_capability_display() {
432        assert_eq!(Capability::CodeGeneration.to_string(), "CodeGeneration");
433        assert_eq!(
434            Capability::QuestionAnswering.to_string(),
435            "QuestionAnswering"
436        );
437    }
438
439    #[test]
440    fn test_operation_display() {
441        assert_eq!(Operation::GenerateCode.to_string(), "GenerateCode");
442        assert_eq!(Operation::AnswerQuestion.to_string(), "AnswerQuestion");
443    }
444
445    #[test]
446    fn test_serialization_mode_context() {
447        let context = ModeContext::new("test-session".to_string());
448        let json = serde_json::to_string(&context).unwrap();
449        let deserialized: ModeContext = serde_json::from_str(&json).unwrap();
450        assert_eq!(deserialized.session_id, context.session_id);
451    }
452
453    #[test]
454    fn test_serialization_mode_response() {
455        let response = ModeResponse::new("Test".to_string(), "test-mode".to_string());
456        let json = serde_json::to_string(&response).unwrap();
457        let deserialized: ModeResponse = serde_json::from_str(&json).unwrap();
458        assert_eq!(deserialized.content, response.content);
459        assert_eq!(deserialized.metadata.mode, response.metadata.mode);
460    }
461}