Files
OmniRoute/tests/unit/cache-write-openai-shape.test.ts
Nguyễn Viết Tuấn 4d20d37974 fix(sse): keep cache-write tokens in OpenAI-shaped usage (#11814)
Mantém tokens de cache-write no formato de usage do OpenAI, com teste próprio (`cache-write-openai-shape.test.ts`) e atualização do teste existente de tokens detalhados. Validado no worktree combinado (typecheck limpo, 351/351 testes focados). Obrigado!
2026-08-30 11:09:15 -03:00

199 lines
7.0 KiB
TypeScript

/**
* Regression: cache-write (cache creation) tokens were dropped whenever the usage
* payload arrived in an OpenAI-shaped container.
*
* Symptom: the same Claude model shows "Cache Write: 1,911" through an
* anthropic-compatible provider but "Cache Write: N/A" through an
* openai-compatible `/v1/chat/completions` provider, because every consumer only
* recognised the top-level Claude key `cache_creation_input_tokens`.
*
* Three shapes are produced inside this repo and none of them were read back:
* - `prompt_tokens_details.cache_creation_tokens`
* (open-sse/translator/response/claude-to-openai.ts, #2215)
* - `input_tokens_details.cache_write_tokens`
* (open-sse/vendor/codex-chatgpt-web/bridge.ts)
* - top-level `cache_write_tokens`
* (open-sse/executors/devin-desktop.ts, OpenRouter)
*
* A provider that genuinely has no cache-write concept (plain gpt/codex) must
* still report `null`, NOT `0` — `null` means "not reported", `0` means
* "reported as zero". That distinction is load-bearing for cache debugging.
*/
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import {
getPromptCacheCreationTokens,
getPromptCacheCreationTokensOrNull,
} from "../../src/lib/usage/tokenAccounting.ts";
import { buildCacheUsageLogMeta } from "../../open-sse/handlers/chatCore/cacheUsageMeta.ts";
import { extractUsage, normalizeUsage } from "../../open-sse/utils/usageTracking.ts";
import { extractUsageFromResponse } from "../../open-sse/handlers/usageExtractor.ts";
describe("cache-write tokens survive OpenAI-shaped usage", () => {
describe("tokenAccounting alias coverage", () => {
it("reads prompt_tokens_details.cache_creation_tokens (claude-to-openai #2215 shape)", () => {
const tokens = {
prompt_tokens: 5000,
completion_tokens: 100,
prompt_tokens_details: { cached_tokens: 0, cache_creation_tokens: 1911 },
};
assert.equal(getPromptCacheCreationTokens(tokens), 1911);
assert.equal(getPromptCacheCreationTokensOrNull(tokens), 1911);
});
it("reads input_tokens_details.cache_write_tokens (codex-chatgpt-web bridge shape)", () => {
const tokens = {
prompt_tokens: 5000,
completion_tokens: 100,
input_tokens_details: { cached_tokens: 0, cache_write_tokens: 1911 },
};
assert.equal(getPromptCacheCreationTokens(tokens), 1911);
assert.equal(getPromptCacheCreationTokensOrNull(tokens), 1911);
});
it("reads top-level cache_write_tokens (devin-desktop / OpenRouter shape)", () => {
const tokens = {
prompt_tokens: 5,
completion_tokens: 100,
prompt_tokens_details: { cached_tokens: 0 },
cache_write_tokens: 1911,
};
assert.equal(getPromptCacheCreationTokens(tokens), 1911);
assert.equal(getPromptCacheCreationTokensOrNull(tokens), 1911);
});
it("reported-zero cache_write_tokens stays 0, never null", () => {
const tokens = {
prompt_tokens: 5,
completion_tokens: 100,
prompt_tokens_details: { cached_tokens: 0, cache_write_tokens: 0 },
};
assert.equal(getPromptCacheCreationTokensOrNull(tokens), 0);
});
it("plain gpt/codex usage (no cache-write concept) still returns null", () => {
const tokens = {
prompt_tokens: 54042,
completion_tokens: 8000,
prompt_tokens_details: { cached_tokens: 53221 },
completion_tokens_details: { reasoning_tokens: 6433 },
};
assert.equal(
getPromptCacheCreationTokensOrNull(tokens),
null,
"not reported must stay null, not 0"
);
});
});
describe("extractUsageFromResponse (non-streaming)", () => {
it("keeps cache creation from an OpenAI-shaped body", () => {
const usage = extractUsageFromResponse(
{
usage: {
prompt_tokens: 5000,
completion_tokens: 100,
prompt_tokens_details: { cached_tokens: 0, cache_creation_tokens: 1911 },
},
},
"openai-compatible"
);
assert.equal(getPromptCacheCreationTokensOrNull(usage), 1911);
});
it("keeps a top-level cache_write_tokens alias", () => {
const usage = extractUsageFromResponse(
{
usage: {
prompt_tokens: 5000,
completion_tokens: 100,
cache_write_tokens: 1911,
},
},
"openai-compatible"
);
assert.equal(getPromptCacheCreationTokensOrNull(usage), 1911);
});
it("does not invent a cache-creation field when the provider omits it", () => {
const usage = extractUsageFromResponse(
{ usage: { prompt_tokens: 10, completion_tokens: 2 } },
"openai-compatible"
);
assert.equal(getPromptCacheCreationTokensOrNull(usage), null);
});
});
describe("extractUsage (streaming chunk)", () => {
it("keeps cache creation nested in prompt_tokens_details", () => {
const usage = extractUsage({
usage: {
prompt_tokens: 5000,
completion_tokens: 100,
prompt_tokens_details: { cached_tokens: 0, cache_creation_tokens: 1911 },
},
});
assert.equal(getPromptCacheCreationTokensOrNull(usage), 1911);
});
it("keeps cache creation from a Responses-API completed event", () => {
const usage = extractUsage({
type: "response.completed",
response: {
usage: {
input_tokens: 5000,
output_tokens: 100,
input_tokens_details: { cached_tokens: 0, cache_write_tokens: 1911 },
},
},
});
assert.equal(getPromptCacheCreationTokensOrNull(usage), 1911);
});
});
describe("normalizeUsage", () => {
it("maps the cache_write_tokens alias onto the canonical key", () => {
const normalized = normalizeUsage({
prompt_tokens: 5000,
completion_tokens: 100,
cache_write_tokens: 1911,
});
assert.equal(normalized?.cache_creation_input_tokens, 1911);
});
it("does not overwrite an explicit canonical value", () => {
const normalized = normalizeUsage({
prompt_tokens: 5000,
completion_tokens: 100,
cache_creation_input_tokens: 1911,
cache_write_tokens: 7,
});
assert.equal(normalized?.cache_creation_input_tokens, 1911);
});
});
describe("buildCacheUsageLogMeta", () => {
it("reports cache creation from the OpenAI-shaped nested key", () => {
const meta = buildCacheUsageLogMeta({
prompt_tokens: 5000,
prompt_tokens_details: { cached_tokens: 0, cache_creation_tokens: 1911 },
});
assert.equal(meta?.cacheCreationTokens, 1911);
});
it("reports cache creation from the cache_write_tokens alias", () => {
const meta = buildCacheUsageLogMeta({
prompt_tokens: 5000,
cache_write_tokens: 1911,
});
assert.equal(meta?.cacheCreationTokens, 1911);
});
it("still returns null when no cache field is present at all", () => {
assert.equal(buildCacheUsageLogMeta({ prompt_tokens: 10, completion_tokens: 2 }), null);
});
});
});