Die Box meldet bei jedem Download-Fortschritt ihre Netzwerkkarten (echte Karten, die "up" sind) mit Tempo. Der Container sieht nur sein eigenes eth0, darum liest er das /sys des Rechners (neu: /sys:/hostsys:ro im llm-adapter). Das Download-Fenster zeigt pro Download z.B. "🔌 enp5s0 100 Mbit/s (max. ~12 MB/s — Kabel/Port prüfen)" — orange unter 1 Gbit/s. Anlass: ki-box hing an einem 100-Mbit-Port, 8 Verbindungen brachten darum nichts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
239 lines
13 KiB
YAML
239 lines
13 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}
|
|
- ARIA_GPU=${F5TTS_GPU:-0} # nur Anzeige (Toolkit macht aus NVIDIA_VISIBLE_DEVICES "void")
|
|
- 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}
|
|
- ARIA_GPU=${WHISPER_GPU:-1} # nur Anzeige (Toolkit macht aus NVIDIA_VISIBLE_DEVICES "void")
|
|
- 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
|
|
- ARIA_GPU=${VOXTRAL_GPU:-1} # nur Anzeige (Toolkit macht aus NVIDIA_VISIBLE_DEVICES "void")
|
|
- 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:
|
|
# Alle Karten sichtbar — welches Modell auf welcher Karte laeuft, legt der
|
|
# llm-adapter pro Modell fest (CUDA_VISIBLE_DEVICES=<Karte> in der
|
|
# generierten Config). Erlaubt sind nur die LLM-Karten aus LLM_GPUS.
|
|
- NVIDIA_VISIBLE_DEVICES=all
|
|
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
|
|
# CUDA zaehlt Karten sonst "schnellste zuerst" — dann waere CUDA-Karte 0
|
|
# nicht unbedingt nvidia-smi-Karte 0. PCI-Reihenfolge = wie nvidia-smi.
|
|
- CUDA_DEVICE_ORDER=PCI_BUS_ID
|
|
# Echten Treiber vom Host zuerst laden. Das Image traegt in seiner
|
|
# ld.so.conf die eigene CUDA-Kompatibilitaets-libcuda (fuer Treiber 575)
|
|
# VOR dem eingehaengten Host-Treiber ein. Die hilft nur Rechenzentrums-
|
|
# karten; bei GeForce scheitert llama.cpp damit ("forward compatibility …"
|
|
# bei aelterem, "unsupported display driver / cuda driver combination" bei
|
|
# neuerem Treiber) und rechnet still auf der CPU. Auf der Box getestet:
|
|
# so sieht llama.cpp beide Karten.
|
|
- LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:/usr/local/cuda/lib64
|
|
- LLAMA_CACHE=/models # llama-server legt -hf-Downloads hier ab
|
|
# Liest die vom llm-adapter generierte Config. --watch-config: Aenderungen
|
|
# (Modell in Karte geladen/entladen) greifen von selbst — einen Reload-
|
|
# Endpunkt hat llama-swap nicht. Beim allerersten Boot faengt restart:
|
|
# unless-stopped die Reihenfolge ab, bis der Adapter die Config geschrieben hat.
|
|
command: ["--config", "/models/llama-swap.config.yaml", "--listen", "0.0.0.0:8080", "--watch-config"]
|
|
# 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)
|
|
- /sys:/hostsys:ro # nur lesen: Tempo der Netzwerkkarte (Download-Fenster)
|
|
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}
|
|
# Welche Karten dem LLM gehoeren. Jede haelt genau ein Modell, geladen
|
|
# wird nur ueber die Diagnostic. Mehrere: LLM_GPUS=1,2 — eine: LLM_GPU=1
|
|
# (alte Schreibweise, gilt weiter, wenn LLM_GPUS leer ist).
|
|
- LLM_GPUS=${LLM_GPUS:-}
|
|
- 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
|