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.
@@ -32,7 +32,7 @@ from .log import logger
32
32
 
33
33
 
34
34
  class DeepgramPlugin(Plugin):
35
- def __init__(self):
35
+ def __init__(self) -> None:
36
36
  super().__init__(__name__, __version__, __package__, logger)
37
37
 
38
38
 
@@ -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
- self._api_key = api_key if is_given(api_key) else os.environ.get("DEEPGRAM_API_KEY")
185
- if not self._api_key:
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
- [tasks_group, wait_reconnect_task],
572
+ (tasks_group, wait_reconnect_task),
573
573
  return_when=asyncio.FIRST_COMPLETED,
574
- ) # type: ignore
574
+ )
575
575
 
576
576
  # propagate exceptions from completed tasks
577
577
  for task in done:
@@ -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: NotGivenOr[str] = NOT_GIVEN,
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
- self._api_key = api_key if is_given(api_key) else os.environ.get("DEEPGRAM_API_KEY")
76
- if not self._api_key:
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._base_url, websocket=True),
110
- headers={"Authorization": f"Token {self._api_key}"},
113
+ _to_deepgram_url(config, self._opts.base_url, websocket=True),
114
+ headers={"Authorization": f"Token {self._opts.api_key}"},
111
115
  ),
112
- self._conn_options.timeout,
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
- args:
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._opts = opts
199
- self._session = session
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
- config = {
212
- "encoding": self._opts.encoding,
213
- "model": self._opts.model,
214
- "sample_rate": self._opts.sample_rate,
215
- "mip_opt_out": self._opts.mip_opt_out,
216
- }
217
- async with self._session.post(
218
- _to_deepgram_url(config, self._base_url, websocket=False),
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._api_key}",
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(connect=self._conn_options.timeout, total=30),
225
- ) as res:
226
- if res.status != 200:
227
- raise APIStatusError(
228
- message=res.reason or "Unknown error occurred.",
229
- status_code=res.status,
230
- request_id=request_id,
231
- body=await res.json(),
232
- )
233
-
234
- async for bytes_data, _ in res.content.iter_chunks():
235
- for frame in audio_bstream.write(bytes_data):
236
- self._event_ch.send_nowait(
237
- tts.SynthesizedAudio(
238
- request_id=request_id,
239
- frame=frame,
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
- status_code=e.status,
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._opts = opts
274
- self._session = session
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 update_options(
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
- segment_id = utils.shortuuid()
297
- audio_bstream = utils.audio.AudioByteStream(
223
+ output_emitter.initialize(
224
+ request_id=request_id,
298
225
  sample_rate=self._opts.sample_rate,
299
- num_channels=NUM_CHANNELS,
226
+ num_channels=1,
227
+ mime_type="audio/pcm",
228
+ stream=True,
300
229
  )
301
230
 
302
- @utils.log_exceptions(logger=logger)
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
- @utils.log_exceptions(logger=logger)
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
- async for word in word_stream:
323
- speak_msg = {"type": "Speak", "text": f"{word.token} "}
324
- self._mark_started()
325
- await ws.send_str(json.dumps(speak_msg))
326
-
327
- # Always flush after a segment
328
- flush_msg = {"type": "Flush"}
329
- await ws.send_str(json.dumps(flush_msg))
330
-
331
- # after all segments, close
332
- close_msg = {"type": "Close"}
333
- closing_ws = True
334
- await ws.send_str(json.dumps(close_msg))
335
-
336
- async def recv_task(ws: aiohttp.ClientWebSocketResponse):
337
- emitter = tts.SynthesizedAudioEmitter(
338
- event_ch=self._event_ch,
339
- request_id=request_id,
340
- segment_id=segment_id,
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
- if not closing_ws:
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
- data = msg.data
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
- for frame in audio_bstream.flush():
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 def _connection_timeout():
377
- # Deepgram has a 60-minute timeout period for websocket connections
378
- await asyncio.sleep(3300)
379
- logger.warning("Deepgram TTS maximum connection time reached. Reconnecting...")
380
- self._reconnect_event.set()
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
- config = {
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
- if ws is not None and not ws.closed:
438
- await ws.close()
318
+ await utils.aio.gracefully_cancel(*tasks)
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- __version__ = "1.0.23"
15
+ __version__ = "1.1.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: livekit-plugins-deepgram
3
- Version: 1.0.23
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.23
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,,