use std::error::Error;
use crate::{AnyaError, AnyaResult};
pub use crate::dao::{Proposal, ProposalMetrics, RiskMetrics};
pub use crate::ml::service::MLModel;
use std::collections::HashMap;
use std::path::Path;
use std::sync::{Arc, Mutex};
mod service;
pub use service::MLService;
pub mod agent_system;
pub use agent_system::MLAgentSystem;
#[derive(Debug, Clone)]
pub struct MLConfig {
pub enabled: bool,
pub model_path: Option<String>,
pub use_gpu: bool,
pub federated_learning: bool,
pub max_model_size: usize,
}
impl Default for MLConfig {
fn default() -> Self {
Self {
enabled: true,
model_path: Some("./data/models".to_string()),
use_gpu: true,
federated_learning: true,
max_model_size: 100 * 1024 * 1024, }
}
}
pub struct MLSystem {
config: MLConfig,
service: MLService,
models: HashMap<String, Arc<Mutex<dyn MLModel>>>,
}
impl std::fmt::Debug for MLSystem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MLSystem")
.field("config", &self.config)
.field("service", &"<MLService>")
.field("models", &format!("{} models", self.models.len()))
.finish()
}
}
unsafe impl Send for MLSystem {}
unsafe impl Sync for MLSystem {}
impl MLSystem {
pub fn new(config: MLConfig) -> AnyaResult<Self> {
if !config.enabled {
return Ok(Self {
config,
service: MLService::new(),
models: HashMap::new(),
});
}
if let Some(path) = &config.model_path {
if !Path::new(path).exists() {
std::fs::create_dir_all(path)
.map_err(|e| AnyaError::ML(format!("Failed to create model directory: {e}")))?;
}
}
let ml_service = MLService::new();
Ok(Self {
config,
service: ml_service,
models: HashMap::new(),
})
}
pub fn service(&self) -> &MLService {
&self.service
}
pub fn register_model<M: MLModel + 'static>(&mut self, name: &str, model: M) -> AnyaResult<()> {
self.models
.insert(name.to_string(), Arc::new(Mutex::new(model)));
Ok(())
}
pub fn get_model(&self, name: &str) -> Option<Arc<Mutex<dyn MLModel>>> {
self.models.get(name).cloned()
}
pub fn get_health_metrics(&self) -> HashMap<String, f64> {
let mut metrics = HashMap::new();
metrics.insert("model_count".to_string(), self.models.len() as f64);
metrics.insert(
"enabled".to_string(),
if self.config.enabled { 1.0 } else { 0.0 },
);
metrics.insert(
"federated_learning".to_string(),
if self.config.federated_learning {
1.0
} else {
0.0
},
);
metrics
}
pub fn list_models(&self) -> Vec<String> {
self.models.keys().cloned().collect()
}
pub fn get_model_health_metrics(&self) -> HashMap<String, HashMap<String, f64>> {
let mut metrics = HashMap::new();
metrics.insert("service".to_string(), self.service.get_health_metrics());
for (name, model) in &self.models {
if let Ok(model_lock) = model.lock() {
metrics.insert(name.clone(), model_lock.get_health_metrics());
}
}
metrics
}
}
pub trait MLModelPlaceholder {}
#[derive(Debug, Clone)]
pub struct MLInput {
pub features: Vec<f64>,
pub label: f64,
pub metadata: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone)]
pub struct MLOutput {
pub prediction: f64,
pub confidence: f64,
pub additional_info: Option<HashMap<String, Vec<f64>>>,
}
pub struct FederatedNode {
pub id: String,
pub url: String,
pub public_key: Vec<u8>,
}
#[allow(dead_code)]
pub struct FederatedLearningManager {
nodes: Vec<FederatedNode>,
aggregation_method: String,
}
impl Default for FederatedLearningManager {
fn default() -> Self {
Self::new()
}
}
impl FederatedLearningManager {
pub fn new() -> Self {
Self {
nodes: Vec::new(),
aggregation_method: "average".to_string(),
}
}
pub fn add_node(&mut self, node: FederatedNode) {
self.nodes.push(node);
}
pub fn remove_node(&mut self, node_id: &str) -> Result<(), Box<dyn Error>> {
self.nodes.retain(|n| n.id != node_id);
Ok(())
}
pub fn list_nodes(&self) -> &[FederatedNode] {
&self.nodes
}
}
pub mod agent_checker;
pub use agent_checker::AgentChecker;
pub use agent_checker::ComponentStatus;
pub use agent_checker::SystemHealth;
pub use agent_checker::SystemStage;
pub const DEV_THRESHOLD: f64 = 0.60;
pub const PROD_THRESHOLD: f64 = 0.90;
pub const RELEASE_THRESHOLD: f64 = 0.99;
pub fn create_agent_checker() -> AgentChecker {
AgentChecker::new(20)
}
pub fn is_ready_for_stage(health: f64, stage: SystemStage) -> bool {
match stage {
SystemStage::Development => health >= DEV_THRESHOLD,
SystemStage::Production => health >= PROD_THRESHOLD,
SystemStage::Release => health >= RELEASE_THRESHOLD,
SystemStage::Unavailable => false,
}
}
pub mod agents;
pub use agents::*;
pub mod models;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stage_readiness() -> Result<(), Box<dyn Error>> {
assert!(!is_ready_for_stage(0.55, SystemStage::Development));
assert!(is_ready_for_stage(0.65, SystemStage::Development));
assert!(!is_ready_for_stage(0.85, SystemStage::Production));
assert!(is_ready_for_stage(0.95, SystemStage::Production));
Ok(())
}
}