From 3cc4b0d191d06d03c373da0c15d318b0a9d2112b Mon Sep 17 00:00:00 2001 From: kang-heewon Date: Fri, 18 Sep 2026 12:48:10 +0900 Subject: [PATCH 1/2] fix(reasoning): resolve 400 errors and capability mismatch for gpt-5.6-luna - Align policy capability check with provider stripping to correctly support reasoning effort on provider-prefixed models (e.g. openai/gpt-5.6-luna, github/gpt-5.6-luna) - Declare supportedThinkingEfforts in GPT_5_6_API_CAPABILITIES, opencode-zen, opencode-go, and github registries - Route gpt-5.6 family in opencode-zen to openai-responses format to prevent upstream Chat Completions 400 errors - Exclude ultra effort for Luna in ReasoningRoutingRules UI and clamp current effort - Add regression test coverage for Luna reasoning effort routing and capability gates --- config/quality/eslint-suppressions.json | 46 ----- .../config/providers/registry/github/index.ts | 6 + .../providers/registry/opencode/go/index.ts | 1 + .../providers/registry/opencode/zen/index.ts | 27 ++- open-sse/config/providers/shared.ts | 1 + src/lib/reasoningRouting/policy.ts | 2 +- .../components/ReasoningRoutingRules.tsx | 32 ++- tests/unit/luna-reasoning-effort-400.test.ts | 190 ++++++++++++++++++ 8 files changed, 246 insertions(+), 59 deletions(-) create mode 100644 tests/unit/luna-reasoning-effort-400.test.ts diff --git a/config/quality/eslint-suppressions.json b/config/quality/eslint-suppressions.json index d966760de860..79b183effed6 100644 --- a/config/quality/eslint-suppressions.json +++ b/config/quality/eslint-suppressions.json @@ -24,11 +24,6 @@ "count": 6 } }, - "open-sse/config/providers/registry/vertex/index.ts": { - "@typescript-eslint/no-unused-vars": { - "count": 1 - } - }, "open-sse/config/providers/shared.ts": { "@typescript-eslint/no-unused-vars": { "count": 1 @@ -192,14 +187,6 @@ "count": 2 } }, - "open-sse/executors/vertex.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 3 - }, - "@typescript-eslint/no-unused-vars": { - "count": 1 - } - }, "open-sse/handlers/chatCore/executorHelpers.ts": { "@typescript-eslint/no-unused-vars": { "count": 1 @@ -3804,11 +3791,6 @@ "count": 7 } }, - "tests/unit/executor-nlpcloud.test.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 2 - } - }, "tests/unit/executor-pollinations.test.ts": { "@typescript-eslint/no-explicit-any": { "count": 4 @@ -4492,11 +4474,6 @@ "count": 3 } }, - "tests/unit/qoder-unwrap-error-envelope.test.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 3 - } - }, "tests/unit/quota-cache-is-exhausted-per-window-5923.test.ts": { "@typescript-eslint/no-explicit-any": { "count": 6 @@ -5264,32 +5241,9 @@ "count": 1 } }, - "tests/unit/ui/use-improve-prompt.test.tsx": { - "react-hooks/immutability": { - "count": 1 - } - }, - "tests/unit/ui/use-presets.test.tsx": { - "react-hooks/immutability": { - "count": 1 - } - }, "tests/unit/ui/use-stream-metrics.test.tsx": { "@typescript-eslint/no-unused-vars": { "count": 1 - }, - "react-hooks/immutability": { - "count": 1 - } - }, - "tests/unit/ui/use-structured-output.test.tsx": { - "react-hooks/immutability": { - "count": 1 - } - }, - "tests/unit/ui/use-tools-builder.test.tsx": { - "react-hooks/immutability": { - "count": 1 } }, "tests/unit/ui/use-traffic-stream.test.tsx": { diff --git a/open-sse/config/providers/registry/github/index.ts b/open-sse/config/providers/registry/github/index.ts index d6189fd79110..7c6b99010406 100644 --- a/open-sse/config/providers/registry/github/index.ts +++ b/open-sse/config/providers/registry/github/index.ts @@ -144,18 +144,24 @@ export const githubProvider: RegistryEntry = { { id: "gpt-5.6-sol", name: "GPT-5.6 Sol", + supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], targetFormat: "openai-responses", maxOutputTokens: 128000, }, { id: "gpt-5.6-terra", name: "GPT-5.6 Terra", + supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], targetFormat: "openai-responses", maxOutputTokens: 128000, }, { id: "gpt-5.6-luna", name: "GPT-5.6 Luna", + supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], targetFormat: "openai-responses", maxOutputTokens: 128000, }, diff --git a/open-sse/config/providers/registry/opencode/go/index.ts b/open-sse/config/providers/registry/opencode/go/index.ts index dcc428037ff7..7ab44b1c849b 100644 --- a/open-sse/config/providers/registry/opencode/go/index.ts +++ b/open-sse/config/providers/registry/opencode/go/index.ts @@ -329,6 +329,7 @@ export const opencode_goProvider: RegistryEntry = { id: "gpt-5.6-luna", name: "GPT-5.6 Luna", supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], targetFormat: "openai-responses", maxOutputTokens: 128000, }, diff --git a/open-sse/config/providers/registry/opencode/zen/index.ts b/open-sse/config/providers/registry/opencode/zen/index.ts index 1bb2382aad1c..eb7f01df1c4e 100644 --- a/open-sse/config/providers/registry/opencode/zen/index.ts +++ b/open-sse/config/providers/registry/opencode/zen/index.ts @@ -30,9 +30,30 @@ export const opencode_zenProvider: RegistryEntry = { ...OPENCODE_ZEN_GO_SHARED_MODELS, // models[0] (big-pickle) is the dashboard default; SHARED spread kept after it. - { id: "gpt-5.6-sol", name: "GPT 5.6 Sol" }, - { id: "gpt-5.6-terra", name: "GPT 5.6 Terra" }, - { id: "gpt-5.6-luna", name: "GPT 5.6 Luna" }, + { + id: "gpt-5.6-sol", + name: "GPT 5.6 Sol", + supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], + targetFormat: "openai-responses", + maxOutputTokens: 128000, + }, + { + id: "gpt-5.6-terra", + name: "GPT 5.6 Terra", + supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], + targetFormat: "openai-responses", + maxOutputTokens: 128000, + }, + { + id: "gpt-5.6-luna", + name: "GPT 5.6 Luna", + supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], + targetFormat: "openai-responses", + maxOutputTokens: 128000, + }, { id: "gpt-5.4", name: "GPT 5.4" }, { id: "gpt-5.4-mini", name: "GPT 5.4 Mini" }, { id: "gpt-5.4-nano", name: "GPT 5.4 Nano" }, diff --git a/open-sse/config/providers/shared.ts b/open-sse/config/providers/shared.ts index 8007b12627d2..2d6c96cbede1 100644 --- a/open-sse/config/providers/shared.ts +++ b/open-sse/config/providers/shared.ts @@ -311,6 +311,7 @@ export const GPT_5_6_API_CAPABILITIES = { supportsReasoning: true, supportsVision: true, supportsXHighEffort: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], contextLength: 1050000, maxInputTokens: 922000, maxOutputTokens: 128000, diff --git a/src/lib/reasoningRouting/policy.ts b/src/lib/reasoningRouting/policy.ts index 8a42526de45b..0e86a6bdbc1e 100644 --- a/src/lib/reasoningRouting/policy.ts +++ b/src/lib/reasoningRouting/policy.ts @@ -312,7 +312,7 @@ function capabilityFor( if (capabilities.reasoningEffortsOverride && Array.isArray(declaredEfforts)) { return "unsupported" as const; } - const normalized = model.toLowerCase().replace(/^(?:codex|cx)\//, ""); + const normalized = modelIdForRegistry.toLowerCase(); const supported = targetEffort === "ultra" ? /^gpt-5\.6-(?:sol|terra)(?:-|$)/.test(normalized) diff --git a/src/shared/components/ReasoningRoutingRules.tsx b/src/shared/components/ReasoningRoutingRules.tsx index 8d47d7635838..221ee550212b 100644 --- a/src/shared/components/ReasoningRoutingRules.tsx +++ b/src/shared/components/ReasoningRoutingRules.tsx @@ -95,7 +95,7 @@ function supportsExtendedCodexEffort(model: string, effort: "max" | "ultra"): bo const normalized = model .trim() .toLowerCase() - .replace(/^(?:codex|cx)\//, ""); + .replace(/^[^/]+\//, ""); return effort === "ultra" ? /^gpt-5\.6-(?:sol|terra)(?:-|$)/.test(normalized) : /^gpt-5\.6-(?:sol|terra|luna)(?:-|$)/.test(normalized); @@ -212,31 +212,45 @@ export default function ReasoningRoutingRules({ const targetModelForCapability = form.targetKind === "model" ? form.targetModel : form.modelPattern; + const isLunaTarget = useMemo(() => { + const normalized = targetModelForCapability + .trim() + .toLowerCase() + .replace(/^[^/]+\//, ""); + return /^gpt-5\.6-luna(?:-|$)/.test(normalized); + }, [targetModelForCapability]); + + const currentTargetEffort = + isLunaTarget && form.targetEffort === "ultra" ? "max" : form.targetEffort; + const effortOptions = useMemo(() => { const values = [...STANDARD_EFFORTS]; for (const effort of EXTENDED_EFFORTS) { + if (effort === "ultra" && isLunaTarget) { + continue; + } if ( supportsExtendedCodexEffort(targetModelForCapability, effort as "max" | "ultra") || - form.targetEffort === effort + currentTargetEffort === effort ) { values.push(effort); } } return values.map((value) => ({ value, label: value })); - }, [form.targetEffort, targetModelForCapability]); + }, [currentTargetEffort, isLunaTarget, targetModelForCapability]); const capabilityWarning = useMemo(() => { if (form.effortMode === "inherit") return ""; - if (!EXTENDED_EFFORTS.includes(form.targetEffort)) return ""; + if (!EXTENDED_EFFORTS.includes(currentTargetEffort)) return ""; if (form.targetKind === "combo") return t("extendedComboWarning"); if (!targetModelForCapability.trim()) return t("extendedUnknownWarning"); return supportsExtendedCodexEffort( targetModelForCapability, - form.targetEffort as "max" | "ultra" + currentTargetEffort as "max" | "ultra" ) ? "" : t("extendedUnsupportedWarning"); - }, [form.effortMode, form.targetEffort, form.targetKind, t, targetModelForCapability]); + }, [currentTargetEffort, form.effortMode, form.targetKind, t, targetModelForCapability]); const dirty = editorOpen && JSON.stringify(form) !== baseline; const confirmDiscard = (action: () => void) => { @@ -322,7 +336,7 @@ export default function ReasoningRoutingRules({ .split(",") .map((tag) => tag.trim()) .filter(Boolean), - targetEffort: form.effortMode === "inherit" ? null : form.targetEffort, + targetEffort: form.effortMode === "inherit" ? null : currentTargetEffort, targetKind, targetModel: targetKind === "model" ? form.targetModel || null : null, targetComboId: targetKind === "combo" ? form.targetComboId || null : null, @@ -835,7 +849,7 @@ export default function ReasoningRoutingRules({