google-genai 1.32.0__py3-none-any.whl → 1.33.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.
@@ -584,13 +584,9 @@ class BaseApiClient:
584
584
  # Initialize the lock. This lock will be used to protect access to the
585
585
  # credentials. This is crucial for thread safety when multiple coroutines
586
586
  # might be accessing the credentials at the same time.
587
- try:
588
- self._sync_auth_lock = threading.Lock()
589
- self._async_auth_lock = asyncio.Lock()
590
- except RuntimeError:
591
- asyncio.set_event_loop(asyncio.new_event_loop())
592
- self._sync_auth_lock = threading.Lock()
593
- self._async_auth_lock = asyncio.Lock()
587
+ self._sync_auth_lock = threading.Lock()
588
+ self._async_auth_lock: Optional[asyncio.Lock] = None
589
+ self._async_auth_lock_creation_lock: Optional[asyncio.Lock] = None
594
590
 
595
591
  # Handle when to use Vertex AI in express mode (api key).
596
592
  # Explicit initializer arguments are already validated above.
@@ -903,10 +899,36 @@ class BaseApiClient:
903
899
  else:
904
900
  raise RuntimeError('Could not resolve API token from the environment')
905
901
 
902
+ async def _get_async_auth_lock(self) -> asyncio.Lock:
903
+ """Lazily initializes and returns an asyncio.Lock for async authentication.
904
+
905
+ This method ensures that a single `asyncio.Lock` instance is created and
906
+ shared among all asynchronous operations that require authentication,
907
+ preventing race conditions when accessing or refreshing credentials.
908
+
909
+ The lock is created on the first call to this method. An internal async lock
910
+ is used to protect the creation of the main authentication lock to ensure
911
+ it's a singleton within the client instance.
912
+
913
+ Returns:
914
+ The asyncio.Lock instance for asynchronous authentication operations.
915
+ """
916
+ if self._async_auth_lock is None:
917
+ # Create async creation lock if needed
918
+ if self._async_auth_lock_creation_lock is None:
919
+ self._async_auth_lock_creation_lock = asyncio.Lock()
920
+
921
+ async with self._async_auth_lock_creation_lock:
922
+ if self._async_auth_lock is None:
923
+ self._async_auth_lock = asyncio.Lock()
924
+
925
+ return self._async_auth_lock
926
+
906
927
  async def _async_access_token(self) -> Union[str, Any]:
907
928
  """Retrieves the access token for the credentials asynchronously."""
908
929
  if not self._credentials:
909
- async with self._async_auth_lock:
930
+ async_auth_lock = await self._get_async_auth_lock()
931
+ async with async_auth_lock:
910
932
  # This ensures that only one coroutine can execute the auth logic at a
911
933
  # time for thread safety.
912
934
  if not self._credentials:
@@ -920,7 +942,8 @@ class BaseApiClient:
920
942
  if self._credentials:
921
943
  if self._credentials.expired or not self._credentials.token:
922
944
  # Only refresh when it needs to. Default expiration is 3600 seconds.
923
- async with self._async_auth_lock:
945
+ async_auth_lock = await self._get_async_auth_lock()
946
+ async with async_auth_lock:
924
947
  if self._credentials.expired or not self._credentials.token:
925
948
  # Double check that the credentials expired before refreshing.
926
949
  await asyncio.to_thread(refresh_auth, self._credentials)
@@ -471,6 +471,21 @@ class ReplayApiClient(BaseApiClient):
471
471
  expected = interaction.response.sdk_response_segments[
472
472
  self._sdk_response_index
473
473
  ]
474
+ # The sdk_http_response.body has format in the string, need to get rid of
475
+ # the format information before comparing.
476
+ if isinstance(expected, dict):
477
+ if 'sdk_http_response' in expected and isinstance(
478
+ expected['sdk_http_response'], dict
479
+ ):
480
+ if 'body' in expected['sdk_http_response']:
481
+ raw_body = expected['sdk_http_response']['body']
482
+ print('raw_body length: ', len(raw_body))
483
+ print('raw_body: ', raw_body)
484
+ if isinstance(raw_body, str) and raw_body != '':
485
+ raw_body = json.loads(raw_body)
486
+ raw_body = json.dumps(raw_body)
487
+ expected['sdk_http_response']['body'] = raw_body
488
+
474
489
  assert (
475
490
  actual == expected
476
491
  ), f'SDK response mismatch:\nActual: {actual}\nExpected: {expected}'
