Gov_Workflow_RL / app /config.py
Siddharaj Shirke
deploy: clean code-only snapshot for HF Space
df97e68
# ── 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()