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.
Files changed (33) hide show
  1. google_genai-1.4.0/MANIFEST.in +2 -0
  2. {google_genai-1.3.0 → google_genai-1.4.0}/PKG-INFO +7 -1
  3. {google_genai-1.3.0 → google_genai-1.4.0}/README.md +6 -0
  4. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_api_client.py +30 -26
  5. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_api_module.py +1 -1
  6. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_automatic_function_calling_util.py +12 -12
  7. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_common.py +2 -2
  8. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_extra_utils.py +7 -6
  9. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_replay_api_client.py +3 -2
  10. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_test_api_client.py +8 -8
  11. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/_transformers.py +158 -47
  12. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/batches.py +170 -124
  13. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/caches.py +306 -206
  14. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/chats.py +179 -35
  15. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/client.py +3 -3
  16. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/errors.py +1 -2
  17. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/files.py +153 -110
  18. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/live.py +73 -64
  19. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/models.py +828 -591
  20. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/operations.py +86 -56
  21. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/pagers.py +5 -5
  22. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/tunings.py +163 -103
  23. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/types.py +174 -157
  24. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/version.py +1 -1
  25. {google_genai-1.3.0 → google_genai-1.4.0}/google_genai.egg-info/PKG-INFO +7 -1
  26. {google_genai-1.3.0 → google_genai-1.4.0}/google_genai.egg-info/SOURCES.txt +1 -0
  27. {google_genai-1.3.0 → google_genai-1.4.0}/pyproject.toml +2 -1
  28. {google_genai-1.3.0 → google_genai-1.4.0}/LICENSE +0 -0
  29. {google_genai-1.3.0 → google_genai-1.4.0}/google/genai/__init__.py +0 -0
  30. {google_genai-1.3.0 → google_genai-1.4.0}/google_genai.egg-info/dependency_links.txt +0 -0
  31. {google_genai-1.3.0 → google_genai-1.4.0}/google_genai.egg-info/requires.txt +0 -0
  32. {google_genai-1.3.0 → google_genai-1.4.0}/google_genai.egg-info/top_level.txt +0 -0
  33. {google_genai-1.3.0 → google_genai-1.4.0}/setup.cfg +0 -0
@@ -0,0 +1,2 @@
1
+ include LICENSE
2
+ include README.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: google-genai
3
- Version: 1.3.0
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: HttpOptionsDict
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 ApiClient:
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: Union[bool, None] = None,
233
- api_key: Union[str, None] = None,
234
- credentials: google.auth.credentials.Credentials = None,
235
- project: Union[str, None] = None,
236
- location: Union[str, None] = None,
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 a dict is provided.
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
- http_options = http_options.model_dump()
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, 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
- data=json.dumps(http_request.data),
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
- data=json.dumps(http_request.data) if http_request.data else None,
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: HttpOptionsDict = None,
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, cls=RequestJsonEncoder)
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
@@ -21,7 +21,7 @@ from . import _api_client
21
21
 
22
22
  class BaseModule:
23
23
 
24
- def __init__(self, api_client_: _api_client.ApiClient):
24
+ def __init__(self, api_client_: _api_client.BaseApiClient):
25
25
  self._api_client = api_client_
26
26
 
27
27
  @property
@@ -31,12 +31,12 @@ else:
31
31
  VersionedUnionType = typing._UnionGenericAlias
32
32
 
33
33
  _py_builtin_type_to_schema_type = {
34
- str: 'STRING',
35
- int: 'INTEGER',
36
- float: 'NUMBER',
37
- bool: 'BOOLEAN',
38
- list: 'ARRAY',
39
- dict: 'OBJECT',
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 = 'OBJECT'
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 = 'OBJECT'
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 = 'STRING'
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 = 'ARRAY'
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 = 'OBJECT'
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 = 'OBJECT'
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
- response = {'result': invoke_function_from_dict_args(args, func)}
225
+ func_response = {'result': invoke_function_from_dict_args(args, func)}
225
226
  except Exception as e: # pylint: disable=broad-except
226
- response = {'error': str(e)}
227
- func_response = types.Part.from_function_response(
228
- name=func_name, response=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(func_response)
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 ApiClient
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(ApiClient):
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 ApiClient
20
+ from .api_client import BaseApiClient
21
21
 
22
22
 
23
- @patch('genai.api_client.ApiClient._build_request')
24
- @patch('genai.api_client.ApiClient._request')
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 = ApiClient(api_key='test_api_key')
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.ApiClient._build_request')
60
- @patch('genai.api_client.ApiClient._async_request')
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.ApiClient._build_request')
103
- @patch('genai.api_client.ApiClient._async_request')
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