fix(patch): cherry-pick aa9163d to release/v0.29.5-pr-19991 [CONFLICTS] (#20039)

Co-authored-by: Sehoon Shon <sshon@google.com>
This commit is contained in:
gemini-cli-robot
2026-02-23 14:48:37 -05:00
committed by GitHub
parent aee560c2eb
commit af14d988d1
5 changed files with 113 additions and 18 deletions

View File

@@ -58,7 +58,7 @@ describe('Fallback Integration', () => {
);
});
it('should NOT fallback if config is NOT in AUTO mode', () => {
it('should fallback for Gemini 3 models even if config is NOT in AUTO mode', () => {
// 1. Config is explicitly set to Pro, not Auto
vi.spyOn(config, 'getModel').mockReturnValue(PREVIEW_GEMINI_MODEL);
@@ -71,7 +71,7 @@ describe('Fallback Integration', () => {
// 4. Apply model selection
const result = applyModelSelection(config, { model: requestedModel });
// 5. Expect it to stay on Pro (because single model chain)
expect(result.model).toBe(PREVIEW_GEMINI_MODEL);
// 5. Expect it to fallback to Flash (because Gemini 3 uses PREVIEW_CHAIN)
expect(result.model).toBe(PREVIEW_GEMINI_FLASH_MODEL);
});
});

View File

@@ -12,6 +12,8 @@ import {
} from './policyCatalog.js';
import {
DEFAULT_GEMINI_MODEL,
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
PREVIEW_GEMINI_3_1_MODEL,
PREVIEW_GEMINI_MODEL,
} from '../config/models.js';
@@ -22,6 +24,27 @@ describe('policyCatalog', () => {
expect(chain).toHaveLength(2);
});
it('returns Gemini 3.1 chain when useGemini31 is true', () => {
const chain = getModelPolicyChain({
previewEnabled: true,
useGemini31: true,
});
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_3_1_MODEL);
expect(chain).toHaveLength(2);
expect(chain[1]?.model).toBe('gemini-3-flash-preview');
});
it('returns Gemini 3.1 Custom Tools chain when useGemini31 and useCustomToolModel are true', () => {
const chain = getModelPolicyChain({
previewEnabled: true,
useGemini31: true,
useCustomToolModel: true,
});
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL);
expect(chain).toHaveLength(2);
expect(chain[1]?.model).toBe('gemini-3-flash-preview');
});
it('returns default chain when preview disabled', () => {
const chain = getModelPolicyChain({ previewEnabled: false });
expect(chain[0]?.model).toBe(DEFAULT_GEMINI_MODEL);

View File

@@ -16,6 +16,7 @@ import {
DEFAULT_GEMINI_MODEL,
PREVIEW_GEMINI_FLASH_MODEL,
PREVIEW_GEMINI_MODEL,
resolveModel,
} from '../config/models.js';
import type { UserTierId } from '../code_assist/types.js';
@@ -28,6 +29,8 @@ type PolicyConfig = Omit<ModelPolicy, 'actions' | 'stateTransitions'> & {
export interface ModelPolicyOptions {
previewEnabled: boolean;
userTier?: UserTierId;
useGemini31?: boolean;
useCustomToolModel?: boolean;
}
const DEFAULT_ACTIONS: ModelPolicyActionMap = {
@@ -56,11 +59,6 @@ const DEFAULT_CHAIN: ModelPolicyChain = [
definePolicy({ model: DEFAULT_GEMINI_FLASH_MODEL, isLastResort: true }),
];
const PREVIEW_CHAIN: ModelPolicyChain = [
definePolicy({ model: PREVIEW_GEMINI_MODEL }),
definePolicy({ model: PREVIEW_GEMINI_FLASH_MODEL, isLastResort: true }),
];
const FLASH_LITE_CHAIN: ModelPolicyChain = [
definePolicy({
model: DEFAULT_GEMINI_FLASH_LITE_MODEL,
@@ -84,7 +82,15 @@ export function getModelPolicyChain(
options: ModelPolicyOptions,
): ModelPolicyChain {
if (options.previewEnabled) {
return cloneChain(PREVIEW_CHAIN);
const previewModel = resolveModel(
PREVIEW_GEMINI_MODEL,
options.useGemini31,
options.useCustomToolModel,
);
return [
definePolicy({ model: previewModel }),
definePolicy({ model: PREVIEW_GEMINI_FLASH_MODEL, isLastResort: true }),
];
}
return cloneChain(DEFAULT_CHAIN);

View File

@@ -15,12 +15,17 @@ import type { Config } from '../config/config.js';
import {
DEFAULT_GEMINI_FLASH_LITE_MODEL,
DEFAULT_GEMINI_MODEL_AUTO,
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
PREVIEW_GEMINI_3_1_MODEL,
} from '../config/models.js';
import { AuthType } from '../core/contentGenerator.js';
const createMockConfig = (overrides: Partial<Config> = {}): Config =>
({
getUserTier: () => undefined,
getModel: () => 'gemini-2.5-pro',
getGemini31LaunchedSync: () => false,
getContentGeneratorConfig: () => ({ authType: undefined }),
...overrides,
}) as unknown as Config;
@@ -115,6 +120,40 @@ describe('policyHelpers', () => {
expect(chain[0]?.model).toBe('gemini-2.5-flash');
expect(chain[1]?.model).toBe('gemini-2.5-pro');
});
it('proactively returns Gemini 2.5 chain if Gemini 3 requested but user lacks access', () => {
const config = createMockConfig({
getModel: () => 'auto-gemini-3',
getHasAccessToPreviewModel: () => false,
});
const chain = resolvePolicyChain(config);
// Should downgrade to [Pro 2.5, Flash 2.5]
expect(chain).toHaveLength(2);
expect(chain[0]?.model).toBe('gemini-2.5-pro');
expect(chain[1]?.model).toBe('gemini-2.5-flash');
});
it('returns Gemini 3.1 Pro chain when launched and auto-gemini-3 requested', () => {
const config = createMockConfig({
getModel: () => 'auto-gemini-3',
getGemini31LaunchedSync: () => true,
});
const chain = resolvePolicyChain(config);
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_3_1_MODEL);
expect(chain[1]?.model).toBe('gemini-3-flash-preview');
});
it('returns Gemini 3.1 Pro Custom Tools chain when launched, auth is Gemini, and auto-gemini-3 requested', () => {
const config = createMockConfig({
getModel: () => 'auto-gemini-3',
getGemini31LaunchedSync: () => true,
getContentGeneratorConfig: () => ({ authType: AuthType.USE_GEMINI }),
});
const chain = resolvePolicyChain(config);
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL);
expect(chain[1]?.model).toBe('gemini-3-flash-preview');
});
});
describe('buildFallbackPolicyContext', () => {

View File

@@ -6,6 +6,7 @@
import type { GenerateContentConfig } from '@google/genai';
import type { Config } from '../config/config.js';
import { AuthType } from '../core/contentGenerator.js';
import type {
FailureKind,
FallbackAction,
@@ -24,6 +25,7 @@ import {
DEFAULT_GEMINI_MODEL,
PREVIEW_GEMINI_MODEL_AUTO,
isAutoModel,
isGemini3Model,
resolveModel,
} from '../config/models.js';
import type { ModelSelectionResult } from './modelAvailabilityService.js';
@@ -43,23 +45,48 @@ export function resolvePolicyChain(
const configuredModel = config.getModel();
let chain;
const useGemini31 = config.getGemini31LaunchedSync?.() ?? false;
const useCustomToolModel =
useGemini31 &&
config.getContentGeneratorConfig?.()?.authType === AuthType.USE_GEMINI;
const resolvedModel = resolveModel(
modelFromConfig,
config.getGemini31LaunchedSync?.() ?? false,
useGemini31,
useCustomToolModel,
);
const isAutoPreferred = preferredModel ? isAutoModel(preferredModel) : false;
const isAutoConfigured = isAutoModel(configuredModel);
const hasAccessToPreview = config.getHasAccessToPreviewModel?.() ?? true;
if (resolvedModel === DEFAULT_GEMINI_FLASH_LITE_MODEL) {
chain = getFlashLitePolicyChain();
} else if (isAutoPreferred || isAutoConfigured) {
const previewEnabled =
preferredModel === PREVIEW_GEMINI_MODEL_AUTO ||
configuredModel === PREVIEW_GEMINI_MODEL_AUTO;
chain = getModelPolicyChain({
previewEnabled,
userTier: config.getUserTier(),
});
} else if (
isGemini3Model(resolvedModel) ||
isAutoPreferred ||
isAutoConfigured
) {
if (hasAccessToPreview) {
const previewEnabled =
isGemini3Model(resolvedModel) ||
preferredModel === PREVIEW_GEMINI_MODEL_AUTO ||
configuredModel === PREVIEW_GEMINI_MODEL_AUTO;
chain = getModelPolicyChain({
previewEnabled,
userTier: config.getUserTier(),
useGemini31,
useCustomToolModel,
});
} else {
// User requested Gemini 3 but has no access. Proactively downgrade
// to the stable Gemini 2.5 chain.
return getModelPolicyChain({
previewEnabled: false,
userTier: config.getUserTier(),
useGemini31,
useCustomToolModel,
});
}
} else {
chain = createSingleModelChain(modelFromConfig);
}