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.
- google/genai/_api_client.py +12 -8
- google/genai/_interactions/__init__.py +3 -0
- google/genai/_interactions/_client.py +57 -3
- google/genai/_interactions/_client_adapter.py +48 -0
- google/genai/_interactions/types/__init__.py +4 -0
- google/genai/_interactions/types/audio_content.py +2 -0
- google/genai/_interactions/types/audio_content_param.py +2 -0
- google/genai/_interactions/types/content.py +2 -0
- google/genai/_interactions/types/content_delta.py +10 -2
- google/genai/_interactions/types/content_param.py +2 -0
- google/genai/_interactions/types/content_start.py +1 -2
- google/genai/_interactions/types/content_stop.py +1 -2
- google/genai/_interactions/types/document_content.py +2 -0
- google/genai/_interactions/types/document_content_param.py +2 -0
- google/genai/_interactions/types/error_event.py +1 -2
- google/genai/_interactions/types/file_search_call_content.py +32 -0
- google/genai/_interactions/types/file_search_call_content_param.py +31 -0
- google/genai/_interactions/types/generation_config.py +4 -0
- google/genai/_interactions/types/generation_config_param.py +4 -0
- google/genai/_interactions/types/image_config.py +31 -0
- google/genai/_interactions/types/image_config_param.py +30 -0
- google/genai/_interactions/types/image_content.py +2 -0
- google/genai/_interactions/types/image_content_param.py +2 -0
- google/genai/_interactions/types/interaction.py +2 -0
- google/genai/_interactions/types/interaction_create_params.py +2 -0
- google/genai/_interactions/types/interaction_event.py +1 -2
- google/genai/_interactions/types/interaction_sse_event.py +5 -3
- google/genai/_interactions/types/interaction_status_update.py +1 -2
- google/genai/_interactions/types/video_content.py +2 -0
- google/genai/_interactions/types/video_content_param.py +2 -0
- google/genai/_live_converters.py +84 -0
- google/genai/_transformers.py +15 -21
- google/genai/client.py +61 -55
- google/genai/live.py +1 -1
- google/genai/tests/client/test_custom_client.py +27 -0
- google/genai/tests/client/test_http_options.py +1 -1
- google/genai/tests/interactions/test_auth.py +1 -4
- google/genai/tests/interactions/test_integration.py +2 -0
- google/genai/tests/transformers/test_blobs.py +16 -3
- google/genai/types.py +58 -2
- google/genai/version.py +1 -1
- {google_genai-1.57.0.dist-info → google_genai-1.58.0.dist-info}/METADATA +6 -11
- {google_genai-1.57.0.dist-info → google_genai-1.58.0.dist-info}/RECORD +46 -41
- {google_genai-1.57.0.dist-info → google_genai-1.58.0.dist-info}/WHEEL +0 -0
- {google_genai-1.57.0.dist-info → google_genai-1.58.0.dist-info}/licenses/LICENSE +0 -0
- {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 =
|
|
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})
|
google/genai/_live_converters.py
CHANGED
|
@@ -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
|
google/genai/_transformers.py
CHANGED
|
@@ -285,29 +285,23 @@ def t_caches_model(
|
|
|
285
285
|
return model
|
|
286
286
|
|
|
287
287
|
|
|
288
|
-
def pil_to_blob(
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
|
|
300
|
-
and
|
|
301
|
-
|
|
292
|
+
image.format == 'JPEG'
|
|
293
|
+
and getattr(image, 'filename', '')
|
|
294
|
+
and image.mode in ['1', 'L', 'RGB', 'RGBX', 'CMYK']
|
|
302
295
|
):
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
|
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,
|
|
47
|
-
from .
|
|
48
|
-
|
|
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', '
|
|
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
|
-
|
|
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({
|
|
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(
|
|
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(
|
|
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