fix(agy): remove deprecated 4.5 selector options

This commit is contained in:
Tam Nhu Tran
2026-03-03 03:10:53 +07:00
parent dbe8f6f68c
commit 5913f730ac
6 changed files with 88 additions and 123 deletions
-30
View File
@@ -91,18 +91,6 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
// extendedContext: true,
extendedContext: false,
},
{
id: 'claude-opus-4-5-thinking',
name: 'Claude Opus 4.5 Thinking',
description: 'Previous flagship, extended thinking',
thinking: {
type: 'budget',
min: 1024,
max: 100000,
zeroAllowed: false,
dynamicAllowed: true,
},
},
{
id: 'claude-sonnet-4-6',
name: 'Claude Sonnet 4.6',
@@ -115,24 +103,6 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
dynamicAllowed: true,
},
},
{
id: 'claude-sonnet-4-5-thinking',
name: 'Claude Sonnet 4.5 Thinking',
description: 'Balanced with extended thinking',
thinking: {
type: 'budget',
min: 1024,
max: 100000,
zeroAllowed: false,
dynamicAllowed: true,
},
},
{
id: 'claude-sonnet-4-5',
name: 'Claude Sonnet 4.5',
description: 'Fast and capable',
thinking: { type: 'none' },
},
{
id: 'gemini-3-pro-preview',
name: 'Gemini 3 Pro',
+17 -4
View File
@@ -23,6 +23,12 @@ const CLAUDE_DOTTED_THINKING_REGEX =
const DEPRECATED_ANTIGRAVITY_SONNET_46_THINKING_REGEX =
/claude-sonnet-4(?:[.-])6-thinking(?=(?:$|-|\[|\(|\/))/gi;
const CANONICAL_ANTIGRAVITY_SONNET_46_MODEL = 'claude-sonnet-4-6';
const DEPRECATED_ANTIGRAVITY_OPUS_45_THINKING_REGEX =
/claude-opus-4(?:[.-])5-thinking(?=(?:$|-|\[|\(|\/))/gi;
const CANONICAL_ANTIGRAVITY_OPUS_46_THINKING_MODEL = 'claude-opus-4-6-thinking';
const DEPRECATED_ANTIGRAVITY_SONNET_45_THINKING_REGEX =
/claude-sonnet-4(?:[.-])5-thinking(?=(?:$|-|\[|\(|\/))/gi;
const DEPRECATED_ANTIGRAVITY_SONNET_45_REGEX = /claude-sonnet-4(?:[.-])5(?=(?:$|-|\[|\(|\/))/gi;
const CODEX_EFFORT_SUFFIX_REGEX = /-(xhigh|high|medium)$/i;
function trimModelId(model: string): string {
@@ -86,10 +92,17 @@ export function normalizeClaudeDottedThinkingMajorMinor(model: string): string {
* Canonicalize legacy aliases to `claude-sonnet-4-6` while preserving suffixes.
*/
export function normalizeDeprecatedAntigravityModelAliases(model: string): string {
return model.replace(
DEPRECATED_ANTIGRAVITY_SONNET_46_THINKING_REGEX,
CANONICAL_ANTIGRAVITY_SONNET_46_MODEL
);
return model
.replace(
DEPRECATED_ANTIGRAVITY_OPUS_45_THINKING_REGEX,
CANONICAL_ANTIGRAVITY_OPUS_46_THINKING_MODEL
)
.replace(DEPRECATED_ANTIGRAVITY_SONNET_45_THINKING_REGEX, CANONICAL_ANTIGRAVITY_SONNET_46_MODEL)
.replace(DEPRECATED_ANTIGRAVITY_SONNET_45_REGEX, CANONICAL_ANTIGRAVITY_SONNET_46_MODEL)
.replace(
DEPRECATED_ANTIGRAVITY_SONNET_46_THINKING_REGEX,
CANONICAL_ANTIGRAVITY_SONNET_46_MODEL
);
}
/**
@@ -3,16 +3,28 @@ import { findModel, supportsThinking } from '../../../src/cliproxy/model-catalog
describe('model-catalog compatibility lookups', () => {
it('finds agy Claude models using dotted major.minor IDs', () => {
const dottedThinking = findModel('agy', 'claude-sonnet-4.5-thinking');
const dottedNonThinking = findModel('agy', 'claude-sonnet-4.5');
const dottedThinking = findModel('agy', 'claude-opus-4.6-thinking');
const dottedNonThinking = findModel('agy', 'claude-sonnet-4.6');
expect(dottedThinking?.id).toBe('claude-sonnet-4-5-thinking');
expect(dottedNonThinking?.id).toBe('claude-sonnet-4-5');
expect(dottedThinking?.id).toBe('claude-opus-4-6-thinking');
expect(dottedNonThinking?.id).toBe('claude-sonnet-4-6');
});
it('maps legacy agy 4.5 IDs to canonical 4.6 catalog models', () => {
const legacyOpusThinking = findModel('agy', 'claude-opus-4.5-thinking');
const legacySonnetThinking = findModel('agy', 'claude-sonnet-4.5-thinking');
const legacySonnet = findModel('agy', 'claude-sonnet-4.5');
expect(legacyOpusThinking?.id).toBe('claude-opus-4-6-thinking');
expect(legacySonnetThinking?.id).toBe('claude-sonnet-4-6');
expect(legacySonnet?.id).toBe('claude-sonnet-4-6');
});
it('supports thinking checks for dotted agy model IDs', () => {
expect(supportsThinking('agy', 'claude-opus-4.6-thinking')).toBe(true);
expect(supportsThinking('agy', 'claude-sonnet-4.5')).toBe(false);
expect(supportsThinking('agy', 'claude-sonnet-4.6')).toBe(true);
expect(supportsThinking('agy', 'claude-opus-4.5-thinking')).toBe(true);
expect(supportsThinking('agy', 'claude-sonnet-4.5')).toBe(true);
});
it('maps legacy sonnet 4.6 thinking aliases to canonical agy model', () => {
+43 -47
View File
@@ -69,20 +69,11 @@ describe('Model Catalog', () => {
assert.strictEqual(MODEL_CATALOG.agy.defaultModel, 'claude-opus-4-6-thinking');
});
it('includes Claude Opus 4.5 Thinking', () => {
it('includes Claude Opus 4.6 Thinking', () => {
const { MODEL_CATALOG } = modelCatalog;
const opus = MODEL_CATALOG.agy.models.find((m) => m.id === 'claude-opus-4-5-thinking');
assert(opus, 'Should include Claude Opus 4.5 Thinking');
assert.strictEqual(opus.name, 'Claude Opus 4.5 Thinking');
});
it('includes Claude Sonnet 4.5 Thinking', () => {
const { MODEL_CATALOG } = modelCatalog;
const sonnetThinking = MODEL_CATALOG.agy.models.find(
(m) => m.id === 'claude-sonnet-4-5-thinking'
);
assert(sonnetThinking, 'Should include Claude Sonnet 4.5 Thinking');
assert.strictEqual(sonnetThinking.name, 'Claude Sonnet 4.5 Thinking');
const opus = MODEL_CATALOG.agy.models.find((m) => m.id === 'claude-opus-4-6-thinking');
assert(opus, 'Should include Claude Opus 4.6 Thinking');
assert.strictEqual(opus.name, 'Claude Opus 4.6 Thinking');
});
it('includes Claude Sonnet 4.6', () => {
@@ -92,11 +83,12 @@ describe('Model Catalog', () => {
assert.strictEqual(sonnet.name, 'Claude Sonnet 4.6');
});
it('includes Claude Sonnet 4.5', () => {
it('does not include deprecated Claude 4.5 AGY models', () => {
const { MODEL_CATALOG } = modelCatalog;
const sonnet = MODEL_CATALOG.agy.models.find((m) => m.id === 'claude-sonnet-4-5');
assert(sonnet, 'Should include Claude Sonnet 4.5');
assert.strictEqual(sonnet.name, 'Claude Sonnet 4.5');
const ids = MODEL_CATALOG.agy.models.map((m) => m.id);
assert.strictEqual(ids.includes('claude-opus-4-5-thinking'), false);
assert.strictEqual(ids.includes('claude-sonnet-4-5-thinking'), false);
assert.strictEqual(ids.includes('claude-sonnet-4-5'), false);
});
it('includes Gemini 3 Pro (free via Antigravity)', () => {
@@ -108,9 +100,9 @@ describe('Model Catalog', () => {
assert.strictEqual(gem3.tier, undefined, 'AGY models should not have paid tier');
});
it('has 6 models total', () => {
it('has 3 models total', () => {
const { MODEL_CATALOG } = modelCatalog;
assert.strictEqual(MODEL_CATALOG.agy.models.length, 6);
assert.strictEqual(MODEL_CATALOG.agy.models.length, 3);
});
});
@@ -203,11 +195,11 @@ describe('Model Catalog', () => {
});
describe('findModel', () => {
it('finds Claude Opus 4.5 Thinking in agy', () => {
it('finds Claude Opus 4.6 Thinking in agy', () => {
const { findModel } = modelCatalog;
const model = findModel('agy', 'claude-opus-4-5-thinking');
const model = findModel('agy', 'claude-opus-4-6-thinking');
assert(model, 'Should find model');
assert.strictEqual(model.name, 'Claude Opus 4.5 Thinking');
assert.strictEqual(model.name, 'Claude Opus 4.6 Thinking');
});
it('finds Gemini 2.5 Pro in gemini', () => {
@@ -217,6 +209,16 @@ describe('Model Catalog', () => {
assert.strictEqual(model.name, 'Gemini 2.5 Pro');
});
it('maps legacy AGY 4.5 IDs to supported 4.6 models', () => {
const { findModel } = modelCatalog;
const legacyOpus = findModel('agy', 'claude-opus-4-5-thinking');
const legacySonnetThinking = findModel('agy', 'claude-sonnet-4-5-thinking');
const legacySonnet = findModel('agy', 'claude-sonnet-4-5');
assert.strictEqual(legacyOpus?.id, 'claude-opus-4-6-thinking');
assert.strictEqual(legacySonnetThinking?.id, 'claude-sonnet-4-6');
assert.strictEqual(legacySonnet?.id, 'claude-sonnet-4-6');
});
it('returns undefined for unknown model', () => {
const { findModel } = modelCatalog;
const model = findModel('agy', 'unknown-model');
@@ -269,19 +271,17 @@ describe('Model Catalog', () => {
});
describe('Thinking models ordering', () => {
it('Claude Opus 4.5 Thinking is not deprecated', () => {
it('Claude Opus 4.6 Thinking is not deprecated', () => {
const { MODEL_CATALOG } = modelCatalog;
const opus = MODEL_CATALOG.agy.models.find((m) => m.id === 'claude-opus-4-5-thinking');
assert(opus, 'Should include Claude Opus 4.5 Thinking');
const opus = MODEL_CATALOG.agy.models.find((m) => m.id === 'claude-opus-4-6-thinking');
assert(opus, 'Should include Claude Opus 4.6 Thinking');
assert.strictEqual(opus.deprecated, undefined, 'Should not be marked as deprecated');
});
it('Claude Sonnet 4.5 Thinking is not deprecated', () => {
it('Claude Sonnet 4.6 is not deprecated', () => {
const { MODEL_CATALOG } = modelCatalog;
const sonnetThinking = MODEL_CATALOG.agy.models.find(
(m) => m.id === 'claude-sonnet-4-5-thinking'
);
assert(sonnetThinking, 'Should include Claude Sonnet 4.5 Thinking');
const sonnetThinking = MODEL_CATALOG.agy.models.find((m) => m.id === 'claude-sonnet-4-6');
assert(sonnetThinking, 'Should include Claude Sonnet 4.6');
assert.strictEqual(
sonnetThinking.deprecated,
undefined,
@@ -294,31 +294,27 @@ describe('Model Catalog', () => {
const models = MODEL_CATALOG.agy.models;
// Find indices of thinking models
const opusIdx = models.findIndex((m) => m.id === 'claude-opus-4-5-thinking');
const sonnetThinkingIdx = models.findIndex((m) => m.id === 'claude-sonnet-4-5-thinking');
const opusIdx = models.findIndex((m) => m.id === 'claude-opus-4-6-thinking');
const sonnetThinkingIdx = models.findIndex((m) => m.id === 'claude-sonnet-4-6');
// Find indices of non-thinking models
const sonnetIdx = models.findIndex((m) => m.id === 'claude-sonnet-4-5');
// Find indices of the remaining non-Claude model
const geminiIdx = models.findIndex((m) => m.id === 'gemini-3-pro-preview');
// Thinking models should come before non-thinking models
assert(opusIdx < sonnetIdx, 'Opus Thinking should be above non-thinking Sonnet');
assert(opusIdx < geminiIdx, 'Opus Thinking should be above non-thinking Gemini');
assert(sonnetThinkingIdx < sonnetIdx, 'Sonnet Thinking should be above non-thinking Sonnet');
assert(sonnetThinkingIdx < geminiIdx, 'Sonnet Thinking should be above non-thinking Gemini');
// Primary Claude choices should appear ahead of Gemini fallback.
assert(opusIdx < geminiIdx, 'Opus should be above Gemini');
assert(sonnetThinkingIdx < geminiIdx, 'Sonnet should be above Gemini');
});
});
describe('isModelDeprecated', () => {
it('returns false for thinking models (no longer deprecated)', () => {
it('returns false for active AGY Claude models', () => {
const { isModelDeprecated } = modelCatalog;
assert.strictEqual(isModelDeprecated('agy', 'claude-opus-4-5-thinking'), false);
assert.strictEqual(isModelDeprecated('agy', 'claude-sonnet-4-5-thinking'), false);
assert.strictEqual(isModelDeprecated('agy', 'claude-opus-4-6-thinking'), false);
assert.strictEqual(isModelDeprecated('agy', 'claude-sonnet-4-6'), false);
});
it('returns false for non-deprecated models', () => {
const { isModelDeprecated } = modelCatalog;
assert.strictEqual(isModelDeprecated('agy', 'claude-sonnet-4-5'), false);
assert.strictEqual(isModelDeprecated('agy', 'gemini-3-pro-preview'), false);
});
@@ -329,15 +325,15 @@ describe('Model Catalog', () => {
});
describe('getModelDeprecationReason', () => {
it('returns undefined for thinking models (no longer deprecated)', () => {
it('returns undefined for active AGY Claude models', () => {
const { getModelDeprecationReason } = modelCatalog;
assert.strictEqual(getModelDeprecationReason('agy', 'claude-opus-4-5-thinking'), undefined);
assert.strictEqual(getModelDeprecationReason('agy', 'claude-sonnet-4-5-thinking'), undefined);
assert.strictEqual(getModelDeprecationReason('agy', 'claude-opus-4-6-thinking'), undefined);
assert.strictEqual(getModelDeprecationReason('agy', 'claude-sonnet-4-6'), undefined);
});
it('returns undefined for non-deprecated models', () => {
const { getModelDeprecationReason } = modelCatalog;
assert.strictEqual(getModelDeprecationReason('agy', 'claude-sonnet-4-5'), undefined);
assert.strictEqual(getModelDeprecationReason('agy', 'gemini-3-pro-preview'), undefined);
});
});
});
@@ -64,6 +64,13 @@ describe('model-id-normalizer', () => {
expect(normalizeModelIdForProvider('claude-opus-4.6-thinking', 'agy')).toBe(
'claude-opus-4-6-thinking'
);
expect(normalizeModelIdForProvider('claude-opus-4.5-thinking', 'agy')).toBe(
'claude-opus-4-6-thinking'
);
expect(normalizeModelIdForProvider('claude-sonnet-4.5-thinking', 'agy')).toBe(
'claude-sonnet-4-6'
);
expect(normalizeModelIdForProvider('claude-sonnet-4.5', 'agy')).toBe('claude-sonnet-4-6');
expect(normalizeModelIdForProvider('claude-opus-4.6-thinking', 'gemini')).toBe(
'claude-opus-4.6-thinking'
);
@@ -98,9 +105,9 @@ describe('model-id-normalizer', () => {
describe('env normalization', () => {
it('normalizes model env vars for antigravity only', () => {
const input: NodeJS.ProcessEnv = {
ANTHROPIC_MODEL: 'claude-sonnet-4.6-thinking',
ANTHROPIC_DEFAULT_OPUS_MODEL: 'claude-opus-4.6-thinking',
ANTHROPIC_DEFAULT_SONNET_MODEL: 'claude-sonnet-4.6',
ANTHROPIC_MODEL: 'claude-sonnet-4.5-thinking',
ANTHROPIC_DEFAULT_OPUS_MODEL: 'claude-opus-4.5-thinking',
ANTHROPIC_DEFAULT_SONNET_MODEL: 'claude-sonnet-4.5',
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'claude-haiku-4.5',
UNRELATED: 'keep-me',
};
@@ -113,7 +120,7 @@ describe('model-id-normalizer', () => {
expect(normalized.UNRELATED).toBe('keep-me');
const unchanged = normalizeModelEnvVarsForProvider(input, 'gemini');
expect(unchanged.ANTHROPIC_MODEL).toBe('claude-sonnet-4.6-thinking');
expect(unchanged.ANTHROPIC_MODEL).toBe('claude-sonnet-4.5-thinking');
expect(unchanged.UNRELATED).toBe('keep-me');
});
});
-33
View File
@@ -26,17 +26,6 @@ export const MODEL_CATALOGS: Record<string, ProviderCatalog> = {
haiku: 'claude-sonnet-4-6',
},
},
{
id: 'claude-opus-4-5-thinking',
name: 'Claude Opus 4.5 Thinking',
description: 'Previous flagship, extended thinking',
presetMapping: {
default: 'claude-opus-4-5-thinking',
opus: 'claude-opus-4-5-thinking',
sonnet: 'claude-sonnet-4-6',
haiku: 'claude-sonnet-4-6',
},
},
{
id: 'claude-sonnet-4-6',
name: 'Claude Sonnet 4.6',
@@ -48,28 +37,6 @@ export const MODEL_CATALOGS: Record<string, ProviderCatalog> = {
haiku: 'claude-sonnet-4-6',
},
},
{
id: 'claude-sonnet-4-5-thinking',
name: 'Claude Sonnet 4.5 Thinking',
description: 'Balanced with extended thinking',
presetMapping: {
default: 'claude-sonnet-4-5-thinking',
opus: 'claude-opus-4-6-thinking',
sonnet: 'claude-sonnet-4-5-thinking',
haiku: 'claude-sonnet-4-5',
},
},
{
id: 'claude-sonnet-4-5',
name: 'Claude Sonnet 4.5',
description: 'Fast and capable',
presetMapping: {
default: 'claude-sonnet-4-5',
opus: 'claude-opus-4-6-thinking',
sonnet: 'claude-sonnet-4-5',
haiku: 'claude-sonnet-4-5',
},
},
{
id: 'gemini-3-pro-preview',
name: 'Gemini 3 Pro',