fix(hermes-voice): switch Atlas Piper TTS to hfc_female, bake amy as alternate
Brad's 2026-08-20 decision: default the private Hermes chat TTS voice to a warmer, pleasant feminine voice (en_US-hfc_female-medium) instead of Lessac. en_US-amy-medium is baked into the same image as a selectable alternate so the choice can be revisited without another Jenkins build. Lessac models are kept (not deleted) since the task didn't require reclaiming image size; the image grows by ~120.6 MiB (2 medium voices, onnx+json, from the pinned rhasspy/piper-voices revision already used for lessac). The Piper server only reads HERMES_TTS_VOICE at process start; the "voice" field in the WebUI's request body is not consulted by the server (dockerfiles/hermes-jetson-tts-server.py:59-72). That WebUI string is aligned here for honesty, but the env var remains the effective control -- align image digest and env var rollout order (see PR description). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
11bd04cce5
commit
e0f1a4504c
@ -24,6 +24,18 @@ ADD --checksum=sha256:f7d01dde371555732c4c314111ac79672b1a5ce2fc19266ab42178fd8d
|
||||
ADD --checksum=sha256:45754dfdebb3b8661c3fc564713772deec6e064feeb5b4e9594857dc7305193a --chmod=0444 \
|
||||
https://huggingface.co/rhasspy/piper-voices/resolve/ea046e8458f6acd997706d6e6066a022b42f6fb1/en/en_US/lessac/low/en_US-lessac-low.onnx.json?download=true \
|
||||
/opt/models/piper/en_US-lessac-low.onnx.json
|
||||
ADD --checksum=sha256:914c473788fc1fa8b63ace1cdcdb44588f4ae523d3ab37df1536616835a140b7 --chmod=0444 \
|
||||
https://huggingface.co/rhasspy/piper-voices/resolve/ea046e8458f6acd997706d6e6066a022b42f6fb1/en/en_US/hfc_female/medium/en_US-hfc_female-medium.onnx?download=true \
|
||||
/opt/models/piper/en_US-hfc_female-medium.onnx
|
||||
ADD --checksum=sha256:03f1fa0622b80463283592d97aca9f6e89aec345a5c56b7257723e0093c58b6c --chmod=0444 \
|
||||
https://huggingface.co/rhasspy/piper-voices/resolve/ea046e8458f6acd997706d6e6066a022b42f6fb1/en/en_US/hfc_female/medium/en_US-hfc_female-medium.onnx.json?download=true \
|
||||
/opt/models/piper/en_US-hfc_female-medium.onnx.json
|
||||
ADD --checksum=sha256:b3a6e47b57b8c7fbe6a0ce2518161a50f59a9cdd8a50835c02cb02bdd6206c18 --chmod=0444 \
|
||||
https://huggingface.co/rhasspy/piper-voices/resolve/ea046e8458f6acd997706d6e6066a022b42f6fb1/en/en_US/amy/medium/en_US-amy-medium.onnx?download=true \
|
||||
/opt/models/piper/en_US-amy-medium.onnx
|
||||
ADD --checksum=sha256:95a23eb4d42909d38df73bb9ac7f45f597dbfcde2d1bf9526fdeaf5466977d77 --chmod=0444 \
|
||||
https://huggingface.co/rhasspy/piper-voices/resolve/ea046e8458f6acd997706d6e6066a022b42f6fb1/en/en_US/amy/medium/en_US-amy-medium.onnx.json?download=true \
|
||||
/opt/models/piper/en_US-amy-medium.onnx.json
|
||||
RUN chmod 0555 /opt/models /opt/models/piper
|
||||
|
||||
COPY dockerfiles/hermes-jetson-tts-server.py /opt/atlas/hermes-jetson-tts-server.py
|
||||
@ -31,11 +43,11 @@ RUN chmod 0555 /opt/atlas/hermes-jetson-tts-server.py
|
||||
|
||||
# Load the actual pinned voice during the ARM64 build. This catches package or
|
||||
# model-format drift before the image can reach Flux.
|
||||
RUN python -c "import stat; from pathlib import Path; from piper import PiperVoice; p=Path('/opt/models/piper'); models=[p/'en_US-lessac-high.onnx',p/'en_US-lessac-medium.onnx',p/'en_US-lessac-low.onnx']; assert stat.S_IMODE(p.stat().st_mode)==0o555; assert all(stat.S_IMODE(model.stat().st_mode)==0o444 for model in models); voices=[PiperVoice.load(model,Path(str(model)+'.json'),use_cuda=False,download_dir=p) for model in models]; assert all(voice.config.sample_rate>0 for voice in voices)"
|
||||
RUN python -c "import stat; from pathlib import Path; from piper import PiperVoice; p=Path('/opt/models/piper'); models=[p/'en_US-lessac-high.onnx',p/'en_US-lessac-medium.onnx',p/'en_US-lessac-low.onnx',p/'en_US-hfc_female-medium.onnx',p/'en_US-amy-medium.onnx']; assert stat.S_IMODE(p.stat().st_mode)==0o555; assert all(stat.S_IMODE(model.stat().st_mode)==0o444 for model in models); voices=[PiperVoice.load(model,Path(str(model)+'.json'),use_cuda=False,download_dir=p) for model in models]; assert all(voice.config.sample_rate>0 for voice in voices)"
|
||||
|
||||
ENV HERMES_TTS_HOST=0.0.0.0 \
|
||||
HERMES_TTS_PORT=9001 \
|
||||
HERMES_TTS_VOICE=en_US-lessac-medium \
|
||||
HERMES_TTS_VOICE=en_US-hfc_female-medium \
|
||||
HERMES_TTS_CACHE=/opt/models/piper \
|
||||
OMP_NUM_THREADS=2 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
|
||||
@ -17,7 +17,7 @@ from piper import PiperConfig, PiperVoice, SynthesisConfig
|
||||
|
||||
HOST = os.getenv("HERMES_TTS_HOST", "0.0.0.0")
|
||||
PORT = int(os.getenv("HERMES_TTS_PORT", "9001"))
|
||||
VOICE_NAME = os.getenv("HERMES_TTS_VOICE", "en_US-lessac-high")
|
||||
VOICE_NAME = os.getenv("HERMES_TTS_VOICE", "en_US-hfc_female-medium")
|
||||
CACHE_DIR = Path(os.getenv("HERMES_TTS_CACHE", "/cache/piper"))
|
||||
MAX_TEXT_CHARS = 5000
|
||||
ONNX_THREADS = max(1, int(os.getenv("HERMES_TTS_ONNX_THREADS", "4")))
|
||||
|
||||
@ -63,7 +63,7 @@ atlas = ''' # ── Atlas private Jetson TTS ──────────
|
||||
request_body = json.dumps({
|
||||
"model": "piper",
|
||||
"input": text,
|
||||
"voice": "en_US-lessac-high",
|
||||
"voice": "en_US-hfc_female-medium",
|
||||
"speed": speed,
|
||||
}).encode("utf-8")
|
||||
request = Request(atlas_url, data=request_body, headers={
|
||||
|
||||
@ -114,7 +114,7 @@ spec:
|
||||
app: hermes-tts
|
||||
annotations:
|
||||
ai.bstein.dev/role: private-chat-text-to-speech
|
||||
ai.bstein.dev/model: piper-en-us-lessac-medium
|
||||
ai.bstein.dev/model: piper-en-us-hfc-female-medium
|
||||
ai.bstein.dev/gpu: CPU-only beside Whisper on the voice node
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
@ -131,7 +131,7 @@ spec:
|
||||
- {name: HOME, value: /tmp}
|
||||
- {name: XDG_CACHE_HOME, value: /tmp/cache}
|
||||
- {name: HERMES_TTS_PORT, value: "9001"}
|
||||
- {name: HERMES_TTS_VOICE, value: en_US-lessac-medium}
|
||||
- {name: HERMES_TTS_VOICE, value: en_US-hfc_female-medium}
|
||||
- {name: HERMES_TTS_CACHE, value: /opt/models/piper}
|
||||
- {name: HERMES_TTS_ONNX_THREADS, value: "2"}
|
||||
startupProbe:
|
||||
|
||||
@ -398,7 +398,11 @@ def test_voice_models_are_baked_and_runtime_has_no_public_egress():
|
||||
assert "HERMES_STT_CACHE=/opt/models/whisper" in stt_dockerfile
|
||||
assert "ADD --checksum=sha256:4cabf7c3" in tts_dockerfile
|
||||
assert "ADD --checksum=sha256:db42b97d" in tts_dockerfile
|
||||
assert tts_dockerfile.count("--chmod=0444") == 6
|
||||
assert "ADD --checksum=sha256:914c473788fc1fa8b63ace1cdcdb44588f4ae523d3ab37df1536616835a140b7" in tts_dockerfile
|
||||
assert "ADD --checksum=sha256:03f1fa0622b80463283592d97aca9f6e89aec345a5c56b7257723e0093c58b6c" in tts_dockerfile
|
||||
assert "ADD --checksum=sha256:b3a6e47b57b8c7fbe6a0ce2518161a50f59a9cdd8a50835c02cb02bdd6206c18" in tts_dockerfile
|
||||
assert "ADD --checksum=sha256:95a23eb4d42909d38df73bb9ac7f45f597dbfcde2d1bf9526fdeaf5466977d77" in tts_dockerfile
|
||||
assert tts_dockerfile.count("--chmod=0444") == 10
|
||||
assert "chmod 0555 /opt/models /opt/models/piper" in tts_dockerfile
|
||||
assert "HERMES_TTS_CACHE=/opt/models/piper" in tts_dockerfile
|
||||
tts_server = (ROOT / "dockerfiles" / "hermes-jetson-tts-server.py").read_text()
|
||||
@ -449,7 +453,7 @@ def test_voice_workloads_have_deliberate_xavier_placement():
|
||||
tts_env = {
|
||||
item["name"]: item["value"] for item in tts["containers"][0]["env"]
|
||||
}
|
||||
assert tts_env["HERMES_TTS_VOICE"] == "en_US-lessac-medium"
|
||||
assert tts_env["HERMES_TTS_VOICE"] == "en_US-hfc_female-medium"
|
||||
assert tts_env["HERMES_TTS_ONNX_THREADS"] == "2"
|
||||
assert tts["containers"][0]["resources"]["limits"]["cpu"] == "4"
|
||||
assert all("hostPath" not in volume for volume in stt["volumes"])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user