ai_call per
attempt — success, error, or cancelled — including tokens (if the provider
reports them), latency, and any error message. That single table powers the
admin dashboard at /admin/usage and is all you need to spot a runaway model
or a broken provider.
Prerequisites
- Migrated DB so the
ai_calltable exists (pnpm db:push). - A few real AI calls behind you — the table is empty until then.
- Read
src/ai/manager.tsrecordCall()— that’s where every row is born.
The columns that matter
userId, (provider, model), status, createdAt cover the
queries the dashboard runs.
Why no cost column
We deliberately don’t compute or store dollar cost. Provider pricing is moving target — caching tiers, fine-tune surcharges, agreement discounts — and any local copy drifts within weeks. Vibestrap’s job is to record what happened (calls, tokens, latency, errors); your provider’s billing dashboard is the source of truth for what it cost. If your product does need a cost view (e.g., you bill end-users by provider call), add acost_micro_cents column back with your own
estimation logic. The schema is intentionally trivial to extend.
Why tokens can be NULL
Token-returning APIs (OpenAI / Anthropic / OpenRouter chat) populateinputTokens and outputTokens. Image / video / audio APIs (Replicate /
fal.ai) typically do not — those columns stay NULL for those rows. Don’t
sum them as “0” silently; treat NULL as “not applicable” in your queries.
Step-by-step: add a usage report
Three useful queries straight againstai_call.
Daily call volume, last 30 days
Error rate per model
Latency p95 per model
Postgres haspercentile_cont:
Tying calls to credits
Every successful chat call passes through the manager’s token-based credit reservation. The sameai_call row that records inputTokens /
outputTokens is the basis for tokensToCredits() (in
src/credits/index.ts). If you change siteConfig.credits.perKToken,
historical rows don’t get re-priced — only future calls.
For images, the manager charges siteConfig.credits.perImage per
generated image, regardless of provider. Override in your own code
if you want compute-time-based pricing.