Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/python_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ The wrapper accepts `**kwargs`, not a universally validated parameter schema. A
| `batch_size_s` | `300` | VAD-path batching budget in seconds, based on longest padded segment times segment count, not a hard file-duration limit. CPU VAD processing uses single-segment batches. |
| `batch_size_threshold_s` | `60` | VAD batching threshold for segment duration, not a VAD splitting setting. |
| `merge_vad` | `False` | Enables merging detected VAD regions for this call. |
| `merge_length_s` | `15` | VAD merge target read before call overrides are merged into ASR config. Set this at construction in this checkout. |
| `merge_length_s` | `15` | Target length in seconds for merged VAD regions when `merge_vad=True`. A per-call value overrides the construction value. |
| `cache` | No wrapper-managed session | Pass a caller-owned dictionary on every streaming call. It is not a hub/model download cache. |
| `sentence_timestamp` | `False` | Requests sentence records in the VAD pipeline; depends on available timestamps and punctuation, with limited VAD fallback. |
| `return_spk_res` | `True` | Includes clustered speaker results when VAD and a speaker model are configured. `False` does not avoid construction or segment embedding computation. |
Expand Down
2 changes: 1 addition & 1 deletion docs/python_api_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
| `batch_size_s` | `300` | VAD 路径的秒级批处理预算,按最长补齐片段长度乘以片段数量估算,不是文件时长硬上限。CPU 的 VAD 路径按单片段处理。 |
| `batch_size_threshold_s` | `60` | VAD 片段参与组批的时长阈值,不是 VAD 切分参数。 |
| `merge_vad` | `False` | 为本次调用启用 VAD 区域合并。 |
| `merge_length_s` | `15` | VAD 合并目标,在本次参数合并进 ASR 配置之前读取。当前代码版本应在构建时设置它。 |
| `merge_length_s` | `15` | `merge_vad=True` 时合并后 VAD 片段的目标长度,单位为秒。本次调用传入的值会覆盖构建时设置的值。 |
| `cache` | 包装层不维护会话 | 每次流式调用都传入调用方持有的字典。它不是模型下载缓存。 |
| `sentence_timestamp` | `False` | 请求 VAD 流程输出句子记录,依赖实际时间戳与标点,部分情形可回退到 VAD 区域。 |
| `return_spk_res` | `True` | 配置 VAD 与说话人模型时输出聚类结果。设为 `False` 并不能省去模型构建或片段说话人特征计算。 |
Expand Down
6 changes: 3 additions & 3 deletions funasr/auto/auto_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1043,10 +1043,10 @@ def inference_with_vad(self, input, input_len=None, **cfg):

# FIX(gcf): concat the vad clips for sense vocie model for better aed
if cfg.get("merge_vad", False):
# Call options are merged into kwargs only after this point.
merge_length_s = cfg.get("merge_length_s", kwargs.get("merge_length_s", 15))
for i in range(len(res)):
res[i]["value"] = merge_vad(
res[i]["value"], kwargs.get("merge_length_s", 15) * 1000
)
res[i]["value"] = merge_vad(res[i]["value"], merge_length_s * 1000)

# step.2 compute asr model
model = self.model
Expand Down
81 changes: 81 additions & 0 deletions tests/test_merge_vad_length.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""Regression tests for the merge_length_s option of AutoModel.generate()."""

import unittest
from unittest.mock import MagicMock, patch

import numpy as np


VAD_SEGMENTS = [[0, 4000], [5000, 9000], [10000, 14000]]


class TestMergeVadLength(unittest.TestCase):
def _make_auto_model(self, **init_kwargs):
from funasr.auto.auto_model import AutoModel

am = AutoModel.__new__(AutoModel)
am.model = MagicMock()
am.vad_model = MagicMock()
am.punc_model = None
am.punc_kwargs = {}
am.spk_model = None
am.spk_kwargs = {}
am.cb_model = None
am.spk_mode = None
am.vad_kwargs = {}
am.kwargs = {
"batch_size_s": 300,
"batch_size_threshold_s": 60,
"device": "cpu",
"disable_pbar": True,
"frontend": MagicMock(fs=16000),
"fs": 16000,
**init_kwargs,
}
am._reset_runtime_configs = MagicMock()

def mock_inference(data, input_len=None, model=None, kwargs=None, **cfg):
if model is am.vad_model:
return [{"key": "utt", "value": [list(s) for s in VAD_SEGMENTS]}]
return [{"text": "x"} for _ in data]

am.inference = MagicMock(side_effect=mock_inference)
return am

@patch("funasr.auto.auto_model.slice_padding_audio_samples")
@patch("funasr.auto.auto_model.load_audio_text_image_video")
@patch("funasr.auto.auto_model.prepare_data_iterator")
def _asr_segments(self, am, mock_prep, mock_load, mock_slice, **cfg):
mock_prep.return_value = (["utt"], [np.zeros(16000 * 14, dtype=np.float32)])
mock_load.return_value = np.zeros(16000 * 14, dtype=np.float32)
mock_slice.side_effect = lambda speech, speech_lengths, segments: (
[np.zeros(16000, dtype=np.float32) for _ in segments],
[16000 for _ in segments],
)

am.inference_with_vad("dummy_input", merge_vad=True, **cfg)

segments = []
for call in mock_slice.call_args_list:
segments.extend(seg for seg, _ in call.args[2])
return sorted(segments)

def test_default_merge_length_is_15_seconds(self):
am = self._make_auto_model()
self.assertEqual(self._asr_segments(am), [[0, 14000]])

def test_generate_merge_length_s_is_applied(self):
am = self._make_auto_model()
self.assertEqual(self._asr_segments(am, merge_length_s=10), [[0, 9000], [9000, 14000]])

def test_construction_merge_length_s_is_applied(self):
am = self._make_auto_model(merge_length_s=10)
self.assertEqual(self._asr_segments(am), [[0, 9000], [9000, 14000]])

def test_generate_merge_length_s_overrides_construction_value(self):
am = self._make_auto_model(merge_length_s=10)
self.assertEqual(self._asr_segments(am, merge_length_s=15), [[0, 14000]])


if __name__ == "__main__":
unittest.main()
Loading