From fb2a30d459725f9de9894093a8e04370750677ce Mon Sep 17 00:00:00 2001 From: jenkins Date: Mon, 10 Aug 2026 01:21:51 -0300 Subject: [PATCH] perf(hermes): pipeline conversational voice playback --- dockerfiles/Dockerfile.hermes-jetson-tts | 16 ++++++++++-- dockerfiles/hermes-webui-atlas-voice.js | 31 ++++++++++++++--------- services/hermes/agent-deployment.yaml | 2 +- services/hermes/chat-statefulset.yaml | 2 +- services/hermes/deployment.yaml | 2 +- services/hermes/voice-deployment.yaml | 8 +++--- testing/tests/test_hermes_chat_quality.py | 7 +++-- 7 files changed, 45 insertions(+), 23 deletions(-) diff --git a/dockerfiles/Dockerfile.hermes-jetson-tts b/dockerfiles/Dockerfile.hermes-jetson-tts index cf313ea25..deba40801 100644 --- a/dockerfiles/Dockerfile.hermes-jetson-tts +++ b/dockerfiles/Dockerfile.hermes-jetson-tts @@ -12,6 +12,18 @@ ADD --checksum=sha256:4cabf7c3a638017137f34a1516522032d4fe3f38228a843cc9b764ddcb ADD --checksum=sha256:db42b97d9859f257bc1561b8ed980e7fb2398402050a74ddd6cbec931a92412f --chmod=0444 \ https://huggingface.co/rhasspy/piper-voices/resolve/ea046e8458f6acd997706d6e6066a022b42f6fb1/en/en_US/lessac/high/en_US-lessac-high.onnx.json?download=true \ /opt/models/piper/en_US-lessac-high.onnx.json +ADD --checksum=sha256:5efe09e69902187827af646e1a6e9d269dee769f9877d17b16b1b46eeaaf019f --chmod=0444 \ + https://huggingface.co/rhasspy/piper-voices/resolve/ea046e8458f6acd997706d6e6066a022b42f6fb1/en/en_US/lessac/medium/en_US-lessac-medium.onnx?download=true \ + /opt/models/piper/en_US-lessac-medium.onnx +ADD --checksum=sha256:efe19c417bed055f2d69908248c6ba650fa135bc868b0e6abb3da181dab690a0 --chmod=0444 \ + https://huggingface.co/rhasspy/piper-voices/resolve/ea046e8458f6acd997706d6e6066a022b42f6fb1/en/en_US/lessac/medium/en_US-lessac-medium.onnx.json?download=true \ + /opt/models/piper/en_US-lessac-medium.onnx.json +ADD --checksum=sha256:f7d01dde371555732c4c314111ac79672b1a5ce2fc19266ab42178fd8df7f375 --chmod=0444 \ + https://huggingface.co/rhasspy/piper-voices/resolve/ea046e8458f6acd997706d6e6066a022b42f6fb1/en/en_US/lessac/low/en_US-lessac-low.onnx?download=true \ + /opt/models/piper/en_US-lessac-low.onnx +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 RUN chmod 0555 /opt/models /opt/models/piper COPY dockerfiles/hermes-jetson-tts-server.py /opt/atlas/hermes-jetson-tts-server.py @@ -19,11 +31,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'); assert stat.S_IMODE(p.stat().st_mode)==0o555; assert stat.S_IMODE((p/'en_US-lessac-high.onnx').stat().st_mode)==0o444; v=PiperVoice.load(p/'en_US-lessac-high.onnx', p/'en_US-lessac-high.onnx.json', use_cuda=False, download_dir=p); assert v.config.sample_rate > 0" +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)" ENV HERMES_TTS_HOST=0.0.0.0 \ HERMES_TTS_PORT=9001 \ - HERMES_TTS_VOICE=en_US-lessac-high \ + HERMES_TTS_VOICE=en_US-lessac-medium \ HERMES_TTS_CACHE=/opt/models/piper \ OMP_NUM_THREADS=2 \ PYTHONDONTWRITEBYTECODE=1 \ diff --git a/dockerfiles/hermes-webui-atlas-voice.js b/dockerfiles/hermes-webui-atlas-voice.js index a59007a27..0519ba313 100644 --- a/dockerfiles/hermes-webui-atlas-voice.js +++ b/dockerfiles/hermes-webui-atlas-voice.js @@ -186,6 +186,19 @@ }); } + async function fetchSpeech(chunk){ + const response=await fetch('/api/tts',{ + method:'POST', + headers:{'Content-Type':'application/json'}, + body:JSON.stringify({text:chunk,engine:'atlas'}), + }); + if(!response.ok){ + const payload=await response.json().catch(function(){return {};}); + throw new Error(payload.error||('Local speech request failed: '+response.status)); + } + return response.blob(); + } + async function speakResponse(token){ if(!active||token!==generation) return; const currentSession=(typeof S!=='undefined'&&S.session)?S.session.session_id:null; @@ -200,20 +213,14 @@ const text=cleanForSpeech(rows[rows.length-1].dataset.rawText||''); if(!text){restartSoon(token,250);return;} setState('speaking'); - const chunks=typeof window._splitForTTS==='function'?window._splitForTTS(text,900):[text]; + const chunks=typeof window._splitForTTS==='function'?window._splitForTTS(text,280):[text]; try{ - for(const chunk of chunks){ + let pending=fetchSpeech(chunks[0]); + for(let index=0;index