@@ -27,11 +27,31 @@ def _suppress_experimental_warning():
2727def _get_text_output (interaction ):
2828 """Extract the first text output, skipping any thought content."""
2929 for output in interaction .outputs :
30- if output . type == "text" :
30+ if _get_field ( output , " type" ) == "text" :
3131 return output
3232 return None
3333
3434
35+ def _get_field (value , key , default = None ):
36+ """Get a field from either an SDK object or a dict payload."""
37+ if isinstance (value , dict ):
38+ return value .get (key , default )
39+ return getattr (value , key , default )
40+
41+
42+ def _get_event_type (event ):
43+ """Get a canonical event type string across google-genai versions."""
44+ return _get_field (event , "event_type" , type (event ).__name__ )
45+
46+
47+ def _get_content_delta_text (event ):
48+ """Extract text from a content.delta event for both SDK object styles."""
49+ delta = _get_field (event , "delta" )
50+ if delta is None :
51+ return None
52+ return _get_field (delta , "text" )
53+
54+
3555def test_interactions_non_streaming_basic (genai_client , text_model_id ):
3656 """Basic non-streaming interaction returns a valid Google Interactions response."""
3757 interaction = genai_client .interactions .create (
@@ -43,8 +63,10 @@ def test_interactions_non_streaming_basic(genai_client, text_model_id):
4363 assert interaction .status == "completed" , f"Status should be 'completed', got: { interaction .status } "
4464 assert len (interaction .outputs ) > 0 , "Expected at least one output"
4565 text_output = _get_text_output (interaction )
46- assert text_output is not None , f"Expected a text output, got types: { [o .type for o in interaction .outputs ]} "
47- assert len (text_output .text ) > 0
66+ assert text_output is not None , (
67+ f"Expected a text output, got types: { [_get_field (o , 'type' ) for o in interaction .outputs ]} "
68+ )
69+ assert len (_get_field (text_output , "text" , "" )) > 0
4870 assert interaction .usage .total_input_tokens > 0
4971 assert interaction .usage .total_output_tokens > 0
5072 assert (
@@ -64,7 +86,7 @@ def test_interactions_non_streaming_system_instruction(genai_client, text_model_
6486 assert len (interaction .outputs ) > 0
6587 text_output = _get_text_output (interaction )
6688 assert text_output is not None
67- assert len (text_output . text ) > 0
89+ assert len (_get_field ( text_output , " text" , "" ) ) > 0
6890
6991
7092def test_interactions_non_streaming_multi_turn (genai_client , text_model_id ):
@@ -82,7 +104,7 @@ def test_interactions_non_streaming_multi_turn(genai_client, text_model_id):
82104 assert len (interaction .outputs ) > 0
83105 text_output = _get_text_output (interaction )
84106 assert text_output is not None
85- assert "alice" in text_output . text .lower ()
107+ assert "alice" in _get_field ( text_output , " text" , "" ) .lower ()
86108
87109
88110def test_interactions_non_streaming_generation_config (genai_client , text_model_id ):
@@ -100,7 +122,7 @@ def test_interactions_non_streaming_generation_config(genai_client, text_model_i
100122 assert len (interaction .outputs ) > 0
101123 text_output = _get_text_output (interaction )
102124 assert text_output is not None
103- assert len (text_output . text ) > 0
125+ assert len (_get_field ( text_output , " text" , "" ) ) > 0
104126
105127
106128def test_interactions_non_streaming_response_shape (genai_client , text_model_id ):
@@ -131,25 +153,28 @@ def test_interactions_streaming_basic(genai_client, text_model_id):
131153 interaction_id = None
132154
133155 for event in stream :
134- event_name = type (event ). __name__
135- event_types .append (event_name )
156+ event_type = _get_event_type (event )
157+ event_types .append (event_type )
136158
137- if event_name == "InteractionStartEvent" and hasattr (event , "interaction" ) and event .interaction :
138- interaction_id = event .interaction .id
159+ if event_type == "interaction.start" :
160+ interaction = _get_field (event , "interaction" )
161+ interaction_id = _get_field (interaction , "id" )
139162
140- if event_name == "ContentDelta" and hasattr (event , "delta" ) and event .delta and hasattr (event .delta , "text" ):
141- text_parts .append (event .delta .text )
163+ if event_type == "content.delta" :
164+ text = _get_content_delta_text (event )
165+ if text :
166+ text_parts .append (text )
142167
143168 full_text = "" .join (text_parts )
144169 assert len (full_text ) > 0 , "Streaming should produce text"
145170 assert interaction_id is not None , "Should have received an interaction ID"
146171
147172 # Verify event sequence contains expected types
148- assert "InteractionStartEvent " in event_types
149- assert "ContentStart " in event_types
150- assert "ContentDelta " in event_types
151- assert "ContentStop " in event_types
152- assert "InteractionCompleteEvent " in event_types
173+ assert "interaction.start " in event_types
174+ assert "content.start " in event_types
175+ assert "content.delta " in event_types
176+ assert "content.stop " in event_types
177+ assert "interaction.complete " in event_types
153178
154179
155180def test_interactions_streaming_text_concatenation (genai_client , text_model_id ):
@@ -162,9 +187,10 @@ def test_interactions_streaming_text_concatenation(genai_client, text_model_id):
162187
163188 text_parts = []
164189 for event in stream :
165- if type (event ).__name__ == "ContentDelta" and hasattr (event , "delta" ) and event .delta :
166- if hasattr (event .delta , "text" ):
167- text_parts .append (event .delta .text )
190+ if _get_event_type (event ) == "content.delta" :
191+ text = _get_content_delta_text (event )
192+ if text :
193+ text_parts .append (text )
168194
169195 full_text = "" .join (text_parts )
170196 assert len (full_text ) > 0
@@ -179,23 +205,21 @@ def test_interactions_streaming_event_order(genai_client, text_model_id):
179205 )
180206
181207 events = list (stream )
182- event_names = [type (e ). __name__ for e in events ]
183- assert len (events ) >= 4 , f"Expected at least 4 events, got { len (events )} : { event_names } "
208+ event_types = [_get_event_type (e ) for e in events ]
209+ assert len (events ) >= 4 , f"Expected at least 4 events, got { len (events )} : { event_types } "
184210
185211 # Verify all required event types are present
186- required = ["InteractionStartEvent " , "ContentStart " , "ContentDelta " , "ContentStop " , "InteractionCompleteEvent " ]
212+ required = ["interaction.start " , "content.start " , "content.delta " , "content.stop " , "interaction.complete " ]
187213 for req in required :
188- assert req in event_names , f"Missing required event type { req } , got: { event_names } "
214+ assert req in event_types , f"Missing required event type { req } , got: { event_types } "
189215
190216 # Verify relative ordering: start before content, content before complete
191217 def _first (name ):
192- return event_names .index (name )
218+ return event_types .index (name )
193219
194- assert _first ("InteractionStartEvent" ) < _first ("ContentStart" ), "InteractionStartEvent should precede ContentStart"
195- assert _first ("ContentStart" ) < _first ("ContentDelta" ), "ContentStart should precede ContentDelta"
196- assert _first ("ContentStop" ) < _first ("InteractionCompleteEvent" ), (
197- "ContentStop should precede InteractionCompleteEvent"
198- )
220+ assert _first ("interaction.start" ) < _first ("content.start" ), "interaction.start should precede content.start"
221+ assert _first ("content.start" ) < _first ("content.delta" ), "content.start should precede content.delta"
222+ assert _first ("content.stop" ) < _first ("interaction.complete" ), "content.stop should precede interaction.complete"
199223
200224
201225def test_interactions_tool_calling_function_call_output (genai_client , text_model_id ):
@@ -226,15 +250,15 @@ def test_interactions_tool_calling_function_call_output(genai_client, text_model
226250 assert len (interaction .outputs ) > 0
227251
228252 # Model should produce a function_call output
229- function_calls = [o for o in interaction .outputs if o . type == "function_call" ]
253+ function_calls = [o for o in interaction .outputs if _get_field ( o , " type" ) == "function_call" ]
230254 if not function_calls :
231255 pytest .skip ("Model answered directly without calling the tool" )
232256
233257 fc = function_calls [0 ]
234- assert fc . name == "get_weather"
235- assert fc . id is not None
258+ assert _get_field ( fc , " name" ) == "get_weather"
259+ assert _get_field ( fc , "id" ) is not None
236260 # SDK uses 'arguments' attribute for function call args
237- fc_args = getattr (fc , "arguments" , None ) or getattr (fc , "args" , None ) or {}
261+ fc_args = _get_field (fc , "arguments" ) or _get_field (fc , "args" ) or {}
238262 assert isinstance (fc_args , dict )
239263
240264
@@ -269,12 +293,14 @@ def test_interactions_tool_calling_round_trip(genai_client, text_model_id):
269293 ],
270294 )
271295
272- function_calls = [o for o in interaction .outputs if o . type == "function_call" ]
296+ function_calls = [o for o in interaction .outputs if _get_field ( o , " type" ) == "function_call" ]
273297 if not function_calls :
274298 pytest .skip ("Model answered directly without calling the tool" )
275299
276300 fc = function_calls [0 ]
277- fc_args = getattr (fc , "arguments" , None ) or getattr (fc , "args" , None ) or {}
301+ fc_args = _get_field (fc , "arguments" ) or _get_field (fc , "args" ) or {}
302+ fc_id = _get_field (fc , "id" )
303+ fc_name = _get_field (fc , "name" )
278304
279305 # Step 2: Send function_response and get a final text answer
280306 interaction2 = genai_client .interactions .create (
@@ -286,8 +312,8 @@ def test_interactions_tool_calling_round_trip(genai_client, text_model_id):
286312 "content" : [
287313 {
288314 "type" : "function_call" ,
289- "id" : fc . id ,
290- "name" : fc . name ,
315+ "id" : fc_id ,
316+ "name" : fc_name ,
291317 "arguments" : fc_args ,
292318 }
293319 ],
@@ -297,8 +323,8 @@ def test_interactions_tool_calling_round_trip(genai_client, text_model_id):
297323 "content" : [
298324 {
299325 "type" : "function_result" ,
300- "call_id" : fc . id ,
301- "name" : fc . name ,
326+ "call_id" : fc_id ,
327+ "name" : fc_name ,
302328 "result" : {"temperature_celsius" : 22 , "condition" : "Cloudy" },
303329 }
304330 ],
@@ -324,7 +350,7 @@ def test_interactions_tool_calling_round_trip(genai_client, text_model_id):
324350 )
325351
326352 assert interaction2 .status == "completed"
327- text_outputs = [o for o in interaction2 .outputs if o . type == "text" ]
353+ text_outputs = [o for o in interaction2 .outputs if _get_field ( o , " type" ) == "text" ]
328354 assert len (text_outputs ) > 0 , "Expected a text response after providing function result"
329355
330356
@@ -366,10 +392,10 @@ def test_interactions_tool_calling_multiple_tools(genai_client, text_model_id):
366392 assert interaction .status in ("completed" , "requires_action" )
367393 assert len (interaction .outputs ) > 0
368394
369- function_calls = [o for o in interaction .outputs if o . type == "function_call" ]
395+ function_calls = [o for o in interaction .outputs if _get_field ( o , " type" ) == "function_call" ]
370396 if function_calls :
371397 # If the model called a tool, it should have picked get_time
372- assert function_calls [0 ]. name == "get_time"
398+ assert _get_field ( function_calls [0 ], " name" ) == "get_time"
373399
374400
375401def test_interactions_no_tools_no_function_call (genai_client , text_model_id ):
@@ -380,7 +406,7 @@ def test_interactions_no_tools_no_function_call(genai_client, text_model_id):
380406 )
381407
382408 assert interaction .status == "completed"
383- function_calls = [o for o in interaction .outputs if o . type == "function_call" ]
409+ function_calls = [o for o in interaction .outputs if _get_field ( o , " type" ) == "function_call" ]
384410 assert len (function_calls ) == 0 , "Should not produce function_call without tools"
385411
386412
0 commit comments