Files
ARIA-AGENT/xtts/docker-compose.yml
T
duffyduckandClaude Opus 5.5 864237ff2c feat(llm): Download-Fortschritt beim Modell-Laden — und "Laden" kommt endlich an
Eigentlicher Fehler zuerst: llm_provision_model/_result und llm_remove_model
standen nie in der RVS-Allow-List. Der RVS verwirft unbekannte Typen mit einem
stillen return — jeder Klick auf "Laden" im Modell-Katalog ist seit Stage D
(19.09.) an dieser Liste gestorben, die Box hat nie davon erfahren. Laden
liessen sich nur die Modelle aus der Basis-Config der Box.

Gefunden, weil grep rvs/server.js fuer eine Binaerdatei hielt und still
uebersprang: in Zeile 164 stand seit a7998bcd ein ECHTES Nullbyte im
Template-String statt eines Escapes. Node stoert das nicht, aber git zeigte
fuer jede Aenderung am RVS nur noch "Binary files differ". Jetzt \u0000 —
zur Laufzeit derselbe String.

Fortschritt: llama.cpp zieht das GGUF per -hf selbst und meldet nichts. Der
llm-adapter fragt vorab bei Hugging Face, welche Dateien (Groesse, sha256)
kommen, und misst genau diese im HF-Cache-Layout, das llama.cpp verwendet:
  /models/models--org--repo/blobs/<sha256>.downloadInProgress  (waechst)
  /models/models--org--repo/blobs/<sha256>                     (fertig)
