Skip to content

cloudflare-workers-ai 0.0.4: quota row shows today's actually used Neurons - #366

Open
skygunner wants to merge 3 commits into
magpie-community:mainfrom
skygunner:cf-neurons-usage-0.0.4
Open

skygunner wants to merge 3 commits into
magpie-community:mainfrom
skygunner:cf-neurons-usage-0.0.4

Conversation

@skygunner

@skygunner skygunner commented Oct 24, 2026 •

Copy link
Copy Markdown

Problem

The usage() hook hard-coded amount: null on the premise that "Cloudflare publishes no REST for per-account Neurons (dashboard only)". magpie renders a null amount as a full allowance, so an account that had already burned its entire daily 10,000 Neurons showed as 10,000 / 10,000 · 100% remaining while every inference request failed with:

AiError: you have used up your daily free allocation of 10,000 neurons (HTTP 429, code 3036)

Verified live on 2026-10-24: the account consumed 10,756.27 Neurons in one 16-minute window (Cloudflare GraphQL Analytics), after which the gateway silently stopped routing to Workers AI members for the rest of the day — and the quota card still read 100% remaining.

Fix

Workers AI indeed has no REST endpoint for Neurons, but the GraphQL Analytics API does — POST /client/v4/graphql, aiInferenceAdaptiveGroups.sum.totalNeurons, filterable by datetimeHour. usage() now:

  • sums today's UTC-day buckets into amount (an empty bucket list is a real zero; a missing list, an errors payload, or a failed request keeps null with a display hint naming the missing Account Analytics → Read scope);
  • reports the window's used percent (min(100, 100 · amount / limit)) — this is what the router acts on (see below);
  • sends span in seconds per the contract (internal/plugin/providers.go: // seconds); the previous 86,400,000 (ms) made magpie derive the window start as resetsAt − span = 2026-10-25 minus 1,000 days = the bogus 2024-01-29 the card chart showed;
  • keeps the previous behavior untouched otherwise (window, resetsAt, plan detection, fallbacks).

Verified against the real API: a token without the Analytics scope returns 200 + {"code":"authz","message":"not authorized for that account"} — handled as unknown, no crash, no fake zero. Same query against a token with the scope returns the true consumed amount (10,756.27 for the account above).

Routing-layer effect (why this matters beyond the card)

Cloudflare's 429 carries no reset hint — no Retry-After header, no timestamp in the body (verified against the live endpoint). magpie's router then falls back to quotaRest = 15 * time.Minute (internal/gateway/routing.go: "out of quota, with no word of when it resets"): the channel is retried every 15 minutes, eats a 429, and rests again — for the ~10 hours until 00:00 UTC, while the card's countdown shows a meaningless "recovers in ~14 minutes".

Once this plugin reports used ≥ ~100% with the window's resetsAt, the router takes the other branch: Allowance.Full (internal/provider/routing.go) benches the channel until the window renews (capped at longestQuota = 8 days; see TestRoutingHoldsAWindowRestToLongestQuota). So the fix is not cosmetic — it is what finally tells magpie when the quota is back. Note the router keys on the used percent, not on amount/limit (those only feed the card) — hence the used field in the window.

Tests

  • 3 new cases in test/plugin.test.mjs (sum → amount + used%, 403 → null, errors-payload → null + empty-day → 0): node --test passes 18/18
  • version bumped 0.0.3 → 0.0.4; README (en/zh/ja) documents the new optional Account Analytics → Read scope and the misleading-full-allowance symptom in Troubleshooting

Note for reviewers: magpie core still renders amount: null as an untouched allowance (UsageWindow.Amount float64 + app.js w.amount || 0), which amplifies this — tracked in yetone/magpie#3487.

vincent added 2 commits October 24, 2026 23:26
…urons

The usage() hook returned amount: null on the premise that Cloudflare
publishes no API for per-account Neurons. magpie renders a null amount
as a full allowance (10,000 / 10,000, 100% left), so an account that had
already burned its whole day showed as untouched while every request
failed with 'used up your daily free allocation'.

Cloudflare's GraphQL Analytics API does report per-account Neurons
(aiInferenceAdaptiveGroups.sum.totalNeurons). Sum today's buckets into
amount; keep null (with a display hint naming the missing scope) when
the token lacks Account Analytics -> Read, the payload carries errors,
or the request fails. An empty bucket list stays a real zero.
UsageWindow.span is documented as seconds (internal/plugin/providers.go),
but the window carried DAY_MS (86,400,000). magpie reads it as seconds and
derives the window start as resetsAt - span: 2026-10-25 minus 1,000 days
lands on 2024-01-29, the bogus '1月29日' the quota card chart showed.
@skygunner

Copy link
Copy Markdown
Author

Added 5241c21: the window's span was sent as DAY_MS (ms) while magpie's UsageWindow.span contract is seconds — magpie derived the window start as resetsAt − span, so 2026-10-25 − 86,400,000 s (= 1,000 days) produced the bogus 2024-01-29 start behind the '1月29日' axis label on the quota card. Now sends 86400 (seconds); node --test still 18/18.

magpie's router benches an exhausted channel on the window's used
percent (Allowance.Full), not on Amount/Limit — Amount only feeds the
card. With amount known, also report used = min(100, 100*amount/limit)
so a spent account rests until resetsAt (next UTC midnight) instead of
cycling the 15-minute no-reset quota backoff.
@skygunner

Copy link
Copy Markdown
Author

Added 1e3bd71 after verifying the routing path in magpie's source: the router benches an exhausted channel on the window's used percent (Allowance.Full, internal/provider/routing.go) — amount/limit only feed the card. The window now also reports used = min(100, 100·amount/limit), so a spent account rests until the window's resetsAt (next UTC midnight, within longestQuota) instead of cycling the 15-minute no-reset backoff (quotaRest, internal/gateway/routing.go:156) every quarter hour until 00:00 UTC — Cloudflare's 429 itself carries no Retry-After or reset timestamp (verified against the live endpoint). PR body updated with the full story; node --test 18/18.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant