google-genai 1.54.0__py3-none-any.whl → 1.55.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 (319) hide show
  1. google/genai/__init__.py +1 -0
  2. google/genai/_interactions/__init__.py +117 -0
  3. google/genai/_interactions/_base_client.py +2019 -0
  4. google/genai/_interactions/_client.py +511 -0
  5. google/genai/_interactions/_compat.py +234 -0
  6. google/genai/_interactions/_constants.py +29 -0
  7. google/genai/_interactions/_exceptions.py +122 -0
  8. google/genai/_interactions/_files.py +139 -0
  9. google/genai/_interactions/_models.py +873 -0
  10. google/genai/_interactions/_qs.py +165 -0
  11. google/genai/_interactions/_resource.py +58 -0
  12. google/genai/_interactions/_response.py +847 -0
  13. google/genai/_interactions/_streaming.py +354 -0
  14. google/genai/_interactions/_types.py +276 -0
  15. google/genai/_interactions/_utils/__init__.py +79 -0
  16. google/genai/_interactions/_utils/_compat.py +61 -0
  17. google/genai/_interactions/_utils/_datetime_parse.py +151 -0
  18. google/genai/_interactions/_utils/_logs.py +40 -0
  19. google/genai/_interactions/_utils/_proxy.py +80 -0
  20. google/genai/_interactions/_utils/_reflection.py +57 -0
  21. google/genai/_interactions/_utils/_resources_proxy.py +39 -0
  22. google/genai/_interactions/_utils/_streams.py +27 -0
  23. google/genai/_interactions/_utils/_sync.py +73 -0
  24. google/genai/_interactions/_utils/_transform.py +472 -0
  25. google/genai/_interactions/_utils/_typing.py +172 -0
  26. google/genai/_interactions/_utils/_utils.py +437 -0
  27. google/genai/_interactions/_version.py +18 -0
  28. google/genai/_interactions/resources/__init__.py +34 -0
  29. google/genai/_interactions/resources/interactions.py +1350 -0
  30. google/genai/_interactions/types/__init__.py +107 -0
  31. google/genai/_interactions/types/allowed_tools.py +33 -0
  32. google/genai/_interactions/types/allowed_tools_param.py +35 -0
  33. google/genai/_interactions/types/annotation.py +42 -0
  34. google/genai/_interactions/types/annotation_param.py +42 -0
  35. google/genai/_interactions/types/audio_content.py +38 -0
  36. google/genai/_interactions/types/audio_content_param.py +45 -0
  37. google/genai/_interactions/types/audio_mime_type.py +25 -0
  38. google/genai/_interactions/types/audio_mime_type_param.py +27 -0
  39. google/genai/_interactions/types/code_execution_call_arguments.py +33 -0
  40. google/genai/_interactions/types/code_execution_call_arguments_param.py +32 -0
  41. google/genai/_interactions/types/code_execution_call_content.py +37 -0
  42. google/genai/_interactions/types/code_execution_call_content_param.py +37 -0
  43. google/genai/_interactions/types/code_execution_result_content.py +42 -0
  44. google/genai/_interactions/types/code_execution_result_content_param.py +41 -0
  45. google/genai/_interactions/types/content_delta.py +358 -0
  46. google/genai/_interactions/types/content_start.py +79 -0
  47. google/genai/_interactions/types/content_stop.py +35 -0
  48. google/genai/_interactions/types/deep_research_agent_config.py +33 -0
  49. google/genai/_interactions/types/deep_research_agent_config_param.py +32 -0
  50. google/genai/_interactions/types/document_content.py +36 -0
  51. google/genai/_interactions/types/document_content_param.py +43 -0
  52. google/genai/_interactions/types/dynamic_agent_config.py +44 -0
  53. google/genai/_interactions/types/dynamic_agent_config_param.py +33 -0
  54. google/genai/_interactions/types/error_event.py +46 -0
  55. google/genai/_interactions/types/file_search_result_content.py +46 -0
  56. google/genai/_interactions/types/file_search_result_content_param.py +46 -0
  57. google/genai/_interactions/types/function.py +38 -0
  58. google/genai/_interactions/types/function_call_content.py +39 -0
  59. google/genai/_interactions/types/function_call_content_param.py +39 -0
  60. google/genai/_interactions/types/function_param.py +37 -0
  61. google/genai/_interactions/types/function_result_content.py +52 -0
  62. google/genai/_interactions/types/function_result_content_param.py +54 -0
  63. google/genai/_interactions/types/generation_config.py +57 -0
  64. google/genai/_interactions/types/generation_config_param.py +59 -0
  65. google/genai/_interactions/types/google_search_call_arguments.py +29 -0
  66. google/genai/_interactions/types/google_search_call_arguments_param.py +31 -0
  67. google/genai/_interactions/types/google_search_call_content.py +37 -0
  68. google/genai/_interactions/types/google_search_call_content_param.py +37 -0
  69. google/genai/_interactions/types/google_search_result.py +35 -0
  70. google/genai/_interactions/types/google_search_result_content.py +43 -0
  71. google/genai/_interactions/types/google_search_result_content_param.py +44 -0
  72. google/genai/_interactions/types/google_search_result_param.py +35 -0
  73. google/genai/_interactions/types/image_content.py +41 -0
  74. google/genai/_interactions/types/image_content_param.py +48 -0
  75. google/genai/_interactions/types/image_mime_type.py +23 -0
  76. google/genai/_interactions/types/image_mime_type_param.py +25 -0
  77. google/genai/_interactions/types/interaction.py +165 -0
  78. google/genai/_interactions/types/interaction_create_params.py +212 -0
  79. google/genai/_interactions/types/interaction_event.py +37 -0
  80. google/genai/_interactions/types/interaction_get_params.py +46 -0
  81. google/genai/_interactions/types/interaction_sse_event.py +32 -0
  82. google/genai/_interactions/types/interaction_status_update.py +37 -0
  83. google/genai/_interactions/types/mcp_server_tool_call_content.py +42 -0
  84. google/genai/_interactions/types/mcp_server_tool_call_content_param.py +42 -0
  85. google/genai/_interactions/types/mcp_server_tool_result_content.py +52 -0
  86. google/genai/_interactions/types/mcp_server_tool_result_content_param.py +54 -0
  87. google/genai/_interactions/types/model.py +36 -0
  88. google/genai/_interactions/types/model_param.py +38 -0
  89. google/genai/_interactions/types/speech_config.py +35 -0
  90. google/genai/_interactions/types/speech_config_param.py +35 -0
  91. google/genai/_interactions/types/text_content.py +37 -0
  92. google/genai/_interactions/types/text_content_param.py +38 -0
  93. google/genai/_interactions/types/thinking_level.py +22 -0
  94. google/genai/_interactions/types/thought_content.py +41 -0
  95. google/genai/_interactions/types/thought_content_param.py +47 -0
  96. google/genai/_interactions/types/tool.py +100 -0
  97. google/genai/_interactions/types/tool_choice.py +26 -0
  98. google/genai/_interactions/types/tool_choice_config.py +28 -0
  99. google/genai/_interactions/types/tool_choice_config_param.py +29 -0
  100. google/genai/_interactions/types/tool_choice_param.py +28 -0
  101. google/genai/_interactions/types/tool_choice_type.py +22 -0
  102. google/genai/_interactions/types/tool_param.py +97 -0
  103. google/genai/_interactions/types/turn.py +76 -0
  104. google/genai/_interactions/types/turn_param.py +73 -0
  105. google/genai/_interactions/types/url_context_call_arguments.py +29 -0
  106. google/genai/_interactions/types/url_context_call_arguments_param.py +31 -0
  107. google/genai/_interactions/types/url_context_call_content.py +37 -0
  108. google/genai/_interactions/types/url_context_call_content_param.py +37 -0
  109. google/genai/_interactions/types/url_context_result.py +33 -0
  110. google/genai/_interactions/types/url_context_result_content.py +43 -0
  111. google/genai/_interactions/types/url_context_result_content_param.py +44 -0
  112. google/genai/_interactions/types/url_context_result_param.py +32 -0
  113. google/genai/_interactions/types/usage.py +106 -0
  114. google/genai/_interactions/types/usage_param.py +106 -0
  115. google/genai/_interactions/types/video_content.py +41 -0
  116. google/genai/_interactions/types/video_content_param.py +48 -0
  117. google/genai/_interactions/types/video_mime_type.py +36 -0
  118. google/genai/_interactions/types/video_mime_type_param.py +38 -0
  119. google/genai/_live_converters.py +31 -0
  120. google/genai/_tokens_converters.py +5 -0
  121. google/genai/batches.py +7 -0
  122. google/genai/client.py +223 -0
  123. google/genai/interactions.py +17 -0
  124. google/genai/live.py +4 -3
  125. google/genai/models.py +12 -0
  126. google/genai/tests/__init__.py +21 -0
  127. google/genai/tests/afc/__init__.py +21 -0
  128. google/genai/tests/afc/test_convert_if_exist_pydantic_model.py +309 -0
  129. google/genai/tests/afc/test_convert_number_values_for_function_call_args.py +63 -0
  130. google/genai/tests/afc/test_find_afc_incompatible_tool_indexes.py +240 -0
  131. google/genai/tests/afc/test_generate_content_stream_afc.py +530 -0
  132. google/genai/tests/afc/test_generate_content_stream_afc_thoughts.py +77 -0
  133. google/genai/tests/afc/test_get_function_map.py +176 -0
  134. google/genai/tests/afc/test_get_function_response_parts.py +277 -0
  135. google/genai/tests/afc/test_get_max_remote_calls_for_afc.py +130 -0
  136. google/genai/tests/afc/test_invoke_function_from_dict_args.py +241 -0
  137. google/genai/tests/afc/test_raise_error_for_afc_incompatible_config.py +159 -0
  138. google/genai/tests/afc/test_should_append_afc_history.py +53 -0
  139. google/genai/tests/afc/test_should_disable_afc.py +214 -0
  140. google/genai/tests/batches/__init__.py +17 -0
  141. google/genai/tests/batches/test_cancel.py +77 -0
  142. google/genai/tests/batches/test_create.py +78 -0
  143. google/genai/tests/batches/test_create_with_bigquery.py +113 -0
  144. google/genai/tests/batches/test_create_with_file.py +82 -0
  145. google/genai/tests/batches/test_create_with_gcs.py +125 -0
  146. google/genai/tests/batches/test_create_with_inlined_requests.py +255 -0
  147. google/genai/tests/batches/test_delete.py +86 -0
  148. google/genai/tests/batches/test_embedding.py +157 -0
  149. google/genai/tests/batches/test_get.py +78 -0
  150. google/genai/tests/batches/test_list.py +79 -0
  151. google/genai/tests/caches/__init__.py +17 -0
  152. google/genai/tests/caches/constants.py +29 -0
  153. google/genai/tests/caches/test_create.py +210 -0
  154. google/genai/tests/caches/test_create_custom_url.py +105 -0
  155. google/genai/tests/caches/test_delete.py +54 -0
  156. google/genai/tests/caches/test_delete_custom_url.py +52 -0
  157. google/genai/tests/caches/test_get.py +94 -0
  158. google/genai/tests/caches/test_get_custom_url.py +52 -0
  159. google/genai/tests/caches/test_list.py +68 -0
  160. google/genai/tests/caches/test_update.py +70 -0
  161. google/genai/tests/caches/test_update_custom_url.py +58 -0
  162. google/genai/tests/chats/__init__.py +1 -0
  163. google/genai/tests/chats/test_get_history.py +597 -0
  164. google/genai/tests/chats/test_send_message.py +844 -0
  165. google/genai/tests/chats/test_validate_response.py +90 -0
  166. google/genai/tests/client/__init__.py +17 -0
  167. google/genai/tests/client/test_async_stream.py +427 -0
  168. google/genai/tests/client/test_client_close.py +197 -0
  169. google/genai/tests/client/test_client_initialization.py +1687 -0
  170. google/genai/tests/client/test_client_requests.py +355 -0
  171. google/genai/tests/client/test_custom_client.py +77 -0
  172. google/genai/tests/client/test_http_options.py +178 -0
  173. google/genai/tests/client/test_replay_client_equality.py +168 -0
  174. google/genai/tests/client/test_retries.py +846 -0
  175. google/genai/tests/client/test_upload_errors.py +136 -0
  176. google/genai/tests/common/__init__.py +17 -0
  177. google/genai/tests/common/test_common.py +954 -0
  178. google/genai/tests/conftest.py +162 -0
  179. google/genai/tests/documents/__init__.py +17 -0
  180. google/genai/tests/documents/test_delete.py +51 -0
  181. google/genai/tests/documents/test_get.py +85 -0
  182. google/genai/tests/documents/test_list.py +72 -0
  183. google/genai/tests/errors/__init__.py +1 -0
  184. google/genai/tests/errors/test_api_error.py +417 -0
  185. google/genai/tests/file_search_stores/__init__.py +17 -0
  186. google/genai/tests/file_search_stores/test_create.py +66 -0
  187. google/genai/tests/file_search_stores/test_delete.py +64 -0
  188. google/genai/tests/file_search_stores/test_get.py +94 -0
  189. google/genai/tests/file_search_stores/test_import_file.py +112 -0
  190. google/genai/tests/file_search_stores/test_list.py +57 -0
  191. google/genai/tests/file_search_stores/test_upload_to_file_search_store.py +141 -0
  192. google/genai/tests/files/__init__.py +17 -0
  193. google/genai/tests/files/test_delete.py +46 -0
  194. google/genai/tests/files/test_download.py +85 -0
  195. google/genai/tests/files/test_get.py +46 -0
  196. google/genai/tests/files/test_list.py +72 -0
  197. google/genai/tests/files/test_upload.py +255 -0
  198. google/genai/tests/imports/test_no_optional_imports.py +28 -0
  199. google/genai/tests/interactions/__init__.py +0 -0
  200. google/genai/tests/interactions/test_integration.py +80 -0
  201. google/genai/tests/live/__init__.py +16 -0
  202. google/genai/tests/live/test_live.py +2177 -0
  203. google/genai/tests/live/test_live_music.py +362 -0
  204. google/genai/tests/live/test_live_response.py +163 -0
  205. google/genai/tests/live/test_send_client_content.py +147 -0
  206. google/genai/tests/live/test_send_realtime_input.py +268 -0
  207. google/genai/tests/live/test_send_tool_response.py +222 -0
  208. google/genai/tests/local_tokenizer/__init__.py +17 -0
  209. google/genai/tests/local_tokenizer/test_local_tokenizer.py +343 -0
  210. google/genai/tests/local_tokenizer/test_local_tokenizer_loader.py +235 -0
  211. google/genai/tests/mcp/__init__.py +17 -0
  212. google/genai/tests/mcp/test_has_mcp_tool_usage.py +89 -0
  213. google/genai/tests/mcp/test_mcp_to_gemini_tools.py +191 -0
  214. google/genai/tests/mcp/test_parse_config_for_mcp_sessions.py +201 -0
  215. google/genai/tests/mcp/test_parse_config_for_mcp_usage.py +130 -0
  216. google/genai/tests/mcp/test_set_mcp_usage_header.py +72 -0
  217. google/genai/tests/models/__init__.py +17 -0
  218. google/genai/tests/models/constants.py +8 -0
  219. google/genai/tests/models/test_compute_tokens.py +120 -0
  220. google/genai/tests/models/test_count_tokens.py +159 -0
  221. google/genai/tests/models/test_delete.py +107 -0
  222. google/genai/tests/models/test_edit_image.py +264 -0
  223. google/genai/tests/models/test_embed_content.py +94 -0
  224. google/genai/tests/models/test_function_call_streaming.py +442 -0
  225. google/genai/tests/models/test_generate_content.py +2502 -0
  226. google/genai/tests/models/test_generate_content_cached_content.py +132 -0
  227. google/genai/tests/models/test_generate_content_config_zero_value.py +103 -0
  228. google/genai/tests/models/test_generate_content_from_apikey.py +44 -0
  229. google/genai/tests/models/test_generate_content_http_options.py +40 -0
  230. google/genai/tests/models/test_generate_content_image_generation.py +143 -0
  231. google/genai/tests/models/test_generate_content_mcp.py +343 -0
  232. google/genai/tests/models/test_generate_content_media_resolution.py +97 -0
  233. google/genai/tests/models/test_generate_content_model.py +139 -0
  234. google/genai/tests/models/test_generate_content_part.py +821 -0
  235. google/genai/tests/models/test_generate_content_thought.py +76 -0
  236. google/genai/tests/models/test_generate_content_tools.py +1761 -0
  237. google/genai/tests/models/test_generate_images.py +191 -0
  238. google/genai/tests/models/test_generate_videos.py +759 -0
  239. google/genai/tests/models/test_get.py +104 -0
  240. google/genai/tests/models/test_list.py +233 -0
  241. google/genai/tests/models/test_recontext_image.py +189 -0
  242. google/genai/tests/models/test_segment_image.py +148 -0
  243. google/genai/tests/models/test_update.py +95 -0
  244. google/genai/tests/models/test_upscale_image.py +157 -0
  245. google/genai/tests/operations/__init__.py +17 -0
  246. google/genai/tests/operations/test_get.py +38 -0
  247. google/genai/tests/public_samples/__init__.py +17 -0
  248. google/genai/tests/public_samples/test_gemini_text_only.py +34 -0
  249. google/genai/tests/pytest_helper.py +229 -0
  250. google/genai/tests/shared/__init__.py +16 -0
  251. google/genai/tests/shared/batches/__init__.py +14 -0
  252. google/genai/tests/shared/batches/test_create_delete.py +57 -0
  253. google/genai/tests/shared/batches/test_create_get_cancel.py +56 -0
  254. google/genai/tests/shared/batches/test_list.py +40 -0
  255. google/genai/tests/shared/caches/__init__.py +14 -0
  256. google/genai/tests/shared/caches/test_create_get_delete.py +67 -0
  257. google/genai/tests/shared/caches/test_create_update_get.py +71 -0
  258. google/genai/tests/shared/caches/test_list.py +40 -0
  259. google/genai/tests/shared/chats/__init__.py +14 -0
  260. google/genai/tests/shared/chats/test_send_message.py +48 -0
  261. google/genai/tests/shared/chats/test_send_message_stream.py +50 -0
  262. google/genai/tests/shared/files/__init__.py +14 -0
  263. google/genai/tests/shared/files/test_list.py +41 -0
  264. google/genai/tests/shared/files/test_upload_get_delete.py +54 -0
  265. google/genai/tests/shared/models/__init__.py +14 -0
  266. google/genai/tests/shared/models/test_compute_tokens.py +41 -0
  267. google/genai/tests/shared/models/test_count_tokens.py +40 -0
  268. google/genai/tests/shared/models/test_edit_image.py +67 -0
  269. google/genai/tests/shared/models/test_embed.py +40 -0
  270. google/genai/tests/shared/models/test_generate_content.py +39 -0
  271. google/genai/tests/shared/models/test_generate_content_stream.py +54 -0
  272. google/genai/tests/shared/models/test_generate_images.py +40 -0
  273. google/genai/tests/shared/models/test_generate_videos.py +38 -0
  274. google/genai/tests/shared/models/test_list.py +37 -0
  275. google/genai/tests/shared/models/test_recontext_image.py +55 -0
  276. google/genai/tests/shared/models/test_segment_image.py +52 -0
  277. google/genai/tests/shared/models/test_upscale_image.py +52 -0
  278. google/genai/tests/shared/tunings/__init__.py +16 -0
  279. google/genai/tests/shared/tunings/test_create.py +46 -0
  280. google/genai/tests/shared/tunings/test_create_get_cancel.py +56 -0
  281. google/genai/tests/shared/tunings/test_list.py +39 -0
  282. google/genai/tests/tokens/__init__.py +16 -0
  283. google/genai/tests/tokens/test_create.py +154 -0
  284. google/genai/tests/transformers/__init__.py +17 -0
  285. google/genai/tests/transformers/test_blobs.py +71 -0
  286. google/genai/tests/transformers/test_bytes.py +15 -0
  287. google/genai/tests/transformers/test_duck_type.py +96 -0
  288. google/genai/tests/transformers/test_function_responses.py +72 -0
  289. google/genai/tests/transformers/test_schema.py +653 -0
  290. google/genai/tests/transformers/test_t_batch.py +286 -0
  291. google/genai/tests/transformers/test_t_content.py +160 -0
  292. google/genai/tests/transformers/test_t_contents.py +398 -0
  293. google/genai/tests/transformers/test_t_part.py +85 -0
  294. google/genai/tests/transformers/test_t_parts.py +87 -0
  295. google/genai/tests/transformers/test_t_tool.py +157 -0
  296. google/genai/tests/transformers/test_t_tools.py +195 -0
  297. google/genai/tests/tunings/__init__.py +16 -0
  298. google/genai/tests/tunings/test_cancel.py +39 -0
  299. google/genai/tests/tunings/test_end_to_end.py +106 -0
  300. google/genai/tests/tunings/test_get.py +67 -0
  301. google/genai/tests/tunings/test_list.py +75 -0
  302. google/genai/tests/tunings/test_tune.py +268 -0
  303. google/genai/tests/types/__init__.py +16 -0
  304. google/genai/tests/types/test_bytes_internal.py +271 -0
  305. google/genai/tests/types/test_bytes_type.py +152 -0
  306. google/genai/tests/types/test_future.py +101 -0
  307. google/genai/tests/types/test_optional_types.py +36 -0
  308. google/genai/tests/types/test_part_type.py +616 -0
  309. google/genai/tests/types/test_schema_from_json_schema.py +417 -0
  310. google/genai/tests/types/test_schema_json_schema.py +468 -0
  311. google/genai/tests/types/test_types.py +2903 -0
  312. google/genai/types.py +72 -0
  313. google/genai/version.py +1 -1
  314. {google_genai-1.54.0.dist-info → google_genai-1.55.0.dist-info}/METADATA +3 -1
  315. google_genai-1.55.0.dist-info/RECORD +345 -0
  316. google_genai-1.54.0.dist-info/RECORD +0 -41
  317. {google_genai-1.54.0.dist-info → google_genai-1.55.0.dist-info}/WHEEL +0 -0
  318. {google_genai-1.54.0.dist-info → google_genai-1.55.0.dist-info}/licenses/LICENSE +0 -0
  319. {google_genai-1.54.0.dist-info → google_genai-1.55.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,597 @@
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_invalid_content():
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_invalid_content():
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
+ )
100
+ ]
101
+ )
102
+ ]
103
+ yield mock_generate_content
104
+
105
+
106
+ @pytest.fixture
107
+ def mock_generate_content_stream_empty_content():
108
+ with mock.patch.object(
109
+ models.Models, 'generate_content_stream'
110
+ ) as mock_generate_content:
111
+ mock_generate_content.return_value = [
112
+ types.GenerateContentResponse(candidates=[])
113
+ ]
114
+ yield mock_generate_content
115
+
116
+
117
+ @pytest.fixture
118
+ def mock_generate_content_afc_history():
119
+ with mock.patch.object(
120
+ models.Models, 'generate_content'
121
+ ) as mock_generate_content:
122
+ mock_generate_content.return_value = types.GenerateContentResponse(
123
+ candidates=[
124
+ types.Candidate(
125
+ content=types.Content(
126
+ role='model',
127
+ parts=[types.Part.from_text(text='afc output')],
128
+ )
129
+ )
130
+ ],
131
+ automatic_function_calling_history=AFC_HISTORY,
132
+ )
133
+ yield mock_generate_content
134
+
135
+
136
+ @pytest.fixture
137
+ def mock_generate_content_stream_afc_history():
138
+ with mock.patch.object(
139
+ models.Models, 'generate_content_stream'
140
+ ) as mock_generate_content:
141
+ mock_generate_content.return_value = [
142
+ types.GenerateContentResponse(
143
+ candidates=[
144
+ types.Candidate(
145
+ content=types.Content(
146
+ role='model',
147
+ parts=[types.Part.from_text(text='afc output')],
148
+ ),
149
+ finish_reason=types.FinishReason.STOP,
150
+ )
151
+ ],
152
+ automatic_function_calling_history=AFC_HISTORY,
153
+ )
154
+ ]
155
+ yield mock_generate_content
156
+
157
+
158
+ def test_history_start_with_valid_model_content():
159
+ history = [
160
+ types.Content(
161
+ role='model',
162
+ parts=[types.Part.from_text(text='Hello there! how can I help you?')],
163
+ ),
164
+ types.Content(role='user', parts=[types.Part.from_text(text='Hello')]),
165
+ ]
166
+
167
+ models_module = models.Models(mock_api_client)
168
+ chats_module = chats.Chats(modules=models_module)
169
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
170
+
171
+ assert chat.get_history() == history
172
+ assert chat.get_history(curated=True) == history
173
+
174
+
175
+ def test_history_start_with_invalid_model_content():
176
+ history = [
177
+ types.Content(
178
+ role='model',
179
+ parts=[],
180
+ ),
181
+ types.Content(role='user', parts=[types.Part.from_text(text='Hello')]),
182
+ ]
183
+
184
+ models_module = models.Models(mock_api_client)
185
+ chats_module = chats.Chats(modules=models_module)
186
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
187
+
188
+ assert chat.get_history() == history
189
+ assert chat.get_history(curated=True) == [types.Content(role='user', parts=[types.Part.from_text(text='Hello')])]
190
+
191
+
192
+ def test_history_with_consecutive_valid_user_inputs():
193
+ history = [
194
+ types.Content(
195
+ role='user',
196
+ parts=[types.Part.from_text(text='user input 1')],
197
+ ),
198
+ types.Content(
199
+ role='user',
200
+ parts=[types.Part.from_text(text='user input 2')],
201
+ ),
202
+ ]
203
+
204
+ models_module = models.Models(mock_api_client)
205
+ chats_module = chats.Chats(modules=models_module)
206
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
207
+
208
+ assert chat.get_history() == history
209
+ assert chat.get_history(curated=True) == history
210
+
211
+
212
+ def test_history_with_valid_and_invalid_user_inputs():
213
+ history = [
214
+ types.Content(
215
+ role='user',
216
+ parts=[types.Part.from_text(text='user input 1')],
217
+ ),
218
+ types.Content(
219
+ role='user',
220
+ parts=[], # invalid content
221
+ ),
222
+ types.Content(
223
+ role='user',
224
+ parts=[types.Part.from_text(text='user input 2')],
225
+ ),
226
+ ]
227
+
228
+ models_module = models.Models(mock_api_client)
229
+ chats_module = chats.Chats(modules=models_module)
230
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
231
+
232
+ assert chat.get_history() == history
233
+ assert chat.get_history(curated=True) == history
234
+
235
+
236
+ def test_history_with_consecutive_valid_model_outputs():
237
+ history = [
238
+ types.Content(
239
+ role='model',
240
+ parts=[types.Part.from_text(text='model output 1')],
241
+ ),
242
+ types.Content(
243
+ role='model',
244
+ parts=[types.Part.from_text(text='model output 2')],
245
+ ),
246
+ ]
247
+
248
+ models_module = models.Models(mock_api_client)
249
+ chats_module = chats.Chats(modules=models_module)
250
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
251
+
252
+ assert chat.get_history() == history
253
+ assert chat.get_history(curated=True) == history
254
+
255
+
256
+ def test_history_with_valid_and_invalid_model_output():
257
+ history = [
258
+ types.Content(
259
+ role='model',
260
+ parts=[types.Part.from_text(text='model output 1')],
261
+ ),
262
+ types.Content(
263
+ role='model',
264
+ parts=[], # invalid content
265
+ ),
266
+ types.Content(
267
+ role='model',
268
+ parts=[types.Part.from_text(text='model output 2')],
269
+ ),
270
+ ]
271
+
272
+ models_module = models.Models(mock_api_client)
273
+ chats_module = chats.Chats(modules=models_module)
274
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
275
+
276
+ assert chat.get_history() == history
277
+ assert chat.get_history(curated=True) == []
278
+
279
+
280
+ def test_history_end_with_user_input():
281
+ history = [
282
+ types.Content(
283
+ role='user',
284
+ parts=[types.Part.from_text(text='user input 1')],
285
+ ),
286
+ types.Content(
287
+ role='model',
288
+ parts=[types.Part.from_text(text='model output')],
289
+ ),
290
+ types.Content(
291
+ role='user',
292
+ parts=[types.Part.from_text(text='user input 2')],
293
+ ),
294
+ ]
295
+
296
+ models_module = models.Models(mock_api_client)
297
+ chats_module = chats.Chats(modules=models_module)
298
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
299
+
300
+ assert chat.get_history() == history
301
+ assert chat.get_history(curated=True) == history
302
+
303
+
304
+ def test_unrecognized_role_in_history():
305
+ history = [
306
+ types.Content(role='user', parts=[types.Part.from_text(text='Hello')]),
307
+ types.Content(
308
+ role='invalid_role',
309
+ parts=[types.Part.from_text(text='Hello there! how can I help you?')],
310
+ ),
311
+ ]
312
+
313
+ models_module = models.Models(mock_api_client)
314
+ chats_module = chats.Chats(modules=models_module)
315
+ with pytest.raises(ValueError) as e:
316
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
317
+
318
+ assert 'Role must be user or model' in str(e)
319
+
320
+
321
+ def test_sync_chat_create():
322
+ history = [
323
+ types.Content(
324
+ role='user', parts=[types.Part.from_text(text='user input turn 1')]
325
+ ),
326
+ types.Content(
327
+ role='model',
328
+ parts=[types.Part.from_text(text='model output turn 1')],
329
+ ),
330
+ types.Content(
331
+ role='model',
332
+ parts=[types.Part.from_text(text='model output turn 1')],
333
+ ),
334
+ types.Content(
335
+ role='model',
336
+ parts=[types.Part.from_text(text='user input turn 2')],
337
+ ),
338
+ ]
339
+
340
+ models_module = models.Models(mock_api_client)
341
+ chats_module = chats.Chats(modules=models_module)
342
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
343
+
344
+ assert chat.get_history() == history
345
+ assert chat.get_history(curated=True) == history
346
+
347
+
348
+ def test_async_chat_create():
349
+ history = [
350
+ types.Content(
351
+ role='user', parts=[types.Part.from_text(text='user input turn 1')]
352
+ ),
353
+ types.Content(
354
+ role='model',
355
+ parts=[types.Part.from_text(text='model output turn 1')],
356
+ ),
357
+ types.Content(
358
+ role='model',
359
+ parts=[types.Part.from_text(text='model output turn 1')],
360
+ ),
361
+ types.Content(
362
+ role='model',
363
+ parts=[types.Part.from_text(text='user input turn 2')],
364
+ ),
365
+ types.Content(
366
+ role='model',
367
+ parts=[types.Part.from_text(text='model output turn 2')],
368
+ ),
369
+ ]
370
+
371
+ models_module = models.AsyncModels(mock_api_client)
372
+ chats_module = chats.AsyncChats(modules=models_module)
373
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
374
+
375
+ assert chat.get_history() == history
376
+ assert chat.get_history(curated=True) == history
377
+
378
+
379
+ def test_sync_chat_create_with_history_dict():
380
+ history = [
381
+ {'role': 'user', 'parts': [{'text': 'user input turn 1'}]},
382
+ {'role': 'model', 'parts': [{'text': 'model output turn 1'}]},
383
+ {'role': 'user', 'parts': [{'text': 'user input turn 2'}]},
384
+ {'role': 'model', 'parts': [{'text': 'model output turn 2'}]},
385
+ ]
386
+ models_module = models.Models(mock_api_client)
387
+ chats_module = chats.Chats(modules=models_module)
388
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
389
+
390
+ expected_history = [
391
+ types.Content(
392
+ role='user', parts=[types.Part.from_text(text='user input turn 1')]
393
+ ),
394
+ types.Content(
395
+ role='model',
396
+ parts=[types.Part.from_text(text='model output turn 1')],
397
+ ),
398
+ types.Content(
399
+ role='user', parts=[types.Part.from_text(text='user input turn 2')]
400
+ ),
401
+ types.Content(
402
+ role='model',
403
+ parts=[types.Part.from_text(text='model output turn 2')],
404
+ ),
405
+ ]
406
+ assert chat.get_history() == expected_history
407
+ assert chat.get_history(curated=True) == expected_history
408
+
409
+
410
+ def test_async_chat_create_with_history_dict():
411
+ history = [
412
+ {'role': 'user', 'parts': [{'text': 'user input turn 1'}]},
413
+ {'role': 'model', 'parts': [{'text': 'model output turn 1'}]},
414
+ {'role': 'user', 'parts': [{'text': 'user input turn 2'}]},
415
+ {'role': 'model', 'parts': [{'text': 'model output turn 2'}]},
416
+ ]
417
+ models_module = models.AsyncModels(mock_api_client)
418
+ chats_module = chats.AsyncChats(modules=models_module)
419
+ chat = chats_module.create(model='gemini-2.5-flash', history=history)
420
+
421
+ expected_history = [
422
+ types.Content(
423
+ role='user', parts=[types.Part.from_text(text='user input turn 1')]
424
+ ),
425
+ types.Content(
426
+ role='model',
427
+ parts=[types.Part.from_text(text='model output turn 1')],
428
+ ),
429
+ types.Content(
430
+ role='user', parts=[types.Part.from_text(text='user input turn 2')]
431
+ ),
432
+ types.Content(
433
+ role='model',
434
+ parts=[types.Part.from_text(text='model output turn 2')],
435
+ ),
436
+ ]
437
+ assert chat.get_history() == expected_history
438
+ assert chat.get_history(curated=True) == expected_history
439
+
440
+
441
+ def test_history_with_invalid_turns():
442
+ valid_input = types.Content(
443
+ role='user', parts=[types.Part.from_text(text='Hello')]
444
+ )
445
+ valid_output = [
446
+ types.Content(
447
+ role='model',
448
+ parts=[types.Part.from_text(text='Hello there! how can I help you?')],
449
+ ),
450
+ types.Content(
451
+ role='model',
452
+ parts=[types.Part.from_text(text='Hello there! how can I help you?')],
453
+ ),
454
+ ]
455
+ invalid_input = types.Content(
456
+ role='user',
457
+ parts=[
458
+ types.Part.from_text(text='a input will be rejected by the model')
459
+ ],
460
+ )
461
+ invalid_output = types.Content(
462
+ role='model',
463
+ parts=[],
464
+ )
465
+ comprehensive_history = []
466
+ comprehensive_history.append(valid_input)
467
+ comprehensive_history.extend(valid_output)
468
+ comprehensive_history.append(invalid_input)
469
+ comprehensive_history.append(invalid_output)
470
+ curated_history = []
471
+ curated_history.append(valid_input)
472
+ curated_history.extend(valid_output)
473
+
474
+ models_module = models.Models(mock_api_client)
475
+ chats_module = chats.Chats(modules=models_module)
476
+ chat = chats_module.create(
477
+ model='gemini-2.5-flash', history=comprehensive_history
478
+ )
479
+
480
+ assert chat.get_history() == comprehensive_history
481
+ assert chat.get_history(curated=True) == curated_history
482
+
483
+
484
+ def test_chat_with_invalid_content(mock_generate_content_invalid_content):
485
+ models_module = models.Models(mock_api_client)
486
+ chats_module = chats.Chats(modules=models_module)
487
+ chat = chats_module.create(model='gemini-2.5-flash')
488
+
489
+ chat.send_message('Hello')
490
+
491
+ expected_comprehensive_history = [
492
+ types.UserContent(parts=[types.Part.from_text(text='Hello')]),
493
+ types.Content(
494
+ parts=[types.Part(text='')],
495
+ role='model',
496
+ ),
497
+ ]
498
+ assert chat.get_history() == expected_comprehensive_history
499
+ assert not chat.get_history(curated=True)
500
+
501
+
502
+ def test_chat_with_empty_content(mock_generate_content_empty_content):
503
+ models_module = models.Models(mock_api_client)
504
+ chats_module = chats.Chats(modules=models_module)
505
+ chat = chats_module.create(model='gemini-2.5-flash')
506
+
507
+ chat.send_message('Hello')
508
+
509
+ expected_comprehensive_history = [
510
+ types.UserContent(parts=[types.Part.from_text(text='Hello')]),
511
+ types.Content(
512
+ parts=[],
513
+ role='model',
514
+ ),
515
+ ]
516
+ assert chat.get_history() == expected_comprehensive_history
517
+ assert not chat.get_history(curated=True)
518
+
519
+
520
+ def test_chat_stream_with_invalid_content(
521
+ mock_generate_content_stream_invalid_content,
522
+ ):
523
+ models_module = models.Models(mock_api_client)
524
+ chats_module = chats.Chats(modules=models_module)
525
+ chat = chats_module.create(model='gemini-2.5-flash')
526
+
527
+ chunks = chat.send_message_stream('Hello')
528
+ for chunk in chunks:
529
+ pass
530
+
531
+ expected_comprehensive_history = [
532
+ types.UserContent(parts=[types.Part.from_text(text='Hello')]),
533
+ types.Content(
534
+ parts=[types.Part(text='')],
535
+ role='model',
536
+ ),
537
+ ]
538
+ assert chat.get_history() == expected_comprehensive_history
539
+ assert not chat.get_history(curated=True)
540
+
541
+
542
+ def test_chat_stream_with_empty_content(
543
+ mock_generate_content_stream_empty_content,
544
+ ):
545
+ models_module = models.Models(mock_api_client)
546
+ chats_module = chats.Chats(modules=models_module)
547
+ chat = chats_module.create(model='gemini-2.5-flash')
548
+
549
+ chunks = chat.send_message_stream('Hello')
550
+ for chunk in chunks:
551
+ pass
552
+
553
+ expected_comprehensive_history = [
554
+ types.UserContent(parts=[types.Part.from_text(text='Hello')]),
555
+ types.Content(
556
+ parts=[],
557
+ role='model',
558
+ ),
559
+ ]
560
+ assert chat.get_history() == expected_comprehensive_history
561
+ assert not chat.get_history(curated=True)
562
+
563
+
564
+ def test_chat_with_afc_history(mock_generate_content_afc_history):
565
+ models_module = models.Models(mock_api_client)
566
+ chats_module = chats.Chats(modules=models_module)
567
+ chat = chats_module.create(model='gemini-2.5-flash')
568
+
569
+ chat.send_message('Hello')
570
+
571
+ expected_history = AFC_HISTORY + [
572
+ types.Content(
573
+ role='model',
574
+ parts=[types.Part.from_text(text='afc output')],
575
+ ),
576
+ ]
577
+ assert chat.get_history() == expected_history
578
+ assert chat.get_history(curated=True) == expected_history
579
+
580
+
581
+ def test_chat_stream_with_afc_history(mock_generate_content_stream_afc_history):
582
+ models_module = models.Models(mock_api_client)
583
+ chats_module = chats.Chats(modules=models_module)
584
+ chat = chats_module.create(model='gemini-2.5-flash')
585
+
586
+ chunks = chat.send_message_stream('Hello')
587
+ for chunk in chunks:
588
+ pass
589
+
590
+ expected_history = AFC_HISTORY + [
591
+ types.Content(
592
+ role='model',
593
+ parts=[types.Part.from_text(text='afc output')],
594
+ ),
595
+ ]
596
+ assert chat.get_history() == expected_history
597
+ assert chat.get_history(curated=True) == expected_history