ruvector_memopt/neural/
engine.rs

1//! Neural Decision Engine
2
3use std::path::Path;
4use chrono::{Datelike, Timelike};
5use tracing::{debug, info};
6
7use crate::core::config::OptimizerConfig;
8use crate::core::optimizer::OptimizationDecision;
9use crate::core::patterns::{MemoryPattern, LabeledPattern};
10use crate::windows::memory::{MemoryStatus, OptimizationResult};
11
12use super::hnsw_patterns::PatternIndex;
13use super::ewc_learner::EWCLearner;
14use super::attention::AttentionScorer;
15
16pub struct NeuralDecisionEngine {
17    pattern_index: PatternIndex,
18    attention: AttentionScorer,
19    ewc: EWCLearner,
20    config: OptimizerConfig,
21    history: Vec<LabeledPattern>,
22}
23
24impl NeuralDecisionEngine {
25    pub fn new(config: &OptimizerConfig) -> Result<Self, String> {
26        let pattern_index = PatternIndex::new(MemoryPattern::DIM)
27            .map_err(|e| format!("Failed to create pattern index: {}", e))?;
28        let attention = AttentionScorer::new();
29        let ewc = EWCLearner::new(config.ewc_lambda);
30        let history = Self::load_history(&config.model_path).unwrap_or_default();
31        info!("Neural engine initialized with {} historical patterns", history.len());
32        Ok(Self { pattern_index, attention, ewc, config: config.clone(), history })
33    }
34
35    pub async fn decide(&self, pattern: &MemoryPattern, status: &MemoryStatus) -> Result<OptimizationDecision, String> {
36        let pattern_vec = pattern.to_vector();
37        let similar = self.pattern_index.search(&pattern_vec, 5)?;
38        let (_, base_confidence) = self.analyze_similar_patterns(&similar);
39        let attention_score = self.attention.score(pattern, status);
40        let ewc_adjustment = self.ewc.get_confidence_adjustment(pattern);
41        let final_confidence = (base_confidence * 0.5 + attention_score * 0.3 + ewc_adjustment * 0.2).clamp(0.0, 1.0);
42        let should_optimize = status.memory_load_percent >= self.config.pressure_threshold || final_confidence > 0.7;
43        let aggressive = status.memory_load_percent >= self.config.critical_threshold || (should_optimize && final_confidence > 0.9);
44        let reason = format!("Neural: conf={:.2}, attn={:.2}, similar={}", final_confidence, attention_score, similar.len());
45        debug!("{}", reason);
46        Ok(OptimizationDecision { should_optimize, aggressive, confidence: final_confidence, reason, target_processes: vec![] })
47    }
48
49    fn analyze_similar_patterns(&self, similar: &[(usize, f32)]) -> (bool, f32) {
50        if similar.is_empty() { return (false, 0.5); }
51        let mut success_weight = 0.0f32;
52        let mut total_weight = 0.0f32;
53        for (idx, similarity) in similar {
54            if let Some(labeled) = self.history.get(*idx) {
55                if labeled.success { success_weight += similarity; }
56                total_weight += similarity;
57            }
58        }
59        if total_weight > 0.0 { (success_weight / total_weight > 0.5, success_weight / total_weight) } else { (false, 0.5) }
60    }
61
62    pub async fn learn_from_result(&mut self, decision: &OptimizationDecision, result: &OptimizationResult, success: bool) {
63        let now = chrono::Local::now();
64        let pattern = LabeledPattern {
65            pattern: MemoryPattern {
66                load: (result.before_available_mb / 100.0) as f32,
67                consumption_rate: 0.0,
68                available_ratio: (result.before_available_mb / 32000.0) as f32,
69                page_file_ratio: 0.0,
70                process_count: result.processes_trimmed as u32,
71                hour: now.hour() as u8,
72                day_of_week: now.weekday().num_days_from_monday() as u8,
73                time_since_last_opt: 0.0,
74            },
75            optimized: true,
76            aggressive: decision.aggressive,
77            freed_mb: result.freed_mb as f32,
78            success,
79        };
80        let vec = pattern.pattern.to_vector();
81        let _ = self.pattern_index.add(&vec);
82        self.ewc.update(&pattern);
83        self.history.push(pattern);
84        if self.history.len() % 100 == 0 { let _ = self.save_history(&self.config.model_path); }
85        info!("Learned: success={}, freed={:.1}MB", success, result.freed_mb);
86    }
87
88    fn load_history(path: &Path) -> Result<Vec<LabeledPattern>, String> {
89        let file_path = path.join("patterns.json");
90        if !file_path.exists() { return Ok(vec![]); }
91        let content = std::fs::read_to_string(&file_path).map_err(|e| e.to_string())?;
92        serde_json::from_str(&content).map_err(|e| e.to_string())
93    }
94
95    fn save_history(&self, path: &Path) -> Result<(), String> {
96        std::fs::create_dir_all(path).map_err(|e| e.to_string())?;
97        let content = serde_json::to_string_pretty(&self.history).map_err(|e| e.to_string())?;
98        std::fs::write(path.join("patterns.json"), content).map_err(|e| e.to_string())
99    }
100
101    pub fn pattern_count(&self) -> usize { self.history.len() }
102}