google-genai 0.7.0__py3-none-any.whl → 1.0.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.
@@ -34,10 +34,12 @@ import pydantic
34
34
  from . import _api_client
35
35
  from . import types
36
36
 
37
- if sys.version_info >= (3, 11):
38
- from types import UnionType
37
+ if sys.version_info >= (3, 10):
38
+ VersionedUnionType = typing.types.UnionType
39
+ _UNION_TYPES = (typing.Union, typing.types.UnionType)
39
40
  else:
40
- UnionType = typing._UnionGenericAlias
41
+ VersionedUnionType = typing._UnionGenericAlias
42
+ _UNION_TYPES = (typing.Union,)
41
43
 
42
44
 
43
45
  def _resource_name(
@@ -225,6 +227,7 @@ PartType = Union[types.Part, types.PartDict, str, 'PIL.Image.Image']
225
227
  def t_part(client: _api_client.ApiClient, part: PartType) -> types.Part:
226
228
  try:
227
229
  import PIL.Image
230
+
228
231
  PIL_Image = PIL.Image.Image
229
232
  except ImportError:
230
233
  PIL_Image = None
@@ -342,7 +345,7 @@ def handle_null_fields(schema: dict[str, Any]):
342
345
  "type": "null"
343
346
  }
344
347
  ],
345
- "default": null,
348
+ "default": None,
346
349
  "title": "Total Area Sq Mi"
347
350
  }
348
351
  }
@@ -356,16 +359,12 @@ def handle_null_fields(schema: dict[str, Any]):
356
359
  "total_area_sq_mi": {
357
360
  "type": "integer",
358
361
  "nullable": true,
359
- "default": null,
362
+ "default": None,
360
363
  "title": "Total Area Sq Mi"
361
364
  }
362
365
  }
