Skip to content

Commit 8aff514

Browse files
wukathcopybara-github
authored andcommitted
fix: Update custom gemini llm connection logic to be used for all 3_x models, not just 3.1
Co-authored-by: Kathy Wu <wukathy@google.com> PiperOrigin-RevId: 940680554
1 parent 5afa9db commit 8aff514

5 files changed

Lines changed: 47 additions & 41 deletions

File tree

src/google/adk/flows/llm_flows/basic.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,14 @@ def _build_basic_request(
120120
getattr(getattr(agent, 'canonical_live_model', None), 'model', None)
121121
or llm_request.model
122122
)
123-
is_gemini_31 = model_name_utils.is_gemini_3_1_flash_live(active_model_name)
123+
is_gemini_3_x = model_name_utils._is_gemini_3_x_live(active_model_name)
124124
llm_request.live_connect_config.enable_affective_dialog = (
125125
None
126-
if is_gemini_31
126+
if is_gemini_3_x
127127
else invocation_context.run_config.enable_affective_dialog
128128
)
129129
llm_request.live_connect_config.proactivity = (
130-
None if is_gemini_31 else invocation_context.run_config.proactivity
130+
None if is_gemini_3_x else invocation_context.run_config.proactivity
131131
)
132132
llm_request.live_connect_config.session_resumption = (
133133
invocation_context.run_config.session_resumption

src/google/adk/models/gemini_llm_connection.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def __init__(
5050
self._output_transcription_text: str = ''
5151
self._api_backend = api_backend
5252
self._model_version = model_version
53-
self._is_gemini_3_1_flash_live = model_name_utils.is_gemini_3_1_flash_live(
53+
self._is_gemini_3_x_live = model_name_utils._is_gemini_3_x_live(
5454
model_version
5555
)
5656
self._is_gemini_3_5_live_translate = (
@@ -115,11 +115,11 @@ async def send_content(self, content: types.Content):
115115
else:
116116
logger.debug('Sending LLM new content %s', content)
117117
if (
118-
self._is_gemini_3_1_flash_live
118+
self._is_gemini_3_x_live
119119
and len(content.parts) == 1
120120
and content.parts[0].text
121121
):
122-
logger.debug('Using send_realtime_input for Gemini 3.1 text input')
122+
logger.debug('Using send_realtime_input for Gemini 3.x Live text input')
123123
await self._gemini_session.send_realtime_input(
124124
text=content.parts[0].text
125125
)
@@ -140,7 +140,7 @@ async def send_realtime(self, input: RealtimeInput):
140140
if isinstance(input, types.Blob):
141141
# The blob is binary and is very large. So let's not log it.
142142
logger.debug('Sending LLM Blob.')
143-
if self._is_gemini_3_1_flash_live or self._is_gemini_3_5_live_translate:
143+
if self._is_gemini_3_x_live or self._is_gemini_3_5_live_translate:
144144
if input.mime_type and input.mime_type.startswith('audio/'):
145145
await self._gemini_session.send_realtime_input(audio=input)
146146
elif input.mime_type and input.mime_type.startswith('image/'):
@@ -362,9 +362,9 @@ async def receive(self) -> AsyncGenerator[LlmResponse, None]:
362362
# generation_complete, causing transcription to appear after
363363
# tool_call in the session log.
364364
if message.server_content.input_transcription:
365-
# Gemini 3.1 Flash Live only sends a single final input
365+
# Gemini 3.x Live only sends a single final input
366366
# transcription
367-
if self._is_gemini_3_1_flash_live:
367+
if self._is_gemini_3_x_live:
368368
if message.server_content.input_transcription.text:
369369
yield LlmResponse(
370370
input_transcription=types.Transcription(
@@ -464,7 +464,7 @@ async def receive(self) -> AsyncGenerator[LlmResponse, None]:
464464
or last_grounding_metadata
465465
or (
466466
types.GroundingMetadata()
467-
if self._is_gemini_3_1_flash_live
467+
if self._is_gemini_3_x_live
468468
else None
469469
)
470470
)
@@ -512,7 +512,7 @@ async def receive(self) -> AsyncGenerator[LlmResponse, None]:
512512
or last_grounding_metadata
513513
or (
514514
types.GroundingMetadata()
515-
if self._is_gemini_3_1_flash_live
515+
if self._is_gemini_3_x_live
516516
else None
517517
),
518518
model_version=self._model_version,
@@ -559,17 +559,17 @@ async def receive(self) -> AsyncGenerator[LlmResponse, None]:
559559
types.Part(function_call=function_call)
560560
for function_call in message.tool_call.function_calls
561561
])
562-
if not self._is_gemini_3_1_flash_live:
562+
if not self._is_gemini_3_x_live:
563563
if tool_call_metadata is None:
564564
tool_call_metadata = last_grounding_metadata
565-
# Gemini 3.1 does not emit turn_complete until it receives the
565+
# Gemini 3.x Live does not emit turn_complete until it receives the
566566
# tool response, so yield tool calls immediately to avoid
567567
# deadlocking the conversation. Other models (e.g. 2.5-pro,
568568
# native-audio) send turn_complete after tool calls, so buffer
569569
# and merge them into a single response at turn_complete.
570-
if self._is_gemini_3_1_flash_live and tool_call_parts:
570+
if self._is_gemini_3_x_live and tool_call_parts:
571571
logger.debug(
572-
'Yielding tool_call_parts immediately for Gemini 3.1 live tool'
572+
'Yielding tool_call_parts immediately for Gemini 3.x live tool'
573573
' call'
574574
)
575575
yield LlmResponse(

src/google/adk/utils/model_name_utils.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,19 +184,23 @@ def _is_gemini_eap_model(model_string: Optional[str]) -> bool:
184184
)
185185

186186

187-
def is_gemini_3_1_flash_live(model_string: Optional[str]) -> bool:
188-
"""Check if the model is a Gemini 3.1 Flash Live model.
187+
def _is_gemini_3_x_live(model_string: Optional[str]) -> bool:
188+
"""Check if the model is a Gemini 3.x Live model.
189189
190190
Args:
191191
model_string: The model name
192192
193193
Returns:
194-
True if it's a Gemini 3.1 Flash Live model, False otherwise
194+
True if it's a Gemini 3.x Live model, False otherwise
195195
"""
196196
if not model_string:
197197
return False
198198
model_name = extract_model_name(model_string)
199-
return model_name.startswith('gemini-3.1-flash-live')
199+
return (
200+
model_name.startswith('gemini-3.')
201+
and '-live' in model_name
202+
and not is_gemini_3_5_live_translate(model_string)
203+
)
200204

201205

202206
def is_gemini_3_5_live_translate(model_string: Optional[str]) -> bool:

tests/unittests/flows/llm_flows/test_basic_processor.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,13 @@ async def test_skips_output_schema_for_task_mode(self):
210210
assert llm_request.config.response_schema is None
211211

212212
@pytest.mark.asyncio
213-
async def test_disables_affective_dialog_and_proactivity_for_gemini_3_1_live(
213+
async def test_disables_affective_dialog_and_proactivity_for_gemini_3_x_live(
214214
self,
215215
):
216-
"""Gemini 3.1 Live does not support affective_dialog/proactivity."""
216+
"""Gemini 3.x Live does not support affective_dialog/proactivity."""
217217
agent = LlmAgent(
218218
name='test_agent',
219-
model='gemini-3.1-flash-live-preview',
219+
model='gemini-3.5-flash-lite-live-preview',
220220
)
221221
invocation_context = await _create_invocation_context(agent)
222222
invocation_context.run_config = RunConfig(
@@ -233,10 +233,10 @@ async def test_disables_affective_dialog_and_proactivity_for_gemini_3_1_live(
233233
assert llm_request.live_connect_config.proactivity is None
234234

235235
@pytest.mark.asyncio
236-
async def test_keeps_affective_dialog_and_proactivity_for_non_gemini_3_1(
236+
async def test_keeps_affective_dialog_and_proactivity_for_non_gemini_3_x_live(
237237
self,
238238
):
239-
"""Non-3.1 live models keep the configured affective_dialog/proactivity."""
239+
"""Non-3.x live models keep the configured affective_dialog/proactivity."""
240240
agent = LlmAgent(
241241
name='test_agent',
242242
model='gemini-2.5-flash-live',

tests/unittests/utils/test_model_name_utils.py

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
"""Tests for model name utility functions."""
1616

1717
from google.adk.models.llm_request import LlmRequest
18+
from google.adk.utils.model_name_utils import _is_gemini_3_x_live
1819
from google.adk.utils.model_name_utils import _is_managed_agent
1920
from google.adk.utils.model_name_utils import extract_model_name
2021
from google.adk.utils.model_name_utils import is_gemini_1_model
21-
from google.adk.utils.model_name_utils import is_gemini_3_1_flash_live
2222
from google.adk.utils.model_name_utils import is_gemini_3_5_live_translate
2323
from google.adk.utils.model_name_utils import is_gemini_eap_or_2_or_above
2424
from google.adk.utils.model_name_utils import is_gemini_model
@@ -387,31 +387,33 @@ def test_true_enables_check_bypass(self, monkeypatch):
387387
assert is_gemini_model_id_check_disabled() is True
388388

389389

390-
class TestIsGemini31FlashLive:
391-
"""Test the is_gemini_3_1_flash_live function."""
390+
class TestIsGemini3XLive:
391+
"""Test the _is_gemini_3_x_live function."""
392392

393-
def test_is_gemini_3_1_flash_live_simple_name(self):
393+
def test_is_gemini_3_x_live_simple_name(self):
394394
"""Test with simple model name format."""
395-
assert is_gemini_3_1_flash_live('gemini-3.1-flash-live') is True
396-
assert is_gemini_3_1_flash_live('gemini-3.1-flash-live-preview') is True
397-
assert is_gemini_3_1_flash_live('gemini-3.1-pro-live') is False
398-
assert is_gemini_3_1_flash_live('gemini-2.5-flash-live') is False
399-
400-
def test_is_gemini_3_1_flash_live_path_based_name(self):
395+
assert _is_gemini_3_x_live('gemini-3.1-flash-live') is True
396+
assert _is_gemini_3_x_live('gemini-3.1-flash-live-preview') is True
397+
assert _is_gemini_3_x_live('gemini-3.5-flash-lite-live-preview') is True
398+
assert _is_gemini_3_x_live('gemini-3.5-live-translate') is False
399+
assert _is_gemini_3_x_live('gemini-3.1-pro') is False
400+
assert _is_gemini_3_x_live('gemini-2.5-flash-live') is False
401+
402+
def test_is_gemini_3_x_live_path_based_name(self):
401403
"""Test with path-based format (Vertex AI etc.)."""
402404
vertex_path = 'projects/123/locations/us-central1/publishers/google/models/gemini-3.1-flash-live'
403-
assert is_gemini_3_1_flash_live(vertex_path) is True
405+
assert _is_gemini_3_x_live(vertex_path) is True
404406

405-
vertex_path_preview = 'projects/123/locations/us-central1/publishers/google/models/gemini-3.1-flash-live-preview'
406-
assert is_gemini_3_1_flash_live(vertex_path_preview) is True
407+
vertex_path_preview = 'projects/123/locations/us-central1/publishers/google/models/gemini-3.5-flash-lite-live-preview'
408+
assert _is_gemini_3_x_live(vertex_path_preview) is True
407409

408410
non_live_path = 'projects/123/locations/us-central1/publishers/google/models/gemini-3.1-flash'
409-
assert is_gemini_3_1_flash_live(non_live_path) is False
411+
assert _is_gemini_3_x_live(non_live_path) is False
410412

411-
def test_is_gemini_3_1_flash_live_edge_cases(self):
413+
def test_is_gemini_3_x_live_edge_cases(self):
412414
"""Test edge cases."""
413-
assert is_gemini_3_1_flash_live(None) is False
414-
assert is_gemini_3_1_flash_live('') is False
415+
assert _is_gemini_3_x_live(None) is False
416+
assert _is_gemini_3_x_live('') is False
415417

416418

417419
class TestIsGemini35LiveTranslate:

0 commit comments

Comments
 (0)