fix(realtime): cancel the generation when a speech is interrupted - #6962
Merged
Merged
Conversation
SpeechHandle.interrupt() marked the handle interrupted and released wait_for_playout(), but only AgentActivity.interrupt() paired that with RealtimeSession.interrupt(). An app that interrupts a handle directly left the model generating server-side, and AgentActivity's own interrupt deadlocks inside an AgentTask because its future waits for the task's own speech. The realtime generation task owns the response, so it now sends the cancel wherever it observes the interrupt. The cancel is session-wide, so both streams of the generation are drained through a tee and the cancel is skipped once the provider ended the response. Playout lags generation by seconds, so without that guard an interrupt on a speech that is still playing can stop whichever response took its place.
davidzhao
approved these changes
Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
SpeechHandle.interrupt()marks the handle interrupted and releaseswait_for_playout(). It does not stop the model.Only
AgentActivity.interrupt()pairs the local interruption withRealtimeSession.interrupt(), and anAgentTaskcannot use it. Its future resolves when every interrupted speech is done, and one of them is the task's ownon_enterspeech.So an app that interrupts a handle directly keeps the response alive. The interrupted reply completes a few seconds later.
Fix
The realtime generation task owns the server-side response, so it now sends the cancel where it observes the interrupt. This covers every interrupt path and adds no new API.
The cancel is session-wide, so it must not go out after the provider ended the response. Both streams of the generation are drained through a tee, and the cancel is skipped once they close.
Playout lags generation by seconds, so that guard is load-bearing. Without it, an interrupt on a speech that is still playing buffered audio can stop whichever response took its place.