Merge remote-tracking branch 'origin/main' into fix/review-2175

# Conflicts:
#	docs/engines/mlx-audio.md
This commit is contained in:
Palash Debnath
2026-09-17 22:46:11 +05:30
4 changed files with 114 additions and 1 deletions
+1
View File
@@ -41,6 +41,7 @@ the frozen-backend fallback mirror it for their toolchains.
- Correct the Windows Rustup installation command in tooling and documentation (#2066) — thanks @Rukhaam!
- Show local setup guidance when remote native engine installation is unavailable (#2166)
- Show scrubbed native error tails and exit codes for failed dubbing extraction (#2167)
- Name every language the installed Kokoro table supports when rejecting an unsupported one (#2174) — thanks @paranoyouz-collab and @raya-mansouri!
### CI
+32 -1
View File
@@ -1624,6 +1624,7 @@ class KittenTTSBackend(TTSBackend):
# resolved unchanged by `resolve_kokoro_lang_code()` below.
_KOKORO_ISO_BY_FULL_NAME = {
"english": "en",
"british english": "en-gb",
"spanish": "es",
"french": "fr",
"hindi": "hi",
@@ -1634,6 +1635,31 @@ _KOKORO_ISO_BY_FULL_NAME = {
}
def _kokoro_supported_labels(aliases: dict, lang_codes: dict) -> list[str]:
"""Human labels for every language Kokoro accepts, read from its own tables.
Derived from the installed package, never from
``_KOKORO_ISO_BY_FULL_NAME``: that map exists to translate full names
*into* Kokoro's codes, and reusing it to describe what Kokoro supports
understates the model when a newly supported code has no full-name alias.
Read every installed code so new languages remain visible without changing
the input-name map.
"""
labels: dict[str, str] = {}
# Prefer the full name a caller can actually pass.
for name, iso in _KOKORO_ISO_BY_FULL_NAME.items():
code = aliases.get(iso, iso)
if code in lang_codes:
labels.setdefault(code, name.title())
# Then whatever the installed table supports that no full name reaches. Its
# own description is a display name for some codes ("British English") and
# an ISO tag for others ("pt-br"); either names the language better than
# dropping it.
for code, described in lang_codes.items():
labels.setdefault(code, str(described))
return sorted(labels.values())
def resolve_kokoro_lang_code(language: str) -> str:
"""Map a full language name / ISO code to Kokoro's single-letter
`lang_code`, against the AUTHORITATIVE table read from the installed
@@ -1651,7 +1677,12 @@ def resolve_kokoro_lang_code(language: str) -> str:
iso = _KOKORO_ISO_BY_FULL_NAME.get(key, key)
code = ALIASES.get(iso, iso)
if code not in LANG_CODES:
supported = ", ".join(sorted(name.title() for name in _KOKORO_ISO_BY_FULL_NAME))
# Labels from newer installed tables must remain selectable even when
# they have no entry in our compatibility map of full names.
code = next((candidate for candidate, label in LANG_CODES.items()
if str(label).strip().lower() == key), code)
if code not in LANG_CODES:
supported = ", ".join(_kokoro_supported_labels(ALIASES, LANG_CODES))
raise ValueError(
f"mlx-audio's Kokoro model (mlx-community/Kokoro-82M-bf16) doesn't "
f"support language={language!r}. Kokoro supports: {supported}. "
+5
View File
@@ -89,3 +89,8 @@ preserve filter context at chunk boundaries; rate changes start a new group.
Profile language refusals are terminal request errors on both local and remote
rendering, including streaming. Electron and web show localized guidance that
Auto inherits the profile language; neither silently retries the same refusal.
Kokoro language errors list every language in the installed models table.
“British English” and `en-gb` both select its British English voice pipeline.
Display names from newer installed Kokoro tables are accepted too, so a language
advertised by the error message can be selected without updating a hardcoded map.
+76
View File
@@ -419,6 +419,82 @@ def test_mlx_audio_kokoro_rejects_unsupported_language_cleanly(language):
assert "English" in msg # names what Kokoro DOES support
# The label derivation takes the tables as arguments, so it runs on every
# platform — unlike the resolution tests above, which need the real
# Apple-Silicon-only package. The bug it guards is invisible on the runners
# that skip.
def test_kokoro_resolves_british_english_label(monkeypatch):
pipeline = types.ModuleType("mlx_audio.tts.models.kokoro.pipeline")
pipeline.ALIASES = {"en-gb": "b"}
pipeline.LANG_CODES = {"b": "British English"}
monkeypatch.setitem(sys.modules, "mlx_audio.tts.models.kokoro.pipeline", pipeline)
assert tts_backend.resolve_kokoro_lang_code("British English") == "b"
def test_kokoro_advertised_labels_round_trip_through_installed_table(monkeypatch):
pipeline = types.ModuleType("mlx_audio.tts.models.kokoro.pipeline")
pipeline.ALIASES = {"en": "a", "en-gb": "b", "xx": "x"}
pipeline.LANG_CODES = {"a": "American English", "b": "British English", "x": "Newly Added Language"}
monkeypatch.setitem(sys.modules, "mlx_audio.tts.models.kokoro.pipeline", pipeline)
labels = tts_backend._kokoro_supported_labels(pipeline.ALIASES, pipeline.LANG_CODES)
assert {tts_backend.resolve_kokoro_lang_code(label) for label in labels} == {"a", "b", "x"}
def test_kokoro_supported_labels_name_a_code_only_an_alias_reaches():
"""A code reached only through an alias is still named.
British English is `en-gb` -> "b". The supported list must include it
alongside the other installed language codes.
"""
aliases = {"en": "a", "en-gb": "b", "es": "e"}
lang_codes = {"a": "American English", "b": "British English", "e": "es"}
labels = tts_backend._kokoro_supported_labels(aliases, lang_codes)
assert "British English" in labels
assert "English" in labels # the full name a caller can pass
assert "Spanish" in labels
def test_kokoro_supported_labels_track_the_installed_table():
"""A language a later mlx-audio adds appears without editing our map.
That is the whole point of reading the vendored table rather than a
hardcoded one.
"""
aliases = {"en": "a", "xx": "x"}
lang_codes = {"a": "American English", "x": "Newly Added Language"}
assert "Newly Added Language" in tts_backend._kokoro_supported_labels(aliases, lang_codes)
def test_kokoro_supported_labels_prefer_the_passable_full_name():
"""The label is the name a caller can actually pass.
`LANG_CODES` describes Spanish as the ISO tag "es", but "Spanish" is what
the frontend sends, so that is the more useful label to print.
"""
labels = tts_backend._kokoro_supported_labels({"es": "e"}, {"e": "es"})
assert labels == ["Spanish"]
def test_kokoro_supported_labels_skip_codes_the_installed_table_lacks():
"""An install exposing one language is not described as supporting eight.
Our map knows eight; the installed table is what decides.
"""
labels = tts_backend._kokoro_supported_labels({"it": "i"}, {"i": "it"})
assert labels == ["Italian"]
def test_mlx_audio_kokoro_error_names_british_english():
"""The real message names British English, which it previously omitted."""
pytest.importorskip("mlx_audio", reason="mlx-audio is Apple-Silicon-only")
with pytest.raises(ValueError) as ei:
tts_backend.resolve_kokoro_lang_code("Persian")
# Reachable as "en-gb" and previously missing from the message.
assert "British English" in str(ei.value)
def test_mlx_audio_generate_rejects_unsupported_kokoro_language_before_calling_model():
pytest.importorskip("mlx_audio", reason="mlx-audio is Apple-Silicon-only")
backend = tts_backend.MLXAudioBackend()