roverdevkit / webapp /backend /config.py
jjreif's picture
Deploy roverdevkit @ 2676a67
b3d14e3
Raw
History Blame Contribute Delete
3.44 kB
"""Backend configuration: artifact paths, CORS origins, dataset version.
All paths default to in-repo runtime artifacts so the backend works
out of the box from a fresh clone. Each value can be overridden via environment variable so the same
container image can be repointed at a remote object store / mounted
volume in deployment without code changes.
Environment variables
---------------------
``ROVERDEVKIT_QUANTILE_BUNDLES``
Path to ``quantile_bundles.joblib`` (calibrated quantile XGB heads).
Default: ``models/surrogate_v9/quantile_bundles.joblib`` — the v9
recalibration on lhs_v9.parquet after scientific payload was
promoted from a per-rover ``chassis_mass_kg`` convention to two
explicit mission-requirement inputs (``payload_mass_kg`` /
``payload_power_w``), each an LHS feature uniform on [0, 30]. With
the v9 calibration, the Mission-Inputs panel's payload sliders stay
on the surrogate path with calibrated 90 % PIs across the full
override range.
``ROVERDEVKIT_TUNED_PARAMS``
Path to ``tuned_best_params.json`` (tuned XGB hyperparameters).
Currently informational only; reserved for later steps that may
need to refit. Default:
``reports/tuned_v9/tuned_best_params.json`` (50-trial Optuna
sweep on the v9 dataset for the four primary regressors and the
``stalled`` classifier; re-run for v9 because the input
dimensionality changed from 25 to 27 columns).
``ROVERDEVKIT_DATASET_VERSION``
Dataset version label echoed in ``/version``. Default ``v9``.
``ROVERDEVKIT_CORS_ORIGINS``
Comma-separated allow-list. Defaults to the Vite dev server.
"""
from __future__ import annotations
import os
from dataclasses import dataclass
from pathlib import Path
REPO_ROOT: Path = Path(__file__).resolve().parents[2]
@dataclass(frozen=True)
class Settings:
"""Resolved backend configuration. Built once via :func:`get_settings`."""
quantile_bundles_path: Path
tuned_params_path: Path
dataset_version: str
cors_origins: tuple[str, ...]
repo_root: Path
@property
def artifacts_present(self) -> bool:
"""True iff the surrogate artifact exists on disk."""
return self.quantile_bundles_path.exists()
def _env_path(name: str, default: Path) -> Path:
raw = os.environ.get(name)
return Path(raw).expanduser().resolve() if raw else default
def _env_csv(name: str, default: tuple[str, ...]) -> tuple[str, ...]:
raw = os.environ.get(name)
if not raw:
return default
return tuple(x.strip() for x in raw.split(",") if x.strip())
def get_settings() -> Settings:
"""Build a :class:`Settings` object from process env + repo defaults.
Called once on app startup and re-resolved on each call so tests
can monkey-patch via ``os.environ`` between invocations.
"""
return Settings(
quantile_bundles_path=_env_path(
"ROVERDEVKIT_QUANTILE_BUNDLES",
REPO_ROOT / "models" / "surrogate_v9" / "quantile_bundles.joblib",
),
tuned_params_path=_env_path(
"ROVERDEVKIT_TUNED_PARAMS",
REPO_ROOT / "reports" / "tuned_v9" / "tuned_best_params.json",
),
dataset_version=os.environ.get("ROVERDEVKIT_DATASET_VERSION", "v9"),
cors_origins=_env_csv(
"ROVERDEVKIT_CORS_ORIGINS",
("http://localhost:5173", "http://127.0.0.1:5173"),
),
repo_root=REPO_ROOT,
)