363
366
  """
364
- if (
365
- isinstance(schema, dict)
366
- and 'type' in schema
367
- and schema['type'] == 'null'
368
- ):
367
+ if schema.get('type', None) == 'null':
369
368
  schema['nullable'] = True
370
369
  del schema['type']
371
370
  elif 'anyOf' in schema:
@@ -445,6 +444,11 @@ def process_schema(
445
444
  if client and not client.vertexai:
446
445
  schema.pop('title', None)
447
446
 
447
+ if schema.get('default') is not None:
448
+ raise ValueError(
449
+ 'Default value is not supported in the response schema for the Gemmini API.'
450
+ )
451
+
448
452
  if defs is None:
449
453
  defs = schema.pop('$defs', {})
450
454
  for _, sub_schema in defs.items():
@@ -454,8 +458,19 @@ def process_schema(
454
458
 
455
459
  any_of = schema.get('anyOf', None)
456
460
  if any_of is not None:
461
+ if not client.vertexai:
462
+ raise ValueError(
463
+ 'AnyOf is not supported in the response schema for the Gemini API.'
464
+ )
457
465
  for sub_schema in any_of:
458
- process_schema(sub_schema, client, defs)
466
+ # $ref is present in any_of if the schema is a union of Pydantic classes
467
+ ref_key = sub_schema.get('$ref', None)
468
+ if ref_key is None:
469
+ process_schema(sub_schema, client, defs)
470
+ else:
471
+ ref = defs[ref_key.split('defs/')[-1]]
472
+ any_of.append(ref)
473
+ schema['anyOf'] = [item for item in any_of if '$ref' not in item]
459
474
  return
460
475
 
461
476
  schema_type = schema.get('type', None)
@@ -526,15 +541,18 @@ def t_schema(
526
541
  if (
527
542
  # in Python 3.9 Generic alias list[int] counts as a type,
528
543
  # and breaks issubclass because it's not a class.
529
- not isinstance(origin, GenericAlias) and
530
- isinstance(origin, type) and
544
+ not isinstance(origin, GenericAlias) and
545
+ isinstance(origin, type) and
531
546
  issubclass(origin, pydantic.BaseModel)
532
547
  ):
533
548
  schema = origin.model_json_schema()
534
549
  process_schema(schema, client)
535
550
  return types.Schema.model_validate(schema)
536
551
  elif (
537
- isinstance(origin, GenericAlias) or isinstance(origin, type) or isinstance(origin, UnionType)
552
+ isinstance(origin, GenericAlias)
553
+ or isinstance(origin, type)
554
+ or isinstance(origin, VersionedUnionType)
555
+ or typing.get_origin(origin) in _UNION_TYPES
538
556
  ):
539
557
  class Placeholder(pydantic.BaseModel):
540
558
  placeholder: origin
google/genai/errors.py CHANGED
@@ -128,3 +128,7 @@ class FunctionInvocationError(ValueError):
128
128
  """Raised when the function cannot be invoked with the given arguments."""
129
129
 
130
130
  pass
131
+
132
+
133
+ class ExperimentalWarning(Warning):
134
+ """Warning for experimental features."""
google/genai/files.py CHANGED
@@ -19,7 +19,7 @@ import io
19
19
  import mimetypes
20
20
  import os
21
21
  import pathlib
22
- from typing import Optional, Union
22
+ from typing import Any, Optional, Union
23
23
  from urllib.parse import urlencode
24
24
  from . import _api_module
25
25
  from . import _common
@@ -351,12 +351,12 @@ def _DeleteFileParameters_to_vertex(
351
351
  return to_object
352
352
 
353
353
 
354
- def _FileState_to_vertex_enum_validate(enum_value: any):
354
+ def _FileState_to_vertex_enum_validate(enum_value: Any):
355
355
  if enum_value in set(['STATE_UNSPECIFIED', 'PROCESSING', 'ACTIVE', 'FAILED']):
356
356
  raise ValueError(f'{enum_value} enum value is not supported in Vertex AI.')
357
357
 
358
358
 
359
- def _FileSource_to_vertex_enum_validate(enum_value: any):
359
+ def _FileSource_to_vertex_enum_validate(enum_value: Any):
360
360
  if enum_value in set(['SOURCE_UNSPECIFIED', 'UPLOADED', 'GENERATED']):
361
361
  raise ValueError(f'{enum_value} enum value is not supported in Vertex AI.')
362
362
 
@@ -494,6 +494,8 @@ def _CreateFileResponse_from_mldev(
494
494
  parent_object: dict = None,
495
495
  ) -> dict:
496
496
  to_object = {}
497
+ if getv(from_object, ['httpHeaders']) is not None:
498
+ setv(to_object, ['http_headers'], getv(from_object, ['httpHeaders']))
497
499
 
498
500
  return to_object
499
501
 
@@ -504,6 +506,8 @@ def _CreateFileResponse_from_vertex(
504
506
  parent_object: dict = None,
505
507
  ) -> dict:
506
508
  to_object = {}
509
+ if getv(from_object, ['httpHeaders']) is not None:
510
+ setv(to_object, ['http_headers'], getv(from_object, ['httpHeaders']))
507
511
 
508
512
  return to_object
509
513
 
@@ -781,16 +785,17 @@ class Files(_api_module.BaseModule):
781
785
  def upload(
782
786
  self,
783
787
  *,
784
- path: Union[str, pathlib.Path, os.PathLike, io.IOBase],
788
+ file: Union[str, pathlib.Path, os.PathLike, io.IOBase],
785
789
  config: Optional[types.UploadFileConfigOrDict] = None,
786
790
  ) -> types.File:
787
791
  """Calls the API to upload a file using a supported file service.
788
792
 
789
793
  Args:
790
- path: The path to the file or an `IOBase` object to be uploaded. If it's
791
- an IOBase object, it must be opened in blocking mode and binary mode. In
792
- other words, do not use non-blocking mode or text mode. The given stream
793
- must be seekable, that is, it must be able to call seek() on 'path'.
794
+ file: A path to the file or an `IOBase` object to be uploaded. If it's an
795
+ IOBase object, it must be opened in blocking (the default) mode and
796
+ binary mode. In other words, do not use non-blocking mode or text mode.
797
+ The given stream must be seekable, that is, it must be able to call
798
+ `seek()` on 'path'.
794
799
  config: Optional parameters to set `diplay_name`, `mime_type`, and `name`.
