Skip to content

vein: speech-to-text core — streaming dictation with hotwords - #1655

Open
Evanfeenstra wants to merge 1 commit into
mainfrom
vein-stt
Open

vein: speech-to-text core — streaming dictation with hotwords#1655
Evanfeenstra wants to merge 1 commit into
mainfrom
vein-stt

Conversation

@Evanfeenstra

Copy link
Copy Markdown
Collaborator

What

Streaming speech-to-text inside vein over sherpa-onnx, per the rewritten §4 of vein/plans/local-desktop-and-stt.md. Dictation over a WebSocket is the product surface; workflows sit around the recognizer as the learning loop ("dream cycles", §4.8). No STT workflow step in v1.

  • src/audio/stt.ts — service: model download + sha256 verify + tar xjf, cached recognizers, streams (PCM16 in → partial/final events out), two-recognizer mode (fast greedy NeMo model for partials, hotword-capable Zipformer for finals + endpointing), batch transcribe. sherpa-onnx-node is an optionalDependency, lazy-imported; without it /audio/* answers 501 and everything else is untouched.
  • src/audio/hotwords.ts — contextual biasing. Two verified gotchas baked in: sherpa needs modelingUnit: "bpe" (unset → cjkchar → silent no-op), and a bpe.vocab the released models don't ship, synthesized from tokens.txt. Named lists under <dataDir>/audio/hotwords/ are the dream cycle's promotion artifact.
  • src/audio/sessions.ts — finals + user corrections per session (training data for the dream cycle).
  • src/audio/ws.tsGET /audio/stream via raw ws on the Node server (@hono/node-ws doesn't support node-server 2.x). Bearer or ?key=.
  • src/audio/routes.ts/audio/models (+ SSE download), /audio/transcribe, /audio/hotwords/:name, /audio/sessions/:id (+ corrections).
  • createVein({ stt }), ctx.services.stt, VEIN_MODEL_DIR (alias of VEIN_MODEL_CACHE), VEIN_STT_MODEL, VEIN_STT_PARTIAL_MODEL.

Measured (M-series Mac, see doc §4.1)

model partials every hotwords notes
zipformer-en-kroko (57 MB) 1.3 s yes cased + punctuated; finals model
nemo-fast-conformer-en-80ms (103 MB) ~150 ms no (greedy-only in sherpa) partials model
nemo-fast-conformer-en-480ms (106 MB) ~570 ms no
nemotron-speech-en-80ms (463 MB) ~150 ms no accuracy ceiling, RTF ≈ 0.55

End to end over the socket at real-time pace: partials 50–100 ms behind the audio, final ~300 ms after end. Hotwords turned "on this FHIC swarm" into "on the Sphinx swarm" and "hive" into "Hive".

Tests

  • 21 new unit tests (scripted fake engine, ws protocol, auth), no addon needed; full suite 686/686.
  • npm run test:stt (opt-in VEIN_TEST_STT=1) downloads kroko and streams its bundled clip: 3/3 locally.

… hotwords

Streaming STT is the product surface; workflows learn around it (dream
cycles), no STT step. src/audio/: service (model download+verify, cached
recognizers, two-recognizer streams: fast greedy NeMo partials + hotword-
capable Zipformer finals, batch transcribe), catalog, hotwords compiler
(synthesized bpe.vocab — sherpa needs modelingUnit bpe or the list is a
silent no-op), sessions + corrections, /audio/stream WebSocket (raw ws on
the Node server; Bearer or ?key=), /audio/* routes. sherpa-onnx-node is an
optionalDependency, lazy-loaded; routes answer 501 without it.

Plan doc rewritten from the bake-off: measured latency/accuracy for kroko,
NeMo fast-conformer 80/480 ms, Nemotron; NeMo online transducers are
greedy-only in sherpa (no hotwords); sample-rate change aborts the process.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant