diff --git a/app/admin_queries.py b/app/admin_queries.py index 8bec4a1..003ec4c 100644 --- a/app/admin_queries.py +++ b/app/admin_queries.py @@ -175,12 +175,20 @@ def collect_entries(self) -> list[schemas.AdminModelEntry]: catalog_entries = [ self.build_entry(model) for model in self.ctx.manager.catalog if self.is_visible(model) ] + catalog_ids = {entry.id for entry in catalog_entries} + retired_entries = [ + self.build_entry(model) + for installed in self.ctx.manager.installed() + if installed.retired + and installed.id not in catalog_ids + and (model := self.ctx.manager.catalog_model(installed.id)) is not None + ] custom_entries = [ self.build_custom_entry(custom) for custom in self.ctx.manager.installed() if custom.id.startswith("custom:") ] - return catalog_entries + custom_entries + return catalog_entries + retired_entries + custom_entries def is_visible(self, model: Any) -> bool: if self.system.is_apple_silicon: @@ -216,6 +224,9 @@ def build_entry(self, model: Any) -> schemas.AdminModelEntry: downloaded_bytes=download.downloaded_bytes if download else None, total_bytes=download.total_bytes if download else None, error=resolution[2], + retired=model.retired, + replacement_id=model.replacement_id, + retirement_reason=model.retirement_reason, ) def build_custom_entry(self, custom: Any) -> schemas.AdminModelEntry: diff --git a/app/catalog.py b/app/catalog.py index 9105c6b..4bf719e 100644 --- a/app/catalog.py +++ b/app/catalog.py @@ -59,6 +59,8 @@ UKRAINIAN_LANGUAGE_CODE = "uk" VIETNAMESE_LANGUAGE_CODE = "vi" CHINESE_LANGUAGE_CODE = "zh" +HINDI_LANGUAGE_CODE = "hi" +TAGALOG_LANGUAGE_CODE = "tl" MIT_LICENSE = "MIT" ENGLISH_ONLY = "English only" SHERPA_MODEL_FILE = "model.int8.onnx" @@ -77,6 +79,74 @@ FAST_QUALITY = "Fast" BALANCED_QUALITY = "Balanced" MOST_ACCURATE_QUALITY = "Most accurate" +MOONSHINE_REVISION_V015 = "moonshine-voice-0.1.5" +MOONSHINE_015_REVISION = MOONSHINE_REVISION_V015 # noqa: WPS114 +MOONSHINE_STREAMING_SMALL_VARIANT = "Small Streaming" +MOONSHINE_STREAMING_TINY_VARIANT = "Tiny Streaming" +MOONSHINE_STREAMING_BALANCED_QUALITY = "Balanced · cached streaming" +MOONSHINE_STREAMING_FASTEST_QUALITY = "Fastest · cached streaming" +MOONSHINE_RETIREMENT_REASON = ( + "Moonshine deprecated this Community batch model after publishing an MIT streaming replacement." +) +MOONSHINE_RETIREMENT_PLURAL_REASON = ( + "Moonshine deprecated this Community batch model after publishing MIT streaming replacements." +) +NEMOTRON_SIZE_BYTES = 682_215_471 +BENGALI_ZIPFORMER_SIZE_BYTES = 94_119_939 +MOONSHINE_EN_MEDIUM_STREAMING_SIZE_BYTES = 269_141_623 +MOONSHINE_EN_SMALL_STREAMING_SIZE_BYTES = 142_300_974 +MOONSHINE_EN_TINY_STREAMING_SIZE_BYTES = 45_233_659 +MOONSHINE_EN_BASE_SIZE_BYTES = 141_001_190 +MOONSHINE_EN_TINY_SIZE_BYTES = 43_943_830 +MOONSHINE_AR_TINY_STREAMING_SIZE_BYTES = 32_349_411 +MOONSHINE_DE_SMALL_STREAMING_SIZE_BYTES = 121_800_823 +MOONSHINE_DE_TINY_STREAMING_SIZE_BYTES = 32_317_004 +MOONSHINE_ES_SMALL_STREAMING_SIZE_BYTES = 121_800_392 +MOONSHINE_ES_TINY_STREAMING_SIZE_BYTES = 32_316_573 +MOONSHINE_JA_SMALL_STREAMING_SIZE_BYTES = 121_803_780 +MOONSHINE_JA_TINY_STREAMING_SIZE_BYTES = 32_319_961 +MOONSHINE_ZH_TINY_STREAMING_SIZE_BYTES = 32_290_152 +MOONSHINE_TL_TINY_STREAMING_SIZE_BYTES = 32_309_481 +MOONSHINE_VI_TINY_STREAMING_SIZE_BYTES = 32_309_008 +MOONSHINE_KO_SIZE_BYTES = 71_815_486 +MOONSHINE_UK_SIZE_BYTES = 141_001_214 +DISTIL_LARGE_V3_SIZE_BYTES = 1_515_408_824 + +# Qwen3-ASR's upstream card lists 30 languages plus Chinese dialects. The +# dialects are represented by the model's Mandarin/`yue` capability rather +# than exposed as separate gateway language selectors. +_QWEN3_LANGUAGE_CODES: tuple[str, ...] = ( + ENGLISH_LANGUAGE_CODE, + CHINESE_LANGUAGE_CODE, + "yue", + JAPANESE_LANGUAGE_CODE, + KOREAN_LANGUAGE_CODE, + SPANISH_LANGUAGE_CODE, + FRENCH_LANGUAGE_CODE, + GERMAN_LANGUAGE_CODE, + RUSSIAN_LANGUAGE_CODE, + ARABIC_LANGUAGE_CODE, + ITALIAN_LANGUAGE_CODE, + PORTUGUESE_LANGUAGE_CODE, + "id", + "th", + VIETNAMESE_LANGUAGE_CODE, + "tr", + HINDI_LANGUAGE_CODE, + "ms", + "nl", + "sv", + "da", + "fi", + "pl", + "cs", + "fil", + "fa", + "el", + "hu", + "mk", + "ro", +) @dataclass(frozen=True, slots=True) @@ -113,6 +183,9 @@ class CatalogModel: language_codes: tuple[str, ...] = () apple_silicon_only: bool = False detects_language_automatically: bool = False + retired: bool = False + replacement_id: str | None = None + retirement_reason: str | None = None PinsMap = dict[str, dict[str, Any]] @@ -208,7 +281,7 @@ def whisper_cpp( language_codes=tuple( kwargs.get("language_codes") or cls.whisper_language_codes(cls._languages(args)) ), - license_name=str(kwargs.get("license_name", "See model source")), + license_name=str(kwargs.get("license_name", "See model source")), # noqa: WPS226 ) @classmethod @@ -263,6 +336,8 @@ def faster_whisper( source="faster-whisper", marker_file="model.bin", language_codes=cls.whisper_language_codes(cls._languages(args)), + license_name=str(kwargs.get("license_name", "See model source")), + commercial_use=bool(kwargs.get("commercial_use", True)), ) @classmethod @@ -340,10 +415,20 @@ def moonshine( language_codes=(language,), model_arch=int(args[1]), supports_streaming=bool(kwargs.get("supports_streaming", False)), - license_name=( - MIT_LICENSE if language == ENGLISH_LANGUAGE_CODE else "Moonshine Community License" + license_name=str( + kwargs.get( + "license_name", + MIT_LICENSE + if language == ENGLISH_LANGUAGE_CODE + else "Moonshine Community License", + ) ), - commercial_use=language == ENGLISH_LANGUAGE_CODE, + commercial_use=bool(kwargs.get("commercial_use", language == ENGLISH_LANGUAGE_CODE)), + required_files=tuple(kwargs.get("required_files", ())), + revision=kwargs.get("revision"), + retired=bool(kwargs.get("retired", False)), + replacement_id=kwargs.get("replacement_id"), + retirement_reason=kwargs.get("retirement_reason"), ) @classmethod @@ -468,7 +553,7 @@ def _validate_sherpa_source( "ha": "Hausa", "haw": "Hawaiian", "he": "Hebrew", - "hi": "Hindi", + HINDI_LANGUAGE_CODE: "Hindi", CROATIAN_LANGUAGE_CODE: "Croatian", "ht": "Haitian Creole", HUNGARIAN_LANGUAGE_CODE: "Hungarian", @@ -531,7 +616,7 @@ def _validate_sherpa_source( "tg": "Tajik", "th": "Thai", "tk": "Turkmen", - "tl": "Tagalog", + TAGALOG_LANGUAGE_CODE: "Tagalog", "tr": "Turkish", "tt": "Tatar", "ug": "Uyghur", @@ -627,7 +712,7 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "id", VIETNAMESE_LANGUAGE_CODE, "ct", - "hi", + HINDI_LANGUAGE_CODE, "ur", "ms", "uz", @@ -639,7 +724,7 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "ug", "gu", "my", - "tl", + TAGALOG_LANGUAGE_CODE, "kk", "or", "ne", @@ -665,16 +750,35 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: _MOONSHINE_LANGUAGE_NAMES = MappingProxyType( { ARABIC_LANGUAGE_CODE: "Arabic", + GERMAN_LANGUAGE_CODE: "German", ENGLISH_LANGUAGE_CODE: "English", SPANISH_LANGUAGE_CODE: "Spanish", JAPANESE_LANGUAGE_CODE: "Japanese", KOREAN_LANGUAGE_CODE: "Korean", + TAGALOG_LANGUAGE_CODE: "Tagalog", UKRAINIAN_LANGUAGE_CODE: "Ukrainian", VIETNAMESE_LANGUAGE_CODE: "Vietnamese", CHINESE_LANGUAGE_CODE: "Mandarin Chinese", } ) +_MOONSHINE_STREAMING_FILES: tuple[str, ...] = ( + "adapter.ort", + "cross_kv.ort", + "decoder_kv.ort", + "encoder.ort", + "frontend.model.ort", + "frontend.weights.ort", + "streaming_config.json", + "tokenizer.bin", +) + +_MOONSHINE_BATCH_FILES: tuple[str, ...] = ( + "decoder_model_merged.ort", + "encoder_model.ort", + "tokenizer.bin", +) + _BASE_CATALOG: tuple[CatalogModel, ...] = ( _sherpa_onnx( @@ -910,7 +1014,7 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "qwen3-asr-0.6b-int8", "Qwen3-ASR 0.6B INT8", _megabytes("987"), - "11 languages", + "30 languages + Chinese dialects", "Accurate multilingual · punctuation", 6, huggingface_repo="csukuangfj2/sherpa-onnx-qwen3-asr-0.6B-int8-2026-03-25", @@ -925,28 +1029,96 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "tokenizer/tokenizer_config.json", ), model_type="qwen3_asr", + language_codes=_QWEN3_LANGUAGE_CODES, + family="Qwen3-ASR", + description=( + "Alibaba's speech-aware Qwen3 converted to INT8 ONNX. An LLM decoder rather than a " + "CTC or transducer head, so it punctuates well but decodes more slowly. It detects " + "the language itself and cannot be pinned to one. The converted artifact is advertised " + "for the 30 languages in the Qwen3-ASR model card; Chinese dialect support is not " + "selectable as separate gateway language codes." + ), + license_name=APACHE_LICENSE, + detects_language_automatically=True, + ), + _sherpa_onnx( + "nemotron-3.5-asr-streaming-0.6b-320ms-int8", + "Nemotron 3.5 ASR Streaming 0.6B INT8", + NEMOTRON_SIZE_BYTES, + "32 ready or broad-coverage locales", + "Multilingual streaming · punctuation", + 8, + huggingface_repo=( + "csukuangfj2/sherpa-onnx-nemotron-3.5-asr-streaming-0.6b-320ms-int8-2026-06-11" + ), + required_files=( + ENCODER_INT8_FILE, + DECODER_INT8_FILE, + "joiner.int8.onnx", + TOKENS_FILE, + ), + model_type="streaming_zipformer", language_codes=( ENGLISH_LANGUAGE_CODE, - CHINESE_LANGUAGE_CODE, - JAPANESE_LANGUAGE_CODE, - KOREAN_LANGUAGE_CODE, SPANISH_LANGUAGE_CODE, FRENCH_LANGUAGE_CODE, + ITALIAN_LANGUAGE_CODE, + PORTUGUESE_LANGUAGE_CODE, + DUTCH_LANGUAGE_CODE, GERMAN_LANGUAGE_CODE, + "tr", RUSSIAN_LANGUAGE_CODE, ARABIC_LANGUAGE_CODE, - ITALIAN_LANGUAGE_CODE, - PORTUGUESE_LANGUAGE_CODE, + HINDI_LANGUAGE_CODE, + JAPANESE_LANGUAGE_CODE, + KOREAN_LANGUAGE_CODE, + VIETNAMESE_LANGUAGE_CODE, + UKRAINIAN_LANGUAGE_CODE, + "pl", + SWEDISH_LANGUAGE_CODE, + CZECH_LANGUAGE_CODE, + "no", + DANISH_LANGUAGE_CODE, + BULGARIAN_LANGUAGE_CODE, + FINNISH_LANGUAGE_CODE, + CROATIAN_LANGUAGE_CODE, + SLOVAK_LANGUAGE_CODE, + CHINESE_LANGUAGE_CODE, + HUNGARIAN_LANGUAGE_CODE, + ROMANIAN_LANGUAGE_CODE, + ESTONIAN_LANGUAGE_CODE, ), - family="Qwen3-ASR", + family="Nemotron 3.5 ASR", description=( - "Alibaba's speech-aware Qwen3 converted to INT8 ONNX. An LLM decoder rather than a " - "CTC or transducer head, so it punctuates well but decodes more slowly. It detects " - "the language itself and cannot be pinned to one." + "NVIDIA's cache-aware multilingual RNNT converted to INT8 ONNX for sherpa-onnx. " + "The 320 ms export supports explicit language prompts and Automatic mode, with " + "punctuation and capitalization. Greek, Lithuanian, Latvian, Maltese, Slovenian, " + "Hebrew, Thai, and Norwegian Nynorsk are adaptation-ready only and are not advertised." ), - license_name=APACHE_LICENSE, + license_name="OpenMDW 1.1", + supports_streaming=True, detects_language_automatically=True, ), + _sherpa_onnx( + "streaming-zipformer-bn-vosk-2026-02-09", + "Bengali Streaming Zipformer", + BENGALI_ZIPFORMER_SIZE_BYTES, + "Bengali only", + "Compact Bengali streaming", + 2, + huggingface_repo="csukuangfj2/sherpa-onnx-streaming-zipformer-bn-vosk-2026-02-09", + required_files=("encoder.onnx", "decoder.onnx", "joiner.onnx", TOKENS_FILE), + model_type="streaming_zipformer", + language_codes=("bn",), + family="Zipformer", + description=( + "Alpha Cephei's Bengali streaming Zipformer converted to sherpa-onnx. A small CPU " + "model for live Bengali dictation; punctuation and robustness are validated by the " + "gateway benchmark rather than inferred from the upstream card." + ), + license_name=APACHE_LICENSE, + supports_streaming=True, + ), _mlx_audio( "whisper-large-v3-turbo-4bit", "MLX Whisper Large v3 Turbo 4-bit", @@ -1023,57 +1195,35 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "qwen3-asr-0.6b-4bit", "MLX Qwen3-ASR 0.6B 4-bit", _megabytes("713"), - "11 languages", + "30 languages + Chinese dialects", "Accurate multilingual · punctuation", 8, repository="mlx-community/Qwen3-ASR-0.6B-4bit", family="Qwen3-ASR / MLX", description=( - "Quantized Qwen3-ASR running natively on Apple silicon through MLX. An LLM decoder, " - "so it punctuates well but decodes more slowly than Parakeet." + "Quantized Qwen3-ASR running natively on Apple silicon through MLX. The upstream " + "card covers 30 languages plus Chinese dialects; an LLM decoder punctuates well but " + "decodes more slowly than Parakeet." ), license_name=APACHE_LICENSE, - language_codes=( - ENGLISH_LANGUAGE_CODE, - CHINESE_LANGUAGE_CODE, - JAPANESE_LANGUAGE_CODE, - KOREAN_LANGUAGE_CODE, - SPANISH_LANGUAGE_CODE, - FRENCH_LANGUAGE_CODE, - GERMAN_LANGUAGE_CODE, - RUSSIAN_LANGUAGE_CODE, - ARABIC_LANGUAGE_CODE, - ITALIAN_LANGUAGE_CODE, - PORTUGUESE_LANGUAGE_CODE, - ), + language_codes=_QWEN3_LANGUAGE_CODES, ), _mlx_audio( "qwen3-asr-1.7b-4bit", "MLX Qwen3-ASR 1.7B 4-bit", _megabytes("1608"), - "11 languages", + "30 languages + Chinese dialects", "Most accurate multilingual · punctuation", HIGH_MEMORY_RAM_GB, repository="mlx-community/Qwen3-ASR-1.7B-4bit", family="Qwen3-ASR / MLX", description=( - "The larger Qwen3-ASR for Macs with memory to spare; the same 11 languages as the " - "0.6B entry, with better accuracy on accented and noisy speech." + "The larger Qwen3-ASR for Macs with memory to spare; the same 30-language coverage " + "and Chinese dialect support as the 0.6B entry, with better accuracy on accented " + "and noisy speech." ), license_name=APACHE_LICENSE, - language_codes=( - ENGLISH_LANGUAGE_CODE, - CHINESE_LANGUAGE_CODE, - JAPANESE_LANGUAGE_CODE, - KOREAN_LANGUAGE_CODE, - SPANISH_LANGUAGE_CODE, - FRENCH_LANGUAGE_CODE, - GERMAN_LANGUAGE_CODE, - RUSSIAN_LANGUAGE_CODE, - ARABIC_LANGUAGE_CODE, - ITALIAN_LANGUAGE_CODE, - PORTUGUESE_LANGUAGE_CODE, - ), + language_codes=_QWEN3_LANGUAGE_CODES, ), _mlx_audio( "granite-speech-4.1-2b-nar", @@ -1100,30 +1250,36 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "Medium Streaming", 5, "Moonshine English Medium Streaming", - _megabytes("304"), - "Most accurate · cached streaming", + MOONSHINE_EN_MEDIUM_STREAMING_SIZE_BYTES, + MOONSHINE_STREAMING_FASTEST_QUALITY, supports_streaming=True, minimum_ram_gb=4, + required_files=_MOONSHINE_STREAMING_FILES, + revision=MOONSHINE_REVISION_V015, ), _moonshine( "en-small-streaming", ENGLISH_LANGUAGE_CODE, - "Small Streaming", + MOONSHINE_STREAMING_SMALL_VARIANT, 4, "Moonshine English Small Streaming", - _megabytes("165"), - "Balanced · cached streaming", + MOONSHINE_EN_SMALL_STREAMING_SIZE_BYTES, + MOONSHINE_STREAMING_BALANCED_QUALITY, supports_streaming=True, + required_files=_MOONSHINE_STREAMING_FILES, + revision=MOONSHINE_015_REVISION, ), _moonshine( "en-tiny-streaming", ENGLISH_LANGUAGE_CODE, - "Tiny Streaming", + MOONSHINE_STREAMING_TINY_VARIANT, 2, "Moonshine English Tiny Streaming", - _megabytes("52"), - "Fastest · cached streaming", + MOONSHINE_EN_TINY_STREAMING_SIZE_BYTES, + MOONSHINE_STREAMING_FASTEST_QUALITY, supports_streaming=True, + required_files=_MOONSHINE_STREAMING_FILES, + revision=MOONSHINE_015_REVISION, ), _moonshine( "en-base", @@ -1131,8 +1287,10 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: BASE_MODEL_VARIANT, 1, "Moonshine English Base", - _megabytes(MOONSHINE_BASE_SIZE_MB), + MOONSHINE_EN_BASE_SIZE_BYTES, "Accurate · batch", + required_files=_MOONSHINE_BATCH_FILES, + revision=MOONSHINE_015_REVISION, ), _moonshine( "en-tiny", @@ -1140,8 +1298,150 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "Tiny", 0, "Moonshine English Tiny", - _megabytes("44"), + MOONSHINE_EN_TINY_SIZE_BYTES, "Smallest · batch", + required_files=_MOONSHINE_BATCH_FILES, + revision=MOONSHINE_015_REVISION, + ), + _moonshine( + "ar-tiny-streaming", + ARABIC_LANGUAGE_CODE, + MOONSHINE_STREAMING_TINY_VARIANT, + 2, + "Moonshine Arabic Tiny Streaming", + MOONSHINE_AR_TINY_STREAMING_SIZE_BYTES, + MOONSHINE_STREAMING_FASTEST_QUALITY, + supports_streaming=True, + required_files=_MOONSHINE_STREAMING_FILES, + license_name=MIT_LICENSE, + commercial_use=True, + revision=MOONSHINE_015_REVISION, + ), + _moonshine( + "de-small-streaming", + GERMAN_LANGUAGE_CODE, + MOONSHINE_STREAMING_SMALL_VARIANT, + 4, + "Moonshine German Small Streaming", + MOONSHINE_DE_SMALL_STREAMING_SIZE_BYTES, + MOONSHINE_STREAMING_BALANCED_QUALITY, + supports_streaming=True, + required_files=_MOONSHINE_STREAMING_FILES, + license_name=MIT_LICENSE, + commercial_use=True, + revision=MOONSHINE_015_REVISION, + ), + _moonshine( + "de-tiny-streaming", + GERMAN_LANGUAGE_CODE, + MOONSHINE_STREAMING_TINY_VARIANT, + 2, + "Moonshine German Tiny Streaming", + MOONSHINE_DE_TINY_STREAMING_SIZE_BYTES, + MOONSHINE_STREAMING_FASTEST_QUALITY, + supports_streaming=True, + required_files=_MOONSHINE_STREAMING_FILES, + license_name=MIT_LICENSE, + commercial_use=True, + revision=MOONSHINE_015_REVISION, + ), + _moonshine( + "es-small-streaming", + SPANISH_LANGUAGE_CODE, + MOONSHINE_STREAMING_SMALL_VARIANT, + 4, + "Moonshine Spanish Small Streaming", + MOONSHINE_ES_SMALL_STREAMING_SIZE_BYTES, + MOONSHINE_STREAMING_BALANCED_QUALITY, + supports_streaming=True, + required_files=_MOONSHINE_STREAMING_FILES, + license_name=MIT_LICENSE, + commercial_use=True, + revision=MOONSHINE_015_REVISION, + ), + _moonshine( + "es-tiny-streaming", + SPANISH_LANGUAGE_CODE, + MOONSHINE_STREAMING_TINY_VARIANT, + 2, + "Moonshine Spanish Tiny Streaming", + MOONSHINE_ES_TINY_STREAMING_SIZE_BYTES, + MOONSHINE_STREAMING_FASTEST_QUALITY, + supports_streaming=True, + required_files=_MOONSHINE_STREAMING_FILES, + license_name=MIT_LICENSE, + commercial_use=True, + revision=MOONSHINE_015_REVISION, + ), + _moonshine( + "ja-small-streaming", + JAPANESE_LANGUAGE_CODE, + MOONSHINE_STREAMING_SMALL_VARIANT, + 4, + "Moonshine Japanese Small Streaming", + MOONSHINE_JA_SMALL_STREAMING_SIZE_BYTES, + MOONSHINE_STREAMING_BALANCED_QUALITY, + supports_streaming=True, + required_files=_MOONSHINE_STREAMING_FILES, + license_name=MIT_LICENSE, + commercial_use=True, + revision=MOONSHINE_015_REVISION, + ), + _moonshine( + "ja-tiny-streaming", + JAPANESE_LANGUAGE_CODE, + MOONSHINE_STREAMING_TINY_VARIANT, + 2, + "Moonshine Japanese Tiny Streaming", + MOONSHINE_JA_TINY_STREAMING_SIZE_BYTES, + MOONSHINE_STREAMING_FASTEST_QUALITY, + supports_streaming=True, + required_files=_MOONSHINE_STREAMING_FILES, + license_name=MIT_LICENSE, + commercial_use=True, + revision=MOONSHINE_015_REVISION, + ), + _moonshine( + "zh-tiny-streaming", + CHINESE_LANGUAGE_CODE, + MOONSHINE_STREAMING_TINY_VARIANT, + 2, + "Moonshine Mandarin Tiny Streaming", + MOONSHINE_ZH_TINY_STREAMING_SIZE_BYTES, + MOONSHINE_STREAMING_FASTEST_QUALITY, + supports_streaming=True, + required_files=_MOONSHINE_STREAMING_FILES, + license_name=MIT_LICENSE, + commercial_use=True, + revision=MOONSHINE_015_REVISION, + ), + _moonshine( + "tl-tiny-streaming", + TAGALOG_LANGUAGE_CODE, + MOONSHINE_STREAMING_TINY_VARIANT, + 2, + "Moonshine Tagalog Tiny Streaming", + MOONSHINE_TL_TINY_STREAMING_SIZE_BYTES, + MOONSHINE_STREAMING_FASTEST_QUALITY, + supports_streaming=True, + required_files=_MOONSHINE_STREAMING_FILES, + license_name=MIT_LICENSE, + commercial_use=True, + revision=MOONSHINE_015_REVISION, + ), + _moonshine( + "vi-tiny-streaming", + VIETNAMESE_LANGUAGE_CODE, + MOONSHINE_STREAMING_TINY_VARIANT, + 2, + "Moonshine Vietnamese Tiny Streaming", + MOONSHINE_VI_TINY_STREAMING_SIZE_BYTES, + MOONSHINE_STREAMING_FASTEST_QUALITY, + supports_streaming=True, + required_files=_MOONSHINE_STREAMING_FILES, + license_name=MIT_LICENSE, + commercial_use=True, + revision=MOONSHINE_015_REVISION, ), _moonshine( SPANISH_LANGUAGE_CODE, @@ -1151,6 +1451,9 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "Moonshine Spanish", _megabytes("65"), FAST_BATCH_QUALITY, + retired=True, + replacement_id="moonshine:es-small-streaming", + retirement_reason=MOONSHINE_RETIREMENT_PLURAL_REASON, ), _moonshine( ARABIC_LANGUAGE_CODE, @@ -1160,6 +1463,9 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "Moonshine Arabic", _megabytes(MOONSHINE_BASE_SIZE_MB), FAST_BATCH_QUALITY, + retired=True, + replacement_id="moonshine:ar-tiny-streaming", + retirement_reason=MOONSHINE_RETIREMENT_REASON, ), _moonshine( JAPANESE_LANGUAGE_CODE, @@ -1169,6 +1475,9 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "Moonshine Japanese Base", _megabytes(MOONSHINE_BASE_SIZE_MB), FAST_BATCH_QUALITY, + retired=True, + replacement_id="moonshine:ja-small-streaming", + retirement_reason=MOONSHINE_RETIREMENT_PLURAL_REASON, ), _moonshine( "ja-tiny", @@ -1178,6 +1487,9 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "Moonshine Japanese Tiny", _megabytes("72"), "Fastest · batch", + retired=True, + replacement_id="moonshine:ja-tiny-streaming", + retirement_reason=MOONSHINE_RETIREMENT_REASON, ), _moonshine( KOREAN_LANGUAGE_CODE, @@ -1185,8 +1497,10 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "Tiny", 0, "Moonshine Korean", - _megabytes("72"), + MOONSHINE_KO_SIZE_BYTES, "Fastest · batch", + required_files=_MOONSHINE_BATCH_FILES, + revision=MOONSHINE_015_REVISION, ), _moonshine( CHINESE_LANGUAGE_CODE, @@ -1196,6 +1510,9 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "Moonshine Mandarin", _megabytes(MOONSHINE_BASE_SIZE_MB), FAST_BATCH_QUALITY, + retired=True, + replacement_id="moonshine:zh-tiny-streaming", + retirement_reason=MOONSHINE_RETIREMENT_REASON, ), _moonshine( UKRAINIAN_LANGUAGE_CODE, @@ -1203,8 +1520,10 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: BASE_MODEL_VARIANT, 1, "Moonshine Ukrainian", - _megabytes(MOONSHINE_BASE_SIZE_MB), + MOONSHINE_UK_SIZE_BYTES, FAST_BATCH_QUALITY, + required_files=_MOONSHINE_BATCH_FILES, + revision=MOONSHINE_015_REVISION, ), _moonshine( VIETNAMESE_LANGUAGE_CODE, @@ -1214,6 +1533,9 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "Moonshine Vietnamese", _megabytes(MOONSHINE_BASE_SIZE_MB), FAST_BATCH_QUALITY, + retired=True, + replacement_id="moonshine:vi-tiny-streaming", + retirement_reason=MOONSHINE_RETIREMENT_REASON, ), _faster_whisper( "tiny.en", @@ -1264,6 +1586,15 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: "Accurate · distilled", 8, ), + _faster_whisper( + "distil-large-v3", + "Distil-Whisper Large v3", + DISTIL_LARGE_V3_SIZE_BYTES, + ENGLISH_ONLY, + "Most accurate · distilled", + VERY_HIGH_MEMORY_RAM_GB, + license_name=MIT_LICENSE, + ), _whisperkit( "openai_whisper-tiny", "WhisperKit Tiny", _megabytes("66"), MULTILINGUAL, FASTEST_QUALITY, 4 ), @@ -1438,7 +1769,13 @@ def _github_release_page(cls, archive_url: str | None) -> str | None: ), ) -DEFAULT_CATALOG: tuple[CatalogModel, ...] = apply_pins(_BASE_CATALOG) +_PINNED_CATALOG: tuple[CatalogModel, ...] = apply_pins(_BASE_CATALOG) +DEFAULT_CATALOG: tuple[CatalogModel, ...] = tuple( + model for model in _PINNED_CATALOG if not model.retired +) +RETIRED_CATALOG: tuple[CatalogModel, ...] = tuple( + model for model in _PINNED_CATALOG if model.retired +) def catalog_by_id(catalog: tuple[CatalogModel, ...] = DEFAULT_CATALOG) -> dict[str, CatalogModel]: diff --git a/app/model_manager.py b/app/model_manager.py index f0ea6e3..3af150d 100644 --- a/app/model_manager.py +++ b/app/model_manager.py @@ -24,6 +24,7 @@ ENGINE_SHERPA_ONNX, ENGINE_WHISPER_CPP, ENGINE_WHISPERKIT, + RETIRED_CATALOG, CatalogModel, catalog_by_id, ) @@ -133,6 +134,8 @@ class InstalledModel: path: Path size_bytes: int custom: bool = False + retired: bool = False + replacement_id: str | None = None @dataclass(slots=True) @@ -149,10 +152,17 @@ def __init__( self, models_dir: Path, catalog: tuple[CatalogModel, ...] = DEFAULT_CATALOG, + retired_catalog: tuple[CatalogModel, ...] | None = None, ) -> None: self.models_dir = models_dir self.catalog = catalog + self.retired_catalog = ( + RETIRED_CATALOG + if retired_catalog is None and catalog is DEFAULT_CATALOG + else retired_catalog or () + ) self._by_id = catalog_by_id(catalog) + self._retired_by_id = catalog_by_id(self.retired_catalog) self._downloads: dict[str, _DownloadHandle] = {} # ------------------------------------------------------------------ paths @@ -170,7 +180,9 @@ def installed_path(self, model_id: str) -> Path | None: def installed(self) -> list[InstalledModel]: installed_models: list[InstalledModel] = [] catalog_paths: set[Path] = set() - for model in self.catalog: + catalog_models = [(model, False) for model in self.catalog] + catalog_models.extend((model, True) for model in self.retired_catalog) + for model, retired in catalog_models: path = self.model_path(model) marker = path / model.marker_file if model.marker_file else path if marker.exists(): @@ -182,6 +194,8 @@ def installed(self) -> list[InstalledModel]: key=model.key, path=path, size_bytes=_directory_size(path) if path.is_dir() else path.stat().st_size, + retired=retired, + replacement_id=model.replacement_id, ) ) whisper_cpp_dir = self.models_dir / ENGINE_WHISPER_CPP @@ -233,13 +247,15 @@ def download_state(self, model_id: str) -> DownloadState | None: def catalog_model(self, model_id: str) -> CatalogModel | None: """Return catalog metadata without exposing the manager's mutable index.""" - return self._by_id.get(model_id) + return self._by_id.get(model_id) or self._retired_by_id.get(model_id) # ------------------------------------------------------------- downloads def start_download(self, model_id: str) -> DownloadState: model = self._by_id.get(model_id) if model is None: + if model_id in self._retired_by_id: + raise UnknownModelError(f"Model {model_id} is retired and cannot be downloaded.") raise UnknownModelError(model_id) return self._start( model_id, @@ -477,7 +493,22 @@ async def _run_moonshine_download( ) if download_handle.cancel.is_set(): raise DownloadCancelled - relative_path = Path(model_path).resolve().relative_to(partial_dir.resolve()) + resolved_model_path = Path(model_path).resolve() + staging_root = partial_dir.resolve() + if not resolved_model_path.is_relative_to(staging_root): + raise RuntimeError( + "Moonshine downloader returned a path outside its staging directory." + ) + relative_path = resolved_model_path.relative_to(staging_root) + if model.required_files: + missing = [ + name + for name in model.required_files + if not (resolved_model_path / name).is_file() + ] + if missing: + raise RuntimeError(_missing_model_files_message(missing)) + await asyncio.to_thread(_verify_extracted_files, model, resolved_model_path) metadata = { "model_id": model.id, "language": model.language_code or model.key, @@ -599,13 +630,13 @@ async def _download_repo_file( ) def _by_key(self, key: str, engine: str) -> CatalogModel | None: - for model in self.catalog: + for model in (*self.catalog, *self.retired_catalog): if model.key == key and model.engine == engine: return model return None def _path_for_id(self, model_id: str) -> Path | None: - model = self._by_id.get(model_id) + model = self.catalog_model(model_id) if model is not None: return self.model_path(model) if model_id.startswith("custom:"): diff --git a/app/model_pins.json b/app/model_pins.json index a1683b0..0a27271 100644 --- a/app/model_pins.json +++ b/app/model_pins.json @@ -13,6 +13,12 @@ "model.bin": "2a166925539a16005f14ff328359f9b9adb9dc4fb631bb3b227526862e93e2ef" } }, + "faster-whisper:distil-large-v3": { + "revision": "c3058b475261292e64a0412df1d2681c06260fab", + "file_digests": { + "model.bin": "b79368e19b6623813609431a6e5ee309a71506701ebc49fd7820e692dec7c5f5" + } + }, "faster-whisper:distil-medium.en": { "revision": "80ddfce281f77766d8943d63109199fc8145dfa5", "file_digests": { @@ -88,6 +94,207 @@ "model.safetensors": "45298f6dc48df8c11e0a8d1dc5e0197c688bfa530646fa21f1a0238d2b0ecda3" } }, + "moonshine:ar-tiny-streaming": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "adapter.ort": "9c1278a338b4ff268fc5741be01446d56ca9cba9ff5f4a75c5e1b2c2e2739361", + "cross_kv.ort": "e317af289233894f49da429d4561150c2f61ca86c1b9ca91c6edd3fb4fdeecb5", + "decoder_kv.ort": "5c9a2bf2d9fc543fd4596b3ab4ab8d09f791aac5682eb1379fd8d59e34237db0", + "encoder.ort": "6785ca9932d59265375249a8f3bc62f8333fb4dee6ab7db8dfd6f1daef66f59e", + "frontend.model.ort": "04c552e66efe3f7206d0a69779864f93bf3cfaf2fa595c9ce5101c8e3854a414", + "frontend.weights.ort": "6cc4d70efffe757452996574d11065e7dac6a7ff0a9be3e2fec1d0266b062b0e", + "streaming_config.json": "d31afd7c2dd5ee9ae3fa232312c5b42cb3e4fe1c6e9c53d885f2bea63023790a", + "tokenizer.bin": "3acea39a89cdf44fa11f10bfdd7d05d9015d901999f8c8fa18988f19dcc6b267" + } + }, + "moonshine:de-small-streaming": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "adapter.ort": "be438a3eb12ba3697a9e474d20f899e52d8e27b43a6fc4c4c809d2a099646744", + "cross_kv.ort": "0c329cbf001bebbb9e34835e95676b9eb29e010405da46f65af511b8da16620e", + "decoder_kv.ort": "7da8ac9a1da2a486158ccde88c756fd876bf7f11ef1c2ba14b80e8edcd30028c", + "encoder.ort": "f31ef482f940e23af87906aeec986bf2e0a4862cad577bbc745264caab5a1858", + "frontend.model.ort": "a93453223d93aa41d21691568cde3a01125fe00710a5f7004a825e1a322d3025", + "frontend.weights.ort": "8b417e2e0ee762e34d7163d24383823bb8bd1d037fb6966c32b3f86a3de4961f", + "streaming_config.json": "12d16c7f5ea6734d197b79baf47914ea7e996d6fca8d303a19aca10d0617cecc", + "tokenizer.bin": "5725ad3771a1339802f8cebeda0fa8f7723f5282b1ee726c2544774d7399b9a7" + } + }, + "moonshine:de-tiny-streaming": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "adapter.ort": "23b91142a35cfbc533a7f03ad8387d0b6d32856b465c6fe7a14ede44a254a345", + "cross_kv.ort": "25e6d9d4f16569c2ede7e23a381fe7c60cc4b0f141adc88580557d4530634f17", + "decoder_kv.ort": "768b1464074fc4fb7327e8b863fc13dcd12cb7bbe8d8e9428fa7292f7fd9ca6e", + "encoder.ort": "74610b818058e396f4b9d9ffd730cf8e36772122464d989dd3011dfa76bd6baf", + "frontend.model.ort": "4445bb4c2f41b0dd993999e06dc9367a0dde0d42a2e777116e7d93be102dc43d", + "frontend.weights.ort": "233fda61d583403bbf13ea71b6ba2335fdf57eac71be85a97dcbb90a6c2f4186", + "streaming_config.json": "d31afd7c2dd5ee9ae3fa232312c5b42cb3e4fe1c6e9c53d885f2bea63023790a", + "tokenizer.bin": "5725ad3771a1339802f8cebeda0fa8f7723f5282b1ee726c2544774d7399b9a7" + } + }, + "moonshine:en": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "adapter.ort": "3f2a287def57cc094367a0eec3c4f5fc36a32ec420e86764b696920991b20281", + "cross_kv.ort": "642f6e21cd305be79342207c6f9e6b681d469d55bc48c72b27b84846fb71fd1e", + "decoder_kv.ort": "193bb366492b74fc4ad338c6778e8d8eb916aaa11b5aa264f9057f4db7759486", + "encoder.ort": "12915e76ebac7dd287c5ea63965d06103a53ba1ce242a4a34f318f3958c60c37", + "frontend.model.ort": "95768855c70c8251eeecc05fedf69999da1b8ab16f605c9f457fd3354b0ad6b5", + "frontend.weights.ort": "5ac941f490cbe035b335b99a414cc393d62d4c6f9f2423495b286870d271d709", + "streaming_config.json": "28e83b7a28e91472692a035e0dae3116422ae43aeb2bef5ed822c44ce89b88af", + "tokenizer.bin": "6884b35fd6377d4c4d32336a0bc152f36b64d1e45b6503683cdc238250a8472d" + } + }, + "moonshine:en-base": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "decoder_model_merged.ort": "d9d7b333af34bc552580576ddcf248a1c6c839e0d3b43b09afb9376ed009899d", + "encoder_model.ort": "7c66495948d0d08ec1af454cd4b5514862ae6511e94712a60e6d83eaec8dc8cf", + "tokenizer.bin": "6884b35fd6377d4c4d32336a0bc152f36b64d1e45b6503683cdc238250a8472d" + } + }, + "moonshine:en-small-streaming": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "adapter.ort": "c665f742364febad597cc9ac1e0b341ffbee0e24a1466e2f3bde95e6e4771762", + "cross_kv.ort": "e2d3417144e9514055ebfefe8dcc4c0a55a55adcb8530435844c75c53e352bf6", + "decoder_kv.ort": "1a05465b1dd955858dfcbee039c0020fb5dd982b0f5094c34e61735d518d771b", + "encoder.ort": "2d4d973e91e8aca08c51e7e7efa28a46ab265b63d809d5294d18b86bcd85b993", + "frontend.model.ort": "09b1210ae30dc5f0f3e45f0ebab914c254741323114f53fbbe5ae62cca35058f", + "frontend.weights.ort": "7ef97521bd4bad3928f5bb6808586f4fcc6e92bd5990394112eed7d4052ec338", + "streaming_config.json": "26f02b6afb22d60871a5efd85c3d38e569cc0ddb6c5eb6e93d3260152ae8a47a", + "tokenizer.bin": "6884b35fd6377d4c4d32336a0bc152f36b64d1e45b6503683cdc238250a8472d" + } + }, + "moonshine:en-tiny": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "decoder_model_merged.ort": "cf524c4862d36e9e5ab032eddc73637efd822d70e868ac575cf1a46e1e4708a0", + "encoder_model.ort": "94e90a4654fc45cdfedb77c4c08e1739f48862998e58fada384b25118134f221", + "tokenizer.bin": "6884b35fd6377d4c4d32336a0bc152f36b64d1e45b6503683cdc238250a8472d" + } + }, + "moonshine:en-tiny-streaming": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "adapter.ort": "22ecc949e146c49667fda28d102d4e30749a107dc88a396292aa8f277ef1347c", + "cross_kv.ort": "143a36667b8d05fd9d04e8c337b7ee121f37ef299aea6b3d82bdb3d3401950b4", + "decoder_kv.ort": "8852553f312adb6c9aa4d17418015049b30f412209ee569d336548c0044627de", + "encoder.ort": "a8414e1a5dedf9f2093d7680601dd8a9b0433e7020260eafe0e370ead91134ca", + "frontend.model.ort": "5121b561417b638afce0c6c31b760e37c93cf97f80d9b0031aad1fe7b6f25d61", + "frontend.weights.ort": "217da24ac6f522ebf02da8ef288e77d1ac68d50d4a6821433182e4fbf4204bbd", + "streaming_config.json": "74fe5ddebd63b17caf59e8a3b18c17547ff7bce1642050edbb1c3962674f8950", + "tokenizer.bin": "6884b35fd6377d4c4d32336a0bc152f36b64d1e45b6503683cdc238250a8472d" + } + }, + "moonshine:es-small-streaming": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "adapter.ort": "04b54114c8aab534222922640f7ca0882948ff9f6ed76777f6e184e55a8e8b15", + "cross_kv.ort": "4bfd0a641d72ccdae22751f86bbb2e25ff70c1fba4f81213f2415a1accff9618", + "decoder_kv.ort": "5b77c3d6baf801ef925a5bc54d7eb3db0c35ebbb86f8eaf5390d7f5bb42fef37", + "encoder.ort": "a9b8d6d5d9348d0e319cceffdb0196ef622df8e4e9f3fb4797dcd8dfafd50857", + "frontend.model.ort": "69c76287f49db365aa278d4908ec450e69ca1b4bfb7e836159d963d623ee0c13", + "frontend.weights.ort": "2f5a0eb5f3004c9447810d74274a352746319a32144e31d95ef410f425b84dda", + "streaming_config.json": "12d16c7f5ea6734d197b79baf47914ea7e996d6fca8d303a19aca10d0617cecc", + "tokenizer.bin": "5fbb7d4314dcb18e03c5f975609e4a4cd572b22b01d2b2accb1b3e6830696f36" + } + }, + "moonshine:es-tiny-streaming": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "adapter.ort": "2efd97cd2ee7d89578dd1d429a849be9fe37ac81dd9c4e1d31c8a2e7f85d43e6", + "cross_kv.ort": "9e268b1b3bb0f9ff79bca6e76055d41298de0a5ba52505ee04907a2642efa3ae", + "decoder_kv.ort": "7adf56b982df98e385d71823b9a40cf9296ae3a746efcf03f3d7bb4604032db0", + "encoder.ort": "ec9eb43a01fbfe242a2c6a86050cffe4a31050e1c9f084e98560d6c100004505", + "frontend.model.ort": "9d19434b861de0c1d2aff52556911ae565cf3212ebe6d7f797c76c5c5066c2fc", + "frontend.weights.ort": "5e4d5471cd5d984d4b94a603d861539ebf1dc72de11b0cf13f38316725d5ce48", + "streaming_config.json": "d31afd7c2dd5ee9ae3fa232312c5b42cb3e4fe1c6e9c53d885f2bea63023790a", + "tokenizer.bin": "5fbb7d4314dcb18e03c5f975609e4a4cd572b22b01d2b2accb1b3e6830696f36" + } + }, + "moonshine:ja-small-streaming": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "adapter.ort": "edcf20b9d0bc0a92bc0ddf37d8b96599de35a6cbee970368ed74c774b055e546", + "cross_kv.ort": "fbd18ec7b9514391d7121f4d4abec1be05a60e94a0f174061428380bf772c3e7", + "decoder_kv.ort": "1246d81eeb96df05d0163dcd48ce271a061b01aa3aea047239fbde22485248be", + "encoder.ort": "1da7cc82cb91d30ad9dc4e338fb80e7a2336b2c8ad634b826419222698fbe3a5", + "frontend.model.ort": "de65092295f011a39ee569f03d073e3564d94252efd74ebde16d720bcc43e38b", + "frontend.weights.ort": "4e89578a495a6c4dd5632e7acaf2fc38841e6831738c2702ef10f873125a14ec", + "streaming_config.json": "12d16c7f5ea6734d197b79baf47914ea7e996d6fca8d303a19aca10d0617cecc", + "tokenizer.bin": "9f2599f20c3a9b03d79dbe122e443647c6589ce8e5c73e65e5c5d6f73485d5de" + } + }, + "moonshine:ja-tiny-streaming": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "adapter.ort": "25f302e6203028e487d1b1f93db6fe5baf587373498062190160de70e0c4ae58", + "cross_kv.ort": "170ee70aeb3c82fd067193add0d1a8619cd16d4de80a5e0239dac12d956e8575", + "decoder_kv.ort": "ccc047610727f9fe039212a0b2370a7f970e80776376a229181a76998ff5af20", + "encoder.ort": "6420c180be55732d10db7a85f8966237bbbeee3b47addab4790ed81c22dde2cf", + "frontend.model.ort": "c6fdee4c81d421617b9571e43cf952dc1e7e4f45d0cb1dbf13104ee8af4ffd45", + "frontend.weights.ort": "673a7177f030da9ce948daa1af44ec5425b6f0825348aacb80f6a01c18e42a07", + "streaming_config.json": "d31afd7c2dd5ee9ae3fa232312c5b42cb3e4fe1c6e9c53d885f2bea63023790a", + "tokenizer.bin": "9f2599f20c3a9b03d79dbe122e443647c6589ce8e5c73e65e5c5d6f73485d5de" + } + }, + "moonshine:ko": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "decoder_model_merged.ort": "95aa9f2e764b80625d2889d6ec9f05c965808e540ac50c16abd10c7ea33fe44b", + "encoder_model.ort": "947260d46252f48eada86a34986b3f70c01d68a343959949a77375b94debd055", + "tokenizer.bin": "6884b35fd6377d4c4d32336a0bc152f36b64d1e45b6503683cdc238250a8472d" + } + }, + "moonshine:tl-tiny-streaming": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "adapter.ort": "a47413f3bab023770a82c3d2426edaac2571d4b6ef11f1de4f5ef04fe8921815", + "cross_kv.ort": "d449d3b4969cac1c0c07536c5c339bda1d8fa2768cebbfc81f4487f365a933bd", + "decoder_kv.ort": "7c34e8da5f52d98883a5a47ea5951fd33b2cdbde871189e623baf4f3accf96fc", + "encoder.ort": "6613d539cebdbfc78d492400a4a53042e1eeb2065c9efb2569e08eda2b716988", + "frontend.model.ort": "a94955c8ef260db0f9cc04c96ab0d35fd59bab8d61acbb1b8b466dad638e0a87", + "frontend.weights.ort": "83a518fdf989752c52338031fd997d23f84ae9c1c45945a30cac91dcaba93518", + "streaming_config.json": "d31afd7c2dd5ee9ae3fa232312c5b42cb3e4fe1c6e9c53d885f2bea63023790a", + "tokenizer.bin": "f1d4f9f04a3943a535cf3ad53329ae465237dd4a11bd38c5a9db47a9164bf892" + } + }, + "moonshine:uk": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "decoder_model_merged.ort": "2c0c1ebc20ba75a21ff5315fd039e53d2fc9ba77a9ca9ad3bc4c7c1c1de93cbc", + "encoder_model.ort": "ea37ff7a2c308b566def1fd6e6860e23db4968590f192d5cc0fbc494666e30f9", + "tokenizer.bin": "6884b35fd6377d4c4d32336a0bc152f36b64d1e45b6503683cdc238250a8472d" + } + }, + "moonshine:vi-tiny-streaming": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "adapter.ort": "8351344095ce2e7e8dca7a932c102c7976fd5031d9f50946c05fe4e8ea520cab", + "cross_kv.ort": "0a245cc6b617f9c3a2aebe3ba5e4e12dc6118750fb7e478ccb95fe629abe56cf", + "decoder_kv.ort": "b398871f7668c667f9853758e1bd5ffb77d1ddf72f90ae73ecd4b1f05b3df1d9", + "encoder.ort": "4774270b09a51af0bc7d7a6f5d687aa832ef6ca049ea965ba0ca23f965f3f3dd", + "frontend.model.ort": "fb8c8ae3cbc72f28e56a6f7577440b4e41e41a86b183d50ebd3d35215b712a7d", + "frontend.weights.ort": "beb51a94a57cb33ea740c23d7841ca2855e39fd9df7468a407dfd5eea471d8ca", + "streaming_config.json": "d31afd7c2dd5ee9ae3fa232312c5b42cb3e4fe1c6e9c53d885f2bea63023790a", + "tokenizer.bin": "004723b832413eca43dc95f5c1e14311fa78206f81e6bf99ce0e561c657ab209" + } + }, + "moonshine:zh-tiny-streaming": { + "revision": "moonshine-voice-0.1.5", + "file_digests": { + "adapter.ort": "84d1bed88310f5b6dd452203bcaffb765f017a7d06da8d84a898235ef5c90e12", + "cross_kv.ort": "22c2261f3120b766a91deceb4f32ca0bef0a8f2b181509ad0dc9fe6adbd0c23a", + "decoder_kv.ort": "5caf9e4e9eaa39b93122338bfbad2917630afc51293f6229b935ae7df28bed33", + "encoder.ort": "4270ca0e1ecd10e78d658b08b91d81c7f913511177e1ad461c8a9d9d0903503d", + "frontend.model.ort": "d27a9c994b43fa6568121f502e8b99d511ffd0425d4090ffddcc82c9a5667971", + "frontend.weights.ort": "ac56e8ee7780e1f254ee26f2d9c9a3b3c6cfd2f6822cb3944924ae321f86dc50", + "streaming_config.json": "d31afd7c2dd5ee9ae3fa232312c5b42cb3e4fe1c6e9c53d885f2bea63023790a", + "tokenizer.bin": "f172da002a818b9968d5c7a995cc7c51acf2168331ab5f914b5b0860ceab8236" + } + }, "sherpa-onnx:canary-180m-flash-en-int8": { "revision": "9077164e0d3dd1d5353743e89ceaa1d3a770838c", "file_digests": { @@ -121,6 +328,14 @@ "joiner.onnx": "fd1d02f45c2ad3d6b67cc149811ad794ab4b020ed49a0a9e2790a8619d1cddd8" } }, + "sherpa-onnx:nemotron-3.5-asr-streaming-0.6b-320ms-int8": { + "revision": "424ce58898995b713f84341f2e1492f9207a26aa", + "file_digests": { + "decoder.int8.onnx": "19f9c98fc6d0a2c33a65a43b36fdb2e914c26c0aa9764be3aebc502a1e982fb0", + "encoder.int8.onnx": "f79c3fcc149f268b54b7d5754bdc2ba5c47c16b1fc70d15728a56f6efbf60ca5", + "joiner.int8.onnx": "4101c7c679a0bc30483794b27a059e34e79232aa2068d78d51231a22c8b0d7ce" + } + }, "sherpa-onnx:parakeet-tdt-0.6b-v2-int8": { "revision": "1ab9323565ddb038682214b292f588070a538ce2", "file_digests": { @@ -139,6 +354,14 @@ "tokenizer/vocab.json": "ca10d7e9fb3ed18575dd1e277a2579c16d108e32f27439684afa0e10b1440910" } }, + "sherpa-onnx:streaming-zipformer-bn-vosk-2026-02-09": { + "revision": "a7c3c1547450a7c546c876be9ca8a6ab54464423", + "file_digests": { + "decoder.onnx": "fc03e8ca154593b95748198a56de115d4e6048d97c41692f93dfefb05f3693b4", + "encoder.onnx": "779f2412d6fa56f76be9fe71349cdf3edd10434ae9b1fc5e49834fb904320516", + "joiner.onnx": "1df7319f4f9c36d7130cacaf629e1d52e904070e7a14bca9d4f209edeb162da6" + } + }, "sherpa-onnx:streaming-zipformer-en-20m-int8": { "revision": "d42f2d9f7ca24806fb667456a18a9f1b60f70d16", "file_digests": { diff --git a/app/models/moonshine.py b/app/models/moonshine.py index 105aae1..e4fa418 100644 --- a/app/models/moonshine.py +++ b/app/models/moonshine.py @@ -20,10 +20,12 @@ STREAM_UPDATE_INTERVAL_SECONDS = 0.35 LANGUAGE_ALIASES = ( ("ar", frozenset(("ar", "ar-SA"))), + ("de", frozenset(("de", "de-DE"))), ("en", frozenset(("en", "en-US", "en-GB"))), ("es", frozenset(("es", "es-ES", "es-MX"))), ("ja", frozenset(("ja", "ja-JP"))), ("ko", frozenset(("ko", "ko-KR"))), + ("tl", frozenset(("tl", "tl-PH", "fil"))), ("uk", frozenset(("uk", "uk-UA"))), ("vi", frozenset(("vi", "vi-VN"))), ("zh", frozenset(("zh", "zh-CN", "zh-TW", "cmn"))), diff --git a/app/models/sherpa_onnx.py b/app/models/sherpa_onnx.py index dfc37b4..9d287ae 100644 --- a/app/models/sherpa_onnx.py +++ b/app/models/sherpa_onnx.py @@ -2,11 +2,13 @@ import asyncio import os +import re import time import wave from collections.abc import Callable from importlib import util as importlib_util from pathlib import Path +from types import MappingProxyType from typing import Any from app import catalog, errors @@ -18,6 +20,41 @@ MAXIMUM_ERROR_MESSAGE_LENGTH = 240 PCM_SAMPLE_SCALE = 32_768.0 CPU_DEVICE = "cpu" +AUTO_LANGUAGE = "auto" +NEMOTRON_MODEL_KEY = "nemotron-3.5-asr-streaming-0.6b-320ms-int8" +NEMOTRON_LANGUAGE_TAG = re.compile(r"\s*<([a-z]{2,3}(?:-[A-Z]{2})?)>\s*$") +NEMOTRON_LANGUAGE_LOCALES = MappingProxyType( + { + "en": "en-US", + "es": "es-US", + "fr": "fr-FR", + "it": "it-IT", + "pt": "pt-BR", + "nl": "nl-NL", + "de": "de-DE", + "tr": "tr-TR", + "ru": "ru-RU", + "ar": "ar-AR", + "hi": "hi-IN", + "ja": "ja-JP", + "ko": "ko-KR", + "vi": "vi-VN", + "uk": "uk-UA", + "pl": "pl-PL", + "sv": "sv-SE", + "cs": "cs-CZ", + "no": "nb-NO", + "da": "da-DK", + "bg": "bg-BG", + "fi": "fi-FI", + "hr": "hr-HR", + "sk": "sk-SK", + "zh": "zh-CN", + "hu": "hu-HU", + "ro": "ro-RO", + "et": "et-EE", + } +) class _SherpaRecognizerBuilder: @@ -41,13 +78,25 @@ def build(self) -> Any: def validate_language(self, language: str) -> None: supported = self.model.language_codes if self.model else () normalized = language.lower().split("-", maxsplit=1)[0] - if language != "auto" and supported and normalized not in supported: + if language != AUTO_LANGUAGE and supported and normalized not in supported: choices = ", ".join(supported) raise errors.LanguageUnsupportedError( f"The selected model does not support {language}. Choose Auto, {choices}, or " "another model." ) + def stream_language(self, language: str) -> str: + normalized = language.lower().split("-", maxsplit=1)[0] + if self.model and self.model.key == NEMOTRON_MODEL_KEY: + return NEMOTRON_LANGUAGE_LOCALES.get(normalized, normalized) + return language + + def uses_stream_language_locale(self) -> bool: + return self.model is not None and self.model.key == NEMOTRON_MODEL_KEY + + def strips_stream_language_tags(self) -> bool: + return self.uses_stream_language_locale() + def _build_streaming(self, sherpa: Any) -> Any: if self.root is None or self.model is None: return None @@ -71,7 +120,7 @@ def _build_offline(self, sherpa: Any) -> Any: model=str(self.root / "model.int8.onnx"), tokens=self.tokens, num_threads=self.threads, - language="auto", + language=AUTO_LANGUAGE, use_itn=True, provider=CPU_DEVICE, ) @@ -138,6 +187,7 @@ def __init__( line_id: int = 0, text: str = "", lines: list[_SherpaOnnxStreamAdapter] | None = None, + language_mapper: Callable[[str], str] | None = None, ) -> None: self._recognizer = recognizer self._stream = stream @@ -149,28 +199,42 @@ def __init__( self.text = text self.line = self self.lines = lines or [] + self._language_mapper = language_mapper + self._strip_language_tags = language_mapper is not None + self.detected_language: str | None = None def add_audio(self, samples: list[float], sample_rate: int) -> None: self._stream.accept_waveform(sample_rate, samples) self._drain() + def set_language(self, language: str) -> None: + """Apply a per-stream language option when the export exposes one.""" + mapped = self._language_mapper(language) if self._language_mapper else language + _set_stream_language( + self._stream, + mapped, + preserve_locale=self._language_mapper is not None, + ) + def add_listener(self, listener: Callable[[object], None]) -> None: self._listener = listener def stop(self) -> _SherpaOnnxStreamAdapter: self._stream.input_finished() self._drain() - trailing = str(self._recognizer.get_result(self._stream)).strip() + trailing = self._clean_text(str(self._recognizer.get_result(self._stream))) if trailing: line = _SherpaOnnxStreamAdapter(line_id=self._next_line_id, text=trailing) self._completed_lines.append(line) - return _SherpaOnnxStreamAdapter(lines=list(self._completed_lines)) + completed = _SherpaOnnxStreamAdapter(lines=list(self._completed_lines)) + completed.detected_language = self.detected_language + return completed def _drain(self) -> None: while self._recognizer.is_ready(self._stream): self._recognizer.decode_stream(self._stream) if self._recognizer.is_endpoint(self._stream): - text = str(self._recognizer.get_result(self._stream)).strip() + text = self._clean_text(str(self._recognizer.get_result(self._stream))) if text: line = _SherpaOnnxStreamAdapter(line_id=self._next_line_id, text=text) self._completed_lines.append(line) @@ -179,11 +243,21 @@ def _drain(self) -> None: self._last_partial = "" self._recognizer.reset(self._stream) return - partial = str(self._recognizer.get_result(self._stream)).strip() + partial = self._clean_text(str(self._recognizer.get_result(self._stream))) if partial and partial != self._last_partial: self._last_partial = partial self._notify(_SherpaOnnxStreamAdapter(line_id=self._next_line_id, text=partial)) + def _clean_text(self, text: str) -> str: + cleaned = text.strip() + if not self._strip_language_tags: + return cleaned + match = NEMOTRON_LANGUAGE_TAG.search(cleaned) + if match: + self.detected_language = match.group(1) + cleaned = cleaned[: match.start()].rstrip() + return cleaned + def _notify(self, line: _SherpaOnnxStreamAdapter) -> None: if self._listener is not None: self._listener(line) @@ -223,7 +297,22 @@ async def create_stream(self) -> _SherpaOnnxStreamAdapter: ) recognizer, _ = await self._ensure_recognizer() stream = await asyncio.to_thread(recognizer.create_stream) - return _SherpaOnnxStreamAdapter(recognizer, stream) + return _SherpaOnnxStreamAdapter( + recognizer, + stream, + language_mapper=( + self._builder.stream_language + if self._builder.uses_stream_language_locale() + else None + ), + ) + + def configure_stream(self, stream: object, language: str) -> None: + """Validate and configure a newly-created stream for a request.""" + self._builder.validate_language(language) + setter = getattr(stream, "set_language", None) + if callable(setter): + setter(language) async def health(self) -> EngineHealth: package_ready = importlib_util.find_spec("sherpa_onnx") is not None @@ -257,9 +346,16 @@ async def transcribe( if loaded_now: load_ms = max(0, int((time.monotonic() - start_time) * 1000)) start_time = time.monotonic() - text = await _run_sherpa_inference(recognizer, audio_path, self.supports_streaming) + text = await _run_sherpa_inference( + recognizer, + audio_path, + self.supports_streaming, + self._builder.stream_language(options.language), + self._builder.uses_stream_language_locale(), + self._builder.strips_stream_language_tags(), + ) if not text: - if options.language != "auto": + if options.language != AUTO_LANGUAGE: raise errors.LanguageUnsupportedError( f"The selected model returned nothing for {options.language}. " "It probably does not cover that language — choose another " @@ -316,21 +412,46 @@ def _decode_wave(recognizer: Any, audio_path: Path) -> str: return str(stream.result.text).strip() -def _decode_wave_online(recognizer: Any, audio_path: Path) -> str: +def _decode_wave_online( + recognizer: Any, + audio_path: Path, + language: str = AUTO_LANGUAGE, + preserve_locale: bool = False, + strip_language_tags: bool = False, +) -> str: sample_rate, floats = _read_wave_samples(audio_path) stream = recognizer.create_stream() + _set_stream_language(stream, language, preserve_locale=preserve_locale) stream.accept_waveform(sample_rate, floats) stream.input_finished() while recognizer.is_ready(stream): recognizer.decode_stream(stream) - return str(recognizer.get_result(stream)).strip() - - -async def _run_sherpa_inference(recognizer: Any, audio_path: Path, is_streaming: bool) -> str: - decode_fn = _decode_wave_online if is_streaming else _decode_wave + text = str(recognizer.get_result(stream)).strip() + return _strip_language_tag(text) if strip_language_tags else text + + +async def _run_sherpa_inference( + recognizer: Any, + audio_path: Path, + is_streaming: bool, + language: str = AUTO_LANGUAGE, + preserve_locale: bool = False, + strip_language_tags: bool = False, +) -> str: try: + if is_streaming: + decode_result = asyncio.to_thread( + _decode_wave_online, + recognizer, + audio_path, + language, + preserve_locale, + strip_language_tags, + ) + else: + decode_result = asyncio.to_thread(_decode_wave, recognizer, audio_path) return await asyncio.wait_for( - asyncio.to_thread(decode_fn, recognizer, audio_path), + decode_result, timeout=TRANSCRIPTION_TIMEOUT_SECONDS, ) except TimeoutError as error: @@ -338,3 +459,29 @@ async def _run_sherpa_inference(recognizer: Any, audio_path: Path, is_streaming: except Exception as error: detail = str(error)[-MAXIMUM_ERROR_MESSAGE_LENGTH:] raise errors.TranscriptionProcessError(f"sherpa-onnx failed: {detail}") from error + + +def _set_stream_language(stream: Any, language: str, *, preserve_locale: bool = False) -> None: + """Set sherpa's optional language stream option without breaking fixed exports.""" + setter = getattr(stream, "set_option", None) + if not callable(setter): + return + has_option = getattr(stream, "has_option", None) + if callable(has_option): + try: + has_language = bool(has_option("language")) + except Exception: # noqa: BLE001 - older bindings may not probe cleanly + has_language = True + if not has_language: + return + normalized = ( + language + if language == AUTO_LANGUAGE or preserve_locale + else language.lower().split("-", maxsplit=1)[0] + ) + setter("language", normalized) + + +def _strip_language_tag(text: str) -> str: + """Remove Nemotron's automatic ```` suffix from clean text.""" + return NEMOTRON_LANGUAGE_TAG.sub("", text).strip() diff --git a/app/routes/streaming.py b/app/routes/streaming.py index 5ea9fe9..9528fdc 100644 --- a/app/routes/streaming.py +++ b/app/routes/streaming.py @@ -195,6 +195,9 @@ async def _loop(self) -> None: _StreamPackets.configure(self, start) async with self._engine.streaming_lock: self._stream = await self._engine.create_stream() + configure = getattr(self._engine, "configure_stream", None) + if callable(configure): + await asyncio.to_thread(configure, self._stream, self._language) await self._listen() async def _listen(self) -> None: diff --git a/app/schemas.py b/app/schemas.py index 44b4414..f87dfa6 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -209,6 +209,9 @@ class AdminModelEntry(BaseModel): downloaded_bytes: int | None = None total_bytes: int | None = None error: str | None = None + retired: bool = False + replacement_id: str | None = None + retirement_reason: str | None = None class CustomDownloadRequest(BaseModel): diff --git a/app/templates/models/model_card.html b/app/templates/models/model_card.html index d4f5475..3c9158f 100644 --- a/app/templates/models/model_card.html +++ b/app/templates/models/model_card.html @@ -48,6 +48,9 @@

