diff --git a/BEDIENUNGSANLEITUNG.md b/BEDIENUNGSANLEITUNG.md new file mode 100644 index 0000000..b22bffe --- /dev/null +++ b/BEDIENUNGSANLEITUNG.md @@ -0,0 +1,283 @@ +# 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. diff --git a/INSTALL_FROM_ARCHIVE.md b/INSTALL_FROM_ARCHIVE.md new file mode 100644 index 0000000..2cfc12f --- /dev/null +++ b/INSTALL_FROM_ARCHIVE.md @@ -0,0 +1,114 @@ +# Installation aus dem Archiv — llamacppctl + +Diese Anleitung beschreibt, wie du `llamacppctl` aus dem fertigen Archiv +`llamacppctl-installable.tar.gz` installierst. Für die vollständige Bedienung +siehe [`BEDIENUNGSANLEITUNG.md`](BEDIENUNGSANLEITUNG.md). + +--- + +## 1. Voraussetzungen auf der Zielmaschine + +- **Python ≥ 3.10** (`python3 --version`) +- **Docker** mit erreichbarem Daemon (`docker version`) +- **NVIDIA-GPU** + Treiber + **NVIDIA Container Toolkit** (für `--gpus`) +- Ein **GGUF-Modell** auf dem Host + +> Für einen reinen Konfigurations-/Trockenlauf (`--print-effective-config`, +> `--dry-run`) reicht Python; Docker/GPU/Modell werden erst für echte +> `--start`/`--check`/`--chat`-Aufrufe gebraucht. + +--- + +## 2. Integrität prüfen (optional, empfohlen) + +```bash +gzip -t llamacppctl-installable.tar.gz && echo "Archiv OK" +tar tzf llamacppctl-installable.tar.gz | head # Inhalt ansehen +``` + +Das Archiv enthält ausschließlich Quellcode, Tests, Doku, Manpage, +`llama.cpp.config.example` und `scripts/smoke.sh` — **keine** persönliche +`llama.cpp.config`, keine `.venv`, keine `.git`-Historie und keine Geheimnisse. + +--- + +## 3. Entpacken und installieren + +```bash +tar xzf llamacppctl-installable.tar.gz +cd llamacppctl + +python3 -m venv .venv +source .venv/bin/activate +pip install . +``` + +Danach steht das Konsolen-Kommando `llamacppctl` zur Verfügung: + +```bash +llamacppctl --help +``` + +> Zum Entwickeln stattdessen `pip install -e ".[dev]"` (editable + Tests). + +--- + +## 4. Konfiguration anlegen + +```bash +cp llama.cpp.config.example llama.cpp.config +# llama.cpp.config an den Host anpassen: hf_home (z. B. ${HF_HOME}), +# model_path, gpu_device, host_port, container_name … +``` + +Auflösung prüfen, ohne etwas zu starten: + +```bash +llamacppctl --print-effective-config --config llama.cpp.config --start --dry-run +``` + +Das zeigt die vollständig aufgelöste Konfiguration als JSON **und** das +`docker run`-Kommando, das `--start` ausführen würde. + +--- + +## 5. Erststart + +```bash +llamacppctl --start --config llama.cpp.config +llamacppctl --check --config llama.cpp.config +llamacppctl --chat --config llama.cpp.config -p "Sag kurz Hallo." +``` + +Weitere Aktionen, Optionen und Fehlersuche: [`BEDIENUNGSANLEITUNG.md`](BEDIENUNGSANLEITUNG.md). + +--- + +## 6. Tests (optional) + +```bash +pip install -e ".[dev]" +pytest -q +``` + +--- + +## 7. Archiv selbst neu bauen / verifizieren + +Das Archiv wird reproduzierbar von `build_archive.py` erzeugt. Es + +1. prüft, ob alle erforderlichen Projektdateien vorhanden sind, +2. gleicht die deklarierten Abhängigkeiten in einer frischen venv ab, +3. lässt die komplette Test-Suite laufen, +4. baut das `.tar.gz` (nur Allowlist: `src/ tests/ docs/ man/ scripts/` + `pyproject.toml`, `README.md`, `requirements*.txt`, `llama.cpp.config.example`, `build_archive.py`, `BEDIENUNGSANLEITUNG.md`, `INSTALL_FROM_ARCHIVE.md`), +5. öffnet das Archiv erneut und verifiziert die enthaltenen Dateien, +6. installiert das Paket aus dem Archiv in einer weiteren frischen venv und testet das Konsolenskript. + +```bash +python3 build_archive.py # -> ../llamacppctl-installable.tar.gz +python3 build_archive.py --output /pfad/x.tar.gz +python3 build_archive.py --skip-pip-check --skip-smoke-test # ohne Netz-Schritte +``` + +Weil `build_archive.py` selbst im Archiv liegt, lässt sich das Archiv jederzeit +unabhängig neu bauen und verifizieren. diff --git a/build_archive.py b/build_archive.py index c733e07..805186e 100644 --- a/build_archive.py +++ b/build_archive.py @@ -72,6 +72,8 @@ REQUIRED_FILES = [ "tests/test_actions.py", "docs/How_to_use.md", "scripts/smoke.sh", + "BEDIENUNGSANLEITUNG.md", + "INSTALL_FROM_ARCHIVE.md", ] # Top-level directories to include wholesale (in addition to REQUIRED_FILES), @@ -85,6 +87,8 @@ INCLUDE_FILES = [ "requirements-dev.txt", "llama.cpp.config.example", "build_archive.py", + "BEDIENUNGSANLEITUNG.md", + "INSTALL_FROM_ARCHIVE.md", ] EXCLUDE_DIR_NAMES = {"__pycache__", ".pytest_cache", ".venv", "venv", ".git", "*.egg-info"}