Initial commit: llm-router — smart OpenAI/Anthropic/Ollama router

A stdlib pre-router in front of Ollama with LiteLLM backend:
- auto model selection by content/tools/modality, with fallbacks
- OpenAI /v1, Anthropic /v1/messages, and Ollama-native /api/* endpoints
- Whisper-shaped /v1/audio/transcriptions + in-chat audio
- key-based fleet policies (e.g. force a client onto uncensored models)
- optional Bearer auth; launchd/systemd service install
- benchmark harnesses (speed, quality, agentic tool use) with sample results

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Joseph Costa
2026-07-05 02:05:16 -05:00
co-authored by Claude Opus 4.8
commit 9938d46a67
32 changed files with 2419 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
# Routing
## How `model: "auto"` decides
`choose_model()` in [`router.py`](../router.py) runs cheap→expensive, first match wins:
1. **Image in the request** → vision model (`vision_heavy` if the text mentions
OCR/document/table, else `vision`).
2. **Request carries `tools`** → agentic model (`code_heavy` if the text looks
code-ish, else `agentic`).
3. **Text heuristics** → code regex → coding model; reasoning words → reasoning
model; very short (< `SHORT_CHARS`) → the small fast model.
4. **Tiny classifier** — anything left over is labeled by a small always-warm
model (`CLASSIFIER`) into code / reason / vision / general.
The chosen model, plus any fallback, is reported in the response headers
`x-router-initial-model` (the decision) and `x-router-model` (what actually served).
## The model map — customize this for your machine
Two dicts at the top of `router.py`:
```python
M = {
"fast": "qwen3:8b", "general": "qwen3:14b",
"code": "glm-4.7-flash", "code_heavy": "qwen3-coder:30b",
"reason": "qwen3.6:35b-a3b", "agentic": "glm-4.7-flash",
"vision": "qwen3-vl:8b", "vision_heavy": "qwen3-vl:30b-a3b-instruct",
}
CLASSIFIER = "qwen3:8b"
```
Point these at models you've actually pulled (`ollama list`). Names must match
exactly, including any `:tag`. Restart the router after editing.
`FALLBACK` maps each model to a backup used when it errors or isn't pulled yet —
so the router degrades gracefully instead of failing.
## Explicit model override
Any request that names a real model (not `auto`) is passed straight through:
`{"model": "glm-4.7-flash", ...}`. Unknown names are handled by LiteLLM's catch-all
(`model_name: "*"` in `litellm.config.yaml`), which forwards *any* name to Ollama —
so `:tag` variants and models you pull later work without config changes.
## Policy: forcing a client onto a fleet (the "uncensored" example)
Put a token in `.uncensored_key`. Any request whose `Authorization: Bearer <token>`
matches is forced through `choose_uncensored()` — the same content heuristics, but
only ever selecting from the `UNCENSORED` map — **regardless of the model asked for**.
This pins a specific client (identified by its key) to a specific fleet.
Clients can also opt in per-request with `model: "auto-uncensored"` (no key needed).
Use this pattern for any policy split (a "coding-only" client, a "cheap models"
client, etc.): add a map + a `choose_*` function + a trigger in `do_POST`.
## Adding a model from Hugging Face
Ollama pulls GGUF repos directly:
```bash
ollama pull hf.co/USER/REPO:Q4_K_M # tag must match the quant in the filename
ollama cp hf.co/USER/REPO:Q4_K_M friendly-name # optional: rename
```
Then add `friendly-name` to the relevant map in `router.py`. Multi-file repos:
Ollama auto-pulls a vision `mmproj` if present; `mtp`/draft files are ignored.
Big prompts truncate at Ollama's default context — make a bigger-context copy with
`./scripts/make-context-variant.sh <model> <num_ctx>`.