Skip to main content

feagi_services/impls/
system_service_impl.rs

1// Copyright 2025 Neuraville Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4/*!
5System service implementation.
6
7Copyright 2025 Neuraville Inc.
8Licensed under the Apache License, Version 2.0
9*/
10
11use crate::traits::SystemService;
12use crate::types::*;
13use async_trait::async_trait;
14use feagi_brain_development::ConnectomeManager;
15use feagi_npu_burst_engine::BurstLoopRunner;
16use parking_lot::RwLock;
17use std::sync::Arc;
18use std::time::SystemTime;
19use tracing::trace;
20
21/// Default implementation of SystemService
22pub struct SystemServiceImpl {
23    connectome: Arc<RwLock<ConnectomeManager>>,
24    burst_runner: Option<Arc<RwLock<BurstLoopRunner>>>,
25    start_time: SystemTime,
26    version_info: VersionInfo,
27}
28
29impl SystemServiceImpl {
30    /// Create new SystemServiceImpl
31    ///
32    /// The version_info should be populated by the application (e.g., feagi-rust)
33    /// with the versions of all crates it was compiled with
34    pub fn new(
35        connectome: Arc<RwLock<ConnectomeManager>>,
36        burst_runner: Option<Arc<RwLock<BurstLoopRunner>>>,
37        version_info: VersionInfo,
38    ) -> Self {
39        Self {
40            connectome,
41            burst_runner,
42            start_time: SystemTime::now(),
43            version_info,
44        }
45    }
46
47    /// Get uptime in seconds
48    fn get_uptime_seconds(&self) -> u64 {
49        self.start_time.elapsed().map(|d| d.as_secs()).unwrap_or(0)
50    }
51
52    /// Get FEAGI session timestamp in milliseconds (Unix timestamp when FEAGI started)
53    pub fn get_feagi_session_timestamp(&self) -> i64 {
54        self.start_time
55            .duration_since(SystemTime::UNIX_EPOCH)
56            .map(|d| d.as_millis() as i64)
57            .unwrap_or(0)
58    }
59
60    /// Get current timestamp in ISO 8601 format
61    fn get_timestamp() -> String {
62        chrono::Utc::now().to_rfc3339()
63    }
64}
65
66#[async_trait]
67impl SystemService for SystemServiceImpl {
68    async fn get_health(&self) -> ServiceResult<HealthStatus> {
69        trace!(target: "feagi-services", "Getting system health");
70
71        let mut components = Vec::new();
72
73        // Check connectome health
74        let connectome_initialized = self.connectome.read().is_initialized();
75        components.push(ComponentHealth {
76            name: "Connectome".to_string(),
77            status: if connectome_initialized {
78                "healthy".to_string()
79            } else {
80                "degraded".to_string()
81            },
82            message: if connectome_initialized {
83                Some("Genome loaded and brain initialized".to_string())
84            } else {
85                Some("No genome loaded".to_string())
86            },
87        });
88
89        // Check NPU health
90        let has_npu = self.connectome.read().has_npu();
91        components.push(ComponentHealth {
92            name: "NPU".to_string(),
93            status: if has_npu {
94                "healthy".to_string()
95            } else {
96                "unhealthy".to_string()
97            },
98            message: if has_npu {
99                Some("NPU connected".to_string())
100            } else {
101                Some("NPU not connected".to_string())
102            },
103        });
104
105        // Check burst engine health
106        let burst_engine_running = if let Some(ref runner) = self.burst_runner {
107            runner.read().is_running()
108        } else {
109            false
110        };
111        components.push(ComponentHealth {
112            name: "BurstEngine".to_string(),
113            status: if burst_engine_running {
114                "healthy".to_string()
115            } else {
116                "degraded".to_string()
117            },
118            message: if burst_engine_running {
119                Some("Burst engine active".to_string())
120            } else {
121                Some("Burst engine stopped".to_string())
122            },
123        });
124
125        // Determine overall status
126        let overall_status = if components.iter().all(|c| c.status == "healthy") {
127            "healthy"
128        } else if components.iter().any(|c| c.status == "unhealthy") {
129            "unhealthy"
130        } else {
131            "degraded"
132        };
133
134        Ok(HealthStatus {
135            overall_status: overall_status.to_string(),
136            components,
137            timestamp: Self::get_timestamp(),
138        })
139    }
140
141    async fn get_status(&self) -> ServiceResult<SystemStatus> {
142        trace!(target: "feagi-services", "Getting system status");
143
144        let manager = self.connectome.read();
145
146        let is_initialized = manager.is_initialized();
147
148        // CRITICAL: Read from StateManager's atomic cache - NO NPU lock, NO iteration
149        let state_manager_instance = feagi_state_manager::StateManager::instance();
150        let (neuron_count, synapse_count) =
151            if let Some(state_manager) = state_manager_instance.try_read() {
152                let core_state = state_manager.get_core_state();
153                (
154                    core_state.get_neuron_count() as usize,
155                    core_state.get_synapse_count() as usize,
156                )
157            } else {
158                // Fallback to ConnectomeManager if StateManager unavailable (shouldn't happen)
159                (manager.get_neuron_count(), manager.get_synapse_count())
160            };
161
162        let cortical_area_count = manager.get_cortical_area_count();
163        let brain_region_count = manager.get_brain_region_ids().len();
164
165        let (burst_engine_running, burst_count, current_burst_rate_hz, avg_burst_time_ms) =
166            if let Some(ref runner) = self.burst_runner {
167                let runner_lock = runner.read();
168                (
169                    runner_lock.is_running(),
170                    runner_lock.get_burst_count(),
171                    0.0, // TODO: Implement rate tracking in BurstLoopRunner
172                    0.0, // TODO: Implement timing tracking in BurstLoopRunner
173                )
174            } else {
175                (false, 0, 0.0, 0.0)
176            };
177
178        Ok(SystemStatus {
179            is_initialized,
180            burst_engine_running,
181            burst_count,
182            neuron_count,
183            synapse_count,
184            cortical_area_count,
185            brain_region_count,
186            uptime_seconds: self.get_uptime_seconds(),
187            current_burst_rate_hz,
188            avg_burst_time_ms,
189        })
190    }
191
192    async fn get_version(&self) -> ServiceResult<VersionInfo> {
193        trace!(target: "feagi-services", "Getting version information");
194
195        // Return the version info that was provided by the application at startup
196        // The application (e.g., feagi-rust) knows which crates it compiled with
197        // and provides that information when creating SystemServiceImpl
198        Ok(self.version_info.clone())
199    }
200
201    async fn is_initialized(&self) -> ServiceResult<bool> {
202        trace!(target: "feagi-services", "Checking if system is initialized");
203        Ok(self.connectome.read().is_initialized())
204    }
205
206    async fn get_burst_count(&self) -> ServiceResult<u64> {
207        trace!(target: "feagi-services", "Getting burst count");
208
209        if let Some(ref runner) = self.burst_runner {
210            Ok(runner.read().get_burst_count())
211        } else {
212            Ok(0)
213        }
214    }
215
216    async fn get_runtime_stats(&self) -> ServiceResult<RuntimeStats> {
217        trace!(target: "feagi-services", "Getting runtime statistics");
218
219        let burst_count = if let Some(ref runner) = self.burst_runner {
220            runner.read().get_burst_count()
221        } else {
222            0
223        };
224
225        // TODO: Implement detailed runtime statistics in BurstLoopRunner
226        // For now, return basic stats
227        Ok(RuntimeStats {
228            total_bursts: burst_count,
229            total_neurons_fired: 0,      // TODO: Track in BurstLoopRunner
230            total_processing_time_ms: 0, // TODO: Track in BurstLoopRunner
231            avg_burst_time_ms: 0.0,      // TODO: Track in BurstLoopRunner
232            avg_neurons_per_burst: 0.0,  // TODO: Track in BurstLoopRunner
233            current_rate_hz: 0.0,        // TODO: Track in BurstLoopRunner
234            peak_rate_hz: 0.0,           // TODO: Track in BurstLoopRunner
235            uptime_seconds: self.get_uptime_seconds(),
236        })
237    }
238
239    async fn get_memory_usage(&self) -> ServiceResult<MemoryUsage> {
240        trace!(target: "feagi-services", "Getting memory usage");
241
242        // TODO: Implement actual memory tracking
243        // For now, estimate based on neuron/synapse counts
244        let manager = self.connectome.read();
245
246        // CRITICAL: Read from StateManager's atomic cache - NO NPU lock, NO iteration
247        let state_manager_instance = feagi_state_manager::StateManager::instance();
248        let (neuron_count, synapse_count) =
249            if let Some(state_manager) = state_manager_instance.try_read() {
250                let core_state = state_manager.get_core_state();
251                (
252                    core_state.get_neuron_count() as usize,
253                    core_state.get_synapse_count() as usize,
254                )
255            } else {
256                // Fallback to ConnectomeManager if StateManager unavailable (shouldn't happen)
257                (manager.get_neuron_count(), manager.get_synapse_count())
258            };
259
260        // Rough estimates (actual sizes depend on NPU implementation)
261        let npu_neurons_bytes = neuron_count * 64; // ~64 bytes per neuron
262        let npu_synapses_bytes = synapse_count * 16; // ~16 bytes per synapse
263        let npu_total_bytes = npu_neurons_bytes + npu_synapses_bytes;
264
265        let connectome_metadata_bytes = manager.get_cortical_area_count() * 512 + // ~512 bytes per area metadata
266            manager.get_brain_region_ids().len() * 256; // ~256 bytes per region
267
268        let total_allocated_bytes = npu_total_bytes + connectome_metadata_bytes;
269
270        // System memory (requires platform-specific code)
271        let (system_total_bytes, system_available_bytes) = (0, 0); // TODO: Implement
272
273        Ok(MemoryUsage {
274            npu_neurons_bytes,
275            npu_synapses_bytes,
276            npu_total_bytes,
277            connectome_metadata_bytes,
278            total_allocated_bytes,
279            system_total_bytes,
280            system_available_bytes,
281        })
282    }
283
284    async fn get_capacity(&self) -> ServiceResult<CapacityInfo> {
285        trace!(target: "feagi-services", "Getting capacity information");
286
287        let manager = self.connectome.read();
288        let config = manager.get_config();
289
290        // CRITICAL: Read from StateManager's atomic cache - NO NPU lock, NO iteration
291        let state_manager_instance = feagi_state_manager::StateManager::instance();
292        let (current_neurons, current_synapses) =
293            if let Some(state_manager) = state_manager_instance.try_read() {
294                let core_state = state_manager.get_core_state();
295                (
296                    core_state.get_neuron_count() as usize,
297                    core_state.get_synapse_count() as usize,
298                )
299            } else {
300                (manager.get_neuron_count(), manager.get_synapse_count())
301            };
302
303        let max_neurons = config.max_neurons;
304        let neuron_utilization_percent = (current_neurons as f64 / max_neurons as f64) * 100.0;
305        let max_synapses = config.max_synapses;
306        let synapse_utilization_percent = (current_synapses as f64 / max_synapses as f64) * 100.0;
307
308        let current_cortical_areas = manager.get_cortical_area_count();
309        let max_cortical_areas = 10000; // TODO: Make this configurable
310
311        Ok(CapacityInfo {
312            current_neurons,
313            max_neurons,
314            neuron_utilization_percent,
315            current_synapses,
316            max_synapses,
317            synapse_utilization_percent,
318            current_cortical_areas,
319            max_cortical_areas,
320        })
321    }
322}