Skip to content

Support adaptive interruption for realtime models#6488

Merged
longcw merged 7 commits into
mainfrom
longc/realtime-barge-in
Jul 23, 2026
Merged

Support adaptive interruption for realtime models#6488
longcw merged 7 commits into
mainfrom
longc/realtime-barge-in

Conversation

@longcw

@longcw longcw commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Enables client-side adaptive interruption (barge-in) for OpenAI-compatible realtime models when the model's own server-side turn detection is disabled — so a backchannel ("uh-huh") while the agent is speaking doesn't trigger a spurious reply, without requiring a separate STT.

The gate. With no STT there's no transcript to gatekeep, so the audio recognition layer tracks a turn-scoped signal — whether the turn's speech overlapped agent speech and was classified a backchannel — and surfaces it on the end-of-turn info. When set, the user turn is dropped and the buffered input audio is cleared, so no reply is generated.

Difference from the STT gate. The STT path is timestamp-windowed: it drops the backchannel portion of a turn and re-emits the rest. Realtime is all-or-nothing — clear_audio() wipes the whole input buffer and the model buffers the turn as one unit, so there's no per-timestamp split. It's correct for the dominant cases (pure backchannel, real interruption) and drops only on a confirmed backchannel; a slow verdict after the agent has stopped falls back to committing rather than silently dropping a possible real barge-in.

STT + realtime. If you want the finer-grained STT gate, pass an STT alongside the realtime model — the realtime drop path is skipped when an STT is present and transcript-based backchanneling takes over.

@longcw longcw changed the title Support adaptive interruption (barge-in) for realtime models Support adaptive interruption for realtime models Jul 20, 2026
@longcw
longcw marked this pull request as ready for review July 21, 2026 03:06
@longcw
longcw requested a review from a team as a code owner July 21, 2026 03:06
devin-ai-integration[bot]

This comment was marked as resolved.

# only honor the verdict while this turn's overlap is unresolved so a late verdict
# can't leak into the next turn; an interruption supersedes a prior backchannel
if self._overlap_in_current_turn:
self._turn_backchannel_over_agent = not ev.is_interruption

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The overlap speech also ends when agent finishes speaking first, so we trigger a synthesized is_interruption=False, but the user speech might not be done, and we might carry over the wrong flag (til the end of turn).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

added a flag agent_ended to distinguish two types of is_interruption=False

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

if there are one backchannel ev + one agent ends first ev during the agent speech, self._turn_backchannel_over_agent would be stale, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed.

Comment thread livekit-agents/livekit/agents/voice/agent_activity.py
@longcw
longcw force-pushed the longc/realtime-barge-in branch from a557e26 to 2926544 Compare July 22, 2026 01:49
Comment thread livekit-agents/livekit/agents/voice/audio_recognition.py
longcw added 7 commits July 23, 2026 14:01
A realtime model transcribes internally and commits turns manually, so barge-in can gatekeep the overlapping user turn by withholding the commit instead of holding and flushing STT transcripts. Relax _resolve_interruption_detection so a realtime model with server-side turn detection disabled no longer requires a separate STT; the STT pipeline path is unchanged.
When a realtime model runs client-side barge-in with server-side turn detection disabled, an overlapping utterance the adaptive detector classifies as a backchannel must not create a user turn. Track the backchannel verdict per turn and skip the end-of-turn commit; clear the model's audio buffer so the backchannel audio doesn't leak into the next turn.
Move the backchannel verdict from an AgentActivity flag reset on VAD start-of-speech to a turn-scoped signal owned by the audio recognition layer and surfaced on _EndOfTurnInfo. A second start-of-speech within a logical turn no longer resets the verdict, and a late verdict can no longer leak into the next turn (it is only recorded while the current turn has an unresolved overlap).

Clear the buffered input audio at the drop point rather than at the verdict, so it also covers the case where the agent is still speaking and the verdict has not arrived, and no longer truncates a turn that escalates from a backchannel into an interruption.
Clear the turn's backchannel verdict when a new speech segment starts without overlapping agent speech, so a real turn following a backchannel in the same logical turn commits instead of being dropped. A stutter while the agent is still speaking keeps the verdict, so pure backchannels are still dropped.
…irst

When the agent finishes speaking while the user is still overlapping, the overlap is force-ended and a synthesized is_interruption=False event is emitted. That isn't a real verdict — the user may still be mid-turn — but it was latched as a backchannel and carried to the end of the turn, dropping genuine speech.

Tag the agent-driven overlap-end via agent_ended on the sentinel and OverlappingSpeechEvent, and skip the backchannel latch for it so the turn falls back to committing.
The backchannel verdict persisted a True from an earlier overlap into a later inconclusive overlap within the same agent turn, so a user turn that continued past the agent could be wrongly dropped. Reset it on every speech onset so each overlap re-derives its own verdict.
A confirmed backchannel over the agent left its audio in the realtime input buffer, so it prefixed the next committed turn. Clear the buffer when the backchannel is confirmed while the user is between segments; deferring when the user is already mid next-segment avoids clipping a real turn. This also applies when an STT is attached, since the realtime session buffers input regardless.
@longcw
longcw force-pushed the longc/realtime-barge-in branch from 82a241c to 2b70881 Compare July 23, 2026 06:01
Comment thread livekit-agents/livekit/agents/inference/interruption.py
@longcw
longcw merged commit cfd6ea0 into main Jul 23, 2026
26 of 27 checks passed
@longcw
longcw deleted the longc/realtime-barge-in branch July 23, 2026 13:18
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.

2 participants