google-genai 1.3.0__tar.gz → 1.4.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.4.0/MANIFEST.in +2 -0
- {google_genai-1.3.0 → google_genai-1.4.0}/PKG-INFO +7 -1
- {google_genai-1.3.0 → google_genai-1.4.0}/README.md +6 -0
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_api_client.py +30 -26
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_api_module.py +1 -1
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_automatic_function_calling_util.py +12 -12
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_common.py +2 -2
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_extra_utils.py +7 -6
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_replay_api_client.py +3 -2
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_test_api_client.py +8 -8
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_transformers.py +158 -47
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/batches.py +170 -124
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/caches.py +306 -206
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/chats.py +179 -35
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/client.py +3 -3
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/errors.py +1 -2
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/files.py +153 -110
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/live.py +73 -64
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/models.py +828 -591
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/operations.py +86 -56
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/pagers.py +5 -5
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/tunings.py +163 -103
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/types.py +174 -157
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/version.py +1 -1
- {google_genai-1.3.0 → google_genai-1.4.0}/google_genai.egg-info/PKG-INFO +7 -1
- {google_genai-1.3.0 → google_genai-1.4.0}/google_genai.egg-info/SOURCES.txt +1 -0
- {google_genai-1.3.0 → google_genai-1.4.0}/pyproject.toml +2 -1
- {google_genai-1.3.0 → google_genai-1.4.0}/LICENSE +0 -0
- {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/__init__.py +0 -0
- {google_genai-1.3.0 → google_genai-1.4.0}/google_genai.egg-info/dependency_links.txt +0 -0
- {google_genai-1.3.0 → google_genai-1.4.0}/google_genai.egg-info/requires.txt +0 -0
- {google_genai-1.3.0 → google_genai-1.4.0}/google_genai.egg-info/top_level.txt +0 -0
- {google_genai-1.3.0 → google_genai-1.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: google-genai
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.4.0
|
4
4
|
Summary: GenAI Python SDK
|
5
5
|
Author-email: Google LLC <googleapis-packages@google.com>
|
6
6
|
License: Apache-2.0
|
@@ -192,12 +192,18 @@ contents=[
|
|
192
192
|
|
193
193
|
### System Instructions and Other Configs
|
194
194
|
|
195
|
+
The output of the model can be influenced by several optional settings
|
196
|
+
available in generate_content's config parameter. For example, the
|
197
|
+
variability and length of the output can be influenced by the temperature
|
198
|
+
and max_output_tokens respectively.
|
199
|
+
|
195
200
|
```python
|
196
201
|
response = client.models.generate_content(
|
197
202
|
model='gemini-2.0-flash-001',
|
198
203
|
contents='high',
|
199
204
|
config=types.GenerateContentConfig(
|
200
205
|
system_instruction='I say high, you say low',
|
206
|
+
max_output_tokens=3,
|
201
207
|
temperature=0.3,
|
202
208
|
),
|
203
209
|
)
|
@@ -163,12 +163,18 @@ contents=[
|
|
163
163
|
|
164
164
|
### System Instructions and Other Configs
|
165
165
|
|
166
|
+
The output of the model can be influenced by several optional settings
|
167
|
+
available in generate_content's config parameter. For example, the
|
168
|
+
variability and length of the output can be influenced by the temperature
|
169
|
+
and max_output_tokens respectively.
|
170
|
+
|
166
171
|
```python
|
167
172
|
response = client.models.generate_content(
|
168
173
|
model='gemini-2.0-flash-001',
|
169
174
|
contents='high',
|
170
175
|
config=types.GenerateContentConfig(
|
171
176
|
system_instruction='I say high, you say low',
|
177
|
+
max_output_tokens=3,
|
172
178
|
temperature=0.3,
|
173
179
|
),
|
174
180
|
)
|
@@ -14,7 +14,10 @@
|
|
14
14
|
#
|
15
15
|
|
16
16
|
|
17
|
-
"""Base client for calling HTTP APIs sending and receiving JSON.
|
17
|
+
"""Base client for calling HTTP APIs sending and receiving JSON.
|
18
|
+
|
19
|
+
The BaseApiClient is intended to be a private module and is subject to change.
|
20
|
+
"""
|
18
21
|
|
19
22
|
import asyncio
|
20
23
|
import copy
|
@@ -65,7 +68,7 @@ def _append_library_version_headers(headers: dict[str, str]) -> None:
|
|
65
68
|
|
66
69
|
|
67
70
|
def _patch_http_options(
|
68
|
-
options: HttpOptionsDict, patch_options:
|
71
|
+
options: HttpOptionsDict, patch_options: dict[str, Any]
|
69
72
|
) -> HttpOptionsDict:
|
70
73
|
# use shallow copy so we don't override the original objects.
|
71
74
|
copy_option = HttpOptionsDict()
|
@@ -131,7 +134,7 @@ class HttpRequest:
|
|
131
134
|
# TODO(b/394358912): Update this class to use a SDKResponse class that can be
|
132
135
|
# generated and used for all languages.
|
133
136
|
class BaseResponse(_common.BaseModel):
|
134
|
-
http_headers: dict[str, str] = Field(
|
137
|
+
http_headers: Optional[dict[str, str]] = Field(
|
135
138
|
default=None, description='The http headers of the response.'
|
136
139
|
)
|
137
140
|
|
@@ -224,17 +227,17 @@ class HttpResponse:
|
|
224
227
|
response_payload[attribute] = copy.deepcopy(getattr(self, attribute))
|
225
228
|
|
226
229
|
|
227
|
-
class
|
230
|
+
class BaseApiClient:
|
228
231
|
"""Client for calling HTTP APIs sending and receiving JSON."""
|
229
232
|
|
230
233
|
def __init__(
|
231
234
|
self,
|
232
|
-
vertexai:
|
233
|
-
api_key:
|
234
|
-
credentials: google.auth.credentials.Credentials = None,
|
235
|
-
project:
|
236
|
-
location:
|
237
|
-
http_options: HttpOptionsOrDict = None,
|
235
|
+
vertexai: Optional[bool] = None,
|
236
|
+
api_key: Optional[str] = None,
|
237
|
+
credentials: Optional[google.auth.credentials.Credentials] = None,
|
238
|
+
project: Optional[str] = None,
|
239
|
+
location: Optional[str] = None,
|
240
|
+
http_options: Optional[HttpOptionsOrDict] = None,
|
238
241
|
):
|
239
242
|
self.vertexai = vertexai
|
240
243
|
if self.vertexai is None:
|
@@ -258,14 +261,15 @@ class ApiClient:
|
|
258
261
|
' initializer.'
|
259
262
|
)
|
260
263
|
|
261
|
-
# Validate http_options if
|
264
|
+
# Validate http_options if it is provided.
|
265
|
+
validated_http_options: dict[str, Any]
|
262
266
|
if isinstance(http_options, dict):
|
263
267
|
try:
|
264
|
-
HttpOptions.model_validate(http_options)
|
268
|
+
validated_http_options = HttpOptions.model_validate(http_options).model_dump()
|
265
269
|
except ValidationError as e:
|
266
270
|
raise ValueError(f'Invalid http_options: {e}')
|
267
271
|
elif isinstance(http_options, HttpOptions):
|
268
|
-
|
272
|
+
validated_http_options = http_options.model_dump()
|
269
273
|
|
270
274
|
# Retrieve implicitly set values from the environment.
|
271
275
|
env_project = os.environ.get('GOOGLE_CLOUD_PROJECT', None)
|
@@ -347,7 +351,7 @@ class ApiClient:
|
|
347
351
|
self._http_options['headers']['x-goog-api-key'] = self.api_key
|
348
352
|
# Update the http options with the user provided http options.
|
349
353
|
if http_options:
|
350
|
-
self._http_options = _patch_http_options(self._http_options,
|
354
|
+
self._http_options = _patch_http_options(self._http_options, validated_http_options)
|
351
355
|
else:
|
352
356
|
_append_library_version_headers(self._http_options['headers'])
|
353
357
|
|
@@ -386,7 +390,7 @@ class ApiClient:
|
|
386
390
|
http_method: str,
|
387
391
|
path: str,
|
388
392
|
request_dict: dict[str, object],
|
389
|
-
http_options: HttpOptionsOrDict = None,
|
393
|
+
http_options: Optional[HttpOptionsOrDict] = None,
|
390
394
|
) -> HttpRequest:
|
391
395
|
# Remove all special dict keys such as _url and _query.
|
392
396
|
keys_to_delete = [key for key in request_dict.keys() if key.startswith('_')]
|
@@ -424,7 +428,7 @@ class ApiClient:
|
|
424
428
|
patched_http_options['api_version'] + '/' + path,
|
425
429
|
)
|
426
430
|
|
427
|
-
timeout_in_seconds = patched_http_options.get('timeout', None)
|
431
|
+
timeout_in_seconds: Optional[Union[float, int]] = patched_http_options.get('timeout', None)
|
428
432
|
if timeout_in_seconds:
|
429
433
|
timeout_in_seconds = timeout_in_seconds / 1000.0
|
430
434
|
else:
|
@@ -471,7 +475,7 @@ class ApiClient:
|
|
471
475
|
http_request: HttpRequest,
|
472
476
|
stream: bool = False,
|
473
477
|
) -> HttpResponse:
|
474
|
-
data = None
|
478
|
+
data: Optional[Union[str, bytes]] = None
|
475
479
|
if http_request.data:
|
476
480
|
if not isinstance(http_request.data, bytes):
|
477
481
|
data = json.dumps(http_request.data)
|
@@ -507,7 +511,7 @@ class ApiClient:
|
|
507
511
|
httpx_request = httpx.Request(
|
508
512
|
method=http_request.method,
|
509
513
|
url=http_request.url,
|
510
|
-
|
514
|
+
content=json.dumps(http_request.data),
|
511
515
|
headers=http_request.headers,
|
512
516
|
)
|
513
517
|
aclient = httpx.AsyncClient()
|
@@ -525,7 +529,7 @@ class ApiClient:
|
|
525
529
|
method=http_request.method,
|
526
530
|
url=http_request.url,
|
527
531
|
headers=http_request.headers,
|
528
|
-
|
532
|
+
content=json.dumps(http_request.data) if http_request.data else None,
|
529
533
|
timeout=http_request.timeout,
|
530
534
|
)
|
531
535
|
errors.APIError.raise_for_response(response)
|
@@ -545,7 +549,7 @@ class ApiClient:
|
|
545
549
|
http_method: str,
|
546
550
|
path: str,
|
547
551
|
request_dict: dict[str, object],
|
548
|
-
http_options: HttpOptionsOrDict = None,
|
552
|
+
http_options: Optional[HttpOptionsOrDict] = None,
|
549
553
|
):
|
550
554
|
http_request = self._build_request(
|
551
555
|
http_method, path, request_dict, http_options
|
@@ -563,7 +567,7 @@ class ApiClient:
|
|
563
567
|
http_method: str,
|
564
568
|
path: str,
|
565
569
|
request_dict: dict[str, object],
|
566
|
-
http_options: HttpOptionsDict = None,
|
570
|
+
http_options: Optional[HttpOptionsDict] = None,
|
567
571
|
):
|
568
572
|
http_request = self._build_request(
|
569
573
|
http_method, path, request_dict, http_options
|
@@ -578,7 +582,7 @@ class ApiClient:
|
|
578
582
|
http_method: str,
|
579
583
|
path: str,
|
580
584
|
request_dict: dict[str, object],
|
581
|
-
http_options:
|
585
|
+
http_options: Optional[HttpOptionsOrDict] = None,
|
582
586
|
) -> dict[str, object]:
|
583
587
|
http_request = self._build_request(
|
584
588
|
http_method, path, request_dict, http_options
|
@@ -595,7 +599,7 @@ class ApiClient:
|
|
595
599
|
http_method: str,
|
596
600
|
path: str,
|
597
601
|
request_dict: dict[str, object],
|
598
|
-
http_options: HttpOptionsDict = None,
|
602
|
+
http_options: Optional[HttpOptionsDict] = None,
|
599
603
|
):
|
600
604
|
http_request = self._build_request(
|
601
605
|
http_method, path, request_dict, http_options
|
@@ -704,10 +708,10 @@ class ApiClient:
|
|
704
708
|
self,
|
705
709
|
http_request: HttpRequest,
|
706
710
|
) -> HttpResponse:
|
707
|
-
data = None
|
711
|
+
data: str | bytes | None = None
|
708
712
|
if http_request.data:
|
709
713
|
if not isinstance(http_request.data, bytes):
|
710
|
-
data = json.dumps(http_request.data
|
714
|
+
data = json.dumps(http_request.data)
|
711
715
|
else:
|
712
716
|
data = http_request.data
|
713
717
|
|
@@ -792,5 +796,5 @@ class ApiClient:
|
|
792
796
|
# This method does nothing in the real api client. It is used in the
|
793
797
|
# replay_api_client to verify the response from the SDK method matches the
|
794
798
|
# recorded response.
|
795
|
-
def _verify_response(self, response_model: BaseModel):
|
799
|
+
def _verify_response(self, response_model: _common.BaseModel):
|
796
800
|
pass
|
@@ -31,12 +31,12 @@ else:
|
|
31
31
|
VersionedUnionType = typing._UnionGenericAlias
|
32
32
|
|
33
33
|
_py_builtin_type_to_schema_type = {
|
34
|
-
str:
|
35
|
-
int:
|
36
|
-
float:
|
37
|
-
bool:
|
38
|
-
list:
|
39
|
-
dict:
|
34
|
+
str: types.Type.STRING,
|
35
|
+
int: types.Type.INTEGER,
|
36
|
+
float: types.Type.NUMBER,
|
37
|
+
bool: types.Type.BOOLEAN,
|
38
|
+
list: types.Type.ARRAY,
|
39
|
+
dict: types.Type.OBJECT,
|
40
40
|
}
|
41
41
|
|
42
42
|
|
@@ -145,7 +145,7 @@ def _parse_schema_from_parameter(
|
|
145
145
|
for arg in get_args(param.annotation)
|
146
146
|
)
|
147
147
|
):
|
148
|
-
schema.type =
|
148
|
+
schema.type = _py_builtin_type_to_schema_type[dict]
|
149
149
|
schema.any_of = []
|
150
150
|
unique_types = set()
|
151
151
|
for arg in get_args(param.annotation):
|
@@ -183,7 +183,7 @@ def _parse_schema_from_parameter(
|
|
183
183
|
origin = get_origin(param.annotation)
|
184
184
|
args = get_args(param.annotation)
|
185
185
|
if origin is dict:
|
186
|
-
schema.type =
|
186
|
+
schema.type = _py_builtin_type_to_schema_type[dict]
|
187
187
|
if param.default is not inspect.Parameter.empty:
|
188
188
|
if not _is_default_value_compatible(param.default, param.annotation):
|
189
189
|
raise ValueError(default_value_error_msg)
|
@@ -195,7 +195,7 @@ def _parse_schema_from_parameter(
|
|
195
195
|
raise ValueError(
|
196
196
|
f'Literal type {param.annotation} must be a list of strings.'
|
197
197
|
)
|
198
|
-
schema.type =
|
198
|
+
schema.type = _py_builtin_type_to_schema_type[str]
|
199
199
|
schema.enum = list(args)
|
200
200
|
if param.default is not inspect.Parameter.empty:
|
201
201
|
if not _is_default_value_compatible(param.default, param.annotation):
|
@@ -204,7 +204,7 @@ def _parse_schema_from_parameter(
|
|
204
204
|
_raise_if_schema_unsupported(api_option, schema)
|
205
205
|
return schema
|
206
206
|
if origin is list:
|
207
|
-
schema.type =
|
207
|
+
schema.type = _py_builtin_type_to_schema_type[list]
|
208
208
|
schema.items = _parse_schema_from_parameter(
|
209
209
|
api_option,
|
210
210
|
inspect.Parameter(
|
@@ -222,7 +222,7 @@ def _parse_schema_from_parameter(
|
|
222
222
|
return schema
|
223
223
|
if origin is Union:
|
224
224
|
schema.any_of = []
|
225
|
-
schema.type =
|
225
|
+
schema.type = _py_builtin_type_to_schema_type[dict]
|
226
226
|
unique_types = set()
|
227
227
|
for arg in args:
|
228
228
|
# The first check is for NoneType in Python 3.9, since the __name__
|
@@ -280,7 +280,7 @@ def _parse_schema_from_parameter(
|
|
280
280
|
and param.default is not None
|
281
281
|
):
|
282
282
|
schema.default = param.default
|
283
|
-
schema.type =
|
283
|
+
schema.type = _py_builtin_type_to_schema_type[dict]
|
284
284
|
schema.properties = {}
|
285
285
|
for field_name, field_info in param.annotation.model_fields.items():
|
286
286
|
schema.properties[field_name] = _parse_schema_from_parameter(
|
@@ -205,7 +205,7 @@ class BaseModel(pydantic.BaseModel):
|
|
205
205
|
|
206
206
|
@classmethod
|
207
207
|
def _from_response(
|
208
|
-
cls, response: dict[str, object], kwargs: dict[str, object]
|
208
|
+
cls, *, response: dict[str, object], kwargs: dict[str, object]
|
209
209
|
) -> 'BaseModel':
|
210
210
|
# To maintain forward compatibility, we need to remove extra fields from
|
211
211
|
# the response.
|
@@ -266,7 +266,7 @@ def encode_unserializable_types(data: dict[str, object]) -> dict[str, object]:
|
|
266
266
|
A dictionary with json.dumps() incompatible type (e.g. bytes datetime)
|
267
267
|
to compatible type (e.g. base64 encoded string, isoformat date string).
|
268
268
|
"""
|
269
|
-
processed_data = {}
|
269
|
+
processed_data: dict[str, object] = {}
|
270
270
|
if not isinstance(data, dict):
|
271
271
|
return data
|
272
272
|
for key, value in data.items():
|
@@ -76,7 +76,7 @@ def get_function_map(
|
|
76
76
|
if config and isinstance(config, dict)
|
77
77
|
else config
|
78
78
|
)
|
79
|
-
function_map = {}
|
79
|
+
function_map: dict[str, object] = {}
|
80
80
|
if not config_model:
|
81
81
|
return function_map
|
82
82
|
if config_model.tools:
|
@@ -220,15 +220,16 @@ def get_function_response_parts(
|
|
220
220
|
func_name = part.function_call.name
|
221
221
|
func = function_map[func_name]
|
222
222
|
args = convert_number_values_for_function_call_args(part.function_call.args)
|
223
|
+
func_response: dict[str, Any]
|
223
224
|
try:
|
224
|
-
|
225
|
+
func_response = {'result': invoke_function_from_dict_args(args, func)}
|
225
226
|
except Exception as e: # pylint: disable=broad-except
|
226
|
-
|
227
|
-
|
228
|
-
name=func_name, response=
|
227
|
+
func_response = {'error': str(e)}
|
228
|
+
func_response_part = types.Part.from_function_response(
|
229
|
+
name=func_name, response=func_response
|
229
230
|
)
|
230
231
|
|
231
|
-
func_response_parts.append(
|
232
|
+
func_response_parts.append(func_response_part)
|
232
233
|
return func_response_parts
|
233
234
|
|
234
235
|
|
@@ -29,7 +29,7 @@ import google.auth
|
|
29
29
|
from requests.exceptions import HTTPError
|
30
30
|
|
31
31
|
from . import errors
|
32
|
-
from ._api_client import
|
32
|
+
from ._api_client import BaseApiClient
|
33
33
|
from ._api_client import HttpOptions
|
34
34
|
from ._api_client import HttpRequest
|
35
35
|
from ._api_client import HttpResponse
|
@@ -179,7 +179,7 @@ class ReplayFile(BaseModel):
|
|
179
179
|
interactions: list[ReplayInteraction]
|
180
180
|
|
181
181
|
|
182
|
-
class ReplayApiClient(
|
182
|
+
class ReplayApiClient(BaseApiClient):
|
183
183
|
"""For integration testing, send recorded response or records a response."""
|
184
184
|
|
185
185
|
def __init__(
|
@@ -457,6 +457,7 @@ class ReplayApiClient(ApiClient):
|
|
457
457
|
method='POST', url='', data={'file_path': file_path}, headers={}
|
458
458
|
)
|
459
459
|
if self._should_call_api():
|
460
|
+
result: Union[str, HttpResponse]
|
460
461
|
try:
|
461
462
|
result = super().upload_file(file_path, upload_url, upload_size)
|
462
463
|
except HTTPError as e:
|
@@ -17,13 +17,13 @@ import asyncio
|
|
17
17
|
import time
|
18
18
|
from unittest.mock import MagicMock, patch
|
19
19
|
import pytest
|
20
|
-
from .api_client import
|
20
|
+
from .api_client import BaseApiClient
|
21
21
|
|
22
22
|
|
23
|
-
@patch('genai.api_client.
|
24
|
-
@patch('genai.api_client.
|
23
|
+
@patch('genai.api_client.BaseApiClient._build_request')
|
24
|
+
@patch('genai.api_client.BaseApiClient._request')
|
25
25
|
def test_request_streamed_non_blocking(mock_request, mock_build_request):
|
26
|
-
api_client =
|
26
|
+
api_client = BaseApiClient(api_key='test_api_key')
|
27
27
|
http_method = 'GET'
|
28
28
|
path = 'test/path'
|
29
29
|
request_dict = {'key': 'value'}
|
@@ -56,8 +56,8 @@ def test_request_streamed_non_blocking(mock_request, mock_build_request):
|
|
56
56
|
assert end_time - start_time > 0.3
|
57
57
|
|
58
58
|
|
59
|
-
@patch('genai.api_client.
|
60
|
-
@patch('genai.api_client.
|
59
|
+
@patch('genai.api_client.BaseApiClient._build_request')
|
60
|
+
@patch('genai.api_client.BaseApiClient._async_request')
|
61
61
|
@pytest.mark.asyncio
|
62
62
|
async def test_async_request(mock_async_request, mock_build_request):
|
63
63
|
api_client = ApiClient(api_key='test_api_key')
|
@@ -99,8 +99,8 @@ async def test_async_request(mock_async_request, mock_build_request):
|
|
99
99
|
assert 0.1 <= end_time - start_time < 0.15
|
100
100
|
|
101
101
|
|
102
|
-
@patch('genai.api_client.
|
103
|
-
@patch('genai.api_client.
|
102
|
+
@patch('genai.api_client.BaseApiClient._build_request')
|
103
|
+
@patch('genai.api_client.BaseApiClient._async_request')
|
104
104
|
@pytest.mark.asyncio
|
105
105
|
async def test_async_request_streamed_non_blocking(
|
106
106
|
mock_async_request, mock_build_request
|