From 455fec53a4d52a63e912ba70e30f662b9d66cb0d Mon Sep 17 00:00:00 2001 From: Liang Wu <18244712+wuliang229@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:07:23 -0700 Subject: [PATCH 01/13] Rename Gemini 3.1 references to Gemini 3.x --- .../adk/models/gemini_llm_connection.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/google/adk/models/gemini_llm_connection.py b/src/google/adk/models/gemini_llm_connection.py index c469b75959f..62b1d7ee321 100644 --- a/src/google/adk/models/gemini_llm_connection.py +++ b/src/google/adk/models/gemini_llm_connection.py @@ -50,7 +50,7 @@ def __init__( self._output_transcription_text: str = '' self._api_backend = api_backend self._model_version = model_version - self._is_gemini_3_1_flash_live = model_name_utils.is_gemini_3_1_flash_live( + self._is_gemini_3_x_live = model_name_utils.is_gemini_3_1_flash_live( model_version ) @@ -112,11 +112,11 @@ async def send_content(self, content: types.Content): else: logger.debug('Sending LLM new content %s', content) if ( - self._is_gemini_3_1_flash_live + self._is_gemini_3_x_live and len(content.parts) == 1 and content.parts[0].text ): - logger.debug('Using send_realtime_input for Gemini 3.1 text input') + logger.debug('Using send_realtime_input for Gemini 3.x Live text input') await self._gemini_session.send_realtime_input( text=content.parts[0].text ) @@ -137,7 +137,7 @@ async def send_realtime(self, input: RealtimeInput): if isinstance(input, types.Blob): # The blob is binary and is very large. So let's not log it. logger.debug('Sending LLM Blob.') - if self._is_gemini_3_1_flash_live: + if self._is_gemini_3_x_live: if input.mime_type and input.mime_type.startswith('audio/'): await self._gemini_session.send_realtime_input(audio=input) elif input.mime_type and input.mime_type.startswith('image/'): @@ -281,9 +281,9 @@ async def receive(self) -> AsyncGenerator[LlmResponse, None]: # generation_complete, causing transcription to appear after # tool_call in the session log. if message.server_content.input_transcription: - # Gemini 3.1 Flash Live only sends a single final input + # Gemini 3.x Live only sends a single final input # transcription - if self._is_gemini_3_1_flash_live: + if self._is_gemini_3_x_live: if message.server_content.input_transcription.text: yield LlmResponse( input_transcription=types.Transcription( @@ -400,7 +400,7 @@ async def receive(self) -> AsyncGenerator[LlmResponse, None]: or g_metadata_to_yield or ( types.GroundingMetadata() - if self._is_gemini_3_1_flash_live + if self._is_gemini_3_x_live else None ), model_version=self._model_version, @@ -433,14 +433,14 @@ async def receive(self) -> AsyncGenerator[LlmResponse, None]: types.Part(function_call=function_call) for function_call in message.tool_call.function_calls ]) - # Gemini 3.1 does not emit turn_complete until it receives the + # Gemini 3.x Live does not emit turn_complete until it receives the # tool response, so yield tool calls immediately to avoid # deadlocking the conversation. Other models (e.g. 2.5-pro, # native-audio) send turn_complete after tool calls, so buffer # and merge them into a single response at turn_complete. - if self._is_gemini_3_1_flash_live and tool_call_parts: + if self._is_gemini_3_x_live and tool_call_parts: logger.debug( - 'Yielding tool_call_parts immediately for Gemini 3.1 live tool' + 'Yielding tool_call_parts immediately for Gemini 3.x live tool' ' call' ) yield LlmResponse( From 53bf1b5f49b8d4b9afbb5008e1fdae7f63c224c9 Mon Sep 17 00:00:00 2001 From: Liang Wu <18244712+wuliang229@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:09:04 -0700 Subject: [PATCH 02/13] Update model_name_utils.py --- src/google/adk/utils/model_name_utils.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/google/adk/utils/model_name_utils.py b/src/google/adk/utils/model_name_utils.py index dbb3a08193c..25dd9803934 100644 --- a/src/google/adk/utils/model_name_utils.py +++ b/src/google/adk/utils/model_name_utils.py @@ -161,16 +161,21 @@ def _is_gemini_eap_model(model_string: Optional[str]) -> bool: ) -def is_gemini_3_1_flash_live(model_string: Optional[str]) -> bool: - """Check if the model is a Gemini 3.1 Flash Live model. +def _is_gemini_3_x_live(model_string: Optional[str]) -> bool: + """Check if the model is a Gemini 3.x Live model. Args: model_string: The model name Returns: - True if it's a Gemini 3.1 Flash Live model, False otherwise + True if it's a Gemini 3.x Live model, False otherwise """ if not model_string: return False model_name = extract_model_name(model_string) - return model_name.startswith('gemini-3.1-flash-live') + return ( + model_name.startswith('gemini-3.') + and '-live' in model_name + and not is_gemini_3_5_live_translate(model_string) + ) + From 28fbd6abbf3e4791949353898ae7b81a3e51ab8c Mon Sep 17 00:00:00 2001 From: Liang Wu <18244712+wuliang229@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:10:32 -0700 Subject: [PATCH 03/13] Rename tests for is_gemini_3_1_flash_live to is_gemini_3_x_live --- .../unittests/utils/test_model_name_utils.py | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/tests/unittests/utils/test_model_name_utils.py b/tests/unittests/utils/test_model_name_utils.py index f962fb6f5c2..18e21c5a8bb 100644 --- a/tests/unittests/utils/test_model_name_utils.py +++ b/tests/unittests/utils/test_model_name_utils.py @@ -14,9 +14,9 @@ """Tests for model name utility functions.""" +from google.adk.utils.model_name_utils import _is_gemini_3_x_live from google.adk.utils.model_name_utils import extract_model_name from google.adk.utils.model_name_utils import is_gemini_1_model -from google.adk.utils.model_name_utils import is_gemini_3_1_flash_live from google.adk.utils.model_name_utils import is_gemini_eap_or_2_or_above from google.adk.utils.model_name_utils import is_gemini_model from google.adk.utils.model_name_utils import is_gemini_model_id_check_disabled @@ -341,26 +341,30 @@ def test_true_enables_check_bypass(self, monkeypatch): assert is_gemini_model_id_check_disabled() is True -class TestIsGemini31FlashLive: - """Test the is_gemini_3_1_flash_live function.""" +class TestIsGemini3XLive: + """Test the _is_gemini_3_x_live function.""" - def test_is_gemini_3_1_flash_live_simple_name(self): + def test_is_gemini_3_x_live_simple_name(self): """Test with simple model name format.""" - assert is_gemini_3_1_flash_live('gemini-3.1-flash-live') is True - assert is_gemini_3_1_flash_live('gemini-3.1-flash-live-preview') is True - assert is_gemini_3_1_flash_live('gemini-3.1-pro-live') is False - assert is_gemini_3_1_flash_live('gemini-2.5-flash-live') is False - - def test_is_gemini_3_1_flash_live_path_based_name(self): + assert _is_gemini_3_x_live('gemini-3.1-flash-live') is True + assert _is_gemini_3_x_live('gemini-3.1-flash-live-preview') is True + assert _is_gemini_3_x_live('gemini-3.5-flash-lite-live-preview') is True + assert _is_gemini_3_x_live('gemini-3.5-live-translate') is False + assert _is_gemini_3_x_live('gemini-3.1-pro') is False + assert _is_gemini_3_x_live('gemini-2.5-flash-live') is False + + def test_is_gemini_3_x_live_path_based_name(self): """Test with path-based format (Vertex AI etc.).""" vertex_path = 'projects/123/locations/us-central1/publishers/google/models/gemini-3.1-flash-live' - assert is_gemini_3_1_flash_live(vertex_path) is True - vertex_path_preview = 'projects/123/locations/us-central1/publishers/google/models/gemini-3.1-flash-live-preview' - assert is_gemini_3_1_flash_live(vertex_path_preview) is True + assert _is_gemini_3_x_live(vertex_path) is True + + vertex_path_preview = 'projects/123/locations/us-central1/publishers/google/models/gemini-3.5-flash-lite-live-preview' + assert _is_gemini_3_x_live(vertex_path_preview) is True + non_live_path = 'projects/123/locations/us-central1/publishers/google/models/gemini-3.1-flash' - assert is_gemini_3_1_flash_live(non_live_path) is False + assert _is_gemini_3_x_live(non_live_path) is False - def test_is_gemini_3_1_flash_live_edge_cases(self): + def test_is_gemini_3_x_live_edge_cases(self): """Test edge cases.""" - assert is_gemini_3_1_flash_live(None) is False - assert is_gemini_3_1_flash_live('') is False + assert _is_gemini_3_x_live(None) is False + assert _is_gemini_3_x_live('') is False From 3fb3dc35f02cfab21f75a47ff9d63aecbf425e04 Mon Sep 17 00:00:00 2001 From: Liang Wu <18244712+wuliang229@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:11:35 -0700 Subject: [PATCH 04/13] Update Gemini model check in llm_flows --- src/google/adk/flows/llm_flows/basic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/google/adk/flows/llm_flows/basic.py b/src/google/adk/flows/llm_flows/basic.py index 50f03d0bf17..7bd42932949 100644 --- a/src/google/adk/flows/llm_flows/basic.py +++ b/src/google/adk/flows/llm_flows/basic.py @@ -83,14 +83,14 @@ def _build_basic_request( getattr(getattr(agent, 'canonical_live_model', None), 'model', None) or llm_request.model ) - is_gemini_31 = model_name_utils.is_gemini_3_1_flash_live(active_model_name) + is_gemini_3_x = model_name_utils._is_gemini_3_x_live(active_model_name) llm_request.live_connect_config.enable_affective_dialog = ( None - if is_gemini_31 + if is_gemini_3_x else invocation_context.run_config.enable_affective_dialog ) llm_request.live_connect_config.proactivity = ( - None if is_gemini_31 else invocation_context.run_config.proactivity + None if is_gemini_3_x else invocation_context.run_config.proactivity ) llm_request.live_connect_config.session_resumption = ( invocation_context.run_config.session_resumption From d4875868784e215e1e75630a4bb5f0f69a6ec399 Mon Sep 17 00:00:00 2001 From: Liang Wu <18244712+wuliang229@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:14:45 -0700 Subject: [PATCH 05/13] Add tests for affective dialog and proactivity settings --- .../flows/llm_flows/test_basic_processor.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/unittests/flows/llm_flows/test_basic_processor.py b/tests/unittests/flows/llm_flows/test_basic_processor.py index 5f3cd4ef446..450ee6dff2c 100644 --- a/tests/unittests/flows/llm_flows/test_basic_processor.py +++ b/tests/unittests/flows/llm_flows/test_basic_processor.py @@ -188,3 +188,49 @@ async def test_sets_model_name(self): # Should have set the model name assert llm_request.model == 'gemini-2.5-flash' + + @pytest.mark.asyncio + async def test_disables_affective_dialog_and_proactivity_for_gemini_3_x_live( + self, + ): + """Gemini 3.x Live does not support affective_dialog/proactivity.""" + agent = LlmAgent( + name='test_agent', + model='gemini-3.5-flash-lite-live-preview', + ) + invocation_context = await _create_invocation_context(agent) + invocation_context.run_config = RunConfig( + enable_affective_dialog=True, + proactivity=types.ProactivityConfig(), + ) + llm_request = LlmRequest() + processor = _BasicLlmRequestProcessor() + + async for _ in processor.run_async(invocation_context, llm_request): + pass + + assert llm_request.live_connect_config.enable_affective_dialog is None + assert llm_request.live_connect_config.proactivity is None + + @pytest.mark.asyncio + async def test_keeps_affective_dialog_and_proactivity_for_non_gemini_3_x_live( + self, + ): + """Non-3.x live models keep the configured affective_dialog/proactivity.""" + agent = LlmAgent( + name='test_agent', + model='gemini-2.5-flash-live', + ) + invocation_context = await _create_invocation_context(agent) + invocation_context.run_config = RunConfig( + enable_affective_dialog=True, + proactivity=types.ProactivityConfig(), + ) + llm_request = LlmRequest() + processor = _BasicLlmRequestProcessor() + + async for _ in processor.run_async(invocation_context, llm_request): + pass + + assert llm_request.live_connect_config.enable_affective_dialog is True + assert llm_request.live_connect_config.proactivity is not None From ee8b5bcd4e6a9ca3bbbdb983948db8851eb9799b Mon Sep 17 00:00:00 2001 From: Liang Wu <18244712+wuliang229@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:16:43 -0700 Subject: [PATCH 06/13] Remove unnecessary blank line in model_name_utils.py --- src/google/adk/utils/model_name_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/google/adk/utils/model_name_utils.py b/src/google/adk/utils/model_name_utils.py index 25dd9803934..41b718b26b4 100644 --- a/src/google/adk/utils/model_name_utils.py +++ b/src/google/adk/utils/model_name_utils.py @@ -178,4 +178,3 @@ def _is_gemini_3_x_live(model_string: Optional[str]) -> bool: and '-live' in model_name and not is_gemini_3_5_live_translate(model_string) ) - From d124ae1fe724bf18a0ba62da2b77e6075bf1e854 Mon Sep 17 00:00:00 2001 From: Liang Wu <18244712+wuliang229@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:18:08 -0700 Subject: [PATCH 07/13] Remove condition for Gemini 3.5 live translation --- src/google/adk/utils/model_name_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/google/adk/utils/model_name_utils.py b/src/google/adk/utils/model_name_utils.py index 41b718b26b4..f9e7c59e6c1 100644 --- a/src/google/adk/utils/model_name_utils.py +++ b/src/google/adk/utils/model_name_utils.py @@ -176,5 +176,4 @@ def _is_gemini_3_x_live(model_string: Optional[str]) -> bool: return ( model_name.startswith('gemini-3.') and '-live' in model_name - and not is_gemini_3_5_live_translate(model_string) ) From 6b2f45ce87fcbd65f60b38da30d5bb28d99ae06d Mon Sep 17 00:00:00 2001 From: Liang Wu <18244712+wuliang229@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:19:04 -0700 Subject: [PATCH 08/13] Update method to check Gemini 3.x live status --- src/google/adk/models/gemini_llm_connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/google/adk/models/gemini_llm_connection.py b/src/google/adk/models/gemini_llm_connection.py index 62b1d7ee321..6646fe9a798 100644 --- a/src/google/adk/models/gemini_llm_connection.py +++ b/src/google/adk/models/gemini_llm_connection.py @@ -50,7 +50,7 @@ def __init__( self._output_transcription_text: str = '' self._api_backend = api_backend self._model_version = model_version - self._is_gemini_3_x_live = model_name_utils.is_gemini_3_1_flash_live( + self._is_gemini_3_x_live = model_name_utils._is_gemini_3_x_live( model_version ) From 82b66cca8e2ad663cb230bf03147db0d49902adc Mon Sep 17 00:00:00 2001 From: Liang Wu <18244712+wuliang229@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:28:32 -0700 Subject: [PATCH 09/13] Add import for google.genai.types in test_basic_processor --- tests/unittests/flows/llm_flows/test_basic_processor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unittests/flows/llm_flows/test_basic_processor.py b/tests/unittests/flows/llm_flows/test_basic_processor.py index 450ee6dff2c..884557de8d6 100644 --- a/tests/unittests/flows/llm_flows/test_basic_processor.py +++ b/tests/unittests/flows/llm_flows/test_basic_processor.py @@ -23,6 +23,7 @@ from google.adk.models.llm_request import LlmRequest from google.adk.sessions.in_memory_session_service import InMemorySessionService from google.adk.tools.function_tool import FunctionTool +from google.genai import types from pydantic import BaseModel from pydantic import Field import pytest From 301eceb1ec3255d505195fbf29342d1b5fd2fbc0 Mon Sep 17 00:00:00 2001 From: Liang Wu <18244712+wuliang229@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:29:05 -0700 Subject: [PATCH 10/13] Remove test case for gemini-3.5-live-translate Removed a test case that checked for 'gemini-3.5-live-translate' being live. --- tests/unittests/utils/test_model_name_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unittests/utils/test_model_name_utils.py b/tests/unittests/utils/test_model_name_utils.py index 18e21c5a8bb..09ba33b8e41 100644 --- a/tests/unittests/utils/test_model_name_utils.py +++ b/tests/unittests/utils/test_model_name_utils.py @@ -349,7 +349,6 @@ def test_is_gemini_3_x_live_simple_name(self): assert _is_gemini_3_x_live('gemini-3.1-flash-live') is True assert _is_gemini_3_x_live('gemini-3.1-flash-live-preview') is True assert _is_gemini_3_x_live('gemini-3.5-flash-lite-live-preview') is True - assert _is_gemini_3_x_live('gemini-3.5-live-translate') is False assert _is_gemini_3_x_live('gemini-3.1-pro') is False assert _is_gemini_3_x_live('gemini-2.5-flash-live') is False From f225fd0d94a0c4a631c30629475cc3d1d944740d Mon Sep 17 00:00:00 2001 From: Liang Wu <18244712+wuliang229@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:43:28 -0700 Subject: [PATCH 11/13] Update model_name_utils.py --- src/google/adk/utils/model_name_utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/google/adk/utils/model_name_utils.py b/src/google/adk/utils/model_name_utils.py index f9e7c59e6c1..28ba295a355 100644 --- a/src/google/adk/utils/model_name_utils.py +++ b/src/google/adk/utils/model_name_utils.py @@ -173,7 +173,4 @@ def _is_gemini_3_x_live(model_string: Optional[str]) -> bool: if not model_string: return False model_name = extract_model_name(model_string) - return ( - model_name.startswith('gemini-3.') - and '-live' in model_name - ) +return model_name.startswith('gemini-3.') and '-live' in model_name From caa9c5d689daf5abd641dee6cbe97844e1b61ee5 Mon Sep 17 00:00:00 2001 From: Liang Wu <18244712+wuliang229@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:45:11 -0700 Subject: [PATCH 12/13] Fix formatting issue in model_name_utils.py --- src/google/adk/utils/model_name_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/google/adk/utils/model_name_utils.py b/src/google/adk/utils/model_name_utils.py index 28ba295a355..7ab3be817b2 100644 --- a/src/google/adk/utils/model_name_utils.py +++ b/src/google/adk/utils/model_name_utils.py @@ -173,4 +173,4 @@ def _is_gemini_3_x_live(model_string: Optional[str]) -> bool: if not model_string: return False model_name = extract_model_name(model_string) -return model_name.startswith('gemini-3.') and '-live' in model_name + return model_name.startswith('gemini-3.') and '-live' in model_name From 534d01b746803a29442328096c956eb78ff91d82 Mon Sep 17 00:00:00 2001 From: Liang Wu <18244712+wuliang229@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:47:11 -0700 Subject: [PATCH 13/13] Fix formatting in test_basic_processor.py --- tests/unittests/flows/llm_flows/test_basic_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unittests/flows/llm_flows/test_basic_processor.py b/tests/unittests/flows/llm_flows/test_basic_processor.py index 884557de8d6..d59e0056d00 100644 --- a/tests/unittests/flows/llm_flows/test_basic_processor.py +++ b/tests/unittests/flows/llm_flows/test_basic_processor.py @@ -189,7 +189,7 @@ async def test_sets_model_name(self): # Should have set the model name assert llm_request.model == 'gemini-2.5-flash' - + @pytest.mark.asyncio async def test_disables_affective_dialog_and_proactivity_for_gemini_3_x_live( self,