google-genai 1.9.0__py3-none-any.whl → 1.10.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.
- google/genai/_api_client.py +117 -28
- google/genai/_automatic_function_calling_util.py +1 -1
- google/genai/_extra_utils.py +1 -1
- google/genai/_replay_api_client.py +32 -8
- google/genai/_transformers.py +43 -59
- google/genai/files.py +22 -6
- google/genai/live.py +545 -46
- google/genai/models.py +23 -9
- google/genai/operations.py +17 -9
- google/genai/tunings.py +0 -3
- google/genai/types.py +555 -57
- google/genai/version.py +1 -1
- {google_genai-1.9.0.dist-info → google_genai-1.10.0.dist-info}/METADATA +1 -1
- google_genai-1.10.0.dist-info/RECORD +27 -0
- google_genai-1.9.0.dist-info/RECORD +0 -27
- {google_genai-1.9.0.dist-info → google_genai-1.10.0.dist-info}/WHEEL +0 -0
- {google_genai-1.9.0.dist-info → google_genai-1.10.0.dist-info}/licenses/LICENSE +0 -0
- {google_genai-1.9.0.dist-info → google_genai-1.10.0.dist-info}/top_level.txt +0 -0
google/genai/_api_client.py
CHANGED
@@ -19,18 +19,21 @@
|
|
19
19
|
The BaseApiClient is intended to be a private module and is subject to change.
|
20
20
|
"""
|
21
21
|
|
22
|
-
import anyio
|
23
22
|
import asyncio
|
24
23
|
import copy
|
25
24
|
from dataclasses import dataclass
|
26
25
|
import datetime
|
26
|
+
import http
|
27
27
|
import io
|
28
28
|
import json
|
29
29
|
import logging
|
30
|
+
import math
|
30
31
|
import os
|
31
32
|
import sys
|
33
|
+
import time
|
32
34
|
from typing import Any, AsyncIterator, Optional, Tuple, Union
|
33
35
|
from urllib.parse import urlparse, urlunparse
|
36
|
+
import anyio
|
34
37
|
import google.auth
|
35
38
|
import google.auth.credentials
|
36
39
|
from google.auth.credentials import Credentials
|
@@ -95,6 +98,14 @@ def _patch_http_options(
|
|
95
98
|
return copy_option
|
96
99
|
|
97
100
|
|
101
|
+
def _populate_server_timeout_header(
|
102
|
+
headers: dict[str, str], timeout_in_seconds: Optional[Union[float, int]]
|
103
|
+
) -> None:
|
104
|
+
"""Populates the server timeout header in the headers dict."""
|
105
|
+
if timeout_in_seconds and 'X-Server-Timeout' not in headers:
|
106
|
+
headers['X-Server-Timeout'] = str(math.ceil(timeout_in_seconds))
|
107
|
+
|
108
|
+
|
98
109
|
def _join_url_path(base_url: str, path: str) -> str:
|
99
110
|
parsed_base = urlparse(base_url)
|
100
111
|
base_path = (
|
@@ -128,6 +139,19 @@ def _refresh_auth(credentials: Credentials) -> Credentials:
|
|
128
139
|
return credentials
|
129
140
|
|
130
141
|
|
142
|
+
def _get_timeout_in_seconds(
|
143
|
+
timeout: Optional[Union[float, int]],
|
144
|
+
) -> Optional[float]:
|
145
|
+
"""Converts the timeout to seconds."""
|
146
|
+
if timeout:
|
147
|
+
# HttpOptions.timeout is in milliseconds. But httpx.Client.request()
|
148
|
+
# expects seconds.
|
149
|
+
timeout_in_seconds = timeout / 1000.0
|
150
|
+
else:
|
151
|
+
timeout_in_seconds = None
|
152
|
+
return timeout_in_seconds
|
153
|
+
|
154
|
+
|
131
155
|
@dataclass
|
132
156
|
class HttpRequest:
|
133
157
|
headers: dict[str, str]
|
@@ -520,18 +544,13 @@ class BaseApiClient:
|
|
520
544
|
versioned_path,
|
521
545
|
)
|
522
546
|
|
523
|
-
timeout_in_seconds
|
524
|
-
patched_http_options.timeout
|
525
|
-
)
|
526
|
-
if timeout_in_seconds:
|
527
|
-
# HttpOptions.timeout is in milliseconds. But httpx.Client.request()
|
528
|
-
# expects seconds.
|
529
|
-
timeout_in_seconds = timeout_in_seconds / 1000.0
|
530
|
-
else:
|
531
|
-
timeout_in_seconds = None
|
547
|
+
timeout_in_seconds = _get_timeout_in_seconds(patched_http_options.timeout)
|
532
548
|
|
533
549
|
if patched_http_options.headers is None:
|
534
550
|
raise ValueError('Request headers must be set.')
|
551
|
+
_populate_server_timeout_header(
|
552
|
+
patched_http_options.headers, timeout_in_seconds
|
553
|
+
)
|
535
554
|
return HttpRequest(
|
536
555
|
method=http_method,
|
537
556
|
url=url,
|
@@ -712,7 +731,12 @@ class BaseApiClient:
|
|
712
731
|
return async_generator()
|
713
732
|
|
714
733
|
def upload_file(
|
715
|
-
self,
|
734
|
+
self,
|
735
|
+
file_path: Union[str, io.IOBase],
|
736
|
+
upload_url: str,
|
737
|
+
upload_size: int,
|
738
|
+
*,
|
739
|
+
http_options: Optional[HttpOptionsOrDict] = None,
|
716
740
|
) -> HttpResponse:
|
717
741
|
"""Transfers a file to the given URL.
|
718
742
|
|
@@ -723,18 +747,28 @@ class BaseApiClient:
|
|
723
747
|
upload_url: The URL to upload the file to.
|
724
748
|
upload_size: The size of file content to be uploaded, this will have to
|
725
749
|
match the size requested in the resumable upload request.
|
750
|
+
http_options: The http options to use for the request.
|
726
751
|
|
727
752
|
returns:
|
728
753
|
The HttpResponse object from the finalize request.
|
729
754
|
"""
|
730
755
|
if isinstance(file_path, io.IOBase):
|
731
|
-
return self._upload_fd(
|
756
|
+
return self._upload_fd(
|
757
|
+
file_path, upload_url, upload_size, http_options=http_options
|
758
|
+
)
|
732
759
|
else:
|
733
760
|
with open(file_path, 'rb') as file:
|
734
|
-
return self._upload_fd(
|
761
|
+
return self._upload_fd(
|
762
|
+
file, upload_url, upload_size, http_options=http_options
|
763
|
+
)
|
735
764
|
|
736
765
|
def _upload_fd(
|
737
|
-
self,
|
766
|
+
self,
|
767
|
+
file: io.IOBase,
|
768
|
+
upload_url: str,
|
769
|
+
upload_size: int,
|
770
|
+
*,
|
771
|
+
http_options: Optional[HttpOptionsOrDict] = None,
|
738
772
|
) -> HttpResponse:
|
739
773
|
"""Transfers a file to the given URL.
|
740
774
|
|
@@ -743,6 +777,7 @@ class BaseApiClient:
|
|
743
777
|
upload_url: The URL to upload the file to.
|
744
778
|
upload_size: The size of file content to be uploaded, this will have to
|
745
779
|
match the size requested in the resumable upload request.
|
780
|
+
http_options: The http options to use for the request.
|
746
781
|
|
747
782
|
returns:
|
748
783
|
The HttpResponse object from the finalize request.
|
@@ -758,15 +793,32 @@ class BaseApiClient:
|
|
758
793
|
# If last chunk, finalize the upload.
|
759
794
|
if chunk_size + offset >= upload_size:
|
760
795
|
upload_command += ', finalize'
|
796
|
+
http_options = http_options if http_options else self._http_options
|
797
|
+
timeout = (
|
798
|
+
http_options.get('timeout')
|
799
|
+
if isinstance(http_options, dict)
|
800
|
+
else http_options.timeout
|
801
|
+
)
|
802
|
+
if timeout is None:
|
803
|
+
# Per request timeout is not configured. Check the global timeout.
|
804
|
+
timeout = (
|
805
|
+
self._http_options.timeout
|
806
|
+
if isinstance(self._http_options, dict)
|
807
|
+
else self._http_options.timeout
|
808
|
+
)
|
809
|
+
timeout_in_seconds = _get_timeout_in_seconds(timeout)
|
810
|
+
upload_headers = {
|
811
|
+
'X-Goog-Upload-Command': upload_command,
|
812
|
+
'X-Goog-Upload-Offset': str(offset),
|
813
|
+
'Content-Length': str(chunk_size),
|
814
|
+
}
|
815
|
+
_populate_server_timeout_header(upload_headers, timeout_in_seconds)
|
761
816
|
response = self._httpx_client.request(
|
762
817
|
method='POST',
|
763
818
|
url=upload_url,
|
764
|
-
headers=
|
765
|
-
'X-Goog-Upload-Command': upload_command,
|
766
|
-
'X-Goog-Upload-Offset': str(offset),
|
767
|
-
'Content-Length': str(chunk_size),
|
768
|
-
},
|
819
|
+
headers=upload_headers,
|
769
820
|
content=file_chunk,
|
821
|
+
timeout=timeout_in_seconds,
|
770
822
|
)
|
771
823
|
offset += chunk_size
|
772
824
|
if response.headers['x-goog-upload-status'] != 'active':
|
@@ -783,7 +835,12 @@ class BaseApiClient:
|
|
783
835
|
)
|
784
836
|
return HttpResponse(response.headers, response_stream=[response.text])
|
785
837
|
|
786
|
-
def download_file(
|
838
|
+
def download_file(
|
839
|
+
self,
|
840
|
+
path: str,
|
841
|
+
*,
|
842
|
+
http_options: Optional[HttpOptionsOrDict] = None,
|
843
|
+
):
|
787
844
|
"""Downloads the file data.
|
788
845
|
|
789
846
|
Args:
|
@@ -822,6 +879,8 @@ class BaseApiClient:
|
|
822
879
|
file_path: Union[str, io.IOBase],
|
823
880
|
upload_url: str,
|
824
881
|
upload_size: int,
|
882
|
+
*,
|
883
|
+
http_options: Optional[HttpOptionsOrDict] = None,
|
825
884
|
) -> HttpResponse:
|
826
885
|
"""Transfers a file asynchronously to the given URL.
|
827
886
|
|
@@ -831,23 +890,30 @@ class BaseApiClient:
|
|
831
890
|
upload_url: The URL to upload the file to.
|
832
891
|
upload_size: The size of file content to be uploaded, this will have to
|
833
892
|
match the size requested in the resumable upload request.
|
893
|
+
http_options: The http options to use for the request.
|
834
894
|
|
835
895
|
returns:
|
836
896
|
The HttpResponse object from the finalize request.
|
837
897
|
"""
|
838
898
|
if isinstance(file_path, io.IOBase):
|
839
|
-
return await self._async_upload_fd(
|
899
|
+
return await self._async_upload_fd(
|
900
|
+
file_path, upload_url, upload_size, http_options=http_options
|
901
|
+
)
|
840
902
|
else:
|
841
903
|
file = anyio.Path(file_path)
|
842
904
|
fd = await file.open('rb')
|
843
905
|
async with fd:
|
844
|
-
return await self._async_upload_fd(
|
906
|
+
return await self._async_upload_fd(
|
907
|
+
fd, upload_url, upload_size, http_options=http_options
|
908
|
+
)
|
845
909
|
|
846
910
|
async def _async_upload_fd(
|
847
911
|
self,
|
848
912
|
file: Union[io.IOBase, anyio.AsyncFile],
|
849
913
|
upload_url: str,
|
850
914
|
upload_size: int,
|
915
|
+
*,
|
916
|
+
http_options: Optional[HttpOptionsOrDict] = None,
|
851
917
|
) -> HttpResponse:
|
852
918
|
"""Transfers a file asynchronously to the given URL.
|
853
919
|
|
@@ -856,6 +922,7 @@ class BaseApiClient:
|
|
856
922
|
upload_url: The URL to upload the file to.
|
857
923
|
upload_size: The size of file content to be uploaded, this will have to
|
858
924
|
match the size requested in the resumable upload request.
|
925
|
+
http_options: The http options to use for the request.
|
859
926
|
|
860
927
|
returns:
|
861
928
|
The HttpResponse object from the finalized request.
|
@@ -874,15 +941,32 @@ class BaseApiClient:
|
|
874
941
|
# If last chunk, finalize the upload.
|
875
942
|
if chunk_size + offset >= upload_size:
|
876
943
|
upload_command += ', finalize'
|
944
|
+
http_options = http_options if http_options else self._http_options
|
945
|
+
timeout = (
|
946
|
+
http_options.get('timeout')
|
947
|
+
if isinstance(http_options, dict)
|
948
|
+
else http_options.timeout
|
949
|
+
)
|
950
|
+
if timeout is None:
|
951
|
+
# Per request timeout is not configured. Check the global timeout.
|
952
|
+
timeout = (
|
953
|
+
self._http_options.timeout
|
954
|
+
if isinstance(self._http_options, dict)
|
955
|
+
else self._http_options.timeout
|
956
|
+
)
|
957
|
+
timeout_in_seconds = _get_timeout_in_seconds(timeout)
|
958
|
+
upload_headers = {
|
959
|
+
'X-Goog-Upload-Command': upload_command,
|
960
|
+
'X-Goog-Upload-Offset': str(offset),
|
961
|
+
'Content-Length': str(chunk_size),
|
962
|
+
}
|
963
|
+
_populate_server_timeout_header(upload_headers, timeout_in_seconds)
|
877
964
|
response = await self._async_httpx_client.request(
|
878
965
|
method='POST',
|
879
966
|
url=upload_url,
|
880
967
|
content=file_chunk,
|
881
|
-
headers=
|
882
|
-
|
883
|
-
'X-Goog-Upload-Offset': str(offset),
|
884
|
-
'Content-Length': str(chunk_size),
|
885
|
-
},
|
968
|
+
headers=upload_headers,
|
969
|
+
timeout=timeout_in_seconds,
|
886
970
|
)
|
887
971
|
offset += chunk_size
|
888
972
|
if response.headers.get('x-goog-upload-status') != 'active':
|
@@ -899,7 +983,12 @@ class BaseApiClient:
|
|
899
983
|
)
|
900
984
|
return HttpResponse(response.headers, response_stream=[response.text])
|
901
985
|
|
902
|
-
async def async_download_file(
|
986
|
+
async def async_download_file(
|
987
|
+
self,
|
988
|
+
path: str,
|
989
|
+
*,
|
990
|
+
http_options: Optional[HttpOptionsOrDict] = None,
|
991
|
+
):
|
903
992
|
"""Downloads the file data.
|
904
993
|
|
905
994
|
Args:
|
@@ -28,7 +28,7 @@ from . import types
|
|
28
28
|
if sys.version_info >= (3, 10):
|
29
29
|
VersionedUnionType = builtin_types.UnionType
|
30
30
|
else:
|
31
|
-
VersionedUnionType = typing._UnionGenericAlias
|
31
|
+
VersionedUnionType = typing._UnionGenericAlias # type: ignore[attr-defined]
|
32
32
|
|
33
33
|
_py_builtin_type_to_schema_type = {
|
34
34
|
str: types.Type.STRING,
|
google/genai/_extra_utils.py
CHANGED
@@ -34,6 +34,8 @@ from ._api_client import HttpOptions
|
|
34
34
|
from ._api_client import HttpRequest
|
35
35
|
from ._api_client import HttpResponse
|
36
36
|
from ._common import BaseModel
|
37
|
+
from .types import HttpOptionsOrDict
|
38
|
+
from .types import GenerateVideosOperation
|
37
39
|
|
38
40
|
|
39
41
|
def _redact_version_numbers(version_string: str) -> str:
|
@@ -396,7 +398,12 @@ class ReplayApiClient(BaseApiClient):
|
|
396
398
|
if isinstance(response_model, list):
|
397
399
|
response_model = response_model[0]
|
398
400
|
print('response_model: ', response_model.model_dump(exclude_none=True))
|
399
|
-
|
401
|
+
if isinstance(response_model, GenerateVideosOperation):
|
402
|
+
actual = response_model.model_dump(
|
403
|
+
exclude={'result'}, exclude_none=True, mode='json'
|
404
|
+
)
|
405
|
+
else:
|
406
|
+
actual = response_model.model_dump(exclude_none=True, mode='json')
|
400
407
|
expected = interaction.response.sdk_response_segments[
|
401
408
|
self._sdk_response_index
|
402
409
|
]
|
@@ -461,7 +468,14 @@ class ReplayApiClient(BaseApiClient):
|
|
461
468
|
else:
|
462
469
|
return self._build_response_from_replay(http_request)
|
463
470
|
|
464
|
-
def upload_file(
|
471
|
+
def upload_file(
|
472
|
+
self,
|
473
|
+
file_path: Union[str, io.IOBase],
|
474
|
+
upload_url: str,
|
475
|
+
upload_size: int,
|
476
|
+
*,
|
477
|
+
http_options: Optional[HttpOptionsOrDict] = None,
|
478
|
+
) -> HttpResponse:
|
465
479
|
if isinstance(file_path, io.IOBase):
|
466
480
|
offset = file_path.tell()
|
467
481
|
content = file_path.read()
|
@@ -479,7 +493,9 @@ class ReplayApiClient(BaseApiClient):
|
|
479
493
|
if self._should_call_api():
|
480
494
|
result: Union[str, HttpResponse]
|
481
495
|
try:
|
482
|
-
result = super().upload_file(
|
496
|
+
result = super().upload_file(
|
497
|
+
file_path, upload_url, upload_size, http_options=http_options
|
498
|
+
)
|
483
499
|
except HTTPError as e:
|
484
500
|
result = HttpResponse(
|
485
501
|
dict(e.response.headers), [json.dumps({'reason': e.response.reason})]
|
@@ -496,6 +512,8 @@ class ReplayApiClient(BaseApiClient):
|
|
496
512
|
file_path: Union[str, io.IOBase],
|
497
513
|
upload_url: str,
|
498
514
|
upload_size: int,
|
515
|
+
*,
|
516
|
+
http_options: Optional[HttpOptionsOrDict] = None,
|
499
517
|
) -> HttpResponse:
|
500
518
|
if isinstance(file_path, io.IOBase):
|
501
519
|
offset = file_path.tell()
|
@@ -515,7 +533,7 @@ class ReplayApiClient(BaseApiClient):
|
|
515
533
|
result: HttpResponse
|
516
534
|
try:
|
517
535
|
result = await super().async_upload_file(
|
518
|
-
file_path, upload_url, upload_size
|
536
|
+
file_path, upload_url, upload_size, http_options=http_options
|
519
537
|
)
|
520
538
|
except HTTPError as e:
|
521
539
|
result = HttpResponse(
|
@@ -528,14 +546,16 @@ class ReplayApiClient(BaseApiClient):
|
|
528
546
|
else:
|
529
547
|
return self._build_response_from_replay(request)
|
530
548
|
|
531
|
-
def download_file(
|
549
|
+
def download_file(
|
550
|
+
self, path: str, *, http_options: Optional[HttpOptionsOrDict] = None
|
551
|
+
):
|
532
552
|
self._initialize_replay_session_if_not_loaded()
|
533
553
|
request = self._build_request(
|
534
554
|
'get', path=path, request_dict={}, http_options=http_options
|
535
555
|
)
|
536
556
|
if self._should_call_api():
|
537
557
|
try:
|
538
|
-
result = super().download_file(path, http_options)
|
558
|
+
result = super().download_file(path, http_options=http_options)
|
539
559
|
except HTTPError as e:
|
540
560
|
result = HttpResponse(
|
541
561
|
dict(e.response.headers), [json.dumps({'reason': e.response.reason})]
|
@@ -547,14 +567,18 @@ class ReplayApiClient(BaseApiClient):
|
|
547
567
|
else:
|
548
568
|
return self._build_response_from_replay(request).byte_stream[0]
|
549
569
|
|
550
|
-
async def async_download_file(
|
570
|
+
async def async_download_file(
|
571
|
+
self, path: str, *, http_options: Optional[HttpOptionsOrDict] = None
|
572
|
+
):
|
551
573
|
self._initialize_replay_session_if_not_loaded()
|
552
574
|
request = self._build_request(
|
553
575
|
'get', path=path, request_dict={}, http_options=http_options
|
554
576
|
)
|
555
577
|
if self._should_call_api():
|
556
578
|
try:
|
557
|
-
result = await super().async_download_file(
|
579
|
+
result = await super().async_download_file(
|
580
|
+
path, http_options=http_options
|
581
|
+
)
|
558
582
|
except HTTPError as e:
|
559
583
|
result = HttpResponse(
|
560
584
|
dict(e.response.headers), [json.dumps({'reason': e.response.reason})]
|
google/genai/_transformers.py
CHANGED
@@ -43,7 +43,7 @@ if sys.version_info >= (3, 10):
|
|
43
43
|
_UNION_TYPES = (typing.Union, builtin_types.UnionType)
|
44
44
|
from typing import TypeGuard
|
45
45
|
else:
|
46
|
-
VersionedUnionType = typing._UnionGenericAlias
|
46
|
+
VersionedUnionType = typing._UnionGenericAlias # type: ignore[attr-defined]
|
47
47
|
_UNION_TYPES = (typing.Union,)
|
48
48
|
from typing_extensions import TypeGuard
|
49
49
|
|
@@ -657,51 +657,55 @@ def process_schema(
|
|
657
657
|
)
|
658
658
|
|
659
659
|
if schema.get('title') == 'PlaceholderLiteralEnum':
|
660
|
-
schema
|
661
|
-
|
662
|
-
#
|
663
|
-
#
|
664
|
-
|
665
|
-
|
660
|
+
del schema['title']
|
661
|
+
|
662
|
+
# Standardize spelling for relevant schema fields. For example, if a dict is
|
663
|
+
# provided directly to response_schema, it may use `any_of` instead of `anyOf.
|
664
|
+
# Otherwise, model_json_schema() uses `anyOf`.
|
665
|
+
for from_name, to_name in [
|
666
|
+
('any_of', 'anyOf'),
|
667
|
+
('property_ordering', 'propertyOrdering'),
|
668
|
+
]:
|
669
|
+
if (value := schema.pop(from_name, None)) is not None:
|
670
|
+
schema[to_name] = value
|
666
671
|
|
667
672
|
if defs is None:
|
668
673
|
defs = schema.pop('$defs', {})
|
669
674
|
for _, sub_schema in defs.items():
|
670
|
-
|
675
|
+
# We can skip the '$ref' check, because JSON schema forbids a '$ref' from
|
676
|
+
# directly referencing another '$ref':
|
677
|
+
# https://json-schema.org/understanding-json-schema/structuring#recursion
|
678
|
+
process_schema(
|
679
|
+
sub_schema, client, defs, order_properties=order_properties
|
680
|
+
)
|
671
681
|
|
672
682
|
handle_null_fields(schema)
|
673
683
|
|
674
684
|
# After removing null fields, Optional fields with only one possible type
|
675
685
|
# will have a $ref key that needs to be flattened
|
676
686
|
# For example: {'default': None, 'description': 'Name of the person', 'nullable': True, '$ref': '#/$defs/TestPerson'}
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
if ref_key is None:
|
690
|
-
process_schema(sub_schema, client, defs)
|
691
|
-
else:
|
692
|
-
ref = defs[ref_key.split('defs/')[-1]]
|
693
|
-
any_of.append(ref)
|
694
|
-
schema['anyOf'] = [item for item in any_of if '$ref' not in item]
|
687
|
+
if (ref := schema.pop('$ref', None)) is not None:
|
688
|
+
schema.update(defs[ref.split('defs/')[-1]])
|
689
|
+
|
690
|
+
def _recurse(sub_schema: dict[str, Any]) -> dict[str, Any]:
|
691
|
+
"""Returns the processed `sub_schema`, resolving its '$ref' if any."""
|
692
|
+
if (ref := sub_schema.pop('$ref', None)) is not None:
|
693
|
+
sub_schema = defs[ref.split('defs/')[-1]]
|
694
|
+
process_schema(sub_schema, client, defs, order_properties=order_properties)
|
695
|
+
return sub_schema
|
696
|
+
|
697
|
+
if (any_of := schema.get('anyOf')) is not None:
|
698
|
+
schema['anyOf'] = [_recurse(sub_schema) for sub_schema in any_of]
|
695
699
|
return
|
696
700
|
|
697
|
-
schema_type = schema.get('type'
|
701
|
+
schema_type = schema.get('type')
|
698
702
|
if isinstance(schema_type, Enum):
|
699
703
|
schema_type = schema_type.value
|
700
704
|
schema_type = schema_type.upper()
|
701
705
|
|
702
706
|
# model_json_schema() returns a schema with a 'const' field when a Literal with one value is provided as a pydantic field
|
703
707
|
# For example `genre: Literal['action']` becomes: {'const': 'action', 'title': 'Genre', 'type': 'string'}
|
704
|
-
const = schema.get('const'
|
708
|
+
const = schema.get('const')
|
705
709
|
if const is not None:
|
706
710
|
if schema_type == 'STRING':
|
707
711
|
schema['enum'] = [const]
|
@@ -710,38 +714,18 @@ def process_schema(
|
|
710
714
|
raise ValueError('Literal values must be strings.')
|
711
715
|
|
712
716
|
if schema_type == 'OBJECT':
|
713
|
-
properties
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
process_schema(ref, client, defs)
|
723
|
-
properties[name] = ref
|
724
|
-
if (
|
725
|
-
len(properties.items()) > 1
|
726
|
-
and order_properties
|
727
|
-
and all(
|
728
|
-
ordering_key not in schema
|
729
|
-
for ordering_key in ['property_ordering', 'propertyOrdering']
|
730
|
-
)
|
731
|
-
):
|
732
|
-
property_names = list(properties.keys())
|
733
|
-
schema['property_ordering'] = property_names
|
717
|
+
if (properties := schema.get('properties')) is not None:
|
718
|
+
for name, sub_schema in list(properties.items()):
|
719
|
+
properties[name] = _recurse(sub_schema)
|
720
|
+
if (
|
721
|
+
len(properties.items()) > 1
|
722
|
+
and order_properties
|
723
|
+
and 'propertyOrdering' not in schema
|
724
|
+
):
|
725
|
+
schema['property_ordering'] = list(properties.keys())
|
734
726
|
elif schema_type == 'ARRAY':
|
735
|
-
|
736
|
-
|
737
|
-
return
|
738
|
-
ref_key = sub_schema.get('$ref', None)
|
739
|
-
if ref_key is None:
|
740
|
-
process_schema(sub_schema, client, defs)
|
741
|
-
else:
|
742
|
-
ref = defs[ref_key.split('defs/')[-1]]
|
743
|
-
process_schema(ref, client, defs)
|
744
|
-
schema['items'] = ref
|
727
|
+
if (items := schema.get('items')) is not None:
|
728
|
+
schema['items'] = _recurse(items)
|
745
729
|
|
746
730
|
|
747
731
|
def _process_enum(
|
google/genai/files.py
CHANGED
@@ -658,6 +658,14 @@ class Files(_api_module.BaseModule):
|
|
658
658
|
http_options: types.HttpOptions
|
659
659
|
if config_model and config_model.http_options:
|
660
660
|
http_options = config_model.http_options
|
661
|
+
http_options.api_version = ''
|
662
|
+
http_options.headers = {
|
663
|
+
'Content-Type': 'application/json',
|
664
|
+
'X-Goog-Upload-Protocol': 'resumable',
|
665
|
+
'X-Goog-Upload-Command': 'start',
|
666
|
+
'X-Goog-Upload-Header-Content-Length': f'{file_obj.size_bytes}',
|
667
|
+
'X-Goog-Upload-Header-Content-Type': f'{file_obj.mime_type}',
|
668
|
+
}
|
661
669
|
else:
|
662
670
|
http_options = types.HttpOptions(
|
663
671
|
api_version='',
|
@@ -685,11 +693,11 @@ class Files(_api_module.BaseModule):
|
|
685
693
|
|
686
694
|
if isinstance(file, io.IOBase):
|
687
695
|
return_file = self._api_client.upload_file(
|
688
|
-
file, upload_url, file_obj.size_bytes
|
696
|
+
file, upload_url, file_obj.size_bytes, http_options=http_options
|
689
697
|
)
|
690
698
|
else:
|
691
699
|
return_file = self._api_client.upload_file(
|
692
|
-
fs_path, upload_url, file_obj.size_bytes
|
700
|
+
fs_path, upload_url, file_obj.size_bytes, http_options=http_options
|
693
701
|
)
|
694
702
|
|
695
703
|
return types.File._from_response(
|
@@ -778,7 +786,7 @@ class Files(_api_module.BaseModule):
|
|
778
786
|
|
779
787
|
data = self._api_client.download_file(
|
780
788
|
path,
|
781
|
-
http_options,
|
789
|
+
http_options=http_options,
|
782
790
|
)
|
783
791
|
|
784
792
|
if isinstance(file, types.Video):
|
@@ -1122,6 +1130,14 @@ class AsyncFiles(_api_module.BaseModule):
|
|
1122
1130
|
http_options: types.HttpOptions
|
1123
1131
|
if config_model and config_model.http_options:
|
1124
1132
|
http_options = config_model.http_options
|
1133
|
+
http_options.api_version = ''
|
1134
|
+
http_options.headers = {
|
1135
|
+
'Content-Type': 'application/json',
|
1136
|
+
'X-Goog-Upload-Protocol': 'resumable',
|
1137
|
+
'X-Goog-Upload-Command': 'start',
|
1138
|
+
'X-Goog-Upload-Header-Content-Length': f'{file_obj.size_bytes}',
|
1139
|
+
'X-Goog-Upload-Header-Content-Type': f'{file_obj.mime_type}',
|
1140
|
+
}
|
1125
1141
|
else:
|
1126
1142
|
http_options = types.HttpOptions(
|
1127
1143
|
api_version='',
|
@@ -1148,11 +1164,11 @@ class AsyncFiles(_api_module.BaseModule):
|
|
1148
1164
|
|
1149
1165
|
if isinstance(file, io.IOBase):
|
1150
1166
|
return_file = await self._api_client.async_upload_file(
|
1151
|
-
file, upload_url, file_obj.size_bytes
|
1167
|
+
file, upload_url, file_obj.size_bytes, http_options=http_options
|
1152
1168
|
)
|
1153
1169
|
else:
|
1154
1170
|
return_file = await self._api_client.async_upload_file(
|
1155
|
-
fs_path, upload_url, file_obj.size_bytes
|
1171
|
+
fs_path, upload_url, file_obj.size_bytes, http_options=http_options
|
1156
1172
|
)
|
1157
1173
|
|
1158
1174
|
return types.File._from_response(
|
@@ -1231,7 +1247,7 @@ class AsyncFiles(_api_module.BaseModule):
|
|
1231
1247
|
|
1232
1248
|
data = await self._api_client.async_download_file(
|
1233
1249
|
path,
|
1234
|
-
http_options,
|
1250
|
+
http_options=http_options,
|
1235
1251
|
)
|
1236
1252
|
|
1237
1253
|
return data
|