"""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, )