Files
duffyduckandClaude Opus 5.5 23feb3ba2d feat(llm): ein Modell pro Karte, Clients waehlen nur aus — und Doku fuer Drittprogramme
Bisher konnte jeder Client per Modellname llama-swap zum Wechseln bringen —
ein Fremdprogramm haette ARIA das Modell unter den Fuessen weggetauscht.
Jetzt:

- Jede LLM-Karte einer Box (LLM_GPUS=1,2, alt LLM_GPU=1) haelt GENAU ein
  Modell, dauerhaft. Welches, legt nur die Diagnostic fest. Karten, die nicht
  fuers LLM vorgesehen sind, tauchen beim LLM nicht auf und werden als Ziel
  abgelehnt.
- Clients (ARIA, Fremdprogramme ueber RVS, OpenHands ueber den OpenAI-Port)
  sehen nur die geladenen Modelle und laden nie: Anfrage fuer ein anderes →
  model_not_loaded mit Liste des Geladenen (RVS) bzw. HTTP 404 (OpenAI-Port).
  Nichts wird verdraengt.
- Mehrere Clients koennen dasselbe Modell gleichzeitig nutzen.

llm-adapter: Belegung in /models/aria_slots.json. Die llama-swap-Config
enthaelt nur belegte Karten: pro Modell CUDA_VISIBLE_DEVICES=<Karte>, ttl 0,
groups.swap=false, hooks.on_startup.preload. Alles gegen llama-swap
ausprobiert (zwei Modelle gleichzeitig ready, keins verdraengt, Unbekanntes
404, env kommt beim jeweiligen llama-server an). llama-swap laeuft mit
--watch-config; die frueher aufgerufenen Reload-Endpunkte (/reload,
/api/config/reload) gibt es nicht (404). Neu: llm_unload_model (Karte leeren),
llm_models_request → llm_models (Erkundung, jede Box antwortet einzeln).
Fehlgeschlagenes Laden setzt die Karte auf ihr vorheriges Modell zurueck,
sonst risse jedes Neuladen die anderen Karten mit. Uebernahme: ohne
Belegungsdatei landet LLM_MODEL auf der ersten LLM-Karte.

Compose: llama-swap sieht alle Karten (NVIDIA_VISIBLE_DEVICES=all) und
zaehlt sie wie nvidia-smi (CUDA_DEVICE_ORDER=PCI_BUS_ID); das Pinnen pro
Modell macht der Adapter.

Bridge: uebernimmt models/slots auch aus worker_ping (nach "Entladen" sonst
30 s veraltet) und sagt sofort ab, wenn das Modell auf keiner Box liegt,
statt per Broadcast in den Timeout zu laufen.

Diagnostic: Flotte zeigt pro LLM-Karte Modell + Zustand + "Entladen";
Katalog waehlt eine Ziel-KARTE (leere vorausgewaehlt, Ersetzen mit
Rueckfrage); Download-Fenster nennt Karte und was ersetzt wird.

RVS: llm_unload_model, llm_models_request, llm_models freigegeben.

docs/LLM-SCHNITTSTELLE.md: Schnittstelle fuer Drittprogramme (RVS + OpenAI-
Port), Fehlercodes, Gleichzeitigkeit, Python-Beispiel.

Tests: 23 gegen llama-swap (Adapter direkt) + Ende-zu-Ende ueber den echten
RVS mit dem Adapter als Prozess: Laden per RVS mit Fortschritt, das
Doku-Beispiel unveraendert (nur Adresse) findet beide Karten und bekommt eine
Antwort, Absage/Schweigen wie dokumentiert, zwei Clients parallel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:04:24 +02:00

103 lines
5.9 KiB
Bash

