emma4ever-live / server.py
aterreno's picture
Emma-5 live server: Dockerfile + FastAPI (/health, /chat)
869e2bc verified
Raw
History Blame Contribute Delete
2.24 kB
#!/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
@app.get("/health")
def health():
return {"ok": True, "model": "emma-5"}
@app.post("/chat", response_model=ChatResponse, dependencies=[Depends(require_key)])
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))