refactor: decouple auto model description and configuration from releaseChannel (#27227)

Co-authored-by: David Pierce <davidapierce@google.com>
This commit is contained in:
Daniel Weis
2026-05-18 16:15:55 -04:00
committed by GitHub
parent 6973b963ae
commit 792654c88b
11 changed files with 47 additions and 94 deletions
-13
View File
@@ -1037,12 +1037,6 @@ their corresponding top-level category object in your `settings.json` file.
"auto": {
"default": "gemini-3-pro-preview",
"contexts": [
{
"condition": {
"releaseChannel": "stable"
},
"target": "gemini-2.5-pro"
},
{
"condition": {
"hasAccessToPreview": false
@@ -1186,13 +1180,6 @@ their corresponding top-level category object in your `settings.json` file.
},
"target": "gemini-2.5-pro"
},
{
"condition": {
"releaseChannel": "stable",
"requestedModels": ["auto"]
},
"target": "gemini-2.5-pro"
},
{
"condition": {
"requestedModels": ["gemini-2.5-pro", "auto-gemini-2.5"]
+4 -5
View File
@@ -22,7 +22,6 @@ import {
getDisplayString,
AuthType,
ToolConfirmationOutcome,
getChannelFromVersion,
getAutoModelDescription,
} from '@google/gemini-cli-core';
import type * as acp from '@agentclientprotocol/sdk';
@@ -272,8 +271,6 @@ export function buildAvailableModels(
const useCustomToolModel =
useGemini31 && selectedAuthType === AuthType.USE_GEMINI;
const releaseChannel = getChannelFromVersion(config.clientVersion);
// --- DYNAMIC PATH ---
if (
config.getExperimentalDynamicModelConfiguration?.() === true &&
@@ -284,7 +281,6 @@ export function buildAvailableModels(
useGemini3_1FlashLite: useGemini31FlashLite,
useCustomTools: useCustomToolModel,
hasAccessToPreview: shouldShowPreviewModels,
releaseChannel,
});
return {
@@ -298,7 +294,10 @@ export function buildAvailableModels(
{
value: GEMINI_MODEL_ALIAS_AUTO,
title: getDisplayString(GEMINI_MODEL_ALIAS_AUTO),
description: getAutoModelDescription(releaseChannel, useGemini31),
description: getAutoModelDescription(
shouldShowPreviewModels,
useGemini31,
),
},
];
@@ -34,6 +34,11 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
await importOriginal<typeof import('@google/gemini-cli-core')>();
return {
...actual,
getAutoModelDescription: (
hasAccessToPreview: boolean,
useGemini3_1?: boolean,
) =>
`Auto Model Description (preview: ${hasAccessToPreview}, 3.1: ${useGemini3_1})`,
getDisplayString: (val: string) => mockGetDisplayString(val),
logModelSlashCommand: (config: Config, event: ModelSlashCommandEvent) =>
mockLogModelSlashCommand(config, event),
+5 -13
View File
@@ -26,7 +26,6 @@ import {
AuthType,
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
isProModel,
getChannelFromVersion,
getAutoModelDescription,
} from '@google/gemini-cli-core';
import { useKeypress } from '../hooks/useKeypress.js';
@@ -66,7 +65,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
// Determine the Preferred Model (read once when the dialog opens).
const preferredModel = config?.getModel() || GEMINI_MODEL_ALIAS_AUTO;
const shouldShowPreviewModels = config?.getHasAccessToPreviewModel();
const shouldShowPreviewModels = config?.getHasAccessToPreviewModel() ?? false;
const useGemini31 = config?.getGemini31LaunchedSync?.() ?? false;
const useGemini31FlashLite =
config?.getGemini31FlashLiteLaunchedSync?.() ?? false;
@@ -122,12 +121,6 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
},
{ isActive: true },
);
const releaseChannel = useMemo(
() => getChannelFromVersion(config?.clientVersion ?? ''),
[config?.clientVersion],
);
const mainOptions = useMemo(() => {
// --- DYNAMIC PATH ---
if (
@@ -142,7 +135,6 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
useCustomTools: useCustomToolModel,
hasAccessToPreview: shouldShowPreviewModels,
hasAccessToProModel,
releaseChannel,
});
const list = allOptions
@@ -170,7 +162,10 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
{
value: GEMINI_MODEL_ALIAS_AUTO,
title: getDisplayString(GEMINI_MODEL_ALIAS_AUTO),
description: getAutoModelDescription(releaseChannel, useGemini31),
description: getAutoModelDescription(
shouldShowPreviewModels,
useGemini31,
),
key: GEMINI_MODEL_ALIAS_AUTO,
},
{
@@ -192,7 +187,6 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
useGemini31FlashLite,
useCustomToolModel,
hasAccessToProModel,
releaseChannel,
]);
const manualOptions = useMemo(() => {
@@ -209,7 +203,6 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
useCustomTools: useCustomToolModel,
hasAccessToPreview: shouldShowPreviewModels,
hasAccessToProModel,
releaseChannel,
});
return allOptions
@@ -302,7 +295,6 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
useGemini31FlashLite,
useCustomToolModel,
hasAccessToProModel,
releaseChannel,
config,
]);
@@ -220,18 +220,7 @@ describe('policyHelpers', () => {
];
testCases.forEach(
({
name,
model,
useGemini31,
hasAccess,
authType,
wrapsAround,
...rest
}) => {
const releaseChannel = (rest as Record<string, unknown>)[
'releaseChannel'
] as string | undefined;
({ name, model, useGemini31, hasAccess, authType, wrapsAround }) => {
it(`achieves parity for: ${name}`, () => {
const createBaseConfig = (dynamic: boolean) =>
createMockConfig({
@@ -241,7 +230,7 @@ describe('policyHelpers', () => {
getGemini31FlashLiteLaunchedSync: () => false,
getHasAccessToPreviewModel: () => hasAccess ?? true,
getContentGeneratorConfig: () => ({ authType }),
getReleaseChannel: () => releaseChannel ?? 'preview',
getReleaseChannel: () => 'preview',
modelConfigService: new ModelConfigService(DEFAULT_MODEL_CONFIGS),
});
@@ -86,7 +86,6 @@ export function resolvePolicyChain(
useGemini3_1: useGemini31,
useGemini3_1FlashLite: useGemini31FlashLite,
useCustomTools: useCustomToolModel,
releaseChannel: config.getReleaseChannel?.(),
};
if (resolvedModel === DEFAULT_GEMINI_FLASH_LITE_MODEL) {
@@ -467,7 +467,6 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
auto: {
default: 'gemini-3-pro-preview',
contexts: [
{ condition: { releaseChannel: 'stable' }, target: 'gemini-2.5-pro' },
{ condition: { hasAccessToPreview: false }, target: 'gemini-2.5-pro' },
{
condition: { useGemini3_1: true, useCustomTools: true },
@@ -559,10 +558,6 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
condition: { hasAccessToPreview: false },
target: 'gemini-2.5-pro',
},
{
condition: { releaseChannel: 'stable', requestedModels: ['auto'] },
target: 'gemini-2.5-pro',
},
{
condition: { requestedModels: ['gemini-2.5-pro', 'auto-gemini-2.5'] },
target: 'gemini-2.5-pro',
+21
View File
@@ -34,6 +34,7 @@ import {
isProModel,
GEMMA_4_31B_IT_MODEL,
GEMMA_4_26B_A4B_IT_MODEL,
getAutoModelDescription,
} from './models.js';
import type { Config } from './config.js';
import { ModelConfigService } from '../services/modelConfigService.js';
@@ -704,3 +705,23 @@ describe('Gemini 3.1 Config Resolution', () => {
).toBeDefined();
});
});
describe('getAutoModelDescription', () => {
it('should return Gemini 2.5 description when hasAccessToPreview is false', () => {
const desc = getAutoModelDescription(false, false);
expect(desc).toContain('gemini-2.5-pro');
expect(desc).toContain('gemini-2.5-flash');
});
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');
});
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');
});
});
+3 -9
View File
@@ -49,7 +49,6 @@ export interface IModelConfigService {
export interface ModelCapabilityContext {
readonly modelConfigService: IModelConfigService;
getExperimentalDynamicModelConfiguration(): boolean;
getReleaseChannel?(): string;
}
export const PREVIEW_GEMINI_MODEL = 'gemini-3-pro-preview';
@@ -97,16 +96,15 @@ export const DEFAULT_GEMINI_EMBEDDING_MODEL = 'gemini-embedding-001';
export const DEFAULT_THINKING_MODE = 8192;
export function getAutoModelDescription(
releaseChannel: string = 'stable',
hasAccessToPreview: boolean,
useGemini3_1: boolean = false,
) {
const isPreview = releaseChannel === 'preview';
const proModel = isPreview
const proModel = hasAccessToPreview
? useGemini3_1
? 'gemini-3.1-pro'
: 'gemini-3-pro'
: 'gemini-2.5-pro';
const flashModel = isPreview ? 'gemini-3-flash' : 'gemini-2.5-flash';
const flashModel = hasAccessToPreview ? 'gemini-3-flash' : 'gemini-2.5-flash';
return `Let Gemini CLI decide the best model for the task: ${proModel}, ${flashModel}`;
}
@@ -126,7 +124,6 @@ export function resolveModel(
useCustomToolModel: boolean = false,
hasAccessToPreview: boolean = true,
config?: ModelCapabilityContext,
releaseChannel?: string,
): string {
// Defensive check against non-string inputs at runtime
const normalizedModel = Array.isArray(requestedModel)
@@ -135,15 +132,12 @@ export function resolveModel(
? String(requestedModel ?? '').trim() || ''
: requestedModel.trim() || '';
const currentReleaseChannel = releaseChannel ?? config?.getReleaseChannel?.();
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
const resolved = config.modelConfigService.resolveModelId(normalizedModel, {
useGemini3_1,
useGemini3_1FlashLite,
useCustomTools: useCustomToolModel,
hasAccessToPreview,
releaseChannel: currentReleaseChannel,
});
if (!hasAccessToPreview && isPreviewModel(resolved, config)) {
@@ -102,7 +102,6 @@ export interface ResolutionContext {
hasAccessToPreview?: boolean;
hasAccessToProModel?: boolean;
requestedModel?: string;
releaseChannel?: string;
}
/** The requirements defined in the registry. */
@@ -113,7 +112,6 @@ export interface ResolutionCondition {
hasAccessToPreview?: boolean;
/** Matches if the current model is in this list. */
requestedModels?: string[];
releaseChannel?: string;
}
export interface ModelConfigServiceConfig {
@@ -159,7 +157,6 @@ export class ModelConfigService {
const shouldShowPreviewModels = context.hasAccessToPreview ?? false;
const useGemini31 = context.useGemini3_1 ?? false;
const useGemini31FlashLite = context.useGemini3_1FlashLite ?? false;
const releaseChannel = context.releaseChannel ?? 'stable';
const mainOptions = Object.entries(definitions)
.filter(([_, m]) => {
@@ -171,7 +168,10 @@ export class ModelConfigService {
.map(([id, m]) => {
let description = m.dialogDescription ?? '';
if (id === 'auto') {
description = getAutoModelDescription(releaseChannel, useGemini31);
description = getAutoModelDescription(
shouldShowPreviewModels,
useGemini31,
);
} else if (id === 'auto-gemini-3' && useGemini31) {
description = description.replace('gemini-3-pro', 'gemini-3.1-pro');
}
@@ -265,8 +265,6 @@ export class ModelConfigService {
!!context.requestedModel &&
value.includes(context.requestedModel)
);
case 'releaseChannel':
return value === context.releaseChannel;
default:
return false;
}
File diff suppressed because one or more lines are too long