File size: 3,272 Bytes
c519923
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
"""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