# Bedienungsanleitung — llamacppctl `llamacppctl` startet, prüft, stoppt, wechselt und promptet einen **llama.cpp-Server, der als Docker-Container** läuft — über **eine** CLI statt mehrerer Shell-Skripte. Die OpenAI-kompatible API des Servers ist standardmäßig nur auf `127.0.0.1` erreichbar. Diese Anleitung deckt **Installation** und **Benutzung** vollständig ab. Zur Installation aus dem fertigen Archiv siehe zusätzlich [`INSTALL_FROM_ARCHIVE.md`](INSTALL_FROM_ARCHIVE.md). Details zum Sicherheits- und Betriebsmodell stehen in [`docs/SECURITY_AND_OPERATIONS.md`](docs/SECURITY_AND_OPERATIONS.md); die vollständige Optionsreferenz in der Manpage (`man/llamacppctl.1`). --- ## 1. Voraussetzungen - **Docker** (Daemon läuft, Nutzer in der `docker`-Gruppe): `docker version` muss funktionieren. - **NVIDIA-GPU** + Treiber + **NVIDIA Container Toolkit** (damit `--gpus` im Container greift): `nvidia-smi` funktioniert, und `docker run --rm --gpus all nvidia-smi` zeigt die GPU. - **Python ≥ 3.10**. - Ein **GGUF-Modell** auf dem Host, das read-only in den Container gemountet wird. - Das Docker-Image, z. B. `ghcr.io/ggml-org/llama.cpp:server-cuda` (wird beim ersten Start automatisch gezogen). --- ## 2. Installation ### 2.1 Aus dem Quellcode (empfohlen für Entwicklung) ```bash cd llamacppctl python3 -m venv .venv source .venv/bin/activate pip install -e . # editable: CLI == Quellcode in src/ # optional für Tests: pip install -e ".[dev]" ``` > **Wichtig:** Bei `pip install .` (ohne `-e`) läuft das CLI aus einer Kopie in > `site-packages` — Änderungen an `src/` wirken dann NICHT. Für Entwicklung immer > `-e` verwenden. Kontrolle: `python -c "import llamacppctl.actions as a; print(a.__file__)"` > muss auf `src/` zeigen. ### 2.2 Aus dem Archiv Siehe [`INSTALL_FROM_ARCHIVE.md`](INSTALL_FROM_ARCHIVE.md). ### 2.3 Installation prüfen (ohne Docker) ```bash llamacppctl --help llamacppctl --print-effective-config --config llama.cpp.config --start --dry-run ``` --- ## 3. Konfiguration ### 3.1 Datei anlegen ```bash cp llama.cpp.config.example llama.cpp.config # llama.cpp.config an den Host anpassen (Pfade, GPU, Ports) ``` Der Standard-Pfad ist `llama.cpp.config` im aktuellen Verzeichnis, überschreibbar mit `--config `. ### 3.2 Aufbau (INI) - `[default]` — globale Werte, überschreiben die eingebauten Defaults. - `[model.]` — ein Modell-Profil, gewählt mit `--profile `. - `[prompt.]` — ein *Fallback*-Systemprompt, gewählt mit `--system-prompt-profile ` (ein explizites `-s`/`--system-file`/`--system-url` gewinnt immer). **Auflösungsreihenfolge** (niedrig → hoch): eingebaute Defaults → `[default]` → `[model.]` → CLI-Overrides. ### 3.3 Wichtige Felder | Feld / CLI | Bedeutung | |---|---| | `image` / `--image` | Docker-Image (z. B. `ghcr.io/ggml-org/llama.cpp:server-cuda`) | | `hf_home` / `--hf-home` | Host-Verzeichnis, read-only gemountet. **Env-Variablen erlaubt**, z. B. `hf_home = ${HF_HOME}` | | `model_path` / `--model-path` | Modellpfad, relativ zu `hf_home` (oder absolut) | | `container_name` / `--container-name` | **Pflicht**, eindeutig pro parallel laufendem Profil (Identitätsanker für Naming/Lock/Stop/Check) | | `host_port` / `--host-port` | Host-Port (1–65535) | | `container_port` / `--container-port` | Container-interner Port | | `model_alias` / `--model-alias` | Modell-Alias in der API | | `gpu_device` / `--gpu` | GPU-Device für `--gpus device=` (auch mehrere: `1,2`) | | `ctx_size` / `--ctx-size` | Kontextfenster (KV-Cache wächst damit) | | `n_predict` / `--n-predict` | Server-seitige max. Vorhersage-Tokens | | `temp`, `top_p`, `top_k`, `min_p`, `repeat_penalty` | Sampling (beim Start in den Container gebacken) | | `expose` / `--expose` / `--no-expose` | Port auf allen Interfaces (LAN) statt nur `127.0.0.1`. **Default: aus** | | `api_key` / `--api-key` | API-Key: Server verlangt ihn, Tool sendet ihn als Bearer-Token | | `max_tokens` / `--max-tokens` | Antwortbudget für `--chat`/`--start`-Antwort (Default 2048) | | `chat_temperature` / `--chat-temp` | Temperatur des Chat-Requests (leer = Server-`temp`) | | `timeout` / `--timeout` | Readiness-Timeout in Sekunden | | `reasoning` / `--reasoning {on,off}` | Reasoning-Modus | Boolean-Schalter mit Paar-Form: `--jinja/--no-jinja`, `--fa/--no-fa`, `--kv-unified/--no-kv-unified`, `--cont-batching/--no-cont-batching`, `--no-context-shift/--context-shift`. ### 3.4 Beispiel ```ini [default] image = ghcr.io/ggml-org/llama.cpp:server-cuda hf_home = ${HF_HOME} model_path = models/qwen3/mein-modell-Q4_K_M.gguf model_alias = mein_llm container_name = llama_cpp_server host_port = 8001 gpu_device = 1 ctx_size = 65536 max_tokens = 8192 [prompt.knapp] system_prompt = Antworte kurz, präzise und technisch. [model.zweitmodell] model_path = models/qwen3/anderes-modell.gguf model_alias = zweit_llm container_name = llama_cpp_zweit host_port = 8002 gpu_device = 2 ``` --- ## 4. Die fünf Aktionen Genau **eine** Aktion pro Aufruf ist erforderlich. ### `--start` — Server starten Entfernt einen vorhandenen Container gleichen Namens, startet neu, wartet per echter Chat-Completion auf Readiness (nicht nur offener Port). Läuft unter Lock. ```bash llamacppctl --start llamacppctl --start --profile zweitmodell llamacppctl --start -p "Sag kurz Hallo." # + Testantwort nach dem Start llamacppctl --start --dry-run # nur das docker-run-Kommando zeigen ``` ### `--check` — Status prüfen Zeigt Container-/HTTP-Status; mit User-Prompt zusätzlich eine echte Chat-Probe. **Scriptbar:** Exit 0 = läuft & erreichbar, sonst 5. ```bash llamacppctl --check llamacppctl --check -p "ping" && echo "OK" || echo "nicht bereit" ``` ### `--stop` — Container stoppen und entfernen ```bash llamacppctl --stop llamacppctl --stop --force # auch bei Inkonsistenzen entfernen ``` ### `--change` — Modell/Config wechseln Stoppt, konfiguriert neu, startet — unter Lock. **Validiert den Modellpfad, bevor** der laufende Container entfernt wird (kein unnötiger Ausfall). ```bash llamacppctl --change --profile zweitmodell llamacppctl --change --model-path models/qwen3/neues-modell.gguf --ctx-size 32768 ``` ### `--chat` — an laufenden Server prompten ```bash llamacppctl --chat -p "Was ist 2+2?" llamacppctl --chat -s "Du antwortest nur mit Zahlen." -p "Wie viel ist 6*7?" llamacppctl --chat --stream -p "Schreibe eine kurze Geschichte." ``` --- ## 5. Prompts (System & User) Pro Feld genau **eine** Quelle: | System-Prompt | User-Prompt | Quelle | |---|---|---| | `-s`, `--system` | `-p`, `--prompt` | Literaler Text | | `--system-file` | `--prompt-file` | Lokale Datei (nur reguläre Dateien, UTF-8, Größenlimit) | | `--system-url` | `--prompt-url` | HTTPS-URL (SSRF-gehärtet) | ```bash llamacppctl --chat --system-file ./prompts/reviewer.md --prompt-file ./frage.txt llamacppctl --start --system-prompt-profile knapp -p "Erkläre Backups." ``` URL-Quellen sind standardmäßig HTTPS-only, blockieren private/loopback/metadata- IPs und Redirects; Details in `docs/SECURITY_AND_OPERATIONS.md`. --- ## 6. Antwortsteuerung & Reasoning - `--max-tokens N` — Budget der Antwort (Default 2048). - `--chat-temp F` — Temperatur des Requests (sonst gilt die Server-`temp`). - `--stream` — Tokens live auf stdout, während sie generiert werden. > **Reasoning-Modelle** (z. B. Qwen3.x mit `reasoning = on`) „denken" erst > intern (oft mehrere tausend Tokens) und geben **dann** die eigentliche > Antwort. Ist `--max-tokens` zu klein, endet die Generierung im Denken und der > sichtbare `content` bleibt **leer** — das Tool warnt dann > (`finish_reason=length`). Für lange Texte großzügig setzen (z. B. `--max-tokens 8000`). --- ## 7. Netzwerk & Sicherheit (Kurz) - Der Port wird per Default nur auf **`127.0.0.1`** veröffentlicht — die unauthentifizierte API ist **nicht** im LAN erreichbar. - Für LAN-Zugriff: `--expose`/`expose = true`. **Dann unbedingt** `--api-key`/`api_key` setzen. - Mit Key schützt llama.cpp `/v1/chat/completions` (ohne Key → 401); `/health` und `/v1/models` bleiben bewusst offen (für Health-Checks). - `hf_home` wird **read-only** gemountet; alle Docker-Aufrufe nutzen Argumentlisten (keine Shell-Injection). --- ## 8. GPU & Kontextgröße - Ein Modell muss samt KV-Cache in den GPU-Speicher passen. Der KV-Cache wächst linear mit `ctx_size` und wird beim Laden komplett allokiert. - Mehrere GPUs: `gpu_device = 1,2` (bzw. `--gpu 1,2`) verteilt das Modell. - Vor einer Änderung mit `--dry-run` das Kommando prüfen, mit `--print-effective-config` die aufgelösten Werte kontrollieren. --- ## 9. Exit-Codes | Code | Bedeutung | |---|---| | 0 | Erfolg (bzw. `--check`: läuft & erreichbar) | | 1 | Allgemeiner Fehler / leere Antwort trotz Budget | | 2 | CLI-/Argument-Fehler | | 3 | Modelldatei nicht gefunden | | 4 | Docker-Fehler | | 5 | HTTP/Readiness nicht bereit (auch `--check`, wenn nicht erreichbar) | | 6 | Lock belegt (anderer `--start`/`--change` läuft; ggf. `--force`) | --- ## 10. Fehlersuche | Symptom | Ursache | Lösung | |---|---|---| | `docker is not available on PATH` | Docker-CLI/-Daemon fehlt | Docker installieren/starten, Nutzer in `docker`-Gruppe | | `model file not found: …` | `model_path`/`hf_home` falsch | Pfade prüfen; `${HF_HOME}` gesetzt? | | `Server did not become ready within s` | Modell lädt langsam / OOM | `--timeout` erhöhen, `--logs` für Log-Tail, ggf. `ctx_size` senken | | Leere Antwort + Hinweis `finish_reason=length` | Reasoning verbraucht das Budget | `--max-tokens` erhöhen | | `HTTP 401: Invalid API Key` | Server verlangt Key | `--api-key`/`api_key` passend setzen | | `lock busy: …` | Paralleler `--start`/`--change` | warten oder `--force` (hängender Lock) | | `--check` zeigt `health=unhealthy`, Server antwortet aber | bildeigener Docker-Healthcheck ≠ HTTP-Probe | unkritisch: `HTTP: OK` + Exit 0 zählt | --- ## 11. Tests ```bash pip install -e ".[dev]" pytest -q # Unit-Tests (kein Docker nötig) ``` End-to-End gegen einen echten Server (opt-in, braucht Docker + GPU + Modell): ```bash SMOKE_GPU=1 scripts/smoke.sh ``` --- ## 12. Weiterführend - `man/llamacppctl.1` — vollständige Optionsreferenz (`man ./man/llamacppctl.1`). - `docs/SECURITY_AND_OPERATIONS.md` — Architektur, Sicherheits- und Betriebsmodell. - `README.md` — Kurzüberblick. - `INSTALL_FROM_ARCHIVE.md` — Installation aus dem `.tar.gz`-Archiv.