jev-router
★ 650▲ 24gargpratyush/jev-router
Per-turn model routing for Claude Code and Codex. Simple work goes to the fast tier and difficult work to the strong tier.
Ranks the OpenRouter catalogue per task, takes the Pareto frontier over quality, cost and latency, and routes pi to the knee point.
View on GitHub →Hands-on review
Five typed questions about a task, then a Pareto frontier over the OpenRouter catalogue picks the model. Shadow mode is the default.
Good for
Watch out for
Tested Sep 21, 2026 at b9650e680795 · node:24 in Docker; its own test suite, and the shipped classifier driven against a local stand-in with one patched constant, reverted and diffed
How we reviewed this: we ran the test suite in node:24, then drove the shipped classifier against a local stand-in of ours by patching the one base-URL constant — reverted and diffed afterwards — and forced the two failure paths. We made no Jev calls.
A pi extension that picks which OpenRouter model should do each task. One batched request asks Jev five questions about the work item — category (a seven-way choice), complexity and risk (four-level scores), brief (sufficient / inspect / clarify) and decompose (a noul) — and the answers become inputs to a selection policy, not the decision itself.
The selection is the interesting half. It ranks the whole OpenRouter catalogue, filters to models that fit the task, computes a Pareto frontier over quality, cost and latency, and takes the knee — the frontier member farthest from the chord joining the cheapest and dearest. As the README puts it, that needs no weights, so the pick follows the catalogue and the recorded evidence rather than a tuning file someone has to maintain. When the frontier is too small or too flat for a knee, a weighted value function takes over.
AGENTS.md states the rules the code actually follows, and the first one is the right one:
- Jev output is evidence, not truth. Never treat a Jev answer as a guarantee of task success.
- Never route mid-tool-call. Route only at task, subtask, and retry boundaries.
- Never trust a worker’s self-report. Run the verifier. Read the exit code.
Default mode is shadow — it recommends and logs, and you opt into auto for it to actually switch models.
npm test passes: six files of pure-function tests covering the frontier maths, selector behaviour, catalogue normalisation and role routing. The assertions are the ones you would want — three-point frontier picks the knee, empty catalog falls back, relaxes proven gate when nothing qualifies, classifier down still selects, writing is the most cost averse.
Driving the real classifier against our stand-in, one request carried all five questions:
{"model": "typesafe/jev-1.13",
"state": {"taskId": "t-1", "role": "implementer", "objective": "…",
"acceptanceCriteria": ["…"], "relevantContext": "…", "facts": {…}},
"questions": {"category": …, "complexity": …, "risk": …, "brief": …, "decompose": …}}
Answers are validated on the way back rather than trusted: a non-finite score or an invalid probability map drops that answer instead of poisoning the selection, and ok is false when nothing survives.
Both failure paths behave:
| what we did | result |
|---|---|
| stand-in returns HTTP 503 | ok: false, classifierUnavailable: true, error recorded, 51 ms |
| no key anywhere | ok: false, classifierUnavailable: true, “OPENROUTER_API_KEY not set”, 0 ms |
In both cases the selector still returns a model from its fallback table — which is what the classifier down still selects test asserts, and what you want from a router: a slow or missing classifier should cost you a better pick, not the task.
The state is the whole task envelope: objective, acceptance criteria, relevantContext, and a facts block with repository, branch and file list. We planted a canary string in relevantContext and it went out, as it must — that field is the context you chose to attach. The thing to know is that there is no separate summarisation step before the request: what you put in the envelope is what the classifier sees. (summarizeEnvelope exists, but it is for display and logging — role plus objective, one line — and the comment says so: “never full history”.)
The key comes from OPENROUTER_API_KEY, or, failing that, out of ~/.pi/agent/auth.json. Reading pi’s own credential file is the convention for a pi extension and saves a duplicate key, but it is worth knowing that installing this extension gives it your pi OpenRouter token.
The classifier is pinned to typesafe/jev-1.13 and runs through OpenRouter, so it is billed on the same account as the models it routes to, with one overall deadline and maxRetries: 0 — no hidden retry cascade behind a timeout.
MIT, one author, active on 20 September, with AGENTS.md scoping the project tightly (“Do not add a server, a dashboard, or an MCP layer”). There is no .github directory, so the tests do not run anywhere but your machine.
eval/ holds a benchmark harness and one recorded run from 20 September with its report. Those are the author’s numbers on the author’s account, and the quality inputs are third-party indices — Artificial Analysis for general models, EQ-Bench Creative Writing Elo for prose, which is a more careful distinction than most routers make. We did not reproduce them.
The most principled model router we have looked at: the model answers questions about the task, a documented policy turns those answers into a pick, and the two are separate files with the boundary written down. Shadow-by-default and a fallback table that survives a dead classifier are the details that make it safe to try.
Read AGENTS.md before you install it — it is the clearest statement of intent in this batch — and decide whether you want an extension holding your pi OpenRouter token.
For routing between Claude models instead, see jev-router; for routing between capabilities rather than models, JevRouter.
See how it compares with other tools in Best Jev tools, tested hands-on.
Review updated Sep 21, 2026. Numbers quoted from the project are its author's own; we don't publish our own measurements of Jev.
gargpratyush/jev-router
Per-turn model routing for Claude Code and Codex. Simple work goes to the fast tier and difficult work to the strong tier.
BillionsBobby/JevRouter
Routes each agent step to a model, subagent, Skill, MCP tool, or CLI with one Jev Choice, requiring confirmation for risky capabilities and keeping decision receipts.
higress-group/HiRoute
Local-first routing and coordination engine for long-running agent work, with a jev-decider extension that routes on typed decisions.
New Jev releases, pricing changes, and the best new projects, once a week. No spam; unsubscribe anytime.
Powered by Buttondown. See our privacy policy.