diff --git a/desktop/src-tauri/src/huddle/stt.rs b/desktop/src-tauri/src/huddle/stt.rs index 3721c2fa6e0..cc03b0bf3b5 100644 --- a/desktop/src-tauri/src/huddle/stt.rs +++ b/desktop/src-tauri/src/huddle/stt.rs @@ -719,10 +719,15 @@ fn decode_speech(recognizer: &sherpa_onnx::OfflineRecognizer, speech_buf: &[f32] } fn send_transcript(text: String, text_tx: &tokio_mpsc::Sender) { - if !text.is_empty() { - if let Err(e) = text_tx.blocking_send(text) { - eprintln!("buzz-desktop: STT text channel closed: {e}"); - } + // Parakeet hallucinates bare punctuation (".", "?") on non-speech audio + // that clears the VAD's minimum-voiced-frames gate. A transcript with no + // alphanumeric characters carries no speech content — drop it here so it + // never reaches the posting task and becomes a channel message. + if !text.chars().any(char::is_alphanumeric) { + return; + } + if let Err(e) = text_tx.blocking_send(text) { + eprintln!("buzz-desktop: STT text channel closed: {e}"); } } diff --git a/desktop/src-tauri/src/huddle/stt_tests.rs b/desktop/src-tauri/src/huddle/stt_tests.rs index 3fba9adafa6..c6a16e1a246 100644 --- a/desktop/src-tauri/src/huddle/stt_tests.rs +++ b/desktop/src-tauri/src/huddle/stt_tests.rs @@ -253,3 +253,26 @@ fn held_push_to_talk_never_silence_flushes() { // Manually open mic with the shortcut up: normal VAD behavior. assert!(vad_flush_allowed(true, true, false)); } + +#[test] +fn punctuation_only_transcripts_are_dropped() { + // Capacity exceeds total sends so the pre-fix code fails on the assertion + // below (hallucinations received) instead of deadlocking blocking_send. + let (text_tx, mut text_rx) = tokio::sync::mpsc::channel::(16); + + // Parakeet hallucinations on non-speech audio: no alphanumeric content. + for hallucinated in [".", "?", "!", ",", "...", ". .", "—"] { + super::send_transcript(hallucinated.to_string(), &text_tx); + } + // Real speech survives, including single-word and non-ASCII replies. + for speech in ["yes", "ok.", "привет", "第九"] { + super::send_transcript(speech.to_string(), &text_tx); + } + drop(text_tx); + + let mut received = Vec::new(); + while let Ok(text) = text_rx.try_recv() { + received.push(text); + } + assert_eq!(received, ["yes", "ok.", "привет", "第九"]); +}