"""Central configuration for WitnessBox. One place for model ids, backend selection, audio rates, and game tuning so the rest of the codebase never hardcodes a magic number. Everything here is plain data; importing this module has no side effects and pulls in no heavy deps. """ from __future__ import annotations import os # --------------------------------------------------------------------------- # # Backend selection # --------------------------------------------------------------------------- # # "mock" -> pure-Python backends, no GPU/Modal needed; the whole loop runs # locally (this is the default so the app boots anywhere). # "modal" -> real models served from a deployed Modal app (see modal_app.py). BACKEND = os.environ.get("WITNESSBOX_BACKEND", "mock").strip().lower() # Name the Modal app is deployed under (`modal deploy modal_app.py`). MODAL_APP_NAME = os.environ.get("WITNESSBOX_MODAL_APP", "witnessbox") # If a Modal lookup fails (secrets unset, app not deployed), fall back to mock # rather than crashing the Space. Mirrors PRD risk #10 ("Space boots even if # Modal secrets unset"). Set to "0" to hard-fail instead (useful in CI). FALLBACK_TO_MOCK = os.environ.get("WITNESSBOX_FALLBACK_TO_MOCK", "1") != "0" # --------------------------------------------------------------------------- # # Models (all < 32B; combined ~12B) — ids verified in PRD.md / HACKATHON-CONTEXT.md # --------------------------------------------------------------------------- # WITNESS_LLM = "openbmb/MiniCPM4.1-8B" # 8.2B — witness's brain (clean text model; we run text-only, so the omni model's deps weren't worth it) WITNESS_VOICE = "openbmb/VoxCPM2" # 2B — the witness's voice; style = game state PLAYER_ASR = "nvidia/nemotron-speech-streaming-en-0.6b" # 0.6B — player transcription PLAYER_ASR_FALLBACK = "openai/whisper-small" # local fallback if Nemotron install fights us # --------------------------------------------------------------------------- # # Audio # --------------------------------------------------------------------------- # ASR_SR = 16_000 # ASR models expect 16 kHz mono VOICE_SR = 48_000 # VoxCPM2 emits 48 kHz # --------------------------------------------------------------------------- # # Game tuning # --------------------------------------------------------------------------- # CATCHES_TO_WIN = 3 # surface this many contradictions -> the witness breaks SOFT_TURN_BUDGET = 12 # narrative pacing target; not a hard cap # Player credibility = the lose resource. The judge excuses the witness at 0. CREDIBILITY_START = 100 CREDIBILITY_ON_CATCH = +12 # landing a contradiction restores standing with the bench CREDIBILITY_ON_WHIFF = -14 # a question that goes nowhere costs you # Witness composure = the continuous backing for the discrete witness tiers and # drives voice-style escalation. Starts high; each catch knocks it down a band. COMPOSURE_START = 100 COMPOSURE_ON_CATCH = -30 COMPOSURE_ON_PRESSURE = -4 # confident delivery with no catch still rattles him a little # Contradiction detector: minimum match score (0..1) to count as a catch. CATCH_THRESHOLD = 0.62 # Hard ceiling so a runaway session still terminates. MAX_TURNS = 24