Files
OmniRoute/open-sse/handlers/chatCore/cacheUsageMeta.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

75 lines
2.8 KiB
TypeScript

/**
* chatCore cache-usage log meta helpers (Quality Gate v2 / Fase 9 — chatCore god-file decomposition,
* #3501).
*
* Pure helpers extracted from chatCore: coerce an unknown to a positive number, derive cache
* read/creation token counts from a usage object (handling both top-level and prompt_tokens_details
* shapes), and attach an `_omniroute` meta blob to a log payload. Side-effect-free; behaviour is
* byte-identical to the previous module-level functions.
*/
export function toPositiveNumber(value: unknown) {
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
}
export function buildCacheUsageLogMeta(usage: Record<string, unknown> | null | undefined) {
if (!usage || typeof usage !== "object") return null;
const promptTokenDetails =
usage.prompt_tokens_details && typeof usage.prompt_tokens_details === "object"
? (usage.prompt_tokens_details as Record<string, unknown>)
: undefined;
// `cache_write_tokens` is the cache-creation alias emitted by OpenRouter, Devin
// Desktop and the codex-chatgpt-web bridge; without it an OpenAI-shaped usage
// payload logged a cache write of 0 for a model that actually reported one.
const hasCacheFields =
"cache_read_input_tokens" in usage ||
"cached_tokens" in usage ||
"cache_creation_input_tokens" in usage ||
"cache_write_tokens" in usage ||
(!!promptTokenDetails &&
("cached_tokens" in promptTokenDetails ||
"cache_creation_tokens" in promptTokenDetails ||
"cache_write_tokens" in promptTokenDetails));
const cacheReadTokens = toPositiveNumber(
usage.cache_read_input_tokens ?? usage.cached_tokens ?? promptTokenDetails?.cached_tokens
);
const cacheCreationTokens = toPositiveNumber(
usage.cache_creation_input_tokens ??
promptTokenDetails?.cache_creation_tokens ??
promptTokenDetails?.cache_write_tokens ??
usage.cache_write_tokens
);
if (!hasCacheFields) return null;
return {
cacheReadTokens,
cacheCreationTokens,
};
}
export function attachLogMeta(
payload: Record<string, unknown> | null | undefined,
meta: Record<string, unknown> | null | undefined
) {
if (!meta || typeof meta !== "object") return payload;
const compactMeta = Object.fromEntries(
Object.entries(meta).filter(([, value]) => value !== null && value !== undefined)
);
if (Object.keys(compactMeta).length === 0) return payload;
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
return { _omniroute: compactMeta, _payload: payload ?? null };
}
const existing =
payload._omniroute &&
typeof payload._omniroute === "object" &&
!Array.isArray(payload._omniroute)
? payload._omniroute
: {};
return {
...payload,
_omniroute: {
...existing,
...compactMeta,
},
};
}