livekit-plugins-volcenginee 1.3.4__tar.gz → 1.3.6__tar.gz
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_volcenginee-1.3.4 → livekit_plugins_volcenginee-1.3.6}/.gitignore +2 -0
- {livekit_plugins_volcenginee-1.3.4 → livekit_plugins_volcenginee-1.3.6}/PKG-INFO +1 -1
- {livekit_plugins_volcenginee-1.3.4 → livekit_plugins_volcenginee-1.3.6}/livekit/plugins/volcengine/llm.py +2 -2
- {livekit_plugins_volcenginee-1.3.4 → livekit_plugins_volcenginee-1.3.6}/livekit/plugins/volcengine/realtime.py +6 -3
- {livekit_plugins_volcenginee-1.3.4 → livekit_plugins_volcenginee-1.3.6}/livekit/plugins/volcengine/stt.py +9 -2
- {livekit_plugins_volcenginee-1.3.4 → livekit_plugins_volcenginee-1.3.6}/livekit/plugins/volcengine/tts.py +31 -4
- livekit_plugins_volcenginee-1.3.6/livekit/plugins/volcengine/utils.py +25 -0
- livekit_plugins_volcenginee-1.3.6/livekit/plugins/volcengine/version.py +1 -0
- livekit_plugins_volcenginee-1.3.4/livekit/plugins/volcengine/utils.py +0 -154
- livekit_plugins_volcenginee-1.3.4/livekit/plugins/volcengine/version.py +0 -1
- {livekit_plugins_volcenginee-1.3.4 → livekit_plugins_volcenginee-1.3.6}/README.md +0 -0
- {livekit_plugins_volcenginee-1.3.4 → livekit_plugins_volcenginee-1.3.6}/livekit/plugins/volcengine/__init__.py +0 -0
- {livekit_plugins_volcenginee-1.3.4 → livekit_plugins_volcenginee-1.3.6}/livekit/plugins/volcengine/log.py +0 -0
- {livekit_plugins_volcenginee-1.3.4 → livekit_plugins_volcenginee-1.3.6}/livekit/plugins/volcengine/py.typed +0 -0
- {livekit_plugins_volcenginee-1.3.4 → livekit_plugins_volcenginee-1.3.6}/pyproject.toml +0 -0
|
@@ -21,7 +21,7 @@ from livekit.agents.types import (
|
|
|
21
21
|
from livekit.agents.utils import is_given
|
|
22
22
|
|
|
23
23
|
from .log import logger
|
|
24
|
-
from .utils import
|
|
24
|
+
from .utils import to_fnc_ctx
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
@dataclass
|
|
@@ -206,7 +206,7 @@ class LLMStream(llm.LLMStream):
|
|
|
206
206
|
stream: openai.AsyncStream[
|
|
207
207
|
ChatCompletionChunk
|
|
208
208
|
] = await self._client.chat.completions.create(
|
|
209
|
-
messages=
|
|
209
|
+
messages=self._chat_ctx.to_provider_format(format="openai")[0],
|
|
210
210
|
tools=to_fnc_ctx(self._tools) if self._tools else openai.NOT_GIVEN,
|
|
211
211
|
model=self._model,
|
|
212
212
|
stream_options={"include_usage": True},
|
|
@@ -259,7 +259,7 @@ class RealtimeModel(llm.RealtimeModel):
|
|
|
259
259
|
speaking_style: str = "你的说话风格简洁明了,语速适中,语调自然。",
|
|
260
260
|
speaker: str = "zh_female_vv_jupiter_bigtts",
|
|
261
261
|
opening: str | None = None,
|
|
262
|
-
api_key: str
|
|
262
|
+
api_key: NotGivenOr[str] = NOT_GIVEN,
|
|
263
263
|
system_role: str | None = None,
|
|
264
264
|
character_manifest: str = None,
|
|
265
265
|
model: Literal["O", "SC"] = "O",
|
|
@@ -295,9 +295,12 @@ class RealtimeModel(llm.RealtimeModel):
|
|
|
295
295
|
logger.info(
|
|
296
296
|
f"Volc Websearch No Result Message: {volc_websearch_no_result_message}"
|
|
297
297
|
)
|
|
298
|
-
api_key = api_key
|
|
298
|
+
api_key = api_key if utils.is_given(api_key) else os.environ.get("VOLCENGINE_REALTIME_API_KEY")
|
|
299
299
|
if api_key is None:
|
|
300
|
-
raise ValueError(
|
|
300
|
+
raise ValueError(
|
|
301
|
+
"api_key is required (pass it explicitly or set the "
|
|
302
|
+
"VOLCENGINE_REALTIME_API_KEY environment variable)"
|
|
303
|
+
)
|
|
301
304
|
self._opts = _RealtimeOptions(
|
|
302
305
|
api_key=api_key,
|
|
303
306
|
bot_name=bot_name,
|
|
@@ -22,7 +22,7 @@ from livekit.agents.types import (
|
|
|
22
22
|
NOT_GIVEN,
|
|
23
23
|
NotGivenOr,
|
|
24
24
|
)
|
|
25
|
-
from livekit.agents.utils import AudioBuffer
|
|
25
|
+
from livekit.agents.utils import AudioBuffer, is_given
|
|
26
26
|
|
|
27
27
|
from .log import logger
|
|
28
28
|
|
|
@@ -180,7 +180,7 @@ class STT(stt.STT):
|
|
|
180
180
|
def __init__(
|
|
181
181
|
self,
|
|
182
182
|
*,
|
|
183
|
-
api_key: str
|
|
183
|
+
api_key: NotGivenOr[str] = NOT_GIVEN,
|
|
184
184
|
base_url: str = "wss://openspeech.bytedance.com/api/v3/sauc/bigmodel",
|
|
185
185
|
resource_id: str | None = None,
|
|
186
186
|
model_name: str = "bigmodel",
|
|
@@ -199,6 +199,13 @@ class STT(stt.STT):
|
|
|
199
199
|
)
|
|
200
200
|
)
|
|
201
201
|
|
|
202
|
+
api_key = api_key if is_given(api_key) else os.getenv("VOLCENGINE_STT_API_KEY")
|
|
203
|
+
if api_key is None:
|
|
204
|
+
raise ValueError(
|
|
205
|
+
"api_key is required (pass it explicitly or set the "
|
|
206
|
+
"VOLCENGINE_STT_API_KEY environment variable)"
|
|
207
|
+
)
|
|
208
|
+
|
|
202
209
|
self._opts = STTOptions(
|
|
203
210
|
base_url=base_url,
|
|
204
211
|
api_key=api_key,
|
|
@@ -19,7 +19,12 @@ from livekit.agents import (
|
|
|
19
19
|
tts,
|
|
20
20
|
utils,
|
|
21
21
|
)
|
|
22
|
-
from livekit.agents.types import
|
|
22
|
+
from livekit.agents.types import (
|
|
23
|
+
DEFAULT_API_CONNECT_OPTIONS,
|
|
24
|
+
NOT_GIVEN,
|
|
25
|
+
NotGivenOr,
|
|
26
|
+
)
|
|
27
|
+
from livekit.agents.utils import is_given
|
|
23
28
|
|
|
24
29
|
from .log import logger
|
|
25
30
|
|
|
@@ -104,28 +109,44 @@ class _TTSOptions(BaseModel):
|
|
|
104
109
|
class TTS(tts.TTS):
|
|
105
110
|
def __init__(
|
|
106
111
|
self,
|
|
107
|
-
|
|
112
|
+
*,
|
|
113
|
+
api_key: NotGivenOr[str] = NOT_GIVEN,
|
|
108
114
|
resource_id: str | None = None,
|
|
109
115
|
voice: str = "zh_female_xiaohe_uranus_bigtts",
|
|
110
116
|
speed: float = 1.0,
|
|
111
117
|
volume: float = 1.0,
|
|
112
118
|
pitch: float = 1.0,
|
|
119
|
+
min_sentence_length: int = 10,
|
|
113
120
|
sample_rate: Literal[24000, 16000, 8000] = 16000,
|
|
114
121
|
http_session: aiohttp.ClientSession | None = None,
|
|
115
122
|
):
|
|
116
123
|
"""VolcEngine TTS
|
|
117
124
|
|
|
118
125
|
Args:
|
|
119
|
-
api_key (str): the API key of the tts, you can get it from the new console.
|
|
126
|
+
api_key (NotGivenOr[str], optional): the API key of the tts, you can get it from the new console.
|
|
127
|
+
Falls back to the ``VOLCENGINE_TTS_API_KEY`` environment variable when not provided.
|
|
120
128
|
resource_id (str | None, optional): VolcEngine TTS resource id. Use `seed-tts-2.0` for Doubao TTS 2.0 voices, `seed-tts-1.0` / `seed-tts-1.0-concurr` for Doubao TTS 1.0 voices, `seed-icl-2.0` for voice cloning 2.0, and `seed-icl-1.0` / `seed-icl-1.0-concurr` for voice cloning 1.0. Defaults to None.
|
|
121
129
|
voice (str, optional): the voice id used by the tts request. Defaults to `zh_female_xiaohe_uranus_bigtts`.
|
|
122
130
|
speed (float, optional): speech rate multiplier (OpenAI-style). Range [0.2, 3.0]; 1.0 = normal, 2.0 = 2x, 0.5 = 0.5x. Internally converted to the Volcengine ``speech_rate`` int. Defaults to 1.0.
|
|
123
131
|
volume (float, optional): volume multiplier (OpenAI-style). Range (0.1, 3.0]; 1.0 = normal, 2.0 = 2x, 0.5 = 0.5x. Internally converted to the Volcengine ``loudness_rate`` int. Not supported for mix voices. Defaults to 1.0.
|
|
124
132
|
pitch (float, optional): kept for backwards compatibility, not currently sent to the API. Defaults to 1.0.
|
|
133
|
+
min_sentence_length (int, optional): minimum number of characters
|
|
134
|
+
(Chinese chars / tokens) the internal ``TextStreamSentencizer``
|
|
135
|
+
must accumulate before yielding a sentence to TTS. Lower
|
|
136
|
+
values trade off TTS efficiency (more / shorter requests)
|
|
137
|
+
against latency — useful for short-reply agents where the
|
|
138
|
+
model often returns <10 chars. Defaults to 10 (the upstream
|
|
139
|
+
default; matches the legacy behavior).
|
|
125
140
|
sample_rate (Literal[24000, 16000, 8000], optional): the sample rate of the tts. Defaults to 16000.
|
|
126
141
|
streaming (bool, optional): whether to use the streaming api. Defaults to True.
|
|
127
142
|
http_session (aiohttp.ClientSession | None, optional): the http session to use. Defaults to None.
|
|
128
143
|
"""
|
|
144
|
+
api_key = api_key if is_given(api_key) else os.getenv("VOLCENGINE_TTS_API_KEY")
|
|
145
|
+
if api_key is None:
|
|
146
|
+
raise ValueError(
|
|
147
|
+
"api_key is required (pass it explicitly or set the "
|
|
148
|
+
"VOLCENGINE_TTS_API_KEY environment variable)"
|
|
149
|
+
)
|
|
129
150
|
super().__init__(
|
|
130
151
|
capabilities=tts.TTSCapabilities(streaming=True),
|
|
131
152
|
sample_rate=sample_rate,
|
|
@@ -141,6 +162,7 @@ class TTS(tts.TTS):
|
|
|
141
162
|
pitch=pitch,
|
|
142
163
|
)
|
|
143
164
|
self._session = http_session
|
|
165
|
+
self._min_sentence_length = min_sentence_length
|
|
144
166
|
self._streams = weakref.WeakSet[SynthesizeStream]()
|
|
145
167
|
|
|
146
168
|
def _ensure_session(self) -> aiohttp.ClientSession:
|
|
@@ -159,6 +181,7 @@ class TTS(tts.TTS):
|
|
|
159
181
|
conn_options=conn_options,
|
|
160
182
|
opts=self._opts,
|
|
161
183
|
session=self._ensure_session(),
|
|
184
|
+
min_sentence_length=self._min_sentence_length,
|
|
162
185
|
)
|
|
163
186
|
self._streams.add(stream)
|
|
164
187
|
return stream
|
|
@@ -178,15 +201,19 @@ class SynthesizeStream(tts.SynthesizeStream):
|
|
|
178
201
|
session: aiohttp.ClientSession,
|
|
179
202
|
tts: TTS,
|
|
180
203
|
conn_options=None,
|
|
204
|
+
min_sentence_length: int = 10,
|
|
181
205
|
):
|
|
182
206
|
super().__init__(tts=tts, conn_options=conn_options)
|
|
183
207
|
self._opts: _TTSOptions = opts
|
|
184
208
|
self._session = session
|
|
209
|
+
self._min_sentence_length = min_sentence_length
|
|
185
210
|
|
|
186
211
|
async def _run(self, emitter: tts.AudioEmitter):
|
|
187
212
|
request_id = utils.shortuuid()
|
|
188
213
|
|
|
189
|
-
sentence_splitter = TextStreamSentencizer(
|
|
214
|
+
sentence_splitter = TextStreamSentencizer(
|
|
215
|
+
min_sentence_length=self._min_sentence_length
|
|
216
|
+
)
|
|
190
217
|
emitter.initialize(
|
|
191
218
|
request_id=request_id,
|
|
192
219
|
sample_rate=self._opts.sample_rate,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from typing import Awaitable, Callable, Union, cast
|
|
6
|
+
|
|
7
|
+
from livekit.agents import llm
|
|
8
|
+
from openai.types.chat import ChatCompletionToolParam
|
|
9
|
+
|
|
10
|
+
AsyncAzureADTokenProvider = Callable[[], Union[str, Awaitable[str]]]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_base_url(base_url: str | None) -> str:
|
|
14
|
+
if not base_url:
|
|
15
|
+
base_url = os.getenv(
|
|
16
|
+
"VOLCENGINE_LLM_BASE_URL", "https://ark.cn-beijing.volces.com/api/v3"
|
|
17
|
+
)
|
|
18
|
+
return base_url
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def to_fnc_ctx(fnc_ctx: Sequence[llm.Tool]) -> list[ChatCompletionToolParam]:
|
|
22
|
+
return cast(
|
|
23
|
+
list[ChatCompletionToolParam],
|
|
24
|
+
llm.ToolContext(fnc_ctx).parse_function_tools("openai"),
|
|
25
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.3.6"
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import base64
|
|
4
|
-
import os
|
|
5
|
-
from collections import OrderedDict
|
|
6
|
-
from collections.abc import Awaitable, Sequence
|
|
7
|
-
from typing import Any, Callable, Union, cast
|
|
8
|
-
|
|
9
|
-
from openai.types.chat import (
|
|
10
|
-
ChatCompletionContentPartParam,
|
|
11
|
-
ChatCompletionMessageParam,
|
|
12
|
-
ChatCompletionToolParam,
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
from livekit.agents import llm
|
|
16
|
-
|
|
17
|
-
AsyncAzureADTokenProvider = Callable[[], Union[str, Awaitable[str]]]
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def get_base_url(base_url: str | None) -> str:
|
|
21
|
-
if not base_url:
|
|
22
|
-
base_url = os.getenv(
|
|
23
|
-
"VOLCENGINE_LLM_BASE_URL", "https://ark.cn-beijing.volces.com/api/v3"
|
|
24
|
-
)
|
|
25
|
-
return base_url
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def to_fnc_ctx(fnc_ctx: Sequence[llm.Tool]) -> list[ChatCompletionToolParam]:
|
|
29
|
-
return cast(
|
|
30
|
-
list[ChatCompletionToolParam],
|
|
31
|
-
llm.ToolContext(fnc_ctx).parse_function_tools("openai"),
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def to_chat_ctx(
|
|
36
|
-
chat_ctx: llm.ChatContext, cache_key: Any
|
|
37
|
-
) -> list[ChatCompletionMessageParam]:
|
|
38
|
-
# group the message and function_calls
|
|
39
|
-
item_groups: dict[str, list[llm.ChatItem]] = OrderedDict()
|
|
40
|
-
for item in chat_ctx.items:
|
|
41
|
-
if (
|
|
42
|
-
item.type == "message" and item.role == "assistant"
|
|
43
|
-
) or item.type == "function_call":
|
|
44
|
-
group_id = item.id.split("/")[0]
|
|
45
|
-
if group_id not in item_groups:
|
|
46
|
-
item_groups[group_id] = []
|
|
47
|
-
item_groups[group_id].append(item)
|
|
48
|
-
else:
|
|
49
|
-
item_groups[item.id] = [item]
|
|
50
|
-
|
|
51
|
-
return [_group_to_chat_item(items, cache_key) for items in item_groups.values()]
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def _group_to_chat_item(
|
|
55
|
-
items: list[llm.ChatItem], cache_key: Any
|
|
56
|
-
) -> ChatCompletionMessageParam:
|
|
57
|
-
if len(items) == 1:
|
|
58
|
-
return _to_chat_item(items[0], cache_key)
|
|
59
|
-
else:
|
|
60
|
-
msg = {"role": "assistant", "tool_calls": []}
|
|
61
|
-
for item in items:
|
|
62
|
-
if item.type == "message":
|
|
63
|
-
assert item.role == "assistant", (
|
|
64
|
-
"only assistant messages can be grouped"
|
|
65
|
-
)
|
|
66
|
-
assert "content" not in msg, (
|
|
67
|
-
"only one assistant message is allowed in a group"
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
msg.update(_to_chat_item(item, cache_key))
|
|
71
|
-
elif item.type == "function_call":
|
|
72
|
-
msg["tool_calls"].append(
|
|
73
|
-
{
|
|
74
|
-
"id": item.call_id,
|
|
75
|
-
"type": "function",
|
|
76
|
-
"function": {"name": item.name, "arguments": item.arguments},
|
|
77
|
-
}
|
|
78
|
-
)
|
|
79
|
-
return msg
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def _to_chat_item(msg: llm.ChatItem, cache_key: Any) -> ChatCompletionMessageParam:
|
|
83
|
-
if msg.type == "message":
|
|
84
|
-
list_content: list[ChatCompletionContentPartParam] = []
|
|
85
|
-
text_content = ""
|
|
86
|
-
for content in msg.content:
|
|
87
|
-
if isinstance(content, str):
|
|
88
|
-
if text_content:
|
|
89
|
-
text_content += "\n"
|
|
90
|
-
text_content += content
|
|
91
|
-
elif isinstance(content, llm.ImageContent):
|
|
92
|
-
list_content.append(_to_image_content(content, cache_key))
|
|
93
|
-
|
|
94
|
-
if not list_content:
|
|
95
|
-
# certain providers require text-only content in a string vs a list.
|
|
96
|
-
# for max-compatibility, we will combine all text content into a single string.
|
|
97
|
-
return {
|
|
98
|
-
"role": msg.role, # type: ignore
|
|
99
|
-
"content": text_content,
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if text_content:
|
|
103
|
-
list_content.append({"type": "text", "text": text_content})
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
"role": msg.role, # type: ignore
|
|
107
|
-
"content": list_content,
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
elif msg.type == "function_call":
|
|
111
|
-
return {
|
|
112
|
-
"role": "assistant",
|
|
113
|
-
"tool_calls": [
|
|
114
|
-
{
|
|
115
|
-
"id": msg.call_id,
|
|
116
|
-
"type": "function",
|
|
117
|
-
"function": {
|
|
118
|
-
"name": msg.name,
|
|
119
|
-
"arguments": msg.arguments,
|
|
120
|
-
},
|
|
121
|
-
}
|
|
122
|
-
],
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
elif msg.type == "function_call_output":
|
|
126
|
-
return {
|
|
127
|
-
"role": "tool",
|
|
128
|
-
"tool_call_id": msg.call_id,
|
|
129
|
-
"content": msg.output,
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
def _to_image_content(
|
|
134
|
-
image: llm.ImageContent, cache_key: Any
|
|
135
|
-
) -> ChatCompletionContentPartParam:
|
|
136
|
-
img = llm.utils.serialize_image(image)
|
|
137
|
-
if img.external_url:
|
|
138
|
-
return {
|
|
139
|
-
"type": "image_url",
|
|
140
|
-
"image_url": {
|
|
141
|
-
"url": img.external_url,
|
|
142
|
-
"detail": img.inference_detail,
|
|
143
|
-
},
|
|
144
|
-
}
|
|
145
|
-
if cache_key not in image._cache:
|
|
146
|
-
image._cache[cache_key] = img.data_bytes
|
|
147
|
-
b64_data = base64.b64encode(image._cache[cache_key]).decode("utf-8")
|
|
148
|
-
return {
|
|
149
|
-
"type": "image_url",
|
|
150
|
-
"image_url": {
|
|
151
|
-
"url": f"data:{img.mime_type};base64,{b64_data}",
|
|
152
|
-
"detail": img.inference_detail,
|
|
153
|
-
},
|
|
154
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.3.4"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|