google-genai 1.28.0__py3-none-any.whl → 1.30.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 +144 -55
- google/genai/_automatic_function_calling_util.py +35 -7
- google/genai/_live_converters.py +64 -6
- google/genai/_tokens_converters.py +25 -3
- google/genai/_transformers.py +51 -0
- google/genai/batches.py +44 -5
- google/genai/caches.py +50 -6
- google/genai/chats.py +1 -0
- google/genai/errors.py +5 -2
- google/genai/live.py +92 -88
- google/genai/models.py +443 -36
- google/genai/tokens.py +1 -0
- google/genai/tunings.py +314 -43
- google/genai/types.py +1398 -293
- google/genai/version.py +1 -1
- {google_genai-1.28.0.dist-info → google_genai-1.30.0.dist-info}/METADATA +2 -2
- google_genai-1.30.0.dist-info/RECORD +35 -0
- google_genai-1.28.0.dist-info/RECORD +0 -35
- {google_genai-1.28.0.dist-info → google_genai-1.30.0.dist-info}/WHEEL +0 -0
- {google_genai-1.28.0.dist-info → google_genai-1.30.0.dist-info}/licenses/LICENSE +0 -0
- {google_genai-1.28.0.dist-info → google_genai-1.30.0.dist-info}/top_level.txt +0 -0
google/genai/_api_client.py
CHANGED
@@ -20,23 +20,21 @@ The BaseApiClient is intended to be a private module and is subject to change.
|
|
20
20
|
"""
|
21
21
|
|
22
22
|
import asyncio
|
23
|
-
from collections.abc import
|
23
|
+
from collections.abc import Generator
|
24
24
|
import copy
|
25
25
|
from dataclasses import dataclass
|
26
|
-
import datetime
|
27
|
-
import http
|
28
26
|
import inspect
|
29
27
|
import io
|
30
28
|
import json
|
31
29
|
import logging
|
32
30
|
import math
|
33
31
|
import os
|
34
|
-
import ssl
|
35
32
|
import random
|
33
|
+
import ssl
|
36
34
|
import sys
|
37
35
|
import threading
|
38
36
|
import time
|
39
|
-
from typing import Any, AsyncIterator,
|
37
|
+
from typing import Any, AsyncIterator, Iterator, Optional, Tuple, TYPE_CHECKING, Union
|
40
38
|
from urllib.parse import urlparse
|
41
39
|
from urllib.parse import urlunparse
|
42
40
|
|
@@ -48,7 +46,6 @@ from google.auth.credentials import Credentials
|
|
48
46
|
from google.auth.transport.requests import Request
|
49
47
|
import httpx
|
50
48
|
from pydantic import BaseModel
|
51
|
-
from pydantic import Field
|
52
49
|
from pydantic import ValidationError
|
53
50
|
import tenacity
|
54
51
|
|
@@ -56,11 +53,11 @@ from . import _common
|
|
56
53
|
from . import errors
|
57
54
|
from . import version
|
58
55
|
from .types import HttpOptions
|
59
|
-
from .types import HttpOptionsDict
|
60
56
|
from .types import HttpOptionsOrDict
|
61
57
|
from .types import HttpResponse as SdkHttpResponse
|
62
58
|
from .types import HttpRetryOptions
|
63
59
|
|
60
|
+
|
64
61
|
try:
|
65
62
|
from websockets.asyncio.client import connect as ws_connect
|
66
63
|
except ModuleNotFoundError:
|
@@ -238,12 +235,12 @@ class HttpResponse:
|
|
238
235
|
self.headers = headers
|
239
236
|
elif isinstance(headers, httpx.Headers):
|
240
237
|
self.headers = {
|
241
|
-
|
242
|
-
|
238
|
+
key: ', '.join(headers.get_list(key)) for key in headers.keys()
|
239
|
+
}
|
243
240
|
elif type(headers).__name__ == 'CIMultiDictProxy':
|
244
241
|
self.headers = {
|
245
|
-
key: ', '.join(headers.getall(key))
|
246
|
-
|
242
|
+
key: ', '.join(headers.getall(key)) for key in headers.keys()
|
243
|
+
}
|
247
244
|
|
248
245
|
self.status_code: int = 200
|
249
246
|
self.response_stream = response_stream
|
@@ -265,68 +262,32 @@ class HttpResponse:
|
|
265
262
|
def json(self) -> Any:
|
266
263
|
if not self.response_stream[0]: # Empty response
|
267
264
|
return ''
|
268
|
-
return
|
265
|
+
return self._load_json_from_response(self.response_stream[0])
|
269
266
|
|
270
267
|
def segments(self) -> Generator[Any, None, None]:
|
271
268
|
if isinstance(self.response_stream, list):
|
272
269
|
# list of objects retrieved from replay or from non-streaming API.
|
273
270
|
for chunk in self.response_stream:
|
274
|
-
yield
|
271
|
+
yield self._load_json_from_response(chunk) if chunk else {}
|
275
272
|
elif self.response_stream is None:
|
276
273
|
yield from []
|
277
274
|
else:
|
278
275
|
# Iterator of objects retrieved from the API.
|
279
|
-
for chunk in self.
|
280
|
-
|
281
|
-
# In streaming mode, the chunk of JSON is prefixed with "data:" which
|
282
|
-
# we must strip before parsing.
|
283
|
-
if not isinstance(chunk, str):
|
284
|
-
chunk = chunk.decode('utf-8')
|
285
|
-
if chunk.startswith('data: '):
|
286
|
-
chunk = chunk[len('data: ') :]
|
287
|
-
yield json.loads(chunk)
|
276
|
+
for chunk in self._iter_response_stream():
|
277
|
+
yield self._load_json_from_response(chunk)
|
288
278
|
|
289
279
|
async def async_segments(self) -> AsyncIterator[Any]:
|
290
280
|
if isinstance(self.response_stream, list):
|
291
281
|
# list of objects retrieved from replay or from non-streaming API.
|
292
282
|
for chunk in self.response_stream:
|
293
|
-
yield
|
283
|
+
yield self._load_json_from_response(chunk) if chunk else {}
|
294
284
|
elif self.response_stream is None:
|
295
285
|
async for c in []: # type: ignore[attr-defined]
|
296
286
|
yield c
|
297
287
|
else:
|
298
288
|
# Iterator of objects retrieved from the API.
|
299
|
-
|
300
|
-
|
301
|
-
# This is httpx.Response.
|
302
|
-
if chunk:
|
303
|
-
# In async streaming mode, the chunk of JSON is prefixed with
|
304
|
-
# "data:" which we must strip before parsing.
|
305
|
-
if not isinstance(chunk, str):
|
306
|
-
chunk = chunk.decode('utf-8')
|
307
|
-
if chunk.startswith('data: '):
|
308
|
-
chunk = chunk[len('data: ') :]
|
309
|
-
yield json.loads(chunk)
|
310
|
-
elif hasattr(self.response_stream, 'content'):
|
311
|
-
# This is aiohttp.ClientResponse.
|
312
|
-
try:
|
313
|
-
while True:
|
314
|
-
chunk = await self.response_stream.content.readline()
|
315
|
-
if not chunk:
|
316
|
-
break
|
317
|
-
# In async streaming mode, the chunk of JSON is prefixed with
|
318
|
-
# "data:" which we must strip before parsing.
|
319
|
-
chunk = chunk.decode('utf-8')
|
320
|
-
if chunk.startswith('data: '):
|
321
|
-
chunk = chunk[len('data: ') :]
|
322
|
-
chunk = chunk.strip()
|
323
|
-
if chunk:
|
324
|
-
yield json.loads(chunk)
|
325
|
-
finally:
|
326
|
-
if hasattr(self, '_session') and self._session:
|
327
|
-
await self._session.close()
|
328
|
-
else:
|
329
|
-
raise ValueError('Error parsing streaming response.')
|
289
|
+
async for chunk in self._aiter_response_stream():
|
290
|
+
yield self._load_json_from_response(chunk)
|
330
291
|
|
331
292
|
def byte_segments(self) -> Generator[Union[bytes, Any], None, None]:
|
332
293
|
if isinstance(self.byte_stream, list):
|
@@ -345,6 +306,134 @@ class HttpResponse:
|
|
345
306
|
for attribute in dir(self):
|
346
307
|
response_payload[attribute] = copy.deepcopy(getattr(self, attribute))
|
347
308
|
|
309
|
+
def _iter_response_stream(self) -> Iterator[str]:
|
310
|
+
"""Iterates over chunks retrieved from the API."""
|
311
|
+
if not isinstance(self.response_stream, httpx.Response):
|
312
|
+
raise TypeError(
|
313
|
+
'Expected self.response_stream to be an httpx.Response object, '
|
314
|
+
f'but got {type(self.response_stream).__name__}.'
|
315
|
+
)
|
316
|
+
|
317
|
+
chunk = ''
|
318
|
+
balance = 0
|
319
|
+
for line in self.response_stream.iter_lines():
|
320
|
+
if not line:
|
321
|
+
continue
|
322
|
+
|
323
|
+
# In streaming mode, the response of JSON is prefixed with "data: " which
|
324
|
+
# we must strip before parsing.
|
325
|
+
if line.startswith('data: '):
|
326
|
+
yield line[len('data: '):]
|
327
|
+
continue
|
328
|
+
|
329
|
+
# When API returns an error message, it comes line by line. So we buffer
|
330
|
+
# the lines until a complete JSON string is read. A complete JSON string
|
331
|
+
# is found when the balance is 0.
|
332
|
+
for c in line:
|
333
|
+
if c == '{':
|
334
|
+
balance += 1
|
335
|
+
elif c == '}':
|
336
|
+
balance -= 1
|
337
|
+
|
338
|
+
chunk += line
|
339
|
+
if balance == 0:
|
340
|
+
yield chunk
|
341
|
+
chunk = ''
|
342
|
+
|
343
|
+
# If there is any remaining chunk, yield it.
|
344
|
+
if chunk:
|
345
|
+
yield chunk
|
346
|
+
|
347
|
+
async def _aiter_response_stream(self) -> AsyncIterator[str]:
|
348
|
+
"""Asynchronously iterates over chunks retrieved from the API."""
|
349
|
+
is_valid_response = isinstance(self.response_stream, httpx.Response) or (
|
350
|
+
has_aiohttp and isinstance(self.response_stream, aiohttp.ClientResponse)
|
351
|
+
)
|
352
|
+
if not is_valid_response:
|
353
|
+
raise TypeError(
|
354
|
+
'Expected self.response_stream to be an httpx.Response or'
|
355
|
+
' aiohttp.ClientResponse object, but got'
|
356
|
+
f' {type(self.response_stream).__name__}.'
|
357
|
+
)
|
358
|
+
|
359
|
+
chunk = ''
|
360
|
+
balance = 0
|
361
|
+
# httpx.Response has a dedicated async line iterator.
|
362
|
+
if isinstance(self.response_stream, httpx.Response):
|
363
|
+
async for line in self.response_stream.aiter_lines():
|
364
|
+
if not line:
|
365
|
+
continue
|
366
|
+
# In streaming mode, the response of JSON is prefixed with "data: "
|
367
|
+
# which we must strip before parsing.
|
368
|
+
if line.startswith('data: '):
|
369
|
+
yield line[len('data: '):]
|
370
|
+
continue
|
371
|
+
|
372
|
+
# When API returns an error message, it comes line by line. So we buffer
|
373
|
+
# the lines until a complete JSON string is read. A complete JSON string
|
374
|
+
# is found when the balance is 0.
|
375
|
+
for c in line:
|
376
|
+
if c == '{':
|
377
|
+
balance += 1
|
378
|
+
elif c == '}':
|
379
|
+
balance -= 1
|
380
|
+
|
381
|
+
chunk += line
|
382
|
+
if balance == 0:
|
383
|
+
yield chunk
|
384
|
+
chunk = ''
|
385
|
+
|
386
|
+
# aiohttp.ClientResponse uses a content stream that we read line by line.
|
387
|
+
elif has_aiohttp and isinstance(
|
388
|
+
self.response_stream, aiohttp.ClientResponse
|
389
|
+
):
|
390
|
+
while True:
|
391
|
+
# Read a line from the stream. This returns bytes.
|
392
|
+
line_bytes = await self.response_stream.content.readline()
|
393
|
+
if not line_bytes:
|
394
|
+
break
|
395
|
+
# Decode the bytes and remove trailing whitespace and newlines.
|
396
|
+
line = line_bytes.decode('utf-8').rstrip()
|
397
|
+
if not line:
|
398
|
+
continue
|
399
|
+
|
400
|
+
# In streaming mode, the response of JSON is prefixed with "data: "
|
401
|
+
# which we must strip before parsing.
|
402
|
+
if line.startswith('data: '):
|
403
|
+
yield line[len('data: '):]
|
404
|
+
continue
|
405
|
+
|
406
|
+
# When API returns an error message, it comes line by line. So we buffer
|
407
|
+
# the lines until a complete JSON string is read. A complete JSON string
|
408
|
+
# is found when the balance is 0.
|
409
|
+
for c in line:
|
410
|
+
if c == '{':
|
411
|
+
balance += 1
|
412
|
+
elif c == '}':
|
413
|
+
balance -= 1
|
414
|
+
|
415
|
+
chunk += line
|
416
|
+
if balance == 0:
|
417
|
+
yield chunk
|
418
|
+
chunk = ''
|
419
|
+
|
420
|
+
# If there is any remaining chunk, yield it.
|
421
|
+
if chunk:
|
422
|
+
yield chunk
|
423
|
+
|
424
|
+
if hasattr(self, '_session') and self._session:
|
425
|
+
await self._session.close()
|
426
|
+
|
427
|
+
@classmethod
|
428
|
+
def _load_json_from_response(cls, response: Any) -> Any:
|
429
|
+
"""Loads JSON from the response, or raises an error if the parsing fails."""
|
430
|
+
try:
|
431
|
+
return json.loads(response)
|
432
|
+
except json.JSONDecodeError as e:
|
433
|
+
raise errors.UnknownApiResponseError(
|
434
|
+
f'Failed to parse response as JSON. Raw response: {response}'
|
435
|
+
) from e
|
436
|
+
|
348
437
|
|
349
438
|
# Default retry options.
|
350
439
|
# The config is based on https://cloud.google.com/storage/docs/retry-strategy.
|
@@ -548,7 +637,7 @@ class BaseApiClient:
|
|
548
637
|
|
549
638
|
has_sufficient_auth = (self.project and self.location) or self.api_key
|
550
639
|
|
551
|
-
if
|
640
|
+
if not has_sufficient_auth and not validated_http_options.base_url:
|
552
641
|
# Skip sufficient auth check if base url is provided in http options.
|
553
642
|
raise ValueError(
|
554
643
|
'Project and location or API key must be set when using the Vertex '
|
@@ -41,6 +41,39 @@ _py_builtin_type_to_schema_type = {
|
|
41
41
|
}
|
42
42
|
|
43
43
|
|
44
|
+
def _raise_for_unsupported_param(
|
45
|
+
param: inspect.Parameter, func_name: str, exception: Union[Exception, type[Exception]]
|
46
|
+
) -> None:
|
47
|
+
raise ValueError(
|
48
|
+
f'Failed to parse the parameter {param} of function {func_name} for'
|
49
|
+
' automatic function calling.Automatic function calling works best with'
|
50
|
+
' simpler function signature schema, consider manually parsing your'
|
51
|
+
f' function declaration for function {func_name}.'
|
52
|
+
) from exception
|
53
|
+
|
54
|
+
|
55
|
+
def _handle_params_as_deferred_annotations(param: inspect.Parameter, annotation_under_future: dict[str, Any], name: str) -> inspect.Parameter:
|
56
|
+
"""Catches the case when type hints are stored as strings."""
|
57
|
+
if isinstance(param.annotation, str):
|
58
|
+
param = param.replace(annotation=annotation_under_future[name])
|
59
|
+
return param
|
60
|
+
|
61
|
+
|
62
|
+
def _add_unevaluated_items_to_fixed_len_tuple_schema(
|
63
|
+
json_schema: dict[str, Any]
|
64
|
+
) -> dict[str, Any]:
|
65
|
+
if (
|
66
|
+
json_schema.get('maxItems')
|
67
|
+
and (
|
68
|
+
json_schema.get('prefixItems')
|
69
|
+
and len(json_schema['prefixItems']) == json_schema['maxItems']
|
70
|
+
)
|
71
|
+
and json_schema.get('type') == 'array'
|
72
|
+
):
|
73
|
+
json_schema['unevaluatedItems'] = False
|
74
|
+
return json_schema
|
75
|
+
|
76
|
+
|
44
77
|
def _is_builtin_primitive_or_compound(
|
45
78
|
annotation: inspect.Parameter.annotation, # type: ignore[valid-type]
|
46
79
|
) -> bool:
|
@@ -92,7 +125,7 @@ def _is_default_value_compatible(
|
|
92
125
|
return False
|
93
126
|
|
94
127
|
|
95
|
-
def _parse_schema_from_parameter(
|
128
|
+
def _parse_schema_from_parameter( # type: ignore[return]
|
96
129
|
api_option: Literal['VERTEX_AI', 'GEMINI_API'],
|
97
130
|
param: inspect.Parameter,
|
98
131
|
func_name: str,
|
@@ -267,12 +300,7 @@ def _parse_schema_from_parameter(
|
|
267
300
|
)
|
268
301
|
schema.required = _get_required_fields(schema)
|
269
302
|
return schema
|
270
|
-
|
271
|
-
f'Failed to parse the parameter {param} of function {func_name} for'
|
272
|
-
' automatic function calling.Automatic function calling works best with'
|
273
|
-
' simpler function signature schema, consider manually parsing your'
|
274
|
-
f' function declaration for function {func_name}.'
|
275
|
-
)
|
303
|
+
_raise_for_unsupported_param(param, func_name, ValueError)
|
276
304
|
|
277
305
|
|
278
306
|
def _get_required_fields(schema: types.Schema) -> Optional[list[str]]:
|
google/genai/_live_converters.py
CHANGED
@@ -312,6 +312,11 @@ def _GoogleSearch_to_mldev(
|
|
312
312
|
_Interval_to_mldev(getv(from_object, ['time_range_filter']), to_object),
|
313
313
|
)
|
314
314
|
|
315
|
+
if getv(from_object, ['exclude_domains']) is not None:
|
316
|
+
raise ValueError(
|
317
|
+
'exclude_domains parameter is not supported in Gemini API.'
|
318
|
+
)
|
319
|
+
|
315
320
|
return to_object
|
316
321
|
|
317
322
|
|
@@ -359,6 +364,17 @@ def _UrlContext_to_mldev(
|
|
359
364
|
return to_object
|
360
365
|
|
361
366
|
|
367
|
+
def _ToolComputerUse_to_mldev(
|
368
|
+
from_object: Union[dict[str, Any], object],
|
369
|
+
parent_object: Optional[dict[str, Any]] = None,
|
370
|
+
) -> dict[str, Any]:
|
371
|
+
to_object: dict[str, Any] = {}
|
372
|
+
if getv(from_object, ['environment']) is not None:
|
373
|
+
setv(to_object, ['environment'], getv(from_object, ['environment']))
|
374
|
+
|
375
|
+
return to_object
|
376
|
+
|
377
|
+
|
362
378
|
def _Tool_to_mldev(
|
363
379
|
from_object: Union[dict[str, Any], object],
|
364
380
|
parent_object: Optional[dict[str, Any]] = None,
|
@@ -408,12 +424,18 @@ def _Tool_to_mldev(
|
|
408
424
|
_UrlContext_to_mldev(getv(from_object, ['url_context']), to_object),
|
409
425
|
)
|
410
426
|
|
427
|
+
if getv(from_object, ['computer_use']) is not None:
|
428
|
+
setv(
|
429
|
+
to_object,
|
430
|
+
['computerUse'],
|
431
|
+
_ToolComputerUse_to_mldev(
|
432
|
+
getv(from_object, ['computer_use']), to_object
|
433
|
+
),
|
434
|
+
)
|
435
|
+
|
411
436
|
if getv(from_object, ['code_execution']) is not None:
|
412
437
|
setv(to_object, ['codeExecution'], getv(from_object, ['code_execution']))
|
413
438
|
|
414
|
-
if getv(from_object, ['computer_use']) is not None:
|
415
|
-
setv(to_object, ['computerUse'], getv(from_object, ['computer_use']))
|
416
|
-
|
417
439
|
return to_object
|
418
440
|
|
419
441
|
|
@@ -1098,6 +1120,13 @@ def _LiveMusicGenerationConfig_to_mldev(
|
|
1098
1120
|
getv(from_object, ['only_bass_and_drums']),
|
1099
1121
|
)
|
1100
1122
|
|
1123
|
+
if getv(from_object, ['music_generation_mode']) is not None:
|
1124
|
+
setv(
|
1125
|
+
to_object,
|
1126
|
+
['musicGenerationMode'],
|
1127
|
+
getv(from_object, ['music_generation_mode']),
|
1128
|
+
)
|
1129
|
+
|
1101
1130
|
return to_object
|
1102
1131
|
|
1103
1132
|
|
@@ -1437,6 +1466,9 @@ def _GoogleSearch_to_vertex(
|
|
1437
1466
|
),
|
1438
1467
|
)
|
1439
1468
|
|
1469
|
+
if getv(from_object, ['exclude_domains']) is not None:
|
1470
|
+
setv(to_object, ['excludeDomains'], getv(from_object, ['exclude_domains']))
|
1471
|
+
|
1440
1472
|
return to_object
|
1441
1473
|
|
1442
1474
|
|
@@ -1480,6 +1512,8 @@ def _EnterpriseWebSearch_to_vertex(
|
|
1480
1512
|
parent_object: Optional[dict[str, Any]] = None,
|
1481
1513
|
) -> dict[str, Any]:
|
1482
1514
|
to_object: dict[str, Any] = {}
|
1515
|
+
if getv(from_object, ['exclude_domains']) is not None:
|
1516
|
+
setv(to_object, ['excludeDomains'], getv(from_object, ['exclude_domains']))
|
1483
1517
|
|
1484
1518
|
return to_object
|
1485
1519
|
|
@@ -1559,6 +1593,17 @@ def _UrlContext_to_vertex(
|
|
1559
1593
|
return to_object
|
1560
1594
|
|
1561
1595
|
|
1596
|
+
def _ToolComputerUse_to_vertex(
|
1597
|
+
from_object: Union[dict[str, Any], object],
|
1598
|
+
parent_object: Optional[dict[str, Any]] = None,
|
1599
|
+
) -> dict[str, Any]:
|
1600
|
+
to_object: dict[str, Any] = {}
|
1601
|
+
if getv(from_object, ['environment']) is not None:
|
1602
|
+
setv(to_object, ['environment'], getv(from_object, ['environment']))
|
1603
|
+
|
1604
|
+
return to_object
|
1605
|
+
|
1606
|
+
|
1562
1607
|
def _Tool_to_vertex(
|
1563
1608
|
from_object: Union[dict[str, Any], object],
|
1564
1609
|
parent_object: Optional[dict[str, Any]] = None,
|
@@ -1618,12 +1663,18 @@ def _Tool_to_vertex(
|
|
1618
1663
|
_UrlContext_to_vertex(getv(from_object, ['url_context']), to_object),
|
1619
1664
|
)
|
1620
1665
|
|
1666
|
+
if getv(from_object, ['computer_use']) is not None:
|
1667
|
+
setv(
|
1668
|
+
to_object,
|
1669
|
+
['computerUse'],
|
1670
|
+
_ToolComputerUse_to_vertex(
|
1671
|
+
getv(from_object, ['computer_use']), to_object
|
1672
|
+
),
|
1673
|
+
)
|
1674
|
+
|
1621
1675
|
if getv(from_object, ['code_execution']) is not None:
|
1622
1676
|
setv(to_object, ['codeExecution'], getv(from_object, ['code_execution']))
|
1623
1677
|
|
1624
|
-
if getv(from_object, ['computer_use']) is not None:
|
1625
|
-
setv(to_object, ['computerUse'], getv(from_object, ['computer_use']))
|
1626
|
-
|
1627
1678
|
return to_object
|
1628
1679
|
|
1629
1680
|
|
@@ -2871,6 +2922,13 @@ def _LiveMusicGenerationConfig_from_mldev(
|
|
2871
2922
|
getv(from_object, ['onlyBassAndDrums']),
|
2872
2923
|
)
|
2873
2924
|
|
2925
|
+
if getv(from_object, ['musicGenerationMode']) is not None:
|
2926
|
+
setv(
|
2927
|
+
to_object,
|
2928
|
+
['music_generation_mode'],
|
2929
|
+
getv(from_object, ['musicGenerationMode']),
|
2930
|
+
)
|
2931
|
+
|
2874
2932
|
return to_object
|
2875
2933
|
|
2876
2934
|
|
@@ -312,6 +312,11 @@ def _GoogleSearch_to_mldev(
|
|
312
312
|
_Interval_to_mldev(getv(from_object, ['time_range_filter']), to_object),
|
313
313
|
)
|
314
314
|
|
315
|
+
if getv(from_object, ['exclude_domains']) is not None:
|
316
|
+
raise ValueError(
|
317
|
+
'exclude_domains parameter is not supported in Gemini API.'
|
318
|
+
)
|
319
|
+
|
315
320
|
return to_object
|
316
321
|
|
317
322
|
|
@@ -359,6 +364,17 @@ def _UrlContext_to_mldev(
|
|
359
364
|
return to_object
|
360
365
|
|
361
366
|
|
367
|
+
def _ToolComputerUse_to_mldev(
|
368
|
+
from_object: Union[dict[str, Any], object],
|
369
|
+
parent_object: Optional[dict[str, Any]] = None,
|
370
|
+
) -> dict[str, Any]:
|
371
|
+
to_object: dict[str, Any] = {}
|
372
|
+
if getv(from_object, ['environment']) is not None:
|
373
|
+
setv(to_object, ['environment'], getv(from_object, ['environment']))
|
374
|
+
|
375
|
+
return to_object
|
376
|
+
|
377
|
+
|
362
378
|
def _Tool_to_mldev(
|
363
379
|
from_object: Union[dict[str, Any], object],
|
364
380
|
parent_object: Optional[dict[str, Any]] = None,
|
@@ -408,12 +424,18 @@ def _Tool_to_mldev(
|
|
408
424
|
_UrlContext_to_mldev(getv(from_object, ['url_context']), to_object),
|
409
425
|
)
|
410
426
|
|
427
|
+
if getv(from_object, ['computer_use']) is not None:
|
428
|
+
setv(
|
429
|
+
to_object,
|
430
|
+
['computerUse'],
|
431
|
+
_ToolComputerUse_to_mldev(
|
432
|
+
getv(from_object, ['computer_use']), to_object
|
433
|
+
),
|
434
|
+
)
|
435
|
+
|
411
436
|
if getv(from_object, ['code_execution']) is not None:
|
412
437
|
setv(to_object, ['codeExecution'], getv(from_object, ['code_execution']))
|
413
438
|
|
414
|
-
if getv(from_object, ['computer_use']) is not None:
|
415
|
-
setv(to_object, ['computerUse'], getv(from_object, ['computer_use']))
|
416
|
-
|
417
439
|
return to_object
|
418
440
|
|
419
441
|
|
google/genai/_transformers.py
CHANGED
@@ -28,6 +28,7 @@ import types as builtin_types
|
|
28
28
|
import typing
|
29
29
|
from typing import Any, GenericAlias, List, Optional, Sequence, Union # type: ignore[attr-defined]
|
30
30
|
from ._mcp_utils import mcp_to_gemini_tool
|
31
|
+
from ._common import get_value_by_path as getv
|
31
32
|
|
32
33
|
if typing.TYPE_CHECKING:
|
33
34
|
import PIL.Image
|
@@ -1224,3 +1225,53 @@ def t_tool_response(
|
|
1224
1225
|
f'Could not convert input (type "{type(input)}") to '
|
1225
1226
|
'`types.LiveClientToolResponse`'
|
1226
1227
|
) from e
|
1228
|
+
|
1229
|
+
|
1230
|
+
def t_metrics(
|
1231
|
+
metrics: list[types.MetricSubclass]
|
1232
|
+
) -> list[dict[str, Any]]:
|
1233
|
+
"""Prepares the metric payload for the evaluation request.
|
1234
|
+
|
1235
|
+
Args:
|
1236
|
+
request_dict: The dictionary containing the request details.
|
1237
|
+
resolved_metrics: A list of resolved metric objects.
|
1238
|
+
|
1239
|
+
Returns:
|
1240
|
+
The updated request dictionary with the prepared metric payload.
|
1241
|
+
"""
|
1242
|
+
metrics_payload = []
|
1243
|
+
|
1244
|
+
for metric in metrics:
|
1245
|
+
metric_payload_item: dict[str, Any] = {}
|
1246
|
+
metric_payload_item['aggregation_metrics'] = [
|
1247
|
+
'AVERAGE',
|
1248
|
+
'STANDARD_DEVIATION',
|
1249
|
+
]
|
1250
|
+
|
1251
|
+
metric_name = getv(metric, ['name']).lower()
|
1252
|
+
|
1253
|
+
if metric_name == 'exact_match':
|
1254
|
+
metric_payload_item['exact_match_spec'] = {}
|
1255
|
+
elif metric_name == 'bleu':
|
1256
|
+
metric_payload_item['bleu_spec'] = {}
|
1257
|
+
elif metric_name.startswith('rouge'):
|
1258
|
+
rouge_type = metric_name.replace("_", "")
|
1259
|
+
metric_payload_item['rouge_spec'] = {'rouge_type': rouge_type}
|
1260
|
+
|
1261
|
+
elif hasattr(metric, 'prompt_template') and metric.prompt_template:
|
1262
|
+
pointwise_spec = {'metric_prompt_template': metric.prompt_template}
|
1263
|
+
system_instruction = getv(metric, ['judge_model_system_instruction'])
|
1264
|
+
if system_instruction:
|
1265
|
+
pointwise_spec['system_instruction'] = system_instruction
|
1266
|
+
return_raw_output = getv(metric, ['return_raw_output'])
|
1267
|
+
if return_raw_output:
|
1268
|
+
pointwise_spec['custom_output_format_config'] = { # type: ignore[assignment]
|
1269
|
+
'return_raw_output': return_raw_output
|
1270
|
+
}
|
1271
|
+
metric_payload_item['pointwise_metric_spec'] = pointwise_spec
|
1272
|
+
else:
|
1273
|
+
raise ValueError(
|
1274
|
+
'Unsupported metric type or invalid metric name:' f' {metric_name}'
|
1275
|
+
)
|
1276
|
+
metrics_payload.append(metric_payload_item)
|
1277
|
+
return metrics_payload
|
google/genai/batches.py
CHANGED
@@ -338,6 +338,11 @@ def _GoogleSearch_to_mldev(
|
|
338
338
|
_Interval_to_mldev(getv(from_object, ['time_range_filter']), to_object),
|
339
339
|
)
|
340
340
|
|
341
|
+
if getv(from_object, ['exclude_domains']) is not None:
|
342
|
+
raise ValueError(
|
343
|
+
'exclude_domains parameter is not supported in Gemini API.'
|
344
|
+
)
|
345
|
+
|
341
346
|
return to_object
|
342
347
|
|
343
348
|
|
@@ -385,6 +390,17 @@ def _UrlContext_to_mldev(
|
|
385
390
|
return to_object
|
386
391
|
|
387
392
|
|
393
|
+
def _ToolComputerUse_to_mldev(
|
394
|
+
from_object: Union[dict[str, Any], object],
|
395
|
+
parent_object: Optional[dict[str, Any]] = None,
|
396
|
+
) -> dict[str, Any]:
|
397
|
+
to_object: dict[str, Any] = {}
|
398
|
+
if getv(from_object, ['environment']) is not None:
|
399
|
+
setv(to_object, ['environment'], getv(from_object, ['environment']))
|
400
|
+
|
401
|
+
return to_object
|
402
|
+
|
403
|
+
|
388
404
|
def _Tool_to_mldev(
|
389
405
|
from_object: Union[dict[str, Any], object],
|
390
406
|
parent_object: Optional[dict[str, Any]] = None,
|
@@ -434,12 +450,18 @@ def _Tool_to_mldev(
|
|
434
450
|
_UrlContext_to_mldev(getv(from_object, ['url_context']), to_object),
|
435
451
|
)
|
436
452
|
|
453
|
+
if getv(from_object, ['computer_use']) is not None:
|
454
|
+
setv(
|
455
|
+
to_object,
|
456
|
+
['computerUse'],
|
457
|
+
_ToolComputerUse_to_mldev(
|
458
|
+
getv(from_object, ['computer_use']), to_object
|
459
|
+
),
|
460
|
+
)
|
461
|
+
|
437
462
|
if getv(from_object, ['code_execution']) is not None:
|
438
463
|
setv(to_object, ['codeExecution'], getv(from_object, ['code_execution']))
|
439
464
|
|
440
|
-
if getv(from_object, ['computer_use']) is not None:
|
441
|
-
setv(to_object, ['computerUse'], getv(from_object, ['computer_use']))
|
442
|
-
|
443
465
|
return to_object
|
444
466
|
|
445
467
|
|
@@ -1492,6 +1514,9 @@ def _GenerateContentResponse_from_mldev(
|
|
1492
1514
|
if getv(from_object, ['promptFeedback']) is not None:
|
1493
1515
|
setv(to_object, ['prompt_feedback'], getv(from_object, ['promptFeedback']))
|
1494
1516
|
|
1517
|
+
if getv(from_object, ['responseId']) is not None:
|
1518
|
+
setv(to_object, ['response_id'], getv(from_object, ['responseId']))
|
1519
|
+
|
1495
1520
|
if getv(from_object, ['usageMetadata']) is not None:
|
1496
1521
|
setv(to_object, ['usage_metadata'], getv(from_object, ['usageMetadata']))
|
1497
1522
|
|
@@ -1648,6 +1673,11 @@ def _DeleteResourceJob_from_mldev(
|
|
1648
1673
|
parent_object: Optional[dict[str, Any]] = None,
|
1649
1674
|
) -> dict[str, Any]:
|
1650
1675
|
to_object: dict[str, Any] = {}
|
1676
|
+
if getv(from_object, ['sdkHttpResponse']) is not None:
|
1677
|
+
setv(
|
1678
|
+
to_object, ['sdk_http_response'], getv(from_object, ['sdkHttpResponse'])
|
1679
|
+
)
|
1680
|
+
|
1651
1681
|
if getv(from_object, ['name']) is not None:
|
1652
1682
|
setv(to_object, ['name'], getv(from_object, ['name']))
|
1653
1683
|
|
@@ -1815,6 +1845,11 @@ def _DeleteResourceJob_from_vertex(
|
|
1815
1845
|
parent_object: Optional[dict[str, Any]] = None,
|
1816
1846
|
) -> dict[str, Any]:
|
1817
1847
|
to_object: dict[str, Any] = {}
|
1848
|
+
if getv(from_object, ['sdkHttpResponse']) is not None:
|
1849
|
+
setv(
|
1850
|
+
to_object, ['sdk_http_response'], getv(from_object, ['sdkHttpResponse'])
|
1851
|
+
)
|
1852
|
+
|
1818
1853
|
if getv(from_object, ['name']) is not None:
|
1819
1854
|
setv(to_object, ['name'], getv(from_object, ['name']))
|
1820
1855
|
|
@@ -2186,7 +2221,9 @@ class Batches(_api_module.BaseModule):
|
|
2186
2221
|
return_value = types.DeleteResourceJob._from_response(
|
2187
2222
|
response=response_dict, kwargs=parameter_model.model_dump()
|
2188
2223
|
)
|
2189
|
-
|
2224
|
+
return_value.sdk_http_response = types.HttpResponse(
|
2225
|
+
headers=response.headers
|
2226
|
+
)
|
2190
2227
|
self._api_client._verify_response(return_value)
|
2191
2228
|
return return_value
|
2192
2229
|
|
@@ -2619,7 +2656,9 @@ class AsyncBatches(_api_module.BaseModule):
|
|
2619
2656
|
return_value = types.DeleteResourceJob._from_response(
|
2620
2657
|
response=response_dict, kwargs=parameter_model.model_dump()
|
2621
2658
|
)
|
2622
|
-
|
2659
|
+
return_value.sdk_http_response = types.HttpResponse(
|
2660
|
+
headers=response.headers
|
2661
|
+
)
|
2623
2662
|
self._api_client._verify_response(return_value)
|
2624
2663
|
return return_value
|
2625
2664
|
|