mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-21 02:06:00 +00:00
fix(provider): force openai reasoning variants (#34702)
This commit is contained in:
@@ -1263,6 +1263,16 @@ const SLUG_OVERRIDES: Record<string, string> = {
|
||||
}
|
||||
|
||||
export function providerOptions(model: Provider.Model, options: { [x: string]: any }) {
|
||||
const usesOpenAIReasoningGate =
|
||||
model.api.npm === "@ai-sdk/openai" ||
|
||||
model.api.npm === "@ai-sdk/azure" ||
|
||||
model.api.npm === "@ai-sdk/amazon-bedrock/mantle"
|
||||
const normalized =
|
||||
usesOpenAIReasoningGate &&
|
||||
(model.capabilities.reasoning || options.reasoningEffort !== undefined || options.reasoningSummary !== undefined)
|
||||
? { ...options, forceReasoning: true }
|
||||
: options
|
||||
|
||||
if (model.api.npm === "@ai-sdk/gateway") {
|
||||
// Gateway providerOptions are split across two namespaces:
|
||||
// - `gateway`: gateway-native routing/caching controls (order, only, byok, etc.)
|
||||
@@ -1272,8 +1282,8 @@ export function providerOptions(model: Provider.Model, options: { [x: string]: a
|
||||
const i = model.api.id.indexOf("/")
|
||||
const rawSlug = i > 0 ? model.api.id.slice(0, i) : undefined
|
||||
const slug = rawSlug ? (SLUG_OVERRIDES[rawSlug] ?? rawSlug) : undefined
|
||||
const gateway = options.gateway
|
||||
const rest = Object.fromEntries(Object.entries(options).filter(([k]) => k !== "gateway"))
|
||||
const gateway = normalized.gateway
|
||||
const rest = Object.fromEntries(Object.entries(normalized).filter(([k]) => k !== "gateway"))
|
||||
const has = Object.keys(rest).length > 0
|
||||
|
||||
const result: Record<string, any> = {}
|
||||
@@ -1307,9 +1317,9 @@ export function providerOptions(model: Provider.Model, options: { [x: string]: a
|
||||
// providerOptions["openai"], but OpenAIResponsesLanguageModel checks
|
||||
// "azure" first. Pass both so model options work on either code path.
|
||||
if (model.api.npm === "@ai-sdk/azure") {
|
||||
return { openai: options, azure: options }
|
||||
return { openai: normalized, azure: normalized }
|
||||
}
|
||||
return { [key]: options }
|
||||
return { [key]: normalized }
|
||||
}
|
||||
|
||||
export function maxOutputTokens(model: Provider.Model, outputTokenMax = OUTPUT_TOKEN_MAX): number {
|
||||
|
||||
@@ -613,6 +613,84 @@ describe("ProviderTransform.providerOptions", () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("forces reasoning for custom OpenAI package models with explicit effort", () => {
|
||||
const model = createModel({
|
||||
providerID: "meta",
|
||||
api: {
|
||||
id: "muse-spark",
|
||||
url: "https://api.ai.meta.com/v1",
|
||||
npm: "@ai-sdk/openai",
|
||||
},
|
||||
})
|
||||
|
||||
expect(ProviderTransform.providerOptions(model, { reasoningEffort: "xhigh", reasoningSummary: "auto" })).toEqual({
|
||||
openai: { forceReasoning: true, reasoningEffort: "xhigh", reasoningSummary: "auto" },
|
||||
})
|
||||
})
|
||||
|
||||
test("forces reasoning for OpenAI package models marked reasoning-capable", () => {
|
||||
expect(ProviderTransform.providerOptions(createModel(), { store: false })).toEqual({
|
||||
openai: { forceReasoning: true, store: false },
|
||||
})
|
||||
})
|
||||
|
||||
test("forces reasoning for explicit effort even when model is not marked reasoning-capable", () => {
|
||||
const model = createModel({
|
||||
capabilities: {
|
||||
temperature: true,
|
||||
reasoning: false,
|
||||
attachment: true,
|
||||
toolcall: true,
|
||||
input: { text: true, audio: false, image: true, video: false, pdf: false },
|
||||
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||
interleaved: false,
|
||||
},
|
||||
})
|
||||
|
||||
expect(ProviderTransform.providerOptions(model, { reasoningEffort: "xhigh" })).toEqual({
|
||||
openai: { forceReasoning: true, reasoningEffort: "xhigh" },
|
||||
})
|
||||
})
|
||||
|
||||
test("forces reasoning for Azure OpenAI models with explicit effort", () => {
|
||||
const model = createModel({
|
||||
providerID: "azure",
|
||||
api: {
|
||||
id: "custom-gpt-5-deployment",
|
||||
url: "https://azure.openai.example.com/openai/v1",
|
||||
npm: "@ai-sdk/azure",
|
||||
},
|
||||
})
|
||||
|
||||
expect(ProviderTransform.providerOptions(model, { reasoningEffort: "xhigh" })).toEqual({
|
||||
openai: { forceReasoning: true, reasoningEffort: "xhigh" },
|
||||
azure: { forceReasoning: true, reasoningEffort: "xhigh" },
|
||||
})
|
||||
})
|
||||
|
||||
test("forces reasoning for Bedrock Mantle OpenAI models with explicit effort", () => {
|
||||
const model = createModel({
|
||||
providerID: "amazon-bedrock",
|
||||
api: {
|
||||
id: "openai.gpt-5-custom",
|
||||
url: "https://bedrock-mantle.us-east-2.api.aws/openai/v1",
|
||||
npm: "@ai-sdk/amazon-bedrock/mantle",
|
||||
},
|
||||
})
|
||||
|
||||
expect(ProviderTransform.providerOptions(model, { reasoningEffort: "xhigh" })).toEqual({
|
||||
openai: { forceReasoning: true, reasoningEffort: "xhigh" },
|
||||
})
|
||||
})
|
||||
|
||||
test("overrides forceReasoning false when reasoning should be forced", () => {
|
||||
expect(
|
||||
ProviderTransform.providerOptions(createModel(), { forceReasoning: false, reasoningEffort: "xhigh" }),
|
||||
).toEqual({
|
||||
openai: { forceReasoning: true, reasoningEffort: "xhigh" },
|
||||
})
|
||||
})
|
||||
|
||||
test("uses gateway model provider slug for gateway models", () => {
|
||||
const model = createModel({
|
||||
providerID: "vercel",
|
||||
@@ -707,7 +785,7 @@ describe("ProviderTransform.providerOptions", () => {
|
||||
})
|
||||
|
||||
expect(ProviderTransform.providerOptions(model, { reasoningEffort: "medium" })).toEqual({
|
||||
openai: { reasoningEffort: "medium" },
|
||||
openai: { forceReasoning: true, reasoningEffort: "medium" },
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user