feagi-api 0.0.12

FEAGI REST API layer with HTTP and ZMQ transport adapters
Documentation
// Copyright 2025 Neuraville Inc.
// SPDX-License-Identifier: Apache-2.0

/*!
 * FEAGI v1 Training API
 *
 * Endpoints for training, reinforcement learning, and fitness evaluation
 * Maps to Python: feagi/api/v1/training.py
 */

use crate::common::ApiState;
use crate::common::{ApiError, ApiResult, Json, State};
// Removed - using crate::common::State instead
use serde_json::{json, Value};
use std::collections::HashMap;

// ============================================================================
// REINFORCEMENT LEARNING
// ============================================================================

/// Configure shock/punishment scenarios for reinforcement learning.
#[utoipa::path(
    post,
    path = "/v1/training/shock",
    tag = "training",
    responses(
        (status = 200, description = "Shock configured", body = HashMap<String, String>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn post_shock(
    State(_state): State<ApiState>,
    Json(request): Json<HashMap<String, Value>>,
) -> ApiResult<Json<HashMap<String, String>>> {
    // Validate shock configuration
    let _shock = request
        .get("shock")
        .ok_or_else(|| ApiError::invalid_input("Missing 'shock' field"))?;

    // TODO: Configure shock scenarios
    tracing::info!(target: "feagi-api", "Shock configuration updated");

    Ok(Json(HashMap::from([(
        "message".to_string(),
        "Shock configured successfully".to_string(),
    )])))
}

/// Get available shock/punishment options for training.
#[utoipa::path(
    get,
    path = "/v1/training/shock/options",
    tag = "training",
    responses(
        (status = 200, description = "Shock options", body = HashMap<String, serde_json::Value>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn get_shock_options(
    State(_state): State<ApiState>,
) -> ApiResult<Json<HashMap<String, Value>>> {
    // TODO: Retrieve available shock options
    let mut response = HashMap::new();
    response.insert("options".to_string(), json!(Vec::<String>::new()));

    Ok(Json(response))
}

/// Get current shock/punishment status and active scenarios.
#[utoipa::path(
    get,
    path = "/v1/training/shock/status",
    tag = "training",
    responses(
        (status = 200, description = "Shock status", body = HashMap<String, serde_json::Value>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn get_shock_status(
    State(_state): State<ApiState>,
) -> ApiResult<Json<HashMap<String, Value>>> {
    // TODO: Retrieve shock status
    let mut response = HashMap::new();
    response.insert("active".to_string(), json!(false));
    response.insert("scenarios".to_string(), json!(Vec::<String>::new()));

    Ok(Json(response))
}

/// Set reward intensity for positive reinforcement.
#[utoipa::path(
    post,
    path = "/v1/training/reward/intensity",
    tag = "training",
    responses(
        (status = 200, description = "Reward intensity set", body = HashMap<String, String>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn post_reward_intensity(
    State(_state): State<ApiState>,
    Json(_request): Json<HashMap<String, Value>>,
) -> ApiResult<Json<HashMap<String, String>>> {
    // TODO: Set reward intensity

    Ok(Json(HashMap::from([(
        "message".to_string(),
        "Reward intensity set successfully".to_string(),
    )])))
}

/// Set punishment intensity for negative reinforcement.
#[utoipa::path(
    post,
    path = "/v1/training/punishment/intensity",
    tag = "training",
    responses(
        (status = 200, description = "Punishment intensity set", body = HashMap<String, String>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn post_punishment_intensity(
    State(_state): State<ApiState>,
    Json(_request): Json<HashMap<String, Value>>,
) -> ApiResult<Json<HashMap<String, String>>> {
    // TODO: Set punishment intensity

    Ok(Json(HashMap::from([(
        "message".to_string(),
        "Punishment intensity set successfully".to_string(),
    )])))
}

/// Signal game over condition for episode termination.
#[utoipa::path(
    post,
    path = "/v1/training/gameover",
    tag = "training",
    responses(
        (status = 200, description = "Game over signaled", body = HashMap<String, String>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn post_gameover(
    State(_state): State<ApiState>,
) -> ApiResult<Json<HashMap<String, String>>> {
    // TODO: Process game over condition

    Ok(Json(HashMap::from([(
        "message".to_string(),
        "Game over processed".to_string(),
    )])))
}

// ============================================================================
// FITNESS & EVOLUTION
// ============================================================================

/// Get current brain fitness score for evolutionary evaluation.
#[utoipa::path(
    get,
    path = "/v1/training/brain_fitness",
    tag = "training",
    responses(
        (status = 200, description = "Brain fitness", body = HashMap<String, serde_json::Value>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn get_brain_fitness(
    State(_state): State<ApiState>,
) -> ApiResult<Json<HashMap<String, Value>>> {
    // TODO: Calculate and return brain fitness
    let mut response = HashMap::new();
    response.insert("fitness".to_string(), json!(0.0));

    Ok(Json(response))
}

/// Get fitness evaluation criteria used for brain assessment.
#[utoipa::path(
    get,
    path = "/v1/training/fitness_criteria",
    tag = "training",
    responses(
        (status = 200, description = "Fitness criteria", body = HashMap<String, serde_json::Value>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn get_fitness_criteria(
    State(_state): State<ApiState>,
) -> ApiResult<Json<HashMap<String, Value>>> {
    // TODO: Retrieve fitness criteria
    let mut response = HashMap::new();
    response.insert("criteria".to_string(), json!({}));

    Ok(Json(response))
}

/// Update fitness evaluation criteria for brain assessment.
#[utoipa::path(
    put,
    path = "/v1/training/fitness_criteria",
    tag = "training",
    responses(
        (status = 200, description = "Fitness criteria updated", body = HashMap<String, String>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn put_fitness_criteria(
    State(_state): State<ApiState>,
    Json(_request): Json<HashMap<String, Value>>,
) -> ApiResult<Json<HashMap<String, String>>> {
    // TODO: Update fitness criteria

    Ok(Json(HashMap::from([(
        "message".to_string(),
        "Fitness criteria updated successfully".to_string(),
    )])))
}

/// Get fitness statistics including historical performance data.
#[utoipa::path(
    get,
    path = "/v1/training/fitness_stats",
    tag = "training",
    responses(
        (status = 200, description = "Fitness statistics", body = HashMap<String, serde_json::Value>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn get_fitness_stats(
    State(_state): State<ApiState>,
) -> ApiResult<Json<HashMap<String, Value>>> {
    // TODO: Retrieve fitness statistics
    let mut response = HashMap::new();
    response.insert("stats".to_string(), json!({}));

    Ok(Json(response))
}

/// Get training progress report with performance metrics and insights.
#[utoipa::path(
    get,
    path = "/v1/training/training_report",
    tag = "training",
    responses(
        (status = 200, description = "Training report", body = HashMap<String, serde_json::Value>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn get_training_report(
    State(_state): State<ApiState>,
) -> ApiResult<Json<HashMap<String, Value>>> {
    // TODO: Generate training report
    let mut response = HashMap::new();
    response.insert("report".to_string(), json!({}));

    Ok(Json(response))
}

/// Get training system status including active state and current mode.
#[utoipa::path(
    get,
    path = "/v1/training/status",
    tag = "training",
    responses(
        (status = 200, description = "Training status", body = HashMap<String, serde_json::Value>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn get_status(State(_state): State<ApiState>) -> ApiResult<Json<HashMap<String, Value>>> {
    // TODO: Retrieve training status
    let mut response = HashMap::new();
    response.insert("active".to_string(), json!(false));
    response.insert("mode".to_string(), json!("idle"));

    Ok(Json(response))
}

/// Get training statistics including episodes and rewards.
#[utoipa::path(
    get,
    path = "/v1/training/stats",
    tag = "training",
    responses(
        (status = 200, description = "Training statistics", body = HashMap<String, serde_json::Value>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn get_stats(State(_state): State<ApiState>) -> ApiResult<Json<HashMap<String, Value>>> {
    // TODO: Retrieve training statistics
    let mut response = HashMap::new();
    response.insert("total_episodes".to_string(), json!(0));
    response.insert("total_rewards".to_string(), json!(0.0));

    Ok(Json(response))
}

/// Configure training parameters including learning rates and reward settings.
#[utoipa::path(
    post,
    path = "/v1/training/config",
    tag = "training",
    responses(
        (status = 200, description = "Training configured", body = HashMap<String, String>),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn post_config(
    State(_state): State<ApiState>,
    Json(_request): Json<HashMap<String, Value>>,
) -> ApiResult<Json<HashMap<String, String>>> {
    // TODO: Apply training configuration

    Ok(Json(HashMap::from([(
        "message".to_string(),
        "Training configured successfully".to_string(),
    )])))
}

// EXACT Python paths:
/// Apply reward signal for positive reinforcement learning.
#[utoipa::path(post, path = "/v1/training/reward", tag = "training")]
pub async fn post_reward(
    State(_state): State<ApiState>,
    Json(_req): Json<HashMap<String, Value>>,
) -> ApiResult<Json<HashMap<String, String>>> {
    Ok(Json(HashMap::from([(
        "message".to_string(),
        "Reward applied".to_string(),
    )])))
}

/// Apply punishment signal for negative reinforcement learning.
#[utoipa::path(post, path = "/v1/training/punishment", tag = "training")]
pub async fn post_punishment(
    State(_state): State<ApiState>,
    Json(_req): Json<HashMap<String, Value>>,
) -> ApiResult<Json<HashMap<String, String>>> {
    Ok(Json(HashMap::from([(
        "message".to_string(),
        "Punishment applied".to_string(),
    )])))
}

/// Activate shock/punishment scenario immediately.
#[utoipa::path(post, path = "/v1/training/shock/activate", tag = "training")]
pub async fn post_shock_activate(
    State(_state): State<ApiState>,
) -> ApiResult<Json<HashMap<String, String>>> {
    Ok(Json(HashMap::from([(
        "message".to_string(),
        "Shock activated".to_string(),
    )])))
}

/// Set fitness evaluation criteria (alternative endpoint).
#[utoipa::path(post, path = "/v1/training/fitness_criteria", tag = "training")]
pub async fn post_fitness_criteria(
    State(_state): State<ApiState>,
    Json(_req): Json<HashMap<String, Value>>,
) -> ApiResult<Json<HashMap<String, String>>> {
    Ok(Json(HashMap::from([(
        "message".to_string(),
        "Fitness criteria set".to_string(),
    )])))
}

/// Update fitness statistics with new data.
#[utoipa::path(put, path = "/v1/training/fitness_stats", tag = "training")]
pub async fn put_fitness_stats(
    State(_state): State<ApiState>,
    Json(_req): Json<HashMap<String, Value>>,
) -> ApiResult<Json<HashMap<String, String>>> {
    Ok(Json(HashMap::from([(
        "message".to_string(),
        "Fitness stats updated".to_string(),
    )])))
}

/// Delete fitness statistics data.
#[utoipa::path(delete, path = "/v1/training/fitness_stats", tag = "training")]
pub async fn delete_fitness_stats(
    State(_state): State<ApiState>,
) -> ApiResult<Json<HashMap<String, String>>> {
    Ok(Json(HashMap::from([(
        "message".to_string(),
        "Fitness stats deleted".to_string(),
    )])))
}

/// Reset fitness statistics to initial state.
#[utoipa::path(delete, path = "/v1/training/reset_fitness_stats", tag = "training")]
pub async fn delete_reset_fitness_stats(
    State(_state): State<ApiState>,
) -> ApiResult<Json<HashMap<String, String>>> {
    Ok(Json(HashMap::from([(
        "message".to_string(),
        "Fitness stats reset".to_string(),
    )])))
}