5.0 KiB
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-longinchat.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 dann200. - 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 überPOST /<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).
./deploy/deploy.sh
Das Skript:
- Überträgt
ai_profile_router.py,install.shund die systemd-Unit per SCP nach/tmp/ai-profile-router/auf dem Zielsystem. - Führt
install.shper 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.serviceaktiviert (Start beim Boot) und startet,GET /statusverifiziert.
- den alten Router (
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
./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
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,profilist 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 dieoverride.conf.