Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/transforms.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,73 @@ describe("transforms", () => {
)
})

it("transformBody caps budget_tokens when equal to max_tokens", () => {
const input = JSON.stringify({
model: "claude-opus-4-6",
max_tokens: 128000,
thinking: { type: "enabled", budget_tokens: 128000 },
messages: [{ role: "user", content: "test" }],
})

const output = transformBody(input)
const parsed = JSON.parse(output as string) as {
max_tokens: number
thinking: { type: string; budget_tokens: number }
}

assert.equal(parsed.max_tokens, 128000, "max_tokens should be unchanged")
assert.equal(
parsed.thinking.budget_tokens,
102400,
"budget_tokens should be capped to 80% of max_tokens",
)
assert.ok(
parsed.max_tokens > parsed.thinking.budget_tokens,
"max_tokens must be greater than budget_tokens",
)
})

it("transformBody caps budget_tokens when greater than max_tokens", () => {
const input = JSON.stringify({
model: "claude-opus-4-6",
max_tokens: 32000,
thinking: { type: "enabled", budget_tokens: 50000 },
messages: [{ role: "user", content: "test" }],
})

const output = transformBody(input)
const parsed = JSON.parse(output as string) as {
max_tokens: number
thinking: { type: string; budget_tokens: number }
}

assert.equal(parsed.max_tokens, 32000)
assert.equal(parsed.thinking.budget_tokens, 25600)
assert.ok(parsed.max_tokens > parsed.thinking.budget_tokens)
})

it("transformBody does not modify budget_tokens when already less than max_tokens", () => {
const input = JSON.stringify({
model: "claude-opus-4-6",
max_tokens: 128000,
thinking: { type: "enabled", budget_tokens: 100000 },
messages: [{ role: "user", content: "test" }],
})

const output = transformBody(input)
const parsed = JSON.parse(output as string) as {
max_tokens: number
thinking: { type: string; budget_tokens: number }
}

assert.equal(parsed.max_tokens, 128000)
assert.equal(
parsed.thinking.budget_tokens,
100000,
"budget_tokens should be unchanged when already valid",
)
})

it("transformBody preserves effort for non-haiku models", () => {
const input = JSON.stringify({
model: "claude-opus-4-6",
Expand Down
14 changes: 14 additions & 0 deletions src/transforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ export function transformBody(
try {
const parsed = JSON.parse(body) as {
model?: string
// eslint-disable-next-line @typescript-eslint/naming-convention
max_tokens?: number
system?: SystemEntry[]
thinking?: Record<string, unknown>
// eslint-disable-next-line @typescript-eslint/naming-convention
Expand Down Expand Up @@ -173,6 +175,18 @@ export function transformBody(
}
}

// Ensure max_tokens > thinking.budget_tokens (API requirement).
// OpenCode may set budget_tokens >= max_tokens for custom models that
// aren't in its built-in registry, causing a 400 error from the API.
if (
parsed.thinking &&
typeof parsed.thinking.budget_tokens === "number" &&
typeof parsed.max_tokens === "number" &&
parsed.max_tokens <= parsed.thinking.budget_tokens
) {
parsed.thinking.budget_tokens = Math.floor(parsed.max_tokens * 0.8)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Potential sub-1024 budget_tokens after cap

The Anthropic API requires budget_tokens >= 1024 for extended thinking to be valid, in addition to the max_tokens > budget_tokens constraint. If max_tokens is between 1 and 1279, Math.floor(max_tokens * 0.8) yields a value below 1024, so the request would still get a 400 error — just for a different reason. Adding a lower bound guard (e.g., Math.max(1024, Math.floor(max_tokens * 0.8))) would make the invariant complete.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/transforms.ts
Line: 187

Comment:
**Potential sub-1024 `budget_tokens` after cap**

The Anthropic API requires `budget_tokens >= 1024` for extended thinking to be valid, in addition to the `max_tokens > budget_tokens` constraint. If `max_tokens` is between 1 and 1279, `Math.floor(max_tokens * 0.8)` yields a value below 1024, so the request would still get a 400 error — just for a different reason. Adding a lower bound guard (e.g., `Math.max(1024, Math.floor(max_tokens * 0.8))`) would make the invariant complete.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Cursor Fix in Claude Code Fix in Codex

}

if (Array.isArray(parsed.tools)) {
parsed.tools = parsed.tools.map((tool) => ({
...tool,
Expand Down
Loading