Skip to content

feat(ui): admin-only AI usage viewer + pricing/retention editor#115

Merged
eliotlim merged 3 commits into
mainfrom
feat/ai-usage-admin-ui
Jul 6, 2026
Merged

feat(ui): admin-only AI usage viewer + pricing/retention editor#115
eliotlim merged 3 commits into
mainfrom
feat/ai-usage-admin-ui

Conversation

@eliotlim

@eliotlim eliotlim commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Problem

C1 added the AI usage-attribution backend, but there was no way for an admin to see usage or edit the pricing/retention that drive it. Board: C2 · Admin-only AI usage viewer + pricing/retention editor UI — the final slice of the Epic.

Solution

An admin-only surface in the Admin settings tab, plus the read-path C1 didn't expose:

  • Usage viewer — Calls / Input / Output / Total cost summary cards + a recent per-call table (time, user, model+provider, tokens, cost). Shows "Showing the latest N of M calls" when capped, and a graceful empty state before any AI is used. Backed by a new admin-gated GET /api/ai/usage (requireInstanceAdmin) via AiUsageLog.report() — read-only, never seeds (a fresh workspace reports {exists:false}).
  • Model pricing editor — per-model Input/Output $/Mtok; shipped defaults shown as placeholders, an "overridden" badge, and clear-a-field-to-reset; persists minimal-diff overrides (setAiPricing), preserving cache-tier prices.
  • Usage retention — a days control → setAiUsageRetention (updates the usage DB's autoExpiry.days).
  • Role-gating — renders nothing for non-admins (youRole fast-path + 403-from-endpoint hide, MembersSettings pattern). Viewers/guests see no AI section.

Key files: packages/ui/src/components/settings/AiUsageSettings.tsx (new) + AdminSettings.tsx mount + i18n/messages/en.ts; packages/server/src/ai/routes.ts (GET /api/ai/usage) + ai/usage.ts (report()); packages/sdk/src/{ai,routes,client,index}.ts; localClient.ts stub. New deps: none.

Before / After

Behavior Before After
Admin sees AI usage nothing
Edit model pricing nothing
Edit retention nothing
Non-admin (viewer) no AI section:

Test procedure

  • pnpm verify → green (ui 948, server 645 unit + e2e).
  • aiUsageSettings.test.tsx (9): admin shows all; viewer hidden without probing; guest-403 hide-not-break; price override round-trip; default placeholder + clear-to-reset; retention; empty state; total 2dp vs per-row 4dp; row-cap hint.
  • aiUsage.test.ts (28): GET /api/ai/usage 403 for viewer/guest; no-seed-on-read; rows newest-first + totals over EVERY row + row cap; no key/property-id leak.

Operational notes

  • No migration, no env, no deps. Admin-only (owner/admin via requireInstanceAdmin + the C1 restricted usage DB). Read-only route — doesn't weaken any C1 write-lockout/visibility invariant.
  • AI usage/pricing live under the Admin ("Backups & data") tab; kept there rather than a dedicated tab to avoid showing an empty tab to non-admins (the tab rail has no per-tab role hiding today) — an optional future tweak.
  • Based on main f6fbc55.

Verify: green — sdk 171 · ui 948 · server 645 unit + e2e (251 / 40). Gates cleared: design ✅ · code ✅ · security ✅.

🤖 Generated with Claude Code

https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG

eliotlim and others added 3 commits July 6, 2026 22:45
Add a client-reachable, admin-gated way to READ the AI usage log for the C2
UI. The usage database is created lazily and previously only its pricing +
retention were client-reachable; the rows/totals had no route.

- SDK: `aiUsage` route constant, `getAiUsage()` client method, and the
  `AiUsageRow`/`AiUsageTotals`/`AiUsageResponse` types (raw `p_*` property ids
  resolved to named fields so the client never depends on the schema).
- Server: `AiUsageLog.report()` projects the DB into recent rows (newest first,
  capped) + aggregate totals + the retention window, WITHOUT seeding (a
  workspace that never used AI reports `exists:false`, no phantom page).
- `GET /api/ai/usage` behind `requireInstanceAdmin` (same gate as pricing).
- LocalDataClient stub for the no-hosted-engine in-app mode.

Tests: viewer/guest 403; owner+admin get rows+totals+retention; a read on a
fresh workspace reports exists:false and creates no database.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG
C2 admin-facing surface for the C1 backend, mounted in the Admin ("Backups &
data") settings tab and RENDERING NOTHING for a non-admin — the whole surface
is self-gated on the effective instance role AND re-gated by a 403 from the
admin-only endpoints (the MembersSettings hide-not-break pattern), so a viewer
or guest sees none of it.

- AiUsageSettings.tsx: a recent-usage table + totals summary (graceful empty
  state when the usage DB doesn't exist yet), an editable per-provider/model
  $/Mtok pricing table (numeric-sanitised; persists only the models that differ
  from the shipped default, carrying cache prices through), and a retention-days
  control (setAiUsageRetention).
- Wired into AdminSettings; en i18n strings.

Tests (aiUsageSettings.test.tsx): admin sees viewer+editor+retention; a viewer
renders nothing and never probes the admin endpoints; a 403 guest hides;
editing a price persists as a minimal override and round-trips; retention calls
setAiUsageRetention; empty state renders without a table.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG
- pricing inputs show the shipped default as a placeholder; clearing a
  field drops the override so the model falls back to its default
- reword the pricing hint to match ("Clear a price to fall back to its default")
- keep the model id on one line in the usage table (whitespace-nowrap)
- format the total-cost summary card to 2dp; per-row cost stays 4dp
- add a "Showing N of M calls" hint when totals exceed the shown rows;
  clarify the AiUsageTotals + buildOverride comments (totals span ALL rows)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X78XTAemKrFw1uJpDpCEaG
@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app.book.pub Ready Ready Preview, Comment Jul 6, 2026 3:41pm

Request Review

@eliotlim eliotlim merged commit da593c4 into main Jul 6, 2026
7 checks passed
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