fix(provider): align GPT-5 reasoning variants (#26268)
This commit is contained in:
@@ -500,6 +500,13 @@ export function topK(model: Provider.Model) {
|
||||
|
||||
const WIDELY_SUPPORTED_EFFORTS = ["low", "medium", "high"]
|
||||
const OPENAI_EFFORTS = ["none", "minimal", ...WIDELY_SUPPORTED_EFFORTS, "xhigh"]
|
||||
const OPENAI_GPT5_1_EFFORTS = ["none", ...WIDELY_SUPPORTED_EFFORTS]
|
||||
const OPENAI_GPT5_2_PLUS_EFFORTS = [...OPENAI_GPT5_1_EFFORTS, "xhigh"]
|
||||
const OPENAI_GPT5_PRO_EFFORTS = ["high"]
|
||||
const OPENAI_GPT5_PRO_2_PLUS_EFFORTS = ["medium", "high", "xhigh"]
|
||||
const OPENAI_GPT5_CHAT_EFFORTS = ["medium"]
|
||||
const OPENAI_GPT5_CODEX_XHIGH_EFFORTS = [...WIDELY_SUPPORTED_EFFORTS, "xhigh"]
|
||||
const OPENAI_GPT5_CODEX_3_PLUS_EFFORTS = ["none", ...OPENAI_GPT5_CODEX_XHIGH_EFFORTS]
|
||||
|
||||
// OpenAI rolled out the `none` reasoning_effort tier on this date (Responses API).
|
||||
// Models released before it 400 on `reasoning_effort: "none"`, so we only expose
|
||||
@@ -513,17 +520,49 @@ const OPENAI_XHIGH_EFFORT_RELEASE_DATE = "2025-12-04"
|
||||
// "gpt-5", "gpt-5-nano", "gpt-5.4", "openai/gpt-5.4-codex".
|
||||
// Anchored to start-of-string or "/" so it doesn't false-match "gpt-50" or "gpt-5o".
|
||||
const GPT5_FAMILY_RE = /(?:^|\/)gpt-5(?:[.-]|$)/
|
||||
const GPT5_VERSION_RE = /(?:^|\/)gpt-5[.-](\d+)(?:[.-]|$)/
|
||||
const GPT5_PRO_RE = /(?:^|\/)gpt-5[.-]?pro(?:[.-]|$)/
|
||||
const GPT5_VERSIONED_PRO_RE = /(?:^|\/)gpt-5[.-]\d+[.-]pro(?:[.-]|$)/
|
||||
|
||||
function gpt5Version(apiId: string) {
|
||||
return Number(GPT5_VERSION_RE.exec(apiId)?.[1]) || undefined
|
||||
}
|
||||
|
||||
function versionedGpt5ReasoningEfforts(apiId: string) {
|
||||
if (GPT5_VERSIONED_PRO_RE.test(apiId)) return OPENAI_GPT5_PRO_2_PLUS_EFFORTS
|
||||
const version = gpt5Version(apiId)
|
||||
if (version === undefined) return undefined
|
||||
if (version === 1) return OPENAI_GPT5_1_EFFORTS
|
||||
return OPENAI_GPT5_2_PLUS_EFFORTS
|
||||
}
|
||||
|
||||
function gpt5CodexReasoningEfforts(apiId: string) {
|
||||
if (!GPT5_FAMILY_RE.test(apiId) || !apiId.includes("codex")) return undefined
|
||||
const version = gpt5Version(apiId)
|
||||
if (version !== undefined && version >= 3) return OPENAI_GPT5_CODEX_3_PLUS_EFFORTS
|
||||
if (apiId.includes("codex-max") || (version !== undefined && version >= 2)) return OPENAI_GPT5_CODEX_XHIGH_EFFORTS
|
||||
return WIDELY_SUPPORTED_EFFORTS
|
||||
}
|
||||
|
||||
function gpt5ChatReasoningEfforts(apiId: string) {
|
||||
if (!GPT5_FAMILY_RE.test(apiId) || !apiId.includes("-chat")) return undefined
|
||||
return gpt5Version(apiId) === undefined ? [] : OPENAI_GPT5_CHAT_EFFORTS
|
||||
}
|
||||
|
||||
// Computes the reasoning_effort tiers an OpenAI (or OpenAI-compatible upstream
|
||||
// routed through it, e.g. cf-ai-gateway) model exposes. Returns null for models
|
||||
// with no tunable effort knob (gpt-5-pro). Effort order: weakest to strongest.
|
||||
function openaiReasoningEfforts(apiId: string, releaseDate: string): string[] | null {
|
||||
// routed through it, e.g. cf-ai-gateway) model exposes. Effort order: weakest
|
||||
// to strongest.
|
||||
function openaiReasoningEfforts(apiId: string, releaseDate: string) {
|
||||
const id = apiId.toLowerCase()
|
||||
if (id === "gpt-5-pro" || id === "openai/gpt-5-pro") return null
|
||||
if (id.includes("codex")) {
|
||||
if (id.includes("5.2") || id.includes("5.3")) return [...WIDELY_SUPPORTED_EFFORTS, "xhigh"]
|
||||
return [...WIDELY_SUPPORTED_EFFORTS]
|
||||
}
|
||||
const chatEfforts = gpt5ChatReasoningEfforts(id)
|
||||
if (chatEfforts) return chatEfforts
|
||||
if (GPT5_PRO_RE.test(id)) return OPENAI_GPT5_PRO_EFFORTS
|
||||
const codexEfforts = gpt5CodexReasoningEfforts(id)
|
||||
if (codexEfforts) return codexEfforts
|
||||
const versionedEfforts = versionedGpt5ReasoningEfforts(id)
|
||||
// GPT-5.1 replaced GPT-5's `minimal` effort with `none`; GPT-5.2+
|
||||
// additionally accepts `xhigh`. Model pages list the supported subset.
|
||||
if (versionedEfforts) return versionedEfforts
|
||||
const efforts = [...WIDELY_SUPPORTED_EFFORTS]
|
||||
if (GPT5_FAMILY_RE.test(id)) efforts.unshift("minimal")
|
||||
if (releaseDate >= OPENAI_NONE_EFFORT_RELEASE_DATE) efforts.unshift("none")
|
||||
@@ -531,6 +570,14 @@ function openaiReasoningEfforts(apiId: string, releaseDate: string): string[] |
|
||||
return efforts
|
||||
}
|
||||
|
||||
function openaiCompatibleReasoningEfforts(id: string) {
|
||||
const apiId = id.toLowerCase()
|
||||
const chatEfforts = gpt5ChatReasoningEfforts(apiId)
|
||||
if (chatEfforts) return chatEfforts
|
||||
if (GPT5_PRO_RE.test(apiId)) return OPENAI_GPT5_PRO_EFFORTS
|
||||
return gpt5CodexReasoningEfforts(apiId) ?? versionedGpt5ReasoningEfforts(apiId) ?? OPENAI_EFFORTS
|
||||
}
|
||||
|
||||
function anthropicAdaptiveEfforts(apiId: string): string[] | null {
|
||||
if (["opus-4-7", "opus-4.7"].some((v) => apiId.includes(v))) {
|
||||
return ["low", "medium", "high", "xhigh", "max"]
|
||||
@@ -577,8 +624,13 @@ export function variants(model: Provider.Model): Record<string, Record<string, a
|
||||
|
||||
switch (model.api.npm) {
|
||||
case "@openrouter/ai-sdk-provider":
|
||||
if (!model.id.includes("gpt") && !model.id.includes("gemini-3") && !model.id.includes("claude")) return {}
|
||||
return Object.fromEntries(OPENAI_EFFORTS.map((effort) => [effort, { reasoning: { effort } }]))
|
||||
if (!id.includes("gpt") && !id.includes("gemini-3") && !id.includes("claude")) return {}
|
||||
return Object.fromEntries(
|
||||
(id.includes("gpt") ? openaiCompatibleReasoningEfforts(id) : OPENAI_EFFORTS).map((effort) => [
|
||||
effort,
|
||||
{ reasoning: { effort } },
|
||||
]),
|
||||
)
|
||||
|
||||
case "ai-gateway-provider": {
|
||||
// Cloudflare AI Gateway routes every upstream through its OpenAI-compatible
|
||||
@@ -589,7 +641,6 @@ export function variants(model: Provider.Model): Record<string, Record<string, a
|
||||
// models that support it.
|
||||
if (model.api.id.startsWith("openai/")) {
|
||||
const efforts = openaiReasoningEfforts(model.api.id, model.release_date)
|
||||
if (!efforts) return {}
|
||||
return Object.fromEntries(efforts.map((effort) => [effort, { reasoningEffort: effort }]))
|
||||
}
|
||||
return Object.fromEntries(WIDELY_SUPPORTED_EFFORTS.map((effort) => [effort, { reasoningEffort: effort }]))
|
||||
@@ -652,7 +703,9 @@ export function variants(model: Provider.Model): Record<string, Record<string, a
|
||||
]),
|
||||
)
|
||||
}
|
||||
return Object.fromEntries(OPENAI_EFFORTS.map((effort) => [effort, { reasoningEffort: effort }]))
|
||||
return Object.fromEntries(
|
||||
openaiCompatibleReasoningEfforts(model.api.id).map((effort) => [effort, { reasoningEffort: effort }]),
|
||||
)
|
||||
|
||||
case "@ai-sdk/github-copilot":
|
||||
if (model.id.includes("gemini")) {
|
||||
@@ -700,12 +753,11 @@ export function variants(model: Provider.Model): Record<string, Record<string, a
|
||||
case "@ai-sdk/azure":
|
||||
// https://v5.ai-sdk.dev/providers/ai-sdk-providers/azure
|
||||
if (id === "o1-mini") return {}
|
||||
const azureEfforts = ["low", "medium", "high"]
|
||||
if (id.includes("gpt-5-") || id === "gpt-5") {
|
||||
azureEfforts.unshift("minimal")
|
||||
}
|
||||
return Object.fromEntries(
|
||||
azureEfforts.map((effort) => [
|
||||
(GPT5_FAMILY_RE.test(id) && gpt5Version(id) === undefined
|
||||
? ["minimal", ...WIDELY_SUPPORTED_EFFORTS]
|
||||
: WIDELY_SUPPORTED_EFFORTS
|
||||
).map((effort) => [
|
||||
effort,
|
||||
{
|
||||
reasoningEffort: effort,
|
||||
@@ -717,7 +769,6 @@ export function variants(model: Provider.Model): Record<string, Record<string, a
|
||||
case "@ai-sdk/openai": {
|
||||
// https://v5.ai-sdk.dev/providers/ai-sdk-providers/openai
|
||||
const efforts = openaiReasoningEfforts(model.api.id, model.release_date)
|
||||
if (!efforts) return {}
|
||||
return Object.fromEntries(
|
||||
efforts.map((effort) => [
|
||||
effort,
|
||||
@@ -1102,6 +1153,11 @@ export function smallOptions(model: Provider.Model) {
|
||||
model.api.npm === "@ai-sdk/github-copilot"
|
||||
) {
|
||||
if (model.api.id.includes("gpt-5")) {
|
||||
if (model.api.id.includes("-chat")) {
|
||||
if (gpt5Version(model.api.id) === undefined) return { store: false }
|
||||
return { store: false, reasoningEffort: "medium" }
|
||||
}
|
||||
if (model.api.id.includes("search-api")) return { store: false }
|
||||
if (model.api.id.includes("5.") || model.api.id.includes("5-mini")) {
|
||||
return { store: false, reasoningEffort: "low" }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user