Skip to content

Commit 71a7e8e

Browse files
authored
fix: max output tokens when using large thinking budget (anomalyco#2976)
1 parent 5f7ae64 commit 71a7e8e

2 files changed

Lines changed: 26 additions & 10 deletions

File tree

packages/opencode/src/provider/transform.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,18 +105,29 @@ export namespace ProviderTransform {
105105
return result
106106
}
107107

108-
export function maxOutputTokens(providerID: string, outputLimit: number, options: Record<string, any>): number {
108+
export function maxOutputTokens(
109+
providerID: string,
110+
options: Record<string, any>,
111+
modelLimit: number,
112+
globalLimit: number,
113+
): number {
114+
const modelCap = modelLimit || globalLimit
115+
const standardLimit = Math.min(modelCap, globalLimit)
116+
109117
if (providerID === "anthropic") {
110-
const thinking = options["thinking"]
111-
if (typeof thinking === "object" && thinking !== null) {
112-
const type = thinking["type"]
113-
const budgetTokens = thinking["budgetTokens"]
114-
if (type === "enabled" && typeof budgetTokens === "number" && budgetTokens > 0) {
115-
return outputLimit - budgetTokens
118+
const thinking = options?.["thinking"]
119+
const budgetTokens = typeof thinking?.["budgetTokens"] === "number" ? thinking["budgetTokens"] : 0
120+
const enabled = thinking?.["type"] === "enabled"
121+
if (enabled && budgetTokens > 0) {
122+
// Return text tokens so that text + thinking <= model cap, preferring 32k text when possible.
123+
if (budgetTokens + standardLimit <= modelCap) {
124+
return standardLimit
116125
}
126+
return modelCap - budgetTokens
117127
}
118128
}
119-
return outputLimit
129+
130+
return standardLimit
120131
}
121132

122133
export function schema(_providerID: string, _modelID: string, schema: JSONSchema.BaseSchema) {

packages/opencode/src/session/prompt.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ export namespace SessionPrompt {
159159
agent,
160160
model: input.model,
161161
}).then((x) => Provider.getModel(x.providerID, x.modelID))
162-
const outputLimit = Math.min(model.info.limit.output, OUTPUT_TOKEN_MAX) || OUTPUT_TOKEN_MAX
162+
163163
using abort = lock(input.sessionID)
164164

165165
const system = await resolveSystemPrompt({
@@ -266,7 +266,12 @@ export namespace SessionPrompt {
266266
: undefined,
267267
maxRetries: 10,
268268
activeTools: Object.keys(tools).filter((x) => x !== "invalid"),
269-
maxOutputTokens: ProviderTransform.maxOutputTokens(model.providerID, outputLimit, params.options),
269+
maxOutputTokens: ProviderTransform.maxOutputTokens(
270+
model.providerID,
271+
params.options,
272+
model.info.limit.output,
273+
OUTPUT_TOKEN_MAX,
274+
),
270275
abortSignal: abort.signal,
271276
providerOptions: {
272277
[model.npm === "@ai-sdk/openai" ? "openai" : model.providerID]: params.options,

0 commit comments

Comments
 (0)