Initial commit: llamacppctl – llama.cpp Docker server control CLI
Steuert einen llama.cpp-Server als Docker-Container: --start/--check/--stop/
--change/--chat, INI-Konfiguration (builtin defaults -> [default] ->
[model.<profile>] -> CLI), SSRF-gehärtete Prompt-Eingabe (Datei/HTTPS-URL),
File-Locking für --start/--change und ein OpenAI-kompatibler HTTP-Layer.
Enthält u. a.:
- Env-Var-Expansion in hf_home (hf_home = ${HF_HOME})
- konfigurierbares Chat-Antwortbudget (max_tokens/chat_temperature,
CLI: --max-tokens/--chat-temp); temperature defer an Server-Default
- DNS-Pinning gegen DNS-Rebinding bei URL-Quellen
- dry-run als nebenwirkungsfreie Vorschau (kein Lock/Removal/Modell-Check)
- 98 Tests (pytest)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
commit
3158d16f9b
32 changed files with 3912 additions and 0 deletions
53
docs/Archiv_fertig_-_Check_und_Installation.md
Normal file
53
docs/Archiv_fertig_-_Check_und_Installation.md
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
Das Archiv ist fertig und übergeben: llamacppctl-installable.tar.gz.
|
||||
|
||||
Was gebaut wurde
|
||||
|
||||
Statt manuell zu tarballen, gibt es jetzt ein eigenständiges Python-Skript build_archive.py, das:
|
||||
|
||||
Prüft, ob alle 27 erforderlichen Projektdateien vorhanden sind (Quellcode, Tests, Doku, Manpage, Config-Beispiel)
|
||||
|
||||
pyproject.toml-Abhängigkeiten gegen requirements.txt abgleicht
|
||||
|
||||
Die deklarierte Abhängigkeit (requests>=2.31,<3) in einer frischen, isolierten virtuellen Umgebung installiert, um echte Installierbarkeit zu bestätigen (nicht nur einen Trockenlauf)
|
||||
|
||||
Die vollständige Test-Suite (93 Tests, alle grün) laufen lässt
|
||||
|
||||
Das .tar.gz über Pythons tarfile-Modul baut (nicht rohes Shell-tar)
|
||||
|
||||
Das Archiv erneut öffnet und verifiziert, dass jede erwartete Datei tatsächlich enthalten ist
|
||||
|
||||
Das Archiv in ein temporäres Verzeichnis entpackt, in einer weiteren frischen venv installiert und den llamacppctl-Befehl ausführt, um Konsolenskript und Konfigurationsauflösung Ende-zu-Ende zu bestätigen
|
||||
|
||||
Da diese Sandbox keinen Docker-Daemon hat, bricht der letzte Schritt erwartungsgemäß an der Docker-Verfügbarkeitsprüfung ab — das Skript erkennt genau diesen Fall und wertet ihn nicht als Fehler, da Paketinstallation, Konsolenskript-Registrierung und Argumentparsing bereits vorher erfolgreich bestätigt wurden.
|
||||
|
||||
Enthaltene Projektstruktur
|
||||
|
||||
src/llamacppctl/ — die vollständige Implementierung: cli.py, config.py, prompt_io.py (SSRF-gehärtete Eingabeschicht), docker_ops.py, http_ops.py, lock_ops.py, actions.py, main.py
|
||||
|
||||
tests/ — 93 Tests (Prompt-Quellen, Datei-/URL-Sicherheit mit gemockter DNS-Auflösung, Konfigurationsauflösung, CLI-Validierung, Docker-/HTTP-Operationen mit gemocktem subprocess/requests, File-Locking)
|
||||
|
||||
docs/SECURITY_AND_OPERATIONS.md — vollständige Architektur-, Konfigurations- und Sicherheitsdokumentation
|
||||
|
||||
man/llamacppctl.1 — klassische Unix-Manpage
|
||||
|
||||
README.md, llama.cpp.config.example, requirements.txt, pyproject.toml
|
||||
|
||||
Selbst verifizieren
|
||||
|
||||
Nach dem Herunterladen kannst du die Integrität unabhängig prüfen, ohne mir zu vertrauen:
|
||||
|
||||
bash
|
||||
gzip -t llamacppctl-installable.tar.gz && echo "Archiv OK"
|
||||
tar tzf llamacppctl-installable.tar.gz
|
||||
|
||||
Installation:
|
||||
|
||||
bash
|
||||
tar xzf llamacppctl-installable.tar.gz
|
||||
cd llamacppctl
|
||||
python3 -m venv .venv && source .venv/bin/activate
|
||||
pip install .
|
||||
cp llama.cpp.config.example llama.cpp.config # anpassen
|
||||
llamacppctl --print-effective-config --config llama.cpp.config --profile qwen35 --start
|
||||
|
||||
Da build_archive.py selbst mit im Archiv liegt, kannst du das Archiv auf deinem eigenen Rechner jederzeit neu bauen und erneut verifizieren, ganz ohne auf mich angewiesen zu sein.
|
||||
24
docs/How_to_use.md
Normal file
24
docs/How_to_use.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
### Ist das start- stop- change-Skript etc auch dabei ?
|
||||
|
||||
Ja — aber als ein einziges vereinheitlichtes CLI-Tool statt separater Shell-Skripte. Das war ja genau der Punkt der Neuentwicklung: `llamacppctl` ersetzt die einzelnen Skripte (`start-llm-server.sh`, `status-llm-server.sh`, `switch-llm.sh`, …) durch **eine** Python-Anwendung mit Unterbefehlen (Actions).
|
||||
|
||||
Alle Funktionen sind im Archiv enthalten, als Actions von `llamacppctl`:
|
||||
|
||||
| Alte Skript-Idee | 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 --profile <name> -p "..."` |
|
||||
|
||||
Die Logik dahinter liegt in `src/llamacppctl/actions.py` (`do_start`, `do_check`, `do_stop`, `do_change`, `do_chat`) und `src/llamacppctl/docker_ops.py` (baut den `docker run`-Befehl, startet/stoppt/entfernt Container, liest Logs).
|
||||
|
||||
Besonderheiten gegenüber den ursprünglichen Shell-Skripten:
|
||||
|
||||
- `--change` nutzt eine exklusive Dateisperre (`/tmp/llamacppctl.<container_name>.lock`), damit zwei gleichzeitige Modellwechsel für denselben Container sich nicht in die Quere kommen.
|
||||
- Die Start-Bereitschaftsprüfung (`--start`/`--change`) wartet auf eine echte Chat-Completion-Antwort, nicht nur auf einen offenen Port — das ist zuverlässiger als ein reiner Port-Check.
|
||||
- `--dry-run` zeigt dir den vollständigen `docker run`-Befehl an, ohne ihn auszuführen — praktisch, um vor einer echten Änderung zu prüfen, was passieren würde.
|
||||
|
||||
Die vollständige Referenz zu allen Optionen steht in der Manpage (`man/llamacppctl.1`) und in `README.md`/`docs/SECURITY_AND_OPERATIONS.md` im Archiv.
|
||||
|
||||
255
docs/SECURITY_AND_OPERATIONS.md
Normal file
255
docs/SECURITY_AND_OPERATIONS.md
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
# llamacppctl — Sicherheits- und Betriebsdokumentation
|
||||
|
||||
Diese Datei beschreibt die Architektur, das Konfigurationsmodell, das
|
||||
Sicherheitsmodell der Prompt-Eingabeschicht sowie den empfohlenen
|
||||
Betriebsmodus auf einem dedizierten GPU-Host.
|
||||
|
||||
## 1. Architektur / Modulübersicht
|
||||
|
||||
```
|
||||
src/llamacppctl/
|
||||
cli.py Argumentparsing (argparse) + semantische Cross-Field-Validierung
|
||||
schema.py Dataclasses: ServerConfig, PromptConfig, CheckResult
|
||||
config.py INI-Konfiguration laden/mergen -> ServerConfig/PromptConfig
|
||||
prompt_io.py Sicherheitsgehärtete Eingabeschicht für System-/User-Prompts
|
||||
docker_ops.py Alle Docker-Interaktionen (subprocess mit Argumentlisten)
|
||||
http_ops.py HTTP-Health-Checks und Chat-Completions gegen die
|
||||
OpenAI-kompatible llama.cpp-API
|
||||
lock_ops.py Exklusives, nicht-blockierendes File-Locking für --change
|
||||
actions.py Orchestrierung: do_start, do_check, do_stop, do_change, do_chat
|
||||
main.py Dünner Einstiegspunkt: parse -> validate -> resolve -> dispatch
|
||||
```
|
||||
|
||||
Verantwortungstrennung: `prompt_io.py` kennt kein argparse und arbeitet nur
|
||||
mit `PromptSource` + `InputPolicy`. `config.py` kennt keine Docker- oder
|
||||
HTTP-Details. `docker_ops.py`/`http_ops.py` kennen keine CLI-Semantik. Diese
|
||||
Trennung hält die sicherheitsrelevante Logik (Eingabevalidierung) unabhängig
|
||||
testbar von der Orchestrierung.
|
||||
|
||||
## 2. Konfigurationsmodell
|
||||
|
||||
`llama.cpp.config` ist eine Standard-INI-Datei (`configparser`). Auflösung
|
||||
(niedrigste zu höchster Priorität):
|
||||
|
||||
1. **Eingebaute Defaults** (`config.builtin_defaults()`) — spiegeln die
|
||||
Standardwerte der ursprünglichen Shell-Skripte wider (Image
|
||||
`ghcr.io/ggml-org/llama.cpp:server-cuda`, `host_port=8001`,
|
||||
`container_name=va_llm`, `gpu_device=1`, `jinja/fa/kv_unified/
|
||||
cont_batching/no_context_shift=true`, `reasoning=on`,
|
||||
`cache_type_k/v=q4_0`, `batch_size=1024`, `ubatch_size=512`,
|
||||
`timeout=300`, `poll_interval=2`, …).
|
||||
2. **`[default]`**-Sektion — überschreibt die eingebauten Defaults global.
|
||||
3. **`[model.<profile>]`**-Sektion — ausgewählt über `--profile <name>`,
|
||||
überschreibt `[default]` selektiv (nur gesetzte Schlüssel).
|
||||
4. **CLI-Overrides** — z. B. `--image`, `--host-port`, `--container-name`,
|
||||
`--ctx-size`, … überschreiben alles Vorherige.
|
||||
|
||||
`[prompt.<name>]`-Sektionen werden **separat** aufgelöst und liefern
|
||||
ausschließlich einen *Fallback*-Systemprompt (`system_prompt = ...`). Ein
|
||||
explizit übergebener `-s/--system`, `--system-file` oder `--system-url`
|
||||
gewinnt in jedem Fall.
|
||||
|
||||
### `container_name` ist verpflichtend
|
||||
|
||||
`config.build_server_config()` wirft einen `ConfigError`, wenn
|
||||
`container_name` nach vollständiger Auflösung leer oder nur aus Whitespace
|
||||
besteht. Grund: `container_name` ist der **einzige Identitätsanker** für:
|
||||
|
||||
- Docker-Container-Benennung (`docker run --name <container_name>`)
|
||||
- Ziel für `--stop`/--check`
|
||||
- Ableitung des Lock-Pfads (`/tmp/llamacppctl.<container_name>.lock`),
|
||||
sofern `--lock-file`/`lock_file` nicht explizit gesetzt ist
|
||||
|
||||
Wer mehrere `[model.<name>]`-Profile definiert, die **gleichzeitig** als
|
||||
separate Container laufen sollen (z. B. zwei Modelle auf zwei GPUs), **muss**
|
||||
jedem Profil einen eigenen, eindeutigen `container_name` geben. Andernfalls
|
||||
würde ein zweiter `--start` den ersten Container unbeabsichtigt ersetzen
|
||||
(`start_llama_container()` entfernt einen vorhandenen Container gleichen
|
||||
Namens, bevor es einen neuen startet).
|
||||
|
||||
Eine leere `--container-name`-Angabe auf der Kommandozeile wird bereits von
|
||||
`cli.validate_args()` zurückgewiesen (Exit-Code 2, bevor überhaupt eine
|
||||
Konfigurationsdatei gelesen wird). Eine leere Angabe **in der Config-Datei**
|
||||
selbst (z. B. `container_name =` ohne Wert) wird erst bei der
|
||||
Konfigurationsauflösung als `ConfigError` erkannt.
|
||||
|
||||
## 3. Sicherheitsmodell der Prompt-Eingabeschicht (`prompt_io.py`)
|
||||
|
||||
Jedes der beiden Eingabefelder (System-Prompt, User-Prompt) akzeptiert genau
|
||||
**eine** von drei Quellenarten, gegenseitig ausschließend über
|
||||
argparse-Mutually-Exclusive-Groups **und** eine zweite Verteidigungslinie in
|
||||
`resolve_source()`:
|
||||
|
||||
- **Literal** (`-s`/`-p`): Text direkt auf der Kommandozeile.
|
||||
- **Datei** (`--system-file`/--prompt-file`): lokaler Dateipfad.
|
||||
- **URL** (`--system-url`/--prompt-url`): entfernte HTTPS(S)-Ressource.
|
||||
|
||||
### 3.1 Datei-Quellen
|
||||
|
||||
`load_text_file()`:
|
||||
|
||||
- Nur reguläre Dateien (`path.is_file()`); Verzeichnisse etc. werden
|
||||
abgelehnt.
|
||||
- Symlinks werden standardmäßig abgelehnt und sind nur über `--allow-symlinks`
|
||||
freischaltbar (`InputPolicy.allow_symlinks`, Default `False`).
|
||||
- Größenlimit `--max-input-bytes` (Default 1 MiB) wird **vor** dem Lesen
|
||||
anhand von `stat().st_size` und **nach** der Normalisierung anhand der
|
||||
UTF-8-kodierten Zeichenlänge geprüft.
|
||||
- Binärinhalte (NUL-Byte in den Rohbytes) werden abgelehnt.
|
||||
- Dekodierung: UTF-8, mit Fallback auf `utf-8-sig` (BOM); ungültige
|
||||
UTF-8-Daten führen zu einem `PromptSourceError`.
|
||||
- Zeilenenden werden normalisiert (`\r\n`/`\r` -> `\n`), ein führendes BOM
|
||||
wird entfernt.
|
||||
|
||||
### 3.2 URL-Quellen (SSRF-Härtung)
|
||||
|
||||
`validate_url_target()` wird vor **jedem** HTTP-Request ausgeführt —
|
||||
einschließlich jedem einzelnen Redirect-Hop, falls Redirects aktiviert sind:
|
||||
|
||||
1. Nur `https://` standardmäßig erlaubt; `http://` erfordert explizit
|
||||
`--allow-insecure-http`.
|
||||
2. Kein Hostname -> Ablehnung.
|
||||
3. Eingebettete Zugangsdaten in der URL (`user:pass@host`) -> Ablehnung.
|
||||
4. Optionale Host-Allowlist (`--url-allow-host`, wiederholbar): falls
|
||||
gesetzt, muss der Hostname exakt (case-insensitiv) enthalten sein.
|
||||
5. `localhost` als Hostname wird immer abgelehnt.
|
||||
6. IP-Literale als Hostname (`https://127.0.0.1/...`) werden standardmäßig
|
||||
abgelehnt; `--allow-ip-host` erlaubt sie explizit.
|
||||
7. Alle DNS-Antworten für den Hostnamen werden aufgelöst
|
||||
(`socket.getaddrinfo`); **jede** zurückgegebene Adresse wird klassifiziert
|
||||
(`ipaddress`-Modul: `is_private`, `is_loopback`, `is_link_local`,
|
||||
`is_multicast`, `is_reserved`, `is_unspecified`). Trifft **eine** dieser
|
||||
Eigenschaften zu, wird die Anfrage abgelehnt (Rebinding-Schutz: es reicht,
|
||||
dass irgendeine aufgelöste Adresse privat ist).
|
||||
8. Die Cloud-Metadata-Adresse `169.254.169.254` wird explizit zusätzlich
|
||||
geprüft und immer blockiert, sofern nicht `--allow-private-url` gesetzt
|
||||
ist.
|
||||
9. `--allow-private-url` deaktiviert die IP-Klassifikationsprüfungen 7 und 8
|
||||
komplett (bewusste Eskalation für vertrauenswürdige interne Ziele) und
|
||||
kann **nicht** gleichzeitig mit `--url-allow-host` verwendet werden
|
||||
(widersprüchliche Sicherheitsmodelle: „alles erlauben“ vs. „nur
|
||||
bestimmte Hosts“).
|
||||
|
||||
Nach erfolgreicher Zielvalidierung (`load_text_url()`):
|
||||
|
||||
- Anfrage mit `stream=True`, `allow_redirects=False` (Redirects werden
|
||||
manuell behandelt, damit jeder Hop erneut validiert wird), separaten
|
||||
Connect-/Read-Timeouts (`--connect-timeout`, `--read-timeout`).
|
||||
- Redirect-Statuscodes (3xx) werden nur verfolgt, wenn `--follow-redirects`
|
||||
gesetzt ist; sonst Ablehnung. Die maximale Anzahl Hops ist begrenzt
|
||||
(`InputPolicy.max_redirects`, Default 3).
|
||||
- Content-Type-Allowlist: nur Text-artige Typen
|
||||
(`text/plain`, `text/markdown`, `text/csv`, `application/json`,
|
||||
`application/xml`, `application/yaml`, …); `text/html` nur mit
|
||||
`--allow-html-input`.
|
||||
- Größenlimit wird sowohl über den `Content-Length`-Header **als auch**
|
||||
während des Streamens (`iter_content`) durchgesetzt, sodass ein Server, der
|
||||
einen falschen (zu niedrigen) `Content-Length`-Header sendet, keinen
|
||||
Speicher-Erschöpfungsangriff durchführen kann.
|
||||
- Antwort muss gültiges UTF-8 sein.
|
||||
|
||||
### 3.3 Warum das wichtig ist
|
||||
|
||||
Diese Härtung verhindert, dass `--system-url`/--prompt-url` als
|
||||
SSRF-Vektor missbraucht werden kann, um interne Dienste (z. B.
|
||||
Docker-Socket-Proxies, interne Admin-APIs, Cloud-Metadata-Endpunkte) vom
|
||||
GPU-Host aus zu erreichen, falls `llamacppctl` jemals in einem Kontext
|
||||
läuft, in dem die übergebene URL nicht vollständig vertrauenswürdig ist
|
||||
(z. B. durch ein Webhook, ein anderes Automatisierungs-Skript oder einen
|
||||
mehrbenutzerfähigen Wrapper um `llamacppctl`).
|
||||
|
||||
## 4. Docker- / Host-Vertrauensmodell
|
||||
|
||||
- `docker_ops.py` ruft ausschließlich `subprocess.run(["docker", ...])` mit
|
||||
**Argumentlisten** auf, niemals mit `shell=True` oder zusammengesetzten
|
||||
Shell-Strings. Es gibt daher keine Shell-Injection-Fläche über
|
||||
Konfigurationswerte oder CLI-Argumente.
|
||||
- `llamacppctl` vertraut dem lokalen Docker-Daemon vollständig (wie jedes
|
||||
Docker-CLI-Tool) — es ist kein Sandboxing gegenüber Docker selbst
|
||||
vorgesehen. Wer `llamacppctl` einsetzt, muss dem Nutzerkonto, das den
|
||||
Befehl ausführt, den gleichen Vertrauensgrad einräumen wie direktem
|
||||
Docker-CLI-Zugriff (i. d. R. Mitgliedschaft in der `docker`-Gruppe).
|
||||
- `HF_HOME` wird **read-only** (`:ro`) in den Container gemountet — der
|
||||
Container kann Modelldateien lesen, aber nicht verändern oder löschen.
|
||||
- GPU-Zuweisung erfolgt über `--gpus device=<gpu_device>`, sodass mehrere
|
||||
Profile gezielt auf unterschiedliche GPUs (z. B. RTX 3090 #1/#2) gepinnt
|
||||
werden können.
|
||||
|
||||
## 5. Locking (`--change`)
|
||||
|
||||
`lock_ops.FileLock` verwendet `fcntl.flock(LOCK_EX | LOCK_NB)` auf einer
|
||||
Lock-Datei unter `/tmp/llamacppctl.<container_name>.lock` (Default, ableitbar
|
||||
über `--lock-file`). `--change` hält den Lock über die gesamte
|
||||
Stop-Reconfigure-Restart-Sequenz. Ein zweiter, gleichzeitiger `--change`-Aufruf
|
||||
für **denselben** `container_name` schlägt sofort mit `LockError` (Exit-Code
|
||||
6) fehl, statt zu blockieren oder Race-Conditions am Container zu riskieren.
|
||||
Da der Lock-Pfad an `container_name` gekoppelt ist, blockieren sich zwei
|
||||
Profile mit unterschiedlichem `container_name` gegenseitig nicht.
|
||||
|
||||
## 6. `--dry-run` und `--print-effective-config` als Sicherheitswerkzeuge
|
||||
|
||||
- `--print-effective-config` gibt die vollständig aufgelöste Konfiguration
|
||||
(Server- und Prompt-Konfiguration) als JSON aus, **bevor** irgendeine
|
||||
Docker- oder HTTP-Aktion ausgeführt wird. Damit lässt sich prüfen, welche
|
||||
Werte aus welcher Quelle (Defaults/[default]/[model.*]/CLI) tatsächlich
|
||||
gewonnen haben, ohne einen Container anzufassen.
|
||||
- `--dry-run` (in Kombination mit `--start`/--change`) zeigt den vollständig
|
||||
zusammengesetzten `docker run`-Befehl (Shell-quotiert zur Anzeige) an,
|
||||
**ohne** ihn auszuführen. Empfohlen vor jeder Änderung an einer
|
||||
Produktionskonfiguration, insbesondere nach Anpassungen an
|
||||
`llama.cpp.config`.
|
||||
|
||||
Beide Flags erfordern **keinen** erreichbaren Docker-Daemon für ihre reine
|
||||
Ausgabe — `main.run()` prüft `docker_available()` derzeit vor der
|
||||
Konfigurationsauflösung; auf einem Host ganz ohne Docker (z. B. zur reinen
|
||||
Konfigurationsvalidierung) schlägt der Aufruf entsprechend mit einer
|
||||
expliziten Fehlermeldung fehl statt still falsche Annahmen zu treffen.
|
||||
|
||||
## 7. Betriebsmodell auf einem dedizierten GPU-Host
|
||||
|
||||
Empfohlenes Setup (passend zu einer Zwei-GPU-Umgebung, z. B. 2× RTX 3090):
|
||||
|
||||
```ini
|
||||
[default]
|
||||
hf_home = /srv/models
|
||||
gpu_device = 0
|
||||
container_name = llama_cpp_server
|
||||
host_port = 8001
|
||||
|
||||
[model.qwen35]
|
||||
container_name = llama_cpp_qwen35
|
||||
gpu_device = 1
|
||||
host_port = 8002
|
||||
|
||||
[model.deepseek]
|
||||
container_name = llama_cpp_deepseek
|
||||
gpu_device = 1
|
||||
host_port = 8003
|
||||
ctx_size = 131072
|
||||
```
|
||||
|
||||
- Jedes Profil, das parallel laufen soll, bekommt eine eigene
|
||||
`container_name`/`host_port`-Kombination.
|
||||
- `--profile` wählt bei jedem Aufruf explizit aus, welches Profil
|
||||
angesprochen wird — es gibt keinen impliziten "aktuellen" Zustand außer
|
||||
dem, was tatsächlich in Docker läuft.
|
||||
- `--check --profile <name>` ist der empfohlene Weg für Monitoring/Cronjobs,
|
||||
um sowohl Container-Status als auch tatsächliche Inferenzfähigkeit
|
||||
(`chat_ok`) zu prüfen.
|
||||
|
||||
## 8. Installation, Nutzung, Fehlersuche
|
||||
|
||||
Siehe [`README.md`](../README.md) für Installationsschritte und
|
||||
Nutzungsbeispiele.
|
||||
|
||||
Häufige Fehlerbilder:
|
||||
|
||||
| Symptom | Ursache | Lösung |
|
||||
|---|---|---|
|
||||
| `docker is not available on PATH` | Docker-CLI fehlt oder Daemon nicht erreichbar | Docker installieren/starten, Nutzer zur `docker`-Gruppe hinzufügen |
|
||||
| `container_name is mandatory and must not be empty` | Kein `container_name` in `[default]`/`[model.*]`/CLI gesetzt | `container_name` in der Config oder via `--container-name` setzen |
|
||||
| `model profile not found: <name>` | `--profile <name>` verweist auf nicht existierende `[model.<name>]`-Sektion | Sektionsname prüfen/anlegen |
|
||||
| `http requires --allow-insecure-http` | `--system-url`/--prompt-url` nutzt `http://` | Auf `https://` wechseln oder bewusst `--allow-insecure-http` setzen |
|
||||
| `resolved to blocked IP ...` | URL löst auf eine private/loopback/reserved Adresse auf | Ziel korrigieren oder bewusst `--allow-private-url` setzen (nur für vertrauenswürdige interne Ziele) |
|
||||
| `lock busy: /tmp/llamacppctl.<name>.lock` | Ein anderer `--change`-Lauf für denselben `container_name` läuft bereits | Warten, bis der andere Lauf beendet ist, oder Ursache des hängenden Laufs prüfen |
|
||||
| `Server did not become ready within <n>s` | Modell braucht länger zum Laden als `timeout`, oder Startfehler | `--timeout` erhöhen, `--logs` für Container-Log-Tail nutzen |
|
||||
Loading…
Add table
Add a link
Reference in a new issue