1use std::collections::HashMap;
4use midstreamer_strange_loop::{StrangeLoop, StrangeLoopConfig, MetaLevel, MetaKnowledge};
5use crate::{MitigationOutcome, FeedbackSignal};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct AdaptiveRule {
11 pub id: String,
12 pub pattern: ThreatPattern,
13 pub confidence: f64,
14 pub created_at: chrono::DateTime<chrono::Utc>,
15 pub updated_at: chrono::DateTime<chrono::Utc>,
16 pub success_count: u64,
17 pub failure_count: u64,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ThreatPattern {
23 pub features: HashMap<String, f64>,
24 pub threat_type: String,
25 pub severity_threshold: f64,
26}
27
28impl Default for ThreatPattern {
29 fn default() -> Self {
30 Self {
31 features: HashMap::new(),
32 threat_type: "unknown".to_string(),
33 severity_threshold: 0.5,
34 }
35 }
36}
37
38impl ThreatPattern {
39 pub fn from_features(features: &HashMap<String, f64>) -> Self {
40 Self {
41 features: features.clone(),
42 threat_type: "detected".to_string(),
43 severity_threshold: 0.5,
44 }
45 }
46}
47
48pub struct MetaLearningEngine {
50 learner: StrangeLoop,
52
53 learned_patterns: Vec<AdaptiveRule>,
55
56 pattern_effectiveness: HashMap<String, EffectivenessMetrics>,
58
59 current_level: usize,
61
62 learning_rate: f64,
64}
65
66impl MetaLearningEngine {
67 pub fn new() -> Self {
69 let config = StrangeLoopConfig {
70 max_meta_depth: 25,
71 enable_self_modification: true,
72 max_modifications_per_cycle: 10,
73 safety_check_enabled: true,
74 };
75
76 Self {
77 learner: StrangeLoop::new(config),
78 learned_patterns: Vec::new(),
79 pattern_effectiveness: HashMap::new(),
80 current_level: 0,
81 learning_rate: 0.1,
82 }
83 }
84
85 pub async fn learn_from_outcome(&mut self, outcome: &MitigationOutcome) {
87 let pattern = self.extract_pattern(outcome);
89
90 self.update_pattern_effectiveness(&pattern, outcome.success);
92
93 if self.is_significant_pattern(&pattern) {
95 self.apply_meta_learning(pattern).await;
96 }
97 }
98
99 pub async fn learn_from_incident(&mut self, incident: &ThreatIncident) {
101 let features = self.extract_incident_features(incident);
103
104 let rule = AdaptiveRule {
106 id: uuid::Uuid::new_v4().to_string(),
107 pattern: ThreatPattern::from_features(&features),
108 confidence: 0.5, created_at: chrono::Utc::now(),
110 updated_at: chrono::Utc::now(),
111 success_count: 0,
112 failure_count: 0,
113 };
114
115 self.learned_patterns.push(rule);
117
118 self.optimize_patterns().await;
120 }
121
122 pub fn optimize_strategy(&mut self, feedback: &[FeedbackSignal]) {
134 for signal in feedback {
135 let metrics = self
136 .pattern_effectiveness
137 .entry(signal.strategy_id.clone())
138 .or_insert_with(EffectivenessMetrics::new);
139 metrics.update(signal.effectiveness_score, signal.success);
140 }
141
142 self.recursive_optimize(self.current_level);
144
145 if self.should_advance_level() {
147 self.current_level = (self.current_level + 1).min(25);
148 }
149 }
150
151 pub fn learned_patterns_count(&self) -> usize {
153 self.learned_patterns.len()
154 }
155
156 pub fn current_optimization_level(&self) -> usize {
158 self.current_level
159 }
160
161 fn extract_pattern(&self, outcome: &MitigationOutcome) -> LearnedPattern {
163 LearnedPattern {
164 id: uuid::Uuid::new_v4().to_string(),
165 strategy_id: outcome.strategy_id.clone(),
166 threat_type: outcome.threat_type.clone(),
167 features: outcome.features.clone(),
168 success: outcome.success,
169 timestamp: chrono::Utc::now(),
170 }
171 }
172
173 fn update_pattern_effectiveness(&mut self, pattern: &LearnedPattern, success: bool) {
175 let metrics = self.pattern_effectiveness
176 .entry(pattern.id.clone())
177 .or_insert_with(EffectivenessMetrics::new);
178
179 metrics.update(if success { 1.0 } else { 0.0 }, success);
180 }
181
182 fn is_significant_pattern(&self, pattern: &LearnedPattern) -> bool {
184 if let Some(metrics) = self.pattern_effectiveness.get(&pattern.id) {
185 metrics.total_applications >= 5 && metrics.average_score > 0.6
186 } else {
187 false
188 }
189 }
190
191 async fn apply_meta_learning(&mut self, pattern: LearnedPattern) {
193 let meta_level = MetaLevel(self.current_level);
195 let confidence = self.calculate_pattern_confidence(&pattern);
196
197 let knowledge_data = vec![
199 format!("pattern_id: {}", pattern.id),
200 format!("threat_type: {}", pattern.threat_type),
201 format!("confidence: {}", confidence),
202 ];
203
204 if let Ok(meta_knowledge_vec) = self.learner.learn_at_level(
206 meta_level,
207 &knowledge_data,
208 ) {
209 if let Some(meta_knowledge) = meta_knowledge_vec.first() {
211 self.update_learned_patterns_from_knowledge(&pattern.id, meta_knowledge.clone());
212 }
213 }
214 }
215
216 fn calculate_pattern_confidence(&self, pattern: &LearnedPattern) -> f64 {
218 if let Some(metrics) = self.pattern_effectiveness.get(&pattern.id) {
219 metrics.average_score
220 } else {
221 0.5
222 }
223 }
224
225 fn update_learned_patterns_from_knowledge(&mut self, pattern_id: &str, knowledge: MetaKnowledge) {
227 if let Some(rule) = self.learned_patterns.iter_mut()
229 .find(|r| r.id == pattern_id) {
230 rule.confidence = knowledge.confidence;
231 rule.updated_at = chrono::Utc::now();
232 }
233 }
234
235 fn extract_incident_features(&self, incident: &ThreatIncident) -> HashMap<String, f64> {
237 let mut features = HashMap::new();
238
239 features.insert("severity".to_string(), incident.severity as f64);
240 features.insert("confidence".to_string(), incident.confidence);
241
242 match &incident.threat_type {
244 ThreatType::Anomaly(score) => {
245 features.insert("anomaly_score".to_string(), *score);
246 }
247 ThreatType::Attack(attack_type) => {
248 features.insert("attack_type_id".to_string(), attack_type.to_id() as f64);
249 }
250 ThreatType::Intrusion(level) => {
251 features.insert("intrusion_level".to_string(), *level as f64);
252 }
253 }
254
255 features
256 }
257
258 async fn optimize_patterns(&mut self) {
260 for level in 0..=self.current_level {
262 self.recursive_optimize(level);
263 }
264
265 self.learned_patterns.retain(|p| p.confidence > 0.3);
267 }
268
269 fn recursive_optimize(&mut self, level: usize) {
271 let optimization_effectiveness = self.calculate_optimization_effectiveness();
273
274 if optimization_effectiveness > 0.8 {
276 self.learning_rate *= 1.1; } else if optimization_effectiveness < 0.4 {
278 self.learning_rate *= 0.9; }
280
281 let learning_rate = self.learning_rate;
283 for pattern in &mut self.learned_patterns {
284 let refinement = learning_rate * (level as f64 / 25.0);
286 pattern.confidence = (pattern.confidence + refinement).clamp(0.0, 1.0);
287 }
288 }
289
290 fn calculate_optimization_effectiveness(&self) -> f64 {
292 let map_score = if self.pattern_effectiveness.is_empty() {
306 None
307 } else {
308 let total: f64 = self
309 .pattern_effectiveness
310 .values()
311 .map(|m| m.average_score)
312 .sum();
313 Some(total / self.pattern_effectiveness.len() as f64)
314 };
315
316 let rules_score = {
317 let totals: (u64, u64) = self
318 .learned_patterns
319 .iter()
320 .fold((0u64, 0u64), |acc, r| {
321 (acc.0 + r.success_count, acc.1 + r.failure_count)
322 });
323 let attempts = totals.0 + totals.1;
324 if attempts == 0 {
325 None
326 } else {
327 Some(totals.0 as f64 / attempts as f64)
328 }
329 };
330
331 match (map_score, rules_score) {
332 (Some(m), Some(r)) => (m + r) / 2.0,
333 (Some(m), None) => m,
334 (None, Some(r)) => r,
335 (None, None) => 0.5,
336 }
337 }
338
339 #[allow(dead_code)]
341 fn refine_confidence(&self, current: f64, level: usize) -> f64 {
342 let refinement = self.learning_rate * (level as f64 / 25.0);
344 (current + refinement).clamp(0.0, 1.0)
345 }
346
347 fn should_advance_level(&self) -> bool {
349 let effectiveness = self.calculate_optimization_effectiveness();
350 effectiveness > 0.75 && self.learned_patterns.len() >= 10
351 }
352}
353
354impl Default for MetaLearningEngine {
355 fn default() -> Self {
356 Self::new()
357 }
358}
359
360#[derive(Debug, Clone, Serialize, Deserialize)]
362struct LearnedPattern {
363 id: String,
364 strategy_id: String,
365 threat_type: String,
366 features: HashMap<String, f64>,
367 success: bool,
368 timestamp: chrono::DateTime<chrono::Utc>,
369}
370
371#[derive(Debug, Clone)]
373struct EffectivenessMetrics {
374 total_applications: u64,
375 successful_applications: u64,
376 average_score: f64,
377 last_updated: chrono::DateTime<chrono::Utc>,
378}
379
380impl EffectivenessMetrics {
381 fn new() -> Self {
382 Self {
383 total_applications: 0,
384 successful_applications: 0,
385 average_score: 0.0,
386 last_updated: chrono::Utc::now(),
387 }
388 }
389
390 fn update(&mut self, score: f64, success: bool) {
391 self.total_applications += 1;
392 if success {
393 self.successful_applications += 1;
394 }
395
396 self.average_score = (self.average_score * (self.total_applications - 1) as f64 + score)
398 / self.total_applications as f64;
399
400 self.last_updated = chrono::Utc::now();
401 }
402}
403
404#[derive(Debug, Clone)]
406pub struct ThreatIncident {
407 pub id: String,
408 pub threat_type: ThreatType,
409 pub severity: u8,
410 pub confidence: f64,
411 pub timestamp: chrono::DateTime<chrono::Utc>,
412}
413
414#[derive(Debug, Clone)]
416pub enum ThreatType {
417 Anomaly(f64),
418 Attack(AttackType),
419 Intrusion(u8),
420}
421
422#[derive(Debug, Clone)]
424pub enum AttackType {
425 DDoS,
426 SqlInjection,
427 XSS,
428 CSRF,
429 Other(String),
430}
431
432impl AttackType {
433 fn to_id(&self) -> u8 {
434 match self {
435 AttackType::DDoS => 1,
436 AttackType::SqlInjection => 2,
437 AttackType::XSS => 3,
438 AttackType::CSRF => 4,
439 AttackType::Other(_) => 99,
440 }
441 }
442}
443
444#[cfg(test)]
445mod tests {
446 use super::*;
447
448 #[tokio::test]
449 async fn test_meta_learning_creation() {
450 let engine = MetaLearningEngine::new();
451 assert_eq!(engine.current_level, 0);
452 assert_eq!(engine.learned_patterns_count(), 0);
453 }
454
455 #[tokio::test]
456 async fn test_pattern_learning() {
457 let mut engine = MetaLearningEngine::new();
458
459 let incident = ThreatIncident {
460 id: "test-1".to_string(),
461 threat_type: ThreatType::Anomaly(0.85),
462 severity: 7,
463 confidence: 0.9,
464 timestamp: chrono::Utc::now(),
465 };
466
467 engine.learn_from_incident(&incident).await;
468 assert!(engine.learned_patterns_count() > 0);
469 }
470
471 #[test]
472 fn test_effectiveness_metrics() {
473 let mut metrics = EffectivenessMetrics::new();
474
475 metrics.update(0.8, true);
476 assert_eq!(metrics.total_applications, 1);
477 assert_eq!(metrics.successful_applications, 1);
478 assert_eq!(metrics.average_score, 0.8);
479
480 metrics.update(0.6, false);
481 assert_eq!(metrics.total_applications, 2);
482 assert_eq!(metrics.successful_applications, 1);
483 assert_eq!(metrics.average_score, 0.7);
484 }
485
486 #[test]
487 fn test_optimization_level_advancement() {
488 let mut engine = MetaLearningEngine::new();
489
490 for i in 0..15 {
492 engine.learned_patterns.push(AdaptiveRule {
493 id: format!("rule-{}", i),
494 pattern: ThreatPattern::default(),
495 confidence: 0.8,
496 created_at: chrono::Utc::now(),
497 updated_at: chrono::Utc::now(),
498 success_count: 10,
499 failure_count: 2,
500 });
501 }
502
503 assert!(engine.should_advance_level());
505 }
506}