Skip to content

feat(ambient): pluggable ASR backend — SenseVoice for multilingual capture - #41

Merged
WingedGuardian merged 1 commit into
mainfrom
feat/ambient-sense-voice-backend
Jul 20, 2026
Merged

feat(ambient): pluggable ASR backend — SenseVoice for multilingual capture#41
WingedGuardian merged 1 commit into
mainfrom
feat/ambient-sense-voice-backend

Conversation

@WingedGuardian

@WingedGuardian WingedGuardian commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Summary

The passive ambient STT is a Nov-2023 English-only LibriSpeech Zipformer. On a multilingual install, non-English speech gets force-decoded into English gibberish. This adds a pluggable ASR backend selector so multilingual installs can run SenseVoice-Small (zh/en/ja/ko/yue, auto per-utterance language detect + ITN punctuation) while English-only installs keep the incumbent Zipformer unchanged.

Change

  • config.py: AMBIENT_ASR_BACKEND (zipformer default | sense_voice) + AMBIENT_SENSE_VOICE_DIR. Default is zipformer, so existing installs are byte-for-byte unaffected; a multilingual edge flips the backend via a systemd drop-in env line. Only the selected backend's model files need to be present.
  • pipeline.py: factory dispatch in AmbientEngine.__init__ (from_sense_voice vs from_transducer). The arena-off ORT provider string reaches both backends; the transducer-only decode knobs (decoding_method/max_active_paths) stay scoped to the transducer branch. meta.asr now carries the active backend label.
  • _asr_feats: omits ys_log_probs when empty (SenseVoice/CTC emit none, so it's no longer stored as noise on every row) and strips SenseVoice's <|…|> token delimiters so meta.lang is a clean value ("zh", not "<|zh|>").

Why SenseVoice-2024-07-17 (int8)

Benchmarked on the edge VM (4 threads, int8) against the incumbent and the newer 2025-09-09 release, scored by token error rate vs ground-truth references:

zipformer (incumbent) SenseVoice 2024-07-17 SenseVoice 2025-09-09
English ER 12.5% 0.0% 10.7%
Non-English ER ~100% (gibberish) 0.0% 0.0%
Code-switch ER ~91% 5.2% 1.7%
RTF (4 threads) 0.083 0.120 0.108

English must not regress: the 2024 model matches/beats Zipformer on English and fixes the multilingual failure, so it wins. The 2025 WenetSpeech-Yue release traded English accuracy for expanded dialect coverage and is rejected.

Testing

  • 6 new unit tests (config parsing; backend dispatch per factory; provider plumbing for SenseVoice; _asr_feats omits empty log-probs + strips <|…|> delimiters). Full ambient suite green (154 tests); ruff clean.
  • Real-model E2E on the edge (isolated scratch path; live deployment untouched): the sense_voice branch builds a real recognizer and decodes English / non-English / code-switch correctly with clean lang tags, and the default zipformer path still constructs. The delimiter bug above was caught by this E2E, not by the stubbed unit tests.

Deploy

No-op for existing installs (default unchanged). A multilingual edge sets AMBIENT_ASR_BACKEND=sense_voice, installs the model into ~/models/sense-voice (README documents the download), and restarts the ambient bridge.

🤖 Generated with Claude Code

…pture

The passive ambient STT was a Nov-2023 English-only LibriSpeech Zipformer;
in a multilingual (Mandarin+English) household non-English speech decodes to
gibberish. Add an AMBIENT_ASR_BACKEND selector: "zipformer" (default,
unchanged) or "sense_voice" (SenseVoice-Small — zh/en/ja/ko/yue, auto
per-utterance language detect + ITN punctuation).

- config: asr_backend + sense_voice_dir (default zipformer → existing installs
  unaffected; edge flips via systemd drop-in).
- pipeline: factory dispatch (from_sense_voice vs from_transducer); the
  arena-off provider string reaches both. transducer-only decode knobs stay
  scoped to that branch. meta.asr carries the backend label.
- _asr_feats: omit ys_log_probs when empty (CTC/SenseVoice emit none) and
  strip SenseVoice's <|...|> delimiters so meta.lang is clean ("zh" not
  "<|zh|>") — caught by real-model E2E, not the stubbed unit tests.

Bench (edge, int8, 4 threads): SenseVoice-2024-07-17 English 0% ER (vs
zipformer 12.5%), Mandarin/code-switch usable (vs gibberish), RTF 0.12.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@WingedGuardian
WingedGuardian merged commit 4f09777 into main Jul 20, 2026
4 checks passed
@WingedGuardian
WingedGuardian deleted the feat/ambient-sense-voice-backend branch July 20, 2026 01:36

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1f507c3261

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +80 to +81
if ys:
feats["ys_log_probs"] = ys

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bump the shadow version for changed ASR metadata

When an utterance has no log-probs (SenseVoice/CTC, and any empty transducer vector), this now omits ys_log_probs while _SHADOW_VER stays p0.1. That mixes old p0.1 rows where the key was always present with new p0.1 rows where it can be absent, so offline quality analysis filtered by shadow_ver can misinterpret missing values as corrupt data or bias experiments; bump the shadow version with this schema change.

Useful? React with 👍 / 👎.

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