WIP: Prepare fixed multilingual Piper voices without deployment activation (supersedes #26) #39

Closed
hermes-automation wants to merge 1 commits from hermes/tts-voice-multilingual-prerequisite into main

Multilingual Piper prerequisite (supersedes PR #26; deployment activation intentionally split)

This draft supersedes unmerged PR #26. PR #26 cannot be repaired in place and
Brad will close it unmerged. This successor is one clean commit on current
main base e3de466ad1a267c01aaf2ea58f49743861f458e2.

Scope

This PR prepares the next hermes-tts image for server-side multilingual
Piper routing without activating that image in the live deployment:

  • bakes checksum-pinned amy, irina, and claude ONNX/JSON assets alongside the
    existing lessac assets and loads all pinned models during the image build;
  • routes a strict server-side language allow-list to English amy, Russian
    irina, or Spanish claude, with missing/unknown/malformed values falling back
    to amy;
  • gives users no TTS speaker/model choice: the deterministic pinned-WebUI
    patch removes the actual settings selector, label/description translations,
    browser/server preference persistence, and legacy browser preference;
  • omits client voice from normal, auto-read, extension, and hands-free TTS
    requests while retaining the server-side defense that ignores any hostile or
    legacy voice field;
  • preserves dictation, the TTS engine and rate/pitch controls, hands-free Voice
    Mode, and the conversation instrument;
  • eagerly preloads the three policy voices and fails closed when a required
    baked asset is missing;
  • documents the still-separate WebUI language-signal work and adds focused
    language-routing plus final-patched-artifact coverage.

Users choose no voice. The Piper server maps only validated language to fixed
local voices: en -> amy, ru -> irina, es -> claude, with amy as the
fallback.

services/hermes/voice-deployment.yaml has zero delta from main in this
PR. It therefore retains all four currently deployed values together:

  • image: registry.bstein.dev/bstein/hermes-jetson-tts@sha256:5cb9e57faab46365bff606c559af57b9505892be2909aea1ac523568d478b2cc
  • HERMES_TTS_VOICE=en_US-lessac-medium
  • ai.bstein.dev/model=piper-en-us-lessac-medium
  • memory request: 512Mi

That is deliberate. Flux tracks main, so merging this prerequisite alone is
a no-op for the hermes-tts pod template. The old digest continues running the
old lessac-compatible server. The new server's rejection of lessac remains
latent in source until an image built from merged main is activated.

The image/server DEFAULT_VOICE_NAME remains en_US-amy-medium. Amy is the
fixed English fallback and a member of the new server's three-name baked
allow-list; lessac is intentionally not in that allow-list. Changing the new
server default back to lessac would make the newly built image internally
invalid, while it would not improve live safety because this PR does not change
the live digest or environment.

Required post-merge activation sequence

  1. Merge this prerequisite PR.
  2. Build Dockerfile.hermes-jetson-tts from the resulting merged main commit
    and record the immutable image digest.
  3. Land one atomic follow-up PR that changes the image digest,
    HERMES_TTS_VOICE to en_US-amy-medium, the multilingual model annotation,
    and the memory request to 768Mi together.
  4. After review and merge, let Flux reconcile that single atomic pod-template
    change and verify startup/health.

Do not deploy the new image first under the old lessac environment, and do not
land the amy environment before the new digest. Either partial ordering is
incompatible; the four activation values must move together.

The activation follow-up is intentionally not opened here. It depends on Brad
merging this PR first and on the image being built from merged main.

Verification

  • Focused routing and chat-quality tests: 54 passed.
  • Final-patched-WebUI/voice-instrument tests: 5 passed.
  • Full candidate suite at 7fb557710e89405372a36e5b38a7f00d72f1b080:
    2663 passed, 4 failed, 4 skipped.
  • Fresh pristine origin/main (e3de466ad1a267c01aaf2ea58f49743861f458e2)
    baseline: 2620 passed, 4 failed, 4 skipped.
  • The exact same four unrelated failures occurred on candidate and baseline;
    the candidate adds 43 passing tests and no new pytest failure.
  • Full candidate and baseline lint both report the same three pre-existing
    findings (two UP041 in scm_broker.py, one B905 in
    test_hermes_image_builder_fresh_review.py); the candidate-touched Ruff
    subset passes.
  • The production patch applied successfully to the complete pinned Hermes
    WebUI revision 7a94e34a6d639576576baa9131acf6765f6d2b98; the final patched DOM/static
    sources contain no voice selector/persisted preference/request field, while
    hands-free Voice Mode and the conversation instrument remain.
  • kustomize build services/hermes, Python compilation, and git diff --check
    pass.
  • The rendered hermes-tts pod template is byte-identical to pristine current
    main, with matching SHA-256
    9571aa126f71a2f5e5303629aef57b57abb2bf8de9903b6a2929729bb781654b.

The four pre-existing failures are:

  • testing/tests/test_hermes_agent_layout.py::test_cli_lane_reserves_cpu_headroom_for_ui_and_auth
  • testing/tests/test_hermes_agent_layout.py::test_agent_avoids_unhealthy_nodes_and_fits_its_remaining_capacity
  • testing/tests/test_hermes_gitea_pr_integration.py::test_http_error_path_redacts_token
  • testing/tests/test_hermes_kanban_supervisor.py::test_auto_supervise_flag_defaults_false_in_configmap

Files changed

  • dockerfiles/Dockerfile.hermes-jetson-tts
  • dockerfiles/hermes-jetson-tts-server.py
  • dockerfiles/hermes-webui-atlas-patch.py
  • services/hermes/NOTES.md
  • testing/fixtures/hermes-webui-0.52.181/api/config.py
  • testing/fixtures/hermes-webui-0.52.181/static/boot.js
  • testing/fixtures/hermes-webui-0.52.181/static/i18n.js
  • testing/fixtures/hermes-webui-0.52.181/static/index.html
  • testing/fixtures/hermes-webui-0.52.181/static/panels.js
  • testing/fixtures/hermes-webui-0.52.181/static/ui.js
  • testing/tests/test_hermes_chat_quality.py
  • testing/tests/test_hermes_tts_language_routing.py
  • testing/tests/test_hermes_voice_instrument.py
# Multilingual Piper prerequisite (supersedes PR #26; deployment activation intentionally split) This draft supersedes unmerged PR #26. PR #26 cannot be repaired in place and Brad will close it unmerged. This successor is one clean commit on current `main` base `e3de466ad1a267c01aaf2ea58f49743861f458e2`. ## Scope This PR prepares the next `hermes-tts` image for server-side multilingual Piper routing without activating that image in the live deployment: - bakes checksum-pinned amy, irina, and claude ONNX/JSON assets alongside the existing lessac assets and loads all pinned models during the image build; - routes a strict server-side `language` allow-list to English amy, Russian irina, or Spanish claude, with missing/unknown/malformed values falling back to amy; - gives users no TTS speaker/model choice: the deterministic pinned-WebUI patch removes the actual settings selector, label/description translations, browser/server preference persistence, and legacy browser preference; - omits client `voice` from normal, auto-read, extension, and hands-free TTS requests while retaining the server-side defense that ignores any hostile or legacy `voice` field; - preserves dictation, the TTS engine and rate/pitch controls, hands-free Voice Mode, and the conversation instrument; - eagerly preloads the three policy voices and fails closed when a required baked asset is missing; - documents the still-separate WebUI language-signal work and adds focused language-routing plus final-patched-artifact coverage. Users choose no voice. The Piper server maps only validated language to fixed local voices: `en` -> amy, `ru` -> irina, `es` -> claude, with amy as the fallback. `services/hermes/voice-deployment.yaml` has **zero delta from `main`** in this PR. It therefore retains all four currently deployed values together: - image: `registry.bstein.dev/bstein/hermes-jetson-tts@sha256:5cb9e57faab46365bff606c559af57b9505892be2909aea1ac523568d478b2cc` - `HERMES_TTS_VOICE=en_US-lessac-medium` - `ai.bstein.dev/model=piper-en-us-lessac-medium` - memory request: `512Mi` That is deliberate. Flux tracks `main`, so merging this prerequisite alone is a no-op for the `hermes-tts` pod template. The old digest continues running the old lessac-compatible server. The new server's rejection of lessac remains latent in source until an image built from merged `main` is activated. The image/server `DEFAULT_VOICE_NAME` remains `en_US-amy-medium`. Amy is the fixed English fallback and a member of the new server's three-name baked allow-list; lessac is intentionally not in that allow-list. Changing the new server default back to lessac would make the newly built image internally invalid, while it would not improve live safety because this PR does not change the live digest or environment. ## Required post-merge activation sequence 1. Merge this prerequisite PR. 2. Build `Dockerfile.hermes-jetson-tts` from the resulting merged `main` commit and record the immutable image digest. 3. Land **one atomic follow-up PR** that changes the image digest, `HERMES_TTS_VOICE` to `en_US-amy-medium`, the multilingual model annotation, and the memory request to `768Mi` together. 4. After review and merge, let Flux reconcile that single atomic pod-template change and verify startup/health. Do not deploy the new image first under the old lessac environment, and do not land the amy environment before the new digest. Either partial ordering is incompatible; the four activation values must move together. The activation follow-up is intentionally not opened here. It depends on Brad merging this PR first and on the image being built from merged `main`. ## Verification - Focused routing and chat-quality tests: `54 passed`. - Final-patched-WebUI/voice-instrument tests: `5 passed`. - Full candidate suite at `7fb557710e89405372a36e5b38a7f00d72f1b080`: `2663 passed, 4 failed, 4 skipped`. - Fresh pristine `origin/main` (`e3de466ad1a267c01aaf2ea58f49743861f458e2`) baseline: `2620 passed, 4 failed, 4 skipped`. - The exact same four unrelated failures occurred on candidate and baseline; the candidate adds 43 passing tests and no new pytest failure. - Full candidate and baseline lint both report the same three pre-existing findings (two UP041 in `scm_broker.py`, one B905 in `test_hermes_image_builder_fresh_review.py`); the candidate-touched Ruff subset passes. - The production patch applied successfully to the complete pinned Hermes WebUI revision `7a94e34a6d639576576baa9131acf6765f6d2b98`; the final patched DOM/static sources contain no voice selector/persisted preference/request field, while hands-free Voice Mode and the conversation instrument remain. - `kustomize build services/hermes`, Python compilation, and `git diff --check` pass. - The rendered `hermes-tts` pod template is byte-identical to pristine current main, with matching SHA-256 `9571aa126f71a2f5e5303629aef57b57abb2bf8de9903b6a2929729bb781654b`. The four pre-existing failures are: - `testing/tests/test_hermes_agent_layout.py::test_cli_lane_reserves_cpu_headroom_for_ui_and_auth` - `testing/tests/test_hermes_agent_layout.py::test_agent_avoids_unhealthy_nodes_and_fits_its_remaining_capacity` - `testing/tests/test_hermes_gitea_pr_integration.py::test_http_error_path_redacts_token` - `testing/tests/test_hermes_kanban_supervisor.py::test_auto_supervise_flag_defaults_false_in_configmap` ## Files changed - `dockerfiles/Dockerfile.hermes-jetson-tts` - `dockerfiles/hermes-jetson-tts-server.py` - `dockerfiles/hermes-webui-atlas-patch.py` - `services/hermes/NOTES.md` - `testing/fixtures/hermes-webui-0.52.181/api/config.py` - `testing/fixtures/hermes-webui-0.52.181/static/boot.js` - `testing/fixtures/hermes-webui-0.52.181/static/i18n.js` - `testing/fixtures/hermes-webui-0.52.181/static/index.html` - `testing/fixtures/hermes-webui-0.52.181/static/panels.js` - `testing/fixtures/hermes-webui-0.52.181/static/ui.js` - `testing/tests/test_hermes_chat_quality.py` - `testing/tests/test_hermes_tts_language_routing.py` - `testing/tests/test_hermes_voice_instrument.py`
hermes-automation added 1 commit 2026-08-21 09:04:28 +00:00
Supersede draft PR #26 with a merge-safe prerequisite: bake and preload the amy, irina, and claude Piper models, route only validated server-side language to fixed voices, and leave the live voice deployment manifest unchanged.

Remove the pinned WebUI speaker selector and its persisted preference, omit client voice fields from every outbound TTS path, and keep hands-free Voice Mode and the conversation instrument intact. Hostile or legacy voice fields remain ignored by the Piper server.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
bstein closed this pull request 2026-08-22 20:21:44 +00:00

Pull request closed

Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: titan/atlas-iac#39
No description provided.