feagi_api/endpoints/
system.rs

1// Copyright 2025 Neuraville Inc.
2// Licensed under the Apache License, Version 2.0
3
4//! System API Endpoints - Exact port from Python `/v1/system/*`
5//!
6//! Reference: feagi-py/feagi/api/v1/system.py
7
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11use crate::common::ApiState;
12use crate::common::{ApiError, ApiResult, Json, State};
13
14// ============================================================================
15// REQUEST/RESPONSE MODELS (matching Python schemas exactly)
16// ============================================================================
17
18#[allow(non_snake_case)] // Field name matches Python API for compatibility
19#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
20pub struct HealthCheckResponse {
21    pub burst_engine: bool,
22    pub connected_agents: Option<i32>,
23    pub influxdb_availability: bool,
24    pub neuron_count_max: i64,
25    pub synapse_count_max: i64,
26    pub latest_changes_saved_externally: bool,
27    pub genome_availability: bool,
28    pub genome_validity: Option<bool>,
29    pub brain_readiness: bool,
30    pub feagi_session: Option<i64>,
31    pub fitness: Option<f64>,
32    pub cortical_area_count: Option<i32>,
33    pub neuron_count: Option<i64>,
34    pub memory_neuron_count: Option<i64>,
35    pub regular_neuron_count: Option<i64>,
36    pub synapse_count: Option<i64>,
37    pub estimated_brain_size_in_MB: Option<f64>,
38    pub genome_num: Option<i32>,
39    pub genome_timestamp: Option<i64>,
40    pub simulation_timestep: Option<f64>,
41    pub memory_area_stats: Option<HashMap<String, HashMap<String, serde_json::Value>>>,
42    pub amalgamation_pending: Option<HashMap<String, serde_json::Value>>,
43    /// Root brain region ID (UUID string) for O(1) root lookup
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub brain_regions_root: Option<String>,
46}
47
48// ============================================================================
49// ENDPOINTS
50// ============================================================================
51
52/// Get comprehensive system health including burst engine status, neuron/synapse counts, and genome availability.
53#[utoipa::path(
54    get,
55    path = "/v1/system/health_check",
56    responses(
57        (status = 200, description = "System health retrieved successfully", body = HealthCheckResponse),
58        (status = 500, description = "Internal server error")
59    ),
60    tag = "system"
61)]
62pub async fn get_health_check(
63    State(state): State<ApiState>,
64) -> ApiResult<Json<HealthCheckResponse>> {
65    let analytics_service = state.analytics_service.as_ref();
66
67    // Get system health from analytics service
68    let health = analytics_service
69        .get_system_health()
70        .await
71        .map_err(|e| ApiError::internal(format!("Failed to get system health: {}", e)))?;
72
73    // Get runtime status if available
74    let burst_engine_active = state
75        .runtime_service
76        .get_status()
77        .await
78        .map(|status| status.is_running)
79        .unwrap_or(false);
80
81    let _burst_count = state.runtime_service.get_burst_count().await.ok();
82
83    // Get connected agents count from agent service
84    let connected_agents = if let Some(agent_service) = state.agent_service.as_ref() {
85        agent_service
86            .list_agents()
87            .await
88            .ok()
89            .map(|agents| agents.len() as i32)
90    } else {
91        None
92    };
93
94    // Get total synapse count from analytics service
95    let synapse_count = analytics_service
96        .get_total_synapse_count()
97        .await
98        .ok()
99        .map(|count| count as i64);
100
101    // Get regular and memory neuron counts
102    let regular_neuron_count = analytics_service
103        .get_regular_neuron_count()
104        .await
105        .ok()
106        .map(|count| count as i64);
107
108    let memory_neuron_count = analytics_service
109        .get_memory_neuron_count()
110        .await
111        .ok()
112        .map(|count| count as i64);
113
114    // Get genome info for simulation_timestep, genome_num, and genome_timestamp
115    let genome_info = state.genome_service.get_genome_info().await.ok();
116
117    let simulation_timestep = genome_info.as_ref().map(|info| info.simulation_timestep);
118    let genome_num = genome_info.as_ref().and_then(|info| info.genome_num);
119    let genome_timestamp = genome_info.as_ref().and_then(|info| info.genome_timestamp);
120
121    // Calculate estimated brain size in MB
122    // Rough estimates: ~64 bytes per neuron + ~16 bytes per synapse + metadata
123    #[allow(non_snake_case)] // Matching Python API field name for compatibility
124    let estimated_brain_size_in_MB = {
125        let neuron_bytes = health.neuron_count * 64;
126        let synapse_bytes = synapse_count.unwrap_or(0) as usize * 16;
127        let metadata_bytes = health.cortical_area_count * 512; // ~512 bytes per area
128        let total_bytes = neuron_bytes + synapse_bytes + metadata_bytes;
129        Some((total_bytes as f64) / (1024.0 * 1024.0))
130    };
131
132    // Get actual NPU capacity from SystemHealth (single source of truth from config)
133    let neuron_count_max = health.neuron_capacity as i64;
134    let synapse_count_max = health.synapse_capacity as i64;
135
136    // Configuration values (should eventually come from config service)
137    let influxdb_availability = false; // TODO: Get from monitoring service
138    let latest_changes_saved_externally = false; // TODO: Get from state manager
139    let genome_availability = health.cortical_area_count > 0;
140    let genome_validity = Some(health.brain_readiness);
141
142    // Get FEAGI session timestamp (unique identifier for this FEAGI instance)
143    let feagi_session = Some(state.feagi_session_timestamp);
144
145    // Fields requiring future service implementations
146    let fitness = None; // TODO: Get from evolution service
147    let memory_area_stats = None; // TODO: Requires memory area analysis
148    let amalgamation_pending = None; // TODO: Get from evolution/genome merging service
149
150    // Get root region ID from ConnectomeManager (only available when services feature is enabled)
151    #[cfg(feature = "services")]
152    let brain_regions_root = feagi_brain_development::ConnectomeManager::instance()
153        .read()
154        .get_root_region_id();
155    #[cfg(not(feature = "services"))]
156    let brain_regions_root = None; // WASM: Use connectome service instead
157
158    Ok(Json(HealthCheckResponse {
159        burst_engine: burst_engine_active,
160        connected_agents,
161        influxdb_availability,
162        neuron_count_max,
163        synapse_count_max,
164        latest_changes_saved_externally,
165        genome_availability,
166        genome_validity,
167        brain_readiness: health.brain_readiness,
168        feagi_session,
169        fitness,
170        cortical_area_count: Some(health.cortical_area_count as i32),
171        neuron_count: Some(health.neuron_count as i64),
172        memory_neuron_count,
173        regular_neuron_count,
174        synapse_count,
175        estimated_brain_size_in_MB,
176        genome_num,
177        genome_timestamp,
178        simulation_timestep,
179        memory_area_stats,
180        amalgamation_pending,
181        brain_regions_root, // NEW: Root region ID for O(1) lookup
182    }))
183}
184
185/// Get the visualization skip rate (how many frames to skip during visualization).
186#[utoipa::path(
187    get,
188    path = "/v1/system/cortical_area_visualization_skip_rate",
189    responses(
190        (status = 200, description = "Skip rate retrieved successfully", body = i32),
191        (status = 500, description = "Internal server error")
192    ),
193    tag = "system"
194)]
195pub async fn get_cortical_area_visualization_skip_rate(
196    State(_state): State<ApiState>,
197) -> ApiResult<Json<i32>> {
198    // TODO: Get from visualization config service
199    // For now return default value
200    Ok(Json(1))
201}
202
203/// Set the visualization skip rate to reduce visualization frequency and improve performance.
204#[utoipa::path(
205    put,
206    path = "/v1/system/cortical_area_visualization_skip_rate",
207    request_body = i32,
208    responses(
209        (status = 200, description = "Skip rate updated successfully"),
210        (status = 500, description = "Internal server error")
211    ),
212    tag = "system"
213)]
214pub async fn set_cortical_area_visualization_skip_rate(
215    State(_state): State<ApiState>,
216    Json(skip_rate): Json<i32>,
217) -> ApiResult<Json<serde_json::Value>> {
218    // TODO: Set in visualization config service
219    Ok(Json(serde_json::json!({
220        "message": format!("Skip rate set to {}", skip_rate)
221    })))
222}
223
224/// Get the threshold below which cortical areas are suppressed from visualization.
225#[utoipa::path(
226    get,
227    path = "/v1/system/cortical_area_visualization_suppression_threshold",
228    responses(
229        (status = 200, description = "Threshold retrieved successfully", body = i32),
230        (status = 500, description = "Internal server error")
231    ),
232    tag = "system"
233)]
234pub async fn get_cortical_area_visualization_suppression_threshold(
235    State(_state): State<ApiState>,
236) -> ApiResult<Json<i32>> {
237    // TODO: Get from visualization config service
238    // For now return default value
239    Ok(Json(0))
240}
241
242/// Set the threshold for suppressing low-activity cortical areas from visualization.
243#[utoipa::path(
244    put,
245    path = "/v1/system/cortical_area_visualization_suppression_threshold",
246    request_body = i32,
247    responses(
248        (status = 200, description = "Threshold updated successfully"),
249        (status = 500, description = "Internal server error")
250    ),
251    tag = "system"
252)]
253pub async fn set_cortical_area_visualization_suppression_threshold(
254    State(_state): State<ApiState>,
255    Json(threshold): Json<i32>,
256) -> ApiResult<Json<serde_json::Value>> {
257    // TODO: Set in visualization config service
258    Ok(Json(serde_json::json!({
259        "message": format!("Suppression threshold set to {}", threshold)
260    })))
261}
262
263// ============================================================================
264// SYSTEM VERSION & INFO ENDPOINTS
265// ============================================================================
266
267/// Get the current FEAGI version string.
268#[utoipa::path(
269    get,
270    path = "/v1/system/version",
271    tag = "system",
272    responses(
273        (status = 200, description = "Version string", body = String)
274    )
275)]
276pub async fn get_version(State(_state): State<ApiState>) -> ApiResult<Json<String>> {
277    Ok(Json(env!("CARGO_PKG_VERSION").to_string()))
278}
279
280/// Get detailed version information for all FEAGI crates and components.
281#[utoipa::path(
282    get,
283    path = "/v1/system/versions",
284    tag = "system",
285    responses(
286        (status = 200, description = "Version information", body = HashMap<String, String>)
287    )
288)]
289pub async fn get_versions(
290    State(state): State<ApiState>,
291) -> ApiResult<Json<HashMap<String, String>>> {
292    // Use system service to get version information
293    // The application (feagi-rust) provides this at startup with all crates it was compiled with
294    match state.system_service.get_version().await {
295        Ok(version_info) => {
296            let mut versions = version_info.crates.clone();
297
298            // Add build metadata
299            versions.insert("rust".to_string(), version_info.rust_version);
300            versions.insert("build_timestamp".to_string(), version_info.build_timestamp);
301
302            Ok(Json(versions))
303        }
304        Err(e) => {
305            // Fallback to minimal version info
306            tracing::warn!(
307                "Failed to get version from system service: {}, using fallback",
308                e
309            );
310            let mut versions = HashMap::new();
311            versions.insert(
312                "error".to_string(),
313                "system service unavailable".to_string(),
314            );
315            Ok(Json(versions))
316        }
317    }
318}
319
320/// Get system configuration including API settings, neuron capacity, and synapse limits.
321#[utoipa::path(
322    get,
323    path = "/v1/system/configuration",
324    tag = "system",
325    responses(
326        (status = 200, description = "System configuration", body = HashMap<String, serde_json::Value>)
327    )
328)]
329pub async fn get_configuration(
330    State(state): State<ApiState>,
331) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
332    // Get actual NPU capacity from analytics service
333    let health = state
334        .analytics_service
335        .get_system_health()
336        .await
337        .map_err(|e| ApiError::internal(format!("Failed to get system health: {}", e)))?;
338
339    let mut config = HashMap::new();
340    config.insert("api_host".to_string(), serde_json::json!("0.0.0.0"));
341    config.insert("api_port".to_string(), serde_json::json!(8000));
342    // Use actual NPU capacity from system health (NOT hardcoded values)
343    config.insert(
344        "max_neurons".to_string(),
345        serde_json::json!(health.neuron_capacity),
346    );
347    config.insert(
348        "max_synapses".to_string(),
349        serde_json::json!(health.synapse_capacity),
350    );
351
352    Ok(Json(config))
353}
354
355/// Get user preferences including advanced mode, UI magnification, and auto-creation settings.
356#[utoipa::path(
357    get,
358    path = "/v1/system/user_preferences",
359    tag = "system",
360    responses(
361        (status = 200, description = "User preferences", body = HashMap<String, serde_json::Value>)
362    )
363)]
364pub async fn get_user_preferences(
365    State(_state): State<ApiState>,
366) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
367    let mut prefs = HashMap::new();
368    prefs.insert("adv_mode".to_string(), serde_json::json!(false));
369    prefs.insert("ui_magnification".to_string(), serde_json::json!(1.0));
370    prefs.insert(
371        "auto_pns_area_creation".to_string(),
372        serde_json::json!(true),
373    );
374
375    Ok(Json(prefs))
376}
377
378/// Update user preferences for UI customization and behavior settings.
379#[utoipa::path(
380    put,
381    path = "/v1/system/user_preferences",
382    tag = "system",
383    responses(
384        (status = 200, description = "Preferences updated", body = HashMap<String, String>)
385    )
386)]
387pub async fn put_user_preferences(
388    State(_state): State<ApiState>,
389    Json(_prefs): Json<HashMap<String, serde_json::Value>>,
390) -> ApiResult<Json<HashMap<String, String>>> {
391    Ok(Json(HashMap::from([(
392        "message".to_string(),
393        "User preferences updated successfully".to_string(),
394    )])))
395}
396
397/// Get list of available cortical area types (Sensory, Motor, Custom, Memory, Core).
398#[utoipa::path(
399    get,
400    path = "/v1/system/cortical_area_types",
401    tag = "system",
402    responses(
403        (status = 200, description = "Cortical area types", body = Vec<String>)
404    )
405)]
406pub async fn get_cortical_area_types_list(
407    State(_state): State<ApiState>,
408) -> ApiResult<Json<Vec<String>>> {
409    Ok(Json(vec![
410        "Sensory".to_string(),
411        "Motor".to_string(),
412        "Custom".to_string(),
413        "Memory".to_string(),
414        "Core".to_string(),
415    ]))
416}
417
418/// Enable the Fire Queue (FQ) sampler for visualization data streaming.
419#[utoipa::path(
420    post,
421    path = "/v1/system/enable_visualization_fq_sampler",
422    tag = "system",
423    responses(
424        (status = 200, description = "FQ sampler enabled", body = HashMap<String, String>)
425    )
426)]
427pub async fn post_enable_visualization_fq_sampler(
428    State(state): State<ApiState>,
429) -> ApiResult<Json<HashMap<String, String>>> {
430    let runtime_service = state.runtime_service.as_ref();
431
432    runtime_service
433        .set_fcl_sampler_config(None, Some(1))
434        .await
435        .map_err(|e| ApiError::internal(format!("Failed to enable FQ sampler: {}", e)))?;
436
437    Ok(Json(HashMap::from([(
438        "message".to_string(),
439        "Visualization FQ sampler enabled".to_string(),
440    )])))
441}
442
443/// Disable the Fire Queue (FQ) sampler to stop visualization data streaming.
444#[utoipa::path(
445    post,
446    path = "/v1/system/disable_visualization_fq_sampler",
447    tag = "system",
448    responses(
449        (status = 200, description = "FQ sampler disabled", body = HashMap<String, String>)
450    )
451)]
452pub async fn post_disable_visualization_fq_sampler(
453    State(state): State<ApiState>,
454) -> ApiResult<Json<HashMap<String, String>>> {
455    let runtime_service = state.runtime_service.as_ref();
456
457    runtime_service
458        .set_fcl_sampler_config(None, Some(0))
459        .await
460        .map_err(|e| ApiError::internal(format!("Failed to disable FQ sampler: {}", e)))?;
461
462    Ok(Json(HashMap::from([(
463        "message".to_string(),
464        "Visualization FQ sampler disabled".to_string(),
465    )])))
466}
467
468/// Get Fire Candidate List (FCL) sampler status including frequency and consumer state.
469#[utoipa::path(
470    get,
471    path = "/v1/system/fcl_status",
472    tag = "system",
473    responses(
474        (status = 200, description = "FCL status", body = HashMap<String, serde_json::Value>)
475    )
476)]
477pub async fn get_fcl_status_system(
478    State(state): State<ApiState>,
479) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
480    let runtime_service = state.runtime_service.as_ref();
481
482    let (frequency, consumer) = runtime_service
483        .get_fcl_sampler_config()
484        .await
485        .map_err(|e| ApiError::internal(format!("Failed to get FCL status: {}", e)))?;
486
487    let mut response = HashMap::new();
488    response.insert("available".to_string(), serde_json::json!(true));
489    response.insert("frequency".to_string(), serde_json::json!(frequency));
490    response.insert("consumer".to_string(), serde_json::json!(consumer));
491    response.insert("enabled".to_string(), serde_json::json!(consumer > 0));
492
493    Ok(Json(response))
494}
495
496/// Reset the Fire Candidate List (FCL) to clear all pending fire candidates.
497#[utoipa::path(
498    post,
499    path = "/v1/system/fcl_reset",
500    tag = "system",
501    responses(
502        (status = 200, description = "FCL reset", body = HashMap<String, String>)
503    )
504)]
505pub async fn post_fcl_reset_system(
506    State(_state): State<ApiState>,
507) -> ApiResult<Json<HashMap<String, String>>> {
508    tracing::info!(target: "feagi-api", "FCL reset requested");
509
510    Ok(Json(HashMap::from([(
511        "message".to_string(),
512        "FCL reset successfully".to_string(),
513    )])))
514}
515
516/// Get status of active system processes including burst engine and API server.
517#[utoipa::path(
518    get,
519    path = "/v1/system/processes",
520    tag = "system",
521    responses(
522        (status = 200, description = "Active processes", body = HashMap<String, serde_json::Value>)
523    )
524)]
525pub async fn get_processes(
526    State(state): State<ApiState>,
527) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
528    let runtime_service = state.runtime_service.as_ref();
529
530    let status = runtime_service
531        .get_status()
532        .await
533        .map_err(|e| ApiError::internal(format!("Failed to get processes: {}", e)))?;
534
535    let mut processes = HashMap::new();
536    processes.insert(
537        "burst_engine".to_string(),
538        serde_json::json!({
539            "active": status.is_running,
540            "paused": status.is_paused
541        }),
542    );
543    processes.insert(
544        "api_server".to_string(),
545        serde_json::json!({"active": true}),
546    );
547
548    Ok(Json(processes))
549}
550
551/// Get collection of unique log messages for debugging and monitoring.
552#[utoipa::path(
553    get,
554    path = "/v1/system/unique_logs",
555    tag = "system",
556    responses(
557        (status = 200, description = "Unique logs", body = HashMap<String, Vec<String>>)
558    )
559)]
560pub async fn get_unique_logs(
561    State(_state): State<ApiState>,
562) -> ApiResult<Json<HashMap<String, Vec<String>>>> {
563    let mut response = HashMap::new();
564    response.insert("logs".to_string(), Vec::new());
565
566    Ok(Json(response))
567}
568
569/// Configure logging settings including log level and output destinations.
570#[utoipa::path(
571    post,
572    path = "/v1/system/logs",
573    tag = "system",
574    responses(
575        (status = 200, description = "Log config updated", body = HashMap<String, String>)
576    )
577)]
578pub async fn post_logs(
579    State(_state): State<ApiState>,
580    Json(_config): Json<HashMap<String, serde_json::Value>>,
581) -> ApiResult<Json<HashMap<String, String>>> {
582    Ok(Json(HashMap::from([(
583        "message".to_string(),
584        "Log configuration updated".to_string(),
585    )])))
586}
587
588/// Get list of all beacon subscribers currently monitoring system events.
589#[utoipa::path(
590    get,
591    path = "/v1/system/beacon/subscribers",
592    tag = "system",
593    responses(
594        (status = 200, description = "Beacon subscribers", body = Vec<String>)
595    )
596)]
597pub async fn get_beacon_subscribers(
598    State(_state): State<ApiState>,
599) -> ApiResult<Json<Vec<String>>> {
600    Ok(Json(Vec::new()))
601}
602
603/// Subscribe to system beacon for event notifications and status updates.
604#[utoipa::path(
605    post,
606    path = "/v1/system/beacon/subscribe",
607    tag = "system",
608    responses(
609        (status = 200, description = "Subscribed", body = HashMap<String, String>)
610    )
611)]
612pub async fn post_beacon_subscribe(
613    State(_state): State<ApiState>,
614    Json(_request): Json<HashMap<String, String>>,
615) -> ApiResult<Json<HashMap<String, String>>> {
616    Ok(Json(HashMap::from([(
617        "message".to_string(),
618        "Subscribed to beacon".to_string(),
619    )])))
620}
621
622/// Unsubscribe from system beacon to stop receiving event notifications.
623#[utoipa::path(
624    delete,
625    path = "/v1/system/beacon/unsubscribe",
626    tag = "system",
627    responses(
628        (status = 200, description = "Unsubscribed", body = HashMap<String, String>)
629    )
630)]
631pub async fn delete_beacon_unsubscribe(
632    State(_state): State<ApiState>,
633    Json(_request): Json<HashMap<String, String>>,
634) -> ApiResult<Json<HashMap<String, String>>> {
635    Ok(Json(HashMap::from([(
636        "message".to_string(),
637        "Unsubscribed from beacon".to_string(),
638    )])))
639}
640
641/// Get global activity visualization configuration including enabled state and frequency.
642#[utoipa::path(
643    get,
644    path = "/v1/system/global_activity_visualization",
645    tag = "system",
646    responses(
647        (status = 200, description = "Global activity viz status", body = HashMap<String, serde_json::Value>)
648    )
649)]
650pub async fn get_global_activity_visualization(
651    State(_state): State<ApiState>,
652) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
653    let mut response = HashMap::new();
654    response.insert("enabled".to_string(), serde_json::json!(false));
655    response.insert("frequency_hz".to_string(), serde_json::json!(30.0));
656
657    Ok(Json(response))
658}
659
660/// Configure global activity visualization settings and frequency.
661#[utoipa::path(
662    put,
663    path = "/v1/system/global_activity_visualization",
664    tag = "system",
665    responses(
666        (status = 200, description = "Configured", body = HashMap<String, String>)
667    )
668)]
669pub async fn put_global_activity_visualization(
670    State(_state): State<ApiState>,
671    Json(_config): Json<HashMap<String, serde_json::Value>>,
672) -> ApiResult<Json<HashMap<String, String>>> {
673    Ok(Json(HashMap::from([(
674        "message".to_string(),
675        "Global activity visualization configured".to_string(),
676    )])))
677}
678
679/// Set the file system path for the circuit library storage location.
680#[utoipa::path(
681    post,
682    path = "/v1/system/circuit_library_path",
683    tag = "system",
684    responses(
685        (status = 200, description = "Path set", body = HashMap<String, String>)
686    )
687)]
688pub async fn post_circuit_library_path(
689    State(_state): State<ApiState>,
690    Json(_request): Json<HashMap<String, String>>,
691) -> ApiResult<Json<HashMap<String, String>>> {
692    Ok(Json(HashMap::from([(
693        "message".to_string(),
694        "Circuit library path updated".to_string(),
695    )])))
696}
697
698/// Test connectivity to InfluxDB database for time-series data storage.
699#[utoipa::path(
700    get,
701    path = "/v1/system/db/influxdb/test",
702    tag = "system",
703    responses(
704        (status = 200, description = "Test result", body = HashMap<String, bool>)
705    )
706)]
707pub async fn get_influxdb_test(
708    State(_state): State<ApiState>,
709) -> ApiResult<Json<HashMap<String, bool>>> {
710    let mut response = HashMap::new();
711    response.insert("connected".to_string(), false);
712    response.insert("available".to_string(), false);
713
714    Ok(Json(response))
715}
716
717/// Register a new system component or module with FEAGI.
718#[utoipa::path(
719    post,
720    path = "/v1/system/register",
721    tag = "system",
722    responses(
723        (status = 200, description = "Registered", body = HashMap<String, String>)
724    )
725)]
726pub async fn post_register_system(
727    State(_state): State<ApiState>,
728    Json(_request): Json<HashMap<String, serde_json::Value>>,
729) -> ApiResult<Json<HashMap<String, String>>> {
730    Ok(Json(HashMap::from([(
731        "message".to_string(),
732        "System component registered".to_string(),
733    )])))
734}