Files
80e3019a74 feat(parakeet): add optional Underdog Bark 3.0 models, int8 and 4-bit (#1031)
* feat(parakeet): add optional Underdog Bark 3.0 int8 model

Adds bark-3.0 to the Parakeet model picker: Underdog Bark 3.0, an
English fine-tune of Parakeet TDT 0.6B v3, as the int8 sherpa-onnx
conversion published at velddev/Underdog-Bark-3.0-sherpa-onnx. It runs
on the existing sherpa-onnx recognizer, pinned to a revision with every
file checked against model_checksums.txt, like Orukeet (#840).

The conversion writes the unknown-word token where a quote mark was, so
the Parakeet transcript drops <unk>; the other models never emit it.

* docs(parakeet): say when to pick Bark 3.0; loosen its catalog test

The README now says Bark suits English dictation of conversational
speech and v3-european stays the choice for read or multilingual
speech. The test checks that bark-3.0 is offered rather than that it is
the last catalog entry, so a later model does not break it.

* test(parakeet): add docstrings to the Bark tests

* feat(parakeet): add optional 4-bit Underdog Bark 3.0 build

Adds bark-3.0-int4: the same Bark 3.0 weights quantized to 4-bit with
onnxruntime's HQQ quantizer, for machines short on memory. It needs
about half the RAM of the int8 build (0.64 GB against 1.28 GB peak) and
a 379 MiB download, at two to four times the CPU time (about 2x on
Intel, 3.7x on AMD by the publisher's measurement). The files are
published in velddev/Underdog-Bark-3.0-sherpa-onnx (Hugging Face PR #1),
and both Bark entries now pin its revision 3ae3af1, which holds both
builds; the int8 files are unchanged there.

Catalog entries gain an optional "precision" key, so the loader opens
encoder.int4.onnx and friends instead of assuming int8; every existing
model keeps the int8 default. The README section now covers both
builds in a few lines.

* test(parakeet): type the Bark test signatures

* docs(parakeet): name the Bark sherpa-onnx source and its pins

The README now says where both Bark builds download from (a pinned revision of
velddev/Underdog-Bark-3.0-sherpa-onnx), that it is an unofficial conversion, and
that each file is checked against its bundled SHA-256 pin, as the Orukeet
section does.

---------

Co-authored-by: Jatin <jatinkrmalik@gmail.com>
2026-11-02 12:31:08 -08:00

56 lines
2.5 KiB
Python

"""Optional Underdog Bark 3.0 catalog entries, their file names and <unk> output."""
import sys
import threading
from pathlib import Path
from unittest.mock import MagicMock
import pytest
from vocalinux.speech_recognition.recognition_manager import SpeechRecognitionManager
from vocalinux.utils import parakeet_model_info as models
from vocalinux.utils.model_checksums import expected_for
REPO = "velddev/Underdog-Bark-3.0-sherpa-onnx"
@pytest.mark.parametrize("model, precision", [("bark-3.0", "int8"), ("bark-3.0-int4", "int4")])
def test_bark_is_optional_pinned_and_keeps_its_license_files(model: str, precision: str) -> None:
"""Both Bark builds are offered but not recommended, and every file they download is pinned."""
assert models.RECOMMENDED_MODEL == "v3-european"
assert model in models.MODEL_SIZES
assert {"LICENSE", "NOTICE", f"encoder.{precision}.onnx"} <= set(models.model_files(model))
for name in models.model_files(model):
assert expected_for(models.manifest_key(model, name)) is not None
assert models.get_model_file_url(model, "NOTICE").startswith(f"https://huggingface.co/{REPO}/")
def test_int4_build_loads_its_own_file_names(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
"""The 4-bit entry's precision key makes the loader open the *.int4.onnx files."""
manager = SpeechRecognitionManager.__new__(SpeechRecognitionManager)
manager.model_size = "bark-3.0-int4"
manager._defer_download = False
sherpa = MagicMock()
monkeypatch.setitem(sys.modules, "sherpa_onnx", sherpa)
monkeypatch.setattr(models, "get_model_path", lambda _: str(tmp_path))
monkeypatch.setattr(models, "is_model_downloaded", lambda _: True)
monkeypatch.setattr(manager, "_parakeet_model_is_verified", lambda *_: True)
manager._init_parakeet()
kwargs = sherpa.OfflineRecognizer.from_transducer.call_args.kwargs
assert [kwargs[part].rsplit("/", 1)[1] for part in ("encoder", "decoder", "joiner")] == [
"encoder.int4.onnx",
"decoder.int4.onnx",
"joiner.int4.onnx",
]
def test_unknown_word_token_is_dropped_from_the_transcript() -> None:
"""Parakeet transcripts drop <unk> and collapse the spaces it leaves."""
manager = SpeechRecognitionManager.__new__(SpeechRecognitionManager)
manager._model_lock = threading.Lock()
manager.model = MagicMock()
manager.model.create_stream.return_value.result.text = " like a shampooer,<unk>Next man.<unk>"
assert manager._transcribe_with_parakeet([b"\x00\x00" * 160]) == "like a shampooer, Next man."