ASR Speech Recognition

(This page is the recognizer's debugging and benchmark entry point; its place in the chain and turn detection live in VAD and Turn Detection.)

Architecture

The ASR trait lives in service/src/component/asr.rs, while the engine implementation and singleton live in api/src/component/asr/, backed by sherpa-onnx.

pub trait Asr: Send + Sync {
    fn create_stream(&self) -> Box<dyn AsrStream>;
    async fn transcribe(&self, sample_rate: u32, samples: &[f32])
        -> Result<RecognizerResult, AppError>;
}

RecognizerResult { text: String, prob: f32 } is the shared output of both paths. The streaming path is AsrStream: accept_waveform / decode / is_endpoint / get_partial / finish / reset.

AsrManager::init(config).await;          // initialise at startup
let model = AsrManager::global().default(); // Arc<dyn Asr>, shareable across turns
let mut stream = model.create_stream();
stream.accept_waveform(&samples);
stream.decode();

Initialisation happens in the server startup path; there are two consumers: AsrNode (the real-time voice chain) and the Matrix client (whole-utterance transcription).

Models

ModelConfigLocationNotes
XAsrx_asrdata/asr/model/x_asr/default/zh/en/ja/ko/yue; usable both streaming and whole-utterance
Voidvoidno filesReturns fixed text "void" and confidence 1.0 for testing the chain

Models come from https://github.com/k2-fsa/sherpa-onnx and are managed by downloader manifests:

moon run server:downloader -- asr
moon run server:downloader -- asr x_asr

Benchmarks and diagnostics

api/tests/common/asr.rs turns each test's timing into one diagnostic line:

CER=XX.X%(X) WER=XX.X% Acc=XX.X% Perf=XX.X% Total=XX.X% | RTF=X.XXX ASR=X.Xs Audio=X.XXs <verdict>
MetricComputation
CERCharacter-level Levenshtein distance / reference length
WERWord-level Levenshtein distance / reference word count
RTFASR time / audio duration
Accuracy1 − CER
ScoreAccuracy × 70 + (1 − min(RTF, 1)) × 30
GradeCERRTF
A< 3%< 0.05
B3–6%0.05–0.10
C6–10%0.10–0.20
D10–20%0.20–0.50
F≥ 20%≥ 0.50

The old page's "TTS loopback CER 0%, RTF 0.110" were measurements from before the refactor and were not re-measured this round; the commands below are how to find out what they are now.

Tests

api/tests/asr_test.rs has five, all calling AsrManager::create_model directly:

TestModelAssertion
test_asr_model_voidVoidtext == "void", prob == 1.0
test_asr_model_void_streamVoidStreaming surface works: no endpoint, no partial, finish returns the same
test_asr_model_x_asr_transcribeXAsrWhole-utterance transcription non-empty, prob > 0
test_asr_model_x_asr_streamingXAsrFeed every 100 ms; at least one partial and a non-empty finish
test_asr_transcribe_short_audioXAsrDiagnostic comparison of 1 s truncation, +300 ms silence, and the full file

The XAsr tests need data/asr/model/x_asr/default/ downloaded (moon run server:downloader -- asr).

# Void: no model files needed
cargo test --package api --test asr_test -- test_asr_model_void

# XAsr: model required
cargo test --package api --test asr_test -- test_asr_model_x_asr_transcribe --nocapture
cargo test --package api --test asr_test -- test_asr_model_x_asr_streaming --nocapture

# release is faster
cargo test --package api --test asr_test --release -- test_asr_model_x_asr_streaming --nocapture

Chain-level regression lives in api/tests/session/: test_chat_flow_listen_manual, test_chat_flow_listen_auto, test_chat_flow_listen_realtime, test_chat_flow_voice_text_voice_round_trips, test_chat_flow_resample_48k_multi_round.

Adding an ASR model

  1. Manifest: apps/server/src/downloader/manifests/asr/<model>.json
  2. Implementation: api/src/component/asr/model/<model>/mod.rs, implementing Asr (and AsrStream if streaming)
  3. Register the module: add pub mod <model> in api/src/component/asr/model/mod.rs
  4. Enum: add a variant to AsrModel in api/src/config/mod.rs
  5. Factory: add a match arm in create_model() in api/src/component/asr/mod.rs
  6. Tests: add whole-utterance and streaming cases in api/tests/asr_test.rs, at least asserting non-empty output and a partial

Key files

FilePurpose
apps/server/service/src/component/asr.rsAsr / AsrStream traits, RecognizerResult
apps/server/api/src/component/asr/mod.rsManager singleton and model factory
apps/server/api/src/component/asr/model/x_asr/XAsr implementation
apps/server/api/src/component/asr/model/void/Void stand-in
apps/server/api/src/config/asr.rsAsrConfig
apps/server/api/src/config/mod.rsAsrModel enum
apps/server/src/downloader/manifests/asr/Download manifests
apps/server/api/tests/asr_test.rsThe five tests above
apps/server/api/tests/common/asr.rsDiagnostics and scoring