google-genai 1.9.0__py3-none-any.whl → 1.11.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 +192 -35
- google/genai/_automatic_function_calling_util.py +1 -1
- google/genai/_extra_utils.py +70 -10
- google/genai/_replay_api_client.py +32 -8
- google/genai/_transformers.py +172 -59
- google/genai/files.py +22 -6
- google/genai/live.py +136 -580
- google/genai/live_converters.py +1298 -0
- google/genai/models.py +97 -15
- google/genai/operations.py +17 -9
- google/genai/tunings.py +0 -3
- google/genai/types.py +1064 -78
- google/genai/version.py +1 -1
- {google_genai-1.9.0.dist-info → google_genai-1.11.0.dist-info}/METADATA +1 -1
- google_genai-1.11.0.dist-info/RECORD +28 -0
- google_genai-1.9.0.dist-info/RECORD +0 -27
- {google_genai-1.9.0.dist-info → google_genai-1.11.0.dist-info}/WHEEL +0 -0
- {google_genai-1.9.0.dist-info → google_genai-1.11.0.dist-info}/licenses/LICENSE +0 -0
- {google_genai-1.9.0.dist-info → google_genai-1.11.0.dist-info}/top_level.txt +0 -0
google/genai/live.py
CHANGED
@@ -13,14 +13,14 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
#
|
15
15
|
|
16
|
-
"""Live client.
|
16
|
+
"""[Preview] Live API client."""
|
17
17
|
|
18
18
|
import asyncio
|
19
19
|
import base64
|
20
20
|
import contextlib
|
21
21
|
import json
|
22
22
|
import logging
|
23
|
-
from typing import Any, AsyncIterator, Dict, Optional, Sequence, Union, get_args
|
23
|
+
from typing import Any, AsyncIterator, Dict, Optional, Sequence, Union, cast, get_args
|
24
24
|
import warnings
|
25
25
|
|
26
26
|
import google.auth
|
@@ -33,21 +33,12 @@ from . import _transformers as t
|
|
33
33
|
from . import client
|
34
34
|
from . import types
|
35
35
|
from ._api_client import BaseApiClient
|
36
|
-
from ._common import experimental_warning
|
37
36
|
from ._common import get_value_by_path as getv
|
38
37
|
from ._common import set_value_by_path as setv
|
39
|
-
from .
|
40
|
-
from .models import _Content_from_vertex
|
38
|
+
from . import live_converters
|
41
39
|
from .models import _Content_to_mldev
|
42
40
|
from .models import _Content_to_vertex
|
43
|
-
|
44
|
-
from .models import _GenerateContentConfig_to_vertex
|
45
|
-
from .models import _SafetySetting_to_mldev
|
46
|
-
from .models import _SafetySetting_to_vertex
|
47
|
-
from .models import _SpeechConfig_to_mldev
|
48
|
-
from .models import _SpeechConfig_to_vertex
|
49
|
-
from .models import _Tool_to_mldev
|
50
|
-
from .models import _Tool_to_vertex
|
41
|
+
|
51
42
|
|
52
43
|
try:
|
53
44
|
from websockets.asyncio.client import ClientConnection # type: ignore
|
@@ -65,61 +56,8 @@ _FUNCTION_RESPONSE_REQUIRES_ID = (
|
|
65
56
|
)
|
66
57
|
|
67
58
|
|
68
|
-
def _ClientContent_to_mldev(
|
69
|
-
api_client: BaseApiClient,
|
70
|
-
from_object: types.LiveClientContent,
|
71
|
-
) -> dict:
|
72
|
-
client_content = from_object.model_dump(exclude_none=True, mode='json')
|
73
|
-
if 'turns' in client_content:
|
74
|
-
client_content['turns'] = [
|
75
|
-
_Content_to_mldev(api_client=api_client, from_object=item)
|
76
|
-
for item in client_content['turns']
|
77
|
-
]
|
78
|
-
return client_content
|
79
|
-
|
80
|
-
|
81
|
-
def _ClientContent_to_vertex(
|
82
|
-
api_client: BaseApiClient,
|
83
|
-
from_object: types.LiveClientContent,
|
84
|
-
) -> dict:
|
85
|
-
client_content = from_object.model_dump(exclude_none=True, mode='json')
|
86
|
-
if 'turns' in client_content:
|
87
|
-
client_content['turns'] = [
|
88
|
-
_Content_to_vertex(api_client=api_client, from_object=item)
|
89
|
-
for item in client_content['turns']
|
90
|
-
]
|
91
|
-
return client_content
|
92
|
-
|
93
|
-
|
94
|
-
def _ToolResponse_to_mldev(
|
95
|
-
api_client: BaseApiClient,
|
96
|
-
from_object: types.LiveClientToolResponse,
|
97
|
-
) -> dict:
|
98
|
-
tool_response = from_object.model_dump(exclude_none=True, mode='json')
|
99
|
-
for response in tool_response.get('function_responses', []):
|
100
|
-
if response.get('id') is None:
|
101
|
-
raise ValueError(_FUNCTION_RESPONSE_REQUIRES_ID)
|
102
|
-
return tool_response
|
103
|
-
|
104
|
-
|
105
|
-
def _ToolResponse_to_vertex(
|
106
|
-
api_client: BaseApiClient,
|
107
|
-
from_object: types.LiveClientToolResponse,
|
108
|
-
) -> dict:
|
109
|
-
tool_response = from_object.model_dump(exclude_none=True, mode='json')
|
110
|
-
return tool_response
|
111
|
-
|
112
|
-
|
113
|
-
def _AudioTranscriptionConfig_to_vertex(
|
114
|
-
api_client: BaseApiClient,
|
115
|
-
from_object: types.AudioTranscriptionConfig,
|
116
|
-
) -> dict:
|
117
|
-
audio_transcription: dict[str, Any] = {}
|
118
|
-
return audio_transcription
|
119
|
-
|
120
|
-
|
121
59
|
class AsyncSession:
|
122
|
-
"""AsyncSession.
|
60
|
+
"""[Preview] AsyncSession."""
|
123
61
|
|
124
62
|
def __init__(
|
125
63
|
self, api_client: client.BaseApiClient, websocket: ClientConnection
|
@@ -143,7 +81,12 @@ class AsyncSession:
|
|
143
81
|
] = None,
|
144
82
|
end_of_turn: Optional[bool] = False,
|
145
83
|
):
|
146
|
-
"""Send input to the model.
|
84
|
+
"""[Deprecated] Send input to the model.
|
85
|
+
|
86
|
+
> **Warning**: This method is deprecated and will be removed in a future
|
87
|
+
version (not before Q3 2025). Please use one of the more specific methods:
|
88
|
+
`send_client_content`, `send_realtime_input`, or `send_tool_response`
|
89
|
+
instead.
|
147
90
|
|
148
91
|
The method will send the input request to the server.
|
149
92
|
|
@@ -162,6 +105,14 @@ class AsyncSession:
|
|
162
105
|
async for message in session.receive():
|
163
106
|
print(message)
|
164
107
|
"""
|
108
|
+
warnings.warn(
|
109
|
+
'The `session.send` method is deprecated and will be removed in a '
|
110
|
+
'future version (not before Q3 2025).\n'
|
111
|
+
'Please use one of the more specific methods: `send_client_content`, '
|
112
|
+
'`send_realtime_input`, or `send_tool_response` instead.',
|
113
|
+
DeprecationWarning,
|
114
|
+
stacklevel=2,
|
115
|
+
)
|
165
116
|
client_message = self._parse_client_message(input, end_of_turn)
|
166
117
|
await self._ws.send(json.dumps(client_message))
|
167
118
|
|
@@ -218,8 +169,14 @@ class AsyncSession:
|
|
218
169
|
```
|
219
170
|
import google.genai
|
220
171
|
from google.genai import types
|
172
|
+
import os
|
173
|
+
|
174
|
+
if os.environ.get('GOOGLE_GENAI_USE_VERTEXAI'):
|
175
|
+
MODEL_NAME = 'gemini-2.0-flash-live-preview-04-09'
|
176
|
+
else:
|
177
|
+
MODEL_NAME = 'gemini-2.0-flash-live-001';
|
221
178
|
|
222
|
-
client = genai.Client(
|
179
|
+
client = genai.Client()
|
223
180
|
async with client.aio.live.connect(
|
224
181
|
model=MODEL_NAME,
|
225
182
|
config={"response_modalities": ["TEXT"]}
|
@@ -233,14 +190,14 @@ class AsyncSession:
|
|
233
190
|
print(msg.text)
|
234
191
|
```
|
235
192
|
"""
|
236
|
-
client_content =
|
193
|
+
client_content = t.t_client_content(turns, turn_complete)
|
237
194
|
|
238
195
|
if self._api_client.vertexai:
|
239
|
-
client_content_dict =
|
196
|
+
client_content_dict = live_converters._LiveClientContent_to_vertex(
|
240
197
|
api_client=self._api_client, from_object=client_content
|
241
198
|
)
|
242
199
|
else:
|
243
|
-
client_content_dict =
|
200
|
+
client_content_dict = live_converters._LiveClientContent_to_mldev(
|
244
201
|
api_client=self._api_client, from_object=client_content
|
245
202
|
)
|
246
203
|
|
@@ -270,8 +227,16 @@ class AsyncSession:
|
|
270
227
|
from google.genai import types
|
271
228
|
|
272
229
|
import PIL.Image
|
230
|
+
|
231
|
+
import os
|
273
232
|
|
274
|
-
|
233
|
+
if os.environ.get('GOOGLE_GENAI_USE_VERTEXAI'):
|
234
|
+
MODEL_NAME = 'gemini-2.0-flash-live-preview-04-09'
|
235
|
+
else:
|
236
|
+
MODEL_NAME = 'gemini-2.0-flash-live-001';
|
237
|
+
|
238
|
+
|
239
|
+
client = genai.Client()
|
275
240
|
|
276
241
|
async with client.aio.live.connect(
|
277
242
|
model=MODEL_NAME,
|
@@ -289,7 +254,7 @@ class AsyncSession:
|
|
289
254
|
print(f'{msg.text}')
|
290
255
|
```
|
291
256
|
"""
|
292
|
-
realtime_input =
|
257
|
+
realtime_input = t.t_realtime_input(media)
|
293
258
|
realtime_input_dict = realtime_input.model_dump(
|
294
259
|
exclude_none=True, mode='json'
|
295
260
|
)
|
@@ -320,7 +285,14 @@ class AsyncSession:
|
|
320
285
|
from google import genai
|
321
286
|
from google.genai import types
|
322
287
|
|
323
|
-
|
288
|
+
import os
|
289
|
+
|
290
|
+
if os.environ.get('GOOGLE_GENAI_USE_VERTEXAI'):
|
291
|
+
MODEL_NAME = 'gemini-2.0-flash-live-preview-04-09'
|
292
|
+
else:
|
293
|
+
MODEL_NAME = 'gemini-2.0-flash-live-001';
|
294
|
+
|
295
|
+
client = genai.Client()
|
324
296
|
|
325
297
|
tools = [{'function_declarations': [{'name': 'turn_on_the_lights'}]}]
|
326
298
|
config = {
|
@@ -329,13 +301,13 @@ class AsyncSession:
|
|
329
301
|
}
|
330
302
|
|
331
303
|
async with client.aio.live.connect(
|
332
|
-
model='gemini-2.0-flash-
|
304
|
+
model='models/gemini-2.0-flash-live-001',
|
333
305
|
config=config
|
334
306
|
) as session:
|
335
307
|
prompt = "Turn on the lights please"
|
336
308
|
await session.send_client_content(
|
337
|
-
turns=prompt
|
338
|
-
|
309
|
+
turns={"parts": [{'text': prompt}]}
|
310
|
+
)
|
339
311
|
|
340
312
|
async for chunk in session.receive():
|
341
313
|
if chunk.server_content:
|
@@ -356,13 +328,13 @@ class AsyncSession:
|
|
356
328
|
|
357
329
|
print('_'*80)
|
358
330
|
"""
|
359
|
-
tool_response =
|
331
|
+
tool_response = t.t_tool_response(function_responses)
|
360
332
|
if self._api_client.vertexai:
|
361
|
-
tool_response_dict =
|
333
|
+
tool_response_dict = live_converters._LiveClientToolResponse_to_vertex(
|
362
334
|
api_client=self._api_client, from_object=tool_response
|
363
335
|
)
|
364
336
|
else:
|
365
|
-
tool_response_dict =
|
337
|
+
tool_response_dict = live_converters._LiveClientToolResponse_to_mldev(
|
366
338
|
api_client=self._api_client, from_object=tool_response
|
367
339
|
)
|
368
340
|
await self._ws.send(json.dumps({'tool_response': tool_response_dict}))
|
@@ -375,8 +347,6 @@ class AsyncSession:
|
|
375
347
|
is function call, user must call `send` with the function response to
|
376
348
|
continue the turn.
|
377
349
|
|
378
|
-
The live module is experimental.
|
379
|
-
|
380
350
|
Yields:
|
381
351
|
The model responses from the server.
|
382
352
|
|
@@ -401,15 +371,18 @@ class AsyncSession:
|
|
401
371
|
async def start_stream(
|
402
372
|
self, *, stream: AsyncIterator[bytes], mime_type: str
|
403
373
|
) -> AsyncIterator[types.LiveServerMessage]:
|
404
|
-
"""
|
374
|
+
"""[Deprecated] Start a live session from a data stream.
|
375
|
+
|
376
|
+
> **Warning**: This method is deprecated and will be removed in a future
|
377
|
+
version (not before Q2 2025). Please use one of the more specific methods:
|
378
|
+
`send_client_content`, `send_realtime_input`, or `send_tool_response`
|
379
|
+
instead.
|
405
380
|
|
406
381
|
The interaction terminates when the input stream is complete.
|
407
382
|
This method will start two async tasks. One task will be used to send the
|
408
383
|
input stream to the model and the other task will be used to receive the
|
409
384
|
responses from the model.
|
410
385
|
|
411
|
-
The live module is experimental.
|
412
|
-
|
413
386
|
Args:
|
414
387
|
stream: An iterator that yields the model response.
|
415
388
|
mime_type: The MIME type of the data in the stream.
|
@@ -432,6 +405,13 @@ class AsyncSession:
|
|
432
405
|
mime_type = 'audio/pcm'):
|
433
406
|
play_audio_chunk(audio.data)
|
434
407
|
"""
|
408
|
+
warnings.warn(
|
409
|
+
'Setting `AsyncSession.start_stream` is deprecated, '
|
410
|
+
'and will be removed in a future release (not before Q3 2025). '
|
411
|
+
'Please use the `receive`, and `send_realtime_input`, methods instead.',
|
412
|
+
DeprecationWarning,
|
413
|
+
stacklevel=4,
|
414
|
+
)
|
435
415
|
stop_event = asyncio.Event()
|
436
416
|
# Start the send loop. When stream is complete stop_event is set.
|
437
417
|
asyncio.create_task(self._send_loop(stream, mime_type, stop_event))
|
@@ -465,7 +445,7 @@ class AsyncSession:
|
|
465
445
|
try:
|
466
446
|
raw_response = await self._ws.recv(decode=False)
|
467
447
|
except TypeError:
|
468
|
-
raw_response = await self._ws.recv()
|
448
|
+
raw_response = await self._ws.recv() # type: ignore[assignment]
|
469
449
|
if raw_response:
|
470
450
|
try:
|
471
451
|
response = json.loads(raw_response)
|
@@ -473,10 +453,11 @@ class AsyncSession:
|
|
473
453
|
raise ValueError(f'Failed to parse response: {raw_response!r}')
|
474
454
|
else:
|
475
455
|
response = {}
|
456
|
+
|
476
457
|
if self._api_client.vertexai:
|
477
|
-
response_dict =
|
458
|
+
response_dict = live_converters._LiveServerMessage_from_vertex(self._api_client, response)
|
478
459
|
else:
|
479
|
-
response_dict =
|
460
|
+
response_dict = live_converters._LiveServerMessage_from_mldev(self._api_client, response)
|
480
461
|
|
481
462
|
return types.LiveServerMessage._from_response(
|
482
463
|
response=response_dict, kwargs=parameter_model.model_dump()
|
@@ -498,152 +479,6 @@ class AsyncSession:
|
|
498
479
|
# Give a chance for the receiver to process the last response.
|
499
480
|
stop_event.set()
|
500
481
|
|
501
|
-
def _LiveServerContent_from_mldev(
|
502
|
-
self,
|
503
|
-
from_object: Union[dict, object],
|
504
|
-
) -> Dict[str, Any]:
|
505
|
-
to_object: dict[str, Any] = {}
|
506
|
-
if getv(from_object, ['modelTurn']) is not None:
|
507
|
-
setv(
|
508
|
-
to_object,
|
509
|
-
['model_turn'],
|
510
|
-
_Content_from_mldev(
|
511
|
-
self._api_client,
|
512
|
-
getv(from_object, ['modelTurn']),
|
513
|
-
),
|
514
|
-
)
|
515
|
-
if getv(from_object, ['turnComplete']) is not None:
|
516
|
-
setv(to_object, ['turn_complete'], getv(from_object, ['turnComplete']))
|
517
|
-
if getv(from_object, ['interrupted']) is not None:
|
518
|
-
setv(to_object, ['interrupted'], getv(from_object, ['interrupted']))
|
519
|
-
if getv(from_object, ['generationComplete']) is not None:
|
520
|
-
setv(
|
521
|
-
to_object,
|
522
|
-
['generation_complete'],
|
523
|
-
getv(from_object, ['generationComplete']),
|
524
|
-
)
|
525
|
-
return to_object
|
526
|
-
|
527
|
-
def _LiveToolCall_from_mldev(
|
528
|
-
self,
|
529
|
-
from_object: Union[dict, object],
|
530
|
-
) -> Dict[str, Any]:
|
531
|
-
to_object: dict[str, Any] = {}
|
532
|
-
if getv(from_object, ['functionCalls']) is not None:
|
533
|
-
setv(
|
534
|
-
to_object,
|
535
|
-
['function_calls'],
|
536
|
-
getv(from_object, ['functionCalls']),
|
537
|
-
)
|
538
|
-
return to_object
|
539
|
-
|
540
|
-
def _LiveToolCall_from_vertex(
|
541
|
-
self,
|
542
|
-
from_object: Union[dict, object],
|
543
|
-
) -> Dict[str, Any]:
|
544
|
-
to_object: dict[str, Any] = {}
|
545
|
-
if getv(from_object, ['functionCalls']) is not None:
|
546
|
-
setv(
|
547
|
-
to_object,
|
548
|
-
['function_calls'],
|
549
|
-
getv(from_object, ['functionCalls']),
|
550
|
-
)
|
551
|
-
return to_object
|
552
|
-
|
553
|
-
def _LiveServerMessage_from_mldev(
|
554
|
-
self,
|
555
|
-
from_object: Union[dict, object],
|
556
|
-
) -> Dict[str, Any]:
|
557
|
-
to_object: dict[str, Any] = {}
|
558
|
-
if getv(from_object, ['serverContent']) is not None:
|
559
|
-
setv(
|
560
|
-
to_object,
|
561
|
-
['server_content'],
|
562
|
-
self._LiveServerContent_from_mldev(
|
563
|
-
getv(from_object, ['serverContent'])
|
564
|
-
),
|
565
|
-
)
|
566
|
-
if getv(from_object, ['toolCall']) is not None:
|
567
|
-
setv(
|
568
|
-
to_object,
|
569
|
-
['tool_call'],
|
570
|
-
self._LiveToolCall_from_mldev(getv(from_object, ['toolCall'])),
|
571
|
-
)
|
572
|
-
if getv(from_object, ['toolCallCancellation']) is not None:
|
573
|
-
setv(
|
574
|
-
to_object,
|
575
|
-
['tool_call_cancellation'],
|
576
|
-
getv(from_object, ['toolCallCancellation']),
|
577
|
-
)
|
578
|
-
return to_object
|
579
|
-
|
580
|
-
def _LiveServerContent_from_vertex(
|
581
|
-
self,
|
582
|
-
from_object: Union[dict, object],
|
583
|
-
) -> Dict[str, Any]:
|
584
|
-
to_object: dict[str, Any] = {}
|
585
|
-
if getv(from_object, ['modelTurn']) is not None:
|
586
|
-
setv(
|
587
|
-
to_object,
|
588
|
-
['model_turn'],
|
589
|
-
_Content_from_vertex(
|
590
|
-
self._api_client,
|
591
|
-
getv(from_object, ['modelTurn']),
|
592
|
-
),
|
593
|
-
)
|
594
|
-
if getv(from_object, ['turnComplete']) is not None:
|
595
|
-
setv(to_object, ['turn_complete'], getv(from_object, ['turnComplete']))
|
596
|
-
if getv(from_object, ['generationComplete']) is not None:
|
597
|
-
setv(
|
598
|
-
to_object,
|
599
|
-
['generation_complete'],
|
600
|
-
getv(from_object, ['generationComplete']),
|
601
|
-
)
|
602
|
-
# Vertex supports transcription.
|
603
|
-
if getv(from_object, ['inputTranscription']) is not None:
|
604
|
-
setv(
|
605
|
-
to_object,
|
606
|
-
['input_transcription'],
|
607
|
-
getv(from_object, ['inputTranscription']),
|
608
|
-
)
|
609
|
-
if getv(from_object, ['outputTranscription']) is not None:
|
610
|
-
setv(
|
611
|
-
to_object,
|
612
|
-
['output_transcription'],
|
613
|
-
getv(from_object, ['outputTranscription']),
|
614
|
-
)
|
615
|
-
if getv(from_object, ['interrupted']) is not None:
|
616
|
-
setv(to_object, ['interrupted'], getv(from_object, ['interrupted']))
|
617
|
-
return to_object
|
618
|
-
|
619
|
-
def _LiveServerMessage_from_vertex(
|
620
|
-
self,
|
621
|
-
from_object: Union[dict, object],
|
622
|
-
) -> Dict[str, Any]:
|
623
|
-
to_object: dict[str, Any] = {}
|
624
|
-
if getv(from_object, ['serverContent']) is not None:
|
625
|
-
setv(
|
626
|
-
to_object,
|
627
|
-
['server_content'],
|
628
|
-
self._LiveServerContent_from_vertex(
|
629
|
-
getv(from_object, ['serverContent'])
|
630
|
-
),
|
631
|
-
)
|
632
|
-
|
633
|
-
if getv(from_object, ['toolCall']) is not None:
|
634
|
-
setv(
|
635
|
-
to_object,
|
636
|
-
['tool_call'],
|
637
|
-
self._LiveToolCall_from_vertex(getv(from_object, ['toolCall'])),
|
638
|
-
)
|
639
|
-
if getv(from_object, ['toolCallCancellation']) is not None:
|
640
|
-
setv(
|
641
|
-
to_object,
|
642
|
-
['tool_call_cancellation'],
|
643
|
-
getv(from_object, ['toolCallCancellation']),
|
644
|
-
)
|
645
|
-
return to_object
|
646
|
-
|
647
482
|
def _parse_client_message(
|
648
483
|
self,
|
649
484
|
input: Optional[
|
@@ -947,325 +782,16 @@ class AsyncSession:
|
|
947
782
|
|
948
783
|
return client_message
|
949
784
|
|
950
|
-
async def close(self):
|
785
|
+
async def close(self) -> None:
|
951
786
|
# Close the websocket connection.
|
952
787
|
await self._ws.close()
|
953
788
|
|
954
789
|
|
955
|
-
def _t_content_strict(content: types.ContentOrDict):
|
956
|
-
if isinstance(content, dict):
|
957
|
-
return types.Content.model_validate(content)
|
958
|
-
elif isinstance(content, types.Content):
|
959
|
-
return content
|
960
|
-
else:
|
961
|
-
raise ValueError(
|
962
|
-
f'Could not convert input (type "{type(content)}") to '
|
963
|
-
'`types.Content`'
|
964
|
-
)
|
965
|
-
|
966
|
-
|
967
|
-
def _t_contents_strict(
|
968
|
-
contents: Union[Sequence[types.ContentOrDict], types.ContentOrDict]):
|
969
|
-
if isinstance(contents, Sequence):
|
970
|
-
return [_t_content_strict(content) for content in contents]
|
971
|
-
else:
|
972
|
-
return [_t_content_strict(contents)]
|
973
|
-
|
974
|
-
|
975
|
-
def _t_client_content(
|
976
|
-
turns: Optional[
|
977
|
-
Union[Sequence[types.ContentOrDict], types.ContentOrDict]
|
978
|
-
] = None,
|
979
|
-
turn_complete: bool = True,
|
980
|
-
) -> types.LiveClientContent:
|
981
|
-
if turns is None:
|
982
|
-
return types.LiveClientContent(turn_complete=turn_complete)
|
983
|
-
|
984
|
-
try:
|
985
|
-
return types.LiveClientContent(
|
986
|
-
turns=_t_contents_strict(contents=turns),
|
987
|
-
turn_complete=turn_complete,
|
988
|
-
)
|
989
|
-
except Exception as e:
|
990
|
-
raise ValueError(
|
991
|
-
f'Could not convert input (type "{type(turns)}") to '
|
992
|
-
'`types.LiveClientContent`'
|
993
|
-
) from e
|
994
|
-
|
995
|
-
|
996
|
-
def _t_realtime_input(
|
997
|
-
media: t.BlobUnion,
|
998
|
-
) -> types.LiveClientRealtimeInput:
|
999
|
-
try:
|
1000
|
-
return types.LiveClientRealtimeInput(media_chunks=[t.t_blob(blob=media)])
|
1001
|
-
except Exception as e:
|
1002
|
-
raise ValueError(
|
1003
|
-
f'Could not convert input (type "{type(input)}") to '
|
1004
|
-
'`types.LiveClientRealtimeInput`'
|
1005
|
-
) from e
|
1006
|
-
|
1007
|
-
|
1008
|
-
def _t_tool_response(
|
1009
|
-
input: Union[
|
1010
|
-
types.FunctionResponseOrDict,
|
1011
|
-
Sequence[types.FunctionResponseOrDict],
|
1012
|
-
],
|
1013
|
-
) -> types.LiveClientToolResponse:
|
1014
|
-
if not input:
|
1015
|
-
raise ValueError(f'A tool response is required, got: \n{input}')
|
1016
|
-
|
1017
|
-
try:
|
1018
|
-
return types.LiveClientToolResponse(
|
1019
|
-
function_responses=t.t_function_responses(function_responses=input)
|
1020
|
-
)
|
1021
|
-
except Exception as e:
|
1022
|
-
raise ValueError(
|
1023
|
-
f'Could not convert input (type "{type(input)}") to '
|
1024
|
-
'`types.LiveClientToolResponse`'
|
1025
|
-
) from e
|
1026
|
-
|
1027
790
|
|
1028
791
|
class AsyncLive(_api_module.BaseModule):
|
1029
|
-
"""AsyncLive.
|
792
|
+
"""[Preview] AsyncLive."""
|
1030
793
|
|
1031
|
-
def _LiveSetup_to_mldev(
|
1032
|
-
self, model: str, config: Optional[types.LiveConnectConfig] = None
|
1033
|
-
):
|
1034
794
|
|
1035
|
-
to_object: dict[str, Any] = {}
|
1036
|
-
if getv(config, ['generation_config']) is not None:
|
1037
|
-
setv(
|
1038
|
-
to_object,
|
1039
|
-
['generationConfig'],
|
1040
|
-
_GenerateContentConfig_to_mldev(
|
1041
|
-
self._api_client,
|
1042
|
-
getv(config, ['generation_config']),
|
1043
|
-
to_object,
|
1044
|
-
),
|
1045
|
-
)
|
1046
|
-
if getv(config, ['response_modalities']) is not None:
|
1047
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1048
|
-
to_object['generationConfig']['responseModalities'] = getv(
|
1049
|
-
config, ['response_modalities']
|
1050
|
-
)
|
1051
|
-
else:
|
1052
|
-
to_object['generationConfig'] = {
|
1053
|
-
'responseModalities': getv(config, ['response_modalities'])
|
1054
|
-
}
|
1055
|
-
if getv(config, ['speech_config']) is not None:
|
1056
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1057
|
-
to_object['generationConfig']['speechConfig'] = _SpeechConfig_to_mldev(
|
1058
|
-
self._api_client,
|
1059
|
-
t.t_speech_config(
|
1060
|
-
self._api_client, getv(config, ['speech_config'])
|
1061
|
-
),
|
1062
|
-
to_object,
|
1063
|
-
)
|
1064
|
-
else:
|
1065
|
-
to_object['generationConfig'] = {
|
1066
|
-
'speechConfig': _SpeechConfig_to_mldev(
|
1067
|
-
self._api_client,
|
1068
|
-
t.t_speech_config(
|
1069
|
-
self._api_client, getv(config, ['speech_config'])
|
1070
|
-
),
|
1071
|
-
to_object,
|
1072
|
-
)
|
1073
|
-
}
|
1074
|
-
if getv(config, ['temperature']) is not None:
|
1075
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1076
|
-
to_object['generationConfig']['temperature'] = getv(
|
1077
|
-
config, ['temperature']
|
1078
|
-
)
|
1079
|
-
else:
|
1080
|
-
to_object['generationConfig'] = {
|
1081
|
-
'temperature': getv(config, ['temperature'])
|
1082
|
-
}
|
1083
|
-
if getv(config, ['top_p']) is not None:
|
1084
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1085
|
-
to_object['generationConfig']['topP'] = getv(config, ['top_p'])
|
1086
|
-
else:
|
1087
|
-
to_object['generationConfig'] = {'topP': getv(config, ['top_p'])}
|
1088
|
-
if getv(config, ['top_k']) is not None:
|
1089
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1090
|
-
to_object['generationConfig']['topK'] = getv(config, ['top_k'])
|
1091
|
-
else:
|
1092
|
-
to_object['generationConfig'] = {'topK': getv(config, ['top_k'])}
|
1093
|
-
if getv(config, ['max_output_tokens']) is not None:
|
1094
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1095
|
-
to_object['generationConfig']['maxOutputTokens'] = getv(
|
1096
|
-
config, ['max_output_tokens']
|
1097
|
-
)
|
1098
|
-
else:
|
1099
|
-
to_object['generationConfig'] = {
|
1100
|
-
'maxOutputTokens': getv(config, ['max_output_tokens'])
|
1101
|
-
}
|
1102
|
-
if getv(config, ['seed']) is not None:
|
1103
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1104
|
-
to_object['generationConfig']['seed'] = getv(config, ['seed'])
|
1105
|
-
else:
|
1106
|
-
to_object['generationConfig'] = {'seed': getv(config, ['seed'])}
|
1107
|
-
if getv(config, ['system_instruction']) is not None:
|
1108
|
-
setv(
|
1109
|
-
to_object,
|
1110
|
-
['systemInstruction'],
|
1111
|
-
_Content_to_mldev(
|
1112
|
-
self._api_client,
|
1113
|
-
t.t_content(
|
1114
|
-
self._api_client, getv(config, ['system_instruction'])
|
1115
|
-
),
|
1116
|
-
to_object,
|
1117
|
-
),
|
1118
|
-
)
|
1119
|
-
if getv(config, ['tools']) is not None:
|
1120
|
-
setv(
|
1121
|
-
to_object,
|
1122
|
-
['tools'],
|
1123
|
-
[
|
1124
|
-
_Tool_to_mldev(
|
1125
|
-
self._api_client, t.t_tool(self._api_client, item), to_object
|
1126
|
-
)
|
1127
|
-
for item in t.t_tools(self._api_client, getv(config, ['tools']))
|
1128
|
-
],
|
1129
|
-
)
|
1130
|
-
|
1131
|
-
return_value = {'setup': {'model': model}}
|
1132
|
-
return_value['setup'].update(to_object)
|
1133
|
-
return return_value
|
1134
|
-
|
1135
|
-
def _LiveSetup_to_vertex(
|
1136
|
-
self, model: str, config: Optional[types.LiveConnectConfig] = None
|
1137
|
-
):
|
1138
|
-
|
1139
|
-
to_object: dict[str, Any] = {}
|
1140
|
-
|
1141
|
-
if getv(config, ['generation_config']) is not None:
|
1142
|
-
setv(
|
1143
|
-
to_object,
|
1144
|
-
['generationConfig'],
|
1145
|
-
_GenerateContentConfig_to_vertex(
|
1146
|
-
self._api_client,
|
1147
|
-
getv(config, ['generation_config']),
|
1148
|
-
to_object,
|
1149
|
-
),
|
1150
|
-
)
|
1151
|
-
if getv(config, ['response_modalities']) is not None:
|
1152
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1153
|
-
to_object['generationConfig']['responseModalities'] = getv(
|
1154
|
-
config, ['response_modalities']
|
1155
|
-
)
|
1156
|
-
else:
|
1157
|
-
to_object['generationConfig'] = {
|
1158
|
-
'responseModalities': getv(config, ['response_modalities'])
|
1159
|
-
}
|
1160
|
-
else:
|
1161
|
-
# Set default to AUDIO to align with MLDev API.
|
1162
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1163
|
-
to_object['generationConfig'].update({'responseModalities': ['AUDIO']})
|
1164
|
-
else:
|
1165
|
-
to_object.update(
|
1166
|
-
{'generationConfig': {'responseModalities': ['AUDIO']}}
|
1167
|
-
)
|
1168
|
-
if getv(config, ['speech_config']) is not None:
|
1169
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1170
|
-
to_object['generationConfig']['speechConfig'] = _SpeechConfig_to_vertex(
|
1171
|
-
self._api_client,
|
1172
|
-
t.t_speech_config(
|
1173
|
-
self._api_client, getv(config, ['speech_config'])
|
1174
|
-
),
|
1175
|
-
to_object,
|
1176
|
-
)
|
1177
|
-
else:
|
1178
|
-
to_object['generationConfig'] = {
|
1179
|
-
'speechConfig': _SpeechConfig_to_vertex(
|
1180
|
-
self._api_client,
|
1181
|
-
t.t_speech_config(
|
1182
|
-
self._api_client, getv(config, ['speech_config'])
|
1183
|
-
),
|
1184
|
-
to_object,
|
1185
|
-
)
|
1186
|
-
}
|
1187
|
-
if getv(config, ['temperature']) is not None:
|
1188
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1189
|
-
to_object['generationConfig']['temperature'] = getv(
|
1190
|
-
config, ['temperature']
|
1191
|
-
)
|
1192
|
-
else:
|
1193
|
-
to_object['generationConfig'] = {
|
1194
|
-
'temperature': getv(config, ['temperature'])
|
1195
|
-
}
|
1196
|
-
if getv(config, ['top_p']) is not None:
|
1197
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1198
|
-
to_object['generationConfig']['topP'] = getv(config, ['top_p'])
|
1199
|
-
else:
|
1200
|
-
to_object['generationConfig'] = {'topP': getv(config, ['top_p'])}
|
1201
|
-
if getv(config, ['top_k']) is not None:
|
1202
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1203
|
-
to_object['generationConfig']['topK'] = getv(config, ['top_k'])
|
1204
|
-
else:
|
1205
|
-
to_object['generationConfig'] = {'topK': getv(config, ['top_k'])}
|
1206
|
-
if getv(config, ['max_output_tokens']) is not None:
|
1207
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1208
|
-
to_object['generationConfig']['maxOutputTokens'] = getv(
|
1209
|
-
config, ['max_output_tokens']
|
1210
|
-
)
|
1211
|
-
else:
|
1212
|
-
to_object['generationConfig'] = {
|
1213
|
-
'maxOutputTokens': getv(config, ['max_output_tokens'])
|
1214
|
-
}
|
1215
|
-
if getv(config, ['seed']) is not None:
|
1216
|
-
if getv(to_object, ['generationConfig']) is not None:
|
1217
|
-
to_object['generationConfig']['seed'] = getv(config, ['seed'])
|
1218
|
-
else:
|
1219
|
-
to_object['generationConfig'] = {'seed': getv(config, ['seed'])}
|
1220
|
-
if getv(config, ['system_instruction']) is not None:
|
1221
|
-
setv(
|
1222
|
-
to_object,
|
1223
|
-
['systemInstruction'],
|
1224
|
-
_Content_to_vertex(
|
1225
|
-
self._api_client,
|
1226
|
-
t.t_content(
|
1227
|
-
self._api_client, getv(config, ['system_instruction'])
|
1228
|
-
),
|
1229
|
-
to_object,
|
1230
|
-
),
|
1231
|
-
)
|
1232
|
-
if getv(config, ['tools']) is not None:
|
1233
|
-
setv(
|
1234
|
-
to_object,
|
1235
|
-
['tools'],
|
1236
|
-
[
|
1237
|
-
_Tool_to_vertex(
|
1238
|
-
self._api_client, t.t_tool(self._api_client, item), to_object
|
1239
|
-
)
|
1240
|
-
for item in t.t_tools(self._api_client, getv(config, ['tools']))
|
1241
|
-
],
|
1242
|
-
)
|
1243
|
-
if getv(config, ['input_audio_transcription']) is not None:
|
1244
|
-
setv(
|
1245
|
-
to_object,
|
1246
|
-
['inputAudioTranscription'],
|
1247
|
-
_AudioTranscriptionConfig_to_vertex(
|
1248
|
-
self._api_client,
|
1249
|
-
getv(config, ['input_audio_transcription']),
|
1250
|
-
),
|
1251
|
-
)
|
1252
|
-
if getv(config, ['output_audio_transcription']) is not None:
|
1253
|
-
setv(
|
1254
|
-
to_object,
|
1255
|
-
['outputAudioTranscription'],
|
1256
|
-
_AudioTranscriptionConfig_to_vertex(
|
1257
|
-
self._api_client,
|
1258
|
-
getv(config, ['output_audio_transcription']),
|
1259
|
-
),
|
1260
|
-
)
|
1261
|
-
|
1262
|
-
return_value = {'setup': {'model': model}}
|
1263
|
-
return_value['setup'].update(to_object)
|
1264
|
-
return return_value
|
1265
|
-
|
1266
|
-
@experimental_warning(
|
1267
|
-
'The live API is experimental and may change in future versions.',
|
1268
|
-
)
|
1269
795
|
@contextlib.asynccontextmanager
|
1270
796
|
async def connect(
|
1271
797
|
self,
|
@@ -1273,9 +799,9 @@ class AsyncLive(_api_module.BaseModule):
|
|
1273
799
|
model: str,
|
1274
800
|
config: Optional[types.LiveConnectConfigOrDict] = None,
|
1275
801
|
) -> AsyncIterator[AsyncSession]:
|
1276
|
-
"""Connect to the live server.
|
802
|
+
"""[Preview] Connect to the live server.
|
1277
803
|
|
1278
|
-
|
804
|
+
Note: the live API is currently in preview.
|
1279
805
|
|
1280
806
|
Usage:
|
1281
807
|
|
@@ -1290,44 +816,28 @@ class AsyncLive(_api_module.BaseModule):
|
|
1290
816
|
"""
|
1291
817
|
base_url = self._api_client._websocket_base_url()
|
1292
818
|
transformed_model = t.t_model(self._api_client, model)
|
1293
|
-
|
1294
|
-
|
1295
|
-
parameter_model = types.LiveConnectConfig()
|
1296
|
-
elif isinstance(config, dict):
|
1297
|
-
if config.get('system_instruction') is None:
|
1298
|
-
system_instruction = None
|
1299
|
-
else:
|
1300
|
-
system_instruction = t.t_content(
|
1301
|
-
self._api_client, config.get('system_instruction')
|
1302
|
-
)
|
1303
|
-
parameter_model = types.LiveConnectConfig(
|
1304
|
-
generation_config=config.get('generation_config'),
|
1305
|
-
response_modalities=config.get('response_modalities'),
|
1306
|
-
speech_config=config.get('speech_config'),
|
1307
|
-
temperature=config.get('temperature'),
|
1308
|
-
top_p=config.get('top_p'),
|
1309
|
-
top_k=config.get('top_k'),
|
1310
|
-
max_output_tokens=config.get('max_output_tokens'),
|
1311
|
-
seed=config.get('seed'),
|
1312
|
-
system_instruction=system_instruction,
|
1313
|
-
tools=config.get('tools'),
|
1314
|
-
input_audio_transcription=config.get('input_audio_transcription'),
|
1315
|
-
output_audio_transcription=config.get('output_audio_transcription'),
|
1316
|
-
)
|
1317
|
-
else:
|
1318
|
-
parameter_model = config
|
819
|
+
|
820
|
+
parameter_model = _t_live_connect_config(self._api_client, config)
|
1319
821
|
|
1320
822
|
if self._api_client.api_key:
|
1321
823
|
api_key = self._api_client.api_key
|
1322
824
|
version = self._api_client._http_options.api_version
|
1323
825
|
uri = f'{base_url}/ws/google.ai.generativelanguage.{version}.GenerativeService.BidiGenerateContent?key={api_key}'
|
1324
826
|
headers = self._api_client._http_options.headers
|
827
|
+
|
1325
828
|
request_dict = _common.convert_to_dict(
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
829
|
+
live_converters._LiveConnectParameters_to_mldev(
|
830
|
+
api_client=self._api_client,
|
831
|
+
from_object=types.LiveConnectParameters(
|
832
|
+
model=transformed_model,
|
833
|
+
config=parameter_model,
|
834
|
+
).model_dump(exclude_none=True)
|
1329
835
|
)
|
1330
836
|
)
|
837
|
+
del request_dict['config']
|
838
|
+
|
839
|
+
setv(request_dict, ['setup', 'model'], transformed_model)
|
840
|
+
|
1331
841
|
request = json.dumps(request_dict)
|
1332
842
|
else:
|
1333
843
|
# Get bearer token through Application Default Credentials.
|
@@ -1354,11 +864,19 @@ class AsyncLive(_api_module.BaseModule):
|
|
1354
864
|
f'projects/{project}/locations/{location}/' + transformed_model
|
1355
865
|
)
|
1356
866
|
request_dict = _common.convert_to_dict(
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
867
|
+
live_converters._LiveConnectParameters_to_vertex(
|
868
|
+
api_client=self._api_client,
|
869
|
+
from_object=types.LiveConnectParameters(
|
870
|
+
model=transformed_model,
|
871
|
+
config=parameter_model,
|
872
|
+
).model_dump(exclude_none=True)
|
1360
873
|
)
|
1361
874
|
)
|
875
|
+
del request_dict['config']
|
876
|
+
|
877
|
+
if getv(request_dict, ['setup', 'generationConfig', 'responseModalities']) is None:
|
878
|
+
setv(request_dict, ['setup', 'generationConfig', 'responseModalities'], ['AUDIO'])
|
879
|
+
|
1362
880
|
request = json.dumps(request_dict)
|
1363
881
|
|
1364
882
|
try:
|
@@ -1374,3 +892,41 @@ class AsyncLive(_api_module.BaseModule):
|
|
1374
892
|
logger.info(await ws.recv())
|
1375
893
|
|
1376
894
|
yield AsyncSession(api_client=self._api_client, websocket=ws)
|
895
|
+
|
896
|
+
|
897
|
+
def _t_live_connect_config(
|
898
|
+
api_client: BaseApiClient,
|
899
|
+
config: Optional[types.LiveConnectConfigOrDict],
|
900
|
+
) -> types.LiveConnectConfig:
|
901
|
+
# Ensure the config is a LiveConnectConfig.
|
902
|
+
if config is None:
|
903
|
+
parameter_model = types.LiveConnectConfig()
|
904
|
+
elif isinstance(config, dict):
|
905
|
+
if getv(config, ['system_instruction']) is not None:
|
906
|
+
converted_system_instruction = t.t_content(
|
907
|
+
api_client, getv(config, ['system_instruction'])
|
908
|
+
)
|
909
|
+
else:
|
910
|
+
converted_system_instruction = None
|
911
|
+
parameter_model = types.LiveConnectConfig(**config)
|
912
|
+
parameter_model.system_instruction = converted_system_instruction
|
913
|
+
else:
|
914
|
+
if config.system_instruction is None:
|
915
|
+
system_instruction = None
|
916
|
+
else:
|
917
|
+
system_instruction = t.t_content(
|
918
|
+
api_client, getv(config, ['system_instruction'])
|
919
|
+
)
|
920
|
+
parameter_model = config
|
921
|
+
parameter_model.system_instruction = system_instruction
|
922
|
+
|
923
|
+
if parameter_model.generation_config is not None:
|
924
|
+
warnings.warn(
|
925
|
+
'Setting `LiveConnectConfig.generation_config` is deprecated, '
|
926
|
+
'please set the fields on `LiveConnectConfig` directly. This will '
|
927
|
+
'become an error in a future version (not before Q3 2025)',
|
928
|
+
DeprecationWarning,
|
929
|
+
stacklevel=4,
|
930
|
+
)
|
931
|
+
|
932
|
+
return parameter_model
|