mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-28 10:11:14 +00:00
fix(agents): validate cacheRetention values (#94494)
* fix(agents): map cacheRetention 'standard' to 'short' for Bedrock Claude models * fix(agents): scope standard cacheRetention alias to Anthropic cache family only * fix(agents): validate prompt cache retention * chore: keep changelog release-owned --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -21,6 +21,7 @@ Provider references:
|
||||
### `cacheRetention`
|
||||
|
||||
Values: `"none" | "short" | "long"`. Configurable as a global default, per model, and per agent.
|
||||
`"standard"` is not an alias; use `"short"` for the provider's default cache window. Invalid values are ignored with a warning.
|
||||
|
||||
```yaml
|
||||
agents:
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { StreamFn } from "openclaw/plugin-sdk/agent-core";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createLlmStreamSimpleMock } from "../../../test/helpers/agents/llm-stream-simple-mock.js";
|
||||
import { testing as extraParamsTesting, applyExtraParamsToAgent } from "./extra-params.js";
|
||||
import { log } from "./logger.js";
|
||||
import { resolveCacheRetention } from "./prompt-cache-retention.js";
|
||||
|
||||
function applyAndExpectWrapped(params: {
|
||||
@@ -44,6 +45,7 @@ vi.mock("./logger.js", () => ({
|
||||
vi.mock("../../llm/stream.js", () => createLlmStreamSimpleMock());
|
||||
|
||||
beforeEach(() => {
|
||||
vi.mocked(log.warn).mockClear();
|
||||
extraParamsTesting.setProviderRuntimeDepsForTest({
|
||||
prepareProviderExtraParams: () => undefined,
|
||||
resolveProviderExtraParamsForTransport: () => undefined,
|
||||
@@ -257,6 +259,30 @@ describe("cacheRetention default behavior", () => {
|
||||
).toBe("long");
|
||||
});
|
||||
|
||||
it("warns instead of creating an undocumented cacheRetention alias", () => {
|
||||
applyAndExpectWrapped({
|
||||
cfg: {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"amazon-bedrock/us.anthropic.claude-sonnet-4-6": {
|
||||
params: { cacheRetention: "standard" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
modelId: "us.anthropic.claude-sonnet-4-6",
|
||||
model: { api: "openai-completions" } as Parameters<typeof applyExtraParamsToAgent>[8],
|
||||
provider: "amazon-bedrock",
|
||||
});
|
||||
|
||||
expect(log.warn).toHaveBeenCalledOnce();
|
||||
expect(log.warn).toHaveBeenCalledWith(
|
||||
'ignoring invalid cacheRetention param; expected "none", "short", or "long"',
|
||||
);
|
||||
});
|
||||
|
||||
it("defaults to 'short' for anthropic-vertex without explicit config", () => {
|
||||
expect(
|
||||
resolveCacheRetention(
|
||||
|
||||
@@ -42,7 +42,7 @@ import type { AgentRuntimeTransport } from "../runtime-plan/types.js";
|
||||
import type { StreamFn } from "../runtime/index.js";
|
||||
import type { SettingsManager } from "../sessions/index.js";
|
||||
import { log } from "./logger.js";
|
||||
import { resolveCacheRetention } from "./prompt-cache-retention.js";
|
||||
import { parseCacheRetention, resolveCacheRetention } from "./prompt-cache-retention.js";
|
||||
import type { ProviderThinkLevel } from "./utils.js";
|
||||
|
||||
const defaultProviderRuntimeDeps = {
|
||||
@@ -452,6 +452,15 @@ function createStreamFnWithExtraParams(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (
|
||||
Object.hasOwn(extraParams, "cacheRetention") &&
|
||||
parseCacheRetention(extraParams.cacheRetention) === undefined
|
||||
) {
|
||||
// Provider params stay open-ended, so validate this shared knob at its consumer boundary.
|
||||
// Never echo the authored value: model params can contain sensitive custom data.
|
||||
log.warn('ignoring invalid cacheRetention param; expected "none", "short", or "long"');
|
||||
}
|
||||
|
||||
const streamParams: CacheRetentionStreamOptions = {};
|
||||
if (typeof extraParams.temperature === "number") {
|
||||
streamParams.temperature = extraParams.temperature;
|
||||
|
||||
@@ -63,6 +63,17 @@ describe("prompt cache retention", () => {
|
||||
).toBe("none");
|
||||
});
|
||||
|
||||
it("keeps undocumented cacheRetention values outside the Bedrock runtime contract", () => {
|
||||
expect(
|
||||
resolveCacheRetention(
|
||||
{ cacheRetention: "standard" },
|
||||
"amazon-bedrock",
|
||||
"openai-completions",
|
||||
"us.anthropic.claude-sonnet-4-6",
|
||||
),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not honor explicit cacheRetention for openai-completions without supportsPromptCacheKey", () => {
|
||||
// Providers that route via openai-completions but do not advertise prompt
|
||||
// caching must keep retention out of outgoing payloads.
|
||||
|
||||
@@ -6,6 +6,10 @@ import { resolveAnthropicCacheRetentionFamily } from "../../llm/providers/stream
|
||||
|
||||
type CacheRetention = "none" | "short" | "long";
|
||||
|
||||
export function parseCacheRetention(value: unknown): CacheRetention | undefined {
|
||||
return value === "none" || value === "short" || value === "long" ? value : undefined;
|
||||
}
|
||||
|
||||
export function isGooglePromptCacheEligible(params: {
|
||||
modelApi?: string;
|
||||
modelId?: string;
|
||||
@@ -46,8 +50,8 @@ export function resolveCacheRetention(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const newVal = extraParams?.cacheRetention;
|
||||
if (newVal === "none" || newVal === "short" || newVal === "long") {
|
||||
const newVal = parseCacheRetention(extraParams?.cacheRetention);
|
||||
if (newVal) {
|
||||
return newVal;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user