Skip to content

Commit da639e1

Browse files
committed
fix prompt caching for sdk
1 parent 97a50af commit da639e1

File tree

2 files changed

+65
-36
lines changed

2 files changed

+65
-36
lines changed

common/src/util/messages.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,19 @@ export function withCacheControl<
2727
wrapper.providerOptions = {}
2828
}
2929

30-
for (const provider of ['anthropic', 'openrouter', 'codebuff'] as const) {
30+
/* 'codebuff' provider name is not compatible with providerMetadata for
31+
* messages, so we need to use 'openaiCompatible' instead.
32+
* https://github.com/vercel/ai/blob/8e4fdac31b4f8c6a8d07a606a8833e74adf99470/packages/openai-compatible/src/chat/convert-to-openai-compatible-chat-messages.ts#L9
33+
*/
34+
for (const provider of [
35+
'anthropic',
36+
'openrouter',
37+
'openaiCompatible',
38+
] as const) {
3139
if (!wrapper.providerOptions[provider]) {
3240
wrapper.providerOptions[provider] = {}
3341
}
34-
wrapper.providerOptions[provider].cacheControl = { type: 'ephemeral' }
42+
wrapper.providerOptions[provider].cache_control = { type: 'ephemeral' }
3543
}
3644

3745
return wrapper
@@ -42,15 +50,15 @@ export function withoutCacheControl<
4250
>(obj: T): T {
4351
const wrapper = cloneDeep(obj)
4452

45-
for (const provider of ['anthropic', 'openrouter', 'codebuff'] as const) {
46-
if (has(wrapper.providerOptions?.[provider]?.cacheControl, 'type')) {
47-
delete wrapper.providerOptions?.[provider]?.cacheControl?.type
53+
for (const provider of ['anthropic', 'openrouter', 'openaiCompatible'] as const) {
54+
if (has(wrapper.providerOptions?.[provider]?.cache_control, 'type')) {
55+
delete wrapper.providerOptions?.[provider]?.cache_control?.type
4856
}
4957
if (
50-
Object.keys(wrapper.providerOptions?.[provider]?.cacheControl ?? {})
58+
Object.keys(wrapper.providerOptions?.[provider]?.cache_control ?? {})
5159
.length === 0
5260
) {
53-
delete wrapper.providerOptions?.[provider]?.cacheControl
61+
delete wrapper.providerOptions?.[provider]?.cache_control
5462
}
5563
if (Object.keys(wrapper.providerOptions?.[provider] ?? {}).length === 0) {
5664
delete wrapper.providerOptions?.[provider]

sdk/src/impl/llm.ts

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ import {
88
checkLiveUserInput,
99
getLiveUserInputIds,
1010
} from '@codebuff/agent-runtime/live-user-inputs'
11-
import { PROFIT_MARGIN } from '@codebuff/common/old-constants'
11+
import { models, PROFIT_MARGIN } from '@codebuff/common/old-constants'
1212
import { buildArray } from '@codebuff/common/util/array'
1313
import { getErrorObject } from '@codebuff/common/util/error'
1414
import { convertCbToModelMessages } from '@codebuff/common/util/messages'
15+
import { isExplicitlyDefinedModel } from '@codebuff/common/util/model-utils'
1516
import { StopSequenceHandler } from '@codebuff/common/util/stop-sequence'
1617
import { streamText, APICallError, generateText, generateObject } from 'ai'
1718

@@ -24,8 +25,8 @@ import type {
2425
PromptAiSdkStructuredInput,
2526
PromptAiSdkStructuredOutput,
2627
} from '@codebuff/common/types/contracts/llm'
27-
import type { Logger } from '@codebuff/common/types/contracts/logger'
2828
import type { ParamsOf } from '@codebuff/common/types/function-params'
29+
import type { JSONObject } from '@codebuff/common/types/json'
2930
import type { OpenRouterProviderOptions } from '@openrouter/ai-sdk-provider'
3031
import type z from 'zod/v4'
3132

@@ -37,18 +38,59 @@ type OpenRouterUsageAccounting = {
3738
}
3839
}
3940

41+
// Provider routing documentation: https://openrouter.ai/docs/features/provider-routing
42+
const providerOrder = {
43+
[models.openrouter_claude_sonnet_4]: [
44+
'Google',
45+
'Anthropic',
46+
'Amazon Bedrock',
47+
],
48+
[models.openrouter_claude_sonnet_4_5]: [
49+
'Google',
50+
'Anthropic',
51+
'Amazon Bedrock',
52+
],
53+
[models.openrouter_claude_opus_4]: ['Google', 'Anthropic'],
54+
}
55+
4056
function calculateUsedCredits(params: { costDollars: number }): number {
4157
const { costDollars } = params
4258

4359
return Math.round(costDollars * (1 + PROFIT_MARGIN) * 100)
4460
}
4561

62+
function getProviderOptions(params: {
63+
model: string
64+
runId: string
65+
clientSessionId: string
66+
}): { codebuff: JSONObject } {
67+
const { model, runId, clientSessionId } = params
68+
69+
// Set allow_fallbacks based on whether model is explicitly defined
70+
const isExplicitlyDefined = isExplicitlyDefinedModel(model)
71+
72+
return {
73+
// Could either be "codebuff" or "openaiCompatible"
74+
codebuff: {
75+
// All values here get appended to the request body
76+
codebuff_metadata: {
77+
run_id: runId,
78+
client_id: clientSessionId,
79+
},
80+
transforms: ['middle-out'],
81+
provider: {
82+
order: providerOrder[model as keyof typeof providerOrder],
83+
allow_fallbacks: !isExplicitlyDefined,
84+
},
85+
},
86+
}
87+
}
88+
4689
function getAiSdkModel(params: {
4790
apiKey: string
4891
model: string
49-
logger: Logger
5092
}): LanguageModelV2 {
51-
const { apiKey, model, logger } = params
93+
const { apiKey, model } = params
5294

5395
const openrouterUsage: OpenRouterUsageAccounting = {
5496
cost: null,
@@ -58,7 +100,7 @@ function getAiSdkModel(params: {
58100
}
59101

60102
const codebuffBackendModel = new OpenAICompatibleChatLanguageModel(model, {
61-
provider: 'codebuff.chat',
103+
provider: 'codebuff',
62104
url: ({ path: endpoint }) =>
63105
new URL(path.join('/api/v1', endpoint), WEBSITE_URL).toString(),
64106
headers: () => ({
@@ -132,14 +174,7 @@ export async function* promptAiSdkStream(
132174
prompt: undefined,
133175
model: aiSDKModel,
134176
messages: convertCbToModelMessages(params),
135-
providerOptions: {
136-
codebuff: {
137-
codebuff_metadata: {
138-
run_id: params.runId,
139-
client_id: params.clientSessionId,
140-
},
141-
},
142-
},
177+
providerOptions: getProviderOptions(params),
143178
})
144179

145180
let content = ''
@@ -285,14 +320,7 @@ export async function promptAiSdk(
285320
prompt: undefined,
286321
model: aiSDKModel,
287322
messages: convertCbToModelMessages(params),
288-
providerOptions: {
289-
codebuff: {
290-
codebuff_metadata: {
291-
run_id: params.runId,
292-
client_id: params.clientSessionId,
293-
},
294-
},
295-
},
323+
providerOptions: getProviderOptions(params),
296324
})
297325
const content = response.text
298326

@@ -343,14 +371,7 @@ export async function promptAiSdkStructured<T>(
343371
model: aiSDKModel,
344372
output: 'object',
345373
messages: convertCbToModelMessages(params),
346-
providerOptions: {
347-
codebuff: {
348-
codebuff_metadata: {
349-
run_id: params.runId,
350-
client_id: params.clientSessionId,
351-
},
352-
},
353-
},
374+
providerOptions: getProviderOptions(params),
354375
})
355376

356377
const content = response.object

0 commit comments

Comments
 (0)