diff --git a/BEDIENUNGSANLEITUNG.md b/BEDIENUNGSANLEITUNG.md index 2191a4a..28371a1 100644 --- a/BEDIENUNGSANLEITUNG.md +++ b/BEDIENUNGSANLEITUNG.md @@ -379,6 +379,8 @@ Die wichtigsten Kommandos auf einen Blick — Details in den Abschnitten darunte | Profil `cloud` — nur Gateway | `make run` | | Profil `hybrid` / `local-dev` — llama.cpp + Gateway | `make start` | | Profil `hybrid` / `local-dev` — Ollama + Gateway | `sudo systemctl start ollama && make run` | +| LLM-Backend → Ollama wechseln | `make llm-ollama` (→ § 4.7) | +| LLM-Backend → llama.cpp wechseln | `make llm-llamacpp` (→ § 4.7) | | systemd-Dienst starten | `systemctl --user start voice-assistant` | **Stoppen:** @@ -571,18 +573,37 @@ VA_PROFILE=local-dev make run # alles lokal (STT/TTS in-process, LLM via Ollam ### 4.7 Zwischen llama.cpp und Ollama wechseln Beide nutzen denselben Gateway-Provider `local-openai-compatible` (OpenAI-kompatible -API). Der Wechsel besteht daher aus drei Dingen: **(A)** den jeweils anderen -Backend-Prozess stoppen (beide teilen sich den GPU-Speicher), **(B)** das gewünschte -Backend starten, **(C)** die drei `LOCAL_LLM_*`-Zeilen in `.env` umstellen und das -Gateway neu starten. `DEFAULT_LLM_PROVIDER` bleibt unverändert. +API). `DEFAULT_LLM_PROVIDER` bleibt beim Wechsel unverändert. + +#### Schnellster Weg: Make-Targets (empfohlen) + +```bash +make llm-ollama # -> Ollama (Default-Modell gemma3:latest) +make llm-llamacpp # -> llama.cpp (Alias va_llm) + +# Anderes Ollama-Modell: +OLLAMA_MODEL=qwen2.5:latest make llm-ollama +``` + +Das Target erledigt automatisch alle Schritte: es gibt den GPU-Speicher des anderen +Backends frei (llama.cpp-Container stoppen bzw. geladene Ollama-Modelle entladen — der +Ollama-*Dienst* bleibt für andere Nutzungen laufen), startet das gewünschte Backend, +passt die `LOCAL_LLM_*`-Zeilen in `.env` an und startet das Gateway neu (als Dienst) +bzw. weist auf den manuellen Neustart hin. Skript: `scripts/llm-server/switch-llm.sh`. + +> **Warum der Gateway-Neustart nötig ist:** Das `Makefile` exportiert die `.env`-Werte +> als echte Umgebungsvariablen an `uvicorn` — und Env-Variablen haben **Vorrang vor der +> `.env`-Datei**. Eine reine `.env`-Änderung wirkt daher erst, wenn das Gateway neu +> gestartet wird (uvicorn `--reload` reagiert nur auf Code-, nicht auf `.env`-Änderungen). +> Starte es **in einer frischen Shell** neu (`make run`) bzw. als Dienst: +> `systemctl --user restart voice-assistant.service`. + +#### Manuell (was die Targets im Hintergrund tun) **Merkhilfe:** - llama.cpp = Docker-Container `va_llm` → `make llm-up` / `make llm-down` (Port 8001) - Ollama = systemd-Dienst → `sudo systemctl start/stop ollama` (Port 11434) - -> **Wichtig:** `.env`-Änderungen werden erst bei einem **Gateway-Neustart** wirksam -> (uvicorn `--reload` lädt nur bei Code-Änderungen neu, nicht bei `.env`). Als Dienst: -> `systemctl --user restart voice-assistant.service`. + GPU freigeben ohne Dienst-Stopp: `ollama stop ` #### Von llama.cpp → Ollama wechseln diff --git a/Makefile b/Makefile index e26955e..4d6a25f 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ HOST ?= 0.0.0.0 CONTAINER_NAME ?= va_llm -.PHONY: ensure-env install run start stop restart test smoke docker-build llm-up llm-down llm-status +.PHONY: ensure-env install run start stop restart test smoke docker-build llm-up llm-down llm-status llm-ollama llm-llamacpp ensure-env: @if [ ! -f .env ] && [ -f .env.example ]; then \ @@ -63,3 +63,13 @@ llm-down: llm-status: bash scripts/llm-server/status-llm-server.sh + +# --- LLM-Backend wechseln (Ollama <-> llama.cpp) ----------------------------- +# Passt die LOCAL_LLM_*-Zeilen in .env an, gibt den GPU-Speicher des anderen +# Backends frei, startet das gewuenschte und startet das Gateway (falls Dienst) neu. +# Modell ueberschreibbar: OLLAMA_MODEL=qwen2.5:latest make llm-ollama +llm-ollama: + bash scripts/llm-server/switch-llm.sh ollama + +llm-llamacpp: + bash scripts/llm-server/switch-llm.sh llamacpp diff --git a/scripts/llm-server/switch-llm.sh b/scripts/llm-server/switch-llm.sh new file mode 100755 index 0000000..3df4c45 --- /dev/null +++ b/scripts/llm-server/switch-llm.sh @@ -0,0 +1,91 @@ +#!/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" + +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."