"""FastAPI backend for LLM Council.""" from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from typing import List, Dict, Any import uuid from . import storage from .council import run_full_council app = FastAPI(title="LLM Council API") # Enable CORS for local development app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:5173", "http://localhost:3000"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) class CreateConversationRequest(BaseModel): """Request to create a new conversation.""" pass class SendMessageRequest(BaseModel): """Request to send a message in a conversation.""" content: str class ConversationMetadata(BaseModel): """Conversation metadata for list view.""" id: str created_at: str message_count: int class Conversation(BaseModel): """Full conversation with all messages.""" id: str created_at: str messages: List[Dict[str, Any]] @app.get("/") async def root(): """Health check endpoint.""" return {"status": "ok", "service": "LLM Council API"} @app.get("/api/conversations", response_model=List[ConversationMetadata]) async def list_conversations(): """List all conversations (metadata only).""" return storage.list_conversations() @app.post("/api/conversations", response_model=Conversation) async def create_conversation(request: CreateConversationRequest): """Create a new conversation.""" conversation_id = str(uuid.uuid4()) conversation = storage.create_conversation(conversation_id) return conversation @app.get("/api/conversations/{conversation_id}", response_model=Conversation) async def get_conversation(conversation_id: str): """Get a specific conversation with all its messages.""" conversation = storage.get_conversation(conversation_id) if conversation is None: raise HTTPException(status_code=404, detail="Conversation not found") return conversation @app.post("/api/conversations/{conversation_id}/message") async def send_message(conversation_id: str, request: SendMessageRequest): """ Send a message and run the 3-stage council process. Returns the complete response with all stages. """ # Check if conversation exists conversation = storage.get_conversation(conversation_id) if conversation is None: raise HTTPException(status_code=404, detail="Conversation not found") # Add user message storage.add_user_message(conversation_id, request.content) # Run the 3-stage council process stage1_results, stage2_results, stage3_result, metadata = await run_full_council( request.content ) # Add assistant message with all stages storage.add_assistant_message( conversation_id, stage1_results, stage2_results, stage3_result ) # Return the complete response with metadata return { "stage1": stage1_results, "stage2": stage2_results, "stage3": stage3_result, "metadata": metadata } if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8001)