docs: README und .gitignore

This commit is contained in:
Mikei386
2026-08-18 22:52:48 +02:00
parent c753bd120c
commit bddbc35476
2 changed files with 135 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
__pycache__/
*.pyc
.DS_Store
+132
View File
@@ -0,0 +1,132 @@
# AI Profile Router
Kleiner OpenAI-kompatibler Proxy (Python, nur Standardbibliothek) vor einem
lokalen llama.cpp-Server. Er leitet normale OpenAI-Requests transparent
weiter (Streaming, Tool Calls, JSON) und schaltet zwischen drei festen
llama.cpp-Profilen um.
## Zielsystem
| | |
|---|---|
| Host | 192.168.1.196 |
| SSH | `root` mit Key `lmstudio_unraid` |
| llama.cpp | `http://127.0.0.1:8080` (Service `mike-ai-llama-ui.service`) |
| Profil-Skript | `/usr/local/bin/llama-profile {fast\|medium\|long}` |
| Router-Port | **8081** |
| Router-Service | `mike-ai-profile-router.service` |
## Profile / virtuelle Modelle
| Profil | Modell | Kontext |
|---|---|---|
| `fast` | `qwen-fast` | 73728 |
| `medium` | `qwen-medium` | 94208 |
| `long` | `qwen-long` | 131072 |
## Endpunkte
| Endpunkt | Beschreibung |
|---|---|
| `GET /v1/models` | Die drei virtuellen Modelle inkl. `context_length`/`context_window` |
| `GET /status` | Aktives Profil, Upstream-Zustand, Modell, Kontext, Uptime |
| `POST /fast` `/medium` `/long` | Profilwechsel (auch `GET` möglich) |
| `POST /v1/chat/completions` | Weiterleitung an llama.cpp (Streaming + Tool Calls) |
| alles andere | Transparente Weiterleitung an llama.cpp |
### Verhalten
- **Virtuelles Modell** (`qwen-fast`/`qwen-medium`/`qwen-long` in
`chat.completions`): Der Router stellt sicher, dass das passende Profil
aktiv ist (notfalls Wechsel + Warten), ersetzt das Modell durch das echte
llama.cpp-Modell und leitet weiter.
- **Profilwechsel** (`POST /fast` …): Führt `/usr/local/bin/llama-profile
<profil>` aus (ohne Shell, feste Argumente → keine Injection), wartet dann,
bis llama.cpp wieder erreichbar ist, und liefert erst dann `200`.
- **Impliziter Wechsel bei downem llama.cpp**: Ein Chat-Request mit virtuellem
Modell liefert sofort `502`, wenn das Profil bereits aktiv ist, aber
llama.cpp down ist (kein stiller Neustart). Der Neustart wird explizit über
`POST /<profil>` angestoßen.
- **Ungültige Profile/Modelle**: `POST /<anderes>` → `400`;
`qwen-<anderes>` als Modell → `400`. Nur die drei festen Profile sind
schaltbar.
- **Fehlerformat**: OpenAI-kompatibel (`{"error": {"message", "type", "code"}}`).
## Repository-Struktur
```
router/ai_profile_router.py # der Router (einzige Laufzeit-Datei)
deploy/mike-ai-profile-router.service # systemd-Unit
deploy/install.sh # läuft auf dem Zielsystem (per SSH)
deploy/deploy.sh # läuft lokal: SCP + SSH
dev/ # lokale Tests (Mock-llama.cpp, Fake-Profil-Skript)
```
Entwicklungsdateien (`dev/`) und Deployment-Dateien (`router/`, `deploy/`)
sind getrennt. Auf dem Zielsystem landet nur `router/` + `deploy/`.
## Deployment
Voraussetzung: SSH-Key `~/.ssh/lmstudio_unraid` (bereits vorhanden).
```bash
./deploy/deploy.sh
```
Das Skript:
1. Überträgt `ai_profile_router.py`, `install.sh` und die systemd-Unit per
SCP nach `/tmp/ai-profile-router/` auf dem Zielsystem.
2. Führt `install.sh` per SSH aus, das:
- den alten Router (`mike-ai-local-llm-router.service` +
`/opt/mike-ai/local-llm-router`) **mit Backup** entfernt,
- den neuen Router nach `/opt/mike-ai/ai-profile-router/` installiert,
- `mike-ai-profile-router.service` aktiviert (Start beim Boot) und startet,
- `GET /status` verifiziert.
Die Installation ist idempotent (Update = erneut ausführen).
## Konfiguration (Umgebungsvariablen in der systemd-Unit)
| Variable | Default | Bedeutung |
|---|---|---|
| `ROUTER_HOST` | `0.0.0.0` | Bind-Adresse |
| `ROUTER_PORT` | `8081` | Port |
| `UPSTREAM_URL` | `http://127.0.0.1:8080` | llama.cpp |
| `PROFILE_SCRIPT` | `/usr/local/bin/llama-profile` | Profil-Skript |
| `PROFILE_DIR` | `/etc/systemd/system/mike-ai-llama-ui.service.d` | Ort der `override.conf` |
| `SWITCH_TIMEOUT` | `600` | Warten auf llama.cpp nach Wechsel (s) |
| `REQUEST_TIMEOUT` | `600` | Read-Timeout für Upstream-Requests (s) |
| `CONNECT_TIMEOUT` | `10` | Connect-Timeout Upstream (s) |
| `POLL_INTERVAL` | `2` | Polling-Intervall (s) |
| `LOG_LEVEL` | `INFO` | Logging-Level |
## Lokale Tests
```bash
./dev/test_local.sh
```
Startet einen Mock-llama.cpp und den Router mit einem Fake-Profil-Skript und
prüft: `/v1/models`, `/status`, Forwarding, Streaming, Tool Calls,
Profilwechsel (fast→medium→fast), virtuelles Modell triggert Wechsel,
ungültige Profile, Upstream down → 502, Recovery.
## Betrieb
```bash
systemctl status mike-ai-profile-router
journalctl -u mike-ai-profile-router -f
curl -s http://192.168.1.196:8081/status | python3 -m json.tool
curl -s -X POST http://192.168.1.196:8081/medium
```
## Sicherheit
- Keine Shell-Aufrufe: Profil-Skript wird mit `subprocess.run([script, profil])`
aufgerufen, `profil` ist Whitelist-geprüft (`fast|medium|long`).
- Keine Secrets/Tokens im Code oder in der Unit.
- systemd-Hardening: `NoNewPrivileges=true`, `PrivateTmp=true`.
- Die bestehende llama.cpp-/Profil-Konfiguration wird nicht verändert; der
Router nutzt nur das vorhandene `llama-profile`-Skript und liest die
`override.conf`.