mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 14:10:37 -07:00
feat: Add flash lite utility fallback chain (#17056)
This commit is contained in:
@@ -17,6 +17,11 @@ policies.
|
|||||||
may prompt you to switch to a fallback model (by default always prompts
|
may prompt you to switch to a fallback model (by default always prompts
|
||||||
you).
|
you).
|
||||||
|
|
||||||
|
Some internal utility calls (such as prompt completion and classification)
|
||||||
|
use a silent fallback chain for `gemini-2.5-flash-lite` and will fall back
|
||||||
|
to `gemini-2.5-flash` and `gemini-2.5-pro` without prompting or changing the
|
||||||
|
configured model.
|
||||||
|
|
||||||
3. **Model switch:** If approved, or if the policy allows for silent fallback,
|
3. **Model switch:** If approved, or if the policy allows for silent fallback,
|
||||||
the CLI will use an available fallback model for the current turn or the
|
the CLI will use an available fallback model for the current turn or the
|
||||||
remainder of the session.
|
remainder of the session.
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ If you are using the default "pro" model and the CLI detects that you are being
|
|||||||
rate-limited, it automatically switches to the "flash" model for the current
|
rate-limited, it automatically switches to the "flash" model for the current
|
||||||
session. This allows you to continue working without interruption.
|
session. This allows you to continue working without interruption.
|
||||||
|
|
||||||
|
Internal utility calls that use `gemini-2.5-flash-lite` (for example, prompt
|
||||||
|
completion and classification) silently fall back to `gemini-2.5-flash` and
|
||||||
|
`gemini-2.5-pro` when quota is exhausted, without changing the configured model.
|
||||||
|
|
||||||
## File discovery service
|
## File discovery service
|
||||||
|
|
||||||
The file discovery service is responsible for finding files in the project that
|
The file discovery service is responsible for finding files in the project that
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import type {
|
|||||||
ModelPolicyStateMap,
|
ModelPolicyStateMap,
|
||||||
} from './modelPolicy.js';
|
} from './modelPolicy.js';
|
||||||
import {
|
import {
|
||||||
|
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||||
DEFAULT_GEMINI_FLASH_MODEL,
|
DEFAULT_GEMINI_FLASH_MODEL,
|
||||||
DEFAULT_GEMINI_MODEL,
|
DEFAULT_GEMINI_MODEL,
|
||||||
PREVIEW_GEMINI_FLASH_MODEL,
|
PREVIEW_GEMINI_FLASH_MODEL,
|
||||||
@@ -36,6 +37,13 @@ const DEFAULT_ACTIONS: ModelPolicyActionMap = {
|
|||||||
unknown: 'prompt',
|
unknown: 'prompt',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SILENT_ACTIONS: ModelPolicyActionMap = {
|
||||||
|
terminal: 'silent',
|
||||||
|
transient: 'silent',
|
||||||
|
not_found: 'silent',
|
||||||
|
unknown: 'silent',
|
||||||
|
};
|
||||||
|
|
||||||
const DEFAULT_STATE: ModelPolicyStateMap = {
|
const DEFAULT_STATE: ModelPolicyStateMap = {
|
||||||
terminal: 'terminal',
|
terminal: 'terminal',
|
||||||
transient: 'terminal',
|
transient: 'terminal',
|
||||||
@@ -53,6 +61,22 @@ const PREVIEW_CHAIN: ModelPolicyChain = [
|
|||||||
definePolicy({ model: PREVIEW_GEMINI_FLASH_MODEL, isLastResort: true }),
|
definePolicy({ model: PREVIEW_GEMINI_FLASH_MODEL, isLastResort: true }),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const FLASH_LITE_CHAIN: ModelPolicyChain = [
|
||||||
|
definePolicy({
|
||||||
|
model: DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||||
|
actions: SILENT_ACTIONS,
|
||||||
|
}),
|
||||||
|
definePolicy({
|
||||||
|
model: DEFAULT_GEMINI_FLASH_MODEL,
|
||||||
|
actions: SILENT_ACTIONS,
|
||||||
|
}),
|
||||||
|
definePolicy({
|
||||||
|
model: DEFAULT_GEMINI_MODEL,
|
||||||
|
isLastResort: true,
|
||||||
|
actions: SILENT_ACTIONS,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default ordered model policy chain for the user.
|
* Returns the default ordered model policy chain for the user.
|
||||||
*/
|
*/
|
||||||
@@ -70,6 +94,10 @@ export function createSingleModelChain(model: string): ModelPolicyChain {
|
|||||||
return [definePolicy({ model, isLastResort: true })];
|
return [definePolicy({ model, isLastResort: true })];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getFlashLitePolicyChain(): ModelPolicyChain {
|
||||||
|
return cloneChain(FLASH_LITE_CHAIN);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a default policy scaffold for models not present in the catalog.
|
* Provides a default policy scaffold for models not present in the catalog.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ import {
|
|||||||
} from './policyHelpers.js';
|
} from './policyHelpers.js';
|
||||||
import { createDefaultPolicy } from './policyCatalog.js';
|
import { createDefaultPolicy } from './policyCatalog.js';
|
||||||
import type { Config } from '../config/config.js';
|
import type { Config } from '../config/config.js';
|
||||||
import { DEFAULT_GEMINI_MODEL_AUTO } from '../config/models.js';
|
import {
|
||||||
|
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||||
|
DEFAULT_GEMINI_MODEL_AUTO,
|
||||||
|
} from '../config/models.js';
|
||||||
|
|
||||||
const createMockConfig = (overrides: Partial<Config> = {}): Config =>
|
const createMockConfig = (overrides: Partial<Config> = {}): Config =>
|
||||||
({
|
({
|
||||||
@@ -53,6 +56,26 @@ describe('policyHelpers', () => {
|
|||||||
expect(chain[1]?.model).toBe('gemini-2.5-flash');
|
expect(chain[1]?.model).toBe('gemini-2.5-flash');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses auto chain when preferred model is auto', () => {
|
||||||
|
const config = createMockConfig({
|
||||||
|
getModel: () => 'gemini-2.5-pro',
|
||||||
|
});
|
||||||
|
const chain = resolvePolicyChain(config, DEFAULT_GEMINI_MODEL_AUTO);
|
||||||
|
expect(chain).toHaveLength(2);
|
||||||
|
expect(chain[0]?.model).toBe('gemini-2.5-pro');
|
||||||
|
expect(chain[1]?.model).toBe('gemini-2.5-flash');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses auto chain when configured model is auto even if preferred is concrete', () => {
|
||||||
|
const config = createMockConfig({
|
||||||
|
getModel: () => DEFAULT_GEMINI_MODEL_AUTO,
|
||||||
|
});
|
||||||
|
const chain = resolvePolicyChain(config, 'gemini-2.5-pro');
|
||||||
|
expect(chain).toHaveLength(2);
|
||||||
|
expect(chain[0]?.model).toBe('gemini-2.5-pro');
|
||||||
|
expect(chain[1]?.model).toBe('gemini-2.5-flash');
|
||||||
|
});
|
||||||
|
|
||||||
it('starts chain from preferredModel when model is "auto"', () => {
|
it('starts chain from preferredModel when model is "auto"', () => {
|
||||||
const config = createMockConfig({
|
const config = createMockConfig({
|
||||||
getModel: () => DEFAULT_GEMINI_MODEL_AUTO,
|
getModel: () => DEFAULT_GEMINI_MODEL_AUTO,
|
||||||
@@ -62,6 +85,28 @@ describe('policyHelpers', () => {
|
|||||||
expect(chain[0]?.model).toBe('gemini-2.5-flash');
|
expect(chain[0]?.model).toBe('gemini-2.5-flash');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns flash-lite chain when preferred model is flash-lite', () => {
|
||||||
|
const config = createMockConfig({
|
||||||
|
getModel: () => DEFAULT_GEMINI_MODEL_AUTO,
|
||||||
|
});
|
||||||
|
const chain = resolvePolicyChain(config, DEFAULT_GEMINI_FLASH_LITE_MODEL);
|
||||||
|
expect(chain).toHaveLength(3);
|
||||||
|
expect(chain[0]?.model).toBe('gemini-2.5-flash-lite');
|
||||||
|
expect(chain[1]?.model).toBe('gemini-2.5-flash');
|
||||||
|
expect(chain[2]?.model).toBe('gemini-2.5-pro');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns flash-lite chain when configured model is flash-lite', () => {
|
||||||
|
const config = createMockConfig({
|
||||||
|
getModel: () => DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||||
|
});
|
||||||
|
const chain = resolvePolicyChain(config);
|
||||||
|
expect(chain).toHaveLength(3);
|
||||||
|
expect(chain[0]?.model).toBe('gemini-2.5-flash-lite');
|
||||||
|
expect(chain[1]?.model).toBe('gemini-2.5-flash');
|
||||||
|
expect(chain[2]?.model).toBe('gemini-2.5-pro');
|
||||||
|
});
|
||||||
|
|
||||||
it('wraps around the chain when wrapsAround is true', () => {
|
it('wraps around the chain when wrapsAround is true', () => {
|
||||||
const config = createMockConfig({
|
const config = createMockConfig({
|
||||||
getModel: () => DEFAULT_GEMINI_MODEL_AUTO,
|
getModel: () => DEFAULT_GEMINI_MODEL_AUTO,
|
||||||
|
|||||||
@@ -17,11 +17,13 @@ import {
|
|||||||
createDefaultPolicy,
|
createDefaultPolicy,
|
||||||
createSingleModelChain,
|
createSingleModelChain,
|
||||||
getModelPolicyChain,
|
getModelPolicyChain,
|
||||||
|
getFlashLitePolicyChain,
|
||||||
} from './policyCatalog.js';
|
} from './policyCatalog.js';
|
||||||
import {
|
import {
|
||||||
|
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||||
DEFAULT_GEMINI_MODEL,
|
DEFAULT_GEMINI_MODEL,
|
||||||
DEFAULT_GEMINI_MODEL_AUTO,
|
|
||||||
PREVIEW_GEMINI_MODEL_AUTO,
|
PREVIEW_GEMINI_MODEL_AUTO,
|
||||||
|
isAutoModel,
|
||||||
resolveModel,
|
resolveModel,
|
||||||
} from '../config/models.js';
|
} from '../config/models.js';
|
||||||
import type { ModelSelectionResult } from './modelAvailabilityService.js';
|
import type { ModelSelectionResult } from './modelAvailabilityService.js';
|
||||||
@@ -38,24 +40,30 @@ export function resolvePolicyChain(
|
|||||||
): ModelPolicyChain {
|
): ModelPolicyChain {
|
||||||
const modelFromConfig =
|
const modelFromConfig =
|
||||||
preferredModel ?? config.getActiveModel?.() ?? config.getModel();
|
preferredModel ?? config.getActiveModel?.() ?? config.getModel();
|
||||||
|
const configuredModel = config.getModel();
|
||||||
|
|
||||||
let chain;
|
let chain;
|
||||||
|
const resolvedModel = resolveModel(modelFromConfig);
|
||||||
|
const isAutoPreferred = preferredModel ? isAutoModel(preferredModel) : false;
|
||||||
|
const isAutoConfigured = isAutoModel(configuredModel);
|
||||||
|
|
||||||
if (
|
if (resolvedModel === DEFAULT_GEMINI_FLASH_LITE_MODEL) {
|
||||||
config.getModel() === PREVIEW_GEMINI_MODEL_AUTO ||
|
chain = getFlashLitePolicyChain();
|
||||||
config.getModel() === DEFAULT_GEMINI_MODEL_AUTO
|
} else if (isAutoPreferred || isAutoConfigured) {
|
||||||
) {
|
const previewEnabled =
|
||||||
|
preferredModel === PREVIEW_GEMINI_MODEL_AUTO ||
|
||||||
|
configuredModel === PREVIEW_GEMINI_MODEL_AUTO;
|
||||||
chain = getModelPolicyChain({
|
chain = getModelPolicyChain({
|
||||||
previewEnabled: config.getModel() === PREVIEW_GEMINI_MODEL_AUTO,
|
previewEnabled,
|
||||||
userTier: config.getUserTier(),
|
userTier: config.getUserTier(),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
chain = createSingleModelChain(modelFromConfig);
|
chain = createSingleModelChain(modelFromConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeModel = resolveModel(modelFromConfig);
|
const activeIndex = chain.findIndex(
|
||||||
|
(policy) => policy.model === resolvedModel,
|
||||||
const activeIndex = chain.findIndex((policy) => policy.model === activeModel);
|
);
|
||||||
if (activeIndex !== -1) {
|
if (activeIndex !== -1) {
|
||||||
return wrapsAround
|
return wrapsAround
|
||||||
? [...chain.slice(activeIndex), ...chain.slice(0, activeIndex)]
|
? [...chain.slice(activeIndex), ...chain.slice(0, activeIndex)]
|
||||||
@@ -64,7 +72,7 @@ export function resolvePolicyChain(
|
|||||||
|
|
||||||
// If the user specified a model not in the default chain, we assume they want
|
// If the user specified a model not in the default chain, we assume they want
|
||||||
// *only* that model. We do not fallback to the default chain.
|
// *only* that model. We do not fallback to the default chain.
|
||||||
return [createDefaultPolicy(activeModel, { isLastResort: true })];
|
return [createDefaultPolicy(resolvedModel, { isLastResort: true })];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user