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 }) {
|
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") {
|
if (model.api.npm === "@ai-sdk/gateway") {
|
||||||
// Gateway providerOptions are split across two namespaces:
|
// Gateway providerOptions are split across two namespaces:
|
||||||
// - `gateway`: gateway-native routing/caching controls (order, only, byok, etc.)
|
// - `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 i = model.api.id.indexOf("/")
|
||||||
const rawSlug = i > 0 ? model.api.id.slice(0, i) : undefined
|
const rawSlug = i > 0 ? model.api.id.slice(0, i) : undefined
|
||||||
const slug = rawSlug ? (SLUG_OVERRIDES[rawSlug] ?? rawSlug) : undefined
|
const slug = rawSlug ? (SLUG_OVERRIDES[rawSlug] ?? rawSlug) : undefined
|
||||||
const gateway = options.gateway
|
const gateway = normalized.gateway
|
||||||
const rest = Object.fromEntries(Object.entries(options).filter(([k]) => k !== "gateway"))
|
const rest = Object.fromEntries(Object.entries(normalized).filter(([k]) => k !== "gateway"))
|
||||||
const has = Object.keys(rest).length > 0
|
const has = Object.keys(rest).length > 0
|
||||||
|
|
||||||
const result: Record<string, any> = {}
|
const result: Record<string, any> = {}
|
||||||
@@ -1307,9 +1317,9 @@ export function providerOptions(model: Provider.Model, options: { [x: string]: a
|
|||||||
// providerOptions["openai"], but OpenAIResponsesLanguageModel checks
|
// providerOptions["openai"], but OpenAIResponsesLanguageModel checks
|
||||||
// "azure" first. Pass both so model options work on either code path.
|
// "azure" first. Pass both so model options work on either code path.
|
||||||
if (model.api.npm === "@ai-sdk/azure") {
|
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 {
|
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", () => {
|
test("uses gateway model provider slug for gateway models", () => {
|
||||||
const model = createModel({
|
const model = createModel({
|
||||||
providerID: "vercel",
|
providerID: "vercel",
|
||||||
@@ -707,7 +785,7 @@ describe("ProviderTransform.providerOptions", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
expect(ProviderTransform.providerOptions(model, { reasoningEffort: "medium" })).toEqual({
|
expect(ProviderTransform.providerOptions(model, { reasoningEffort: "medium" })).toEqual({
|
||||||
openai: { reasoningEffort: "medium" },
|
openai: { forceReasoning: true, reasoningEffort: "medium" },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user