fix(docker): download piper voices via Python urllib (curl not in slim)

python:3.14-slim doesn't ship curl or wget. The previous voice-download
step assumed it did and failed with "curl: not found" (exit 127) in
build stage 8.

Replaced with a Docker BuildKit heredoc that runs python3 directly,
using urllib.request.urlretrieve. Python is already installed (it's
the base image), so this needs no additional apt packages and keeps
the image footprint identical. The `# syntax=docker/dockerfile:1`
directive at the top of this file already pulls in a BuildKit
frontend that supports heredoc syntax.

The download itself is unchanged: en_US-amy-medium and en_US-ryan-medium
into /opt/piper-voices, with both .onnx and .onnx.json sidecar files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-22 08:06:28 -04:00
parent a28f75994a
commit 4a9d8eaa2d
+22 -7
View File
@@ -36,13 +36,28 @@ RUN --mount=type=cache,target=/root/.cache/pip \
# Additional voices can be downloaded at runtime into /data/voices via the
# admin UI (see services/tts.py for the voice-discovery logic).
# Voice catalog: https://huggingface.co/rhasspy/piper-voices
RUN mkdir -p /opt/piper-voices && cd /opt/piper-voices && \
for VOICE in en_US-amy-medium en_US-ryan-medium; do \
DATASET="${VOICE#en_US-}"; DATASET="${DATASET%-medium}"; \
BASE="https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/${DATASET}/medium"; \
curl -fsSL -o "${VOICE}.onnx" "${BASE}/${VOICE}.onnx"; \
curl -fsSL -o "${VOICE}.onnx.json" "${BASE}/${VOICE}.onnx.json"; \
done
#
# Using Python's urllib instead of curl/wget because python:3.14-slim
# ships neither; python is obviously available. The heredoc requires
# the BuildKit Dockerfile 1.3+ frontend, which the `# syntax=...:1`
# directive at the top of this file already pulls in.
RUN <<'PYEOF' python3
import os, urllib.request
VOICES = ["en_US-amy-medium", "en_US-ryan-medium"]
BASE = "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US"
TARGET = "/opt/piper-voices"
os.makedirs(TARGET, exist_ok=True)
for v in VOICES:
dataset = v.split("-")[1]
for ext in ("onnx", "onnx.json"):
url = f"{BASE}/{dataset}/medium/{v}.{ext}"
path = os.path.join(TARGET, f"{v}.{ext}")
print(f"Downloading {url}", flush=True)
urllib.request.urlretrieve(url, path)
size = os.path.getsize(path)
print(f" -> {path} ({size:,} bytes)", flush=True)
PYEOF
# Build the fable-mcp wheel so it can be served for download
COPY fable-mcp/ fable-mcp/