import base64 import shutil from pathlib import Path from typing import Dict, List, Optional from contextlib import asynccontextmanager from fastapi import FastAPI, HTTPException from pydantic import BaseModel from agent import run_agent class ChatRequest(BaseModel): user_id: str user_message: str api_key: str provider: str = "openrouter" model: str = "perplexity/llama-3-sonar-large-32k-online" current_chat_history: Optional[List[Dict[str, str]]] = None user_files: Optional[Dict[str, str]] = None class ChatResponse(BaseModel): agent_response: str updated_chat_history: List[Dict[str, str]] updated_files: Dict[str, str] @asynccontextmanager async def lifespan(app: FastAPI): yield app = FastAPI(title="React Agent API", lifespan=lifespan) @app.post("/chat", response_model=ChatResponse) async def chat(request: ChatRequest): if not request.api_key: raise HTTPException(status_code=400, detail="api_key is required") workspace = Path(f"/tmp/{request.user_id}") workspace.mkdir(parents=True, exist_ok=True) try: if request.user_files: for filename, content_b64 in request.user_files.items(): filepath = workspace / filename filepath.parent.mkdir(parents=True, exist_ok=True) filepath.write_bytes(base64.b64decode(content_b64)) result = await run_agent( api_key=request.api_key, model=request.model, provider=request.provider, workspace=workspace, user_message=request.user_message, chat_history=request.current_chat_history or [], ) updated_files = {} if workspace.exists(): for filepath in sorted(workspace.rglob("*")): if filepath.is_file(): rel_path = str(filepath.relative_to(workspace)) updated_files[rel_path] = base64.b64encode( filepath.read_bytes() ).decode() return ChatResponse( agent_response=result["agent_response"], updated_chat_history=result["updated_chat_history"], updated_files=updated_files, ) finally: if workspace.exists(): shutil.rmtree(workspace, ignore_errors=True)