| """Drive a full examination end-to-end in the terminal (mock backend). |
| |
| python3 scripts/demo_playthrough.py |
| |
| Doubles as the dry-run harness referenced in the demo-video plan: it prints each |
| turn's perceived stance, the witness's line, and the live contradiction verdict, |
| then asserts the win fires with a cached voice-crack take. |
| """ |
| import os |
| import sys |
|
|
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
|
|
| import numpy as np |
|
|
| from witnessbox.backends import get_backends |
| from witnessbox.engine import WitnessBoxEngine |
| from witnessbox import stance as stance_mod |
|
|
| SCRIPT = [ |
| "So, Mr. Reid — comfortable up there?", |
| "The wire to Meridian cleared March 6th — before the board approved it on the 14th.", |
| "Anything over $5 million needs the CFO's sign-off, and your credentials are on the authorization log.", |
| "You were cc'd on Meridian's incorporation filing two years ago — Dana Voss, your colleague.", |
| ] |
|
|
|
|
| def bar(pct, n=20): |
| f = int(round(pct / 100 * n)) |
| return "█" * f + "·" * (n - f) |
|
|
|
|
| def _speechlike(dur_s=2.4, sr=16000, syl_rate=5.0, pause_frac=0.15, wobble=0.0, seed=0): |
| """A crude but *speech-like* clip: a voiced carrier (f0 + harmonics, optional |
| pitch wobble) gated by a train of syllable bumps. Unlike a pure sine, its |
| pause ratio, onset rate and pitch steadiness move the way real delivery does — |
| so the stance read comes out in the right direction. |
| high syl_rate + low pause_frac + flat pitch -> CONFIDENT |
| low syl_rate + high pause_frac + wobble -> HESITANT |
| """ |
| rng = np.random.RandomState(seed) |
| n = int(dur_s * sr) |
| t = np.arange(n) / sr |
| f0 = 135.0 * (1.0 + wobble * np.sin(2 * np.pi * 0.8 * t + rng.rand())) |
| phase = 2 * np.pi * np.cumsum(f0) / sr |
| carrier = np.sin(phase) + 0.5 * np.sin(2 * phase) + 0.33 * np.sin(3 * phase) |
| env = np.zeros(n) |
| period = max(1, int(sr / syl_rate)) |
| syl_len = max(1, int(period * (1.0 - pause_frac))) |
| for start in range(0, n, period): |
| seg = min(syl_len, n - start) |
| if seg <= 1: |
| break |
| env[start:start + seg] = 0.5 - 0.5 * np.cos(2 * np.pi * np.arange(seg) / seg) |
| return (0.4 * carrier * env).astype(np.float32) |
|
|
|
|
| def main(): |
| eng = WitnessBoxEngine(get_backends()) |
| intro = eng.start() |
| print(f"\n BACKEND: {intro['backend']} — {intro['backend_note']}") |
| print(f"\n ⚖️ THE COURT: {intro['narration']}") |
| print(f" 🎙️ REID: {intro['opening_text']}\n") |
| print(" " + "─" * 64) |
|
|
| last = None |
| for line in SCRIPT: |
| last = eng.take_turn(typed_text=line) |
| s, st = last.status, last.stance |
| print(f"\n ⚖️ YOU [{st.tier.lower()}]: {last.examiner_text}") |
| print(f" 🎙️ REID ({s['witness_tier']}): {last.witness_text}") |
| if last.evidence: |
| for ln in last.evidence.splitlines(): |
| print(f" │ {ln}") |
| audio = "🔊" if last.witness_audio is not None else "—" |
| print(f" catches {s['catches']}/{s['catches_to_win']} " |
| f"composure [{bar(s['composure'])}] standing [{bar(s['credibility'])}] {audio}") |
| if last.events.won: |
| print(f"\n 💥 HE BREAKS — voice-crack take: " |
| f"{len(last.witness_audio)} samples @ {last.audio_sr} Hz, " |
| f"epilogue {'present' if last.epilogue_audio is not None else 'missing'}") |
|
|
| print("\n " + "─" * 64) |
| print(" Stance scoring on speech-like clips (no real mic needed):") |
| for name, (dur, syl_rate, pause_frac, wobble) in ( |
| ("fluent / steady", (2.4, 5.0, 0.12, 0.0)), |
| ("halting / unsure", (3.2, 1.4, 0.72, 0.20)), |
| ): |
| clip = _speechlike(dur_s=dur, syl_rate=syl_rate, pause_frac=pause_frac, wobble=wobble) |
| r = stance_mod.analyze(clip, 16000) |
| print(f" {name:18s} -> {r.tier:9s} conf={r.confidence:5.1f} " |
| f"(pause={r.features.get('pause_ratio')}, rate={r.features.get('rate_hz')}, " |
| f"pitch_std={r.features.get('pitch_std_semitones')})") |
|
|
| assert last.events.won, "expected a win after three catches" |
| print("\n ✅ End-to-end win path verified.\n") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|