google-genai 0.5.0__py3-none-any.whl → 0.7.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,11 +15,13 @@
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
21
22
  from typing import Optional, Union
22
23
  from urllib.parse import urlencode
24
+ from . import _api_module
23
25
  from . import _common
24
26
  from . import _transformers as t
25
27
  from . import types
@@ -35,8 +37,6 @@ def _ListFilesConfig_to_mldev(
35
37
  parent_object: dict = None,
36
38
  ) -> dict:
37
39
  to_object = {}
38
- if getv(from_object, ['http_options']) is not None:
39
- setv(to_object, ['httpOptions'], getv(from_object, ['http_options']))
40
40
 
41
41
  if getv(from_object, ['page_size']) is not None:
42
42
  setv(
@@ -59,8 +59,6 @@ def _ListFilesConfig_to_vertex(
59
59
  parent_object: dict = None,
60
60
  ) -> dict:
61
61
  to_object = {}
62
- if getv(from_object, ['http_options']) is not None:
63
- setv(to_object, ['httpOptions'], getv(from_object, ['http_options']))
64
62
 
65
63
  if getv(from_object, ['page_size']) is not None:
66
64
  setv(
@@ -176,9 +174,15 @@ def _File_to_mldev(
176
174
  if getv(from_object, ['uri']) is not None:
177
175
  setv(to_object, ['uri'], getv(from_object, ['uri']))
178
176
 
177
+ if getv(from_object, ['download_uri']) is not None:
178
+ setv(to_object, ['downloadUri'], getv(from_object, ['download_uri']))
179
+
179
180
  if getv(from_object, ['state']) is not None:
180
181
  setv(to_object, ['state'], getv(from_object, ['state']))
181
182
 
183
+ if getv(from_object, ['source']) is not None:
184
+ setv(to_object, ['source'], getv(from_object, ['source']))
185
+
182
186
  if getv(from_object, ['video_metadata']) is not None:
183
187
  setv(to_object, ['videoMetadata'], getv(from_object, ['video_metadata']))
184
188
 
@@ -227,9 +231,15 @@ def _File_to_vertex(
227
231
  if getv(from_object, ['uri']) is not None:
228
232
  raise ValueError('uri parameter is not supported in Vertex AI.')
229
233
 
234
+ if getv(from_object, ['download_uri']) is not None:
235
+ raise ValueError('download_uri parameter is not supported in Vertex AI.')
236
+
230
237
  if getv(from_object, ['state']) is not None:
231
238
  raise ValueError('state parameter is not supported in Vertex AI.')
232
239
 
240
+ if getv(from_object, ['source']) is not None:
241
+ raise ValueError('source parameter is not supported in Vertex AI.')
242
+
233
243
  if getv(from_object, ['video_metadata']) is not None:
234
244
  raise ValueError('video_metadata parameter is not supported in Vertex AI.')
235
245
 
@@ -239,30 +249,6 @@ def _File_to_vertex(
239
249
  return to_object
240
250
 
241
251
 
242
- def _CreateFileConfig_to_mldev(
243
- api_client: ApiClient,
244
- from_object: Union[dict, object],
245
- parent_object: dict = None,
246
- ) -> dict:
247
- to_object = {}
248
- if getv(from_object, ['http_options']) is not None:
249
- setv(to_object, ['httpOptions'], getv(from_object, ['http_options']))
250
-
251
- return to_object
252
-
253
-
254
- def _CreateFileConfig_to_vertex(
255
- api_client: ApiClient,
256
- from_object: Union[dict, object],
257
- parent_object: dict = None,
258
- ) -> dict:
259
- to_object = {}
260
- if getv(from_object, ['http_options']) is not None:
261
- setv(to_object, ['httpOptions'], getv(from_object, ['http_options']))
262
-
263
- return to_object
264
-
265
-
266
252
  def _CreateFileParameters_to_mldev(
267
253
  api_client: ApiClient,
268
254
  from_object: Union[dict, object],
@@ -277,13 +263,7 @@ def _CreateFileParameters_to_mldev(
277
263
  )
278
264
 
279
265
  if getv(from_object, ['config']) is not None:
280
- setv(
281
- to_object,
282
- ['config'],
283
- _CreateFileConfig_to_mldev(
284
- api_client, getv(from_object, ['config']), to_object
285
- ),
286
- )
266
+ setv(to_object, ['config'], getv(from_object, ['config']))
287
267
 
288
268
  return to_object
289
269
 
@@ -303,30 +283,6 @@ def _CreateFileParameters_to_vertex(
303
283
  return to_object
304
284
 
305
285
 
306
- def _GetFileConfig_to_mldev(
307
- api_client: ApiClient,
308
- from_object: Union[dict, object],
309
- parent_object: dict = None,
310
- ) -> dict:
311
- to_object = {}
312
- if getv(from_object, ['http_options']) is not None:
313
- setv(to_object, ['httpOptions'], getv(from_object, ['http_options']))
314
-
315
- return to_object
316
-
317
-
318
- def _GetFileConfig_to_vertex(
319
- api_client: ApiClient,
320
- from_object: Union[dict, object],
321
- parent_object: dict = None,
322
- ) -> dict:
323
- to_object = {}
324
- if getv(from_object, ['http_options']) is not None:
325
- setv(to_object, ['httpOptions'], getv(from_object, ['http_options']))
326
-
327
- return to_object
328
-
329
-
330
286
  def _GetFileParameters_to_mldev(
331
287
  api_client: ApiClient,
332
288
  from_object: Union[dict, object],
@@ -341,13 +297,7 @@ def _GetFileParameters_to_mldev(
341
297
  )
342
298
 
343
299
  if getv(from_object, ['config']) is not None:
344
- setv(
345
- to_object,
346
- ['config'],
347
- _GetFileConfig_to_mldev(
348
- api_client, getv(from_object, ['config']), to_object
349
- ),
350
- )
300
+ setv(to_object, ['config'], getv(from_object, ['config']))
351
301
 
352
302
  return to_object
353
303
 
@@ -367,30 +317,6 @@ def _GetFileParameters_to_vertex(
367
317
  return to_object
368
318
 
369
319
 
370
- def _DeleteFileConfig_to_mldev(
371
- api_client: ApiClient,
372
- from_object: Union[dict, object],
373
- parent_object: dict = None,
374
- ) -> dict:
375
- to_object = {}
376
- if getv(from_object, ['http_options']) is not None:
377
- setv(to_object, ['httpOptions'], getv(from_object, ['http_options']))
378
-
379
- return to_object
380
-
381
-
382
- def _DeleteFileConfig_to_vertex(
383
- api_client: ApiClient,
384
- from_object: Union[dict, object],
385
- parent_object: dict = None,
386
- ) -> dict:
387
- to_object = {}
388
- if getv(from_object, ['http_options']) is not None:
389
- setv(to_object, ['httpOptions'], getv(from_object, ['http_options']))
390
-
391
- return to_object
392
-
393
-
394
320
  def _DeleteFileParameters_to_mldev(
395
321
  api_client: ApiClient,
396
322
  from_object: Union[dict, object],
@@ -405,13 +331,7 @@ def _DeleteFileParameters_to_mldev(
405
331
  )
406
332
 
407
333
  if getv(from_object, ['config']) is not None:
408
- setv(
409
- to_object,
410
- ['config'],
411
- _DeleteFileConfig_to_mldev(
412
- api_client, getv(from_object, ['config']), to_object
413
- ),
414
- )
334
+ setv(to_object, ['config'], getv(from_object, ['config']))
415
335
 
416
336
  return to_object
417
337
 
@@ -431,6 +351,16 @@ def _DeleteFileParameters_to_vertex(
431
351
  return to_object
432
352
 
433
353
 
354
+ def _FileState_to_vertex_enum_validate(enum_value: any):
355
+ if enum_value in set(['STATE_UNSPECIFIED', 'PROCESSING', 'ACTIVE', 'FAILED']):
356
+ raise ValueError(f'{enum_value} enum value is not supported in Vertex AI.')
357
+
358
+
359
+ def _FileSource_to_vertex_enum_validate(enum_value: any):
360
+ if enum_value in set(['SOURCE_UNSPECIFIED', 'UPLOADED', 'GENERATED']):
361
+ raise ValueError(f'{enum_value} enum value is not supported in Vertex AI.')
362
+
363
+
434
364
  def _FileStatus_from_mldev(
435
365
  api_client: ApiClient,
436
366
  from_object: Union[dict, object],
@@ -492,9 +422,15 @@ def _File_from_mldev(
492
422
  if getv(from_object, ['uri']) is not None:
493
423
  setv(to_object, ['uri'], getv(from_object, ['uri']))
494
424
 
425
+ if getv(from_object, ['downloadUri']) is not None:
426
+ setv(to_object, ['download_uri'], getv(from_object, ['downloadUri']))
427
+
495
428
  if getv(from_object, ['state']) is not None:
496
429
  setv(to_object, ['state'], getv(from_object, ['state']))
497
430
 
431
+ if getv(from_object, ['source']) is not None:
432
+ setv(to_object, ['source'], getv(from_object, ['source']))
433
+
498
434
  if getv(from_object, ['videoMetadata']) is not None:
499
435
  setv(to_object, ['video_metadata'], getv(from_object, ['videoMetadata']))
500
436
 
@@ -592,7 +528,7 @@ def _DeleteFileResponse_from_vertex(
592
528
  return to_object
593
529
 
594
530
 
595
- class Files(_common.BaseModule):
531
+ class Files(_api_module.BaseModule):
596
532
 
597
533
  def _list(
598
534
  self, *, config: Optional[types.ListFilesConfigOrDict] = None
@@ -630,10 +566,16 @@ class Files(_common.BaseModule):
630
566
  if query_params:
631
567
  path = f'{path}?{urlencode(query_params)}'
632
568
  # TODO: remove the hack that pops config.
633
- config = request_dict.pop('config', None)
634
- http_options = config.pop('httpOptions', None) if config else None
569
+ request_dict.pop('config', None)
570
+
571
+ http_options = None
572
+ if isinstance(config, dict):
573
+ http_options = config.get('http_options', None)
574
+ elif hasattr(config, 'http_options'):
575
+ http_options = config.http_options
576
+
635
577
  request_dict = _common.convert_to_dict(request_dict)
636
- request_dict = _common.apply_base64_encoding(request_dict)
578
+ request_dict = _common.encode_unserializable_types(request_dict)
637
579
 
638
580
  response_dict = self._api_client.request(
639
581
  'get', path, request_dict, http_options
@@ -649,7 +591,7 @@ class Files(_common.BaseModule):
649
591
  )
650
592
 
651
593
  return_value = types.ListFilesResponse._from_response(
652
- response_dict, parameter_model
594
+ response=response_dict, kwargs=parameter_model
653
595
  )
654
596
  self._api_client._verify_response(return_value)
655
597
  return return_value
@@ -677,10 +619,16 @@ class Files(_common.BaseModule):
677
619
  if query_params:
678
620
  path = f'{path}?{urlencode(query_params)}'
679
621
  # TODO: remove the hack that pops config.
680
- config = request_dict.pop('config', None)
681
- http_options = config.pop('httpOptions', None) if config else None
622
+ request_dict.pop('config', None)
623
+
624
+ http_options = None
625
+ if isinstance(config, dict):
626
+ http_options = config.get('http_options', None)
627
+ elif hasattr(config, 'http_options'):
628
+ http_options = config.http_options
629
+
682
630
  request_dict = _common.convert_to_dict(request_dict)
683
- request_dict = _common.apply_base64_encoding(request_dict)
631
+ request_dict = _common.encode_unserializable_types(request_dict)
684
632
 
685
633
  response_dict = self._api_client.request(
686
634
  'post', path, request_dict, http_options
@@ -696,7 +644,7 @@ class Files(_common.BaseModule):
696
644
  )
697
645
 
698
646
  return_value = types.CreateFileResponse._from_response(
699
- response_dict, parameter_model
647
+ response=response_dict, kwargs=parameter_model
700
648
  )
701
649
  self._api_client._verify_response(return_value)
702
650
  return return_value
@@ -738,10 +686,16 @@ class Files(_common.BaseModule):
738
686
  if query_params:
739
687
  path = f'{path}?{urlencode(query_params)}'
740
688
  # TODO: remove the hack that pops config.
741
- config = request_dict.pop('config', None)
742
- http_options = config.pop('httpOptions', None) if config else None
689
+ request_dict.pop('config', None)
690
+
691
+ http_options = None
692
+ if isinstance(config, dict):
693
+ http_options = config.get('http_options', None)
694
+ elif hasattr(config, 'http_options'):
695
+ http_options = config.http_options
696
+
743
697
  request_dict = _common.convert_to_dict(request_dict)
744
- request_dict = _common.apply_base64_encoding(request_dict)
698
+ request_dict = _common.encode_unserializable_types(request_dict)
745
699
 
746
700
  response_dict = self._api_client.request(
747
701
  'get', path, request_dict, http_options
@@ -752,7 +706,9 @@ class Files(_common.BaseModule):
752
706
  else:
753
707
  response_dict = _File_from_mldev(self._api_client, response_dict)
754
708
 
755
- return_value = types.File._from_response(response_dict, parameter_model)
709
+ return_value = types.File._from_response(
710
+ response=response_dict, kwargs=parameter_model
711
+ )
756
712
  self._api_client._verify_response(return_value)
757
713
  return return_value
758
714
 
@@ -792,10 +748,16 @@ class Files(_common.BaseModule):
792
748
  if query_params:
793
749
  path = f'{path}?{urlencode(query_params)}'
794
750
  # TODO: remove the hack that pops config.
795
- config = request_dict.pop('config', None)
796
- http_options = config.pop('httpOptions', None) if config else None
751
+ request_dict.pop('config', None)
752
+
753
+ http_options = None
754
+ if isinstance(config, dict):
755
+ http_options = config.get('http_options', None)
756
+ elif hasattr(config, 'http_options'):
757
+ http_options = config.http_options
758
+
797
759
  request_dict = _common.convert_to_dict(request_dict)
798
- request_dict = _common.apply_base64_encoding(request_dict)
760
+ request_dict = _common.encode_unserializable_types(request_dict)
799
761
 
800
762
  response_dict = self._api_client.request(
801
763
  'delete', path, request_dict, http_options
@@ -811,7 +773,7 @@ class Files(_common.BaseModule):
811
773
  )
812
774
 
813
775
  return_value = types.DeleteFileResponse._from_response(
814
- response_dict, parameter_model
776
+ response=response_dict, kwargs=parameter_model
815
777
  )
816
778
  self._api_client._verify_response(return_value)
817
779
  return return_value
@@ -819,13 +781,16 @@ class Files(_common.BaseModule):
819
781
  def upload(
820
782
  self,
821
783
  *,
822
- path: str | pathlib.Path | os.PathLike,
784
+ path: Union[str, pathlib.Path, os.PathLike, io.IOBase],
823
785
  config: Optional[types.UploadFileConfigOrDict] = None,
824
786
  ) -> types.File:
825
787
  """Calls the API to upload a file using a supported file service.
826
788
 
827
789
  Args:
828
- path: The path or a path-like object points to the file to to be uploaded.
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'.
829
794
  config: Optional parameters to set `diplay_name`, `mime_type`, and `name`.
830
795
  """
831
796
  if self._api_client.vertexai:
@@ -849,17 +814,31 @@ class Files(_common.BaseModule):
849
814
  if file.name is not None and not file.name.startswith('files/'):
850
815
  file.name = f'files/{file.name}'
851
816
 
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
- )
817
+ if isinstance(path, io.IOBase):
818
+ if file.mime_type is None:
819
+ raise ValueError(
820
+ 'Unknown mime type: Could not determine the mimetype for your'
821
+ ' file\n please set the `mime_type` argument'
822
+ )
823
+ if hasattr(path, 'mode'):
824
+ if 'b' not in path.mode:
825
+ 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)
830
+ else:
831
+ fs_path = os.fspath(path)
832
+ 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 ValueError(
839
+ 'Unknown mime type: Could not determine the mimetype for your'
840
+ ' file\n please set the `mime_type` argument'
841
+ )
863
842
  response = {}
864
843
  if config_model and config_model.http_options:
865
844
  http_options = config_model.http_options
@@ -873,9 +852,10 @@ class Files(_common.BaseModule):
873
852
  'X-Goog-Upload-Header-Content-Length': f'{file.size_bytes}',
874
853
  'X-Goog-Upload-Header-Content-Type': f'{file.mime_type}',
875
854
  },
876
- 'response_payload': response,
855
+ 'deprecated_response_payload': response,
877
856
  }
878
857
  self._create(file=file, config={'http_options': http_options})
858
+
879
859
  if (
880
860
  'headers' not in response
881
861
  or 'X-Goog-Upload-URL' not in response['headers']
@@ -886,9 +866,14 @@ class Files(_common.BaseModule):
886
866
  )
887
867
  upload_url = response['headers']['X-Goog-Upload-URL']
888
868
 
889
- return_file = self._api_client.upload_file(
890
- fs_path, upload_url, file.size_bytes
891
- )
869
+ if isinstance(path, io.IOBase):
870
+ return_file = self._api_client.upload_file(
871
+ path, upload_url, file.size_bytes
872
+ )
873
+ else:
874
+ return_file = self._api_client.upload_file(
875
+ fs_path, upload_url, file.size_bytes
876
+ )
892
877
 
893
878
  return types.File._from_response(
894
879
  _File_from_mldev(self._api_client, return_file['file']), None
@@ -904,8 +889,76 @@ class Files(_common.BaseModule):
904
889
  config,
905
890
  )
906
891
 
892
+ def download(
893
+ self,
894
+ *,
895
+ file: Union[str, types.File],
896
+ config: Optional[types.DownloadFileConfigOrDict] = None,
897
+ ) -> bytes:
898
+ """Downloads a file's data from storage.
899
+
900
+ Files created by `upload` can't be downloaded. You can tell which files are
901
+ downloadable by checking the `source` or `download_uri` property.
902
+
903
+ Args:
904
+ file (str): A file name, uri, or file object. Identifying which file to
905
+ download.
906
+ config (DownloadFileConfigOrDict): Optional, configuration for the get
907
+ method.
908
+
909
+ Returns:
910
+ File: The file data as bytes.
911
+
912
+ Usage:
913
+
914
+ .. code-block:: python
915
+
916
+ for file client.files.list():
917
+ if file.download_uri is not None:
918
+ break
919
+ else:
920
+ raise ValueError('No files found with a `download_uri`.')
921
+ data = client.files.download(file=file)
922
+ # data = client.files.download(file=file.name)
923
+ # data = client.files.download(file=file.download_uri)
924
+ """
925
+ if self._api_client.vertexai:
926
+ raise ValueError(
927
+ 'Vertex AI does not support the Files API. Use GCS files instead.'
928
+ )
929
+
930
+ config_model = None
931
+ if config:
932
+ if isinstance(config, dict):
933
+ config_model = types.DownloadFileConfig(**config)
934
+ else:
935
+ config_model = config
936
+
937
+ if isinstance(file, types.File) and file.download_uri is None:
938
+ raise ValueError(
939
+ "Only generated files can be downloaded, uploaded files can't be "
940
+ 'downloaded. You can tell which files are downloadable by checking '
941
+ 'the `source` or `download_uri` property.'
942
+ )
943
+ name = t.t_file_name(self, file)
944
+
945
+ path = f'files/{name}:download'
946
+
947
+ query_params = {'alt': 'media'}
948
+ path = f'{path}?{urlencode(query_params)}'
949
+ http_options = None
950
+ if getv(config_model, ['http_options']) is not None:
951
+ http_options = getv(config_model, ['http_options'])
952
+
953
+ data = self._api_client.download_file(
954
+ path,
955
+ http_options,
956
+ )
957
+
958
+ return data
959
+
907
960
 
908
- class AsyncFiles(_common.BaseModule):
961
+ class AsyncFiles(_api_module.BaseModule):
909
962
 
910
963
  async def _list(
911
964
  self, *, config: Optional[types.ListFilesConfigOrDict] = None
@@ -943,10 +996,16 @@ class AsyncFiles(_common.BaseModule):
943
996
  if query_params:
944
997
  path = f'{path}?{urlencode(query_params)}'
945
998
  # TODO: remove the hack that pops config.
946
- config = request_dict.pop('config', None)
947
- http_options = config.pop('httpOptions', None) if config else None
999
+ request_dict.pop('config', None)
1000
+
1001
+ http_options = None
1002
+ if isinstance(config, dict):
1003
+ http_options = config.get('http_options', None)
1004
+ elif hasattr(config, 'http_options'):
1005
+ http_options = config.http_options
1006
+
948
1007
  request_dict = _common.convert_to_dict(request_dict)
949
- request_dict = _common.apply_base64_encoding(request_dict)
1008
+ request_dict = _common.encode_unserializable_types(request_dict)
950
1009
 
951
1010
  response_dict = await self._api_client.async_request(
952
1011
  'get', path, request_dict, http_options
@@ -962,7 +1021,7 @@ class AsyncFiles(_common.BaseModule):
962
1021
  )
963
1022
 
964
1023
  return_value = types.ListFilesResponse._from_response(
965
- response_dict, parameter_model
1024
+ response=response_dict, kwargs=parameter_model
966
1025
  )
967
1026
  self._api_client._verify_response(return_value)
968
1027
  return return_value
@@ -990,10 +1049,16 @@ class AsyncFiles(_common.BaseModule):
990
1049
  if query_params:
991
1050
  path = f'{path}?{urlencode(query_params)}'
992
1051
  # TODO: remove the hack that pops config.
993
- config = request_dict.pop('config', None)
994
- http_options = config.pop('httpOptions', None) if config else None
1052
+ request_dict.pop('config', None)
1053
+
1054
+ http_options = None
1055
+ if isinstance(config, dict):
1056
+ http_options = config.get('http_options', None)
1057
+ elif hasattr(config, 'http_options'):
1058
+ http_options = config.http_options
1059
+
995
1060
  request_dict = _common.convert_to_dict(request_dict)
996
- request_dict = _common.apply_base64_encoding(request_dict)
1061
+ request_dict = _common.encode_unserializable_types(request_dict)
997
1062
 
998
1063
  response_dict = await self._api_client.async_request(
999
1064
  'post', path, request_dict, http_options
@@ -1009,7 +1074,7 @@ class AsyncFiles(_common.BaseModule):
1009
1074
  )
1010
1075
 
1011
1076
  return_value = types.CreateFileResponse._from_response(
1012
- response_dict, parameter_model
1077
+ response=response_dict, kwargs=parameter_model
1013
1078
  )
1014
1079
  self._api_client._verify_response(return_value)
1015
1080
  return return_value
@@ -1051,10 +1116,16 @@ class AsyncFiles(_common.BaseModule):
1051
1116
  if query_params:
1052
1117
  path = f'{path}?{urlencode(query_params)}'
1053
1118
  # TODO: remove the hack that pops config.
1054
- config = request_dict.pop('config', None)
1055
- http_options = config.pop('httpOptions', None) if config else None
1119
+ request_dict.pop('config', None)
1120
+
1121
+ http_options = None
1122
+ if isinstance(config, dict):
1123
+ http_options = config.get('http_options', None)
1124
+ elif hasattr(config, 'http_options'):
1125
+ http_options = config.http_options
1126
+
1056
1127
  request_dict = _common.convert_to_dict(request_dict)
1057
- request_dict = _common.apply_base64_encoding(request_dict)
1128
+ request_dict = _common.encode_unserializable_types(request_dict)
1058
1129
 
1059
1130
  response_dict = await self._api_client.async_request(
1060
1131
  'get', path, request_dict, http_options
@@ -1065,7 +1136,9 @@ class AsyncFiles(_common.BaseModule):
1065
1136
  else:
1066
1137
  response_dict = _File_from_mldev(self._api_client, response_dict)
1067
1138
 
1068
- return_value = types.File._from_response(response_dict, parameter_model)
1139
+ return_value = types.File._from_response(
1140
+ response=response_dict, kwargs=parameter_model
1141
+ )
1069
1142
  self._api_client._verify_response(return_value)
1070
1143
  return return_value
1071
1144
 
@@ -1105,10 +1178,16 @@ class AsyncFiles(_common.BaseModule):
1105
1178
  if query_params:
1106
1179
  path = f'{path}?{urlencode(query_params)}'
1107
1180
  # TODO: remove the hack that pops config.
1108
- config = request_dict.pop('config', None)
1109
- http_options = config.pop('httpOptions', None) if config else None
1181
+ request_dict.pop('config', None)
1182
+
1183
+ http_options = None
1184
+ if isinstance(config, dict):
1185
+ http_options = config.get('http_options', None)
1186
+ elif hasattr(config, 'http_options'):
1187
+ http_options = config.http_options
1188
+
1110
1189
  request_dict = _common.convert_to_dict(request_dict)
1111
- request_dict = _common.apply_base64_encoding(request_dict)
1190
+ request_dict = _common.encode_unserializable_types(request_dict)
1112
1191
 
1113
1192
  response_dict = await self._api_client.async_request(
1114
1193
  'delete', path, request_dict, http_options
@@ -1124,7 +1203,7 @@ class AsyncFiles(_common.BaseModule):
1124
1203
  )
1125
1204
 
1126
1205
  return_value = types.DeleteFileResponse._from_response(
1127
- response_dict, parameter_model
1206
+ response=response_dict, kwargs=parameter_model
1128
1207
  )
1129
1208
  self._api_client._verify_response(return_value)
1130
1209
  return return_value
@@ -1132,13 +1211,16 @@ class AsyncFiles(_common.BaseModule):
1132
1211
  async def upload(
1133
1212
  self,
1134
1213
  *,
1135
- path: str | pathlib.Path | os.PathLike,
1214
+ path: Union[str, pathlib.Path, os.PathLike, io.IOBase],
1136
1215
  config: Optional[types.UploadFileConfigOrDict] = None,
1137
1216
  ) -> types.File:
1138
1217
  """Calls the API to upload a file asynchronously using a supported file service.
1139
1218
 
1140
1219
  Args:
1141
- path: The path or a path-like object points to the file to be uploaded.
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'.
1142
1224
  config: Optional parameters to set `diplay_name`, `mime_type`, and `name`.
1143
1225
  """
1144
1226
  if self._api_client.vertexai:
@@ -1162,17 +1244,32 @@ class AsyncFiles(_common.BaseModule):
1162
1244
  if file.name is not None and not file.name.startswith('files/'):
1163
1245
  file.name = f'files/{file.name}'
1164
1246
 
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
- )
1247
+ if isinstance(path, io.IOBase):
1248
+ if file.mime_type is None:
1249
+ raise ValueError(
1250
+ 'Unknown mime type: Could not determine the mimetype for your'
1251
+ ' file\n please set the `mime_type` argument'
1252
+ )
1253
+ if hasattr(path, 'mode'):
1254
+ if 'b' not in path.mode:
1255
+ 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)
1260
+ else:
1261
+ fs_path = os.fspath(path)
1262
+ 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:
1268
+ raise ValueError(
1269
+ 'Unknown mime type: Could not determine the mimetype for your'
1270
+ ' file\n please set the `mime_type` argument'
1271
+ )
1272
+
1176
1273
  response = {}
1177
1274
  if config_model and config_model.http_options:
1178
1275
  http_options = config_model.http_options
@@ -1186,7 +1283,7 @@ class AsyncFiles(_common.BaseModule):
1186
1283
  'X-Goog-Upload-Header-Content-Length': f'{file.size_bytes}',
1187
1284
  'X-Goog-Upload-Header-Content-Type': f'{file.mime_type}',
1188
1285
  },
1189
- 'response_payload': response,
1286
+ 'deprecated_response_payload': response,
1190
1287
  }
1191
1288
  await self._create(file=file, config={'http_options': http_options})
1192
1289
  if (
@@ -1199,9 +1296,14 @@ class AsyncFiles(_common.BaseModule):
1199
1296
  )
1200
1297
  upload_url = response['headers']['X-Goog-Upload-URL']
1201
1298
 
1202
- return_file = await self._api_client.async_upload_file(
1203
- fs_path, upload_url, file.size_bytes
1204
- )
1299
+ if isinstance(path, io.IOBase):
1300
+ return_file = await self._api_client.async_upload_file(
1301
+ path, upload_url, file.size_bytes
1302
+ )
1303
+ else:
1304
+ return_file = await self._api_client.async_upload_file(
1305
+ fs_path, upload_url, file.size_bytes
1306
+ )
1205
1307
 
1206
1308
  return types.File._from_response(
1207
1309
  _File_from_mldev(self._api_client, return_file['file']), None
@@ -1216,3 +1318,69 @@ class AsyncFiles(_common.BaseModule):
1216
1318
  await self._list(config=config),
1217
1319
  config,
1218
1320
  )
1321
+
1322
+ async def download(
1323
+ self,
1324
+ *,
1325
+ file: Union[str, types.File],
1326
+ config: Optional[types.DownloadFileConfigOrDict] = None,
1327
+ ) -> bytes:
1328
+ """Downloads a file's data from the file service.
1329
+
1330
+ The Vertex-AI implementation of the API foes not include the file service.
1331
+
1332
+ Files created by `upload` can't be downloaded. You can tell which files are
1333
+ downloadable by checking the `download_uri` property.
1334
+
1335
+ Args:
1336
+ File (str): A file name, uri, or file object. Identifying which file to
1337
+ download.
1338
+ config (DownloadFileConfigOrDict): Optional, configuration for the get
1339
+ method.
1340
+
1341
+ Returns:
1342
+ File: The file data as bytes.
1343
+
1344
+ Usage:
1345
+
1346
+ .. code-block:: python
1347
+
1348
+ for file client.files.list():
1349
+ if file.download_uri is not None:
1350
+ break
1351
+ else:
1352
+ raise ValueError('No files found with a `download_uri`.')
1353
+ data = client.files.download(file=file)
1354
+ # data = client.files.download(file=file.name)
1355
+ # data = client.files.download(file=file.uri)
1356
+ """
1357
+ if self._api_client.vertexai:
1358
+ raise ValueError(
1359
+ 'Vertex AI does not support the Files API. Use GCS files instead.'
1360
+ )
1361
+
1362
+ config_model = None
1363
+ if config:
1364
+ if isinstance(config, dict):
1365
+ config_model = types.DownloadFileConfig(**config)
1366
+ else:
1367
+ config_model = config
1368
+
1369
+ name = t.t_file_name(self, file)
1370
+
1371
+ path = f'files/{name}:download'
1372
+
1373
+ http_options = None
1374
+ if getv(config_model, ['http_options']) is not None:
1375
+ http_options = getv(config_model, ['http_options'])
1376
+
1377
+ query_params = {'alt': 'media'}
1378
+ if query_params:
1379
+ path = f'{path}?{urlencode(query_params)}'
1380
+
1381
+ data = await self._api_client.async_download_file(
1382
+ path,
1383
+ http_options,
1384
+ )
1385
+
1386
+ return data