From ee019d1cc149bc66e62cd2a66ff38980c143682a Mon Sep 17 00:00:00 2001 From: Gabriel Anzziani Date: Mon, 13 Jul 2026 16:12:14 -0400 Subject: [PATCH] test(tts): add lang_code regression tests, document tts.openai.language Address review feedback on #31693: - Regression tests assert extra_body == {"lang_code": ...} is forwarded when tts.openai.language is configured, and omitted when unset/empty - Document tts.openai.language as intended for OpenAI-compatible endpoints that support lang_code (e.g. Kokoro-FastAPI) Co-Authored-By: Claude Fable 5 --- tests/tools/test_tts_speed.py | 52 +++++++++++++++++++++++++ website/docs/user-guide/features/tts.md | 3 ++ 2 files changed, 55 insertions(+) diff --git a/tests/tools/test_tts_speed.py b/tests/tools/test_tts_speed.py index 1c9ee5a17314..d64eab8c6af1 100644 --- a/tests/tools/test_tts_speed.py +++ b/tests/tools/test_tts_speed.py @@ -114,6 +114,58 @@ class TestOpenaiTtsSpeed: assert kwargs["speed"] == 4.0 +# --------------------------------------------------------------------------- +# OpenAI TTS language (lang_code for OpenAI-compatible endpoints) +# --------------------------------------------------------------------------- + +class TestOpenaiTtsLangCode: + def _run(self, tts_config, tmp_path, monkeypatch): + monkeypatch.setenv("OPENAI_API_KEY", "test-key") + mock_response = MagicMock() + mock_client = MagicMock() + mock_client.audio.speech.create.return_value = mock_response + mock_cls = MagicMock(return_value=mock_client) + + with patch("tools.tts_tool._import_openai_client", return_value=mock_cls), \ + patch("tools.tts_tool._resolve_openai_audio_client_config", + return_value=("test-key", None)): + from tools.tts_tool import _generate_openai_tts + _generate_openai_tts("Hola", str(tmp_path / "out.mp3"), tts_config) + return mock_client.audio.speech.create + + def test_default_no_extra_body(self, tmp_path, monkeypatch): + """No language config => no extra_body kwarg in create call.""" + create = self._run({}, tmp_path, monkeypatch) + kwargs = create.call_args[1] + assert "extra_body" not in kwargs + + def test_language_forwarded_as_lang_code(self, tmp_path, monkeypatch): + """tts.openai.language is forwarded as extra_body lang_code.""" + create = self._run({"openai": {"language": "es"}}, tmp_path, monkeypatch) + kwargs = create.call_args[1] + assert kwargs["extra_body"] == {"lang_code": "es"} + + def test_empty_language_omitted(self, tmp_path, monkeypatch): + """Empty language string => extra_body omitted.""" + create = self._run({"openai": {"language": ""}}, tmp_path, monkeypatch) + kwargs = create.call_args[1] + assert "extra_body" not in kwargs + + def test_global_language_not_forwarded(self, tmp_path, monkeypatch): + """Only tts.openai.language is honored, not a top-level tts.language.""" + create = self._run({"language": "es"}, tmp_path, monkeypatch) + kwargs = create.call_args[1] + assert "extra_body" not in kwargs + + def test_language_coexists_with_speed(self, tmp_path, monkeypatch): + """language and speed are forwarded independently.""" + create = self._run({"openai": {"language": "es", "speed": 2.0}}, + tmp_path, monkeypatch) + kwargs = create.call_args[1] + assert kwargs["extra_body"] == {"lang_code": "es"} + assert kwargs["speed"] == 2.0 + + # --------------------------------------------------------------------------- # MiniMax TTS (t2a_v2 endpoint: nested voice_setting/audio_setting, # JSON response with hex-encoded audio. Falls back to the legacy diff --git a/website/docs/user-guide/features/tts.md b/website/docs/user-guide/features/tts.md index b337e4fc9d68..5f1333458190 100644 --- a/website/docs/user-guide/features/tts.md +++ b/website/docs/user-guide/features/tts.md @@ -56,6 +56,7 @@ tts: voice: "alloy" # alloy, echo, fable, onyx, nova, shimmer base_url: "https://api.openai.com/v1" # Override for OpenAI-compatible TTS endpoints speed: 1.0 # 0.25 - 4.0 + # language: "es" # Sent as lang_code — only for OpenAI-compatible endpoints that support it (e.g. Kokoro) minimax: model: "speech-02-hd" # speech-02-hd (default), speech-02-turbo voice_id: "English_Graceful_Lady" # See https://platform.minimax.io/faq/system-voice-id @@ -133,6 +134,8 @@ tts: The rewrite uses `auxiliary.tts_audio_tags` and defaults to your main chat model. Override that auxiliary task if you want tag insertion handled by a cheaper or faster model. +**Language (OpenAI-compatible endpoints)**: `tts.openai.language` is forwarded to the endpoint as a `lang_code` request parameter. It is intended for OpenAI-compatible TTS servers that support `lang_code` — for example [Kokoro-FastAPI](https://github.com/remsky/Kokoro-FastAPI), where `language: "es"` selects the Spanish phonemizer instead of the English default. Leave it unset when using the official OpenAI API, which does not accept this parameter. When unset, nothing extra is sent. + ### Input length limits