google/genai/files.py CHANGED
@@ -434,6 +434,7 @@ class Files(_api_module.BaseModule):
434
434
  config, 'should_return_http_response', None
435
435
  ):
436
436
  return_value = types.CreateFileResponse(sdk_http_response=response)
437
+ self._api_client._verify_response(return_value)
437
438
  return return_value
438
439
 
439
440
  response_dict = '' if not response.body else json.loads(response.body)
@@ -916,6 +917,7 @@ class AsyncFiles(_api_module.BaseModule):
916
917
  config, 'should_return_http_response', None
917
918
  ):
918
919
  return_value = types.CreateFileResponse(sdk_http_response=response)
920
+ self._api_client._verify_response(return_value)
919
921
  return return_value
920
922
 
921
923
  response_dict = '' if not response.body else json.loads(response.body)
google/genai/models.py CHANGED
@@ -1330,7 +1330,11 @@ def _GenerateVideosConfig_to_mldev(
1330
1330
  )
1331
1331
 
1332
1332
  if getv(from_object, ['resolution']) is not None:
1333
- raise ValueError('resolution parameter is not supported in Gemini API.')
1333
+ setv(
1334
+ parent_object,
1335
+ ['parameters', 'resolution'],
1336
+ getv(from_object, ['resolution']),
1337
+ )
1334
1338
 
1335
1339
  if getv(from_object, ['person_generation']) is not None:
1336
1340
  setv(
@@ -5256,6 +5260,13 @@ class Models(_api_module.BaseModule):
5256
5260
  'post', path, request_dict, http_options
5257
5261
  )
5258
5262
 
5263
+ if config is not None and getattr(
5264
+ config, 'should_return_http_response', None
5265
+ ):
5266
+ return_value = types.GenerateContentResponse(sdk_http_response=response)
5267
+ self._api_client._verify_response(return_value)
5268
+ return return_value
5269
+
5259
5270
  response_dict = '' if not response.body else json.loads(response.body)
5260
5271
 
5261
5272
  if self._api_client.vertexai:
@@ -5455,13 +5466,7 @@ class Models(_api_module.BaseModule):
5455
5466
  prompt: str,
5456
5467
  config: Optional[types.GenerateImagesConfigOrDict] = None,
5457
5468
  ) -> types.GenerateImagesResponse:
5458
- """Generates images based on a text description and configuration.
5459
-
5460
- Args:
5461
- model (str): The model to use.
5462
- prompt (str): A text description of the images to generate.
5463
- config (GenerateImagesConfig): Configuration for generation.
5464
- """
5469
+ """Private method for generating images."""
5465
5470
 
5466
5471
  parameter_model = types._GenerateImagesParameters(
5467
5472
  model=model,
@@ -5534,47 +5539,7 @@ class Models(_api_module.BaseModule):
5534
5539
  reference_images: list[types._ReferenceImageAPIOrDict],
5535
5540
  config: Optional[types.EditImageConfigOrDict] = None,
5536
5541
  ) -> types.EditImageResponse:
5537
- """Edits an image based on a text description and configuration.
5538
-
5539
- Args:
5540
- model (str): The model to use.
5541
- prompt (str): A text description of the edit to apply to the image.
5542
- reference_images (list[Union[RawReferenceImage, MaskReferenceImage,
5543
- ControlReferenceImage, StyleReferenceImage, SubjectReferenceImage]): The
5544
- reference images for editing.
5545
- config (EditImageConfig): Configuration for editing.
5546
-
5547
- Usage:
5548
-
5549
- .. code-block:: python
5550
-
5551
- from google.genai.types import RawReferenceImage, MaskReferenceImage
5552
-
5553
- raw_ref_image = RawReferenceImage(
5554
- reference_id=1,
5555
- reference_image=types.Image.from_file(IMAGE_FILE_PATH),
5556
- )
5557
-
5558
- mask_ref_image = MaskReferenceImage(
5559
- reference_id=2,
5560
- config=types.MaskReferenceConfig(
5561
- mask_mode='MASK_MODE_FOREGROUND',
5562
- mask_dilation=0.06,
5563
- ),
5564
- )
5565
- response = client.models.edit_image(
5566
- model='imagen-3.0-capability-001',
5567
- prompt='man with dog',
5568
- reference_images=[raw_ref_image, mask_ref_image],
5569
- config=types.EditImageConfig(
5570
- edit_mode= "EDIT_MODE_INPAINT_INSERTION",
5571
- number_of_images= 1,
5572
- include_rai_reason= True,
5573
- )
5574
- )
5575
- response.generated_images[0].image.show()
5576
- # Shows a man with a dog instead of a cat.
5577
- """
5542
+ """Private method for editing an image."""
5578
5543
 
