Files
llmrouter/README.md
T
eltonandClaude Fable 5.1 3b9ad85825 llmrouter: Alias-Art schwelle (Index + Mindestkontext), Stufen 25/40/47/50
billigsterUeberSchwelle in openrouter-auswahl, gemeinsamer klasseFiltern
mit dem Referenzmodus. Stufen jetzt als Schwelle: low 25/256k, medium
40/256k, high 47/500k, max 50/500k (Andreas 2026-09-05).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 07:27:34 +02:00

91 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# llmrouter
Leichtgewichtiger Ersatz für den LiteLLM-Gateway `llm.lan` (siehe `../llmlite`).
Ein OpenAI-kompatibler Reverse-Proxy in Bun/TypeScript mit genau vier
Eingriffen je Anfrage: Client-Key prüfen, Alias auf ein Modell auflösen,
Usage und Kosten aus der Antwort mitlesen, eine Log-Zeile schreiben.
Keine Format-Übersetzung, keine Datenbank, kein Admin-UI. Alle Upstreams
sprechen OpenAI-Format (OpenRouter, llama-server, Ollama, Mistral), deshalb
reicht Durchreichen. Entscheidung Andreas, 2026-09-05.
## Aliasse (config.json)
Zwei Arten:
- **`alternative`** — der Alias nennt ein Referenzmodell mit optionaler
Effort-Stufe (`openai/gpt-5.6-luna:low`, `anthropic/claude-opus-5:medium`).
Gerufen wird das **günstigste OpenRouter-Modell derselben Klasse**, nach der
Regel in `../llmlite/openrouter-auswahl.ts` (Intelligence-Index je Stufe von
Artificial Analysis, Kontext, Tool-Calls, Sperrliste, Live-Probe, Last-Probe
bei Wechsel). Findet sich nichts Billigeres, wird die Referenz selbst gerufen.
Die Effort-Stufe des Ziels wird als `reasoning.effort` gesetzt; ein vom
Client mitgeschicktes `reasoning_effort` wird verworfen, die Klasse gehört
zum Alias.
- **`schwelle`** — Index-Schwelle (0100) plus Mindestkontext, ohne Referenzmodell:
das günstigste Modell, das beides erfüllt, mit der niedrigsten ausreichenden
Effort-Stufe. Stand 2026-09-05: low 25/256k, medium 40/256k, high 47/500k,
max 50/500k. Stabiler als eine Referenz, sagt aber weniger über die Bedeutung.
- **`fest`** — Upstream und Modell stehen fest (lokales Qwen, TTS/STT,
Embeddings, kira auf deizo). Optional ein `body`-Patch, etwa
`chat_template_kwargs.enable_thinking` für die Qwen-Stufen.
`altnamen` übersetzt die LiteLLM-Namen (`42i/marvin-2608:low` …), damit kein
Container angefasst werden muss; geloggt wird der neue Name. Abbauen, sobald
die Clients umgestellt sind.
## Auflösung und Takt
`src/resolver.ts` löst alle `alternative`-Aliasse auf und schreibt
`log/state.json`. OpenRouter-Preise ändern sich oft: alle `refreshHours` (12)
neu. AA-Indexe ändern sich nie, nur neue Modelle kommen dazu: `log/aa-cache.json`,
alle `aaRefreshDays` (7) neu. Der Server frischt selbst auf (beim Start, wenn
veraltet, und im 15-Minuten-Takt geprüft); Wechsel stehen in `log/changes.log`.
```bash
bun run src/resolver.ts --force # jetzt auflösen, Tabelle zeigen
LLMROUTER_LASTTEST=0 bun run src/resolver.ts --force # ohne Last-Probe (Tests; spart ~210k Token je Wechsel)
```
Der erste Lauf mit leerem State prüft jeden Alias unter Last, das ist gewollt.
## Keys und Rechte
- `key-acls.json` (versioniert): Name → erlaubte Aliasse, `[]` = alles.
- `KEYS_FILE` (nur auf dem Host, 0600): Token → Name. Pflege mit
`KEYS_FILE=… bun run src/keys.ts add <name> [--token <bestehend>]`; mit
`--token` lässt sich der LiteLLM-Key eines Agenten übernehmen, sodass der
Container nichts merkt.
- Upstream-Schlüssel aus der Umgebung: `OPENROUTER_API_KEY`, `MISTRAL_API_KEY`.
- `ADMIN_KEY` schützt `GET /stats?days=N`, `GET /zuordnung`, `POST /auffrischen`.
## Log und Statistik
Je Anfrage eine Zeile in `log/requests.jsonl` und in `log/requests.sqlite`:
Key, Alias, angefragter Name, tatsächliches Modell, Effort, Tokens
(prompt/completion/cached), **Kosten wie von OpenRouter gemeldet**
(`usage.cost`, auch im Stream über den letzten Chunk). Lokale Upstreams haben
keine Kosten, das Feld bleibt leer, wir erfinden keine Preise.
```bash
bun run src/stats.ts 7 # Tag × Key: Anfragen, Tokens, Kosten, Modelle
curl -H "Authorization: Bearer $ADMIN_KEY" http://llm.lan:4010/stats?days=7
```
Die Antwort trägt `x-llmrouter-model`, damit ein Client sieht, was wirklich lief.
## Betrieb
```bash
PORT=4010 KEYS_FILE=/srv/llmrouter/keys.json ADMIN_KEY=OPENROUTER_API_KEY=… bun run src/server.ts
bun run build # dist/llmrouter, ein statisches Linux-Binary
```
Parallelbetrieb neben LiteLLM (Port 4000) im LXC 185020 auf Port 4010; die
Umschaltung ist ein Wechsel der `base_url` in den Client-Configs.
Bedient: `/v1/chat/completions`, `/v1/completions`, `/v1/responses`,
`/v1/embeddings`, `/v1/audio/speech`, `/v1/audio/transcriptions`,
`/v1/audio/translations`, `/v1/models` (zeigt je Key die erlaubten Aliasse),
`/health`. Der Präfix `/v1` ist optional.