google-genai 1.35.0__tar.gz → 1.37.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.
- {google_genai-1.35.0/google_genai.egg-info → google_genai-1.37.0}/PKG-INFO +7 -1
- {google_genai-1.35.0 → google_genai-1.37.0}/README.md +6 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_api_client.py +177 -186
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_replay_api_client.py +0 -1
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_transformers.py +6 -2
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/errors.py +3 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/operations.py +142 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/tunings.py +6 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/types.py +101 -2
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/version.py +1 -1
- {google_genai-1.35.0 → google_genai-1.37.0/google_genai.egg-info}/PKG-INFO +7 -1
- {google_genai-1.35.0 → google_genai-1.37.0}/pyproject.toml +1 -1
- {google_genai-1.35.0 → google_genai-1.37.0}/LICENSE +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/MANIFEST.in +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/__init__.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_adapters.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_api_module.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_automatic_function_calling_util.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_base_transformers.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_base_url.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_common.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_extra_utils.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_live_converters.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_local_tokenizer_loader.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_mcp_utils.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_operations_converters.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_test_api_client.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_tokens_converters.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/batches.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/caches.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/chats.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/client.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/files.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/live.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/live_music.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/local_tokenizer.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/models.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/pagers.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/py.typed +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google/genai/tokens.py +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google_genai.egg-info/SOURCES.txt +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google_genai.egg-info/dependency_links.txt +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google_genai.egg-info/requires.txt +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/google_genai.egg-info/top_level.txt +0 -0
- {google_genai-1.35.0 → google_genai-1.37.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: google-genai
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.37.0
|
4
4
|
Summary: GenAI Python SDK
|
5
5
|
Author-email: Google LLC <googleapis-packages@google.com>
|
6
6
|
License: Apache-2.0
|
@@ -58,6 +58,12 @@ APIs.
|
|
58
58
|
pip install google-genai
|
59
59
|
```
|
60
60
|
|
61
|
+
<small>With `uv`:</small>
|
62
|
+
|
63
|
+
```sh
|
64
|
+
uv pip install google-genai
|
65
|
+
```
|
66
|
+
|
61
67
|
## Imports
|
62
68
|
|
63
69
|
```python
|
@@ -229,7 +229,6 @@ class HttpResponse:
|
|
229
229
|
headers: Union[dict[str, str], httpx.Headers, 'CIMultiDictProxy[str]'],
|
230
230
|
response_stream: Union[Any, str] = None,
|
231
231
|
byte_stream: Union[Any, bytes] = None,
|
232
|
-
session: Optional['aiohttp.ClientSession'] = None,
|
233
232
|
):
|
234
233
|
if isinstance(headers, dict):
|
235
234
|
self.headers = headers
|
@@ -245,7 +244,6 @@ class HttpResponse:
|
|
245
244
|
self.status_code: int = 200
|
246
245
|
self.response_stream = response_stream
|
247
246
|
self.byte_stream = byte_stream
|
248
|
-
self._session = session
|
249
247
|
|
250
248
|
# Async iterator for async streaming.
|
251
249
|
def __aiter__(self) -> 'HttpResponse':
|
@@ -360,69 +358,76 @@ class HttpResponse:
|
|
360
358
|
balance = 0
|
361
359
|
# httpx.Response has a dedicated async line iterator.
|
362
360
|
if isinstance(self.response_stream, httpx.Response):
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
361
|
+
try:
|
362
|
+
async for line in self.response_stream.aiter_lines():
|
363
|
+
if not line:
|
364
|
+
continue
|
365
|
+
# In streaming mode, the response of JSON is prefixed with "data: "
|
366
|
+
# which we must strip before parsing.
|
367
|
+
if line.startswith('data: '):
|
368
|
+
yield line[len('data: '):]
|
369
|
+
continue
|
370
|
+
|
371
|
+
# When API returns an error message, it comes line by line. So we buffer
|
372
|
+
# the lines until a complete JSON string is read. A complete JSON string
|
373
|
+
# is found when the balance is 0.
|
374
|
+
for c in line:
|
375
|
+
if c == '{':
|
376
|
+
balance += 1
|
377
|
+
elif c == '}':
|
378
|
+
balance -= 1
|
379
|
+
|
380
|
+
chunk += line
|
381
|
+
if balance == 0:
|
382
|
+
yield chunk
|
383
|
+
chunk = ''
|
384
|
+
# If there is any remaining chunk, yield it.
|
385
|
+
if chunk:
|
383
386
|
yield chunk
|
384
|
-
|
387
|
+
finally:
|
388
|
+
# Close the response and release the connection.
|
389
|
+
await self.response_stream.aclose()
|
385
390
|
|
386
391
|
# aiohttp.ClientResponse uses a content stream that we read line by line.
|
387
392
|
elif has_aiohttp and isinstance(
|
388
393
|
self.response_stream, aiohttp.ClientResponse
|
389
394
|
):
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
395
|
+
try:
|
396
|
+
while True:
|
397
|
+
# Read a line from the stream. This returns bytes.
|
398
|
+
line_bytes = await self.response_stream.content.readline()
|
399
|
+
if not line_bytes:
|
400
|
+
break
|
401
|
+
# Decode the bytes and remove trailing whitespace and newlines.
|
402
|
+
line = line_bytes.decode('utf-8').rstrip()
|
403
|
+
if not line:
|
404
|
+
continue
|
405
|
+
|
406
|
+
# In streaming mode, the response of JSON is prefixed with "data: "
|
407
|
+
# which we must strip before parsing.
|
408
|
+
if line.startswith('data: '):
|
409
|
+
yield line[len('data: '):]
|
410
|
+
continue
|
411
|
+
|
412
|
+
# When API returns an error message, it comes line by line. So we
|
413
|
+
# buffer the lines until a complete JSON string is read. A complete
|
414
|
+
# JSON strings found when the balance is 0.
|
415
|
+
for c in line:
|
416
|
+
if c == '{':
|
417
|
+
balance += 1
|
418
|
+
elif c == '}':
|
419
|
+
balance -= 1
|
420
|
+
|
421
|
+
chunk += line
|
422
|
+
if balance == 0:
|
423
|
+
yield chunk
|
424
|
+
chunk = ''
|
425
|
+
# If there is any remaining chunk, yield it.
|
426
|
+
if chunk:
|
417
427
|
yield chunk
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
if chunk:
|
422
|
-
yield chunk
|
423
|
-
|
424
|
-
if hasattr(self, '_session') and self._session:
|
425
|
-
await self._session.close()
|
428
|
+
finally:
|
429
|
+
# Release the connection back to the pool for potential reuse.
|
430
|
+
self.response_stream.release()
|
426
431
|
|
427
432
|
@classmethod
|
428
433
|
def _load_json_from_response(cls, response: Any) -> Any:
|
@@ -683,12 +688,25 @@ class BaseApiClient:
|
|
683
688
|
self._async_client_session_request_args = self._ensure_aiohttp_ssl_ctx(
|
684
689
|
self._http_options
|
685
690
|
)
|
686
|
-
|
691
|
+
# Initialize the aiohttp client session.
|
692
|
+
self._aiohttp_session: Optional[aiohttp.ClientSession] = None
|
687
693
|
|
688
694
|
retry_kwargs = retry_args(self._http_options.retry_options)
|
695
|
+
self._websocket_ssl_ctx = self._ensure_websocket_ssl_ctx(self._http_options)
|
689
696
|
self._retry = tenacity.Retrying(**retry_kwargs)
|
690
697
|
self._async_retry = tenacity.AsyncRetrying(**retry_kwargs)
|
691
698
|
|
699
|
+
async def _get_aiohttp_session(self) -> 'aiohttp.ClientSession':
|
700
|
+
"""Returns the aiohttp client session."""
|
701
|
+
if self._aiohttp_session is None or self._aiohttp_session.closed:
|
702
|
+
# Initialize the aiohttp client session if it's not set up or closed.
|
703
|
+
self._aiohttp_session = aiohttp.ClientSession(
|
704
|
+
connector=aiohttp.TCPConnector(limit=0),
|
705
|
+
trust_env=True,
|
706
|
+
read_bufsize=READ_BUFFER_SIZE,
|
707
|
+
)
|
708
|
+
return self._aiohttp_session
|
709
|
+
|
692
710
|
@staticmethod
|
693
711
|
def _ensure_httpx_ssl_ctx(
|
694
712
|
options: HttpOptions,
|
@@ -767,7 +785,6 @@ class BaseApiClient:
|
|
767
785
|
Returns:
|
768
786
|
An async aiohttp ClientSession._request args.
|
769
787
|
"""
|
770
|
-
|
771
788
|
verify = 'ssl' # keep it consistent with httpx.
|
772
789
|
async_args = options.async_client_args
|
773
790
|
ctx = async_args.get(verify) if async_args else None
|
@@ -1132,13 +1149,9 @@ class BaseApiClient:
|
|
1132
1149
|
|
1133
1150
|
if stream:
|
1134
1151
|
if self._use_aiohttp():
|
1135
|
-
|
1136
|
-
headers=http_request.headers,
|
1137
|
-
trust_env=True,
|
1138
|
-
read_bufsize=READ_BUFFER_SIZE,
|
1139
|
-
)
|
1152
|
+
self._aiohttp_session = await self._get_aiohttp_session()
|
1140
1153
|
try:
|
1141
|
-
response = await
|
1154
|
+
response = await self._aiohttp_session.request(
|
1142
1155
|
method=http_request.method,
|
1143
1156
|
url=http_request.url,
|
1144
1157
|
headers=http_request.headers,
|
@@ -1159,12 +1172,8 @@ class BaseApiClient:
|
|
1159
1172
|
self._ensure_aiohttp_ssl_ctx(self._http_options)
|
1160
1173
|
)
|
1161
1174
|
# Instantiate a new session with the updated SSL context.
|
1162
|
-
|
1163
|
-
|
1164
|
-
trust_env=True,
|
1165
|
-
read_bufsize=READ_BUFFER_SIZE,
|
1166
|
-
)
|
1167
|
-
response = await session.request(
|
1175
|
+
self._aiohttp_session = await self._get_aiohttp_session()
|
1176
|
+
response = await self._aiohttp_session.request(
|
1168
1177
|
method=http_request.method,
|
1169
1178
|
url=http_request.url,
|
1170
1179
|
headers=http_request.headers,
|
@@ -1174,7 +1183,7 @@ class BaseApiClient:
|
|
1174
1183
|
)
|
1175
1184
|
|
1176
1185
|
await errors.APIError.raise_for_async_response(response)
|
1177
|
-
return HttpResponse(response.headers, response
|
1186
|
+
return HttpResponse(response.headers, response)
|
1178
1187
|
else:
|
1179
1188
|
# aiohttp is not available. Fall back to httpx.
|
1180
1189
|
httpx_request = self._async_httpx_client.build_request(
|
@@ -1192,22 +1201,18 @@ class BaseApiClient:
|
|
1192
1201
|
return HttpResponse(client_response.headers, client_response)
|
1193
1202
|
else:
|
1194
1203
|
if self._use_aiohttp():
|
1204
|
+
self._aiohttp_session = await self._get_aiohttp_session()
|
1195
1205
|
try:
|
1196
|
-
|
1206
|
+
response = await self._aiohttp_session.request(
|
1207
|
+
method=http_request.method,
|
1208
|
+
url=http_request.url,
|
1197
1209
|
headers=http_request.headers,
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
headers=http_request.headers,
|
1205
|
-
data=data,
|
1206
|
-
timeout=aiohttp.ClientTimeout(connect=http_request.timeout),
|
1207
|
-
**self._async_client_session_request_args,
|
1208
|
-
)
|
1209
|
-
await errors.APIError.raise_for_async_response(response)
|
1210
|
-
return HttpResponse(response.headers, [await response.text()])
|
1210
|
+
data=data,
|
1211
|
+
timeout=aiohttp.ClientTimeout(connect=http_request.timeout),
|
1212
|
+
**self._async_client_session_request_args,
|
1213
|
+
)
|
1214
|
+
await errors.APIError.raise_for_async_response(response)
|
1215
|
+
return HttpResponse(response.headers, [await response.text()])
|
1211
1216
|
except (
|
1212
1217
|
aiohttp.ClientConnectorError,
|
1213
1218
|
aiohttp.ClientConnectorDNSError,
|
@@ -1221,21 +1226,17 @@ class BaseApiClient:
|
|
1221
1226
|
self._ensure_aiohttp_ssl_ctx(self._http_options)
|
1222
1227
|
)
|
1223
1228
|
# Instantiate a new session with the updated SSL context.
|
1224
|
-
|
1229
|
+
self._aiohttp_session = await self._get_aiohttp_session()
|
1230
|
+
response = await self._aiohttp_session.request(
|
1231
|
+
method=http_request.method,
|
1232
|
+
url=http_request.url,
|
1225
1233
|
headers=http_request.headers,
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
headers=http_request.headers,
|
1233
|
-
data=data,
|
1234
|
-
timeout=aiohttp.ClientTimeout(connect=http_request.timeout),
|
1235
|
-
**self._async_client_session_request_args,
|
1236
|
-
)
|
1237
|
-
await errors.APIError.raise_for_async_response(response)
|
1238
|
-
return HttpResponse(response.headers, [await response.text()])
|
1234
|
+
data=data,
|
1235
|
+
timeout=aiohttp.ClientTimeout(connect=http_request.timeout),
|
1236
|
+
**self._async_client_session_request_args,
|
1237
|
+
)
|
1238
|
+
await errors.APIError.raise_for_async_response(response)
|
1239
|
+
return HttpResponse(response.headers, [await response.text()])
|
1239
1240
|
else:
|
1240
1241
|
# aiohttp is not available. Fall back to httpx.
|
1241
1242
|
client_response = await self._async_httpx_client.request(
|
@@ -1551,85 +1552,79 @@ class BaseApiClient:
|
|
1551
1552
|
offset = 0
|
1552
1553
|
# Upload the file in chunks
|
1553
1554
|
if self._use_aiohttp(): # pylint: disable=g-import-not-at-top
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1555
|
+
self._aiohttp_session = await self._get_aiohttp_session()
|
1556
|
+
while True:
|
1557
|
+
if isinstance(file, io.IOBase):
|
1558
|
+
file_chunk = file.read(CHUNK_SIZE)
|
1559
|
+
else:
|
1560
|
+
file_chunk = await file.read(CHUNK_SIZE)
|
1561
|
+
chunk_size = 0
|
1562
|
+
if file_chunk:
|
1563
|
+
chunk_size = len(file_chunk)
|
1564
|
+
upload_command = 'upload'
|
1565
|
+
# If last chunk, finalize the upload.
|
1566
|
+
if chunk_size + offset >= upload_size:
|
1567
|
+
upload_command += ', finalize'
|
1568
|
+
http_options = http_options if http_options else self._http_options
|
1569
|
+
timeout = (
|
1570
|
+
http_options.get('timeout')
|
1571
|
+
if isinstance(http_options, dict)
|
1572
|
+
else http_options.timeout
|
1573
|
+
)
|
1574
|
+
if timeout is None:
|
1575
|
+
# Per request timeout is not configured. Check the global timeout.
|
1572
1576
|
timeout = (
|
1573
|
-
|
1574
|
-
if isinstance(
|
1575
|
-
else
|
1577
|
+
self._http_options.timeout
|
1578
|
+
if isinstance(self._http_options, dict)
|
1579
|
+
else self._http_options.timeout
|
1580
|
+
)
|
1581
|
+
timeout_in_seconds = get_timeout_in_seconds(timeout)
|
1582
|
+
upload_headers = {
|
1583
|
+
'X-Goog-Upload-Command': upload_command,
|
1584
|
+
'X-Goog-Upload-Offset': str(offset),
|
1585
|
+
'Content-Length': str(chunk_size),
|
1586
|
+
}
|
1587
|
+
populate_server_timeout_header(upload_headers, timeout_in_seconds)
|
1588
|
+
|
1589
|
+
retry_count = 0
|
1590
|
+
response = None
|
1591
|
+
while retry_count < MAX_RETRY_COUNT:
|
1592
|
+
response = await self._aiohttp_session.request(
|
1593
|
+
method='POST',
|
1594
|
+
url=upload_url,
|
1595
|
+
data=file_chunk,
|
1596
|
+
headers=upload_headers,
|
1597
|
+
timeout=aiohttp.ClientTimeout(connect=timeout_in_seconds),
|
1576
1598
|
)
|
1577
|
-
if timeout is None:
|
1578
|
-
# Per request timeout is not configured. Check the global timeout.
|
1579
|
-
timeout = (
|
1580
|
-
self._http_options.timeout
|
1581
|
-
if isinstance(self._http_options, dict)
|
1582
|
-
else self._http_options.timeout
|
1583
|
-
)
|
1584
|
-
timeout_in_seconds = get_timeout_in_seconds(timeout)
|
1585
|
-
upload_headers = {
|
1586
|
-
'X-Goog-Upload-Command': upload_command,
|
1587
|
-
'X-Goog-Upload-Offset': str(offset),
|
1588
|
-
'Content-Length': str(chunk_size),
|
1589
|
-
}
|
1590
|
-
populate_server_timeout_header(upload_headers, timeout_in_seconds)
|
1591
|
-
|
1592
|
-
retry_count = 0
|
1593
|
-
response = None
|
1594
|
-
while retry_count < MAX_RETRY_COUNT:
|
1595
|
-
response = await session.request(
|
1596
|
-
method='POST',
|
1597
|
-
url=upload_url,
|
1598
|
-
data=file_chunk,
|
1599
|
-
headers=upload_headers,
|
1600
|
-
timeout=aiohttp.ClientTimeout(connect=timeout_in_seconds),
|
1601
|
-
)
|
1602
|
-
|
1603
|
-
if response.headers.get('X-Goog-Upload-Status'):
|
1604
|
-
break
|
1605
|
-
delay_seconds = INITIAL_RETRY_DELAY * (
|
1606
|
-
DELAY_MULTIPLIER**retry_count
|
1607
|
-
)
|
1608
|
-
retry_count += 1
|
1609
|
-
time.sleep(delay_seconds)
|
1610
|
-
|
1611
|
-
offset += chunk_size
|
1612
|
-
if (
|
1613
|
-
response is not None
|
1614
|
-
and response.headers.get('X-Goog-Upload-Status') != 'active'
|
1615
|
-
):
|
1616
|
-
break # upload is complete or it has been interrupted.
|
1617
1599
|
|
1618
|
-
if
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
|
1600
|
+
if response.headers.get('X-Goog-Upload-Status'):
|
1601
|
+
break
|
1602
|
+
delay_seconds = INITIAL_RETRY_DELAY * (DELAY_MULTIPLIER**retry_count)
|
1603
|
+
retry_count += 1
|
1604
|
+
await asyncio.sleep(delay_seconds)
|
1605
|
+
|
1606
|
+
offset += chunk_size
|
1623
1607
|
if (
|
1624
1608
|
response is not None
|
1625
|
-
and response.headers.get('X-Goog-Upload-Status') != '
|
1609
|
+
and response.headers.get('X-Goog-Upload-Status') != 'active'
|
1626
1610
|
):
|
1611
|
+
break # upload is complete or it has been interrupted.
|
1612
|
+
|
1613
|
+
if upload_size <= offset: # Status is not finalized.
|
1627
1614
|
raise ValueError(
|
1628
|
-
'
|
1615
|
+
f'All content has been uploaded, but the upload status is not'
|
1616
|
+
f' finalized.'
|
1629
1617
|
)
|
1630
|
-
|
1631
|
-
|
1618
|
+
if (
|
1619
|
+
response is not None
|
1620
|
+
and response.headers.get('X-Goog-Upload-Status') != 'final'
|
1621
|
+
):
|
1622
|
+
raise ValueError(
|
1623
|
+
'Failed to upload file: Upload status is not finalized.'
|
1632
1624
|
)
|
1625
|
+
return HttpResponse(
|
1626
|
+
response.headers, response_stream=[await response.text()]
|
1627
|
+
)
|
1633
1628
|
else:
|
1634
1629
|
# aiohttp is not available. Fall back to httpx.
|
1635
1630
|
while True:
|
@@ -1735,23 +1730,19 @@ class BaseApiClient:
|
|
1735
1730
|
data = http_request.data
|
1736
1731
|
|
1737
1732
|
if self._use_aiohttp():
|
1738
|
-
|
1733
|
+
self._aiohttp_session = await self._get_aiohttp_session()
|
1734
|
+
response = await self._aiohttp_session.request(
|
1735
|
+
method=http_request.method,
|
1736
|
+
url=http_request.url,
|
1739
1737
|
headers=http_request.headers,
|
1740
|
-
|
1741
|
-
|
1742
|
-
)
|
1743
|
-
|
1744
|
-
method=http_request.method,
|
1745
|
-
url=http_request.url,
|
1746
|
-
headers=http_request.headers,
|
1747
|
-
data=data,
|
1748
|
-
timeout=aiohttp.ClientTimeout(connect=http_request.timeout),
|
1749
|
-
)
|
1750
|
-
await errors.APIError.raise_for_async_response(response)
|
1738
|
+
data=data,
|
1739
|
+
timeout=aiohttp.ClientTimeout(connect=http_request.timeout),
|
1740
|
+
)
|
1741
|
+
await errors.APIError.raise_for_async_response(response)
|
1751
1742
|
|
1752
|
-
|
1753
|
-
|
1754
|
-
|
1743
|
+
return HttpResponse(
|
1744
|
+
response.headers, byte_stream=[await response.read()]
|
1745
|
+
).byte_stream[0]
|
1755
1746
|
else:
|
1756
1747
|
# aiohttp is not available. Fall back to httpx.
|
1757
1748
|
client_response = await self._async_httpx_client.request(
|
@@ -1290,10 +1290,14 @@ def t_metrics(
|
|
1290
1290
|
|
1291
1291
|
elif hasattr(metric, 'prompt_template') and metric.prompt_template:
|
1292
1292
|
pointwise_spec = {'metric_prompt_template': metric.prompt_template}
|
1293
|
-
system_instruction = getv(
|
1293
|
+
system_instruction = getv(
|
1294
|
+
metric, ['judge_model_system_instruction']
|
1295
|
+
)
|
1294
1296
|
if system_instruction:
|
1295
1297
|
pointwise_spec['system_instruction'] = system_instruction
|
1296
|
-
return_raw_output = getv(
|
1298
|
+
return_raw_output = getv(
|
1299
|
+
metric, ['return_raw_output']
|
1300
|
+
)
|
1297
1301
|
if return_raw_output:
|
1298
1302
|
pointwise_spec['custom_output_format_config'] = { # type: ignore[assignment]
|
1299
1303
|
'return_raw_output': return_raw_output
|
@@ -42,6 +42,9 @@ class APIError(Exception):
|
|
42
42
|
Union['ReplayResponse', httpx.Response, 'aiohttp.ClientResponse']
|
43
43
|
] = None,
|
44
44
|
):
|
45
|
+
if isinstance(response_json, list) and len(response_json) == 1:
|
46
|
+
response_json = response_json[0]
|
47
|
+
|
45
48
|
self.response = response
|
46
49
|
self.details = response_json
|
47
50
|
self.message = self._get_message(response_json)
|
@@ -87,6 +87,42 @@ def _FetchPredictOperationParameters_to_vertex(
|
|
87
87
|
return to_object
|
88
88
|
|
89
89
|
|
90
|
+
def _GetProjectOperationParameters_to_vertex(
|
91
|
+
from_object: Union[dict[str, Any], object],
|
92
|
+
parent_object: Optional[dict[str, Any]] = None,
|
93
|
+
) -> dict[str, Any]:
|
94
|
+
to_object: dict[str, Any] = {}
|
95
|
+
if getv(from_object, ['operation_id']) is not None:
|
96
|
+
setv(
|
97
|
+
to_object, ['_url', 'operation_id'], getv(from_object, ['operation_id'])
|
98
|
+
)
|
99
|
+
|
100
|
+
if getv(from_object, ['config']) is not None:
|
101
|
+
setv(to_object, ['config'], getv(from_object, ['config']))
|
102
|
+
|
103
|
+
return to_object
|
104
|
+
|
105
|
+
|
106
|
+
def _ProjectOperation_from_vertex(
|
107
|
+
from_object: Union[dict[str, Any], object],
|
108
|
+
parent_object: Optional[dict[str, Any]] = None,
|
109
|
+
) -> dict[str, Any]:
|
110
|
+
to_object: dict[str, Any] = {}
|
111
|
+
if getv(from_object, ['name']) is not None:
|
112
|
+
setv(to_object, ['name'], getv(from_object, ['name']))
|
113
|
+
|
114
|
+
if getv(from_object, ['metadata']) is not None:
|
115
|
+
setv(to_object, ['metadata'], getv(from_object, ['metadata']))
|
116
|
+
|
117
|
+
if getv(from_object, ['done']) is not None:
|
118
|
+
setv(to_object, ['done'], getv(from_object, ['done']))
|
119
|
+
|
120
|
+
if getv(from_object, ['error']) is not None:
|
121
|
+
setv(to_object, ['error'], getv(from_object, ['error']))
|
122
|
+
|
123
|
+
return to_object
|
124
|
+
|
125
|
+
|
90
126
|
class Operations(_api_module.BaseModule):
|
91
127
|
|
92
128
|
def _get_videos_operation(
|
@@ -188,6 +224,58 @@ class Operations(_api_module.BaseModule):
|
|
188
224
|
|
189
225
|
return response_dict
|
190
226
|
|
227
|
+
def _get(
|
228
|
+
self,
|
229
|
+
*,
|
230
|
+
operation_id: str,
|
231
|
+
config: Optional[types.GetOperationConfigOrDict] = None,
|
232
|
+
) -> types.ProjectOperation:
|
233
|
+
parameter_model = types._GetProjectOperationParameters(
|
234
|
+
operation_id=operation_id,
|
235
|
+
config=config,
|
236
|
+
)
|
237
|
+
|
238
|
+
request_url_dict: Optional[dict[str, str]]
|
239
|
+
if not self._api_client.vertexai:
|
240
|
+
raise ValueError('This method is only supported in the Vertex AI client.')
|
241
|
+
else:
|
242
|
+
request_dict = _GetProjectOperationParameters_to_vertex(parameter_model)
|
243
|
+
request_url_dict = request_dict.get('_url')
|
244
|
+
if request_url_dict:
|
245
|
+
path = 'operations/{operation_id}'.format_map(request_url_dict)
|
246
|
+
else:
|
247
|
+
path = 'operations/{operation_id}'
|
248
|
+
|
249
|
+
query_params = request_dict.get('_query')
|
250
|
+
if query_params:
|
251
|
+
path = f'{path}?{urlencode(query_params)}'
|
252
|
+
# TODO: remove the hack that pops config.
|
253
|
+
request_dict.pop('config', None)
|
254
|
+
|
255
|
+
http_options: Optional[types.HttpOptions] = None
|
256
|
+
if (
|
257
|
+
parameter_model.config is not None
|
258
|
+
and parameter_model.config.http_options is not None
|
259
|
+
):
|
260
|
+
http_options = parameter_model.config.http_options
|
261
|
+
|
262
|
+
request_dict = _common.convert_to_dict(request_dict)
|
263
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
264
|
+
|
265
|
+
response = self._api_client.request('get', path, request_dict, http_options)
|
266
|
+
|
267
|
+
response_dict = '' if not response.body else json.loads(response.body)
|
268
|
+
|
269
|
+
if self._api_client.vertexai:
|
270
|
+
response_dict = _ProjectOperation_from_vertex(response_dict)
|
271
|
+
|
272
|
+
return_value = types.ProjectOperation._from_response(
|
273
|
+
response=response_dict, kwargs=parameter_model.model_dump()
|
274
|
+
)
|
275
|
+
|
276
|
+
self._api_client._verify_response(return_value)
|
277
|
+
return return_value
|
278
|
+
|
191
279
|
T = TypeVar('T', bound=types.Operation)
|
192
280
|
|
193
281
|
def get(
|
@@ -344,6 +432,60 @@ class AsyncOperations(_api_module.BaseModule):
|
|
344
432
|
|
345
433
|
return response_dict
|
346
434
|
|
435
|
+
async def _get(
|
436
|
+
self,
|
437
|
+
*,
|
438
|
+
operation_id: str,
|
439
|
+
config: Optional[types.GetOperationConfigOrDict] = None,
|
440
|
+
) -> types.ProjectOperation:
|
441
|
+
parameter_model = types._GetProjectOperationParameters(
|
442
|
+
operation_id=operation_id,
|
443
|
+
config=config,
|
444
|
+
)
|
445
|
+
|
446
|
+
request_url_dict: Optional[dict[str, str]]
|
447
|
+
if not self._api_client.vertexai:
|
448
|
+
raise ValueError('This method is only supported in the Vertex AI client.')
|
449
|
+
else:
|
450
|
+
request_dict = _GetProjectOperationParameters_to_vertex(parameter_model)
|
451
|
+
request_url_dict = request_dict.get('_url')
|
452
|
+
if request_url_dict:
|
453
|
+
path = 'operations/{operation_id}'.format_map(request_url_dict)
|
454
|
+
else:
|
455
|
+
path = 'operations/{operation_id}'
|
456
|
+
|
457
|
+
query_params = request_dict.get('_query')
|
458
|
+
if query_params:
|
459
|
+
path = f'{path}?{urlencode(query_params)}'
|
460
|
+
# TODO: remove the hack that pops config.
|
461
|
+
request_dict.pop('config', None)
|
462
|
+
|
463
|
+
http_options: Optional[types.HttpOptions] = None
|
464
|
+
if (
|
465
|
+
parameter_model.config is not None
|
466
|
+
and parameter_model.config.http_options is not None
|
467
|
+
):
|
468
|
+
http_options = parameter_model.config.http_options
|
469
|
+
|
470
|
+
request_dict = _common.convert_to_dict(request_dict)
|
471
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
472
|
+
|
473
|
+
response = await self._api_client.async_request(
|
474
|
+
'get', path, request_dict, http_options
|
475
|
+
)
|
476
|
+
|
477
|
+
response_dict = '' if not response.body else json.loads(response.body)
|
478
|
+
|
479
|
+
if self._api_client.vertexai:
|
480
|
+
response_dict = _ProjectOperation_from_vertex(response_dict)
|
481
|
+
|
482
|
+
return_value = types.ProjectOperation._from_response(
|
483
|
+
response=response_dict, kwargs=parameter_model.model_dump()
|
484
|
+
)
|
485
|
+
|
486
|
+
self._api_client._verify_response(return_value)
|
487
|
+
return return_value
|
488
|
+
|
347
489
|
T = TypeVar('T', bound=types.Operation)
|
348
490
|
|
349
491
|
async def get(
|
@@ -209,6 +209,9 @@ def _CreateTuningJobConfig_to_mldev(
|
|
209
209
|
'evaluation_config parameter is not supported in Gemini API.'
|
210
210
|
)
|
211
211
|
|
212
|
+
if getv(from_object, ['labels']) is not None:
|
213
|
+
raise ValueError('labels parameter is not supported in Gemini API.')
|
214
|
+
|
212
215
|
return to_object
|
213
216
|
|
214
217
|
|
@@ -507,6 +510,9 @@ def _CreateTuningJobConfig_to_vertex(
|
|
507
510
|
),
|
508
511
|
)
|
509
512
|
|
513
|
+
if getv(from_object, ['labels']) is not None:
|
514
|
+
setv(parent_object, ['labels'], getv(from_object, ['labels']))
|
515
|
+
|
510
516
|
return to_object
|
511
517
|
|
512
518
|
|
@@ -609,6 +609,26 @@ class VideoGenerationReferenceType(_common.CaseInSensitiveEnum):
|
|
609
609
|
such as 'anime', 'photography', 'origami', etc."""
|
610
610
|
|
611
611
|
|
612
|
+
class VideoGenerationMaskMode(_common.CaseInSensitiveEnum):
|
613
|
+
"""Enum for the mask mode of a video generation mask."""
|
614
|
+
|
615
|
+
INSERT = 'INSERT'
|
616
|
+
"""The image mask contains a masked rectangular region which is
|
617
|
+
applied on the first frame of the input video. The object described in
|
618
|
+
the prompt is inserted into this region and will appear in subsequent
|
619
|
+
frames."""
|
620
|
+
REMOVE = 'REMOVE'
|
621
|
+
"""The image mask is used to determine an object in the
|
622
|
+
first video frame to track. This object is removed from the video."""
|
623
|
+
REMOVE_STATIC = 'REMOVE_STATIC'
|
624
|
+
"""The image mask is used to determine a region in the
|
625
|
+
video. Objects in this region will be removed."""
|
626
|
+
OUTPAINT = 'OUTPAINT'
|
627
|
+
"""The image mask contains a masked rectangular region where
|
628
|
+
the input video will go. The remaining area will be generated. Video
|
629
|
+
masks are not supported."""
|
630
|
+
|
631
|
+
|
612
632
|
class VideoCompressionQuality(_common.CaseInSensitiveEnum):
|
613
633
|
"""Enum that controls the compression quality of the generated videos."""
|
614
634
|
|
@@ -8422,7 +8442,7 @@ class VideoGenerationMask(_common.BaseModel):
|
|
8422
8442
|
default=None,
|
8423
8443
|
description="""The image mask to use for generating videos.""",
|
8424
8444
|
)
|
8425
|
-
mask_mode: Optional[
|
8445
|
+
mask_mode: Optional[VideoGenerationMaskMode] = Field(
|
8426
8446
|
default=None,
|
8427
8447
|
description="""Describes how the mask will be used. Inpainting masks must
|
8428
8448
|
match the aspect ratio of the input video. Outpainting masks can be
|
@@ -8436,7 +8456,7 @@ class VideoGenerationMaskDict(TypedDict, total=False):
|
|
8436
8456
|
image: Optional[ImageDict]
|
8437
8457
|
"""The image mask to use for generating videos."""
|
8438
8458
|
|
8439
|
-
mask_mode: Optional[
|
8459
|
+
mask_mode: Optional[VideoGenerationMaskMode]
|
8440
8460
|
"""Describes how the mask will be used. Inpainting masks must
|
8441
8461
|
match the aspect ratio of the input video. Outpainting masks can be
|
8442
8462
|
either 9:16 or 16:9."""
|
@@ -10410,6 +10430,10 @@ class CreateTuningJobConfig(_common.BaseModel):
|
|
10410
10430
|
evaluation_config: Optional[EvaluationConfig] = Field(
|
10411
10431
|
default=None, description="""Evaluation config for the tuning job."""
|
10412
10432
|
)
|
10433
|
+
labels: Optional[dict[str, str]] = Field(
|
10434
|
+
default=None,
|
10435
|
+
description="""Optional. The labels with user-defined metadata to organize TuningJob and generated resources such as Model and Endpoint. Label keys and values can be no longer than 64 characters (Unicode codepoints), can only contain lowercase letters, numeric characters, underscores and dashes. International characters are allowed. See https://goo.gl/xmQnxf for more information and examples of labels.""",
|
10436
|
+
)
|
10413
10437
|
|
10414
10438
|
|
10415
10439
|
class CreateTuningJobConfigDict(TypedDict, total=False):
|
@@ -10451,6 +10475,9 @@ class CreateTuningJobConfigDict(TypedDict, total=False):
|
|
10451
10475
|
evaluation_config: Optional[EvaluationConfigDict]
|
10452
10476
|
"""Evaluation config for the tuning job."""
|
10453
10477
|
|
10478
|
+
labels: Optional[dict[str, str]]
|
10479
|
+
"""Optional. The labels with user-defined metadata to organize TuningJob and generated resources such as Model and Endpoint. Label keys and values can be no longer than 64 characters (Unicode codepoints), can only contain lowercase letters, numeric characters, underscores and dashes. International characters are allowed. See https://goo.gl/xmQnxf for more information and examples of labels."""
|
10480
|
+
|
10454
10481
|
|
10455
10482
|
CreateTuningJobConfigOrDict = Union[
|
10456
10483
|
CreateTuningJobConfig, CreateTuningJobConfigDict
|
@@ -12331,6 +12358,78 @@ _FetchPredictOperationParametersOrDict = Union[
|
|
12331
12358
|
]
|
12332
12359
|
|
12333
12360
|
|
12361
|
+
class _GetProjectOperationParameters(_common.BaseModel):
|
12362
|
+
"""Parameters for the getProjectOperation method."""
|
12363
|
+
|
12364
|
+
operation_id: Optional[str] = Field(
|
12365
|
+
default=None,
|
12366
|
+
description="""The ID of the project-level Vertex operation to get. For example if the operation resource name is
|
12367
|
+
projects/123/locations/us-central1/operations/456, the operation_id is
|
12368
|
+
456.""",
|
12369
|
+
)
|
12370
|
+
config: Optional[GetOperationConfig] = Field(
|
12371
|
+
default=None,
|
12372
|
+
description="""Used to override the default configuration.""",
|
12373
|
+
)
|
12374
|
+
|
12375
|
+
|
12376
|
+
class _GetProjectOperationParametersDict(TypedDict, total=False):
|
12377
|
+
"""Parameters for the getProjectOperation method."""
|
12378
|
+
|
12379
|
+
operation_id: Optional[str]
|
12380
|
+
"""The ID of the project-level Vertex operation to get. For example if the operation resource name is
|
12381
|
+
projects/123/locations/us-central1/operations/456, the operation_id is
|
12382
|
+
456."""
|
12383
|
+
|
12384
|
+
config: Optional[GetOperationConfigDict]
|
12385
|
+
"""Used to override the default configuration."""
|
12386
|
+
|
12387
|
+
|
12388
|
+
_GetProjectOperationParametersOrDict = Union[
|
12389
|
+
_GetProjectOperationParameters, _GetProjectOperationParametersDict
|
12390
|
+
]
|
12391
|
+
|
12392
|
+
|
12393
|
+
class ProjectOperation(_common.BaseModel):
|
12394
|
+
"""A project-level operation in Vertex."""
|
12395
|
+
|
12396
|
+
name: Optional[str] = Field(
|
12397
|
+
default=None,
|
12398
|
+
description="""The server-assigned name, which is only unique within the same service that originally returns it. If you use the default HTTP mapping, the `name` should be a resource name ending with `operations/{unique_id}`.""",
|
12399
|
+
)
|
12400
|
+
metadata: Optional[dict[str, Any]] = Field(
|
12401
|
+
default=None,
|
12402
|
+
description="""Service-specific metadata associated with the operation. It typically contains progress information and common metadata such as create time. Some services might not provide such metadata. Any method that returns a long-running operation should document the metadata type, if any.""",
|
12403
|
+
)
|
12404
|
+
done: Optional[bool] = Field(
|
12405
|
+
default=None,
|
12406
|
+
description="""If the value is `false`, it means the operation is still in progress. If `true`, the operation is completed, and either `error` or `response` is available.""",
|
12407
|
+
)
|
12408
|
+
error: Optional[dict[str, Any]] = Field(
|
12409
|
+
default=None,
|
12410
|
+
description="""The error result of the operation in case of failure or cancellation.""",
|
12411
|
+
)
|
12412
|
+
|
12413
|
+
|
12414
|
+
class ProjectOperationDict(TypedDict, total=False):
|
12415
|
+
"""A project-level operation in Vertex."""
|
12416
|
+
|
12417
|
+
name: Optional[str]
|
12418
|
+
"""The server-assigned name, which is only unique within the same service that originally returns it. If you use the default HTTP mapping, the `name` should be a resource name ending with `operations/{unique_id}`."""
|
12419
|
+
|
12420
|
+
metadata: Optional[dict[str, Any]]
|
12421
|
+
"""Service-specific metadata associated with the operation. It typically contains progress information and common metadata such as create time. Some services might not provide such metadata. Any method that returns a long-running operation should document the metadata type, if any."""
|
12422
|
+
|
12423
|
+
done: Optional[bool]
|
12424
|
+
"""If the value is `false`, it means the operation is still in progress. If `true`, the operation is completed, and either `error` or `response` is available."""
|
12425
|
+
|
12426
|
+
error: Optional[dict[str, Any]]
|
12427
|
+
"""The error result of the operation in case of failure or cancellation."""
|
12428
|
+
|
12429
|
+
|
12430
|
+
ProjectOperationOrDict = Union[ProjectOperation, ProjectOperationDict]
|
12431
|
+
|
12432
|
+
|
12334
12433
|
class TestTableItem(_common.BaseModel):
|
12335
12434
|
|
12336
12435
|
name: Optional[str] = Field(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: google-genai
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.37.0
|
4
4
|
Summary: GenAI Python SDK
|
5
5
|
Author-email: Google LLC <googleapis-packages@google.com>
|
6
6
|
License: Apache-2.0
|
@@ -58,6 +58,12 @@ APIs.
|
|
58
58
|
pip install google-genai
|
59
59
|
```
|
60
60
|
|
61
|
+
<small>With `uv`:</small>
|
62
|
+
|
63
|
+
```sh
|
64
|
+
uv pip install google-genai
|
65
|
+
```
|
66
|
+
|
61
67
|
## Imports
|
62
68
|
|
63
69
|
```python
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{google_genai-1.35.0 → google_genai-1.37.0}/google/genai/_automatic_function_calling_util.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|