ai-meter
PG-atomic reserve/reconcile token metering for LLM calls, per-tenant spend caps, a circuit breaker, and a MinHash dedup gate. Integer credits only, no floats.
@caisson/ai-meter is the money path for metered AI inference: estimate a call's cost
before it runs, reserve that amount up front, then true the charge to the provider's
actual usage once the call completes. Built on the integer credit ledger, so a charge is
never a float and never drifts.
What it does
- Estimate → reserve → reconcile.
reserve()prices a call from a versioned, per-provider/modelprice book and debits the wallet before the provider is ever called, a short wallet or an open circuit breaker fails the call with no spend and no provider round-trip.reconcile()trues the reservation to the provider's reported usage: refunds an over-reservation, charges a shortfall, or leaves the ledger untouched when the estimate was exact. - A per-tenant spend window + soft/hard caps. Every reserve bumps an atomic running-spend counter for the tenant's current window (day/month/etc.); crossing a soft cap warns, crossing a hard cap trips a circuit breaker so every subsequent call fails closed until an operator resets it.
- A bundled, overridable price book. Ships default per-million-token rates for common
provider/model pairs; a buyer can override the whole book or the credit denomination.
resolvePriceEntrythrows on an unrecognized provider/model instead of metering at zero. - Idempotent by construction. Both
reserve()andreconcile()key off the caller'scallId: a retried call settles exactly once instead of double-charging. - A pre-call dedup gate.
checkDedupGate()flags a prompt that's near-identical to one already in flight (an agent loop rewording a retry, a user re-asking the same question) before the price book ever prices it. Detection only, it never auto-skips a call or moves a credit itself.
Quickstart
import { withTenant } from "@caisson/tenancy-rls";
import { reserve, reconcile } from "@caisson/ai-meter";
await withTenant(db, accountId, async (tx) => {
const reserved = await reserve(tx, {
accountId,
callId,
provider: "openai",
model: "gpt-4o",
lane: "default",
messages: [{ role: "user", content: "hello" }],
});
// ... call the provider, using reserved.reservedCredits to size the request ...
await reconcile(tx, {
accountId,
callId,
provider: "openai",
model: "gpt-4o",
lane: "default",
reservedCredits: reserved.reservedCredits,
usage: { inputTokens: 12, outputTokens: 40, cachedInputTokens: 0 },
windowKey: reserved.windowKey,
});
});Circuit breaker
assertBreakerClosed runs before every reserve(): an open breaker throws
SpendCapError (402) with no provider call made. A crossed hard cap trips it
(tripBreaker); it stays open until an operator calls resetBreaker, so a runaway loop
can't spend past the cap on the next retry.
import { assertBreakerClosed, resetBreaker } from "@caisson/ai-meter";
await assertBreakerClosed(tx, accountId, "default"); // throws SpendCapError if open
// ... after investigating a tripped breaker ...
await resetBreaker(tx, accountId, "default");Dedup-before-meter gate
checkDedupGate runs a dependency-free MinHash/LSH similarity check against recent calls
in the same account and scope, ahead of the price-book estimate, a Jaccard similarity
above 0.92 returns duplicate-of so the caller can choose to skip or reuse the earlier
result.
Configuration
parsePriceBook and parseCreditConversion validate an operator-supplied price book or
credit denomination before it replaces BUNDLED_PRICE_BOOK / CREDIT_CONVERSION: an
invalid override fails closed rather than metering silently at zero.
Composing with the base
ai-meter is a base primitive, it never imports an edition. It runs on the same
Postgres-atomic accounting as @caisson/credits and is scoped per tenant through
@caisson/tenancy-rls's withTenant. The AI Production Kit's inference gateway composes
reserve()/reconcile() around the provider call site; ai-meter itself never talks to
a provider.
Sold standalone or in AI-Production
Buy ai-meter standalone onto the free base, or get it, plus guardrails and the prompt
registry, composed into the AI-Production bundle.
AI-Production
The production-rigor layer for AI features, token metering with spend caps, a CI eval gate, PII/moderation guardrails, and a versioned prompt registry.
ai-evals
A regression gate for prompt and model changes, defineEval() scores a dataset through a grader taxonomy, gateAgainstBaseline() fails the build on a real score drop, all offline and deterministic.