Inference Is the Last LLM Moat
OpenAI and Anthropic's moat for developers is subsidized inference, meaning reliable, fast, Western-hosted access to running models at an affordable monthly price.…
When I run out of Codex and Claude Code usage, I switch to DeepSeek V4.1 Flash in pi through OpenRouter. In Inference Is the Last LLM Moat I called the providers on OpenRouter a mixed bag. This week I read pi's session logs against OpenRouter's generation API and found my supposedly cheap setup paying about three times what it should. This is the configuration I use now to avoid the most common traps.
Pi takes custom models from ~/.pi/agent/models.json. Log in once with /login openrouter inside pi, then add this entry:
{
"providers": {
"openrouter": {
"models": [
{
"id": "deepseek/deepseek-v4.1-flash",
"name": "DeepSeek V4.1 Flash (Fireworks, ZDR)",
"reasoning": true,
"thinkingLevelMap": { "off": null, "minimal": "low", "low": "low", "medium": "high", "high": "high", "xhigh": "max", "max": "max" },
"input": ["text"],
"contextWindow": 262144,
"maxTokens": 131072,
"cost": { "input": 0.22, "output": 0.66, "cacheRead": 0.007, "cacheWrite": 0 },
"compat": {
"thinkingFormat": "openrouter",
"sendSessionAffinityHeaders": true,
"sessionAffinityFormat": "openrouter",
"openRouterRouting": {
"only": ["fireworks"],
"allow_fallbacks": false,
"data_collection": "deny",
"zdr": true
}
}
}
]
}
}
}
And in ~/.pi/agent/settings.json:
{
"defaultProvider": "openrouter",
"defaultModel": "deepseek/deepseek-v4.1-flash",
"defaultThinkingLevel": "high",
"retry": { "enabled": true, "maxRetries": 5, "baseDelayMs": 2000 }
}
Pi passes the openRouterRouting object straight through as OpenRouter's provider field, so anything from the provider routing docs works there. The cost block only feeds pi's cost display. contextWindow controls when pi compacts, which I get to at the end.
DeepSeek V4.1 Flash costs $0.22 to $0.30 per million input tokens on the OpenRouter providers I would consider, and a cached read costs $0.006 or $0.007. Pi sends the whole conversation on every turn, so after a few tool calls almost the entire prompt should be cache reads. At 120k tokens of context a turn costs less than a tenth of a cent when the cache hits and 3 to 4 cents when it misses.
Across 123 turns in two sessions on one day, the cache dropped fully five times and partially seven times. Those twelve turns were half of the bill. I assumed OpenRouter had switched providers on me. It had not. All 123 requests went to Parasail, on the same endpoint ID, and Parasail dropped the cache on its own. On the older V4 Flash 0731 model it was worse, with hits on 5 of 17 turns.
My old config listed four zero-data-retention providers and let OpenRouter choose between them. For plain requests OpenRouter load-balances by price. Every pi request includes tools, and for tool-calling requests OpenRouter runs Auto Exacto by default, a routing step that reorders providers by OpenRouter's own tool-calling benchmarks and ignores price. For this model it picks Parasail, which was the most expensive provider in my pool and had the worst uptime. The docs themselves say Auto Exacto can cause cache misses mid-conversation in agent loops, and that sort: "price" turns it off.
The prompt cache lives on the provider's endpoint. OpenRouter's sticky routing keeps a session on one provider, and pi already sends the x-session-id header it keys on, so that part works without configuration. With allow_fallbacks: true, though, one upstream rate limit is enough to move the session to another provider, which has no cache. In one test DeepInfra returned a 429, OpenRouter fell back to Fireworks, and the whole prompt was billed as fresh input. Pinning one provider and turning fallbacks off means a 429 becomes a retry with backoff instead. That is why the retry count in settings.json goes up from three to five. Waiting half a minute on a retry is cheaper than paying four cents for a fallback turn.
I benchmarked the candidates with a 15k-token prompt over 20 turns, one provider pinned at a time, checking cached_tokens in every response.
| Provider | Cache hits | Errors | Input / output / cache read per million |
|---|---|---|---|
| Fireworks | 19 of 19 | none | $0.22 / $0.66 / $0.007 |
| Novita | 18 of 19 | none | $0.30 / $1.20 / $0.006 |
| Parasail | 8 of 9 in a shorter run, worse in real sessions | none | $0.30 / $1.20 / $0.006 |
| Morph | 9 of 11, with 3 to 19 second latencies | none | $0.195 / $0.78 / $0.006 |
| DeepInfra | 1 answered out of 12 | 11 rate limits | $0.20 / $0.60 / $0.006 |
Cache read pricing matters less than I thought. With the cache holding, cached reads were about a third of my daily bill, and fresh input and output were the other two thirds, so Fireworks at $0.007 per cached million still comes out 20 percent cheaper than Novita overall. The outliers are what to watch for. BaseTen charges $0.03 per million cached tokens for the same model, five times the others, so check all three prices on the model's provider list before pinning anything.
The rate limits are upstream, per model, shared across everyone on OpenRouter, and they come and go. DeepInfra was unusable for V4.1 Flash but fine for 0731, where it is also the cheapest usable option at $0.06 per million input. Fireworks returned four 429s in ten calls at one point in the afternoon and none in the 31 calls after that. If Fireworks starts stalling on retries, I move the pin to Novita.
zdr: true and data_collection: "deny" restrict routing to providers that do not retain prompts. That rules out DeepSeek's own endpoint, which would be the cheapest at $0.15 per million input and $0.003 per cached million. I do not want client code ending up in a training set, so I accept that.
Pi compacts when the context passes contextWindow minus a 16,384-token reserve. DeepSeek V4.1 Flash advertises a million tokens, and if you write 1,048,576 into models.json, pi basically never compacts. Context costs more as it grows, even when every read is cached. A miss at a million tokens costs 30 cents and takes a minute to prefill. A small fast model does not get better from hundreds of thousands of tokens of old tool output. Codex by default runs on a 272k window and compacts near the top of it. I set 262144, which puts pi's threshold at about 245k. My long sessions peak around 120k, so it rarely fires, and it caps the damage when one runs away. When I switch tasks inside a session I run /compact by hand instead of carrying the old context along.
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com