docs: bring docs/ and build_archive.py up to date
Die docs/ waren gegenüber den neuen Features veraltet.
- SECURITY_AND_OPERATIONS.md: Netzwerk-Exposition (Loopback-Default,
--expose/--api-key inkl. 401-Verhalten der Endpunkte), DNS-Pinning,
--start/--change unter Lock + --force, --change validate-before-remove,
Chat-Parameter (max_tokens/chat_temperature/--stream), ${ENV}-Expansion,
--check-Exit-Codes, smoke.sh, ChatReply; Fehler-Tabelle erweitert
(401, Trunkierung, Docker-health vs. HTTP-OK).
- How_to_use.md: Abschnitt zu den neuen Optionen.
- Archiv-Report: Testzahl (130) und Dateiliste aktualisiert.
- build_archive.py: scripts/ in INCLUDE_DIRS (sonst fehlt smoke.sh im Archiv),
test_actions.py/How_to_use.md/smoke.sh in REQUIRED_FILES aufgenommen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
aa1a9fc232
commit
600c128fed
4 changed files with 85 additions and 19 deletions
|
|
@ -69,12 +69,15 @@ REQUIRED_FILES = [
|
||||||
"tests/test_docker_ops.py",
|
"tests/test_docker_ops.py",
|
||||||
"tests/test_http_ops.py",
|
"tests/test_http_ops.py",
|
||||||
"tests/test_lock_ops.py",
|
"tests/test_lock_ops.py",
|
||||||
|
"tests/test_actions.py",
|
||||||
|
"docs/How_to_use.md",
|
||||||
|
"scripts/smoke.sh",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Top-level directories to include wholesale (in addition to REQUIRED_FILES),
|
# Top-level directories to include wholesale (in addition to REQUIRED_FILES),
|
||||||
# so that any file added later without updating REQUIRED_FILES is still
|
# so that any file added later without updating REQUIRED_FILES is still
|
||||||
# packaged. REQUIRED_FILES remains the source of truth for *verification*.
|
# packaged. REQUIRED_FILES remains the source of truth for *verification*.
|
||||||
INCLUDE_DIRS = ["src", "tests", "docs", "man"]
|
INCLUDE_DIRS = ["src", "tests", "docs", "man", "scripts"]
|
||||||
INCLUDE_FILES = [
|
INCLUDE_FILES = [
|
||||||
"pyproject.toml",
|
"pyproject.toml",
|
||||||
"README.md",
|
"README.md",
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ Was gebaut wurde
|
||||||
|
|
||||||
Statt manuell zu tarballen, gibt es jetzt ein eigenständiges Python-Skript build_archive.py, das:
|
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)
|
Prüft, ob alle erforderlichen Projektdateien vorhanden sind (Quellcode, Tests, Doku, Manpage, Config-Beispiel, scripts/smoke.sh)
|
||||||
|
|
||||||
pyproject.toml-Abhängigkeiten gegen requirements.txt abgleicht
|
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 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
|
Die vollständige Test-Suite (aktuell 130 Tests, alle grün) laufen lässt
|
||||||
|
|
||||||
Das .tar.gz über Pythons tarfile-Modul baut (nicht rohes Shell-tar)
|
Das .tar.gz über Pythons tarfile-Modul baut (nicht rohes Shell-tar)
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ 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
|
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)
|
tests/ — 130 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
|
docs/SECURITY_AND_OPERATIONS.md — vollständige Architektur-, Konfigurations- und Sicherheitsdokumentation
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,16 @@ 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.
|
- `--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.
|
- 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.
|
- `--dry-run` zeigt dir den vollständigen `docker run`-Befehl an, ohne ihn auszuführen (reine Vorschau: kein Lock, kein Container-Abriss).
|
||||||
|
- `--change` validiert den Modellpfad **vor** dem Entfernen des laufenden Containers und läuft — wie `--start` — unter Lock; `--force` umgeht einen hängenden Lock.
|
||||||
|
|
||||||
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.
|
Neuere Optionen (Details in Manpage/README):
|
||||||
|
|
||||||
|
- **Antwortsteuerung:** `--max-tokens` (Reasoning-Modelle brauchen viel Budget, sonst leere Antwort), `--chat-temp`, und `--stream` für token-weise Live-Ausgabe bei `--chat`.
|
||||||
|
- **Netzwerk/Auth:** Port wird per Default nur auf `127.0.0.1` veröffentlicht; `--expose` bindet auf alle Interfaces, `--api-key` schützt die Chat-API (Bearer-Token).
|
||||||
|
- **Scriptbar:** `--check` liefert Exit-Code 0 (läuft/erreichbar) bzw. 5 — geeignet für Monitoring/Cron.
|
||||||
|
- **`hf_home`** darf Env-Variablen enthalten, z. B. `hf_home = ${HF_HOME}`.
|
||||||
|
- **End-to-End-Test:** `scripts/smoke.sh` (opt-in) gegen einen echten Server.
|
||||||
|
|
||||||
|
Die vollständige Referenz zu allen Optionen steht in der Manpage (`man/llamacppctl.1`) und in `README.md`/`docs/SECURITY_AND_OPERATIONS.md`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,14 @@ Betriebsmodus auf einem dedizierten GPU-Host.
|
||||||
```
|
```
|
||||||
src/llamacppctl/
|
src/llamacppctl/
|
||||||
cli.py Argumentparsing (argparse) + semantische Cross-Field-Validierung
|
cli.py Argumentparsing (argparse) + semantische Cross-Field-Validierung
|
||||||
schema.py Dataclasses: ServerConfig, PromptConfig, CheckResult
|
schema.py Dataclasses: ServerConfig, PromptConfig, ChatReply, CheckResult
|
||||||
config.py INI-Konfiguration laden/mergen -> ServerConfig/PromptConfig
|
config.py INI-Konfiguration laden/mergen -> ServerConfig/PromptConfig
|
||||||
|
(inkl. ${ENV}- und ~-Expansion in hf_home)
|
||||||
prompt_io.py Sicherheitsgehärtete Eingabeschicht für System-/User-Prompts
|
prompt_io.py Sicherheitsgehärtete Eingabeschicht für System-/User-Prompts
|
||||||
docker_ops.py Alle Docker-Interaktionen (subprocess mit Argumentlisten)
|
docker_ops.py Alle Docker-Interaktionen (subprocess mit Argumentlisten)
|
||||||
http_ops.py HTTP-Health-Checks und Chat-Completions gegen die
|
http_ops.py HTTP-Health-Checks, Chat-Completions und SSE-Streaming gegen
|
||||||
OpenAI-kompatible llama.cpp-API
|
die OpenAI-kompatible llama.cpp-API
|
||||||
lock_ops.py Exklusives, nicht-blockierendes File-Locking für --change
|
lock_ops.py Exklusives, nicht-blockierendes File-Locking für --start/--change
|
||||||
actions.py Orchestrierung: do_start, do_check, do_stop, do_change, do_chat
|
actions.py Orchestrierung: do_start, do_check, do_stop, do_change, do_chat
|
||||||
main.py Dünner Einstiegspunkt: parse -> validate -> resolve -> dispatch
|
main.py Dünner Einstiegspunkt: parse -> validate -> resolve -> dispatch
|
||||||
```
|
```
|
||||||
|
|
@ -37,13 +38,26 @@ testbar von der Orchestrierung.
|
||||||
`container_name=va_llm`, `gpu_device=1`, `jinja/fa/kv_unified/
|
`container_name=va_llm`, `gpu_device=1`, `jinja/fa/kv_unified/
|
||||||
cont_batching/no_context_shift=true`, `reasoning=on`,
|
cont_batching/no_context_shift=true`, `reasoning=on`,
|
||||||
`cache_type_k/v=q4_0`, `batch_size=1024`, `ubatch_size=512`,
|
`cache_type_k/v=q4_0`, `batch_size=1024`, `ubatch_size=512`,
|
||||||
`timeout=300`, `poll_interval=2`, …).
|
`timeout=300`, `poll_interval=2`, `expose=false`, `api_key=`,
|
||||||
|
`max_tokens=2048`, `chat_temperature=`, …).
|
||||||
2. **`[default]`**-Sektion — überschreibt die eingebauten Defaults global.
|
2. **`[default]`**-Sektion — überschreibt die eingebauten Defaults global.
|
||||||
3. **`[model.<profile>]`**-Sektion — ausgewählt über `--profile <name>`,
|
3. **`[model.<profile>]`**-Sektion — ausgewählt über `--profile <name>`,
|
||||||
überschreibt `[default]` selektiv (nur gesetzte Schlüssel).
|
überschreibt `[default]` selektiv (nur gesetzte Schlüssel).
|
||||||
4. **CLI-Overrides** — z. B. `--image`, `--host-port`, `--container-name`,
|
4. **CLI-Overrides** — z. B. `--image`, `--host-port`, `--container-name`,
|
||||||
`--ctx-size`, … überschreiben alles Vorherige.
|
`--ctx-size`, … überschreiben alles Vorherige.
|
||||||
|
|
||||||
|
`hf_home` unterstützt Environment-Variablen und `~` (z. B. `hf_home = ${HF_HOME}`);
|
||||||
|
die Expansion erfolgt in `build_server_config()` über `os.path.expandvars` +
|
||||||
|
`expanduser`.
|
||||||
|
|
||||||
|
Neben den Container-/Server-Parametern liefert die Auflösung auch die
|
||||||
|
**Chat-Request-Parameter** (`PromptConfig`, wirken nur auf `--chat` und die
|
||||||
|
Antwort nach `--start`, nicht auf den Container): `max_tokens` (Default 2048;
|
||||||
|
Reasoning-Modelle brauchen viel Budget, sonst bricht die Antwort mitten im
|
||||||
|
„Denken“ ab und `content` ist leer) und `chat_temperature` (leer = die
|
||||||
|
Server-`temp` gilt). CLI-Overrides: `--max-tokens`, `--chat-temp`. Die Ausgabe
|
||||||
|
kann mit `--stream` token-weise (SSE) erfolgen.
|
||||||
|
|
||||||
`[prompt.<name>]`-Sektionen werden **separat** aufgelöst und liefern
|
`[prompt.<name>]`-Sektionen werden **separat** aufgelöst und liefern
|
||||||
ausschließlich einen *Fallback*-Systemprompt (`system_prompt = ...`). Ein
|
ausschließlich einen *Fallback*-Systemprompt (`system_prompt = ...`). Ein
|
||||||
explizit übergebener `-s/--system`, `--system-file` oder `--system-url`
|
explizit übergebener `-s/--system`, `--system-file` oder `--system-url`
|
||||||
|
|
@ -147,6 +161,13 @@ Nach erfolgreicher Zielvalidierung (`load_text_url()`):
|
||||||
einen falschen (zu niedrigen) `Content-Length`-Header sendet, keinen
|
einen falschen (zu niedrigen) `Content-Length`-Header sendet, keinen
|
||||||
Speicher-Erschöpfungsangriff durchführen kann.
|
Speicher-Erschöpfungsangriff durchführen kann.
|
||||||
- Antwort muss gültiges UTF-8 sein.
|
- Antwort muss gültiges UTF-8 sein.
|
||||||
|
- **DNS-Pinning gegen Rebinding:** `validate_url_target()` löst den Hostnamen
|
||||||
|
auf und klassifiziert die IPs; der eigentliche `requests.get` würde jedoch
|
||||||
|
**erneut** auflösen (TOCTOU-Fenster). `_pin_dns()` beschränkt `socket.getaddrinfo`
|
||||||
|
für die Dauer des Requests auf genau die bereits validierten IPs — löst der
|
||||||
|
Name zwischenzeitlich auf eine andere (z. B. private) Adresse um, wird der
|
||||||
|
Connect mit `gaierror` abgebrochen. TLS/SNI und Zertifikatsprüfung bleiben
|
||||||
|
intakt, weil die URL weiterhin den Hostnamen trägt.
|
||||||
|
|
||||||
### 3.3 Warum das wichtig ist
|
### 3.3 Warum das wichtig ist
|
||||||
|
|
||||||
|
|
@ -175,16 +196,42 @@ mehrbenutzerfähigen Wrapper um `llamacppctl`).
|
||||||
Profile gezielt auf unterschiedliche GPUs (z. B. RTX 3090 #1/#2) gepinnt
|
Profile gezielt auf unterschiedliche GPUs (z. B. RTX 3090 #1/#2) gepinnt
|
||||||
werden können.
|
werden können.
|
||||||
|
|
||||||
## 5. Locking (`--change`)
|
### 4.1 Netzwerk-Exposition und API-Key
|
||||||
|
|
||||||
|
Die llama.cpp-OpenAI-API ist standardmäßig **nicht** authentifiziert. Deshalb:
|
||||||
|
|
||||||
|
- Der Port wird per Default nur auf **`127.0.0.1`** veröffentlicht
|
||||||
|
(`_port_publish()` -> `-p 127.0.0.1:<host_port>:<container_port>`), d. h. der
|
||||||
|
Endpunkt ist **nicht** aus dem LAN erreichbar. `--expose`/`expose = true`
|
||||||
|
bindet auf alle Interfaces (`-p <host_port>:<container_port>`).
|
||||||
|
- Wird exponiert, sollte per `--api-key`/`api_key` ein Schlüssel gesetzt werden.
|
||||||
|
Er wird dem Server übergeben (`--api-key`) und vom Tool als
|
||||||
|
`Authorization: Bearer <key>` auf jeder Anfrage mitgesendet.
|
||||||
|
- **Wichtig:** llama.cpp schützt damit `/v1/chat/completions` (ohne Key -> 401),
|
||||||
|
lässt aber `/health` und `/v1/models` bewusst offen (für Health-Checks). Die
|
||||||
|
Reachability-Checks des Tools (`check_health`/`check_models`) funktionieren
|
||||||
|
daher auch ohne Key; nur die Chat-Endpunkte verlangen ihn.
|
||||||
|
|
||||||
|
## 5. Locking (`--start` / `--change`)
|
||||||
|
|
||||||
`lock_ops.FileLock` verwendet `fcntl.flock(LOCK_EX | LOCK_NB)` auf einer
|
`lock_ops.FileLock` verwendet `fcntl.flock(LOCK_EX | LOCK_NB)` auf einer
|
||||||
Lock-Datei unter `/tmp/llamacppctl.<container_name>.lock` (Default, ableitbar
|
Lock-Datei unter `/tmp/llamacppctl.<container_name>.lock` (Default, ableitbar
|
||||||
über `--lock-file`). `--change` hält den Lock über die gesamte
|
über `--lock-file`). **Sowohl `--start` als auch `--change`** halten den Lock
|
||||||
Stop-Reconfigure-Restart-Sequenz. Ein zweiter, gleichzeitiger `--change`-Aufruf
|
über die gesamte (Stop-)Reconfigure-Restart-Sequenz. Ein zweiter, gleichzeitiger
|
||||||
für **denselben** `container_name` schlägt sofort mit `LockError` (Exit-Code
|
Aufruf für **denselben** `container_name` schlägt sofort mit `LockError`
|
||||||
6) fehl, statt zu blockieren oder Race-Conditions am Container zu riskieren.
|
(Exit-Code 6) fehl, statt zu blockieren oder Race-Conditions am Container zu
|
||||||
Da der Lock-Pfad an `container_name` gekoppelt ist, blockieren sich zwei
|
riskieren. Da der Lock-Pfad an `container_name` gekoppelt ist, blockieren sich
|
||||||
Profile mit unterschiedlichem `container_name` gegenseitig nicht.
|
zwei Profile mit unterschiedlichem `container_name` gegenseitig nicht.
|
||||||
|
|
||||||
|
`--force` umgeht einen belegten Lock bewusst (mit Warnung auf stderr) — gedacht
|
||||||
|
für hängende/verwaiste Locks. Zusätzlich lässt `--force` `--stop`
|
||||||
|
Inkonsistenzen (z. B. halb erstellte Container) schlucken, statt mit
|
||||||
|
Docker-Fehler abzubrechen.
|
||||||
|
|
||||||
|
`--dry-run` ist eine reine Vorschau und nimmt **weder** den Lock **noch**
|
||||||
|
entfernt es den Container; `--change` validiert außerdem den Modellpfad
|
||||||
|
**vor** dem Entfernen des laufenden Containers, sodass eine Fehlkonfiguration
|
||||||
|
keinen vermeidbaren Ausfall verursacht.
|
||||||
|
|
||||||
## 6. `--dry-run` und `--print-effective-config` als Sicherheitswerkzeuge
|
## 6. `--dry-run` und `--print-effective-config` als Sicherheitswerkzeuge
|
||||||
|
|
||||||
|
|
@ -235,7 +282,11 @@ ctx_size = 131072
|
||||||
dem, was tatsächlich in Docker läuft.
|
dem, was tatsächlich in Docker läuft.
|
||||||
- `--check --profile <name>` ist der empfohlene Weg für Monitoring/Cronjobs,
|
- `--check --profile <name>` ist der empfohlene Weg für Monitoring/Cronjobs,
|
||||||
um sowohl Container-Status als auch tatsächliche Inferenzfähigkeit
|
um sowohl Container-Status als auch tatsächliche Inferenzfähigkeit
|
||||||
(`chat_ok`) zu prüfen.
|
(`chat_ok`) zu prüfen. `--check` ist **scriptbar**: Exit-Code 0, wenn der
|
||||||
|
Container läuft und HTTP erreichbar ist, sonst 5.
|
||||||
|
- Für einen End-to-End-Test gegen einen echten Docker + llama.cpp-Server
|
||||||
|
(`--api-key`-Round-Trip, Streaming, Exit-Codes) gibt es das opt-in Skript
|
||||||
|
`scripts/smoke.sh` (eigener Container/Port, Cleanup am Ende).
|
||||||
|
|
||||||
## 8. Installation, Nutzung, Fehlersuche
|
## 8. Installation, Nutzung, Fehlersuche
|
||||||
|
|
||||||
|
|
@ -253,3 +304,6 @@ Häufige Fehlerbilder:
|
||||||
| `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) |
|
| `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 |
|
| `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 |
|
| `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 |
|
||||||
|
| `chat completion returned HTTP 401: Invalid API Key` | Server verlangt `api_key`, das Tool sendet keinen/falschen | `--api-key`/`api_key` passend setzen |
|
||||||
|
| Leere Antwort + `Hinweis: ... abgeschnitten (finish_reason=length)` | Reasoning-Modell hat das `max_tokens`-Budget im „Denken“ verbraucht | `--max-tokens`/`max_tokens` erhöhen |
|
||||||
|
| `--check` meldet `health=unhealthy`, obwohl der Server antwortet | bildeigener Docker-Healthcheck vs. eigene HTTP-Probe des Tools | unkritisch: `HTTP: OK` + Exit 0 zählt; Docker-Health ist separat |
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue