跳到正文
Indiana
控制台
文档
章节目录 / AI Token 计量与恢复

AI Token 计量与恢复

配置模型价格,接入 AI SDK / OpenRouter,持久保存用量并恢复原扣款。

SDK 0.0.1更新于

配置模型与可信计量

npm @tinkerlandai/indiana-sdk@0.0.1 提供服务端 AI SDK v7 中间件及独立 OpenRouter 入口。先保存 AI 计量功能及模型价格/加价配置,授予客户可用额度。Key 需要 credits:read 和 credits:consume。createConfiguredAiPricing 读取当前配置;新配置保存后重新创建 pricing,已有报价保持原价格。

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,});

OpenRouter 自动采集

宿主安装 @openrouter/sdk;当前适配 chat.send。下面是业务片段,allocateDurableProviderCallContext 与 saveUsageRecord 必须由宿主实现:每次上游调用先持久分配独立幂等键,绑定已验证 customerId、entitlementId 和可选 entityId,并保存完整记录。不能用只存在内存的随机数或空回调替代。

配置价格键须覆盖响应的真实模型(openrouter/ 前缀),不是只覆盖请求路由名称。reasoning: output 把推理计入输出价,separate 使用独立推理价。缺少最终 Token 或价格明确报错;显式免费模型记录真实 Token 和零费用。流式调用在结束时结算,取消或中断保存 incomplete,不推算用量。

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 与失败恢复

中间件支持 LanguageModelV4;模型和供应商由宿主提供。每个工具步骤独立计量,不能共用整个用户请求的幂等键。记录依次保存 captured、quoted、settled;先保存真实用量和完整原报价,再原子消费。模型生成与余额消费不是跨服务事务,额度不足或存储失败会明确抛错。

上面的恢复片段适用于 AI SDK quoted 记录。OpenRouter 用 trackOpenRouterUsage,并传回原 generationId、真实模型、usage 和 quote,以保留供应商成本。收费响应丢失只重放原扣款,不重新调用模型或按新价格重报价。实际用量含零费用事件可用 readUsageAnalytics;积分来源分配分析使用 readCreditAnalytics,不把不同单位直接相加。

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");