1use 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
24pub struct AdvancedPatternAnalysisTool {
26 memory: Arc<SelfLearningMemory>,
27}
28
29impl AdvancedPatternAnalysisTool {
30 pub fn new(memory: Arc<SelfLearningMemory>) -> Self {
32 Self { memory }
33 }
34
35 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 #[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 self.validate_input(&input)?;
121
122 let data = self.prepare_data(&input.time_series_data)?;
124
125 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 let summary = self.generate_summary(&statistical_results, &predictive_results, &data);
152
153 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 pub fn validate_input(&self, input: &AdvancedPatternAnalysisInput) -> Result<()> {
173 let validator = InputValidator::new();
174 validator.validate(input)
175 }
176
177 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 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 fn calculate_performance_metrics(&self, start_time: std::time::Instant) -> PerformanceMetrics {
199 let calculator = MetricsCalculator::new();
200 calculator.calculate(start_time)
201 }
202
203 #[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 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 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 let extractor = TimeSeriesExtractor::new();
241 let mut time_series = HashMap::new();
242
243 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 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 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 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}