Projekt
Session-Slot-Router – lokale KI-Sessionen sauber auf Slots verteilen
Ein kleiner Python-Proxy zwischen Agenten und llama-server: Jede Agent-Session wird einem Slot des LLM-Servers zugewiesen und bleibt dort – geteilte Präfixe werden gecacht statt still von vorne geprefillt.
Wer mehrere lokale Agenten (Hermes, Pi, Cron-Jobs) an einem einzigen llama-server betreibt, stößt auf dasselbe Problem: Jede Session bekommt ihren eigenen Slot, jeder Slot seinen eigenen Prompt-Cache – und geteilte Präfixe werden trotzdem immer wieder von vorne geprefillt.
Der Session-Slot-Router ist ein Testaufbau, der das umdreht: Ein kleiner Python-Proxy auf Port 8081 kennt die Session-ID jeder Anfrage, ordnet jede Session einem Slot zu und hält sie per LRU dort. Der Artikel beschreibt Aufbau, Anbindung von Pi und Hermes, die Slot-Logik und die Grenzen dieser ersten Fassung.
Hinweis: Das ist eine bewusste Test-/Prototyp-Variante des Routers – kein produktionsreifes Paket und nicht als generisches Upstream-Tool gedacht. Der Quellcode wird im eigenen GitHub-Account unter github.com/urpid/session-slot-router veröffentlicht, sobald ein eigenes Repository angelegt ist. Bis dahin zeigt der Link auf den Account. Die Logik (Slot-Zuordnung, LRU, Weiterleitung) ist aber vollständig und getestet.
Voraussetzungen
- ein laufender llama-server mit mehreren Slots (hier: http://127.0.0.1:8080/v1)
- Python 3 für den Router (hier: 3.14)
- der Router lauscht auf 0.0.0.0:8081, lokal also http://127.0.0.1:8081/v1
- Agenten, die die Session-ID und den Client-Typ anhängen können (Pi-Extension, Hermes-Middleware)
- sqlite3 für die Slot-Zuordnung und das Ereignis-Log (ohne Prompts)
Teil 1: Aufbau und Start
Der Router ist ein einzelnes Python-Modul (router.py), das per start-router.sh mit nohup im Hintergrund startet, seine PID ausgibt und sein Log nach router.log schreibt. SQLite liegt unabhängig vom Startverzeichnis unter router.sqlite. Nur eine Instanz kann Port 8081 belegen – ein zweiter Start meldet den Konflikt im Log.
1. Router starten
Das Startskript macht drei Dinge: Prozess starten, PID ausgeben, Log-Datei benennen. Zum Verfolgen einfach:
./start-router.sh
tail -f router.log Der Log enthält Datum/Uhrzeit mit Zeitzone, HTTP-Methode und Route (ohne Query), agent_name, agent_session_id, client_type, das vom Client angegebene id_slot, die erkannten Slots, den tatsächlichen Slot, die Aktion (einschließlich einer verdrängten bisherige Session), den Antwortstatus und Ablehnungen. Prompts, Tools, Request-Bodies oder API-Schlüssel werden bewusst nicht geloggt.
2. Wie die Slot-Zuordnung funktioniert
Der Router reicht Methode, Route, Query, Header und Body ohne agent_session_id unverändert an llama-server auf Port 8080 weiter. Enthält ein JSON-Body eine agent_session_id, dann:
- der Router liest /slots aus und wählt den zugeordneten Slot
- neue Sessions belegen freie Slots, sonst den inaktivsten per LRU
- agent_name, agent_session_id und client_type werden entfernt, id_slot überschrieben
- laufende Anfragen blockieren ihren Slot; wenn alle belegt sind, wartet die neue Anfrage
Das client_type ist reine Protokollinformation und beeinflusst die Slotwahl nicht – bei alten Clients bleibt es NULL. Ein cli- oder print-Typ beweist nicht, dass ein Mensch statt eines Skripts die Anfrage ausgelöst hat.
3. Anbindung von Pi
Die Pi-Extension (~/.pi/agent/extensions/pi-session.ts, Kopie unter plugin-pi/) hängt Session-ID und client_type nur an, wenn die verwendete Basis-URL explizit Port 8081 hat. ctx.mode wird unverändert gesendet (tui, print, rpc oder json). Der Testaufruf:
pi --provider llama-slot-test --model Qwen3.8-27B-S1 Das Modell steht in ~/.pi/agent/models.json; die Extension wird automatisch geladen.
4. Anbindung von Hermes
plugin-hermes/ ist ein allgemeines Hermes-Plugin mit einer llm_request-Middleware – bewusst kein Modell-Provider. Es hängt die Session-ID nur an, wenn die Basis-URL Port 8081 hat. Bei Cron-Plattform oder passender Cron-Session-ID sendet es cronjob, sonst den Plattformnamen bzw. unknown.
Eine Kopie liegt unter ~/.hermes/profiles/<PROFIL>/plugins/session-slot-router/ und ist in <PROFIL>s plugins.enabled eingetragen; sie ist nicht global unter ~/.hermes/plugins/ installiert. Wichtig: Das Plugin ergänzt nur die Haupt-Agent-Anfragen; zusätzliche Hilfsmodell-Anfragen durchlaufen den Middleware-Hook nicht.
Session-IDs im Muster cron_<12 hex Job-ID>_<YYYYMMDD_HHMMSS> werden in cron:<Job-ID> übersetzt, damit Läufe desselben Jobs dieselbe Routing-ID senden. Alle anderen Session-IDs bleiben unverändert.
5. Grenzen der ersten Fassung
- Verdrängung ist ohne Nachfrage und ohne garantierte Sicherung – ein alter Kontext kann erneut eingelesen werden. Direkte Anfragen an Port 8080 umgehen den Router vollständig.
- Der Router liest keine bestehenden Kontexte aus den Slots; nach einem Neustart von Router oder llama-server beweist eine gespeicherte Zuordnung keinen erhaltenen KV-Zustand.
- Keine Authentifizierung: Auf 0.0.0.0:8081 ist der Router von allen netzseitig erreichbaren Interfaces aus erreichbar, sofern die Firewall es zulässt. Für Netzzugang die UFW-Freigabe auf vertrauenswürdige Quell-IPs beschränken.
- Keine garantierte Wiederaufnahme abgebrochener Streaming-Anfragen und keine Begrenzung der Wartezeit bei belegten Slots.
- Weitergeleitet werden GET, HEAD, POST, PUT, PATCH, DELETE und OPTIONS; WebSockets und Bodies ohne Content-Length sind nicht implementiert.
6. Tests
Die Tests liegen unter tests/ und werden aus dem Projektverzeichnis ausgeführt:
python3 -m unittest discover -s tests -v Sie prüfen unter anderem Passthrough ohne Session-ID, Routing/LRU mit Session-ID, Streaming, HTTP-Methoden, die SQLite-Migration, das Terminal-Logging sowie die Metadaten der Pi- und Hermes-Plugins. Pi und <PROFIL> wurden gegen den laufenden Router und llama-server verwendet.
Bericht
Meine Kommentare