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
4 changes: 2 additions & 2 deletions .github/workflows/test-funasr-onnx-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jobs:
import importlib.util
from importlib.metadata import requires, version

assert version("funasr-onnx") == "0.4.10"
assert version("funasr-onnx") == "0.4.11"
assert importlib.util.find_spec("torch") is None

requirements = requires("funasr-onnx") or []
Expand Down Expand Up @@ -130,7 +130,7 @@ jobs:
if: matrix.python-version == '3.12'
uses: actions/upload-artifact@v7
with:
name: funasr-onnx-0.4.10
name: funasr-onnx-0.4.11
path: dist/funasr-onnx/*
if-no-files-found: error

10 changes: 8 additions & 2 deletions runtime/python/onnxruntime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ does not update the installed ONNX wrappers. Check their versions separately
with `python -m pip show funasr-onnx funasr`; use the source installation below
when a required wrapper fix is newer than the published PyPI package.

Version 0.4.11 keeps timestamps independent of padded samples in a batch
(#3871), bounds Contextual/Seaco hotwords to the exported ten-character
embedding input and skips whitespace-only entries (#3882), and preserves
terminal abbreviations and their timestamps (#3869). These are wrapper and
postprocessing fixes, not model-weight updates or ASR accuracy claims.

Version 0.4.10 restores direct librosa decoding for non-WAV audio in offline
and streaming Paraformer, SenseVoice and offline VAD (#3865), without creating a WAV sidecar
or treating every non-WAV input as MP3. Codec support still depends on the
Expand Down Expand Up @@ -45,9 +51,9 @@ and postprocessing fixes, not a model-weight update or an ASR accuracy claim.
install from pip

```shell
pip install -U "funasr-onnx>=0.4.10"
pip install -U "funasr-onnx>=0.4.11"
# For the users in China, you could install with the command:
# pip install -U "funasr-onnx>=0.4.10" -i https://mirror.sjtu.edu.cn/pypi/web/simple
# pip install -U "funasr-onnx>=0.4.11" -i https://mirror.sjtu.edu.cn/pypi/web/simple
# If you want to export .onnx file, you should install modelscope and funasr
pip install -U modelscope funasr
# For the users in China, you could install with the command:
Expand Down
2 changes: 1 addition & 1 deletion runtime/python/onnxruntime/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def get_readme():


MODULE_NAME = "funasr_onnx"
VERSION_NUM = "0.4.10"
VERSION_NUM = "0.4.11"

setuptools.setup(
name=MODULE_NAME,
Expand Down
67 changes: 66 additions & 1 deletion tests/test_funasr_onnx_installed.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,71 @@ class InstalledOnnxPackageTest(unittest.TestCase):
LOADERS = (funasr_onnx.Paraformer, ParaformerOnline,
funasr_onnx.SenseVoiceSmall, funasr_onnx.Fsmn_vad)

def test_long_and_empty_hotwords_stay_within_embedding_bounds(self):
for cls in (funasr_onnx.ContextualParaformer, funasr_onnx.SeacoParaformer):
with self.subTest(wrapper=cls.__name__):
model = cls.__new__(cls)
model.vocab = {'a': 3, 'b': 4}
matrix, positions = model.proc_hotword(' ab abababababab ')
self.assertEqual(matrix.tolist(), [
[3, 4, 0, 0, 0, 0, 0, 0, 0, 0],
[3, 4, 3, 4, 3, 4, 3, 4, 3, 4],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
])
self.assertEqual(positions.tolist(), [1, 9, 0])
for value in ('', ' \t\n'):
matrix, positions = model.proc_hotword(value)
self.assertEqual(matrix.tolist(), [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
self.assertEqual(positions.tolist(), [0])

def test_batched_timestamps_use_each_input_length(self):
def features(lengths):
return np.zeros((len(lengths), max(lengths), 4), dtype=np.float32), np.array(lengths)

def acoustic_output(feats, lengths):
peaks = np.zeros((len(lengths), feats.shape[1] * 3), dtype=np.float32)
for row, length in enumerate(lengths):
peaks[row, np.linspace(5, length * 3 - 6, 3).round().astype(int)] = 1
return None, None, np.zeros_like(peaks), peaks

# Acoustic outputs are synthetic. The installed batching, timestamp and
# postprocessing code must keep the shorter sample independent of padding.
for cls in (funasr_onnx.Paraformer, funasr_onnx.ContextualParaformer,
funasr_onnx.SeacoParaformer):
with self.subTest(wrapper=cls.__name__):
model = cls.__new__(cls)
model.batch_size = 2
model.plot_timestamp_to = ''
model.language = 'zh'
model.frontend = SimpleNamespace(opts=SimpleNamespace(
frame_opts=SimpleNamespace(samp_freq=16000)))
model.load_data = lambda values, fs: values
model.extract_feat = features
model.decode = lambda scores, lens: [['\u4f60', '\u597d'] for _ in range(model._rows)]

def infer(feats, lengths, *unused):
model._rows = len(lengths)
return acoustic_output(feats, lengths)

model.infer = infer
model.bb_infer = infer
model.vocab = {'a': 3}
model.eb_infer = lambda hotwords, lengths: [np.zeros((10, len(hotwords), 4), dtype=np.float32)]
args = () if cls is funasr_onnx.Paraformer else ('a',)
single = [model([length], *args)[0] for length in [17, 50]]
self.assertEqual([x['timestamp'] for x in single], [
[[70, 470], [470, 945]], [[70, 670], [1450, 2925]],
])
self.assertEqual(model([17, 50], *args), single)

def test_terminal_abbreviation_preserves_text_and_timestamps(self):
from funasr_onnx.utils.postprocess_utils import abbr_dispose

self.assertEqual(abbr_dispose(['a', ' ', 'b']), ['AB'])
self.assertEqual(abbr_dispose(['a', ' ', 'b'], [[0, 100], [100, 200]]),
(['AB'], [[0, 200]]))
self.assertEqual(abbr_dispose(['a', ' ', 'b', ' ', 'c']), ['ABC'])

def test_lossless_non_wav_without_sidecar(self):
samples = (np.arange(4800, dtype=np.int16) - 2400).astype(np.float32) / 32768
with tempfile.TemporaryDirectory() as directory:
Expand Down Expand Up @@ -91,7 +156,7 @@ def test_invalid_audio_remains_visible_without_sidecar(self):

def test_installed_distribution_version_and_origin(self):
distribution = importlib.metadata.distribution('funasr-onnx')
self.assertEqual(distribution.version, '0.4.10')
self.assertEqual(distribution.version, '0.4.11')
self.assertEqual(
Path(funasr_onnx.__file__).resolve().parent,
Path(distribution.locate_file('funasr_onnx')).resolve(),
Expand Down
7 changes: 5 additions & 2 deletions tests/test_funasr_onnx_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
ROOT = Path(__file__).resolve().parents[1]
PACKAGE_ROOT = ROOT / "runtime" / "python" / "onnxruntime"
SETUP_PATH = PACKAGE_ROOT / "setup.py"
EXPECTED_VERSION = "0.4.10"
EXPECTED_VERSION = "0.4.11"
REQUIREMENT_NAME_PATTERN = re.compile(r"^\s*([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?)")


Expand Down Expand Up @@ -44,7 +44,7 @@ def normalized_requirement_name(requirement):


class FunASROnnxReleaseContractTest(unittest.TestCase):
def test_release_version_is_0_4_10(self):
def test_release_version_is_0_4_11(self):
self.assertEqual(assigned_literal(read_setup_tree(), "VERSION_NUM"), EXPECTED_VERSION)

def test_standalone_license_preserves_repository_notice(self):
Expand Down Expand Up @@ -83,6 +83,9 @@ def test_readme_names_the_standalone_fix_release(self):
self.assertIn("#3847", readme)
self.assertIn("empty or sub-frame", readme)
self.assertIn("preserves the streaming cache", readme)
self.assertIn("#3869", readme)
self.assertIn("#3871", readme)
self.assertIn("#3882", readme)

def test_runtime_dependencies_keep_onnx_install_torch_free(self):
requirements = setup_keyword_literal(read_setup_tree(), "install_requires")
Expand Down
Loading