diff --git a/src/cliproxy/services/variant-service.ts b/src/cliproxy/services/variant-service.ts index a2372961..5915424e 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); } } diff --git a/src/cliproxy/services/variant-settings.ts b/src/cliproxy/services/variant-settings.ts index 3dd93b4d..8dc6425d 100644 --- a/src/cliproxy/services/variant-settings.ts +++ b/src/cliproxy/services/variant-settings.ts @@ -44,8 +44,7 @@ function buildSettingsEnv( port: number = CLIPROXY_DEFAULT_PORT ): SettingsEnv { const baseEnv = getClaudeEnvVars(provider as CLIProxyProvider, port); - const codexSonnetModel = - provider === 'codex' && !model.match(/-(xhigh|high|medium)$/) ? `${model}-high` : model; + const codexSonnetModel = normalizeCodexSonnetModel(provider, model); return { ANTHROPIC_BASE_URL: baseEnv.ANTHROPIC_BASE_URL || '', @@ -57,6 +56,14 @@ function buildSettingsEnv( }; } +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 */ @@ -268,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( @@ -288,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'); + }); });