google-genai 0.5.0__py3-none-any.whl → 0.6.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/files.py CHANGED
@@ -15,6 +15,7 @@
15
15
 
16
16
  # Code generated by the Google Gen AI SDK generator DO NOT EDIT.
17
17
 
18
+ import io
18
19
  import mimetypes
19
20
  import os
20
21
  import pathlib
@@ -176,9 +177,15 @@ def _File_to_mldev(
176
177
  if getv(from_object, ['uri']) is not None:
177
178
  setv(to_object, ['uri'], getv(from_object, ['uri']))
178
179
 
180
+ if getv(from_object, ['download_uri']) is not None:
181
+ setv(to_object, ['downloadUri'], getv(from_object, ['download_uri']))
182
+
179
183
  if getv(from_object, ['state']) is not None:
180
184
  setv(to_object, ['state'], getv(from_object, ['state']))
181
185
 
186
+ if getv(from_object, ['source']) is not None:
187
+ setv(to_object, ['source'], getv(from_object, ['source']))
188
+
182
189
  if getv(from_object, ['video_metadata']) is not None:
183
190
  setv(to_object, ['videoMetadata'], getv(from_object, ['video_metadata']))
184
191
 
@@ -227,9 +234,15 @@ def _File_to_vertex(
227
234
  if getv(from_object, ['uri']) is not None:
228
235
  raise ValueError('uri parameter is not supported in Vertex AI.')
229
236
 
237
+ if getv(from_object, ['download_uri']) is not None:
238
+ raise ValueError('download_uri parameter is not supported in Vertex AI.')
239
+
230
240
  if getv(from_object, ['state']) is not None:
231
241
  raise ValueError('state parameter is not supported in Vertex AI.')
232
242
 
243
+ if getv(from_object, ['source']) is not None:
244
+ raise ValueError('source parameter is not supported in Vertex AI.')
245
+
233
246
  if getv(from_object, ['video_metadata']) is not None:
234
247
  raise ValueError('video_metadata parameter is not supported in Vertex AI.')
235
248
 
@@ -492,9 +505,15 @@ def _File_from_mldev(
492
505
  if getv(from_object, ['uri']) is not None:
493
506
  setv(to_object, ['uri'], getv(from_object, ['uri']))
494
507
 
508
+ if getv(from_object, ['downloadUri']) is not None:
509
+ setv(to_object, ['download_uri'], getv(from_object, ['downloadUri']))
510
+
495
511
  if getv(from_object, ['state']) is not None:
496
512
  setv(to_object, ['state'], getv(from_object, ['state']))
497
513
 
514
+ if getv(from_object, ['source']) is not None:
515
+ setv(to_object, ['source'], getv(from_object, ['source']))
516
+
498
517
  if getv(from_object, ['videoMetadata']) is not None:
499
518
  setv(to_object, ['video_metadata'], getv(from_object, ['videoMetadata']))
500
519
 
@@ -633,7 +652,7 @@ class Files(_common.BaseModule):
633
652
  config = request_dict.pop('config', None)
634
653
  http_options = config.pop('httpOptions', None) if config else None
635
654
  request_dict = _common.convert_to_dict(request_dict)
636
- request_dict = _common.apply_base64_encoding(request_dict)
655
+ request_dict = _common.encode_unserializable_types(request_dict)
637
656
 
638
657
  response_dict = self._api_client.request(
639
658
  'get', path, request_dict, http_options
@@ -680,7 +699,7 @@ class Files(_common.BaseModule):
680
699
  config = request_dict.pop('config', None)
681
700
  http_options = config.pop('httpOptions', None) if config else None
682
701
  request_dict = _common.convert_to_dict(request_dict)
683
- request_dict = _common.apply_base64_encoding(request_dict)
702
+ request_dict = _common.encode_unserializable_types(request_dict)
684
703
 
685
704
  response_dict = self._api_client.request(
686
705
  'post', path, request_dict, http_options
@@ -741,7 +760,7 @@ class Files(_common.BaseModule):
741
760
  config = request_dict.pop('config', None)
742
761
  http_options = config.pop('httpOptions', None) if config else None
743
762
  request_dict = _common.convert_to_dict(request_dict)
744
- request_dict = _common.apply_base64_encoding(request_dict)
763
+ request_dict = _common.encode_unserializable_types(request_dict)
745
764
 
746
765
  response_dict = self._api_client.request(
747
766
  'get', path, request_dict, http_options
@@ -795,7 +814,7 @@ class Files(_common.BaseModule):
795
814
  config = request_dict.pop('config', None)
796
815
  http_options = config.pop('httpOptions', None) if config else None
797
816
  request_dict = _common.convert_to_dict(request_dict)
798
- request_dict = _common.apply_base64_encoding(request_dict)
817
+ request_dict = _common.encode_unserializable_types(request_dict)
799
818
 
800
819
  response_dict = self._api_client.request(
801
820
  'delete', path, request_dict, http_options
@@ -819,13 +838,16 @@ class Files(_common.BaseModule):
819
838
  def upload(
820
839
  self,
821
840
  *,
822
- path: str | pathlib.Path | os.PathLike,
841
+ path: Union[str, pathlib.Path, os.PathLike, io.IOBase],
823
842
  config: Optional[types.UploadFileConfigOrDict] = None,
824
843
  ) -> types.File:
825
844
  """Calls the API to upload a file using a supported file service.
826
845
 
827
846
  Args:
828
- path: The path or a path-like object points to the file to to be uploaded.
847
+ path: The path to the file or an `IOBase` object to be uploaded. If it's
848
+ an IOBase object, it must be opened in blocking mode and binary mode. In
849
+ other words, do not use non-blocking mode or text mode. The given stream
850
+ must be seekable, that is, it must be able to call seek() on 'path'.
829
851
  config: Optional parameters to set `diplay_name`, `mime_type`, and `name`.
830
852
  """
831
853
  if self._api_client.vertexai:
@@ -849,17 +871,31 @@ class Files(_common.BaseModule):
849
871
  if file.name is not None and not file.name.startswith('files/'):
850
872
  file.name = f'files/{file.name}'
851
873
 
852
- fs_path = os.fspath(path)
853
- if not fs_path or not os.path.isfile(fs_path):
854
- raise FileNotFoundError(f'{path} is not a valid file path.')
855
- file.size_bytes = os.path.getsize(fs_path)
856
- if file.mime_type is None:
857
- file.mime_type, _ = mimetypes.guess_type(fs_path)
858
- if file.mime_type is None:
859
- raise ValueError(
860
- 'Unknown mime type: Could not determine the mimetype for your file\n'
861
- ' please set the `mime_type` argument'
862
- )
874
+ if isinstance(path, io.IOBase):
875
+ if file.mime_type is None:
876
+ raise ValueError(
877
+ 'Unknown mime type: Could not determine the mimetype for your'
878
+ ' file\n please set the `mime_type` argument'
879
+ )
880
+ if hasattr(path, 'mode'):
881
+ if 'b' not in path.mode:
882
+ raise ValueError('The file must be opened in binary mode.')
883
+ offset = path.tell()
884
+ path.seek(0, os.SEEK_END)
885
+ file.size_bytes = path.tell() - offset
886
+ path.seek(offset, os.SEEK_SET)
887
+ else:
888
+ fs_path = os.fspath(path)
889
+ if not fs_path or not os.path.isfile(fs_path):
890
+ raise FileNotFoundError(f'{path} is not a valid file path.')
891
+ file.size_bytes = os.path.getsize(fs_path)
892
+ if file.mime_type is None:
893
+ file.mime_type, _ = mimetypes.guess_type(fs_path)
894
+ if file.mime_type is None:
895
+ raise ValueError(
896
+ 'Unknown mime type: Could not determine the mimetype for your'
897
+ ' file\n please set the `mime_type` argument'
898
+ )
863
899
  response = {}
864
900
  if config_model and config_model.http_options:
865
901
  http_options = config_model.http_options
@@ -876,6 +912,7 @@ class Files(_common.BaseModule):
876
912
  'response_payload': response,
877
913
  }
878
914
  self._create(file=file, config={'http_options': http_options})
915
+
879
916
  if (
880
917
  'headers' not in response
881
918
  or 'X-Goog-Upload-URL' not in response['headers']
@@ -886,9 +923,14 @@ class Files(_common.BaseModule):
886
923
  )
887
924
  upload_url = response['headers']['X-Goog-Upload-URL']
888
925
 
889
- return_file = self._api_client.upload_file(
890
- fs_path, upload_url, file.size_bytes
891
- )
926
+ if isinstance(path, io.IOBase):
927
+ return_file = self._api_client.upload_file(
928
+ path, upload_url, file.size_bytes
929
+ )
930
+ else:
931
+ return_file = self._api_client.upload_file(
932
+ fs_path, upload_url, file.size_bytes
933
+ )
892
934
 
893
935
  return types.File._from_response(
894
936
  _File_from_mldev(self._api_client, return_file['file']), None
@@ -904,6 +946,74 @@ class Files(_common.BaseModule):
904
946
  config,
905
947
  )
906
948
 
949
+ def download(
950
+ self,
951
+ *,
952
+ file: Union[str, types.File],
953
+ config: Optional[types.DownloadFileConfigOrDict] = None,
954
+ ) -> bytes:
955
+ """Downloads a file's data from storage.
956
+
957
+ Files created by `upload` can't be downloaded. You can tell which files are
958
+ downloadable by checking the `source` or `download_uri` property.
959
+
960
+ Args:
961
+ file (str): A file name, uri, or file object. Identifying which file to
962
+ download.
963
+ config (DownloadFileConfigOrDict): Optional, configuration for the get
964
+ method.
965
+
966
+ Returns:
967
+ File: The file data as bytes.
968
+
969
+ Usage:
970
+
971
+ .. code-block:: python
972
+
973
+ for file client.files.list():
974
+ if file.download_uri is not None:
975
+ break
976
+ else:
977
+ raise ValueError('No files found with a `download_uri`.')
978
+ data = client.files.download(file=file)
979
+ # data = client.files.download(file=file.name)
980
+ # data = client.files.download(file=file.download_uri)
981
+ """
982
+ if self._api_client.vertexai:
983
+ raise ValueError(
984
+ 'Vertex AI does not support the Files API. Use GCS files instead.'
985
+ )
986
+
987
+ config_model = None
988
+ if config:
989
+ if isinstance(config, dict):
990
+ config_model = types.DownloadFileConfig(**config)
991
+ else:
992
+ config_model = config
993
+
994
+ if isinstance(file, types.File) and file.download_uri is None:
995
+ raise ValueError(
996
+ "Only generated files can be downloaded, uploaded files can't be "
997
+ 'downloaded. You can tell which files are downloadable by checking '
998
+ 'the `source` or `download_uri` property.'
999
+ )
1000
+ name = t.t_file_name(self, file)
1001
+
1002
+ path = f'files/{name}:download'
1003
+
1004
+ query_params = {'alt': 'media'}
1005
+ path = f'{path}?{urlencode(query_params)}'
1006
+ http_options = None
1007
+ if getv(config_model, ['http_options']) is not None:
1008
+ http_options = getv(config_model, ['http_options'])
1009
+
1010
+ data = self._api_client.download_file(
1011
+ path,
1012
+ http_options,
1013
+ )
1014
+
1015
+ return data
1016
+
907
1017
 
908
1018
  class AsyncFiles(_common.BaseModule):
909
1019
 
@@ -946,7 +1056,7 @@ class AsyncFiles(_common.BaseModule):
946
1056
  config = request_dict.pop('config', None)
947
1057
  http_options = config.pop('httpOptions', None) if config else None
948
1058
  request_dict = _common.convert_to_dict(request_dict)
949
- request_dict = _common.apply_base64_encoding(request_dict)
1059
+ request_dict = _common.encode_unserializable_types(request_dict)
950
1060
 
951
1061
  response_dict = await self._api_client.async_request(
952
1062
  'get', path, request_dict, http_options
@@ -993,7 +1103,7 @@ class AsyncFiles(_common.BaseModule):
993
1103
  config = request_dict.pop('config', None)
994
1104
  http_options = config.pop('httpOptions', None) if config else None
995
1105
  request_dict = _common.convert_to_dict(request_dict)
996
- request_dict = _common.apply_base64_encoding(request_dict)
1106
+ request_dict = _common.encode_unserializable_types(request_dict)
997
1107
 
998
1108
  response_dict = await self._api_client.async_request(
999
1109
  'post', path, request_dict, http_options
@@ -1054,7 +1164,7 @@ class AsyncFiles(_common.BaseModule):
1054
1164
  config = request_dict.pop('config', None)
1055
1165
  http_options = config.pop('httpOptions', None) if config else None
1056
1166
  request_dict = _common.convert_to_dict(request_dict)
1057
- request_dict = _common.apply_base64_encoding(request_dict)
1167
+ request_dict = _common.encode_unserializable_types(request_dict)
1058
1168
 
1059
1169
  response_dict = await self._api_client.async_request(
1060
1170
  'get', path, request_dict, http_options
@@ -1108,7 +1218,7 @@ class AsyncFiles(_common.BaseModule):
1108
1218
  config = request_dict.pop('config', None)
1109
1219
  http_options = config.pop('httpOptions', None) if config else None
1110
1220
  request_dict = _common.convert_to_dict(request_dict)
1111
- request_dict = _common.apply_base64_encoding(request_dict)
1221
+ request_dict = _common.encode_unserializable_types(request_dict)
1112
1222
 
1113
1223
  response_dict = await self._api_client.async_request(
1114
1224
  'delete', path, request_dict, http_options
@@ -1132,13 +1242,16 @@ class AsyncFiles(_common.BaseModule):
1132
1242
  async def upload(
1133
1243
  self,
1134
1244
  *,
1135
- path: str | pathlib.Path | os.PathLike,
1245
+ path: Union[str, pathlib.Path, os.PathLike, io.IOBase],
1136
1246
  config: Optional[types.UploadFileConfigOrDict] = None,
1137
1247
  ) -> types.File:
1138
1248
  """Calls the API to upload a file asynchronously using a supported file service.
1139
1249
 
1140
1250
  Args:
1141
- path: The path or a path-like object points to the file to be uploaded.
1251
+ path: The path to the file or an `IOBase` object to be uploaded. If it's
1252
+ an IOBase object, it must be opened in blocking mode and binary mode. In
1253
+ other words, do not use non-blocking mode or text mode. The given stream
1254
+ must be seekable, that is, it must be able to call seek() on 'path'.
1142
1255
  config: Optional parameters to set `diplay_name`, `mime_type`, and `name`.
1143
1256
  """
1144
1257
  if self._api_client.vertexai:
@@ -1162,17 +1275,32 @@ class AsyncFiles(_common.BaseModule):
1162
1275
  if file.name is not None and not file.name.startswith('files/'):
1163
1276
  file.name = f'files/{file.name}'
1164
1277
 
1165
- fs_path = os.fspath(path)
1166
- if not fs_path or not os.path.isfile(fs_path):
1167
- raise FileNotFoundError(f'{path} is not a valid file path.')
1168
- file.size_bytes = os.path.getsize(fs_path)
1169
- if file.mime_type is None:
1170
- file.mime_type, _ = mimetypes.guess_type(fs_path)
1171
- if file.mime_type is None:
1172
- raise ValueError(
1173
- 'Unknown mime type: Could not determine the mimetype for your file\n'
1174
- ' please set the `mime_type` argument'
1175
- )
1278
+ if isinstance(path, io.IOBase):
1279
+ if file.mime_type is None:
1280
+ raise ValueError(
1281
+ 'Unknown mime type: Could not determine the mimetype for your'
1282
+ ' file\n please set the `mime_type` argument'
1283
+ )
1284
+ if hasattr(path, 'mode'):
1285
+ if 'b' not in path.mode:
1286
+ raise ValueError('The file must be opened in binary mode.')
1287
+ offset = path.tell()
1288
+ path.seek(0, os.SEEK_END)
1289
+ file.size_bytes = path.tell() - offset
1290
+ path.seek(offset, os.SEEK_SET)
1291
+ else:
1292
+ fs_path = os.fspath(path)
1293
+ if not fs_path or not os.path.isfile(fs_path):
1294
+ raise FileNotFoundError(f'{path} is not a valid file path.')
1295
+ file.size_bytes = os.path.getsize(fs_path)
1296
+ if file.mime_type is None:
1297
+ file.mime_type, _ = mimetypes.guess_type(fs_path)
1298
+ if file.mime_type is None:
1299
+ raise ValueError(
1300
+ 'Unknown mime type: Could not determine the mimetype for your'
1301
+ ' file\n please set the `mime_type` argument'
1302
+ )
1303
+
1176
1304
  response = {}
1177
1305
  if config_model and config_model.http_options:
1178
1306
  http_options = config_model.http_options
@@ -1199,9 +1327,14 @@ class AsyncFiles(_common.BaseModule):
1199
1327
  )
1200
1328
  upload_url = response['headers']['X-Goog-Upload-URL']
1201
1329
 
1202
- return_file = await self._api_client.async_upload_file(
1203
- fs_path, upload_url, file.size_bytes
1204
- )
1330
+ if isinstance(path, io.IOBase):
1331
+ return_file = await self._api_client.async_upload_file(
1332
+ path, upload_url, file.size_bytes
1333
+ )
1334
+ else:
1335
+ return_file = await self._api_client.async_upload_file(
1336
+ fs_path, upload_url, file.size_bytes
1337
+ )
1205
1338
 
1206
1339
  return types.File._from_response(
1207
1340
  _File_from_mldev(self._api_client, return_file['file']), None
@@ -1216,3 +1349,69 @@ class AsyncFiles(_common.BaseModule):
1216
1349
  await self._list(config=config),
1217
1350
  config,
1218
1351
  )
1352
+
1353
+ async def download(
1354
+ self,
1355
+ *,
1356
+ file: Union[str, types.File],
1357
+ config: Optional[types.DownloadFileConfigOrDict] = None,
1358
+ ) -> bytes:
1359
+ """Downloads a file's data from the file service.
1360
+
1361
+ The Vertex-AI implementation of the API foes not include the file service.
1362
+
1363
+ Files created by `upload` can't be downloaded. You can tell which files are
1364
+ downloadable by checking the `download_uri` property.
1365
+
1366
+ Args:
1367
+ File (str): A file name, uri, or file object. Identifying which file to
1368
+ download.
1369
+ config (DownloadFileConfigOrDict): Optional, configuration for the get
1370
+ method.
1371
+
1372
+ Returns:
1373
+ File: The file data as bytes.
1374
+
1375
+ Usage:
1376
+
1377
+ .. code-block:: python
1378
+
1379
+ for file client.files.list():
1380
+ if file.download_uri is not None:
1381
+ break
1382
+ else:
1383
+ raise ValueError('No files found with a `download_uri`.')
1384
+ data = client.files.download(file=file)
1385
+ # data = client.files.download(file=file.name)
1386
+ # data = client.files.download(file=file.uri)
1387
+ """
1388
+ if self._api_client.vertexai:
1389
+ raise ValueError(
1390
+ 'Vertex AI does not support the Files API. Use GCS files instead.'
1391
+ )
1392
+
1393
+ config_model = None
1394
+ if config:
1395
+ if isinstance(config, dict):
1396
+ config_model = types.DownloadFileConfig(**config)
1397
+ else:
1398
+ config_model = config
1399
+
1400
+ name = t.t_file_name(self, file)
1401
+
1402
+ path = f'files/{name}:download'
1403
+
1404
+ http_options = None
1405
+ if getv(config_model, ['http_options']) is not None:
1406
+ http_options = getv(config_model, ['http_options'])
1407
+
1408
+ query_params = {'alt': 'media'}
1409
+ if query_params:
1410
+ path = f'{path}?{urlencode(query_params)}'
1411
+
1412
+ data = await self._api_client.async_download_file(
1413
+ path,
1414
+ http_options,
1415
+ )
1416
+
1417
+ return data
google/genai/live.py CHANGED
@@ -108,7 +108,7 @@ class AsyncSession:
108
108
 
109
109
  The method will yield the model responses from the server. The returned
110
110
  responses will represent a complete model turn. When the returned message
111
- is fuction call, user must call `send` with the function response to
111
+ is function call, user must call `send` with the function response to
112
112
  continue the turn.
113
113
 
114
114
  Yields:
@@ -380,6 +380,10 @@ class AsyncSession:
380
380
  isinstance(c, dict) and 'name' in c and 'response' in c for c in input
381
381
  ):
382
382
  # ToolResponse.FunctionResponse
383
+ if not (self._api_client.vertexai):
384
+ for item in input:
385
+ if 'id' not in item:
386
+ raise ValueError(_FUNCTION_RESPONSE_REQUIRES_ID)
383
387
  client_message = {'tool_response': {'function_responses': input}}
384
388
  elif isinstance(input, Sequence) and any(isinstance(c, str) for c in input):
385
389
  to_object = {}