From 780e458f115737fd301e459d89c5a1eb7f172ab8 Mon Sep 17 00:00:00 2001 From: localai-org-maint-bot Date: Tue, 8 Sep 2026 23:12:57 +0200 Subject: [PATCH] fix(realtime): skip responses for empty transcripts (#11940) Realtime turns could invoke the LLM and TTS even when speech transcription returned only whitespace. This let ambient noise produce unsolicited assistant output and polluted conversation history with an empty user turn. Require non-blank transcript text before automatic response generation while preserving the completed transcription event. Assisted-by: Codex:gpt-5 golangci-lint Co-authored-by: Ettore Di Giacinto --- core/http/endpoints/openai/realtime.go | 3 ++- .../endpoints/openai/realtime_semantic_vad_test.go | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/core/http/endpoints/openai/realtime.go b/core/http/endpoints/openai/realtime.go index 32b08b3fd..8b17a8238 100644 --- a/core/http/endpoints/openai/realtime.go +++ b/core/http/endpoints/openai/realtime.go @@ -12,6 +12,7 @@ import ( "math" "os" "strconv" + "strings" "sync" "time" @@ -1923,7 +1924,7 @@ func commitUtteranceWithTranscript(ctx context.Context, utt []byte, live *liveUt // Generate an LLM response only when there is a transcript to feed it. A // sound-detection-only session (no transcription) has no LLM stage, so it // stops here after emitting the sound-detection event. - if session.InputAudioTranscription != nil && !session.TranscriptionOnly { + if session.InputAudioTranscription != nil && !session.TranscriptionOnly && strings.TrimSpace(transcript) != "" { generateResponse(ctx, session, utt, transcript, speaker, conv, t) } } diff --git a/core/http/endpoints/openai/realtime_semantic_vad_test.go b/core/http/endpoints/openai/realtime_semantic_vad_test.go index c3f5d7ef8..c36e13563 100644 --- a/core/http/endpoints/openai/realtime_semantic_vad_test.go +++ b/core/http/endpoints/openai/realtime_semantic_vad_test.go @@ -355,6 +355,19 @@ var _ = Describe("commitUtteranceWithTranscript", func() { Expect(tr.countEvents(types.ServerEventTypeConversationItemInputAudioTranscriptionCompleted)).To(Equal(1)) }) + + It("does not generate a response for a blank transcript", func() { + session, model := itSession(nil) + model.transcribeFinal = &schema.TranscriptionResult{Text: " \t\n"} + tr := &fakeTransport{} + conv := &Conversation{} + + commitUtterance(context.Background(), []byte{1, 2}, session, conv, tr) + + Expect(tr.countEvents(types.ServerEventTypeConversationItemInputAudioTranscriptionCompleted)).To(Equal(1)) + Expect(conv.Items).To(BeEmpty()) + Expect(tr.countEvents(types.ServerEventTypeResponseCreated)).To(Equal(0)) + }) }) // transcribeUtterance is the retranscribe gate's offline decode of the