fix: move TTS patch from build-time to runtime

The build-time COPY+RUN of patch_tts_tool.py failed because
the Dockerfile starts from debian:stable-slim and only copies
the ai/ build context — there's no tools/tts_tool.py in the
image at build time (Hermes is on the mounted data volume).

Move patching to fix-permissions.sh which runs at container
startup when the data volume is mounted, so tts_tool.py is
available via the venv site-packages.

Also make patch_tts_tool.py robust: searches multiple paths
for tts_tool.py, accepts path as argument, exits 0 instead
of 1 when file/pattern not found (build must not fail).
This commit is contained in:
Thierry Pouplier
2026-05-09 17:36:26 +00:00
parent 0609720b33
commit cfa2a898c3
3 changed files with 67 additions and 25 deletions

View File

@@ -1,11 +1,58 @@
#!/usr/bin/env python3
"""Patch Hermes TTS tool: remove Edge TTS, replace with Piper as default/fallback."""
"""Patch Hermes TTS tool: remove Edge TTS, replace with Piper as default/fallback.
Searches multiple paths for tts_tool.py so it works both at build time
(in the image venv) and at runtime (on the mounted data volume).
"""
import sys
import os
tts_path = '/opt/hermes/tools/tts_tool.py'
# Search order: argument > site-packages > /opt/hermes/tools > /opt/hermes checkout
SEARCH_PATHS = []
with open(tts_path) as f:
code = f.read()
# Accept path as first argument
if len(sys.argv) > 1:
SEARCH_PATHS.append(sys.argv[1])
# Add known locations
SEARCH_PATHS.extend([
"/opt/hermes/.venv/lib/python3.13/site-packages/tools/tts_tool.py",
"/opt/hermes/tools/tts_tool.py",
])
tts_path = None
code = None
for p in SEARCH_PATHS:
if os.path.exists(p):
tts_path = p
with open(tts_path) as f:
code = f.read()
print(f"Found tts_tool.py at: {tts_path}")
break
if code is None:
# Try one more time: find it in the venv site-packages
import subprocess
try:
result = subprocess.run(
[sys.executable, "-c", "import tools.tts_tool; print(tools.tts_tool.__file__)"],
capture_output=True, text=True, timeout=5
)
if result.returncode == 0:
p = result.stdout.strip()
if os.path.exists(p):
tts_path = p
with open(tts_path) as f:
code = f.read()
print(f"Found tts_tool.py via import at: {tts_path}")
except Exception:
pass
if code is None:
print("WARNING: tts_tool.py not found. Patching deferred to runtime.")
print(f"Searched: {SEARCH_PATHS}")
sys.exit(0)
# Replace the Edge fallback with Piper fallback
old_edge = ''' else:
@@ -77,20 +124,13 @@ new_piper = ''' else:
if old_edge in code:
code = code.replace(old_edge, new_piper)
print("Edge fallback replaced with Piper")
elif 'Default: Piper TTS' in code:
print("Piper fallback already present")
else:
if 'Default: Piper TTS' in code:
print("Piper fallback already present")
else:
print("ERROR: Could not find Edge fallback in tts_tool.py")
# Debug output
import re
for m in re.finditer(r' else:\n # Default:', code):
start = max(0, m.start() - 100)
end = min(len(code), m.end() + 200)
print(f"Found else/default at position {m.start()}:")
print(code[start:end])
sys.exit(1)
print("WARNING: Could not find Edge fallback in tts_tool.py")
print("The tts_tool.py may be a version not matching this patch.")
sys.exit(0)
with open(tts_path, 'w') as f:
f.write(code)
print("tts_tool.py patched successfully")
print(f"tts_tool.py patched successfully at: {tts_path}")