use crate::common::ApiState;
use crate::common::{ApiError, ApiResult, Json, State};
use serde_json::{json, Value};
use std::collections::HashMap;
#[utoipa::path(
get,
path = "/v1/physiology/",
tag = "physiology",
responses(
(status = 200, description = "Physiology parameters", body = HashMap<String, serde_json::Value>),
(status = 500, description = "Internal server error")
)
)]
pub async fn get_physiology(
State(state): State<ApiState>,
) -> ApiResult<Json<HashMap<String, Value>>> {
let _connectome_service = state.connectome_service.as_ref();
let runtime_service = state.runtime_service.as_ref();
let status = runtime_service
.get_status()
.await
.map_err(|e| ApiError::internal(format!("Failed to get runtime status: {}", e)))?;
let simulation_timestep = if status.frequency_hz > 0.0 {
1.0 / status.frequency_hz
} else {
0.0
};
let physiology = json!({
"simulation_timestep": simulation_timestep,
"max_age": 0,
"evolution_burst_count": 0,
"ipu_idle_threshold": 0,
"plasticity_queue_depth": 0,
"lifespan_mgmt_interval": 0,
"sleep_trigger_inactivity_window": 0,
"sleep_trigger_neural_activity_max": 0.0
});
let mut response = HashMap::new();
response.insert("physiology".to_string(), physiology);
Ok(Json(response))
}
#[utoipa::path(
put,
path = "/v1/physiology/",
tag = "physiology",
responses(
(status = 200, description = "Physiology updated", body = HashMap<String, serde_json::Value>),
(status = 500, description = "Internal server error")
)
)]
pub async fn put_physiology(
State(state): State<ApiState>,
Json(request): Json<HashMap<String, Value>>,
) -> ApiResult<Json<HashMap<String, Value>>> {
let allowed_keys = [
"simulation_timestep",
"max_age",
"evolution_burst_count",
"ipu_idle_threshold",
"plasticity_queue_depth",
"lifespan_mgmt_interval",
"sleep_trigger_inactivity_window",
"sleep_trigger_neural_activity_max",
];
let updates = request
.get("physiology")
.and_then(|v| v.as_object())
.map(|obj| {
obj.iter()
.filter(|(k, _)| allowed_keys.contains(&k.as_str()))
.map(|(k, v)| (k.clone(), v.clone()))
.collect::<HashMap<String, Value>>()
})
.unwrap_or_default();
if updates.is_empty() {
return Ok(Json(HashMap::from([
("success".to_string(), json!(false)),
("updated".to_string(), json!({})),
(
"message".to_string(),
json!("No valid physiology parameters provided"),
),
])));
}
if let Some(timestep) = updates.get("simulation_timestep").and_then(|v| v.as_f64()) {
let runtime_service = state.runtime_service.as_ref();
if timestep > 0.0 {
let frequency = 1.0 / timestep;
runtime_service
.set_frequency(frequency)
.await
.map_err(|e| ApiError::internal(format!("Failed to set timestep: {}", e)))?;
tracing::info!(target: "feagi-api", "Updated simulation timestep to {:.6}s ({:.2} Hz)",
timestep, frequency);
}
}
tracing::info!(target: "feagi-api", "Physiology parameters updated: {:?}",
updates.keys().collect::<Vec<_>>());
let mut response = HashMap::new();
response.insert("success".to_string(), json!(true));
response.insert("updated".to_string(), json!(updates));
Ok(Json(response))
}