| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- import { describe, expect, test } from "vitest";
- import { parseUsageFromResponseText } from "@/app/v1/_lib/proxy/response-handler";
- function buildSse(events: Array<{ event: string; data: unknown }>): string {
- return events
- .flatMap(({ event, data }) => [`event: ${event}`, `data: ${JSON.stringify(data)}`, ""])
- .join("\n");
- }
- describe("parseUsageFromResponseText (Anthropic/Claude SSE usage)", () => {
- test("prefers message_delta and falls back to message_start for missing fields", () => {
- const sse = buildSse([
- {
- event: "message_start",
- data: {
- type: "message_start",
- message: {
- usage: {
- input_tokens: 12,
- cache_creation_input_tokens: 1641,
- cache_read_input_tokens: 171876,
- output_tokens: 1,
- cache_creation: { ephemeral_1h_input_tokens: 1641 },
- },
- },
- },
- },
- {
- event: "message_delta",
- data: {
- type: "message_delta",
- delta: { stop_reason: "end_turn" },
- usage: {
- input_tokens: 9,
- cache_creation_input_tokens: 458843,
- cache_read_input_tokens: 14999,
- output_tokens: 2273,
- },
- },
- },
- ]);
- const { usageMetrics, usageRecord } = parseUsageFromResponseText(sse, "anthropic");
- expect(usageRecord).not.toBeNull();
- expect(usageMetrics).toMatchObject({
- input_tokens: 9,
- output_tokens: 2273,
- cache_creation_input_tokens: 458843,
- cache_read_input_tokens: 14999,
- cache_creation_1h_input_tokens: 1641,
- cache_ttl: "1h",
- });
- });
- test("falls back to message_start when message_delta only provides output_tokens", () => {
- const sse = buildSse([
- {
- event: "message_start",
- data: {
- type: "message_start",
- message: {
- usage: {
- input_tokens: 12,
- cache_creation_input_tokens: 1641,
- cache_read_input_tokens: 171876,
- output_tokens: 1,
- cache_creation: { ephemeral_1h_input_tokens: 1641 },
- },
- },
- },
- },
- {
- event: "message_delta",
- data: {
- type: "message_delta",
- delta: { stop_reason: "end_turn" },
- usage: { output_tokens: 2273 },
- },
- },
- ]);
- const { usageMetrics, usageRecord } = parseUsageFromResponseText(sse, "anthropic");
- expect(usageRecord).not.toBeNull();
- expect(usageMetrics).toMatchObject({
- input_tokens: 12,
- output_tokens: 2273,
- cache_creation_input_tokens: 1641,
- cache_read_input_tokens: 171876,
- cache_creation_1h_input_tokens: 1641,
- cache_ttl: "1h",
- });
- });
- test("handles message_delta-only streams", () => {
- const sse = buildSse([
- {
- event: "message_delta",
- data: {
- type: "message_delta",
- delta: { stop_reason: "end_turn" },
- usage: {
- input_tokens: 9,
- cache_creation_input_tokens: 458843,
- cache_read_input_tokens: 14999,
- output_tokens: 2273,
- },
- },
- },
- ]);
- const { usageMetrics, usageRecord } = parseUsageFromResponseText(sse, "anthropic");
- expect(usageRecord).not.toBeNull();
- expect(usageMetrics).toMatchObject({
- input_tokens: 9,
- output_tokens: 2273,
- cache_creation_input_tokens: 458843,
- cache_read_input_tokens: 14999,
- });
- });
- });
|