fix(bedrock): enable prompt caching for custom ARN and default to ON (#11373)

* fix(bedrock): enable prompt caching for custom ARN and default to ON

- Set supportsPromptCache to true for custom-arn model info in useSelectedModel.ts
- Change awsUsePromptCache default from false to true using nullish coalescing
- Add tests for custom-arn prompt caching support

Closes #10846

* fix(bedrock): align backend awsUsePromptCache default with UI (?? true)

The backend treated undefined awsUsePromptCache as falsy (OFF) while the
UI checkbox defaulted to true via nullish coalescing. This caused the UI
to show prompt caching as ON but the backend to keep it OFF for new users.

Apply the same ?? true default in the backend so both sides agree.

---------

Co-authored-by: Roo Code <roomote@roocode.com>
This commit is contained in:
roomote[bot] 2026-02-20 00:06:52 -07:00 committed by GitHub
parent 3a7a01f2f7
commit 27095553ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 103 additions and 3 deletions

View file

@ -1275,4 +1275,56 @@ describe("AwsBedrockHandler", () => {
expect(mockCaptureException).toHaveBeenCalled()
})
})
describe("prompt cache default behavior", () => {
beforeEach(() => {
mockConverseStreamCommand.mockReset()
})
// System prompt must exceed minTokensPerCachePoint (1024) for cache points to be placed
const longSystemPrompt = "You are a helpful assistant. ".repeat(200)
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
it("should enable prompt caching by default when awsUsePromptCache is undefined", async () => {
const defaultHandler = new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "us-east-1",
// awsUsePromptCache is intentionally omitted (undefined)
})
const generator = defaultHandler.createMessage(longSystemPrompt, messages)
await generator.next() // Start the generator
expect(mockConverseStreamCommand).toHaveBeenCalled()
const commandArg = mockConverseStreamCommand.mock.calls[0][0] as any
// System content should include a cachePoint entry since prompt caching defaults to ON
const systemBlocks = commandArg.system
const hasCachePoint = systemBlocks?.some((block: any) => block.cachePoint !== undefined)
expect(hasCachePoint).toBe(true)
})
it("should disable prompt caching when awsUsePromptCache is explicitly false", async () => {
const disabledHandler = new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "us-east-1",
awsUsePromptCache: false,
})
const generator = disabledHandler.createMessage(longSystemPrompt, messages)
await generator.next() // Start the generator
expect(mockConverseStreamCommand).toHaveBeenCalled()
const commandArg = mockConverseStreamCommand.mock.calls[0][0] as any
// System content should NOT include cachePoint since caching is explicitly disabled
const systemBlocks = commandArg.system
const hasCachePoint = systemBlocks?.some((block: any) => block.cachePoint !== undefined)
expect(hasCachePoint).toBe(false)
})
})
})

View file

@ -357,7 +357,9 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
},
): ApiStream {
const modelConfig = this.getModel()
const usePromptCache = Boolean(this.options.awsUsePromptCache && this.supportsAwsPromptCache(modelConfig))
const usePromptCache = Boolean(
(this.options.awsUsePromptCache ?? true) && this.supportsAwsPromptCache(modelConfig),
)
const conversationId =
messages.length > 0

View file

@ -198,7 +198,7 @@ export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedMo
{selectedModelInfo?.supportsPromptCache && (
<>
<Checkbox
checked={apiConfiguration?.awsUsePromptCache || false}
checked={apiConfiguration?.awsUsePromptCache ?? true}
onChange={handleInputChange("awsUsePromptCache", noTransform)}>
<div className="flex items-center gap-1">
<span>{t("settings:providers.enablePromptCaching")}</span>

View file

@ -496,6 +496,52 @@ describe("useSelectedModel", () => {
})
})
describe("bedrock provider with custom ARN", () => {
beforeEach(() => {
mockUseRouterModels.mockReturnValue({
data: {
openrouter: {},
requesty: {},
litellm: {},
},
isLoading: false,
isError: false,
} as any)
mockUseOpenRouterModelProviders.mockReturnValue({
data: {},
isLoading: false,
isError: false,
} as any)
})
it("should enable supportsPromptCache for custom-arn model", () => {
const apiConfiguration: ProviderSettings = {
apiProvider: "bedrock",
apiModelId: "custom-arn",
}
const wrapper = createWrapper()
const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper })
expect(result.current.id).toBe("custom-arn")
expect(result.current.info?.supportsPromptCache).toBe(true)
})
it("should enable supportsImages for custom-arn model", () => {
const apiConfiguration: ProviderSettings = {
apiProvider: "bedrock",
apiModelId: "custom-arn",
}
const wrapper = createWrapper()
const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper })
expect(result.current.id).toBe("custom-arn")
expect(result.current.info?.supportsImages).toBe(true)
})
})
describe("litellm provider", () => {
beforeEach(() => {
mockUseOpenRouterModelProviders.mockReturnValue({

View file

@ -182,7 +182,7 @@ function getSelectedModel({
if (id === "custom-arn") {
return {
id,
info: { maxTokens: 5000, contextWindow: 128_000, supportsPromptCache: false, supportsImages: true },
info: { maxTokens: 5000, contextWindow: 128_000, supportsPromptCache: true, supportsImages: true },
}
}