Skip to content

fix(deepgram): keep streaming audio while an utterance is in progress - #2243

Open
CampbellMBXJ wants to merge 1 commit into
livekit:mainfrom
CampbellMBXJ:fix/deepgram-audio-gating-during-utterance
Open

fix(deepgram): keep streaming audio while an utterance is in progress#2243
CampbellMBXJ wants to merge 1 commit into
livekit:mainfrom
CampbellMBXJ:fix/deepgram-audio-gating-during-utterance

Conversation

@CampbellMBXJ

Copy link
Copy Markdown

Description

Fixes #2242.

AudioEnergyFilter stops forwarding frames once its cooldown elapses with no audio above the RMS threshold. Deepgram only fires endpointing on silence it actually receives, so when the gate closed after Deepgram had reported speech but before it had endpointed the utterance, speech_final never arrived. The pending final was only flushed when the speaker started talking again, and until then audio_recognition had no transcript to commit the turn with - runEOUDetection skips on an empty audioTranscript, so the agent sat silent until userAwayTimeout fired 15s later.

Changes Made

  • plugins/deepgram/src/stt.ts - bypass the energy gate while an utterance is in flight, using the existing #speaking flag (set on SpeechStarted or the first non-empty result, cleared on speech_final/UtteranceEnd).
  • plugins/deepgram/src/stt.ts - reset #speaking at the start of #runWS. This is a pre-existing leak (updateOptions() resolves #resetWS and forces a reconnect, e.g. via deferred keyterm updates), but the bypass above raises its cost from a stale flag to the gate being bypassed for the rest of the session. plugins/sarvam/src/stt.ts already resets its equivalent flag per session; this matches it.
  • plugins/deepgram/src/stt.test.ts - three regression tests on the existing local-websocket harness.

Pre-Review Checklist

  • Build passes: build, lint and the deepgram test file pass.
  • AI-generated code reviewed: Removed unnecessary comments and ensured code quality
  • Changes explained: All changes are properly documented and justified above
  • Scope appropriate: All changes relate to the PR title, or explanations provided for why they're included
  • Video demo: no user-facing surface to demo. The failure is a timing race on the STT socket; the regression tests below capture it deterministically instead.

Testing

  • Automated tests added/updated (if applicable)
  • All tests pass
  • Make sure both restaurant_agent.ts and realtime_agent.ts work properly (for major changes)

Three tests added to plugins/deepgram/src/stt.test.ts, using the existing WebSocketServer harness.

  1. stops sending low-energy audio once the cooldown elapses - pins the existing gating behaviour, so the fix can't silently disable the filter.
  2. keeps sending low-energy audio while an utterance is in progress - the fix itself.
  3. does not carry an unfinished utterance into a reconnected websocket - the #runWS reset. Asserts _speaking is false after a reconnect triggered mid-utterance by updateOptions.

Additional Notes

Test 3 asserts on _speaking, which is @internal. It's the most direct deterministic signal for the reconnect case. I first tried asserting on forwarded-frame counts, but reconnection drops frames on its own, so the signal was 4 vs 7 frames rather than 4 vs 15 — too timing-dependent to assert on without flake. Happy to change the approach if you'd rather not have a test reach for that getter.

Billing. More audio is forwarded between gate-close and endpoint - normally a few hundred milliseconds per utterance. The duration collector only counts sent frames, so reported recognition usage reflects it. The #runWS reset is what bounds this: without it a stuck #speaking would forward audio indefinitely.

I marked the changeset patch. It is a bugfix, but it does change audio-forwarding behaviour for every Deepgram streaming user, so say the word if you'd rather it were a minor.

@CampbellMBXJ
CampbellMBXJ requested a review from a team as a code owner August 7, 2026 19:39
@changeset-bot

changeset-bot Bot commented Aug 7, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3f04b5b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 39 packages
Name Type
@livekit/agents-plugin-deepgram Patch
@livekit/agents Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-anthropic Patch
@livekit/agents-plugin-assemblyai Patch
@livekit/agents-plugin-azure Patch
@livekit/agents-plugin-baseten Patch
@livekit/agents-plugin-bey Patch
@livekit/agents-plugin-cartesia Patch
@livekit/agents-plugin-cerebras Patch
@livekit/agents-plugin-did Patch
@livekit/agents-plugin-elevenlabs Patch
@livekit/agents-plugin-fishaudio Patch
@livekit/agents-plugin-google Patch
@livekit/agents-plugin-hedra Patch
@livekit/agents-plugin-hume Patch
@livekit/agents-plugin-inworld Patch
@livekit/agents-plugin-krisp Patch
@livekit/agents-plugin-lemonslice Patch
@livekit/agents-plugin-liveavatar Patch
@livekit/agents-plugin-livekit Patch
@livekit/agents-plugin-minimax Patch
@livekit/agents-plugin-mistral Patch
@livekit/agents-plugin-mistralai Patch
@livekit/agents-plugin-neuphonic Patch
@livekit/agents-plugin-openai Patch
@livekit/agents-plugin-perplexity Patch
@livekit/agents-plugin-phonic Patch
@livekit/agents-plugin-protoface Patch
@livekit/agents-plugin-resemble Patch
@livekit/agents-plugin-rime Patch
@livekit/agents-plugin-runway Patch
@livekit/agents-plugin-sarvam Patch
@livekit/agents-plugin-silero Patch
@livekit/agents-plugin-soniox Patch
@livekit/agents-plugin-tavus Patch
@livekit/agents-plugin-trugen Patch
@livekit/agents-plugin-xai Patch
@livekit/agents-plugins-test Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@CLAassistant

CLAassistant commented Aug 7, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

AudioEnergyFilter stops forwarding frames once its cooldown elapses with no
audio above the RMS threshold. Deepgram only fires endpointing on silence it
actually receives, so when the gate closed after Deepgram had reported speech
but before it had endpointed the utterance, speech_final never arrived. The
pending final was only flushed when the speaker started talking again, and
until then audio_recognition had no transcript to commit the turn with.

Bypass the gate between start of speech and endpoint, and clear that state when
a websocket session starts so a reconnect cannot inherit an unfinished
utterance from the previous socket. The sarvam plugin already resets its
equivalent flag per session.

pushFrame is still evaluated on every frame so the filter's cooldown stays
accurate once the utterance ends.
@CampbellMBXJ
CampbellMBXJ force-pushed the fix/deepgram-audio-gating-during-utterance branch from 47f6a72 to 3f04b5b Compare August 7, 2026 19:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deepgram plugin stops sending audio before Deepgram can endpoint, so speech_final never arrives

2 participants