Skip to content

Commit 85e549a

Browse files
committed
feat(opencode): add cache_point_ttl option for Bedrock provider
1 parent cb8b668 commit 85e549a

3 files changed

Lines changed: 122 additions & 4 deletions

File tree

packages/opencode/src/config/provider.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ export const Info = Schema.Struct({
100100
description:
101101
"Timeout in milliseconds between streamed SSE chunks for this provider. If no chunk arrives within this window, the request is aborted.",
102102
}),
103+
cache_point_ttl: Schema.optional(Schema.Literals(["5m", "1h"])).annotate({
104+
description: "TTL for Bedrock cache points. Only applies to amazon-bedrock provider.",
105+
}),
103106
}),
104107
[Schema.Record(Schema.String, Schema.Any)],
105108
),

packages/opencode/src/provider/transform.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,16 @@ function normalizeMessages(
251251
return msgs
252252
}
253253

254-
function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] {
254+
function applyCaching(msgs: ModelMessage[], model: Provider.Model, options?: Record<string, unknown>): ModelMessage[] {
255255
const system = msgs.filter((msg) => msg.role === "system").slice(0, 2)
256256
const final = msgs.filter((msg) => msg.role !== "system").slice(-2)
257257

258+
const ttl = options?.cache_point_ttl as string | undefined
259+
const bedrockCachePoint: Record<string, unknown> = { type: "default" }
260+
if (ttl) {
261+
bedrockCachePoint.ttl = ttl
262+
}
263+
258264
const providerOptions = {
259265
anthropic: {
260266
cacheControl: { type: "ephemeral" },
@@ -263,7 +269,7 @@ function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage
263269
cacheControl: { type: "ephemeral" },
264270
},
265271
bedrock: {
266-
cachePoint: { type: "default" },
272+
cachePoint: bedrockCachePoint,
267273
},
268274
openaiCompatible: {
269275
cache_control: { type: "ephemeral" },
@@ -351,10 +357,12 @@ export function message(msgs: ModelMessage[], model: Provider.Model, options: Re
351357
model.id.includes("anthropic") ||
352358
model.id.includes("claude") ||
353359
model.api.npm === "@ai-sdk/anthropic" ||
354-
model.api.npm === "@ai-sdk/alibaba") &&
360+
model.api.npm === "@ai-sdk/alibaba" ||
361+
model.providerID.includes("bedrock") ||
362+
model.api.npm === "@ai-sdk/amazon-bedrock") &&
355363
model.api.npm !== "@ai-sdk/gateway"
356364
) {
357-
msgs = applyCaching(msgs, model)
365+
msgs = applyCaching(msgs, model, options)
358366
}
359367

360368
// Remap providerOptions keys from stored providerID to expected SDK key

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

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3331,3 +3331,110 @@ describe("ProviderTransform.variants", () => {
33313331
})
33323332
})
33333333
})
3334+
3335+
describe("ProviderTransform.message - bedrock cache_point_ttl", () => {
3336+
const bedrockModel = {
3337+
id: "amazon-bedrock/anthropic.claude-opus-4-6",
3338+
providerID: "amazon-bedrock",
3339+
api: {
3340+
id: "anthropic.claude-opus-4-6",
3341+
url: "https://bedrock-runtime.us-east-1.amazonaws.com",
3342+
npm: "@ai-sdk/amazon-bedrock",
3343+
},
3344+
name: "Claude Opus 4.6",
3345+
capabilities: {
3346+
temperature: true,
3347+
reasoning: false,
3348+
attachment: true,
3349+
toolcall: true,
3350+
input: { text: true, audio: false, image: true, video: false, pdf: true },
3351+
output: { text: true, audio: false, image: false, video: false, pdf: false },
3352+
interleaved: false,
3353+
},
3354+
cost: {
3355+
input: 0.003,
3356+
output: 0.015,
3357+
cache: { read: 0.0003, write: 0.00375 },
3358+
},
3359+
limit: {
3360+
context: 200000,
3361+
output: 8192,
3362+
},
3363+
status: "active",
3364+
options: {},
3365+
headers: {},
3366+
} as any
3367+
3368+
test("should add ttl to bedrock cachePoint when cache_point_ttl is set to 5m", () => {
3369+
const msgs = [
3370+
{
3371+
role: "system",
3372+
content: "You are a helpful assistant.",
3373+
},
3374+
{
3375+
role: "user",
3376+
content: "Hello",
3377+
},
3378+
] as any[]
3379+
3380+
const result = ProviderTransform.message(msgs, bedrockModel, { cache_point_ttl: "5m" })
3381+
3382+
expect(result).toHaveLength(2)
3383+
expect(result[0].providerOptions?.bedrock?.cachePoint).toEqual({
3384+
type: "default",
3385+
ttl: "5m",
3386+
})
3387+
expect(result[1].providerOptions?.bedrock?.cachePoint).toEqual({
3388+
type: "default",
3389+
ttl: "5m",
3390+
})
3391+
})
3392+
3393+
test("should add ttl to bedrock cachePoint when cache_point_ttl is set to 1h", () => {
3394+
const msgs = [
3395+
{
3396+
role: "system",
3397+
content: "You are a helpful assistant.",
3398+
},
3399+
{
3400+
role: "user",
3401+
content: "Hello",
3402+
},
3403+
] as any[]
3404+
3405+
const result = ProviderTransform.message(msgs, bedrockModel, { cache_point_ttl: "1h" })
3406+
3407+
expect(result).toHaveLength(2)
3408+
expect(result[0].providerOptions?.bedrock?.cachePoint).toEqual({
3409+
type: "default",
3410+
ttl: "1h",
3411+
})
3412+
expect(result[1].providerOptions?.bedrock?.cachePoint).toEqual({
3413+
type: "default",
3414+
ttl: "1h",
3415+
})
3416+
})
3417+
3418+
test("should not add ttl to bedrock cachePoint when cache_point_ttl is not set", () => {
3419+
const msgs = [
3420+
{
3421+
role: "system",
3422+
content: "You are a helpful assistant.",
3423+
},
3424+
{
3425+
role: "user",
3426+
content: "Hello",
3427+
},
3428+
] as any[]
3429+
3430+
const result = ProviderTransform.message(msgs, bedrockModel, {})
3431+
3432+
expect(result).toHaveLength(2)
3433+
expect(result[0].providerOptions?.bedrock?.cachePoint).toEqual({
3434+
type: "default",
3435+
})
3436+
expect(result[1].providerOptions?.bedrock?.cachePoint).toEqual({
3437+
type: "default",
3438+
})
3439+
})
3440+
})

0 commit comments

Comments
 (0)