Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions desktop/src-tauri/src/huddle/stt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -719,10 +719,15 @@ fn decode_speech(recognizer: &sherpa_onnx::OfflineRecognizer, speech_buf: &[f32]
}

fn send_transcript(text: String, text_tx: &tokio_mpsc::Sender<String>) {
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}");
}
}

Expand Down
23 changes: 23 additions & 0 deletions desktop/src-tauri/src/huddle/stt_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<String>(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.", "привет", "第九"]);
}
Loading