Skip to content

Commit af14d98

Browse files
fix(patch): cherry-pick aa9163d to release/v0.29.5-pr-19991 [CONFLICTS] (#20039)
Co-authored-by: Sehoon Shon <sshon@google.com>
1 parent aee560c commit af14d98

5 files changed

Lines changed: 113 additions & 18 deletions

File tree

packages/core/src/availability/fallbackIntegration.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ describe('Fallback Integration', () => {
5858
);
5959
});
6060

61-
it('should NOT fallback if config is NOT in AUTO mode', () => {
61+
it('should fallback for Gemini 3 models even if config is NOT in AUTO mode', () => {
6262
// 1. Config is explicitly set to Pro, not Auto
6363
vi.spyOn(config, 'getModel').mockReturnValue(PREVIEW_GEMINI_MODEL);
6464

@@ -71,7 +71,7 @@ describe('Fallback Integration', () => {
7171
// 4. Apply model selection
7272
const result = applyModelSelection(config, { model: requestedModel });
7373

74-
// 5. Expect it to stay on Pro (because single model chain)
75-
expect(result.model).toBe(PREVIEW_GEMINI_MODEL);
74+
// 5. Expect it to fallback to Flash (because Gemini 3 uses PREVIEW_CHAIN)
75+
expect(result.model).toBe(PREVIEW_GEMINI_FLASH_MODEL);
7676
});
7777
});

packages/core/src/availability/policyCatalog.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
} from './policyCatalog.js';
1313
import {
1414
DEFAULT_GEMINI_MODEL,
15+
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
16+
PREVIEW_GEMINI_3_1_MODEL,
1517
PREVIEW_GEMINI_MODEL,
1618
} from '../config/models.js';
1719

@@ -22,6 +24,27 @@ describe('policyCatalog', () => {
2224
expect(chain).toHaveLength(2);
2325
});
2426

27+
it('returns Gemini 3.1 chain when useGemini31 is true', () => {
28+
const chain = getModelPolicyChain({
29+
previewEnabled: true,
30+
useGemini31: true,
31+
});
32+
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_3_1_MODEL);
33+
expect(chain).toHaveLength(2);
34+
expect(chain[1]?.model).toBe('gemini-3-flash-preview');
35+
});
36+
37+
it('returns Gemini 3.1 Custom Tools chain when useGemini31 and useCustomToolModel are true', () => {
38+
const chain = getModelPolicyChain({
39+
previewEnabled: true,
40+
useGemini31: true,
41+
useCustomToolModel: true,
42+
});
43+
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL);
44+
expect(chain).toHaveLength(2);
45+
expect(chain[1]?.model).toBe('gemini-3-flash-preview');
46+
});
47+
2548
it('returns default chain when preview disabled', () => {
2649
const chain = getModelPolicyChain({ previewEnabled: false });
2750
expect(chain[0]?.model).toBe(DEFAULT_GEMINI_MODEL);

packages/core/src/availability/policyCatalog.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
DEFAULT_GEMINI_MODEL,
1717
PREVIEW_GEMINI_FLASH_MODEL,
1818
PREVIEW_GEMINI_MODEL,
19+
resolveModel,
1920
} from '../config/models.js';
2021
import type { UserTierId } from '../code_assist/types.js';
2122

@@ -28,6 +29,8 @@ type PolicyConfig = Omit<ModelPolicy, 'actions' | 'stateTransitions'> & {
2829
export interface ModelPolicyOptions {
2930
previewEnabled: boolean;
3031
userTier?: UserTierId;
32+
useGemini31?: boolean;
33+
useCustomToolModel?: boolean;
3134
}
3235

3336
const DEFAULT_ACTIONS: ModelPolicyActionMap = {
@@ -56,11 +59,6 @@ const DEFAULT_CHAIN: ModelPolicyChain = [
5659
definePolicy({ model: DEFAULT_GEMINI_FLASH_MODEL, isLastResort: true }),
5760
];
5861

59-
const PREVIEW_CHAIN: ModelPolicyChain = [
60-
definePolicy({ model: PREVIEW_GEMINI_MODEL }),
61-
definePolicy({ model: PREVIEW_GEMINI_FLASH_MODEL, isLastResort: true }),
62-
];
63-
6462
const FLASH_LITE_CHAIN: ModelPolicyChain = [
6563
definePolicy({
6664
model: DEFAULT_GEMINI_FLASH_LITE_MODEL,
@@ -84,7 +82,15 @@ export function getModelPolicyChain(
8482
options: ModelPolicyOptions,
8583
): ModelPolicyChain {
8684
if (options.previewEnabled) {
87-
return cloneChain(PREVIEW_CHAIN);
85+
const previewModel = resolveModel(
86+
PREVIEW_GEMINI_MODEL,
87+
options.useGemini31,
88+
options.useCustomToolModel,
89+
);
90+
return [
91+
definePolicy({ model: previewModel }),
92+
definePolicy({ model: PREVIEW_GEMINI_FLASH_MODEL, isLastResort: true }),
93+
];
8894
}
8995

9096
return cloneChain(DEFAULT_CHAIN);

packages/core/src/availability/policyHelpers.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,17 @@ import type { Config } from '../config/config.js';
1515
import {
1616
DEFAULT_GEMINI_FLASH_LITE_MODEL,
1717
DEFAULT_GEMINI_MODEL_AUTO,
18+
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
19+
PREVIEW_GEMINI_3_1_MODEL,
1820
} from '../config/models.js';
21+
import { AuthType } from '../core/contentGenerator.js';
1922

2023
const createMockConfig = (overrides: Partial<Config> = {}): Config =>
2124
({
2225
getUserTier: () => undefined,
2326
getModel: () => 'gemini-2.5-pro',
27+
getGemini31LaunchedSync: () => false,
28+
getContentGeneratorConfig: () => ({ authType: undefined }),
2429
...overrides,
2530
}) as unknown as Config;
2631

@@ -115,6 +120,40 @@ describe('policyHelpers', () => {
115120
expect(chain[0]?.model).toBe('gemini-2.5-flash');
116121
expect(chain[1]?.model).toBe('gemini-2.5-pro');
117122
});
123+
124+
it('proactively returns Gemini 2.5 chain if Gemini 3 requested but user lacks access', () => {
125+
const config = createMockConfig({
126+
getModel: () => 'auto-gemini-3',
127+
getHasAccessToPreviewModel: () => false,
128+
});
129+
const chain = resolvePolicyChain(config);
130+
131+
// Should downgrade to [Pro 2.5, Flash 2.5]
132+
expect(chain).toHaveLength(2);
133+
expect(chain[0]?.model).toBe('gemini-2.5-pro');
134+
expect(chain[1]?.model).toBe('gemini-2.5-flash');
135+
});
136+
137+
it('returns Gemini 3.1 Pro chain when launched and auto-gemini-3 requested', () => {
138+
const config = createMockConfig({
139+
getModel: () => 'auto-gemini-3',
140+
getGemini31LaunchedSync: () => true,
141+
});
142+
const chain = resolvePolicyChain(config);
143+
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_3_1_MODEL);
144+
expect(chain[1]?.model).toBe('gemini-3-flash-preview');
145+
});
146+
147+
it('returns Gemini 3.1 Pro Custom Tools chain when launched, auth is Gemini, and auto-gemini-3 requested', () => {
148+
const config = createMockConfig({
149+
getModel: () => 'auto-gemini-3',
150+
getGemini31LaunchedSync: () => true,
151+
getContentGeneratorConfig: () => ({ authType: AuthType.USE_GEMINI }),
152+
});
153+
const chain = resolvePolicyChain(config);
154+
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL);
155+
expect(chain[1]?.model).toBe('gemini-3-flash-preview');
156+
});
118157
});
119158

120159
describe('buildFallbackPolicyContext', () => {

packages/core/src/availability/policyHelpers.ts

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import type { GenerateContentConfig } from '@google/genai';
88
import type { Config } from '../config/config.js';
9+
import { AuthType } from '../core/contentGenerator.js';
910
import type {
1011
FailureKind,
1112
FallbackAction,
@@ -24,6 +25,7 @@ import {
2425
DEFAULT_GEMINI_MODEL,
2526
PREVIEW_GEMINI_MODEL_AUTO,
2627
isAutoModel,
28+
isGemini3Model,
2729
resolveModel,
2830
} from '../config/models.js';
2931
import type { ModelSelectionResult } from './modelAvailabilityService.js';
@@ -43,23 +45,48 @@ export function resolvePolicyChain(
4345
const configuredModel = config.getModel();
4446

4547
let chain;
48+
const useGemini31 = config.getGemini31LaunchedSync?.() ?? false;
49+
const useCustomToolModel =
50+
useGemini31 &&
51+
config.getContentGeneratorConfig?.()?.authType === AuthType.USE_GEMINI;
52+
4653
const resolvedModel = resolveModel(
4754
modelFromConfig,
48-
config.getGemini31LaunchedSync?.() ?? false,
55+
useGemini31,
56+
useCustomToolModel,
4957
);
5058
const isAutoPreferred = preferredModel ? isAutoModel(preferredModel) : false;
5159
const isAutoConfigured = isAutoModel(configuredModel);
60+
const hasAccessToPreview = config.getHasAccessToPreviewModel?.() ?? true;
5261

5362
if (resolvedModel === DEFAULT_GEMINI_FLASH_LITE_MODEL) {
5463
chain = getFlashLitePolicyChain();
55-
} else if (isAutoPreferred || isAutoConfigured) {
56-
const previewEnabled =
57-
preferredModel === PREVIEW_GEMINI_MODEL_AUTO ||
58-
configuredModel === PREVIEW_GEMINI_MODEL_AUTO;
59-
chain = getModelPolicyChain({
60-
previewEnabled,
61-
userTier: config.getUserTier(),
62-
});
64+
} else if (
65+
isGemini3Model(resolvedModel) ||
66+
isAutoPreferred ||
67+
isAutoConfigured
68+
) {
69+
if (hasAccessToPreview) {
70+
const previewEnabled =
71+
isGemini3Model(resolvedModel) ||
72+
preferredModel === PREVIEW_GEMINI_MODEL_AUTO ||
73+
configuredModel === PREVIEW_GEMINI_MODEL_AUTO;
74+
chain = getModelPolicyChain({
75+
previewEnabled,
76+
userTier: config.getUserTier(),
77+
useGemini31,
78+
useCustomToolModel,
79+
});
80+
} else {
81+
// User requested Gemini 3 but has no access. Proactively downgrade
82+
// to the stable Gemini 2.5 chain.
83+
return getModelPolicyChain({
84+
previewEnabled: false,
85+
userTier: config.getUserTier(),
86+
useGemini31,
87+
useCustomToolModel,
88+
});
89+
}
6390
} else {
6491
chain = createSingleModelChain(modelFromConfig);
6592
}

0 commit comments

Comments
 (0)