ricecoder_domain_agents/
domain_agents.rs

1//! Specialized domain agents
2
3use crate::error::Result;
4use crate::models::{DomainAgentConfig, DomainAgentMetadata};
5use async_trait::async_trait;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use tracing::debug;
9
10/// Input for domain agent execution
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct DomainAgentInput {
13    /// Domain for this agent
14    pub domain: String,
15    /// Task description
16    pub task: String,
17    /// Context or code to analyze
18    pub context: String,
19    /// Additional parameters
20    pub parameters: HashMap<String, serde_json::Value>,
21}
22
23/// Output from domain agent execution
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct DomainAgentOutput {
26    /// Domain that processed this
27    pub domain: String,
28    /// Generated response
29    pub response: String,
30    /// Suggestions or recommendations
31    pub suggestions: Vec<String>,
32    /// Confidence score (0.0 to 1.0)
33    pub confidence: f32,
34    /// Metadata about the execution
35    pub metadata: HashMap<String, serde_json::Value>,
36}
37
38/// Trait for domain-specific agents
39#[async_trait]
40pub trait DomainAgent: Send + Sync {
41    /// Get agent metadata
42    fn metadata(&self) -> &DomainAgentMetadata;
43
44    /// Get agent configuration
45    fn config(&self) -> &DomainAgentConfig;
46
47    /// Execute the agent on input
48    async fn execute(&self, input: DomainAgentInput) -> Result<DomainAgentOutput>;
49
50    /// Validate input for this agent
51    fn validate_input(&self, input: &DomainAgentInput) -> Result<()>;
52}
53
54/// Frontend domain agent
55pub struct FrontendAgent {
56    metadata: DomainAgentMetadata,
57    config: DomainAgentConfig,
58}
59
60impl FrontendAgent {
61    /// Create a new frontend agent
62    pub fn new() -> Self {
63        Self {
64            metadata: DomainAgentMetadata {
65                domain: "frontend".to_string(),
66                name: "Frontend Agent".to_string(),
67                version: "1.0.0".to_string(),
68                capabilities: vec![
69                    "component-design".to_string(),
70                    "state-management".to_string(),
71                    "performance-optimization".to_string(),
72                    "accessibility".to_string(),
73                ],
74                required_tools: vec!["code-analysis".to_string()],
75                optional_tools: vec!["performance-profiling".to_string()],
76            },
77            config: DomainAgentConfig {
78                domain: "frontend".to_string(),
79                name: "Frontend Agent".to_string(),
80                description: "Specialized agent for frontend development".to_string(),
81                system_prompt: "You are an expert frontend developer specializing in React, Vue, and Angular. Provide best practices for component design, state management, and performance optimization.".to_string(),
82                tools: vec!["code-analysis".to_string(), "performance-profiling".to_string()],
83                model: None,
84                temperature: Some(0.7),
85                max_tokens: Some(2000),
86                custom_config: HashMap::new(),
87            },
88        }
89    }
90}
91
92impl Default for FrontendAgent {
93    fn default() -> Self {
94        Self::new()
95    }
96}
97
98#[async_trait]
99impl DomainAgent for FrontendAgent {
100    fn metadata(&self) -> &DomainAgentMetadata {
101        &self.metadata
102    }
103
104    fn config(&self) -> &DomainAgentConfig {
105        &self.config
106    }
107
108    async fn execute(&self, input: DomainAgentInput) -> Result<DomainAgentOutput> {
109        debug!("Frontend agent executing task: {}", input.task);
110
111        self.validate_input(&input)?;
112
113        Ok(DomainAgentOutput {
114            domain: "frontend".to_string(),
115            response: format!("Frontend analysis for: {}", input.task),
116            suggestions: vec![
117                "Consider using React hooks for state management".to_string(),
118                "Implement proper error boundaries".to_string(),
119                "Optimize component re-renders".to_string(),
120            ],
121            confidence: 0.85,
122            metadata: HashMap::new(),
123        })
124    }
125
126    fn validate_input(&self, input: &DomainAgentInput) -> Result<()> {
127        if input.domain != "frontend" {
128            return Err(crate::error::DomainAgentError::InvalidConfiguration(
129                "Domain mismatch".to_string(),
130            ));
131        }
132        Ok(())
133    }
134}
135
136/// Backend domain agent
137pub struct BackendAgent {
138    metadata: DomainAgentMetadata,
139    config: DomainAgentConfig,
140}
141
142impl BackendAgent {
143    /// Create a new backend agent
144    pub fn new() -> Self {
145        Self {
146            metadata: DomainAgentMetadata {
147                domain: "backend".to_string(),
148                name: "Backend Agent".to_string(),
149                version: "1.0.0".to_string(),
150                capabilities: vec![
151                    "api-design".to_string(),
152                    "database-optimization".to_string(),
153                    "security".to_string(),
154                    "scalability".to_string(),
155                ],
156                required_tools: vec!["code-analysis".to_string()],
157                optional_tools: vec!["performance-profiling".to_string()],
158            },
159            config: DomainAgentConfig {
160                domain: "backend".to_string(),
161                name: "Backend Agent".to_string(),
162                description: "Specialized agent for backend development".to_string(),
163                system_prompt: "You are an expert backend developer specializing in Node.js, Python, and Go. Provide best practices for API design, database optimization, and security.".to_string(),
164                tools: vec!["code-analysis".to_string(), "performance-profiling".to_string()],
165                model: None,
166                temperature: Some(0.7),
167                max_tokens: Some(2000),
168                custom_config: HashMap::new(),
169            },
170        }
171    }
172}
173
174impl Default for BackendAgent {
175    fn default() -> Self {
176        Self::new()
177    }
178}
179
180#[async_trait]
181impl DomainAgent for BackendAgent {
182    fn metadata(&self) -> &DomainAgentMetadata {
183        &self.metadata
184    }
185
186    fn config(&self) -> &DomainAgentConfig {
187        &self.config
188    }
189
190    async fn execute(&self, input: DomainAgentInput) -> Result<DomainAgentOutput> {
191        debug!("Backend agent executing task: {}", input.task);
192
193        self.validate_input(&input)?;
194
195        Ok(DomainAgentOutput {
196            domain: "backend".to_string(),
197            response: format!("Backend analysis for: {}", input.task),
198            suggestions: vec![
199                "Implement proper error handling".to_string(),
200                "Add database indexing for performance".to_string(),
201                "Use connection pooling".to_string(),
202            ],
203            confidence: 0.88,
204            metadata: HashMap::new(),
205        })
206    }
207
208    fn validate_input(&self, input: &DomainAgentInput) -> Result<()> {
209        if input.domain != "backend" {
210            return Err(crate::error::DomainAgentError::InvalidConfiguration(
211                "Domain mismatch".to_string(),
212            ));
213        }
214        Ok(())
215    }
216}
217
218/// DevOps domain agent
219pub struct DevOpsAgent {
220    metadata: DomainAgentMetadata,
221    config: DomainAgentConfig,
222}
223
224impl DevOpsAgent {
225    /// Create a new DevOps agent
226    pub fn new() -> Self {
227        Self {
228            metadata: DomainAgentMetadata {
229                domain: "devops".to_string(),
230                name: "DevOps Agent".to_string(),
231                version: "1.0.0".to_string(),
232                capabilities: vec![
233                    "infrastructure-design".to_string(),
234                    "deployment".to_string(),
235                    "monitoring".to_string(),
236                    "security".to_string(),
237                ],
238                required_tools: vec!["infrastructure-analysis".to_string()],
239                optional_tools: vec!["monitoring-setup".to_string()],
240            },
241            config: DomainAgentConfig {
242                domain: "devops".to_string(),
243                name: "DevOps Agent".to_string(),
244                description: "Specialized agent for DevOps and infrastructure".to_string(),
245                system_prompt: "You are an expert DevOps engineer specializing in Kubernetes, Docker, and cloud infrastructure. Provide best practices for deployment, monitoring, and security.".to_string(),
246                tools: vec!["infrastructure-analysis".to_string(), "monitoring-setup".to_string()],
247                model: None,
248                temperature: Some(0.7),
249                max_tokens: Some(2000),
250                custom_config: HashMap::new(),
251            },
252        }
253    }
254}
255
256impl Default for DevOpsAgent {
257    fn default() -> Self {
258        Self::new()
259    }
260}
261
262#[async_trait]
263impl DomainAgent for DevOpsAgent {
264    fn metadata(&self) -> &DomainAgentMetadata {
265        &self.metadata
266    }
267
268    fn config(&self) -> &DomainAgentConfig {
269        &self.config
270    }
271
272    async fn execute(&self, input: DomainAgentInput) -> Result<DomainAgentOutput> {
273        debug!("DevOps agent executing task: {}", input.task);
274
275        self.validate_input(&input)?;
276
277        Ok(DomainAgentOutput {
278            domain: "devops".to_string(),
279            response: format!("DevOps analysis for: {}", input.task),
280            suggestions: vec![
281                "Implement infrastructure as code".to_string(),
282                "Set up proper monitoring and alerting".to_string(),
283                "Use container orchestration".to_string(),
284            ],
285            confidence: 0.82,
286            metadata: HashMap::new(),
287        })
288    }
289
290    fn validate_input(&self, input: &DomainAgentInput) -> Result<()> {
291        if input.domain != "devops" {
292            return Err(crate::error::DomainAgentError::InvalidConfiguration(
293                "Domain mismatch".to_string(),
294            ));
295        }
296        Ok(())
297    }
298}
299
300#[cfg(test)]
301mod tests {
302    use super::*;
303
304    #[tokio::test]
305    async fn test_frontend_agent_creation() {
306        let agent = FrontendAgent::new();
307        assert_eq!(agent.metadata().domain, "frontend");
308        assert_eq!(agent.config().domain, "frontend");
309    }
310
311    #[tokio::test]
312    async fn test_backend_agent_creation() {
313        let agent = BackendAgent::new();
314        assert_eq!(agent.metadata().domain, "backend");
315        assert_eq!(agent.config().domain, "backend");
316    }
317
318    #[tokio::test]
319    async fn test_devops_agent_creation() {
320        let agent = DevOpsAgent::new();
321        assert_eq!(agent.metadata().domain, "devops");
322        assert_eq!(agent.config().domain, "devops");
323    }
324
325    #[tokio::test]
326    async fn test_frontend_agent_execution() {
327        let agent = FrontendAgent::new();
328        let input = DomainAgentInput {
329            domain: "frontend".to_string(),
330            task: "Design a React component".to_string(),
331            context: "Component for user profile".to_string(),
332            parameters: HashMap::new(),
333        };
334
335        let output = agent.execute(input).await.unwrap();
336        assert_eq!(output.domain, "frontend");
337        assert!(!output.suggestions.is_empty());
338    }
339
340    #[tokio::test]
341    async fn test_backend_agent_execution() {
342        let agent = BackendAgent::new();
343        let input = DomainAgentInput {
344            domain: "backend".to_string(),
345            task: "Design an API endpoint".to_string(),
346            context: "User management API".to_string(),
347            parameters: HashMap::new(),
348        };
349
350        let output = agent.execute(input).await.unwrap();
351        assert_eq!(output.domain, "backend");
352        assert!(!output.suggestions.is_empty());
353    }
354
355    #[tokio::test]
356    async fn test_devops_agent_execution() {
357        let agent = DevOpsAgent::new();
358        let input = DomainAgentInput {
359            domain: "devops".to_string(),
360            task: "Set up deployment pipeline".to_string(),
361            context: "Kubernetes cluster".to_string(),
362            parameters: HashMap::new(),
363        };
364
365        let output = agent.execute(input).await.unwrap();
366        assert_eq!(output.domain, "devops");
367        assert!(!output.suggestions.is_empty());
368    }
369
370    #[tokio::test]
371    async fn test_domain_mismatch_validation() {
372        let agent = FrontendAgent::new();
373        let input = DomainAgentInput {
374            domain: "backend".to_string(),
375            task: "Design a React component".to_string(),
376            context: "Component for user profile".to_string(),
377            parameters: HashMap::new(),
378        };
379
380        let result = agent.execute(input).await;
381        assert!(result.is_err());
382    }
383}