google-genai 1.14.0__py3-none-any.whl → 1.16.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/pagers.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Google LLC
1
+ # Copyright 2025 Google LLC
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
google/genai/tokens.py ADDED
@@ -0,0 +1,357 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+
16
+ """[Experimental] Auth Tokens API client."""
17
+
18
+ import logging
19
+ from typing import Any, Dict, List, Optional
20
+ from urllib.parse import urlencode
21
+ from . import _api_module
22
+ from . import _common
23
+ from . import _tokens_converters as tokens_converters
24
+ from . import types
25
+
26
+ logger = logging.getLogger('google_genai.tokens')
27
+
28
+
29
+ def _get_field_masks(setup: Dict[str, Any]) -> str:
30
+ """Return field_masks"""
31
+ fields = []
32
+ for k, v in setup.items():
33
+ # 2nd layer, recursively get field masks see TODO(b/418290100)
34
+ if isinstance(v, dict) and v:
35
+ field = [f'{k}.{kk}' for kk in v.keys()]
36
+ else:
37
+ field = [k] # 1st layer
38
+ fields.extend(field)
39
+
40
+ return ','.join(fields)
41
+
42
+
43
+ def _convert_bidi_setup_to_token_setup(
44
+ request_dict: dict[str, Any],
45
+ config: Optional[types.CreateAuthTokenConfigOrDict] = None,
46
+ ) -> Dict[str, Any]:
47
+ """Converts bidiGenerateContentSetup."""
48
+ bidi_setup = request_dict.get('bidiGenerateContentSetup')
49
+ if bidi_setup and bidi_setup.get('setup'):
50
+ # Handling mismatch between AuthToken service and
51
+ # BidiGenerateContent service
52
+ request_dict['bidiGenerateContentSetup'] = bidi_setup.get('setup')
53
+
54
+ # Convert non null bidiGenerateContentSetup to field_mask
55
+ field_mask = _get_field_masks(request_dict['bidiGenerateContentSetup'])
56
+
57
+ if (
58
+ isinstance(config, dict)
59
+ and config.get('lock_additional_fields') is not None
60
+ and not config.get('lock_additional_fields')
61
+ ) or (
62
+ isinstance(config, types.CreateAuthTokenConfig)
63
+ and config.lock_additional_fields is not None
64
+ and not config.lock_additional_fields # pylint: disable=literal-comparison
65
+ ):
66
+ # Empty list, lock non null fields
67
+ request_dict['fieldMask'] = field_mask
68
+ elif (
69
+ isinstance(config, dict)
70
+ and config.get('lock_additional_fields') is None
71
+ ) or (
72
+ isinstance(config, types.CreateAuthTokenConfig)
73
+ and config.lock_additional_fields is None
74
+ ):
75
+ # None. Global lock. unset fieldMask
76
+ request_dict.pop('fieldMask', None)
77
+ elif request_dict['fieldMask']:
78
+ # Lock non null + additional fields
79
+ additional_fields_list: Optional[List[str]] = request_dict.get(
80
+ 'fieldMask'
81
+ )
82
+ generation_config_list = types.GenerationConfig().model_dump().keys()
83
+ if additional_fields_list:
84
+ field_mask_list = []
85
+ for field in additional_fields_list:
86
+ if field in generation_config_list:
87
+ field = f'generationConfig.{field}'
88
+ field_mask_list.append(field)
89
+ else:
90
+ field_mask_list = []
91
+ request_dict['fieldMask'] = (
92
+ field_mask + ',' + ','.join(field_mask_list)
93
+ if field_mask_list
94
+ else field_mask
95
+ )
96
+ else:
97
+ # Lock all fields
98
+ request_dict.pop('fieldMask', None)
99
+ else:
100
+ field_mask = request_dict.get('fieldMask', [])
101
+ field_mask_str = ','.join(field_mask)
102
+ if field_mask:
103
+ request_dict['fieldMask'] = field_mask_str
104
+ else:
105
+ request_dict.pop('fieldMask', None)
106
+ if not request_dict.get('bidiGenerateContentSetup'):
107
+ request_dict.pop('bidiGenerateContentSetup', None)
108
+
109
+ return request_dict
110
+
111
+
112
+ class Tokens(_api_module.BaseModule):
113
+ """[Experimental] Auth Tokens API client.
114
+
115
+ This class provides methods for creating auth tokens.
116
+ """
117
+
118
+ @_common.experimental_warning(
119
+ "The SDK's token creation implementation is experimental, "
120
+ 'and may change in future versions.',
121
+ )
122
+ def create(
123
+ self, *, config: Optional[types.CreateAuthTokenConfigOrDict] = None
124
+ ) -> types.AuthToken:
125
+ """[Experimental] Creates an auth token.
126
+
127
+ Args:
128
+ config (CreateAuthTokenConfig): Optional configuration for the request.
129
+
130
+ The CreateAuthTokenConfig's `live_constrained_parameters` attrubite
131
+ Can be used to lock the parameters of the live session so they
132
+ can't be changed client side. This behavior has two basic modes depending on
133
+ whether `lock_additional_fields` is set:
134
+
135
+ If you do not pass `lock_additional_fields` the entire
136
+ `live_constrained_parameters` is locked and can't be changed
137
+ by the token's user.
138
+
139
+ If you set `lock_additional_fields`, then the non-null fields of
140
+ `live_constrained_parameters` are locked, and any additional fields
141
+ specified in `lock_additional_fields`.
142
+
143
+ Usage:
144
+
145
+ .. code-block:: python
146
+ # Case 1: If LiveEphemeralParameters is unset, unlock LiveConnectConfig
147
+ # when using the token in Live API sessions. Each session connection can
148
+ # use a different configuration.
149
+
150
+ config = types.CreateAuthTokenConfig(
151
+ uses=10,
152
+ expire_time='2025-05-01T00:00:00Z',
153
+ )
154
+ auth_token = client.tokens.create(config=config)
155
+
156
+ .. code-block:: python
157
+ # Case 2: If LiveEphemeralParameters is set, lock all fields in
158
+ # LiveConnectConfig when using the token in Live API sessions. For
159
+ # example, changing `output_audio_transcription` in the Live API
160
+ # connection will be ignored by the API.
161
+
162
+ auth_token = client.tokens.create(
163
+ config=types.CreateAuthTokenConfig(
164
+ uses=10,
165
+ live_constrained_parameters=types.LiveEphemeralParameters(
166
+ model='gemini-2.0-flash-live-001',
167
+ config=types.LiveConnectConfig(
168
+ system_instruction='You are an LLM called Gemini.'
169
+ ),
170
+ ),
171
+ )
172
+ )
173
+ .. code-block:: python
174
+ # Case 3: If LiveEphemeralParameters is set and lockAdditionalFields is
175
+ # empty, lock LiveConnectConfig with set fields (e.g.
176
+ # system_instruction in this example) when using the token in Live API
177
+ # sessions.
178
+ auth_token = client.tokens.create(
179
+ config=types.CreateAuthTokenConfig(
180
+ uses=10,
181
+ live_constrained_parameters=types.LiveEphemeralParameters(
182
+ config=types.LiveConnectConfig(
183
+ system_instruction='You are an LLM called Gemini.'
184
+ ),
185
+ ),
186
+ lock_additional_fields=[],
187
+ )
188
+ )
189
+
190
+ .. code-block:: python
191
+ # Case 4: If LiveEphemeralParameters is set and lockAdditionalFields is
192
+ # set, lock LiveConnectConfig with set and additional fields (e.g.
193
+ # system_instruction, temperature in this example) when using the token
194
+ # in Live API sessions.
195
+ auth_token = client.tokens.create(
196
+ config=types.CreateAuthTokenConfig(
197
+ uses=10,
198
+ live_constrained_parameters=types.LiveEphemeralParameters(
199
+ model='gemini-2.0-flash-live-001',
200
+ config=types.LiveConnectConfig(
201
+ system_instruction='You are an LLM called Gemini.'
202
+ ),
203
+ ),
204
+ lock_additional_fields=['temperature'],
205
+ )
206
+ )
207
+ """
208
+
209
+ parameter_model = types.CreateAuthTokenParameters(
210
+ config=config,
211
+ )
212
+ request_url_dict: Optional[dict[str, str]]
213
+ if self._api_client.vertexai:
214
+ raise ValueError(
215
+ 'This method is only supported in the Gemini Developer client.'
216
+ )
217
+ else:
218
+ request_dict = tokens_converters._CreateAuthTokenParameters_to_mldev(
219
+ self._api_client, parameter_model
220
+ )
221
+ request_url_dict = request_dict.get('_url')
222
+ if request_url_dict:
223
+ path = 'auth_tokens'.format_map(request_url_dict)
224
+ else:
225
+ path = 'auth_tokens'
226
+
227
+ query_params = request_dict.get('_query')
228
+ if query_params:
229
+ path = f'{path}?{urlencode(query_params)}'
230
+ # TODO: remove the hack that pops config.
231
+
232
+ request_dict.pop('config', None)
233
+
234
+ # Token creation request data need to replace 'setup' with
235
+ # 'bidiGenerateContentSetup'
236
+ if request_dict:
237
+ request_dict = _convert_bidi_setup_to_token_setup(request_dict, config)
238
+
239
+ http_options: Optional[types.HttpOptions] = None
240
+ if (
241
+ parameter_model is not None
242
+ and parameter_model.config is not None
243
+ and parameter_model.config.http_options is not None
244
+ ):
245
+ http_options = parameter_model.config.http_options
246
+
247
+ request_dict = _common.convert_to_dict(request_dict)
248
+ request_dict = _common.encode_unserializable_types(request_dict)
249
+
250
+ response_dict = self._api_client.request(
251
+ 'post', path, request_dict, http_options
252
+ )
253
+
254
+ if not self._api_client.vertexai:
255
+ response_dict = tokens_converters._AuthToken_from_mldev(
256
+ self._api_client, response_dict
257
+ )
258
+
259
+ return_value = types.AuthToken._from_response(
260
+ response=response_dict, kwargs=parameter_model.model_dump()
261
+ )
262
+ self._api_client._verify_response(return_value)
263
+ return return_value
264
+
265
+
266
+ class AsyncTokens(_api_module.BaseModule):
267
+ """[Experimental] Async Auth Tokens API client.
268
+
269
+ This class provides asynchronous methods for creating auth tokens.
270
+ """
271
+
272
+ @_common.experimental_warning(
273
+ "The SDK's token creation implementation is experimental, "
274
+ 'and may change in future versions.',
275
+ )
276
+ async def create(
277
+ self, *, config: Optional[types.CreateAuthTokenConfigOrDict] = None
278
+ ) -> types.AuthToken:
279
+ """Creates an auth token asynchronously.
280
+
281
+ Args:
282
+ config (CreateAuthTokenConfig): Optional configuration for the request.
283
+
284
+ Usage:
285
+
286
+ .. code-block:: python
287
+
288
+ auth_token = await client.aio.tokens.create(
289
+ config=types.CreateAuthTokenConfig(
290
+ uses=10,
291
+ live_constrained_parameters=types.LiveEphemeralParameters(
292
+ model='gemini-2.0-flash-live-001',
293
+ config=types.LiveConnectConfig(
294
+ system_instruction='You are an LLM called Gemini.'
295
+ ),
296
+ ),
297
+ )
298
+ )
299
+ """
300
+
301
+ parameter_model = types.CreateAuthTokenParameters(
302
+ config=config,
303
+ )
304
+
305
+ request_url_dict: Optional[dict[str, str]]
306
+ if self._api_client.vertexai:
307
+ raise ValueError(
308
+ 'This method is only supported in the Gemini Developer client.'
309
+ )
310
+ else:
311
+ request_dict = tokens_converters._CreateAuthTokenParameters_to_mldev(
312
+ self._api_client, parameter_model
313
+ )
314
+ request_url_dict = request_dict.get('_url')
315
+ if request_url_dict:
316
+ path = 'auth_tokens'.format_map(request_url_dict)
317
+ else:
318
+ path = 'auth_tokens'
319
+
320
+ query_params = request_dict.get('_query')
321
+ if query_params:
322
+ path = f'{path}?{urlencode(query_params)}'
323
+ # TODO: remove the hack that pops config.
324
+ request_dict.pop('config', None)
325
+
326
+ # Token creation request data need to replace 'setup' with
327
+ # 'bidiGenerateContentSetup'
328
+ request_dict = _convert_bidi_setup_to_token_setup(request_dict, config)
329
+
330
+ http_options: Optional[types.HttpOptions] = None
331
+ if (
332
+ parameter_model is not None
333
+ and parameter_model.config is not None
334
+ and parameter_model.config.http_options is not None
335
+ ):
336
+ http_options = parameter_model.config.http_options
337
+
338
+ request_dict = _common.convert_to_dict(request_dict)
339
+ request_dict = _common.encode_unserializable_types(request_dict)
340
+
341
+ response_dict = await self._api_client.async_request(
342
+ 'post',
343
+ path,
344
+ request_dict,
345
+ http_options=http_options,
346
+ )
347
+
348
+ if not self._api_client.vertexai:
349
+ response_dict = tokens_converters._AuthToken_from_mldev(
350
+ self._api_client, response_dict
351
+ )
352
+
353
+ return_value = types.AuthToken._from_response(
354
+ response=response_dict, kwargs=parameter_model.model_dump()
355
+ )
356
+ self._api_client._verify_response(return_value)
357
+ return return_value
google/genai/tunings.py CHANGED
@@ -173,6 +173,11 @@ def _CreateTuningJobConfig_to_mldev(
173
173
  getv(from_object, ['learning_rate_multiplier']),
174
174
  )
