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