UNPKG

@tanstack/ai

Version:

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

195 lines (182 loc) 5.19 kB
import { describe, expect, it } from 'vitest' import { fromSpecTokenUsage, rebuildTokenUsage, toSpecTokenUsage, } from './ag-ui-usage' import type { TokenUsage } from '../types' const rich: TokenUsage = { promptTokens: 100, completionTokens: 50, totalTokens: 150, promptTokensDetails: { cachedTokens: 10 }, completionTokensDetails: { reasoningTokens: 7 }, billed: { quantity: 3, unit: 'units' }, cost: 0.02, costDetails: { upstreamInputCost: 0.01, upstreamOutputCost: 0.01 }, providerUsageDetails: { foo: 1 }, durationSeconds: 2, unitsBilled: 3, } describe('toSpecTokenUsage', () => { it('maps core fields to spec usage[0] and keeps leftovers under tanstack.usage', () => { const { usage, leftover } = toSpecTokenUsage(rich, { provider: 'openai', model: 'gpt-5.5', }) expect(usage).toEqual([ { provider: 'openai', model: 'gpt-5.5', inputTokens: 100, outputTokens: 50, totalTokens: 150, cachedInputTokens: 10, reasoningTokens: 7, }, ]) expect(leftover).toEqual({ billed: { quantity: 3, unit: 'units' }, cost: 0.02, costDetails: { upstreamInputCost: 0.01, upstreamOutputCost: 0.01 }, providerUsageDetails: { foo: 1 }, durationSeconds: 2, unitsBilled: 3, }) }) it('returns undefined leftover when only mapped token fields exist', () => { const { leftover } = toSpecTokenUsage({ promptTokens: 1, completionTokens: 2, totalTokens: 3, promptTokensDetails: { cachedTokens: 10 }, completionTokensDetails: { reasoningTokens: 7 }, }) expect(leftover).toBeUndefined() }) it('keeps unmapped detail keys in leftover', () => { const { usage, leftover } = toSpecTokenUsage({ promptTokens: 1, completionTokens: 2, totalTokens: 3, promptTokensDetails: { cachedTokens: 10, audioTokens: 4 }, completionTokensDetails: { reasoningTokens: 7, audioTokens: 1 }, }) expect(usage[0]?.cachedInputTokens).toBe(10) expect(usage[0]?.reasoningTokens).toBe(7) expect(leftover).toEqual({ promptTokensDetails: { audioTokens: 4 }, completionTokensDetails: { audioTokens: 1 }, }) }) }) describe('fromSpecTokenUsage', () => { it('rebuilds TokenUsage from spec array plus leftover', () => { const { usage, leftover } = toSpecTokenUsage(rich, { provider: 'openai', model: 'gpt-5.5', }) expect(fromSpecTokenUsage(usage, leftover)).toEqual(rich) }) it('rebuilds from spec array alone', () => { expect( fromSpecTokenUsage([{ inputTokens: 4, outputTokens: 5, totalTokens: 9 }]), ).toEqual({ promptTokens: 4, completionTokens: 5, totalTokens: 9, }) }) it('rebuilds spec-only cachedInputTokens and reasoningTokens into details', () => { expect( fromSpecTokenUsage([ { inputTokens: 1, outputTokens: 2, totalTokens: 3, cachedInputTokens: 10, reasoningTokens: 7, }, ]), ).toEqual({ promptTokens: 1, completionTokens: 2, totalTokens: 3, promptTokensDetails: { cachedTokens: 10 }, completionTokensDetails: { reasoningTokens: 7 }, }) }) it('keeps spec cachedTokens and leftover extra promptTokensDetails keys', () => { expect( fromSpecTokenUsage( [ { inputTokens: 1, outputTokens: 2, totalTokens: 3, cachedInputTokens: 10, }, ], { promptTokensDetails: { audioTokens: 5 } }, ), ).toEqual({ promptTokens: 1, completionTokens: 2, totalTokens: 3, promptTokensDetails: { cachedTokens: 10, audioTokens: 5 }, }) }) it('keeps spec reasoningTokens and leftover extra completionTokensDetails keys', () => { expect( fromSpecTokenUsage( [ { inputTokens: 1, outputTokens: 2, totalTokens: 3, reasoningTokens: 7, }, ], { completionTokensDetails: { audioTokens: 4 } }, ), ).toEqual({ promptTokens: 1, completionTokens: 2, totalTokens: 3, completionTokensDetails: { reasoningTokens: 7, audioTokens: 4 }, }) }) it('returns undefined for missing or empty spec usage without leftover', () => { expect(fromSpecTokenUsage(undefined)).toBeUndefined() expect(fromSpecTokenUsage([])).toBeUndefined() }) }) describe('rebuildTokenUsage', () => { it('returns TokenUsage objects unchanged', () => { expect( rebuildTokenUsage({ promptTokens: 1, completionTokens: 2, totalTokens: 3, cost: 0.01, }), ).toEqual({ promptTokens: 1, completionTokens: 2, totalTokens: 3, cost: 0.01, }) }) it('rebuilds spec usage[] plus leftover into TokenUsage', () => { expect( rebuildTokenUsage([{ inputTokens: 4, outputTokens: 5, totalTokens: 9 }], { cost: 0.02, }), ).toEqual({ promptTokens: 4, completionTokens: 5, totalTokens: 9, cost: 0.02, }) }) })