livekit-plugins-google 1.0.0rc6__py3-none-any.whl → 1.0.0rc8__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.
- livekit/plugins/google/llm.py +18 -10
- livekit/plugins/google/tts.py +13 -4
- livekit/plugins/google/utils.py +18 -7
- livekit/plugins/google/version.py +1 -1
- {livekit_plugins_google-1.0.0rc6.dist-info → livekit_plugins_google-1.0.0rc8.dist-info}/METADATA +2 -2
- {livekit_plugins_google-1.0.0rc6.dist-info → livekit_plugins_google-1.0.0rc8.dist-info}/RECORD +7 -7
- {livekit_plugins_google-1.0.0rc6.dist-info → livekit_plugins_google-1.0.0rc8.dist-info}/WHEEL +0 -0
livekit/plugins/google/llm.py
CHANGED
@@ -25,7 +25,7 @@ from google.auth._default_async import default_async
|
|
25
25
|
from google.genai import types
|
26
26
|
from google.genai.errors import APIError, ClientError, ServerError
|
27
27
|
from livekit.agents import APIConnectionError, APIStatusError, llm, utils
|
28
|
-
from livekit.agents.llm import FunctionTool, ToolChoice
|
28
|
+
from livekit.agents.llm import FunctionTool, ToolChoice, utils as llm_utils
|
29
29
|
from livekit.agents.types import (
|
30
30
|
DEFAULT_API_CONNECT_OPTIONS,
|
31
31
|
NOT_GIVEN,
|
@@ -36,7 +36,7 @@ from livekit.agents.utils import is_given
|
|
36
36
|
|
37
37
|
from .log import logger
|
38
38
|
from .models import ChatModels
|
39
|
-
from .utils import to_chat_ctx, to_fnc_ctx
|
39
|
+
from .utils import to_chat_ctx, to_fnc_ctx, to_response_format
|
40
40
|
|
41
41
|
|
42
42
|
@dataclass
|
@@ -148,6 +148,9 @@ class LLM(llm.LLM):
|
|
148
148
|
conn_options: APIConnectOptions = DEFAULT_API_CONNECT_OPTIONS,
|
149
149
|
parallel_tool_calls: NotGivenOr[bool] = NOT_GIVEN,
|
150
150
|
tool_choice: NotGivenOr[ToolChoice] = NOT_GIVEN,
|
151
|
+
response_format: NotGivenOr[
|
152
|
+
types.SchemaUnion | type[llm_utils.ResponseFormatT]
|
153
|
+
] = NOT_GIVEN,
|
151
154
|
extra_kwargs: NotGivenOr[dict[str, Any]] = NOT_GIVEN,
|
152
155
|
) -> LLMStream:
|
153
156
|
extra = {}
|
@@ -189,6 +192,10 @@ class LLM(llm.LLM):
|
|
189
192
|
)
|
190
193
|
extra["tool_config"] = gemini_tool_choice
|
191
194
|
|
195
|
+
if is_given(response_format):
|
196
|
+
extra["response_schema"] = to_response_format(response_format)
|
197
|
+
extra["response_mime_type"] = "application/json"
|
198
|
+
|
192
199
|
if is_given(self._opts.temperature):
|
193
200
|
extra["temperature"] = self._opts.temperature
|
194
201
|
if is_given(self._opts.max_output_tokens):
|
@@ -237,10 +244,11 @@ class LLMStream(llm.LLMStream):
|
|
237
244
|
|
238
245
|
try:
|
239
246
|
turns, system_instruction = to_chat_ctx(self._chat_ctx, id(self._llm))
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
247
|
+
function_declarations = to_fnc_ctx(self._tools)
|
248
|
+
if function_declarations:
|
249
|
+
self._extra_kwargs["tools"] = [
|
250
|
+
types.Tool(function_declarations=function_declarations)
|
251
|
+
]
|
244
252
|
config = types.GenerateContentConfig(
|
245
253
|
system_instruction=system_instruction,
|
246
254
|
**self._extra_kwargs,
|
@@ -299,7 +307,7 @@ class LLMStream(llm.LLMStream):
|
|
299
307
|
raise APIStatusError(
|
300
308
|
"gemini llm: client error",
|
301
309
|
status_code=e.code,
|
302
|
-
body=e.message,
|
310
|
+
body=e.message + e.status,
|
303
311
|
request_id=request_id,
|
304
312
|
retryable=False if e.code != 429 else True,
|
305
313
|
) from e
|
@@ -307,7 +315,7 @@ class LLMStream(llm.LLMStream):
|
|
307
315
|
raise APIStatusError(
|
308
316
|
"gemini llm: server error",
|
309
317
|
status_code=e.code,
|
310
|
-
body=e.message,
|
318
|
+
body=e.message + e.status,
|
311
319
|
request_id=request_id,
|
312
320
|
retryable=retryable,
|
313
321
|
) from e
|
@@ -315,13 +323,13 @@ class LLMStream(llm.LLMStream):
|
|
315
323
|
raise APIStatusError(
|
316
324
|
"gemini llm: api error",
|
317
325
|
status_code=e.code,
|
318
|
-
body=e.message,
|
326
|
+
body=e.message + e.status,
|
319
327
|
request_id=request_id,
|
320
328
|
retryable=retryable,
|
321
329
|
) from e
|
322
330
|
except Exception as e:
|
323
331
|
raise APIConnectionError(
|
324
|
-
"gemini llm: error generating content",
|
332
|
+
f"gemini llm: error generating content {str(e)}",
|
325
333
|
retryable=retryable,
|
326
334
|
) from e
|
327
335
|
|
livekit/plugins/google/tts.py
CHANGED
@@ -16,6 +16,7 @@ from __future__ import annotations
|
|
16
16
|
|
17
17
|
from dataclasses import dataclass
|
18
18
|
|
19
|
+
from google.api_core.client_options import ClientOptions
|
19
20
|
from google.api_core.exceptions import DeadlineExceeded, GoogleAPICallError
|
20
21
|
from google.cloud import texttospeech
|
21
22
|
from google.cloud.texttospeech_v1.types import SsmlVoiceGender, SynthesizeSpeechResponse
|
@@ -50,6 +51,7 @@ class TTS(tts.TTS):
|
|
50
51
|
pitch: int = 0,
|
51
52
|
effects_profile_id: str = "",
|
52
53
|
speaking_rate: float = 1.0,
|
54
|
+
location: str = "global",
|
53
55
|
credentials_info: NotGivenOr[dict] = NOT_GIVEN,
|
54
56
|
credentials_file: NotGivenOr[str] = NOT_GIVEN,
|
55
57
|
) -> None:
|
@@ -63,6 +65,7 @@ class TTS(tts.TTS):
|
|
63
65
|
Args:
|
64
66
|
voice (texttospeech.VoiceSelectionParams, optional): Voice selection parameters.
|
65
67
|
sample_rate (int, optional): Audio sample rate in Hz. Default is 24000.
|
68
|
+
location (str, optional): Location for the TTS client. Default is "global".
|
66
69
|
pitch (float, optional): Speaking pitch, ranging from -20.0 to 20.0 semitones relative to the original pitch. Default is 0.
|
67
70
|
effects_profile_id (str): Optional identifier for selecting audio effects profiles to apply to the synthesized speech.
|
68
71
|
speaking_rate (float, optional): Speed of speech. Default is 1.0.
|
@@ -81,7 +84,7 @@ class TTS(tts.TTS):
|
|
81
84
|
self._client: texttospeech.TextToSpeechAsyncClient | None = None
|
82
85
|
self._credentials_info = credentials_info
|
83
86
|
self._credentials_file = credentials_file
|
84
|
-
|
87
|
+
self._location = location
|
85
88
|
if not is_given(voice):
|
86
89
|
voice = texttospeech.VoiceSelectionParams(
|
87
90
|
name="",
|
@@ -119,18 +122,24 @@ class TTS(tts.TTS):
|
|
119
122
|
self._opts.audio_config.speaking_rate = speaking_rate
|
120
123
|
|
121
124
|
def _ensure_client(self) -> texttospeech.TextToSpeechAsyncClient:
|
125
|
+
api_endpoint = "texttospeech.googleapis.com"
|
126
|
+
if self._location != "global":
|
127
|
+
api_endpoint = f"{self._location}-texttospeech.googleapis.com"
|
128
|
+
|
122
129
|
if self._client is None:
|
123
130
|
if self._credentials_info:
|
124
131
|
self._client = texttospeech.TextToSpeechAsyncClient.from_service_account_info(
|
125
|
-
self._credentials_info
|
132
|
+
self._credentials_info, client_options=ClientOptions(api_endpoint=api_endpoint)
|
126
133
|
)
|
127
134
|
|
128
135
|
elif self._credentials_file:
|
129
136
|
self._client = texttospeech.TextToSpeechAsyncClient.from_service_account_file(
|
130
|
-
self._credentials_file
|
137
|
+
self._credentials_file, client_options=ClientOptions(api_endpoint=api_endpoint)
|
131
138
|
)
|
132
139
|
else:
|
133
|
-
self._client = texttospeech.TextToSpeechAsyncClient(
|
140
|
+
self._client = texttospeech.TextToSpeechAsyncClient(
|
141
|
+
client_options=ClientOptions(api_endpoint=api_endpoint)
|
142
|
+
)
|
134
143
|
|
135
144
|
assert self._client is not None
|
136
145
|
return self._client
|
livekit/plugins/google/utils.py
CHANGED
@@ -5,9 +5,11 @@ import re
|
|
5
5
|
from copy import deepcopy
|
6
6
|
from typing import Any
|
7
7
|
|
8
|
+
from pydantic import TypeAdapter
|
9
|
+
|
8
10
|
from google.genai import types
|
9
11
|
from livekit.agents import llm
|
10
|
-
from livekit.agents.llm import FunctionTool
|
12
|
+
from livekit.agents.llm import FunctionTool, utils as llm_utils
|
11
13
|
|
12
14
|
from .log import logger
|
13
15
|
|
@@ -78,6 +80,10 @@ def to_chat_ctx(
|
|
78
80
|
|
79
81
|
if current_role is not None and parts:
|
80
82
|
turns.append(types.Content(role=current_role, parts=parts))
|
83
|
+
|
84
|
+
if not turns:
|
85
|
+
# if no turns, add a user message with a placeholder
|
86
|
+
turns = [types.Content(role="user", parts=[types.Part(text=".")])]
|
81
87
|
return turns, system_instruction
|
82
88
|
|
83
89
|
|
@@ -105,6 +111,16 @@ def _build_gemini_fnc(function_tool: FunctionTool) -> types.FunctionDeclaration:
|
|
105
111
|
)
|
106
112
|
|
107
113
|
|
114
|
+
def to_response_format(response_format: type | dict) -> types.SchemaUnion:
|
115
|
+
_, json_schema_type = llm_utils.to_response_format_param(response_format)
|
116
|
+
if isinstance(json_schema_type, TypeAdapter):
|
117
|
+
schema = json_schema_type.json_schema()
|
118
|
+
else:
|
119
|
+
schema = json_schema_type.model_json_schema()
|
120
|
+
|
121
|
+
return _GeminiJsonSchema(schema).simplify()
|
122
|
+
|
123
|
+
|
108
124
|
class _GeminiJsonSchema:
|
109
125
|
"""
|
110
126
|
Transforms the JSON Schema from Pydantic to be suitable for Gemini.
|
@@ -136,6 +152,7 @@ class _GeminiJsonSchema:
|
|
136
152
|
def _simplify(self, schema: dict[str, Any], refs_stack: tuple[str, ...]) -> None:
|
137
153
|
schema.pop("title", None)
|
138
154
|
schema.pop("default", None)
|
155
|
+
schema.pop("additionalProperties", None)
|
139
156
|
if ref := schema.pop("$ref", None):
|
140
157
|
key = re.sub(r"^#/\$defs/", "", ref)
|
141
158
|
if key in refs_stack:
|
@@ -196,7 +213,6 @@ class _GeminiJsonSchema:
|
|
196
213
|
"maxItems": "max_items",
|
197
214
|
"minProperties": "min_properties",
|
198
215
|
"maxProperties": "max_properties",
|
199
|
-
"additionalProperties": "additional_properties",
|
200
216
|
}
|
201
217
|
|
202
218
|
for json_name, gemini_name in mappings.items():
|
@@ -204,11 +220,6 @@ class _GeminiJsonSchema:
|
|
204
220
|
schema[gemini_name] = schema.pop(json_name)
|
205
221
|
|
206
222
|
def _object(self, schema: dict[str, Any], refs_stack: tuple[str, ...]) -> None:
|
207
|
-
# Gemini doesn't support additionalProperties
|
208
|
-
ad_props = schema.pop("additional_properties", None)
|
209
|
-
if ad_props:
|
210
|
-
raise ValueError("Additional properties in JSON Schema are not supported by Gemini")
|
211
|
-
|
212
223
|
if properties := schema.get("properties"):
|
213
224
|
for value in properties.values():
|
214
225
|
self._simplify(value, refs_stack)
|
{livekit_plugins_google-1.0.0rc6.dist-info → livekit_plugins_google-1.0.0rc8.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: livekit-plugins-google
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.0rc8
|
4
4
|
Summary: Agent Framework plugin for services from Google Cloud
|
5
5
|
Project-URL: Documentation, https://docs.livekit.io
|
6
6
|
Project-URL: Website, https://livekit.io/
|
@@ -22,7 +22,7 @@ Requires-Dist: google-auth<3,>=2
|
|
22
22
|
Requires-Dist: google-cloud-speech<3,>=2
|
23
23
|
Requires-Dist: google-cloud-texttospeech<3,>=2
|
24
24
|
Requires-Dist: google-genai==1.5.0
|
25
|
-
Requires-Dist: livekit-agents>=1.0.0.
|
25
|
+
Requires-Dist: livekit-agents>=1.0.0.rc8
|
26
26
|
Description-Content-Type: text/markdown
|
27
27
|
|
28
28
|
# LiveKit Plugins Google
|
{livekit_plugins_google-1.0.0rc6.dist-info → livekit_plugins_google-1.0.0rc8.dist-info}/RECORD
RENAMED
@@ -1,18 +1,18 @@
|
|
1
1
|
livekit/plugins/google/__init__.py,sha256=e_kSlFNmKhyyeliz7f4WOKc_Y0-y39QjO5nCWuguhss,1171
|
2
|
-
livekit/plugins/google/llm.py,sha256=
|
2
|
+
livekit/plugins/google/llm.py,sha256=81LCCJPmpMOkApX0S0a-zu5xIvcm2Pk8lTTz-PoK5m0,14740
|
3
3
|
livekit/plugins/google/log.py,sha256=GI3YWN5YzrafnUccljzPRS_ZALkMNk1i21IRnTl2vNA,69
|
4
4
|
livekit/plugins/google/models.py,sha256=SGjAumdDK97NNLwMFcqZdKR68f1NoGB2Rk1UP2-imG0,1457
|
5
5
|
livekit/plugins/google/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
6
|
livekit/plugins/google/stt.py,sha256=fT5JtDM8ck2iMAzRvFKVeyT1oPt_R-bDkqiLa-ysikc,22539
|
7
|
-
livekit/plugins/google/tts.py,sha256=
|
8
|
-
livekit/plugins/google/utils.py,sha256=
|
9
|
-
livekit/plugins/google/version.py,sha256=
|
7
|
+
livekit/plugins/google/tts.py,sha256=aA3VuNaMcE6I1M43Sm-2mmvNyA9D2EyqfpyAporMUSg,8042
|
8
|
+
livekit/plugins/google/utils.py,sha256=dLkq-8lbWDC7AQ7nULd9unWwu_Wv9czdlxchyiJQ2KQ,8740
|
9
|
+
livekit/plugins/google/version.py,sha256=5qCM_eosxi507YOAQ3_Cf_-PVZbiE3NumVYwZ2yRcHQ,604
|
10
10
|
livekit/plugins/google/beta/__init__.py,sha256=AxRYc7NGG62Tv1MmcZVCDHNvlhbC86hM-_yP01Qb28k,47
|
11
11
|
livekit/plugins/google/beta/realtime/__init__.py,sha256=_fW2NMN22F-hnQ4xAJ_g5lPbR7CvM_xXzSWlUQY-E-U,188
|
12
12
|
livekit/plugins/google/beta/realtime/api_proto.py,sha256=VO6QqOGOrxzsaOLBqnwNd8c-BId0PjwKicdrPTJisy0,688
|
13
13
|
livekit/plugins/google/beta/realtime/realtime_api.py,sha256=ERM6WvcTtrfIyKpukzoSYrkhd3eYxIY-I09mKWp8vLk,22576
|
14
14
|
livekit/plugins/google/beta/realtime/temp.py,sha256=an_YueuS_tUw3_QC6xWkkcw5JrJOBQFv2pJh6atpNcc,108
|
15
15
|
livekit/plugins/google/beta/realtime/transcriber.py,sha256=DD7q894xc25GeeuKDar6-GwH-MxStEwhwBiX-KZ-Jo4,9559
|
16
|
-
livekit_plugins_google-1.0.
|
17
|
-
livekit_plugins_google-1.0.
|
18
|
-
livekit_plugins_google-1.0.
|
16
|
+
livekit_plugins_google-1.0.0rc8.dist-info/METADATA,sha256=eEzJyC5OWDJoXzs5SdI6HyRhYzVJhJtwttc7px5daX4,3496
|
17
|
+
livekit_plugins_google-1.0.0rc8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
18
|
+
livekit_plugins_google-1.0.0rc8.dist-info/RECORD,,
|
{livekit_plugins_google-1.0.0rc6.dist-info → livekit_plugins_google-1.0.0rc8.dist-info}/WHEEL
RENAMED
File without changes
|