Spaces:
Running on Zero
Running on Zero
File size: 2,723 Bytes
f1ef7e2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | import os
import threading
from contextlib import asynccontextmanager
from pathlib import Path
from dotenv import load_dotenv
# Load the repo-root .env for local dev BEFORE importing anything that reads
# env at import time (database.py builds the engine from DATABASE_URL). On the
# Space there is no .env; the real environment (Space secrets) is used and
# always takes precedence -- load_dotenv never overrides existing vars.
load_dotenv(Path(__file__).resolve().parents[2] / ".env")
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.database import init_db
from app.routers import calls, sentiment, artifacts
from app import worker
def _boot_model_prewarm():
"""Pre-download the acoustic weights so the first job doesn't pay for it.
Best-effort, off-thread; only when acoustic is enabled."""
if os.environ.get("ENABLE_ACOUSTIC", "0").strip().lower() in ("", "0", "false", "no"):
return
try:
from app.pipeline_bridge import install
install()
from acoustic import resolve_model_dir
resolve_model_dir()
print("[startup] acoustic weights ready")
except Exception as e:
print(f"[startup] acoustic pre-warm skipped: {e}")
@asynccontextmanager
async def lifespan(app):
try:
init_db()
except Exception as e:
print(f"[startup] init_db skipped (db unreachable?): {e}")
if os.environ.get("START_WORKER", "1").strip().lower() not in ("", "0", "false", "no"):
worker.start()
threading.Thread(target=_boot_model_prewarm, name="model-prewarm",
daemon=True).start()
yield
app = FastAPI(
title="AI/ML Capstone API",
description="Fresh-call ingest, pipeline worker, and artifact serving",
version="1.0.0",
lifespan=lifespan,
)
# CORS: the Vercel frontend calls this API cross-origin. FRONTEND_ORIGIN pins
# the allowed origin(s) in production (comma-separated); "*" for local dev.
# Credentials are disabled under wildcard (the spec forbids "*" + credentials);
# the frontend uses plain fetch with no cookies, so this is fine either way.
_origins = os.environ.get("FRONTEND_ORIGIN", "*")
_wild = _origins.strip() == "*"
app.add_middleware(
CORSMiddleware,
allow_origins=["*"] if _wild else [o.strip() for o in _origins.split(",")],
allow_credentials=not _wild,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(calls.router)
app.include_router(sentiment.router)
app.include_router(artifacts.router)
@app.get("/")
def read_root():
return {"status": "online", "service": "backend-orchestration"}
@app.get("/health")
def health_check():
return {"status": "ok", "service": "capstone_api"}
|