Spike 2 delivers the full voice pipeline: queue → worker pool → sr-voice
child process (stdio JSONL) → cache → disk. Three rounds of quality testing
with Paula, Mellanie, and Gestalt produced iterative prompt improvements.
Prompt engine (prompt_builder.rs):
- Example-based epistemic marker integration (not keyword lists)
- Length-aware Angry tell variant (preserves facts on long content)
- Double-prompt technique: REMEMBER block repeats constraints near OUTPUT:
- Imperative injection framing (composition engine controls frequency)
- Anti-invention constraint ("do not add information not in the input")
- Universal RULES cleaned: worldbuilding moved to culture personas
Worker pool (worker.rs):
- Output post-processor strips after first newline (prevents prompt leakage)
- Watchdog poll loop (1s ticks) replaces blocking sleep for cancel
- Child health check before writing (try_wait)
Test infrastructure:
- voice_pipeline.rs: end-to-end test, auto-detects real sr-voice or mock
- voice_quality_batch.rs: 39 edge-case prompts for quality review
- mock-stdio.sh: Python JSONL mock for CI (no model needed)
- Makefile targets: test-voice-mock, test-voice-real
Quality results (Gemma 2B Q4_K_M, CPU ~13 t/s):
- Epistemic markers: naturally integrated (round 1 comma-lists fixed)
- Tell differentiation: 3/5 working (Nervous, Guarded, Angry)
- Information preservation: ~90% (up from ~70%)
- Prompt leakage: eliminated
- Open: Friendly/RoutineDeviation tells inert (#651), Factual bypass (#650)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
56 lines
1.6 KiB
Bash
Executable File
56 lines
1.6 KiB
Bash
Executable File
#!/usr/bin/env python3
|
|
"""Mock sr-voice stdio mode for pipeline testing (D-138).
|
|
|
|
Reads JSONL from stdin, writes JSONL to stdout. Simulates inference
|
|
by uppercasing the base text portion of the prompt as the "voiced" output.
|
|
|
|
Usage: echo '{"prompt":"Re-voice this.","seed":42}' | ./mock-stdio.sh serve --stdio
|
|
"""
|
|
import json
|
|
import sys
|
|
|
|
print("mock-sr-voice: stdio mode (no model loaded)", file=sys.stderr, flush=True)
|
|
|
|
# Use readline() loop — Python's `for line in sys.stdin` has an internal
|
|
# read-ahead buffer that blocks on piped stdin until 8KB is available.
|
|
while True:
|
|
line = sys.stdin.readline()
|
|
if not line:
|
|
break
|
|
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
|
|
try:
|
|
req = json.loads(line)
|
|
except json.JSONDecodeError as e:
|
|
print(json.dumps({"error": f"invalid JSON: {e}"}), flush=True)
|
|
continue
|
|
|
|
prompt = req.get("prompt", "")
|
|
if not prompt:
|
|
print(json.dumps({"error": "missing prompt field"}), flush=True)
|
|
continue
|
|
|
|
# Extract the INPUT line from the prompt (last INPUT: before OUTPUT:)
|
|
input_text = ""
|
|
for pline in prompt.split("\n"):
|
|
if pline.startswith("INPUT: "):
|
|
input_text = pline[7:]
|
|
if not input_text:
|
|
input_text = prompt[:80]
|
|
|
|
voiced = f"[VOICED] {input_text.upper()}"
|
|
|
|
result = {
|
|
"text": voiced,
|
|
"tokens_generated": len(voiced.split()),
|
|
"generation_time_ms": 50,
|
|
"tokens_per_sec": 240.0,
|
|
"prefill_time_ms": 10,
|
|
}
|
|
print(json.dumps(result), flush=True)
|
|
|
|
print("mock-sr-voice: stdin closed, exiting", file=sys.stderr, flush=True)
|