Hamza4100's picture
Update backend/server/api.py
45fe286 verified
"""
FastAPI server for the AI Agent.
Exposes REST endpoints for agent interaction with A2UI streaming support.
"""
import os
import json
from typing import Optional
from pathlib import Path
from fastapi import FastAPI, HTTPException
from fastapi.responses import StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
import uvicorn
from agent import get_agent, A2UIMessage
# ============================================================================
# Configuration
# ============================================================================
# Get configuration from environment
FRONTEND_URL = os.getenv("FRONTEND_URL", "http://localhost:3000")
API_PORT = int(os.getenv("API_PORT", 8000))
API_HOST = os.getenv("API_HOST", "0.0.0.0")
DEBUG = os.getenv("DEBUG", "False").lower() == "true"
# ============================================================================
# FastAPI App Setup
# ============================================================================
app = FastAPI(
title="AI Agent API",
description="API for AI Agent with A2UI streaming support",
version="1.0.0"
)
# ============================================================================
# CORS Configuration (for frontend-backend communication)
# ============================================================================
cors_origins = [
"http://localhost:3000",
"http://localhost:5173", # Vite default
FRONTEND_URL,
]
if DEBUG:
cors_origins.append("*")
app.add_middleware(
CORSMiddleware,
allow_origins=cors_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ============================================================================
# Serve Static Frontend Files
# ============================================================================
frontend_dist = Path(__file__).parent.parent / "frontend" / "dist"
if frontend_dist.exists():
app.mount("/static", StaticFiles(directory=str(frontend_dist), html=False), name="static")
static_available = True
else:
static_available = False
# ============================================================================
# Request/Response Models
# ============================================================================
class ChatRequest(BaseModel):
"""Chat request model."""
message: str
class HealthResponse(BaseModel):
"""Health check response."""
status: str
message: str
# ============================================================================
# API Routes
# ============================================================================
@app.get("/health", response_model=HealthResponse)
async def health_check():
"""
Health check endpoint.
Returns:
HealthResponse: Status of the API
"""
return {
"status": "healthy",
"message": "AI Agent API is running"
}
@app.post("/chat")
async def chat(request: ChatRequest):
"""
Chat endpoint with A2UI streaming.
Processes user message through the AI Agent and streams A2UI events.
Args:
request: ChatRequest containing user message
Returns:
StreamingResponse: Server-Sent Events stream of A2UI messages
"""
if not request.message or not request.message.strip():
raise HTTPException(status_code=400, detail="Message cannot be empty")
try:
agent = get_agent()
def event_generator():
"""Generate A2UI events from agent processing."""
try:
for a2ui_message in agent.process_message(request.message.strip()):
# Convert A2UIMessage to dict
event_data = a2ui_message.to_dict()
# Format as JSON and send as Server-Sent Event
json_data = json.dumps(event_data)
yield f"data: {json_data}\n\n"
except Exception as e:
import traceback
error_msg = traceback.format_exc()
print(f"ERROR in event_generator: {error_msg}")
raise
return StreamingResponse(
event_generator(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no",
}
)
except Exception as e:
import traceback
error_msg = traceback.format_exc()
print(f"ERROR in /chat: {error_msg}")
raise HTTPException(
status_code=500,
detail=f"Error processing message: {str(e)}"
)
@app.delete("/chat/history")
async def clear_history():
"""
Clear chat history.
Returns:
dict: Confirmation message
"""
try:
agent = get_agent()
agent.clear_history()
return {
"status": "success",
"message": "Chat history cleared"
}
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Error clearing history: {str(e)}"
)
@app.get("/tools")
async def get_tools():
"""
Get available tools.
Returns:
dict: List of available tools
"""
try:
agent = get_agent()
return {
"tools": agent.available_tools
}
except Exception as e:
import traceback
error_msg = traceback.format_exc()
print(f"ERROR in /tools: {error_msg}")
raise HTTPException(
status_code=500,
detail=f"Error getting tools: {str(e)}"
)
# ============================================================================
# Serve Static Frontend Files (mount at root with lower precedence)
# ============================================================================
if frontend_dist.exists():
app.mount("/", StaticFiles(directory=str(frontend_dist), html=True), name="static")
static_available = True
else:
static_available = False
# ============================================================================
# Main
# ============================================================================
if __name__ == "__main__":
uvicorn.run(
app,
host=API_HOST,
port=API_PORT,
reload=DEBUG
)