Files
ARIA-AGENT/xtts
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
..

xtts — AI-Box (Compute-Node) Setup & Bootstrap

Der xtts-Stack sind ARIAs GPU-Dienste (STT: Voxtral/Whisper · TTS: F5-TTS · lokales LLM). Er läuft auf einer oder mehreren AI-Boxen — jede per .env konfiguriert (COMPOSE_PROFILES, GPU-Zuordnung). Details zu Profilen/GPU-Wahl stehen im Haupt-README (Abschnitt „Compute-Nodes").

bootstrap.sh macht aus einem frisch installierten Debian Trixie (headless, nur SSH) einen startklaren GPU-Host für diesen Stack.

Was das Script tut

  1. Basis-Pakete (curl, gnupg, git …)
  2. contrib non-free non-free-firmware aktivieren (Trixie-deb822-Format berücksichtigt)
  3. NVIDIA-Treiber installieren (nvidia-driver + firmware)
  4. Docker Engine + Compose-Plugin
  5. NVIDIA Container Toolkit + Docker-Runtime auf NVIDIA konfigurieren
  6. xtts/.env aus .env.example anlegen (RVS_TOKEN optional gleich setzen)
  7. GPU-im-Container-Test (docker run --gpus all … nvidia-smi)
  8. optional (--up): den xtts-Stack hochziehen

Alles idempotent — mehrfach ausführbar.

Ablauf

git clone <repo> ARIA-AGENT
cd ARIA-AGENT/xtts

sudo ./bootstrap.sh
# → Wenn der Treiber frisch installiert wurde: einmal neu starten, dann nochmal:
sudo reboot
# … nach dem Boot:
cd ARIA-AGENT/xtts
sudo ./bootstrap.sh --up --token <DEIN_RVS_TOKEN>

Der Treiber-Reboot ist normal (Kernel-Modul wird erst beim Boot geladen). Beim zweiten Lauf überspringt das Script alles Erledigte und macht nur noch den GPU-Test + Stack-Start.

Optionen

Option Wirkung
--up am Ende docker compose up -d --build (Default-Profil)
--token <TOK> RVS_TOKEN in xtts/.env eintragen (auch via RVS_TOKEN=… env)
--rvs-host <H> RVS_HOST setzen

Wichtig

  • Stimm-Daten (nicht in git): falls von der alten Box noch vorhanden, xtts/voice-id/ (Speaker-Fingerprint) + xtts/voices/ (F5-Referenz) herkopieren. Sonst egal — in der App neu anlegen: Stimme neu enrollen (ohne Fingerprint läuft die Speaker-ID fail-open, alles geht durch) + F5-Voice-Referenz neu hochladen.
  • Voxtral bleibt aus auf der 3060 (braucht ≥16 GB VRAM). Das Default-Profil fährt Whisper (mit dem M0.1-Fix) + F5-TTS + lokales LLM. Voxtral erst mit der 24-GB-Karte: docker compose stop whisper-bridge && docker compose --profile voxtral up -d --build.
  • Erster Start lädt Modelle (mehrere GB via HuggingFace nach xtts/hf-cache
    • xtts/models) — genug Platz (1 TB NVMe ✓) und etwas Geduld.
  • Box im selben Netz wie der RVS (z.B. Rechenzentrum, direkt auf die interne IP statt NAT-Hairpin): in xtts/.env RVS_HOST=<interne-ip> + RVS_SNI=<zert-name> (Name, für den das Caddy-Zert gilt). Ohne das scheitert TLS an tlsv1 alert internal error. Zuhause / normal: RVS_SNI leer lassen.

Verifizieren

nvidia-smi                                   # Host sieht die GPU
docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi   # Container auch
docker logs -f aria-whisper-bridge           # "RVS verbunden" + service_status ready

Lokales LLM von außen nutzen (OpenAI-Endpunkt)

llama-swap spricht OpenAI-kompatibel (/v1/chat/completions, /v1/models). Damit können Fremd-Tools direkt auf das lokale Modell: OpenHands, Continue, Open WebUI, Aider …

Base-URL:  http://<box>:<LLM_OPENAI_PORT>/v1
Modell:    qwen3-8b   (oder ein anderer Key aus llama-swap/config.yaml)
API-Key:   LLM_OPENAI_KEY  (falls gesetzt)

In der .env:

Variable Bedeutung
LLM_OPENAI_BIND 127.0.0.1 = nur diese Maschine (Default, sicher) · 0.0.0.0 = ganzes LAN
LLM_OPENAI_PORT Host-Port (Default 8081)
LLM_OPENAI_KEY API-Key; leer = ohne Authentifizierung

⚠️ LLM_OPENAI_BIND=0.0.0.0 ohne LLM_OPENAI_KEY heißt: jeder im Netz kann das Modell benutzen. Entweder Key setzen oder auf localhost gebunden lassen.

Wo wird was konfiguriert?

  • Port und Erreichbarkeit immer in der .env. Beides ist ein Docker-Port-Mapping und steht beim Container-Start fest — es lässt sich nicht aus der Ferne ändern.
  • API-Key: entweder in der .env oder — wenn dort leer — in der ARIA-Diagnostic unter Compute-Flotte bei der jeweiligen Box. Dort abgelegt landet er auf der Box unter /models/openai_key.json (Rechte 0600).
  • Die .env hat immer Vorrang. Steht LLM_OPENAI_KEY dort, ist das Feld in der Diagnostic schreibgeschützt (🔒-Hinweis).
  • Ein geänderter Key greift, sobald ein Modell das nächste Mal geladen wird (llama-server bekommt ihn per --api-key). Laufendes Modell also einmal entladen lassen (TTL) oder ein anderes Modell anfragen.

Prüfen:

curl -s http://localhost:8081/v1/models                       # ohne Key
curl -s -H "Authorization: Bearer $LLM_OPENAI_KEY" \
     http://localhost:8081/v1/models                          # mit Key

OpenHands (optionales Profil)

OpenHands ist rein optional und liegt in einem eigenen Profil. Wer es nicht in COMPOSE_PROFILES aufführt, bei dem wird der Container gar nicht erst angelegt — das Image wird nicht einmal geladen. Für den normalen ARIA-Betrieb also einfach weglassen:

COMPOSE_PROFILES=voxtral,f5tts,llm docker compose up -d   # ohne OpenHands
COMPOSE_PROFILES=llm,openhands     docker compose up -d   # mit OpenHands

Dann http://<box>:<OPENHANDS_PORT> (Default 3000) öffnen. Das lokale Modell ist schon vorbelegt (OPENHANDS_LLM_BASE_URL, OPENHANDS_LLM_MODEL) und in der Oberfläche änderbar.

Variable Bedeutung
OPENHANDS_BIND 127.0.0.1 (Default) oder 0.0.0.0 fürs LAN
OPENHANDS_PORT Host-Port (Default 3000)
OPENHANDS_LLM_BASE_URL Default http://llama-swap:8080/v1 (Container-intern — nicht der Host-Port)
OPENHANDS_LLM_MODEL Default openai/qwen3-8b

⚠️ OpenHands startet eigene Arbeits-Container und bekommt dafür den Docker-Socket. Das ist faktisch Root-Zugriff auf diese Maschine — wer die Oberfläche erreicht, kann im Zweifel alles auf der Box.

Deshalb bleibt OPENHANDS_BIND auf 127.0.0.1. Von einem anderen Rechner greifst Du per SSH-Tunnel zu, statt den Port ins LAN zu öffnen:

ssh -L 3000:127.0.0.1:3000 user@box     # dann lokal http://localhost:3000

Das kostet nichts und ist die wirksamste Absicherung, solange der Socket durchgereicht wird. Wer echte Isolation braucht: OpenHands in eine eigene VM, oder einen rootless Docker-Daemon als Beiwagen statt des Host-Sockets.

Ohne das Profil ändert sich nichts: Wer nur ARIAs Box fahren will, startet OpenHands gar nicht.