- admin_llm: switch_backend() mit strikter Allowlist (backend ∈ {ollama,llamacpp},
Modell gegen 'ollama list' + Format-Regex), detached (Self-Restart-sicher),
niemals shell=True. restart_gateway_detached() für systemd-User-Dienst.
- switch-llm.sh: flock-Lock gegen parallele Backend-Wechsel (Exit 75).
- Endpunkte POST /api/admin/llm/backend (422 bei ungültig) und
POST /api/admin/gateway/restart (require_admin).
- Status-Tab: Steuerung (Backend-Dropdown + Modell, Wechseln/Neustart) mit
Poll bis das Gateway wieder antwortet; Hinweis auf systemd-Voraussetzung.
- Tests: Auth + Allowlist (Shell-Metazeichen/unbekanntes Modell -> 422). 165 grün.
- Doku §7.5.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
101 lines
3.6 KiB
Bash
Executable file
101 lines
3.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Wechselt das lokale LLM-Backend zwischen Ollama und llama.cpp.
|
|
# Aufruf: switch-llm.sh ollama | llamacpp
|
|
#
|
|
# Das Skript (1) gibt den GPU-Speicher des jeweils anderen Backends frei,
|
|
# (2) startet das gewuenschte Backend, (3) passt die aktiven LOCAL_LLM_*-Zeilen
|
|
# in .env an und (4) sorgt dafuer, dass das Gateway die .env neu liest
|
|
# (Dienst-Neustart, sonst Hinweis). Provider bleibt 'local-openai-compatible'.
|
|
#
|
|
# Modelle/Ports ueber ENV ueberschreibbar, z. B.:
|
|
# OLLAMA_MODEL=qwen2.5:latest scripts/llm-server/switch-llm.sh ollama
|
|
|
|
TARGET="${1:-}"
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
ENV_FILE="$ROOT_DIR/.env"
|
|
|
|
# Lock gegen parallele Backend-Wechsel (z. B. zwei Admin-Klicks gleichzeitig).
|
|
# flock haelt das Lock fuer die gesamte Laufzeit dieses Skripts; ein zweiter
|
|
# Aufruf scheitert sofort mit Exit 75 (-> Endpoint meldet "läuft bereits").
|
|
LOCK_FILE="${LOCK_FILE:-/tmp/va-llm-switch.lock}"
|
|
exec 9>"$LOCK_FILE"
|
|
if ! flock -n 9; then
|
|
echo "[!] Ein Backend-Wechsel laeuft bereits (Lock: $LOCK_FILE)." >&2
|
|
exit 75
|
|
fi
|
|
|
|
OLLAMA_BASE_URL="${OLLAMA_BASE_URL:-http://127.0.0.1:11434/v1}"
|
|
OLLAMA_MODEL="${OLLAMA_MODEL:-gemma3:latest}"
|
|
LLAMACPP_BASE_URL="${LLAMACPP_BASE_URL:-http://127.0.0.1:8001/v1}"
|
|
LLAMACPP_MODEL="${LLAMACPP_MODEL:-va_llm}"
|
|
CONTAINER_NAME="${CONTAINER_NAME:-va_llm}"
|
|
|
|
# Setzt eine aktive (unkommentierte) KEY=VALUE-Zeile in .env (kommentierte # KEY bleiben unberuehrt).
|
|
set_env() {
|
|
local key="$1" val="$2"
|
|
if grep -qE "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
|
sed -i "s|^${key}=.*|${key}=${val}|" "$ENV_FILE"
|
|
else
|
|
printf '%s=%s\n' "$key" "$val" >> "$ENV_FILE"
|
|
fi
|
|
}
|
|
|
|
# Entlaedt alle in Ollama geladenen Modelle (gibt GPU frei) -- der Dienst bleibt laufen.
|
|
unload_ollama_models() {
|
|
local loaded
|
|
loaded="$(ollama ps 2>/dev/null | awk 'NR>1 {print $1}')" || true
|
|
for m in $loaded; do
|
|
echo "[*] Entlade Ollama-Modell: $m"
|
|
ollama stop "$m" >/dev/null 2>&1 || true
|
|
done
|
|
}
|
|
|
|
restart_gateway() {
|
|
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
|
|
if systemctl --user is-active --quiet voice-assistant.service 2>/dev/null; then
|
|
echo "[*] Starte Gateway-Dienst neu (liest .env neu) ..."
|
|
systemctl --user restart voice-assistant.service
|
|
else
|
|
echo "[!] Gateway laeuft nicht als Dienst (vermutlich 'make run' im Vordergrund)."
|
|
echo " -> Dort neu starten, damit die neue .env greift:"
|
|
echo " Strg+C, dann in einer FRISCHEN Shell: make run"
|
|
fi
|
|
}
|
|
|
|
case "$TARGET" in
|
|
ollama)
|
|
echo "[*] Wechsel auf OLLAMA (Modell: $OLLAMA_MODEL)"
|
|
if docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1; then
|
|
echo "[*] llama.cpp-Container '$CONTAINER_NAME' gestoppt (GPU frei)."
|
|
else
|
|
echo "[*] llama.cpp lief nicht."
|
|
fi
|
|
if ! curl -s -m 5 -o /dev/null "${OLLAMA_BASE_URL%/v1}/api/tags" 2>/dev/null; then
|
|
echo "[*] ollama-Dienst nicht erreichbar -> starte ihn ..."
|
|
sudo systemctl start ollama || true
|
|
fi
|
|
set_env LOCAL_LLM_BASE_URL "$OLLAMA_BASE_URL"
|
|
set_env LOCAL_LLM_API_KEY "ollama"
|
|
set_env LOCAL_LLM_MODEL "$OLLAMA_MODEL"
|
|
echo "[*] .env -> $OLLAMA_BASE_URL | Modell $OLLAMA_MODEL"
|
|
;;
|
|
llamacpp)
|
|
echo "[*] Wechsel auf LLAMA.CPP (Modell-Alias: $LLAMACPP_MODEL)"
|
|
unload_ollama_models
|
|
bash "$SCRIPT_DIR/start-llm-server.sh"
|
|
set_env LOCAL_LLM_BASE_URL "$LLAMACPP_BASE_URL"
|
|
set_env LOCAL_LLM_API_KEY "dummy"
|
|
set_env LOCAL_LLM_MODEL "$LLAMACPP_MODEL"
|
|
echo "[*] .env -> $LLAMACPP_BASE_URL | Modell $LLAMACPP_MODEL"
|
|
;;
|
|
*)
|
|
echo "Aufruf: $0 ollama|llamacpp" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
|
|
restart_gateway
|
|
echo "[✓] LLM-Backend auf '$TARGET' umgestellt."
|