usage-meter-test
v0.1.0
Published
Usage metering core (credits + quota + holds + ledger) for AI and other compute costs.
Readme
usage-meter
A small TypeScript library for credits + quota + holds + ledger metering (e.g. AI token billing).
Core concepts
- Credits: integer unit (
bigint) deducted from a user account. - Hold: pre-authorize a maximum cost (freeze credits + reserve quota).
- Capture: settle actual cost (deduct balance + release unused hold/quota).
- Release: cancel a hold (unfreeze credits + unreserve quota).
- Ledger: append-only, idempotent charge records.
Quick start (in-memory)
import { DEFAULT_PRICING_CONFIG, InMemoryMeteringPersistence, UsageMeter, bps, credits } from "usage-meter";
const persistence = new InMemoryMeteringPersistence({ quota: true, usage: true });
// Seed credits
await persistence.accounts.addCredits("u1", credits(10_000));
// Optional: quota policy (daily 5_000 credits)
persistence.quota?.setPolicy("u1", { policyId: "free-daily", period: "daily", limitCredits: credits(5_000) });
const meter = new UsageMeter(persistence, {
pricing: {
...DEFAULT_PRICING_CONFIG,
inputTokenCost: credits(1),
outputTokenCost: credits(2),
minCost: credits(1),
defaultModelMultiplierBps: bps(10_000),
modelRules: [{ model: "premium", multiplierBps: bps(20_000) }],
toolRules: [{ tool: "web_search", unitCost: credits(50) }],
},
});
const estimate = meter.estimate({
userId: "u1",
requestId: "req-1",
model: "premium",
tokens: { inputTokens: 100, outputTokens: 200 },
maxOutputTokens: 800,
tools: [{ tool: "web_search", units: 1 }],
});
// Hold max possible cost (freeze + reserve quota)
await meter.hold({
userId: "u1",
requestId: "req-1",
amount: estimate.maxPossibleCost,
idempotencyKey: "hold:req-1",
expiresAt: new Date(Date.now() + 2 * 60 * 1000),
});
// Capture actual cost when done
await meter.capture({
userId: "u1",
requestId: "req-1",
actualCost: estimate.estimatedCost,
idempotencyKey: "charge:req-1",
usage: {
requestId: "req-1",
model: "premium",
tokens: { inputTokens: 100, outputTokens: 200 },
tools: [{ tool: "web_search", units: 1 }],
},
});Prisma adapter (route A)
- Add the models/enums from
usage-meter/prisma/schema.prisma.snippetinto your app'sschema.prisma - Run
prisma migrate dev(orprisma migrate deploy)
import { createPrismaPersistence, UsageMeter, DEFAULT_PRICING_CONFIG } from "usage-meter";
import { PrismaClient, Prisma } from "@prisma/client";
const prisma = new PrismaClient();
const persistence = createPrismaPersistence(prisma, {
isolationLevel: Prisma.TransactionIsolationLevel.Serializable,
});
const meter = new UsageMeter(persistence, { pricing: DEFAULT_PRICING_CONFIG });