google-genai 1.56.0__py3-none-any.whl → 1.58.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. google/genai/_api_client.py +49 -26
  2. google/genai/_interactions/__init__.py +3 -0
  3. google/genai/_interactions/_base_client.py +1 -1
  4. google/genai/_interactions/_client.py +57 -3
  5. google/genai/_interactions/_client_adapter.py +48 -0
  6. google/genai/_interactions/types/__init__.py +6 -0
  7. google/genai/_interactions/types/audio_content.py +2 -0
  8. google/genai/_interactions/types/audio_content_param.py +2 -0
  9. google/genai/_interactions/types/content.py +65 -0
  10. google/genai/_interactions/types/content_delta.py +10 -2
  11. google/genai/_interactions/types/content_param.py +63 -0
  12. google/genai/_interactions/types/content_start.py +5 -46
  13. google/genai/_interactions/types/content_stop.py +1 -2
  14. google/genai/_interactions/types/document_content.py +2 -0
  15. google/genai/_interactions/types/document_content_param.py +2 -0
  16. google/genai/_interactions/types/error_event.py +1 -2
  17. google/genai/_interactions/types/file_search_call_content.py +32 -0
  18. google/genai/_interactions/types/file_search_call_content_param.py +31 -0
  19. google/genai/_interactions/types/generation_config.py +4 -0
  20. google/genai/_interactions/types/generation_config_param.py +4 -0
  21. google/genai/_interactions/types/image_config.py +31 -0
  22. google/genai/_interactions/types/image_config_param.py +30 -0
  23. google/genai/_interactions/types/image_content.py +2 -0
  24. google/genai/_interactions/types/image_content_param.py +2 -0
  25. google/genai/_interactions/types/interaction.py +6 -52
  26. google/genai/_interactions/types/interaction_create_params.py +4 -22
  27. google/genai/_interactions/types/interaction_event.py +1 -2
  28. google/genai/_interactions/types/interaction_sse_event.py +5 -3
  29. google/genai/_interactions/types/interaction_status_update.py +1 -2
  30. google/genai/_interactions/types/model.py +1 -0
  31. google/genai/_interactions/types/model_param.py +1 -0
  32. google/genai/_interactions/types/turn.py +3 -44
  33. google/genai/_interactions/types/turn_param.py +4 -40
  34. google/genai/_interactions/types/usage.py +1 -1
  35. google/genai/_interactions/types/usage_param.py +1 -1
  36. google/genai/_interactions/types/video_content.py +2 -0
  37. google/genai/_interactions/types/video_content_param.py +2 -0
  38. google/genai/_live_converters.py +118 -34
  39. google/genai/_local_tokenizer_loader.py +1 -0
  40. google/genai/_tokens_converters.py +14 -14
  41. google/genai/_transformers.py +15 -21
  42. google/genai/batches.py +27 -22
  43. google/genai/caches.py +42 -42
  44. google/genai/chats.py +0 -2
  45. google/genai/client.py +61 -55
  46. google/genai/files.py +224 -0
  47. google/genai/live.py +1 -1
  48. google/genai/models.py +56 -44
  49. google/genai/tests/__init__.py +21 -0
  50. google/genai/tests/afc/__init__.py +21 -0
  51. google/genai/tests/afc/test_convert_if_exist_pydantic_model.py +309 -0
  52. google/genai/tests/afc/test_convert_number_values_for_function_call_args.py +63 -0
  53. google/genai/tests/afc/test_find_afc_incompatible_tool_indexes.py +240 -0
  54. google/genai/tests/afc/test_generate_content_stream_afc.py +530 -0
  55. google/genai/tests/afc/test_generate_content_stream_afc_thoughts.py +77 -0
  56. google/genai/tests/afc/test_get_function_map.py +176 -0
  57. google/genai/tests/afc/test_get_function_response_parts.py +277 -0
  58. google/genai/tests/afc/test_get_max_remote_calls_for_afc.py +130 -0
  59. google/genai/tests/afc/test_invoke_function_from_dict_args.py +241 -0
  60. google/genai/tests/afc/test_raise_error_for_afc_incompatible_config.py +159 -0
  61. google/genai/tests/afc/test_should_append_afc_history.py +53 -0
  62. google/genai/tests/afc/test_should_disable_afc.py +214 -0
  63. google/genai/tests/batches/__init__.py +17 -0
  64. google/genai/tests/batches/test_cancel.py +77 -0
  65. google/genai/tests/batches/test_create.py +78 -0
  66. google/genai/tests/batches/test_create_with_bigquery.py +113 -0
  67. google/genai/tests/batches/test_create_with_file.py +82 -0
  68. google/genai/tests/batches/test_create_with_gcs.py +125 -0
  69. google/genai/tests/batches/test_create_with_inlined_requests.py +255 -0
  70. google/genai/tests/batches/test_delete.py +86 -0
  71. google/genai/tests/batches/test_embedding.py +157 -0
  72. google/genai/tests/batches/test_get.py +78 -0
  73. google/genai/tests/batches/test_list.py +79 -0
  74. google/genai/tests/caches/__init__.py +17 -0
  75. google/genai/tests/caches/constants.py +29 -0
  76. google/genai/tests/caches/test_create.py +210 -0
  77. google/genai/tests/caches/test_create_custom_url.py +105 -0
  78. google/genai/tests/caches/test_delete.py +54 -0
  79. google/genai/tests/caches/test_delete_custom_url.py +52 -0
  80. google/genai/tests/caches/test_get.py +94 -0
  81. google/genai/tests/caches/test_get_custom_url.py +52 -0
  82. google/genai/tests/caches/test_list.py +68 -0
  83. google/genai/tests/caches/test_update.py +70 -0
  84. google/genai/tests/caches/test_update_custom_url.py +58 -0
  85. google/genai/tests/chats/__init__.py +1 -0
  86. google/genai/tests/chats/test_get_history.py +598 -0
  87. google/genai/tests/chats/test_send_message.py +844 -0
  88. google/genai/tests/chats/test_validate_response.py +90 -0
  89. google/genai/tests/client/__init__.py +17 -0
  90. google/genai/tests/client/test_async_stream.py +427 -0
  91. google/genai/tests/client/test_client_close.py +197 -0
  92. google/genai/tests/client/test_client_initialization.py +1687 -0
  93. google/genai/tests/client/test_client_requests.py +221 -0
  94. google/genai/tests/client/test_custom_client.py +104 -0
  95. google/genai/tests/client/test_http_options.py +178 -0
  96. google/genai/tests/client/test_replay_client_equality.py +168 -0
  97. google/genai/tests/client/test_retries.py +846 -0
  98. google/genai/tests/client/test_upload_errors.py +136 -0
  99. google/genai/tests/common/__init__.py +17 -0
  100. google/genai/tests/common/test_common.py +954 -0
  101. google/genai/tests/conftest.py +162 -0
  102. google/genai/tests/documents/__init__.py +17 -0
  103. google/genai/tests/documents/test_delete.py +51 -0
  104. google/genai/tests/documents/test_get.py +85 -0
  105. google/genai/tests/documents/test_list.py +72 -0
  106. google/genai/tests/errors/__init__.py +1 -0
  107. google/genai/tests/errors/test_api_error.py +417 -0
  108. google/genai/tests/file_search_stores/__init__.py +17 -0
  109. google/genai/tests/file_search_stores/test_create.py +66 -0
  110. google/genai/tests/file_search_stores/test_delete.py +64 -0
  111. google/genai/tests/file_search_stores/test_get.py +94 -0
  112. google/genai/tests/file_search_stores/test_import_file.py +112 -0
  113. google/genai/tests/file_search_stores/test_list.py +57 -0
  114. google/genai/tests/file_search_stores/test_upload_to_file_search_store.py +141 -0
  115. google/genai/tests/files/__init__.py +17 -0
  116. google/genai/tests/files/test_delete.py +46 -0
  117. google/genai/tests/files/test_download.py +85 -0
  118. google/genai/tests/files/test_get.py +46 -0
  119. google/genai/tests/files/test_list.py +72 -0
  120. google/genai/tests/files/test_register.py +272 -0
  121. google/genai/tests/files/test_register_table.py +70 -0
  122. google/genai/tests/files/test_upload.py +255 -0
  123. google/genai/tests/imports/test_no_optional_imports.py +28 -0
  124. google/genai/tests/interactions/test_auth.py +476 -0
  125. google/genai/tests/interactions/test_integration.py +84 -0
  126. google/genai/tests/interactions/test_paths.py +105 -0
  127. google/genai/tests/live/__init__.py +16 -0
  128. google/genai/tests/live/test_live.py +2143 -0
  129. google/genai/tests/live/test_live_music.py +362 -0
  130. google/genai/tests/live/test_live_response.py +163 -0
  131. google/genai/tests/live/test_send_client_content.py +147 -0
  132. google/genai/tests/live/test_send_realtime_input.py +268 -0
  133. google/genai/tests/live/test_send_tool_response.py +222 -0
  134. google/genai/tests/local_tokenizer/__init__.py +17 -0
  135. google/genai/tests/local_tokenizer/test_local_tokenizer.py +343 -0
  136. google/genai/tests/local_tokenizer/test_local_tokenizer_loader.py +235 -0
  137. google/genai/tests/mcp/__init__.py +17 -0
  138. google/genai/tests/mcp/test_has_mcp_tool_usage.py +89 -0
  139. google/genai/tests/mcp/test_mcp_to_gemini_tools.py +191 -0
  140. google/genai/tests/mcp/test_parse_config_for_mcp_sessions.py +201 -0
  141. google/genai/tests/mcp/test_parse_config_for_mcp_usage.py +130 -0
  142. google/genai/tests/mcp/test_set_mcp_usage_header.py +72 -0
  143. google/genai/tests/models/__init__.py +17 -0
  144. google/genai/tests/models/constants.py +8 -0
  145. google/genai/tests/models/test_compute_tokens.py +120 -0
  146. google/genai/tests/models/test_count_tokens.py +159 -0
  147. google/genai/tests/models/test_delete.py +107 -0
  148. google/genai/tests/models/test_edit_image.py +264 -0
  149. google/genai/tests/models/test_embed_content.py +94 -0
  150. google/genai/tests/models/test_function_call_streaming.py +442 -0
  151. google/genai/tests/models/test_generate_content.py +2501 -0
  152. google/genai/tests/models/test_generate_content_cached_content.py +132 -0
  153. google/genai/tests/models/test_generate_content_config_zero_value.py +103 -0
  154. google/genai/tests/models/test_generate_content_from_apikey.py +44 -0
  155. google/genai/tests/models/test_generate_content_http_options.py +40 -0
  156. google/genai/tests/models/test_generate_content_image_generation.py +143 -0
  157. google/genai/tests/models/test_generate_content_mcp.py +343 -0
  158. google/genai/tests/models/test_generate_content_media_resolution.py +97 -0
  159. google/genai/tests/models/test_generate_content_model.py +139 -0
  160. google/genai/tests/models/test_generate_content_part.py +821 -0
  161. google/genai/tests/models/test_generate_content_thought.py +76 -0
  162. google/genai/tests/models/test_generate_content_tools.py +1761 -0
  163. google/genai/tests/models/test_generate_images.py +191 -0
  164. google/genai/tests/models/test_generate_videos.py +759 -0
  165. google/genai/tests/models/test_get.py +104 -0
  166. google/genai/tests/models/test_list.py +233 -0
  167. google/genai/tests/models/test_recontext_image.py +189 -0
  168. google/genai/tests/models/test_segment_image.py +148 -0
  169. google/genai/tests/models/test_update.py +95 -0
  170. google/genai/tests/models/test_upscale_image.py +157 -0
  171. google/genai/tests/operations/__init__.py +17 -0
  172. google/genai/tests/operations/test_get.py +38 -0
  173. google/genai/tests/public_samples/__init__.py +17 -0
  174. google/genai/tests/public_samples/test_gemini_text_only.py +34 -0
  175. google/genai/tests/pytest_helper.py +246 -0
  176. google/genai/tests/shared/__init__.py +16 -0
  177. google/genai/tests/shared/batches/__init__.py +14 -0
  178. google/genai/tests/shared/batches/test_create_delete.py +57 -0
  179. google/genai/tests/shared/batches/test_create_get_cancel.py +56 -0
  180. google/genai/tests/shared/batches/test_list.py +40 -0
  181. google/genai/tests/shared/caches/__init__.py +14 -0
  182. google/genai/tests/shared/caches/test_create_get_delete.py +67 -0
  183. google/genai/tests/shared/caches/test_create_update_get.py +71 -0
  184. google/genai/tests/shared/caches/test_list.py +40 -0
  185. google/genai/tests/shared/chats/__init__.py +14 -0
  186. google/genai/tests/shared/chats/test_send_message.py +48 -0
  187. google/genai/tests/shared/chats/test_send_message_stream.py +50 -0
  188. google/genai/tests/shared/files/__init__.py +14 -0
  189. google/genai/tests/shared/files/test_list.py +41 -0
  190. google/genai/tests/shared/files/test_upload_get_delete.py +54 -0
  191. google/genai/tests/shared/models/__init__.py +14 -0
  192. google/genai/tests/shared/models/test_compute_tokens.py +41 -0
  193. google/genai/tests/shared/models/test_count_tokens.py +40 -0
  194. google/genai/tests/shared/models/test_edit_image.py +67 -0
  195. google/genai/tests/shared/models/test_embed.py +40 -0
  196. google/genai/tests/shared/models/test_generate_content.py +39 -0
  197. google/genai/tests/shared/models/test_generate_content_stream.py +54 -0
  198. google/genai/tests/shared/models/test_generate_images.py +40 -0
  199. google/genai/tests/shared/models/test_generate_videos.py +38 -0
  200. google/genai/tests/shared/models/test_list.py +37 -0
  201. google/genai/tests/shared/models/test_recontext_image.py +55 -0
  202. google/genai/tests/shared/models/test_segment_image.py +52 -0
  203. google/genai/tests/shared/models/test_upscale_image.py +52 -0
  204. google/genai/tests/shared/tunings/__init__.py +16 -0
  205. google/genai/tests/shared/tunings/test_create.py +46 -0
  206. google/genai/tests/shared/tunings/test_create_get_cancel.py +56 -0
  207. google/genai/tests/shared/tunings/test_list.py +39 -0
  208. google/genai/tests/tokens/__init__.py +16 -0
  209. google/genai/tests/tokens/test_create.py +154 -0
  210. google/genai/tests/transformers/__init__.py +17 -0
  211. google/genai/tests/transformers/test_blobs.py +84 -0
  212. google/genai/tests/transformers/test_bytes.py +15 -0
  213. google/genai/tests/transformers/test_duck_type.py +96 -0
  214. google/genai/tests/transformers/test_function_responses.py +72 -0
  215. google/genai/tests/transformers/test_schema.py +653 -0
  216. google/genai/tests/transformers/test_t_batch.py +286 -0
  217. google/genai/tests/transformers/test_t_content.py +160 -0
  218. google/genai/tests/transformers/test_t_contents.py +398 -0
  219. google/genai/tests/transformers/test_t_part.py +85 -0
  220. google/genai/tests/transformers/test_t_parts.py +87 -0
  221. google/genai/tests/transformers/test_t_tool.py +157 -0
  222. google/genai/tests/transformers/test_t_tools.py +195 -0
  223. google/genai/tests/tunings/__init__.py +16 -0
  224. google/genai/tests/tunings/test_cancel.py +39 -0
  225. google/genai/tests/tunings/test_end_to_end.py +106 -0
  226. google/genai/tests/tunings/test_get.py +67 -0
  227. google/genai/tests/tunings/test_list.py +75 -0
  228. google/genai/tests/tunings/test_tune.py +268 -0
  229. google/genai/tests/types/__init__.py +16 -0
  230. google/genai/tests/types/test_bytes_internal.py +271 -0
  231. google/genai/tests/types/test_bytes_type.py +152 -0
  232. google/genai/tests/types/test_future.py +101 -0
  233. google/genai/tests/types/test_optional_types.py +36 -0
  234. google/genai/tests/types/test_part_type.py +616 -0
  235. google/genai/tests/types/test_schema_from_json_schema.py +417 -0
  236. google/genai/tests/types/test_schema_json_schema.py +468 -0
  237. google/genai/tests/types/test_types.py +2903 -0
  238. google/genai/types.py +631 -488
  239. google/genai/version.py +1 -1
  240. {google_genai-1.56.0.dist-info → google_genai-1.58.0.dist-info}/METADATA +6 -11
  241. google_genai-1.58.0.dist-info/RECORD +358 -0
  242. google_genai-1.56.0.dist-info/RECORD +0 -162
  243. /google/genai/{_interactions/py.typed → tests/interactions/__init__.py} +0 -0
  244. {google_genai-1.56.0.dist-info → google_genai-1.58.0.dist-info}/WHEEL +0 -0
  245. {google_genai-1.56.0.dist-info → google_genai-1.58.0.dist-info}/licenses/LICENSE +0 -0
  246. {google_genai-1.56.0.dist-info → google_genai-1.58.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,530 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ from unittest import mock
16
+ import pytest
17
+ from ... import _api_client
18
+ from ... import _extra_utils
19
+ from ... import client
20
+ from ... import models
21
+ from ... import types
22
+
23
+
24
+ TEST_NO_AFC_PART = types.Part(
25
+ text=(
26
+ 'Okay, here is the weather in San Francisco'
27
+ ' as of approximately 8:10 pm PST on May'
28
+ ' 10, 2023. Please note that the weather'
29
+ ' can change rapidly.'
30
+ )
31
+ )
32
+
33
+ TEST_FUNCTION_CALL_PART = types.Part(
34
+ function_call=types.FunctionCall(
35
+ name='get_current_weather',
36
+ args={'location': 'San Francisco'},
37
+ )
38
+ )
39
+
40
+ TEST_FUNCTION_RESPONSE_PART = types.Part(
41
+ function_response=types.FunctionResponse(
42
+ name='get_current_weather',
43
+ response={'result': 'sunny'},
44
+ )
45
+ )
46
+
47
+ TEST_AFC_TEXT_PART = types.Part(text='San Francisco weather is sunny.')
48
+
49
+ TEST_NO_AFC_CONTENT = types.Content(
50
+ parts=[TEST_NO_AFC_PART],
51
+ role='model',
52
+ )
53
+
54
+ TEST_FUNCTION_CALL_CONTENT = types.Content(
55
+ parts=[TEST_FUNCTION_CALL_PART],
56
+ role='model',
57
+ )
58
+
59
+ TEST_FUNCTION_RESPONSE_CONTENT = types.Content(
60
+ parts=[TEST_FUNCTION_RESPONSE_PART],
61
+ role='user',
62
+ )
63
+
64
+ TEST_AFC_TEXT_CONTENT = types.Content(
65
+ parts=[TEST_AFC_TEXT_PART],
66
+ role='model',
67
+ )
68
+
69
+ TEST_AFC_HISTORY = [
70
+ types.Content(
71
+ parts=[types.Part(text='what is the weather in San Francisco?')],
72
+ role='user',
73
+ ),
74
+ TEST_FUNCTION_CALL_CONTENT,
75
+ TEST_FUNCTION_RESPONSE_CONTENT,
76
+ ]
77
+
78
+
79
+ def get_current_weather(location: str) -> str:
80
+ """Returns the current weather.
81
+
82
+ Args:
83
+ location: The location of a city and state, e.g. "San Francisco, CA".
84
+ """
85
+ return 'windy'
86
+
87
+
88
+ async def get_current_weather_async(location: str) -> str:
89
+ """Returns the current weather.
90
+
91
+ Args:
92
+ location: The location of a city and state, e.g. "San Francisco, CA".
93
+ """
94
+ return 'windy'
95
+
96
+
97
+ def get_aqi_from_city(location: str) -> str:
98
+ """Returns the aqi index of a city.
99
+
100
+ Args:
101
+ location: The city and State, e.g. San Francisco, CA.
102
+ """
103
+ return None
104
+
105
+
106
+ @pytest.fixture
107
+ def mock_api_client(vertexai=False):
108
+ api_client = mock.MagicMock(spec=client.ApiClient)
109
+ api_client.api_key = 'TEST_API_KEY'
110
+ api_client._host = lambda: 'test_host'
111
+ api_client._http_options = {'headers': {}} # Ensure headers exist
112
+ api_client.vertexai = vertexai
113
+ return api_client
114
+
115
+
116
+ @pytest.fixture
117
+ def mock_get_function_response_parts_none():
118
+ with mock.patch.object(
119
+ _extra_utils,
120
+ 'get_function_response_parts',
121
+ ) as mock_get_function_response_parts_none:
122
+ mock_get_function_response_parts_none.return_value = None
123
+ yield mock_get_function_response_parts_none
124
+
125
+
126
+ @pytest.fixture
127
+ def mock_get_function_response_parts_none_async():
128
+ with mock.patch.object(
129
+ _extra_utils,
130
+ 'get_function_response_parts_async',
131
+ ) as mock_get_function_response_parts_none_async:
132
+ mock_get_function_response_parts_none_async.return_value = None
133
+ yield mock_get_function_response_parts_none_async
134
+
135
+
136
+ @pytest.fixture
137
+ def mock_get_function_response_parts() -> list[types.Part]:
138
+ with mock.patch.object(
139
+ _extra_utils, 'get_function_response_parts'
140
+ ) as mock_get_function_response_parts:
141
+ mock_get_function_response_parts.side_effect = [
142
+ [TEST_FUNCTION_RESPONSE_PART],
143
+ [], # Breaks when the function response is not returned.
144
+ ]
145
+ yield mock_get_function_response_parts
146
+
147
+
148
+ @pytest.fixture
149
+ def mock_get_function_response_parts_async() -> list[types.Part]:
150
+ with mock.patch.object(
151
+ _extra_utils, 'get_function_response_parts_async'
152
+ ) as mock_get_function_response_parts_async:
153
+ mock_get_function_response_parts_async.side_effect = [
154
+ [TEST_FUNCTION_RESPONSE_PART],
155
+ [], # Breaks when the function response is not returned.
156
+ ]
157
+ yield mock_get_function_response_parts_async
158
+
159
+
160
+ @pytest.fixture
161
+ def mock_generate_content_stream_no_afc():
162
+ with mock.patch.object(
163
+ models.Models, '_generate_content_stream'
164
+ ) as mock_stream_no_afc:
165
+ mock_stream_no_afc.return_value = [
166
+ types.GenerateContentResponse(
167
+ candidates=[types.Candidate(content=TEST_NO_AFC_CONTENT)]
168
+ )
169
+ ]
170
+ yield mock_stream_no_afc
171
+
172
+
173
+ @pytest.fixture
174
+ def mock_generate_content_stream_with_afc():
175
+ with mock.patch.object(
176
+ models.Models, '_generate_content_stream'
177
+ ) as mock_stream_with_afc:
178
+ mock_stream_with_afc.side_effect = [
179
+ [
180
+ types.GenerateContentResponse(
181
+ candidates=[types.Candidate(content=TEST_FUNCTION_CALL_CONTENT)]
182
+ )
183
+ ],
184
+ [
185
+ types.GenerateContentResponse(
186
+ candidates=[types.Candidate(content=TEST_AFC_TEXT_CONTENT)]
187
+ )
188
+ ],
189
+ ]
190
+ yield mock_stream_with_afc
191
+
192
+
193
+ @pytest.fixture
194
+ def mock_generate_content_stream_no_afc_async():
195
+ with mock.patch.object(
196
+ models.AsyncModels, '_generate_content_stream'
197
+ ) as mock_stream_no_afc:
198
+
199
+ async def async_generator():
200
+ yield types.GenerateContentResponse(
201
+ candidates=[types.Candidate(content=TEST_NO_AFC_CONTENT)]
202
+ )
203
+
204
+ mock_stream_no_afc.return_value = async_generator()
205
+ yield mock_stream_no_afc
206
+
207
+
208
+ @pytest.fixture
209
+ def mock_generate_content_stream_with_afc_async():
210
+ with mock.patch.object(
211
+ models.AsyncModels, '_generate_content_stream'
212
+ ) as mock_stream_with_afc:
213
+
214
+ async def async_generator_1():
215
+ yield types.GenerateContentResponse(
216
+ candidates=[types.Candidate(content=TEST_FUNCTION_CALL_CONTENT)]
217
+ )
218
+
219
+ async def async_generator_2():
220
+ yield types.GenerateContentResponse(
221
+ candidates=[types.Candidate(content=TEST_AFC_TEXT_CONTENT)]
222
+ )
223
+
224
+ mock_stream_with_afc.side_effect = [
225
+ async_generator_1(),
226
+ async_generator_2(),
227
+ ]
228
+ yield mock_stream_with_afc
229
+
230
+
231
+ def test_generate_content_stream_no_function_map(
232
+ mock_generate_content_stream_no_afc,
233
+ mock_get_function_response_parts_none,
234
+ ):
235
+ """Test when no function tools are provided.
236
+
237
+ Expected to answer past weather.
238
+ """
239
+ models_instance = models.Models(api_client_=mock_api_client)
240
+ stream = models_instance.generate_content_stream(
241
+ model='test_model', contents='what is the weather in San Francisco?'
242
+ )
243
+ for chunk in stream:
244
+ assert chunk.text == TEST_NO_AFC_PART.text
245
+
246
+ assert mock_generate_content_stream_no_afc.call_count == 1
247
+ assert mock_get_function_response_parts_none.call_count == 0
248
+
249
+
250
+ def test_generate_content_stream_afc_disabled(
251
+ mock_generate_content_stream_with_afc,
252
+ mock_get_function_response_parts_none,
253
+ ):
254
+ """Test when function tools are provided but AFC is disabled.
255
+
256
+ Expected to respond with function call.
257
+ """
258
+ models_instance = models.Models(api_client_=mock_api_client)
259
+ stream = models_instance.generate_content_stream(
260
+ model='test_model',
261
+ contents='what is the weather in San Francisco?',
262
+ config=types.GenerateContentConfig(
263
+ tools=[get_current_weather],
264
+ automatic_function_calling=types.AutomaticFunctionCallingConfig(
265
+ disable=True
266
+ ),
267
+ ),
268
+ )
269
+ for chunk in stream:
270
+ # Work as manual function calling.
271
+ assert chunk.candidates[0].content.parts[0].function_call
272
+
273
+ assert mock_generate_content_stream_with_afc.call_count == 1
274
+ assert mock_get_function_response_parts_none.call_count == 0
275
+
276
+
277
+ def test_generate_content_stream_no_function_response(
278
+ mock_generate_content_stream_no_afc,
279
+ mock_get_function_response_parts_none,
280
+ ):
281
+ """Test when function tools are provided and function responses are not returned.
282
+
283
+ Expected to answer past weather.
284
+ """
285
+ models_instance = models.Models(api_client_=mock_api_client)
286
+ config = types.GenerateContentConfig(tools=[get_aqi_from_city])
287
+ stream = models_instance.generate_content_stream(
288
+ model='test_model',
289
+ contents='what is the weather in San Francisco?',
290
+ config=config,
291
+ )
292
+ for chunk in stream:
293
+ assert chunk.text == TEST_NO_AFC_PART.text
294
+
295
+ assert mock_generate_content_stream_no_afc.call_count == 1
296
+ assert mock_get_function_response_parts_none.call_count == 1
297
+
298
+
299
+ def test_generate_content_stream_with_function_tools_used(
300
+ mock_generate_content_stream_with_afc,
301
+ mock_get_function_response_parts,
302
+ ):
303
+ """Test when function tools are provided and function responses are returned.
304
+
305
+ Expected to answer weather based on function response.
306
+ """
307
+ models_instance = models.Models(api_client_=mock_api_client)
308
+ config = types.GenerateContentConfig(tools=[get_current_weather])
309
+ stream = models_instance.generate_content_stream(
310
+ model='test_model',
311
+ contents='what is the weather in San Francisco?',
312
+ config=config,
313
+ )
314
+
315
+ chunk = None
316
+ for chunk in stream:
317
+ assert chunk.text == TEST_AFC_TEXT_PART.text
318
+
319
+ assert mock_generate_content_stream_with_afc.call_count == 2
320
+ assert mock_get_function_response_parts.call_count == 2
321
+
322
+ assert chunk is not None
323
+ for i in range(len(chunk.automatic_function_calling_history)):
324
+ assert chunk.automatic_function_calling_history[i].model_dump(
325
+ exclude_none=True
326
+ ) == TEST_AFC_HISTORY[i].model_dump(exclude_none=True)
327
+
328
+
329
+ def test_generate_content_stream_with_thought_summaries(
330
+ mock_generate_content_stream_with_afc,
331
+ mock_get_function_response_parts,
332
+ ):
333
+ """Test when function tools are provided and thought summaries are enabled.
334
+
335
+ Expected to answer weather based on function response.
336
+ """
337
+ models_instance = models.Models(api_client_=mock_api_client)
338
+ config = types.GenerateContentConfig(
339
+ tools=[get_current_weather],
340
+ thinking_config=types.ThinkingConfig(include_thoughts=True),
341
+ )
342
+ stream = models_instance.generate_content_stream(
343
+ model='test_model',
344
+ contents='what is the weather in San Francisco?',
345
+ config=config,
346
+ )
347
+
348
+ chunk = None
349
+ for chunk in stream:
350
+ assert chunk.text == TEST_AFC_TEXT_PART.text
351
+
352
+ assert mock_generate_content_stream_with_afc.call_count == 2
353
+ assert mock_get_function_response_parts.call_count == 2
354
+
355
+ assert chunk is not None
356
+ for i in range(len(chunk.automatic_function_calling_history)):
357
+ assert chunk.automatic_function_calling_history[i].model_dump(
358
+ exclude_none=True
359
+ ) == TEST_AFC_HISTORY[i].model_dump(exclude_none=True)
360
+
361
+
362
+ @pytest.mark.asyncio
363
+ async def test_generate_content_stream_no_function_map_async(
364
+ mock_generate_content_stream_no_afc,
365
+ mock_get_function_response_parts_none,
366
+ ):
367
+ """Test when no function tools are provided.
368
+
369
+ Expected to answer past weather.
370
+ """
371
+ models_instance = models.Models(api_client_=mock_api_client)
372
+ stream = models_instance.generate_content_stream(
373
+ model='test_model', contents='what is the weather in San Francisco?'
374
+ )
375
+ for chunk in stream:
376
+ assert chunk.text == TEST_NO_AFC_PART.text
377
+
378
+ assert mock_generate_content_stream_no_afc.call_count == 1
379
+ assert mock_get_function_response_parts_none.call_count == 0
380
+
381
+
382
+ @pytest.mark.asyncio
383
+ async def test_generate_content_stream_afc_disabled_async(
384
+ mock_generate_content_stream_with_afc_async,
385
+ mock_get_function_response_parts_none,
386
+ ):
387
+ """Test when function tools are provided but AFC is disabled.
388
+
389
+ Expected to respond with function call.
390
+ """
391
+ models_instance = models.AsyncModels(api_client_=mock_api_client)
392
+ stream = await models_instance.generate_content_stream(
393
+ model='test_model',
394
+ contents='what is the weather in San Francisco?',
395
+ config=types.GenerateContentConfig(
396
+ tools=[get_current_weather],
397
+ automatic_function_calling=types.AutomaticFunctionCallingConfig(
398
+ disable=True
399
+ ),
400
+ ),
401
+ )
402
+ async for chunk in stream:
403
+ # Work as manual function calling.
404
+ assert chunk.candidates[0].content.parts[0].function_call
405
+
406
+ assert mock_generate_content_stream_with_afc_async.call_count == 1
407
+ assert mock_get_function_response_parts_none.call_count == 0
408
+
409
+
410
+ @pytest.mark.asyncio
411
+ async def test_generate_content_stream_no_function_response_async(
412
+ mock_generate_content_stream_no_afc_async,
413
+ mock_get_function_response_parts_none_async,
414
+ ):
415
+ """Test when function tools are provided and function responses are not returned.
416
+
417
+ Expected to answer past weather.
418
+ """
419
+ models_instance = models.AsyncModels(api_client_=mock_api_client)
420
+ config = types.GenerateContentConfig(tools=[get_aqi_from_city])
421
+ stream = await models_instance.generate_content_stream(
422
+ model='test_model',
423
+ contents='what is the weather in San Francisco?',
424
+ config=config,
425
+ )
426
+ async for chunk in stream:
427
+ assert chunk.text == TEST_NO_AFC_PART.text
428
+
429
+ assert mock_generate_content_stream_no_afc_async.call_count == 1
430
+
431
+ assert mock_get_function_response_parts_none_async.call_count == 1
432
+
433
+
434
+ @pytest.mark.asyncio
435
+ async def test_generate_content_stream_with_function_tools_used_async(
436
+ mock_generate_content_stream_with_afc_async,
437
+ mock_get_function_response_parts_async,
438
+ ):
439
+ """Test when function tools are provided and function responses are returned.
440
+
441
+ Expected to answer weather based on function response.
442
+ """
443
+ models_instance = models.AsyncModels(api_client_=mock_api_client)
444
+ config = types.GenerateContentConfig(tools=[get_current_weather])
445
+ stream = await models_instance.generate_content_stream(
446
+ model='test_model',
447
+ contents='what is the weather in San Francisco?',
448
+ config=config,
449
+ )
450
+
451
+ chunk = None
452
+ async for chunk in stream:
453
+ assert chunk.text == TEST_AFC_TEXT_PART.text
454
+
455
+ assert mock_generate_content_stream_with_afc_async.call_count == 2
456
+
457
+ assert mock_get_function_response_parts_async.call_count == 2
458
+
459
+ assert chunk is not None
460
+ for i in range(len(chunk.automatic_function_calling_history)):
461
+ assert chunk.automatic_function_calling_history[i].model_dump(
462
+ exclude_none=True
463
+ ) == TEST_AFC_HISTORY[i].model_dump(exclude_none=True)
464
+
465
+
466
+ @pytest.mark.asyncio
467
+ async def test_generate_content_stream_with_function_async_function_used_async(
468
+ mock_generate_content_stream_with_afc_async,
469
+ mock_get_function_response_parts_async,
470
+ ):
471
+ """Test when function tools are provided and function responses are returned.
472
+
473
+ Expected to answer weather based on function response.
474
+ """
475
+ models_instance = models.AsyncModels(api_client_=mock_api_client)
476
+ config = types.GenerateContentConfig(tools=[get_current_weather_async])
477
+ stream = await models_instance.generate_content_stream(
478
+ model='test_model',
479
+ contents='what is the weather in San Francisco?',
480
+ config=config,
481
+ )
482
+
483
+ chunk = None
484
+ async for chunk in stream:
485
+ assert chunk.text == TEST_AFC_TEXT_PART.text
486
+
487
+ assert mock_generate_content_stream_with_afc_async.call_count == 2
488
+
489
+ assert mock_get_function_response_parts_async.call_count == 2
490
+
491
+ assert chunk is not None
492
+ for i in range(len(chunk.automatic_function_calling_history)):
493
+ assert chunk.automatic_function_calling_history[i].model_dump(
494
+ exclude_none=True
495
+ ) == TEST_AFC_HISTORY[i].model_dump(exclude_none=True)
496
+
497
+
498
+ @pytest.mark.asyncio
499
+ async def test_generate_content_stream_with_thought_summaries_async(
500
+ mock_generate_content_stream_with_afc_async,
501
+ mock_get_function_response_parts_async,
502
+ ):
503
+ """Test when function tools are provided and thought summaries are enabled.
504
+
505
+ Expected to answer weather based on function response.
506
+ """
507
+ models_instance = models.AsyncModels(api_client_=mock_api_client)
508
+ config = types.GenerateContentConfig(
509
+ tools=[get_current_weather],
510
+ thinking_config=types.ThinkingConfig(include_thoughts=True),
511
+ )
512
+ stream = await models_instance.generate_content_stream(
513
+ model='test_model',
514
+ contents='what is the weather in San Francisco?',
515
+ config=config,
516
+ )
517
+
518
+ chunk = None
519
+ async for chunk in stream:
520
+ assert chunk.text == TEST_AFC_TEXT_PART.text
521
+
522
+ assert mock_generate_content_stream_with_afc_async.call_count == 2
523
+
524
+ assert mock_get_function_response_parts_async.call_count == 2
525
+
526
+ assert chunk is not None
527
+ for i in range(len(chunk.automatic_function_calling_history)):
528
+ assert chunk.automatic_function_calling_history[i].model_dump(
529
+ exclude_none=True
530
+ ) == TEST_AFC_HISTORY[i].model_dump(exclude_none=True)
@@ -0,0 +1,77 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ import pytest
16
+ from ... import types
17
+ from .. import pytest_helper
18
+
19
+
20
+ def get_current_weather(location: str) -> str:
21
+ """Returns the current weather.
22
+
23
+ Args:
24
+ location: The location of a city and state, e.g. "San Francisco, CA".
25
+ """
26
+ return 'windy'
27
+
28
+
29
+ pytestmark = pytest_helper.setup(
30
+ file=__file__,
31
+ globals_for_file=globals(),
32
+ test_method='models.generate_content',
33
+ )
34
+ pytest_plugins = ('pytest_asyncio',)
35
+
36
+
37
+ def test_generate_content_stream_with_function_and_thought_summaries(client):
38
+ """Test when function tools are provided and thought summaries are enabled.
39
+
40
+ Expected to answer weather based on function response.
41
+ """
42
+ config = types.GenerateContentConfig(
43
+ tools=[get_current_weather],
44
+ thinking_config=types.ThinkingConfig(include_thoughts=True),
45
+ )
46
+ stream = client.models.generate_content_stream(
47
+ model='gemini-2.5-flash',
48
+ contents='what is the weather in San Francisco, CA?',
49
+ config=config,
50
+ )
51
+
52
+ chunk = None
53
+ for chunk in stream:
54
+ assert chunk is not None
55
+
56
+
57
+ @pytest.mark.asyncio
58
+ async def test_generate_content_stream_with_function_and_thought_summaries_async(
59
+ client,
60
+ ):
61
+ """Test when function tools are provided and thought summaries are enabled.
62
+
63
+ Expected to answer weather based on function response.
64
+ """
65
+ config = types.GenerateContentConfig(
66
+ tools=[get_current_weather],
67
+ thinking_config=types.ThinkingConfig(include_thoughts=True),
68
+ )
69
+ stream = await client.aio.models.generate_content_stream(
70
+ model='gemini-2.5-flash',
71
+ contents='what is the weather in San Francisco, CA?',
72
+ config=config,
73
+ )
74
+
75
+ chunk = None
76
+ async for chunk in stream:
77
+ assert chunk is not None