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(
426 get,
427 path = "/v1/connectome/{cortical_area_id}/synapses",
428 tag = "connectome"
429)]
430pub async fn get_area_synapses(
431 State(state): State<ApiState>,
432 Path(area_id): Path<String>,
433) -> ApiResult<Json<Vec<HashMap<String, serde_json::Value>>>> {
434 use tracing::debug;
435
436 let connectome_service = state.connectome_service.as_ref();
437 let neuron_service = state.neuron_service.as_ref();
438
439 let area_info = connectome_service
442 .get_cortical_area(&area_id)
443 .await
444 .map_err(|_| ApiError::not_found("CorticalArea", &area_id))?;
445
446 let cortical_idx = area_info.cortical_idx;
447
448 let neurons = neuron_service
450 .list_neurons_in_area(&area_id, None)
451 .await
452 .map_err(|e| ApiError::internal(format!("Failed to get neurons: {}", e)))?;
453
454 tracing::debug!(
455 target: "feagi-api",
456 "Getting synapses for area {} (idx={}): {} neurons",
457 area_id,
458 cortical_idx,
459 neurons.len()
460 );
461
462 warn!(
465 "[API] /v1/connectome/cortical_area/{}/synapses endpoint called - this acquires NPU lock!",
466 area_id
467 );
468 let manager = feagi_brain_development::ConnectomeManager::instance();
469 let manager_lock = manager.read();
470 let npu_arc = manager_lock
471 .get_npu()
472 .ok_or_else(|| ApiError::internal("NPU not initialized"))?;
473 let lock_start = std::time::Instant::now();
474 tracing::debug!("[NPU-LOCK] CONNECTOME-API: Acquiring NPU lock for synapse queries");
475 let npu_lock = npu_arc.lock().unwrap();
476 let lock_wait = lock_start.elapsed();
477 tracing::debug!(
478 "[NPU-LOCK] CONNECTOME-API: Lock acquired (waited {:.2}ms)",
479 lock_wait.as_secs_f64() * 1000.0
480 );
481
482 let mut all_synapses = Vec::new();
483 for neuron_info in &neurons {
484 let neuron_id = neuron_info.id as u32;
485 let outgoing = npu_lock.get_outgoing_synapses(neuron_id);
486
487 for (target_id, weight, psp, synapse_type) in outgoing {
488 let mut synapse_obj = HashMap::new();
489 synapse_obj.insert("source_neuron_id".to_string(), serde_json::json!(neuron_id));
490 synapse_obj.insert("target_neuron_id".to_string(), serde_json::json!(target_id));
491 synapse_obj.insert("weight".to_string(), serde_json::json!(weight));
492 synapse_obj.insert("postsynaptic_potential".to_string(), serde_json::json!(psp));
493 synapse_obj.insert("synapse_type".to_string(), serde_json::json!(synapse_type));
494 all_synapses.push(synapse_obj);
495 }
496 }
497
498 debug!(target: "feagi-api", "Found {} synapses from area {}", all_synapses.len(), area_id);
499 Ok(Json(all_synapses))
500}
501
502#[utoipa::path(
510 get,
511 path = "/v1/connectome/{cortical_area_id}/synapses/incoming",
512 tag = "connectome"
513)]
514pub async fn get_area_synapses_incoming(
515 State(state): State<ApiState>,
516 Path(area_id): Path<String>,
517) -> ApiResult<Json<Vec<HashMap<String, serde_json::Value>>>> {
518 use tracing::debug;
519
520 let connectome_service = state.connectome_service.as_ref();
521 let neuron_service = state.neuron_service.as_ref();
522
523 let _area_info = connectome_service
524 .get_cortical_area(&area_id)
525 .await
526 .map_err(|_| ApiError::not_found("CorticalArea", &area_id))?;
527
528 let neurons = neuron_service
529 .list_neurons_in_area(&area_id, None)
530 .await
531 .map_err(|e| ApiError::internal(format!("Failed to get neurons: {}", e)))?;
532
533 debug!(
534 target: "feagi-api",
535 "Getting incoming synapses for area {}: {} neurons",
536 area_id,
537 neurons.len()
538 );
539
540 warn!(
541 "[API] /v1/connectome/cortical_area/{}/synapses/incoming - acquiring NPU lock",
542 area_id
543 );
544 let manager = feagi_brain_development::ConnectomeManager::instance();
545 let manager_lock = manager.read();
546 let npu_arc = manager_lock
547 .get_npu()
548 .ok_or_else(|| ApiError::internal("NPU not initialized"))?;
549 let npu_lock = npu_arc.lock().unwrap();
550
551 let mut all_synapses = Vec::new();
552 for neuron_info in &neurons {
553 let neuron_id = neuron_info.id as u32;
554 let incoming = npu_lock.get_incoming_synapses(neuron_id);
555
556 for (source_id, weight, psp, synapse_type) in incoming {
557 let mut synapse_obj = HashMap::new();
558 synapse_obj.insert("source_neuron_id".to_string(), serde_json::json!(source_id));
559 synapse_obj.insert("target_neuron_id".to_string(), serde_json::json!(neuron_id));
560 synapse_obj.insert("weight".to_string(), serde_json::json!(weight));
561 synapse_obj.insert("postsynaptic_potential".to_string(), serde_json::json!(psp));
562 synapse_obj.insert("synapse_type".to_string(), serde_json::json!(synapse_type));
563 all_synapses.push(synapse_obj);
564 }
565 }
566
567 debug!(
568 target: "feagi-api",
569 "Found {} incoming synapses to area {}",
570 all_synapses.len(),
571 area_id
572 );
573 Ok(Json(all_synapses))
574}
575
576#[utoipa::path(
578 get,
579 path = "/v1/connectome/cortical_info/{cortical_area}",
580 tag = "connectome"
581)]
582pub async fn get_cortical_info(
583 State(state): State<ApiState>,
584 Path(cortical_area): Path<String>,
585) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
586 let connectome_service = state.connectome_service.as_ref();
587 let area = connectome_service
588 .get_cortical_area(&cortical_area)
589 .await
590 .map_err(|e| ApiError::not_found("area", &format!("{}", e)))?;
591 let mut response = HashMap::new();
592 response.insert(
593 "cortical_id".to_string(),
594 serde_json::json!(area.cortical_id),
595 );
596 response.insert("cortical_name".to_string(), serde_json::json!(area.name));
597 Ok(Json(response))
598}
599
600#[utoipa::path(
602 get,
603 path = "/v1/connectome/stats/cortical/cumulative/{cortical_area}",
604 tag = "connectome"
605)]
606pub async fn get_stats_cortical_cumulative(
607 State(_state): State<ApiState>,
608 Path(_area): Path<String>,
609) -> ApiResult<Json<HashMap<String, i64>>> {
610 let mut response = HashMap::new();
611 response.insert("total_fires".to_string(), 0);
612 Ok(Json(response))
613}
614
615#[utoipa::path(
617 get,
618 path = "/v1/connectome/neuron/{neuron_id}/properties",
619 tag = "connectome"
620)]
621pub async fn get_neuron_properties_by_id(
622 State(state): State<ApiState>,
623 Path(neuron_id): Path<u64>,
624) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
625 let connectome_service = state.connectome_service.as_ref();
626 let props = connectome_service
627 .get_neuron_properties(neuron_id)
628 .await
629 .map_err(ApiError::from)?;
630 Ok(Json(props))
631}
632
633#[utoipa::path(get, path = "/v1/connectome/neuron_properties", tag = "connectome")]
635pub async fn get_neuron_properties_query(
636 State(state): State<ApiState>,
637 Query(params): Query<HashMap<String, String>>,
638) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
639 let neuron_id: u64 = params
640 .get("neuron_id")
641 .ok_or_else(|| ApiError::invalid_input("neuron_id required"))?
642 .parse()
643 .map_err(|_| ApiError::invalid_input("neuron_id must be an integer"))?;
644
645 let connectome_service = state.connectome_service.as_ref();
646 let props = connectome_service
647 .get_neuron_properties(neuron_id)
648 .await
649 .map_err(ApiError::from)?;
650 Ok(Json(props))
651}
652
653#[derive(Debug, Clone, Deserialize, IntoParams, ToSchema)]
654#[into_params(parameter_in = Query)]
655pub struct NeuronPropertiesAtQuery {
656 pub cortical_id: String,
658 pub x: u32,
660 pub y: u32,
662 pub z: u32,
664}
665
666#[utoipa::path(
672 get,
673 path = "/v1/connectome/neuron_properties_at",
674 tag = "connectome",
675 params(NeuronPropertiesAtQuery)
676)]
677pub async fn get_neuron_properties_at_query(
678 State(state): State<ApiState>,
679 Query(params): Query<NeuronPropertiesAtQuery>,
680) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
681 let cortical_id = params.cortical_id;
682 let x = params.x;
683 let y = params.y;
684 let z = params.z;
685
686 let connectome_service = state.connectome_service.as_ref();
688 let area = connectome_service
689 .get_cortical_area(&cortical_id)
690 .await
691 .map_err(|_| ApiError::not_found("CorticalArea", &cortical_id))?;
692
693 let neuron_id_u32: u32 = {
698 let manager = feagi_brain_development::ConnectomeManager::instance();
700 let manager_lock = manager.read();
701 let npu_arc = manager_lock
702 .get_npu()
703 .ok_or_else(|| ApiError::internal("NPU not initialized"))?;
704 let npu_lock = npu_arc.lock().unwrap();
705
706 npu_lock
707 .get_neuron_id_at_coordinate(area.cortical_idx, x, y, z)
708 .ok_or_else(|| {
709 ApiError::not_found(
710 "Neuron",
711 &format!("cortical_id={} x={} y={} z={}", cortical_id, x, y, z),
712 )
713 })?
714 };
715
716 let mut props = connectome_service
717 .get_neuron_properties(neuron_id_u32 as u64)
718 .await
719 .map_err(ApiError::from)?;
720
721 props.insert(
723 "neuron_id".to_string(),
724 serde_json::json!(neuron_id_u32 as u64),
725 );
726 props.insert("cortical_id".to_string(), serde_json::json!(cortical_id));
727 props.insert(
728 "cortical_idx".to_string(),
729 serde_json::json!(area.cortical_idx),
730 );
731
732 Ok(Json(props))
733}
734
735#[utoipa::path(get, path = "/v1/connectome/area_neurons", tag = "connectome")]
737pub async fn get_area_neurons_query(
738 State(_state): State<ApiState>,
739 Query(_params): Query<HashMap<String, String>>,
740) -> ApiResult<Json<Vec<u64>>> {
741 Ok(Json(Vec::new()))
742}
743
744#[utoipa::path(
746 get,
747 path = "/v1/connectome/fire_queue/{cortical_area}",
748 tag = "connectome"
749)]
750pub async fn get_fire_queue_area(
751 State(_state): State<ApiState>,
752 Path(_area): Path<String>,
753) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
754 Ok(Json(HashMap::new()))
755}
756
757#[utoipa::path(get, path = "/v1/connectome/plasticity", tag = "connectome")]
759pub async fn get_plasticity_info(
760 State(_state): State<ApiState>,
761) -> ApiResult<Json<HashMap<String, bool>>> {
762 let mut response = HashMap::new();
763 response.insert("enabled".to_string(), true);
764 Ok(Json(response))
765}
766
767#[utoipa::path(get, path = "/v1/connectome/path", tag = "connectome")]
769pub async fn get_path_query(
770 State(_state): State<ApiState>,
771 Query(_params): Query<HashMap<String, String>>,
772) -> ApiResult<Json<Vec<Vec<String>>>> {
773 Ok(Json(Vec::new()))
774}
775
776#[utoipa::path(get, path = "/v1/connectome/download", tag = "connectome")]
778pub async fn get_download_connectome(
779 State(state): State<ApiState>,
780) -> ApiResult<Json<serde_json::Value>> {
781 let snapshot = state
783 .connectome_service
784 .export_connectome()
785 .await
786 .map_err(ApiError::from)?;
787
788 let json_value = serde_json::to_value(&snapshot)
790 .map_err(|e| ApiError::internal(format!("Failed to serialize connectome: {}", e)))?;
791
792 Ok(Json(json_value))
793}
794
795#[utoipa::path(
797 get,
798 path = "/v1/connectome/download-cortical-area/{cortical_area}",
799 tag = "connectome"
800)]
801pub async fn get_download_cortical_area(
802 State(_state): State<ApiState>,
803 Path(_area): Path<String>,
804) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
805 Ok(Json(HashMap::new()))
806}
807
808#[utoipa::path(post, path = "/v1/connectome/upload", tag = "connectome")]
810pub async fn post_upload_connectome(
811 State(state): State<ApiState>,
812 Json(data): Json<serde_json::Value>,
813) -> ApiResult<Json<HashMap<String, String>>> {
814 let snapshot: feagi_npu_neural::types::connectome::ConnectomeSnapshot =
816 serde_json::from_value(data).map_err(|e| {
817 ApiError::invalid_input(format!("Invalid connectome snapshot format: {}", e))
818 })?;
819
820 state
822 .connectome_service
823 .import_connectome(snapshot)
824 .await
825 .map_err(ApiError::from)?;
826
827 Ok(Json(HashMap::from([(
828 "message".to_string(),
829 "Connectome imported successfully".to_string(),
830 )])))
831}
832
833#[utoipa::path(post, path = "/v1/connectome/upload-cortical-area", tag = "connectome")]
835pub async fn post_upload_cortical_area(
836 State(_state): State<ApiState>,
837 Json(_data): Json<HashMap<String, serde_json::Value>>,
838) -> ApiResult<Json<HashMap<String, String>>> {
839 Ok(Json(HashMap::from([(
840 "message".to_string(),
841 "Upload not yet implemented".to_string(),
842 )])))
843}
844
845#[utoipa::path(
847 get,
848 path = "/v1/connectome/cortical_area/list/types",
849 tag = "connectome",
850 responses(
851 (status = 200, description = "List of cortical types with their cortical IDs and group IDs", body = HashMap<String, serde_json::Value>),
852 (status = 500, description = "Internal server error")
853 )
854)]
855pub async fn get_cortical_area_list_types(
856 State(state): State<ApiState>,
857) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
858 use std::collections::{HashMap, HashSet};
860
861 let connectome_service = state.connectome_service.as_ref();
862 let areas = connectome_service
863 .list_cortical_areas()
864 .await
865 .map_err(|e| ApiError::internal(format!("Failed to list cortical areas: {}", e)))?;
866
867 fn get_cortical_type_title(subtype: &str) -> String {
869 match subtype {
870 "svi" => "segmented vision".to_string(),
871 "mot" => "motor".to_string(),
872 "bat" => "battery".to_string(),
873 "mis" => "miscellaneous".to_string(),
874 "gaz" => "gaze control".to_string(),
875 "pow" => "power".to_string(),
876 "dea" => "death".to_string(),
877 _ => {
878 if !subtype.is_empty() {
880 let mut chars = subtype.chars();
881 let first = chars.next().unwrap().to_uppercase().collect::<String>();
882 let rest: String = chars.collect();
883 format!("{}{}", first, rest)
884 } else {
885 "unknown".to_string()
886 }
887 }
888 }
889 }
890
891 let mut type_map: HashMap<String, (String, Vec<String>, HashSet<u8>)> = HashMap::new();
893
894 for area in areas {
895 use feagi_structures::genomic::cortical_area::CorticalID;
897 if let Ok(cortical_id_typed) = CorticalID::try_from_base_64(&area.cortical_id) {
898 if let Some(subtype) = cortical_id_typed.extract_subtype() {
900 let entry = type_map.entry(subtype.clone()).or_insert_with(|| {
901 let title = get_cortical_type_title(&subtype);
902 (title, Vec::new(), HashSet::new())
903 });
904
905 entry.1.push(area.cortical_id.clone());
907
908 if let Some(group_id) = cortical_id_typed.extract_group_id() {
910 entry.2.insert(group_id);
911 }
912 }
913 }
914 }
915
916 let mut response: HashMap<String, serde_json::Value> = HashMap::new();
918 for (subtype, (title, mut cortical_ids, group_ids)) in type_map {
919 cortical_ids.sort();
921
922 let mut group_ids_vec: Vec<u8> = group_ids.into_iter().collect();
923 group_ids_vec.sort_unstable();
924
925 response.insert(
926 subtype,
927 serde_json::json!({
928 "title": title,
929 "cortical_ids": cortical_ids,
930 "group_ids": group_ids_vec
931 }),
932 );
933 }
934
935 Ok(Json(response))
936}
937
938#[utoipa::path(
940 get,
941 path = "/v1/connectome/memory_neuron",
942 tag = "connectome",
943 params(MemoryNeuronQuery),
944 responses(
945 (status = 200, description = "Memory neuron detail", body = MemoryNeuronDetailResponse),
946 (status = 400, description = "Invalid neuron id"),
947 (status = 404, description = "Not found"),
948 (status = 500, description = "Internal server error")
949 )
950)]
951pub async fn get_memory_neuron(
952 State(_state): State<ApiState>,
953 Query(q): Query<MemoryNeuronQuery>,
954) -> ApiResult<Json<MemoryNeuronDetailResponse>> {
955 if !feagi_npu_plasticity::NeuronIdManager::is_memory_neuron_id(q.neuron_id) {
956 return Err(ApiError::invalid_input(format!(
957 "neuron_id must be a memory neuron id in range {}..={}",
958 feagi_npu_plasticity::MEMORY_NEURON_ID_START,
959 feagi_npu_plasticity::MEMORY_NEURON_ID_MAX
960 )));
961 }
962
963 let manager = feagi_brain_development::ConnectomeManager::instance();
964 let mgr = manager.read();
965
966 let detail = {
970 let exec = mgr
971 .get_plasticity_executor()
972 .ok_or_else(|| ApiError::internal("Plasticity executor not available"))?;
973 let ex = exec
974 .lock()
975 .map_err(|_| ApiError::internal("Plasticity executor lock poisoned"))?;
976 ex.memory_neuron_detail(q.neuron_id)
977 .ok_or_else(|| ApiError::not_found("Memory neuron", &q.neuron_id.to_string()))?
978 };
979
980 let cortical_idx = detail.cortical_area_idx;
981 let (cortical_id, cortical_name) = mgr
982 .get_cortical_id(cortical_idx)
983 .and_then(|cid| {
984 mgr.get_cortical_area(cid)
985 .map(|a| (cid.as_base_64(), a.name.clone()))
986 })
987 .unwrap_or_else(|| (String::new(), String::new()));
988
989 let outgoing_full = mgr.get_outgoing_synapses(q.neuron_id as u64);
990 let incoming_full = mgr.get_incoming_synapses(q.neuron_id as u64);
991 let oc = outgoing_full.len();
992 let ic = incoming_full.len();
993 let (out_json, in_json) =
994 synapse_details_for_neuron(&mgr, q.neuron_id, &outgoing_full, &incoming_full);
995
996 Ok(Json(MemoryNeuronDetailResponse {
997 neuron_id: q.neuron_id as u64,
998 cortical_idx,
999 cortical_id,
1000 cortical_name,
1001 pattern_hash: detail.pattern_hash,
1002 is_longterm_memory: detail.is_longterm_memory,
1003 is_active: detail.is_active,
1004 lifespan_current: detail.lifespan_current,
1005 lifespan_initial: detail.lifespan_initial,
1006 lifespan_growth_rate: detail.lifespan_growth_rate,
1007 creation_burst: detail.creation_burst,
1008 last_activation_burst: detail.last_activation_burst,
1009 activation_count: detail.activation_count,
1010 outgoing_synapse_count: oc,
1011 incoming_synapse_count: ic,
1012 outgoing_synapses: out_json,
1013 incoming_synapses: in_json,
1014 }))
1015}