1use crate::common::ApiState;
8use crate::common::{ApiError, ApiResult, Json, Path, Query, State};
9use crate::endpoints::cortical_area::synapse_details_for_neuron;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use tracing::warn;
13use utoipa::{IntoParams, ToSchema};
14
15#[derive(Debug, Clone, Deserialize, IntoParams, ToSchema)]
17#[into_params(parameter_in = Query)]
18pub struct MemoryNeuronQuery {
19 pub neuron_id: u32,
21}
22
23#[derive(Debug, Clone, Serialize, ToSchema)]
25pub struct MemoryNeuronDetailResponse {
26 pub neuron_id: u64,
27 pub cortical_idx: u32,
28 pub cortical_id: String,
29 pub cortical_name: String,
30 pub pattern_hash: Option<u64>,
31 pub is_longterm_memory: bool,
32 pub is_active: bool,
33 pub lifespan_current: u32,
34 pub lifespan_initial: u32,
35 pub lifespan_growth_rate: f32,
36 pub creation_burst: u64,
37 pub last_activation_burst: u64,
38 pub activation_count: u32,
39 pub outgoing_synapse_count: usize,
40 pub incoming_synapse_count: usize,
41 pub outgoing_synapses: serde_json::Value,
42 pub incoming_synapses: serde_json::Value,
43}
44
45#[utoipa::path(
47 get,
48 path = "/v1/connectome/cortical_areas/list/detailed",
49 tag = "connectome",
50 responses(
51 (status = 200, description = "Detailed cortical areas list", body = HashMap<String, serde_json::Value>),
52 (status = 500, description = "Internal server error")
53 )
54)]
55pub async fn get_cortical_areas_list_detailed(
56 State(state): State<ApiState>,
57) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
58 let connectome_service = state.connectome_service.as_ref();
59 match connectome_service.list_cortical_areas().await {
60 Ok(areas) => {
61 tracing::info!(target: "feagi-api",
62 "[DETAILED-LIST] Returning {} cortical areas", areas.len()
63 );
64
65 let detailed: HashMap<String, serde_json::Value> = areas
66 .into_iter()
67 .map(|area| {
68 tracing::debug!(target: "feagi-api",
69 "[DETAILED-LIST] Area {}: cortical_type='{}', is_mem_type={:?}",
70 area.cortical_id, area.cortical_type,
71 area.properties.get("is_mem_type")
72 );
73
74 let json_value = serde_json::to_value(&area).unwrap_or_default();
75
76 tracing::debug!(target: "feagi-api",
77 "[DETAILED-LIST] Serialized area {} has cortical_type: {}",
78 area.cortical_id, json_value.get("cortical_type").is_some()
79 );
80
81 (area.cortical_id.clone(), json_value)
82 })
83 .collect();
84 Ok(Json(detailed))
85 }
86 Err(e) => Err(ApiError::internal(format!(
87 "Failed to get detailed list: {}",
88 e
89 ))),
90 }
91}
92
93#[utoipa::path(get, path = "/v1/connectome/properties/dimensions", tag = "connectome")]
95pub async fn get_properties_dimensions(
96 State(_state): State<ApiState>,
97) -> ApiResult<Json<(usize, usize, usize)>> {
98 Ok(Json((0, 0, 0)))
101}
102
103#[utoipa::path(get, path = "/v1/connectome/properties/mappings", tag = "connectome")]
105pub async fn get_properties_mappings(
106 State(_state): State<ApiState>,
107) -> ApiResult<Json<HashMap<String, Vec<String>>>> {
108 Ok(Json(HashMap::new()))
110}
111
112#[utoipa::path(get, path = "/v1/connectome/snapshot", tag = "connectome", responses((status = 200, body = HashMap<String, serde_json::Value>)))]
114pub async fn get_snapshot(
115 State(state): State<ApiState>,
116) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
117 let connectome_service = state.connectome_service.as_ref();
118 let areas = connectome_service
119 .list_cortical_areas()
120 .await
121 .map_err(|e| ApiError::internal(format!("{}", e)))?;
122 let regions = connectome_service
123 .list_brain_regions()
124 .await
125 .map_err(|e| ApiError::internal(format!("{}", e)))?;
126 let mut response = HashMap::new();
127 response.insert(
128 "cortical_area_count".to_string(),
129 serde_json::json!(areas.len()),
130 );
131 response.insert(
132 "brain_region_count".to_string(),
133 serde_json::json!(regions.len()),
134 );
135 Ok(Json(response))
136}
137
138#[utoipa::path(get, path = "/v1/connectome/stats", tag = "connectome", responses((status = 200, body = HashMap<String, serde_json::Value>)))]
140pub async fn get_stats(
141 State(state): State<ApiState>,
142) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
143 let analytics_service = state.analytics_service.as_ref();
144 let health = analytics_service
145 .get_system_health()
146 .await
147 .map_err(|e| ApiError::internal(format!("{}", e)))?;
148 let mut response = HashMap::new();
149 response.insert(
150 "neuron_count".to_string(),
151 serde_json::json!(health.neuron_count),
152 );
153 response.insert(
154 "cortical_area_count".to_string(),
155 serde_json::json!(health.cortical_area_count),
156 );
157 Ok(Json(response))
158}
159
160#[utoipa::path(
162 post,
163 path = "/v1/connectome/batch_neuron_operations",
164 tag = "connectome"
165)]
166pub async fn post_batch_neuron_operations(
167 State(_state): State<ApiState>,
168 Json(_ops): Json<Vec<HashMap<String, serde_json::Value>>>,
169) -> ApiResult<Json<HashMap<String, i32>>> {
170 let mut response = HashMap::new();
171 response.insert("processed".to_string(), 0);
172 Ok(Json(response))
173}
174
175#[utoipa::path(
177 post,
178 path = "/v1/connectome/batch_synapse_operations",
179 tag = "connectome"
180)]
181pub async fn post_batch_synapse_operations(
182 State(_state): State<ApiState>,
183 Json(_ops): Json<Vec<HashMap<String, serde_json::Value>>>,
184) -> ApiResult<Json<HashMap<String, i32>>> {
185 let mut response = HashMap::new();
186 response.insert("processed".to_string(), 0);
187 Ok(Json(response))
188}
189
190#[utoipa::path(get, path = "/v1/connectome/neuron_count", tag = "connectome")]
192pub async fn get_neuron_count(State(state): State<ApiState>) -> ApiResult<Json<i64>> {
193 let analytics = state.analytics_service.as_ref();
194 let health = analytics
195 .get_system_health()
196 .await
197 .map_err(|e| ApiError::internal(format!("{}", e)))?;
198 Ok(Json(health.neuron_count as i64))
199}
200
201#[utoipa::path(get, path = "/v1/connectome/synapse_count", tag = "connectome")]
203pub async fn get_synapse_count(State(_state): State<ApiState>) -> ApiResult<Json<i64>> {
204 Ok(Json(0))
205}
206
207#[utoipa::path(get, path = "/v1/connectome/paths", tag = "connectome")]
209pub async fn get_paths(
210 State(_state): State<ApiState>,
211 Query(_params): Query<HashMap<String, String>>,
212) -> ApiResult<Json<Vec<Vec<String>>>> {
213 Ok(Json(Vec::new()))
214}
215
216#[utoipa::path(get, path = "/v1/connectome/cumulative_stats", tag = "connectome")]
218pub async fn get_cumulative_stats(
219 State(_state): State<ApiState>,
220) -> ApiResult<Json<HashMap<String, i64>>> {
221 let mut response = HashMap::new();
222 response.insert("total_bursts".to_string(), 0);
223 Ok(Json(response))
224}
225
226#[utoipa::path(get, path = "/v1/connectome/area_details", tag = "connectome")]
228pub async fn get_area_details(
229 State(state): State<ApiState>,
230 Query(params): Query<HashMap<String, String>>,
231) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
232 let area_ids_str = params
233 .get("area_ids")
234 .ok_or_else(|| ApiError::invalid_input("area_ids required"))?;
235 let area_ids: Vec<&str> = area_ids_str.split(',').collect();
236 let connectome_service = state.connectome_service.as_ref();
237 let mut details = HashMap::new();
238 for area_id in area_ids {
239 if let Ok(area) = connectome_service.get_cortical_area(area_id).await {
240 details.insert(
241 area_id.to_string(),
242 serde_json::json!({"cortical_id": area.cortical_id}),
243 );
244 }
245 }
246 Ok(Json(details))
247}
248
249#[utoipa::path(post, path = "/v1/connectome/rebuild", tag = "connectome")]
251pub async fn post_rebuild(
252 State(_state): State<ApiState>,
253) -> ApiResult<Json<HashMap<String, String>>> {
254 Ok(Json(HashMap::from([(
255 "message".to_string(),
256 "Not yet implemented".to_string(),
257 )])))
258}
259
260#[utoipa::path(get, path = "/v1/connectome/structure", tag = "connectome")]
262pub async fn get_structure(
263 State(state): State<ApiState>,
264) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
265 let connectome_service = state.connectome_service.as_ref();
266 let areas = connectome_service
267 .list_cortical_areas()
268 .await
269 .map_err(|e| ApiError::internal(format!("{}", e)))?;
270 let mut response = HashMap::new();
271 response.insert("cortical_areas".to_string(), serde_json::json!(areas.len()));
272 Ok(Json(response))
273}
274
275#[utoipa::path(post, path = "/v1/connectome/clear", tag = "connectome")]
277pub async fn post_clear(
278 State(_state): State<ApiState>,
279) -> ApiResult<Json<HashMap<String, String>>> {
280 Ok(Json(HashMap::from([(
281 "message".to_string(),
282 "Not yet implemented".to_string(),
283 )])))
284}
285
286#[utoipa::path(get, path = "/v1/connectome/validation", tag = "connectome")]
288pub async fn get_validation(
289 State(_state): State<ApiState>,
290) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
291 let mut response = HashMap::new();
292 response.insert("valid".to_string(), serde_json::json!(true));
293 Ok(Json(response))
294}
295
296#[utoipa::path(get, path = "/v1/connectome/topology", tag = "connectome")]
298pub async fn get_topology(
299 State(_state): State<ApiState>,
300) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
301 let mut response = HashMap::new();
302 response.insert("layers".to_string(), serde_json::json!(0));
303 Ok(Json(response))
304}
305
306#[utoipa::path(post, path = "/v1/connectome/optimize", tag = "connectome")]
308pub async fn post_optimize(
309 State(_state): State<ApiState>,
310) -> ApiResult<Json<HashMap<String, String>>> {
311 Ok(Json(HashMap::from([(
312 "message".to_string(),
313 "Not yet implemented".to_string(),
314 )])))
315}
316
317#[utoipa::path(get, path = "/v1/connectome/connectivity_matrix", tag = "connectome")]
319pub async fn get_connectivity_matrix(
320 State(_state): State<ApiState>,
321) -> ApiResult<Json<HashMap<String, Vec<Vec<i32>>>>> {
322 let mut response = HashMap::new();
323 response.insert("matrix".to_string(), Vec::new());
324 Ok(Json(response))
325}
326
327#[utoipa::path(post, path = "/v1/connectome/neurons/batch", tag = "connectome")]
329pub async fn post_neurons_batch(
330 State(_state): State<ApiState>,
331 Json(_ops): Json<Vec<HashMap<String, serde_json::Value>>>,
332) -> ApiResult<Json<HashMap<String, i32>>> {
333 let mut response = HashMap::new();
334 response.insert("processed".to_string(), 0);
335 Ok(Json(response))
336}
337
338#[utoipa::path(post, path = "/v1/connectome/synapses/batch", tag = "connectome")]
340pub async fn post_synapses_batch(
341 State(_state): State<ApiState>,
342 Json(_ops): Json<Vec<HashMap<String, serde_json::Value>>>,
343) -> ApiResult<Json<HashMap<String, i32>>> {
344 let mut response = HashMap::new();
345 response.insert("processed".to_string(), 0);
346 Ok(Json(response))
347}
348
349#[utoipa::path(
352 get,
353 path = "/v1/connectome/cortical_areas/list/summary",
354 tag = "connectome"
355)]
356pub async fn get_cortical_areas_list_summary(
357 State(state): State<ApiState>,
358) -> ApiResult<Json<Vec<HashMap<String, serde_json::Value>>>> {
359 let connectome_service = state.connectome_service.as_ref();
360 let areas = connectome_service
361 .list_cortical_areas()
362 .await
363 .map_err(|e| ApiError::internal(format!("{}", e)))?;
364 let summary: Vec<HashMap<String, serde_json::Value>> = areas
365 .iter()
366 .map(|a| {
367 let mut map = HashMap::new();
368 map.insert("cortical_id".to_string(), serde_json::json!(a.cortical_id));
369 map.insert("cortical_name".to_string(), serde_json::json!(a.name));
370 map
371 })
372 .collect();
373 Ok(Json(summary))
374}
375
376#[utoipa::path(
378 get,
379 path = "/v1/connectome/cortical_areas/list/transforming",
380 tag = "connectome"
381)]
382pub async fn get_cortical_areas_list_transforming(
383 State(_state): State<ApiState>,
384) -> ApiResult<Json<Vec<String>>> {
385 Ok(Json(Vec::new()))
386}
387
388#[utoipa::path(
390 get,
391 path = "/v1/connectome/cortical_area/{cortical_id}/neurons",
392 tag = "connectome"
393)]
394pub async fn get_cortical_area_neurons(
395 State(state): State<ApiState>,
396 Path(cortical_id): Path<String>,
397) -> ApiResult<Json<Vec<u64>>> {
398 use tracing::debug;
399
400 let neuron_service = state.neuron_service.as_ref();
401
402 let neurons = neuron_service
404 .list_neurons_in_area(&cortical_id, None)
405 .await
406 .map_err(|e| {
407 ApiError::internal(format!(
408 "Failed to get neurons in area {}: {}",
409 cortical_id, e
410 ))
411 })?;
412
413 let neuron_ids: Vec<u64> = neurons.iter().map(|n| n.id).collect();
414
415 debug!(target: "feagi-api", "GET /connectome/cortical_area/{}/neurons - found {} neurons", cortical_id, neuron_ids.len());
416 Ok(Json(neuron_ids))
417}
418
419#[utoipa::path(
421 get,
422 path = "/v1/connectome/{cortical_area_id}/synapses",
423 tag = "connectome"
424)]
425pub async fn get_area_synapses(
426 State(state): State<ApiState>,
427 Path(area_id): Path<String>,
428) -> ApiResult<Json<Vec<HashMap<String, serde_json::Value>>>> {
429 use tracing::debug;
430
431 let connectome_service = state.connectome_service.as_ref();
432 let neuron_service = state.neuron_service.as_ref();
433
434 let area_info = connectome_service
437 .get_cortical_area(&area_id)
438 .await
439 .map_err(|_| ApiError::not_found("CorticalArea", &area_id))?;
440
441 let cortical_idx = area_info.cortical_idx;
442
443 let neurons = neuron_service
445 .list_neurons_in_area(&area_id, None)
446 .await
447 .map_err(|e| ApiError::internal(format!("Failed to get neurons: {}", e)))?;
448
449 tracing::debug!(
450 target: "feagi-api",
451 "Getting synapses for area {} (idx={}): {} neurons",
452 area_id,
453 cortical_idx,
454 neurons.len()
455 );
456
457 warn!(
460 "[API] /v1/connectome/cortical_area/{}/synapses endpoint called - this acquires NPU lock!",
461 area_id
462 );
463 let manager = feagi_brain_development::ConnectomeManager::instance();
464 let manager_lock = manager.read();
465 let npu_arc = manager_lock
466 .get_npu()
467 .ok_or_else(|| ApiError::internal("NPU not initialized"))?;
468 let lock_start = std::time::Instant::now();
469 tracing::debug!("[NPU-LOCK] CONNECTOME-API: Acquiring NPU lock for synapse queries");
470 let npu_lock = npu_arc.lock().unwrap();
471 let lock_wait = lock_start.elapsed();
472 tracing::debug!(
473 "[NPU-LOCK] CONNECTOME-API: Lock acquired (waited {:.2}ms)",
474 lock_wait.as_secs_f64() * 1000.0
475 );
476
477 let mut all_synapses = Vec::new();
478 for neuron_info in &neurons {
479 let neuron_id = neuron_info.id as u32;
480 let outgoing = npu_lock.get_outgoing_synapses(neuron_id);
481
482 for (target_id, weight, psp, synapse_type) in outgoing {
483 let mut synapse_obj = HashMap::new();
484 synapse_obj.insert("source_neuron_id".to_string(), serde_json::json!(neuron_id));
485 synapse_obj.insert("target_neuron_id".to_string(), serde_json::json!(target_id));
486 synapse_obj.insert("weight".to_string(), serde_json::json!(weight));
487 synapse_obj.insert("postsynaptic_potential".to_string(), serde_json::json!(psp));
488 synapse_obj.insert("synapse_type".to_string(), serde_json::json!(synapse_type));
489 all_synapses.push(synapse_obj);
490 }
491 }
492
493 debug!(target: "feagi-api", "Found {} synapses from area {}", all_synapses.len(), area_id);
494 Ok(Json(all_synapses))
495}
496
497#[utoipa::path(
499 get,
500 path = "/v1/connectome/cortical_info/{cortical_area}",
501 tag = "connectome"
502)]
503pub async fn get_cortical_info(
504 State(state): State<ApiState>,
505 Path(cortical_area): Path<String>,
506) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
507 let connectome_service = state.connectome_service.as_ref();
508 let area = connectome_service
509 .get_cortical_area(&cortical_area)
510 .await
511 .map_err(|e| ApiError::not_found("area", &format!("{}", e)))?;
512 let mut response = HashMap::new();
513 response.insert(
514 "cortical_id".to_string(),
515 serde_json::json!(area.cortical_id),
516 );
517 response.insert("cortical_name".to_string(), serde_json::json!(area.name));
518 Ok(Json(response))
519}
520
521#[utoipa::path(
523 get,
524 path = "/v1/connectome/stats/cortical/cumulative/{cortical_area}",
525 tag = "connectome"
526)]
527pub async fn get_stats_cortical_cumulative(
528 State(_state): State<ApiState>,
529 Path(_area): Path<String>,
530) -> ApiResult<Json<HashMap<String, i64>>> {
531 let mut response = HashMap::new();
532 response.insert("total_fires".to_string(), 0);
533 Ok(Json(response))
534}
535
536#[utoipa::path(
538 get,
539 path = "/v1/connectome/neuron/{neuron_id}/properties",
540 tag = "connectome"
541)]
542pub async fn get_neuron_properties_by_id(
543 State(state): State<ApiState>,
544 Path(neuron_id): Path<u64>,
545) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
546 let connectome_service = state.connectome_service.as_ref();
547 let props = connectome_service
548 .get_neuron_properties(neuron_id)
549 .await
550 .map_err(ApiError::from)?;
551 Ok(Json(props))
552}
553
554#[utoipa::path(get, path = "/v1/connectome/neuron_properties", tag = "connectome")]
556pub async fn get_neuron_properties_query(
557 State(state): State<ApiState>,
558 Query(params): Query<HashMap<String, String>>,
559) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
560 let neuron_id: u64 = params
561 .get("neuron_id")
562 .ok_or_else(|| ApiError::invalid_input("neuron_id required"))?
563 .parse()
564 .map_err(|_| ApiError::invalid_input("neuron_id must be an integer"))?;
565
566 let connectome_service = state.connectome_service.as_ref();
567 let props = connectome_service
568 .get_neuron_properties(neuron_id)
569 .await
570 .map_err(ApiError::from)?;
571 Ok(Json(props))
572}
573
574#[derive(Debug, Clone, Deserialize, IntoParams, ToSchema)]
575#[into_params(parameter_in = Query)]
576pub struct NeuronPropertiesAtQuery {
577 pub cortical_id: String,
579 pub x: u32,
581 pub y: u32,
583 pub z: u32,
585}
586
587#[utoipa::path(
593 get,
594 path = "/v1/connectome/neuron_properties_at",
595 tag = "connectome",
596 params(NeuronPropertiesAtQuery)
597)]
598pub async fn get_neuron_properties_at_query(
599 State(state): State<ApiState>,
600 Query(params): Query<NeuronPropertiesAtQuery>,
601) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
602 let cortical_id = params.cortical_id;
603 let x = params.x;
604 let y = params.y;
605 let z = params.z;
606
607 let connectome_service = state.connectome_service.as_ref();
609 let area = connectome_service
610 .get_cortical_area(&cortical_id)
611 .await
612 .map_err(|_| ApiError::not_found("CorticalArea", &cortical_id))?;
613
614 let neuron_id_u32: u32 = {
619 let manager = feagi_brain_development::ConnectomeManager::instance();
621 let manager_lock = manager.read();
622 let npu_arc = manager_lock
623 .get_npu()
624 .ok_or_else(|| ApiError::internal("NPU not initialized"))?;
625 let npu_lock = npu_arc.lock().unwrap();
626
627 npu_lock
628 .get_neuron_id_at_coordinate(area.cortical_idx, x, y, z)
629 .ok_or_else(|| {
630 ApiError::not_found(
631 "Neuron",
632 &format!("cortical_id={} x={} y={} z={}", cortical_id, x, y, z),
633 )
634 })?
635 };
636
637 let mut props = connectome_service
638 .get_neuron_properties(neuron_id_u32 as u64)
639 .await
640 .map_err(ApiError::from)?;
641
642 props.insert(
644 "neuron_id".to_string(),
645 serde_json::json!(neuron_id_u32 as u64),
646 );
647 props.insert("cortical_id".to_string(), serde_json::json!(cortical_id));
648 props.insert(
649 "cortical_idx".to_string(),
650 serde_json::json!(area.cortical_idx),
651 );
652
653 Ok(Json(props))
654}
655
656#[utoipa::path(get, path = "/v1/connectome/area_neurons", tag = "connectome")]
658pub async fn get_area_neurons_query(
659 State(_state): State<ApiState>,
660 Query(_params): Query<HashMap<String, String>>,
661) -> ApiResult<Json<Vec<u64>>> {
662 Ok(Json(Vec::new()))
663}
664
665#[utoipa::path(
667 get,
668 path = "/v1/connectome/fire_queue/{cortical_area}",
669 tag = "connectome"
670)]
671pub async fn get_fire_queue_area(
672 State(_state): State<ApiState>,
673 Path(_area): Path<String>,
674) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
675 Ok(Json(HashMap::new()))
676}
677
678#[utoipa::path(get, path = "/v1/connectome/plasticity", tag = "connectome")]
680pub async fn get_plasticity_info(
681 State(_state): State<ApiState>,
682) -> ApiResult<Json<HashMap<String, bool>>> {
683 let mut response = HashMap::new();
684 response.insert("enabled".to_string(), true);
685 Ok(Json(response))
686}
687
688#[utoipa::path(get, path = "/v1/connectome/path", tag = "connectome")]
690pub async fn get_path_query(
691 State(_state): State<ApiState>,
692 Query(_params): Query<HashMap<String, String>>,
693) -> ApiResult<Json<Vec<Vec<String>>>> {
694 Ok(Json(Vec::new()))
695}
696
697#[utoipa::path(get, path = "/v1/connectome/download", tag = "connectome")]
699pub async fn get_download_connectome(
700 State(state): State<ApiState>,
701) -> ApiResult<Json<serde_json::Value>> {
702 let snapshot = state
704 .connectome_service
705 .export_connectome()
706 .await
707 .map_err(ApiError::from)?;
708
709 let json_value = serde_json::to_value(&snapshot)
711 .map_err(|e| ApiError::internal(format!("Failed to serialize connectome: {}", e)))?;
712
713 Ok(Json(json_value))
714}
715
716#[utoipa::path(
718 get,
719 path = "/v1/connectome/download-cortical-area/{cortical_area}",
720 tag = "connectome"
721)]
722pub async fn get_download_cortical_area(
723 State(_state): State<ApiState>,
724 Path(_area): Path<String>,
725) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
726 Ok(Json(HashMap::new()))
727}
728
729#[utoipa::path(post, path = "/v1/connectome/upload", tag = "connectome")]
731pub async fn post_upload_connectome(
732 State(state): State<ApiState>,
733 Json(data): Json<serde_json::Value>,
734) -> ApiResult<Json<HashMap<String, String>>> {
735 let snapshot: feagi_npu_neural::types::connectome::ConnectomeSnapshot =
737 serde_json::from_value(data).map_err(|e| {
738 ApiError::invalid_input(format!("Invalid connectome snapshot format: {}", e))
739 })?;
740
741 state
743 .connectome_service
744 .import_connectome(snapshot)
745 .await
746 .map_err(ApiError::from)?;
747
748 Ok(Json(HashMap::from([(
749 "message".to_string(),
750 "Connectome imported successfully".to_string(),
751 )])))
752}
753
754#[utoipa::path(post, path = "/v1/connectome/upload-cortical-area", tag = "connectome")]
756pub async fn post_upload_cortical_area(
757 State(_state): State<ApiState>,
758 Json(_data): Json<HashMap<String, serde_json::Value>>,
759) -> ApiResult<Json<HashMap<String, String>>> {
760 Ok(Json(HashMap::from([(
761 "message".to_string(),
762 "Upload not yet implemented".to_string(),
763 )])))
764}
765
766#[utoipa::path(
768 get,
769 path = "/v1/connectome/cortical_area/list/types",
770 tag = "connectome",
771 responses(
772 (status = 200, description = "List of cortical types with their cortical IDs and group IDs", body = HashMap<String, serde_json::Value>),
773 (status = 500, description = "Internal server error")
774 )
775)]
776pub async fn get_cortical_area_list_types(
777 State(state): State<ApiState>,
778) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
779 use std::collections::{HashMap, HashSet};
781
782 let connectome_service = state.connectome_service.as_ref();
783 let areas = connectome_service
784 .list_cortical_areas()
785 .await
786 .map_err(|e| ApiError::internal(format!("Failed to list cortical areas: {}", e)))?;
787
788 fn get_cortical_type_title(subtype: &str) -> String {
790 match subtype {
791 "svi" => "segmented vision".to_string(),
792 "mot" => "motor".to_string(),
793 "bat" => "battery".to_string(),
794 "mis" => "miscellaneous".to_string(),
795 "gaz" => "gaze control".to_string(),
796 "pow" => "power".to_string(),
797 "dea" => "death".to_string(),
798 _ => {
799 if !subtype.is_empty() {
801 let mut chars = subtype.chars();
802 let first = chars.next().unwrap().to_uppercase().collect::<String>();
803 let rest: String = chars.collect();
804 format!("{}{}", first, rest)
805 } else {
806 "unknown".to_string()
807 }
808 }
809 }
810 }
811
812 let mut type_map: HashMap<String, (String, Vec<String>, HashSet<u8>)> = HashMap::new();
814
815 for area in areas {
816 use feagi_structures::genomic::cortical_area::CorticalID;
818 if let Ok(cortical_id_typed) = CorticalID::try_from_base_64(&area.cortical_id) {
819 if let Some(subtype) = cortical_id_typed.extract_subtype() {
821 let entry = type_map.entry(subtype.clone()).or_insert_with(|| {
822 let title = get_cortical_type_title(&subtype);
823 (title, Vec::new(), HashSet::new())
824 });
825
826 entry.1.push(area.cortical_id.clone());
828
829 if let Some(group_id) = cortical_id_typed.extract_group_id() {
831 entry.2.insert(group_id);
832 }
833 }
834 }
835 }
836
837 let mut response: HashMap<String, serde_json::Value> = HashMap::new();
839 for (subtype, (title, mut cortical_ids, group_ids)) in type_map {
840 cortical_ids.sort();
842
843 let mut group_ids_vec: Vec<u8> = group_ids.into_iter().collect();
844 group_ids_vec.sort_unstable();
845
846 response.insert(
847 subtype,
848 serde_json::json!({
849 "title": title,
850 "cortical_ids": cortical_ids,
851 "group_ids": group_ids_vec
852 }),
853 );
854 }
855
856 Ok(Json(response))
857}
858
859#[utoipa::path(
861 get,
862 path = "/v1/connectome/memory_neuron",
863 tag = "connectome",
864 params(MemoryNeuronQuery),
865 responses(
866 (status = 200, description = "Memory neuron detail", body = MemoryNeuronDetailResponse),
867 (status = 400, description = "Invalid neuron id"),
868 (status = 404, description = "Not found"),
869 (status = 500, description = "Internal server error")
870 )
871)]
872pub async fn get_memory_neuron(
873 State(_state): State<ApiState>,
874 Query(q): Query<MemoryNeuronQuery>,
875) -> ApiResult<Json<MemoryNeuronDetailResponse>> {
876 if !feagi_npu_plasticity::NeuronIdManager::is_memory_neuron_id(q.neuron_id) {
877 return Err(ApiError::invalid_input(format!(
878 "neuron_id must be a memory neuron id in range {}..={}",
879 feagi_npu_plasticity::MEMORY_NEURON_ID_START,
880 feagi_npu_plasticity::MEMORY_NEURON_ID_MAX
881 )));
882 }
883
884 let manager = feagi_brain_development::ConnectomeManager::instance();
885 let mgr = manager.read();
886
887 let detail = {
891 let exec = mgr
892 .get_plasticity_executor()
893 .ok_or_else(|| ApiError::internal("Plasticity executor not available"))?;
894 let ex = exec
895 .lock()
896 .map_err(|_| ApiError::internal("Plasticity executor lock poisoned"))?;
897 ex.memory_neuron_detail(q.neuron_id)
898 .ok_or_else(|| ApiError::not_found("Memory neuron", &q.neuron_id.to_string()))?
899 };
900
901 let cortical_idx = detail.cortical_area_idx;
902 let (cortical_id, cortical_name) = mgr
903 .get_cortical_id(cortical_idx)
904 .and_then(|cid| {
905 mgr.get_cortical_area(cid)
906 .map(|a| (cid.as_base_64(), a.name.clone()))
907 })
908 .unwrap_or_else(|| (String::new(), String::new()));
909
910 let outgoing_full = mgr.get_outgoing_synapses(q.neuron_id as u64);
911 let incoming_full = mgr.get_incoming_synapses(q.neuron_id as u64);
912 let oc = outgoing_full.len();
913 let ic = incoming_full.len();
914 let (out_json, in_json) =
915 synapse_details_for_neuron(&mgr, q.neuron_id, &outgoing_full, &incoming_full);
916
917 Ok(Json(MemoryNeuronDetailResponse {
918 neuron_id: q.neuron_id as u64,
919 cortical_idx,
920 cortical_id,
921 cortical_name,
922 pattern_hash: detail.pattern_hash,
923 is_longterm_memory: detail.is_longterm_memory,
924 is_active: detail.is_active,
925 lifespan_current: detail.lifespan_current,
926 lifespan_initial: detail.lifespan_initial,
927 lifespan_growth_rate: detail.lifespan_growth_rate,
928 creation_burst: detail.creation_burst,
929 last_activation_burst: detail.last_activation_burst,
930 activation_count: detail.activation_count,
931 outgoing_synapse_count: oc,
932 incoming_synapse_count: ic,
933 outgoing_synapses: out_json,
934 incoming_synapses: in_json,
935 }))
936}