google-genai 1.60.0__py3-none-any.whl → 1.62.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/_interactions/_base_client.py +5 -2
- google/genai/_interactions/_compat.py +3 -3
- google/genai/_interactions/_utils/_json.py +50 -0
- google/genai/_interactions/resources/interactions.py +50 -28
- google/genai/_interactions/types/__init__.py +2 -1
- google/genai/_interactions/types/content_delta.py +1 -1
- google/genai/_interactions/types/function_result_content.py +2 -1
- google/genai/_interactions/types/function_result_content_param.py +4 -4
- google/genai/_interactions/types/{interaction_event.py → interaction_complete_event.py} +3 -3
- google/genai/_interactions/types/interaction_create_params.py +4 -4
- google/genai/_interactions/types/interaction_get_params.py +3 -0
- google/genai/_interactions/types/interaction_sse_event.py +11 -2
- google/genai/_interactions/types/interaction_start_event.py +36 -0
- google/genai/batches.py +3 -0
- google/genai/errors.py +19 -6
- google/genai/files.py +15 -15
- google/genai/live.py +22 -2
- google/genai/live_music.py +14 -1
- google/genai/models.py +486 -197
- google/genai/tests/batches/test_create_with_inlined_requests.py +31 -15
- google/genai/tests/batches/test_get.py +1 -1
- google/genai/tests/client/test_client_close.py +0 -1
- google/genai/tests/errors/test_api_error.py +38 -0
- google/genai/tests/files/test_register_table.py +1 -1
- google/genai/tests/transformers/test_schema.py +10 -1
- google/genai/tests/tunings/test_tune.py +87 -0
- google/genai/tunings.py +211 -20
- google/genai/types.py +178 -14
- google/genai/version.py +1 -1
- {google_genai-1.60.0.dist-info → google_genai-1.62.0.dist-info}/METADATA +1 -1
- {google_genai-1.60.0.dist-info → google_genai-1.62.0.dist-info}/RECORD +34 -32
- {google_genai-1.60.0.dist-info → google_genai-1.62.0.dist-info}/WHEEL +1 -1
- {google_genai-1.60.0.dist-info → google_genai-1.62.0.dist-info}/licenses/LICENSE +0 -0
- {google_genai-1.60.0.dist-info → google_genai-1.62.0.dist-info}/top_level.txt +0 -0
google/genai/errors.py
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
from typing import Any, Callable, Optional, TYPE_CHECKING, Union
|
|
19
19
|
import httpx
|
|
20
20
|
import json
|
|
21
|
+
import websockets
|
|
21
22
|
from . import _common
|
|
22
23
|
|
|
23
24
|
|
|
@@ -69,14 +70,26 @@ class APIError(Exception):
|
|
|
69
70
|
return obj
|
|
70
71
|
|
|
71
72
|
def _get_status(self, response_json: Any) -> Any:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
try:
|
|
74
|
+
status = response_json.get(
|
|
75
|
+
'status', response_json.get('error', {}).get('status', None)
|
|
76
|
+
)
|
|
77
|
+
return status
|
|
78
|
+
except AttributeError:
|
|
79
|
+
# If response_json is not a dict, return close code to handle the case
|
|
80
|
+
# when encountering a websocket error.
|
|
81
|
+
return None
|
|
75
82
|
|
|
76
83
|
def _get_message(self, response_json: Any) -> Any:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
try:
|
|
85
|
+
message = response_json.get(
|
|
86
|
+
'message', response_json.get('error', {}).get('message', None)
|
|
87
|
+
)
|
|
88
|
+
return message
|
|
89
|
+
except AttributeError:
|
|
90
|
+
# If response_json is not a dict, return it as None.
|
|
91
|
+
# This is to handle the case when encountering a websocket error.
|
|
92
|
+
return None
|
|
80
93
|
|
|
81
94
|
def _get_code(self, response_json: Any) -> Any:
|
|
82
95
|
return response_json.get(
|
google/genai/files.py
CHANGED
|
@@ -101,6 +101,17 @@ def _GetFileParameters_to_mldev(
|
|
|
101
101
|
return to_object
|
|
102
102
|
|
|
103
103
|
|
|
104
|
+
def _InternalRegisterFilesParameters_to_mldev(
|
|
105
|
+
from_object: Union[dict[str, Any], object],
|
|
106
|
+
parent_object: Optional[dict[str, Any]] = None,
|
|
107
|
+
) -> dict[str, Any]:
|
|
108
|
+
to_object: dict[str, Any] = {}
|
|
109
|
+
if getv(from_object, ['uris']) is not None:
|
|
110
|
+
setv(to_object, ['uris'], getv(from_object, ['uris']))
|
|
111
|
+
|
|
112
|
+
return to_object
|
|
113
|
+
|
|
114
|
+
|
|
104
115
|
def _ListFilesConfig_to_mldev(
|
|
105
116
|
from_object: Union[dict[str, Any], object],
|
|
106
117
|
parent_object: Optional[dict[str, Any]] = None,
|
|
@@ -152,17 +163,6 @@ def _ListFilesResponse_from_mldev(
|
|
|
152
163
|
return to_object
|
|
153
164
|
|
|
154
165
|
|
|
155
|
-
def _RegisterFilesParameters_to_mldev(
|
|
156
|
-
from_object: Union[dict[str, Any], object],
|
|
157
|
-
parent_object: Optional[dict[str, Any]] = None,
|
|
158
|
-
) -> dict[str, Any]:
|
|
159
|
-
to_object: dict[str, Any] = {}
|
|
160
|
-
if getv(from_object, ['uris']) is not None:
|
|
161
|
-
setv(to_object, ['uris'], getv(from_object, ['uris']))
|
|
162
|
-
|
|
163
|
-
return to_object
|
|
164
|
-
|
|
165
|
-
|
|
166
166
|
def _RegisterFilesResponse_from_mldev(
|
|
167
167
|
from_object: Union[dict[str, Any], object],
|
|
168
168
|
parent_object: Optional[dict[str, Any]] = None,
|
|
@@ -438,7 +438,7 @@ class Files(_api_module.BaseModule):
|
|
|
438
438
|
uris: list[str],
|
|
439
439
|
config: Optional[types.RegisterFilesConfigOrDict] = None,
|
|
440
440
|
) -> types.RegisterFilesResponse:
|
|
441
|
-
parameter_model = types.
|
|
441
|
+
parameter_model = types._InternalRegisterFilesParameters(
|
|
442
442
|
uris=uris,
|
|
443
443
|
config=config,
|
|
444
444
|
)
|
|
@@ -449,7 +449,7 @@ class Files(_api_module.BaseModule):
|
|
|
449
449
|
'This method is only supported in the Gemini Developer client.'
|
|
450
450
|
)
|
|
451
451
|
else:
|
|
452
|
-
request_dict =
|
|
452
|
+
request_dict = _InternalRegisterFilesParameters_to_mldev(parameter_model)
|
|
453
453
|
request_url_dict = request_dict.get('_url')
|
|
454
454
|
if request_url_dict:
|
|
455
455
|
path = 'files:register'.format_map(request_url_dict)
|
|
@@ -977,7 +977,7 @@ class AsyncFiles(_api_module.BaseModule):
|
|
|
977
977
|
uris: list[str],
|
|
978
978
|
config: Optional[types.RegisterFilesConfigOrDict] = None,
|
|
979
979
|
) -> types.RegisterFilesResponse:
|
|
980
|
-
parameter_model = types.
|
|
980
|
+
parameter_model = types._InternalRegisterFilesParameters(
|
|
981
981
|
uris=uris,
|
|
982
982
|
config=config,
|
|
983
983
|
)
|
|
@@ -988,7 +988,7 @@ class AsyncFiles(_api_module.BaseModule):
|
|
|
988
988
|
'This method is only supported in the Gemini Developer client.'
|
|
989
989
|
)
|
|
990
990
|
else:
|
|
991
|
-
request_dict =
|
|
991
|
+
request_dict = _InternalRegisterFilesParameters_to_mldev(parameter_model)
|
|
992
992
|
request_url_dict = request_dict.get('_url')
|
|
993
993
|
if request_url_dict:
|
|
994
994
|
path = 'files:register'.format_map(request_url_dict)
|
google/genai/live.py
CHANGED
|
@@ -26,7 +26,7 @@ import warnings
|
|
|
26
26
|
|
|
27
27
|
import google.auth
|
|
28
28
|
import pydantic
|
|
29
|
-
|
|
29
|
+
import websockets
|
|
30
30
|
|
|
31
31
|
from . import _api_module
|
|
32
32
|
from . import _common
|
|
@@ -41,6 +41,7 @@ from ._common import set_value_by_path as setv
|
|
|
41
41
|
from .live_music import AsyncLiveMusic
|
|
42
42
|
from .models import _Content_to_mldev
|
|
43
43
|
|
|
44
|
+
ConnectionClosed = websockets.ConnectionClosed
|
|
44
45
|
|
|
45
46
|
try:
|
|
46
47
|
from websockets.asyncio.client import ClientConnection
|
|
@@ -534,6 +535,14 @@ class AsyncSession:
|
|
|
534
535
|
raw_response = await self._ws.recv(decode=False)
|
|
535
536
|
except TypeError:
|
|
536
537
|
raw_response = await self._ws.recv() # type: ignore[assignment]
|
|
538
|
+
except ConnectionClosed as e:
|
|
539
|
+
if e.rcvd:
|
|
540
|
+
code = e.rcvd.code
|
|
541
|
+
reason = e.rcvd.reason
|
|
542
|
+
else:
|
|
543
|
+
code = 1006
|
|
544
|
+
reason = websockets.frames.CLOSE_CODE_EXPLANATIONS.get(code, 'Abnormal closure.')
|
|
545
|
+
errors.APIError.raise_error(code, reason, None)
|
|
537
546
|
if raw_response:
|
|
538
547
|
try:
|
|
539
548
|
response = json.loads(raw_response)
|
|
@@ -545,8 +554,11 @@ class AsyncSession:
|
|
|
545
554
|
if self._api_client.vertexai:
|
|
546
555
|
response_dict = live_converters._LiveServerMessage_from_vertex(response)
|
|
547
556
|
else:
|
|
548
|
-
response_dict = response
|
|
557
|
+
response_dict = live_converters._LiveServerMessage_from_mldev(response)
|
|
549
558
|
|
|
559
|
+
if not response_dict and response:
|
|
560
|
+
# Error handling.
|
|
561
|
+
errors.APIError.raise_error(response.get('code'), response, None)
|
|
550
562
|
return types.LiveServerMessage._from_response(
|
|
551
563
|
response=response_dict, kwargs=parameter_model.model_dump()
|
|
552
564
|
)
|
|
@@ -1093,6 +1105,14 @@ class AsyncLive(_api_module.BaseModule):
|
|
|
1093
1105
|
raw_response = await ws.recv(decode=False)
|
|
1094
1106
|
except TypeError:
|
|
1095
1107
|
raw_response = await ws.recv() # type: ignore[assignment]
|
|
1108
|
+
except ConnectionClosed as e:
|
|
1109
|
+
if e.rcvd:
|
|
1110
|
+
code = e.rcvd.code
|
|
1111
|
+
reason = e.rcvd.reason
|
|
1112
|
+
else:
|
|
1113
|
+
code = 1006
|
|
1114
|
+
reason = 'Abnormal closure.'
|
|
1115
|
+
errors.APIError.raise_error(code, reason, None)
|
|
1096
1116
|
if raw_response:
|
|
1097
1117
|
try:
|
|
1098
1118
|
response = json.loads(raw_response)
|
google/genai/live_music.py
CHANGED
|
@@ -19,15 +19,18 @@ import contextlib
|
|
|
19
19
|
import json
|
|
20
20
|
import logging
|
|
21
21
|
from typing import AsyncIterator
|
|
22
|
+
import websockets
|
|
22
23
|
|
|
23
24
|
from . import _api_module
|
|
24
25
|
from . import _common
|
|
25
26
|
from . import _live_converters as live_converters
|
|
26
27
|
from . import _transformers as t
|
|
28
|
+
from . import errors
|
|
27
29
|
from . import types
|
|
28
30
|
from ._api_client import BaseApiClient
|
|
29
31
|
from ._common import set_value_by_path as setv
|
|
30
32
|
|
|
33
|
+
ConnectionClosed = websockets.ConnectionClosed
|
|
31
34
|
|
|
32
35
|
try:
|
|
33
36
|
from websockets.asyncio.client import ClientConnection
|
|
@@ -122,6 +125,14 @@ class AsyncMusicSession:
|
|
|
122
125
|
raw_response = await self._ws.recv(decode=False)
|
|
123
126
|
except TypeError:
|
|
124
127
|
raw_response = await self._ws.recv() # type: ignore[assignment]
|
|
128
|
+
except ConnectionClosed as e:
|
|
129
|
+
if e.rcvd:
|
|
130
|
+
code = e.rcvd.code
|
|
131
|
+
reason = e.rcvd.reason
|
|
132
|
+
else:
|
|
133
|
+
code = 1006
|
|
134
|
+
reason = websockets.frames.CLOSE_CODE_EXPLANATIONS.get(code, 'Abnormal closure.')
|
|
135
|
+
errors.APIError.raise_error(code, reason, None)
|
|
125
136
|
if raw_response:
|
|
126
137
|
try:
|
|
127
138
|
response = json.loads(raw_response)
|
|
@@ -134,7 +145,9 @@ class AsyncMusicSession:
|
|
|
134
145
|
raise NotImplementedError('Live music generation is not supported in Vertex AI.')
|
|
135
146
|
else:
|
|
136
147
|
response_dict = response
|
|
137
|
-
|
|
148
|
+
if not response_dict and response:
|
|
149
|
+
# Error handling.
|
|
150
|
+
errors.APIError.raise_error(response.get('code'), response, None)
|
|
138
151
|
return types.LiveMusicServerMessage._from_response(
|
|
139
152
|
response=response_dict, kwargs=parameter_model.model_dump()
|
|
140
153
|
)
|