Spaces:
Running on Zero
Running on Zero
| 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}") | |
| 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) | |
| def read_root(): | |
| return {"status": "online", "service": "backend-orchestration"} | |
| def health_check(): | |
| return {"status": "ok", "service": "capstone_api"} | |