#!/usr/bin/env bash # ============================================================================ # Install a dedicated Coqui XTTS-v2 (natural Korean voice) venv at /opt/xtts. # # Why a SEPARATE venv (not the brain-bridge /opt/venv or /opt/melo): # - Coqui TTS pulls its own heavy torch/transformers stack; isolating it keeps # the slim bridge env (numpy<2 for faster-whisper) untouched. # - We use python3.11 (installed for the melo layer) because Coqui ships cp311 # wheels and torch cu128 is available for it. # # torch is the CUDA (cu128) build so XTTS runs on the GPU alongside Ollama + # Whisper. cu128 is the Blackwell (sm_120) wheel verified on this host. # The worker selects the device via XTTS_DEVICE=cuda (compose). # # XTTS-v2 is non-commercial (Coqui Public Model License). COQUI_TOS_AGREED=1 # accepts it non-interactively so the model can load in a headless container. # ============================================================================ set -euxo pipefail export DEBIAN_FRONTEND=noninteractive export COQUI_TOS_AGREED=1 # Install python3.11 if not already present, so this layer is self-contained. if ! command -v python3.11 >/dev/null 2>&1; then apt-get update apt-get install -y --no-install-recommends software-properties-common add-apt-repository -y ppa:deadsnakes/ppa apt-get update apt-get install -y --no-install-recommends python3.11 python3.11-venv python3.11-dev rm -rf /var/lib/apt/lists/* fi python3.11 -m venv /opt/xtts /opt/xtts/bin/pip install --no-cache-dir --upgrade pip wheel setuptools # CUDA (cu128) torch first so Coqui's `torch` dep is satisfied with the GPU # build. Pinned to the Blackwell-verified versions for reproducible rebuilds. /opt/xtts/bin/pip install --no-cache-dir torch==2.11.0+cu128 torchaudio==2.11.0+cu128 \ --index-url https://download.pytorch.org/whl/cu128 # Coqui TTS (maintained fork; provides the `TTS` package and XTTS-v2). The # [codec] extra pulls torchcodec, which torch >=2.9 requires for audio IO # (without it the import fails with TORCHCODEC_IMPORT_ERROR). torchcodec also # needs the system FFmpeg shared libs, which are present (ffmpeg apt package). /opt/xtts/bin/pip install --no-cache-dir "coqui-tts[codec]" # Pin transformers to the 4.57+ / <5 range. coqui-tts requires >=4.57 but does # NOT cap the upper bound, and transformers 5.x removed `isin_mps_friendly` # (used by XTTS's tortoise layer), so an unpinned install pulls 5.x and the # model import fails with "cannot import name 'isin_mps_friendly'". Pin <5. /opt/xtts/bin/pip install --no-cache-dir "transformers>=4.57,<5" # Pre-bake the XTTS-v2 model so the worker starts offline and the first Discord # turn pays no download cost. The model is cached under TTS_HOME; we pin a # DEDICATED, non-volume dir (/opt/xtts-cache) AND set it in supervisord, because # runtime volume mounts (whisper_cache over /root/.cache) must not shadow it. export TTS_HOME=/opt/xtts-cache mkdir -p "$TTS_HOME" COQUI_TOS_AGREED=1 TTS_HOME=/opt/xtts-cache XTTS_SPEAKER="Ana Florence" \ /opt/xtts/bin/python - <<'PY' import os os.environ["COQUI_TOS_AGREED"] = "1" from TTS.api import TTS speaker = os.environ.get("XTTS_SPEAKER", "Ana Florence") model = TTS("tts_models/multilingual/multi-dataset/xtts_v2") # downloads to TTS_HOME out = "/tmp/xtts_warm.wav" model.tts_to_file(text="초기화 워밍업입니다.", speaker=speaker, language="ko", file_path=out) print("[setup-xtts] warm-up KR synth OK ->", out, "speaker:", speaker) PY echo "[setup-xtts] Coqui XTTS-v2 venv ready at /opt/xtts (cache /opt/xtts-cache)"