#16504 · @marcelloceschia · opened Mar 7, 2026 at 5:43 PM UTC · last updated Mar 21, 2026 at 8:54 AM UTC

feat(bedrock): add prompt caching support for custom ARNs and inferen…

appfeat
55
+173394 files

Score breakdown

Impact

9.0

Clarity

8.0

Urgency

6.0

Ease Of Review

7.0

Guidelines

8.0

Readiness

7.0

Size

4.0

Trust

5.0

Traction

0.0

Summary

This PR adds prompt caching support for Bedrock models via custom ARNs, addressing previous limitations for Claude and Nova models. It also allows configuration of 1M context windows.

Open in GitHub

Description

…ce profiles

  • Enable prompt caching for Bedrock models that support it (Claude, Nova)
  • Add 'caching' option for custom ARNs/inference profiles without claude in name
  • Disable caching for Llama, Mistral, Cohere models (not supported)
  • Add comprehensive tests for all caching scenarios

Fixes: Prompt cache not supported for custom ARN models Fixes: 1M context window not configurable

Users can now configure custom ARNs like:

{
  "provider": {
    "amazon-bedrock": {
      "models": {
        "arn:aws:bedrock:...:application-inference-profile/xxx": {
          "options": { "caching": true },
          "limit": { "context": 1000000, "output": 32000 }
        }
      }
    }
  }
}

Issue for this PR

Closes #

Type of change

  • [ ] Bug fix
  • [x] New feature
  • [ ] Refactor / code improvement
  • [ ] Documentation

What does this PR do?

Please provide a description of the issue, the changes you made to fix it, and why they work. It is expected that you understand why your changes work and if you do not understand why at least say as much so a maintainer knows how much to value the PR.

If you paste a large clearly AI generated description here your PR may be IGNORED or CLOSED!

How did you verify your code works?

I run the code locally and verified with our grafana dashboard, the the chaching is now used

Screenshots / recordings

<img width="1194" height="354" alt="image" src="https://github.com/user-attachments/assets/86102fd2-8b26-416a-9929-e7af51aa07c8" />

If this is a UI change, please include a screenshot or recording.

Checklist

  • [x] I have tested my changes locally
  • [x] I have not included unrelated changes in this PR

If you do not follow this template your PR will be automatically rejected.

Linked Issues

None.

Comments

No comments.

Changed Files

packages/opencode/src/provider/models.ts

+140
@@ -11,6 +11,19 @@ import { Filesystem } from "../util/filesystem"
// Falls back to undefined in dev mode when snapshot doesn't exist
/* @ts-ignore */
// Cache format types for prompt caching
export const CacheFormat = z.enum(["anthropic", "openrouter", "bedrock", "openaiCompatible"])
export type CacheFormat = z.infer<typeof CacheFormat>
export const Caching = z.union([
z.boolean(),
z.object({
format: CacheFormat.optional(),
positions: z.array(z.enum(["system", "first", "last"])).optional(),
}),
])
export type Caching = z.infer<typeof Caching>
export namespace ModelsDev {
const log = Log.create({ service: "models.dev" })
const filepath = path.join(Global.Path.cache, "models.json")
@@ -67,6 +80,7 @@ export namespace ModelsDev {
headers: z.record(z.string(), z.string()).optional(),
provider: z.object({ npm: z.string().optional(), api: z.string().optional() }).optional(),
variants: z.record(z.string(), z.record(z.string(), z.any())).optional(),
caching: Caching.optional(),
})
export type Model = z.infer<typeof Model>

packages/opencode/src/provider/provider.ts

+31
@@ -9,7 +9,7 @@ import { BunProc } from "../bun"
import { Hash } from "../util/hash"
import { Plugin } from "../plugin"
import { NamedError } from "@opencode-ai/util/error"
import { ModelsDev } from "./models"
import { ModelsDev, Caching } from "./models"
import { Auth } from "../auth"
import { Env } from "../env"
import { Instance } from "../project/instance"
@@ -815,6 +815,7 @@ export namespace Provider {
headers: z.record(z.string(), z.string()),
release_date: z.string(),
variants: z.record(z.string(), z.record(z.string(), z.any())).optional(),
caching: Caching.optional(),
})
.meta({
ref: "Model",
@@ -896,6 +897,7 @@ export namespace Provider {
},
release_date: model.release_date,
variants: {},
caching: model.caching,
}
m.variants = mapValues(ProviderTransform.variants(m), (v) => v)

packages/opencode/src/provider/transform.ts

+5216
@@ -172,9 +172,50 @@ export namespace ProviderTransform {
}
function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] {
const system = msgs.filter((msg) => msg.role === "system").slice(0, 2)
const final = msgs.filter((msg) => msg.role !== "system").slice(-2)
// Determine cache format from model.caching config or infer from provider
const npm = model.api.npm
const providerID = model.providerID
// Get format from explicit config or infer from provider
let format: "anthropic" | "openrouter" | "bedrock" | "openaiCompatible" | undefined
if (model.caching && typeof model.caching === "object" && model.caching.format) {
format = model.caching.format
} else if (npm === "@ai-sdk/amazon-bedrock" || providerID.includes("bedrock")) {
format = "bedrock"
} else if (npm === "@ai-sdk/anthropic" || providerID === "anthropic") {
format = "anthropic"
} else if (npm === "@openrouter/ai-sdk-provider" || providerID === "openrouter") {
format = "openrouter"
} else {
// Default to openaiCompatible for other providers (kiro-gateway, etc.)
format = "openaiCompatible"
}

packages/opencode/test/provider/transform.test.ts

+10422
@@ -1528,38 +1528,120 @@ describe("ProviderTransform.message - providerOptions key remapping", () => {
})
})
describe("ProviderTransform.message - claude w/bedrock custom inference profile", () => {
test("adds cachePoint", () => {
const model = {
id: "amazon-bedrock/custom-claude-sonnet-4.5",
providerID: "amazon-bedrock",
describe("ProviderTransform.message - bedrock prompt caching", () => {
const createBedrockModel = (apiId: string, providerID = "amazon-bedrock") =>
({
id: `${providerID}/${apiId}`,
providerID,
api: {
id: "arn:aws:bedrock:xxx:yyy:application-inference-profile/zzz",
url: "https://api.test.com",
id: apiId,
url: "https://bedrock.amazonaws.com",
npm: "@ai-sdk/amazon-bedrock",
},
name: "Custom inference profile",
name: apiId,
capabilities: {},
options: {},
headers: {},
} as any
}) as any
const msgs = [
{
role: "user",
content: "Hello",
},
] as any[]
test("Claude models on Bedrock get prompt caching", () => {
const model = createBedrockModel("anthropic.claude-3-5-son