#!/bin/bash # AI Profile Router – Installation/Update auf dem Zielsystem. # Wird als root auf dem Zielsystem ausgeführt (per SSH, vgl. deploy.sh). # Erwartet ai_profile_router.py, image_worker.py, xtts_worker.py, # mike-ai-profile-router.service und mike-ai-xtts.service im selben # Verzeichnis wie dieses Skript. set -euo pipefail DIR="$(cd "$(dirname "$0")" && pwd)" INSTALL_DIR=/opt/mike-ai/ai-profile-router SERVICE=mike-ai-profile-router.service XTTS_SERVICE=mike-ai-xtts.service WHISPER_SERVICE=mike-ai-whisper.service OLD_SERVICE=mike-ai-local-llm-router.service OLD_DIR=/opt/mike-ai/local-llm-router BACKUP_DIR=/opt/mike-ai/.backup-ai-profile-router-$(date +%Y%m%d-%H%M%S) VENV="$INSTALL_DIR/venv" MODEL_DIR=/opt/mike-ai/models/FLUX.2-klein-4B IMAGE_DIR="$INSTALL_DIR/images" XTTS_VENV=/opt/mike-ai/xtts/venv XTTS_CACHE=/opt/mike-ai/xtts/.cache echo "== AI Profile Router: Installation/Update ==" # --- 1. Bestehenden (alten) Router entfernen, mit Backup ------------------- if [ -f "/etc/systemd/system/${OLD_SERVICE}" ]; then echo "-- Entferne alten Router ${OLD_SERVICE} (Backup: ${BACKUP_DIR})" mkdir -p "$BACKUP_DIR" systemctl stop "$OLD_SERVICE" 2>/dev/null || true systemctl disable "$OLD_SERVICE" 2>/dev/null || true mv "/etc/systemd/system/${OLD_SERVICE}" "$BACKUP_DIR/" if [ -d "$OLD_DIR" ]; then mv "$OLD_DIR" "$BACKUP_DIR/local-llm-router" fi else echo "-- Kein alter Router gefunden" fi # --- 2. Neue Dateien installieren ------------------------------------------- mkdir -p "$INSTALL_DIR" "$IMAGE_DIR" install -d -m 0700 /etc/mike-ai /var/lib/mike-ai-profile-router install -m 0755 "$DIR/ai_profile_router.py" "$INSTALL_DIR/ai_profile_router.py" install -m 0644 "$DIR/router_support.py" "$INSTALL_DIR/router_support.py" install -m 0644 "$DIR/router_profiles.json" /etc/mike-ai/router-profiles.json install -m 0755 "$DIR/image_worker.py" "$INSTALL_DIR/image_worker.py" install -m 0755 "$DIR/xtts_worker.py" "$INSTALL_DIR/xtts_worker.py" install -m 0755 "$DIR/stt_worker.py" "$INSTALL_DIR/stt_worker.py" install -m 0644 "$DIR/${SERVICE}" "/etc/systemd/system/${SERVICE}" install -m 0644 "$DIR/${XTTS_SERVICE}" "/etc/systemd/system/${XTTS_SERVICE}" install -m 0644 "$DIR/${WHISPER_SERVICE}" "/etc/systemd/system/${WHISPER_SERVICE}" # Eigener Router-Key. Er wird niemals in Git oder ins Installationslog geschrieben. if [ ! -s /etc/mike-ai/router-api-key ]; then umask 077 python3 -c 'import secrets; print(secrets.token_urlsafe(48))' \ > /etc/mike-ai/router-api-key fi chmod 0600 /etc/mike-ai/router-api-key # --- 3. Python-Venv mit Bild-Abhängigkeiten --------------------------------- if [ ! -x "$VENV/bin/python" ]; then echo "-- Erstelle Python-Venv in $VENV" python3 -m venv "$VENV" fi echo "-- Installiere festgeschriebene Bild-Abhängigkeiten" "$VENV/bin/pip" install --quiet --upgrade pip "$VENV/bin/pip" install --quiet torch==2.11.0+cu128 \ --index-url https://download.pytorch.org/whl/cu128 "$VENV/bin/pip" install --quiet -r "$DIR/requirements-image.lock" # --- 4. FLUX-Modell (nur wenn noch nicht vorhanden) -------------------------- if [ -f "$MODEL_DIR/model_index.json" ]; then echo "-- FLUX-Modell vorhanden: $MODEL_DIR" else echo "-- Lade FLUX.2-klein-4B nach $MODEL_DIR (kann dauern)" "$VENV/bin/python" - <<'PY' import os from huggingface_hub import snapshot_download snapshot_download( repo_id="black-forest-labs/FLUX.2-klein-4B", revision="e7b7dc27f91deacad38e78976d1f2b499d76a294", local_dir="/opt/mike-ai/models/FLUX.2-klein-4B", local_dir_use_symlinks=False, ) print("Modell-Download abgeschlossen") PY fi # --- 5. TTS (XTTS-v2) ---------------------------------------------------------- # XTTS-Venv (Python 3.11 via uv + CPU-only torch + Coqui TTS) if [ ! -x "$XTTS_VENV/bin/python" ]; then echo "-- Erstelle XTTS-Venv in $XTTS_VENV (Python 3.11 via uv)" mkdir -p "$XTTS_VENV" # uv installieren (falls noch nicht vorhanden) if ! command -v uv >/dev/null 2>&1; then curl -LsSf https://astral.sh/uv/install.sh | sh export PATH="$HOME/.local/bin:$PATH" fi uv venv --python 3.11 "$XTTS_VENV" "$XTTS_VENV/bin/pip" install --quiet --upgrade pip echo "-- Installiere CPU-only torch" "$XTTS_VENV/bin/pip" install --quiet torch==2.11.0+cpu torchaudio==2.11.0+cpu \ --index-url https://download.pytorch.org/whl/cpu echo "-- Installiere Coqui TTS + Abhängigkeiten" "$XTTS_VENV/bin/pip" install --quiet -r "$DIR/requirements-xtts.lock" # PyTorch 2.6+ weights_only-Patch für Coqui TTS echo "-- Wende weights_only-Patch für Coqui TTS an" "$XTTS_VENV/bin/python" - <<'PY' import os patch_file = os.path.expanduser("/opt/mike-ai/xtts/venv/lib/python3.11/site-packages/TTS/utils/io.py") with open(patch_file, 'r') as f: content = f.read() if 'weights_only=False' not in content: content = content.replace( 'return torch.load(f, map_location=map_location, **kwargs)', 'return torch.load(f, map_location=map_location, weights_only=False, **kwargs)' ) with open(patch_file, 'w') as f: f.write(content) print("Patch angewendet") else: print("Patch bereits vorhanden") PY fi # XTTS-v2-Modell (nur wenn noch nicht vorhanden) if [ ! -d "$XTTS_CACHE/hub/models--coqui--tts_models--multilingual--multi-dataset--xtts_v2" ]; then echo "-- Lade XTTS-v2-Modell (kann dauern, ~1.9 GB)" "$XTTS_VENV/bin/python" - <<'PY' import os os.environ["HF_HOME"] = "/opt/mike-ai/xtts/.cache" from TTS.api import TTS tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2") print("Modell geladen") PY fi # --- 6. Services aktivieren und starten --------------------------------------- systemctl daemon-reload systemctl enable "$SERVICE" "$XTTS_SERVICE" "$WHISPER_SERVICE" systemctl restart "$XTTS_SERVICE" systemctl restart "$WHISPER_SERVICE" systemctl restart "$SERVICE" # --- 7. Verifikation ---------------------------------------------------------- sleep 1 if ! systemctl is-active --quiet "$SERVICE"; then echo "-- FEHLER: Router-Service läuft nicht" >&2 journalctl -u "$SERVICE" -n 20 --no-pager >&2 exit 1 fi echo "-- Router-Service läuft" if ! systemctl is-active --quiet "$XTTS_SERVICE"; then echo "-- FEHLER: XTTS-Service läuft nicht" >&2 journalctl -u "$XTTS_SERVICE" -n 20 --no-pager >&2 exit 1 fi echo "-- XTTS-Service läuft" if ! systemctl is-active --quiet "$WHISPER_SERVICE"; then echo "-- FEHLER: Whisper-Service läuft nicht" >&2 journalctl -u "$WHISPER_SERVICE" -n 20 --no-pager >&2 exit 1 fi echo "-- Whisper-Service läuft" python3 - <<'PY' import json import pathlib import urllib.request key = pathlib.Path("/etc/mike-ai/router-api-key").read_text().strip() request = urllib.request.Request( "http://127.0.0.1:8081/status", headers={"Authorization": f"Bearer {key}"}) with urllib.request.urlopen(request, timeout=10) as response: print(json.dumps(json.load(response), indent=2)) PY echo "-- Router-API-Key liegt ausschließlich in /etc/mike-ai/router-api-key" echo "== Fertig =="