Sieben Verbesserungen; die Dateien überschneiden sich thematisch, daher ein Commit (jeder Commit bleibt grün: 115 Tests). - #1 --check ist scriptbar: Exit 0 wenn Container läuft und erreichbar, sonst 5 (check_exit_code / CheckResult). - #2 --force implementiert: Bypass eines belegten Locks mit Warnung (_container_lock) und stop_container(force=…) schluckt Inkonsistenzen. - #3 stille Trunkierung behoben: chat_completion_text liefert ChatReply (content + finish_reason); bei finish_reason=length Hinweis auf stderr, --chat gibt Exit 1 bei leerem Content zurück. - #4 keine vermeidbare Downtime: --change validiert den Modellpfad VOR dem Entfernen des laufenden Containers. - #5 Netzwerk dicht: Port-Publish standardmäßig nur auf 127.0.0.1 (--expose/expose für alle Interfaces), optionaler --api-key/api_key (Server --api-key + Bearer-Token auf allen Requests). - #6 --stream: Chat-Reply token-weise via SSE auf stdout (stream_chat). - #7 tests/test_actions.py: Orchestrierungs-Ebene (dry-run-Nebenwirkungen, Lock, validate-before-remove, chat/stream/exit-codes). Doku aktualisiert (Manpage, README, llama.cpp.config.example). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
161 lines
6.3 KiB
Markdown
161 lines
6.3 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.
|
|
|
|
## 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
|
|
llamacppctl --start --config llama.cpp.config --profile qwen35
|
|
|
|
# Nur den geplanten docker-run-Befehl anzeigen, nichts ausführen
|
|
llamacppctl --start --config llama.cpp.config --profile qwen35 --dry-run
|
|
|
|
# Effektive Konfiguration als JSON ausgeben
|
|
llamacppctl --print-effective-config --config llama.cpp.config --profile qwen35 --start
|
|
|
|
# Status prüfen
|
|
llamacppctl --check --config llama.cpp.config --profile qwen35
|
|
|
|
# Server stoppen und Container entfernen
|
|
llamacppctl --stop --config llama.cpp.config --profile qwen35
|
|
|
|
# Modell wechseln (stop, neu konfigurieren, restart) unter Lock-Schutz
|
|
llamacppctl --change --config llama.cpp.config --profile deepseek
|
|
|
|
# Direkten Chat-Request an einen laufenden Server senden
|
|
llamacppctl --chat --config llama.cpp.config --profile qwen35 \
|
|
-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 --profile qwen35 \
|
|
--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 --profile qwen35 \
|
|
--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 --profile qwen35 \
|
|
--system-url https://example.com/prompts/system.txt -p "Hallo"
|
|
```
|
|
|
|
Vollständige Optionsliste: `llamacppctl --help` oder die Manpage
|
|
(`man/llamacppctl.1`, siehe unten).
|
|
|
|
## 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]"
|
|
pytest -q
|
|
```
|
|
|
|
Die Test-Suite deckt die Prompt-Eingabeschicht (Datei- und URL-Quellen inkl.
|
|
SSRF-Abwehr mit gemockter DNS-Auflösung), die Konfigurationsauflösung, die
|
|
CLI-Validierung sowie Docker-/HTTP-Operationen (mit gemocktem `subprocess`
|
|
bzw. `requests`) ab.
|
|
|
|
## Lizenz
|
|
|
|
MIT
|