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.
  • Python 94.4%
  • Shell 5.6%
Find a file
dschlueter a88c427ba9 config: model profiles inherit the standard port/container
Replace the stale example profiles (qwen35->8002, deepseek->8003, pointing at
non-existent paths) with profiles for the actually installed models. They now
override ONLY model_path and inherit host_port=8001, container_name and alias
from [default] -> one model at a time on the standard port; --start/--change
swaps it. Distinct port/container is only needed for concurrent operation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 18:47:50 +02:00
docs docs: bring docs/ and build_archive.py up to date 2026-07-06 17:32:46 +02:00
man feat: harden and extend the CLI (security, UX, robustness, tests) 2026-07-06 16:57:47 +02:00
scripts test(smoke): probe a protected endpoint for the api-key check 2026-07-06 17:25:41 +02:00
src/llamacppctl fix(http): force UTF-8 decoding on the SSE chat stream 2026-07-06 18:47:50 +02:00
tests fix(http): force UTF-8 decoding on the SSE chat stream 2026-07-06 18:47:50 +02:00
.gitignore Initial commit: llamacppctl – llama.cpp Docker server control CLI 2026-07-06 16:36:27 +02:00
BEDIENUNGSANLEITUNG.md docs: add BEDIENUNGSANLEITUNG.md and INSTALL_FROM_ARCHIVE.md 2026-07-06 17:48:39 +02:00
build_archive.py docs: add BEDIENUNGSANLEITUNG.md and INSTALL_FROM_ARCHIVE.md 2026-07-06 17:48:39 +02:00
INSTALL_FROM_ARCHIVE.md docs: add BEDIENUNGSANLEITUNG.md and INSTALL_FROM_ARCHIVE.md 2026-07-06 17:48:39 +02:00
llama.cpp.config config: model profiles inherit the standard port/container 2026-07-06 18:47:50 +02:00
llama.cpp.config.example feat: harden and extend the CLI (security, UX, robustness, tests) 2026-07-06 16:57:47 +02:00
pyproject.toml Initial commit: llamacppctl – llama.cpp Docker server control CLI 2026-07-06 16:36:27 +02:00
README.md test: close coverage gaps + opt-in integration smoke 2026-07-06 17:08:10 +02:00
requirements-dev.txt Initial commit: llamacppctl – llama.cpp Docker server control CLI 2026-07-06 16:36:27 +02:00
requirements.txt Initial commit: llamacppctl – llama.cpp Docker server control CLI 2026-07-06 16:36:27 +02:00

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

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):

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:

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

# 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.

  • 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-urlsind standardmäßig HTTPS-only, blockieren IP-Literale, private/loopback/link-local/reserved Adressbereiche sowie die Cloud-Metadata-IP169.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

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 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:

SMOKE_GPU=1 scripts/smoke.sh

Lizenz

MIT