Spaces:
Running
Running
| 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 | |
| async def root(): | |
| return { | |
| "status": "running", | |
| "message": "Proxy Active", | |
| "providers": { | |
| "openai": bool(OPENAI_API_KEY), | |
| "hf": bool(HF_TOKEN), | |
| }, | |
| "endpoints": { | |
| "chat": "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)}") | |
| async def health(): | |
| return { | |
| "status": "healthy", | |
| "openai_configured": bool(OPENAI_API_KEY), | |
| "hf_configured": bool(HF_TOKEN), | |
| } | |