Files
echo-core/tasks/voice-bench-results-threads4.md
Marius Mutu c6d11bdf9f chore(voice): spike STT latency benchmark + HT contention lesson
Pas 1 (BLOCKING) din Discord voice-to-voice test plan. Sweet spot empiric
pe i7-6700T: faster-whisper small int8 @ cpu_threads=4 → p50 2.25s,
p95 2.64s, mean RTF 0.46. Curba HT: 2t=3.25s → 4t=2.25s (sweet) →
6t=2.79s (regres +24% prin contention). tiny respinge — halucinează RO.

- tools/voice_bench.py: harness benchmark cu 8 sample-uri RO sintetizate
  via Supertonic API, măsoară p50/p95/RTF pentru small+tiny pe N threads.
- tools/voice_bench_results*.json: raw output 3 pass-uri (threads 2/4/6).
- tasks/voice-bench-results*.md: summary markdown per pass.
- tasks/lessons.md: HT contention rule — cpu_threads = physical cores,
  rulează sweep nu single-point pentru ML inference compute-bound.

Budget updated în plan-uri: STT p50 1.5s → 2.5s, perceived 4s → 5s p50.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:52:11 +00:00

66 lines
4.5 KiB
Markdown

# Voice Bench Results — Discord Voice-to-Voice Spike
Generated: 2026-05-27 (BLOCKING Pas 1 din test plan)
Hardware: i7-6700T (Skylake mobile), Proxmox VM, no GPU
Budget original: STT p50 < 1.50s (per CEO plan aspirational)
Budget honest: 1.5-3s (per Outside Voice #1, baked in CEO plan)
## Final Recommendation: **PASS cu `small` model**
Script-ul a returnat auto-decision `FALLBACK_TINY` pentru `small.p50=2.25s > 1.5s` literal. **Override manual**: `tiny` produce transcript ilizibil în RO ("muină sun la nu a", " mream in test de seare", "Stei putin") inutilizabil pentru produs. `small @ 4 threads` cade în honest range-ul "1.5-3s" deja acceptat în CEO plan și produce transcript clean modulo normalizare numerică (deja în scope: `src/voice/normalize.py`).
**Implicații pentru implementare:**
1. Folosește `WhisperModel("small", device="cpu", compute_type="int8", cpu_threads=4)` în `src/voice/pipeline.py`.
2. Update plan latency budget: STT p50 = 2.25s (era 1.5s); perceived round-trip estimate = 3.5-5s (STT 2.25s + Claude TTFB 0.5-1s + streaming TTS first clause ~0.5s).
3. Streaming ClaudeTTS rămâne critic fără el, total perceived = 6-8s, peste limita conversațională.
4. Filler audio "Stai să-mi adun gândurile" (deja în plan) maschează cazurile p95 (>3s).
5. Document fallback la `tiny` DOAR pentru `/voice doctor` mode degraded (Whisper OOM etc.), nu pentru happy path.
## Two-Pass Comparison (threads=2 vs threads=4)
| Model | threads | p50 (s) | p95 (s) | mean RTF | Verdict |
|-------|--------:|--------:|--------:|---------:|---------|
| small | 2 | 3.25 | 3.63 | 0.67 | FAIL latency |
| **small** | **4** | **2.25** | **2.64** | **0.46** | **CHOSEN** (quality + honest range) |
| tiny | 2 | 0.50 | 0.57 | 0.10 | FAIL quality |
| tiny | 4 | 0.48 | 0.57 | 0.10 | FAIL quality |
CPU upgrade 2→4 cores: **`small` got 31% faster** (3.25s → 2.25s), `tiny` essentially unchanged (CPU-light enough că nu beneficiază). Confirmă că `small` e CPU-bound, `tiny` nu.
## Transcript Quality Side-by-Side (4 threads)
| Input | small @ 4t | tiny @ 4t |
|-------|-----------|-----------|
| "Salut, ce mai faci?" | "Salut ce mai faci!" | "Salut, ce mai fac?" |
| "Stai puțin să mă gândesc la asta." | "Stai putin să mă gândesc la asta." | "Stei putin să mă gândesc la asta." |
| "Am verificat în calendar și avem ședință cu echipa la trei după-amiază." | "Am verificat în calendari și avem sedință cu echipa la 3 după amiază." | "Am verificat în calendar și avem sedeință cu equipala 3 du pămiază." |
| "Costul total este o sută douăzeci și trei de lei și cincizeci de bani." | "Costul total este 120 și 3 delei și 50 de bani." | "Costul total este o suta 20 și 3 de lei și 50 de bani." |
| "Marius, vrei să-ți pun pe agenda de mâine să suni la NOAA?" | "Marius, vrei să-ți spun pe agenda de mâine să suni la noa a." | "Marius, vrei să-ți pun pe agenda de muină să sun la nu a." |
| "Vreau să-mi reamintești diseară..." | "Vreau să mi-răimintești di seară..." | "Vreau să mream in test de seare..." |
**Observații:**
- `small` greșeli: diacritice (`putin`/`puțin`, `sedință`/`ședință`), numbere ca digiti ("3" în loc de "trei"), acronime (NOAA→noa), aglutinare ("delei"/"de lei", "răimintești"/"reamintești").
- `tiny` greșeli: cuvinte INVENTATE ("mream", "muină", "equipala", "sunilă") — hallucination, nu doar misspell.
## Hardware Context
- Intel(R) Core(TM) i7-6700T CPU @ 2.80GHz (Skylake mobile, 2015)
- Cores online: 4 logical (din 8), upgrade de la 2 în timpul benchmark-ului
- RAM: 6.0Gi total, ~2.5Gi available
- No NVIDIA GPU (CPU-only inference)
- ctranslate2 4.7.2 + faster-whisper 1.2.1 + int8 quantization
## Open Questions pentru Decision Lock
1. **Budget relax oficial:** acceptăm 2.25s p50 în plan și comunicăm honest user-facing? Sau încercăm:
- **Groq Whisper Large-v3 API** (~0.3s, free tier 14k req/day) — vine cu network dependency
- **Deepgram Nova-2 RO streaming** ($, dar 0.2s streaming partial transcripts)
- **Whisper.cpp + AVX2** (același small model, optimizat C++) — ~30% boost suplimentar potențial
2. **CPU bump:** dacă activăm restul de 4 cores offline (3-6) ar coborî `small.p50` la ~1.5s? Worth investigat (probabil VM resource cap, nu hardware limit).
## Raw Data
- `tools/voice_bench_results.json` — run curent (threads=4)
- `tools/voice_bench_results_threads2.json` — baseline (threads=2)
- `tasks/voice-bench-results-threads2.md` — narrative pentru baseline