795
800
  """
796
801
  if self._api_client.vertexai:
@@ -804,42 +809,42 @@ class Files(_api_module.BaseModule):
804
809
  config_model = types.UploadFileConfig(**config)
805
810
  else:
806
811
  config_model = config
807
- file = types.File(
812
+ file_obj = types.File(
808
813
  mime_type=config_model.mime_type,
809
814
  name=config_model.name,
810
815
  display_name=config_model.display_name,
811
816
  )
812
817
  else: # if not config
813
- file = types.File()
814
- if file.name is not None and not file.name.startswith('files/'):
815
- file.name = f'files/{file.name}'
818
+ file_obj = types.File()
819
+ if file_obj.name is not None and not file_obj.name.startswith('files/'):
820
+ file_obj.name = f'files/{file_obj.name}'
816
821
 
817
- if isinstance(path, io.IOBase):
818
- if file.mime_type is None:
822
+ if isinstance(file, io.IOBase):
823
+ if file_obj.mime_type is None:
819
824
  raise ValueError(
820
825
  'Unknown mime type: Could not determine the mimetype for your'
821
826
  ' file\n please set the `mime_type` argument'
822
827
  )
823
- if hasattr(path, 'mode'):
824
- if 'b' not in path.mode:
828
+ if hasattr(file, 'mode'):
829
+ if 'b' not in file.mode:
825
830
  raise ValueError('The file must be opened in binary mode.')
826
- offset = path.tell()
827
- path.seek(0, os.SEEK_END)
828
- file.size_bytes = path.tell() - offset
829
- path.seek(offset, os.SEEK_SET)
831
+ offset = file.tell()
832
+ file.seek(0, os.SEEK_END)
833
+ file_obj.size_bytes = file.tell() - offset
834
+ file.seek(offset, os.SEEK_SET)
830
835
  else:
831
- fs_path = os.fspath(path)
836
+ fs_path = os.fspath(file)
832
837
  if not fs_path or not os.path.isfile(fs_path):
833
- raise FileNotFoundError(f'{path} is not a valid file path.')
834
- file.size_bytes = os.path.getsize(fs_path)
835
- if file.mime_type is None:
836
- file.mime_type, _ = mimetypes.guess_type(fs_path)
837
- if file.mime_type is None:
838
+ raise FileNotFoundError(f'{file} is not a valid file path.')
839
+ file_obj.size_bytes = os.path.getsize(fs_path)
840
+ if file_obj.mime_type is None:
841
+ file_obj.mime_type, _ = mimetypes.guess_type(fs_path)
842
+ if file_obj.mime_type is None:
838
843
  raise ValueError(
839
844
  'Unknown mime type: Could not determine the mimetype for your'
840
845
  ' file\n please set the `mime_type` argument'
841
846
  )
842
- response = {}
847
+
843
848
  if config_model and config_model.http_options:
844
849
  http_options = config_model.http_options
845
850
  else:
@@ -849,30 +854,31 @@ class Files(_api_module.BaseModule):
849
854
  'Content-Type': 'application/json',
850
855
  'X-Goog-Upload-Protocol': 'resumable',
851
856
  'X-Goog-Upload-Command': 'start',
852
- 'X-Goog-Upload-Header-Content-Length': f'{file.size_bytes}',
853
- 'X-Goog-Upload-Header-Content-Type': f'{file.mime_type}',
857
+ 'X-Goog-Upload-Header-Content-Length': f'{file_obj.size_bytes}',
858
+ 'X-Goog-Upload-Header-Content-Type': f'{file_obj.mime_type}',
854
859
  },
855
- 'deprecated_response_payload': response,
856
860
  }
857
- self._create(file=file, config={'http_options': http_options})
861
+ response = self._create(
862
+ file=file_obj, config={'http_options': http_options}
863
+ )
858
864
 
859
865
  if (
860
- 'headers' not in response
861
- or 'X-Goog-Upload-URL' not in response['headers']
866
+ response.http_headers is None
867
+ or 'X-Goog-Upload-URL' not in response.http_headers
862
868
  ):
863
869
  raise KeyError(
864
870
  'Failed to create file. Upload URL did not returned from the create'
865
871
  ' file request.'
866
872
  )
867
- upload_url = response['headers']['X-Goog-Upload-URL']
873
+ upload_url = response.http_headers['X-Goog-Upload-URL']
868
874
 
869
- if isinstance(path, io.IOBase):
875
+ if isinstance(file, io.IOBase):
870
876
  return_file = self._api_client.upload_file(
871
- path, upload_url, file.size_bytes
877
+ file, upload_url, file_obj.size_bytes
872
878
  )
873
879
  else:
874
880
  return_file = self._api_client.upload_file(
875
- fs_path, upload_url, file.size_bytes
881
+ fs_path, upload_url, file_obj.size_bytes
876
882
  )
877
883
 
878
884
  return types.File._from_response(
@@ -1211,16 +1217,17 @@ class AsyncFiles(_api_module.BaseModule):
1211
1217
  async def upload(
1212
1218
  self,
1213
1219
  *,
1214
- path: Union[str, pathlib.Path, os.PathLike, io.IOBase],
1220
+ file: Union[str, pathlib.Path, os.PathLike, io.IOBase],
1215
1221
  config: Optional[types.UploadFileConfigOrDict] = None,
1216
1222
  ) -> types.File:
1217
1223
  """Calls the API to upload a file asynchronously using a supported file service.
