livekit-plugins-volcenginee 1.3.12__tar.gz → 1.3.13__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.12 → livekit_plugins_volcenginee-1.3.13}/PKG-INFO +61 -2
- {livekit_plugins_volcenginee-1.3.12 → livekit_plugins_volcenginee-1.3.13}/README.md +59 -0
- {livekit_plugins_volcenginee-1.3.12 → livekit_plugins_volcenginee-1.3.13}/livekit/plugins/volcengine/stt.py +147 -47
- livekit_plugins_volcenginee-1.3.13/livekit/plugins/volcengine/version.py +1 -0
- {livekit_plugins_volcenginee-1.3.12 → livekit_plugins_volcenginee-1.3.13}/pyproject.toml +2 -4
- livekit_plugins_volcenginee-1.3.12/livekit/plugins/volcengine/version.py +0 -1
- {livekit_plugins_volcenginee-1.3.12 → livekit_plugins_volcenginee-1.3.13}/.gitignore +0 -0
- {livekit_plugins_volcenginee-1.3.12 → livekit_plugins_volcenginee-1.3.13}/livekit/plugins/volcengine/__init__.py +0 -0
- {livekit_plugins_volcenginee-1.3.12 → livekit_plugins_volcenginee-1.3.13}/livekit/plugins/volcengine/llm.py +0 -0
- {livekit_plugins_volcenginee-1.3.12 → livekit_plugins_volcenginee-1.3.13}/livekit/plugins/volcengine/log.py +0 -0
- {livekit_plugins_volcenginee-1.3.12 → livekit_plugins_volcenginee-1.3.13}/livekit/plugins/volcengine/py.typed +0 -0
- {livekit_plugins_volcenginee-1.3.12 → livekit_plugins_volcenginee-1.3.13}/livekit/plugins/volcengine/realtime.py +0 -0
- {livekit_plugins_volcenginee-1.3.12 → livekit_plugins_volcenginee-1.3.13}/livekit/plugins/volcengine/tts.py +0 -0
- {livekit_plugins_volcenginee-1.3.12 → livekit_plugins_volcenginee-1.3.13}/livekit/plugins/volcengine/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: livekit-plugins-volcenginee
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.13
|
|
4
4
|
Summary: LiveKit Agent Plugins for Volcengine
|
|
5
5
|
Author-email: linsanzhu <2287225730@qq.com>
|
|
6
6
|
Keywords: audio,livekit,realtime,video,webrtc
|
|
@@ -13,7 +13,7 @@ Classifier: Topic :: Multimedia :: Sound/Audio
|
|
|
13
13
|
Classifier: Topic :: Multimedia :: Video
|
|
14
14
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
15
|
Requires-Python: >=3.10
|
|
16
|
-
Requires-Dist: livekit-agents
|
|
16
|
+
Requires-Dist: livekit-agents>=1.5.13
|
|
17
17
|
Requires-Dist: numpy
|
|
18
18
|
Requires-Dist: openai>=1.80.0
|
|
19
19
|
Requires-Dist: osc-data~=0.3.1
|
|
@@ -434,12 +434,62 @@ volcengine.STT(
|
|
|
434
434
|
enable_itn: bool = False,
|
|
435
435
|
enable_punc: bool = True,
|
|
436
436
|
enable_ddc: bool = False,
|
|
437
|
+
enable_diarization: bool = False, # 开启说话人分离(同时启用性别识别)
|
|
437
438
|
vad_segment_duration: int = 3000,
|
|
438
439
|
end_window_size: int = 500,
|
|
439
440
|
force_to_speech_time: int = 1000,
|
|
440
441
|
)
|
|
441
442
|
```
|
|
442
443
|
|
|
444
|
+
#### 说话人分离(Diarization)
|
|
445
|
+
|
|
446
|
+
把 `enable_diarization=True` 打开后,插件会:
|
|
447
|
+
|
|
448
|
+
1. 在 `STTCapabilities.diarization` 上声明能力,符合
|
|
449
|
+
[LiveKit STT 文档](https://docs.livekit.io/agents/models/stt/#speaker-diarization)
|
|
450
|
+
要求的三件事之一。
|
|
451
|
+
2. 在每条 `SpeechData` 上填上来自服务端 `additions.speaker` 的 `speaker_id`(形如
|
|
452
|
+
`"speaker_1"`)。
|
|
453
|
+
3. 在 `SpeechData.metadata["gender"]` 里带上 `additions.gender`(按官方文档,性别
|
|
454
|
+
只在 `definite=true` 的分句上返回,所以中间结果不会有)。
|
|
455
|
+
4. 按分句拆分 `utterances` 数组,每段独立事件(START → INTERIM/FINAL → END),
|
|
456
|
+
而不是把多段合并到一个事件里——这样下游的 `MultiSpeakerAdapter` 能在段粒度上
|
|
457
|
+
决定谁是主说话人、要不要抑制背景。
|
|
458
|
+
|
|
459
|
+
> 注意:插件只做"原始说话人分离"的暴露,**不**做主说话人判断或背景抑制。这两个
|
|
460
|
+
> 策略属于 `MultiSpeakerAdapter`。典型用法:
|
|
461
|
+
|
|
462
|
+
```python
|
|
463
|
+
from livekit.agents import Agent, AgentSession, JobContext, cli, WorkerOptions
|
|
464
|
+
from livekit.agents.stt import MultiSpeakerAdapter
|
|
465
|
+
from livekit.plugins import volcengine
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
async def entry_point(ctx: JobContext):
|
|
469
|
+
raw_stt = volcengine.STT(
|
|
470
|
+
api_key="your_api_key",
|
|
471
|
+
enable_diarization=True, # 暴露每段 speaker_id + gender
|
|
472
|
+
)
|
|
473
|
+
# 用官方适配器做主说话人检测 / 背景抑制
|
|
474
|
+
stt = MultiSpeakerAdapter(
|
|
475
|
+
raw_stt,
|
|
476
|
+
detect_primary_speaker=True, # 默认 True:识别谁是主说话人
|
|
477
|
+
suppress_background_speaker=False, # True 则只把主说话人文本交给 agent
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
agent = Agent(instructions="你是一个友好的助手。")
|
|
481
|
+
session = AgentSession(stt=stt, llm=..., tts=...)
|
|
482
|
+
await session.start(agent=agent, room=ctx.room)
|
|
483
|
+
await ctx.connect()
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
if __name__ == "__main__":
|
|
487
|
+
cli.run_app(WorkerOptions(entrypoint_fnc=entry_point))
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
当 `MultiSpeakerAdapter.suppress_background_speaker=True` 时,下游 agent 只会
|
|
491
|
+
看到主说话人的分句,其它说话人(如旁人的声音、背景里的电视)会直接被吞掉。
|
|
492
|
+
|
|
443
493
|
### LLM (大语言模型)
|
|
444
494
|
|
|
445
495
|
```python
|
|
@@ -582,6 +632,15 @@ A: 根据您的应用需求选择合适的版本:
|
|
|
582
632
|
|
|
583
633
|
## 📝 更新日志
|
|
584
634
|
|
|
635
|
+
### 最新变更
|
|
636
|
+
- `volcengine.STT` 新增 `enable_diarization` 参数(默认关闭)。开启后:
|
|
637
|
+
- `STTCapabilities.diarization = True`,可被 `MultiSpeakerAdapter` 包装做主说话人
|
|
638
|
+
检测 / 背景抑制。
|
|
639
|
+
- 每段 `SpeechData` 携带 `speaker_id`(来自 `additions.speaker`),性别写入
|
|
640
|
+
`SpeechData.metadata["gender"]`(按官方文档,gender 仅在 `definite=true` 的分句
|
|
641
|
+
上返回)。
|
|
642
|
+
- `utterances[]` 数组按分句拆分事件,方便下游在段粒度上做判断。
|
|
643
|
+
|
|
585
644
|
### v1.5.4 适配
|
|
586
645
|
- 升级 `livekit-agents` 到 `1.5.4`
|
|
587
646
|
- Python 要求调整为 `>=3.10`
|
|
@@ -407,12 +407,62 @@ volcengine.STT(
|
|
|
407
407
|
enable_itn: bool = False,
|
|
408
408
|
enable_punc: bool = True,
|
|
409
409
|
enable_ddc: bool = False,
|
|
410
|
+
enable_diarization: bool = False, # 开启说话人分离(同时启用性别识别)
|
|
410
411
|
vad_segment_duration: int = 3000,
|
|
411
412
|
end_window_size: int = 500,
|
|
412
413
|
force_to_speech_time: int = 1000,
|
|
413
414
|
)
|
|
414
415
|
```
|
|
415
416
|
|
|
417
|
+
#### 说话人分离(Diarization)
|
|
418
|
+
|
|
419
|
+
把 `enable_diarization=True` 打开后,插件会:
|
|
420
|
+
|
|
421
|
+
1. 在 `STTCapabilities.diarization` 上声明能力,符合
|
|
422
|
+
[LiveKit STT 文档](https://docs.livekit.io/agents/models/stt/#speaker-diarization)
|
|
423
|
+
要求的三件事之一。
|
|
424
|
+
2. 在每条 `SpeechData` 上填上来自服务端 `additions.speaker` 的 `speaker_id`(形如
|
|
425
|
+
`"speaker_1"`)。
|
|
426
|
+
3. 在 `SpeechData.metadata["gender"]` 里带上 `additions.gender`(按官方文档,性别
|
|
427
|
+
只在 `definite=true` 的分句上返回,所以中间结果不会有)。
|
|
428
|
+
4. 按分句拆分 `utterances` 数组,每段独立事件(START → INTERIM/FINAL → END),
|
|
429
|
+
而不是把多段合并到一个事件里——这样下游的 `MultiSpeakerAdapter` 能在段粒度上
|
|
430
|
+
决定谁是主说话人、要不要抑制背景。
|
|
431
|
+
|
|
432
|
+
> 注意:插件只做"原始说话人分离"的暴露,**不**做主说话人判断或背景抑制。这两个
|
|
433
|
+
> 策略属于 `MultiSpeakerAdapter`。典型用法:
|
|
434
|
+
|
|
435
|
+
```python
|
|
436
|
+
from livekit.agents import Agent, AgentSession, JobContext, cli, WorkerOptions
|
|
437
|
+
from livekit.agents.stt import MultiSpeakerAdapter
|
|
438
|
+
from livekit.plugins import volcengine
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
async def entry_point(ctx: JobContext):
|
|
442
|
+
raw_stt = volcengine.STT(
|
|
443
|
+
api_key="your_api_key",
|
|
444
|
+
enable_diarization=True, # 暴露每段 speaker_id + gender
|
|
445
|
+
)
|
|
446
|
+
# 用官方适配器做主说话人检测 / 背景抑制
|
|
447
|
+
stt = MultiSpeakerAdapter(
|
|
448
|
+
raw_stt,
|
|
449
|
+
detect_primary_speaker=True, # 默认 True:识别谁是主说话人
|
|
450
|
+
suppress_background_speaker=False, # True 则只把主说话人文本交给 agent
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
agent = Agent(instructions="你是一个友好的助手。")
|
|
454
|
+
session = AgentSession(stt=stt, llm=..., tts=...)
|
|
455
|
+
await session.start(agent=agent, room=ctx.room)
|
|
456
|
+
await ctx.connect()
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
if __name__ == "__main__":
|
|
460
|
+
cli.run_app(WorkerOptions(entrypoint_fnc=entry_point))
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
当 `MultiSpeakerAdapter.suppress_background_speaker=True` 时,下游 agent 只会
|
|
464
|
+
看到主说话人的分句,其它说话人(如旁人的声音、背景里的电视)会直接被吞掉。
|
|
465
|
+
|
|
416
466
|
### LLM (大语言模型)
|
|
417
467
|
|
|
418
468
|
```python
|
|
@@ -555,6 +605,15 @@ A: 根据您的应用需求选择合适的版本:
|
|
|
555
605
|
|
|
556
606
|
## 📝 更新日志
|
|
557
607
|
|
|
608
|
+
### 最新变更
|
|
609
|
+
- `volcengine.STT` 新增 `enable_diarization` 参数(默认关闭)。开启后:
|
|
610
|
+
- `STTCapabilities.diarization = True`,可被 `MultiSpeakerAdapter` 包装做主说话人
|
|
611
|
+
检测 / 背景抑制。
|
|
612
|
+
- 每段 `SpeechData` 携带 `speaker_id`(来自 `additions.speaker`),性别写入
|
|
613
|
+
`SpeechData.metadata["gender"]`(按官方文档,gender 仅在 `definite=true` 的分句
|
|
614
|
+
上返回)。
|
|
615
|
+
- `utterances[]` 数组按分句拆分事件,方便下游在段粒度上做判断。
|
|
616
|
+
|
|
558
617
|
### v1.5.4 适配
|
|
559
618
|
- 升级 `livekit-agents` 到 `1.5.4`
|
|
560
619
|
- Python 要求调整为 `>=3.10`
|
|
@@ -98,6 +98,16 @@ class STTOptions:
|
|
|
98
98
|
end_window_size: int = 500
|
|
99
99
|
force_to_speech_time: int = 1000
|
|
100
100
|
|
|
101
|
+
# Speaker diarization. When ``enable_diarization`` is true, the
|
|
102
|
+
# plugin asks the Volcengine ASR for per-utterance ``speaker`` ids
|
|
103
|
+
# and exposes them on each emitted ``SpeechData.speaker_id`` so
|
|
104
|
+
# that LiveKit's ``MultiSpeakerAdapter`` can do main-speaker
|
|
105
|
+
# detection or background-speaker suppression on top. Gender
|
|
106
|
+
# detection is bundled in — the Volcengine docs tie it to
|
|
107
|
+
# ``enable_speaker_info``, so once diarization is on we always
|
|
108
|
+
# request gender and surface it via ``SpeechData.metadata``.
|
|
109
|
+
enable_diarization: bool = False
|
|
110
|
+
|
|
101
111
|
def get_ws_url(self):
|
|
102
112
|
return self.base_url
|
|
103
113
|
|
|
@@ -119,9 +129,9 @@ class STTOptions:
|
|
|
119
129
|
"enable_punc": self.enable_punc,
|
|
120
130
|
"enable_ddc": self.enable_ddc,
|
|
121
131
|
"enable_nonstream": True,
|
|
122
|
-
"enable_speaker_info":
|
|
132
|
+
"enable_speaker_info": self.enable_diarization,
|
|
123
133
|
"ssd_version": "200",
|
|
124
|
-
"enable_gender_detection":
|
|
134
|
+
"enable_gender_detection": self.enable_diarization,
|
|
125
135
|
"show_utterance": self.show_utterance,
|
|
126
136
|
"result_type": self.result_type,
|
|
127
137
|
"vad_segment_duration": self.vad_segment_duration,
|
|
@@ -191,17 +201,25 @@ class STT(stt.STT):
|
|
|
191
201
|
enable_itn: bool = False,
|
|
192
202
|
enable_punc: bool = True,
|
|
193
203
|
enable_ddc: bool = False,
|
|
204
|
+
enable_diarization: bool = False,
|
|
194
205
|
vad_segment_duration: int = 3000,
|
|
195
206
|
end_window_size: int = 500,
|
|
196
207
|
force_to_speech_time: int = 1000,
|
|
197
208
|
http_session: aiohttp.ClientSession | None = None,
|
|
198
209
|
interim_results: bool = True,
|
|
199
210
|
) -> None:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
211
|
+
# Per the LiveKit STT API contract, a plugin that exposes
|
|
212
|
+
# per-segment speaker ids must set ``diarization=True`` on its
|
|
213
|
+
# capabilities so the framework knows it can wrap us in
|
|
214
|
+
# ``MultiSpeakerAdapter`` for primary-speaker detection /
|
|
215
|
+
# background-speaker suppression.
|
|
216
|
+
capabilities_kwargs: dict = {
|
|
217
|
+
"streaming": True,
|
|
218
|
+
"interim_results": interim_results,
|
|
219
|
+
"diarization": enable_diarization,
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
super().__init__(capabilities=stt.STTCapabilities(**capabilities_kwargs))
|
|
205
223
|
|
|
206
224
|
api_key = api_key if is_given(api_key) else os.getenv("VOLCENGINE_STT_API_KEY")
|
|
207
225
|
if api_key is None:
|
|
@@ -218,6 +236,7 @@ class STT(stt.STT):
|
|
|
218
236
|
enable_itn=enable_itn,
|
|
219
237
|
enable_punc=enable_punc,
|
|
220
238
|
enable_ddc=enable_ddc,
|
|
239
|
+
enable_diarization=enable_diarization,
|
|
221
240
|
vad_segment_duration=vad_segment_duration,
|
|
222
241
|
end_window_size=end_window_size,
|
|
223
242
|
force_to_speech_time=force_to_speech_time,
|
|
@@ -436,69 +455,127 @@ class SpeechStream(stt.SpeechStream):
|
|
|
436
455
|
text = result.get("text", "")
|
|
437
456
|
if text == "":
|
|
438
457
|
return
|
|
458
|
+
|
|
439
459
|
utterances = result.get("utterances", [])
|
|
440
460
|
if len(utterances) == 0:
|
|
441
461
|
return
|
|
442
|
-
language = self._opts.language
|
|
443
|
-
definite = utterances[0].get("definite", False)
|
|
444
|
-
start_time = utterances[0].get("start_time", 0.0)
|
|
445
|
-
end_time = utterances[0].get("end_time", 0.0)
|
|
446
462
|
confidence = result.get("confidence", 0.0)
|
|
463
|
+
|
|
464
|
+
if not self._opts.enable_diarization:
|
|
465
|
+
# Fast path — no diarization, so we don't need to walk the
|
|
466
|
+
# utterance array. Use the original behavior: top-level
|
|
467
|
+
# ``result.text`` (cumulative across the current utterance)
|
|
468
|
+
# and ``utterances[0]`` for the active utterance's definite
|
|
469
|
+
# flag and timing. ``speaker_id`` stays None so this path
|
|
470
|
+
# is indistinguishable from the pre-diarization plugin.
|
|
471
|
+
self._emit_segment(
|
|
472
|
+
text=text,
|
|
473
|
+
definite=utterances[0].get("definite", False),
|
|
474
|
+
start_time=utterances[0].get("start_time", 0.0),
|
|
475
|
+
end_time=utterances[0].get("end_time", 0.0),
|
|
476
|
+
confidence=confidence,
|
|
477
|
+
speaker_id=None,
|
|
478
|
+
metadata=None,
|
|
479
|
+
)
|
|
480
|
+
return
|
|
481
|
+
|
|
482
|
+
# Diarization path — ``utterances`` is a cumulative list of
|
|
483
|
+
# completed sentence segments (see the Volcengine ASR docs).
|
|
484
|
+
# In streaming mode each response usually carries one active
|
|
485
|
+
# utterance, but end-of-stream flushes can return several at
|
|
486
|
+
# once. Emit each segment as its own event so
|
|
487
|
+
# ``MultiSpeakerAdapter`` (or any other downstream consumer) can
|
|
488
|
+
# apply per-speaker logic on a per-segment basis.
|
|
489
|
+
for utterance in utterances:
|
|
490
|
+
utt_text = utterance.get("text") or ""
|
|
491
|
+
if not utt_text:
|
|
492
|
+
continue
|
|
493
|
+
speaker_id, metadata = self._extract_diarization(utterance)
|
|
494
|
+
self._emit_segment(
|
|
495
|
+
text=utt_text,
|
|
496
|
+
definite=utterance.get("definite", False),
|
|
497
|
+
start_time=utterance.get("start_time", 0.0),
|
|
498
|
+
end_time=utterance.get("end_time", 0.0),
|
|
499
|
+
confidence=confidence,
|
|
500
|
+
speaker_id=speaker_id,
|
|
501
|
+
metadata=metadata,
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
def _extract_diarization(
|
|
505
|
+
self, utterance: dict
|
|
506
|
+
) -> tuple[str | None, dict | None]:
|
|
507
|
+
"""Pull ``speaker_id`` from ``additions.speaker`` and, when the
|
|
508
|
+
segment is definite, the ``gender`` tag for ``metadata``. Per
|
|
509
|
+
the Volcengine docs, ``additions.gender`` is only present on
|
|
510
|
+
definite utterances — interim updates legitimately omit it."""
|
|
511
|
+
additions = utterance.get("additions") or {}
|
|
512
|
+
raw_speaker = additions.get("speaker")
|
|
513
|
+
speaker_id = f"speaker_{raw_speaker}" if raw_speaker is not None else None
|
|
514
|
+
|
|
515
|
+
metadata: dict | None = None
|
|
516
|
+
if utterance.get("definite", False):
|
|
517
|
+
raw_gender = additions.get("gender")
|
|
518
|
+
if raw_gender is not None:
|
|
519
|
+
metadata = {"gender": str(raw_gender)}
|
|
520
|
+
|
|
521
|
+
return speaker_id, metadata
|
|
522
|
+
|
|
523
|
+
def _emit_segment(
|
|
524
|
+
self,
|
|
525
|
+
*,
|
|
526
|
+
text: str,
|
|
527
|
+
definite: bool,
|
|
528
|
+
start_time: float,
|
|
529
|
+
end_time: float,
|
|
530
|
+
confidence: float,
|
|
531
|
+
speaker_id: str | None,
|
|
532
|
+
metadata: dict | None,
|
|
533
|
+
) -> None:
|
|
534
|
+
"""Run the START/INTERIM/FINAL/END state machine for a single
|
|
535
|
+
segment. The plugin itself does not do main-speaker detection
|
|
536
|
+
or background suppression — that's the framework's
|
|
537
|
+
``MultiSpeakerAdapter``'s job. We just emit per-segment events
|
|
538
|
+
with ``speaker_id`` set so the adapter can apply its policy."""
|
|
539
|
+
language = self._opts.language
|
|
540
|
+
|
|
541
|
+
def _make_alternative() -> stt.SpeechData:
|
|
542
|
+
return stt.SpeechData(
|
|
543
|
+
language=language,
|
|
544
|
+
text=text,
|
|
545
|
+
start_time=start_time,
|
|
546
|
+
end_time=end_time,
|
|
547
|
+
confidence=confidence,
|
|
548
|
+
speaker_id=speaker_id,
|
|
549
|
+
metadata=metadata,
|
|
550
|
+
)
|
|
551
|
+
|
|
447
552
|
if not definite and not self._speaking:
|
|
448
553
|
self._speaking = True
|
|
449
554
|
self._event_ch.send_nowait(
|
|
450
555
|
stt.SpeechEvent(type=stt.SpeechEventType.START_OF_SPEECH)
|
|
451
556
|
)
|
|
452
557
|
logger.info("transcription start")
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
stt.
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
start_time=start_time,
|
|
459
|
-
end_time=end_time,
|
|
460
|
-
confidence=confidence,
|
|
461
|
-
)
|
|
462
|
-
]
|
|
463
|
-
self._event_ch.send_nowait(
|
|
464
|
-
stt.SpeechEvent(
|
|
465
|
-
type=stt.SpeechEventType.INTERIM_TRANSCRIPT,
|
|
466
|
-
request_id=self._request_id,
|
|
467
|
-
alternatives=alternatives,
|
|
468
|
-
)
|
|
558
|
+
self._event_ch.send_nowait(
|
|
559
|
+
stt.SpeechEvent(
|
|
560
|
+
type=stt.SpeechEventType.INTERIM_TRANSCRIPT,
|
|
561
|
+
request_id=self._request_id,
|
|
562
|
+
alternatives=[_make_alternative()],
|
|
469
563
|
)
|
|
564
|
+
)
|
|
470
565
|
elif not definite and self._speaking:
|
|
471
|
-
alternatives = [
|
|
472
|
-
stt.SpeechData(
|
|
473
|
-
text=text,
|
|
474
|
-
start_time=start_time,
|
|
475
|
-
end_time=end_time,
|
|
476
|
-
confidence=confidence,
|
|
477
|
-
language=language,
|
|
478
|
-
)
|
|
479
|
-
]
|
|
480
566
|
self._event_ch.send_nowait(
|
|
481
567
|
stt.SpeechEvent(
|
|
482
568
|
type=stt.SpeechEventType.INTERIM_TRANSCRIPT,
|
|
483
569
|
request_id=self._request_id,
|
|
484
|
-
alternatives=
|
|
570
|
+
alternatives=[_make_alternative()],
|
|
485
571
|
)
|
|
486
572
|
)
|
|
487
573
|
elif definite and self._speaking:
|
|
488
|
-
alternatives = [
|
|
489
|
-
stt.SpeechData(
|
|
490
|
-
text=text,
|
|
491
|
-
start_time=start_time,
|
|
492
|
-
end_time=end_time,
|
|
493
|
-
confidence=confidence,
|
|
494
|
-
language=language,
|
|
495
|
-
)
|
|
496
|
-
]
|
|
497
574
|
self._event_ch.send_nowait(
|
|
498
575
|
stt.SpeechEvent(
|
|
499
576
|
type=stt.SpeechEventType.FINAL_TRANSCRIPT,
|
|
500
577
|
request_id=self._request_id,
|
|
501
|
-
alternatives=
|
|
578
|
+
alternatives=[_make_alternative()],
|
|
502
579
|
)
|
|
503
580
|
)
|
|
504
581
|
self._event_ch.send_nowait(
|
|
@@ -509,6 +586,29 @@ class SpeechStream(stt.SpeechStream):
|
|
|
509
586
|
)
|
|
510
587
|
self._speaking = False
|
|
511
588
|
logger.info("transcription end", extra={"text": text})
|
|
589
|
+
elif definite and not self._speaking:
|
|
590
|
+
# The ASR flushed a finished segment without any preceding
|
|
591
|
+
# interim update in this session — typical for short
|
|
592
|
+
# snippets or for segments that arrived in the same batch
|
|
593
|
+
# as a previous FINAL. Emit a complete cycle so the agent
|
|
594
|
+
# still sees the text instead of dropping it.
|
|
595
|
+
self._event_ch.send_nowait(
|
|
596
|
+
stt.SpeechEvent(type=stt.SpeechEventType.START_OF_SPEECH)
|
|
597
|
+
)
|
|
598
|
+
self._event_ch.send_nowait(
|
|
599
|
+
stt.SpeechEvent(
|
|
600
|
+
type=stt.SpeechEventType.FINAL_TRANSCRIPT,
|
|
601
|
+
request_id=self._request_id,
|
|
602
|
+
alternatives=[_make_alternative()],
|
|
603
|
+
)
|
|
604
|
+
)
|
|
605
|
+
self._event_ch.send_nowait(
|
|
606
|
+
stt.SpeechEvent(
|
|
607
|
+
type=stt.SpeechEventType.END_OF_SPEECH,
|
|
608
|
+
request_id=self._request_id,
|
|
609
|
+
)
|
|
610
|
+
)
|
|
611
|
+
logger.info("transcription end (flushed)", extra={"text": text})
|
|
512
612
|
|
|
513
613
|
|
|
514
614
|
def parse_response(res):
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.3.13"
|
|
@@ -3,9 +3,7 @@ name = "livekit-plugins-volcenginee"
|
|
|
3
3
|
dynamic = ["version"]
|
|
4
4
|
description = "LiveKit Agent Plugins for Volcengine"
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
authors = [
|
|
7
|
-
{ name = "linsanzhu", email = "2287225730@qq.com" }
|
|
8
|
-
]
|
|
6
|
+
authors = [{ name = "linsanzhu", email = "2287225730@qq.com" }]
|
|
9
7
|
keywords = ["webrtc", "realtime", "audio", "video", "livekit"]
|
|
10
8
|
requires-python = ">=3.10"
|
|
11
9
|
dependencies = [
|
|
@@ -13,7 +11,7 @@ dependencies = [
|
|
|
13
11
|
"osc-data~=0.3.1",
|
|
14
12
|
"pydantic",
|
|
15
13
|
"numpy",
|
|
16
|
-
"livekit-agents
|
|
14
|
+
"livekit-agents>=1.5.13",
|
|
17
15
|
]
|
|
18
16
|
|
|
19
17
|
classifiers = [
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.3.12"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|