5579
5544
  parameter_model = types._EditImageParameters(
5580
5545
  model=model,
@@ -5638,14 +5603,7 @@ class Models(_api_module.BaseModule):
5638
5603
  upscale_factor: str,
5639
5604
  config: Optional[types._UpscaleImageAPIConfigOrDict] = None,
5640
5605
  ) -> types.UpscaleImageResponse:
5641
- """Upscales an image.
5642
-
5643
- Args:
5644
- model (str): The model to use.
5645
- image (Image): The input image for upscaling.
5646
- upscale_factor (str): The factor to upscale the image (x2 or x4).
5647
- config (_UpscaleImageAPIConfig): Configuration for upscaling.
5648
- """
5606
+ """Private method for upscaling an image."""
5649
5607
 
5650
5608
  parameter_model = types._UpscaleImageAPIParameters(
5651
5609
  model=model,
@@ -6331,39 +6289,7 @@ class Models(_api_module.BaseModule):
6331
6289
  source: Optional[types.GenerateVideosSourceOrDict] = None,
6332
6290
  config: Optional[types.GenerateVideosConfigOrDict] = None,
6333
6291
  ) -> types.GenerateVideosOperation:
6334
- """Generates videos based on an input (text, image, or video) and configuration.
6335
-
6336
- The following use cases are supported:
6337
- 1. Text to video generation.
6338
- 2a. Image to video generation (additional text prompt is optional).
6339
- 2b. Image to video generation with frame interpolation (specify last_frame
6340
- in config).
6341
- 3. Video extension (additional text prompt is optional)
6342
-
6343
- Args:
6344
- model: The model to use.
6345
- prompt: The text prompt for generating the videos. Optional for image to
6346
- video and video extension use cases.
6347
- image: The input image for generating the videos. Optional if prompt is
6348
- provided.
6349
- video: The input video for video extension use cases. Optional if prompt
6350
- or image is provided.
6351
- config: Configuration for generation.
6352
-
6353
- Usage:
6354
-
6355
- ```
6356
- operation = client.models.generate_videos(
6357
- model="veo-2.0-generate-001",
6358
- prompt="A neon hologram of a cat driving at top speed",
6359
- )
6360
- while not operation.done:
6361
- time.sleep(10)
6362
- operation = client.operations.get(operation)
6363
-
6364
- operation.result.generated_videos[0].video.uri
6365
- ```
6366
- """
6292
+ """Private method for generating videos."""
6367
6293
 
6368
6294
  parameter_model = types._GenerateVideosParameters(
6369
6295
  model=model,
@@ -7081,6 +7007,13 @@ class AsyncModels(_api_module.BaseModule):
7081
7007
  'post', path, request_dict, http_options
7082
7008
  )
7083
7009
 
7010
+ if config is not None and getattr(
7011
+ config, 'should_return_http_response', None
7012
+ ):
7013
+ return_value = types.GenerateContentResponse(sdk_http_response=response)
7014
+ self._api_client._verify_response(return_value)
7015
+ return return_value
7016
+
7084
7017
  response_dict = '' if not response.body else json.loads(response.body)
7085
7018
 
7086
7019
  if self._api_client.vertexai:
@@ -7285,13 +7218,7 @@ class AsyncModels(_api_module.BaseModule):
7285
7218
  prompt: str,
7286
7219
  config: Optional[types.GenerateImagesConfigOrDict] = None,
7287
7220
  ) -> types.GenerateImagesResponse:
7288
- """Generates images based on a text description and configuration.
7289
-
7290
- Args:
7291
- model (str): The model to use.
7292
- prompt (str): A text description of the images to generate.
7293
- config (GenerateImagesConfig): Configuration for generation.
7294
- """
7221
+ """Private method for generating images asynchronously."""
7295
7222
 
7296
7223
  parameter_model = types._GenerateImagesParameters(
7297
7224
  model=model,
@@ -7364,47 +7291,7 @@ class AsyncModels(_api_module.BaseModule):
7364
7291
  reference_images: list[types._ReferenceImageAPIOrDict],
7365
7292
  config: Optional[types.EditImageConfigOrDict] = None,
7366
7293
  ) -> types.EditImageResponse:
