From 11c36210934af6bec94c1beb3d92e5bbe0dc9c29 Mon Sep 17 00:00:00 2001 From: javis-bot Date: Tue, 23 Jun 2026 03:27:33 +0900 Subject: [PATCH] fix: make container TTS engine env-driven so melo isn't overridden by piper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docker/jarvis-config.template.json hardcoded "tts_engine": "piper". entrypoint renders it into /app/config/jarvis.json, and bridge _tts_engine_setting() reads that JSON BEFORE the env — so TTS_ENGINE=melo in .env was ignored and the bot synthesised Korean with the English Piper voice (the "foreign accent" the user heard); the warm melo-worker sat unused. Template now carries ${TTS_ENGINE}; compose sets TTS_ENGINE=${TTS_ENGINE:-melo} so envsubst renders the real engine. Verified: envsubst with TTS_ENGINE=melo yields "tts_engine": "melo", and `docker compose config` passes TTS_ENGINE=melo. Added a regression test that the template stays env-driven and renders the configured engine. Co-Authored-By: Claude Opus 4.7 --- docker-compose.yml | 5 +++++ docker/jarvis-config.template.json | 2 +- tests/test_tts_engine_config.py | 35 ++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 tests/test_tts_engine_config.py 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"