From 4bacdc882454fa6ae557d6522f778b630cb4c4a2 Mon Sep 17 00:00:00 2001 From: Ted Li Date: Tue, 21 Apr 2026 02:45:27 -0700 Subject: [PATCH] fix(agents): honor explicit long Anthropic cache TTL on custom hosts (#67800) Merged via squash. Prepared head SHA: 0ffde15713b68c6953da826fca06a67000086b9b Co-authored-by: MonkeyLeeT <6754057+MonkeyLeeT@users.noreply.github.com> Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com> Reviewed-by: @hxy91819 --- CHANGELOG.md | 9 ++++ src/agents/anthropic-payload-policy.test.ts | 55 ++++++++++++++++++++- src/agents/anthropic-payload-policy.ts | 7 ++- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c16773323c2..466b031682d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ Docs: https://docs.openclaw.ai +## Unreleased + +### Changes + +### Fixes + +- Agents/Anthropic: honor explicit `cacheRetention: "long"` for custom `anthropic-messages` endpoints by applying the 1-hour ephemeral cache TTL independently of the Anthropic/Vertex hostname allowlist. Implicit and env-driven long retention still require an allowlisted host. (#67800) Thanks @MonkeyLeeT. +- fix(agents): honor explicit long Anthropic cache TTL on custom hosts (#67800). Thanks @MonkeyLeeT + ## 2026.4.20 ### Changes diff --git a/src/agents/anthropic-payload-policy.test.ts b/src/agents/anthropic-payload-policy.test.ts index 3925359b2a3..3d6583eff8b 100644 --- a/src/agents/anthropic-payload-policy.test.ts +++ b/src/agents/anthropic-payload-policy.test.ts @@ -86,7 +86,7 @@ describe("anthropic payload policy", () => { }); }); - it("denies proxied Anthropic service tier and omits long-TTL upgrades for custom hosts", () => { + it("denies proxied Anthropic service tier but honors explicit long TTL for custom hosts", () => { const policy = resolveAnthropicPayloadPolicy({ provider: "anthropic", api: "anthropic-messages", @@ -103,6 +103,59 @@ describe("anthropic payload policy", () => { applyAnthropicPayloadPolicyToParams(payload, policy); expect(payload).not.toHaveProperty("service_tier"); + expect(payload.system).toEqual([textBlock("Follow policy.", { type: "ephemeral", ttl: "1h" })]); + expect(payload.messages[0]).toEqual({ + role: "user", + content: [{ type: "text", text: "Hello", cache_control: { type: "ephemeral", ttl: "1h" } }], + }); + }); + + it("keeps implicit env-driven long retention conservative for custom hosts", () => { + const previous = process.env.PI_CACHE_RETENTION; + process.env.PI_CACHE_RETENTION = "long"; + try { + const policy = resolveAnthropicPayloadPolicy({ + provider: "anthropic", + api: "anthropic-messages", + baseUrl: "https://proxy.example.com/anthropic", + enableCacheControl: true, + }); + const payload: TestPayload = { + system: [{ type: "text", text: "Follow policy." }], + messages: [{ role: "user", content: "Hello" }], + }; + + applyAnthropicPayloadPolicyToParams(payload, policy); + + expect(payload.system).toEqual([textBlock("Follow policy.", { type: "ephemeral" })]); + expect(payload.messages[0]).toEqual({ + role: "user", + content: [{ type: "text", text: "Hello", cache_control: { type: "ephemeral" } }], + }); + } finally { + if (previous === undefined) { + delete process.env.PI_CACHE_RETENTION; + } else { + process.env.PI_CACHE_RETENTION = previous; + } + } + }); + + it("keeps explicit short retention unchanged for custom hosts", () => { + const policy = resolveAnthropicPayloadPolicy({ + provider: "anthropic", + api: "anthropic-messages", + baseUrl: "https://proxy.example.com/anthropic", + cacheRetention: "short", + enableCacheControl: true, + }); + const payload: TestPayload = { + system: [{ type: "text", text: "Follow policy." }], + messages: [{ role: "user", content: "Hello" }], + }; + + applyAnthropicPayloadPolicyToParams(payload, policy); + expect(payload.system).toEqual([textBlock("Follow policy.", { type: "ephemeral" })]); expect(payload.messages[0]).toEqual({ role: "user", diff --git a/src/agents/anthropic-payload-policy.ts b/src/agents/anthropic-payload-policy.ts index e72be3930d7..4d046bf19f1 100644 --- a/src/agents/anthropic-payload-policy.ts +++ b/src/agents/anthropic-payload-policy.ts @@ -58,7 +58,12 @@ function resolveAnthropicEphemeralCacheControl( if (retention === "none") { return undefined; } - const ttl = retention === "long" && isLongTtlEligibleEndpoint(baseUrl) ? "1h" : undefined; + // Trust explicit long-retention opt-ins for Anthropic-compatible custom providers. + // Keep hostname gating for implicit/env-driven long retention so defaults stay conservative. + const ttl = + retention === "long" && (cacheRetention === "long" || isLongTtlEligibleEndpoint(baseUrl)) + ? "1h" + : undefined; return { type: "ephemeral", ...(ttl ? { ttl } : {}) }; }