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 <noreply@anthropic.com>
This commit is contained in:
Gabriel Anzziani 2026-07-13 16:12:14 -04:00 committed by Teknium
parent c0dda61032
commit ee019d1cc1
2 changed files with 55 additions and 0 deletions

View file

@ -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

View file

@ -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