mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-14 20:10:36 -07:00
refactor: decouple auto model description and configuration from releaseChannel (#27227)
Co-authored-by: David Pierce <davidapierce@google.com>
This commit is contained in:
@@ -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"]
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user