Skip to main content

feagi_api/endpoints/
visualization.rs

1// Copyright 2025 Neuraville Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4/*!
5 * FEAGI v1 Visualization API
6 *
7 * Endpoints for managing visualization clients and streams
8 * Maps to Python: feagi/api/v1/visualization.py
9 */
10
11use crate::common::ApiState;
12use crate::common::{ApiError, ApiResult, Json, State};
13// Removed - using crate::common::State instead
14use serde_json::{json, Value};
15use std::collections::HashMap;
16
17// ============================================================================
18// VISUALIZATION CLIENT MANAGEMENT
19// ============================================================================
20
21/// Register a new visualization client for receiving neural activity streams.
22#[utoipa::path(
23    post,
24    path = "/v1/visualization/register_client",
25    tag = "visualization",
26    responses(
27        (status = 200, description = "Client registered", body = HashMap<String, serde_json::Value>),
28        (status = 500, description = "Internal server error")
29    )
30)]
31pub async fn post_register_client(
32    State(_state): State<ApiState>,
33    Json(request): Json<HashMap<String, Value>>,
34) -> ApiResult<Json<HashMap<String, Value>>> {
35    // Extract or generate client_id
36    let client_id = request
37        .get("client_id")
38        .and_then(|v| v.as_str())
39        .map(|s| s.to_string())
40        .unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
41
42    // TODO: Register visualization client with actual state management
43    tracing::info!(target: "feagi-api", "Registered visualization client: {}", client_id);
44
45    let mut response = HashMap::new();
46    response.insert("client_id".to_string(), json!(client_id));
47    response.insert("success".to_string(), json!(true));
48    response.insert(
49        "message".to_string(),
50        json!("Visualization client registered successfully"),
51    );
52
53    Ok(Json(response))
54}
55
56/// Unregister a visualization client to stop receiving neural activity streams.
57#[utoipa::path(
58    post,
59    path = "/v1/visualization/unregister_client",
60    tag = "visualization",
61    responses(
62        (status = 200, description = "Client unregistered", body = HashMap<String, String>),
63        (status = 500, description = "Internal server error")
64    )
65)]
66pub async fn post_unregister_client(
67    State(_state): State<ApiState>,
68    Json(request): Json<HashMap<String, Value>>,
69) -> ApiResult<Json<HashMap<String, String>>> {
70    // Validate client_id is provided
71    let client_id = request
72        .get("client_id")
73        .and_then(|v| v.as_str())
74        .ok_or_else(|| ApiError::invalid_input("Missing 'client_id' field"))?;
75
76    // TODO: Unregister visualization client
77    tracing::info!(target: "feagi-api", "Unregistered visualization client: {}", client_id);
78
79    Ok(Json(HashMap::from([(
80        "message".to_string(),
81        "Visualization client unregistered successfully".to_string(),
82    )])))
83}
84
85/// Send heartbeat from visualization client to maintain active connection.
86#[utoipa::path(
87    post,
88    path = "/v1/visualization/heartbeat",
89    tag = "visualization",
90    responses(
91        (status = 200, description = "Heartbeat received", body = HashMap<String, String>),
92        (status = 500, description = "Internal server error")
93    )
94)]
95pub async fn post_heartbeat(
96    State(_state): State<ApiState>,
97    Json(_request): Json<HashMap<String, Value>>,
98) -> ApiResult<Json<HashMap<String, String>>> {
99    // TODO: Process visualization heartbeat
100
101    Ok(Json(HashMap::from([(
102        "message".to_string(),
103        "Heartbeat received".to_string(),
104    )])))
105}
106
107/// Get visualization system status including active clients and FQ sampler state.
108#[utoipa::path(
109    get,
110    path = "/v1/visualization/status",
111    tag = "visualization",
112    responses(
113        (status = 200, description = "Visualization status", body = HashMap<String, serde_json::Value>),
114        (status = 500, description = "Internal server error")
115    )
116)]
117pub async fn get_status(State(_state): State<ApiState>) -> ApiResult<Json<HashMap<String, Value>>> {
118    // TODO: Retrieve visualization status
119    let mut response = HashMap::new();
120    response.insert("enabled".to_string(), json!(false));
121    response.insert("active_clients".to_string(), json!(0));
122    response.insert("fq_sampler_enabled".to_string(), json!(false));
123    response.insert("message".to_string(), json!("Visualization system idle"));
124
125    Ok(Json(response))
126}