diff --git a/docker-compose.yml b/docker-compose.yml index 9f63909..3875900 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -66,6 +66,11 @@ services: WHISPER_MODEL: ${WHISPER_MODEL:-medium} WHISPER_DEVICE: ${WHISPER_DEVICE:-cuda} WHISPER_COMPUTE_TYPE: ${WHISPER_COMPUTE_TYPE:-float16} + # TTS engine. Rendered into /app/config/jarvis.json via envsubst (the + # bridge reads that JSON BEFORE the env, so it must carry the real engine, + # not the template's old hardcoded "piper" — otherwise Korean text is read + # by the English Piper voice). Default melo; .env can override. + TTS_ENGINE: ${TTS_ENGINE:-melo} # MeloTTS on the GPU (cu128 torch baked by docker/setup-melo.sh). CPU synth # serialised under load and pushed TTS to 7-8s; GPU does ~0.3s/sentence. MELO_DEVICE: ${MELO_DEVICE:-cuda} diff --git a/docker/jarvis-config.template.json b/docker/jarvis-config.template.json index a83fec0..dde5c3c 100644 --- a/docker/jarvis-config.template.json +++ b/docker/jarvis-config.template.json @@ -6,7 +6,7 @@ "ollama_chat_model": "${OLLAMA_CHAT_MODEL}", "intent_judge_model": "${OLLAMA_INTENT_MODEL}", "tts_enabled": true, - "tts_engine": "piper", + "tts_engine": "${TTS_ENGINE}", "tts_piper_model_path": "${TTS_PIPER_MODEL_PATH}", "whisper_model": "${WHISPER_MODEL}", "whisper_backend": "faster-whisper", diff --git a/tests/test_tts_engine_config.py b/tests/test_tts_engine_config.py new file mode 100644 index 0000000..bb247e7 --- /dev/null +++ b/tests/test_tts_engine_config.py @@ -0,0 +1,35 @@ +"""The container's TTS engine must be env-driven, not hardcoded. + +Regression for a bug where docker/jarvis-config.template.json hardcoded +`"tts_engine": "piper"`. The bridge reads the rendered /app/config/jarvis.json +*before* the environment, so a hardcoded "piper" overrode `TTS_ENGINE=melo` in +.env and the bot read Korean text with the English Piper voice ("foreign +accent"). The template must carry `${TTS_ENGINE}` so envsubst (entrypoint.sh) +renders whatever engine the deployment configured. +""" + +import json +import string +from pathlib import Path + +TEMPLATE = Path(__file__).resolve().parent.parent / "docker" / "jarvis-config.template.json" + + +def _render(**env) -> dict: + """Mimic entrypoint.sh `envsubst < template`: substitute env vars, leaving + any unset ones as literal text (valid JSON string values).""" + raw = TEMPLATE.read_text(encoding="utf-8") + return json.loads(string.Template(raw).safe_substitute(**env)) + + +def test_template_does_not_hardcode_an_engine(): + raw = TEMPLATE.read_text(encoding="utf-8") + assert '"tts_engine": "${TTS_ENGINE}"' in raw + assert '"tts_engine": "piper"' not in raw + assert '"tts_engine": "melo"' not in raw + + +def test_rendered_engine_follows_env(): + assert _render(TTS_ENGINE="melo")["tts_engine"] == "melo" + assert _render(TTS_ENGINE="piper")["tts_engine"] == "piper" + assert _render(TTS_ENGINE="xtts")["tts_engine"] == "xtts"