UNPKG

@tanstack/ai

Version:

Type-safe TypeScript AI SDK for streaming chat, tool calling, agents, structured outputs, and multimodal generation.

129 lines (128 loc) • 5.52 kB
//#region src/utilities/ag-ui-usage.ts function definedDetails(value) { return Object.keys(value).length > 0 ? value : void 0; } function withoutKey(value, key) { const next = { ...value }; delete next[key]; return next; } function isTanstackUsage(usage) { return typeof usage === "object" && usage != null && !Array.isArray(usage) && "promptTokens" in usage; } function toSpecTokenUsage(usage, options) { const { promptTokens, completionTokens, totalTokens, promptTokensDetails, completionTokensDetails, ...rest } = usage; const spec = { ...options?.provider !== void 0 ? { provider: options.provider } : {}, ...options?.model !== void 0 ? { model: options.model } : {}, inputTokens: promptTokens, outputTokens: completionTokens, totalTokens }; const cachedInputTokens = promptTokensDetails?.cachedTokens; if (cachedInputTokens !== void 0) spec.cachedInputTokens = cachedInputTokens; if (promptTokensDetails?.cacheWriteTokens !== void 0) spec.cacheWriteInputTokens = promptTokensDetails.cacheWriteTokens; const reasoningTokens = completionTokensDetails?.reasoningTokens; if (reasoningTokens !== void 0) spec.reasoningTokens = reasoningTokens; const leftoverPrompt = promptTokensDetails ? definedDetails(withoutKey(promptTokensDetails, "cachedTokens")) : void 0; const leftoverCompletion = completionTokensDetails ? definedDetails(withoutKey(completionTokensDetails, "reasoningTokens")) : void 0; return { usage: [spec], leftover: definedDetails({ ...rest, ...leftoverPrompt !== void 0 ? { promptTokensDetails: leftoverPrompt } : {}, ...leftoverCompletion !== void 0 ? { completionTokensDetails: leftoverCompletion } : {} }) }; } function sumNumbers(current, next) { if (!current) return next; if (!next) return current; const result = { ...current }; for (const key of Object.keys(next)) { const value = next[key]; if (typeof value !== "number") continue; const previous = current[key]; result[key] = (typeof previous === "number" ? previous : 0) + value; } return result; } function sumOptional(current, next) { if (current === void 0) return next; if (next === void 0) return current; return current + next; } /** * Add two usage totals, as for a parent run and its children. Numbers add * up. `billed` adds up only in the same unit. `providerUsageDetails` is * opaque, so the latest one stays. `@tanstack/ai-persistence` sums per-run * usage with the same rules. */ function addTokenUsage(current, next) { const promptTokensDetails = sumNumbers(current.promptTokensDetails, next.promptTokensDetails); const completionTokensDetails = sumNumbers(current.completionTokensDetails, next.completionTokensDetails); const costDetails = sumNumbers(current.costDetails, next.costDetails); const cost = sumOptional(current.cost, next.cost); const durationSeconds = sumOptional(current.durationSeconds, next.durationSeconds); const unitsBilled = sumOptional(current.unitsBilled, next.unitsBilled); const billed = current.billed && next.billed && current.billed.unit === next.billed.unit ? { quantity: current.billed.quantity + next.billed.quantity, unit: current.billed.unit } : next.billed ?? current.billed; const providerUsageDetails = next.providerUsageDetails ?? current.providerUsageDetails; return { ...current, ...next, promptTokens: current.promptTokens + next.promptTokens, completionTokens: current.completionTokens + next.completionTokens, totalTokens: current.totalTokens + next.totalTokens, ...promptTokensDetails && { promptTokensDetails }, ...completionTokensDetails && { completionTokensDetails }, ...cost !== void 0 && { cost }, ...costDetails && { costDetails }, ...durationSeconds !== void 0 && { durationSeconds }, ...unitsBilled !== void 0 && { unitsBilled }, ...billed && { billed }, ...providerUsageDetails && { providerUsageDetails } }; } function rebuildTokenUsage(usage, leftover) { if (isTanstackUsage(usage)) return usage; if (Array.isArray(usage)) return fromSpecTokenUsage(usage, leftover); return fromSpecTokenUsage(void 0, leftover); } function fromSpecTokenUsage(usage, leftover) { const spec = usage?.reduce((total, entry) => { for (const key of [ "inputTokens", "outputTokens", "totalTokens", "cachedInputTokens", "cacheWriteInputTokens", "reasoningTokens" ]) if (entry[key] !== void 0) total[key] = (total[key] ?? 0) + entry[key]; return total; }, {}); if ((usage === void 0 || usage.length === 0) && leftover == null) return; const { promptTokensDetails: leftoverPromptDetails, completionTokensDetails: leftoverCompletionDetails, ...leftoverRest } = leftover ?? {}; const promptTokensDetails = definedDetails({ ...spec?.cachedInputTokens !== void 0 ? { cachedTokens: spec.cachedInputTokens } : {}, ...spec?.cacheWriteInputTokens !== void 0 ? { cacheWriteTokens: spec.cacheWriteInputTokens } : {}, ...leftoverPromptDetails }); const completionTokensDetails = definedDetails({ ...spec?.reasoningTokens !== void 0 ? { reasoningTokens: spec.reasoningTokens } : {}, ...leftoverCompletionDetails }); return { promptTokens: spec?.inputTokens ?? 0, completionTokens: spec?.outputTokens ?? 0, totalTokens: spec?.totalTokens ?? 0, ...leftoverRest, ...promptTokensDetails !== void 0 ? { promptTokensDetails } : {}, ...completionTokensDetails !== void 0 ? { completionTokensDetails } : {} }; } //#endregion export { addTokenUsage, fromSpecTokenUsage, isTanstackUsage, rebuildTokenUsage, toSpecTokenUsage }; //# sourceMappingURL=ag-ui-usage.js.map