Skip to main content

do_memory_mcp/mcp/tools/advanced_pattern_analysis/
tool.rs

1//! # Advanced Pattern Analysis Tool Implementation
2//!
3//! MCP tool implementation for advanced pattern analysis combining statistical
4//! and predictive modeling capabilities.
5
6use crate::patterns::{predictive, statistical};
7use crate::types::Tool;
8use anyhow::Result;
9use do_memory_core::SelfLearningMemory;
10use serde_json::json;
11use std::collections::HashMap;
12use std::sync::Arc;
13use tracing::{debug, info, instrument};
14
15use super::summary::{MetricsCalculator, SummaryGenerator};
16use super::time_series::TimeSeriesExtractor;
17use super::validator::{DataPreparer, InputValidator};
18
19use super::types::{
20    AdvancedPatternAnalysisInput, AdvancedPatternAnalysisOutput, AnalysisSummary, AnalysisType,
21    PerformanceMetrics,
22};
23
24/// Advanced pattern analysis tool implementation
25pub struct AdvancedPatternAnalysisTool {
26    memory: Arc<SelfLearningMemory>,
27}
28
29impl AdvancedPatternAnalysisTool {
30    /// Create a new advanced pattern analysis tool
31    pub fn new(memory: Arc<SelfLearningMemory>) -> Self {
32        Self { memory }
33    }
34
35    /// Get the tool definition for MCP
36    pub fn tool_definition() -> Tool {
37        Tool::new(
38            "advanced_pattern_analysis".to_string(),
39            "Perform advanced statistical analysis, predictive modeling, and causal inference on time series data from memory episodes".to_string(),
40            json!({
41                "type": "object",
42                "properties": {
43                    "analysis_type": {
44                        "type": "string",
45                        "enum": ["statistical", "predictive", "comprehensive"],
46                        "description": "Type of analysis to perform"
47                    },
48                    "time_series_data": {
49                        "type": "object",
50                        "description": "Time series data as variable_name -> array of numeric values",
51                        "patternProperties": {
52                            ".*": {
53                                "type": "array",
54                                "items": {"type": "number"}
55                            }
56                        },
57                        "additionalProperties": false
58                    },
59                    "config": {
60                        "type": "object",
61                        "description": "Optional analysis configuration",
62                        "properties": {
63                            "significance_level": {
64                                "type": "number",
65                                "minimum": 0.0,
66                                "maximum": 1.0,
67                                "default": 0.05,
68                                "description": "Significance level for statistical tests"
69                            },
70                            "forecast_horizon": {
71                                "type": "integer",
72                                "minimum": 1,
73                                "maximum": 100,
74                                "default": 10,
75                                "description": "Number of steps to forecast ahead"
76                            },
77                            "anomaly_sensitivity": {
78                                "type": "number",
79                                "minimum": 0.0,
80                                "maximum": 1.0,
81                                "default": 0.5,
82                                "description": "Sensitivity for anomaly detection (higher = more sensitive)"
83                            },
84                            "enable_causal_inference": {
85                                "type": "boolean",
86                                "default": true,
87                                "description": "Whether to perform causal inference analysis"
88                            },
89                            "max_data_points": {
90                                "type": "integer",
91                                "minimum": 10,
92                                "maximum": 100000,
93                                "default": 10000,
94                                "description": "Maximum number of data points to analyze"
95                            },
96                            "parallel_processing": {
97                                "type": "boolean",
98                                "default": true,
99                                "description": "Enable parallel processing for performance"
100                            }
101                        }
102                    }
103                },
104                "required": ["analysis_type", "time_series_data"]
105            }),
106        )
107    }
108
109    /// Execute advanced pattern analysis
110    #[instrument(skip(self, input), fields(analysis_type = ?input.analysis_type))]
111    pub async fn execute(
112        &self,
113        input: AdvancedPatternAnalysisInput,
114    ) -> Result<AdvancedPatternAnalysisOutput> {
115        let start_time = std::time::Instant::now();
116
117        info!("Starting advanced pattern analysis");
118
119        // Validate input
120        self.validate_input(&input)?;
121
122        // Extract and prepare data
123        let data = self.prepare_data(&input.time_series_data)?;
124
125        // Perform analysis based on type
126        let (statistical_results, predictive_results) = match input.analysis_type {
127            AnalysisType::Statistical => {
128                let results = self
129                    .perform_statistical_analysis(&data, &input.config)
130                    .await?;
131                (Some(results), None)
132            }
133            AnalysisType::Predictive => {
134                let results = self
135                    .perform_predictive_analysis(&data, &input.config)
136                    .await?;
137                (None, Some(results))
138            }
139            AnalysisType::Comprehensive => {
140                let statistical = self
141                    .perform_statistical_analysis(&data, &input.config)
142                    .await?;
143                let predictive = self
144                    .perform_predictive_analysis(&data, &input.config)
145                    .await?;
146                (Some(statistical), Some(predictive))
147            }
148        };
149
150        // Generate summary and recommendations
151        let summary = self.generate_summary(&statistical_results, &predictive_results, &data);
152
153        // Calculate performance metrics
154        let performance = self.calculate_performance_metrics(start_time);
155
156        let output = AdvancedPatternAnalysisOutput {
157            statistical_results,
158            predictive_results,
159            summary,
160            performance,
161        };
162
163        info!(
164            "Advanced pattern analysis completed in {}ms",
165            output.performance.total_time_ms
166        );
167
168        Ok(output)
169    }
170
171    /// Validate input parameters
172    pub fn validate_input(&self, input: &AdvancedPatternAnalysisInput) -> Result<()> {
173        let validator = InputValidator::new();
174        validator.validate(input)
175    }
176
177    /// Prepare and validate data for analysis
178    fn prepare_data(
179        &self,
180        raw_data: &HashMap<String, Vec<f64>>,
181    ) -> Result<HashMap<String, Vec<f64>>> {
182        let preparer = DataPreparer::new();
183        preparer.prepare(raw_data)
184    }
185
186    /// Generate analysis summary and recommendations
187    fn generate_summary(
188        &self,
189        statistical: &Option<statistical::StatisticalResults>,
190        predictive: &Option<predictive::PredictiveResults>,
191        data: &HashMap<String, Vec<f64>>,
192    ) -> AnalysisSummary {
193        let generator = SummaryGenerator::new();
194        generator.generate(statistical, predictive, data)
195    }
196
197    /// Calculate performance metrics
198    fn calculate_performance_metrics(&self, start_time: std::time::Instant) -> PerformanceMetrics {
199        let calculator = MetricsCalculator::new();
200        calculator.calculate(start_time)
201    }
202
203    /// Extract time series data from memory episodes
204    #[instrument(skip(self))]
205    pub async fn extract_time_series_from_memory(
206        &self,
207        query: &str,
208        domain: &str,
209        limit: usize,
210    ) -> Result<HashMap<String, Vec<f64>>> {
211        info!("Extracting time series data from memory episodes");
212
213        // Query memory for relevant episodes (returns Vec<Arc<Episode>>)
214        let context = do_memory_core::TaskContext {
215            domain: domain.to_string(),
216            language: None,
217            framework: None,
218            complexity: do_memory_core::ComplexityLevel::Moderate,
219            tags: vec![],
220        };
221
222        let arc_episodes = self
223            .memory
224            .retrieve_relevant_context(query.to_string(), context, limit)
225            .await;
226
227        if arc_episodes.is_empty() {
228            return Err(anyhow::anyhow!(
229                "No relevant episodes found for time series extraction"
230            ));
231        }
232
233        // Convert Vec<Arc<Episode>> to Vec<Episode> for the extractor
234        let episodes: Vec<do_memory_core::Episode> = arc_episodes
235            .into_iter()
236            .map(|arc_ep| arc_ep.as_ref().clone())
237            .collect();
238
239        // Extract metrics from episodes using TimeSeriesExtractor
240        let extractor = TimeSeriesExtractor::new();
241        let mut time_series = HashMap::new();
242
243        // Common metrics to extract
244        let metrics = vec![
245            "execution_time_ms",
246            "success_rate",
247            "complexity_score",
248            "pattern_match_score",
249            "memory_usage_mb",
250        ];
251
252        for metric in metrics {
253            let mut values = Vec::new();
254
255            for episode in &episodes {
256                if let Some(value) = extractor.extract_metric(metric, episode, &episodes) {
257                    values.push(value);
258                }
259            }
260
261            if extractor.meets_threshold(&values, 3) {
262                time_series.insert(metric.to_string(), values);
263            }
264        }
265
266        debug!(
267            "Extracted {} time series from {} episodes",
268            time_series.len(),
269            episodes.len()
270        );
271
272        Ok(time_series)
273    }
274}
275
276#[cfg(test)]
277mod tests {
278    use super::*;
279    use do_memory_core::SelfLearningMemory;
280
281    #[tokio::test]
282    async fn test_tool_definition() {
283        let tool = AdvancedPatternAnalysisTool::tool_definition();
284        assert_eq!(tool.name, "advanced_pattern_analysis");
285        assert!(!tool.description.is_empty());
286        assert!(tool.input_schema.is_object());
287    }
288
289    #[tokio::test]
290    async fn test_input_validation() {
291        let memory = Arc::new(SelfLearningMemory::new());
292        let tool = AdvancedPatternAnalysisTool::new(memory);
293
294        // Valid input
295        let mut data = HashMap::new();
296        data.insert("test".to_string(), vec![1.0, 2.0, 3.0, 4.0, 5.0]);
297
298        let input = AdvancedPatternAnalysisInput {
299            analysis_type: AnalysisType::Statistical,
300            time_series_data: data.clone(),
301            config: None,
302        };
303
304        assert!(tool.validate_input(&input).is_ok());
305
306        // Invalid input - empty data
307        let input_empty = AdvancedPatternAnalysisInput {
308            analysis_type: AnalysisType::Statistical,
309            time_series_data: HashMap::new(),
310            config: None,
311        };
312
313        assert!(tool.validate_input(&input_empty).is_err());
314
315        // Invalid input - insufficient data points
316        let mut small_data = HashMap::new();
317        small_data.insert("small".to_string(), vec![1.0, 2.0]);
318
319        let input_small = AdvancedPatternAnalysisInput {
320            analysis_type: AnalysisType::Statistical,
321            time_series_data: small_data,
322            config: None,
323        };
324
325        assert!(tool.validate_input(&input_small).is_err());
326    }
327
328    #[tokio::test]
329    async fn test_statistical_analysis_execution() {
330        let memory = Arc::new(SelfLearningMemory::new());
331        let tool = AdvancedPatternAnalysisTool::new(memory);
332
333        let mut data = HashMap::new();
334        data.insert("x".to_string(), vec![1.0, 2.0, 3.0, 4.0, 5.0]);
335        data.insert("y".to_string(), vec![2.0, 4.0, 6.0, 8.0, 10.0]);
336
337        let input = AdvancedPatternAnalysisInput {
338            analysis_type: AnalysisType::Statistical,
339            time_series_data: data,
340            config: None,
341        };
342
343        let result = tool.execute(input).await;
344        assert!(result.is_ok());
345
346        let output = result.unwrap();
347        assert!(output.statistical_results.is_some());
348        assert!(output.predictive_results.is_none());
349        assert_eq!(output.summary.variables_analyzed, 2);
350    }
351
352    #[tokio::test]
353    async fn test_predictive_analysis_execution() {
354        let memory = Arc::new(SelfLearningMemory::new());
355        let tool = AdvancedPatternAnalysisTool::new(memory);
356
357        let mut data = HashMap::new();
358        data.insert(
359            "trend".to_string(),
360            vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
361        );
362
363        let input = AdvancedPatternAnalysisInput {
364            analysis_type: AnalysisType::Predictive,
365            time_series_data: data,
366            config: None,
367        };
368
369        let result = tool.execute(input).await;
370        assert!(result.is_ok());
371
372        let output = result.unwrap();
373        assert!(output.statistical_results.is_none());
374        assert!(output.predictive_results.is_some());
375    }
376
377    #[tokio::test]
378    async fn test_comprehensive_analysis_execution() {
379        let memory = Arc::new(SelfLearningMemory::new());
380        let tool = AdvancedPatternAnalysisTool::new(memory);
381
382        let mut data = HashMap::new();
383        data.insert("series1".to_string(), vec![1.0, 2.0, 3.0, 4.0, 5.0]);
384        data.insert("series2".to_string(), vec![2.0, 4.0, 6.0, 8.0, 10.0]);
385
386        let input = AdvancedPatternAnalysisInput {
387            analysis_type: AnalysisType::Comprehensive,
388            time_series_data: data,
389            config: None,
390        };
391
392        let result = tool.execute(input).await;
393        assert!(result.is_ok());
394
395        let output = result.unwrap();
396        assert!(output.statistical_results.is_some());
397        assert!(output.predictive_results.is_some());
398        assert_eq!(output.summary.variables_analyzed, 2);
399    }
400}