livekit-agents 0.8.0.dev2__tar.gz → 0.8.0.dev3__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_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/PKG-INFO +4 -4
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/cli/watcher.py +1 -1
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/ipc/channel.py +1 -1
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/ipc/proc_main.py +10 -8
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/ipc/proc_pool.py +3 -8
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/ipc/supervised_proc.py +19 -10
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/job.py +4 -4
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/transcription/_utils.py +2 -2
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/transcription/stt_forwarder.py +8 -3
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/transcription/tts_forwarder.py +17 -4
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/version.py +1 -1
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/voice_assistant/agent_output.py +26 -45
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/voice_assistant/cancellable_source.py +13 -11
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/voice_assistant/human_input.py +1 -1
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/voice_assistant/voice_assistant.py +13 -8
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/worker.py +10 -2
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit_agents.egg-info/PKG-INFO +4 -4
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit_agents.egg-info/requires.txt +3 -3
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/setup.py +3 -3
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/README.md +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/__init__.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/cli/__init__.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/cli/cli.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/cli/log.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/cli/proto.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/exceptions.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/http_server.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/ipc/__init__.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/ipc/proto.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/llm/__init__.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/llm/_oai_api.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/llm/chat_context.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/llm/function_context.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/llm/llm.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/log.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/plugin.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/py.typed +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/stt/__init__.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/stt/stream_adapter.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/stt/stt.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/__init__.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/_basic_hyphenator.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/_basic_paragraph.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/_basic_sent.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/_basic_word.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/basic.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/token_stream.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/tokenizer.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/transcription/__init__.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tts/__init__.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tts/stream_adapter.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tts/tts.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/__init__.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/aio/__init__.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/aio/channel.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/aio/debug.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/aio/interval.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/aio/sleep.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/aio/task_set.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/audio.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/codecs/__init__.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/codecs/mp3.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/event_emitter.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/exp_filter.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/http_context.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/images/__init__.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/images/image.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/log.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/misc.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/moving_average.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/vad.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/voice_assistant/__init__.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/voice_assistant/log.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/voice_assistant/plotter.py +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit_agents.egg-info/SOURCES.txt +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit_agents.egg-info/dependency_links.txt +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit_agents.egg-info/top_level.txt +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/pyproject.toml +0 -0
- {livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: livekit-agents
|
|
3
|
-
Version: 0.8.0.
|
|
3
|
+
Version: 0.8.0.dev3
|
|
4
4
|
Summary: LiveKit Python Agents
|
|
5
5
|
Home-page: https://github.com/livekit/agents
|
|
6
6
|
License: Apache-2.0
|
|
@@ -20,9 +20,9 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
20
20
|
Requires-Python: >=3.9.0
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
Requires-Dist: click~=8.1
|
|
23
|
-
Requires-Dist: livekit~=0.
|
|
24
|
-
Requires-Dist: livekit-api~=0.
|
|
25
|
-
Requires-Dist: livekit-protocol~=0.
|
|
23
|
+
Requires-Dist: livekit~=0.12.0.dev1
|
|
24
|
+
Requires-Dist: livekit-api~=0.6.0
|
|
25
|
+
Requires-Dist: livekit-protocol~=0.6.0
|
|
26
26
|
Requires-Dist: protobuf>=3
|
|
27
27
|
Requires-Dist: pyjwt>=2.0.0
|
|
28
28
|
Requires-Dist: types-protobuf<5,>=4
|
|
@@ -105,7 +105,7 @@ class AsyncProcChannel(ProcChannel):
|
|
|
105
105
|
|
|
106
106
|
self._read_q = asyncio.Queue[Optional[Message]]()
|
|
107
107
|
self._write_q = queue.Queue[Optional[Message]]()
|
|
108
|
-
self._exit_fut = asyncio.Future()
|
|
108
|
+
self._exit_fut = asyncio.Future[None]()
|
|
109
109
|
|
|
110
110
|
self._read_t = threading.Thread(
|
|
111
111
|
target=self._read_thread, daemon=True, name="proc_channel_read"
|
|
@@ -157,9 +157,10 @@ async def _async_main(
|
|
|
157
157
|
|
|
158
158
|
if isinstance(msg, proto.ShutdownRequest):
|
|
159
159
|
if job_task is not None:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
with contextlib.suppress(asyncio.InvalidStateError):
|
|
161
|
+
job_task.shutdown_fut.set_result(
|
|
162
|
+
_ShutdownInfo(reason=msg.reason, user_initiated=False)
|
|
163
|
+
)
|
|
163
164
|
else:
|
|
164
165
|
exit_proc_fut.set() # there is no running job, we can exit immediately
|
|
165
166
|
|
|
@@ -210,8 +211,9 @@ def main(args: proto.ProcStartArgs) -> None:
|
|
|
210
211
|
# (this signal can be sent by watchfiles on dev mode)
|
|
211
212
|
loop.run_until_complete(main_task)
|
|
212
213
|
finally:
|
|
213
|
-
try:
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
finally:
|
|
217
|
-
|
|
214
|
+
# try:
|
|
215
|
+
loop.run_until_complete(loop.shutdown_default_executor())
|
|
216
|
+
loop.run_until_complete(cch.aclose())
|
|
217
|
+
# finally:
|
|
218
|
+
# loop.close()
|
|
219
|
+
# pass
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
|
|
5
|
-
import sys
|
|
4
|
+
from multiprocessing.context import ForkServerContext, SpawnContext
|
|
6
5
|
from typing import Any, Callable, Coroutine, Literal
|
|
7
6
|
|
|
8
7
|
from .. import utils
|
|
@@ -26,15 +25,11 @@ class ProcPool(utils.EventEmitter[EventTypes]):
|
|
|
26
25
|
num_idle_processes: int,
|
|
27
26
|
initialize_timeout: float,
|
|
28
27
|
close_timeout: float,
|
|
28
|
+
mp_ctx: ForkServerContext | SpawnContext,
|
|
29
29
|
loop: asyncio.AbstractEventLoop,
|
|
30
30
|
) -> None:
|
|
31
31
|
super().__init__()
|
|
32
|
-
|
|
33
|
-
#if sys.platform.startswith("linux"):
|
|
34
|
-
# self._mp_ctx = mp.get_context("forkserver")
|
|
35
|
-
#else:
|
|
36
|
-
self._mp_ctx = mp.get_context("spawn")
|
|
37
|
-
|
|
32
|
+
self._mp_ctx = mp_ctx
|
|
38
33
|
self._initialize_process_fnc = initialize_process_fnc
|
|
39
34
|
self._job_entrypoint_fnc = job_entrypoint_fnc
|
|
40
35
|
self._job_shutdown_fnc = job_shutdown_fnc
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/ipc/supervised_proc.py
RENAMED
|
@@ -32,6 +32,8 @@ class LogQueueListener:
|
|
|
32
32
|
t.start()
|
|
33
33
|
|
|
34
34
|
def stop(self) -> None:
|
|
35
|
+
if self._thread is None:
|
|
36
|
+
return
|
|
35
37
|
self._q.put_nowait(self._sentinel)
|
|
36
38
|
self._thread.join()
|
|
37
39
|
self._thread = None
|
|
@@ -67,7 +69,7 @@ class SupervisedProc:
|
|
|
67
69
|
loop: asyncio.AbstractEventLoop,
|
|
68
70
|
) -> None:
|
|
69
71
|
self._loop = loop
|
|
70
|
-
log_q =
|
|
72
|
+
log_q = mp_ctx.Queue()
|
|
71
73
|
log_q.cancel_join_thread()
|
|
72
74
|
mp_pch, mp_cch = mp_ctx.Pipe(duplex=True)
|
|
73
75
|
|
|
@@ -97,7 +99,7 @@ class SupervisedProc:
|
|
|
97
99
|
self._main_atask: asyncio.Task[None] | None = None
|
|
98
100
|
self._closing = False
|
|
99
101
|
self._kill_sent = False
|
|
100
|
-
self._initialize_fut = asyncio.Future()
|
|
102
|
+
self._initialize_fut = asyncio.Future[None]()
|
|
101
103
|
|
|
102
104
|
@property
|
|
103
105
|
def exitcode(self) -> int | None:
|
|
@@ -145,7 +147,7 @@ class SupervisedProc:
|
|
|
145
147
|
|
|
146
148
|
self._proc.start()
|
|
147
149
|
self._pid = self._proc.pid
|
|
148
|
-
self._join_fut = asyncio.Future()
|
|
150
|
+
self._join_fut = asyncio.Future[None]()
|
|
149
151
|
|
|
150
152
|
def _sync_run():
|
|
151
153
|
self._proc.join()
|
|
@@ -161,7 +163,8 @@ class SupervisedProc:
|
|
|
161
163
|
if not self.started:
|
|
162
164
|
raise RuntimeError("process not started")
|
|
163
165
|
|
|
164
|
-
|
|
166
|
+
if self._main_atask:
|
|
167
|
+
await asyncio.shield(self._main_atask)
|
|
165
168
|
|
|
166
169
|
async def initialize(self) -> None:
|
|
167
170
|
"""initialize the job process, this is calling the user provided initialize_process_fnc
|
|
@@ -198,16 +201,18 @@ class SupervisedProc:
|
|
|
198
201
|
await self._pch.asend(proto.ShutdownRequest())
|
|
199
202
|
|
|
200
203
|
try:
|
|
201
|
-
|
|
202
|
-
asyncio.
|
|
203
|
-
|
|
204
|
+
if self._main_atask:
|
|
205
|
+
await asyncio.wait_for(
|
|
206
|
+
asyncio.shield(self._main_atask), timeout=self._close_timeout
|
|
207
|
+
)
|
|
204
208
|
except asyncio.TimeoutError:
|
|
205
209
|
logger.error(
|
|
206
210
|
"process did not exit in time, killing job", extra=self.logging_extra()
|
|
207
211
|
)
|
|
208
212
|
self._send_kill_signal()
|
|
209
213
|
|
|
210
|
-
|
|
214
|
+
if self._main_atask:
|
|
215
|
+
await asyncio.shield(self._main_atask)
|
|
211
216
|
|
|
212
217
|
async def kill(self) -> None:
|
|
213
218
|
"""forcefully kill the job process"""
|
|
@@ -216,7 +221,8 @@ class SupervisedProc:
|
|
|
216
221
|
|
|
217
222
|
self._closing = True
|
|
218
223
|
self._send_kill_signal()
|
|
219
|
-
|
|
224
|
+
if self._main_atask:
|
|
225
|
+
await asyncio.shield(self._main_atask)
|
|
220
226
|
|
|
221
227
|
async def launch_job(self, info: RunningJobInfo) -> None:
|
|
222
228
|
"""start/assign a job to the process"""
|
|
@@ -230,7 +236,10 @@ class SupervisedProc:
|
|
|
230
236
|
|
|
231
237
|
def _send_kill_signal(self) -> None:
|
|
232
238
|
"""forcefully kill the job process"""
|
|
233
|
-
|
|
239
|
+
try:
|
|
240
|
+
if not self._proc.is_alive():
|
|
241
|
+
return
|
|
242
|
+
except ValueError:
|
|
234
243
|
return
|
|
235
244
|
|
|
236
245
|
logger.debug("killing job process", extra=self.logging_extra())
|
|
@@ -114,8 +114,8 @@ def _apply_auto_subscribe_opts(room: rtc.Room, auto_subscribe: AutoSubscribe) ->
|
|
|
114
114
|
):
|
|
115
115
|
pub.set_subscribed(True)
|
|
116
116
|
|
|
117
|
-
for p in room.
|
|
118
|
-
for pub in p.
|
|
117
|
+
for p in room.remote_participants.values():
|
|
118
|
+
for pub in p.track_publications.values():
|
|
119
119
|
_subscribe_if_needed(pub)
|
|
120
120
|
|
|
121
121
|
@room.on("track_published")
|
|
@@ -128,11 +128,11 @@ def _apply_auto_subscribe_opts(room: rtc.Room, auto_subscribe: AutoSubscribe) ->
|
|
|
128
128
|
class JobProcess:
|
|
129
129
|
def __init__(self, *, start_arguments: Any | None = None) -> None:
|
|
130
130
|
self._mp_proc = mp.current_process()
|
|
131
|
-
self._userdata = {}
|
|
131
|
+
self._userdata: dict[str, Any] = {}
|
|
132
132
|
self._start_arguments = start_arguments
|
|
133
133
|
|
|
134
134
|
@property
|
|
135
|
-
def pid(self) -> int:
|
|
135
|
+
def pid(self) -> int | None:
|
|
136
136
|
return self._mp_proc.pid
|
|
137
137
|
|
|
138
138
|
@property
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/transcription/_utils.py
RENAMED
|
@@ -7,7 +7,7 @@ from livekit import rtc
|
|
|
7
7
|
|
|
8
8
|
def find_micro_track_id(room: rtc.Room, identity: str) -> str:
|
|
9
9
|
p: rtc.RemoteParticipant | rtc.LocalParticipant | None = (
|
|
10
|
-
room.
|
|
10
|
+
room.remote_participants.get(identity)
|
|
11
11
|
)
|
|
12
12
|
if identity == room.local_participant.identity:
|
|
13
13
|
p = room.local_participant
|
|
@@ -17,7 +17,7 @@ def find_micro_track_id(room: rtc.Room, identity: str) -> str:
|
|
|
17
17
|
|
|
18
18
|
# find first micro track
|
|
19
19
|
track_id = None
|
|
20
|
-
for track in p.
|
|
20
|
+
for track in p.track_publications.values():
|
|
21
21
|
if track.source == rtc.TrackSource.SOURCE_MICROPHONE:
|
|
22
22
|
track_id = track.sid
|
|
23
23
|
break
|
|
@@ -45,9 +45,8 @@ class STTSegmentsForwarder:
|
|
|
45
45
|
|
|
46
46
|
transcription = rtc.Transcription(
|
|
47
47
|
participant_identity=self._participant_identity,
|
|
48
|
-
|
|
48
|
+
track_sid=self._track_id,
|
|
49
49
|
segments=[seg], # no history for now
|
|
50
|
-
language="", # TODO(theomonnom)
|
|
51
50
|
)
|
|
52
51
|
await self._room.local_participant.publish_transcription(transcription)
|
|
53
52
|
|
|
@@ -66,13 +65,19 @@ class STTSegmentsForwarder:
|
|
|
66
65
|
start_time=0,
|
|
67
66
|
end_time=0,
|
|
68
67
|
final=False,
|
|
68
|
+
language="", # TODO
|
|
69
69
|
)
|
|
70
70
|
)
|
|
71
71
|
elif ev.type == stt.SpeechEventType.FINAL_TRANSCRIPT:
|
|
72
72
|
text = ev.alternatives[0].text
|
|
73
73
|
self._queue.put_nowait(
|
|
74
74
|
rtc.TranscriptionSegment(
|
|
75
|
-
id=self._current_id,
|
|
75
|
+
id=self._current_id,
|
|
76
|
+
text=text,
|
|
77
|
+
start_time=0,
|
|
78
|
+
end_time=0,
|
|
79
|
+
final=True,
|
|
80
|
+
language="", # TODO
|
|
76
81
|
)
|
|
77
82
|
)
|
|
78
83
|
|
|
@@ -185,6 +185,10 @@ class TTSSegmentsForwarder:
|
|
|
185
185
|
self._forming_segments.q.append(new_seg)
|
|
186
186
|
self._seg_queue.put_nowait(new_seg)
|
|
187
187
|
|
|
188
|
+
@property
|
|
189
|
+
def closed(self) -> bool:
|
|
190
|
+
return self._closed
|
|
191
|
+
|
|
188
192
|
async def aclose(self) -> None:
|
|
189
193
|
if self._closed:
|
|
190
194
|
return
|
|
@@ -213,9 +217,8 @@ class TTSSegmentsForwarder:
|
|
|
213
217
|
|
|
214
218
|
tr = rtc.Transcription(
|
|
215
219
|
participant_identity=self._opts.participant_identity,
|
|
216
|
-
|
|
220
|
+
track_sid=self._opts.track_id,
|
|
217
221
|
segments=[seg], # no history for now, only one segment
|
|
218
|
-
language=self._opts.language,
|
|
219
222
|
)
|
|
220
223
|
await self._opts.room.local_participant.publish_transcription(tr)
|
|
221
224
|
|
|
@@ -294,7 +297,12 @@ class TTSSegmentsForwarder:
|
|
|
294
297
|
await self._sleep_if_not_closed(first_delay)
|
|
295
298
|
rtc_seg_q.put_nowait(
|
|
296
299
|
rtc.TranscriptionSegment(
|
|
297
|
-
id=seg_id,
|
|
300
|
+
id=seg_id,
|
|
301
|
+
text=text,
|
|
302
|
+
start_time=0,
|
|
303
|
+
end_time=0,
|
|
304
|
+
final=False,
|
|
305
|
+
language=self._opts.language,
|
|
298
306
|
)
|
|
299
307
|
)
|
|
300
308
|
await self._sleep_if_not_closed(delay - first_delay)
|
|
@@ -302,7 +310,12 @@ class TTSSegmentsForwarder:
|
|
|
302
310
|
|
|
303
311
|
rtc_seg_q.put_nowait(
|
|
304
312
|
rtc.TranscriptionSegment(
|
|
305
|
-
id=seg_id,
|
|
313
|
+
id=seg_id,
|
|
314
|
+
text=tokenized_sentence,
|
|
315
|
+
start_time=0,
|
|
316
|
+
end_time=0,
|
|
317
|
+
final=True,
|
|
318
|
+
language=self._opts.language,
|
|
306
319
|
)
|
|
307
320
|
)
|
|
308
321
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
import
|
|
4
|
+
import time
|
|
5
5
|
from typing import Any, AsyncIterable, Union
|
|
6
6
|
|
|
7
7
|
from livekit import rtc
|
|
@@ -116,53 +116,42 @@ class AgentOutput:
|
|
|
116
116
|
co = _stream_synthesis_task(handle._speech_source, handle)
|
|
117
117
|
|
|
118
118
|
synth = asyncio.create_task(co)
|
|
119
|
+
synth.add_done_callback(lambda _: handle._buf_ch.close())
|
|
119
120
|
try:
|
|
120
121
|
_ = await asyncio.wait(
|
|
121
122
|
[synth, handle._interrupt_fut], return_when=asyncio.FIRST_COMPLETED
|
|
122
123
|
)
|
|
123
124
|
finally:
|
|
124
|
-
|
|
125
|
-
synth.cancel()
|
|
126
|
-
await synth
|
|
127
|
-
|
|
128
|
-
try:
|
|
129
|
-
if handle.play_handle is not None:
|
|
130
|
-
await handle.play_handle
|
|
131
|
-
finally:
|
|
132
|
-
await handle._tr_fwd.aclose()
|
|
125
|
+
await utils.aio.gracefully_cancel(synth)
|
|
133
126
|
|
|
134
127
|
|
|
135
128
|
@utils.log_exceptions(logger=logger)
|
|
136
129
|
async def _str_synthesis_task(text: str, handle: SynthesisHandle) -> None:
|
|
137
130
|
"""synthesize speech from a string"""
|
|
138
|
-
if handle._tr_fwd
|
|
131
|
+
if handle._tr_fwd and not handle._tr_fwd.closed:
|
|
139
132
|
handle._tr_fwd.push_text(text)
|
|
140
133
|
handle._tr_fwd.mark_text_segment_end()
|
|
141
134
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
# audio_duration = 0.0
|
|
135
|
+
start_time = time.time()
|
|
136
|
+
first_frame = True
|
|
145
137
|
handle._collected_text = text
|
|
146
138
|
|
|
147
139
|
try:
|
|
148
140
|
async for audio in handle._tts.synthesize(text):
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
141
|
+
if first_frame:
|
|
142
|
+
first_frame = False
|
|
143
|
+
dt = time.time() - start_time
|
|
144
|
+
logger.debug(f"AgentOutput._str_synthesis_task: TTFB in {dt:.2f}s")
|
|
153
145
|
|
|
154
146
|
frame = audio.frame
|
|
155
|
-
# audio_duration += frame.samples_per_channel / frame.sample_rate
|
|
156
147
|
|
|
157
148
|
handle._buf_ch.send_nowait(frame)
|
|
158
|
-
if handle._tr_fwd
|
|
149
|
+
if handle._tr_fwd and not handle._tr_fwd.closed:
|
|
159
150
|
handle._tr_fwd.push_audio(frame)
|
|
160
151
|
|
|
161
152
|
finally:
|
|
162
|
-
if handle._tr_fwd
|
|
153
|
+
if handle._tr_fwd and not handle._tr_fwd.closed:
|
|
163
154
|
handle._tr_fwd.mark_audio_segment_end()
|
|
164
|
-
handle._buf_ch.close()
|
|
165
|
-
# self._log_debug(f"tts finished synthesising {audio_duration:.2f}s of audio")
|
|
166
155
|
|
|
167
156
|
|
|
168
157
|
@utils.log_exceptions(logger=logger)
|
|
@@ -173,27 +162,19 @@ async def _stream_synthesis_task(
|
|
|
173
162
|
|
|
174
163
|
@utils.log_exceptions(logger=logger)
|
|
175
164
|
async def _read_generated_audio_task():
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
# audio_duration = 0.0
|
|
165
|
+
start_time = time.time()
|
|
166
|
+
first_frame = True
|
|
179
167
|
async for audio in tts_stream:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
168
|
+
if first_frame:
|
|
169
|
+
first_frame = False
|
|
170
|
+
dt = time.time() - start_time
|
|
171
|
+
logger.debug(f"AgentOutput._stream_synthesis_task: TTFB in {dt:.2f}s")
|
|
184
172
|
|
|
185
|
-
|
|
186
|
-
if handle._tr_fwd is not None:
|
|
173
|
+
if handle._tr_fwd and not handle._tr_fwd.closed:
|
|
187
174
|
handle._tr_fwd.push_audio(audio.frame)
|
|
188
175
|
|
|
189
176
|
handle._buf_ch.send_nowait(audio.frame)
|
|
190
177
|
|
|
191
|
-
# we're only flushing once, so we know we can break at the end of the first segment
|
|
192
|
-
|
|
193
|
-
# self._log_debug(
|
|
194
|
-
# f"tts finished synthesising {audio_duration:.2f}s audio (streamed)"
|
|
195
|
-
# )
|
|
196
|
-
|
|
197
178
|
# otherwise, stream the text to the TTS
|
|
198
179
|
tts_stream = handle._tts.stream()
|
|
199
180
|
read_atask = asyncio.create_task(_read_generated_audio_task())
|
|
@@ -201,20 +182,20 @@ async def _stream_synthesis_task(
|
|
|
201
182
|
try:
|
|
202
183
|
async for seg in streamed_text:
|
|
203
184
|
handle._collected_text += seg
|
|
204
|
-
|
|
185
|
+
|
|
186
|
+
if handle._tr_fwd and not handle._tr_fwd.closed:
|
|
205
187
|
handle._tr_fwd.push_text(seg)
|
|
206
188
|
|
|
207
189
|
tts_stream.push_text(seg)
|
|
208
|
-
|
|
209
190
|
finally:
|
|
210
|
-
|
|
191
|
+
tts_stream.end_input()
|
|
192
|
+
|
|
193
|
+
if handle._tr_fwd and not handle._tr_fwd.closed:
|
|
211
194
|
handle._tr_fwd.mark_text_segment_end()
|
|
212
195
|
|
|
213
|
-
tts_stream.end_input()
|
|
214
196
|
await read_atask
|
|
215
197
|
await tts_stream.aclose()
|
|
216
198
|
|
|
217
|
-
if handle._tr_fwd
|
|
199
|
+
if handle._tr_fwd and not handle._tr_fwd.closed:
|
|
200
|
+
# mark_audio_segment_end must be called *after* mart_text_segment_end
|
|
218
201
|
handle._tr_fwd.mark_audio_segment_end()
|
|
219
|
-
|
|
220
|
-
handle._buf_ch.close()
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
import contextlib
|
|
5
4
|
from typing import AsyncIterable, Literal
|
|
6
5
|
|
|
7
6
|
from livekit import rtc
|
|
@@ -41,8 +40,8 @@ class PlayoutHandle:
|
|
|
41
40
|
|
|
42
41
|
self._interrupted = True
|
|
43
42
|
|
|
44
|
-
def
|
|
45
|
-
return self._done_fut
|
|
43
|
+
def join(self) -> asyncio.Future:
|
|
44
|
+
return self._done_fut
|
|
46
45
|
|
|
47
46
|
|
|
48
47
|
class CancellableAudioSource(utils.EventEmitter[EventTypes]):
|
|
@@ -89,6 +88,7 @@ class CancellableAudioSource(utils.EventEmitter[EventTypes]):
|
|
|
89
88
|
self._playout_atask = asyncio.create_task(
|
|
90
89
|
self._playout_task(self._playout_atask, handle)
|
|
91
90
|
)
|
|
91
|
+
|
|
92
92
|
return handle
|
|
93
93
|
|
|
94
94
|
@utils.log_exceptions(logger=logger)
|
|
@@ -104,9 +104,9 @@ class CancellableAudioSource(utils.EventEmitter[EventTypes]):
|
|
|
104
104
|
|
|
105
105
|
try:
|
|
106
106
|
if old_task is not None:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
await utils.aio.gracefully_cancel(old_task)
|
|
108
|
+
|
|
109
|
+
logger.debug("CancellableAudioSource._playout_task: started")
|
|
110
110
|
|
|
111
111
|
async for frame in handle._playout_source:
|
|
112
112
|
if first_frame:
|
|
@@ -121,7 +121,7 @@ class CancellableAudioSource(utils.EventEmitter[EventTypes]):
|
|
|
121
121
|
break
|
|
122
122
|
|
|
123
123
|
# divide the frame by chunks of 20ms
|
|
124
|
-
ms20 = frame.sample_rate //
|
|
124
|
+
ms20 = frame.sample_rate // 50
|
|
125
125
|
i = 0
|
|
126
126
|
while i < len(frame.data):
|
|
127
127
|
if _should_break():
|
|
@@ -148,10 +148,12 @@ class CancellableAudioSource(utils.EventEmitter[EventTypes]):
|
|
|
148
148
|
handle._time_played += rem / frame.sample_rate
|
|
149
149
|
finally:
|
|
150
150
|
if not first_frame:
|
|
151
|
-
if handle._tr_fwd is not None:
|
|
152
|
-
|
|
153
|
-
handle._tr_fwd.segment_playout_finished()
|
|
151
|
+
if handle._tr_fwd is not None and not cancelled:
|
|
152
|
+
handle._tr_fwd.segment_playout_finished()
|
|
154
153
|
|
|
155
154
|
self.emit("playout_stopped", cancelled)
|
|
156
155
|
|
|
157
|
-
handle._done_fut.set_result(None)
|
|
156
|
+
handle._done_fut.set_result(None)
|
|
157
|
+
if handle._tr_fwd is not None:
|
|
158
|
+
await handle._tr_fwd.aclose()
|
|
159
|
+
logger.debug("CancellableAudioSource._playout_task: ended")
|
|
@@ -75,7 +75,7 @@ class HumanInput(utils.EventEmitter[EventTypes]):
|
|
|
75
75
|
Subscribe to the participant microphone if found and not already subscribed.
|
|
76
76
|
Do nothing if no track is found.
|
|
77
77
|
"""
|
|
78
|
-
for publication in self._participant.
|
|
78
|
+
for publication in self._participant.track_publications.values():
|
|
79
79
|
if publication.source != rtc.TrackSource.SOURCE_MICROPHONE:
|
|
80
80
|
continue
|
|
81
81
|
|
|
@@ -216,7 +216,7 @@ class VoiceAssistant(utils.EventEmitter[EventTypes]):
|
|
|
216
216
|
self._link_participant(participant)
|
|
217
217
|
else:
|
|
218
218
|
# no participant provided, try to find the first in the room
|
|
219
|
-
for participant in self._room.
|
|
219
|
+
for participant in self._room.remote_participants.values():
|
|
220
220
|
self._link_participant(participant.identity)
|
|
221
221
|
break
|
|
222
222
|
|
|
@@ -290,7 +290,7 @@ class VoiceAssistant(utils.EventEmitter[EventTypes]):
|
|
|
290
290
|
self._link_participant(participant.identity)
|
|
291
291
|
|
|
292
292
|
def _link_participant(self, identity: str) -> None:
|
|
293
|
-
participant = self._room.
|
|
293
|
+
participant = self._room.remote_participants.get(identity)
|
|
294
294
|
if participant is None:
|
|
295
295
|
logger.error("_link_participant must be called with a valid identity")
|
|
296
296
|
return
|
|
@@ -312,7 +312,7 @@ class VoiceAssistant(utils.EventEmitter[EventTypes]):
|
|
|
312
312
|
|
|
313
313
|
tv = 1.0
|
|
314
314
|
if self._opts.allow_interruptions:
|
|
315
|
-
tv = max(0, 1 - ev.probability)
|
|
315
|
+
tv = max(0.0, 1.0 - ev.probability)
|
|
316
316
|
self._agent_output.audio_source.target_volume = tv
|
|
317
317
|
|
|
318
318
|
smoothed_tv = self._agent_output.audio_source.smoothed_volume
|
|
@@ -471,6 +471,7 @@ class VoiceAssistant(utils.EventEmitter[EventTypes]):
|
|
|
471
471
|
)
|
|
472
472
|
|
|
473
473
|
async def _play_speech(self, speech_info: _SpeechInfo) -> None:
|
|
474
|
+
logger.debug("VoiceAssistant._play_speech started")
|
|
474
475
|
MIN_TIME_PLAYED_FOR_COMMIT = 1.5
|
|
475
476
|
|
|
476
477
|
assert (
|
|
@@ -485,7 +486,7 @@ class VoiceAssistant(utils.EventEmitter[EventTypes]):
|
|
|
485
486
|
user_speech_commited = False
|
|
486
487
|
|
|
487
488
|
play_handle = synthesis_handle.play()
|
|
488
|
-
|
|
489
|
+
join_fut = play_handle.join()
|
|
489
490
|
self._playing_synthesis = synthesis_handle
|
|
490
491
|
|
|
491
492
|
def _commit_user_message_if_needed() -> None:
|
|
@@ -507,7 +508,7 @@ class VoiceAssistant(utils.EventEmitter[EventTypes]):
|
|
|
507
508
|
# really quickly (barely audible), we don't want to mark this question as "answered".
|
|
508
509
|
if not is_using_tools and (
|
|
509
510
|
play_handle.time_played < MIN_TIME_PLAYED_FOR_COMMIT
|
|
510
|
-
and not
|
|
511
|
+
and not join_fut.done()
|
|
511
512
|
):
|
|
512
513
|
return
|
|
513
514
|
|
|
@@ -519,9 +520,9 @@ class VoiceAssistant(utils.EventEmitter[EventTypes]):
|
|
|
519
520
|
user_speech_commited = True
|
|
520
521
|
|
|
521
522
|
# wait for the play_handle to finish and check every 1s if the user question should be committed
|
|
522
|
-
while not
|
|
523
|
+
while not join_fut.done():
|
|
523
524
|
await asyncio.wait(
|
|
524
|
-
[
|
|
525
|
+
[join_fut], return_when=asyncio.FIRST_COMPLETED, timeout=1.0
|
|
525
526
|
)
|
|
526
527
|
|
|
527
528
|
_commit_user_message_if_needed()
|
|
@@ -579,7 +580,8 @@ class VoiceAssistant(utils.EventEmitter[EventTypes]):
|
|
|
579
580
|
transcript=_llm_stream_to_str_iterable(answer_stream)
|
|
580
581
|
)
|
|
581
582
|
self._playing_synthesis = answer_synthesis
|
|
582
|
-
|
|
583
|
+
play_handle = answer_synthesis.play()
|
|
584
|
+
await play_handle.join()
|
|
583
585
|
|
|
584
586
|
collected_text = answer_synthesis.collected_text
|
|
585
587
|
interrupted = answer_synthesis.interrupted
|
|
@@ -595,6 +597,8 @@ class VoiceAssistant(utils.EventEmitter[EventTypes]):
|
|
|
595
597
|
else:
|
|
596
598
|
self.emit("agent_speech_committed", msg)
|
|
597
599
|
|
|
600
|
+
logger.debug("VoiceAssistant._play_speech ended")
|
|
601
|
+
|
|
598
602
|
|
|
599
603
|
async def _llm_stream_to_str_iterable(stream: LLMStream) -> AsyncIterable[str]:
|
|
600
604
|
async for chunk in stream:
|
|
@@ -677,6 +681,7 @@ class _DeferredAnswerValidation:
|
|
|
677
681
|
self._last_final_transcript = ""
|
|
678
682
|
self._received_end_of_speech = False
|
|
679
683
|
self._validate_fnc()
|
|
684
|
+
logger.debug("_DeferredAnswerValidation speech validated")
|
|
680
685
|
|
|
681
686
|
def _run(self, delay: float) -> None:
|
|
682
687
|
if self._validating_task is not None:
|
|
@@ -17,6 +17,7 @@ from __future__ import annotations
|
|
|
17
17
|
import asyncio
|
|
18
18
|
import contextlib
|
|
19
19
|
import datetime
|
|
20
|
+
import multiprocessing as mp
|
|
20
21
|
import os
|
|
21
22
|
from dataclasses import dataclass, field
|
|
22
23
|
from functools import reduce
|
|
@@ -35,7 +36,7 @@ from .job import JobAcceptArguments, JobContext, JobProcess, JobRequest, Running
|
|
|
35
36
|
from .log import DEV_LEVEL, logger
|
|
36
37
|
from .version import __version__
|
|
37
38
|
|
|
38
|
-
MAX_RECONNECT_ATTEMPTS = 3
|
|
39
|
+
MAX_RECONNECT_ATTEMPTS = 3
|
|
39
40
|
ASSIGNMENT_TIMEOUT = 7.5
|
|
40
41
|
UPDATE_LOAD_INTERVAL = 10.0
|
|
41
42
|
|
|
@@ -43,9 +44,11 @@ UPDATE_LOAD_INTERVAL = 10.0
|
|
|
43
44
|
def _default_initialize_process_fnc(proc: JobProcess) -> Any:
|
|
44
45
|
return
|
|
45
46
|
|
|
47
|
+
|
|
46
48
|
async def _default_shutdown_fnc(proc: JobContext) -> None:
|
|
47
49
|
return
|
|
48
50
|
|
|
51
|
+
|
|
49
52
|
async def _default_request_fnc(ctx: JobRequest) -> None:
|
|
50
53
|
await ctx.accept()
|
|
51
54
|
|
|
@@ -122,12 +125,17 @@ class Worker(utils.EventEmitter[EventTypes]):
|
|
|
122
125
|
self._pending_assignments: dict[str, asyncio.Future[agent.JobAssignment]] = {}
|
|
123
126
|
self._close_future: asyncio.Future[None] | None = None
|
|
124
127
|
self._msg_chan = utils.aio.Chan[agent.WorkerMessage](128, loop=self._loop)
|
|
128
|
+
|
|
129
|
+
# using spawn context for all platforms. We may have further optimizations for
|
|
130
|
+
# Linux with forkserver, but for now, this is the safest option
|
|
131
|
+
mp_ctx = mp.get_context("spawn")
|
|
125
132
|
self._proc_pool = ipc.proc_pool.ProcPool(
|
|
126
133
|
initialize_process_fnc=opts.prewarm_fnc,
|
|
127
134
|
job_entrypoint_fnc=opts.entrypoint_fnc,
|
|
128
135
|
job_shutdown_fnc=_default_shutdown_fnc,
|
|
129
136
|
num_idle_processes=opts.num_idle_processes,
|
|
130
137
|
loop=self._loop,
|
|
138
|
+
mp_ctx=mp_ctx,
|
|
131
139
|
initialize_timeout=opts.initialize_process_timeout,
|
|
132
140
|
close_timeout=opts.shutdown_process_timeout,
|
|
133
141
|
)
|
|
@@ -240,7 +248,7 @@ class Worker(utils.EventEmitter[EventTypes]):
|
|
|
240
248
|
"""_queue_msg raises aio.ChanClosed when the worker is closing/closed"""
|
|
241
249
|
if self._connecting:
|
|
242
250
|
which = msg.WhichOneof("message")
|
|
243
|
-
if which == "update_worker"
|
|
251
|
+
if which == "update_worker":
|
|
244
252
|
return
|
|
245
253
|
elif which == "ping":
|
|
246
254
|
return
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: livekit-agents
|
|
3
|
-
Version: 0.8.0.
|
|
3
|
+
Version: 0.8.0.dev3
|
|
4
4
|
Summary: LiveKit Python Agents
|
|
5
5
|
Home-page: https://github.com/livekit/agents
|
|
6
6
|
License: Apache-2.0
|
|
@@ -20,9 +20,9 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
20
20
|
Requires-Python: >=3.9.0
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
Requires-Dist: click~=8.1
|
|
23
|
-
Requires-Dist: livekit~=0.
|
|
24
|
-
Requires-Dist: livekit-api~=0.
|
|
25
|
-
Requires-Dist: livekit-protocol~=0.
|
|
23
|
+
Requires-Dist: livekit~=0.12.0.dev1
|
|
24
|
+
Requires-Dist: livekit-api~=0.6.0
|
|
25
|
+
Requires-Dist: livekit-protocol~=0.6.0
|
|
26
26
|
Requires-Dist: protobuf>=3
|
|
27
27
|
Requires-Dist: pyjwt>=2.0.0
|
|
28
28
|
Requires-Dist: types-protobuf<5,>=4
|
|
@@ -48,9 +48,9 @@ setuptools.setup(
|
|
|
48
48
|
python_requires=">=3.9.0",
|
|
49
49
|
install_requires=[
|
|
50
50
|
"click~=8.1",
|
|
51
|
-
"livekit~=0.
|
|
52
|
-
"livekit-api~=0.
|
|
53
|
-
"livekit-protocol~=0.
|
|
51
|
+
"livekit~=0.12.0.dev1",
|
|
52
|
+
"livekit-api~=0.6.0",
|
|
53
|
+
"livekit-protocol~=0.6.0",
|
|
54
54
|
"protobuf>=3",
|
|
55
55
|
"pyjwt>=2.0.0",
|
|
56
56
|
"types-protobuf>=4,<5",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/llm/function_context.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/stt/stream_adapter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/_basic_hyphenator.py
RENAMED
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/_basic_paragraph.py
RENAMED
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/_basic_sent.py
RENAMED
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/_basic_word.py
RENAMED
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/token_stream.py
RENAMED
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tokenize/tokenizer.py
RENAMED
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/transcription/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/tts/stream_adapter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/aio/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/aio/interval.py
RENAMED
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/aio/task_set.py
RENAMED
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/codecs/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/event_emitter.py
RENAMED
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/http_context.py
RENAMED
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/images/__init__.py
RENAMED
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/images/image.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/utils/moving_average.py
RENAMED
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/voice_assistant/__init__.py
RENAMED
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/voice_assistant/log.py
RENAMED
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit/agents/voice_assistant/plotter.py
RENAMED
|
File without changes
|
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit_agents.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{livekit_agents-0.8.0.dev2 → livekit_agents-0.8.0.dev3}/livekit_agents.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|