# ════════════════════════════════════════════════
# ARIA Compute-Node — Konfiguration
# Kopieren nach .env und anpassen (pro Worker-Node eine eigene .env)
# ════════════════════════════════════════════════
# ─── Welche Dienste startet DIESER Node? ──────────
# Komma-getrennt aus: voxtral, whisper, f5tts, llm, openhands
# Nur die aufgefuehrten Dienste starten bei `docker compose up -d`.
# nur STT-Box (Voxtral) → COMPOSE_PROFILES=voxtral
# nur STT-Box (Whisper) → COMPOSE_PROFILES=whisper
# nur TTS-Box → COMPOSE_PROFILES=f5tts
# nur LLM-Box → COMPOSE_PROFILES=llm
# Kleine Karte (wenig VRAM): Whisper + F5-TTS auf EINER GPU
# → COMPOSE_PROFILES=whisper,f5tts
# All-in-One, grosse Karte → COMPOSE_PROFILES=voxtral,f5tts,llm
# LLM + OpenHands-Oberflaeche → COMPOSE_PROFILES=llm,openhands
# 'openhands' ist REIN OPTIONAL (Standalone-Entwickler-Agent, s.u.). Wer es nicht
# auffuehrt, bei dem wird der Container gar nicht erst angelegt — das Image wird
# nicht einmal geladen. Fuer den normalen ARIA-Betrieb also einfach weglassen.
# ACHTUNG: Profile brauchen compose v2 (`docker compose`, mit Leerzeichen). Das
# alte `docker-compose` 1.x behandelt sie unzuverlaessig.
# Hinweis: voxtral UND whisper zusammen NICHT sinnvoll — beide sind STT und
# wuerden dieselbe Anfrage doppelt beantworten. Pro Node genau EINEN STT waehlen:
# Voxtral-3B (~9 GB, beste Qualitaet) ODER Whisper (klein, passt neben F5-TTS).
COMPOSE_PROFILES=whisper,f5tts
# ─── Node-Name ────────────────────────────────────
# Freier Name dieses Rechners. Erscheint in Diagnostic (Flotten-Anzeige) und
# in den Logs, und bildet die Instanz-ID der Dienste (z.B. f5tts@ai-box).
NODE_NAME=ai-box
# ─── GPU-Zuordnung pro Dienst ─────────────────────
# Setzt NVIDIA_VISIBLE_DEVICES fuer den jeweiligen Container.
# Einzelne Karte → "0" oder "1"; mehrere Karten → "0,1".
# Braucht das NVIDIA Container Toolkit (registriert die `nvidia`-Runtime).
# Nur die GPUs der aktiven Profile (COMPOSE_PROFILES) sind ueberhaupt relevant.
WHISPER_GPU=0 # kleines STT → passt neben F5-TTS auf EINE Karte
F5TTS_GPU=0 # TTS ist klein → gleiche Karte wie Whisper
VOXTRAL_GPU=1 # STT-3B ~9 GB → nur auf einer groesseren Karte (z.B. 12 GB)
LLM_GPU=0 # lokales LLM auf EINER Karte (teilt sich ggf. die Karte mit F5-TTS)
# Mehrere Karten fuers LLM: Liste statt LLM_GPU. Jede Karte haelt genau EIN
# Modell, dauerhaft geladen — welches, legst du in der Diagnostic fest
# (Modell-Katalog → Laden → Karte). Clients (ARIA, Fremdprogramme, OpenHands)
# koennen nur die geladenen nutzen und nie selbst etwas laden.
# Beispiel 3 Karten: 0 = Whisper + F5-TTS, 1 und 2 nur LLM:
# WHISPER_GPU=0 F5TTS_GPU=0 LLM_GPUS=1,2
#LLM_GPUS=
# ─── RVS-Verbindung ───────────────────────────────────────
# WICHTIG: Host, Port UND Token muessen EXAKT mit dem ARIA-Stack (Bridge/
# Diagnostic) uebereinstimmen — das RVS gruppiert Clients pro Server+Token in
# einen Raum. Bei abweichendem Port/Host landet die Box in einem ANDEREN Raum
# und taucht nicht in der Compute-Flotte auf.
RVS_HOST=example.com
RVS_PORT=444
RVS_TLS=true
RVS_TLS_FALLBACK=true
RVS_TOKEN=dein_token_hier
# RVS_SNI: nur noetig, wenn die Box im SELBEN Netz wie der RVS steht und direkt
# auf dessen interne IP verbinden soll (kein NAT-Hairpin ueber den externen
# Hostnamen). Dann RVS_HOST=<interne-ip> und hier den Zertifikats-/Hostnamen, fuer
# den Caddy sein Cert hat. Zuhause / normal: leer lassen.
# RVS_HOST=10.0.0.2
# RVS_SNI=example.com
RVS_SNI=
# ─── Optional ─────────────────────────────────────
# HF_TOKEN= # nur falls ein HF-gated Modell (z.B. Voxtral) geladen wird
# WHISPER_MODEL=small # tiny|base|small|medium|large-v3 (Hot-Swap via Diagnostic)
# WHISPER_LANGUAGE=de
# LLM_MODEL=qwen3-8b # Key aus llama-swap/config.yaml
# ─── OpenAI-Endpunkt des lokalen LLM (optional) ───
# llama-swap ist OpenAI-kompatibel (/v1/chat/completions, /v1/models). Damit
# koennen Fremd-Tools direkt auf das lokale Modell: OpenHands, Continue,
# Open WebUI, Aider ... Base-URL dann: http://<box>:<LLM_OPENAI_PORT>/v1
#
# LLM_OPENAI_BIND steuert, WER drankommt:
# 127.0.0.1 nur diese Maschine (Default, sicher)
# 0.0.0.0 das ganze LAN → dann UNBEDINGT LLM_OPENAI_KEY setzen!
LLM_OPENAI_BIND=127.0.0.1
LLM_OPENAI_PORT=8081
# API-Key. Leer = Endpunkt ohne Authentifizierung (nur mit BIND=127.0.0.1 ok).
# WICHTIG: Steht hier ein Wert, hat er VORRANG — das Feld ist dann in der ARIA-
# Diagnostic schreibgeschuetzt. Leer lassen, wenn Du den Key lieber dort pro Box
# setzen willst (er wird dann auf der Box unter /models/openai_key.json abgelegt).
# Der Key wirkt, sobald ein Modell das naechste Mal geladen wird.
LLM_OPENAI_KEY=
# ─── OpenHands (optional, eigenes Profil) ─────────
# Standalone-Entwickler-Agent, der das lokale LLM nutzt. Start:
# COMPOSE_PROFILES=llm,openhands docker compose up -d
# Dann http://<box>:<OPENHANDS_PORT> aufrufen.
# ACHTUNG: OpenHands startet eigene Arbeits-Container und bekommt dafuer den
# Docker-Socket — das ist faktisch Root-Zugriff auf diese Maschine. Deshalb
# BIND auf 127.0.0.1 lassen und von aussen per SSH-Tunnel zugreifen:
# ssh -L 3000:127.0.0.1:3000 user@box -> lokal http://localhost:3000
OPENHANDS_BIND=127.0.0.1
OPENHANDS_PORT=3000
# Wie OpenHands das lokale Modell anspricht (Container-intern, daher :8080):
OPENHANDS_LLM_BASE_URL=http://llama-swap:8080/v1
OPENHANDS_LLM_MODEL=openai/qwen3-8b