Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Commit 64055fc

Browse files
committed
fix: enable prompt caching for AWS Bedrock custom ARN models
Add cachableFields to guessModelInfoFromId() for Claude model patterns so that custom ARN models matching known Claude patterns get proper caching metadata. Update supportsAwsPromptCache() to respect the user's explicit opt-in for custom ARN models, even when the model ID from the ARN is not recognized. When the Bedrock API receives cache points for a model that does not support caching, it simply ignores them without error. Fixes #11983
1 parent 137d3f4 commit 64055fc

2 files changed

Lines changed: 124 additions & 6 deletions

File tree

src/api/providers/__tests__/bedrock.spec.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,4 +1327,99 @@ describe("AwsBedrockHandler", () => {
13271327
expect(hasCachePoint).toBe(false)
13281328
})
13291329
})
1330+
1331+
describe("prompt caching with custom ARN", () => {
1332+
beforeEach(() => {
1333+
mockConverseStreamCommand.mockReset()
1334+
})
1335+
1336+
// System prompt must exceed minTokensPerCachePoint (1024) for cache points to be placed
1337+
const longSystemPrompt = "You are a helpful assistant. ".repeat(200)
1338+
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
1339+
1340+
it("should enable prompt caching for custom ARN with recognized Claude model ID", async () => {
1341+
// Custom ARN containing a Claude model ID that matches the guess pattern
1342+
const customArnHandler = new AwsBedrockHandler({
1343+
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
1344+
awsAccessKey: "test-access-key",
1345+
awsSecretKey: "test-secret-key",
1346+
awsRegion: "us-east-1",
1347+
awsCustomArn: "arn:aws:bedrock:us-east-1:123456789012:inference-profile/claude-3-5-sonnet-custom",
1348+
})
1349+
1350+
const generator = customArnHandler.createMessage(longSystemPrompt, messages)
1351+
await generator.next()
1352+
1353+
expect(mockConverseStreamCommand).toHaveBeenCalled()
1354+
const commandArg = mockConverseStreamCommand.mock.calls[0][0] as any
1355+
1356+
// System content should include a cachePoint since prompt caching should work
1357+
const systemBlocks = commandArg.system
1358+
const hasCachePoint = systemBlocks?.some((block: any) => block.cachePoint !== undefined)
1359+
expect(hasCachePoint).toBe(true)
1360+
})
1361+
1362+
it("should enable prompt caching for custom ARN with unrecognized model ID when user opts in", async () => {
1363+
// Custom ARN with an opaque model ID that doesn't match any pattern
1364+
const customArnHandler = new AwsBedrockHandler({
1365+
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
1366+
awsAccessKey: "test-access-key",
1367+
awsSecretKey: "test-secret-key",
1368+
awsRegion: "us-east-1",
1369+
awsUsePromptCache: true,
1370+
awsCustomArn: "arn:aws:bedrock:us-east-1:123456789012:provisioned-model/my-custom-model-xyz",
1371+
})
1372+
1373+
const generator = customArnHandler.createMessage(longSystemPrompt, messages)
1374+
await generator.next()
1375+
1376+
expect(mockConverseStreamCommand).toHaveBeenCalled()
1377+
const commandArg = mockConverseStreamCommand.mock.calls[0][0] as any
1378+
1379+
// System content should include a cachePoint since user explicitly enabled caching
1380+
const systemBlocks = commandArg.system
1381+
const hasCachePoint = systemBlocks?.some((block: any) => block.cachePoint !== undefined)
1382+
expect(hasCachePoint).toBe(true)
1383+
})
1384+
1385+
it("should disable prompt caching for custom ARN when user explicitly disables it", async () => {
1386+
const customArnHandler = new AwsBedrockHandler({
1387+
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
1388+
awsAccessKey: "test-access-key",
1389+
awsSecretKey: "test-secret-key",
1390+
awsRegion: "us-east-1",
1391+
awsUsePromptCache: false,
1392+
awsCustomArn: "arn:aws:bedrock:us-east-1:123456789012:inference-profile/claude-3-5-sonnet-custom",
1393+
})
1394+
1395+
const generator = customArnHandler.createMessage(longSystemPrompt, messages)
1396+
await generator.next()
1397+
1398+
expect(mockConverseStreamCommand).toHaveBeenCalled()
1399+
const commandArg = mockConverseStreamCommand.mock.calls[0][0] as any
1400+
1401+
// System content should NOT include cachePoint since user explicitly disabled caching
1402+
const systemBlocks = commandArg.system
1403+
const hasCachePoint = systemBlocks?.some((block: any) => block.cachePoint !== undefined)
1404+
expect(hasCachePoint).toBe(false)
1405+
})
1406+
1407+
it("should include cachableFields in guessModelInfoFromId for Claude patterns", () => {
1408+
// Test with a custom ARN that has a Claude model ID in it
1409+
const customArnHandler = new AwsBedrockHandler({
1410+
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
1411+
awsAccessKey: "test-access-key",
1412+
awsSecretKey: "test-secret-key",
1413+
awsRegion: "us-east-1",
1414+
awsCustomArn: "arn:aws:bedrock:us-east-1:123456789012:inference-profile/claude-3-5-sonnet-custom",
1415+
})
1416+
1417+
const modelConfig = customArnHandler.getModel()
1418+
expect(modelConfig.info.supportsPromptCache).toBe(true)
1419+
expect((modelConfig.info as any).cachableFields).toBeDefined()
1420+
expect((modelConfig.info as any).cachableFields).toContain("system")
1421+
expect((modelConfig.info as any).cachableFields).toContain("messages")
1422+
expect((modelConfig.info as any).cachableFields).toContain("tools")
1423+
})
1424+
})
13301425
})