7367
- """Edits an image based on a text description and configuration.
7368
-
7369
- Args:
7370
- model (str): The model to use.
7371
- prompt (str): A text description of the edit to apply to the image.
7372
- reference_images (list[Union[RawReferenceImage, MaskReferenceImage,
7373
- ControlReferenceImage, StyleReferenceImage, SubjectReferenceImage]): The
7374
- reference images for editing.
7375
- config (EditImageConfig): Configuration for editing.
7376
-
7377
- Usage:
7378
-
7379
- .. code-block:: python
7380
-
7381
- from google.genai.types import RawReferenceImage, MaskReferenceImage
7382
-
7383
- raw_ref_image = RawReferenceImage(
7384
- reference_id=1,
7385
- reference_image=types.Image.from_file(IMAGE_FILE_PATH),
7386
- )
7387
-
7388
- mask_ref_image = MaskReferenceImage(
7389
- reference_id=2,
7390
- config=types.MaskReferenceConfig(
7391
- mask_mode='MASK_MODE_FOREGROUND',
7392
- mask_dilation=0.06,
7393
- ),
7394
- )
7395
- response = await client.aio.models.edit_image(
7396
- model='imagen-3.0-capability-001',
7397
- prompt='man with dog',
7398
- reference_images=[raw_ref_image, mask_ref_image],
7399
- config=types.EditImageConfig(
7400
- edit_mode= "EDIT_MODE_INPAINT_INSERTION",
7401
- number_of_images= 1,
7402
- include_rai_reason= True,
7403
- )
7404
- )
7405
- response.generated_images[0].image.show()
7406
- # Shows a man with a dog instead of a cat.
7407
- """
7294
+ """Private method for editing an image asynchronously."""
7408
7295
 
7409
7296
  parameter_model = types._EditImageParameters(
7410
7297
  model=model,
@@ -7468,14 +7355,7 @@ class AsyncModels(_api_module.BaseModule):
7468
7355
  upscale_factor: str,
7469
7356
  config: Optional[types._UpscaleImageAPIConfigOrDict] = None,
7470
7357
  ) -> types.UpscaleImageResponse:
7471
- """Upscales an image.
7472
-
7473
- Args:
7474
- model (str): The model to use.
7475
- image (Image): The input image for upscaling.
7476
- upscale_factor (str): The factor to upscale the image (x2 or x4).
7477
- config (_UpscaleImageAPIConfig): Configuration for upscaling.
7478
- """
7358
+ """Private method for upscaling an image asynchronously."""
7479
7359
 
7480
7360
  parameter_model = types._UpscaleImageAPIParameters(
7481
7361
  model=model,
@@ -8167,39 +8047,7 @@ class AsyncModels(_api_module.BaseModule):
8167
8047
  source: Optional[types.GenerateVideosSourceOrDict] = None,
8168
8048
  config: Optional[types.GenerateVideosConfigOrDict] = None,
8169
8049
  ) -> types.GenerateVideosOperation:
8170
- """Generates videos based on an input (text, image, or video) and configuration.
8171
-
8172
- The following use cases are supported:
8173
- 1. Text to video generation.
8174
- 2a. Image to video generation (additional text prompt is optional).
8175
- 2b. Image to video generation with frame interpolation (specify last_frame
8176
- in config).
8177
- 3. Video extension (additional text prompt is optional)
8178
-
8179
- Args:
8180
- model: The model to use.
8181
- prompt: The text prompt for generating the videos. Optional for image to
8182
- video and video extension use cases.
8183
- image: The input image for generating the videos. Optional if prompt is
8184
- provided.
8185
- video: The input video for video extension use cases. Optional if prompt
8186
- or image is provided.
8187
- config: Configuration for generation.
8188
-
8189
- Usage:
8190
-
8191
- ```
8192
- operation = client.models.generate_videos(
8193
- model="veo-2.0-generate-001",
8194
- prompt="A neon hologram of a cat driving at top speed",
8195
- )
8196
- while not operation.done:
8197
- time.sleep(10)
8198
- operation = client.operations.get(operation)
8199
-
8200
- operation.result.generated_videos[0].video.uri
8201
- ```
8202
- """
8050
+ """Private method for generating videos asynchronously."""
8203
8051
 
8204
8052
  parameter_model = types._GenerateVideosParameters(
8205
8053
  model=model,