From 52551e6666246ef42fb96edbb7e349c2f529e506 Mon Sep 17 00:00:00 2001 From: Jake Date: Sun, 19 Apr 2026 17:19:45 +0100 Subject: [PATCH] fix(parakeet): request word-granularity segments (was per-subword 'T Est Ing') MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit transcribe-rs 0.3.10's ParakeetModel::transcribe_raw ignores its options argument and calls self.infer(samples, &TimestampGranularity::default()) where default is TimestampGranularity::Token — per-subword segments. That surfaces in Kon as output like 'T Est Ing One , Two , Three . W Ow , This Is T Ri Ble .' because DictationPage joins segment texts with ' '. Introduce a thin ParakeetWordGranularity wrapper that implements SpeechModel and overrides transcribe_raw to call the concrete ParakeetModel::transcribe_with() with ParakeetParams { timestamp_granularity: Some(Word) }. Pre-existing bug unrelated to Phase 2 work — surfaced during Group 1 dogfooding because Parakeet was being tested for the first time. Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/transcription/src/local_engine.rs | 38 +++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/crates/transcription/src/local_engine.rs b/crates/transcription/src/local_engine.rs index feef65c..66612f1 100644 --- a/crates/transcription/src/local_engine.rs +++ b/crates/transcription/src/local_engine.rs @@ -113,6 +113,42 @@ impl LocalEngine { } } +/// Thin wrapper over `ParakeetModel` that overrides `transcribe_raw` to +/// request word-granularity segments. `transcribe-rs` 0.3's trait impl for +/// `ParakeetModel::transcribe_raw` ignores `TranscribeOptions` and uses +/// `TimestampGranularity::Token` (per-subword) — which surfaces in Kon as +/// "T Est Ing . One , Two , Three" output. The concrete-type method +/// `ParakeetModel::transcribe_with` accepts `ParakeetParams` with an +/// explicit granularity; this wrapper exposes that to the trait object. +struct ParakeetWordGranularity(transcribe_rs::onnx::parakeet::ParakeetModel); + +impl transcribe_rs::SpeechModel for ParakeetWordGranularity { + fn capabilities(&self) -> transcribe_rs::ModelCapabilities { + self.0.capabilities() + } + + fn default_leading_silence_ms(&self) -> u32 { + self.0.default_leading_silence_ms() + } + + fn default_trailing_silence_ms(&self) -> u32 { + self.0.default_trailing_silence_ms() + } + + fn transcribe_raw( + &mut self, + samples: &[f32], + options: &TranscribeOptions, + ) -> std::result::Result { + use transcribe_rs::onnx::parakeet::{ParakeetParams, TimestampGranularity}; + let params = ParakeetParams { + language: options.language.clone(), + timestamp_granularity: Some(TimestampGranularity::Word), + }; + self.0.transcribe_with(samples, ¶ms) + } +} + /// Load a Parakeet model from a directory path. pub fn load_parakeet( model_dir: &Path, @@ -127,7 +163,7 @@ pub fn load_parakeet( "Failed to load Parakeet: {e}" )) })?; - Ok(Box::new(model)) + Ok(Box::new(ParakeetWordGranularity(model))) } /// Load a Whisper model from a GGML file path.