2
2
3
3
import os
4
4
import contextlib
5
+ import inspect
5
6
import dataclasses
6
7
import pathlib
7
- import types
8
8
from typing import Any , cast
9
9
from collections .abc import Sequence
10
10
import httplib2
30
30
__version__ = "0.0.0"
31
31
32
32
USER_AGENT = "genai-py"
33
+
34
+ #### Caution! ####
35
+ # - It would make sense for the discovery URL to respect the client_options.endpoint setting.
36
+ # - That would make testing Files on the staging server possible.
37
+ # - We tried fixing this once, but broke colab in the process because their endpoint didn't forward the discovery
38
+ # requests. https://github.com/google-gemini/generative-ai-python/pull/333
39
+ # - Kaggle would have a similar problem (b/362278209).
40
+ # - I think their proxy would forward the discovery traffic.
41
+ # - But they don't need to intercept the files-service at all, and uploads of large files could overload them.
42
+ # - Do the scotty uploads go to the same domain?
43
+ # - If you do route the discovery call to kaggle, be sure to attach the default_metadata (they need it).
44
+ # - One solution to all this would be if configure could take overrides per service.
45
+ # - set client_options.endpoint, but use a different endpoint for file service? It's not clear how best to do that
46
+ # through the file service.
47
+ ##################
33
48
GENAI_API_DISCOVERY_URL = "https://generativelanguage.googleapis.com/$discovery/rest"
34
49
35
50
@@ -50,7 +65,7 @@ def __init__(self, *args, **kwargs):
50
65
self ._discovery_api = None
51
66
super ().__init__ (* args , ** kwargs )
52
67
53
- def _setup_discovery_api (self ):
68
+ def _setup_discovery_api (self , metadata : dict | Sequence [ tuple [ str , str ]] = () ):
54
69
api_key = self ._client_options .api_key
55
70
if api_key is None :
56
71
raise ValueError (
@@ -61,6 +76,7 @@ def _setup_discovery_api(self):
61
76
http = httplib2 .Http (),
62
77
postproc = lambda resp , content : (resp , content ),
63
78
uri = f"{ GENAI_API_DISCOVERY_URL } ?version=v1beta&key={ api_key } " ,
79
+ headers = dict (metadata ),
64
80
)
65
81
response , content = request .execute ()
66
82
request .http .close ()
@@ -78,9 +94,10 @@ def create_file(
78
94
name : str | None = None ,
79
95
display_name : str | None = None ,
80
96
resumable : bool = True ,
97
+ metadata : Sequence [tuple [str , str ]] = (),
81
98
) -> protos .File :
82
99
if self ._discovery_api is None :
83
- self ._setup_discovery_api ()
100
+ self ._setup_discovery_api (metadata )
84
101
85
102
file = {}
86
103
if name is not None :
@@ -92,6 +109,8 @@ def create_file(
92
109
filename = path , mimetype = mime_type , resumable = resumable
93
110
)
94
111
request = self ._discovery_api .media ().upload (body = {"file" : file }, media_body = media )
112
+ for key , value in metadata :
113
+ request .headers [key ] = value
95
114
result = request .execute ()
96
115
97
116
return self .get_file ({"name" : result ["file" ]["name" ]})
@@ -226,16 +245,14 @@ def make_client(self, name):
226
245
def keep (name , f ):
227
246
if name .startswith ("_" ):
228
247
return False
229
- elif name == "create_file" :
230
- return False
231
- elif not isinstance (f , types .FunctionType ):
232
- return False
233
- elif isinstance (f , classmethod ):
248
+
249
+ if not callable (f ):
234
250
return False
235
- elif isinstance (f , staticmethod ):
251
+
252
+ if "metadata" not in inspect .signature (f ).parameters .keys ():
236
253
return False
237
- else :
238
- return True
254
+
255
+ return True
239
256
240
257
def add_default_metadata_wrapper (f ):
241
258
def call (* args , metadata = (), ** kwargs ):
@@ -244,7 +261,7 @@ def call(*args, metadata=(), **kwargs):
244
261
245
262
return call
246
263
247
- for name , value in cls . __dict__ . items ( ):
264
+ for name , value in inspect . getmembers ( cls ):
248
265
if not keep (name , value ):
249
266
continue
250
267
f = getattr (client , name )
0 commit comments