175
175
 
176
+ if getv(from_object, ['export_last_checkpoint_only']) is not None:
177
+ raise ValueError(
178
+ 'export_last_checkpoint_only parameter is not supported in Gemini API.'
179
+ )
180
+
176
181
  if getv(from_object, ['adapter_size']) is not None:
177
182
  raise ValueError('adapter_size parameter is not supported in Gemini API.')
178
183
 
@@ -367,6 +372,13 @@ def _CreateTuningJobConfig_to_vertex(
367
372
  getv(from_object, ['learning_rate_multiplier']),
368
373
  )
369
374
 
375
+ if getv(from_object, ['export_last_checkpoint_only']) is not None:
376
+ setv(
377
+ parent_object,
378
+ ['supervisedTuningSpec', 'exportLastCheckpointOnly'],
379
+ getv(from_object, ['export_last_checkpoint_only']),
380
+ )
381
+
370
382
  if getv(from_object, ['adapter_size']) is not None:
371
383
  setv(
372
384
  parent_object,
@@ -413,6 +425,16 @@ def _CreateTuningJobParameters_to_vertex(
413
425
  return to_object
414
426
 
415
427
 
428
+ def _TunedModelCheckpoint_from_mldev(
429
+ api_client: BaseApiClient,
430
+ from_object: Union[dict[str, Any], object],
431
+ parent_object: Optional[dict[str, Any]] = None,
432
+ ) -> dict[str, Any]:
433
+ to_object: dict[str, Any] = {}
434
+
435
+ return to_object
436
+
437
+
416
438
  def _TunedModel_from_mldev(
417
439
  api_client: BaseApiClient,
418
440
  from_object: Union[dict[str, Any], object],
@@ -548,6 +570,27 @@ def _Operation_from_mldev(
548
570
  return to_object
549
571
 
550
572
 
573
+ def _TunedModelCheckpoint_from_vertex(
574
+ api_client: BaseApiClient,
575
+ from_object: Union[dict[str, Any], object],
576
+ parent_object: Optional[dict[str, Any]] = None,
577
+ ) -> dict[str, Any]:
578
+ to_object: dict[str, Any] = {}
579
+ if getv(from_object, ['checkpointId']) is not None:
580
+ setv(to_object, ['checkpoint_id'], getv(from_object, ['checkpointId']))
581
+
582
+ if getv(from_object, ['epoch']) is not None:
583
+ setv(to_object, ['epoch'], getv(from_object, ['epoch']))
584
+
585
+ if getv(from_object, ['step']) is not None:
586
+ setv(to_object, ['step'], getv(from_object, ['step']))
587
+
588
+ if getv(from_object, ['endpoint']) is not None:
589
+ setv(to_object, ['endpoint'], getv(from_object, ['endpoint']))
590
+
591
+ return to_object
592
+
593
+
551
594
  def _TunedModel_from_vertex(
552
595
  api_client: BaseApiClient,
553
596
  from_object: Union[dict[str, Any], object],
@@ -560,6 +603,16 @@ def _TunedModel_from_vertex(
560
603
  if getv(from_object, ['endpoint']) is not None:
561
604
  setv(to_object, ['endpoint'], getv(from_object, ['endpoint']))
562
605
 
606
+ if getv(from_object, ['checkpoints']) is not None:
607
+ setv(
608
+ to_object,
609
+ ['checkpoints'],
610
+ [
611
+ _TunedModelCheckpoint_from_vertex(api_client, item, to_object)
612
+ for item in getv(from_object, ['checkpoints'])
613
+ ],
614
+ )
615
+
563
616
  return to_object
564
617
 
565
618