1use std::error::Error;
7use crate::{AnyaError, AnyaResult};
10pub use crate::dao::{Proposal, ProposalMetrics, RiskMetrics};
12pub use crate::ml::service::MLModel;
14use std::collections::HashMap;
15use std::path::Path;
16use std::sync::{Arc, Mutex};
17
18mod service;
19pub use service::MLService;
20
21pub mod agent_system;
23pub use agent_system::MLAgentSystem;
24
25#[derive(Debug, Clone)]
27pub struct MLConfig {
28 pub enabled: bool,
30 pub model_path: Option<String>,
32 pub use_gpu: bool,
34 pub federated_learning: bool,
36 pub max_model_size: usize,
38}
39
40impl Default for MLConfig {
41 fn default() -> Self {
42 Self {
43 enabled: true,
44 model_path: Some("./data/models".to_string()),
45 use_gpu: true,
46 federated_learning: true,
47 max_model_size: 100 * 1024 * 1024, }
49 }
50}
51
52pub struct MLSystem {
54 config: MLConfig,
55 service: MLService,
56 models: HashMap<String, Arc<Mutex<dyn MLModel>>>,
57}
58
59impl std::fmt::Debug for MLSystem {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 f.debug_struct("MLSystem")
62 .field("config", &self.config)
63 .field("service", &"<MLService>")
64 .field("models", &format!("{} models", self.models.len()))
65 .finish()
66 }
67}
68
69unsafe impl Send for MLSystem {}
71unsafe impl Sync for MLSystem {}
72
73impl MLSystem {
74 pub fn new(config: MLConfig) -> AnyaResult<Self> {
76 if !config.enabled {
77 return Ok(Self {
78 config,
79 service: MLService::new(),
80 models: HashMap::new(),
81 });
82 }
83
84 if let Some(path) = &config.model_path {
86 if !Path::new(path).exists() {
87 std::fs::create_dir_all(path)
88 .map_err(|e| AnyaError::ML(format!("Failed to create model directory: {e}")))?;
89 }
90 }
91
92 let ml_service = MLService::new();
93
94 Ok(Self {
95 config,
96 service: ml_service,
97 models: HashMap::new(),
98 })
99 }
100
101 pub fn service(&self) -> &MLService {
103 &self.service
104 }
105
106 pub fn register_model<M: MLModel + 'static>(&mut self, name: &str, model: M) -> AnyaResult<()> {
108 self.models
109 .insert(name.to_string(), Arc::new(Mutex::new(model)));
110 Ok(())
111 }
112
113 pub fn get_model(&self, name: &str) -> Option<Arc<Mutex<dyn MLModel>>> {
115 self.models.get(name).cloned()
116 }
117
118 pub fn get_health_metrics(&self) -> HashMap<String, f64> {
120 let mut metrics = HashMap::new();
121 metrics.insert("model_count".to_string(), self.models.len() as f64);
122 metrics.insert(
123 "enabled".to_string(),
124 if self.config.enabled { 1.0 } else { 0.0 },
125 );
126 metrics.insert(
127 "federated_learning".to_string(),
128 if self.config.federated_learning {
129 1.0
130 } else {
131 0.0
132 },
133 );
134
135 metrics
137 }
138
139 pub fn list_models(&self) -> Vec<String> {
141 self.models.keys().cloned().collect()
142 }
143
144 pub fn get_model_health_metrics(&self) -> HashMap<String, HashMap<String, f64>> {
146 let mut metrics = HashMap::new();
147
148 metrics.insert("service".to_string(), self.service.get_health_metrics());
150
151 for (name, model) in &self.models {
153 if let Ok(model_lock) = model.lock() {
154 metrics.insert(name.clone(), model_lock.get_health_metrics());
155 }
156 }
157
158 metrics
159 }
160}
161
162pub trait MLModelPlaceholder {}
165
166#[derive(Debug, Clone)]
168pub struct MLInput {
169 pub features: Vec<f64>,
171 pub label: f64,
173 pub metadata: Option<HashMap<String, String>>,
175}
176
177#[derive(Debug, Clone)]
179pub struct MLOutput {
180 pub prediction: f64,
182 pub confidence: f64,
184 pub additional_info: Option<HashMap<String, Vec<f64>>>,
186}
187
188pub struct FederatedNode {
190 pub id: String,
192 pub url: String,
194 pub public_key: Vec<u8>,
196}
197
198#[allow(dead_code)]
200pub struct FederatedLearningManager {
201 nodes: Vec<FederatedNode>,
203 aggregation_method: String,
205}
206
207impl Default for FederatedLearningManager {
208 fn default() -> Self {
209 Self::new()
210 }
211}
212
213impl FederatedLearningManager {
214 pub fn new() -> Self {
216 Self {
217 nodes: Vec::new(),
218 aggregation_method: "average".to_string(),
219 }
220 }
221
222 pub fn add_node(&mut self, node: FederatedNode) {
224 self.nodes.push(node);
225 }
226
227 pub fn remove_node(&mut self, node_id: &str) -> Result<(), Box<dyn Error>> {
229 self.nodes.retain(|n| n.id != node_id);
230 Ok(())
231 }
232
233 pub fn list_nodes(&self) -> &[FederatedNode] {
235 &self.nodes
236 }
237}
238
239pub mod agent_checker;
244
245pub use agent_checker::AgentChecker;
247pub use agent_checker::ComponentStatus;
248pub use agent_checker::SystemHealth;
249pub use agent_checker::SystemStage;
250
251pub const DEV_THRESHOLD: f64 = 0.60;
253pub const PROD_THRESHOLD: f64 = 0.90;
254pub const RELEASE_THRESHOLD: f64 = 0.99;
255
256pub fn create_agent_checker() -> AgentChecker {
258 AgentChecker::new(20)
259}
260
261pub fn is_ready_for_stage(health: f64, stage: SystemStage) -> bool {
263 match stage {
264 SystemStage::Development => health >= DEV_THRESHOLD,
265 SystemStage::Production => health >= PROD_THRESHOLD,
266 SystemStage::Release => health >= RELEASE_THRESHOLD,
267 SystemStage::Unavailable => false,
268 }
269}
270
271pub mod agents;
272pub use agents::*;
273
274pub mod models;
275#[cfg(test)]
276mod tests {
277 use super::*;
278
279 #[test]
280 fn test_stage_readiness() -> Result<(), Box<dyn Error>> {
281 assert!(!is_ready_for_stage(0.55, SystemStage::Development));
282 assert!(is_ready_for_stage(0.65, SystemStage::Development));
283 assert!(!is_ready_for_stage(0.85, SystemStage::Production));
284 assert!(is_ready_for_stage(0.95, SystemStage::Production));
285 Ok(())
286 }
287}