220 lines
9 KiB
Markdown
220 lines
9 KiB
Markdown
# llamacppctl
|
|
|
|
Ein einziges, sicherheitsgehärtetes CLI-Werkzeug zum Starten, Prüfen, Stoppen und
|
|
Wechseln von `llama.cpp`-Server-Instanzen, die als Docker-Container laufen —
|
|
inklusive einer SSRF-gehärteten Eingabeschicht für System- und User-Prompts aus
|
|
Text, Datei oder Remote-URL.
|
|
|
|
`llamacppctl` ersetzt eine Sammlung einzelner Shell-Skripte
|
|
(`start-llm-server.sh`, `status-llm-server.sh`, `switch-llm.sh`, …) durch ein
|
|
einziges Python-Paket mit konsistenter Konfiguration, Locking und
|
|
Fehlerbehandlung.
|
|
|
|
## Dokumentation
|
|
|
|
| Dokument | Für wen / wofür |
|
|
|---|---|
|
|
| README (diese Datei) | Schnelleinstieg: Installation, Konfiguration, Verwendung |
|
|
| [`docs/BEDIENUNGSANLEITUNG.md`](docs/BEDIENUNGSANLEITUNG.md) | Vollständiges Benutzerhandbuch (alle Aktionen, Optionen, Fehlersuche) |
|
|
| [`docs/INSTALL_FROM_ARCHIVE.md`](docs/INSTALL_FROM_ARCHIVE.md) | Installation aus dem `.tar.gz`-Archiv + Archiv selbst bauen/verifizieren |
|
|
| [`docs/SECURITY_AND_OPERATIONS.md`](docs/SECURITY_AND_OPERATIONS.md) | Architektur, Modul-/Repo-Übersicht, Sicherheits- & Betriebsmodell |
|
|
| `man/llamacppctl.1` | Vollständige Optionsreferenz (`man llamacppctl` bzw. `man ./man/llamacppctl.1`) |
|
|
| [`CHANGELOG.md`](CHANGELOG.md) | Versionshistorie |
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
tar xzf llamacppctl-installable.tar.gz
|
|
cd llamacppctl
|
|
python3 -m venv .venv
|
|
source .venv/bin/activate
|
|
pip install .
|
|
```
|
|
|
|
Danach steht der Befehl `llamacppctl` innerhalb der aktivierten virtuellen
|
|
Umgebung zur Verfügung. Für eine systemweite Installation (ohne venv):
|
|
|
|
```bash
|
|
pip install --user .
|
|
```
|
|
|
|
Voraussetzungen auf dem Zielsystem:
|
|
|
|
- Python >= 3.10
|
|
- Docker (CLI + laufender Daemon), für `--start`/--check`/--stop`/--change`
|
|
- Netzwerkzugriff auf den Container-Host-Port für `--chat`/--check`
|
|
|
|
`--print-effective-config` und `--dry-run` benötigen keinen laufenden Docker-Daemon
|
|
für die reine Konfigurationsprüfung; `--start`/--check`/--stop`/--change` erfordern
|
|
einen erreichbaren Docker-Daemon.
|
|
|
|
## Konfiguration
|
|
|
|
Kopiere `llama.cpp.config.example` nach `llama.cpp.config` und passe es an:
|
|
|
|
```bash
|
|
cp llama.cpp.config.example llama.cpp.config
|
|
```
|
|
|
|
Abschnittstypen:
|
|
|
|
- `[default]` — globale Defaults, überschreiben die eingebauten Fallback-Werte
|
|
- `[model.<name>]` — ein Modell-Profil, ausgewählt über `--profile <name>`
|
|
- `[prompt.<name>]` — ein System-Prompt-Profil, ausgewählt über
|
|
`--system-prompt-profile <name>` (liefert nur einen *Fallback*-Systemprompt;
|
|
ein explizites `-s/--system-file/--system-url` gewinnt immer)
|
|
|
|
**`container_name` ist Pflicht** und muss für jedes Profil, das parallel
|
|
laufen soll, eindeutig sein — er ist der einzige Identitätsanker für
|
|
Docker-Namensgebung, Locking (`--start`/`--change`) sowie
|
|
`--stop`/`--check`-Ziel.
|
|
|
|
`hf_home` unterstützt Environment-Variablen und `~`, z. B. `hf_home = ${HF_HOME}`.
|
|
|
|
Das Chat-Antwortbudget ist ebenfalls konfigurierbar (wirkt auf `--chat` und die
|
|
Antwort nach `--start`, nicht auf den Container): `max_tokens` (Default 2048 —
|
|
für Reasoning-Modelle / lange Texte hochsetzen, sonst bricht die Antwort mitten
|
|
im „Denken" ab und `content` bleibt leer) und `chat_temperature` (leer =
|
|
Server-`temp` gilt). CLI-Overrides: `--max-tokens`, `--chat-temp`.
|
|
|
|
Auflösungsreihenfolge (niedrigste zu höchster Priorität):
|
|
|
|
1. Eingebaute Defaults
|
|
2. `[default]`-Sektion
|
|
3. `[model.<profile>]`-Sektion (falls `--profile` gesetzt)
|
|
4. CLI-Overrides (`--image`, `--host-port`, …)
|
|
|
|
## Verwendung
|
|
|
|
```bash
|
|
# Server starten (nutzt die [default]-Sektion der Config)
|
|
llamacppctl --start --config llama.cpp.config
|
|
|
|
# Nur den geplanten docker-run-Befehl anzeigen, nichts ausführen
|
|
llamacppctl --start --config llama.cpp.config --dry-run
|
|
|
|
# Effektive Konfiguration als JSON ausgeben
|
|
llamacppctl --print-effective-config --config llama.cpp.config --start
|
|
|
|
# Status prüfen
|
|
llamacppctl --check --config llama.cpp.config
|
|
|
|
# Server stoppen und Container entfernen
|
|
llamacppctl --stop --config llama.cpp.config
|
|
|
|
# Ein bestimmtes Modell-Profil starten (siehe Hinweis unter dem Block)
|
|
llamacppctl --start --config llama.cpp.config --profile qwopus
|
|
|
|
# Modell wechseln (stop, neu konfigurieren, restart) unter Lock-Schutz
|
|
llamacppctl --change --config llama.cpp.config --profile carnice
|
|
|
|
# Direkten Chat-Request an einen laufenden Server senden
|
|
llamacppctl --chat --config llama.cpp.config \
|
|
-s "Du antwortest kurz." -p "Was ist 2+2?"
|
|
|
|
# Chat mit Live-Streaming und großem Antwortbudget (lange Texte)
|
|
llamacppctl --chat --config llama.cpp.config \
|
|
--stream --max-tokens 8000 -p "Schreibe eine kurze Rede zum Jahreswechsel."
|
|
|
|
# System-Prompt aus einer lokalen Datei, User-Prompt als Literal
|
|
llamacppctl --start --config llama.cpp.config \
|
|
--system-file ./prompts/coding.md -p "Refaktoriere diese Funktion."
|
|
|
|
# System-Prompt von einer HTTPS-URL (öffentliches, nicht-privates Ziel)
|
|
llamacppctl --chat --config llama.cpp.config \
|
|
--system-url https://example.com/prompts/system.txt -p "Hallo"
|
|
```
|
|
|
|
Ohne `--profile` gilt die `[default]`-Sektion. Mit `--profile <name>` wählst du
|
|
ein `[model.<name>]`-Profil; in `llama.cpp.config` sind das z. B. `carnice`,
|
|
`qwen27b`, `qwopus`, in der Vorlage `llama.cpp.config.example` `qwen35b` und
|
|
`deepseek`. Die Ziffern (z. B. `35b`) meinen die Modellgröße in Milliarden
|
|
Parametern, nicht die Qwen-Version.
|
|
|
|
Vollständige Optionsliste: `llamacppctl --help` oder die Manpage
|
|
(`man/llamacppctl.1`, siehe unten).
|
|
|
|
## Migration von den Shell-Skripten
|
|
|
|
`llamacppctl` ersetzt die einzelnen Shell-Skripte durch Actions **eines**
|
|
Werkzeugs (`--profile` ist optional; ohne greift die `[default]`-Sektion):
|
|
|
|
| Altes Skript | Neuer Aufruf |
|
|
|---|---|
|
|
| `start-llm-server.sh` | `llamacppctl --start --config llama.cpp.config [--profile <name>]` |
|
|
| `status-llm-server.sh` | `llamacppctl --check --config llama.cpp.config [--profile <name>]` |
|
|
| Stop-Skript | `llamacppctl --stop --config llama.cpp.config [--profile <name>]` |
|
|
| `switch-llm.sh` (Modellwechsel) | `llamacppctl --change --config llama.cpp.config [--profile <name>]` |
|
|
| (neu) direkter Chat-Test | `llamacppctl --chat --config llama.cpp.config -p "..."` |
|
|
|
|
## Sicherheitsmodell (Kurzfassung)
|
|
|
|
Für Details siehe [`docs/SECURITY_AND_OPERATIONS.md`](docs/SECURITY_AND_OPERATIONS.md).
|
|
|
|
- Alle Docker-Aufrufe laufen über `subprocess.run()` mit Argumentlisten —
|
|
niemals über Shell-Strings, somit keine Shell-Injection-Fläche.
|
|
- `--system-file`/--prompt-file` lesen ausschließlich reguläre Dateien,
|
|
Größenlimit (`--max-input-bytes`, Default 1 MiB), UTF-8-Text erzwungen,
|
|
Symlinks standardmäßig abgelehnt, per `--allow-symlinks` freischaltbar.
|
|
- `--system-url`/--prompt-url` sind standardmäßig HTTPS-only, blockieren
|
|
IP-Literale, private/loopback/link-local/reserved Adressbereiche sowie die
|
|
Cloud-Metadata-IP `169.254.169.254`, verbieten eingebettete Zugangsdaten in
|
|
der URL, erlauben standardmäßig keine Redirects (jeder Hop wird bei
|
|
Aktivierung erneut validiert), erzwingen eine Content-Type-Allowlist und ein
|
|
hartes Größenlimit sowohl über `Content-Length` als auch beim Streaming.
|
|
- Der Port wird standardmäßig **nur auf `127.0.0.1`** veröffentlicht — der
|
|
ungeauthentifizierte OpenAI-Endpoint ist also nicht im LAN erreichbar.
|
|
`--expose`/`expose = true` bindet auf alle Interfaces; dann sollte per
|
|
`--api-key`/`api_key` ein Schlüssel gesetzt werden (Server verlangt ihn, das
|
|
Tool sendet ihn als Bearer-Token).
|
|
- `container_name` ist verpflichtend und muss pro parallel betriebenem
|
|
Profil eindeutig sein, um Docker-Namenskollisionen und ungewollte
|
|
Container-Übernahmen zu verhindern.
|
|
- `--start`/--change` nutzen echte Chat-Completions zum Readiness-Check
|
|
(nicht nur einen offenen Port), um sicherzustellen, dass das Modell
|
|
tatsächlich geladen und inferenzfähig ist.
|
|
- `--change` verwendet eine exklusive, nicht-blockierende Dateisperre
|
|
(`/tmp/llamacppctl.<container_name>.lock`), um parallele Änderungen am
|
|
selben Container zu verhindern.
|
|
|
|
## Entwicklung / Tests
|
|
|
|
```bash
|
|
pip install -e ".[dev]"
|
|
python -m pytest -q
|
|
```
|
|
|
|
`python -m pytest` (statt des `pytest`-Skripts) legt das Repo-Root auf
|
|
`sys.path`, damit die Testmodule `import tests.*` auflösen.
|
|
|
|
**Lokales CI-Gate:** `scripts/check.sh` bündelt `ruff` + `mypy` + `pytest`
|
|
(spiegelt `.forgejo/workflows/ci.yml`). Als Pre-Push-Hook aktivieren — er
|
|
blockt einen Push bei Fehlern:
|
|
|
|
```bash
|
|
git config core.hooksPath .githooks # einmalig pro Clone
|
|
```
|
|
|
|
Einen einzelnen Push im Notfall umgehen: `git push --no-verify`.
|
|
|
|
Die Test-Suite deckt die Prompt-Eingabeschicht (Datei- und URL-Quellen inkl.
|
|
SSRF-Abwehr mit gemockter DNS-Auflösung und DNS-Pinning), die
|
|
Konfigurationsauflösung (inkl. `${ENV}`-Expansion), die CLI-Validierung, die
|
|
Action-Orchestrierung (`do_start`/`do_change`/`do_check`/`do_chat`, Locking,
|
|
dry-run) sowie Docker-/HTTP-Operationen (mit gemocktem `subprocess` bzw.
|
|
`requests`) ab.
|
|
|
|
Für einen End-to-End-Test gegen einen **echten** Docker + llama.cpp-Server (u. a.
|
|
`--api-key`-Round-Trip, Streaming, `--check`-Exit-Codes) gibt es einen opt-in
|
|
Rauchtest — er startet einen separaten Container und räumt danach auf:
|
|
|
|
```bash
|
|
SMOKE_GPU=1 scripts/smoke.sh
|
|
```
|
|
|
|
## Lizenz
|
|
|
|
MIT License
|
|
|
|
Copyright (c) 2026 Dieter Schlüter
|
|
|