diff --git a/config/base-codex.settings.json b/config/base-codex.settings.json index cf2915ad..a0ef17f4 100644 --- a/config/base-codex.settings.json +++ b/config/base-codex.settings.json @@ -2,9 +2,9 @@ "env": { "ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/codex", "ANTHROPIC_AUTH_TOKEN": "ccs-internal-managed", - "ANTHROPIC_MODEL": "gpt-5.1-codex-max", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "gpt-5.1-codex-max-high", - "ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-5.1-codex-max", - "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-5.1-codex-mini-high" + "ANTHROPIC_MODEL": "gpt-5.3-codex", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "gpt-5.3-codex", + "ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-5.3-codex-high", + "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-5.3-codex-spark" } } diff --git a/src/cliproxy/model-catalog.ts b/src/cliproxy/model-catalog.ts index 6948a2e7..1705fbea 100644 --- a/src/cliproxy/model-catalog.ts +++ b/src/cliproxy/model-catalog.ts @@ -160,12 +160,34 @@ export const MODEL_CATALOG: Partial> = codex: { provider: 'codex', displayName: 'Copilot Codex', - defaultModel: 'gpt-5.2-codex', + defaultModel: 'gpt-5.3-codex', models: [ + { + id: 'gpt-5.3-codex', + name: 'GPT-5.3 Codex', + description: 'Full reasoning support (xhigh)', + thinking: { + type: 'levels', + levels: ['medium', 'high', 'xhigh'], + maxLevel: 'xhigh', + dynamicAllowed: false, + }, + }, + { + id: 'gpt-5.3-codex-spark', + name: 'GPT-5.3 Codex Spark', + description: 'Ultra-fast coding model', + thinking: { + type: 'levels', + levels: ['medium', 'high', 'xhigh'], + maxLevel: 'xhigh', + dynamicAllowed: false, + }, + }, { id: 'gpt-5.2-codex', name: 'GPT-5.2 Codex', - description: 'Full reasoning support (xhigh)', + description: 'Previous stable Codex model', thinking: { type: 'levels', levels: ['medium', 'high', 'xhigh'], diff --git a/src/cliproxy/model-config.ts b/src/cliproxy/model-config.ts index 1442a93a..8e1644a2 100644 --- a/src/cliproxy/model-config.ts +++ b/src/cliproxy/model-config.ts @@ -142,6 +142,10 @@ export async function configureProviderModel( // Get base env vars for defaults const baseEnv = getClaudeEnvVars(provider); + const codexSonnetModel = + provider === 'codex' && !selectedModel.match(/-(xhigh|high|medium)$/) + ? `${selectedModel}-high` + : selectedModel; // Read existing settings to preserve user customizations let existingSettings: Record = {}; @@ -165,7 +169,7 @@ export async function configureProviderModel( ANTHROPIC_AUTH_TOKEN: baseEnv.ANTHROPIC_AUTH_TOKEN || '', ANTHROPIC_MODEL: selectedModel, ANTHROPIC_DEFAULT_OPUS_MODEL: selectedModel, - ANTHROPIC_DEFAULT_SONNET_MODEL: selectedModel, + ANTHROPIC_DEFAULT_SONNET_MODEL: codexSonnetModel, ANTHROPIC_DEFAULT_HAIKU_MODEL: baseEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL || '', }; diff --git a/src/cliproxy/services/variant-service.ts b/src/cliproxy/services/variant-service.ts index a2372961..5d0dde7a 100644 --- a/src/cliproxy/services/variant-service.ts +++ b/src/cliproxy/services/variant-service.ts @@ -253,7 +253,7 @@ export function updateVariant(name: string, updates: UpdateVariantOptions): Vari existing.port ); } else if (updates.model !== undefined) { - updateSettingsModel(settingsPath, updates.model); + updateSettingsModel(settingsPath, updates.model, existing.provider as CLIProxyProfileName); } } diff --git a/src/cliproxy/services/variant-settings.ts b/src/cliproxy/services/variant-settings.ts index 211ff95c..8dc6425d 100644 --- a/src/cliproxy/services/variant-settings.ts +++ b/src/cliproxy/services/variant-settings.ts @@ -44,17 +44,26 @@ function buildSettingsEnv( port: number = CLIPROXY_DEFAULT_PORT ): SettingsEnv { const baseEnv = getClaudeEnvVars(provider as CLIProxyProvider, port); + const codexSonnetModel = normalizeCodexSonnetModel(provider, model); return { ANTHROPIC_BASE_URL: baseEnv.ANTHROPIC_BASE_URL || '', ANTHROPIC_AUTH_TOKEN: baseEnv.ANTHROPIC_AUTH_TOKEN || '', ANTHROPIC_MODEL: model, ANTHROPIC_DEFAULT_OPUS_MODEL: model, - ANTHROPIC_DEFAULT_SONNET_MODEL: model, + ANTHROPIC_DEFAULT_SONNET_MODEL: codexSonnetModel, ANTHROPIC_DEFAULT_HAIKU_MODEL: baseEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL || model, }; } +function normalizeCodexSonnetModel( + provider: CLIProxyProfileName | undefined, + model: string +): string { + if (provider !== 'codex') return model; + return /-(xhigh|high|medium)$/.test(model) ? model : `${model}-high`; +} + /** * Ensure directory exists */ @@ -266,7 +275,11 @@ export function deleteSettingsFile(settingsPath: string): boolean { /** * Update model in an existing settings file */ -export function updateSettingsModel(settingsPath: string, model: string): void { +export function updateSettingsModel( + settingsPath: string, + model: string, + provider?: CLIProxyProfileName +): void { const fileName = path.basename(settingsPath); if (fileName.startsWith('composite-')) { console.log( @@ -286,9 +299,10 @@ export function updateSettingsModel(settingsPath: string, model: string): void { if (model) { settings.env = settings.env || ({} as SettingsEnv); + const codexSonnetModel = normalizeCodexSonnetModel(provider, model); settings.env.ANTHROPIC_MODEL = model; settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL = model; - settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL = model; + settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL = codexSonnetModel; } else { // Clear model settings to use defaults delete (settings.env as unknown as Record).ANTHROPIC_MODEL; diff --git a/tests/unit/cliproxy/variant-update-service.test.ts b/tests/unit/cliproxy/variant-update-service.test.ts index d23a2e4e..e75c80f7 100644 --- a/tests/unit/cliproxy/variant-update-service.test.ts +++ b/tests/unit/cliproxy/variant-update-service.test.ts @@ -107,4 +107,28 @@ cliproxy: const config = loadOrCreateUnifiedConfig(); expect(config.cliproxy?.variants?.demo?.provider).toBe('codex'); }); + + it('preserves codex sonnet alias on model-only updates', () => { + const toCodex = updateVariant('demo', { + provider: 'codex', + model: 'gpt-5.3-codex', + }); + expect(toCodex.success).toBe(true); + + const settingsPath = path.join(tmpDir, 'gemini-demo.settings.json'); + let settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')) as { + env: Record; + }; + expect(settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL).toBe('gpt-5.3-codex-high'); + + const modelOnly = updateVariant('demo', { model: 'gpt-5.3-codex' }); + expect(modelOnly.success).toBe(true); + + settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')) as { + env: Record; + }; + expect(settings.env.ANTHROPIC_MODEL).toBe('gpt-5.3-codex'); + expect(settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL).toBe('gpt-5.3-codex'); + expect(settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL).toBe('gpt-5.3-codex-high'); + }); }); diff --git a/ui/src/lib/model-catalogs.ts b/ui/src/lib/model-catalogs.ts index 87bce4bd..54b702d0 100644 --- a/ui/src/lib/model-catalogs.ts +++ b/ui/src/lib/model-catalogs.ts @@ -133,16 +133,38 @@ export const MODEL_CATALOGS: Record = { codex: { provider: 'codex', displayName: 'Codex', - defaultModel: 'gpt-5.2-codex', + defaultModel: 'gpt-5.3-codex', models: [ + { + id: 'gpt-5.3-codex', + name: 'GPT-5.3 Codex', + description: 'Full reasoning support (xhigh)', + presetMapping: { + default: 'gpt-5.3-codex', + opus: 'gpt-5.3-codex', + sonnet: 'gpt-5.3-codex-high', + haiku: 'gpt-5.3-codex-spark', + }, + }, + { + id: 'gpt-5.3-codex-spark', + name: 'GPT-5.3 Codex Spark', + description: 'Ultra-fast coding model', + presetMapping: { + default: 'gpt-5.3-codex-spark', + opus: 'gpt-5.3-codex', + sonnet: 'gpt-5.3-codex-high', + haiku: 'gpt-5.3-codex-spark', + }, + }, { id: 'gpt-5.2-codex', name: 'GPT-5.2 Codex', - description: 'Full reasoning support (xhigh)', + description: 'Previous stable Codex model', presetMapping: { default: 'gpt-5.2-codex', opus: 'gpt-5.2-codex', - sonnet: 'gpt-5.2-codex', + sonnet: 'gpt-5.2-codex-high', haiku: 'gpt-5-mini', }, }, @@ -152,7 +174,7 @@ export const MODEL_CATALOGS: Record = { description: 'Fast, capped at high reasoning (no xhigh)', presetMapping: { default: 'gpt-5-mini', - opus: 'gpt-5.2-codex', + opus: 'gpt-5.3-codex', sonnet: 'gpt-5-mini', haiku: 'gpt-5-mini', },