FastAPI service in infra/media-worker implementing every MediaInferenceProvider operation behind HTTP API v1, plus capabilities. Bearer auth, MEDIA_ROOT realpath enforcement, strict request schemas, cancellation on disconnect, GPU/CPU gates with 503 overflow, input size/duration limits, lazy SHA256-pinned model downloads, fail-closed redaction with atomic output, offline pytest suite with fake backends, CPU/CUDA/ROCm Dockerfile and a license review of every model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138YqDj3ttfayRwuMKKrqVj
81 lines
3.0 KiB
Python
81 lines
3.0 KiB
Python
"""Sidecar settings, read once from the environment."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from dataclasses import dataclass, field
|
|
|
|
MIN_TOKEN_LENGTH = 32
|
|
GIB = 1024**3
|
|
|
|
|
|
class ConfigError(Exception):
|
|
pass
|
|
|
|
|
|
def _int(env: dict[str, str], name: str, default: int, minimum: int = 0) -> int:
|
|
raw = env.get(name, "").strip()
|
|
if not raw:
|
|
return default
|
|
try:
|
|
value = int(raw)
|
|
except ValueError as error:
|
|
raise ConfigError(f"{name} must be an integer") from error
|
|
if value < minimum:
|
|
raise ConfigError(f"{name} must be >= {minimum}")
|
|
return value
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Settings:
|
|
token: str
|
|
media_root: str
|
|
models_dir: str
|
|
host: str = "127.0.0.1"
|
|
port: int = 8790
|
|
accelerator: str = "cpu"
|
|
gpu_concurrency: int = 1
|
|
cpu_concurrency: int = 2
|
|
queue_size: int = 8
|
|
max_input_bytes: int = 20 * GIB
|
|
max_seconds: int = 4 * 3600
|
|
allow_model_download: bool = True
|
|
# Backends that are not licensed for commercial use stay off unless named here.
|
|
optional_backends: frozenset[str] = field(default_factory=frozenset)
|
|
insecure_dev: bool = False
|
|
|
|
@staticmethod
|
|
def from_env(env: dict[str, str] | None = None) -> Settings:
|
|
env = dict(os.environ if env is None else env)
|
|
insecure = env.get("MEDIA_WORKER_INSECURE_DEV", "") == "1"
|
|
token = env.get("MEDIA_WORKER_TOKEN", "")
|
|
if len(token) < MIN_TOKEN_LENGTH and not insecure:
|
|
raise ConfigError(f"MEDIA_WORKER_TOKEN must be at least {MIN_TOKEN_LENGTH} characters")
|
|
media_root = env.get("MEDIA_ROOT", "").strip()
|
|
if not media_root or not os.path.isabs(media_root):
|
|
raise ConfigError("MEDIA_ROOT must be an absolute path")
|
|
if not os.path.isdir(media_root):
|
|
raise ConfigError("MEDIA_ROOT does not exist")
|
|
accelerator = env.get("MEDIA_ACCELERATOR", "cpu").strip() or "cpu"
|
|
if accelerator not in ("cpu", "cuda", "rocm"):
|
|
raise ConfigError("MEDIA_ACCELERATOR must be cpu, cuda or rocm")
|
|
optional = frozenset(
|
|
item.strip() for item in env.get("MEDIA_OPTIONAL_BACKENDS", "").split(",") if item.strip()
|
|
)
|
|
return Settings(
|
|
token=token,
|
|
media_root=media_root,
|
|
models_dir=env.get("MEDIA_MODELS_DIR", "/models").strip() or "/models",
|
|
host=env.get("MEDIA_WORKER_HOST", "127.0.0.1").strip() or "127.0.0.1",
|
|
port=_int(env, "MEDIA_WORKER_PORT", 8790, 1),
|
|
accelerator=accelerator,
|
|
gpu_concurrency=_int(env, "MEDIA_GPU_CONCURRENCY", 1, 1),
|
|
cpu_concurrency=_int(env, "MEDIA_CPU_CONCURRENCY", 2, 1),
|
|
queue_size=_int(env, "MEDIA_QUEUE_SIZE", 8, 0),
|
|
max_input_bytes=_int(env, "MEDIA_MAX_INPUT_BYTES", 20 * GIB, 1),
|
|
max_seconds=_int(env, "MEDIA_MAX_SECONDS", 4 * 3600, 1),
|
|
allow_model_download=env.get("MEDIA_MODEL_DOWNLOAD", "1") != "0",
|
|
optional_backends=optional,
|
|
insecure_dev=insecure,
|
|
)
|