Files
OmniRoute/tests/unit/stream-passthrough-usage-estimation.test.ts
Diego Rodrigues de Sa e Souza 1586476183 fix(release): drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys (#12327)
* fix(sse): forward the upstream's real trailing usage in passthrough; estimate only at flush

#12151 injected estimated usage into the finish chunk and dropped the real
trailing usage block that genuine OpenAI upstreams send afterwards — metered
clients got estimates instead of real token counts. The estimate now leaves
via a canonical usage-only chunk at flush, only when the upstream stayed
silent; a real trailing block is forwarded verbatim and wins. The tool_calls
finish_reason normalization now materializes its own rewrite (it piggybacked
on the removed finish-time rewrite), and the dead collectSSE helper goes with
it (subsumes #12324).

* fix(i18n): seed the radarPage limits/training keys the #12320 UI already consumes

RadarCatalogTable.tsx references radarPage.colLimits / trainsOnPrompts /
trainsOnPromptsHelp but #12320 never added them to en.json, so the EN
fallback could not resolve the __MISSING__ markers across 42 locales.
Real translations for pt-BR and vi; the rest resolve via the EN fallback.

* fix(sse): carry the chat stream id into flush-time synthetic chunks

The estimated usage-only chunk emitted at flush used passthroughResponsesId,
which is only ever set on the Responses path — on the chat path the synthetic
chunk shipped id: null, breaking the string-id invariant pinned by
stream-numeric-ids.test.ts. Track the upstream chat-completion id in the
passthrough loop and reuse it (falling back to the Responses id, then a
generated one). Sibling sweep: 74 files importing utils/stream — 603/603.
2026-09-01 16:17:48 -03:00

135 lines
7.1 KiB
TypeScript

// Locks passthrough stream.ts:1981 + empty-choices stream.ts:1754 : even with
// stream_options:{include_usage:true} we estimate when upstream closes on
// finish_reason without usage but with content, and we don't double-forward.
import { test } from "node:test";
import assert from "node:assert/strict";
import { hasValidUsage, estimateUsage, isEmptyUsage } from "../../open-sse/utils/usageTracking.ts";
import { FORMATS } from "../../open-sse/translator/formats.ts";
test("passthrough estimate: finish without usage but with content -> estimate", () => {
// Regression: Gemini single-field usage must count as valid (totalTokenCount was missing before)
assert.equal(hasValidUsage({ totalTokenCount: 15 } as Record<string, unknown>), true);
assert.equal(isEmptyUsage({ totalTokenCount: 15 } as Record<string, unknown>), false);
assert.equal(isEmptyUsage({ totalTokenCount: 0 } as Record<string, unknown>), true);
assert.equal(!hasValidUsage(null) && 534 > 0, true);
assert.equal(isEmptyUsage({ prompt_tokens: 0, completion_tokens: 0 }), true);
const est = estimateUsage({ messages: [{ role: "user", content: "hi" }] }, 534, FORMATS.OPENAI);
assert.equal(hasValidUsage(est as Record<string, unknown>), true);
assert.equal((est as Record<string, unknown>).estimated, true);
});
test("passthrough no double: trailing choices:[] with valid usage -> no estimate", () => {
assert.equal(hasValidUsage({ prompt_tokens: 8, completion_tokens: 6, total_tokens: 14 }), true);
});
test("passthrough no fake: empty response totalContentLength==0 -> no estimate", () => {
assert.equal(!hasValidUsage(null) && 0 > 0, false);
assert.equal(!hasValidUsage({} as Record<string, unknown>) && 0 > 0, false);
});
test("passthrough no fake: tool_only contentLength==0 -> no estimate (tool_calls not counted today)", () => {
// totalContentLength only counts delta.content + reasoningDelta today -> tool_only stays 0, so no estimate
assert.equal(!hasValidUsage(null) && 0 > 0, false);
});
import { createSSEStream } from "../../open-sse/utils/stream.ts";
function parseSSEUsage(sseText: string): unknown[] {
return sseText
.split("\n\n")
.filter((block) => block.includes("data:"))
.map((block) => {
const line = block.split("\n").find((l) => l.startsWith("data:")) ?? "";
const json = line.slice(5).trim();
if (!json || json === "[DONE]") return null;
try {
return JSON.parse(json);
} catch {
return null;
}
})
.filter(Boolean);
}
test("passthrough SSE: finish stop without usage + include_usage:true -> emits usage.estimated:true", async () => {
const body = { model: "m", messages: [{ role: "user", content: "hi" }], stream: true, stream_options: { include_usage: true } };
const stream = createSSEStream({
mode: "passthrough" as const,
body,
sourceFormat: FORMATS.OPENAI,
clientResponseFormat: FORMATS.OPENAI,
provider: "test",
});
const writer = stream.writable.getWriter();
const reader = stream.readable.getReader();
const readAll = (async () => {
const chunks: Uint8Array[] = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}
return Buffer.concat(chunks).toString("utf8");
})();
const enc = new TextEncoder();
// delta content -> accumulates totalContentLength
await writer.write(enc.encode(`data: ${JSON.stringify({ id: "chatcmpl-1", object: "chat.completion.chunk", choices: [{ index: 0, delta: { content: "hello world" }, finish_reason: null }] })}\n\n`));
// finish without usage
await writer.write(enc.encode(`data: ${JSON.stringify({ id: "chatcmpl-1", object: "chat.completion.chunk", choices: [{ index: 0, delta: {}, finish_reason: "stop" }] })}\n\n`));
await writer.write(enc.encode("data: [DONE]\n\n"));
await writer.close();
const text = await readAll;
const parsed = parseSSEUsage(text);
const withUsage = parsed.filter((p: unknown) => (p as Record<string, unknown>).usage);
assert.ok(withUsage.length >= 1, `expected at least 1 chunk with usage, got ${withUsage.length} — text: ${text.slice(0, 600)}`);
const last = withUsage[withUsage.length - 1] as Record<string, unknown>;
const usage = last.usage as Record<string, unknown>;
assert.equal(usage.estimated, true);
assert.ok(typeof usage.prompt_tokens === "number" && usage.prompt_tokens > 0);
assert.ok(typeof usage.completion_tokens === "number" && usage.completion_tokens > 0);
});
test("passthrough SSE: real trailing choices:[] usage is forwarded; no estimate is emitted (real wins)", async () => {
const body = { model: "m", messages: [{ role: "user", content: "hi" }], stream: true, stream_options: { include_usage: true } };
const stream = createSSEStream({
mode: "passthrough" as const,
body,
sourceFormat: FORMATS.OPENAI,
clientResponseFormat: FORMATS.OPENAI,
provider: "test",
});
const writer = stream.writable.getWriter();
const reader = stream.readable.getReader();
const readAll = (async () => {
const chunks: Uint8Array[] = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}
return Buffer.concat(chunks).toString("utf8");
})();
const enc = new TextEncoder();
await writer.write(enc.encode(`data: ${JSON.stringify({ id: "chatcmpl-1", object: "chat.completion.chunk", choices: [{ index: 0, delta: { content: "hello world" }, finish_reason: null }] })}\n\n`));
// finish without usage -> passes through untouched (estimate only happens at flush, and only if no usage ever arrives)
await writer.write(enc.encode(`data: ${JSON.stringify({ id: "chatcmpl-1", object: "chat.completion.chunk", choices: [{ index: 0, delta: {}, finish_reason: "stop" }] })}\n\n`));
// trailing choices:[] with valid usage -> forwarded verbatim (marks passthroughForwardedUsage, so flush skips the estimate)
await writer.write(enc.encode(`data: ${JSON.stringify({ id: "chatcmpl-1", object: "chat.completion.chunk", choices: [], usage: { prompt_tokens: 8, completion_tokens: 6, total_tokens: 14 } })}\n\n`));
await writer.write(enc.encode("data: [DONE]\n\n"));
await writer.close();
const text = await readAll;
const parsed = parseSSEUsage(text);
const withUsage = parsed.filter((p: unknown) => (p as Record<string, unknown>).usage);
// v2 contract (#12151 follow-up): the upstream's REAL trailing usage block is forwarded
// and wins; the estimate exists only for upstreams that never report usage (emitted at
// flush). Exactly one usage block ever reaches the client — never two, never estimated
// when a real one arrived (the v1 "estimated wins" tradeoff was a billing regression).
assert.equal(withUsage.length, 1, `expected 1 usage (the real trailing block), got ${withUsage.length} — usages: ${JSON.stringify(withUsage.map((p) => (p as Record<string, unknown>).usage))}`);
const forwarded = (withUsage[0] as Record<string, unknown> & { usage: Record<string, unknown> }).usage;
assert.equal(forwarded.estimated, undefined);
assert.equal(forwarded.prompt_tokens, 8);
assert.equal(forwarded.completion_tokens, 6);
assert.equal(forwarded.total_tokens, 14);
});