import os from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from typing import List, Literal import httpx import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Read API keys from Hugging Face Secrets OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") HF_TOKEN = os.environ.get("HF_TOKEN") if not OPENAI_API_KEY: logger.warning("OPENAI_API_KEY not set — OpenAI provider will be unavailable") if not HF_TOKEN: logger.warning("HF_TOKEN not set — Hugging Face provider will be unavailable") # Provider config: each provider has a base URL and a key PROVIDERS = { "openai": { "url": "https://api.openai.com/v1/chat/completions", "key": OPENAI_API_KEY, }, "hf": { "url": "https://router.huggingface.co/v1/chat/completions", "key": HF_TOKEN, }, } app = FastAPI(title="Proxy for Unity") # Enable CORS for Unity WebGL builds app.add_middleware( CORSMiddleware, allow_origins=["*"], # In production, specify your domain allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) class Message(BaseModel): role: str content: str class ChatRequest(BaseModel): model: str temperature: float messages: List[Message] provider: Literal["openai", "hf"] = "openai" # default keeps existing Unity code working @app.get("/") async def root(): return { "status": "running", "message": "Proxy Active", "providers": { "openai": bool(OPENAI_API_KEY), "hf": bool(HF_TOKEN), }, "endpoints": { "chat": "POST /chat" } } @app.post("/chat") async def proxy_chat(request: ChatRequest): provider_config = PROVIDERS.get(request.provider) if not provider_config or not provider_config["key"]: raise HTTPException( status_code=400, detail=f"Provider '{request.provider}' is not configured" ) try: logger.info( f"Provider: {request.provider} | Model: {request.model} | " f"Messages: {len(request.messages)}" ) async with httpx.AsyncClient(timeout=60.0) as client: response = await client.post( provider_config["url"], json={ "model": request.model, "temperature": request.temperature, "messages": [ {"role": msg.role, "content": msg.content} for msg in request.messages ] }, headers={ "Authorization": f"Bearer {provider_config['key']}", "Content-Type": "application/json" } ) if response.status_code != 200: logger.error(f"{request.provider} API error: {response.text}") raise HTTPException( status_code=response.status_code, detail=response.text ) result = response.json() logger.info(f"Successfully proxied {request.provider} request") return result except httpx.HTTPError as e: logger.error(f"HTTP error: {str(e)}") raise HTTPException(status_code=500, detail=f"Proxy error: {str(e)}") except Exception as e: logger.error(f"Unexpected error: {str(e)}") raise HTTPException(status_code=500, detail=f"Server error: {str(e)}") @app.get("/health") async def health(): return { "status": "healthy", "openai_configured": bool(OPENAI_API_KEY), "hf_configured": bool(HF_TOKEN), }