google-genai 1.57.0__py3-none-any.whl → 1.58.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.
Files changed (46) hide show
  1. google/genai/_api_client.py +12 -8
  2. google/genai/_interactions/__init__.py +3 -0
  3. google/genai/_interactions/_client.py +57 -3
  4. google/genai/_interactions/_client_adapter.py +48 -0
  5. google/genai/_interactions/types/__init__.py +4 -0
  6. google/genai/_interactions/types/audio_content.py +2 -0
  7. google/genai/_interactions/types/audio_content_param.py +2 -0
  8. google/genai/_interactions/types/content.py +2 -0
  9. google/genai/_interactions/types/content_delta.py +10 -2
  10. google/genai/_interactions/types/content_param.py +2 -0
  11. google/genai/_interactions/types/content_start.py +1 -2
  12. google/genai/_interactions/types/content_stop.py +1 -2
  13. google/genai/_interactions/types/document_content.py +2 -0
  14. google/genai/_interactions/types/document_content_param.py +2 -0
  15. google/genai/_interactions/types/error_event.py +1 -2
  16. google/genai/_interactions/types/file_search_call_content.py +32 -0
  17. google/genai/_interactions/types/file_search_call_content_param.py +31 -0
  18. google/genai/_interactions/types/generation_config.py +4 -0
  19. google/genai/_interactions/types/generation_config_param.py +4 -0
  20. google/genai/_interactions/types/image_config.py +31 -0
  21. google/genai/_interactions/types/image_config_param.py +30 -0
  22. google/genai/_interactions/types/image_content.py +2 -0
  23. google/genai/_interactions/types/image_content_param.py +2 -0
  24. google/genai/_interactions/types/interaction.py +2 -0
  25. google/genai/_interactions/types/interaction_create_params.py +2 -0
  26. google/genai/_interactions/types/interaction_event.py +1 -2
  27. google/genai/_interactions/types/interaction_sse_event.py +5 -3
  28. google/genai/_interactions/types/interaction_status_update.py +1 -2
  29. google/genai/_interactions/types/video_content.py +2 -0
  30. google/genai/_interactions/types/video_content_param.py +2 -0
  31. google/genai/_live_converters.py +84 -0
  32. google/genai/_transformers.py +15 -21
  33. google/genai/client.py +61 -55
  34. google/genai/live.py +1 -1
  35. google/genai/tests/client/test_custom_client.py +27 -0
  36. google/genai/tests/client/test_http_options.py +1 -1
  37. google/genai/tests/interactions/test_auth.py +1 -4
  38. google/genai/tests/interactions/test_integration.py +2 -0
  39. google/genai/tests/transformers/test_blobs.py +16 -3
  40. google/genai/types.py +58 -2
  41. google/genai/version.py +1 -1
  42. {google_genai-1.57.0.dist-info → google_genai-1.58.0.dist-info}/METADATA +6 -11
  43. {google_genai-1.57.0.dist-info → google_genai-1.58.0.dist-info}/RECORD +46 -41
  44. {google_genai-1.57.0.dist-info → google_genai-1.58.0.dist-info}/WHEEL +0 -0
  45. {google_genai-1.57.0.dist-info → google_genai-1.58.0.dist-info}/licenses/LICENSE +0 -0
  46. {google_genai-1.57.0.dist-info → google_genai-1.58.0.dist-info}/top_level.txt +0 -0
@@ -34,6 +34,7 @@ from .document_content import DocumentContent
34
34
  from .dynamic_agent_config import DynamicAgentConfig
35
35
  from .function_call_content import FunctionCallContent
36
36
  from .function_result_content import FunctionResultContent
37
+ from .file_search_call_content import FileSearchCallContent
37
38
  from .url_context_call_content import URLContextCallContent
38
39
  from .deep_research_agent_config import DeepResearchAgentConfig
39
40
  from .file_search_result_content import FileSearchResultContent
@@ -71,6 +72,7 @@ Input: TypeAlias = Union[
71
72
  GoogleSearchResultContent,
72
73
  MCPServerToolCallContent,
73
74
  MCPServerToolResultContent,
75
+ FileSearchCallContent,
74
76
  FileSearchResultContent,
75
77
  ]
76
78
 
@@ -34,6 +34,7 @@ from .generation_config_param import GenerationConfigParam
34
34
  from .dynamic_agent_config_param import DynamicAgentConfigParam
35
35
  from .function_call_content_param import FunctionCallContentParam
36
36
  from .function_result_content_param import FunctionResultContentParam
37
+ from .file_search_call_content_param import FileSearchCallContentParam
37
38
  from .url_context_call_content_param import URLContextCallContentParam
38
39
  from .deep_research_agent_config_param import DeepResearchAgentConfigParam
39
40
  from .file_search_result_content_param import FileSearchResultContentParam
@@ -117,6 +118,7 @@ Input: TypeAlias = Union[
117
118
  GoogleSearchResultContentParam,
118
119
  MCPServerToolCallContentParam,
119
120
  MCPServerToolResultContentParam,
121
+ FileSearchCallContentParam,
120
122
  FileSearchResultContentParam,
121
123
  ]
122
124
 
@@ -27,8 +27,7 @@ __all__ = ["InteractionEvent"]
27
27
  class InteractionEvent(BaseModel):
28
28
  event_id: Optional[str] = None
29
29
  """
30
- The event_id token to be used to resume the interaction stream, from
31
- this event.
30
+ The event_id token to be used to resume the interaction stream, from this event.
32
31
  """
33
32
 
34
33
  event_type: Optional[Literal["interaction.start", "interaction.complete"]] = None
@@ -16,8 +16,9 @@
16
16
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
17
17
 
18
18
  from typing import Union
19
- from typing_extensions import TypeAlias
19
+ from typing_extensions import Annotated, TypeAlias
20
20
 
21
+ from .._utils import PropertyInfo
21
22
  from .error_event import ErrorEvent
22
23
  from .content_stop import ContentStop
23
24
  from .content_delta import ContentDelta
@@ -27,6 +28,7 @@ from .interaction_status_update import InteractionStatusUpdate
27
28
 
28
29
  __all__ = ["InteractionSSEEvent"]
29
30
 
30
- InteractionSSEEvent: TypeAlias = Union[
31
- InteractionEvent, InteractionStatusUpdate, ContentStart, ContentDelta, ContentStop, ErrorEvent
31
+ InteractionSSEEvent: TypeAlias = Annotated[
32
+ Union[InteractionEvent, InteractionStatusUpdate, ContentStart, ContentDelta, ContentStop, ErrorEvent],
33
+ PropertyInfo(discriminator="event_type"),
32
34
  ]
@@ -26,8 +26,7 @@ __all__ = ["InteractionStatusUpdate"]
26
26
  class InteractionStatusUpdate(BaseModel):
27
27
  event_id: Optional[str] = None
28
28
  """
29
- The event_id token to be used to resume the interaction stream, from
30
- this event.
29
+ The event_id token to be used to resume the interaction stream, from this event.
31
30
  """
32
31
 
33
32
  event_type: Optional[Literal["interaction.status_update"]] = None
@@ -30,6 +30,7 @@ class VideoContent(BaseModel):
30
30
  type: Literal["video"]
31
31
 
32
32
  data: Optional[str] = None
33
+ """The video content."""
33
34
 
34
35
  mime_type: Optional[VideoMimeType] = None
35
36
  """The mime type of the video."""
@@ -38,3 +39,4 @@ class VideoContent(BaseModel):
38
39
  """The resolution of the media."""
39
40
 
40
41
  uri: Optional[str] = None
42
+ """The URI of the video."""
@@ -34,6 +34,7 @@ class VideoContentParam(TypedDict, total=False):
34
34
  type: Required[Literal["video"]]
35
35
 
36
36
  data: Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")]
37
+ """The video content."""
37
38
 
38
39
  mime_type: VideoMimeTypeParam
39
40
  """The mime type of the video."""
@@ -42,6 +43,7 @@ class VideoContentParam(TypedDict, total=False):
42
43
  """The resolution of the media."""
43
44
 
44
45
  uri: str
46
+ """The URI of the video."""
45
47
 
46
48
 
47
49
  set_pydantic_config(VideoContentParam, {"arbitrary_types_allowed": True})
@@ -1151,6 +1151,59 @@ def _LiveSendRealtimeInputParameters_to_vertex(
1151
1151
  return to_object
1152
1152
 
1153
1153
 
1154
+ def _LiveServerMessage_from_mldev(
1155
+ from_object: Union[dict[str, Any], object],
1156
+ parent_object: Optional[dict[str, Any]] = None,
1157
+ ) -> dict[str, Any]:
1158
+ to_object: dict[str, Any] = {}
1159
+ if getv(from_object, ['setupComplete']) is not None:
1160
+ setv(to_object, ['setup_complete'], getv(from_object, ['setupComplete']))
1161
+
1162
+ if getv(from_object, ['serverContent']) is not None:
1163
+ setv(to_object, ['server_content'], getv(from_object, ['serverContent']))
1164
+
1165
+ if getv(from_object, ['toolCall']) is not None:
1166
+ setv(to_object, ['tool_call'], getv(from_object, ['toolCall']))
1167
+
1168
+ if getv(from_object, ['toolCallCancellation']) is not None:
1169
+ setv(
1170
+ to_object,
1171
+ ['tool_call_cancellation'],
1172
+ getv(from_object, ['toolCallCancellation']),
1173
+ )
1174
+
1175
+ if getv(from_object, ['usageMetadata']) is not None:
1176
+ setv(to_object, ['usage_metadata'], getv(from_object, ['usageMetadata']))
1177
+
1178
+ if getv(from_object, ['goAway']) is not None:
1179
+ setv(to_object, ['go_away'], getv(from_object, ['goAway']))
1180
+
1181
+ if getv(from_object, ['sessionResumptionUpdate']) is not None:
1182
+ setv(
1183
+ to_object,
1184
+ ['session_resumption_update'],
1185
+ getv(from_object, ['sessionResumptionUpdate']),
1186
+ )
1187
+
1188
+ if getv(from_object, ['voiceActivityDetectionSignal']) is not None:
1189
+ setv(
1190
+ to_object,
1191
+ ['voice_activity_detection_signal'],
1192
+ getv(from_object, ['voiceActivityDetectionSignal']),
1193
+ )
1194
+
1195
+ if getv(from_object, ['voiceActivity']) is not None:
1196
+ setv(
1197
+ to_object,
1198
+ ['voice_activity'],
1199
+ _VoiceActivity_from_mldev(
1200
+ getv(from_object, ['voiceActivity']), to_object
1201
+ ),
1202
+ )
1203
+
1204
+ return to_object
1205
+
1206
+
1154
1207
  def _LiveServerMessage_from_vertex(
1155
1208
  from_object: Union[dict[str, Any], object],
1156
1209
  parent_object: Optional[dict[str, Any]] = None,
@@ -1198,6 +1251,15 @@ def _LiveServerMessage_from_vertex(
1198
1251
  getv(from_object, ['voiceActivityDetectionSignal']),
1199
1252
  )
1200
1253
 
1254
+ if getv(from_object, ['voiceActivity']) is not None:
1255
+ setv(
1256
+ to_object,
1257
+ ['voice_activity'],
1258
+ _VoiceActivity_from_vertex(
1259
+ getv(from_object, ['voiceActivity']), to_object
1260
+ ),
1261
+ )
1262
+
1201
1263
  return to_object
1202
1264
 
1203
1265
 
@@ -1468,3 +1530,25 @@ def _UsageMetadata_from_vertex(
1468
1530
  setv(to_object, ['traffic_type'], getv(from_object, ['trafficType']))
1469
1531
 
1470
1532
  return to_object
1533
+
1534
+
1535
+ def _VoiceActivity_from_mldev(
1536
+ from_object: Union[dict[str, Any], object],
1537
+ parent_object: Optional[dict[str, Any]] = None,
1538
+ ) -> dict[str, Any]:
1539
+ to_object: dict[str, Any] = {}
1540
+ if getv(from_object, ['type']) is not None:
1541
+ setv(to_object, ['voice_activity_type'], getv(from_object, ['type']))
1542
+
1543
+ return to_object
1544
+
1545
+
1546
+ def _VoiceActivity_from_vertex(
1547
+ from_object: Union[dict[str, Any], object],
1548
+ parent_object: Optional[dict[str, Any]] = None,
1549
+ ) -> dict[str, Any]:
1550
+ to_object: dict[str, Any] = {}
1551
+ if getv(from_object, ['type']) is not None:
1552
+ setv(to_object, ['voice_activity_type'], getv(from_object, ['type']))
1553
+
1554
+ return to_object
@@ -285,29 +285,23 @@ def t_caches_model(
285
285
  return model
286
286
 
287
287
 
288
- def pil_to_blob(img: Any) -> types.Blob:
289
- PngImagePlugin: Optional[builtin_types.ModuleType]
290
- try:
291
- import PIL.PngImagePlugin
292
-
293
- PngImagePlugin = PIL.PngImagePlugin
294
- except ImportError:
295
- PngImagePlugin = None
296
-
297
- bytesio = io.BytesIO()
288
+ def pil_to_blob(image: Any) -> types.Blob:
289
+ image_format = 'PNG'
290
+ save_params: dict[str, Any] = dict()
298
291
  if (
299
- PngImagePlugin is not None
300
- and isinstance(img, PngImagePlugin.PngImageFile)
301
- or img.mode == 'RGBA'
292
+ image.format == 'JPEG'
293
+ and getattr(image, 'filename', '')
294
+ and image.mode in ['1', 'L', 'RGB', 'RGBX', 'CMYK']
302
295
  ):
303
- img.save(bytesio, format='PNG')
304
- mime_type = 'image/png'
305
- else:
306
- img.save(bytesio, format='JPEG')
307
- mime_type = 'image/jpeg'
308
- bytesio.seek(0)
309
- data = bytesio.read()
310
- return types.Blob(mime_type=mime_type, data=data)
296
+ image_format = 'JPEG'
297
+ save_params.update(quality='keep')
298
+
299
+ image_io = io.BytesIO()
300
+ image.save(image_io, image_format, **save_params)
301
+ image_bytes = image_io.getvalue()
302
+ mime_type = f'image/{image_format.lower()}'
303
+
304
+ return types.Blob(data=image_bytes, mime_type=mime_type)
311
305
 
312
306
 
313
307
  def t_function_response(
google/genai/client.py CHANGED
@@ -16,7 +16,7 @@
16
16
  import asyncio
17
17
  import os
18
18
  from types import TracebackType
19
- from typing import Optional, Union, cast
19
+ from typing import Optional, Union
20
20
 
21
21
  import google.auth
22
22
  import pydantic
@@ -43,13 +43,65 @@ from ._api_client import has_aiohttp
43
43
 
44
44
  from . import _common
45
45
 
46
- from ._interactions import AsyncGeminiNextGenAPIClient, DEFAULT_MAX_RETRIES, DefaultAioHttpClient, GeminiNextGenAPIClient
47
- from ._interactions._models import FinalRequestOptions
48
- from ._interactions._types import Headers
49
- from ._interactions._utils import is_given
46
+ from ._interactions import AsyncGeminiNextGenAPIClient, DEFAULT_MAX_RETRIES, GeminiNextGenAPIClient
47
+ from . import _interactions
48
+
50
49
  from ._interactions.resources import AsyncInteractionsResource as AsyncNextGenInteractionsResource, InteractionsResource as NextGenInteractionsResource
51
50
  _interactions_experimental_warned = False
52
51
 
52
+ class AsyncGeminiNextGenAPIClientAdapter(_interactions.AsyncGeminiNextGenAPIClientAdapter):
53
+ """Adapter for the Gemini NextGen API Client."""
54
+ def __init__(self, api_client: BaseApiClient):
55
+ self._api_client = api_client
56
+
57
+ def is_vertex_ai(self) -> bool:
58
+ return self._api_client.vertexai or False
59
+
60
+ def get_project(self) -> str | None:
61
+ return self._api_client.project
62
+
63
+ def get_location(self) -> str | None:
64
+ return self._api_client.location
65
+
66
+ async def async_get_auth_headers(self) -> dict[str, str]:
67
+ if self._api_client.api_key:
68
+ return {"x-goog-api-key": self._api_client.api_key}
69
+ access_token = await self._api_client._async_access_token()
70
+ headers = {
71
+ "Authorization": f"Bearer {access_token}",
72
+ }
73
+ if creds := self._api_client._credentials:
74
+ if creds.quota_project_id:
75
+ headers["x-goog-user-project"] = creds.quota_project_id
76
+ return headers
77
+
78
+
79
+ class GeminiNextGenAPIClientAdapter(_interactions.GeminiNextGenAPIClientAdapter):
80
+ """Adapter for the Gemini NextGen API Client."""
81
+ def __init__(self, api_client: BaseApiClient):
82
+ self._api_client = api_client
83
+
84
+ def is_vertex_ai(self) -> bool:
85
+ return self._api_client.vertexai or False
86
+
87
+ def get_project(self) -> str | None:
88
+ return self._api_client.project
89
+
90
+ def get_location(self) -> str | None:
91
+ return self._api_client.location
92
+
93
+ def get_auth_headers(self) -> dict[str, str]:
94
+ if self._api_client.api_key:
95
+ return {"x-goog-api-key": self._api_client.api_key}
96
+ access_token = self._api_client._access_token()
97
+ headers = {
98
+ "Authorization": f"Bearer {access_token}",
99
+ }
100
+ if creds := self._api_client._credentials:
101
+ if creds.quota_project_id:
102
+ headers["x-goog-user-project"] = creds.quota_project_id
103
+ return headers
104
+
53
105
 
54
106
  class AsyncClient:
55
107
  """Client for making asynchronous (non-blocking) requests."""
@@ -122,6 +174,7 @@ class AsyncClient:
122
174
  # uSDk expects ms, nextgen uses a httpx Timeout -> expects seconds.
123
175
  timeout=http_opts.timeout / 1000 if http_opts.timeout else None,
124
176
  max_retries=max_retries,
177
+ client_adapter=AsyncGeminiNextGenAPIClientAdapter(self._api_client)
125
178
  )
126
179
 
127
180
  client = self._nextgen_client_instance
@@ -130,30 +183,6 @@ class AsyncClient:
130
183
  client._vertex_project = self._api_client.project
131
184
  client._vertex_location = self._api_client.location
132
185
 
133
- async def prepare_options(options: FinalRequestOptions) -> FinalRequestOptions:
134
- headers = {}
135
- if is_given(options.headers):
136
- headers = {**options.headers}
137
-
138
- headers['Authorization'] = f'Bearer {await self._api_client._async_access_token()}'
139
- if (
140
- self._api_client._credentials
141
- and self._api_client._credentials.quota_project_id
142
- ):
143
- headers['x-goog-user-project'] = (
144
- self._api_client._credentials.quota_project_id
145
- )
146
- options.headers = headers
147
-
148
- return options
149
-
150
- if self._api_client.project or self._api_client.location:
151
- client._prepare_options = prepare_options # type: ignore[method-assign]
152
-
153
- def validate_headers(headers: Headers, custom_headers: Headers) -> None:
154
- return
155
-
156
- client._validate_headers = validate_headers # type: ignore[method-assign]
157
186
  return self._nextgen_client_instance
158
187
 
159
188
  @property
@@ -279,6 +308,7 @@ class DebugConfig(pydantic.BaseModel):
279
308
  )
280
309
 
281
310
 
311
+
282
312
  class Client:
283
313
  """Client for making synchronous requests.
284
314
 
@@ -492,39 +522,15 @@ class Client:
492
522
  # uSDk expects ms, nextgen uses a httpx Timeout -> expects seconds.
493
523
  timeout=http_opts.timeout / 1000 if http_opts.timeout else None,
494
524
  max_retries=max_retries,
525
+ client_adapter=GeminiNextGenAPIClientAdapter(self._api_client),
495
526
  )
496
527
 
497
528
  client = self._nextgen_client_instance
498
- if self.vertexai:
529
+ if self._api_client.vertexai:
499
530
  client._is_vertex = True
500
531
  client._vertex_project = self._api_client.project
501
532
  client._vertex_location = self._api_client.location
502
533
 
503
- def prepare_options(options: FinalRequestOptions) -> FinalRequestOptions:
504
- headers = {}
505
- if is_given(options.headers):
506
- headers = {**options.headers}
507
- options.headers = headers
508
-
509
- headers['Authorization'] = f'Bearer {self._api_client._access_token()}'
510
- if (
511
- self._api_client._credentials
512
- and self._api_client._credentials.quota_project_id
513
- ):
514
- headers['x-goog-user-project'] = (
515
- self._api_client._credentials.quota_project_id
516
- )
517
-
518
- return options
519
-
520
- if self._api_client.project or self._api_client.location:
521
- client._prepare_options = prepare_options # type: ignore[method-assign]
522
-
523
- def validate_headers(headers: Headers, custom_headers: Headers) -> None:
524
- return
525
-
526
- client._validate_headers = validate_headers # type: ignore[method-assign]
527
-
528
534
  return self._nextgen_client_instance
529
535
 
530
536
  @property
google/genai/live.py CHANGED
@@ -1036,7 +1036,7 @@ class AsyncLive(_api_module.BaseModule):
1036
1036
  if requests is None:
1037
1037
  raise ValueError('The requests module is required to refresh google-auth credentials. Please install with `pip install google-auth[requests]`')
1038
1038
  auth_req = requests.Request() # type: ignore
1039
- creds.refresh(auth_req)
1039
+ creds.refresh(auth_req) # type: ignore[no-untyped-call]
1040
1040
  bearer_token = creds.token
1041
1041
 
1042
1042
  original_headers = self._api_client._http_options.headers
@@ -75,3 +75,30 @@ def test_constructor_with_httpx_clients():
75
75
  assert not mldev_client.models._api_client._use_aiohttp()
76
76
 
77
77
 
78
+ # Aiohttp
79
+ @requires_aiohttp
80
+ @pytest.mark.asyncio
81
+ @pytest.mark.skipif(
82
+ AIOHTTP_NOT_INSTALLED, reason='aiohttp is not installed, skipping test.'
83
+ )
84
+ async def test_constructor_with_aiohttp_clients():
85
+ api_client.has_aiohttp = True
86
+ mldev_http_options = {
87
+ 'aiohttp_client': aiohttp.ClientSession(trust_env=False),
88
+ }
89
+ vertexai_http_options = {
90
+ 'aiohttp_client': aiohttp.ClientSession(trust_env=False),
91
+ }
92
+ mldev_client = Client(
93
+ api_key='google_api_key', http_options=mldev_http_options
94
+ )
95
+ assert not mldev_client.models._api_client._aiohttp_session.trust_env
96
+
97
+ vertexai_client = Client(
98
+ vertexai=True,
99
+ project='fake_project_id',
100
+ location='fake-location',
101
+ http_options=vertexai_http_options,
102
+ )
103
+ assert not vertexai_client.models._api_client._aiohttp_session.trust_env
104
+
@@ -40,7 +40,7 @@ def test_patch_http_options_with_copies_all_fields():
40
40
 
41
41
  for key in http_options_keys:
42
42
  assert hasattr(patched, key)
43
- if key not in ['httpx_client', 'httpx_async_client', 'aiohttp_client_session']:
43
+ if key not in ['httpx_client', 'httpx_async_client', 'aiohttp_client']:
44
44
  assert getattr(patched, key) is not None
45
45
  assert patched.base_url == 'https://fake-url.com/'
46
46
  assert patched.api_version == 'v1'
@@ -214,7 +214,7 @@ def test_interactions_vertex_auth_refresh_on_retry():
214
214
  headers = mock_send.call_args_list[i][0][0].headers
215
215
  assert headers['authorization'] == f'Bearer {token_values[i]}'
216
216
 
217
- @pytest.mark.xfail(reason="extra_headers don't override default auth")
217
+
218
218
  def test_interactions_vertex_extra_headers_override():
219
219
  from ..._api_client import BaseApiClient
220
220
  from httpx import Client as HTTPClient
@@ -358,8 +358,6 @@ async def test_async_interactions_vertex_auth_header():
358
358
  @pytest.mark.asyncio
359
359
  async def test_async_interactions_vertex_key_no_auth_header():
360
360
  from ..._api_client import BaseApiClient
361
- from ..._api_client import AsyncHttpxClient
362
- creds = mock.Mock()
363
361
  client = Client(vertexai=True, api_key='test-api-key')
364
362
 
365
363
  with (
@@ -436,7 +434,6 @@ async def test_async_interactions_vertex_auth_refresh_on_retry():
436
434
  headers = mock_send.call_args_list[i][0][0].headers
437
435
  assert headers['authorization'] == f'Bearer {token_values[i]}'
438
436
 
439
- @pytest.mark.xfail(reason="extra_headers don't override default auth")
440
437
  @pytest.mark.asyncio
441
438
  async def test_async_interactions_vertex_extra_headers_override():
442
439
  from ..._api_client import BaseApiClient
@@ -55,6 +55,7 @@ def test_client_timeout():
55
55
  http_client=mock.ANY,
56
56
  timeout=5.0,
57
57
  max_retries=mock.ANY,
58
+ client_adapter=mock.ANY,
58
59
  )
59
60
 
60
61
 
@@ -79,4 +80,5 @@ async def test_async_client_timeout():
79
80
  http_client=mock.ANY,
80
81
  timeout=5.0,
81
82
  max_retries=mock.ANY,
83
+ client_adapter=mock.ANY,
82
84
  )
@@ -26,14 +26,17 @@ from ... import types
26
26
 
27
27
 
28
28
  def test_blob_dict():
29
- blob = t.t_blob({'data': bytes([0, 0, 0, 0, 0, 0]), 'mime_type': 'audio/pcm'}
30
- )
29
+ blob = t.t_blob({
30
+ 'data': bytes([0, 0, 0, 0, 0, 0]),
31
+ 'mime_type': 'audio/pcm',
32
+ })
31
33
  assert blob.data == bytes([0, 0, 0, 0, 0, 0])
32
34
  assert blob.mime_type == 'audio/pcm'
33
35
 
34
36
 
35
37
  def test_blob():
36
- blob = t.t_blob(types.Blob(data=bytes([0, 0, 0, 0, 0, 0]), mime_type='audio/pcm')
38
+ blob = t.t_blob(
39
+ types.Blob(data=bytes([0, 0, 0, 0, 0, 0]), mime_type='audio/pcm')
37
40
  )
38
41
  assert blob.data == bytes([0, 0, 0, 0, 0, 0])
39
42
  assert blob.mime_type == 'audio/pcm'
@@ -50,10 +53,12 @@ def test_image(image_jpeg):
50
53
  assert round_trip_image.mode == image_jpeg.mode
51
54
  assert round_trip_image.format == image_jpeg.format
52
55
 
56
+
53
57
  def test_not_image():
54
58
  blob = types.Blob(data=bytes([0, 0, 0, 0, 0, 0]), mime_type='audio/pcm')
55
59
  assert blob.as_image() is None
56
60
 
61
+
57
62
  def test_part_image(image_jpeg):
58
63
  part = t.t_part(image_jpeg)
59
64
  assert part.inline_data.data[6:10] == b'JFIF'
@@ -69,3 +74,11 @@ def test_part_image(image_jpeg):
69
74
  def test_part_not_image():
70
75
  part = t.t_part('hello world')
71
76
  assert part.as_image() is None
77
+
78
+
79
+ def test_pil_to_blob_with_memory_pil_image():
80
+ img = PIL.Image.new('RGB', (1, 1), color='red')
81
+ blob = t.pil_to_blob(img)
82
+ assert blob.mime_type == 'image/png'
83
+ assert blob.data and len(blob.data) == 69
84
+ assert blob.data[0:4] == b'\x89PNG'
google/genai/types.py CHANGED
@@ -108,6 +108,20 @@ else:
108
108
  HttpxClient = None
109
109
  HttpxAsyncClient = None
110
110
 
111
+ _is_aiohttp_imported = False
112
+ if typing.TYPE_CHECKING:
113
+ from aiohttp import ClientSession
114
+
115
+ _is_aiohttp_imported = True
116
+ else:
117
+ ClientSession: typing.Type = Any
118
+ try:
119
+ from aiohttp import ClientSession
120
+
121
+ _is_aiohttp_imported = True
122
+ except ImportError:
123
+ ClientSession = None
124
+
111
125
  logger = logging.getLogger('google_genai.types')
112
126
  _from_json_schema_warning_logged = False
113
127
  _json_schema_warning_logged = False
@@ -869,6 +883,17 @@ class VadSignalType(_common.CaseInSensitiveEnum):
869
883
  """End of sentence signal."""
870
884
 
871
885
 
886
+ class VoiceActivityType(_common.CaseInSensitiveEnum):
887
+ """The type of the voice activity signal."""
888
+
889
+ TYPE_UNSPECIFIED = 'TYPE_UNSPECIFIED'
890
+ """The default is VOICE_ACTIVITY_TYPE_UNSPECIFIED."""
891
+ ACTIVITY_START = 'ACTIVITY_START'
892
+ """Start of sentence signal."""
893
+ ACTIVITY_END = 'ACTIVITY_END'
894
+ """End of sentence signal."""
895
+
896
+
872
897
  class StartSensitivity(_common.CaseInSensitiveEnum):
873
898
  """Start of speech sensitivity."""
874
899
 
@@ -1925,6 +1950,10 @@ class HttpOptions(_common.BaseModel):
1925
1950
  default=None,
1926
1951
  description="""A custom httpx async client to be used for the request.""",
1927
1952
  )
1953
+ aiohttp_client: Optional['ClientSession'] = Field(
1954
+ default=None,
1955
+ description="""A custom aiohttp client session to be used for the request.""",
1956
+ )
1928
1957
 
1929
1958
 
1930
1959
  class HttpOptionsDict(TypedDict, total=False):
@@ -16347,6 +16376,24 @@ VoiceActivityDetectionSignalOrDict = Union[
16347
16376
  ]
16348
16377
 
16349
16378
 
16379
+ class VoiceActivity(_common.BaseModel):
16380
+ """Voice activity signal."""
16381
+
16382
+ voice_activity_type: Optional[VoiceActivityType] = Field(
16383
+ default=None, description="""The type of the voice activity signal."""
16384
+ )
16385
+
16386
+
16387
+ class VoiceActivityDict(TypedDict, total=False):
16388
+ """Voice activity signal."""
16389
+
16390
+ voice_activity_type: Optional[VoiceActivityType]
16391
+ """The type of the voice activity signal."""
16392
+
16393
+
16394
+ VoiceActivityOrDict = Union[VoiceActivity, VoiceActivityDict]
16395
+
16396
+
16350
16397
  class LiveServerMessage(_common.BaseModel):
16351
16398
  """Response message for API call."""
16352
16399
 
@@ -16379,7 +16426,13 @@ class LiveServerMessage(_common.BaseModel):
16379
16426
  )
16380
16427
  )
16381
16428
  voice_activity_detection_signal: Optional[VoiceActivityDetectionSignal] = (
16382
- Field(default=None, description="""Voice activity detection signal.""")
16429
+ Field(
16430
+ default=None,
16431
+ description="""Voice activity detection signal. Allowlisted only.""",
16432
+ )
16433
+ )
16434
+ voice_activity: Optional[VoiceActivity] = Field(
16435
+ default=None, description="""Voice activity signal."""
16383
16436
  )
16384
16437
 
16385
16438
  @property
@@ -16478,7 +16531,10 @@ class LiveServerMessageDict(TypedDict, total=False):
16478
16531
  """Update of the session resumption state."""
16479
16532
 
16480
16533
  voice_activity_detection_signal: Optional[VoiceActivityDetectionSignalDict]
16481
- """Voice activity detection signal."""
16534
+ """Voice activity detection signal. Allowlisted only."""
16535
+
16536
+ voice_activity: Optional[VoiceActivityDict]
16537
+ """Voice activity signal."""
16482
16538
 
16483
16539
 
16484
16540
  LiveServerMessageOrDict = Union[LiveServerMessage, LiveServerMessageDict]
google/genai/version.py CHANGED
@@ -13,4 +13,4 @@
13
13
  # limitations under the License.
14
14
  #
15
15
 
16
- __version__ = '1.57.0' # x-release-please-version
16
+ __version__ = '1.58.0' # x-release-please-version