Spaces:
Sleeping
Sleeping
| """ | |
| 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 | |
| # ============================================================================ | |
| async def health_check(): | |
| """ | |
| Health check endpoint. | |
| Returns: | |
| HealthResponse: Status of the API | |
| """ | |
| return { | |
| "status": "healthy", | |
| "message": "AI Agent API is running" | |
| } | |
| 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)}" | |
| ) | |
| 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)}" | |
| ) | |
| 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 | |
| ) | |