Files
agents-marketplace/deploy/simulation/install-voice-extras.sh
jelisaveta-talbot 3f3bc72680 deploy: add apex-simulation host stack (Traefik + PiperTTS + voice extras)
Stand up a second deploy target on apex-simulation dedicated to
"simulation"-style supplier capabilities, starting with CPU-only PiperTTS
served via openedai-speech-min behind Traefik on :80.

- deploy/simulation/docker-compose.traefik.yml: Traefik latest with
  introspection API, settles on Docker daemon 29.x API negotiation
- deploy/simulation/docker-compose.tts-piper.yml: openedai-speech-min
  with curl-based healthcheck (image lacks wget, which Docker would have
  marked unhealthy and Traefik would filter from the docker provider)
- deploy/simulation/deploy.sh: rsync-based deploy mirroring the
  apex-dashboard pattern; selective --traefik-only / --tts-only flags
- deploy/simulation/install-voice-extras.sh + voice-mappings.py:
  idempotent post-deploy step that downloads en_US-lessac-high voice
  files into the bind-mount and registers a "lessac" alias in
  voice_to_speaker.yaml; survives a host wipe without manual steps
- deploy/simulation/README.md: full host runbook
- .gitignore: exclude data/ recursively, *.log, .vitest-cache, .DS_Store,
  __pycache__/, *.pyc, docs/*.zip
2026-05-06 18:59:20 +02:00

73 lines
2.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# install-voice-extras.sh — runs ON THE HOST after `docker compose up`
# for marketplace-tts-piper to install Piper voices and yaml mappings
# beyond what the openedai-speech-min image ships with.
#
# Idempotent: every step is a no-op if it already finished previously.
# Safe to re-run after a host wipe — `deploy.sh` invokes it at the end
# of every tts deploy so a fresh `data/` dir gets the same voices back.
#
# What it does (in order):
# 1. Ensure the bind-mount voice files are present (curl -sSL with `[ -s ]` guard).
# Voice files are large (~100 MB) and gitignored under `data/`, so they
# have to be (re)downloaded onto each fresh host.
# 2. Copy voice-mappings.py into the container and execute it as root.
# The script edits /app/config/voice_to_speaker.yaml (which is a host
# bind-mount) to add custom voice aliases. It prints "modified" or
# "unchanged" — we restart only on the former.
# 3. Restart the container if either step (1) or (2) changed something,
# so Piper picks up the new files / yaml.
#
# Usage:
# bash deploy/simulation/install-voice-extras.sh
set -euo pipefail
SIM_DIR="$(cd "$(dirname "$0")" && pwd)"
VOICES_DIR="$SIM_DIR/data/tts-piper/voices"
CONTAINER="marketplace-tts-piper"
mkdir -p "$VOICES_DIR"
changes=0
# --- 1. Voice files to install (filename → URL) ---
# Add entries here when you want another voice baked into the deploy.
# Each .onnx pairs with a .onnx.json — both must be downloaded.
voices=(
"en_US-lessac-high.onnx|https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/high/en_US-lessac-high.onnx"
"en_US-lessac-high.onnx.json|https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/high/en_US-lessac-high.onnx.json"
)
for entry in "${voices[@]}"; do
fn="${entry%%|*}"
url="${entry##*|}"
if [ -s "$VOICES_DIR/$fn" ]; then
echo "voice: already installed — $fn"
continue
fi
echo "voice: downloading $fn"
# `--fail` so HTTP 4xx/5xx aborts (HF returns html on 404 by default).
# Download to a tmp file then mv, so a partial download from a network
# blip doesn't leave a half-file that the next run treats as installed.
tmp="$VOICES_DIR/.${fn}.partial"
curl -sSL --fail --output "$tmp" "$url"
mv "$tmp" "$VOICES_DIR/$fn"
changes=1
done
# --- 2. Yaml mapping additions inside the container ---
docker cp "$SIM_DIR/voice-mappings.py" "$CONTAINER:/tmp/voice-mappings.py" >/dev/null
mapping_output="$(docker exec "$CONTAINER" python3 /tmp/voice-mappings.py)"
echo "mapping: $mapping_output"
if [[ "$mapping_output" == modified* ]]; then
changes=1
fi
# --- 3. Restart only if anything changed ---
if [ "$changes" -eq 1 ]; then
echo "→ restarting $CONTAINER to pick up new voices/mappings"
docker restart "$CONTAINER" >/dev/null
else
echo "✓ voice extras already up to date — no restart needed"
fi