diff --git a/.github/workflows/test-funasr-onnx-package.yml b/.github/workflows/test-funasr-onnx-package.yml index f94b86e09..226421da6 100644 --- a/.github/workflows/test-funasr-onnx-package.yml +++ b/.github/workflows/test-funasr-onnx-package.yml @@ -86,7 +86,7 @@ jobs: import importlib.util from importlib.metadata import requires, version - assert version("funasr-onnx") == "0.4.9" + assert version("funasr-onnx") == "0.4.10" assert importlib.util.find_spec("torch") is None requirements = requires("funasr-onnx") or [] @@ -130,7 +130,7 @@ jobs: if: matrix.python-version == '3.12' uses: actions/upload-artifact@v7 with: - name: funasr-onnx-0.4.9 + name: funasr-onnx-0.4.10 path: dist/funasr-onnx/* if-no-files-found: error diff --git a/runtime/python/onnxruntime/README.md b/runtime/python/onnxruntime/README.md index 8794ac4c7..e3d18c6d4 100644 --- a/runtime/python/onnxruntime/README.md +++ b/runtime/python/onnxruntime/README.md @@ -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.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 +installed audio backend. This is a runtime loading fix, not a model-weight +or ASR accuracy update. + Version 0.4.9 keeps one result per input when offline VAD receives empty or sub-frame audio, without invoking ONNX Runtime for zero-frame features (#3846). It also returns an empty punctuation chunk while it preserves the streaming cache and avoids @@ -39,9 +45,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.9" +pip install -U "funasr-onnx>=0.4.10" # For the users in China, you could install with the command: -# pip install -U "funasr-onnx>=0.4.9" -i https://mirror.sjtu.edu.cn/pypi/web/simple +# pip install -U "funasr-onnx>=0.4.10" -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: diff --git a/runtime/python/onnxruntime/setup.py b/runtime/python/onnxruntime/setup.py index b5236ffc1..40290072f 100644 --- a/runtime/python/onnxruntime/setup.py +++ b/runtime/python/onnxruntime/setup.py @@ -13,7 +13,7 @@ def get_readme(): MODULE_NAME = "funasr_onnx" -VERSION_NUM = "0.4.9" +VERSION_NUM = "0.4.10" setuptools.setup( name=MODULE_NAME, diff --git a/tests/test_funasr_onnx_installed.py b/tests/test_funasr_onnx_installed.py index 4b3c254ee..b6a74c78e 100644 --- a/tests/test_funasr_onnx_installed.py +++ b/tests/test_funasr_onnx_installed.py @@ -19,6 +19,8 @@ raise unittest.SkipTest('Install the standalone funasr-onnx distribution first.') import funasr_onnx +import numpy as np +import soundfile as sf from funasr_onnx.paraformer_online_bin import Paraformer as ParaformerOnline @@ -36,9 +38,60 @@ class InstalledOnnxPackageTest(unittest.TestCase): + LOADERS = (funasr_onnx.Paraformer, ParaformerOnline, + funasr_onnx.SenseVoiceSmall, funasr_onnx.Fsmn_vad) + + 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: + path = Path(directory) / 'sample.flac' + sf.write(path, samples, 16000, subtype='PCM_16') + before = path.read_bytes() + for cls in self.LOADERS: + with self.subTest(loader=cls.__module__): + result = cls.__new__(cls).load_data(str(path), fs=16000) + self.assertEqual(len(result), 1) + np.testing.assert_array_equal(result[0], samples) + self.assertEqual(path.read_bytes(), before) + self.assertEqual(sorted(p.name for p in path.parent.iterdir()), ['sample.flac']) + + def test_wav_and_flac_list_preserves_input_order(self): + first = np.full(800, 0.125, dtype=np.float32) + second = np.full(1200, -0.25, dtype=np.float32) + with tempfile.TemporaryDirectory() as directory: + paths = [Path(directory) / 'first.wav', Path(directory) / 'second.flac'] + for path, samples in zip(paths, [first, second]): + sf.write(path, samples, 16000, subtype='PCM_16') + for cls in self.LOADERS: + with self.subTest(loader=cls.__module__): + result = cls.__new__(cls).load_data([str(p) for p in paths], fs=16000) + self.assertEqual(len(result), 2) + np.testing.assert_array_equal(result[0], first) + np.testing.assert_array_equal(result[1], second) + self.assertEqual(sorted(p.name for p in Path(directory).iterdir()), ['first.wav', 'second.flac']) + + def test_waveform_array_is_preserved(self): + samples = np.linspace(-0.25, 0.25, 320, dtype=np.float32) + for cls in self.LOADERS: + with self.subTest(loader=cls.__module__): + result = cls.__new__(cls).load_data(samples, fs=16000) + self.assertEqual(len(result), 1) + self.assertIs(result[0], samples) + + def test_invalid_audio_remains_visible_without_sidecar(self): + with tempfile.TemporaryDirectory() as directory: + path = Path(directory) / 'invalid.flac' + path.write_bytes(b'not audio') + for cls in self.LOADERS: + with self.subTest(loader=cls.__module__): + with self.assertRaises(Exception): + cls.__new__(cls).load_data(str(path), fs=16000) + self.assertEqual(path.read_bytes(), b'not audio') + self.assertEqual(sorted(p.name for p in path.parent.iterdir()), ['invalid.flac']) + def test_installed_distribution_version_and_origin(self): distribution = importlib.metadata.distribution('funasr-onnx') - self.assertEqual(distribution.version, '0.4.9') + self.assertEqual(distribution.version, '0.4.10') self.assertEqual( Path(funasr_onnx.__file__).resolve().parent, Path(distribution.locate_file('funasr_onnx')).resolve(), diff --git a/tests/test_funasr_onnx_release.py b/tests/test_funasr_onnx_release.py index 160c24aa4..cb57f6be5 100644 --- a/tests/test_funasr_onnx_release.py +++ b/tests/test_funasr_onnx_release.py @@ -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.9" +EXPECTED_VERSION = "0.4.10" REQUIREMENT_NAME_PATTERN = re.compile(r"^\s*([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?)") @@ -44,7 +44,7 @@ def normalized_requirement_name(requirement): class FunASROnnxReleaseContractTest(unittest.TestCase): - def test_release_version_is_0_4_9(self): + def test_release_version_is_0_4_10(self): self.assertEqual(assigned_literal(read_setup_tree(), "VERSION_NUM"), EXPECTED_VERSION) def test_standalone_license_preserves_repository_notice(self): @@ -68,6 +68,8 @@ def test_readme_names_the_standalone_fix_release(self): readme = (PACKAGE_ROOT / "README.md").read_text() self.assertIn(f'pip install -U "funasr-onnx>={EXPECTED_VERSION}"', readme) self.assertIn("does not update the installed ONNX wrappers", readme) + self.assertIn("#3865", readme) + self.assertIn("without creating a WAV sidecar", readme) self.assertIn("short/final VAD", readme) self.assertIn("zero-overlap", readme) self.assertIn("one independent result per input", readme)