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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,6 @@ RUN --mount=type=cache,id=apt-cache,target=/var/cache/apt,sharing=locked \

# Install CLI tools globally. Separate layer from apt for better cache reuse.
RUN --mount=type=cache,id=npm-cache,target=/root/.npm \
npm install -g --no-audit --no-fund @openai/codex@0.156.1 @anthropic-ai/claude-code droid openclaw@latest
npm install -g --no-audit --no-fund @openai/codex@0.159.2 @anthropic-ai/claude-code droid openclaw@latest

USER node
1 change: 1 addition & 0 deletions changelog.d/features/15171-stable-gpt61-sol.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **feat(codex):** Add GPT-6.1 Sol to the stable fork's OpenAI and Codex catalogs, preserve Max/Ultra routing and tool calls, accept Responses model imports, and align the Codex client profile and optional Docker CLI at 0.159.2. Include Standard short-context price estimates and the purchased-credit Fast multiplier; adapt upstream [#15171](https://github.com/diegosouzapw/OmniRoute/pull/15171) and [#15158](https://github.com/diegosouzapw/OmniRoute/pull/15158) — thanks @xiaoyaner0201 and @HouMinXi.
4 changes: 2 additions & 2 deletions open-sse/config/codexClient.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Codex's OAuth backend gates newer models by client version: GPT-6 Astra rejects
// older clients with "requires a newer version of Codex" (upstream issue #12761).
// Keep this in lockstep with CODEX_CLI_PROFILE in src/shared/constants/clientIdentityProfiles.ts.
// https://github.com/openai/codex/releases/tag/rust-v0.156.1
const DEFAULT_CODEX_CLIENT_VERSION = "0.156.1";
// https://github.com/openai/codex/releases/tag/rust-v0.159.2
const DEFAULT_CODEX_CLIENT_VERSION = "0.159.2";
const DEFAULT_CODEX_USER_AGENT_PLATFORM = "Windows 10.0.26200";
const DEFAULT_CODEX_USER_AGENT_ARCH = "x64";
const CODEX_VERSION_OVERRIDE_ENV = "CODEX_CLIENT_VERSION";
Expand Down
9 changes: 7 additions & 2 deletions open-sse/config/codexModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ export const GPT_5_6_ULTRA_ALIAS_MODELS = new Set(["gpt-5.6-sol", "gpt-5.6-terra
// STANDARD_EFFORT_SUFFIXES, so without this set neither would ever split off the
// model id. `ultra` is an OmniRoute-side tier that goes out as wire effort `max`
// while keeping parallel tool calls for sub-agent delegation.
export const GPT_6_ALIAS_MODELS = new Set(["gpt-6-astra", "gpt-6-sol", "gpt-6-luna"]);
export const GPT_6_ALIAS_MODELS = new Set([
"gpt-6.1-sol",
"gpt-6-astra",
"gpt-6-sol",
"gpt-6-luna",
]);

export function splitCodexReasoningSuffix(model: unknown): {
baseModel: string;
Expand All @@ -26,7 +31,7 @@ export function splitCodexReasoningSuffix(model: unknown): {
}
}

const gpt6AliasMatch = /^(gpt-6-(?:astra|sol|luna))-(max|ultra)$/.exec(modelId);
const gpt6AliasMatch = /^(gpt-6-(?:astra|sol|luna)|gpt-6\.1-sol)-(max|ultra)$/.exec(modelId);
if (gpt6AliasMatch) {
const [, baseModel, alias] = gpt6AliasMatch;
if (GPT_6_ALIAS_MODELS.has(baseModel) && !(baseModel === "gpt-6-luna" && alias === "ultra")) {
Expand Down
8 changes: 8 additions & 0 deletions open-sse/config/providers/registry/codex/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ export const codexProvider: RegistryEntry = {
tokenUrl: "https://auth.openai.com/oauth/token",
},
models: [
// Codex OAuth uses the 872K window; the public API has a separate 1.05M limit.
{ id: "gpt-6.1-sol", name: "GPT 6.1 Sol", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6.1-sol-low", name: "GPT 6.1 Sol (low)", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6.1-sol-medium", name: "GPT 6.1 Sol (medium)", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6.1-sol-high", name: "GPT 6.1 Sol (high)", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6.1-sol-xhigh", name: "GPT 6.1 Sol (xhigh)", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6.1-sol-max", name: "GPT 6.1 Sol (max)", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6.1-sol-ultra", name: "GPT 6.1 Sol (ultra)", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6-sol", name: "GPT 6 sol", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6-sol-low", name: "GPT 6 sol-low", ...GPT_5_6_CODEX_CAPABILITIES },
{ id: "gpt-6-sol-medium", name: "GPT 6 sol-medium", ...GPT_5_6_CODEX_CAPABILITIES },
Expand Down
8 changes: 8 additions & 0 deletions open-sse/config/providers/registry/openai/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ export const openaiProvider: RegistryEntry = {
authHeader: "bearer",
defaultContextLength: 128000,
models: [
// Sol tool calling requires Responses (OpenAI's GPT-6.1 Sol model reference).
{
id: "gpt-6.1-sol",
name: "GPT-6.1 Sol",
...GPT_5_6_API_CAPABILITIES,
targetFormat: "openai-responses",
unsupportedParams: ["temperature", "top_p", "top_logprobs", "logprobs"],
},
{ id: "gpt-6-sol", name: "GPT 6 sol", ...GPT_5_6_API_CAPABILITIES },
{ id: "gpt-6-luna", name: "GPT 6 luna", ...GPT_5_6_API_CAPABILITIES },
{ id: "gpt-5.6", name: "GPT-5.6", ...GPT_5_6_API_CAPABILITIES },
Expand Down
3 changes: 2 additions & 1 deletion open-sse/executors/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ function normalizeServiceTierValue(value: unknown): string | undefined {
* Update this table when Codex releases new models with different caps.
*/
const MAX_EFFORT_BY_MODEL: Record<string, EffortLevel> = {
"gpt-6.1-sol": "ultra",
"gpt-6-astra": "ultra",
"gpt-6-sol": "ultra",
"gpt-6-luna": "max",
Expand Down Expand Up @@ -1331,7 +1332,7 @@ export class CodexExecutor extends BaseExecutor {
const explicitReasoning = normalizeEffortValue(reasoningRecord?.effort);
const requestReasoningEffort = normalizeEffortValue(body.reasoning_effort);
const fallbackReasoningEffort = allowConnectionReasoningDefaults
? requestDefaults.reasoningEffort || "medium"
? requestDefaults.reasoningEffort || (cleanModel === "gpt-6.1-sol" ? "low" : "medium")
: undefined;
// Issue #2331: model suffix aliases (for example gpt-5.5-xhigh) represent an
// explicit model selection, so they must override client-injected defaults such
Expand Down
7 changes: 7 additions & 0 deletions open-sse/services/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ const CODEX_PREFERRED_UNPREFIXED_MODELS = new Set([
// `gpt-5.5 → gpt-5.5-medium` entry is removed to preserve #2877's bare-id contract.
const CODEX_PREFERRED_UNPREFIXED_MODEL_ALIASES = new Map<string, string>([]);
export const CODEX_NATIVE_UNPREFIXED_MODELS = new Set([
"gpt-6.1-sol",
"gpt-6.1-sol-low",
"gpt-6.1-sol-medium",
"gpt-6.1-sol-high",
"gpt-6.1-sol-xhigh",
"gpt-6.1-sol-max",
"gpt-6.1-sol-ultra",
"codex-auto-review",
"gpt-6-sol",
"gpt-6-sol-low",
Expand Down
2 changes: 1 addition & 1 deletion open-sse/translator/request/openai-responses/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function normalizeResponsesReasoningEffort(value: unknown, model?: unknow
const effort = toString(value).toLowerCase();
const codexModel =
typeof model === "string" &&
/^(?:(?:codex|cx)\/)?gpt-6-(?:astra|sol|luna)(?:-(?:low|medium|high|xhigh|max|ultra))?$/.test(
/^(?:(?:codex|cx)\/)?(?:gpt-6-(?:astra|sol|luna)|gpt-6\.1-sol)(?:-(?:low|medium|high|xhigh|max|ultra))?$/.test(
model
);
return effort === "max" && !codexModel ? "xhigh" : effort;
Expand Down
1 change: 1 addition & 0 deletions src/lib/providers/codexFastTier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type CodexFastTierValue = CodexServiceTier;
export type CodexGlobalServiceMode = "none" | CodexServiceTier;

export const CODEX_FAST_TIER_DEFAULT_SUPPORTED_MODELS: readonly string[] = [
"gpt-6.1-sol",
"gpt-6-astra",
"gpt-6-sol",
"gpt-6-luna",
Expand Down
12 changes: 11 additions & 1 deletion src/lib/usage/costCalculator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ export function getCodexFastCostMultiplier(

const modelKey = stripCodexEffortSuffix(normalizeModelName(String(model || "")).toLowerCase());
const compactModelKey = modelKey.replace(/-/g, "");
// Purchased credits use 2x; included subscription allowance uses 2.5x.
if (compactModelKey === "gpt6.1sol") return 2;
// Codex Astra Fast is 2.5x Standard (https://developers.openai.com/codex/pricing).
if (
/^gpt-6-(?:astra|sol|luna)$/.test(modelKey) ||
Expand Down Expand Up @@ -168,7 +170,15 @@ export function computeCostFromPricing(
cost += outputTokens * (outputPrice / 1_000_000);

const reasoningTokens = tokens.reasoning ?? tokens.reasoning_tokens ?? 0;
if (reasoningTokens > 0) cost += reasoningTokens * (reasoningPrice / 1_000_000);
if (reasoningTokens > 0) {
const model = stripCodexEffortSuffix(normalizeModelName(options.model || ""));
const solOutputIncludesReasoning =
model === "gpt-6.1-sol" && ["openai", "codex", "cx"].includes(options.provider || "");
const reasoningRate = solOutputIncludesReasoning
? Math.max(0, reasoningPrice - outputPrice)
: reasoningPrice;
cost += reasoningTokens * (reasoningRate / 1_000_000);
}

if (cacheCreationTokens > 0) cost += cacheCreationTokens * (cacheCreationPrice / 1_000_000);

Expand Down
11 changes: 9 additions & 2 deletions src/lib/vscode/reasoningMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const STANDARD_EFFORT_SUFFIX_PATTERN = /-(xhigh|high|medium|low|none)$/i;
// effort suffixes. Without a match here the base id never splits off, and VS Code
// discovery expands the alias again into invalid ids like `gpt-6-astra-max-high`.
const EXTENDED_EFFORT_SUFFIX_PATTERN =
/^(.*(?:gpt-5\.6-(?:sol|terra|luna)|gpt-6-(?:astra|sol|luna)))-(max|ultra)$/i;
/^(.*(?:gpt-5\.6-(?:sol|terra|luna)|gpt-6-(?:astra|sol|luna)|gpt-6\.1-sol))-(max|ultra)$/i;
const DEFAULT_REASONING_EFFORT = "none";
const KNOWN_REASONING_EFFORTS = new Set(["none", "low", "medium", "high", "xhigh", "max", "ultra"]);

Expand Down Expand Up @@ -182,7 +182,14 @@ export function getReasoningVariantBaseModelId(modelId: string) {
}

export function getDefaultReasoningEffort(model: VscodeCatalogModel, supportedValues?: string[]) {
return inferSelectedReasoningEffort(model, supportedValues) || DEFAULT_REASONING_EFFORT;
const selected = inferSelectedReasoningEffort(model, supportedValues);
if (selected) return selected;
const parsed = parseModel(getCatalogModelName(model));
const provider = parsed.provider || model.owned_by;
if ((provider === "codex" || provider === "cx") && parsed.model === "gpt-6.1-sol") {
return "low";
}
return DEFAULT_REASONING_EFFORT;
}

export function buildReasoningConfigSchema(
Expand Down
2 changes: 1 addition & 1 deletion src/shared/constants/clientIdentityProfiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const CODEX_CLI_PROFILE: ClientIdentityProfile = Object.freeze({
id: "codex-cli",
label: "Codex CLI",
headers: Object.freeze({
"User-Agent": "codex_cli_rs/0.156.1",
"User-Agent": "codex_cli_rs/0.159.2",
originator: "codex_cli_rs",
}),
});
Expand Down
1 change: 1 addition & 0 deletions src/shared/constants/modelSpecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export const MODEL_SPECS: Record<string, ModelSpec> = {
},

// Public model limits; the Codex registry supplies its smaller OAuth window.
"gpt-6.1-sol": { ...GPT_5_6_MODEL_SPEC, aliases: ["openai/gpt-6.1-sol"] },
"gpt-6-sol": { ...GPT_5_6_MODEL_SPEC, aliases: ["openai/gpt-6-sol"] },
"gpt-6-luna": { ...GPT_5_6_MODEL_SPEC, aliases: ["openai/gpt-6-luna"] },
"gpt-6-astra": {
Expand Down
2 changes: 2 additions & 0 deletions src/shared/constants/pricing/frontier-labs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {

export const DEFAULT_PRICING_FRONTIER = {
openai: {
// Standard short-context USD/MTok; API cache writes have a separate rate.
"gpt-6.1-sol": { input: 2, output: 10, cached: 0.1, reasoning: 10, cache_creation: 2.5 },
"gpt-5.6": GPT_5_6_SOL_PRICING,
"gpt-5.6-sol": GPT_5_6_SOL_PRICING,
"gpt-5.6-terra": GPT_5_6_TERRA_PRICING,
Expand Down
12 changes: 12 additions & 0 deletions src/shared/constants/pricing/oauth-subscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import {
GPT_5_6_TERRA_PRICING,
} from "./shared-tiers";

// Codex: 50 / 2.5 / 250 credits per MTok at 25 credits/USD; no cache-write charge.
// https://learn.chatgpt.com/docs/pricing
const GPT_6_1_SOL_CODEX_PRICING = { input: 2, output: 10, cached: 0.1, reasoning: 10 };

export const DEFAULT_PRICING_OAUTH = {
cc: {
"claude-fable-5": {
Expand Down Expand Up @@ -80,6 +84,14 @@ export const DEFAULT_PRICING_OAUTH = {
},
},
cx: {
"gpt-6.1-sol": GPT_6_1_SOL_CODEX_PRICING,
"gpt-6.1-sol-low": GPT_6_1_SOL_CODEX_PRICING,
"gpt-6.1-sol-medium": GPT_6_1_SOL_CODEX_PRICING,
"gpt-6.1-sol-high": GPT_6_1_SOL_CODEX_PRICING,
"gpt-6.1-sol-xhigh": GPT_6_1_SOL_CODEX_PRICING,
"gpt-6.1-sol-max": GPT_6_1_SOL_CODEX_PRICING,
"gpt-6.1-sol-ultra": GPT_6_1_SOL_CODEX_PRICING,

"codex-auto-review": GPT_5_5_PRICING,
// Codex uses credits per 1M tokens. OmniRoute stores the dollar-equivalent
// values below at the documented conversion of 25 credits per USD.
Expand Down
4 changes: 4 additions & 0 deletions src/shared/reasoning/effortStandardization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export function extendCodexGpt56EffortValues(
return values;
}

if (/^gpt-6\.1-sol(?:-(?:low|medium|high|xhigh|max|ultra))?$/.test(normalizedModel)) {
return ["low", "medium", "high", "xhigh", "max", "ultra"];
}

const match = normalizedModel.match(
/^gpt-5\.6-(sol|terra|luna)(?:-(?:none|low|medium|high|xhigh|max|ultra))?$/
);
Expand Down
1 change: 1 addition & 0 deletions src/shared/validation/schemas/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export const providerModelMutationSchema = z.object({
.array(
z.enum([
"chat",
"responses",
"embeddings",
"rerank",
"images",
Expand Down
12 changes: 6 additions & 6 deletions tests/snapshots/provider/translate-path.json
Original file line number Diff line number Diff line change
Expand Up @@ -966,25 +966,25 @@
"Authorization": "Bearer <TOK>",
"Content-Type": "application/json",
"Openai-Beta": "responses=experimental",
"User-Agent": "codex-cli/0.156.1 (<OS>; <ARCH>)",
"Version": "0.156.1",
"User-Agent": "codex-cli/0.159.2 (<OS>; <ARCH>)",
"Version": "0.159.2",
"X-Codex-Beta-Features": "responses_websockets"
},
"nonStream": {
"Authorization": "Bearer <TOK>",
"Content-Type": "application/json",
"Openai-Beta": "responses=experimental",
"User-Agent": "codex-cli/0.156.1 (<OS>; <ARCH>)",
"Version": "0.156.1",
"User-Agent": "codex-cli/0.159.2 (<OS>; <ARCH>)",
"Version": "0.159.2",
"X-Codex-Beta-Features": "responses_websockets"
},
"oauth": {
"Accept": "text/event-stream",
"Authorization": "Bearer <TOK>",
"Content-Type": "application/json",
"Openai-Beta": "responses=experimental",
"User-Agent": "codex-cli/0.156.1 (<OS>; <ARCH>)",
"Version": "0.156.1",
"User-Agent": "codex-cli/0.159.2 (<OS>; <ARCH>)",
"Version": "0.159.2",
"X-Codex-Beta-Features": "responses_websockets"
}
},
Expand Down
8 changes: 4 additions & 4 deletions tests/unit/claude-codex-identity-version-sync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ test("Claude CLI is pinned to the captured 2.1.207 release", () => {
assert.equal(id.CLAUDE_CODE_VERSION, "2.1.207");
});

test("Codex client is pinned to the captured 0.156.1 release", () => {
assert.equal(codexCfg.getCodexClientVersion(), "0.156.1");
assert.equal(codexCfg.getCodexUserAgent(), "codex-cli/0.156.1 (Windows 10.0.26200; x64)");
assert.equal(codexCfg.getCodexDefaultHeaders().Version, "0.156.1");
test("Codex client is pinned to the captured 0.159.2 release", () => {
assert.equal(codexCfg.getCodexClientVersion(), "0.159.2");
assert.equal(codexCfg.getCodexUserAgent(), "codex-cli/0.159.2 (Windows 10.0.26200; x64)");
assert.equal(codexCfg.getCodexDefaultHeaders().Version, "0.159.2");
});

test("Codex CLI preset and optional Docker CLI stay aligned with the wire version", () => {
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/client-identity-profiles.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ test("getClientIdentityProfileHeaders: known CLI profiles expose their preset he
assert.equal(claudeCli["X-App"], "cli");

const codexCli = getClientIdentityProfileHeaders("codex-cli");
assert.equal(codexCli["User-Agent"], "codex_cli_rs/0.156.1");
assert.equal(codexCli["User-Agent"], "codex_cli_rs/0.159.2");
assert.equal(codexCli.originator, "codex_cli_rs");

const geminiCli = getClientIdentityProfileHeaders("gemini-cli");
Expand Down Expand Up @@ -80,7 +80,7 @@ test("a selected profile's headers land in providerSpecificData.customHeaders",
customHeaders: { ...profileHeaders, "X-Operator-Set": "keep-me" },
};

assert.equal(providerSpecificData.customHeaders["User-Agent"], "codex_cli_rs/0.156.1");
assert.equal(providerSpecificData.customHeaders["User-Agent"], "codex_cli_rs/0.159.2");
assert.equal(providerSpecificData.customHeaders.originator, "codex_cli_rs");
assert.equal(providerSpecificData.customHeaders["X-Operator-Set"], "keep-me");
});
Expand Down
1 change: 1 addition & 0 deletions tests/unit/codex-fast-tier.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ test("Codex global service mode distinguishes no setting from explicit tiers", (
enabled: true,
tier: "default",
supportedModels: [
"gpt-6.1-sol",
"gpt-6-astra",
"gpt-6-sol",
"gpt-6-luna",
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/codex-gpt6-astra.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ test("VS Code discovery splits Astra's extended effort aliases off the base id",
test("Codex identifies as a client version Astra accepts", () => {
// Astra rejects older clients with HTTP 400 "requires a newer version of Codex"
// (upstream issue #12761), so the advertised identity gates the whole model.
assert.equal(getCodexClientVersion(), "0.156.1");
assert.equal(getCodexDefaultHeaders().Version, "0.156.1");
assert.equal(getCodexClientVersion(), "0.159.2");
assert.equal(getCodexDefaultHeaders().Version, "0.159.2");
});

test("Codex Fast bills GPT-6 Astra at the 2.5x multiplier", () => {
Expand Down
Loading