1use axum::{
10 body::Body,
11 extract::State,
12 http::{Request, StatusCode},
13 middleware::{self, Next},
14 response::{Html, IntoResponse, Json, Redirect, Response},
15 routing::{get, put},
16 Router,
17};
18use http_body_util::BodyExt;
19use std::sync::Arc;
20use tower_http::{
21 cors::{Any, CorsLayer},
22 trace::TraceLayer,
23};
24use utoipa::OpenApi;
25
26use crate::amalgamation;
27#[cfg(feature = "http")]
28use crate::openapi::ApiDoc;
29#[cfg(feature = "services")]
30use feagi_services::traits::{AgentService, SystemService};
31#[cfg(feature = "services")]
32use feagi_services::{
33 AnalyticsService, ConnectomeService, GenomeService, NeuronService, RuntimeService,
34};
35
36#[derive(Clone)]
38pub struct ApiState {
39 pub agent_service: Option<Arc<dyn AgentService + Send + Sync>>,
40 pub analytics_service: Arc<dyn AnalyticsService + Send + Sync>,
41 pub connectome_service: Arc<dyn ConnectomeService + Send + Sync>,
42 pub genome_service: Arc<dyn GenomeService + Send + Sync>,
43 pub neuron_service: Arc<dyn NeuronService + Send + Sync>,
44 pub runtime_service: Arc<dyn RuntimeService + Send + Sync>,
45 pub system_service: Arc<dyn SystemService + Send + Sync>,
46 pub snapshot_service: Option<Arc<dyn feagi_services::SnapshotService + Send + Sync>>,
47 pub feagi_session_timestamp: i64,
50 pub memory_stats_cache: Option<feagi_npu_plasticity::MemoryStatsCache>,
52 pub amalgamation_state: amalgamation::SharedAmalgamationState,
54 #[cfg(feature = "feagi-agent")]
56 pub agent_connectors: Arc<
57 parking_lot::RwLock<
58 std::collections::HashMap<
59 feagi_agent::sdk::AgentDescriptor,
60 Arc<std::sync::Mutex<feagi_agent::sdk::ConnectorAgent>>,
61 >,
62 >,
63 >,
64}
65
66impl ApiState {
67 #[cfg(feature = "feagi-agent")]
69 pub fn init_agent_connectors() -> Arc<
70 parking_lot::RwLock<
71 std::collections::HashMap<
72 feagi_agent::sdk::AgentDescriptor,
73 Arc<std::sync::Mutex<feagi_agent::sdk::ConnectorAgent>>,
74 >,
75 >,
76 > {
77 Arc::new(parking_lot::RwLock::new(std::collections::HashMap::new()))
78 }
79
80 pub fn init_amalgamation_state() -> amalgamation::SharedAmalgamationState {
82 amalgamation::new_shared_state()
83 }
84}
85
86pub fn create_http_server(state: ApiState) -> Router {
88 Router::new()
89 .route("/", get(root_redirect))
91
92 .route("/swagger-ui/", get(custom_swagger_ui))
94
95 .route("/api-docs/openapi.json", get(|| async {
97 Json(ApiDoc::openapi())
98 }))
99
100 .nest("/v1", create_v1_router())
102
103 .fallback(|| async {
105 tracing::warn!(target: "feagi-api", "Unmatched request - 404 Not Found");
106 (StatusCode::NOT_FOUND, "404 Not Found")
107 })
108
109 .with_state(state)
111
112 .layer(middleware::from_fn(log_request_response_bodies))
114 .layer(create_cors_layer())
115 .layer(
116 TraceLayer::new_for_http()
117 .make_span_with(|request: &axum::http::Request<_>| {
118 tracing::span!(
119 target: "feagi-api",
120 tracing::Level::TRACE,
121 "request",
122 method = %request.method(),
123 uri = %request.uri(),
124 version = ?request.version(),
125 )
126 })
127 .on_request(|request: &axum::http::Request<_>, _span: &tracing::Span| {
128 tracing::trace!(target: "feagi-api", "Incoming request: {} {}", request.method(), request.uri());
129 })
130 .on_response(|response: &axum::http::Response<_>, latency: std::time::Duration, span: &tracing::Span| {
131 tracing::trace!(
132 target: "feagi-api",
133 "Response: status={}, latency={:?}",
134 response.status(),
135 latency
136 );
137 span.record("status", response.status().as_u16());
138 span.record("latency_ms", latency.as_millis());
139 })
140 .on_body_chunk(|chunk: &axum::body::Bytes, latency: std::time::Duration, _span: &tracing::Span| {
141 tracing::trace!(target: "feagi-api", "Response chunk: {} bytes, latency={:?}", chunk.len(), latency);
142 })
143 .on_eos(|_trailers: Option<&axum::http::HeaderMap>, stream_duration: std::time::Duration, _span: &tracing::Span| {
144 tracing::trace!(target: "feagi-api", "Stream ended, duration={:?}", stream_duration);
145 })
146 .on_failure(|error: tower_http::classify::ServerErrorsFailureClass, latency: std::time::Duration, _span: &tracing::Span| {
147 tracing::error!(
148 target: "feagi-api",
149 "Request failed: error_class={:?}, latency={:?}",
150 error, latency
151 );
152 })
153 )
154}
155
156fn create_v1_router() -> Router<ApiState> {
159 use crate::endpoints::agent::*; use crate::endpoints::burst_engine;
161 use crate::endpoints::connectome;
162 use crate::endpoints::cortical_area;
163 use crate::endpoints::cortical_mapping;
164 use crate::endpoints::evolution;
165 use crate::endpoints::genome;
166 use crate::endpoints::input;
167 use crate::endpoints::insight;
168 use crate::endpoints::monitoring;
169 use crate::endpoints::morphology;
170 use crate::endpoints::network;
171 use crate::endpoints::neuroplasticity;
172 use crate::endpoints::outputs;
173 use crate::endpoints::physiology;
174 use crate::endpoints::region;
175 use crate::endpoints::simulation;
176 use crate::endpoints::training;
177 use crate::endpoints::visualization;
178 use crate::endpoints::{agent, system};
179
180 Router::new()
181 .route("/agent/register", axum::routing::post(register_agent))
183 .route("/agent/heartbeat", axum::routing::post(heartbeat))
184 .route("/agent/list", get(list_agents))
185 .route("/agent/properties", get(get_agent_properties))
186 .route(
187 "/agent/properties/:agent_id",
188 get(agent::get_agent_properties_path),
189 )
190 .route("/agent/shared_mem", get(get_shared_memory))
191 .route("/agent/deregister", axum::routing::delete(deregister_agent))
192 .route(
193 "/agent/manual_stimulation",
194 axum::routing::post(manual_stimulation),
195 )
196 .route(
197 "/agent/fq_sampler_status",
198 get(agent::get_fq_sampler_status),
199 )
200 .route("/agent/capabilities", get(agent::get_capabilities))
201 .route(
202 "/agent/capabilities/all",
203 get(agent::get_all_agent_capabilities),
204 )
205 .route("/agent/info/:agent_id", get(agent::get_agent_info))
206 .route(
207 "/agent/configure",
208 axum::routing::post(agent::post_configure),
209 )
210 .route(
211 "/agent/:agent_id/device_registrations",
212 get(agent::export_device_registrations).post(agent::import_device_registrations),
213 )
214 .route("/system/health_check", get(system::get_health_check))
216 .route(
217 "/system/cortical_area_visualization_skip_rate",
218 get(system::get_cortical_area_visualization_skip_rate)
219 .put(system::set_cortical_area_visualization_skip_rate),
220 )
221 .route(
222 "/system/cortical_area_visualization_suppression_threshold",
223 get(system::get_cortical_area_visualization_suppression_threshold)
224 .put(system::set_cortical_area_visualization_suppression_threshold),
225 )
226 .route("/system/version", get(system::get_version))
227 .route("/system/versions", get(system::get_versions))
228 .route("/system/configuration", get(system::get_configuration))
229 .route(
230 "/system/user_preferences",
231 get(system::get_user_preferences).put(system::put_user_preferences),
232 )
233 .route(
234 "/system/cortical_area_types",
235 get(system::get_cortical_area_types_list),
236 )
237 .route(
238 "/system/enable_visualization_fq_sampler",
239 axum::routing::post(system::post_enable_visualization_fq_sampler),
240 )
241 .route(
242 "/system/disable_visualization_fq_sampler",
243 axum::routing::post(system::post_disable_visualization_fq_sampler),
244 )
245 .route("/system/fcl_status", get(system::get_fcl_status_system))
246 .route(
247 "/system/fcl_reset",
248 axum::routing::post(system::post_fcl_reset_system),
249 )
250 .route("/system/processes", get(system::get_processes))
251 .route("/system/unique_logs", get(system::get_unique_logs))
252 .route("/system/logs", axum::routing::post(system::post_logs))
253 .route(
254 "/system/beacon/subscribers",
255 get(system::get_beacon_subscribers),
256 )
257 .route(
258 "/system/beacon/subscribe",
259 axum::routing::post(system::post_beacon_subscribe),
260 )
261 .route(
262 "/system/beacon/unsubscribe",
263 axum::routing::delete(system::delete_beacon_unsubscribe),
264 )
265 .route(
266 "/system/global_activity_visualization",
267 get(system::get_global_activity_visualization)
268 .put(system::put_global_activity_visualization),
269 )
270 .route(
271 "/system/circuit_library_path",
272 axum::routing::post(system::post_circuit_library_path),
273 )
274 .route("/system/db/influxdb/test", get(system::get_influxdb_test))
275 .route(
276 "/system/register",
277 axum::routing::post(system::post_register_system),
278 )
279 .route("/cortical_area/ipu", get(cortical_area::get_ipu))
281 .route(
282 "/cortical_area/ipu/types",
283 get(cortical_area::get_ipu_types),
284 )
285 .route("/cortical_area/opu", get(cortical_area::get_opu))
286 .route(
287 "/cortical_area/opu/types",
288 get(cortical_area::get_opu_types),
289 )
290 .route(
291 "/cortical_area/cortical_area_id_list",
292 get(cortical_area::get_cortical_area_id_list),
293 )
294 .route(
295 "/cortical_area/cortical_area_name_list",
296 get(cortical_area::get_cortical_area_name_list),
297 )
298 .route(
299 "/cortical_area/cortical_id_name_mapping",
300 get(cortical_area::get_cortical_id_name_mapping),
301 )
302 .route(
303 "/cortical_area/cortical_types",
304 get(cortical_area::get_cortical_types),
305 )
306 .route(
307 "/cortical_area/cortical_map_detailed",
308 get(cortical_area::get_cortical_map_detailed),
309 )
310 .route(
311 "/cortical_area/cortical_locations_2d",
312 get(cortical_area::get_cortical_locations_2d),
313 )
314 .route(
315 "/cortical_area/cortical_area/geometry",
316 get(cortical_area::get_cortical_area_geometry),
317 )
318 .route(
319 "/cortical_area/cortical_visibility",
320 get(cortical_area::get_cortical_visibility),
321 )
322 .route(
323 "/cortical_area/cortical_name_location",
324 axum::routing::post(cortical_area::post_cortical_name_location),
325 )
326 .route(
327 "/cortical_area/cortical_area_properties",
328 axum::routing::post(cortical_area::post_cortical_area_properties),
329 )
330 .route(
331 "/cortical_area/multi/cortical_area_properties",
332 axum::routing::post(cortical_area::post_multi_cortical_area_properties),
333 )
334 .route(
335 "/cortical_area/cortical_area",
336 axum::routing::post(cortical_area::post_cortical_area)
337 .put(cortical_area::put_cortical_area)
338 .delete(cortical_area::delete_cortical_area),
339 )
340 .route(
341 "/cortical_area/custom_cortical_area",
342 axum::routing::post(cortical_area::post_custom_cortical_area),
343 )
344 .route(
345 "/cortical_area/clone",
346 axum::routing::post(cortical_area::post_clone),
347 )
348 .route(
349 "/cortical_area/multi/cortical_area",
350 put(cortical_area::put_multi_cortical_area)
351 .delete(cortical_area::delete_multi_cortical_area),
352 )
353 .route("/cortical_area/coord_2d", put(cortical_area::put_coord_2d))
354 .route(
355 "/cortical_area/suppress_cortical_visibility",
356 put(cortical_area::put_suppress_cortical_visibility),
357 )
358 .route("/cortical_area/reset", put(cortical_area::put_reset))
359 .route(
360 "/cortical_area/visualization",
361 get(cortical_area::get_visualization),
362 )
363 .route(
364 "/cortical_area/batch_operations",
365 axum::routing::post(cortical_area::post_batch_operations),
366 )
367 .route("/cortical_area/ipu/list", get(cortical_area::get_ipu_list))
368 .route("/cortical_area/opu/list", get(cortical_area::get_opu_list))
369 .route(
370 "/cortical_area/coordinates_3d",
371 put(cortical_area::put_coordinates_3d),
372 )
373 .route(
374 "/cortical_area/bulk_delete",
375 axum::routing::delete(cortical_area::delete_bulk),
376 )
377 .route(
378 "/cortical_area/resize",
379 axum::routing::post(cortical_area::post_resize),
380 )
381 .route(
382 "/cortical_area/reposition",
383 axum::routing::post(cortical_area::post_reposition),
384 )
385 .route(
386 "/cortical_area/voxel_neurons",
387 axum::routing::post(cortical_area::post_voxel_neurons),
388 )
389 .route(
390 "/cortical_area/cortical_area_index_list",
391 get(cortical_area::get_cortical_area_index_list),
392 )
393 .route(
394 "/cortical_area/cortical_idx_mapping",
395 get(cortical_area::get_cortical_idx_mapping),
396 )
397 .route(
398 "/cortical_area/mapping_restrictions",
399 get(cortical_area::get_mapping_restrictions_query)
400 .post(cortical_area::post_mapping_restrictions),
401 )
402 .route(
403 "/cortical_area/:cortical_id/memory_usage",
404 get(cortical_area::get_memory_usage),
405 )
406 .route(
407 "/cortical_area/:cortical_id/neuron_count",
408 get(cortical_area::get_area_neuron_count),
409 )
410 .route(
411 "/cortical_area/cortical_type_options",
412 axum::routing::post(cortical_area::post_cortical_type_options),
413 )
414 .route(
415 "/cortical_area/mapping_restrictions_between_areas",
416 axum::routing::post(cortical_area::post_mapping_restrictions_between_areas),
417 )
418 .route("/cortical_area/coord_3d", put(cortical_area::put_coord_3d))
419 .route(
421 "/morphology/morphology_list",
422 get(morphology::get_morphology_list),
423 )
424 .route(
425 "/morphology/morphology_types",
426 get(morphology::get_morphology_types),
427 )
428 .route("/morphology/list/types", get(morphology::get_list_types))
429 .route(
430 "/morphology/morphologies",
431 get(morphology::get_morphologies),
432 )
433 .route(
434 "/morphology/morphology",
435 axum::routing::post(morphology::post_morphology)
436 .put(morphology::put_morphology)
437 .delete(morphology::delete_morphology_by_name),
438 )
439 .route(
440 "/morphology/morphology_properties",
441 axum::routing::post(morphology::post_morphology_properties),
442 )
443 .route(
444 "/morphology/morphology_usage",
445 axum::routing::post(morphology::post_morphology_usage),
446 )
447 .route("/morphology/list", get(morphology::get_list))
448 .route("/morphology/info/:morphology_id", get(morphology::get_info))
449 .route(
450 "/morphology/create",
451 axum::routing::post(morphology::post_create),
452 )
453 .route(
454 "/morphology/update",
455 axum::routing::put(morphology::put_update),
456 )
457 .route(
458 "/morphology/delete/:morphology_id",
459 axum::routing::delete(morphology::delete_morphology),
460 )
461 .route("/region/regions_members", get(region::get_regions_members))
463 .route(
464 "/region/region",
465 axum::routing::post(region::post_region)
466 .put(region::put_region)
467 .delete(region::delete_region),
468 )
469 .route("/region/clone", axum::routing::post(region::post_clone))
470 .route(
471 "/region/relocate_members",
472 put(region::put_relocate_members),
473 )
474 .route(
475 "/region/region_and_members",
476 axum::routing::delete(region::delete_region_and_members),
477 )
478 .route("/region/regions", get(region::get_regions))
479 .route("/region/region_titles", get(region::get_region_titles))
480 .route("/region/region/:region_id", get(region::get_region_detail))
481 .route(
482 "/region/change_region_parent",
483 put(region::put_change_region_parent),
484 )
485 .route(
486 "/region/change_cortical_area_region",
487 put(region::put_change_cortical_area_region),
488 )
489 .route(
491 "/cortical_mapping/afferents",
492 axum::routing::post(cortical_mapping::post_afferents),
493 )
494 .route(
495 "/cortical_mapping/efferents",
496 axum::routing::post(cortical_mapping::post_efferents),
497 )
498 .route(
499 "/cortical_mapping/mapping_properties",
500 axum::routing::post(cortical_mapping::post_mapping_properties)
501 .put(cortical_mapping::put_mapping_properties),
502 )
503 .route(
504 "/cortical_mapping/mapping",
505 get(cortical_mapping::get_mapping).delete(cortical_mapping::delete_mapping),
506 )
507 .route(
508 "/cortical_mapping/mapping_list",
509 get(cortical_mapping::get_mapping_list),
510 )
511 .route(
512 "/cortical_mapping/batch_update",
513 axum::routing::post(cortical_mapping::post_batch_update),
514 )
515 .route(
516 "/cortical_mapping/mapping",
517 axum::routing::post(cortical_mapping::post_mapping).put(cortical_mapping::put_mapping),
518 )
519 .route(
521 "/connectome/cortical_areas/list/detailed",
522 get(connectome::get_cortical_areas_list_detailed),
523 )
524 .route(
525 "/connectome/properties/dimensions",
526 get(connectome::get_properties_dimensions),
527 )
528 .route(
529 "/connectome/properties/mappings",
530 get(connectome::get_properties_mappings),
531 )
532 .route("/connectome/snapshot", get(connectome::get_snapshot))
533 .route("/connectome/stats", get(connectome::get_stats))
534 .route(
535 "/connectome/batch_neuron_operations",
536 axum::routing::post(connectome::post_batch_neuron_operations),
537 )
538 .route(
539 "/connectome/batch_synapse_operations",
540 axum::routing::post(connectome::post_batch_synapse_operations),
541 )
542 .route(
543 "/connectome/neuron_count",
544 get(connectome::get_neuron_count),
545 )
546 .route(
547 "/connectome/synapse_count",
548 get(connectome::get_synapse_count),
549 )
550 .route("/connectome/paths", get(connectome::get_paths))
551 .route(
552 "/connectome/cumulative_stats",
553 get(connectome::get_cumulative_stats),
554 )
555 .route(
556 "/connectome/area_details",
557 get(connectome::get_area_details),
558 )
559 .route(
560 "/connectome/rebuild",
561 axum::routing::post(connectome::post_rebuild),
562 )
563 .route("/connectome/structure", get(connectome::get_structure))
564 .route(
565 "/connectome/clear",
566 axum::routing::post(connectome::post_clear),
567 )
568 .route("/connectome/validation", get(connectome::get_validation))
569 .route("/connectome/topology", get(connectome::get_topology))
570 .route(
571 "/connectome/optimize",
572 axum::routing::post(connectome::post_optimize),
573 )
574 .route(
575 "/connectome/connectivity_matrix",
576 get(connectome::get_connectivity_matrix),
577 )
578 .route(
579 "/connectome/neurons/batch",
580 axum::routing::post(connectome::post_neurons_batch),
581 )
582 .route(
583 "/connectome/synapses/batch",
584 axum::routing::post(connectome::post_synapses_batch),
585 )
586 .route(
587 "/connectome/cortical_areas/list/summary",
588 get(connectome::get_cortical_areas_list_summary),
589 )
590 .route(
591 "/connectome/cortical_areas/list/transforming",
592 get(connectome::get_cortical_areas_list_transforming),
593 )
594 .route(
595 "/connectome/cortical_area/list/types",
596 get(connectome::get_cortical_area_list_types),
597 )
598 .route(
599 "/connectome/cortical_area/:cortical_id/neurons",
600 get(connectome::get_cortical_area_neurons),
601 )
602 .route(
603 "/connectome/:cortical_area_id/synapses",
604 get(connectome::get_area_synapses),
605 )
606 .route(
607 "/connectome/cortical_info/:cortical_area",
608 get(connectome::get_cortical_info),
609 )
610 .route(
611 "/connectome/stats/cortical/cumulative/:cortical_area",
612 get(connectome::get_stats_cortical_cumulative),
613 )
614 .route(
615 "/connectome/neuron/:neuron_id/properties",
616 get(connectome::get_neuron_properties_by_id),
617 )
618 .route(
619 "/connectome/neuron_properties",
620 get(connectome::get_neuron_properties_query),
621 )
622 .route(
623 "/connectome/neuron_properties_at",
624 get(connectome::get_neuron_properties_at_query),
625 )
626 .route(
627 "/connectome/area_neurons",
628 get(connectome::get_area_neurons_query),
629 )
630 .route(
631 "/connectome/fire_queue/:cortical_area",
632 get(connectome::get_fire_queue_area),
633 )
634 .route(
635 "/connectome/plasticity",
636 get(connectome::get_plasticity_info),
637 )
638 .route("/connectome/path", get(connectome::get_path_query))
639 .route(
640 "/connectome/download",
641 get(connectome::get_download_connectome),
642 )
643 .route(
644 "/connectome/download-cortical-area/:cortical_area",
645 get(connectome::get_download_cortical_area),
646 )
647 .route(
648 "/connectome/upload",
649 axum::routing::post(connectome::post_upload_connectome),
650 )
651 .route(
652 "/connectome/upload-cortical-area",
653 axum::routing::post(connectome::post_upload_cortical_area),
654 )
655 .route(
657 "/burst_engine/simulation_timestep",
658 get(burst_engine::get_simulation_timestep).post(burst_engine::post_simulation_timestep),
659 )
660 .route("/burst_engine/fcl", get(burst_engine::get_fcl))
661 .route(
662 "/burst_engine/fcl/neuron",
663 get(burst_engine::get_fcl_neuron),
664 )
665 .route(
666 "/burst_engine/fire_queue",
667 get(burst_engine::get_fire_queue),
668 )
669 .route(
670 "/burst_engine/fire_queue/neuron",
671 get(burst_engine::get_fire_queue_neuron),
672 )
673 .route(
674 "/burst_engine/fcl_reset",
675 axum::routing::post(burst_engine::post_fcl_reset),
676 )
677 .route(
678 "/burst_engine/fcl_status",
679 get(burst_engine::get_fcl_status),
680 )
681 .route(
682 "/burst_engine/fcl_sampler/config",
683 get(burst_engine::get_fcl_sampler_config).post(burst_engine::post_fcl_sampler_config),
684 )
685 .route(
686 "/burst_engine/fcl_sampler/area/:area_id/sample_rate",
687 get(burst_engine::get_area_fcl_sample_rate)
688 .post(burst_engine::post_area_fcl_sample_rate),
689 )
690 .route(
691 "/burst_engine/fire_ledger/default_window_size",
692 get(burst_engine::get_fire_ledger_default_window_size)
693 .put(burst_engine::put_fire_ledger_default_window_size),
694 )
695 .route(
696 "/burst_engine/fire_ledger/areas_window_config",
697 get(burst_engine::get_fire_ledger_areas_window_config),
698 )
699 .route("/burst_engine/stats", get(burst_engine::get_stats))
700 .route("/burst_engine/status", get(burst_engine::get_status))
701 .route(
702 "/burst_engine/control",
703 axum::routing::post(burst_engine::post_control),
704 )
705 .route(
706 "/burst_engine/burst_counter",
707 get(burst_engine::get_burst_counter),
708 )
709 .route(
710 "/burst_engine/start",
711 axum::routing::post(burst_engine::post_start),
712 )
713 .route(
714 "/burst_engine/stop",
715 axum::routing::post(burst_engine::post_stop),
716 )
717 .route(
718 "/burst_engine/hold",
719 axum::routing::post(burst_engine::post_hold),
720 )
721 .route(
722 "/burst_engine/resume",
723 axum::routing::post(burst_engine::post_resume),
724 )
725 .route(
726 "/burst_engine/config",
727 get(burst_engine::get_config).put(burst_engine::put_config),
728 )
729 .route(
730 "/burst_engine/fire_ledger/area/:area_id/window_size",
731 get(burst_engine::get_fire_ledger_area_window_size)
732 .put(burst_engine::put_fire_ledger_area_window_size),
733 )
734 .route(
735 "/burst_engine/fire_ledger/area/:area_id/history",
736 get(burst_engine::get_fire_ledger_history),
737 )
738 .route(
739 "/burst_engine/membrane_potentials",
740 get(burst_engine::get_membrane_potentials).put(burst_engine::put_membrane_potentials),
741 )
742 .route(
743 "/burst_engine/frequency_status",
744 get(burst_engine::get_frequency_status),
745 )
746 .route(
747 "/burst_engine/measure_frequency",
748 axum::routing::post(burst_engine::post_measure_frequency),
749 )
750 .route(
751 "/burst_engine/frequency_history",
752 get(burst_engine::get_frequency_history),
753 )
754 .route(
755 "/burst_engine/force_connectome_integration",
756 axum::routing::post(burst_engine::post_force_connectome_integration),
757 )
758 .route("/genome/file_name", get(genome::get_file_name))
760 .route("/genome/circuits", get(genome::get_circuits))
761 .route(
762 "/genome/amalgamation_destination",
763 axum::routing::post(genome::post_amalgamation_destination),
764 )
765 .route(
766 "/genome/amalgamation_cancellation",
767 axum::routing::delete(genome::delete_amalgamation_cancellation),
768 )
769 .route(
770 "/feagi/genome/append",
771 axum::routing::post(genome::post_genome_append),
772 )
773 .route(
774 "/genome/upload/barebones",
775 axum::routing::post(genome::post_upload_barebones_genome),
776 )
777 .route(
778 "/genome/upload/essential",
779 axum::routing::post(genome::post_upload_essential_genome),
780 )
781 .route("/genome/name", get(genome::get_name))
782 .route("/genome/timestamp", get(genome::get_timestamp))
783 .route("/genome/save", axum::routing::post(genome::post_save))
784 .route("/genome/load", axum::routing::post(genome::post_load))
785 .route("/genome/upload", axum::routing::post(genome::post_upload))
786 .route("/genome/download", get(genome::get_download))
787 .route("/genome/properties", get(genome::get_properties))
788 .route(
789 "/genome/validate",
790 axum::routing::post(genome::post_validate),
791 )
792 .route(
793 "/genome/transform",
794 axum::routing::post(genome::post_transform),
795 )
796 .route("/genome/clone", axum::routing::post(genome::post_clone))
797 .route("/genome/reset", axum::routing::post(genome::post_reset))
798 .route("/genome/metadata", get(genome::get_metadata))
799 .route("/genome/merge", axum::routing::post(genome::post_merge))
800 .route("/genome/diff", get(genome::get_diff))
801 .route(
802 "/genome/export_format",
803 axum::routing::post(genome::post_export_format),
804 )
805 .route("/genome/amalgamation", get(genome::get_amalgamation))
806 .route(
807 "/genome/amalgamation_history",
808 get(genome::get_amalgamation_history_exact),
809 )
810 .route(
811 "/genome/cortical_template",
812 get(genome::get_cortical_template),
813 )
814 .route("/genome/defaults/files", get(genome::get_defaults_files))
815 .route("/genome/download_region", get(genome::get_download_region))
816 .route("/genome/genome_number", get(genome::get_genome_number))
817 .route(
818 "/genome/amalgamation_by_filename",
819 axum::routing::post(genome::post_amalgamation_by_filename),
820 )
821 .route(
822 "/genome/amalgamation_by_payload",
823 axum::routing::post(genome::post_amalgamation_by_payload),
824 )
825 .route(
826 "/genome/amalgamation_by_upload",
827 axum::routing::post(genome::post_amalgamation_by_upload),
828 )
829 .route(
830 "/genome/append-file",
831 axum::routing::post(genome::post_append_file),
832 )
833 .route(
834 "/genome/upload/file",
835 axum::routing::post(genome::post_upload_file),
836 )
837 .route(
838 "/genome/upload/file/edit",
839 axum::routing::post(genome::post_upload_file_edit),
840 )
841 .route(
842 "/genome/upload/string",
843 axum::routing::post(genome::post_upload_string),
844 )
845 .route(
847 "/neuroplasticity/plasticity_queue_depth",
848 get(neuroplasticity::get_plasticity_queue_depth)
849 .put(neuroplasticity::put_plasticity_queue_depth),
850 )
851 .route("/neuroplasticity/status", get(neuroplasticity::get_status))
852 .route(
853 "/neuroplasticity/transforming",
854 get(neuroplasticity::get_transforming),
855 )
856 .route(
857 "/neuroplasticity/configure",
858 axum::routing::post(neuroplasticity::post_configure),
859 )
860 .route(
861 "/neuroplasticity/enable/:area_id",
862 axum::routing::post(neuroplasticity::post_enable_area),
863 )
864 .route(
865 "/neuroplasticity/disable/:area_id",
866 axum::routing::post(neuroplasticity::post_disable_area),
867 )
868 .route(
870 "/insight/neurons/membrane_potential_status",
871 axum::routing::post(insight::post_neurons_membrane_potential_status),
872 )
873 .route(
874 "/insight/neuron/synaptic_potential_status",
875 axum::routing::post(insight::post_neuron_synaptic_potential_status),
876 )
877 .route(
878 "/insight/neurons/membrane_potential_set",
879 axum::routing::post(insight::post_neurons_membrane_potential_set),
880 )
881 .route(
882 "/insight/neuron/synaptic_potential_set",
883 axum::routing::post(insight::post_neuron_synaptic_potential_set),
884 )
885 .route("/insight/analytics", get(insight::get_analytics))
886 .route("/insight/data", get(insight::get_data))
887 .route(
889 "/input/vision",
890 get(input::get_vision).post(input::post_vision),
891 )
892 .route("/input/sources", get(input::get_sources))
893 .route(
894 "/input/configure",
895 axum::routing::post(input::post_configure),
896 )
897 .route("/output/targets", get(outputs::get_targets))
899 .route(
900 "/output/configure",
901 axum::routing::post(outputs::post_configure),
902 )
903 .route(
905 "/physiology/",
906 get(physiology::get_physiology).put(physiology::put_physiology),
907 )
908 .route(
910 "/simulation/upload/string",
911 axum::routing::post(simulation::post_stimulation_upload),
912 )
913 .route(
914 "/simulation/reset",
915 axum::routing::post(simulation::post_reset),
916 )
917 .route("/simulation/status", get(simulation::get_status))
918 .route("/simulation/stats", get(simulation::get_stats))
919 .route(
920 "/simulation/config",
921 axum::routing::post(simulation::post_config),
922 )
923 .route("/training/shock", axum::routing::post(training::post_shock))
925 .route("/training/shock/options", get(training::get_shock_options))
926 .route("/training/shock/status", get(training::get_shock_status))
927 .route(
928 "/training/shock/activate",
929 axum::routing::post(training::post_shock_activate),
930 )
931 .route(
932 "/training/reward/intensity",
933 axum::routing::post(training::post_reward_intensity),
934 )
935 .route(
936 "/training/reward",
937 axum::routing::post(training::post_reward),
938 )
939 .route(
940 "/training/punishment/intensity",
941 axum::routing::post(training::post_punishment_intensity),
942 )
943 .route(
944 "/training/punishment",
945 axum::routing::post(training::post_punishment),
946 )
947 .route(
948 "/training/gameover",
949 axum::routing::post(training::post_gameover),
950 )
951 .route("/training/brain_fitness", get(training::get_brain_fitness))
952 .route(
953 "/training/fitness_criteria",
954 get(training::get_fitness_criteria)
955 .put(training::put_fitness_criteria)
956 .post(training::post_fitness_criteria),
957 )
958 .route(
959 "/training/fitness_stats",
960 get(training::get_fitness_stats)
961 .put(training::put_fitness_stats)
962 .delete(training::delete_fitness_stats),
963 )
964 .route(
965 "/training/reset_fitness_stats",
966 axum::routing::delete(training::delete_reset_fitness_stats),
967 )
968 .route(
969 "/training/training_report",
970 get(training::get_training_report),
971 )
972 .route("/training/status", get(training::get_status))
973 .route("/training/stats", get(training::get_stats))
974 .route(
975 "/training/config",
976 axum::routing::post(training::post_config),
977 )
978 .route(
980 "/visualization/register_client",
981 axum::routing::post(visualization::post_register_client),
982 )
983 .route(
984 "/visualization/unregister_client",
985 axum::routing::post(visualization::post_unregister_client),
986 )
987 .route(
988 "/visualization/heartbeat",
989 axum::routing::post(visualization::post_heartbeat),
990 )
991 .route("/visualization/status", get(visualization::get_status))
992 .route("/monitoring/status", get(monitoring::get_status))
994 .route("/monitoring/metrics", get(monitoring::get_metrics))
995 .route("/monitoring/data", get(monitoring::get_data))
996 .route("/monitoring/performance", get(monitoring::get_performance))
997 .route("/evolution/status", get(evolution::get_status))
999 .route(
1000 "/evolution/config",
1001 axum::routing::post(evolution::post_config),
1002 )
1003 .route("/network/status", get(network::get_status))
1020 .route("/network/config", axum::routing::post(network::post_config))
1021}
1022
1023#[allow(dead_code)] async fn openapi_spec() -> Json<utoipa::openapi::OpenApi> {
1026 Json(ApiDoc::openapi())
1027}
1028
1029fn create_cors_layer() -> CorsLayer {
1040 CorsLayer::new()
1041 .allow_origin(Any)
1042 .allow_methods(Any)
1043 .allow_headers(Any)
1044}
1045
1046async fn log_request_response_bodies(
1048 request: Request<Body>,
1049 next: Next,
1050) -> Result<Response, StatusCode> {
1051 let (parts, body) = request.into_parts();
1052
1053 let should_log_request = matches!(parts.method.as_str(), "POST" | "PUT" | "PATCH" | "DELETE");
1055
1056 let body_bytes = if should_log_request {
1057 match body.collect().await {
1059 Ok(collected) => {
1060 let bytes = collected.to_bytes();
1061 if let Ok(body_str) = String::from_utf8(bytes.to_vec()) {
1063 if !body_str.is_empty() {
1064 tracing::trace!(target: "feagi-api", "Request body: {}", body_str);
1065 }
1066 }
1067 bytes
1068 }
1069 Err(_) => {
1070 return Err(StatusCode::INTERNAL_SERVER_ERROR);
1071 }
1072 }
1073 } else {
1074 axum::body::Bytes::new()
1075 };
1076
1077 let request = Request::from_parts(parts, Body::from(body_bytes));
1079
1080 let response = next.run(request).await;
1082
1083 let (parts, body) = response.into_parts();
1085
1086 match body.collect().await {
1087 Ok(collected) => {
1088 let bytes = collected.to_bytes();
1089 if bytes.len() < 10000 {
1091 if let Ok(body_str) = String::from_utf8(bytes.to_vec()) {
1093 if !body_str.is_empty() && body_str.starts_with('{') {
1094 tracing::trace!(target: "feagi-api", "Response body: {}", body_str);
1095 }
1096 }
1097 }
1098 Ok(Response::from_parts(parts, Body::from(bytes)))
1100 }
1101 Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
1102 }
1103}
1104
1105async fn root_redirect() -> Redirect {
1111 Redirect::permanent("/swagger-ui/")
1112}
1113
1114async fn custom_swagger_ui() -> Html<&'static str> {
1117 const CUSTOM_SWAGGER_HTML: &str = include_str!("../../../templates/custom-swagger-ui.html");
1118 Html(CUSTOM_SWAGGER_HTML)
1119}
1120
1121#[allow(dead_code)] async fn placeholder_handler(State(_state): State<ApiState>) -> Response {
1129 (
1130 StatusCode::NOT_IMPLEMENTED,
1131 Json(serde_json::json!({
1132 "error": "Not yet implemented",
1133 "message": "This endpoint is registered but not yet implemented in Rust. See Python implementation."
1134 }))
1135 ).into_response()
1136}
1137
1138#[allow(dead_code)] async fn placeholder_health_check(State(_state): State<ApiState>) -> Response {
1141 (
1142 StatusCode::OK,
1143 Json(serde_json::json!({
1144 "status": "ok",
1145 "message": "Health check placeholder - Python-compatible path structure confirmed",
1146 "burst_engine": false,
1147 "brain_readiness": false
1148 })),
1149 )
1150 .into_response()
1151}