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
1 change: 1 addition & 0 deletions changelog.d/features/gpt6-sol-luna-fork.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add GPT-6 Sol and Luna to the Codex catalog with their supported reasoning variants, pricing, Fast-mode cost estimates and VS Code effort metadata. Align reasoning-routing rules with the executor's supported Max/Ultra models. Ported from upstream PRs #14677 and #14720 by Quang Blue.
34 changes: 34 additions & 0 deletions open-sse/config/providers/registry/codex/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,40 @@ export const codexProvider: RegistryEntry = {
name: "GPT 6 Astra (Low)",
...GPT_5_6_CODEX_CAPABILITIES,
},
// Sol and Luna share Astra's Codex windows; Sol offers low..ultra, Luna low..max.
{ id: "gpt-6-sol", name: "GPT 6 Sol", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6-sol-ultra", name: "GPT 6 Sol (Ultra)", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6-sol-max", name: "GPT 6 Sol (Max)", ...GPT_5_6_CODEX_CAPABILITIES },
{
id: "gpt-6-sol-xhigh",
name: "GPT 6 Sol (xHigh)",
...GPT_5_6_CODEX_CAPABILITIES,
timeoutMs: 1200000,
},
{
id: "gpt-6-sol-high",
name: "GPT 6 Sol (High)",
...GPT_5_6_CODEX_CAPABILITIES,
timeoutMs: 1200000,
},
{ id: "gpt-6-sol-medium", name: "GPT 6 Sol (Medium)", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6-sol-low", name: "GPT 6 Sol (Low)", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6-luna", name: "GPT 6 Luna", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6-luna-max", name: "GPT 6 Luna (Max)", ...GPT_5_6_CODEX_CAPABILITIES },
{
id: "gpt-6-luna-xhigh",
name: "GPT 6 Luna (xHigh)",
...GPT_5_6_CODEX_CAPABILITIES,
timeoutMs: 1200000,
},
{
id: "gpt-6-luna-high",
name: "GPT 6 Luna (High)",
...GPT_5_6_CODEX_CAPABILITIES,
timeoutMs: 1200000,
},
{ id: "gpt-6-luna-medium", name: "GPT 6 Luna (Medium)", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6-luna-low", name: "GPT 6 Luna (Low)", ...GPT_5_6_CODEX_CAPABILITIES },
// gpt-5.5 codex OAuth backend caps context at 400K (not the public-API
// 1.05M). Public refs : openai/codex#19208, #19319, #19464 ;
// opencode#24171. max_output_tokens is stripped server-side
Expand Down
18 changes: 6 additions & 12 deletions open-sse/executors/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ export {
import { isCodexFreePlan, normalizeCodexTools } from "./codex/tools.ts";
import {
CODEX_EFFORT_ORDER as EFFORT_ORDER,
GPT_5_6_ULTRA_ALIAS_MODELS,
GPT_6_ALIAS_MODELS,
CODEX_ULTRA_ALIAS_MODELS,
getCodexAliasEffortCap,
splitCodexReasoningSuffix,
type CodexEffortLevel as EffortLevel,
} from "./codex/reasoningSuffix.ts";
Expand Down Expand Up @@ -173,8 +173,7 @@ function isCodexResponsesLiteRequest(
// delegation silently breaks while the request still returns HTTP 200 (issue #7821).
function isCodexDelegationDependentModel(model: unknown): boolean {
const { baseModel, effort } = splitCodexReasoningSuffix(model);
if (effort === "ultra" && GPT_5_6_ULTRA_ALIAS_MODELS.has(baseModel)) return true;
if (effort === "ultra" && GPT_6_ALIAS_MODELS.has(baseModel)) return true;
if (effort === "ultra" && CODEX_ULTRA_ALIAS_MODELS.has(baseModel)) return true;
if (effort === "max" && baseModel === "gpt-5.6-luna") return true;
return false;
}
Expand Down Expand Up @@ -326,15 +325,10 @@ function normalizeServiceTierValue(value: unknown): string | undefined {
}

/**
* Maximum reasoning effort allowed per Codex model.
* Models not listed here retain the legacy xhigh cap.
* Update this table when Codex releases new models with different caps.
* Maximum reasoning effort per Codex model. Max/ultra-tier models come from the alias
* sets in reasoningSuffix.ts; everything else unlisted keeps the xhigh cap.
*/
const MAX_EFFORT_BY_MODEL: Record<string, EffortLevel> = {
"gpt-6-astra": "ultra",
"gpt-5.6-sol": "ultra",
"gpt-5.6-terra": "ultra",
"gpt-5.6-luna": "max",
"gpt-5.3-codex": "xhigh",
"gpt-5.1-codex-max": "xhigh",
"gpt-5-mini": "high",
Expand All @@ -347,7 +341,7 @@ const MAX_EFFORT_BY_MODEL: Record<string, EffortLevel> = {
* Returns the original value if within limits, or the cap if it exceeds it.
*/
function clampEffort(model: string, requested: string): string {
const max: EffortLevel = MAX_EFFORT_BY_MODEL[model] ?? "xhigh";
const max: EffortLevel = MAX_EFFORT_BY_MODEL[model] ?? getCodexAliasEffortCap(model) ?? "xhigh";
const reqIdx = EFFORT_ORDER.indexOf(requested as EffortLevel);
const maxIdx = EFFORT_ORDER.indexOf(max);
if (reqIdx > maxIdx) {
Expand Down
43 changes: 27 additions & 16 deletions open-sse/executors/codex/reasoningSuffix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,47 @@ export const CODEX_EFFORT_ORDER = [
"ultra",
] as const;
export type CodexEffortLevel = (typeof CODEX_EFFORT_ORDER)[number];
export const GPT_5_6_MAX_ALIAS_MODELS = new Set(["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"]);
export const GPT_5_6_ULTRA_ALIAS_MODELS = new Set(["gpt-5.6-sol", "gpt-5.6-terra"]);
export const GPT_6_ALIAS_MODELS = new Set(["gpt-6-astra"]);
export const CODEX_MAX_ALIAS_MODELS = new Set([
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-6-astra",
"gpt-6-sol",
"gpt-6-luna",
]);
export const CODEX_ULTRA_ALIAS_MODELS = new Set([
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-6-astra",
"gpt-6-sol",
]);

/** Highest effort a max/ultra-tier base model accepts, or null for other models. */
export function getCodexAliasEffortCap(model: string): CodexEffortLevel | null {
if (CODEX_ULTRA_ALIAS_MODELS.has(model)) return "ultra";
if (CODEX_MAX_ALIAS_MODELS.has(model)) return "max";
return null;
}

export function splitCodexReasoningSuffix(model: unknown): {
baseModel: string;
effort: CodexEffortLevel | null;
} {
const modelId = typeof model === "string" ? model : "";
const gpt56Match = /^(gpt-5\.6-(?:sol|terra|luna))(?:-(max|ultra)|\((max|ultra)\))$/.exec(
modelId
);
if (gpt56Match) {
const [, baseModel, hyphenEffort, parenthesizedEffort] = gpt56Match;
const maxTierMatch = /^(.+?)(?:-(max|ultra)|\((max|ultra)\))$/.exec(modelId);
if (maxTierMatch) {
const [, baseModel, hyphenEffort, parenthesizedEffort] = maxTierMatch;
const effort = hyphenEffort ?? parenthesizedEffort;
const supportedModels = parenthesizedEffort
? GPT_5_6_MAX_ALIAS_MODELS
? CODEX_MAX_ALIAS_MODELS
: effort === "ultra"
? GPT_5_6_ULTRA_ALIAS_MODELS
: GPT_5_6_MAX_ALIAS_MODELS;
? CODEX_ULTRA_ALIAS_MODELS
: CODEX_MAX_ALIAS_MODELS;
if (supportedModels.has(baseModel)) {
return { baseModel, effort: effort as CodexEffortLevel };
}
}

const gpt6Match = /^(gpt-6-astra)-(max|ultra)$/.exec(modelId);
if (gpt6Match && GPT_6_ALIAS_MODELS.has(gpt6Match[1])) {
return { baseModel: gpt6Match[1], effort: gpt6Match[2] as CodexEffortLevel };
}

for (const effort of ["none", "low", "medium", "high", "xhigh"] as const) {
if (modelId.endsWith(`-${effort}`)) {
return { baseModel: modelId.slice(0, -`-${effort}`.length), effort };
Expand Down
6 changes: 3 additions & 3 deletions open-sse/translator/request/openai-responses/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ export function imageUrlToText(value: unknown): string {
return toString(record.url);
}

const CODEX_GPT_5_6_MODEL_PATTERN =
/^gpt-5\.6-(?:sol|terra|luna)(?:-(?:none|low|medium|high|xhigh|max|ultra))?$/;
const CODEX_MAX_EFFORT_MODEL_PATTERN =
/^(?:gpt-5\.6-(?:sol|terra|luna)|gpt-6-(?:astra|sol|luna))(?:-(?:none|low|medium|high|xhigh|max|ultra))?$/;
const KIRO_GPT_5_6_MODEL_PATTERN =
/^(?:kiro|kr)\/gpt-5\.6-(?:sol|terra|luna)(?:-(?:none|low|medium|high|xhigh|max))?$/;

Expand All @@ -61,7 +61,7 @@ function supportsNativeMaxReasoningEffort(model: unknown): boolean {
.toLowerCase()
.replace(/^(?:codex|cx)\//, "");
return (
CODEX_GPT_5_6_MODEL_PATTERN.test(normalizedModel) ||
CODEX_MAX_EFFORT_MODEL_PATTERN.test(normalizedModel) ||
KIRO_GPT_5_6_MODEL_PATTERN.test(toString(model).trim().toLowerCase())
);
}
Expand Down
23 changes: 12 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions src/lib/reasoningRouting/policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import {
import { getResolvedModelCapabilities } from "@/lib/modelCapabilities";
import { normalizeRoutingTags } from "@/domain/tagRouter";
import { splitClaudeEffortSuffix } from "@omniroute/open-sse/config/providerModels.ts";
import {
codexModelFamilySupportsExtendedEffort,
isCodexExtendedEffortBaseModel,
} from "@/shared/reasoning/codexExtendedEffort";

type JsonRecord = Record<string, unknown>;
const EFFORTS = new Set<ReasoningEffort>([
Expand Down Expand Up @@ -76,8 +80,9 @@ function splitGenericEffortSuffix(model: string): {
}

function supportsCodexSuffix(candidate: string, normalizedBase: string): boolean {
if (candidate === "max") return /^gpt-5\.6-(?:sol|terra|luna)$/.test(normalizedBase);
if (candidate === "ultra") return /^gpt-5\.6-(?:sol|terra)$/.test(normalizedBase);
if (candidate === "max" || candidate === "ultra") {
return isCodexExtendedEffortBaseModel(normalizedBase, candidate);
}
return true;
}

Expand Down Expand Up @@ -264,12 +269,7 @@ function capabilityFor(
const capabilities = getResolvedModelCapabilities(model);
if (capabilities.supportsThinking === false) return "unsupported" as const;
if (targetEffort === "max" || targetEffort === "ultra") {
const normalized = model.toLowerCase().replace(/^(?:codex|cx)\//, "");
const supported =
targetEffort === "ultra"
? /^gpt-5\.6-(?:sol|terra)(?:-|$)/.test(normalized)
: /^gpt-5\.6-(?:sol|terra|luna)(?:-|$)/.test(normalized);
if (supported) return "supported" as const;
if (codexModelFamilySupportsExtendedEffort(model, targetEffort)) return "supported" as const;
if (capabilities.supportsThinking === null) return "unknown" as const;
return "unsupported" as const;
}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/usage/costCalculator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ export function getCodexFastCostMultiplier(

const modelKey = stripCodexEffortSuffix(normalizeModelName(String(model || "")).toLowerCase());
const compactModelKey = modelKey.replace(/-/g, "");
if (modelKey === "gpt-6-astra" || compactModelKey === "gpt6astra") return 2.5;
// Codex GPT-6 Fast is 2.5x Standard (https://developers.openai.com/codex/pricing).
if (/^gpt6(?:astra|sol|luna)$/.test(compactModelKey)) return 2.5;
if (
/^gpt-5\.6-(?:sol|terra|luna)$/.test(modelKey) ||
/^gpt5\.6(?:sol|terra|luna)$/.test(compactModelKey)
Expand Down
8 changes: 5 additions & 3 deletions src/lib/vscode/reasoningMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export type VscodeCatalogModel = {
};

const STANDARD_EFFORT_SUFFIX_PATTERN = /-(xhigh|high|medium|low|none)$/i;
const GPT_5_6_EXTENDED_EFFORT_SUFFIX_PATTERN = /^(.*gpt-5\.6-(?:sol|terra|luna))-(max|ultra)$/i;
const CODEX_EXTENDED_EFFORT_SUFFIX_PATTERN =
/^(.*gpt-(?:5\.6-(?:sol|terra|luna)|6-(?:astra|sol|luna)))-(max|ultra)$/i;
const DEFAULT_REASONING_EFFORT = "none";
const KNOWN_REASONING_EFFORTS = new Set(["none", "low", "medium", "high", "xhigh", "max", "ultra"]);

Expand All @@ -41,7 +42,7 @@ export function getCatalogModelName(model: VscodeCatalogModel) {
}

function matchReasoningEffortSuffix(modelId: string) {
const extendedMatch = modelId.match(GPT_5_6_EXTENDED_EFFORT_SUFFIX_PATTERN);
const extendedMatch = modelId.match(CODEX_EXTENDED_EFFORT_SUFFIX_PATTERN);
if (extendedMatch?.[1] && extendedMatch[2]) {
return { baseModelId: extendedMatch[1], effort: extendedMatch[2].toLowerCase() };
}
Expand Down Expand Up @@ -190,9 +191,10 @@ function getCodexGpt56DefaultReasoningEffort(model: VscodeCatalogModel) {
.trim()
.toLowerCase();
const match = providerModelId.match(
/^gpt-5\.6-(sol|terra|luna)(?:-(?:none|low|medium|high|xhigh|max|ultra))?$/
/^gpt-(?:5\.6-(sol|terra|luna)|6-(?:astra|sol|luna))(?:-(?:none|low|medium|high|xhigh|max|ultra))?$/
);
if (!match) return undefined;
// The live catalog defaults GPT-5.6 Sol to low and every other model here to medium.
return match[1] === "sol" ? "low" : "medium";
}

Expand Down
18 changes: 6 additions & 12 deletions src/shared/components/ReasoningRoutingRules.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslations } from "next-intl";
import { Button, Card, Input, Select, Toggle } from "@/shared/components";
import { codexModelFamilySupportsExtendedEffort } from "@/shared/reasoning/codexExtendedEffort";

type RuleScope = "global" | "apiKey" | "combo" | "model" | "connection";
type TargetKind = "keep" | "model" | "combo";
Expand Down Expand Up @@ -83,16 +84,6 @@ function emptyRule(apiKeyId?: string): FormState {
};
}

function supportsExtendedCodexEffort(model: string, effort: "max" | "ultra"): boolean {
const normalized = model
.trim()
.toLowerCase()
.replace(/^(?:codex|cx)\//, "");
return effort === "ultra"
? /^gpt-5\.6-(?:sol|terra)(?:-|$)/.test(normalized)
: /^gpt-5\.6-(?:sol|terra|luna)(?:-|$)/.test(normalized);
}

export default function ReasoningRoutingRules({ apiKeyId }: { apiKeyId?: string }) {
const t = useTranslations("reasoningRouting");
const [rules, setRules] = useState<Rule[]>([]);
Expand Down Expand Up @@ -155,7 +146,10 @@ export default function ReasoningRoutingRules({ apiKeyId }: { apiKeyId?: string
const values = [...STANDARD_EFFORTS];
for (const effort of EXTENDED_EFFORTS) {
if (
supportsExtendedCodexEffort(targetModelForCapability, effort as "max" | "ultra") ||
codexModelFamilySupportsExtendedEffort(
targetModelForCapability,
effort as "max" | "ultra"
) ||
form.targetEffort === effort
) {
values.push(effort);
Expand All @@ -169,7 +163,7 @@ export default function ReasoningRoutingRules({ apiKeyId }: { apiKeyId?: string
if (!EXTENDED_EFFORTS.includes(form.targetEffort)) return "";
if (form.targetKind === "combo") return t("extendedComboWarning");
if (!targetModelForCapability.trim()) return t("extendedUnknownWarning");
return supportsExtendedCodexEffort(
return codexModelFamilySupportsExtendedEffort(
targetModelForCapability,
form.targetEffort as "max" | "ultra"
)
Expand Down
30 changes: 30 additions & 0 deletions src/shared/constants/pricing/oauth-subscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@ const ANTIGRAVITY_GEMINI_3_7_PRICING = {
"gemini-3.7-flash-high": GEMINI_3_7_FLASH_PROMO_PRICING,
};

// Codex Standard: Sol 50 / 5 / 250 and Luna 2.5 / 0.25 / 12.5 credits per MTok,
// converted at 25 credits per USD. https://developers.openai.com/codex/pricing
const GPT_6_SOL_CODEX_PRICING = {
input: 2.0,
output: 10.0,
cached: 0.2,
reasoning: 10.0,
cache_creation: 2.5,
};
const GPT_6_LUNA_CODEX_PRICING = {
input: 0.1,
output: 0.5,
cached: 0.01,
reasoning: 0.5,
cache_creation: 0.125,
};

export const DEFAULT_PRICING_OAUTH = {
cc: {
"claude-fable-5": {
Expand Down Expand Up @@ -117,6 +134,19 @@ export const DEFAULT_PRICING_OAUTH = {
"gpt-6-astra-high": GPT_6_ASTRA_PRICING,
"gpt-6-astra-medium": GPT_6_ASTRA_PRICING,
"gpt-6-astra-low": GPT_6_ASTRA_PRICING,
"gpt-6-sol": GPT_6_SOL_CODEX_PRICING,
"gpt-6-sol-ultra": GPT_6_SOL_CODEX_PRICING,
"gpt-6-sol-max": GPT_6_SOL_CODEX_PRICING,
"gpt-6-sol-xhigh": GPT_6_SOL_CODEX_PRICING,
"gpt-6-sol-high": GPT_6_SOL_CODEX_PRICING,
"gpt-6-sol-medium": GPT_6_SOL_CODEX_PRICING,
"gpt-6-sol-low": GPT_6_SOL_CODEX_PRICING,
"gpt-6-luna": GPT_6_LUNA_CODEX_PRICING,
"gpt-6-luna-max": GPT_6_LUNA_CODEX_PRICING,
"gpt-6-luna-xhigh": GPT_6_LUNA_CODEX_PRICING,
"gpt-6-luna-high": GPT_6_LUNA_CODEX_PRICING,
"gpt-6-luna-medium": GPT_6_LUNA_CODEX_PRICING,
"gpt-6-luna-low": GPT_6_LUNA_CODEX_PRICING,
// GPT 5.5
"gpt-5.5": GPT_5_5_PRICING,
"gpt5.5": GPT_5_5_PRICING,
Expand Down
Loading
Loading