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,598 @@
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
+
16
+
17
+ from unittest import mock
18
+
19
+ import pytest
20
+
21
+ from ... import chats
22
+ from ... import client
23
+ from ... import models
24
+ from ... import types
25
+
26
+ AFC_HISTORY = [
27
+ types.Content(
28
+ role='user',
29
+ parts=[types.Part.from_text(text='afc input')],
30
+ ),
31
+ types.Content(
32
+ role='model',
33
+ parts=[
34
+ types.Part(
35
+ function_call=types.FunctionCall(
36
+ name='foo', args={'bar': 'baz'}
37
+ )
38
+ )
39
+ ],
40
+ ),
41
+ ]
42
+
43
+
44
+ pytest_plugins = 'pytest_asyncio'
45
+
46
+
47
+ @pytest.fixture
48
+ def mock_api_client(vertexai=False):
49
+ api_client = mock.MagicMock(spec=client.ApiClient)
50
+ api_client.api_key = 'TEST_API_KEY'
51
+ api_client._host = lambda: 'test_host'
52
+ api_client._http_options = {'headers': {}} # Ensure headers exist
53
+ api_client.vertexai = vertexai
54
+ return api_client
55
+
56
+
57
+ @pytest.fixture
58
+ def mock_generate_content_with_empty_text_part():
59
+ with mock.patch.object(
60
+ models.Models, 'generate_content'
61
+ ) as mock_generate_content:
62
+ mock_generate_content.return_value = types.GenerateContentResponse(
63
+ candidates=[
64
+ types.Candidate(
65
+ content=types.Content(
66
+ role='model',
67
+ parts=[types.Part(text='')],
68
+ )
69
+ )
70
+ ]
71
+ )
72
+ yield mock_generate_content
73
+
74
+
75
+ @pytest.fixture
76
+ def mock_generate_content_empty_content():
77
+ with mock.patch.object(
78
+ models.Models, 'generate_content'
79
+ ) as mock_generate_content:
80
+ mock_generate_content.return_value = types.GenerateContentResponse(
81
+ candidates=[]
82
+ )
83
+ yield mock_generate_content
84
+
85
+
86
+ @pytest.fixture
87
+ def mock_generate_content_stream_with_empty_text_part():
88
+ with mock.patch.object(
89
+ models.Models, 'generate_content_stream'
90
+ ) as mock_generate_content:
91
+ mock_generate_content.return_value = [
92
+ types.GenerateContentResponse(
93
+ candidates=[
94
+ types.Candidate(
95
+ content=types.Content(
96
+ role='model',
97
+ parts=[types.Part(text='')],
98
+ ),
99
+ finish_reason=types.FinishReason.STOP,
100
+ )
101
+ ]
102
+ )
103
+ ]
104
+ yield mock_generate_content
105
+
106
+
107
+ @pytest.fixture
108
+ def mock_generate_content_stream_empty_content():
109
+ with mock.patch.object(
110
+ models.Models, 'generate_content_stream'
111
+ ) as mock_generate_content:
112
+ mock_generate_content.return_value = [
113
+ types.GenerateContentResponse(candidates=[])
114
+ ]
115
+ yield mock_generate_content
116
+
117
+
118
+ @pytest.fixture
119
+ def mock_generate_content_afc_history():
120
+ with mock.patch.object(
121
+ models.Models, 'generate_content'
122
+ ) as mock_generate_content:
123
+ mock_generate_content.return_value = types.GenerateContentResponse(
124
+ candidates=[
125
+ types.Candidate(
126
+ content=types.Content(
127
+ role='model',
128
+ parts=[types.Part.from_text(text='afc output')],
129
+ )
130
+ )
131
+ ],
132
+ automatic_function_calling_history=AFC_HISTORY,
133
+ )
134
+ yield mock_generate_content
135
+
136
+
137
+ @pytest.fixture
138
+ def mock_generate_content_stream_afc_history():
139
+ with mock.patch.object(
140
+ models.Models, 'generate_content_stream'
141
+ ) as mock_generate_content:
142
+ mock_generate_content.return_value = [
143
+ types.GenerateContentResponse(
144
+ candidates=[
145
+ types.Candidate(
146
+ content=types.Content(
147
+ role='model',
148
+ parts=[types.Part.from_text(text='afc output')],
149
+ ),
150
+ finish_reason=types.FinishReason.STOP,
151
+ )
152
+ ],
153
+ automatic_function_calling_history=AFC_HISTORY,
154
+ )
155
+ ]
156
+ yield mock_generate_content
157
+
158
+
159
+ def test_history_start_with_valid_model_content():
160
+ history = [
161
+ types.Content(
162
+ role='model',
163
+ parts=[types.Part.from_text(text='Hello there! how can I help you?')],
164
+ ),
165
+ types.Content(role='user', parts=[types.Part.from_text(text='Hello')]),
166
+ ]
167
+
168
+ models_module = models.Models(mock_api_client)
169
+ chats_module = chats.Chats(modules=models_module)
170
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
171
+
172
+ assert chat.get_history() == history
173
+ assert chat.get_history(curated=True) == history
174
+
175
+
176
+ def test_history_start_with_invalid_model_content():
177
+ history = [
178
+ types.Content(
179
+ role='model',
180
+ parts=[],
181
+ ),
182
+ types.Content(role='user', parts=[types.Part.from_text(text='Hello')]),
183
+ ]
184
+
185
+ models_module = models.Models(mock_api_client)
186
+ chats_module = chats.Chats(modules=models_module)
187
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
188
+
189
+ assert chat.get_history() == history
190
+ assert chat.get_history(curated=True) == [types.Content(role='user', parts=[types.Part.from_text(text='Hello')])]
191
+
192
+
193
+ def test_history_with_consecutive_valid_user_inputs():
194
+ history = [
195
+ types.Content(
196
+ role='user',
197
+ parts=[types.Part.from_text(text='user input 1')],
198
+ ),
199
+ types.Content(
200
+ role='user',
201
+ parts=[types.Part.from_text(text='user input 2')],
202
+ ),
203
+ ]
204
+
205
+ models_module = models.Models(mock_api_client)
206
+ chats_module = chats.Chats(modules=models_module)
207
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
208
+
209
+ assert chat.get_history() == history
210
+ assert chat.get_history(curated=True) == history
211
+
212
+
213
+ def test_history_with_valid_and_invalid_user_inputs():
214
+ history = [
215
+ types.Content(
216
+ role='user',
217
+ parts=[types.Part.from_text(text='user input 1')],
218
+ ),
219
+ types.Content(
220
+ role='user',
221
+ parts=[], # invalid content
222
+ ),
223
+ types.Content(
224
+ role='user',
225
+ parts=[types.Part.from_text(text='user input 2')],
226
+ ),
227
+ ]
228
+
229
+ models_module = models.Models(mock_api_client)
230
+ chats_module = chats.Chats(modules=models_module)
231
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
232
+
233
+ assert chat.get_history() == history
234
+ assert chat.get_history(curated=True) == history
235
+
236
+
237
+ def test_history_with_consecutive_valid_model_outputs():
238
+ history = [
239
+ types.Content(
240
+ role='model',
241
+ parts=[types.Part.from_text(text='model output 1')],
242
+ ),
243
+ types.Content(
244
+ role='model',
245
+ parts=[types.Part.from_text(text='model output 2')],
246
+ ),
247
+ ]
248
+
249
+ models_module = models.Models(mock_api_client)
250
+ chats_module = chats.Chats(modules=models_module)
251
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
252
+
253
+ assert chat.get_history() == history
254
+ assert chat.get_history(curated=True) == history
255
+
256
+
257
+ def test_history_with_valid_and_invalid_model_output():
258
+ history = [
259
+ types.Content(
260
+ role='model',
261
+ parts=[types.Part.from_text(text='model output 1')],
262
+ ),
263
+ types.Content(
264
+ role='model',
265
+ parts=[], # invalid content
266
+ ),
267
+ types.Content(
268
+ role='model',
269
+ parts=[types.Part.from_text(text='model output 2')],
270
+ ),
271
+ ]
272
+
273
+ models_module = models.Models(mock_api_client)
274
+ chats_module = chats.Chats(modules=models_module)
275
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
276
+
277
+ assert chat.get_history() == history
278
+ assert chat.get_history(curated=True) == []
279
+
280
+
281
+ def test_history_end_with_user_input():
282
+ history = [
283
+ types.Content(
284
+ role='user',
285
+ parts=[types.Part.from_text(text='user input 1')],
286
+ ),
287
+ types.Content(
288
+ role='model',
289
+ parts=[types.Part.from_text(text='model output')],
290
+ ),
291
+ types.Content(
292
+ role='user',
293
+ parts=[types.Part.from_text(text='user input 2')],
294
+ ),
295
+ ]
296
+
297
+ models_module = models.Models(mock_api_client)
298
+ chats_module = chats.Chats(modules=models_module)
299
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
300
+
301
+ assert chat.get_history() == history
302
+ assert chat.get_history(curated=True) == history
303
+
304
+
305
+ def test_unrecognized_role_in_history():
306
+ history = [
307
+ types.Content(role='user', parts=[types.Part.from_text(text='Hello')]),
308
+ types.Content(
309
+ role='invalid_role',
310
+ parts=[types.Part.from_text(text='Hello there! how can I help you?')],
311
+ ),
312
+ ]
313
+
314
+ models_module = models.Models(mock_api_client)
315
+ chats_module = chats.Chats(modules=models_module)
316
+ with pytest.raises(ValueError) as e:
317
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
318
+
319
+ assert 'Role must be user or model' in str(e)
320
+
321
+
322
+ def test_sync_chat_create():
323
+ history = [
324
+ types.Content(
325
+ role='user', parts=[types.Part.from_text(text='user input turn 1')]
326
+ ),
327
+ types.Content(
328
+ role='model',
329
+ parts=[types.Part.from_text(text='model output turn 1')],
330
+ ),
331
+ types.Content(
332
+ role='model',
333
+ parts=[types.Part.from_text(text='model output turn 1')],
334
+ ),
335
+ types.Content(
336
+ role='model',
337
+ parts=[types.Part.from_text(text='user input turn 2')],
338
+ ),
339
+ ]
340
+
341
+ models_module = models.Models(mock_api_client)
342
+ chats_module = chats.Chats(modules=models_module)
343
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
344
+
345
+ assert chat.get_history() == history
346
+ assert chat.get_history(curated=True) == history
347
+
348
+
349
+ def test_async_chat_create():
350
+ history = [
351
+ types.Content(
352
+ role='user', parts=[types.Part.from_text(text='user input turn 1')]
353
+ ),
354
+ types.Content(
355
+ role='model',
356
+ parts=[types.Part.from_text(text='model output turn 1')],
357
+ ),
358
+ types.Content(
359
+ role='model',
360
+ parts=[types.Part.from_text(text='model output turn 1')],
361
+ ),
362
+ types.Content(
363
+ role='model',
364
+ parts=[types.Part.from_text(text='user input turn 2')],
365
+ ),
366
+ types.Content(
367
+ role='model',
368
+ parts=[types.Part.from_text(text='model output turn 2')],
369
+ ),
370
+ ]
371
+
372
+ models_module = models.AsyncModels(mock_api_client)
373
+ chats_module = chats.AsyncChats(modules=models_module)
374
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
375
+
376
+ assert chat.get_history() == history
377
+ assert chat.get_history(curated=True) == history
378
+
379
+
380
+ def test_sync_chat_create_with_history_dict():
381
+ history = [
382
+ {'role': 'user', 'parts': [{'text': 'user input turn 1'}]},
383
+ {'role': 'model', 'parts': [{'text': 'model output turn 1'}]},
384
+ {'role': 'user', 'parts': [{'text': 'user input turn 2'}]},
385
+ {'role': 'model', 'parts': [{'text': 'model output turn 2'}]},
386
+ ]
387
+ models_module = models.Models(mock_api_client)
388
+ chats_module = chats.Chats(modules=models_module)
389
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
390
+
391
+ expected_history = [
392
+ types.Content(
393
+ role='user', parts=[types.Part.from_text(text='user input turn 1')]
394
+ ),
395
+ types.Content(
396
+ role='model',
397
+ parts=[types.Part.from_text(text='model output turn 1')],
398
+ ),
399
+ types.Content(
400
+ role='user', parts=[types.Part.from_text(text='user input turn 2')]
401
+ ),
402
+ types.Content(
403
+ role='model',
404
+ parts=[types.Part.from_text(text='model output turn 2')],
405
+ ),
406
+ ]
407
+ assert chat.get_history() == expected_history
408
+ assert chat.get_history(curated=True) == expected_history
409
+
410
+
411
+ def test_async_chat_create_with_history_dict():
412
+ history = [
413
+ {'role': 'user', 'parts': [{'text': 'user input turn 1'}]},
414
+ {'role': 'model', 'parts': [{'text': 'model output turn 1'}]},
415
+ {'role': 'user', 'parts': [{'text': 'user input turn 2'}]},
416
+ {'role': 'model', 'parts': [{'text': 'model output turn 2'}]},
417
+ ]
418
+ models_module = models.AsyncModels(mock_api_client)
419
+ chats_module = chats.AsyncChats(modules=models_module)
420
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
421
+
422
+ expected_history = [
423
+ types.Content(
424
+ role='user', parts=[types.Part.from_text(text='user input turn 1')]
425
+ ),
426
+ types.Content(
427
+ role='model',
428
+ parts=[types.Part.from_text(text='model output turn 1')],
429
+ ),
430
+ types.Content(
431
+ role='user', parts=[types.Part.from_text(text='user input turn 2')]
432
+ ),
433
+ types.Content(
434
+ role='model',
435
+ parts=[types.Part.from_text(text='model output turn 2')],
436
+ ),
437
+ ]
438
+ assert chat.get_history() == expected_history
439
+ assert chat.get_history(curated=True) == expected_history
440
+
441
+
442
+ def test_history_with_invalid_turns():
443
+ valid_input = types.Content(
444
+ role='user', parts=[types.Part.from_text(text='Hello')]
445
+ )
446
+ valid_output = [
447
+ types.Content(
448
+ role='model',
449
+ parts=[types.Part.from_text(text='Hello there! how can I help you?')],
450
+ ),
451
+ types.Content(
452
+ role='model',
453
+ parts=[types.Part.from_text(text='Hello there! how can I help you?')],
454
+ ),
455
+ ]
456
+ invalid_input = types.Content(
457
+ role='user',
458
+ parts=[
459
+ types.Part.from_text(text='a input will be rejected by the model')
460
+ ],
461
+ )
462
+ invalid_output = types.Content(
463
+ role='model',
464
+ parts=[],
465
+ )
466
+ comprehensive_history = []
467
+ comprehensive_history.append(valid_input)
468
+ comprehensive_history.extend(valid_output)
469
+ comprehensive_history.append(invalid_input)
470
+ comprehensive_history.append(invalid_output)
471
+ curated_history = []
472
+ curated_history.append(valid_input)
473
+ curated_history.extend(valid_output)
474
+
475
+ models_module = models.Models(mock_api_client)
476
+ chats_module = chats.Chats(modules=models_module)
477
+ chat = chats_module.create(
478
+ model='gemini-2.5-flash', history=comprehensive_history
479
+ )
480
+
481
+ assert chat.get_history() == comprehensive_history
482
+ assert chat.get_history(curated=True) == curated_history
483
+
484
+
485
+ def test_chat_with_empty_text_part(mock_generate_content_with_empty_text_part):
486
+ models_module = models.Models(mock_api_client)
487
+ chats_module = chats.Chats(modules=models_module)
488
+ chat = chats_module.create(model='gemini-2.5-flash')
489
+
490
+ chat.send_message('Hello')
491
+
492
+ expected_comprehensive_history = [
493
+ types.UserContent(parts=[types.Part.from_text(text='Hello')]),
494
+ types.Content(
495
+ parts=[types.Part(text='')],
496
+ role='model',
497
+ ),
498
+ ]
499
+ assert chat.get_history() == expected_comprehensive_history
500
+ assert chat.get_history(curated=True) == expected_comprehensive_history
501
+
502
+
503
+ def test_chat_with_empty_content(mock_generate_content_empty_content):
504
+ models_module = models.Models(mock_api_client)
505
+ chats_module = chats.Chats(modules=models_module)
506
+ chat = chats_module.create(model='gemini-2.5-flash')
507
+
508
+ chat.send_message('Hello')
509
+
510
+ expected_comprehensive_history = [
511
+ types.UserContent(parts=[types.Part.from_text(text='Hello')]),
512
+ types.Content(
513
+ parts=[],
514
+ role='model',
515
+ ),
516
+ ]
517
+ assert chat.get_history() == expected_comprehensive_history
518
+ assert not chat.get_history(curated=True)
519
+
520
+
521
+ def test_chat_stream_with_empty_text_part(
522
+ mock_generate_content_stream_with_empty_text_part,
523
+ ):
524
+ models_module = models.Models(mock_api_client)
525
+ chats_module = chats.Chats(modules=models_module)
526
+ chat = chats_module.create(model='gemini-2.5-flash')
527
+
528
+ chunks = chat.send_message_stream('Hello')
529
+ for chunk in chunks:
530
+ pass
531
+
532
+ expected_comprehensive_history = [
533
+ types.UserContent(parts=[types.Part.from_text(text='Hello')]),
534
+ types.Content(
535
+ parts=[types.Part(text='')],
536
+ role='model',
537
+ ),
538
+ ]
539
+ assert chat.get_history() == expected_comprehensive_history
540
+ assert chat.get_history(curated=True) == expected_comprehensive_history
541
+
542
+
543
+ def test_chat_stream_with_empty_content(
544
+ mock_generate_content_stream_empty_content,
545
+ ):
546
+ models_module = models.Models(mock_api_client)
547
+ chats_module = chats.Chats(modules=models_module)
548
+ chat = chats_module.create(model='gemini-2.5-flash')
549
+
550
+ chunks = chat.send_message_stream('Hello')
551
+ for chunk in chunks:
552
+ pass
553
+
554
+ expected_comprehensive_history = [
555
+ types.UserContent(parts=[types.Part.from_text(text='Hello')]),
556
+ types.Content(
557
+ parts=[],
558
+ role='model',
559
+ ),
560
+ ]
561
+ assert chat.get_history() == expected_comprehensive_history
562
+ assert not chat.get_history(curated=True)
563
+
564
+
565
+ def test_chat_with_afc_history(mock_generate_content_afc_history):
566
+ models_module = models.Models(mock_api_client)
567
+ chats_module = chats.Chats(modules=models_module)
568
+ chat = chats_module.create(model='gemini-2.5-flash')
569
+
570
+ chat.send_message('Hello')
571
+
572
+ expected_history = AFC_HISTORY + [
573
+ types.Content(
574
+ role='model',
575
+ parts=[types.Part.from_text(text='afc output')],
576
+ ),
577
+ ]
578
+ assert chat.get_history() == expected_history
579
+ assert chat.get_history(curated=True) == expected_history
580
+
581
+
582
+ def test_chat_stream_with_afc_history(mock_generate_content_stream_afc_history):
583
+ models_module = models.Models(mock_api_client)
584
+ chats_module = chats.Chats(modules=models_module)
585
+ chat = chats_module.create(model='gemini-2.5-flash')
586
+
587
+ chunks = chat.send_message_stream('Hello')
588
+ for chunk in chunks:
589
+ pass
590
+
591
+ expected_history = AFC_HISTORY + [
592
+ types.Content(
593
+ role='model',
594
+ parts=[types.Part.from_text(text='afc output')],
595
+ ),
596
+ ]
597
+ assert chat.get_history() == expected_history
598
+ assert chat.get_history(curated=True) == expected_history