Tadiwa-M
Deploy: auto-derive dedup radius (drop hardcoded 3m bypass)
58aefd4
Raw
History Blame Contribute Delete
3.62 kB
"""Prometheus API β€” FastAPI service backing the React dashboard.
Run from the project root:
uvicorn api.main:app --reload --port 8000
Design notes:
* Detection reuses the existing pipeline (src.detection.detector via
api.services.detection_service) β€” one source of truth for inference.
* Heavy deps (torch/ultralytics) are imported lazily inside the detection
service, so /api/health, /api/metrics and the catalog endpoints respond
instantly even on a machine without a model loaded.
"""
from __future__ import annotations
import os
# Anaconda + torch + OpenCV each ship their own OpenMP runtime (libiomp5md.dll);
# loading them together (as the video job does) collides and crashes the whole
# process natively β€” uncatchable, the server just dies mid-job. Allowing the
# duplicate runtime is the standard pragmatic fix for inference. MUST be set
# before torch/cv2/numpy are imported anywhere, so it lives at the very top.
os.environ.setdefault("KMP_DUPLICATE_LIB_OK", "TRUE")
import sys # noqa: E402
from pathlib import Path # noqa: E402
from fastapi import FastAPI # noqa: E402
from fastapi.middleware.cors import CORSMiddleware # noqa: E402
PROJECT_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(PROJECT_ROOT))
from api.routes import catalog, detection, population, survey, telemetry # noqa: E402
from api.schemas import SystemInfo # noqa: E402
from api.services import detection_service # noqa: E402
app = FastAPI(
title="Prometheus API",
description="Aerial wildlife intelligence for the Malilangwe Trust.",
version="0.1.0",
)
# CORS origins are configurable via the ALLOWED_ORIGINS env var (comma-separated)
# so production can lock the API to just the dashboard's domain WITHOUT a code
# change β€” set it at deploy time. Defaults to "*" for local dev.
# e.g. ALLOWED_ORIGINS=https://prometheus.pages.dev,https://app.example.com
_origins_env = os.environ.get("ALLOWED_ORIGINS", "*").strip()
_allow_origins = ["*"] if _origins_env in ("", "*") else [
o.strip() for o in _origins_env.split(",") if o.strip()
]
app.add_middleware(
CORSMiddleware,
allow_origins=_allow_origins,
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(detection.router)
app.include_router(catalog.router)
app.include_router(population.router)
app.include_router(survey.router)
app.include_router(telemetry.router)
@app.get("/api/health", response_model=SystemInfo, tags=["system"])
def health() -> SystemInfo:
cuda = False
device = "cpu"
try:
import torch
cuda = bool(torch.cuda.is_available())
device = "cuda" if cuda else "cpu"
except Exception: # noqa: BLE001 β€” torch absent is a valid (CPU-only) state
pass
return SystemInfo(
status="ok",
version="0.1.0",
device=device,
torch_cuda=cuda,
models_available=len(detection_service.available_models()),
project="Prometheus Β· Malilangwe Trust",
)
# Single-app deploy: if a built dashboard is present in web/, serve it from this
# same server so UI + API + weights live at one origin (one private host). The
# dashboard build uses a relative API base, so its /api/* calls are same-origin
# and carry the host's auth. Mounted LAST so the /api/* routes above win.
# Locally without web/, this is skipped and the server is API-only.
_WEB = PROJECT_ROOT / "web"
if (_WEB / "index.html").exists():
from fastapi.staticfiles import StaticFiles # noqa: E402
app.mount("/", StaticFiles(directory=str(_WEB), html=True), name="web")