File size: 4,419 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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
"""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  # noqa: E402

from witnessbox.backends import get_backends  # noqa: E402
from witnessbox.engine import WitnessBoxEngine  # noqa: E402
from witnessbox import stance as stance_mod  # noqa: E402

SCRIPT = [
    "So, Mr. Reid — comfortable up there?",                       # filler
    "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)),   # dense syllables, few pauses, flat pitch
        ("halting / unsure", (3.2, 1.4, 0.72, 0.20)),  # sparse syllables, long gaps, wavering pitch
    ):
        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()