google-genai 1.16.1__tar.gz → 1.18.0__tar.gz

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 (41) hide show
  1. {google_genai-1.16.1/google_genai.egg-info → google_genai-1.18.0}/PKG-INFO +2 -2
  2. {google_genai-1.16.1 → google_genai-1.18.0}/README.md +1 -1
  3. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/__init__.py +1 -2
  4. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/_api_client.py +10 -0
  5. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/_automatic_function_calling_util.py +1 -0
  6. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/_common.py +36 -1
  7. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/_live_converters.py +134 -31
  8. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/_mcp_utils.py +3 -6
  9. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/_replay_api_client.py +51 -1
  10. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/_tokens_converters.py +75 -13
  11. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/_transformers.py +22 -3
  12. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/caches.py +74 -6
  13. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/chats.py +6 -3
  14. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/live.py +39 -101
  15. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/models.py +349 -17
  16. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/tunings.py +6 -0
  17. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/types.py +333 -162
  18. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/version.py +1 -1
  19. {google_genai-1.16.1 → google_genai-1.18.0/google_genai.egg-info}/PKG-INFO +2 -2
  20. {google_genai-1.16.1 → google_genai-1.18.0}/pyproject.toml +1 -1
  21. {google_genai-1.16.1 → google_genai-1.18.0}/LICENSE +0 -0
  22. {google_genai-1.16.1 → google_genai-1.18.0}/MANIFEST.in +0 -0
  23. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/_adapters.py +0 -0
  24. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/_api_module.py +0 -0
  25. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/_base_url.py +0 -0
  26. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/_extra_utils.py +0 -0
  27. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/_test_api_client.py +0 -0
  28. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/batches.py +0 -0
  29. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/client.py +0 -0
  30. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/errors.py +0 -0
  31. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/files.py +0 -0
  32. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/live_music.py +0 -0
  33. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/operations.py +0 -0
  34. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/pagers.py +0 -0
  35. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/py.typed +0 -0
  36. {google_genai-1.16.1 → google_genai-1.18.0}/google/genai/tokens.py +0 -0
  37. {google_genai-1.16.1 → google_genai-1.18.0}/google_genai.egg-info/SOURCES.txt +0 -0
  38. {google_genai-1.16.1 → google_genai-1.18.0}/google_genai.egg-info/dependency_links.txt +0 -0
  39. {google_genai-1.16.1 → google_genai-1.18.0}/google_genai.egg-info/requires.txt +0 -0
  40. {google_genai-1.16.1 → google_genai-1.18.0}/google_genai.egg-info/top_level.txt +0 -0
  41. {google_genai-1.16.1 → google_genai-1.18.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-genai
3
- Version: 1.16.1
3
+ Version: 1.18.0
4
4
  Summary: GenAI Python SDK
5
5
  Author-email: Google LLC <googleapis-packages@google.com>
6
6
  License: Apache-2.0
@@ -148,7 +148,7 @@ Pydantic model types are available in the `types` module.
148
148
 
149
149
  ## Models
150
150
 
151
- The `client.models` modules exposes model inferencing and model getters.
151
+ The `client.models` module exposes model inferencing and model getters.
152
152
  See the 'Create a client' section above to initialize a client.
153
153
 
154
154
  ### Generate Content
@@ -117,7 +117,7 @@ Pydantic model types are available in the `types` module.
117
117
 
118
118
  ## Models
119
119
 
120
- The `client.models` modules exposes model inferencing and model getters.
120
+ The `client.models` module exposes model inferencing and model getters.
121
121
  See the 'Create a client' section above to initialize a client.
122
122
 
123
123
  ### Generate Content
@@ -17,9 +17,8 @@
17
17
 
18
18
  from . import version
19
19
  from .client import Client
20
- from .live import live_ephemeral_connect
21
20
 
22
21
 
23
22
  __version__ = version.__version__
24
23
 
25
- __all__ = ['Client', 'live_ephemeral_connect']
24
+ __all__ = ['Client']
@@ -63,6 +63,11 @@ MAX_RETRY_COUNT = 3
63
63
  INITIAL_RETRY_DELAY = 1 # second
64
64
  DELAY_MULTIPLIER = 2
65
65
 
66
+
67
+ class EphemeralTokenAPIKeyError(ValueError):
68
+ """Error raised when the API key is invalid."""
69
+
70
+
66
71
  def _append_library_version_headers(headers: dict[str, str]) -> None:
67
72
  """Appends the telemetry header to the headers dict."""
68
73
  library_label = f'google-genai-sdk/{version.__version__}'
@@ -625,6 +630,11 @@ class BaseApiClient:
625
630
  versioned_path,
626
631
  )