Layout gegen den echten llama.cpp-Server ausprobiert, nicht angenommen. Geht
auch bei gesplitteten Modellen und zwei Downloads gleichzeitig; Fallback fuer
aeltere Layouts ueber alle *.downloadInProgress. Phasen: queued → start →
download (%, MB/s, Restzeit) → load ("laedt in die Karte") → ready ("in Karte
N geladen", Name + VRAM aus nvidia-smi) bzw. error.

Mitgefixt: der Warmup wartete LLM_TIMEOUT_SEC (Default 60 s). Ein Modell mit
mehreren GB meldete deshalb "Fehler", waehrend llama-swap weiterlud. Jetzt
LLM_WARMUP_TIMEOUT_S (1900 s, ueber llama-swaps healthCheckTimeout).

Diagnostic: Knopf "⬇ Downloads" neben "Von HuggingFace aktualisieren" mit
Zaehler fuer laufende; "Laden" oeffnet das Fenster direkt. Der Server merkt
sich den Stand pro (Box, Modell), ein spaeter geoeffneter Tab sieht laufende
Downloads. Antwortet die Box nicht, steht nach 30 s eine Warnung da statt
nichts.

Compose (KI-Box): llm-adapter bekommt LLM_GPU (Karte fuer die Meldung) und
HF_TOKEN (Dateiliste gesperrter Repos).

Test: Fortschritt gegen einen echten llama.cpp-Download (Qwen2.5-0.5B Q8,
676 MB): Prozent steigend 0 → 91.6, dann load; zweiter Lauf aus dem Cache
direkt load mit cached=true.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:33:54 +02:00

217 lines
11 KiB
YAML

# ════════════════════════════════════════════════
# ARIA Compute-Node — GPU-Dienste (STT / TTS / LLM)
#
# KEINE feste "AI-Box" mehr: dieser Stack laeuft auf beliebig vielen
# Worker-Nodes. Jeder Node startet ueber COMPOSE_PROFILES nur die Dienste,
# die er anbieten soll, und pinnt sie per *_GPU auf bestimmte Grafikkarten.
# Alles verbindet sich ueber RVS mit der ARIA-Infrastruktur — kein VPN.
#
# Beispiele (in der jeweiligen .env):
# STT-Box → COMPOSE_PROFILES=voxtral
# TTS-Box → COMPOSE_PROFILES=f5tts
# LLM-Box → COMPOSE_PROFILES=llm
# All-in-One → COMPOSE_PROFILES=voxtral,f5tts,llm (die alte AI-Box)
#
# FLUX-Bildgenerierung liegt im /flux Verzeichnis — eigener Stack.
# ════════════════════════════════════════════════
#
# Voraussetzungen:
# - Docker + NVIDIA Container Toolkit (registriert die `nvidia`-Runtime)
# - .env mit RVS-Verbindungsdaten, COMPOSE_PROFILES, NODE_NAME, *_GPU
#
# Start: docker compose up -d (liest COMPOSE_PROFILES aus der .env)
# ════════════════════════════════════════════════
services:
# ─── F5-TTS Bridge (GPU) ──────────────────────
# Empfaengt xtts_request via RVS, rendert via F5-TTS mit Voice-Cloning,
# streamt PCM an die App. Voice-Upload: speichert WAV und laesst eine
# STT-Bridge den Referenztext transkribieren — der User tippt nichts.
f5tts-bridge:
build: ./f5tts
container_name: aria-f5tts-bridge
profiles: ["f5tts"] # startet nur mit COMPOSE_PROFILES=…f5tts…
runtime: nvidia
volumes:
- ./voices:/voices # WAV + TXT Referenz
- ./hf-cache:/root/.cache/huggingface # HF-Cache als Bind-Mount.
# Direkt sichtbar im xtts/hf-cache/,
# einfach manuell zu loeschen, kein
# Docker-Desktop .vhdx Bloat.
# Wird mit STT-Bridges geteilt.
environment:
# GPU-Wahl: NVIDIA_VISIBLE_DEVICES (mehrere via "0,1"). Default GPU 0.
- NVIDIA_VISIBLE_DEVICES=${F5TTS_GPU:-0}
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
- NODE_NAME=${NODE_NAME:-node} # erscheint in Diagnostic + Logs
# Bootstrap-only — alle anderen F5-TTS-Settings (Modell, cfg_strength,
# nfe_step, Custom-Checkpoint) kommen ueber Diagnostic via RVS-config.
- RVS_HOST=${RVS_HOST}
- RVS_PORT=${RVS_PORT:-443}
- RVS_TLS=${RVS_TLS:-true}
- RVS_TLS_FALLBACK=${RVS_TLS_FALLBACK:-true}
- RVS_TOKEN=${RVS_TOKEN}
- F5TTS_DEVICE=${F5TTS_DEVICE:-cuda}
- VOICES_DIR=/voices
restart: unless-stopped
# ─── Whisper STT (GPU) — opt-in Fallback-STT ──
# Faster-Whisper auf CUDA. Verbindet sich selbst per WebSocket an den RVS
# und nimmt stt_request / stt_stream_* Nachrichten entgegen. Zusaetzlich
# nutzt die f5tts-bridge Whisper intern fuer die Referenz-Transkription bei
# Voice-Uploads. Modell-Hot-Swap via Diagnostic (config-Broadcast).
# Nur EINEN STT-Provider pro Node laufen lassen (voxtral ODER whisper) —
# sonst beantworten beide dieselbe Anfrage doppelt.
whisper-bridge:
build: ./whisper
container_name: aria-whisper-bridge
profiles: ["whisper"] # startet nur mit COMPOSE_PROFILES=…whisper…
runtime: nvidia
environment:
- NVIDIA_VISIBLE_DEVICES=${WHISPER_GPU:-1}
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
- NODE_NAME=${NODE_NAME:-node}
- RVS_HOST=${RVS_HOST}
- RVS_PORT=${RVS_PORT:-443}
- RVS_TLS=${RVS_TLS:-true}
- RVS_TLS_FALLBACK=${RVS_TLS_FALLBACK:-true}
- RVS_TOKEN=${RVS_TOKEN}
- WHISPER_MODEL=${WHISPER_MODEL:-small}
- WHISPER_DEVICE=${WHISPER_DEVICE:-cuda}
- WHISPER_COMPUTE_TYPE=${WHISPER_COMPUTE_TYPE:-float16}
- WHISPER_LANGUAGE=${WHISPER_LANGUAGE:-de}
volumes:
- ./hf-cache:/root/.cache/huggingface # gleicher Cache wie f5tts-bridge —
# ein Modell muss nur einmal pro
# Maschine geladen werden, kein
# Re-Download bei Container-Restart.
- ./voice-id:/voice-id # Speaker-ID-Fingerprint (Stefans
# Stimm-Embedding) persistent zwischen
# Container-Restarts.
restart: unless-stopped
# ─── Voxtral STT-3B (Transformers, GPU) — DEFAULT-STT ─────────
# Voxtral-Mini-3B-2507 (~9 GB bf16). Laeuft auf Treiber 550/CUDA 12.4
# (torch cu124, KEIN Treiber-Upgrade noetig). Whisper ist der opt-in
# Fallback — immer nur EINEN STT-Provider pro Node aktiv haben.
voxtral-bridge:
build: ./voxtral
container_name: aria-voxtral-bridge
profiles: ["voxtral"] # startet nur mit COMPOSE_PROFILES=…voxtral…
runtime: nvidia
volumes:
- ./hf-cache:/root/.cache/huggingface # gleicher Modell-Cache wie whisper/f5
- ./voice-id:/voice-id # Speaker-Fingerprint (wie whisper)
environment:
- NVIDIA_VISIBLE_DEVICES=${VOXTRAL_GPU:-1} # STT-3B ~9 GB → 12-GB-Karte
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
- NODE_NAME=${NODE_NAME:-node}
- RVS_HOST=${RVS_HOST}
- RVS_PORT=${RVS_PORT:-443}
- RVS_TLS=${RVS_TLS:-true}
- RVS_TLS_FALLBACK=${RVS_TLS_FALLBACK:-true}
- RVS_TOKEN=${RVS_TOKEN}
- VOXTRAL_MODEL=mistralai/Voxtral-Mini-3B-2507
- VOXTRAL_LANGUAGE=${WHISPER_LANGUAGE:-de}
- HUGGING_FACE_HUB_TOKEN=${HF_TOKEN:-} # falls das Modell HF-gated ist
- PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True # weniger VRAM-Fragmentierung
restart: unless-stopped
# ─── Lokales LLM — llama-swap (GPU) ───────────
# llama-swap laedt/swappt mehrere Modelle on-demand. Welches geladen wird,
# bestimmt das `model`-Feld im Request (Brain schickt es aus local_llm.json).
# Erster Load zieht das GGUF via -hf von HF (Cache unter /models, persistent).
# OpenAI-kompatibel auf :8080, nur im Compose-Netz; die Bruecke macht der
# llm-adapter. Die Modell-Liste erzeugt der llm-adapter dynamisch aus
# ./llama-swap/config.yaml (Basis) + Registry → /models/llama-swap.config.yaml.
llama-swap:
image: ghcr.io/mostlygeek/llama-swap:unified-cuda
container_name: aria-llama-swap
profiles: ["llm"] # startet nur mit COMPOSE_PROFILES=…llm…
runtime: nvidia
volumes:
- ./models:/models # HF-Cache + generierte Config
environment:
- NVIDIA_VISIBLE_DEVICES=${LLM_GPU:-0}
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
- LLAMA_CACHE=/models # llama-server legt -hf-Downloads hier ab
# Liest die vom llm-adapter generierte Config. Beim allerersten Boot faengt
# restart: unless-stopped die Reihenfolge ab, bis der Adapter sie geschrieben hat.
command: ["--config", "/models/llama-swap.config.yaml", "--listen", "0.0.0.0:8080"]
# OpenAI-kompatibler Endpunkt (/v1/chat/completions, /v1/models) — damit
# koennen Fremd-Tools wie OpenHands, Continue oder Open WebUI direkt auf das
# lokale LLM. Standardmaessig NUR auf localhost gebunden (sicher); fuer
# Zugriff aus dem LAN LLM_OPENAI_BIND=0.0.0.0 setzen — dann aber unbedingt
# LLM_OPENAI_KEY vergeben, sonst kann jeder im Netz das Modell nutzen.
ports:
- "${LLM_OPENAI_BIND:-127.0.0.1}:${LLM_OPENAI_PORT:-8081}:8080"
restart: unless-stopped
# ─── Local-LLM-Adapter — RVS <-> llama.cpp ────
# Verbindet sich per Token an den RVS (wie f5tts/whisper), nimmt llm_request
# entgegen, ruft llama.cpp lokal, antwortet llm_response. Verwaltet ausserdem
# llama-swaps Config (Modelle hinzufuegen/entfernen via llm_provision_model).
llm-adapter:
build: ./llm-adapter
container_name: aria-llm-adapter
profiles: ["llm"]
runtime: nvidia # nur fuer nvidia-smi (Auslastungs-Monitor) — kein Compute
depends_on:
- llama-swap
volumes:
- ./models:/models # generierte Config + Registry + Cache
- ./llama-swap:/llamaswap:ro # Basis-Template (config.yaml)
environment:
- NVIDIA_VISIBLE_DEVICES=all # alle Karten sichtbar (nur nvidia-smi)
- NVIDIA_DRIVER_CAPABILITIES=utility # utility = nvidia-smi, KEIN VRAM/Compute
- NODE_NAME=${NODE_NAME:-node}
- RVS_HOST=${RVS_HOST}
- RVS_PORT=${RVS_PORT:-443}
- RVS_TLS=${RVS_TLS:-true}
- RVS_TLS_FALLBACK=${RVS_TLS_FALLBACK:-true}
- RVS_TOKEN=${RVS_TOKEN}
- LLAMA_URL=http://llama-swap:8080
- LLM_MODEL=${LLM_MODEL:-qwen3-8b}
- LLAMA_BASE_CONFIG=/llamaswap/config.yaml
- LLAMA_GEN_CONFIG=/models/llama-swap.config.yaml
- LLM_REGISTRY=/models/aria_models.json
# Erster Load eines Modells kann ein GGUF ziehen (mehrere GB) — grosszuegig.
- LLM_TIMEOUT_SEC=${LLM_TIMEOUT_SEC:-600}
# Auf welche Karte llama-swap gepinnt ist — fuer die Meldung „in Karte N
# geladen" samt VRAM nach dem Laden. Dieselbe Variable wie bei llama-swap.
- LLM_GPU=${LLM_GPU:-0}
# Fuer gesperrte (gated) HF-Repos: die Dateiliste fuer den Fortschritt.
- HF_TOKEN=${HF_TOKEN:-}
restart: unless-stopped
# ─── OpenHands (optional, Standalone) ────────────────
# Eigenstaendige Entwickler-Agenten-Oberflaeche, die das LOKALE LLM ueber den
# OpenAI-Endpunkt von llama-swap nutzt. Bewusst ein eigenes Profil: wer nur
# ARIAs Box fahren will, startet es gar nicht erst.
# COMPOSE_PROFILES=llm,openhands docker compose up -d
# Danach http://<box>:${OPENHANDS_PORT:-3000} und dort als Provider
# "OpenAI-kompatibel" mit Base-URL http://llama-swap:8080/v1 eintragen
# (aus dem Container heraus erreichbar), Modell z.B. qwen3-8b.
openhands:
image: docker.all-hands.dev/all-hands-ai/openhands:latest
container_name: aria-openhands
profiles: ["openhands"]
environment:
- SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:latest
- LOG_ALL_EVENTS=true
# Vorbelegung fuer das lokale Modell — in der Oberflaeche aenderbar.
- LLM_BASE_URL=${OPENHANDS_LLM_BASE_URL:-http://llama-swap:8080/v1}
- LLM_API_KEY=${LLM_OPENAI_KEY:-local}
- LLM_MODEL=${OPENHANDS_LLM_MODEL:-openai/qwen3-8b}
volumes:
# OpenHands startet seine Arbeits-Container selbst -> braucht den Socket.
# ACHTUNG: das gibt dem Container Kontrolle ueber den Docker-Host.
- /var/run/docker.sock:/var/run/docker.sock
- ./openhands-state:/.openhands
ports:
- "${OPENHANDS_BIND:-127.0.0.1}:${OPENHANDS_PORT:-3000}:3000"
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped