"""Runtime configuration for the Incident Command Center environment. All tunables are read from environment variables so the server is 12-factor compatible and can be reconfigured per deployment without rebuilding the image. Every field has a sensible default so local development "just works". """ from __future__ import annotations import os from dataclasses import dataclass ENV_VERSION = "3.0.0" ENV_NAME = "incident_command_center_env" def _int_env(name: str, default: int) -> int: raw = os.getenv(name) if raw is None or raw == "": return default try: return int(raw) except ValueError: return default def _bool_env(name: str, default: bool) -> bool: raw = os.getenv(name) if raw is None: return default return raw.strip().lower() in {"1", "true", "yes", "on"} @dataclass(frozen=True) class EnvConfig: name: str = ENV_NAME version: str = ENV_VERSION default_seed: int = 20260425 easy_budget: int = 28 medium_budget: int = 54 hard_budget: int = 84 easy_sla_minutes: int = 120 medium_sla_minutes: int = 210 hard_sla_minutes: int = 330 sla_tick_minutes: int = 5 max_reward_trace_len: int = 400 structured_logging: bool = True log_level: str = "INFO" @classmethod def from_env(cls) -> "EnvConfig": return cls( name=os.getenv("ENV_NAME", ENV_NAME), version=os.getenv("ENV_VERSION", ENV_VERSION), default_seed=_int_env("ENV_SEED", 20260425), easy_budget=_int_env("ENV_EASY_BUDGET", 28), medium_budget=_int_env("ENV_MEDIUM_BUDGET", 54), hard_budget=_int_env("ENV_HARD_BUDGET", 84), easy_sla_minutes=_int_env("ENV_EASY_SLA", 120), medium_sla_minutes=_int_env("ENV_MEDIUM_SLA", 210), hard_sla_minutes=_int_env("ENV_HARD_SLA", 330), sla_tick_minutes=_int_env("ENV_SLA_TICK", 5), max_reward_trace_len=_int_env("ENV_MAX_REWARD_TRACE_LEN", 400), structured_logging=_bool_env("ENV_STRUCTURED_LOGGING", True), log_level=os.getenv("ENV_LOG_LEVEL", "INFO"), ) def budget_for(self, task_name: str) -> int: return { "easy": self.easy_budget, "medium": self.medium_budget, "hard": self.hard_budget, }.get(task_name, self.medium_budget) def sla_for(self, task_name: str) -> int: return { "easy": self.easy_sla_minutes, "medium": self.medium_sla_minutes, "hard": self.hard_sla_minutes, }.get(task_name, self.medium_sla_minutes)