src/api/providers/bedrock.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,36 +295,42 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
295295
contextWindow: 200_000,
296296
supportsImages: true,
297297
supportsPromptCache: true,
298+
cachableFields: ["system", "messages", "tools"],
298299
},
299300
"claude-3-7": {
300301
maxTokens: 8192,
301302
contextWindow: 200_000,
302303
supportsImages: true,
303304
supportsPromptCache: true,
305+
cachableFields: ["system", "messages", "tools"],
304306
},
305307
"claude-3-5": {
306308
maxTokens: 8192,
307309
contextWindow: 200_000,
308310
supportsImages: true,
309311
supportsPromptCache: true,
312+
cachableFields: ["system", "messages", "tools"],
310313
},
311314
"claude-4-opus": {
312315
maxTokens: 4096,
313316
contextWindow: 200_000,
314317
supportsImages: true,
315318
supportsPromptCache: true,
319+
cachableFields: ["system", "messages", "tools"],
316320
},
317321
"claude-3-opus": {
318322
maxTokens: 4096,
319323
contextWindow: 200_000,
320324
supportsImages: true,
321325
supportsPromptCache: true,
326+
cachableFields: ["system", "messages", "tools"],
322327
},
323328
"claude-3-haiku": {
324329
maxTokens: 4096,
325330
contextWindow: 200_000,
326331
supportsImages: true,
327332
supportsPromptCache: true,
333+
cachableFields: ["system", "messages", "tools"],
328334
},
329335
}
330336

@@ -1172,12 +1178,29 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
11721178
// Check if the model supports prompt cache
11731179
// The cachableFields property is not part of the ModelInfo type in schemas
11741180
// but it's used in the bedrockModels object in shared/api.ts
1175-
return (
1176-
modelConfig?.info?.supportsPromptCache &&
1177-
// Use optional chaining and type assertion to access cachableFields
1178-
(modelConfig?.info as any)?.cachableFields &&
1179-
(modelConfig?.info as any)?.cachableFields?.length > 0
1180-
)
1181+
const hasCachableFields =
1182+
(modelConfig?.info as any)?.cachableFields && (modelConfig?.info as any)?.cachableFields?.length > 0
1183+
1184+
if (modelConfig?.info?.supportsPromptCache && hasCachableFields) {
1185+
return true
1186+
}
1187+
1188+
// When using a custom ARN and the user has enabled prompt caching (or left it
1189+
// at the default), respect their intent even if the model info is incomplete.
1190+
// The model info may lack cachableFields or supportsPromptCache when the model
1191+
// ID extracted from the ARN doesn't match a known model in bedrockModels.
1192+
// In this case, inject defaults so the downstream caching logic works correctly.
1193+
// If the underlying model truly does not support caching, the Bedrock API
1194+
// simply ignores cache points without erroring.
1195+
if (this.options.awsCustomArn && this.options.awsUsePromptCache !== false) {
1196+
if (!hasCachableFields) {
1197+
;(modelConfig.info as any).cachableFields = ["system", "messages", "tools"]
1198+
}
1199+
modelConfig.info.supportsPromptCache = true
1200+
return true
1201+
}
1202+
1203+
return false
11811204
}
11821205

11831206
/**

0 commit comments

Comments
 (0)