627
632
 
633
+ if self.api_key and self.api_key.startswith('auth_tokens/'):
634
+ raise EphemeralTokenAPIKeyError(
635
+ 'Ephemeral tokens can only be used with the live API.'
636
+ )
637
+
628
638
  timeout_in_seconds = _get_timeout_in_seconds(patched_http_options.timeout)
629
639
 
630
640
  if patched_http_options.headers is None:
@@ -37,6 +37,7 @@ _py_builtin_type_to_schema_type = {
37
37
  bool: types.Type.BOOLEAN,
38
38
  list: types.Type.ARRAY,
39
39
  dict: types.Type.OBJECT,
40
+ None: types.Type.NULL,
40
41
  }
41
42
 
42
43
 
@@ -20,7 +20,7 @@ import datetime
20
20
  import enum
21
21
  import functools
22
22
  import typing
23
- from typing import Any, Callable, Optional, Union
23
+ from typing import Any, Callable, Optional, Union, get_origin, get_args
24
24
  import uuid
25
25
  import warnings
26
26
 
@@ -154,6 +154,38 @@ def convert_to_dict(obj: object) -> Any:
154
154
  return obj
155
155
 
156
156
 
157
+ def _is_struct_type(annotation: type) -> bool:
158
+ """Checks if the given annotation is list[dict[str, typing.Any]]
159
+ or typing.List[typing.Dict[str, typing.Any]].
160
+
161
+ This maps to Struct type in the API.
162
+ """
163
+ outer_origin = get_origin(annotation)
164
+ outer_args = get_args(annotation)
165
+
166
+ if outer_origin is not list: # Python 3.9+ normalizes list
167
+ return False
168
+
169
+ if not outer_args or len(outer_args) != 1:
170
+ return False
171
+
172
+ inner_annotation = outer_args[0]
173
+
174
+ inner_origin = get_origin(inner_annotation)
175
+ inner_args = get_args(inner_annotation)
176
+
177
+ if inner_origin is not dict: # Python 3.9+ normalizes to dict
178
+ return False
179
+
180
+ if not inner_args or len(inner_args) != 2:
181
+ # dict should have exactly two type arguments
182
+ return False
183
+
184
+ # Check if the dict arguments are str and typing.Any
185
+ key_type, value_type = inner_args
186
+ return key_type is str and value_type is typing.Any
187
+
188
+
157
189
  def _remove_extra_fields(
158
190
  model: Any, response: dict[str, object]
159
191
  ) -> None:
@@ -188,6 +220,9 @@ def _remove_extra_fields(
188
220
  if isinstance(value, dict) and typing.get_origin(annotation) is not dict:
189
221
  _remove_extra_fields(annotation, value)
190
222
  elif isinstance(value, list):
223
+ if _is_struct_type(annotation):
224
+ continue
225
+
191
226
  for item in value:
192
227
  # assume a list of dict is list of BaseModel
193
228
  if isinstance(item, dict):
@@ -281,6 +281,42 @@ def _Blob_to_vertex(
281
281
  return to_object
282
282
 
283
283
 
284
+ def _FileData_to_mldev(
285
+ api_client: BaseApiClient,
286
+ from_object: Union[dict[str, Any], object],
287
+ parent_object: Optional[dict[str, Any]] = None,
288
+ ) -> dict[str, Any]:
289
+ to_object: dict[str, Any] = {}
290
+ if getv(from_object, ['display_name']) is not None:
291
+ raise ValueError('display_name parameter is not supported in Gemini API.')
292
+
293
+ if getv(from_object, ['file_uri']) is not None:
294
+ setv(to_object, ['fileUri'], getv(from_object, ['file_uri']))
295
+
296
+ if getv(from_object, ['mime_type']) is not None:
297
+ setv(to_object, ['mimeType'], getv(from_object, ['mime_type']))
298
+
299
+ return to_object
300
+
301
+
302
+ def _FileData_to_vertex(
303
+ api_client: BaseApiClient,
304
+ from_object: Union[dict[str, Any], object],
305
+ parent_object: Optional[dict[str, Any]] = None,
306
+ ) -> dict[str, Any]:
307
+ to_object: dict[str, Any] = {}
308
+ if getv(from_object, ['display_name']) is not None:
309
+ setv(to_object, ['displayName'], getv(from_object, ['display_name']))
310
+
311
+ if getv(from_object, ['file_uri']) is not None:
312
+ setv(to_object, ['fileUri'], getv(from_object, ['file_uri']))
313
+
314
+ if getv(from_object, ['mime_type']) is not None:
315
+ setv(to_object, ['mimeType'], getv(from_object, ['mime_type']))
316
+
317
+ return to_object
318
+
319
+
284
320
  def _Part_to_mldev(
285
321
  api_client: BaseApiClient,
286
322
  from_object: Union[dict[str, Any], object],
@@ -308,6 +344,22 @@ def _Part_to_mldev(
308
344
  ),
309
345
  )
310
346
 
347
+ if getv(from_object, ['file_data']) is not None:
348
+ setv(
349
+ to_object,
350
+ ['fileData'],
351
+ _FileData_to_mldev(
352
+ api_client, getv(from_object, ['file_data']), to_object
353
+ ),
354
+ )
355
+
356
+ if getv(from_object, ['thought_signature']) is not None:
357
+ setv(
358
+ to_object,
359
+ ['thoughtSignature'],
360
+ getv(from_object, ['thought_signature']),
361
+ )
362
+
311
363
  if getv(from_object, ['code_execution_result']) is not None:
312
364
  setv(
313
365
  to_object,
@@ -318,9 +370,6 @@ def _Part_to_mldev(
318
370
  if getv(from_object, ['executable_code']) is not None:
319
371
  setv(to_object, ['executableCode'], getv(from_object, ['executable_code']))
320
372
 
321
- if getv(from_object, ['file_data']) is not None:
322
- setv(to_object, ['fileData'], getv(from_object, ['file_data']))
323
-
324
373
  if getv(from_object, ['function_call']) is not None:
325
374
  setv(to_object, ['functionCall'], getv(from_object, ['function_call']))
326
375
 
@@ -364,6 +413,22 @@ def _Part_to_vertex(
364
413
  ),
365
414
  )
366
415
 
416
+ if getv(from_object, ['file_data']) is not None:
417
+ setv(
418
+ to_object,
419
+ ['fileData'],
420
+ _FileData_to_vertex(
421
+ api_client, getv(from_object, ['file_data']), to_object
422
+ ),
423
+ )
424
+
425
+ if getv(from_object, ['thought_signature']) is not None:
426
+ setv(
427
+ to_object,
428
+ ['thoughtSignature'],
429
+ getv(from_object, ['thought_signature']),
430
+ )
431
+
367
432
  if getv(from_object, ['code_execution_result']) is not None:
368
433
  setv(
369
434
  to_object,
@@ -374,9 +439,6 @@ def _Part_to_vertex(
374
439
  if getv(from_object, ['executable_code']) is not None:
375
440
  setv(to_object, ['executableCode'], getv(from_object, ['executable_code']))
376
441
 
377
- if getv(from_object, ['file_data']) is not None:
378
- setv(to_object, ['fileData'], getv(from_object, ['file_data']))
379
-
380
442
  if getv(from_object, ['function_call']) is not None:
381
443
  setv(to_object, ['functionCall'], getv(from_object, ['function_call']))
382
444
 
@@ -2345,13 +2407,6 @@ def _LiveMusicGenerationConfig_to_mldev(
2345
2407
  getv(from_object, ['only_bass_and_drums']),
2346
2408
  )
2347
2409
 
2348
- if getv(from_object, ['music_generation_mode']) is not None:
2349
- setv(
2350
- to_object,
2351
- ['musicGenerationMode'],
2352
- getv(from_object, ['music_generation_mode']),
2353
- )
2354
-
2355
2410
  return to_object
2356
2411
 
2357
2412
 
@@ -2396,11 +2451,6 @@ def _LiveMusicGenerationConfig_to_vertex(
2396
2451
  'only_bass_and_drums parameter is not supported in Vertex AI.'
2397
2452
  )
2398
2453
 
2399
- if getv(from_object, ['music_generation_mode']) is not None:
2400
- raise ValueError(
2401
- 'music_generation_mode parameter is not supported in Vertex AI.'
2402
- )
2403
-
2404
2454
  return to_object
2405
2455
 
2406
2456
 
@@ -2653,6 +2703,40 @@ def _Blob_from_vertex(
2653
2703
  return to_object
2654
2704
 
2655
2705
 
2706
+ def _FileData_from_mldev(
2707
+ api_client: BaseApiClient,
2708
+ from_object: Union[dict[str, Any], object],
2709
+ parent_object: Optional[dict[str, Any]] = None,
2710
+ ) -> dict[str, Any]:
2711
+ to_object: dict[str, Any] = {}
2712
+
2713
+ if getv(from_object, ['fileUri']) is not None:
2714
+ setv(to_object, ['file_uri'], getv(from_object, ['fileUri']))
2715
+
2716
+ if getv(from_object, ['mimeType']) is not None:
2717
+ setv(to_object, ['mime_type'], getv(from_object, ['mimeType']))
2718
+
2719
+ return to_object
2720
+
2721
+
2722
+ def _FileData_from_vertex(
2723
+ api_client: BaseApiClient,
2724
+ from_object: Union[dict[str, Any], object],
2725
+ parent_object: Optional[dict[str, Any]] = None,
2726
+ ) -> dict[str, Any]:
2727
+ to_object: dict[str, Any] = {}
2728
+ if getv(from_object, ['displayName']) is not None:
2729
+ setv(to_object, ['display_name'], getv(from_object, ['displayName']))
2730
+
2731
+ if getv(from_object, ['fileUri']) is not None:
2732
+ setv(to_object, ['file_uri'], getv(from_object, ['fileUri']))
2733
+
2734
+ if getv(from_object, ['mimeType']) is not None:
2735
+ setv(to_object, ['mime_type'], getv(from_object, ['mimeType']))
2736
+
2737
+ return to_object
2738
+
2739
+
2656
2740
  def _Part_from_mldev(
2657
2741
  api_client: BaseApiClient,
2658
2742
  from_object: Union[dict[str, Any], object],
@@ -2680,6 +2764,22 @@ def _Part_from_mldev(
2680
2764
  ),
2681
2765
  )
2682
2766
 
2767
+ if getv(from_object, ['fileData']) is not None:
2768
+ setv(
2769
+ to_object,
2770
+ ['file_data'],
2771
+ _FileData_from_mldev(
2772
+ api_client, getv(from_object, ['fileData']), to_object
2773
+ ),
2774
+ )
2775
+
2776
+ if getv(from_object, ['thoughtSignature']) is not None:
2777
+ setv(
2778
+ to_object,
2779
+ ['thought_signature'],
2780
+ getv(from_object, ['thoughtSignature']),
2781
+ )
2782
+
2683
2783
  if getv(from_object, ['codeExecutionResult']) is not None:
2684
2784
  setv(
2685
2785
  to_object,
@@ -2690,9 +2790,6 @@ def _Part_from_mldev(
2690
2790
  if getv(from_object, ['executableCode']) is not None:
2691
2791
  setv(to_object, ['executable_code'], getv(from_object, ['executableCode']))
2692
2792
 
2693
- if getv(from_object, ['fileData']) is not None:
2694
- setv(to_object, ['file_data'], getv(from_object, ['fileData']))
2695
-
2696
2793
  if getv(from_object, ['functionCall']) is not None:
2697
2794
  setv(to_object, ['function_call'], getv(from_object, ['functionCall']))
2698
2795
 
@@ -2736,6 +2833,22 @@ def _Part_from_vertex(
2736
2833
  ),
2737
2834
  )
2738
2835
 
2836
+ if getv(from_object, ['fileData']) is not None:
2837
+ setv(
2838
+ to_object,
2839
+ ['file_data'],
2840
+ _FileData_from_vertex(
2841
+ api_client, getv(from_object, ['fileData']), to_object
2842
+ ),
2843
+ )
2844
+
2845
+ if getv(from_object, ['thoughtSignature']) is not None:
2846
+ setv(
2847
+ to_object,
2848
+ ['thought_signature'],
2849
+ getv(from_object, ['thoughtSignature']),
2850
+ )
2851
+
2739
2852
  if getv(from_object, ['codeExecutionResult']) is not None:
2740
2853
  setv(
2741
2854
  to_object,
@@ -2746,9 +2859,6 @@ def _Part_from_vertex(
2746
2859
  if getv(from_object, ['executableCode']) is not None:
2747
2860
  setv(to_object, ['executable_code'], getv(from_object, ['executableCode']))
2748
2861
 
2749
- if getv(from_object, ['fileData']) is not None:
2750
- setv(to_object, ['file_data'], getv(from_object, ['fileData']))
2751
-
2752
2862
  if getv(from_object, ['functionCall']) is not None:
2753
2863
  setv(to_object, ['function_call'], getv(from_object, ['functionCall']))
2754
2864
 
@@ -3676,13 +3786,6 @@ def _LiveMusicGenerationConfig_from_mldev(
3676
3786
  getv(from_object, ['onlyBassAndDrums']),
3677
3787
  )
3678
3788
 
3679
- if getv(from_object, ['musicGenerationMode']) is not None:
3680
- setv(
3681
- to_object,
3682
- ['music_generation_mode'],
3683
- getv(from_object, ['musicGenerationMode']),
3684
- )
3685
-
3686
3789
  return to_object
3687
3790
 
3688
3791
 
@@ -83,13 +83,10 @@ def set_mcp_usage_header(headers: dict[str, str]) -> None:
83
83
  version_label = version("mcp")
84
84
  except PackageNotFoundError:
85
85
  version_label = "0.0.0"
86
- # TODO: b/418827318 - Investigate weather the duplicate mcp label check is
87
- # necessary.
88
- mcp_label = f"mcp_used/{version_label}"
89
86
  existing_header = headers.get("x-goog-api-client", "")
90
- if mcp_label in existing_header:
91
- return
92
- headers["x-goog-api-client"] = (existing_header + f" {mcp_label}").lstrip()
87
+ headers["x-goog-api-client"] = (
88
+ existing_header + f" mcp_used/{version_label}"
89
+ ).lstrip()
93
90
 
94
91
 
95
92
  def _filter_to_supported_schema(schema: dict[str, Any]) -> dict[str, Any]:
@@ -18,6 +18,7 @@
18
18
  import base64
19
19
  import copy
20
20
  import datetime
21
+ import enum
21
22
  import inspect
22
23
  import io
23
24
  import json
@@ -37,6 +38,55 @@ from .types import HttpOptions, HttpOptionsOrDict
37
38
  from .types import GenerateVideosOperation
38
39
 
39
40
 
41
+ def to_snake_case(name: str) -> str:
42
+ """Converts a string from camelCase or PascalCase to snake_case."""
43
+
44
+ if not isinstance(name, str):
45
+ name = str(name)
46
+ s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
47
+ return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
48
+
49
+
50
+ def _normalize_json_case(obj: Any) -> Any:
51
+ if isinstance(obj, dict):
52
+ return {
53
+ to_snake_case(k): _normalize_json_case(v)
54
+ for k, v in obj.items()
55
+ }
56
+ elif isinstance(obj, list):
57
+ return [_normalize_json_case(item) for item in obj]
58
+ elif isinstance(obj, enum.Enum):
59
+ return obj.value
60
+ else:
61
+ return obj
62
+
63
+
64
+ def _equals_ignore_key_case(obj1: Any, obj2: Any) -> bool:
65
+ """Compares two Python objects for equality ignoring key casing.
66
+
67
+ Returns:
68
+ bool: True if the two objects are equal regardless of key casing
69
+ (camelCase vs. snake_case). For example, the following are considered equal:
70
+
71
+ {'my_key': 'my_value'}
72
+ {'myKey': 'my_value'}
73
+
74
+ This also considers enums and strings with the same value as equal.
75
+ For example, the following are considered equal:
76
+
77
+ {'type': <Type.STRING: 'STRING'>}}
78
+ {'type': 'STRING'}
79
+ """
80
+
81
+ normalized_obj_1 = _normalize_json_case(obj1)
82
+ normalized_obj_2 = _normalize_json_case(obj2)
83
+
84
+ if normalized_obj_1 == normalized_obj_2:
85
+ return True
86
+ else:
87
+ return False
88
+
89
+
40
90
  def _redact_version_numbers(version_string: str) -> str:
41
91
  """Redacts version numbers in the form x.y.z from a string."""
42
92
  return re.sub(r'\d+\.\d+\.\d+', '{VERSION_NUMBER}', version_string)
@@ -358,7 +408,7 @@ class ReplayApiClient(BaseApiClient):
358
408
 
359
409
  actual_request_body = [request_data_copy]
360
410
  expected_request_body = interaction.request.body_segments
361
- assert actual_request_body == expected_request_body, (
411
+ assert _equals_ignore_key_case(actual_request_body, expected_request_body), (
362
412
  'Request body mismatch:\n'
363
413
  f'Actual: {actual_request_body}\n'
364
414
  f'Expected: {expected_request_body}'
@@ -281,6 +281,42 @@ def _Blob_to_vertex(
281
281
  return to_object
282
282
 
283
283
 
284
+ def _FileData_to_mldev(
285
+ api_client: BaseApiClient,
286
+ from_object: Union[dict[str, Any], object],
287
+ parent_object: Optional[dict[str, Any]] = None,
288
+ ) -> dict[str, Any]:
289
+ to_object: dict[str, Any] = {}
290
+ if getv(from_object, ['display_name']) is not None:
291
+ raise ValueError('display_name parameter is not supported in Gemini API.')
292
+
293
+ if getv(from_object, ['file_uri']) is not None:
294
+ setv(to_object, ['fileUri'], getv(from_object, ['file_uri']))
295
+
296
+ if getv(from_object, ['mime_type']) is not None:
297
+ setv(to_object, ['mimeType'], getv(from_object, ['mime_type']))
298
+
299
+ return to_object
300
+
301
+
302
+ def _FileData_to_vertex(
303
+ api_client: BaseApiClient,
304
+ from_object: Union[dict[str, Any], object],
305
+ parent_object: Optional[dict[str, Any]] = None,
306
+ ) -> dict[str, Any]:
307
+ to_object: dict[str, Any] = {}
308
+ if getv(from_object, ['display_name']) is not None:
309
+ setv(to_object, ['displayName'], getv(from_object, ['display_name']))
310
+
311
+ if getv(from_object, ['file_uri']) is not None:
312
+ setv(to_object, ['fileUri'], getv(from_object, ['file_uri']))
313
+
314
+ if getv(from_object, ['mime_type']) is not None:
315
+ setv(to_object, ['mimeType'], getv(from_object, ['mime_type']))
316
+
317
+ return to_object
318
+
319
+
284
320
  def _Part_to_mldev(
285
321
  api_client: BaseApiClient,
286
322
  from_object: Union[dict[str, Any], object],
@@ -308,6 +344,22 @@ def _Part_to_mldev(
308
344
  ),
309
345
  )
310
346
 
347
+ if getv(from_object, ['file_data']) is not None:
348
+ setv(
349
+ to_object,
350
+ ['fileData'],
351
+ _FileData_to_mldev(
352
+ api_client, getv(from_object, ['file_data']), to_object
353
+ ),
354
+ )
355
+
356
+ if getv(from_object, ['thought_signature']) is not None:
357
+ setv(
358
+ to_object,
359
+ ['thoughtSignature'],
360
+ getv(from_object, ['thought_signature']),
361
+ )
362
+
311
363
  if getv(from_object, ['code_execution_result']) is not None:
312
364
  setv(
313
365
  to_object,
@@ -318,9 +370,6 @@ def _Part_to_mldev(
318
370
  if getv(from_object, ['executable_code']) is not None:
319
371
  setv(to_object, ['executableCode'], getv(from_object, ['executable_code']))
320
372
 
321
- if getv(from_object, ['file_data']) is not None:
322
- setv(to_object, ['fileData'], getv(from_object, ['file_data']))
323
-
324
373
  if getv(from_object, ['function_call']) is not None:
325
374
  setv(to_object, ['functionCall'], getv(from_object, ['function_call']))
326
375
 
@@ -364,6 +413,22 @@ def _Part_to_vertex(
364
413
  ),
365
414
  )
366
415
 
416
+ if getv(from_object, ['file_data']) is not None:
417
+ setv(
418
+ to_object,
419
+ ['fileData'],
420
+ _FileData_to_vertex(
421
+ api_client, getv(from_object, ['file_data']), to_object
422
+ ),
423
+ )
424
+
425
+ if getv(from_object, ['thought_signature']) is not None:
426
+ setv(
427
+ to_object,
428
+ ['thoughtSignature'],
429
+ getv(from_object, ['thought_signature']),
430
+ )
431
+
367
432
  if getv(from_object, ['code_execution_result']) is not None:
368
433
  setv(
369
434
  to_object,
@@ -374,9 +439,6 @@ def _Part_to_vertex(
374
439
  if getv(from_object, ['executable_code']) is not None:
375
440
  setv(to_object, ['executableCode'], getv(from_object, ['executable_code']))
376
441
 
377
- if getv(from_object, ['file_data']) is not None:
378
- setv(to_object, ['fileData'], getv(from_object, ['file_data']))
379
-
380
442
  if getv(from_object, ['function_call']) is not None:
381
443
  setv(to_object, ['functionCall'], getv(from_object, ['function_call']))
382
444
 
@@ -1537,7 +1599,7 @@ def _LiveConnectConfig_to_vertex(
1537
1599
  return to_object
1538
1600
 
1539
1601
 
1540
- def _LiveEphemeralParameters_to_mldev(
1602
+ def _LiveConnectConstraints_to_mldev(
1541
1603
  api_client: BaseApiClient,
1542
1604
  from_object: Union[dict[str, Any], object],
1543
1605
  parent_object: Optional[dict[str, Any]] = None,
@@ -1562,7 +1624,7 @@ def _LiveEphemeralParameters_to_mldev(
1562
1624
  return to_object
1563
1625
 
1564
1626
 
1565
- def _LiveEphemeralParameters_to_vertex(
1627
+ def _LiveConnectConstraints_to_vertex(
1566
1628
  api_client: BaseApiClient,
1567
1629
  from_object: Union[dict[str, Any], object],
1568
1630
  parent_object: Optional[dict[str, Any]] = None,
@@ -1597,13 +1659,13 @@ def _CreateAuthTokenConfig_to_mldev(
1597
1659
  if getv(from_object, ['uses']) is not None:
1598
1660
  setv(parent_object, ['uses'], getv(from_object, ['uses']))
1599
1661
 
1600
- if getv(from_object, ['live_ephemeral_parameters']) is not None:
1662
+ if getv(from_object, ['live_connect_constraints']) is not None:
1601
1663
  setv(
1602
1664
  parent_object,
1603
1665
  ['bidiGenerateContentSetup'],
1604
- _LiveEphemeralParameters_to_mldev(
1666
+ _LiveConnectConstraints_to_mldev(
1605
1667
  api_client,
1606
- getv(from_object, ['live_ephemeral_parameters']),
1668
+ getv(from_object, ['live_connect_constraints']),
1607
1669
  to_object,
1608
1670
  ),
1609
1671
  )
@@ -1636,9 +1698,9 @@ def _CreateAuthTokenConfig_to_vertex(
1636
1698
  if getv(from_object, ['uses']) is not None:
1637
1699
  raise ValueError('uses parameter is not supported in Vertex AI.')
1638
1700
 
1639
- if getv(from_object, ['live_ephemeral_parameters']) is not None:
1701
+ if getv(from_object, ['live_connect_constraints']) is not None:
1640
1702
  raise ValueError(
1641
- 'live_ephemeral_parameters parameter is not supported in Vertex AI.'
1703
+ 'live_connect_constraints parameter is not supported in Vertex AI.'
1642
1704
  )
1643
1705
 
1644
1706
  if getv(from_object, ['lock_additional_fields']) is not None:
@@ -630,6 +630,21 @@ def handle_null_fields(schema: dict[str, Any]) -> None:
630
630
  del schema['anyOf']
631
631
 
632
632
 
633
+ def _raise_for_unsupported_schema_type(origin: Any) -> None:
634
+ """Raises an error if the schema type is unsupported."""
635
+ raise ValueError(f'Unsupported schema type: {origin}')
636
+
637
+
638
+ def _raise_for_unsupported_mldev_properties(schema: Any, client: _api_client.BaseApiClient) -> None:
639
+ if not client.vertexai and (
640
+ schema.get('additionalProperties')
641
+ or schema.get('additional_properties')
642
+ ):
643
+ raise ValueError(
644
+ 'additionalProperties is not supported in the Gemini API.'
645
+ )
646
+
647
+
633
648
  def process_schema(
634
649
  schema: dict[str, Any],
635
650
  client: _api_client.BaseApiClient,
@@ -700,6 +715,8 @@ def process_schema(
700
715
  if schema.get('title') == 'PlaceholderLiteralEnum':
701
716
  del schema['title']
702
717
 
718
+ _raise_for_unsupported_mldev_properties(schema, client)
719
+
703
720
  # Standardize spelling for relevant schema fields. For example, if a dict is
704
721
  # provided directly to response_schema, it may use `any_of` instead of `anyOf.
705
722
  # Otherwise, model_json_schema() uses `anyOf`.
@@ -744,7 +761,8 @@ def process_schema(
744
761
  schema_type = schema.get('type')
745
762
  if isinstance(schema_type, Enum):
746
763
  schema_type = schema_type.value
747
- schema_type = schema_type.upper()
764
+ if isinstance(schema_type, str):
765
+ schema_type = schema_type.upper()
748
766
 
749
767
  # model_json_schema() returns a schema with a 'const' field when a Literal with one value is provided as a pydantic field
750
768
  # For example `genre: Literal['action']` becomes: {'const': 'action', 'title': 'Genre', 'type': 'string'}
@@ -818,8 +836,9 @@ def t_schema(
818
836
  return _process_enum(origin, client)
819
837
  if isinstance(origin, types.Schema):
820
838
  if dict(origin) == dict(types.Schema()):
821
- # response_schema value was coerced to an empty Schema instance because it did not adhere to the Schema field annotation
822
- raise ValueError(f'Unsupported schema type.')
839
+ # response_schema value was coerced to an empty Schema instance because
840
+ # it did not adhere to the Schema field annotation
841
+ _raise_for_unsupported_schema_type(origin)
823
842
  schema = origin.model_dump(exclude_unset=True)
824
843
  process_schema(schema, client)
825
844
  return types.Schema.model_validate(schema)