modelbeast/server/settings.py
MODELBEAST 605b1ae347 Phase 1 + framework: settings/secrets, queue lanes, job mgmt, inbox, 8 new operators
Framework:
- server/settings.py: key/value settings + secrets, env-injected into operator
  subprocesses, secret values masked in API and redacted from job logs
- runner: gpu/cpu/net concurrency lanes, job cancel/retry/delete, multi-input,
  graceful 'not installed' error when a tool venv is missing
- db: settings table, asset_ids column (migrated), MODELBEAST_DATA test override
- main: settings + job-action endpoints, inbox watch folder auto-ingest
- store: operators can tag output asset kind (splat, colmap_dataset)

Operators (11 total):
- fal_trellis/trellis2/hunyuan3d/rodin via shared _lib/fal_common.py (verified
  params + endpoint ids; recursive result-URL extractor handles per-endpoint keys)
- sf3d, trellis_mac: local MPS image-to-3D, installed with Metal kernels built,
  gated on owner HuggingFace auth
- colmap_poses (COLMAP 4.x + GLOMAP global mapper), brush_train (native Metal 3DGS)
- Scan pipeline validated end-to-end through the UI: frames -> colmap (48/48
  registered, 0.6px) -> brush -> splat.ply -> in-app SplatViewer

Frontend:
- Settings modal, operator gating (lock + disabled run when requires_env unmet),
  job cancel/retry/delete, Compare grid (multi-select side-by-side viewers),
  SplatViewer (gaussian-splats-3d, Ply format forced for extensionless URLs)

Tooling: scripts/install_{colmap,brush,sf3d,trellis_mac}.sh; vendor/ + venvs/
gitignored; tests/smoke.sh (12 checks passing); BENCHMARKS.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 21:42:27 +10:00

77 lines
2.3 KiB
Python

"""Settings + secrets: a key/value store surfaced to the UI and injected as env
vars into operator subprocesses. Secret values are redacted from job logs."""
# setting key -> environment variable name handed to operator subprocesses
ENV_MAP = {
"fal_key": "FAL_KEY",
"tripo_key": "TRIPO_KEY",
"meshy_key": "MESHY_KEY",
"replicate_token": "REPLICATE_API_TOKEN",
"hf_token": "HF_TOKEN",
"models_dir": "MODELBEAST_MODELS_DIR",
"archive_host": "MODELBEAST_ARCHIVE_HOST",
"archive_path": "MODELBEAST_ARCHIVE_PATH",
}
# keys whose values are secret: rendered as password fields, redacted from logs
SECRET_KEYS = {"fal_key", "tripo_key", "meshy_key", "replicate_token", "hf_token"}
DEFAULTS = {
"archive_host": "m3ultra@100.69.21.128",
"archive_path": "~/modelbeast-archive",
}
def get_all(con) -> dict:
rows = con.execute("SELECT key, value FROM settings").fetchall()
out = dict(DEFAULTS)
for r in rows:
out[r["key"]] = r["value"]
return out
def set_many(con, updates: dict) -> None:
for key, value in updates.items():
con.execute(
"INSERT INTO settings (key, value) VALUES (?, ?) "
"ON CONFLICT(key) DO UPDATE SET value = excluded.value",
(key, "" if value is None else str(value)),
)
con.commit()
def public_view(settings: dict) -> dict:
"""Mask secret values so the UI can show 'set / unset' without leaking them."""
out = {}
for key, value in settings.items():
if key in SECRET_KEYS:
out[key] = "••••••••" if value else ""
else:
out[key] = value
return out
def env_from_settings(settings: dict) -> dict:
env = {}
for key, env_name in ENV_MAP.items():
value = settings.get(key)
if value:
env[env_name] = value
# HF libraries read several token names
if settings.get("hf_token"):
env["HUGGING_FACE_HUB_TOKEN"] = settings["hf_token"]
return env
def secret_values(settings: dict) -> list[str]:
return [settings[k] for k in SECRET_KEYS if settings.get(k)]
def redact(text: str, secrets: list[str]) -> str:
if not text:
return text
for secret in secrets:
if secret and len(secret) >= 6:
text = text.replace(secret, "••••REDACTED••••")
return text