1218
1224
 
1219
1225
  Args:
1220
- path: The path to the file or an `IOBase` object to be uploaded. If it's
1221
- an IOBase object, it must be opened in blocking mode and binary mode. In
1222
- other words, do not use non-blocking mode or text mode. The given stream
1223
- must be seekable, that is, it must be able to call seek() on 'path'.
1226
+ file: A path to the file or an `IOBase` object to be uploaded. If it's an
1227
+ IOBase object, it must be opened in blocking (the default) mode and
1228
+ binary mode. In other words, do not use non-blocking mode or text mode.
1229
+ The given stream must be seekable, that is, it must be able to call
1230
+ `seek()` on 'path'.
1224
1231
  config: Optional parameters to set `diplay_name`, `mime_type`, and `name`.
1225
1232
  """
1226
1233
  if self._api_client.vertexai:
@@ -1234,43 +1241,42 @@ class AsyncFiles(_api_module.BaseModule):
1234
1241
  config_model = types.UploadFileConfig(**config)
1235
1242
  else:
1236
1243
  config_model = config
1237
- file = types.File(
1244
+ file_obj = types.File(
1238
1245
  mime_type=config_model.mime_type,
1239
1246
  name=config_model.name,
1240
1247
  display_name=config_model.display_name,
1241
1248
  )
1242
1249
  else: # if not config
1243
- file = types.File()
1244
- if file.name is not None and not file.name.startswith('files/'):
1245
- file.name = f'files/{file.name}'
1250
+ file_obj = types.File()
1251
+ if file_obj.name is not None and not file_obj.name.startswith('files/'):
1252
+ file_obj.name = f'files/{file_obj.name}'
1246
1253
 
1247
- if isinstance(path, io.IOBase):
1248
- if file.mime_type is None:
1254
+ if isinstance(file, io.IOBase):
1255
+ if file_obj.mime_type is None:
1249
1256
  raise ValueError(
1250
1257
  'Unknown mime type: Could not determine the mimetype for your'
1251
1258
  ' file\n please set the `mime_type` argument'
1252
1259
  )
1253
- if hasattr(path, 'mode'):
1254
- if 'b' not in path.mode:
1260
+ if hasattr(file, 'mode'):
1261
+ if 'b' not in file.mode:
1255
1262
  raise ValueError('The file must be opened in binary mode.')
1256
- offset = path.tell()
1257
- path.seek(0, os.SEEK_END)
1258
- file.size_bytes = path.tell() - offset
1259
- path.seek(offset, os.SEEK_SET)
1263
+ offset = file.tell()
1264
+ file.seek(0, os.SEEK_END)
1265
+ file_obj.size_bytes = file.tell() - offset
1266
+ file.seek(offset, os.SEEK_SET)
1260
1267
  else:
1261
- fs_path = os.fspath(path)
1268
+ fs_path = os.fspath(file)
1262
1269
  if not fs_path or not os.path.isfile(fs_path):
1263
- raise FileNotFoundError(f'{path} is not a valid file path.')
1264
- file.size_bytes = os.path.getsize(fs_path)
1265
- if file.mime_type is None:
1266
- file.mime_type, _ = mimetypes.guess_type(fs_path)
1267
- if file.mime_type is None:
1270
+ raise FileNotFoundError(f'{file} is not a valid file path.')
1271
+ file_obj.size_bytes = os.path.getsize(fs_path)
1272
+ if file_obj.mime_type is None:
1273
+ file_obj.mime_type, _ = mimetypes.guess_type(fs_path)
1274
+ if file_obj.mime_type is None:
1268
1275
  raise ValueError(
1269
1276
  'Unknown mime type: Could not determine the mimetype for your'
1270
1277
  ' file\n please set the `mime_type` argument'
1271
1278
  )
1272
1279
 
1273
- response = {}
1274
1280
  if config_model and config_model.http_options:
1275
1281
  http_options = config_model.http_options
1276
1282
  else:
@@ -1280,29 +1286,31 @@ class AsyncFiles(_api_module.BaseModule):
1280
1286
  'Content-Type': 'application/json',
1281
1287
  'X-Goog-Upload-Protocol': 'resumable',
1282
1288
  'X-Goog-Upload-Command': 'start',
1283
- 'X-Goog-Upload-Header-Content-Length': f'{file.size_bytes}',
1284
- 'X-Goog-Upload-Header-Content-Type': f'{file.mime_type}',
1289
+ 'X-Goog-Upload-Header-Content-Length': f'{file_obj.size_bytes}',
1290
+ 'X-Goog-Upload-Header-Content-Type': f'{file_obj.mime_type}',
1285
1291
  },
1286
- 'deprecated_response_payload': response,
1287
1292
  }
1288
- await self._create(file=file, config={'http_options': http_options})
1293
+ response = await self._create(
1294
+ file=file_obj, config={'http_options': http_options}
1295
+ )
1296
+
1289
1297
  if (
1290
- 'headers' not in response
1291
- or 'X-Goog-Upload-URL' not in response['headers']
1298
+ response.http_headers is None
1299
+ or 'X-Goog-Upload-URL' not in response.http_headers
1292
1300
  ):
1293
1301
  raise KeyError(
1294
1302
  'Failed to create file. Upload URL did not returned from the create'
1295
1303
  ' file request.'
1296
1304
  )
1297
- upload_url = response['headers']['X-Goog-Upload-URL']
1305
+ upload_url = response.http_headers['X-Goog-Upload-URL']
1298
1306
 
1299
- if isinstance(path, io.IOBase):
1307
+ if isinstance(file, io.IOBase):
1300
1308
  return_file = await self._api_client.async_upload_file(
1301
- path, upload_url, file.size_bytes
1309
+ file, upload_url, file_obj.size_bytes
1302
1310
  )
1303
1311
  else:
1304
1312
  return_file = await self._api_client.async_upload_file(
1305
- fs_path, upload_url, file.size_bytes
1313
+ fs_path, upload_url, file_obj.size_bytes
1306
1314
  )
1307
1315
 
1308
1316
  return types.File._from_response(
google/genai/live.py CHANGED
@@ -29,8 +29,10 @@ from . import _api_module
29
29
  from . import _common
30
30
  from . import _transformers as t
31
31
  from . import client
32
+ from . import errors
32
33
  from . import types
33
34
  from ._api_client import ApiClient
35
+ from ._common import experimental_warning
34
36
  from ._common import get_value_by_path as getv
35
37
  from ._common import set_value_by_path as setv
36
38
  from .models import _Content_from_mldev
@@ -633,6 +635,9 @@ class AsyncLive(_api_module.BaseModule):
633
635
  return_value['setup'].update(to_object)
634
636
  return return_value
635
637
 
638
+ @experimental_warning(
639
+ "The live API is experimental and may change in future versions.",
640
+ )
636
641
  @contextlib.asynccontextmanager
637
642
  async def connect(
638
643
  self,