feagi_api/endpoints/
physiology.rs1use crate::common::ApiState;
12use crate::common::{ApiError, ApiResult, Json, State};
13use serde_json::{json, Value};
15use std::collections::HashMap;
16
17#[utoipa::path(
23 get,
24 path = "/v1/physiology/",
25 tag = "physiology",
26 responses(
27 (status = 200, description = "Physiology parameters", body = HashMap<String, serde_json::Value>),
28 (status = 500, description = "Internal server error")
29 )
30)]
31pub async fn get_physiology(
32 State(state): State<ApiState>,
33) -> ApiResult<Json<HashMap<String, Value>>> {
34 let _connectome_service = state.connectome_service.as_ref();
36
37 let runtime_service = state.runtime_service.as_ref();
39 let status = runtime_service
40 .get_status()
41 .await
42 .map_err(|e| ApiError::internal(format!("Failed to get runtime status: {}", e)))?;
43
44 let simulation_timestep = if status.frequency_hz > 0.0 {
45 1.0 / status.frequency_hz
46 } else {
47 0.0
48 };
49
50 let physiology = json!({
52 "simulation_timestep": simulation_timestep,
53 "max_age": 0,
54 "evolution_burst_count": 0,
55 "ipu_idle_threshold": 0,
56 "plasticity_queue_depth": 0,
57 "lifespan_mgmt_interval": 0,
58 "sleep_trigger_inactivity_window": 0,
59 "sleep_trigger_neural_activity_max": 0.0
60 });
61
62 let mut response = HashMap::new();
63 response.insert("physiology".to_string(), physiology);
64
65 Ok(Json(response))
66}
67
68#[utoipa::path(
70 put,
71 path = "/v1/physiology/",
72 tag = "physiology",
73 responses(
74 (status = 200, description = "Physiology updated", body = HashMap<String, serde_json::Value>),
75 (status = 500, description = "Internal server error")
76 )
77)]
78pub async fn put_physiology(
79 State(state): State<ApiState>,
80 Json(request): Json<HashMap<String, Value>>,
81) -> ApiResult<Json<HashMap<String, Value>>> {
82 let allowed_keys = [
84 "simulation_timestep",
85 "max_age",
86 "evolution_burst_count",
87 "ipu_idle_threshold",
88 "plasticity_queue_depth",
89 "lifespan_mgmt_interval",
90 "sleep_trigger_inactivity_window",
91 "sleep_trigger_neural_activity_max",
92 ];
93
94 let updates = request
96 .get("physiology")
97 .and_then(|v| v.as_object())
98 .map(|obj| {
99 obj.iter()
100 .filter(|(k, _)| allowed_keys.contains(&k.as_str()))
101 .map(|(k, v)| (k.clone(), v.clone()))
102 .collect::<HashMap<String, Value>>()
103 })
104 .unwrap_or_default();
105
106 if updates.is_empty() {
107 return Ok(Json(HashMap::from([
108 ("success".to_string(), json!(false)),
109 ("updated".to_string(), json!({})),
110 (
111 "message".to_string(),
112 json!("No valid physiology parameters provided"),
113 ),
114 ])));
115 }
116
117 if let Some(timestep) = updates.get("simulation_timestep").and_then(|v| v.as_f64()) {
119 let runtime_service = state.runtime_service.as_ref();
120 if timestep > 0.0 {
121 let frequency = 1.0 / timestep;
122 runtime_service
123 .set_frequency(frequency)
124 .await
125 .map_err(|e| ApiError::internal(format!("Failed to set timestep: {}", e)))?;
126 tracing::info!(target: "feagi-api", "Updated simulation timestep to {:.6}s ({:.2} Hz)",
127 timestep, frequency);
128 }
129 }
130
131 tracing::info!(target: "feagi-api", "Physiology parameters updated: {:?}",
133 updates.keys().collect::<Vec<_>>());
134
135 let mut response = HashMap::new();
136 response.insert("success".to_string(), json!(true));
137 response.insert("updated".to_string(), json!(updates));
138
139 Ok(Json(response))
140}