@@ -50,6 +50,9 @@ 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 (
54+ model_version
55+ )
5356
5457 async def send_history (self , history : list [types .Content ]):
5558 """Sends the conversation history to the gemini model.
@@ -80,14 +83,14 @@ async def send_history(self, history: list[types.Content]):
8083 ]
8184
8285 if contents :
83- is_gemini_31 = model_name_utils .is_gemini_3_1_flash_live (
84- self ._model_version
85- )
8686 # Gemini Enterprise Agent Platform does not support history_config in the SDK.
8787 # To initialize a live session with prior history without hitting a 1007
8888 # protocol error (invalid role mid-session), we consolidate previous multi-turn
8989 # interactions into a unified contextual preamble on a single user role turn.
90- if is_gemini_31 and self ._api_backend != GoogleLLMVariant .GEMINI_API :
90+ if (
91+ self ._is_gemini_3_1_flash_live
92+ and self ._api_backend != GoogleLLMVariant .GEMINI_API
93+ ):
9194 collapsed_text = 'Previous conversation history:\n '
9295 for c in contents :
9396 text_parts = '' .join (p .text for p in c .parts if p .text )
@@ -101,7 +104,9 @@ async def send_history(self, history: list[types.Content]):
101104 logger .debug ('Sending history to live connection: %s' , contents )
102105 await self ._gemini_session .send_client_content (
103106 turns = contents ,
104- turn_complete = True if is_gemini_31 else (contents [- 1 ].role == 'user' ),
107+ turn_complete = True
108+ if self ._is_gemini_3_1_flash_live
109+ else (contents [- 1 ].role == 'user' ),
105110 )
106111 else :
107112 logger .info ('no content is sent' )
@@ -126,10 +131,11 @@ async def send_content(self, content: types.Content):
126131 )
127132 else :
128133 logger .debug ('Sending LLM new content %s' , content )
129- is_gemini_31 = model_name_utils .is_gemini_3_1_flash_live (
130- self ._model_version
131- )
132- if is_gemini_31 and len (content .parts ) == 1 and content .parts [0 ].text :
134+ if (
135+ self ._is_gemini_3_1_flash_live
136+ and len (content .parts ) == 1
137+ and content .parts [0 ].text
138+ ):
133139 logger .debug ('Using send_realtime_input for Gemini 3.1 text input' )
134140 await self ._gemini_session .send_realtime_input (
135141 text = content .parts [0 ].text
@@ -151,10 +157,7 @@ async def send_realtime(self, input: RealtimeInput):
151157 if isinstance (input , types .Blob ):
152158 # The blob is binary and is very large. So let's not log it.
153159 logger .debug ('Sending LLM Blob.' )
154- is_gemini_31 = model_name_utils .is_gemini_3_1_flash_live (
155- self ._model_version
156- )
157- if is_gemini_31 :
160+ if self ._is_gemini_3_1_flash_live :
158161 if input .mime_type and input .mime_type .startswith ('audio/' ):
159162 await self ._gemini_session .send_realtime_input (audio = input )
160163 elif input .mime_type and input .mime_type .startswith ('image/' ):
@@ -196,10 +199,15 @@ def __build_full_text_response(
196199 Returns:
197200 An LlmResponse containing the full text.
198201 """
202+ part = types .Part .from_text (text = text )
203+ if is_thought :
204+ part .thought = True
205+ if grounding_metadata is None and self ._is_gemini_3_1_flash_live :
206+ grounding_metadata = types .GroundingMetadata ()
199207 return LlmResponse (
200208 content = types .Content (
201209 role = 'model' ,
202- parts = [types . Part . from_text ( text = text ) ],
210+ parts = [part ],
203211 ),
204212 grounding_metadata = grounding_metadata ,
205213 partial = False ,
@@ -214,6 +222,7 @@ async def receive(self) -> AsyncGenerator[LlmResponse, None]:
214222 """
215223
216224 text = ''
225+ is_thought = False
217226 tool_call_parts = []
218227 pending_grounding_metadata = None
219228 async with Aclosing (self ._gemini_session .receive ()) as agen :
@@ -265,9 +274,12 @@ async def receive(self) -> AsyncGenerator[LlmResponse, None]:
265274 # grounding_metadata is yielded again at turn_complete,
266275 # so avoid duplicating it here if turn_complete is true.
267276 if not message .server_content .turn_complete :
268- llm_response .grounding_metadata = (
269- message .server_content .grounding_metadata
270- )
277+ if message .server_content .grounding_metadata is not None :
278+ llm_response .grounding_metadata = (
279+ message .server_content .grounding_metadata
280+ )
281+ elif self ._is_gemini_3_1_flash_live :
282+ llm_response .grounding_metadata = types .GroundingMetadata ()
271283 has_inline_data = any (p .inline_data for p in content .parts )
272284 for part in content .parts :
273285 if part .text :
@@ -394,7 +406,12 @@ async def receive(self) -> AsyncGenerator[LlmResponse, None]:
394406 turn_complete = True ,
395407 interrupted = message .server_content .interrupted ,
396408 grounding_metadata = message .server_content .grounding_metadata
397- or g_metadata_to_yield ,
409+ or g_metadata_to_yield
410+ or (
411+ types .GroundingMetadata ()
412+ if self ._is_gemini_3_1_flash_live
413+ else None
414+ ),
398415 model_version = self ._model_version ,
399416 live_session_id = live_session_id ,
400417 turn_complete_reason = getattr (
@@ -430,10 +447,7 @@ async def receive(self) -> AsyncGenerator[LlmResponse, None]:
430447 # deadlocking the conversation. Other models (e.g. 2.5-pro,
431448 # native-audio) send turn_complete after tool calls, so buffer
432449 # and merge them into a single response at turn_complete.
433- if (
434- model_name_utils .is_gemini_3_1_flash_live (self ._model_version )
435- and tool_call_parts
436- ):
450+ if self ._is_gemini_3_1_flash_live and tool_call_parts :
437451 logger .debug (
438452 'Yielding tool_call_parts immediately for Gemini 3.1 live tool'
439453 ' call'
@@ -442,6 +456,7 @@ async def receive(self) -> AsyncGenerator[LlmResponse, None]:
442456 content = types .Content (role = 'model' , parts = tool_call_parts ),
443457 model_version = self ._model_version ,
444458 live_session_id = live_session_id ,
459+ grounding_metadata = types .GroundingMetadata (),
445460 )
446461 tool_call_parts = []
447462 if message .session_resumption_update :
0 commit comments