import type { Message } from "@langchain/langgraph-sdk"; import { expect, test } from "vitest"; import { accumulateUsage } from "@/core/messages/usage"; import { getAssistantTurnUsageMessages, getMessageGroups, } from "@/core/messages/utils"; test("accumulates each AI message usage only once by message id", () => { const aiMessage = { id: "ai-1", type: "ai", content: "Answer", usage_metadata: { input_tokens: 10, output_tokens: 5, total_tokens: 15 }, } as Message; expect(accumulateUsage([aiMessage, aiMessage])).toEqual({ inputTokens: 10, outputTokens: 5, totalTokens: 15, }); }); test("counts later usage-bearing snapshots for the same AI message id", () => { const earlySnapshot = { id: "ai-1", type: "ai", content: "Streaming...", } as Message; const completedSnapshot = { id: "ai-1", type: "ai", content: "Complete answer", usage_metadata: { input_tokens: 10, output_tokens: 5, total_tokens: 15 }, } as Message; expect(accumulateUsage([earlySnapshot, completedSnapshot])).toEqual({ inputTokens: 10, outputTokens: 5, totalTokens: 15, }); }); test("keeps header and per-turn aggregation consistent for duplicated UI groups", () => { const messages = [ { id: "human-1", type: "human", content: "Explain this", }, { id: "ai-1", type: "ai", content: "checking contextFinal answer", usage_metadata: { input_tokens: 20, output_tokens: 7, total_tokens: 27 }, }, ] as Message[]; const groups = getMessageGroups(messages); const usageMessagesByGroupIndex = getAssistantTurnUsageMessages(groups); const turnUsageMessages = usageMessagesByGroupIndex.at(-1); expect(groups.map((group) => group.type)).toEqual([ "human", "assistant:processing", "assistant", ]); expect(turnUsageMessages?.map((message) => message.id)).toEqual([ "ai-1", "ai-1", ]); expect(accumulateUsage(messages)).toEqual( accumulateUsage(turnUsageMessages!), ); expect(accumulateUsage(turnUsageMessages!)).toEqual({ inputTokens: 20, outputTokens: 7, totalTokens: 27, }); });