-
Notifications
You must be signed in to change notification settings - Fork 3
plat 120
github-actions[bot] edited this page Sep 20, 2026
·
1 revision
| Coordination | Value |
|---|---|
| Assigned agent | unassigned |
| Ticket state |
implemented_pending_live_reverify — shipped, not yet confirmed against real speech |
| Last synchronized | 2026-08-16 |
- Priority: P2
-
Owner:
agent_go/pkg/voicestt,cmd/server/voice_stt_routes.go,frontend/src/voice/*,agentprofiles.RuntimeCapabilities.Voice
A shared streaming speech-to-text capability, generic across products rather than hardcoded to Video Studio:
-
Engine:
pkg/voicesttwraps sherpa-onnx-go + a Nemotron-3.5 streaming ASR model (int8 ONNX, cache-aware FastConformer). One process-wideEngine(lazily loaded, pre-warmed at route registration so the first real click isn't blocked on model load), cheap per-connectionStreams. -
Transport:
GET /api/voice/stream?profile_id=...&token=...— a WebSocket accepting raw PCM16 mono 16kHz binary frames, streaming back partial/final JSON transcripts. Query-param JWT auth (browsers can't set custom headers on a WS upgrade), same pattern as SSE and terminal live-attach. -
Capability gating:
agentprofiles.RuntimeCapabilities.Voice, the same shape asBrowser/Secrets. Video Studio opts in viavoice: preferredin itsproduct.yaml; the frontend loads capability state generically (utils/agentProfileCapabilities.ts) rather than special-casing any product. -
Frontend:
frontend/src/voice/pcmCapture.ts(AudioWorklet-based raw PCM capture at 16kHz, ported from SparkQuill'snativePcm.ts),useVoiceDictation.ts+MicButton.tsx(ported to match SparkQuill's UX exactly per explicit user spec — level-reactive ring, floating "Listening" banner with live partial text, push-to-talk toggle that flushes the final transcript into the composer on stop).
-
websocket.Upgrader{}'s defaultCheckOriginrejected the frontend's cross-port dev requests (403). -
statusCapturingResponseWriterembedshttp.ResponseWriteras an interface field, which only promotes methods that interface declares —Hijack()was missing, breaking any websocket route wrapped by the response-logging middleware wheneverAPI_REQUEST_LOGis on (default). Latent bug affecting every websocket route, not just this one; fixed with an explicit forwardingHijack(). - Lazy
sync.Oncemodel load caused a 60+ second silent hang on the first real click; fixed with a pre-warm goroutine at route registration.
-
pkg/voicestt/engine_live_test.go(opt-in, real model): transcript text grows across chunks against a real.wav— passed. - A raw Go WebSocket client streamed a real
.wavthrough the live server end-to-end and got correct partial/final transcripts. - A synthetic 440Hz tone through the actual browser capture code produced real, varying PCM (decoded RMS ≈ 0.71) — proves the JS capture → encode → transport path independent of any physical microphone.
- The rebuilt banner/level-ring UI was visually confirmed rendering correctly (pulse dot, "Listening" label, ring scaling with live level).
-
tsc --noEmitandnpm run buildclean.
-
No confirmed end-to-end pass with real human speech. Every real-mic
attempt on the reporter's machine has returned
rms=0.0000— tracked separately as PLAT-122, which blocks this ticket's own closure. - Live text appearing in the banner as real speech streams, and the final transcript landing in the chat composer on stop, are both implemented per the SparkQuill port but unconfirmed against real audio for the same reason.
- A user speaks into a real microphone; partial text appears live in the banner as they talk; stopping delivers the final transcript into the composer input. Currently blocked on PLAT-122.
Auto-synced from docs/ on main. Edit there, not here.