mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-20 22:02:19 +03:00
* fix(vertex): preserve Claude prompt caching * fix(vertex): normalize unsupported cache TTLs * docs(changelog): note Vertex prompt caching fix
242 lines
8.1 KiB
TypeScript
242 lines
8.1 KiB
TypeScript
import { describe, test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { prepareClaudeRequest } from "../../open-sse/translator/helpers/claudeHelper.ts";
|
|
import { FORMATS } from "../../open-sse/translator/formats.ts";
|
|
import { translateRequest } from "../../open-sse/translator/index.ts";
|
|
|
|
describe("Claude cache_control passthrough", () => {
|
|
test("preserveCacheControl=true preserves cache_control in system blocks", () => {
|
|
const body = {
|
|
system: [
|
|
{ type: "text", text: "System prompt 1" },
|
|
{ type: "text", text: "System prompt 2", cache_control: { type: "ephemeral", ttl: "5m" } },
|
|
],
|
|
messages: [],
|
|
};
|
|
|
|
const result = prepareClaudeRequest(body, "claude", true);
|
|
|
|
assert.equal(result.system.length, 2);
|
|
assert.equal(result.system[0].cache_control, undefined);
|
|
assert.deepEqual(result.system[1].cache_control, { type: "ephemeral", ttl: "5m" });
|
|
});
|
|
|
|
test("preserveCacheControl=false replaces cache_control in system blocks", () => {
|
|
const body = {
|
|
system: [
|
|
{ type: "text", text: "System prompt 1" },
|
|
{ type: "text", text: "System prompt 2", cache_control: { type: "ephemeral", ttl: "5m" } },
|
|
],
|
|
messages: [],
|
|
};
|
|
|
|
const result = prepareClaudeRequest(body, "claude", false);
|
|
|
|
assert.equal(result.system.length, 2);
|
|
assert.equal(result.system[0].cache_control, undefined);
|
|
assert.deepEqual(result.system[1].cache_control, { type: "ephemeral", ttl: "1h" });
|
|
});
|
|
|
|
test("preserveCacheControl=true preserves cache_control in message content blocks", () => {
|
|
const body = {
|
|
messages: [
|
|
{
|
|
role: "user",
|
|
content: [
|
|
{ type: "text", text: "User message 1" },
|
|
{ type: "text", text: "User message 2", cache_control: { type: "ephemeral" } },
|
|
],
|
|
},
|
|
{
|
|
role: "assistant",
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: "Assistant response",
|
|
cache_control: { type: "ephemeral", ttl: "10m" },
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
const result = prepareClaudeRequest(body, "claude", true);
|
|
|
|
assert.equal(result.messages.length, 2);
|
|
assert.equal(result.messages[0].content[0].cache_control, undefined);
|
|
assert.deepEqual(result.messages[0].content[1].cache_control, { type: "ephemeral" });
|
|
assert.deepEqual(result.messages[1].content[0].cache_control, {
|
|
type: "ephemeral",
|
|
ttl: "10m",
|
|
});
|
|
});
|
|
|
|
test("preserveCacheControl=false strips and re-adds cache_control in messages", () => {
|
|
const body = {
|
|
messages: [
|
|
{
|
|
role: "user",
|
|
content: [
|
|
{ type: "text", text: "User message 1" },
|
|
{ type: "text", text: "User message 2", cache_control: { type: "ephemeral" } },
|
|
],
|
|
},
|
|
{
|
|
role: "assistant",
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: "Assistant response",
|
|
cache_control: { type: "ephemeral", ttl: "10m" },
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
const result = prepareClaudeRequest(body, "claude", false);
|
|
|
|
// Original cache_control should be stripped and OmniRoute's strategy applied
|
|
assert.equal(result.messages.length, 2);
|
|
// User message should not have cache_control (only second-to-last user gets it)
|
|
assert.equal(result.messages[0].content[0].cache_control, undefined);
|
|
assert.equal(result.messages[0].content[1].cache_control, undefined);
|
|
// Last assistant should have cache_control added by OmniRoute
|
|
assert.deepEqual(result.messages[1].content[0].cache_control, { type: "ephemeral" });
|
|
});
|
|
|
|
test("preserveCacheControl=true preserves cache_control in tools", () => {
|
|
const body = {
|
|
messages: [],
|
|
tools: [
|
|
{ name: "tool1", description: "Tool 1", input_schema: { type: "object" } },
|
|
{
|
|
name: "tool2",
|
|
description: "Tool 2",
|
|
input_schema: { type: "object" },
|
|
cache_control: { type: "ephemeral", ttl: "30m" },
|
|
},
|
|
],
|
|
};
|
|
|
|
const result = prepareClaudeRequest(body, "claude", true);
|
|
|
|
assert.equal(result.tools.length, 2);
|
|
assert.equal(result.tools[0].cache_control, undefined);
|
|
assert.deepEqual(result.tools[1].cache_control, { type: "ephemeral", ttl: "30m" });
|
|
});
|
|
|
|
test("preserveCacheControl=false replaces cache_control in tools", () => {
|
|
const body = {
|
|
messages: [],
|
|
tools: [
|
|
{ name: "tool1", description: "Tool 1", input_schema: { type: "object" } },
|
|
{
|
|
name: "tool2",
|
|
description: "Tool 2",
|
|
input_schema: { type: "object" },
|
|
cache_control: { type: "ephemeral", ttl: "30m" },
|
|
},
|
|
],
|
|
};
|
|
|
|
const result = prepareClaudeRequest(body, "claude", false);
|
|
|
|
assert.equal(result.tools.length, 2);
|
|
assert.equal(result.tools[0].cache_control, undefined);
|
|
assert.deepEqual(result.tools[1].cache_control, { type: "ephemeral", ttl: "1h" });
|
|
});
|
|
|
|
test("preserveCacheControl=true with Claude Code-style caching", () => {
|
|
const body = {
|
|
system: [{ type: "text", text: "System", cache_control: { type: "ephemeral", ttl: "5m" } }],
|
|
messages: [
|
|
{
|
|
role: "user",
|
|
content: [{ type: "text", text: "Turn 1", cache_control: { type: "ephemeral" } }],
|
|
},
|
|
{
|
|
role: "assistant",
|
|
content: [{ type: "text", text: "Response 1" }],
|
|
},
|
|
{
|
|
role: "user",
|
|
content: [{ type: "text", text: "Turn 2" }],
|
|
},
|
|
],
|
|
tools: [
|
|
{
|
|
name: "bash",
|
|
description: "Execute bash",
|
|
input_schema: { type: "object" },
|
|
cache_control: { type: "ephemeral", ttl: "5m" },
|
|
},
|
|
],
|
|
};
|
|
|
|
const result = prepareClaudeRequest(body, "claude", true);
|
|
|
|
// All original cache_control should be preserved
|
|
assert.deepEqual(result.system[0].cache_control, { type: "ephemeral", ttl: "5m" });
|
|
assert.deepEqual(result.messages[0].content[0].cache_control, { type: "ephemeral" });
|
|
assert.equal(result.messages[1].content[0].cache_control, undefined);
|
|
assert.equal(result.messages[2].content[0].cache_control, undefined);
|
|
assert.deepEqual(result.tools[0].cache_control, { type: "ephemeral", ttl: "5m" });
|
|
});
|
|
|
|
for (const provider of ["vertex", "vertex-partner"]) {
|
|
test(`${provider} supports prompt caching with the cost-sensitive default TTL`, () => {
|
|
const body = {
|
|
system: [{ type: "text", text: "Stable system prefix" }],
|
|
messages: [{ role: "user", content: [{ type: "text", text: "Dynamic question" }] }],
|
|
tools: [
|
|
{
|
|
name: "lookup",
|
|
description: "Stable tool definition",
|
|
input_schema: { type: "object" },
|
|
},
|
|
],
|
|
};
|
|
|
|
const result = prepareClaudeRequest(body, provider, false, "claude-sonnet-4-6");
|
|
|
|
// Omitting ttl selects Anthropic's 5-minute default. Vertex does not support
|
|
// ttl:1h on every Claude model, and one-hour writes cost more.
|
|
assert.deepEqual(result.system[0].cache_control, { type: "ephemeral" });
|
|
assert.deepEqual(result.tools[0].cache_control, { type: "ephemeral" });
|
|
});
|
|
|
|
test(`${provider} uses the five-minute default through the full translation path`, () => {
|
|
const result = translateRequest(
|
|
FORMATS.OPENAI,
|
|
FORMATS.CLAUDE,
|
|
"claude-3-7-sonnet",
|
|
{
|
|
messages: [
|
|
{ role: "system", content: "Stable system prefix" },
|
|
{ role: "user", content: "Dynamic question" },
|
|
],
|
|
tools: [
|
|
{
|
|
type: "function",
|
|
function: {
|
|
name: "lookup",
|
|
description: "Stable tool definition",
|
|
parameters: { type: "object" },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
true,
|
|
null,
|
|
provider,
|
|
null,
|
|
{ preserveCacheControl: true }
|
|
);
|
|
|
|
assert.deepEqual(result.system[0].cache_control, { type: "ephemeral" });
|
|
assert.deepEqual(result.tools[0].cache_control, { type: "ephemeral" });
|
|
});
|
|
}
|
|
});
|