Skip to content

Commit effa43e

Browse files
committed
🤖 test: add regression tests for step providerMetadata and cache creation
Cover the fix to prevent regression: - StreamingMessageAggregator: test step providerMetadata storage/retrieval/clear - displayUsage: test cacheCreationInputTokens extraction from providerMetadata _Generated with mux_
1 parent fd8a296 commit effa43e

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed

‎src/browser/utils/messages/StreamingMessageAggregator.test.ts‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,40 @@ describe("StreamingMessageAggregator", () => {
517517
expect(aggregator.getActiveStreamCumulativeProviderMetadata("msg-1")).toBeUndefined();
518518
});
519519

520+
test("stores and retrieves step providerMetadata for cache creation display", () => {
521+
const aggregator = new StreamingMessageAggregator(TEST_CREATED_AT);
522+
523+
aggregator.handleUsageDelta({
524+
type: "usage-delta",
525+
workspaceId: "ws-1",
526+
messageId: "msg-1",
527+
usage: { inputTokens: 1000, outputTokens: 50, totalTokens: 1050 },
528+
cumulativeUsage: { inputTokens: 1000, outputTokens: 50, totalTokens: 1050 },
529+
providerMetadata: {
530+
anthropic: { cacheCreationInputTokens: 800 },
531+
},
532+
});
533+
534+
expect(aggregator.getActiveStreamStepProviderMetadata("msg-1")).toEqual({
535+
anthropic: { cacheCreationInputTokens: 800 },
536+
});
537+
});
538+
539+
test("step providerMetadata is undefined when not provided", () => {
540+
const aggregator = new StreamingMessageAggregator(TEST_CREATED_AT);
541+
542+
aggregator.handleUsageDelta({
543+
type: "usage-delta",
544+
workspaceId: "ws-1",
545+
messageId: "msg-1",
546+
usage: { inputTokens: 1000, outputTokens: 50, totalTokens: 1050 },
547+
cumulativeUsage: { inputTokens: 1000, outputTokens: 50, totalTokens: 1050 },
548+
// No providerMetadata
549+
});
550+
551+
expect(aggregator.getActiveStreamStepProviderMetadata("msg-1")).toBeUndefined();
552+
});
553+
520554
test("clearTokenState clears all usage tracking (step, cumulative, metadata)", () => {
521555
const aggregator = new StreamingMessageAggregator(TEST_CREATED_AT);
522556

@@ -526,18 +560,21 @@ describe("StreamingMessageAggregator", () => {
526560
messageId: "msg-1",
527561
usage: { inputTokens: 1000, outputTokens: 50, totalTokens: 1050 },
528562
cumulativeUsage: { inputTokens: 1000, outputTokens: 50, totalTokens: 1050 },
563+
providerMetadata: { anthropic: { cacheCreationInputTokens: 300 } },
529564
cumulativeProviderMetadata: { anthropic: { cacheCreationInputTokens: 500 } },
530565
});
531566

532567
// All should be defined
533568
expect(aggregator.getActiveStreamUsage("msg-1")).toBeDefined();
569+
expect(aggregator.getActiveStreamStepProviderMetadata("msg-1")).toBeDefined();
534570
expect(aggregator.getActiveStreamCumulativeUsage("msg-1")).toBeDefined();
535571
expect(aggregator.getActiveStreamCumulativeProviderMetadata("msg-1")).toBeDefined();
536572

537573
aggregator.clearTokenState("msg-1");
538574

539575
// All should be cleared
540576
expect(aggregator.getActiveStreamUsage("msg-1")).toBeUndefined();
577+
expect(aggregator.getActiveStreamStepProviderMetadata("msg-1")).toBeUndefined();
541578
expect(aggregator.getActiveStreamCumulativeUsage("msg-1")).toBeUndefined();
542579
expect(aggregator.getActiveStreamCumulativeProviderMetadata("msg-1")).toBeUndefined();
543580
});

‎src/common/utils/tokens/displayUsage.test.ts‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,4 +276,68 @@ describe("createDisplayUsage", () => {
276276
expect(result!.input.tokens).toBe(1000);
277277
expect(result!.cached.tokens).toBe(0);
278278
});
279+
280+
describe("Anthropic cache creation tokens from providerMetadata", () => {
281+
// Cache creation tokens are Anthropic-specific and only available in
282+
// providerMetadata.anthropic.cacheCreationInputTokens, not in LanguageModelV2Usage.
283+
// This is critical for liveUsage display during streaming.
284+
285+
test("extracts cacheCreationInputTokens from providerMetadata", () => {
286+
const usage: LanguageModelV2Usage = {
287+
inputTokens: 1000,
288+
outputTokens: 50,
289+
totalTokens: 1050,
290+
};
291+
292+
const result = createDisplayUsage(usage, "anthropic:claude-sonnet-4-20250514", {
293+
anthropic: { cacheCreationInputTokens: 800 },
294+
});
295+
296+
expect(result).toBeDefined();
297+
expect(result!.cacheCreate.tokens).toBe(800);
298+
});
299+
300+
test("cacheCreate is 0 when providerMetadata is undefined", () => {
301+
const usage: LanguageModelV2Usage = {
302+
inputTokens: 1000,
303+
outputTokens: 50,
304+
totalTokens: 1050,
305+
};
306+
307+
const result = createDisplayUsage(usage, "anthropic:claude-sonnet-4-20250514");
308+
309+
expect(result).toBeDefined();
310+
expect(result!.cacheCreate.tokens).toBe(0);
311+
});
312+
313+
test("cacheCreate is 0 when anthropic metadata lacks cacheCreationInputTokens", () => {
314+
const usage: LanguageModelV2Usage = {
315+
inputTokens: 1000,
316+
outputTokens: 50,
317+
totalTokens: 1050,
318+
};
319+
320+
const result = createDisplayUsage(usage, "anthropic:claude-sonnet-4-20250514", {
321+
anthropic: { someOtherField: 123 },
322+
});
323+
324+
expect(result).toBeDefined();
325+
expect(result!.cacheCreate.tokens).toBe(0);
326+
});
327+
328+
test("handles gateway Anthropic model with cache creation", () => {
329+
const usage: LanguageModelV2Usage = {
330+
inputTokens: 2000,
331+
outputTokens: 100,
332+
totalTokens: 2100,
333+
};
334+
335+
const result = createDisplayUsage(usage, "mux-gateway:anthropic/claude-sonnet-4-5", {
336+
anthropic: { cacheCreationInputTokens: 1500 },
337+
});
338+
339+
expect(result).toBeDefined();
340+
expect(result!.cacheCreate.tokens).toBe(1500);
341+
});
342+
});
279343
});

0 commit comments

Comments
 (0)