fix(opencode): drop max_tokens for OpenAI reasoning models on Cloudflare AI Gateway (#22864)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
This commit is contained in:
@@ -61,5 +61,16 @@ export async function CloudflareAIGatewayAuthPlugin(_input: PluginInput): Promis
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
"chat.params": async (input, output) => {
|
||||||
|
if (input.model.providerID !== "cloudflare-ai-gateway") return
|
||||||
|
// The unified gateway routes through @ai-sdk/openai-compatible, which
|
||||||
|
// always emits max_tokens. OpenAI reasoning models (gpt-5.x, o-series)
|
||||||
|
// reject that field and require max_completion_tokens instead, and the
|
||||||
|
// compatible SDK has no way to rename it. Drop the cap so OpenAI falls
|
||||||
|
// back to the model's default output budget.
|
||||||
|
if (!input.model.api.id.toLowerCase().startsWith("openai/")) return
|
||||||
|
if (!input.model.capabilities.reasoning) return
|
||||||
|
output.maxOutputTokens = undefined
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
68
packages/opencode/test/plugin/cloudflare.test.ts
Normal file
68
packages/opencode/test/plugin/cloudflare.test.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { expect, test } from "bun:test"
|
||||||
|
import { CloudflareAIGatewayAuthPlugin } from "@/plugin/cloudflare"
|
||||||
|
|
||||||
|
const pluginInput = {
|
||||||
|
client: {} as never,
|
||||||
|
project: {} as never,
|
||||||
|
directory: "",
|
||||||
|
worktree: "",
|
||||||
|
experimental_workspace: {
|
||||||
|
register() {},
|
||||||
|
},
|
||||||
|
serverUrl: new URL("https://example.com"),
|
||||||
|
$: {} as never,
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeHookInput(overrides: { providerID?: string; apiId?: string; reasoning?: boolean }) {
|
||||||
|
return {
|
||||||
|
sessionID: "s",
|
||||||
|
agent: "a",
|
||||||
|
provider: {} as never,
|
||||||
|
message: {} as never,
|
||||||
|
model: {
|
||||||
|
providerID: overrides.providerID ?? "cloudflare-ai-gateway",
|
||||||
|
api: { id: overrides.apiId ?? "openai/gpt-5.2-codex", url: "", npm: "ai-gateway-provider" },
|
||||||
|
capabilities: {
|
||||||
|
reasoning: overrides.reasoning ?? true,
|
||||||
|
temperature: false,
|
||||||
|
attachment: true,
|
||||||
|
toolcall: true,
|
||||||
|
input: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||||
|
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||||
|
interleaved: false,
|
||||||
|
},
|
||||||
|
} as never,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeHookOutput() {
|
||||||
|
return { temperature: 0, topP: 1, topK: 0, maxOutputTokens: 32_000 as number | undefined, options: {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
test("omits maxOutputTokens for openai reasoning models on cloudflare-ai-gateway", async () => {
|
||||||
|
const hooks = await CloudflareAIGatewayAuthPlugin(pluginInput)
|
||||||
|
const out = makeHookOutput()
|
||||||
|
await hooks["chat.params"]!(makeHookInput({ apiId: "openai/gpt-5.2-codex", reasoning: true }), out)
|
||||||
|
expect(out.maxOutputTokens).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("keeps maxOutputTokens for openai non-reasoning models", async () => {
|
||||||
|
const hooks = await CloudflareAIGatewayAuthPlugin(pluginInput)
|
||||||
|
const out = makeHookOutput()
|
||||||
|
await hooks["chat.params"]!(makeHookInput({ apiId: "openai/gpt-4-turbo", reasoning: false }), out)
|
||||||
|
expect(out.maxOutputTokens).toBe(32_000)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("keeps maxOutputTokens for non-openai reasoning models on cloudflare-ai-gateway", async () => {
|
||||||
|
const hooks = await CloudflareAIGatewayAuthPlugin(pluginInput)
|
||||||
|
const out = makeHookOutput()
|
||||||
|
await hooks["chat.params"]!(makeHookInput({ apiId: "anthropic/claude-sonnet-4-5", reasoning: true }), out)
|
||||||
|
expect(out.maxOutputTokens).toBe(32_000)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("ignores non-cloudflare-ai-gateway providers", async () => {
|
||||||
|
const hooks = await CloudflareAIGatewayAuthPlugin(pluginInput)
|
||||||
|
const out = makeHookOutput()
|
||||||
|
await hooks["chat.params"]!(makeHookInput({ providerID: "openai", apiId: "gpt-5.2-codex", reasoning: true }), out)
|
||||||
|
expect(out.maxOutputTokens).toBe(32_000)
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user