mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-07 15:21:06 +00:00
fix(providers): align cache-ttl anthropic semantics (#60375)
This commit is contained in:
@@ -16,6 +16,21 @@ export function isOpenRouterAnthropicModelRef(provider: string, modelId: string)
|
||||
return provider.trim().toLowerCase() === "openrouter" && isAnthropicModelRef(modelId);
|
||||
}
|
||||
|
||||
export function isAnthropicFamilyCacheTtlEligible(params: {
|
||||
provider: string;
|
||||
modelApi?: string;
|
||||
modelId: string;
|
||||
}): boolean {
|
||||
const normalizedProvider = params.provider.trim().toLowerCase();
|
||||
if (normalizedProvider === "anthropic") {
|
||||
return true;
|
||||
}
|
||||
if (normalizedProvider === "amazon-bedrock") {
|
||||
return isAnthropicBedrockModel(params.modelId);
|
||||
}
|
||||
return params.modelApi === "anthropic-messages";
|
||||
}
|
||||
|
||||
export function resolveAnthropicCacheRetentionFamily(params: {
|
||||
provider: string;
|
||||
modelApi?: string;
|
||||
@@ -35,7 +50,6 @@ export function resolveAnthropicCacheRetentionFamily(params: {
|
||||
return "anthropic-bedrock";
|
||||
}
|
||||
if (
|
||||
normalizedProvider !== "anthropic" &&
|
||||
normalizedProvider !== "amazon-bedrock" &&
|
||||
params.hasExplicitCacheConfig &&
|
||||
params.modelApi === "anthropic-messages"
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("../../plugins/provider-runtime.js", () => ({
|
||||
resolveProviderCacheTtlEligibility: (params: {
|
||||
context: { provider: string; modelId: string };
|
||||
context: { provider: string; modelId: string; modelApi?: string };
|
||||
}) => {
|
||||
if (params.context.provider === "anthropic") {
|
||||
return true;
|
||||
@@ -47,4 +47,20 @@ describe("isCacheTtlEligibleProvider", () => {
|
||||
expect(isCacheTtlEligibleProvider("openai", "gpt-4o")).toBe(false);
|
||||
expect(isCacheTtlEligibleProvider("openrouter", "openai/gpt-4o")).toBe(false);
|
||||
});
|
||||
|
||||
it("allows custom anthropic-messages providers", () => {
|
||||
expect(isCacheTtlEligibleProvider("litellm", "claude-sonnet-4-6", "anthropic-messages")).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("allows anthropic Bedrock models", () => {
|
||||
expect(
|
||||
isCacheTtlEligibleProvider(
|
||||
"amazon-bedrock",
|
||||
"us.anthropic.claude-sonnet-4-20250514-v1:0",
|
||||
"anthropic-messages",
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { resolveProviderCacheTtlEligibility } from "../../plugins/provider-runtime.js";
|
||||
import { isAnthropicFamilyCacheTtlEligible } from "./anthropic-family-cache-semantics.js";
|
||||
|
||||
type CustomEntryLike = { type?: unknown; customType?: unknown; data?: unknown };
|
||||
|
||||
@@ -10,7 +11,11 @@ export type CacheTtlEntryData = {
|
||||
modelId?: string;
|
||||
};
|
||||
|
||||
export function isCacheTtlEligibleProvider(provider: string, modelId: string): boolean {
|
||||
export function isCacheTtlEligibleProvider(
|
||||
provider: string,
|
||||
modelId: string,
|
||||
modelApi?: string,
|
||||
): boolean {
|
||||
const normalizedProvider = provider.toLowerCase();
|
||||
const normalizedModelId = modelId.toLowerCase();
|
||||
const pluginEligibility = resolveProviderCacheTtlEligibility({
|
||||
@@ -18,12 +23,17 @@ export function isCacheTtlEligibleProvider(provider: string, modelId: string): b
|
||||
context: {
|
||||
provider: normalizedProvider,
|
||||
modelId: normalizedModelId,
|
||||
modelApi,
|
||||
},
|
||||
});
|
||||
if (pluginEligibility !== undefined) {
|
||||
return pluginEligibility;
|
||||
}
|
||||
return false;
|
||||
return isAnthropicFamilyCacheTtlEligible({
|
||||
provider: normalizedProvider,
|
||||
modelId: normalizedModelId,
|
||||
modelApi,
|
||||
});
|
||||
}
|
||||
|
||||
export function readLastCacheTtlTimestamp(sessionManager: unknown): number | null {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { getCompactionSafeguardRuntime } from "../pi-hooks/compaction-safeguard-runtime.js";
|
||||
import compactionSafeguardExtension from "../pi-hooks/compaction-safeguard.js";
|
||||
import contextPruningExtension from "../pi-hooks/context-pruning.js";
|
||||
import { buildEmbeddedExtensionFactories } from "./extensions.js";
|
||||
|
||||
function buildSafeguardFactories(cfg: OpenClawConfig) {
|
||||
@@ -69,4 +70,24 @@ describe("buildEmbeddedExtensionFactories", () => {
|
||||
qualityGuardMaxRetries: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it("enables cache-ttl pruning for custom anthropic-messages providers", () => {
|
||||
const factories = buildEmbeddedExtensionFactories({
|
||||
cfg: {
|
||||
agents: {
|
||||
defaults: {
|
||||
contextPruning: {
|
||||
mode: "cache-ttl",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
sessionManager: {} as SessionManager,
|
||||
provider: "litellm",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
model: { api: "anthropic-messages", contextWindow: 200_000 } as Model<Api>,
|
||||
});
|
||||
|
||||
expect(factories).toContain(contextPruningExtension);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,7 +39,7 @@ function buildContextPruningFactory(params: {
|
||||
if (raw?.mode !== "cache-ttl") {
|
||||
return undefined;
|
||||
}
|
||||
if (!isCacheTtlEligibleProvider(params.provider, params.modelId)) {
|
||||
if (!isCacheTtlEligibleProvider(params.provider, params.modelId, params.model?.api)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ describe("runEmbeddedAttempt cache-ttl tracking after compaction", () => {
|
||||
},
|
||||
provider: "anthropic",
|
||||
modelId: "claude-sonnet-4-20250514",
|
||||
modelApi: "anthropic-messages",
|
||||
isCacheTtlEligibleProvider: () => true,
|
||||
now: 123,
|
||||
});
|
||||
@@ -54,6 +55,7 @@ describe("runEmbeddedAttempt cache-ttl tracking after compaction", () => {
|
||||
},
|
||||
provider: "anthropic",
|
||||
modelId: "claude-sonnet-4-20250514",
|
||||
modelApi: "anthropic-messages",
|
||||
isCacheTtlEligibleProvider: () => true,
|
||||
now: 123,
|
||||
});
|
||||
|
||||
@@ -47,14 +47,15 @@ export function shouldAppendAttemptCacheTtl(params: {
|
||||
config?: OpenClawConfig;
|
||||
provider: string;
|
||||
modelId: string;
|
||||
isCacheTtlEligibleProvider: (provider: string, modelId: string) => boolean;
|
||||
modelApi?: string;
|
||||
isCacheTtlEligibleProvider: (provider: string, modelId: string, modelApi?: string) => boolean;
|
||||
}): boolean {
|
||||
if (params.timedOutDuringCompaction || params.compactionOccurredThisAttempt) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
params.config?.agents?.defaults?.contextPruning?.mode === "cache-ttl" &&
|
||||
params.isCacheTtlEligibleProvider(params.provider, params.modelId)
|
||||
params.isCacheTtlEligibleProvider(params.provider, params.modelId, params.modelApi)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -67,7 +68,8 @@ export function appendAttemptCacheTtlIfNeeded(params: {
|
||||
config?: OpenClawConfig;
|
||||
provider: string;
|
||||
modelId: string;
|
||||
isCacheTtlEligibleProvider: (provider: string, modelId: string) => boolean;
|
||||
modelApi?: string;
|
||||
isCacheTtlEligibleProvider: (provider: string, modelId: string, modelApi?: string) => boolean;
|
||||
now?: number;
|
||||
}): boolean {
|
||||
if (!shouldAppendAttemptCacheTtl(params)) {
|
||||
|
||||
@@ -1722,6 +1722,7 @@ export async function runEmbeddedAttempt(
|
||||
config: params.config,
|
||||
provider: params.provider,
|
||||
modelId: params.modelId,
|
||||
modelApi: params.model.api,
|
||||
isCacheTtlEligibleProvider,
|
||||
});
|
||||
|
||||
|
||||
@@ -730,6 +730,7 @@ export type ProviderCreateEmbeddingProviderContext = {
|
||||
export type ProviderCacheTtlEligibilityContext = {
|
||||
provider: string;
|
||||
modelId: string;
|
||||
modelApi?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user