From 48120cd73e66ab6cb1cd45b3bc15fb36d209ad2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E8=89=BA=E9=9F=AC=28yangyitao=29?= Date: Sun, 15 Mar 2026 02:35:42 +0000 Subject: [PATCH] fix(cloudflare): pass cache control settings through AI Gateway Cloudflare AI Gateway proxies the Anthropic Messages API, so prompt caching headers should be forwarded just like direct Anthropic requests. Previously resolveCacheRetention() only matched provider === 'anthropic' and silently returned undefined for cloudflare-ai-gateway, causing cache_control settings to be stripped. Add 'cloudflare-ai-gateway' to the provider check so users get prompt caching benefits when routing through Cloudflare. Closes #46709 --- ...ic-stream-wrappers.cache-retention.test.ts | 36 +++++++++++++++++++ .../anthropic-stream-wrappers.ts | 7 ++-- 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 src/agents/pi-embedded-runner/anthropic-stream-wrappers.cache-retention.test.ts diff --git a/src/agents/pi-embedded-runner/anthropic-stream-wrappers.cache-retention.test.ts b/src/agents/pi-embedded-runner/anthropic-stream-wrappers.cache-retention.test.ts new file mode 100644 index 00000000000..38a54a6d044 --- /dev/null +++ b/src/agents/pi-embedded-runner/anthropic-stream-wrappers.cache-retention.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from "vitest"; +import { resolveCacheRetention } from "./anthropic-stream-wrappers.js"; + +describe("resolveCacheRetention", () => { + it("returns 'short' by default for anthropic provider", () => { + expect(resolveCacheRetention(undefined, "anthropic")).toBe("short"); + }); + + it("returns 'short' by default for cloudflare-ai-gateway provider", () => { + expect(resolveCacheRetention(undefined, "cloudflare-ai-gateway")).toBe("short"); + }); + + it("returns undefined for unrelated providers", () => { + expect(resolveCacheRetention(undefined, "openai")).toBeUndefined(); + expect(resolveCacheRetention(undefined, "openrouter")).toBeUndefined(); + }); + + it("returns explicit cacheRetention for cloudflare-ai-gateway", () => { + expect(resolveCacheRetention({ cacheRetention: "long" }, "cloudflare-ai-gateway")).toBe("long"); + expect(resolveCacheRetention({ cacheRetention: "none" }, "cloudflare-ai-gateway")).toBe("none"); + }); + + it("returns undefined for amazon-bedrock without explicit override", () => { + expect(resolveCacheRetention(undefined, "amazon-bedrock")).toBeUndefined(); + }); + + it("returns explicit cacheRetention for amazon-bedrock when overridden", () => { + expect(resolveCacheRetention({ cacheRetention: "short" }, "amazon-bedrock")).toBe("short"); + }); + + it("maps legacy cacheControlTtl values", () => { + expect(resolveCacheRetention({ cacheControlTtl: "5m" }, "anthropic")).toBe("short"); + expect(resolveCacheRetention({ cacheControlTtl: "1h" }, "anthropic")).toBe("long"); + expect(resolveCacheRetention({ cacheControlTtl: "5m" }, "cloudflare-ai-gateway")).toBe("short"); + }); +}); diff --git a/src/agents/pi-embedded-runner/anthropic-stream-wrappers.ts b/src/agents/pi-embedded-runner/anthropic-stream-wrappers.ts index 511b70d280d..f789ea43872 100644 --- a/src/agents/pi-embedded-runner/anthropic-stream-wrappers.ts +++ b/src/agents/pi-embedded-runner/anthropic-stream-wrappers.ts @@ -212,11 +212,14 @@ export function resolveCacheRetention( provider: string, ): CacheRetention | undefined { const isAnthropicDirect = provider === "anthropic"; + // Cloudflare AI Gateway proxies the Anthropic Messages API and passes + // cache_control through, so treat it like direct Anthropic (#46709). + const isCloudflareAnthropicProxy = provider === "cloudflare-ai-gateway"; const hasBedrockOverride = extraParams?.cacheRetention !== undefined || extraParams?.cacheControlTtl !== undefined; const isAnthropicBedrock = provider === "amazon-bedrock" && hasBedrockOverride; - if (!isAnthropicDirect && !isAnthropicBedrock) { + if (!isAnthropicDirect && !isCloudflareAnthropicProxy && !isAnthropicBedrock) { return undefined; } @@ -233,7 +236,7 @@ export function resolveCacheRetention( return "long"; } - return isAnthropicDirect ? "short" : undefined; + return isAnthropicDirect || isCloudflareAnthropicProxy ? "short" : undefined; } export function resolveAnthropicBetas(