livekit-plugins-deepgram 1.0.23__py3-none-any.whl → 1.1.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.
- livekit/plugins/deepgram/__init__.py +1 -1
- livekit/plugins/deepgram/stt.py +12 -12
- livekit/plugins/deepgram/tts.py +114 -234
- livekit/plugins/deepgram/version.py +1 -1
- {livekit_plugins_deepgram-1.0.23.dist-info → livekit_plugins_deepgram-1.1.0.dist-info}/METADATA +2 -2
- livekit_plugins_deepgram-1.1.0.dist-info/RECORD +11 -0
- livekit_plugins_deepgram-1.0.23.dist-info/RECORD +0 -11
- {livekit_plugins_deepgram-1.0.23.dist-info → livekit_plugins_deepgram-1.1.0.dist-info}/WHEEL +0 -0
livekit/plugins/deepgram/stt.py
CHANGED
@@ -94,7 +94,7 @@ class AudioEnergyFilter:
|
|
94
94
|
|
95
95
|
@dataclass
|
96
96
|
class STTOptions:
|
97
|
-
language: DeepgramLanguages | str
|
97
|
+
language: DeepgramLanguages | str | None
|
98
98
|
detect_language: bool
|
99
99
|
interim_results: bool
|
100
100
|
punctuate: bool
|
@@ -181,9 +181,10 @@ class STT(stt.STT):
|
|
181
181
|
)
|
182
182
|
self._base_url = base_url
|
183
183
|
|
184
|
-
|
185
|
-
if not
|
184
|
+
deepgram_api_key = api_key if is_given(api_key) else os.environ.get("DEEPGRAM_API_KEY")
|
185
|
+
if not deepgram_api_key:
|
186
186
|
raise ValueError("Deepgram API key is required")
|
187
|
+
self._api_key = deepgram_api_key
|
187
188
|
|
188
189
|
model = _validate_model(model, language)
|
189
190
|
_validate_keyterms(model, language, keyterms, keywords)
|
@@ -305,7 +306,7 @@ class STT(stt.STT):
|
|
305
306
|
numerals: NotGivenOr[bool] = NOT_GIVEN,
|
306
307
|
mip_opt_out: NotGivenOr[bool] = NOT_GIVEN,
|
307
308
|
tags: NotGivenOr[list[str]] = NOT_GIVEN,
|
308
|
-
):
|
309
|
+
) -> None:
|
309
310
|
if is_given(language):
|
310
311
|
self._opts.language = language
|
311
312
|
if is_given(model):
|
@@ -383,14 +384,13 @@ class SpeechStream(stt.SpeechStream):
|
|
383
384
|
http_session: aiohttp.ClientSession,
|
384
385
|
base_url: str,
|
385
386
|
) -> None:
|
386
|
-
super().__init__(stt=stt, conn_options=conn_options, sample_rate=opts.sample_rate)
|
387
|
-
|
388
387
|
if opts.detect_language or opts.language is None:
|
389
388
|
raise ValueError(
|
390
389
|
"language detection is not supported in streaming mode, "
|
391
390
|
"please disable it and specify a language"
|
392
391
|
)
|
393
392
|
|
393
|
+
super().__init__(stt=stt, conn_options=conn_options, sample_rate=opts.sample_rate)
|
394
394
|
self._opts = opts
|
395
395
|
self._api_key = api_key
|
396
396
|
self._session = http_session
|
@@ -429,7 +429,7 @@ class SpeechStream(stt.SpeechStream):
|
|
429
429
|
numerals: NotGivenOr[bool] = NOT_GIVEN,
|
430
430
|
mip_opt_out: NotGivenOr[bool] = NOT_GIVEN,
|
431
431
|
tags: NotGivenOr[list[str]] = NOT_GIVEN,
|
432
|
-
):
|
432
|
+
) -> None:
|
433
433
|
if is_given(language):
|
434
434
|
self._opts.language = language
|
435
435
|
if is_given(model):
|
@@ -466,7 +466,7 @@ class SpeechStream(stt.SpeechStream):
|
|
466
466
|
async def _run(self) -> None:
|
467
467
|
closing_ws = False
|
468
468
|
|
469
|
-
async def keepalive_task(ws: aiohttp.ClientWebSocketResponse):
|
469
|
+
async def keepalive_task(ws: aiohttp.ClientWebSocketResponse) -> None:
|
470
470
|
# if we want to keep the connection alive even if no audio is sent,
|
471
471
|
# Deepgram expects a keepalive message.
|
472
472
|
# https://developers.deepgram.com/reference/listen-live#stream-keepalive
|
@@ -478,7 +478,7 @@ class SpeechStream(stt.SpeechStream):
|
|
478
478
|
return
|
479
479
|
|
480
480
|
@utils.log_exceptions(logger=logger)
|
481
|
-
async def send_task(ws: aiohttp.ClientWebSocketResponse):
|
481
|
+
async def send_task(ws: aiohttp.ClientWebSocketResponse) -> None:
|
482
482
|
nonlocal closing_ws
|
483
483
|
|
484
484
|
# forward audio to deepgram in chunks of 50ms
|
@@ -529,7 +529,7 @@ class SpeechStream(stt.SpeechStream):
|
|
529
529
|
await ws.send_str(SpeechStream._CLOSE_MSG)
|
530
530
|
|
531
531
|
@utils.log_exceptions(logger=logger)
|
532
|
-
async def recv_task(ws: aiohttp.ClientWebSocketResponse):
|
532
|
+
async def recv_task(ws: aiohttp.ClientWebSocketResponse) -> None:
|
533
533
|
nonlocal closing_ws
|
534
534
|
while True:
|
535
535
|
msg = await ws.receive()
|
@@ -569,9 +569,9 @@ class SpeechStream(stt.SpeechStream):
|
|
569
569
|
wait_reconnect_task = asyncio.create_task(self._reconnect_event.wait())
|
570
570
|
try:
|
571
571
|
done, _ = await asyncio.wait(
|
572
|
-
|
572
|
+
(tasks_group, wait_reconnect_task),
|
573
573
|
return_when=asyncio.FIRST_COMPLETED,
|
574
|
-
)
|
574
|
+
)
|
575
575
|
|
576
576
|
# propagate exceptions from completed tasks
|
577
577
|
for task in done:
|
livekit/plugins/deepgram/tts.py
CHANGED
@@ -4,7 +4,7 @@ import asyncio
|
|
4
4
|
import json
|
5
5
|
import os
|
6
6
|
import weakref
|
7
|
-
from dataclasses import dataclass
|
7
|
+
from dataclasses import dataclass, replace
|
8
8
|
|
9
9
|
import aiohttp
|
10
10
|
|
@@ -37,6 +37,8 @@ class _TTSOptions:
|
|
37
37
|
encoding: str
|
38
38
|
sample_rate: int
|
39
39
|
word_tokenizer: tokenize.WordTokenizer
|
40
|
+
base_url: str
|
41
|
+
api_key: str
|
40
42
|
mip_opt_out: bool = False
|
41
43
|
|
42
44
|
|
@@ -47,7 +49,7 @@ class TTS(tts.TTS):
|
|
47
49
|
model: str = "aura-2-andromeda-en",
|
48
50
|
encoding: str = "linear16",
|
49
51
|
sample_rate: int = 24000,
|
50
|
-
api_key:
|
52
|
+
api_key: str | None = None,
|
51
53
|
base_url: str = BASE_URL,
|
52
54
|
word_tokenizer: NotGivenOr[tokenize.WordTokenizer] = NOT_GIVEN,
|
53
55
|
http_session: aiohttp.ClientSession | None = None,
|
@@ -72,8 +74,8 @@ class TTS(tts.TTS):
|
|
72
74
|
num_channels=NUM_CHANNELS,
|
73
75
|
)
|
74
76
|
|
75
|
-
|
76
|
-
if not
|
77
|
+
api_key = api_key or os.environ.get("DEEPGRAM_API_KEY")
|
78
|
+
if not api_key:
|
77
79
|
raise ValueError("Deepgram API key required. Set DEEPGRAM_API_KEY or provide api_key.")
|
78
80
|
|
79
81
|
if not is_given(word_tokenizer):
|
@@ -84,11 +86,13 @@ class TTS(tts.TTS):
|
|
84
86
|
encoding=encoding,
|
85
87
|
sample_rate=sample_rate,
|
86
88
|
word_tokenizer=word_tokenizer,
|
89
|
+
base_url=base_url,
|
90
|
+
api_key=api_key,
|
87
91
|
mip_opt_out=mip_opt_out,
|
88
92
|
)
|
89
93
|
self._session = http_session
|
90
|
-
self._base_url = base_url
|
91
94
|
self._streams = weakref.WeakSet[SynthesizeStream]()
|
95
|
+
|
92
96
|
self._pool = utils.ConnectionPool[aiohttp.ClientWebSocketResponse](
|
93
97
|
connect_cb=self._connect_ws,
|
94
98
|
close_cb=self._close_ws,
|
@@ -96,7 +100,7 @@ class TTS(tts.TTS):
|
|
96
100
|
mark_refreshed_on_get=False,
|
97
101
|
)
|
98
102
|
|
99
|
-
async def _connect_ws(self) -> aiohttp.ClientWebSocketResponse:
|
103
|
+
async def _connect_ws(self, timeout: float) -> aiohttp.ClientWebSocketResponse:
|
100
104
|
session = self._ensure_session()
|
101
105
|
config = {
|
102
106
|
"encoding": self._opts.encoding,
|
@@ -106,13 +110,13 @@ class TTS(tts.TTS):
|
|
106
110
|
}
|
107
111
|
return await asyncio.wait_for(
|
108
112
|
session.ws_connect(
|
109
|
-
_to_deepgram_url(config, self.
|
110
|
-
headers={"Authorization": f"Token {self.
|
113
|
+
_to_deepgram_url(config, self._opts.base_url, websocket=True),
|
114
|
+
headers={"Authorization": f"Token {self._opts.api_key}"},
|
111
115
|
),
|
112
|
-
|
116
|
+
timeout,
|
113
117
|
)
|
114
118
|
|
115
|
-
async def _close_ws(self, ws: aiohttp.ClientWebSocketResponse):
|
119
|
+
async def _close_ws(self, ws: aiohttp.ClientWebSocketResponse) -> None:
|
116
120
|
await ws.close()
|
117
121
|
|
118
122
|
def _ensure_session(self) -> aiohttp.ClientSession:
|
@@ -124,50 +128,23 @@ class TTS(tts.TTS):
|
|
124
128
|
self,
|
125
129
|
*,
|
126
130
|
model: NotGivenOr[str] = NOT_GIVEN,
|
127
|
-
sample_rate: NotGivenOr[int] = NOT_GIVEN,
|
128
131
|
) -> None:
|
129
132
|
"""
|
130
|
-
|
133
|
+
Args:
|
131
134
|
model (str): TTS model to use.
|
132
|
-
sample_rate (int): Sample rate of audio.
|
133
135
|
"""
|
134
136
|
if is_given(model):
|
135
137
|
self._opts.model = model
|
136
|
-
if is_given(sample_rate):
|
137
|
-
self._opts.sample_rate = sample_rate
|
138
|
-
for stream in self._streams:
|
139
|
-
stream.update_options(
|
140
|
-
model=model,
|
141
|
-
sample_rate=sample_rate,
|
142
|
-
)
|
143
138
|
|
144
139
|
def synthesize(
|
145
|
-
self,
|
146
|
-
text: str,
|
147
|
-
*,
|
148
|
-
conn_options: APIConnectOptions = DEFAULT_API_CONNECT_OPTIONS,
|
140
|
+
self, text: str, *, conn_options: APIConnectOptions = DEFAULT_API_CONNECT_OPTIONS
|
149
141
|
) -> ChunkedStream:
|
150
|
-
return ChunkedStream(
|
151
|
-
tts=self,
|
152
|
-
input_text=text,
|
153
|
-
base_url=self._base_url,
|
154
|
-
api_key=self._api_key,
|
155
|
-
conn_options=conn_options,
|
156
|
-
opts=self._opts,
|
157
|
-
session=self._ensure_session(),
|
158
|
-
)
|
142
|
+
return ChunkedStream(tts=self, input_text=text, conn_options=conn_options)
|
159
143
|
|
160
144
|
def stream(
|
161
145
|
self, *, conn_options: APIConnectOptions = DEFAULT_API_CONNECT_OPTIONS
|
162
146
|
) -> SynthesizeStream:
|
163
|
-
stream = SynthesizeStream(
|
164
|
-
tts=self,
|
165
|
-
conn_options=conn_options,
|
166
|
-
base_url=self._base_url,
|
167
|
-
api_key=self._api_key,
|
168
|
-
opts=self._opts,
|
169
|
-
session=self._ensure_session(),
|
170
|
-
)
|
147
|
+
stream = SynthesizeStream(tts=self, conn_options=conn_options)
|
171
148
|
self._streams.add(stream)
|
172
149
|
return stream
|
173
150
|
|
@@ -177,130 +154,81 @@ class TTS(tts.TTS):
|
|
177
154
|
async def aclose(self) -> None:
|
178
155
|
for stream in list(self._streams):
|
179
156
|
await stream.aclose()
|
157
|
+
|
180
158
|
self._streams.clear()
|
159
|
+
|
181
160
|
await self._pool.aclose()
|
182
|
-
await super().aclose()
|
183
161
|
|
184
162
|
|
185
163
|
class ChunkedStream(tts.ChunkedStream):
|
186
|
-
def __init__(
|
187
|
-
self,
|
188
|
-
*,
|
189
|
-
tts: TTS,
|
190
|
-
base_url: str,
|
191
|
-
api_key: str,
|
192
|
-
input_text: str,
|
193
|
-
opts: _TTSOptions,
|
194
|
-
session: aiohttp.ClientSession,
|
195
|
-
conn_options: APIConnectOptions,
|
196
|
-
) -> None:
|
164
|
+
def __init__(self, *, tts: TTS, input_text: str, conn_options: APIConnectOptions) -> None:
|
197
165
|
super().__init__(tts=tts, input_text=input_text, conn_options=conn_options)
|
198
|
-
self.
|
199
|
-
self.
|
200
|
-
self._base_url = base_url
|
201
|
-
self._api_key = api_key
|
202
|
-
|
203
|
-
async def _run(self) -> None:
|
204
|
-
request_id = utils.shortuuid()
|
205
|
-
audio_bstream = utils.audio.AudioByteStream(
|
206
|
-
sample_rate=self._opts.sample_rate,
|
207
|
-
num_channels=NUM_CHANNELS,
|
208
|
-
)
|
166
|
+
self._tts: TTS = tts
|
167
|
+
self._opts = replace(tts._opts)
|
209
168
|
|
169
|
+
async def _run(self, output_emitter: tts.AudioEmitter) -> None:
|
210
170
|
try:
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
171
|
+
async with self._tts._ensure_session().post(
|
172
|
+
_to_deepgram_url(
|
173
|
+
{
|
174
|
+
"encoding": self._opts.encoding,
|
175
|
+
"container": "none",
|
176
|
+
"model": self._opts.model,
|
177
|
+
"sample_rate": self._opts.sample_rate,
|
178
|
+
"mip_opt_out": self._opts.mip_opt_out,
|
179
|
+
},
|
180
|
+
self._opts.base_url,
|
181
|
+
websocket=False,
|
182
|
+
),
|
219
183
|
headers={
|
220
|
-
"Authorization": f"Token {self.
|
184
|
+
"Authorization": f"Token {self._opts.api_key}",
|
221
185
|
"Content-Type": "application/json",
|
222
186
|
},
|
223
187
|
json={"text": self._input_text},
|
224
|
-
timeout=aiohttp.ClientTimeout(
|
225
|
-
) as
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
for frame in audio_bstream.flush():
|
244
|
-
self._event_ch.send_nowait(
|
245
|
-
tts.SynthesizedAudio(request_id=request_id, frame=frame)
|
246
|
-
)
|
247
|
-
|
248
|
-
except asyncio.TimeoutError as e:
|
249
|
-
raise APITimeoutError() from e
|
188
|
+
timeout=aiohttp.ClientTimeout(total=30, sock_connect=self._conn_options.timeout),
|
189
|
+
) as resp:
|
190
|
+
resp.raise_for_status()
|
191
|
+
|
192
|
+
output_emitter.initialize(
|
193
|
+
request_id=utils.shortuuid(),
|
194
|
+
sample_rate=self._opts.sample_rate,
|
195
|
+
num_channels=NUM_CHANNELS,
|
196
|
+
mime_type="audio/pcm",
|
197
|
+
)
|
198
|
+
|
199
|
+
async for data, _ in resp.content.iter_chunks():
|
200
|
+
output_emitter.push(data)
|
201
|
+
|
202
|
+
output_emitter.flush()
|
203
|
+
|
204
|
+
except asyncio.TimeoutError:
|
205
|
+
raise APITimeoutError() from None
|
250
206
|
except aiohttp.ClientResponseError as e:
|
251
207
|
raise APIStatusError(
|
252
|
-
message=e.message,
|
253
|
-
|
254
|
-
request_id=request_id,
|
255
|
-
body=None,
|
256
|
-
) from e
|
208
|
+
message=e.message, status_code=e.status, request_id=None, body=None
|
209
|
+
) from None
|
257
210
|
except Exception as e:
|
258
211
|
raise APIConnectionError() from e
|
259
212
|
|
260
213
|
|
261
214
|
class SynthesizeStream(tts.SynthesizeStream):
|
262
|
-
def __init__(
|
263
|
-
self,
|
264
|
-
*,
|
265
|
-
tts: TTS,
|
266
|
-
base_url: str,
|
267
|
-
api_key: str,
|
268
|
-
opts: _TTSOptions,
|
269
|
-
session: aiohttp.ClientSession,
|
270
|
-
conn_options: APIConnectOptions,
|
271
|
-
):
|
215
|
+
def __init__(self, *, tts: TTS, conn_options: APIConnectOptions):
|
272
216
|
super().__init__(tts=tts, conn_options=conn_options)
|
273
|
-
self.
|
274
|
-
self.
|
275
|
-
self._base_url = base_url
|
276
|
-
self._api_key = api_key
|
217
|
+
self._tts: TTS = tts
|
218
|
+
self._opts = replace(tts._opts)
|
277
219
|
self._segments_ch = utils.aio.Chan[tokenize.WordStream]()
|
278
|
-
self._reconnect_event = asyncio.Event()
|
279
220
|
|
280
|
-
def
|
281
|
-
self,
|
282
|
-
*,
|
283
|
-
model: NotGivenOr[str] = NOT_GIVEN,
|
284
|
-
sample_rate: NotGivenOr[int] = NOT_GIVEN,
|
285
|
-
) -> None:
|
286
|
-
if is_given(model):
|
287
|
-
self._opts.model = model
|
288
|
-
if is_given(sample_rate):
|
289
|
-
self._opts.sample_rate = sample_rate
|
290
|
-
|
291
|
-
self._reconnect_event.set()
|
292
|
-
|
293
|
-
async def _run(self) -> None:
|
294
|
-
closing_ws = False
|
221
|
+
async def _run(self, output_emitter: tts.AudioEmitter) -> None:
|
295
222
|
request_id = utils.shortuuid()
|
296
|
-
|
297
|
-
|
223
|
+
output_emitter.initialize(
|
224
|
+
request_id=request_id,
|
298
225
|
sample_rate=self._opts.sample_rate,
|
299
|
-
num_channels=
|
226
|
+
num_channels=1,
|
227
|
+
mime_type="audio/pcm",
|
228
|
+
stream=True,
|
300
229
|
)
|
301
230
|
|
302
|
-
|
303
|
-
async def _tokenize_input():
|
231
|
+
async def _tokenize_input() -> None:
|
304
232
|
# Converts incoming text into WordStreams and sends them into _segments_ch
|
305
233
|
word_stream = None
|
306
234
|
async for input in self._input_ch:
|
@@ -313,33 +241,47 @@ class SynthesizeStream(tts.SynthesizeStream):
|
|
313
241
|
if word_stream:
|
314
242
|
word_stream.end_input()
|
315
243
|
word_stream = None
|
244
|
+
|
316
245
|
self._segments_ch.close()
|
317
246
|
|
318
|
-
|
319
|
-
async def _run_segments(ws: aiohttp.ClientWebSocketResponse):
|
320
|
-
nonlocal closing_ws
|
247
|
+
async def _run_segments() -> None:
|
321
248
|
async for word_stream in self._segments_ch:
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
249
|
+
await self._run_ws(word_stream, output_emitter)
|
250
|
+
|
251
|
+
tasks = [
|
252
|
+
asyncio.create_task(_tokenize_input()),
|
253
|
+
asyncio.create_task(_run_segments()),
|
254
|
+
]
|
255
|
+
try:
|
256
|
+
await asyncio.gather(*tasks)
|
257
|
+
except asyncio.TimeoutError:
|
258
|
+
raise APITimeoutError() from None
|
259
|
+
except aiohttp.ClientResponseError as e:
|
260
|
+
raise APIStatusError(
|
261
|
+
message=e.message, status_code=e.status, request_id=request_id, body=None
|
262
|
+
) from None
|
263
|
+
except Exception as e:
|
264
|
+
raise APIConnectionError() from e
|
265
|
+
finally:
|
266
|
+
await utils.aio.gracefully_cancel(*tasks)
|
267
|
+
|
268
|
+
async def _run_ws(
|
269
|
+
self, word_stream: tokenize.WordStream, output_emitter: tts.AudioEmitter
|
270
|
+
) -> None:
|
271
|
+
segment_id = utils.shortuuid()
|
272
|
+
output_emitter.start_segment(segment_id=segment_id)
|
273
|
+
|
274
|
+
async def send_task(ws: aiohttp.ClientWebSocketResponse) -> None:
|
275
|
+
async for word in word_stream:
|
276
|
+
speak_msg = {"type": "Speak", "text": f"{word.token} "}
|
277
|
+
self._mark_started()
|
278
|
+
await ws.send_str(json.dumps(speak_msg))
|
342
279
|
|
280
|
+
# Always flush after a segment
|
281
|
+
flush_msg = {"type": "Flush"}
|
282
|
+
await ws.send_str(json.dumps(flush_msg))
|
283
|
+
|
284
|
+
async def recv_task(ws: aiohttp.ClientWebSocketResponse) -> None:
|
343
285
|
while True:
|
344
286
|
msg = await ws.receive()
|
345
287
|
if msg.type in (
|
@@ -347,24 +289,15 @@ class SynthesizeStream(tts.SynthesizeStream):
|
|
347
289
|
aiohttp.WSMsgType.CLOSED,
|
348
290
|
aiohttp.WSMsgType.CLOSING,
|
349
291
|
):
|
350
|
-
|
351
|
-
raise APIStatusError(
|
352
|
-
"Deepgram websocket connection closed unexpectedly",
|
353
|
-
request_id=request_id,
|
354
|
-
)
|
355
|
-
return
|
292
|
+
raise APIStatusError("Deepgram websocket connection closed unexpectedly")
|
356
293
|
|
357
294
|
if msg.type == aiohttp.WSMsgType.BINARY:
|
358
|
-
|
359
|
-
for frame in audio_bstream.write(data):
|
360
|
-
emitter.push(frame)
|
295
|
+
output_emitter.push(msg.data)
|
361
296
|
elif msg.type == aiohttp.WSMsgType.TEXT:
|
362
297
|
resp = json.loads(msg.data)
|
363
298
|
mtype = resp.get("type")
|
364
299
|
if mtype == "Flushed":
|
365
|
-
|
366
|
-
emitter.push(frame)
|
367
|
-
emitter.flush()
|
300
|
+
output_emitter.end_segment()
|
368
301
|
break
|
369
302
|
elif mtype == "Warning":
|
370
303
|
logger.warning("Deepgram warning: %s", resp.get("warn_msg"))
|
@@ -373,66 +306,13 @@ class SynthesizeStream(tts.SynthesizeStream):
|
|
373
306
|
else:
|
374
307
|
logger.debug("Unknown message type: %s", resp)
|
375
308
|
|
376
|
-
async
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
309
|
+
async with self._tts._pool.connection(timeout=self._conn_options.timeout) as ws:
|
310
|
+
tasks = [
|
311
|
+
asyncio.create_task(send_task(ws)),
|
312
|
+
asyncio.create_task(recv_task(ws)),
|
313
|
+
]
|
381
314
|
|
382
|
-
ws: aiohttp.ClientWebSocketResponse | None = None
|
383
|
-
while True:
|
384
315
|
try:
|
385
|
-
|
386
|
-
"encoding": self._opts.encoding,
|
387
|
-
"model": self._opts.model,
|
388
|
-
"sample_rate": self._opts.sample_rate,
|
389
|
-
"mip_opt_out": self._opts.mip_opt_out,
|
390
|
-
}
|
391
|
-
ws = await asyncio.wait_for(
|
392
|
-
self._session.ws_connect(
|
393
|
-
_to_deepgram_url(config, self._base_url, websocket=True),
|
394
|
-
headers={"Authorization": f"Token {self._api_key}"},
|
395
|
-
),
|
396
|
-
self._conn_options.timeout,
|
397
|
-
)
|
398
|
-
closing_ws = False
|
399
|
-
|
400
|
-
tasks = [
|
401
|
-
asyncio.create_task(_tokenize_input()),
|
402
|
-
asyncio.create_task(_run_segments(ws)),
|
403
|
-
asyncio.create_task(recv_task(ws)),
|
404
|
-
]
|
405
|
-
wait_reconnect_task = asyncio.create_task(self._reconnect_event.wait())
|
406
|
-
connection_timeout_task = asyncio.create_task(_connection_timeout())
|
407
|
-
|
408
|
-
try:
|
409
|
-
done, _ = await asyncio.wait(
|
410
|
-
[
|
411
|
-
asyncio.gather(*tasks),
|
412
|
-
wait_reconnect_task,
|
413
|
-
connection_timeout_task,
|
414
|
-
],
|
415
|
-
return_when=asyncio.FIRST_COMPLETED,
|
416
|
-
) # type: ignore
|
417
|
-
if wait_reconnect_task not in done:
|
418
|
-
break
|
419
|
-
self._reconnect_event.clear()
|
420
|
-
finally:
|
421
|
-
await utils.aio.gracefully_cancel(
|
422
|
-
*tasks, wait_reconnect_task, connection_timeout_task
|
423
|
-
)
|
424
|
-
|
425
|
-
except asyncio.TimeoutError as e:
|
426
|
-
raise APITimeoutError() from e
|
427
|
-
except aiohttp.ClientResponseError as e:
|
428
|
-
raise APIStatusError(
|
429
|
-
message=e.message,
|
430
|
-
status_code=e.status,
|
431
|
-
request_id=request_id,
|
432
|
-
body=None,
|
433
|
-
) from e
|
434
|
-
except Exception as e:
|
435
|
-
raise APIConnectionError() from e
|
316
|
+
await asyncio.gather(*tasks)
|
436
317
|
finally:
|
437
|
-
|
438
|
-
await ws.close()
|
318
|
+
await utils.aio.gracefully_cancel(*tasks)
|
{livekit_plugins_deepgram-1.0.23.dist-info → livekit_plugins_deepgram-1.1.0.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: livekit-plugins-deepgram
|
3
|
-
Version: 1.0
|
3
|
+
Version: 1.1.0
|
4
4
|
Summary: Agent Framework plugin for services using Deepgram's API.
|
5
5
|
Project-URL: Documentation, https://docs.livekit.io
|
6
6
|
Project-URL: Website, https://livekit.io/
|
@@ -18,7 +18,7 @@ Classifier: Topic :: Multimedia :: Sound/Audio
|
|
18
18
|
Classifier: Topic :: Multimedia :: Video
|
19
19
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
20
20
|
Requires-Python: >=3.9.0
|
21
|
-
Requires-Dist: livekit-agents[codecs]>=1.0
|
21
|
+
Requires-Dist: livekit-agents[codecs]>=1.1.0
|
22
22
|
Requires-Dist: numpy>=1.26
|
23
23
|
Description-Content-Type: text/markdown
|
24
24
|
|
@@ -0,0 +1,11 @@
|
|
1
|
+
livekit/plugins/deepgram/__init__.py,sha256=GCCbVaiaD8Bw74F34l8Z1_yUCFDvgy6Tg4CnViUPJiI,1366
|
2
|
+
livekit/plugins/deepgram/_utils.py,sha256=NgeR4qKZOeqs1wr8v4G2Q_KPZ5xUSFDE4f2N6WXnZH4,2041
|
3
|
+
livekit/plugins/deepgram/log.py,sha256=isjd2-ROJXiDFhRRnqRmYxv16U5H9dBV6ut2g5bU7q0,71
|
4
|
+
livekit/plugins/deepgram/models.py,sha256=dVguYc9AfjlexreN_O1C0NxX3q-ZK9k8s5B3hWsbtZ0,1236
|
5
|
+
livekit/plugins/deepgram/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
+
livekit/plugins/deepgram/stt.py,sha256=w-T8J4hltd-hQUMfOuw54tMewhrakxJQ6IFWHrIyV4k,32102
|
7
|
+
livekit/plugins/deepgram/tts.py,sha256=BDfzJ6PyHvixs0yNJPVthyAwoGxMcctVetR7v9k8bqg,11198
|
8
|
+
livekit/plugins/deepgram/version.py,sha256=7SjyflIFTjH0djSotKGIRoRykPCqMpVYetIlvHMFuh0,600
|
9
|
+
livekit_plugins_deepgram-1.1.0.dist-info/METADATA,sha256=aCOQc5Ka6yp68RRH1m0xRMJSb0QXkmQw7uahPOCiL-s,1448
|
10
|
+
livekit_plugins_deepgram-1.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
11
|
+
livekit_plugins_deepgram-1.1.0.dist-info/RECORD,,
|
@@ -1,11 +0,0 @@
|
|
1
|
-
livekit/plugins/deepgram/__init__.py,sha256=7sCI3vSCelS8mjtjaIZP_tt61S8KKncEBnuf0urNejw,1358
|
2
|
-
livekit/plugins/deepgram/_utils.py,sha256=NgeR4qKZOeqs1wr8v4G2Q_KPZ5xUSFDE4f2N6WXnZH4,2041
|
3
|
-
livekit/plugins/deepgram/log.py,sha256=isjd2-ROJXiDFhRRnqRmYxv16U5H9dBV6ut2g5bU7q0,71
|
4
|
-
livekit/plugins/deepgram/models.py,sha256=dVguYc9AfjlexreN_O1C0NxX3q-ZK9k8s5B3hWsbtZ0,1236
|
5
|
-
livekit/plugins/deepgram/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
livekit/plugins/deepgram/stt.py,sha256=snDTVjK6r0QJraLL0HzyvCqcy-bAJwuMNWlbJMLFybs,32025
|
7
|
-
livekit/plugins/deepgram/tts.py,sha256=PgQfOJFcBoWzOhTJZEnipGo0hxhBzZNqZhtVW9txmUw,15415
|
8
|
-
livekit/plugins/deepgram/version.py,sha256=BRUqwxRBnPVqEcIODJdaZHGAanu4zkwM4NsAQjNtUEM,601
|
9
|
-
livekit_plugins_deepgram-1.0.23.dist-info/METADATA,sha256=oCc1m7tsmC54XexfzVeuFKXd6JrhtwgosCrdnIaffE4,1450
|
10
|
-
livekit_plugins_deepgram-1.0.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
11
|
-
livekit_plugins_deepgram-1.0.23.dist-info/RECORD,,
|
{livekit_plugins_deepgram-1.0.23.dist-info → livekit_plugins_deepgram-1.1.0.dist-info}/WHEEL
RENAMED
File without changes
|