From 6dbaf8438132ea03ebf3a7d3b8a88fdd69810f66 Mon Sep 17 00:00:00 2001 From: davidapierce Date: Thu, 21 May 2026 19:13:16 +0000 Subject: [PATCH] set it so none status models don't show up in the model dialog. Additionally, display full model name in getAutoModelDescription rather than by magic strings. --- .../cli/src/acp/acpSessionManager.test.ts | 21 ++++ packages/cli/src/acp/acpUtils.ts | 2 +- .../src/ui/components/ModelDialog.test.tsx | 9 +- .../cli/src/ui/components/ModelDialog.tsx | 14 +-- packages/core/src/config/models.test.ts | 95 +++++++++++++------ packages/core/src/config/models.ts | 15 ++- .../src/context/chatCompressionService.ts | 1 + 7 files changed, 109 insertions(+), 48 deletions(-) diff --git a/packages/cli/src/acp/acpSessionManager.test.ts b/packages/cli/src/acp/acpSessionManager.test.ts index 8629e3642b..562b8dd70c 100644 --- a/packages/cli/src/acp/acpSessionManager.test.ts +++ b/packages/cli/src/acp/acpSessionManager.test.ts @@ -243,6 +243,27 @@ describe('AcpSessionManager', () => { ); }); + it('should NOT include retired preview models (none) in available models', async () => { + mockConfig.getContentGeneratorConfig = vi.fn().mockReturnValue({ + apiKey: 'test-key', + }); + mockConfig.getHasAccessToPreviewModel = vi.fn().mockReturnValue(true); + mockConfig.getGemini31LaunchedSync = vi.fn().mockReturnValue(true); + mockConfig.getGemini31FlashLiteLaunchedSync = vi.fn().mockReturnValue(true); + + const response = await manager.newSession( + { + cwd: '/tmp', + mcpServers: [], + }, + {}, + ); + + const modelIds = + response.models?.availableModels?.map((m) => m.modelId) ?? []; + expect(modelIds).not.toContain('none'); + }); + it('should return modes with plan mode when plan is enabled', async () => { mockConfig.getContentGeneratorConfig = vi.fn().mockReturnValue({ apiKey: 'test-key', diff --git a/packages/cli/src/acp/acpUtils.ts b/packages/cli/src/acp/acpUtils.ts index d0c7f26074..c9735fa7c7 100644 --- a/packages/cli/src/acp/acpUtils.ts +++ b/packages/cli/src/acp/acpUtils.ts @@ -336,7 +336,7 @@ export function buildAvailableModels( }, ]; - if (useGemini31FlashLite) { + if (useGemini31FlashLite && PREVIEW_GEMINI_FLASH_LITE_MODEL !== 'none') { previewOptions.push({ value: PREVIEW_GEMINI_FLASH_LITE_MODEL, title: getDisplayString(PREVIEW_GEMINI_FLASH_LITE_MODEL), diff --git a/packages/cli/src/ui/components/ModelDialog.test.tsx b/packages/cli/src/ui/components/ModelDialog.test.tsx index 2136fdb49b..0c30dc07cf 100644 --- a/packages/cli/src/ui/components/ModelDialog.test.tsx +++ b/packages/cli/src/ui/components/ModelDialog.test.tsx @@ -47,7 +47,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => { mockModelSlashCommandEvent(model); } }, - PREVIEW_GEMINI_FLASH_LITE_MODEL: 'gemini-3.1-flash-lite', + PREVIEW_GEMINI_FLASH_LITE_MODEL: 'none', }; }); @@ -159,7 +159,7 @@ describe('', () => { // Verify order: Flash Preview -> Flash Lite (Preview/Default) -> Flash const flashPreviewIdx = output.indexOf(PREVIEW_GEMINI_FLASH_MODEL); - const flashLiteIdx = output.indexOf(PREVIEW_GEMINI_FLASH_LITE_MODEL); + const flashLiteIdx = output.indexOf(DEFAULT_GEMINI_FLASH_LITE_MODEL); const flashIdx = output.indexOf(DEFAULT_GEMINI_FLASH_MODEL); expect(flashPreviewIdx).toBeLessThan(flashLiteIdx); @@ -449,7 +449,7 @@ describe('', () => { unmount(); }); - it('shows Flash Lite Preview model regardless of tier when flag is enabled', async () => { + it('does not show Flash Lite Preview model when it is retired (none) even if flag is enabled', async () => { mockGetProModelNoAccessSync.mockReturnValue(false); mockGetProModelNoAccess.mockResolvedValue(false); mockGetHasAccessToPreviewModel.mockReturnValue(true); @@ -468,7 +468,8 @@ describe('', () => { await waitUntilReady(); const output = lastFrame(); - expect(output).toContain(PREVIEW_GEMINI_FLASH_LITE_MODEL); + expect(output).not.toContain(PREVIEW_GEMINI_FLASH_LITE_MODEL); + expect(output).toContain(DEFAULT_GEMINI_FLASH_LITE_MODEL); unmount(); }); }); diff --git a/packages/cli/src/ui/components/ModelDialog.tsx b/packages/cli/src/ui/components/ModelDialog.tsx index 750150a15f..489f75f736 100644 --- a/packages/cli/src/ui/components/ModelDialog.tsx +++ b/packages/cli/src/ui/components/ModelDialog.tsx @@ -96,7 +96,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element { PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL, PREVIEW_GEMINI_FLASH_LITE_MODEL, PREVIEW_GEMINI_FLASH_MODEL, - ]; + ].filter((m) => m !== 'none'); if (manualModels.includes(preferredModel)) { return preferredModel; } @@ -223,16 +223,16 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element { title: getDisplayString(DEFAULT_GEMINI_MODEL), key: DEFAULT_GEMINI_MODEL, }, - { - value: DEFAULT_GEMINI_FLASH_MODEL, - title: getDisplayString(DEFAULT_GEMINI_FLASH_MODEL), - key: DEFAULT_GEMINI_FLASH_MODEL, - }, { value: DEFAULT_GEMINI_FLASH_LITE_MODEL, title: getDisplayString(DEFAULT_GEMINI_FLASH_LITE_MODEL), key: DEFAULT_GEMINI_FLASH_LITE_MODEL, }, + { + value: DEFAULT_GEMINI_FLASH_MODEL, + title: getDisplayString(DEFAULT_GEMINI_FLASH_MODEL), + key: DEFAULT_GEMINI_FLASH_MODEL, + }, ]; if (showGemmaModels) { @@ -272,7 +272,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element { }, ]; - if (useGemini31FlashLite) { + if (useGemini31FlashLite && PREVIEW_GEMINI_FLASH_LITE_MODEL !== 'none') { previewOptions.push({ value: PREVIEW_GEMINI_FLASH_LITE_MODEL, title: getDisplayString(PREVIEW_GEMINI_FLASH_LITE_MODEL), diff --git a/packages/core/src/config/models.test.ts b/packages/core/src/config/models.test.ts index f02d4d898b..1d4f994d08 100644 --- a/packages/core/src/config/models.test.ts +++ b/packages/core/src/config/models.test.ts @@ -229,13 +229,29 @@ describe('Dynamic Configuration Parity', () => { }); describe('isPreviewModel', () => { - it('should return true for preview models', () => { - expect(isPreviewModel(PREVIEW_GEMINI_MODEL)).toBe(true); - expect(isPreviewModel(PREVIEW_GEMINI_3_1_MODEL)).toBe(true); - expect(isPreviewModel(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL)).toBe(true); - expect(isPreviewModel(PREVIEW_GEMINI_FLASH_MODEL)).toBe(true); + const PREVIEW_MODELS = [ + PREVIEW_GEMINI_MODEL, + PREVIEW_GEMINI_3_1_MODEL, + PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL, + PREVIEW_GEMINI_FLASH_MODEL, + PREVIEW_GEMINI_FLASH_LITE_MODEL, + ]; + + it('should return true for active preview models', () => { + for (const model of PREVIEW_MODELS) { + if (model !== 'none') { + expect(isPreviewModel(model)).toBe(true); + } + } expect(isPreviewModel(PREVIEW_GEMINI_MODEL_AUTO)).toBe(true); - expect(isPreviewModel(PREVIEW_GEMINI_FLASH_LITE_MODEL)).toBe(true); + expect(isPreviewModel(GEMINI_MODEL_ALIAS_AUTO)).toBe(true); + }); + + it('should return false if a preview model is retired (set to none)', () => { + const retiredModels = PREVIEW_MODELS.filter((m) => m === 'none'); + for (const model of retiredModels) { + expect(isPreviewModel(model)).toBe(false); + } }); it('should return false for non-preview models', () => { @@ -625,22 +641,28 @@ describe('isActiveModel', () => { expect(isActiveModel(DEFAULT_GEMINI_MODEL, true)).toBe(true); }); - it('should return true for PREVIEW_GEMINI_FLASH_LITE_MODEL only when useGemini3_1FlashLite is true, and always true for DEFAULT_GEMINI_FLASH_LITE_MODEL', () => { - expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, false, true)).toBe( - true, - ); + it('should handle PREVIEW_GEMINI_FLASH_LITE_MODEL activity correctly based on retirement status', () => { + if (PREVIEW_GEMINI_FLASH_LITE_MODEL === 'none') { + expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, false, true)).toBe( + false, + ); + expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, true, true)).toBe( + false, + ); + } else { + expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, false, true)).toBe( + true, + ); + expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, true, true)).toBe( + true, + ); + } expect(isActiveModel(DEFAULT_GEMINI_FLASH_LITE_MODEL, false, false)).toBe( true, ); - expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, true, true)).toBe( - true, - ); expect(isActiveModel(DEFAULT_GEMINI_FLASH_LITE_MODEL, true, true)).toBe( true, ); - expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, true, false)).toBe( - false, - ); expect(isActiveModel(DEFAULT_GEMINI_FLASH_LITE_MODEL, true, false)).toBe( true, ); @@ -677,9 +699,11 @@ describe('isActiveModel', () => { expect( isActiveModel(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL, false, false, false), ).toBe(false); - expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, false, false)).toBe( - false, - ); + if (PREVIEW_GEMINI_FLASH_LITE_MODEL !== 'none') { + expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, false, false)).toBe( + false, + ); + } expect(isActiveModel(DEFAULT_GEMINI_FLASH_LITE_MODEL, false, false)).toBe( true, ); @@ -707,14 +731,23 @@ describe('Gemini 3.1 Config Resolution', () => { ).toBeDefined(); }); - it('PREVIEW_GEMINI_FLASH_LITE_MODEL should resolve to chat-base-3 config (including thinkingLevel)', () => { - const resolved = modelConfigService.getResolvedConfig({ - model: PREVIEW_GEMINI_FLASH_LITE_MODEL, - isChatModel: true, - }); - expect( - resolved.generateContentConfig?.thinkingConfig?.thinkingLevel, - ).toBeDefined(); + it('PREVIEW_GEMINI_FLASH_LITE_MODEL should resolve to appropriate config based on retirement status', () => { + if (PREVIEW_GEMINI_FLASH_LITE_MODEL === 'none') { + // If none, it falls back to chat-base which may not have thinkingLevel + const resolved = modelConfigService.getResolvedConfig({ + model: PREVIEW_GEMINI_FLASH_LITE_MODEL, + isChatModel: true, + }); + expect(resolved.model).toBe(PREVIEW_GEMINI_FLASH_LITE_MODEL); + } else { + const resolved = modelConfigService.getResolvedConfig({ + model: PREVIEW_GEMINI_FLASH_LITE_MODEL, + isChatModel: true, + }); + expect( + resolved.generateContentConfig?.thinkingConfig?.thinkingLevel, + ).toBeDefined(); + } }); }); @@ -727,13 +760,13 @@ describe('getAutoModelDescription', () => { it('should return Gemini 3.0 description when hasAccessToPreview is true', () => { const desc = getAutoModelDescription(true, false); - expect(desc).toContain('gemini-3-pro'); - expect(desc).toContain('gemini-3-flash'); + expect(desc).toContain('gemini-3-pro-preview'); + expect(desc).toContain('gemini-3-flash-preview'); }); it('should return Gemini 3.1 description when hasAccessToPreview and useGemini3_1 are true', () => { const desc = getAutoModelDescription(true, true); - expect(desc).toContain('gemini-3.1-pro'); - expect(desc).toContain('gemini-3-flash'); + expect(desc).toContain('gemini-3.1-pro-preview'); + expect(desc).toContain('gemini-3-flash-preview'); }); }); diff --git a/packages/core/src/config/models.ts b/packages/core/src/config/models.ts index e645b40f3d..700c8e2222 100644 --- a/packages/core/src/config/models.ts +++ b/packages/core/src/config/models.ts @@ -101,10 +101,12 @@ export function getAutoModelDescription( ) { const proModel = hasAccessToPreview ? useGemini3_1 - ? 'gemini-3.1-pro' - : 'gemini-3-pro' - : 'gemini-2.5-pro'; - const flashModel = hasAccessToPreview ? 'gemini-3-flash' : 'gemini-2.5-flash'; + ? PREVIEW_GEMINI_3_1_MODEL + : PREVIEW_GEMINI_MODEL + : DEFAULT_GEMINI_MODEL; + const flashModel = hasAccessToPreview + ? PREVIEW_GEMINI_FLASH_MODEL + : DEFAULT_GEMINI_FLASH_MODEL; return `Let Gemini CLI decide the best model for the task: ${proModel}, ${flashModel}`; } @@ -332,6 +334,9 @@ export function isPreviewModel( model: string, config?: ModelCapabilityContext, ): boolean { + if (model === 'none') { + return false; + } if (config?.getExperimentalDynamicModelConfiguration?.() === true) { return ( config.modelConfigService.getModelDefinition(model)?.isPreview === true @@ -345,7 +350,7 @@ export function isPreviewModel( model === PREVIEW_GEMINI_FLASH_MODEL || model === PREVIEW_GEMINI_MODEL_AUTO || model === GEMINI_MODEL_ALIAS_AUTO || - (model === PREVIEW_GEMINI_FLASH_LITE_MODEL && model !== 'none') + model === PREVIEW_GEMINI_FLASH_LITE_MODEL ); } diff --git a/packages/core/src/context/chatCompressionService.ts b/packages/core/src/context/chatCompressionService.ts index d9b92abfe6..7eaa4df52a 100644 --- a/packages/core/src/context/chatCompressionService.ts +++ b/packages/core/src/context/chatCompressionService.ts @@ -107,6 +107,7 @@ export function modelStringToModelConfigAlias(model: string): string { case PREVIEW_GEMINI_FLASH_MODEL: return 'chat-compression-3-flash'; case PREVIEW_GEMINI_FLASH_LITE_MODEL: + // fallthrough case DEFAULT_GEMINI_FLASH_LITE_MODEL: return 'chat-compression-3.1-flash-lite'; case 'gemini-2.5-flash-lite':