Repository navigation
Releases: wilbeibi/turnpike
Release list
v0.4.0
turnpike 0.3.0
Prices carry time. prices.json used to be a snapshot of now, so every refresh repriced all of history — DeepSeek's August increase (~2.5x blended on a real workload) would have under-reported new calls until models.dev caught up, then retroactively inflated a month of old ones the instant it did. The table is now a price history: each call is priced at the rates in force when it was made.
"deepseek-v4-pro": {
"input_per_m": 0.435, "output_per_m": 0.87,
"pinned": true, // upstream lists one of the two rates; don't clobber this
"revisions": [{
"effective_from": "2026-08-16T16:00:00Z",
"input_per_m": 1.32, "output_per_m": 3.96,
"off_peak": { "utc": ["10:00-01:00", "04:00-06:00"], "multiplier": 0.5 }
}]
}prices pull appends a dated revision rather than replacing what's there, stamped with the pull time — if you know the real date, edit effective_from and the next pull leaves it alone. Providers that bill by the clock get a recurring UTC off_peak window. Base rates always hold the undiscounted price, so a window you forget to write overcharges you on paper rather than hiding spend — the same bias as turnpike's refusal to report a confident $0.
That makes prices.json the only record of your price history, so it's treated as your data rather than a cache: a pull keeps the previous copy at prices.json.bak and swaps the new one in by rename, and a table it can't parse is an error that leaves the file untouched. A revision only has to state what changed — anything it omits it inherits from the base entry, so a hand-written revision can't quietly delete pricing it didn't mention.
Pre-existing tables have neither field, so they load and price identically.
turnpike stats --by-tool. Every call now records how its identity was learned, so --by-tool can group each one under its single best identity — the declared x-turnpike-client header when the caller sends one, otherwise the observed calling process, falling back to the User-Agent. One column per call instead of three you have to reconcile by hand. The new column forward-migrates onto existing databases; old rows keep the historical client-then-process order.
check has a fourth exit code. 3 is unknown — nothing is broken, turnpike just can't vouch for the number yet (no calls recorded, or some have no price). Previously that collapsed into 2 alongside a typo'd --budget and corrupt data, which meant "go investigate" and "wait for a price table" were the same signal:
turnpike check --budget 50/day -q
case $? in
0) ;; # under budget
1) ntfy send "LLM budget blown" ;; # at/over — the actionable alert
3) ;; # unknown — no data yet, or a price gap; not an error
*) exit $? ;; # 2: something's actually broken
esacIf you branch on check's exit code, this is the one thing to look at when upgrading.
Install: cargo install turnpike (crates.io) or cargo install --path . from a clone.
Full changelog: v0.2.0...v0.3.0
turnpike 0.2.0
Budget checks. turnpike check --budget 50/day answers whether spend in a window crossed a number, and sets its exit code — 0 under, 1 at or over, 2 on error — so it composes with whatever already alerts you:
turnpike check --budget 50/day -q || ntfy send "LLM budget blown"Calendar windows (day/week/month, local time, so month matches a billing cycle) or rolling ones (300/7d, 20/24h). --json for the full verdict. Calls turnpike can't price are counted as $0 and warned about on stderr in every mode, including -q — an under-report biases the verdict toward "under," which is the direction that silently won't fire your alert.
<provider>.localhost now works everywhere. Listeners bind both loopback stacks (127.0.0.1 and ::1, never 0.0.0.0), so name-based addressing no longer depends on your client falling back to IPv4. One port — 4000 — reaches every provider by name.
Docs. README leads with name-based addressing, adds an alternatives comparison, documents that start has no daemon mode (systemd unit included), and corrects two claims that had drifted from the code.
Install: cargo install turnpike (crates.io) or cargo install --path . from a clone.
Full changelog: v0.1.0...v0.2.0