diff --git a/src/google/adk/agents/live_request_queue.py b/src/google/adk/agents/live_request_queue.py index c9c7b82c685..e2f0581858a 100644 --- a/src/google/adk/agents/live_request_queue.py +++ b/src/google/adk/agents/live_request_queue.py @@ -57,6 +57,14 @@ class LiveRequest(BaseModel): activity_start > activity_end > blob > content. state_delta, if set, is always applied regardless of the other fields. """ + audio_stream_end: Optional[bool] = None + """If set to True, signal that the audio stream has ended (e.g. microphone + turned off) to force flush buffered audio with VAD enabled. + + When multiple fields are set, they are processed by priority (highest first): + activity_start > activity_end > audio_stream_end > blob > content. + state_delta, if set, is always applied regardless of the other fields. + """ close: bool = False """If set, close the queue. queue.shutdown() is only supported in Python 3.13+.""" @@ -92,6 +100,10 @@ def send_activity_end(self) -> None: """Sends an activity end signal to mark the end of user input.""" self._queue.put_nowait(LiveRequest(activity_end=types.ActivityEnd())) + def send_audio_stream_end(self) -> None: + """Sends an audio stream end signal to force flush buffered audio with VAD.""" + self._queue.put_nowait(LiveRequest(audio_stream_end=True)) + def send(self, req: LiveRequest) -> None: self._queue.put_nowait(req) diff --git a/src/google/adk/flows/llm_flows/base_llm_flow.py b/src/google/adk/flows/llm_flows/base_llm_flow.py index 2a799d54ff3..ea2dddd78e4 100644 --- a/src/google/adk/flows/llm_flows/base_llm_flow.py +++ b/src/google/adk/flows/llm_flows/base_llm_flow.py @@ -827,6 +827,8 @@ async def _send_to_model( await llm_connection.send_realtime(types.ActivityStart()) elif live_request.activity_end: await llm_connection.send_realtime(types.ActivityEnd()) + elif live_request.audio_stream_end: + await llm_connection.send_realtime(True) elif live_request.blob: # Cache input audio chunks before flushing self.audio_cache_manager.cache_audio( diff --git a/src/google/adk/models/gemini_llm_connection.py b/src/google/adk/models/gemini_llm_connection.py index 66534b39fdc..717623bb8c7 100644 --- a/src/google/adk/models/gemini_llm_connection.py +++ b/src/google/adk/models/gemini_llm_connection.py @@ -29,7 +29,7 @@ logger = logging.getLogger('google_adk.' + __name__) -RealtimeInput = Union[types.Blob, types.ActivityStart, types.ActivityEnd] +RealtimeInput = Union[types.Blob, types.ActivityStart, types.ActivityEnd, bool] from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -173,6 +173,9 @@ async def send_realtime(self, input: RealtimeInput) -> None: elif isinstance(input, types.ActivityEnd): logger.debug('Sending LLM activity end signal.') await self._gemini_session.send_realtime_input(activity_end=input) + elif isinstance(input, bool) and input: + logger.debug('Sending LLM audio stream end signal.') + await self._gemini_session.send_realtime_input(audio_stream_end=True) else: raise ValueError('Unsupported input type: %s' % type(input))