use crate::utils::error::OpenCratesError;
use crate::utils::metrics::OpenCratesMetrics;
use crate::utils::openai_agents::{AgentOrchestrator, AgentConfig, WorkflowDefinition};
use crate::providers::openai::OpenAIProvider;
use crate::stages::CrateContext;
use axum::{
extract::{Path, Query, State, WebSocketUpgrade},
http::StatusCode,
response::{Html, IntoResponse, Json},
routing::{get, post, put, delete},
Router,
};
use axum_extra::extract::WithRejection;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;
use tracing::{info, instrument, error, debug};
use uuid::Uuid;
#[derive(Clone)]
pub struct FastAPIState {
pub openai_provider: Arc<OpenAIProvider>,
pub agent_orchestrator: Arc<AgentOrchestrator>,
pub metrics: Arc<OpenCratesMetrics>,
pub active_sessions: Arc<RwLock<HashMap<String, SessionInfo>>>,
pub workflow_registry: Arc<RwLock<HashMap<String, WorkflowDefinition>>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionInfo {
pub session_id: String,
pub user_id: Option<String>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub last_activity: chrono::DateTime<chrono::Utc>,
pub context: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GenerateCodeRequest {
pub description: String,
pub crate_name: String,
pub version: String,
pub dependencies: Option<HashMap<String, String>>,
pub features: Option<Vec<String>>,
pub template: Option<String>,
pub model: Option<String>,
pub temperature: Option<f32>,
pub max_tokens: Option<u32>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AnalyzeCodeRequest {
pub code: String,
pub language: String,
pub analysis_type: Vec<AnalysisType>,
pub context: Option<HashMap<String, String>>,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum AnalysisType {
Security,
Performance,
Quality,
Documentation,
Testing,
Architecture,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AgentTaskRequest {
pub agent_id: String,
pub task: String,
pub input: String,
pub context: Option<HashMap<String, serde_json::Value>>,
pub timeout_seconds: Option<u64>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct WorkflowExecutionRequest {
pub workflow_id: String,
pub inputs: HashMap<String, String>,
pub priority: Option<u8>,
pub async_execution: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CodeGenerationResponse {
pub success: bool,
pub crate_context: Option<CrateContextResponse>,
pub generated_files: Vec<GeneratedFile>,
pub metrics: GenerationMetrics,
pub warnings: Vec<String>,
pub errors: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CrateContextResponse {
pub name: String,
pub version: String,
pub description: String,
pub dependencies: HashMap<String, String>,
pub features: Vec<String>,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GeneratedFile {
pub path: String,
pub content: String,
pub file_type: String,
pub size_bytes: usize,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GenerationMetrics {
pub tokens_used: u64,
pub generation_time_ms: u64,
pub files_generated: usize,
pub total_lines: usize,
pub complexity_score: f32,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CodeAnalysisResponse {
pub success: bool,
pub analysis_results: HashMap<AnalysisType, AnalysisResult>,
pub overall_score: f32,
pub recommendations: Vec<Recommendation>,
pub metrics: AnalysisMetrics,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AnalysisResult {
pub score: f32,
pub issues: Vec<Issue>,
pub suggestions: Vec<String>,
pub confidence: f32,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Issue {
pub severity: IssueSeverity,
pub category: String,
pub description: String,
pub line_number: Option<u32>,
pub column: Option<u32>,
pub fix_suggestion: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum IssueSeverity {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Recommendation {
pub priority: u8,
pub category: String,
pub title: String,
pub description: String,
pub impact: String,
pub effort: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AnalysisMetrics {
pub analysis_time_ms: u64,
pub lines_analyzed: usize,
pub functions_analyzed: usize,
pub complexity_metrics: HashMap<String, f32>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AgentTaskResponse {
pub success: bool,
pub result: String,
pub agent_id: String,
pub execution_time_ms: u64,
pub tokens_used: Option<u64>,
pub confidence: f32,
pub metadata: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct WorkflowExecutionResponse {
pub success: bool,
pub execution_id: String,
pub status: WorkflowStatus,
pub results: HashMap<String, serde_json::Value>,
pub total_time_ms: u64,
pub steps_completed: usize,
pub steps_total: usize,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum WorkflowStatus {
Started,
Running,
Completed,
Failed,
Cancelled,
}
#[derive(Debug, Deserialize)]
pub struct PaginationQuery {
pub page: Option<u32>,
pub page_size: Option<u32>,
pub sort_by: Option<String>,
pub sort_order: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct FilterQuery {
pub language: Option<String>,
pub category: Option<String>,
pub difficulty: Option<String>,
pub tags: Option<String>,
}
pub fn create_fastapi_router(state: FastAPIState) -> Router {
Router::new()
.route("/health", get(health_check))
.route("/status", get(system_status))
.route("/metrics", get(get_metrics))
.route("/api/v1/generate/code", post(generate_code))
.route("/api/v1/generate/crate", post(generate_crate))
.route("/api/v1/generate/template", post(generate_from_template))
.route("/api/v1/analyze/code", post(analyze_code))
.route("/api/v1/analyze/project", post(analyze_project))
.route("/api/v1/analyze/security", post(security_analysis))
.route("/api/v1/agents", get(list_agents))
.route("/api/v1/agents", post(create_agent))
.route("/api/v1/agents/:agent_id", get(get_agent))
.route("/api/v1/agents/:agent_id", put(update_agent))
.route("/api/v1/agents/:agent_id", delete(delete_agent))
.route("/api/v1/agents/:agent_id/tasks", post(execute_agent_task))
.route("/api/v1/workflows", get(list_workflows))
.route("/api/v1/workflows", post(create_workflow))
.route("/api/v1/workflows/:workflow_id", get(get_workflow))
.route("/api/v1/workflows/:workflow_id/execute", post(execute_workflow))
.route("/api/v1/workflows/:workflow_id/status/:execution_id", get(get_workflow_status))
.route("/api/v1/sessions", post(create_session))
.route("/api/v1/sessions/:session_id", get(get_session))
.route("/api/v1/sessions/:session_id", delete(end_session))
.route("/ws/chat", get(websocket_chat))
.route("/ws/workflow/:workflow_id", get(websocket_workflow))
.route("/api/v1/python/execute", post(execute_python_code))
.route("/api/v1/python/install", post(install_python_package))
.route("/api/v1/batch/generate", post(batch_generate))
.route("/api/v1/batch/analyze", post(batch_analyze))
.route("/api/v1/batch/status/:batch_id", get(get_batch_status))
.layer(CorsLayer::permissive())
.layer(TraceLayer::new_for_http())
.with_state(state)
}
#[instrument]
async fn health_check() -> impl IntoResponse {
Json(serde_json::json!({
"status": "healthy",
"timestamp": chrono::Utc::now(),
"version": env!("CARGO_PKG_VERSION")
}))
}
#[instrument(skip(state))]
async fn system_status(State(state): State<FastAPIState>) -> impl IntoResponse {
let sessions = state.active_sessions.read().await;
let workflows = state.workflow_registry.read().await;
Json(serde_json::json!({
"status": "operational",
"uptime_seconds": 0, "active_sessions": sessions.len(),
"registered_workflows": workflows.len(),
"system_load": get_system_load().await,
"memory_usage": get_memory_usage().await,
"openai_status": check_openai_status(&state).await
}))
}
#[instrument(skip(state))]
async fn get_metrics(State(state): State<FastAPIState>) -> impl IntoResponse {
let metrics = state.metrics.get_summary().await;
Json(metrics)
}
#[instrument(skip(state, request))]
async fn generate_code(
State(state): State<FastAPIState>,
Json(request): Json<GenerateCodeRequest>
) -> Result<Json<CodeGenerationResponse>, StatusCode> {
let start_time = std::time::Instant::now();
let spec = crate::utils::templates::CrateSpec {
name: request.crate_name,
description: request.description,
version: request.version,
dependencies: request.dependencies.unwrap_or_default(),
features: request.features.unwrap_or_default(),
..Default::default()
};
match state.openai_provider.generate_crate(&spec).await {
Ok(context) => {
let generation_time = start_time.elapsed().as_millis() as u64;
let response = CodeGenerationResponse {
success: true,
crate_context: Some(CrateContextResponse {
name: context.crate_name.clone(),
version: context.version.clone(),
description: context.description.clone(),
dependencies: context.dependencies.clone().into_iter().collect(),
features: context.features.clone(),
metadata: context.metadata.clone(),
}),
generated_files: convert_to_generated_files(&context),
metrics: GenerationMetrics {
tokens_used: 0, generation_time_ms: generation_time,
files_generated: context.generated_files.len(),
total_lines: count_total_lines(&context),
complexity_score: 0.0, },
warnings: Vec::new(),
errors: Vec::new(),
};
Ok(Json(response))
}
Err(e) => {
error!("Code generation failed: {}", e);
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
}
}
#[instrument(skip(state, request))]
async fn analyze_code(
State(state): State<FastAPIState>,
Json(request): Json<AnalyzeCodeRequest>
) -> Result<Json<CodeAnalysisResponse>, StatusCode> {
let start_time = std::time::Instant::now();
let analysis_task = format!(
"Analyze this {} code for: {:?}\n\nCode:\n{}",
request.language, request.analysis_type, request.code
);
match state.agent_orchestrator.execute_agent_task("code_analyzer", "analyze", &analysis_task).await {
Ok(result) => {
let analysis_time = start_time.elapsed().as_millis() as u64;
let response = CodeAnalysisResponse {
success: true,
analysis_results: create_mock_analysis_results(&request.analysis_type),
overall_score: 8.5, recommendations: create_mock_recommendations(),
metrics: AnalysisMetrics {
analysis_time_ms: analysis_time,
lines_analyzed: request.code.lines().count(),
functions_analyzed: count_functions(&request.code),
complexity_metrics: HashMap::new(),
},
};
Ok(Json(response))
}
Err(e) => {
error!("Code analysis failed: {}", e);
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
}
}
#[instrument(skip(state, request))]
async fn execute_agent_task(
State(state): State<FastAPIState>,
Json(request): Json<AgentTaskRequest>
) -> Result<Json<AgentTaskResponse>, StatusCode> {
let start_time = std::time::Instant::now();
match state.agent_orchestrator.execute_agent_task(&request.agent_id, &request.task, &request.input).await {
Ok(result) => {
let execution_time = start_time.elapsed().as_millis() as u64;
let response = AgentTaskResponse {
success: true,
result: result.content,
agent_id: request.agent_id,
execution_time_ms: execution_time,
tokens_used: result.usage.map(|u| u.total_tokens as u64),
confidence: result.confidence,
metadata: result.metadata,
};
Ok(Json(response))
}
Err(e) => {
error!("Agent task execution failed: {}", e);
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
}
}
#[instrument(skip(state, request))]
async fn execute_workflow(
State(state): State<FastAPIState>,
Path(workflow_id): Path<String>,
Json(request): Json<WorkflowExecutionRequest>
) -> Result<Json<WorkflowExecutionResponse>, StatusCode> {
let start_time = std::time::Instant::now();
match state.agent_orchestrator.execute_workflow(&workflow_id, request.inputs).await {
Ok(result) => {
let execution_time = start_time.elapsed().as_millis() as u64;
let response = WorkflowExecutionResponse {
success: true,
execution_id: result.execution_id,
status: convert_workflow_status(result.status),
results: convert_step_results(result.step_results),
total_time_ms: execution_time,
steps_completed: result.step_results.len(),
steps_total: result.step_results.len(), };
Ok(Json(response))
}
Err(e) => {
error!("Workflow execution failed: {}", e);
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
}
}
#[instrument(skip(ws, state))]
async fn websocket_chat(
ws: WebSocketUpgrade,
State(state): State<FastAPIState>
) -> impl IntoResponse {
ws.on_upgrade(move |socket| handle_chat_websocket(socket, state))
}
async fn handle_chat_websocket(
socket: axum::extract::ws::WebSocket,
state: FastAPIState
) {
info!("New WebSocket chat connection");
}
#[instrument(skip(state))]
async fn create_session(State(state): State<FastAPIState>) -> impl IntoResponse {
let session_id = Uuid::new_v4().to_string();
let session = SessionInfo {
session_id: session_id.clone(),
user_id: None,
created_at: chrono::Utc::now(),
last_activity: chrono::Utc::now(),
context: HashMap::new(),
};
let mut sessions = state.active_sessions.write().await;
sessions.insert(session_id.clone(), session);
Json(serde_json::json!({
"session_id": session_id,
"created_at": chrono::Utc::now()
}))
}
async fn get_system_load() -> f32 {
0.5
}
async fn get_memory_usage() -> HashMap<String, u64> {
HashMap::new()
}
async fn check_openai_status(state: &FastAPIState) -> String {
match state.openai_provider.verify_connection().await {
Ok(true) => "connected".to_string(),
Ok(false) => "disconnected".to_string(),
Err(_) => "error".to_string(),
}
}
fn convert_to_generated_files(context: &CrateContext) -> Vec<GeneratedFile> {
context.generated_files.iter().map(|(path, content)| {
GeneratedFile {
path: path.clone(),
content: content.clone(),
file_type: get_file_type(path),
size_bytes: content.len(),
}
}).collect()
}
fn get_file_type(path: &str) -> String {
match path.split('.').last() {
Some("rs") => "rust".to_string(),
Some("toml") => "toml".to_string(),
Some("md") => "markdown".to_string(),
Some("yml") | Some("yaml") => "yaml".to_string(),
Some("json") => "json".to_string(),
_ => "text".to_string(),
}
}
fn count_total_lines(context: &CrateContext) -> usize {
context.generated_files.values().map(|content| content.lines().count()).sum()
}
fn count_functions(code: &str) -> usize {
code.lines().filter(|line| line.trim_start().starts_with("fn ")).count()
}
fn convert_workflow_status(status: crate::utils::openai_agents::WorkflowStatus) -> WorkflowStatus {
match status {
crate::utils::openai_agents::WorkflowStatus::Running => WorkflowStatus::Running,
crate::utils::openai_agents::WorkflowStatus::Completed => WorkflowStatus::Completed,
crate::utils::openai_agents::WorkflowStatus::Failed => WorkflowStatus::Failed,
crate::utils::openai_agents::WorkflowStatus::Cancelled => WorkflowStatus::Cancelled,
}
}
fn convert_step_results(
step_results: HashMap<String, crate::utils::openai_agents::StepResult>
) -> HashMap<String, serde_json::Value> {
step_results.into_iter().map(|(k, v)| {
(k, serde_json::json!({
"status": format!("{:?}", v.status),
"output": v.output,
"duration": v.duration_seconds
}))
}).collect()
}
fn create_mock_analysis_results(types: &[AnalysisType]) -> HashMap<AnalysisType, AnalysisResult> {
types.iter().map(|t| {
(t.clone(), AnalysisResult {
score: 8.0,
issues: Vec::new(),
suggestions: vec!["Consider adding documentation".to_string()],
confidence: 0.85,
})
}).collect()
}
fn create_mock_recommendations() -> Vec<Recommendation> {
vec![
Recommendation {
priority: 1,
category: "Performance".to_string(),
title: "Optimize memory usage".to_string(),
description: "Consider using Vec::with_capacity() for better performance".to_string(),
impact: "Medium".to_string(),
effort: "Low".to_string(),
}
]
}
async fn list_agents(State(_state): State<FastAPIState>) -> impl IntoResponse {
Json(serde_json::json!({ "agents": [] }))
}
async fn create_agent(State(_state): State<FastAPIState>) -> impl IntoResponse {
Json(serde_json::json!({ "success": true }))
}
async fn get_agent(State(_state): State<FastAPIState>, Path(_agent_id): Path<String>) -> impl IntoResponse {
Json(serde_json::json!({ "agent": {} }))
}
async fn update_agent(State(_state): State<FastAPIState>, Path(_agent_id): Path<String>) -> impl IntoResponse {
Json(serde_json::json!({ "success": true }))
}
async fn delete_agent(State(_state): State<FastAPIState>, Path(_agent_id): Path<String>) -> impl IntoResponse {
Json(serde_json::json!({ "success": true }))
}
async fn generate_crate(State(_state): State<FastAPIState>) -> impl IntoResponse {
Json(serde_json::json!({ "success": true }))
}
async fn generate_from_template(State(_state): State<FastAPIState>) -> impl IntoResponse {
Json(serde_json::json!({ "success": true }))
}
async fn analyze_project(State(_state): State<FastAPIState>) -> impl IntoResponse {
Json(serde_json::json!({ "success": true }))
}
async fn security_analysis(State(_state): State<FastAPIState>) -> impl IntoResponse {
Json(serde_json::json!({ "success": true }))
}
async fn list_workflows(State(_state): State<FastAPIState>) -> impl IntoResponse {
Json(serde_json::json!({ "workflows": [] }))
}
async fn create_workflow(State(_state): State<FastAPIState>) -> impl IntoResponse {
Json(serde_json::json!({ "success": true }))
}
async fn get_workflow(State(_state): State<FastAPIState>, Path(_workflow_id): Path<String>) -> impl IntoResponse {
Json(serde_json::json!({ "workflow": {} }))
}
async fn get_workflow_status(State(_state): State<FastAPIState>, Path((_workflow_id, _execution_id)): Path<(String, String)>) -> impl IntoResponse {
Json(serde_json::json!({ "status": "completed" }))
}
async fn get_session(State(_state): State<FastAPIState>, Path(_session_id): Path<String>) -> impl IntoResponse {
Json(serde_json::json!({ "session": {} }))
}
async fn end_session(State(_state): State<FastAPIState>, Path(_session_id): Path<String>) -> impl IntoResponse {
Json(serde_json::json!({ "success": true }))
}
async fn websocket_workflow(ws: WebSocketUpgrade, Path(_workflow_id): Path<String>) -> impl IntoResponse {
ws.on_upgrade(|_socket| async { })
}
async fn execute_python_code(State(_state): State<FastAPIState>) -> impl IntoResponse {
Json(serde_json::json!({ "success": true }))
}
async fn install_python_package(State(_state): State<FastAPIState>) -> impl IntoResponse {
Json(serde_json::json!({ "success": true }))
}
async fn batch_generate(State(_state): State<FastAPIState>) -> impl IntoResponse {
Json(serde_json::json!({ "batch_id": "123", "status": "started" }))
}
async fn batch_analyze(State(_state): State<FastAPIState>) -> impl IntoResponse {
Json(serde_json::json!({ "batch_id": "456", "status": "started" }))
}
async fn get_batch_status(State(_state): State<FastAPIState>, Path(_batch_id): Path<String>) -> impl IntoResponse {
Json(serde_json::json!({ "status": "completed" }))
}