Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """HTTP API around Emma-5 — host-agnostic (local, tunnel, HF Space, Cloud Run). | |
| Run locally: | |
| ./.venv/bin/uvicorn server:app --port 8000 | |
| Then POST /chat {"question": "..."} -> {"answer": "...", "seconds": 3.1} | |
| """ | |
| import hmac | |
| import os | |
| import threading | |
| import time | |
| from fastapi import Depends, FastAPI, Header, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel, Field | |
| from emma_chat import Emma, DEFAULT_MODEL_DIR | |
| # Optional shared-secret auth. When EMMA_API_KEY is set (e.g. on a public VM), | |
| # /chat requires a matching X-API-Key header. Unset (local dev) => open. | |
| API_KEY = os.environ.get("EMMA_API_KEY") | |
| def require_key(x_api_key: str | None = Header(default=None)): | |
| if API_KEY and not (x_api_key and hmac.compare_digest(x_api_key, API_KEY)): | |
| raise HTTPException(status_code=401, detail="invalid or missing api key") | |
| app = FastAPI(title="Emma4ever — live Emma-5") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=os.environ.get("EMMA_ALLOWED_ORIGINS", "*").split(","), | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Load once at import (HF Spaces / uvicorn workers each load their own copy). | |
| _emma = Emma(os.environ.get("EMMA_MODEL_DIR", DEFAULT_MODEL_DIR)) | |
| # Serialize inference: one CPU forward at a time keeps memory + latency bounded | |
| # on small free instances. | |
| _lock = threading.Lock() | |
| class ChatRequest(BaseModel): | |
| question: str = Field(min_length=1, max_length=500) | |
| temperature: float = Field(0.85, ge=0.0, le=2.0) | |
| top_p: float = Field(0.95, ge=0.1, le=1.0) | |
| max_new: int = Field(80, ge=1, le=200) | |
| greedy: bool = False | |
| class ChatResponse(BaseModel): | |
| answer: str | |
| seconds: float | |
| def health(): | |
| return {"ok": True, "model": "emma-5"} | |
| def chat(req: ChatRequest): | |
| t0 = time.time() | |
| with _lock: | |
| answer = _emma.generate( | |
| req.question, | |
| max_new=req.max_new, | |
| temperature=req.temperature, | |
| top_p=req.top_p, | |
| greedy=req.greedy, | |
| ) | |
| return ChatResponse(answer=answer, seconds=round(time.time() - t0, 2)) | |