From cadced3f794cd7ac2e2addf3b37936a765a2d041 Mon Sep 17 00:00:00 2001 From: Luke Payyapilli Date: Fri, 23 Jan 2026 10:41:04 -0500 Subject: [PATCH 1/2] feat: handle server_content.interrupted for faster barge-in response --- changelog/3429.added.md | 1 + src/pipecat/services/google/gemini_live/llm.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changelog/3429.added.md diff --git a/changelog/3429.added.md b/changelog/3429.added.md new file mode 100644 index 0000000000..1ba0279d72 --- /dev/null +++ b/changelog/3429.added.md @@ -0,0 +1 @@ +- Added handling for `server_content.interrupted` signal in Gemini Live services for faster interruption response. diff --git a/src/pipecat/services/google/gemini_live/llm.py b/src/pipecat/services/google/gemini_live/llm.py index f61c9826c8..7a6f55f83f 100644 --- a/src/pipecat/services/google/gemini_live/llm.py +++ b/src/pipecat/services/google/gemini_live/llm.py @@ -1198,7 +1198,11 @@ async def _connection_task_handler(self, config: LiveConnectConfig): # Reset failure counter if connection has been stable self._check_and_reset_failure_counter() - if message.server_content and message.server_content.model_turn: + if message.server_content and message.server_content.interrupted: + logger.debug("Gemini VAD: interrupted signal received") + await self.broadcast_frame(UserStartedSpeakingFrame()) + await self.push_interruption_task_frame_and_wait() + elif message.server_content and message.server_content.model_turn: await self._handle_msg_model_turn(message) elif ( message.server_content From b9390ccb1b5593d71e6e92e30a50d73f9e00ada7 Mon Sep 17 00:00:00 2001 From: Luke Payyapilli Date: Mon, 26 Jan 2026 10:08:17 -0500 Subject: [PATCH 2/2] Address review: remove UserStartedSpeakingFrame, add explanatory comment --- changelog/3429.added.md | 2 +- src/pipecat/services/google/gemini_live/llm.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/changelog/3429.added.md b/changelog/3429.added.md index 1ba0279d72..905dcac785 100644 --- a/changelog/3429.added.md +++ b/changelog/3429.added.md @@ -1 +1 @@ -- Added handling for `server_content.interrupted` signal in Gemini Live services for faster interruption response. +- Added handling for `server_content.interrupted` signal in the Gemini Live service for faster interruption response in the case where there isn't already turn tracking in the pipeline, e.g. local VAD + context aggregators. When there is already turn tracking in the pipeline, the additional interruption does no harm. diff --git a/src/pipecat/services/google/gemini_live/llm.py b/src/pipecat/services/google/gemini_live/llm.py index 7a6f55f83f..8954899554 100644 --- a/src/pipecat/services/google/gemini_live/llm.py +++ b/src/pipecat/services/google/gemini_live/llm.py @@ -1199,8 +1199,17 @@ async def _connection_task_handler(self, config: LiveConnectConfig): self._check_and_reset_failure_counter() if message.server_content and message.server_content.interrupted: + # NOTE: while the service triggers interruptions in + # the specific case of barge-ins, it does *not* + # emit UserStarted/StoppedSpeakingFrames, as the + # Gemini Live API does not give us broadly reliable + # signals to base those off of. Pipelines that + # require turn tracking (like those using context + # aggregators) still need an independent way to + # track turns, such as local Silero VAD in + # combination with the context aggregator default + # turn strategies. logger.debug("Gemini VAD: interrupted signal received") - await self.broadcast_frame(UserStartedSpeakingFrame()) await self.push_interruption_task_frame_and_wait() elif message.server_content and message.server_content.model_turn: await self._handle_msg_model_turn(message)