Spaces:
Running
Running
| # ββ Path bootstrap βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| from __future__ import annotations | |
| from pathlib import Path | |
| # Load .env file if it exists β must happen before Pydantic Settings reads env vars | |
| try: | |
| from dotenv import load_dotenv | |
| except (ImportError, AttributeError): | |
| # Keep runtime functional even when python-dotenv is not installed | |
| # or when a conflicting `dotenv` package is present. | |
| def load_dotenv(*args, **kwargs): # type: ignore[no-redef] | |
| return False | |
| _ENV_FILE = Path(__file__).resolve().parent.parent / ".env" | |
| load_dotenv(dotenv_path=_ENV_FILE, override=False) | |
| # override=False means real environment variables always win over .env values | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| from pydantic import Field | |
| from pydantic_settings import BaseSettings, SettingsConfigDict | |
| class ServerSettings(BaseSettings): | |
| """ | |
| HTTP-server configuration. | |
| Read from environment variables prefixed SERVER_. | |
| Example: SERVER_PORT=8080 SERVER_LOG_LEVEL=debug | |
| Intentionally isolated from EnvSettings β changing server bind | |
| options never affects simulation behaviour, and vice-versa. | |
| Both classes are instantiated once at import and treated as | |
| read-only singletons for the lifetime of the process. | |
| """ | |
| host: str = Field("0.0.0.0", description="Bind host") | |
| port: int = Field(7860, description="Bind port β HF Spaces default is 7860") | |
| log_level: str = Field( | |
| "info", description="Uvicorn log level: debug | info | warning | error" | |
| ) | |
| cors_origins: list[str] = Field( | |
| default=["*"], | |
| description="Allowed CORS origins. '*' is required for HF Spaces embedding.", | |
| ) | |
| # NOTE: Keep at 1 when using the in-memory session store. | |
| # Multiple workers do NOT share process memory. | |
| # Use Redis + a shared store before increasing workers in production. | |
| workers: int = Field( | |
| 1, description="Uvicorn worker count β keep at 1 for in-memory sessions" | |
| ) | |
| model_config = SettingsConfigDict(env_prefix="SERVER_", extra="ignore") | |
| class EnvSettings(BaseSettings): | |
| """ | |
| Simulation-environment defaults. | |
| Read from environment variables prefixed ENV_. | |
| Example: ENV_DEFAULT_TASK_ID=mixed_urgency_medium ENV_MAX_SESSIONS=50 | |
| Controls the environment kernel only. No effect on network | |
| binding, logging, or CORS β those belong to ServerSettings. | |
| """ | |
| default_task_id: str = Field( | |
| "district_backlog_easy", | |
| description="Task used when POST /reset is called without an explicit task_id", | |
| ) | |
| default_seed: int = Field( | |
| 11, | |
| description="Seed used when POST /reset is called without an explicit seed", | |
| ) | |
| max_steps_per_episode: int = Field( | |
| 500, | |
| description="Hard cap on step() calls per session before episode is truncated", | |
| ) | |
| max_sessions: int = Field( | |
| 100, | |
| description="Maximum concurrent in-memory sessions. Oldest is evicted when exceeded.", | |
| ) | |
| model_config = SettingsConfigDict(env_prefix="ENV_", extra="ignore") | |
| # ββ Singletons ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Loaded exactly once at import time. Never mutated at runtime. | |
| # Tests may monkeypatch individual fields after import if needed. | |
| server_settings = ServerSettings() | |
| env_settings = EnvSettings() | |