Skip to content

feat(billing): show where AI credits go on the billing page - #2529

Merged
Makisuo merged 10 commits into
fix/ai-credits-bill-provider-costfrom
feat/ai-credits-overview
Nov 12, 2026
Merged

Makisuo merged 10 commits into
fix/ai-credits-bill-provider-costfrom
feat/ai-credits-overview

Conversation

@Makisuo

@Makisuo Makisuo commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #2526.

What

The billing page shows one line for AI credits today ("Code review $3.62"). This adds an AI credits page at /settings/billing/ai-credits (Settings › Billing › AI credits), linked from that row with Details, that answers where the credits went this cycle:

  • Totals: spent this cycle (since the cycle start), code reviews and the pull requests they covered, cost per review, and re-reviews (reviews after a pull request's first this cycle, one per later push).
  • By feature and By model, each with its share. Spend with no model (a security audit's sandbox fee) is an "Unattributed" row, so the table adds up.
  • Code review by repository and Most expensive pull requests, with a "Reviewed N×" chip on pull requests reviewed 4 or more times.

How

  • Per-review cost. New nullable pr_reviews.cost_microusd. When a review turn ends, beside metering, the turn's reported provider cost (from fix(billing): bill AI credits at the provider's reported cost #2526) is added to the review's row (PrReviewService.addCost, coalesce(cost, 0) + x). Superseded, failed and re-run reviews carry what they cost. Turns on the org's own OpenRouter key record nothing, since Maple bills nothing for them.
  • Query. PrReviewAnalyticsService.spend reads the cycle's reviews in Postgres: totals, by repository, top pull requests and re-review cost. It counts every review that ran, superseded ones included, and covers every org a billing org pays for.
  • API. GET /internal/billing/ai-credit-usage gains the cycle bounds, byModel (Autumn events grouped by properties.model, checked against production data) and codeReview. The code review read is optional detail: a failure or a read slower than 5 seconds drops it, never the spend.
  • UI. A page under page/settings/ai-credits, its breakdown in page/kit/billing/ai-credits.ts, built from kit pieces. The billing page's AI credits row gains the Details link; formatCredits and the surface labels moved to the billing kit. The colour dot that repeated across billing views became a round option on the kit's swatch.

Reviews from before this ships count without credits, and the page says how many.

Screenshots

From /lab/ai-credits (states code-review, chat, empty, loading, error) and /lab/billing-orgs?state=ai-credits.

Billing page: the AI credits row links to the page

The page

Light Dark
Phone (390px) No code reviews Nothing spent
Loading Failed read

Verification

  • Spend query (PGlite): superseded and started-then-failed reviews count; skipped-before-start, failed-before-start and other orgs' reviews don't; re-review cost, unpriced count.
  • addCost: adds across a failed turn and ignores another org.
  • recordReviewCost: skipped on the org's own key, outside a review, and when no call was priced.
  • Aggregates: the model split.
  • Page scene tests: fetch on entry, populated (incl. Unattributed and the unpriced note), nothing spent, failed read and retry. Billing scene test: the row's Details link.
  • Suites: backend pr-review and billing (387), AI chat billing and review (89), web billing, AI credits page and routes (123), DB parity (5).
  • Static checks: typecheck for web, API, AI and backend; oxlint -c .oxlintrc.effect.json; ui:patterns:check; ui:index:check; the kit baseline (unchanged); ios:openapi:check.
  • Review: two independent review passes. The first found that the cost was recorded only on a successful submit (fixed in the second commit: recorded per turn). The second found own-key reviews reading as unpriced and failed-before-start reviews counting (fixed in the third commit).

Not covered

  • A run that mixes priced and unpriced calls (a Workers AI fallback) records only the priced part.
  • Reply turns on a review (pr-reply) are billed as code review but have no pr_reviews row, so they show under By feature only.
  • A review re-run in a later cycle (failed, or asked for again) adds its cost to the original row, which is dated in the earlier cycle. A per-run cost ledger would fix that; it only happens when a re-run crosses a cycle boundary.

A new AI credits section under Usage: this cycle's spend, the split by feature
and by model, and for code review the cost by repository, the most expensive
pull requests and the re-reviews later pushes caused.

Each PR review now records what the provider billed for it (pr_reviews.
cost_microusd), so code review spend is read from Postgres per repository and
pull request; the feature and model splits come from Autumn's events grouped by
source and by model. Reviews from before the column count without credits and
the page says how many.
…s the bill

Review feedback: the cost was written only when a submission completed the row,
but every turn is billed. It is now added to the review's row when the turn
ends, beside metering, so superseded, failed and re-run reviews carry what they
cost; turns on the org's own key record nothing, as Maple bills nothing for them.

The breakdown counts every review that ran (a superseded one included), counts
as unpriced only a completed review with no cost, and covers every org a billing
org pays for. By model shows spend with no model as Unattributed, the code
review read gives up after 5 seconds, and pull request links say where they go.
…t never started is not counted

Second review pass: an own-key review recorded no cost, so it read as unpriced
("ran before costs were kept"); it now records 0. A review that failed before
its turn began ran nothing, so it no longer counts as a review or makes the
real one a re-review.
@Makisuo Makisuo added the preview:web Deploy a Workers-only PR preview (no ingest/Electric); see docs/pr-previews.md label Nov 12, 2026
@Makisuo
Makisuo deployed to pr-preview November 12, 2026 13:54 — with GitHub Actions Active
@Makisuo
Makisuo deployed to preview-tinybird November 12, 2026 13:54 — with GitHub Actions Active
@coderabbitai

coderabbitai Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

Explain Change →

Caution

Review failed

The pull request is closed.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@maple-review-bot

This comment has been minimized.

@maple-review-bot maple-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 inline note from Maple's review. The score and summary are in the review comment above.

@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

🍁 Maple PR preview

Note

Preview resources were removed when this pull request closed.

Final commit 49cc012 · View workflow run

@Makisuo
Makisuo deployed to pr-preview November 12, 2026 13:59 — with GitHub Actions Active
The breakdown now lives at /settings/billing/ai-credits (Settings › Billing ›
AI credits) instead of inline on the billing page; the AI credits usage row
links to it with "Details". The response carries the cycle bounds the page
dates its spend from, the breakdown view moved to page/kit/billing, and
/lab/ai-credits shows the page's states.
@Makisuo
Makisuo deployed to preview-tinybird November 12, 2026 14:13 — with GitHub Actions Active
@Makisuo
Makisuo deployed to pr-preview November 12, 2026 14:13 — with GitHub Actions Active
@maple-review-bot

This comment has been minimized.

@Makisuo
Makisuo deployed to pr-preview November 12, 2026 14:16 — with GitHub Actions Active

@maple-review-bot maple-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 inline note from Maple's review. The score and summary are in the review comment above.

@Makisuo
Makisuo deployed to pr-preview November 12, 2026 14:18 — with GitHub Actions Active
@Makisuo
Makisuo deployed to preview-tinybird November 12, 2026 14:18 — with GitHub Actions Active
@maple-review-bot

This comment has been minimized.

@Makisuo
Makisuo deployed to pr-preview November 12, 2026 14:21 — with GitHub Actions Active
… lint and page sample fixes

Maple review: a review superseded or failed mid-run before costs were kept was
counted but not reported as unpriced. Every turn now records its cost, so any
review that ran without one (and is not still running) predates the column.

Also size the billing row's Details link through typography rather than a
className on the kit link (lint:foldkit), and give the AI credits page a sample
URL in the lazy page-slot test.
@Makisuo
Makisuo deployed to pr-preview November 12, 2026 14:29 — with GitHub Actions Active
@Makisuo
Makisuo deployed to preview-tinybird November 12, 2026 14:29 — with GitHub Actions Active
@maple-review-bot

This comment has been minimized.

@Makisuo
Makisuo deployed to pr-preview November 12, 2026 14:31 — with GitHub Actions Active
…eview

The "Most spent on" tile shows the credits as its figure and the feature below
it, so a long feature name no longer runs past the tile at phone width; the
breadcrumb names the tab as the settings nav does ("Billing & usage"); and the
lab's retry answers with its fixture instead of dropping the page's read.
@Makisuo
Makisuo deployed to pr-preview November 12, 2026 14:39 — with GitHub Actions Active
@Makisuo
Makisuo deployed to preview-tinybird November 12, 2026 14:39 — with GitHub Actions Active
@maple-review-bot

This comment has been minimized.

@Makisuo
Makisuo deployed to pr-preview November 12, 2026 14:42 — with GitHub Actions Active

@maple-review-bot maple-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 inline notes from Maple's review. The score and summary are in the review comment above.

… spend when the model split fails

Maple review: a member org's page totalled the whole bill but its code review
breakdown covered only itself; it now resolves the paying org and reads it with
every org it pays for. And a failed by-model aggregate failed the whole read;
it now drops only the model table.
@Makisuo
Makisuo deployed to preview-tinybird November 12, 2026 14:50 — with GitHub Actions Active
@Makisuo
Makisuo deployed to pr-preview November 12, 2026 14:50 — with GitHub Actions Active
@maple-review-bot

This comment has been minimized.

@Makisuo
Makisuo deployed to pr-preview November 12, 2026 14:53 — with GitHub Actions Active
@Makisuo
Makisuo deployed to pr-preview November 12, 2026 14:59 — with GitHub Actions Active
@Makisuo
Makisuo deployed to preview-tinybird November 12, 2026 14:59 — with GitHub Actions Active
@maple-review-bot

maple-review-bot Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

Maple review

🟢 Confidence 10/10 · safe to merge. The tenant-scoped spend query and the additive cost_microusd write are pinned by PGlite and route tests; the fixture-only lab page is the only unexercised surface.
no findings · tests covered · risk medium · 4/4 new units observable

Adds an AI credits page at /settings/billing/ai-credits, fed by per-review pr_reviews.cost_microusd and a new PrReviewAnalyticsService.spend, with cycle bounds, byModel and an optional codeReview on the existing billing endpoint. Safe to merge: the breakdown covers the paying org and its members, and every failure path degrades instead of dropping the spend. The listed McpQueryError issues come from apps/ai's MCP registry bundled as registry.js; this diff only registers a page in apps/web/src/page/registry.ts and does not address them.

  • spend sums per-cycle pr_reviews.cost_microusd across the payer and its member orgs
  • Review turns write addCost in a finalizer; the org's own key records 0
  • byModel drops Autumn's empty group key so unlabeled spend lands in Unattributed
  • Billing page's AI credits row gains a Details link to the new page

Before merge

Fixed since the last review

  • ✅ F6 · unpricedReviews note states a false cause for reviews with no reported cost

Production impact

Open errors in the changed files (2)
Issue Service Occurrences File
@maple/mcp/errors/McpQueryError (8 issues) maple-ai 33 apps/web/src/page/registry.ts
@maple/mcp/errors/McpQueryError (2 issues) maple-chat 9 apps/web/src/page/registry.ts

After this merges, Maple checks whether they stop.

49cc012 · What was checked, coverage and telemetry details are on the Maple / review check. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@Makisuo
Makisuo deployed to pr-preview November 12, 2026 15:02 — with GitHub Actions Active
@Makisuo
Makisuo added this pull request to stack #2531 November 12, 2026 15:29
@Makisuo
Makisuo merged commit cedf936 into main Nov 12, 2026
67 of 69 checks passed
@Makisuo
Makisuo deleted the feat/ai-credits-overview branch November 12, 2026 15:29
@Makisuo
Makisuo deployed to preview-tinybird November 12, 2026 15:29 — with GitHub Actions Active
@Makisuo
Makisuo deployed to pr-preview November 12, 2026 15:29 — with GitHub Actions Active
@maple-review-bot

Copy link
Copy Markdown

Merged with 1 step from "Before merge" still open. If it is done, tick it on the review comment; if not, now is the time.

  • Migration · Check it is safe on production data and with the code still deployed

@maple-review-bot

Copy link
Copy Markdown

Maple: after this shipped

✅ Shipped clean. Nothing this change touches got worse in the hour after it deployed.

Deployed to maple-api (production) at 2026-11-12 15:40 UTC as cedf936. Compared the 60 minutes before and after.

Errors in the changed files:

  • ➖ @maple/mcp/errors/McpQueryError: 0/h → 0/h
  • ➖ @maple/mcp/errors/McpQueryError: 0/h → 0/h
  • ➖ @maple/mcp/errors/McpQueryError: 0/h → 0/h
  • ➖ @maple/mcp/errors/McpQueryError: 0/h → 0/h
  • ➖ @maple/mcp/errors/McpQueryError: 0/h → 0/h
  • ➖ @maple/mcp/errors/McpQueryError: 0/h → 0/h
  • ➖ @maple/mcp/errors/McpQueryError: 0/h → 0/h
  • ➖ @maple/mcp/errors/McpQueryError: 0/h → 0/h
  • ➖ @maple/mcp/errors/McpQueryError: 0/h → 0/h
  • ➖ @maple/mcp/errors/McpQueryError: 0/h → 0/h
Operation Calls/h Errors p95
POST /v1/events.aggregate 0 → 12 0.0% → 0.0% 0 → 797 ms

Maple looks once, an hour after the first deploy that carries the merge.

This branch was successfully deployed

2 active deployments
pr-preview — 49cc0129 Deployed Nov 12, 2026 by Makisuo via deploy-pr-preview #11658
preview-tinybird — 49cc0129 Deployed Nov 12, 2026 by Makisuo via branch #1227
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview:web Deploy a Workers-only PR preview (no ingest/Electric); see docs/pr-previews.md

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant