Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions open-sse/config/providers/registry/github/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,18 +163,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,
},
Expand Down
1 change: 1 addition & 0 deletions open-sse/config/providers/registry/opencode/go/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,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,
},
Expand Down
27 changes: 24 additions & 3 deletions open-sse/config/providers/registry/opencode/zen/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,30 @@ export const opencode_zenProvider: RegistryEntry = {
// opencode.ai/zen/v1 with the same key on both endpoints). #12196 made the
// same declaration for gpt-5.6-luna on opencode-go; the zen entries here
// never got it.
{ id: "gpt-5.6-sol", name: "GPT 5.6 Sol", targetFormat: "openai-responses" },
{ id: "gpt-5.6-terra", name: "GPT 5.6 Terra", targetFormat: "openai-responses" },
{ id: "gpt-5.6-luna", name: "GPT 5.6 Luna", targetFormat: "openai-responses" },
{
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" },
Expand Down
1 change: 1 addition & 0 deletions open-sse/config/providers/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,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,
Expand Down
6 changes: 5 additions & 1 deletion src/lib/reasoningRouting/policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,11 @@ function capabilityFor(
if (capabilities.reasoningEffortsOverride && Array.isArray(declaredEfforts)) {
return "unsupported" as const;
}
if (codexModelFamilySupportsExtendedEffort(model, targetEffort)) return "supported" as const;
// Strip the provider namespace (`openai/`, `github/`, `opencode-zen/`,
// `codex/`…) so every provider serving the family resolves the same way.
if (codexModelFamilySupportsExtendedEffort(modelIdForRegistry, targetEffort)) {
return "supported" as const;
}
if (capabilities.supportsThinking === null) return "unknown" as const;
return "unsupported" as const;
}
Expand Down
44 changes: 28 additions & 16 deletions src/shared/components/ReasoningRoutingRules.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,26 +205,39 @@ 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]);

// gpt-5.6-luna accepts `max` but not `ultra`: a saved `ultra` is coerced to
// `max` so the editor never re-offers (or re-saves) a tier the upstream 400s.
const currentTargetEffort =
isLunaTarget && form.targetEffort === "ultra" ? "max" : form.targetEffort;

const effortOptions = useMemo(
() =>
getReasoningRoutingTargetEffortOptions(targetModelForCapability, form.targetEffort).map(
getReasoningRoutingTargetEffortOptions(targetModelForCapability, currentTargetEffort).map(
(value) => ({ value, label: value })
),
[form.targetEffort, targetModelForCapability]
[currentTargetEffort, targetModelForCapability]
);

const capabilityWarning = useMemo(() => {
if (form.effortMode === "inherit") return "";
if (!(EXTENDED_REASONING_EFFORTS as readonly string[]).includes(form.targetEffort)) return "";
if (!(EXTENDED_REASONING_EFFORTS as readonly string[]).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) => {
Expand Down Expand Up @@ -310,7 +323,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,
Expand Down Expand Up @@ -756,13 +769,12 @@ export default function ReasoningRoutingRules({
options={[
{ value: "any", label: t("any") },
{ value: "missing", label: t("missing") },
...[
...STANDARD_REASONING_EFFORTS,
...EXTENDED_REASONING_EFFORTS,
].map((value) => ({
value,
label: value,
})),
...[...STANDARD_REASONING_EFFORTS, ...EXTENDED_REASONING_EFFORTS].map(
(value) => ({
value,
label: value,
})
),
]}
/>
</div>
Expand Down Expand Up @@ -826,7 +838,7 @@ export default function ReasoningRoutingRules({
<div className={grid}>
<Select
label={t("targetEffort")}
value={form.targetEffort}
value={currentTargetEffort}
onChange={(event) => setForm({ ...form, targetEffort: event.target.value })}
options={effortOptions}
/>
Expand Down Expand Up @@ -910,7 +922,7 @@ export default function ReasoningRoutingRules({
</p>
<p className="mt-1 text-text-muted">
{e("effort." + form.effortMode)}
{form.effortMode !== "inherit" ? ": " + form.targetEffort : ""}
{form.effortMode !== "inherit" ? ": " + currentTargetEffort : ""}
</p>
<p className="mt-2 text-xs text-text-muted">{e("draftNotice")}</p>
</div>
Expand Down Expand Up @@ -954,7 +966,7 @@ export default function ReasoningRoutingRules({
options={[
{ value: "missing", label: t("missing") },
{ value: "signal", label: t("signalOnly") },
...[...STANDARD_EFFORTS, ...EXTENDED_EFFORTS].map((value) => ({
...[...STANDARD_REASONING_EFFORTS, ...EXTENDED_REASONING_EFFORTS].map((value) => ({
value,
label: value,
})),
Expand Down
8 changes: 5 additions & 3 deletions src/shared/reasoning/reasoningRoutingEfforts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ export const EXTENDED_REASONING_EFFORTS = ["max", "ultra"] as const;
type ExtendedReasoningEffort = (typeof EXTENDED_REASONING_EFFORTS)[number];

/**
* The rules editor accepts `codex/`, `cx/` and `openai/` prefixed ids (#14961); the alias-set
* lookup shared with the Codex executor (#14720) knows the first two, so strip `openai/` here.
* The rules editor accepts provider-prefixed ids (`codex/`, `cx/`, `openai/` #14961, and
* `github/`, `opencode-zen/`… #14059); the alias-set lookup shared with the Codex executor
* (#14720) matches bare ids, so strip any single provider namespace here — the same way the
* routing policy resolves `modelIdForRegistry`.
*/
export function supportsExtendedCodexEffort(
model: string,
effort: ExtendedReasoningEffort
): boolean {
return codexModelFamilySupportsExtendedEffort(model.trim().replace(/^openai\//i, ""), effort);
return codexModelFamilySupportsExtendedEffort(model.trim().replace(/^[^/]+\//, ""), effort);
}

export function getReasoningRoutingTargetEffortOptions(
Expand Down
13 changes: 13 additions & 0 deletions tests/unit/dashboard/reasoning-routing-efforts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,16 @@ test("openai/ prefixed ids read the same Codex alias sets as the executor (#1472
"ultra",
]);
});

test("github/ and opencode-zen/ prefixed luna ids offer max but not ultra (#14059)", () => {
for (const model of ["github/gpt-5.6-luna", "opencode-zen/gpt-5.6-luna"]) {
assert.deepEqual(getReasoningRoutingTargetEffortOptions(model, "medium"), [
"none",
"low",
"medium",
"high",
"xhigh",
"max",
]);
}
});
Loading
Loading