Added
-
๐ Live quota from response headers (t2) โ every chat-completion response already carries
x-ratelimit-*headers. The daemon parses them passively on every routed request (and even on health-check pings), exposing live per-provider quota on the TUI footer and Web Dashboard. Zero extra network requests, zero quota waste.- 8 header variants supported in priority order:
x-ratelimit-remaining-requests/-limit-requests(SambaNova-style)x-ratelimit-remaining/-limit(Mistral / generic)ratelimit-remaining-requests/ratelimit-limit-requests(proxy-stripped)ratelimit-remaining/ratelimit-limit(proxy-stripped generic)x-ratelimit-remaining-requests-day/-limit-requests-day(SambaNova daily)x-ratelimit-remaining-day/-limit-day(generic daily)x-ratelimit-remaining-tokens/-limit-tokens(Cerebras tokens)x-ratelimit-remaining-tokens-minute/-limit-tokens-minute(Cerebras token-minute)
- First-match-wins priority โ when multiple pairs are present, the most-specific wins (e.g. daily variant beats generic).
- Case-insensitive โ some proxies vary casing; we handle
X-RateLimit-Remainingandx-ratelimit-remainingidentically. - Garbage-safe โ
limit: 0, non-numeric values, or missing pairs returnnull(no crash, no div-by-zero). - Staleness 5 min โ snapshots older than
STALENESS_MSare excluded fromgetAllQuotas()so the consumer only sees fresh data. The existing active/api/v1/keyfetcher stays as the fallback for idle providers. - Merge with active fetcher โ
getQuota(providerKey)returns whichever is freshest, so quota is always live when traffic flows.
- 8 header variants supported in priority order:
-
๐ฐ๏ธ TUI footer chip โ
๐ groq 78% ยท sambanova 41%(top 5 most-depleted first; icon escalation ๐ healthy โ โ ๏ธ โค 25% โ ๐จ โค 10%). Renders on the same footer line as the existing probe-cache chip. -
๐ Web Dashboard
Provider Quotasection โ per-provider card with source icon (๐ฐ๏ธ passive header vs ๐ active fetcher fallback), provider key, animated progress bar (green โฅ 25% / amber 10โ25% / red โค 10%), and percent label. Auto-sorted most-depleted first. Updated every/api/router/statspoll (5 s). -
๐ Daemon
/api/router/statsexposesquotaโ{ providerKey: { remaining, limit, percent, windowType, source, lastUpdated } }. Thesourcefield tells you whether the value came from a passive header ('header') or the active fetcher ('endpoint') โ useful for debugging why a provider shows the number it does. -
๐งช
processResponseHeaders(providerKey, headers)โ drop-in hook for any future surface (Tauri Desktop, scripts, IDE plugins) that wants to feed the passive map.
Changed
-
๐ฉบ Daemon reverse proxy (
buildUpstreamMetainsrc/core/router-daemon.js) now callsprocessResponseHeaders(providerKey, response.headers)for every routed upstream response, both streaming and non-streaming. Zero new code paths, zero new dependencies. -
๐ก Health-check pings (
src/core/ping.js) also callprocessResponseHeaders(providerKey, resp.headers), so even the CLI-only flow (no daemon) shows live quota after the first ping cycle. -
๐ง Internal refactor โ the existing
extractQuotaPercentinsrc/core/ping.jsnow uses the sharedextractQuota()fromprovider-quota-fetchers.js. Same 8-variant parser, single source of truth, no behaviour change for existing callers.
Maintenance
- ๐งช +37 unit tests (
test/passive-quota.test.js, new file): HEADER_PAIRS structure, STALENESS_MS, all 8 header pairs parse correctly, robustness (missing fields, NaN, limit=0, negative limit, empty object, null/undefined headers), first-pair-wins priority, case-insensitivity, Fetch Headers object support,processResponseHeaderswrites/overwrites/ignores bad providerKey,getQuotapassive fresh/stale/custom maxAgeMs,getAllQuotasunions both stores / excludes stale,formatQuotaStatushealthy/low/critical + day window + stale/missing. - ๐งช 632 โ 671 tests passing (
pnpm test), 118 โ 125 suites. - ๐งน
vite buildsucceeds. - ๐ README updated with a new ๐ Live quota from response headers section (8 variants table, where it shows up,
/stats.quotaJSON shape).
Inspiration
This implementation is informed by apmantza/pi-free's lib/quota-monitor.ts (which itself credits us: "Inspired by free-coding-models' extractQuotaPercent"). The inspiration flows both ways โ we extend their 6-pair design to 8 variants (adding the Cerebras tokens / token-minute pairs we already supported) and ship a Web Dashboard renderer that surfaces them as live progress bars instead of just CLI text.
Files
- Modified:
src/core/provider-quota-fetchers.js(+228 lines: new pure-logic section),src/core/ping.js(callprocessResponseHeaderson every ping),src/core/router-daemon.js(call on every routed response + exposequotain/stats),src/tui/render-table.js(new footer chip),src/tui/app.js(threadquotathrough),web/src/components/router/RouterView.jsx(newProvider Quotasection),web/src/components/router/RouterView.module.css(7 new classes),README.md,tasks/t2.md,package.json(test script). - New:
test/passive-quota.test.js(340 lines, 37 tests),changelog/v0.5.59.md.