#!/usr/bin/env bash # # Opt-in Integrations-Rauchtest gegen einen ECHTEN Docker + llama.cpp-Server. # Läuft NICHT in der normalen pytest-Suite (braucht Docker, GPU-Speicher und ein # echtes GGUF-Modell). Startet einen separaten Container (eigener Name/Port), um # eine ggf. laufende Produktion nicht anzufassen — räumt am Ende auf. # # Voraussetzungen: # - Docker-Daemon erreichbar # - freier GPU-Speicher für das in der Config referenzierte Modell # (ein 21-GB-Modell passt NICHT neben einen bereits laufenden 256k-Server; # ggf. SMOKE_GPU auf eine freie Karte setzen oder Produktion vorher stoppen) # # Konfiguration über Environment-Variablen (alle optional): # CLI Pfad zum CLI (Default: ./.venv/bin/llamacppctl) # SMOKE_CONFIG Config-Datei (Default: llama.cpp.config) # SMOKE_PROFILE --profile (Default: keins) # SMOKE_CONTAINER Container-Name (Default: llamacppctl_smoke) # SMOKE_PORT Host-Port (Default: 8099) # SMOKE_GPU GPU-Device (Default: aus Config) # SMOKE_CTX ctx_size für den Test (Default: 8192, klein = schnell/wenig VRAM) # SMOKE_MAX_TOKENS Chat-Budget (Default: 4000; Reasoning-Modelle brauchen viel) # SMOKE_API_KEY API-Key (Default: zufällig) # # Beispiel: # SMOKE_GPU=1 SMOKE_CTX=8192 scripts/smoke.sh # set -uo pipefail CLI=${CLI:-./.venv/bin/llamacppctl} CONFIG=${SMOKE_CONFIG:-llama.cpp.config} CONTAINER=${SMOKE_CONTAINER:-llamacppctl_smoke} PORT=${SMOKE_PORT:-8099} CTX=${SMOKE_CTX:-8192} MAX_TOKENS=${SMOKE_MAX_TOKENS:-4000} API_KEY=${SMOKE_API_KEY:-smoke-$$-$RANDOM} base=(--config "$CONFIG" --container-name "$CONTAINER" --host-port "$PORT") [ -n "${SMOKE_PROFILE:-}" ] && base+=(--profile "$SMOKE_PROFILE") [ -n "${SMOKE_GPU:-}" ] && base+=(--gpu "$SMOKE_GPU") pass=0 fail=0 ok() { echo " PASS: $1"; pass=$((pass + 1)); } bad() { echo " FAIL: $1"; fail=$((fail + 1)); } cleanup() { echo "== cleanup ==" "$CLI" --stop "${base[@]}" >/dev/null 2>&1 || docker rm -f "$CONTAINER" >/dev/null 2>&1 || true } trap cleanup EXIT command -v docker >/dev/null 2>&1 || { echo "docker fehlt auf PATH"; exit 2; } docker version >/dev/null 2>&1 || { echo "docker-Daemon nicht erreichbar"; exit 2; } echo "== dry-run ==" if "$CLI" --start "${base[@]}" --api-key "$API_KEY" --ctx-size "$CTX" --dry-run | grep -q "docker run"; then ok "dry-run zeigt docker-run-Kommando (kein Start)" else bad "dry-run" fi echo "== start (api-key gesetzt, ctx=$CTX) ==" if "$CLI" --start "${base[@]}" --api-key "$API_KEY" --ctx-size "$CTX"; then ok "start + readiness" else bad "start (Modell/GPU prüfen)"; exit 1 fi echo "== --check Exit-Code (läuft -> 0) ==" rc=0; "$CLI" --check "${base[@]}" >/dev/null 2>&1 || rc=$? [ "$rc" -eq 0 ] && ok "--check == 0" || bad "--check == $rc (erwartet 0)" echo "== API-Key erzwungen: ohne Key -> 401 ==" code=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:$PORT/v1/models") [ "$code" = "401" ] && ok "unauth -> 401" || bad "unauth -> $code (erwartet 401)" echo "== API-Key: mit Key -> 200 ==" code=$(curl -s -o /dev/null -w '%{http_code}' -H "Authorization: Bearer $API_KEY" "http://127.0.0.1:$PORT/v1/models") [ "$code" = "200" ] && ok "auth -> 200" || bad "auth -> $code (erwartet 200)" echo "== chat (Tool sendet Bearer-Key) ==" out=$("$CLI" --chat "${base[@]}" --api-key "$API_KEY" --max-tokens "$MAX_TOKENS" -p "Sag in genau einem Satz Hallo." 2>/dev/null) [ -n "$out" ] && ok "chat liefert Text: ${out:0:60}..." || bad "chat leer" echo "== stream ==" sout=$("$CLI" --chat "${base[@]}" --api-key "$API_KEY" --stream --max-tokens "$MAX_TOKENS" -p "Zähle bis drei." 2>/dev/null) [ -n "$sout" ] && ok "stream liefert Text" || bad "stream leer" echo "== stop -> --check == 5 ==" rc=0; "$CLI" --stop "${base[@]}" >/dev/null 2>&1 || rc=$? [ "$rc" -eq 0 ] && ok "stop" || bad "stop == $rc" rc=0; "$CLI" --check "${base[@]}" >/dev/null 2>&1 || rc=$? [ "$rc" -eq 5 ] && ok "--check nach stop == 5" || bad "--check nach stop == $rc (erwartet 5)" echo echo "== Ergebnis: $pass PASS, $fail FAIL ==" [ "$fail" -eq 0 ]