feat(analytics): log LLM token usage on ai-gateway + generate-profile - #46
Merged
Merged
Conversation
Both routes have been unmetered since launch — the CFO cost-visibility gap first flagged 08-28 and re-flagged 09-04 with no engineering path forward. logLlmUsage() writes provider/model/token counts into the existing events table (event_type: llm_usage) after each call; a new llm_usage query in admin/analytics.ts surfaces 7-day totals and a per-route/provider/model breakdown as a new section on /admin/analytics. Logging is best-effort and never affects the AI response in flight or already sent. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UgHaXtpMZHY1pi3sGYv6zz
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/api/tools/ai-gatewayand/api/tools/generate-profilehave been unmetered since launch — no visibility into which providers/models/token volumes the site's two LLM routes actually use. This is the CFO cost-visibility gap first flagged 2026-08-28, re-flagged 2026-09-04, and standing at flag-2 unshipped as of yesterday's (2026-09-10) CTO brief. Per the routine's own anti-duplication rule ("flagged twice → ship it, don't re-recommend a third time"), this session builds it instead of writing the prompt again.logLlmUsage()helper (src/lib/ai-providers/usageLog.ts) writes{provider, model, inputTokens, outputTokens}into the existingeventstable (event_type: 'llm_usage',page: the route name) after each call — reusing the table every other interaction on the site already logs into, rather than adding a new Neon table. Wired intoai-gateway.tsviastreamText'sonFinishcallback and intogenerate-profile.tsviagenerateText's returnedusage. Logging is best-effort (try/catch, never throws) so a DB hiccup can never affect an AI response already streaming or already sent.admin/analytics.tsgets a 6th query aggregatingllm_usageevents over the last 7 days, grouped by route/provider/model;/admin/analyticsgets a new "LLM usage (7d)" section (3 summary tiles + a breakdown table) so this is actually visible, not just logged.Livelihood stream
(Closest fit: unit-economics visibility for the SaaS/AI-tools stream — Chat Sandbox and Model Arena's cost exposure has been qualitative-only for ~10 weeks per the CFO ledger; this closes the engineering-reachable half of that gap. The other half — real dollar figures — stays blocked on Brooks's Vercel/Anthropic/OpenRouter billing-API access, unchanged.)
Files changed
src/lib/ai-providers/usageLog.ts(new) — shared best-effort loggersrc/pages/api/tools/ai-gateway.ts—onFinishhook onstreamTextsrc/pages/api/tools/generate-profile.ts— logsusagefromgenerateTextsrc/pages/api/admin/analytics.ts— newllm_usageaggregate query + response fieldsrc/pages/admin/analytics.tsx— new "LLM usage (7d)" dashboard sectionsrc/lib/ai-providers/__tests__/usageLog.test.ts(new)src/__apiTests__/tools/ai-gateway-usage-logging.test.ts(new)src/__apiTests__/tools/generate-profile-usage-logging.test.ts(new)src/__apiTests__/admin/analytics.test.ts,analytics-auth.test.ts— updated mock sequence for the new 6th query, plus one new assertion testTest steps
yarn dev, log in at/login, visit/admin/analytics— the page loads with an empty "LLM usage (7d)" state until real traffic hits the AI routes (noPOSTGRES_URLneeded for this to render; it degrades like every other section here).node_modulesdidn't exist untilyarn install --frozen-lockfile, 64.09s):yarn test→ 843/843 passing, 76 files (previously 834/834 before this PR; 3 pre-existing tests inanalytics-auth.test.tsbroke from the new 6th SQL query until their mock sequences were updated — fixed in this PR, not a regression I introduced and left).yarn lint→ clean, 0 warnings.npx tsc --noEmit→ clean, no errors.yarn build→ clean, all routes compile (including/api/tools/ai-gateway,/api/tools/generate-profile,/admin/analytics).Agent checklist
yarn buildpasses locallyyarn testpasses (no regressions to game logic)TODO, placeholder, or stub code.envvalues committedog:title,og:description,og:image,twitter:card— N/A, no new page (existing admin page,noindex)PreText,Reveal,TiltCardused for any new UI text/cards — N/A, admin dashboard uses its existing plain-table style, not the marketing-page components/adminroute, not indexed/public-funnelCLAUDE.md's existingai-gateway.ts/generate-profile.ts/admin/analytics.tsroute descriptions still accurately describe these files' purposeLearn
🤖 Generated with Claude Code
https://claude.ai/code/session_01UgHaXtpMZHY1pi3sGYv6zz
Generated by Claude Code