{{ card.display_label }}

{% elif entry.recommended and not card.suppress_recommended %} recommended {% endif %} + {% if entry.retired %} + retired + {% endif %} {% if entry.detects_language_automatically %} auto language @@ -58,6 +61,11 @@

{{ card.display_label }}

{{ entry.description }}

+ {% if entry.retired %} +

+ Retired{% if entry.replacement_id %}; use {{ entry.replacement_id }}{% endif %}{% if entry.retirement_reason %}. {{ entry.retirement_reason }}{% endif %} +

+ {% endif %}
diff --git a/app/webui/styles.css b/app/webui/styles.css index 334f964..9ede4dd 100644 --- a/app/webui/styles.css +++ b/app/webui/styles.css @@ -2267,6 +2267,11 @@ html[data-theme="dark"] .filter-toggle:has(input:checked) { color: var(--accent) border-color: #f0b4ae; background: var(--error-wash); } +.badge.retired { + color: var(--error); + border-color: #f0b4ae; + background: var(--error-wash); +} /* ------------------------------------------------------------------- controls */ diff --git a/docs/models.md b/docs/models.md index cbdfca0..0b3ddf8 100644 --- a/docs/models.md +++ b/docs/models.md @@ -2,7 +2,7 @@ # Models and languages -Every model in the catalog (58 of them), what it speaks, and which models cover a given language. Generated from `app/catalog.py`, so it always matches the catalog the gateway actually ships. +Every model in the catalog (65 of them), what it speaks, and which models cover a given language. Generated from `app/catalog.py`, so it always matches the catalog the gateway actually ships. The WebUI Models tab shows the same information per card, with a language filter. Use this page to pick a model before installing anything. @@ -36,7 +36,9 @@ Portable INT8 CPU models. Run everywhere, including containers. | Streaming Zipformer English 20M INT8 | 44 MB | English | streaming | Apache 2.0 | | Dolphin Small CTC INT8 | 250 MB | [40 languages](#language-set-40-28c55c73) | auto language | Apache 2.0 | | Dolphin Base CTC INT8 | 104 MB | [40 languages](#language-set-40-28c55c73) | auto language | Apache 2.0 | -| Qwen3-ASR 0.6B INT8 | 987 MB | [11 languages](#language-set-11-66e7cd1f) | auto language | Apache 2.0 | +| Qwen3-ASR 0.6B INT8 | 987 MB | [30 languages](#language-set-30-b59576aa) | auto language | Apache 2.0 | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | 682 MB | [28 languages](#language-set-28-2630314c) | streaming, auto language | OpenMDW 1.1 | +| Bengali Streaming Zipformer | 94 MB | Bengali | streaming | Apache 2.0 | ### faster-whisper @@ -52,6 +54,7 @@ CTranslate2 Whisper. Kept resident; the broad multilingual fallback. | faster-whisper Small | 484 MB | [100 languages](#language-set-100-0b8e4ee5) | — | See model source | | Distil-Whisper Small EN | 332 MB | English | — | See model source | | Distil-Whisper Medium EN | 789 MB | English | — | See model source | +| Distil-Whisper Large v3 | 1.52 GB | English | — | MIT | ### moonshine @@ -59,19 +62,23 @@ Compact dictation models. English tiers support live streaming. | Model | Download | Languages | Flags | Licence | | --- | ---: | --- | --- | --- | -| Moonshine English Medium Streaming | 304 MB | English | streaming | MIT | -| Moonshine English Small Streaming | 165 MB | English | streaming | MIT | -| Moonshine English Tiny Streaming | 52 MB | English | streaming | MIT | +| Moonshine English Medium Streaming | 269 MB | English | streaming | MIT | +| Moonshine English Small Streaming | 142 MB | English | streaming | MIT | +| Moonshine English Tiny Streaming | 45 MB | English | streaming | MIT | | Moonshine English Base | 141 MB | English | — | MIT | | Moonshine English Tiny | 44 MB | English | — | MIT | -| Moonshine Spanish | 65 MB | Spanish | personal use | Moonshine Community License | -| Moonshine Arabic | 141 MB | Arabic | personal use | Moonshine Community License | -| Moonshine Japanese Base | 141 MB | Japanese | personal use | Moonshine Community License | -| Moonshine Japanese Tiny | 72 MB | Japanese | personal use | Moonshine Community License | +| Moonshine Arabic Tiny Streaming | 32 MB | Arabic | streaming | MIT | +| Moonshine German Small Streaming | 122 MB | German | streaming | MIT | +| Moonshine German Tiny Streaming | 32 MB | German | streaming | MIT | +| Moonshine Spanish Small Streaming | 122 MB | Spanish | streaming | MIT | +| Moonshine Spanish Tiny Streaming | 32 MB | Spanish | streaming | MIT | +| Moonshine Japanese Small Streaming | 122 MB | Japanese | streaming | MIT | +| Moonshine Japanese Tiny Streaming | 32 MB | Japanese | streaming | MIT | +| Moonshine Mandarin Tiny Streaming | 32 MB | Mandarin Chinese | streaming | MIT | +| Moonshine Tagalog Tiny Streaming | 32 MB | Tagalog | streaming | MIT | +| Moonshine Vietnamese Tiny Streaming | 32 MB | Vietnamese | streaming | MIT | | Moonshine Korean | 72 MB | Korean | personal use | Moonshine Community License | -| Moonshine Mandarin | 141 MB | Mandarin Chinese | personal use | Moonshine Community License | | Moonshine Ukrainian | 141 MB | Ukrainian | personal use | Moonshine Community License | -| Moonshine Vietnamese | 141 MB | Vietnamese | personal use | Moonshine Community License | ### whisper.cpp @@ -117,22 +124,14 @@ MLX, **Apple silicon only**. Hidden on Linux and in containers. | MLX Whisper Large v3 Turbo 4-bit | 469 MB | [100 languages](#language-set-100-0b8e4ee5) | Apple silicon | MIT | | MLX Parakeet TDT 0.6B v3 | 2.51 GB | [25 languages](#language-set-25-94430147) | Apple silicon | CC BY 4.0 | | MLX Parakeet TDT 0.6B v2 | 2.47 GB | English | Apple silicon | CC BY 4.0 | -| MLX Qwen3-ASR 0.6B 4-bit | 713 MB | [11 languages](#language-set-11-66e7cd1f) | Apple silicon | Apache 2.0 | -| MLX Qwen3-ASR 1.7B 4-bit | 1.61 GB | [11 languages](#language-set-11-66e7cd1f) | Apple silicon | Apache 2.0 | +| MLX Qwen3-ASR 0.6B 4-bit | 713 MB | [30 languages](#language-set-30-b59576aa) | Apple silicon | Apache 2.0 | +| MLX Qwen3-ASR 1.7B 4-bit | 1.61 GB | [30 languages](#language-set-30-b59576aa) | Apple silicon | Apache 2.0 | | MLX Granite Speech 4.1 2B | 2.38 GB | English | Apple silicon | Apache 2.0 | ## Language sets The tables above link here rather than repeating long lists. Each set below is shared verbatim by every model listed with it. - - -### 11 languages - -Used by: MLX Qwen3-ASR 0.6B 4-bit, MLX Qwen3-ASR 1.7B 4-bit, Qwen3-ASR 0.6B INT8. - -English, Mandarin Chinese, Japanese, Korean, Spanish, French, German, Russian, Arabic, Italian, Portuguese. - ### 25 languages @@ -141,6 +140,22 @@ Used by: MLX Parakeet TDT 0.6B v3, Parakeet TDT 0.6B v3 INT8. Bulgarian, Croatian, Czech, Danish, Dutch, English, Estonian, Finnish, French, German, Greek, Hungarian, Italian, Latvian, Lithuanian, Maltese, Polish, Portuguese, Romanian, Slovak, Slovenian, Spanish, Swedish, Russian, Ukrainian. + + +### 28 languages + +Used by: Nemotron 3.5 ASR Streaming 0.6B INT8. + +English, Spanish, French, Italian, Portuguese, Dutch, German, Turkish, Russian, Arabic, Hindi, Japanese, Korean, Vietnamese, Ukrainian, Polish, Swedish, Czech, Norwegian, Danish, Bulgarian, Finnish, Croatian, Slovak, Mandarin Chinese, Hungarian, Romanian, Estonian. + + + +### 30 languages + +Used by: MLX Qwen3-ASR 0.6B 4-bit, MLX Qwen3-ASR 1.7B 4-bit, Qwen3-ASR 0.6B INT8. + +English, Mandarin Chinese, Cantonese, Japanese, Korean, Spanish, French, German, Russian, Arabic, Italian, Portuguese, Indonesian, Thai, Vietnamese, Turkish, Hindi, Malay, Dutch, Swedish, Danish, Finnish, Polish, Czech, Filipino, Persian, Greek, Hungarian, Macedonian, Romanian. + ### 40 languages @@ -240,7 +255,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Arabic (ar) — 24 models +Arabic (ar) — 25 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -250,9 +265,10 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | | MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | | MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | -| Moonshine Arabic | moonshine | 141 MB | personal use | +| Moonshine Arabic Tiny Streaming | moonshine | 32 MB | streaming | | Dolphin Base CTC INT8 | sherpa-onnx | 104 MB | auto language | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | @@ -432,7 +448,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Bengali (bn) — 20 models +Bengali (bn) — 21 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -440,6 +456,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| Bengali Streaming Zipformer | sherpa-onnx | 94 MB | streaming | | Dolphin Base CTC INT8 | sherpa-onnx | 104 MB | auto language | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | @@ -512,7 +529,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Bulgarian (bg) — 20 models +Bulgarian (bg) — 21 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -522,6 +539,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -568,7 +586,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Cantonese (yue) — 19 models +Cantonese (yue) — 22 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -576,7 +594,10 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | SenseVoice Small INT8 | sherpa-onnx | 240 MB | auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -621,7 +642,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Croatian (hr) — 20 models +Croatian (hr) — 21 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -631,6 +652,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -649,7 +671,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Czech (cs) — 20 models +Czech (cs) — 24 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -657,8 +679,12 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -677,7 +703,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Danish (da) — 20 models +Danish (da) — 24 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -685,8 +711,12 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -705,7 +735,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Dutch (nl) — 20 models +Dutch (nl) — 24 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -713,8 +743,12 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -733,7 +767,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-English (en) — 46 models +English (en) — 48 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -745,6 +779,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Small EN | faster-whisper | 484 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | Distil-Whisper Medium EN | faster-whisper | 789 MB | — | +| Distil-Whisper Large v3 | faster-whisper | 1.52 GB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | | MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | | MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | @@ -752,15 +787,16 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Parakeet TDT 0.6B v2 | mlx-audio | 2.47 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Moonshine English Tiny | moonshine | 44 MB | — | -| Moonshine English Tiny Streaming | moonshine | 52 MB | streaming | +| Moonshine English Tiny Streaming | moonshine | 45 MB | streaming | | Moonshine English Base | moonshine | 141 MB | — | -| Moonshine English Small Streaming | moonshine | 165 MB | streaming | -| Moonshine English Medium Streaming | moonshine | 304 MB | streaming | +| Moonshine English Small Streaming | moonshine | 142 MB | streaming | +| Moonshine English Medium Streaming | moonshine | 269 MB | streaming | | Streaming Zipformer English 20M INT8 | sherpa-onnx | 44 MB | streaming | | Canary 180M Flash English INT8 | sherpa-onnx | 210 MB | — | | SenseVoice Small INT8 | sherpa-onnx | 240 MB | auto language | | Parakeet TDT 0.6B v2 INT8 | sherpa-onnx | 661 MB | — | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny EN | whisper.cpp | 75 MB | — | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | @@ -787,7 +823,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Estonian (et) — 20 models +Estonian (et) — 21 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -797,6 +833,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -841,17 +878,20 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Filipino (fil) — 2 models +Filipino (fil) — 5 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | Dolphin Base CTC INT8 | sherpa-onnx | 104 MB | auto language | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language |
-Finnish (fi) — 20 models +Finnish (fi) — 24 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -859,8 +899,12 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -879,7 +923,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-French (fr) — 23 models +French (fr) — 24 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -891,6 +935,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | @@ -962,7 +1007,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-German (de) — 23 models +German (de) — 26 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -973,7 +1018,10 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | | MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | +| Moonshine German Tiny Streaming | moonshine | 32 MB | streaming | +| Moonshine German Small Streaming | moonshine | 122 MB | streaming | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | @@ -993,7 +1041,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Greek (el) — 20 models +Greek (el) — 23 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -1001,8 +1049,11 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -1153,7 +1204,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Hindi (hi) — 20 models +Hindi (hi) — 24 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -1161,8 +1212,12 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | Dolphin Base CTC INT8 | sherpa-onnx | 104 MB | auto language | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -1181,7 +1236,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Hungarian (hu) — 20 models +Hungarian (hu) — 24 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -1189,8 +1244,12 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -1235,7 +1294,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Indonesian (id) — 20 models +Indonesian (id) — 23 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -1243,8 +1302,11 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | Dolphin Base CTC INT8 | sherpa-onnx | 104 MB | auto language | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -1263,7 +1325,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Italian (it) — 23 models +Italian (it) — 24 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -1275,6 +1337,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | @@ -1294,7 +1357,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Japanese (ja) — 26 models +Japanese (ja) — 27 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -1304,11 +1367,12 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | | MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | | MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | -| Moonshine Japanese Tiny | moonshine | 72 MB | personal use | -| Moonshine Japanese Base | moonshine | 141 MB | personal use | +| Moonshine Japanese Tiny Streaming | moonshine | 32 MB | streaming | +| Moonshine Japanese Small Streaming | moonshine | 122 MB | streaming | | Dolphin Base CTC INT8 | sherpa-onnx | 104 MB | auto language | | SenseVoice Small INT8 | sherpa-onnx | 240 MB | auto language | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | @@ -1466,7 +1530,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Korean (ko) — 25 models +Korean (ko) — 26 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -1480,6 +1544,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | Dolphin Base CTC INT8 | sherpa-onnx | 104 MB | auto language | | SenseVoice Small INT8 | sherpa-onnx | 240 MB | auto language | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | @@ -1671,7 +1736,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Macedonian (mk) — 18 models +Macedonian (mk) — 21 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -1679,6 +1744,9 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -1723,7 +1791,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Malay (ms) — 20 models +Malay (ms) — 23 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -1731,8 +1799,11 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | Dolphin Base CTC INT8 | sherpa-onnx | 104 MB | auto language | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -1805,7 +1876,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Mandarin Chinese (zh) — 26 models +Mandarin Chinese (zh) — 27 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -1815,10 +1886,11 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | | MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | | MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | -| Moonshine Mandarin | moonshine | 141 MB | personal use | +| Moonshine Mandarin Tiny Streaming | moonshine | 32 MB | streaming | | Dolphin Base CTC INT8 | sherpa-onnx | 104 MB | auto language | | SenseVoice Small INT8 | sherpa-onnx | 240 MB | auto language | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | @@ -1949,7 +2021,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Norwegian (no) — 18 models +Norwegian (no) — 19 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -1957,6 +2029,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -2065,7 +2138,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Persian (fa) — 20 models +Persian (fa) — 23 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -2073,8 +2146,11 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | Dolphin Base CTC INT8 | sherpa-onnx | 104 MB | auto language | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -2093,7 +2169,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Polish (pl) — 20 models +Polish (pl) — 24 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -2101,8 +2177,12 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -2121,7 +2201,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Portuguese (pt) — 23 models +Portuguese (pt) — 24 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -2133,6 +2213,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | @@ -2180,7 +2261,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Romanian (ro) — 20 models +Romanian (ro) — 24 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -2188,8 +2269,12 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -2208,7 +2293,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Russian (ru) — 27 models +Russian (ru) — 28 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -2224,6 +2309,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | GigaAM v3 RNNT Russian | sherpa-onnx | 230 MB | — | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | @@ -2375,7 +2461,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Slovak (sk) — 20 models +Slovak (sk) — 21 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -2385,6 +2471,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -2457,7 +2544,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Spanish (es) — 24 models +Spanish (es) — 26 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -2468,8 +2555,10 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | | MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | -| Moonshine Spanish | moonshine | 65 MB | personal use | +| Moonshine Spanish Tiny Streaming | moonshine | 32 MB | streaming | +| Moonshine Spanish Small Streaming | moonshine | 122 MB | streaming | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | @@ -2543,7 +2632,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Swedish (sv) — 20 models +Swedish (sv) — 24 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -2551,8 +2640,12 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -2571,7 +2664,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Tagalog (tl) — 20 models +Tagalog (tl) — 21 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -2579,6 +2672,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| Moonshine Tagalog Tiny Streaming | moonshine | 32 MB | streaming | | Dolphin Base CTC INT8 | sherpa-onnx | 104 MB | auto language | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | @@ -2709,7 +2803,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Thai (th) — 20 models +Thai (th) — 23 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -2717,8 +2811,11 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | | Dolphin Base CTC INT8 | sherpa-onnx | 104 MB | auto language | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -2763,7 +2860,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Turkish (tr) — 18 models +Turkish (tr) — 22 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -2771,6 +2868,10 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -2815,7 +2916,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Ukrainian (uk) — 21 models +Ukrainian (uk) — 22 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -2826,6 +2927,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | MLX Parakeet TDT 0.6B v3 | mlx-audio | 2.51 GB | Apple silicon | | Moonshine Ukrainian | moonshine | 141 MB | personal use | | Parakeet TDT 0.6B v3 INT8 | sherpa-onnx | 672 MB | — | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | @@ -2910,7 +3012,7 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria
-Vietnamese (vi) — 21 models +Vietnamese (vi) — 25 models | Model | Engine | Download | Flags | | --- | --- | ---: | --- | @@ -2918,9 +3020,13 @@ Afrikaans, Amharic, Arabic, Assamese, Azerbaijani, Bashkir, Belarusian, Bulgaria | faster-whisper Base | faster-whisper | 145 MB | — | | faster-whisper Small | faster-whisper | 484 MB | — | | MLX Whisper Large v3 Turbo 4-bit | mlx-audio | 469 MB | Apple silicon | -| Moonshine Vietnamese | moonshine | 141 MB | personal use | +| MLX Qwen3-ASR 0.6B 4-bit | mlx-audio | 713 MB | Apple silicon | +| MLX Qwen3-ASR 1.7B 4-bit | mlx-audio | 1.61 GB | Apple silicon | +| Moonshine Vietnamese Tiny Streaming | moonshine | 32 MB | streaming | | Dolphin Base CTC INT8 | sherpa-onnx | 104 MB | auto language | | Dolphin Small CTC INT8 | sherpa-onnx | 250 MB | auto language | +| Nemotron 3.5 ASR Streaming 0.6B INT8 | sherpa-onnx | 682 MB | streaming, auto language | +| Qwen3-ASR 0.6B INT8 | sherpa-onnx | 987 MB | auto language | | whisper.cpp Tiny | whisper.cpp | 75 MB | — | | whisper.cpp Base | whisper.cpp | 142 MB | — | | whisper.cpp Small | whisper.cpp | 466 MB | — | diff --git a/pyproject.toml b/pyproject.toml index 23783ac..eae4ba1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ dependencies = [ [project.optional-dependencies] engines = [ "faster-whisper>=1.2,<2", - "moonshine-voice>=0.1,<0.2", + "moonshine-voice>=0.1.5,<0.2", "sherpa-onnx>=1.13.4,<2", "sherpa-onnx-bin>=1.13.4,<2", ] diff --git a/tests/test_model_manager.py b/tests/test_model_manager.py index 0ddcfff..f8522b2 100644 --- a/tests/test_model_manager.py +++ b/tests/test_model_manager.py @@ -12,7 +12,7 @@ import pytest from app import model_manager -from app.catalog import DEFAULT_CATALOG, CatalogModel +from app.catalog import DEFAULT_CATALOG, RETIRED_CATALOG, CatalogModel from app.model_manager import ( DownloadInProgressError, ModelIntegrityError, @@ -212,22 +212,42 @@ def test_catalog_includes_all_moonshine_lan_aaa() -> None: assert {entry.language_code for entry in entries.values() if entry.engine == "moonshine"} == { "ar", + "de", ENGLISH_LANGUAGE_CODE, SPANISH_LANGUAGE_CODE, "ja", "ko", + "tl", "uk", "vi", "zh", } assert entries["moonshine:en"].model_arch == 5 assert entries["moonshine:en-tiny-streaming"].supports_streaming is True - assert entries[MOONSHINE_SPANISH_ID].supports_streaming is False - assert entries[MOONSHINE_SPANISH_ID].commercial_use is False + retired = {model.id: model for model in RETIRED_CATALOG} + assert retired[MOONSHINE_SPANISH_ID].supports_streaming is False + assert retired[MOONSHINE_SPANISH_ID].commercial_use is False + assert retired[MOONSHINE_SPANISH_ID].replacement_id == "moonshine:es-small-streaming" assert ( entries["faster-whisper:distil-medium.en"].huggingface_repo == "Systran/faster-distil-whisper-medium.en" ) + assert entries["faster-whisper:distil-large-v3"].license_name == "MIT" + + +def test_retired_moonshine_installation_remains_manageable(tmp_path: Path) -> None: + manager = ModelManager(tmp_path / MODELS_DIRECTORY_NAME) + retired_model = next(model for model in RETIRED_CATALOG if model.id == MOONSHINE_SPANISH_ID) + model_root = manager.model_path(retired_model) + model_root.mkdir(parents=True) + (model_root / MODEL_METADATA_NAME).write_text("{}", encoding="utf-8") + + installed = {model.id: model for model in manager.installed()} + assert installed[MOONSHINE_SPANISH_ID].retired is True + assert installed[MOONSHINE_SPANISH_ID].replacement_id == "moonshine:es-small-streaming" + with pytest.raises(UnknownModelError, match="retired"): + manager.start_download(MOONSHINE_SPANISH_ID) + assert manager.delete(MOONSHINE_SPANISH_ID) is True def test_catalog_includes_portable_and_appl_aaaa() -> None: diff --git a/tests/test_sherpa_onnx.py b/tests/test_sherpa_onnx.py index d88bc0a..3c03c5d 100644 --- a/tests/test_sherpa_onnx.py +++ b/tests/test_sherpa_onnx.py @@ -9,10 +9,15 @@ import pytest -from app.catalog import CatalogModel +from app.catalog import DEFAULT_CATALOG, CatalogModel from app.errors import EngineUnavailableError, LanguageUnsupportedError from app.models.base import EngineTranscription, TranscriptionOptions -from app.models.sherpa_onnx import SherpaOnnxEngine, _SherpaOnnxStreamAdapter +from app.models.sherpa_onnx import ( + SherpaOnnxEngine, + _set_stream_language, + _SherpaOnnxStreamAdapter, + _SherpaRecognizerBuilder, +) NORMALIZED_SAMPLE_RATE_HZ = 16_000 SENSE_VOICE_MODEL_TYPE = "sense_voice" @@ -77,6 +82,29 @@ def _wave(path: Path) -> None: output.writeframes(array("h", [0, 100, -100]).tobytes()) +def test_nemotron_stream_language_keeps_locale_prompt() -> None: + model = next( + model + for model in DEFAULT_CATALOG + if model.id == "sherpa-onnx:nemotron-3.5-asr-streaming-0.6b-320ms-int8" + ) + builder = _SherpaRecognizerBuilder(None, model, threads=1) + + class Stream: + def __init__(self) -> None: + self.options: dict[str, str] = {} + + def set_option(self, name: str, value: str) -> None: + self.options[name] = value + + stream = Stream() + mapped = builder.stream_language("de-DE") + _set_stream_language(stream, mapped, preserve_locale=builder.uses_stream_language_locale()) + + assert mapped == "de-DE" + assert stream.options == {"language": "de-DE"} + + async def test_sherpa_keeps_one_recognizer_loaded( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: @@ -427,6 +455,46 @@ def get_result(self, stream: FakeStream) -> str: assert recognizer.stream.waveform is not None +def test_decode_wave_online_sets_optional_language_option(tmp_path: Path) -> None: + from app.models.sherpa_onnx import _decode_wave_online + + audio = tmp_path / "audio.wav" + _wave(audio) + + class FakeStream: + def __init__(self) -> None: + self.options: dict[str, str] = {} + + def has_option(self, name: str) -> bool: + return name == "language" + + def set_option(self, name: str, value: str) -> None: + self.options[name] = value + + def accept_waveform(self, sample_rate: int, samples: list[float]) -> None: + pass + + def input_finished(self) -> None: + pass + + class FakeRecognizer: + def __init__(self) -> None: + self.stream = FakeStream() + + def create_stream(self) -> FakeStream: + return self.stream + + def is_ready(self, stream: FakeStream) -> bool: + return False + + def get_result(self, stream: FakeStream) -> str: + return "text" + + recognizer = FakeRecognizer() + assert _decode_wave_online(recognizer, audio, "de-DE") == "text" + assert recognizer.stream.options == {"language": "de"} + + def test_supports_streaming_only_for_the_st_aa(tmp_path: Path) -> None: streaming_model = _catalog( "streaming_zipformer", @@ -524,6 +592,23 @@ def test_stream_adapter_reports_partial_the_aaaa() -> None: assert stream.finished is True +def test_nemotron_stream_adapter_strips_language_tag() -> None: + recognizer = _FakeOnlineRecognizer() + stream = _FakeOnlineStream() + adapter = _SherpaOnnxStreamAdapter(recognizer, stream, language_mapper=lambda text: text) + events: list[object] = [] + adapter.add_listener(events.append) + + recognizer.text = "Hello world. " + adapter.add_audio([0.1], NORMALIZED_SAMPLE_RATE_HZ) + result = adapter.stop() + + assert events[0].line.text == "Hello world." + assert result.lines[0].text == "Hello world." + assert adapter.detected_language == "en-US" + assert result.detected_language == "en-US" + + def test_stream_adapter_starts_a_new_line_a_aaaaa() -> None: recognizer = _FakeOnlineRecognizer() stream = _FakeOnlineStream() diff --git a/uv.lock b/uv.lock index 4fa3c76..c50228f 100644 --- a/uv.lock +++ b/uv.lock @@ -936,7 +936,7 @@ wheels = [ [[package]] name = "moonshine-voice" -version = "0.1.0" +version = "0.1.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -948,11 +948,11 @@ dependencies = [ { name = "tqdm" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/a4/6e128bb0f2fce9993e0ffd8e8b6214d1d7f694f880182339eedbb6f3f375/moonshine_voice-0.1.0-py3-none-macosx_15_0_arm64.whl", hash = "sha256:9c70431836ed805f9ab30adcdfe97018c17b1e932ba7e205fff0562950dc0913", size = 54399781, upload-time = "2026-07-27T21:01:03.631Z" }, - { url = "https://files.pythonhosted.org/packages/45/99/6157236653594449575de6ef41abce70242c1a5a57c6dbaac408c2f99fe3/moonshine_voice-0.1.0-py3-none-manylinux_2_31_aarch64.manylinux_2_39_aarch64.whl", hash = "sha256:58d5546a656f65061d1d4a376818b08109fe25600a22dfdbbba0179ccb3f0d07", size = 55090195, upload-time = "2026-07-27T23:57:52.377Z" }, - { url = "https://files.pythonhosted.org/packages/05/08/39d25f1ccc106720eb6122bc7a7ab029ac3c407be0f010a8855056b1f593/moonshine_voice-0.1.0-py3-none-manylinux_2_34_aarch64.whl", hash = "sha256:da54a99587edb987c8755f2c746cab72fada0e67cf847f7edfb8105447f405af", size = 55143464, upload-time = "2026-07-27T21:15:00.875Z" }, - { url = "https://files.pythonhosted.org/packages/fd/a3/e3c0156664e9505af7b23072c3e947cec3a7ee938764c70424051366c368/moonshine_voice-0.1.0-py3-none-manylinux_2_34_x86_64.whl", hash = "sha256:0f833deb43bad5dcfb4cfd3257b6df83ef9abd3f27be3199622fe41932e8d916", size = 56369045, upload-time = "2026-07-27T21:10:54.822Z" }, - { url = "https://files.pythonhosted.org/packages/77/78/2e0fb469c6d236eb2782d6129d056a317e75d85efb340680617cebc8d82b/moonshine_voice-0.1.0-py3-none-win_amd64.whl", hash = "sha256:af2b8536ab08c48f33d5b1e126656b05ceae9a8980fc1c95dd70665b0b57c8e7", size = 50534088, upload-time = "2026-07-28T00:49:05.373Z" }, + { url = "https://files.pythonhosted.org/packages/86/2a/9eca2f000e4990315d18aa360c6eb679417b29252e6307c273bcbef38461/moonshine_voice-0.1.5-py3-none-macosx_15_0_arm64.whl", hash = "sha256:9866b1956d12fff11923ad42f1140bcb314bf05bf8d8576fb7b3aa819ec40e51", size = 18589534, upload-time = "2026-08-24T20:50:28.184Z" }, + { url = "https://files.pythonhosted.org/packages/23/1c/cb821747b2a7b40216dd576a62d1ebc77d04fab97064efc97fa460f137ed/moonshine_voice-0.1.5-py3-none-manylinux_2_31_aarch64.whl", hash = "sha256:605300afe33eb7920301e73f11e0ea7493770eb655f389ba65be4aa1db8dedd6", size = 18505651, upload-time = "2026-08-24T21:22:03.754Z" }, + { url = "https://files.pythonhosted.org/packages/56/b3/ca8c6f3eb47498208e92094d68e3dc2a0ce50213fdf980e212f293872455/moonshine_voice-0.1.5-py3-none-manylinux_2_34_aarch64.whl", hash = "sha256:fd9fa38ab58c6a8e64eb43b81b0f491138159c7ce45584bb0736ed7efa539766", size = 18634500, upload-time = "2026-08-24T21:15:04.032Z" }, + { url = "https://files.pythonhosted.org/packages/a9/9d/228f738b48e0e7cc1de97c3f842b5470c6a3c5f7a0bffbf13c3d00eefb87/moonshine_voice-0.1.5-py3-none-manylinux_2_34_x86_64.whl", hash = "sha256:1ed9e0ccf94be4845d69e7fa862dcf4c8b5801a758796966494c694f48184496", size = 19897307, upload-time = "2026-08-24T21:06:47.113Z" }, + { url = "https://files.pythonhosted.org/packages/ea/05/ad3457baa56170a13829c98327598a4d99d6cd4bdff8ae59df4c47ebdd11/moonshine_voice-0.1.5-py3-none-win_amd64.whl", hash = "sha256:7fc9a84e827360ddb85c2735f44a1502c5575c27dcb476a2555646cd20a70e9d", size = 16542073, upload-time = "2026-08-24T23:17:38.555Z" }, ] [[package]] @@ -2028,7 +2028,7 @@ requires-dist = [ { name = "faster-whisper", marker = "extra == 'engines'", specifier = ">=1.2,<2" }, { name = "jinja2", specifier = ">=3.1,<4" }, { name = "mlx-audio", extras = ["stt"], marker = "platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'apple'", specifier = ">=0.4.6,<0.5" }, - { name = "moonshine-voice", marker = "extra == 'engines'", specifier = ">=0.1,<0.2" }, + { name = "moonshine-voice", marker = "extra == 'engines'", specifier = ">=0.1.5,<0.2" }, { name = "pysbd", specifier = ">=0.3.4,<0.4" }, { name = "python-multipart", specifier = ">=0.0.9,<1" }, { name = "qrcode", specifier = ">=7.4,<9" },