#!/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."