Skip to content
Indiana
Dashboard
Docs
Browse docs / AI token accounting & recovery

AI token accounting & recovery

Configure model pricing, integrate AI SDK / OpenRouter, persist usage and recover settlement.

SDK 0.0.1Updated

Configure models and trusted accounting

npm @tinkerlandai/indiana-sdk@0.0.1 provides server middleware for AI SDK v7 and a separate OpenRouter entry. Save an AI metered feature and model prices/markups, then grant customer capacity. Grant credits:read and credits:consume. createConfiguredAiPricing reads current configuration. Recreate pricing after saving changes; saved quotes retain their original prices.

server/ai-pricing.ts · fragmentTypeScript
import { createConfiguredAiPricing } from "@tinkerlandai/indiana-sdk/server";import { groove } from "@/lib/groove-server"; const pricing = await createConfiguredAiPricing(groove, {  entitlementId: configuredAiEntitlementId,});

Automatic OpenRouter accounting

Install @openrouter/sdk in the host; the adapter wraps chat.send. This fragment requires host implementations of allocateDurableProviderCallContext and saveUsageRecord: durably allocate a unique key for each provider call, bind the verified customerId, entitlementId and optional entityId, and store complete records. In-memory IDs and empty callbacks are insufficient.

Price keys must cover the actual response model with the openrouter/ prefix, not just the requested route. reasoning: output includes reasoning in output pricing; separate uses its own rate. Missing final tokens or prices fail explicitly. Explicit free prices record real tokens and zero cost. Streams settle at completion; cancellation or interruption stores incomplete without estimating usage.

server/openrouter.ts · fragmentTypeScript
import { OpenRouter } from "@openrouter/sdk";import { createOpenRouterUsageClient } from "@tinkerlandai/indiana-sdk/openrouter"; const openRouter = createOpenRouterUsageClient({  openRouter: new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY! }),  pricing,  client: groove,  reasoning: "output",  context: () => allocateDurableProviderCallContext(),  persist: (record) => saveUsageRecord(record),});await openRouter.chat.send({  chatRequest: {    model: configuredOpenRouterModelId,    messages: [{ role: "user", content: prompt }],    stream: false,  },});

AI SDK v7 and settlement recovery

The middleware supports LanguageModelV4; hosts supply the model and provider. Each tool step needs separate accounting rather than sharing one user-request key. Store captured, quoted and settled records in order, preserving actual usage and the frozen quote before atomic consumption. Generation and balance consumption are not a cross-service transaction; insufficient credits or storage failures propagate.

The recovery fragment applies to AI SDK quoted records. For OpenRouter, use trackOpenRouterUsage with the saved generationId, actual model, usage and quote to retain provider costs. A lost settlement response replays the original charge without rerunning the model or repricing. readUsageAnalytics includes zero-cost events; readCreditAnalytics reports credit allocations. Keep different measurement units separate.

server/ai-sdk.ts · fragmentTypeScript
import { generateText, wrapLanguageModel } from "ai";import { createAiUsageMiddleware } from "@tinkerlandai/indiana-sdk/server"; const model = wrapLanguageModel({  model: configuredProviderModel,  middleware: createAiUsageMiddleware({    pricing, client: groove, modelId: configuredPricingModelId,    reasoning: "output",    context: () => allocateDurableProviderCallContext(),    persist: (record) => saveUsageRecord(record),  }),});await generateText({ model, prompt });
server/recover-ai-charge.ts · fragmentTypeScript
// Restore the saved pricing helper, context and complete original quote.// OpenRouter recovery also preserves generationId and providerCostUsd.const result = await pricing.trackTokens(groove, {  ...savedRecord.context,  quote: savedRecord.quote,});if (!result.receipt.accepted) throw new Error("AI settlement rejected");