Files
OmniRoute/tests/unit/claude-prepare-request-preserve-fallback.test.ts
Diego Rodrigues de Sa e Souza 7db3ba615b fix(sse): stop thrashing the provider prompt cache for caching-aware clients (#8705)
* fix(sse): stop thrashing the provider prompt cache for caching-aware clients

- shouldPreserveCacheControl: preserve client cache_control markers for every
  combo strategy. The deterministic-strategy gate forced marker rewrites whose
  per-request breakpoint positions are not stable turn-over-turn, thrashing the
  upstream prompt cache (observed in production as ~200k cache_write tokens per
  turn on quota-share combos). Preserving is never worse: on a stable target
  the client's breakpoints advance deterministically; on a target switch both
  approaches miss equally.
- prepareClaudeRequest: translator-path opt-in fallback — when preserve-mode
  has nothing to preserve (client sent no cache_control anywhere), apply the
  standard heuristic so requests never ship with zero cache breakpoints. The
  claude-code-compatible relay path keeps its no-supplement contract.
- anthropic-beta: forward the client-negotiated context-1m-2025-08-07 through
  the allowlist merge so a /model <id>[1m] client keeps its long-context
  negotiation behind the proxy (never forced when the client did not send it).

* fix(sse): surface cache tokens in non-streaming OpenAI usage + finalize pending on quota-share block

- translateNonStreamingResponse (claude→openai): fold cache_read into
  prompt_tokens and expose prompt_tokens_details.cached_tokens /
  cache_creation_tokens, mirroring the streaming contract (#1426/#2215).
  Non-streaming OpenAI clients behind a cached Claude upstream previously saw
  prompt_tokens=<uncached remainder> (e.g. 23 for a ~9k request) with no cache
  visibility.
- chatCore quota-share block: finalize the pending-request slot before
  returning the policy 429 — the path never reaches upstream and the orphaned
  pending lingered as a status-0 call-log row until the reaper swept it.
  Validated live on the staging box (repro: blocked key → orphan row; after:
  clean).

---------

Co-authored-by: diegosouzapw <diegosouzapw24@gmail.com>
2026-07-27 17:24:43 -03:00

115 lines
4.4 KiB
TypeScript

/**
* prepareClaudeRequest preserve-mode fallback.
*
* preserveCacheControl=true must mean "the client manages its own cache
* markers" — but when the client body carries NO cache_control anywhere,
* preserving "nothing" would ship a request with zero prompt-cache
* breakpoints (every token billed uncached on every turn). In that case the
* standard heuristic must still apply, exactly as if preserveCacheControl
* were false. When the client DID place markers, they are preserved verbatim
* and the heuristic must not add any extra ones.
*/
import { describe, test } from "node:test";
import assert from "node:assert/strict";
import { prepareClaudeRequest } from "../../open-sse/translator/helpers/claudeHelper.ts";
type Block = Record<string, unknown>;
function baseBody(): Record<string, unknown> {
return {
model: "claude-sonnet-5",
system: [
{ type: "text", text: "You are a helpful assistant." },
{ type: "text", text: "Project context goes here." },
],
messages: [
{ role: "user", content: [{ type: "text", text: "First question" }] },
{ role: "assistant", content: [{ type: "text", text: "First answer" }] },
{ role: "user", content: [{ type: "text", text: "Second question" }] },
],
max_tokens: 128,
};
}
function collectCacheControls(body: Record<string, unknown>): Block[] {
const found: Block[] = [];
for (const block of (body.system as Block[]) ?? []) {
if (block.cache_control) found.push(block);
}
for (const msg of (body.messages as Array<{ content?: Block[] }>) ?? []) {
for (const block of msg.content ?? []) {
if (block.cache_control) found.push(block);
}
}
return found;
}
describe("prepareClaudeRequest preserve-mode fallback", () => {
test("falls back to the heuristic when the client sent no markers at all (translator opt-in)", () => {
const body = baseBody();
const result = prepareClaudeRequest(body, "claude", true, "claude-sonnet-5", {
fallbackToHeuristicWhenNoMarkers: true,
});
const system = result.system as Block[];
const lastSystem = system[system.length - 1];
assert.ok(
lastSystem.cache_control,
"last system block should carry the heuristic cache_control when the client sent none"
);
assert.ok(
collectCacheControls(result as Record<string, unknown>).length > 0,
"request must not go upstream with zero cache breakpoints"
);
});
test("without the opt-in flag, preserve-mode never supplements markers (CC relay contract)", () => {
const body = baseBody();
const result = prepareClaudeRequest(body, "claude", true, "claude-sonnet-5");
assert.equal(
collectCacheControls(result as Record<string, unknown>).length,
0,
"the claude-code-compatible relay path keeps its no-supplement contract"
);
});
test("preserves client markers verbatim and adds none (system marker)", () => {
const body = baseBody();
(body.system as Block[])[0].cache_control = { type: "ephemeral" };
const result = prepareClaudeRequest(body, "claude", true, "claude-sonnet-5");
const system = result.system as Block[];
assert.deepEqual(
system[0].cache_control,
{ type: "ephemeral" },
"client marker must survive untouched"
);
assert.equal(
collectCacheControls(result as Record<string, unknown>).length,
1,
"no extra heuristic markers may be added when the client manages its own"
);
});
test("preserves client markers verbatim and adds none (message marker)", () => {
const body = baseBody();
const messages = body.messages as Array<{ content: Block[] }>;
messages[2].content[0].cache_control = { type: "ephemeral" };
const result = prepareClaudeRequest(body, "claude", true, "claude-sonnet-5");
const controls = collectCacheControls(result as Record<string, unknown>);
assert.equal(controls.length, 1, "only the client's own marker may remain");
const resultMessages = result.messages as Array<{ content: Block[] }>;
assert.deepEqual(resultMessages[resultMessages.length - 1].content[0].cache_control, {
type: "ephemeral",
});
});
test("non-preserve mode keeps applying the heuristic (regression)", () => {
const body = baseBody();
const result = prepareClaudeRequest(body, "claude", false, "claude-sonnet-5");
const system = result.system as Block[];
assert.ok(system[system.length - 1].cache_control, "heuristic marker on last system block");
});
});