On one pytest issue, Jev Router used 42% fewer output tokens and about 40% less time than running every call at fixed high reasoning effort, and both solved all six attempts. That was the best case. Across all 44 SWE-bench Verified attempts with GPT-6 Astra, the router solved every one, the same as fixed high, with 14% fewer output tokens, 20% fewer input tokens, and 9% less time.
Jev Router is an OpenCode plugin that sets the reasoning effort for each request a coding agent makes. You pick the model, GPT-6 or Claude. Before each call, the router asks Jev how much reasoning the next step needs.
Effort belongs to the step, not the task
A coding agent makes dozens of model calls to solve one task, and they aren't equally hard. Working out why a test fails on only one Python version is hard. Reading the next file, rerunning the tests, or applying an edit the agent already planned is not. Most calls are the second kind.
Effort is normally set once per session, so it has to be right for the hardest step. Running every call at high effort pays for deep reasoning on routine steps. Running every call at medium saves tokens and sometimes fails the hard ones. Choosing effort per call avoids both, as long as the choice is cheap and doesn't cost anything elsewhere.
Until recently it did cost something. Effort lived at the start of the prompt, so changing it broke the cached prefix and the next call paid the uncached rate for the whole history. GPT-6 added a configuration_update item that changes effort from inside the conversation while the cached prefix still matches. The previous post covers how it works. The router keeps the request's own effort fixed and inserts an update only when Jev picks something different.
OpenCode doesn't know about those inserted updates, so the router has to put each one back in its original position on every later request. If one goes missing, everything after it misses the cache. I checked this against the live API with a scripted conversation, once at fixed effort and once with effort changing on a schedule. Across 42 requests, 86.9% of input was cached at fixed effort and 86.8% with effort changing.
Version 0.5.0 added Claude. Changing Claude's top-level output_config.effort invalidates the cached messages, so the router keeps it fixed, at medium for Opus 5.5 and Sonnet 5.5 and high for the other models. Jev's pick goes in as an empty system message just before the newest user message or tool result, which Anthropic accepts behind a beta header. Every result below is from GPT-6.
Results
I ran GPT-6 Astra in OpenCode on SWE-bench Verified, each attempt in a fresh container with the same image, plugin, upstream, and grader, and compared it against fixed high effort, with every call at high. A development set of four issues ran twice each. For the holdout I chose six issues before seeing any results and ran each one six times.
| Cohort | Arm | Solved | Input | Output | Time (s) |
|---|---|---|---|---|---|
| Holdout (6 issues × 6) | Jev | 36/36 | 186,492 | 1,507 | 111.0 |
| Holdout (6 issues × 6) | Fixed high | 36/36 | 238,030 | 1,800 | 118.4 |
| Development (4 issues × 2) | Jev | 8/8 | 404,756 | 2,836 | 195.8 |
| Development (4 issues × 2) | Fixed high | 8/8 | 488,543 | 3,120 | 229.1 |
| Combined | Jev | 44/44 | 226,177 | 1,748 | 126.4 |
| Combined | Fixed high | 44/44 | 283,578 | 2,040 | 138.5 |
The evaluation write-up has the per-run results. Token counts are means per attempt, and output includes reasoning tokens. About 88% of input was cached for Jev and 89% for fixed high, so most of the input saving is in cheaper cached tokens. Uncached input fell from 30,697 to 26,445 tokens per attempt, 14% fewer. The times include Jev itself, which added about 570 ms per call, or roughly 9 seconds over a typical 16-call attempt.
The opening pytest issue is #5262, where Jev averaged 1,112 output tokens against 1,908. Other tasks saved less, and time didn't always improve. In an earlier two-task pilot, Jev was faster on a Flask issue (65 seconds against 126) but slower on a Django issue (232 against 221) while still using fewer output tokens.
The effort mix is where the saving comes from. Across the holdout, Jev chose high 375 times, medium 159 times, and low 42 times, and never xhigh. About a third of its calls ran below high, and none of the 36 attempts failed.
A harder case
Every holdout attempt passed in both arms, so the holdout can't show whether Jev gives up quality on hard steps. For that I needed a task where effort changes the outcome. Pytest #5787 looked like one, so I ran it five times per arm.
| Arm | Solved | Output | Time (s) |
|---|---|---|---|
| Fixed medium | 1/5 | 2,649 | 132.3 |
| Fixed high | 5/5 | 4,113 | 239.8 |
| Fixed xhigh | 5/5 | 7,029 | 336.7 |
| Jev | 5/5 | 3,916 | 218.3 |
Jev solved all five and used less output than high or xhigh. It ran 92 of its 127 calls at high. But 7 of those were fallbacks, where the classifier timed out or errored and the router sent the call at high by default.
So I reran the task with fallback turned off, and the gap mostly closed. Medium solved 4 of 5, high 5 of 5, xhigh 4 of 5, and Jev 5 of 5. Jev used slightly less output than high (4,149 against 4,238) and took longer (248 seconds against 228). This task shows Jev moving to high when steps look hard. It doesn't show that medium reliably fails there.
What these numbers don't show
- Only Astra is in the main comparison. Sol ran in the two-task pilot, which is too small to conclude anything, and Luna hasn't been run on SWE-bench at all.
- SWE-bench Verified is public, so models may have seen these repositories and fixes. The task counts are small, and repeated attempts on one issue aren't independent tasks.
- I picked pytest #5787 after seeing medium fail on it, so it isn't an unbiased estimate of anything.
- The main comparison has no fixed medium arm. On issues this easy, fixed medium might also solve everything with fewer tokens than Jev.
- Token counts aren't dollars. Cached and uncached input are priced differently, and a subscription upstream like Codex bills against a quota.
- Claude hasn't been evaluated. No Claude model has run on SWE-bench, and I haven't measured the Claude cache against the Anthropic API. The one live Claude check went through a local proxy that ignores the mid-conversation effort, so it only shows the requests are accepted.
- On GPT-6, the router only supports standard, single-agent mode, because that's where
configuration_updateexists.
Try it
Add the plugin to your OpenCode config with a Jev key and the models you want it to wrap. It needs OpenCode 2.0.4 or newer. OpenCode 1.x users can pin 0.5.
{
"plugins": [
{
"package": "@robertn702/opencode-jev-router@0.6.1",
"options": {
"jevApiKey": "{env:JEV_API_KEY}",
"jevBaseUrl": "https://ai-gateway.vercel.sh/typesafe",
"wrap": {
"openai": ["openai/gpt-6-astra"],
"anthropic": ["anthropic/claude-opus-5-5"],
},
},
},
],
"model": "jev-router/gpt-6-astra",
}
Each wrapped model shows up as its own jev-router model, so this config adds jev-router/gpt-6-astra and jev-router/claude-opus-5-5. The router reuses each source model's endpoint and API key, so Jev's is the only new key. ChatGPT and Claude subscription logins aren't supported yet. This example classifies through Vercel AI Gateway. For a direct TypeSafe key, drop jevBaseUrl. The README covers how requests are rewritten, fallback settings, the standalone proxy for other Responses and Messages clients, and the full evaluation. The design borrows ideas from jev-codex-router and pi-jev-router.