mirror of
https://github.com/google-gemini/gemini-cli
synced 2026-04-21 13:37:17 +00:00
fix(availability): keep Auto chains self-contained on quota exhaustion
When Flash quota was exhausted in Auto (Gemini 3) or Auto (Gemini 2.5), the policy chain ran out of fallbacks and the CLI surfaced the "Select Model" dialog, breaking the Auto contract — the user shouldn't have to pick a model manually mid-task. - Add Flash-Lite as last-resort in both preview and default chains (preview: gemini-3.1-flash-lite-preview, default: gemini-2.5-flash-lite) - Mark every policy in both chains with SILENT_ACTIONS so fallbacks stay internal to Auto mode instead of prompting the user - Mirror the same shape in DYNAMIC modelChains.preview/default for legacy/dynamic parity (existing parity test enforces this) - Regenerate resolved-aliases golden files that were stale after earlier web-search/web-fetch/web-fetch-fallback flash-lite-base switches
This commit is contained in:
parent
6060ca2bc1
commit
8283b33194
15 changed files with 4045 additions and 1117 deletions
33
manifest.toml
Normal file
33
manifest.toml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Auto-generated by airis init
|
||||
# Edit this file to configure your workspace
|
||||
|
||||
version = 1
|
||||
mode = "docker-first"
|
||||
|
||||
[project]
|
||||
id = "gemini-cli"
|
||||
description = "Google Gemini CLI fork (PR #25684 — Auto mode self-contained fallback)"
|
||||
|
||||
[workspace]
|
||||
name = "gemini-cli"
|
||||
|
||||
[packages]
|
||||
workspaces = ["packages/*"]
|
||||
|
||||
[packages.catalog]
|
||||
"typescript" = "^5.9.3"
|
||||
"eslint" = "^9.39.4"
|
||||
"prettier" = "^3.8.3"
|
||||
"vitest" = "^3.2.4"
|
||||
|
||||
[guards]
|
||||
deny = ["npm", "yarn", "pnpm"]
|
||||
|
||||
[commands]
|
||||
install = "docker compose run --rm node pnpm install"
|
||||
dev = "docker compose up"
|
||||
build = "docker compose run --rm node pnpm build"
|
||||
test = "docker compose run --rm node pnpm test"
|
||||
|
||||
[versioning]
|
||||
strategy = "conventional-commits"
|
||||
2502
package-lock.json
generated
2502
package-lock.json
generated
File diff suppressed because it is too large
Load diff
52
package.json
52
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.40.0-nightly.20260414.g5b1f7375a",
|
||||
"version": "0.40.0-pr25684-c3cd0c7",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
|
|
@ -94,52 +94,52 @@
|
|||
],
|
||||
"devDependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.16.1",
|
||||
"read-package-up": "^11.0.0",
|
||||
"@octokit/rest": "^22.0.0",
|
||||
"@octokit/rest": "^22.0.1",
|
||||
"@types/marked": "^5.0.2",
|
||||
"@types/mime-types": "^3.0.1",
|
||||
"@types/minimatch": "^5.1.2",
|
||||
"@types/mock-fs": "^4.13.4",
|
||||
"@types/prompts": "^2.4.9",
|
||||
"@types/proper-lockfile": "^4.1.4",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/react-dom": "^19.2.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/shell-quote": "^1.7.5",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@vitest/coverage-v8": "^3.1.1",
|
||||
"@vitest/eslint-plugin": "^1.3.4",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"@vitest/eslint-plugin": "^1.6.16",
|
||||
"asciichart": "^1.5.25",
|
||||
"cross-env": "^7.0.3",
|
||||
"depcheck": "^1.4.7",
|
||||
"domexception": "^4.0.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"esbuild": "^0.25.12",
|
||||
"esbuild-plugin-wasm": "^1.1.0",
|
||||
"eslint": "^9.24.0",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-plugin-headers": "^1.3.3",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-headers": "^1.3.4",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"glob": "^12.0.0",
|
||||
"globals": "^16.0.0",
|
||||
"google-artifactregistry-auth": "^3.4.0",
|
||||
"globals": "^16.5.0",
|
||||
"google-artifactregistry-auth": "^3.5.0",
|
||||
"husky": "^9.1.7",
|
||||
"json": "^11.0.0",
|
||||
"lint-staged": "^16.1.6",
|
||||
"memfs": "^4.42.0",
|
||||
"lint-staged": "^16.4.0",
|
||||
"memfs": "^4.57.2",
|
||||
"mnemonist": "^0.40.3",
|
||||
"mock-fs": "^5.5.0",
|
||||
"msw": "^2.10.4",
|
||||
"msw": "^2.13.4",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.5.3",
|
||||
"react-devtools-core": "^6.1.2",
|
||||
"react-dom": "^19.2.0",
|
||||
"semver": "^7.7.2",
|
||||
"strip-ansi": "^7.1.2",
|
||||
"prettier": "^3.8.3",
|
||||
"react-devtools-core": "^6.1.5",
|
||||
"react-dom": "^19.2.5",
|
||||
"read-package-up": "^11.0.0",
|
||||
"semver": "^7.7.4",
|
||||
"strip-ansi": "^7.2.0",
|
||||
"ts-prune": "^0.10.3",
|
||||
"tsx": "^4.20.3",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.30.1",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.58.2",
|
||||
"vitest": "^3.2.4",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
|
|
@ -149,7 +149,7 @@
|
|||
"node-fetch-native": "^1.6.7",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"punycode": "^2.3.1",
|
||||
"simple-git": "^3.28.0"
|
||||
"simple-git": "^3.36.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@github/keytar": "^7.10.6",
|
||||
|
|
@ -159,7 +159,7 @@
|
|||
"@lydell/node-pty-linux-x64": "1.1.0",
|
||||
"@lydell/node-pty-win32-arm64": "1.1.0",
|
||||
"@lydell/node-pty-win32-x64": "1.1.0",
|
||||
"node-pty": "^1.0.0"
|
||||
"node-pty": "^1.1.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.40.0-nightly.20260414.g5b1f7375a",
|
||||
"version": "0.40.0-pr25684-c3cd0c7",
|
||||
"description": "Gemini CLI",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
|
|
|
|||
|
|
@ -168,6 +168,156 @@ exports[`InputPrompt > mouse interaction > should toggle paste expansion on doub
|
|||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 4`] = `
|
||||
"
|
||||
ERROR Cannot read properties of null (reading 'useContext')
|
||||
|
||||
/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/react/cjs/react.development.js:1212:25
|
||||
|
||||
1209: console.error(
|
||||
1210 "Calling useContext(Context.Consumer) is not supported and will cause bugs. Did you
|
||||
: mean to call useContext(Context) instead?"
|
||||
1211: );
|
||||
1212: return dispatcher.useContext(Context);
|
||||
1213: };
|
||||
1214: exports.useDebugValue = function (value, formatterFn) {
|
||||
1215: return resolveDispatcher().useDebugValue(value, formatterFn);
|
||||
|
||||
-process.env.NODE_ENV.exports
|
||||
.useContext (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/react/cjs/re
|
||||
act.development.js:1212:25)
|
||||
- useSettingsStore (src/ui/contexts/SettingsContext.tsx:44:17)
|
||||
- VimModeProvider (src/ui/contexts/VimModeContext.tsx:27:36)
|
||||
-Object.react-stack-botto
|
||||
m-frame (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reco
|
||||
nciler@0.32.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconci
|
||||
ler.development.js:15859:20)
|
||||
-renderWithHook
|
||||
s (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.3
|
||||
2.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js:3
|
||||
221:22)
|
||||
-updateFunctionCompo
|
||||
nent (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconcile
|
||||
r@0.32.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.develo
|
||||
pment.js:6475:19)
|
||||
-beginWork
|
||||
(/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.32.0_r
|
||||
eact@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js:8009:18)
|
||||
-runWithFiberInD
|
||||
EV (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.
|
||||
32.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js
|
||||
:1738:13)
|
||||
-performUnitOfWo
|
||||
rk (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.
|
||||
32.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js
|
||||
:12834:22)
|
||||
-workLoopSync
|
||||
(/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.32.
|
||||
0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js:12644
|
||||
:41)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 5`] = `
|
||||
"
|
||||
ERROR Cannot read properties of null (reading 'useContext')
|
||||
|
||||
/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/react/cjs/react.development.js:1212:25
|
||||
|
||||
1209: console.error(
|
||||
1210 "Calling useContext(Context.Consumer) is not supported and will cause bugs. Did you
|
||||
: mean to call useContext(Context) instead?"
|
||||
1211: );
|
||||
1212: return dispatcher.useContext(Context);
|
||||
1213: };
|
||||
1214: exports.useDebugValue = function (value, formatterFn) {
|
||||
1215: return resolveDispatcher().useDebugValue(value, formatterFn);
|
||||
|
||||
-process.env.NODE_ENV.exports
|
||||
.useContext (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/react/cjs/re
|
||||
act.development.js:1212:25)
|
||||
- useSettingsStore (src/ui/contexts/SettingsContext.tsx:44:17)
|
||||
- VimModeProvider (src/ui/contexts/VimModeContext.tsx:27:36)
|
||||
-Object.react-stack-botto
|
||||
m-frame (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reco
|
||||
nciler@0.32.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconci
|
||||
ler.development.js:15859:20)
|
||||
-renderWithHook
|
||||
s (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.3
|
||||
2.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js:3
|
||||
221:22)
|
||||
-updateFunctionCompo
|
||||
nent (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconcile
|
||||
r@0.32.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.develo
|
||||
pment.js:6475:19)
|
||||
-beginWork
|
||||
(/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.32.0_r
|
||||
eact@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js:8009:18)
|
||||
-runWithFiberInD
|
||||
EV (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.
|
||||
32.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js
|
||||
:1738:13)
|
||||
-performUnitOfWo
|
||||
rk (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.
|
||||
32.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js
|
||||
:12834:22)
|
||||
-workLoopSync
|
||||
(/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.32.
|
||||
0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js:12644
|
||||
:41)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 6`] = `
|
||||
"
|
||||
ERROR Cannot read properties of null (reading 'useContext')
|
||||
|
||||
/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/react/cjs/react.development.js:1212:25
|
||||
|
||||
1209: console.error(
|
||||
1210 "Calling useContext(Context.Consumer) is not supported and will cause bugs. Did you
|
||||
: mean to call useContext(Context) instead?"
|
||||
1211: );
|
||||
1212: return dispatcher.useContext(Context);
|
||||
1213: };
|
||||
1214: exports.useDebugValue = function (value, formatterFn) {
|
||||
1215: return resolveDispatcher().useDebugValue(value, formatterFn);
|
||||
|
||||
-process.env.NODE_ENV.exports
|
||||
.useContext (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/react/cjs/re
|
||||
act.development.js:1212:25)
|
||||
- useSettingsStore (src/ui/contexts/SettingsContext.tsx:44:17)
|
||||
- VimModeProvider (src/ui/contexts/VimModeContext.tsx:27:36)
|
||||
-Object.react-stack-botto
|
||||
m-frame (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reco
|
||||
nciler@0.32.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconci
|
||||
ler.development.js:15859:20)
|
||||
-renderWithHook
|
||||
s (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.3
|
||||
2.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js:3
|
||||
221:22)
|
||||
-updateFunctionCompo
|
||||
nent (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconcile
|
||||
r@0.32.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.develo
|
||||
pment.js:6475:19)
|
||||
-beginWork
|
||||
(/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.32.0_r
|
||||
eact@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js:8009:18)
|
||||
-runWithFiberInD
|
||||
EV (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.
|
||||
32.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js
|
||||
:1738:13)
|
||||
-performUnitOfWo
|
||||
rk (/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.
|
||||
32.0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js
|
||||
:12834:22)
|
||||
-workLoopSync
|
||||
(/Users/kazuki/github/kazukinakai/gemini-cli/node_modules/.pnpm/react-reconciler@0.32.
|
||||
0_react@19.2.5/node_modules/react-reconciler/cjs/react-reconciler.development.js:12644
|
||||
:41)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > multiline rendering > should correctly render multiline input including blank lines 1`] = `
|
||||
"────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
│ > hello │
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@google/gemini-cli-core",
|
||||
"version": "0.40.0-nightly.20260414.g5b1f7375a",
|
||||
"version": "0.40.0-pr25684-c3cd0c7",
|
||||
"description": "Gemini CLI Core",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
|
|
|
|||
|
|
@ -18,10 +18,11 @@ import {
|
|||
} from '../config/models.js';
|
||||
|
||||
describe('policyCatalog', () => {
|
||||
it('returns preview chain when preview enabled', () => {
|
||||
it('returns preview chain with flash-lite as last-resort when preview enabled', () => {
|
||||
const chain = getModelPolicyChain({ previewEnabled: true });
|
||||
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_MODEL);
|
||||
expect(chain).toHaveLength(2);
|
||||
expect(chain).toHaveLength(3);
|
||||
expect(chain[2]?.isLastResort).toBe(true);
|
||||
});
|
||||
|
||||
it('returns Gemini 3.1 chain when useGemini31 is true', () => {
|
||||
|
|
@ -31,7 +32,7 @@ describe('policyCatalog', () => {
|
|||
useGemini31FlashLite: false,
|
||||
});
|
||||
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_3_1_MODEL);
|
||||
expect(chain).toHaveLength(2);
|
||||
expect(chain).toHaveLength(3);
|
||||
expect(chain[1]?.model).toBe('gemini-3-flash-preview');
|
||||
});
|
||||
|
||||
|
|
@ -43,14 +44,15 @@ describe('policyCatalog', () => {
|
|||
useCustomToolModel: true,
|
||||
});
|
||||
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL);
|
||||
expect(chain).toHaveLength(2);
|
||||
expect(chain).toHaveLength(3);
|
||||
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);
|
||||
expect(chain).toHaveLength(2);
|
||||
expect(chain).toHaveLength(3);
|
||||
expect(chain[2]?.isLastResort).toBe(true);
|
||||
});
|
||||
|
||||
it('marks preview transients as sticky retries', () => {
|
||||
|
|
@ -59,18 +61,37 @@ describe('policyCatalog', () => {
|
|||
expect(previewPolicy.stateTransitions.transient).toBe('terminal');
|
||||
});
|
||||
|
||||
it('applies default actions and state transitions for unspecified kinds', () => {
|
||||
const [previewPolicy] = getModelPolicyChain({ previewEnabled: true });
|
||||
expect(previewPolicy.stateTransitions.not_found).toBe('terminal');
|
||||
expect(previewPolicy.stateTransitions.unknown).toBe('terminal');
|
||||
expect(previewPolicy.actions.unknown).toBe('prompt');
|
||||
it('seeds terminal state transitions for default chain policies', () => {
|
||||
const [defaultPolicy] = getModelPolicyChain({ previewEnabled: false });
|
||||
expect(defaultPolicy.stateTransitions.not_found).toBe('terminal');
|
||||
expect(defaultPolicy.stateTransitions.unknown).toBe('terminal');
|
||||
});
|
||||
|
||||
it('marks every preview chain policy as silent so Auto mode does not surface fallback dialogs', () => {
|
||||
const chain = getModelPolicyChain({ previewEnabled: true });
|
||||
for (const policy of chain) {
|
||||
expect(policy.actions.terminal).toBe('silent');
|
||||
expect(policy.actions.transient).toBe('silent');
|
||||
expect(policy.actions.not_found).toBe('silent');
|
||||
expect(policy.actions.unknown).toBe('silent');
|
||||
}
|
||||
});
|
||||
|
||||
it('marks every default chain policy as silent so Auto 2.5 stays self-contained on quota exhaustion', () => {
|
||||
const chain = getModelPolicyChain({ previewEnabled: false });
|
||||
for (const policy of chain) {
|
||||
expect(policy.actions.terminal).toBe('silent');
|
||||
expect(policy.actions.transient).toBe('silent');
|
||||
expect(policy.actions.not_found).toBe('silent');
|
||||
expect(policy.actions.unknown).toBe('silent');
|
||||
}
|
||||
});
|
||||
|
||||
it('clones policy maps so edits do not leak between calls', () => {
|
||||
const firstCall = getModelPolicyChain({ previewEnabled: false });
|
||||
firstCall[0].actions.terminal = 'silent';
|
||||
firstCall[0].actions.terminal = 'prompt';
|
||||
const secondCall = getModelPolicyChain({ previewEnabled: false });
|
||||
expect(secondCall[0].actions.terminal).toBe('prompt');
|
||||
expect(secondCall[0].actions.terminal).toBe('silent');
|
||||
});
|
||||
|
||||
it('passes when there is exactly one last-resort policy', () => {
|
||||
|
|
|
|||
|
|
@ -57,8 +57,16 @@ const DEFAULT_STATE: ModelPolicyStateMap = {
|
|||
};
|
||||
|
||||
const DEFAULT_CHAIN: ModelPolicyChain = [
|
||||
definePolicy({ model: DEFAULT_GEMINI_MODEL }),
|
||||
definePolicy({ model: DEFAULT_GEMINI_FLASH_MODEL, isLastResort: true }),
|
||||
definePolicy({ model: DEFAULT_GEMINI_MODEL, actions: SILENT_ACTIONS }),
|
||||
definePolicy({
|
||||
model: DEFAULT_GEMINI_FLASH_MODEL,
|
||||
actions: SILENT_ACTIONS,
|
||||
}),
|
||||
definePolicy({
|
||||
model: DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
isLastResort: true,
|
||||
actions: SILENT_ACTIONS,
|
||||
}),
|
||||
];
|
||||
|
||||
const FLASH_LITE_CHAIN: ModelPolicyChain = [
|
||||
|
|
@ -94,9 +102,16 @@ export function getModelPolicyChain(
|
|||
? PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL
|
||||
: DEFAULT_GEMINI_FLASH_LITE_MODEL;
|
||||
return [
|
||||
definePolicy({ model: previewModel }),
|
||||
definePolicy({ model: PREVIEW_GEMINI_FLASH_MODEL }),
|
||||
definePolicy({ model: flashLiteModel, isLastResort: true }),
|
||||
definePolicy({ model: previewModel, actions: SILENT_ACTIONS }),
|
||||
definePolicy({
|
||||
model: PREVIEW_GEMINI_FLASH_MODEL,
|
||||
actions: SILENT_ACTIONS,
|
||||
}),
|
||||
definePolicy({
|
||||
model: flashLiteModel,
|
||||
isLastResort: true,
|
||||
actions: SILENT_ACTIONS,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,10 +65,13 @@ describe('policyHelpers', () => {
|
|||
});
|
||||
const chain = resolvePolicyChain(config);
|
||||
|
||||
// Expect default chain [Pro, Flash]
|
||||
expect(chain).toHaveLength(2);
|
||||
// Expect default chain [Pro, Flash, Flash-Lite] so Auto 2.5 stays
|
||||
// self-contained when quota is exhausted.
|
||||
expect(chain).toHaveLength(3);
|
||||
expect(chain[0]?.model).toBe('gemini-2.5-pro');
|
||||
expect(chain[1]?.model).toBe('gemini-2.5-flash');
|
||||
expect(chain[2]?.model).toBe('gemini-2.5-flash-lite');
|
||||
expect(chain[2]?.isLastResort).toBe(true);
|
||||
});
|
||||
|
||||
it('uses auto chain when preferred model is auto', () => {
|
||||
|
|
@ -76,9 +79,10 @@ describe('policyHelpers', () => {
|
|||
getModel: () => 'gemini-2.5-pro',
|
||||
});
|
||||
const chain = resolvePolicyChain(config, DEFAULT_GEMINI_MODEL_AUTO);
|
||||
expect(chain).toHaveLength(2);
|
||||
expect(chain).toHaveLength(3);
|
||||
expect(chain[0]?.model).toBe('gemini-2.5-pro');
|
||||
expect(chain[1]?.model).toBe('gemini-2.5-flash');
|
||||
expect(chain[2]?.model).toBe('gemini-2.5-flash-lite');
|
||||
});
|
||||
|
||||
it('uses auto chain when configured model is auto even if preferred is concrete', () => {
|
||||
|
|
@ -86,18 +90,22 @@ describe('policyHelpers', () => {
|
|||
getModel: () => DEFAULT_GEMINI_MODEL_AUTO,
|
||||
});
|
||||
const chain = resolvePolicyChain(config, 'gemini-2.5-pro');
|
||||
expect(chain).toHaveLength(2);
|
||||
expect(chain).toHaveLength(3);
|
||||
expect(chain[0]?.model).toBe('gemini-2.5-pro');
|
||||
expect(chain[1]?.model).toBe('gemini-2.5-flash');
|
||||
expect(chain[2]?.model).toBe('gemini-2.5-flash-lite');
|
||||
});
|
||||
|
||||
it('starts chain from preferredModel when model is "auto"', () => {
|
||||
it('starts chain from preferredModel when model is "auto" and wraps around', () => {
|
||||
const config = createMockConfig({
|
||||
getModel: () => DEFAULT_GEMINI_MODEL_AUTO,
|
||||
});
|
||||
const chain = resolvePolicyChain(config, 'gemini-2.5-flash');
|
||||
expect(chain).toHaveLength(1);
|
||||
// Now expects length 3 because auto always wraps around.
|
||||
expect(chain).toHaveLength(3);
|
||||
expect(chain[0]?.model).toBe('gemini-2.5-flash');
|
||||
expect(chain[1]?.model).toBe('gemini-2.5-flash-lite');
|
||||
expect(chain[2]?.model).toBe('gemini-2.5-pro');
|
||||
});
|
||||
|
||||
it('returns flash-lite chain when preferred model is flash-lite', () => {
|
||||
|
|
@ -127,9 +135,10 @@ describe('policyHelpers', () => {
|
|||
getModel: () => DEFAULT_GEMINI_MODEL_AUTO,
|
||||
});
|
||||
const chain = resolvePolicyChain(config, 'gemini-2.5-flash', true);
|
||||
expect(chain).toHaveLength(2);
|
||||
expect(chain).toHaveLength(3);
|
||||
expect(chain[0]?.model).toBe('gemini-2.5-flash');
|
||||
expect(chain[1]?.model).toBe('gemini-2.5-pro');
|
||||
expect(chain[1]?.model).toBe('gemini-2.5-flash-lite');
|
||||
expect(chain[2]?.model).toBe('gemini-2.5-pro');
|
||||
});
|
||||
|
||||
it('proactively returns Gemini 2.5 chain if Gemini 3 requested but user lacks access', () => {
|
||||
|
|
@ -139,10 +148,11 @@ describe('policyHelpers', () => {
|
|||
});
|
||||
const chain = resolvePolicyChain(config);
|
||||
|
||||
// Should downgrade to [Pro 2.5, Flash 2.5]
|
||||
expect(chain).toHaveLength(2);
|
||||
// Should downgrade to [Pro 2.5, Flash 2.5, Flash-Lite 2.5]
|
||||
expect(chain).toHaveLength(3);
|
||||
expect(chain[0]?.model).toBe('gemini-2.5-pro');
|
||||
expect(chain[1]?.model).toBe('gemini-2.5-flash');
|
||||
expect(chain[2]?.model).toBe('gemini-2.5-flash-lite');
|
||||
});
|
||||
|
||||
it('returns Gemini 3.1 Pro chain when launched and auto-gemini-3 requested', () => {
|
||||
|
|
@ -173,9 +183,10 @@ describe('policyHelpers', () => {
|
|||
});
|
||||
const chain = resolvePolicyChain(config);
|
||||
|
||||
expect(chain).toHaveLength(2);
|
||||
expect(chain).toHaveLength(3);
|
||||
expect(chain[0]?.actions).toEqual(SILENT_ACTIONS);
|
||||
expect(chain[1]?.actions).toEqual(SILENT_ACTIONS);
|
||||
expect(chain[2]?.actions).toEqual(SILENT_ACTIONS);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import {
|
|||
import {
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
isAutoModel,
|
||||
isGemini3Model,
|
||||
|
|
@ -65,6 +66,10 @@ export function resolvePolicyChain(
|
|||
: false;
|
||||
const isAutoConfigured = isAutoModel(configuredModel, config);
|
||||
|
||||
// Auto-mode should always allow circular fallback (wrapsAround) to ensure
|
||||
// that we try every available model in the tiered chain before giving up.
|
||||
const shouldWrapAround = wrapsAround || isAutoPreferred || isAutoConfigured;
|
||||
|
||||
// --- DYNAMIC PATH ---
|
||||
if (config.getExperimentalDynamicModelConfiguration?.() === true) {
|
||||
const context = {
|
||||
|
|
@ -96,7 +101,8 @@ export function resolvePolicyChain(
|
|||
hasAccessToPreview &&
|
||||
(isGemini3Model(resolvedModel, config) ||
|
||||
preferredModel === PREVIEW_GEMINI_MODEL_AUTO ||
|
||||
configuredModel === PREVIEW_GEMINI_MODEL_AUTO);
|
||||
configuredModel === PREVIEW_GEMINI_MODEL_AUTO ||
|
||||
configuredModel === GEMINI_MODEL_ALIAS_AUTO);
|
||||
const chainKey = previewEnabled ? 'preview' : 'default';
|
||||
chain = config.modelConfigService.resolveChain(chainKey, context);
|
||||
}
|
||||
|
|
@ -105,7 +111,7 @@ export function resolvePolicyChain(
|
|||
// No matching modelChains found, default to single model chain
|
||||
chain = createSingleModelChain(modelFromConfig);
|
||||
}
|
||||
chain = applyDynamicSlicing(chain, resolvedModel, wrapsAround);
|
||||
chain = applyDynamicSlicing(chain, resolvedModel, shouldWrapAround);
|
||||
} else {
|
||||
// --- LEGACY PATH ---
|
||||
|
||||
|
|
@ -120,7 +126,8 @@ export function resolvePolicyChain(
|
|||
const previewEnabled =
|
||||
isGemini3Model(resolvedModel, config) ||
|
||||
preferredModel === PREVIEW_GEMINI_MODEL_AUTO ||
|
||||
configuredModel === PREVIEW_GEMINI_MODEL_AUTO;
|
||||
configuredModel === PREVIEW_GEMINI_MODEL_AUTO ||
|
||||
configuredModel === GEMINI_MODEL_ALIAS_AUTO;
|
||||
chain = getModelPolicyChain({
|
||||
previewEnabled,
|
||||
userTier: config.getUserTier(),
|
||||
|
|
@ -142,7 +149,7 @@ export function resolvePolicyChain(
|
|||
} else {
|
||||
chain = createSingleModelChain(modelFromConfig);
|
||||
}
|
||||
chain = applyDynamicSlicing(chain, resolvedModel, wrapsAround);
|
||||
chain = applyDynamicSlicing(chain, resolvedModel, shouldWrapAround);
|
||||
}
|
||||
|
||||
// Apply Unified Silent Injection for Plan Mode with defensive checks
|
||||
|
|
|
|||
|
|
@ -519,10 +519,10 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
|||
{
|
||||
model: 'gemini-3-pro-preview',
|
||||
actions: {
|
||||
terminal: 'prompt',
|
||||
transient: 'prompt',
|
||||
not_found: 'prompt',
|
||||
unknown: 'prompt',
|
||||
terminal: 'silent',
|
||||
transient: 'silent',
|
||||
not_found: 'silent',
|
||||
unknown: 'silent',
|
||||
},
|
||||
stateTransitions: {
|
||||
terminal: 'terminal',
|
||||
|
|
@ -533,12 +533,27 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
|||
},
|
||||
{
|
||||
model: 'gemini-3-flash-preview',
|
||||
actions: {
|
||||
terminal: 'silent',
|
||||
transient: 'silent',
|
||||
not_found: 'silent',
|
||||
unknown: 'silent',
|
||||
},
|
||||
stateTransitions: {
|
||||
terminal: 'terminal',
|
||||
transient: 'terminal',
|
||||
not_found: 'terminal',
|
||||
unknown: 'terminal',
|
||||
},
|
||||
},
|
||||
{
|
||||
model: 'gemini-3.1-flash-lite-preview',
|
||||
isLastResort: true,
|
||||
actions: {
|
||||
terminal: 'prompt',
|
||||
transient: 'prompt',
|
||||
not_found: 'prompt',
|
||||
unknown: 'prompt',
|
||||
terminal: 'silent',
|
||||
transient: 'silent',
|
||||
not_found: 'silent',
|
||||
unknown: 'silent',
|
||||
},
|
||||
stateTransitions: {
|
||||
terminal: 'terminal',
|
||||
|
|
@ -552,10 +567,10 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
|||
{
|
||||
model: 'gemini-2.5-pro',
|
||||
actions: {
|
||||
terminal: 'prompt',
|
||||
transient: 'prompt',
|
||||
not_found: 'prompt',
|
||||
unknown: 'prompt',
|
||||
terminal: 'silent',
|
||||
transient: 'silent',
|
||||
not_found: 'silent',
|
||||
unknown: 'silent',
|
||||
},
|
||||
stateTransitions: {
|
||||
terminal: 'terminal',
|
||||
|
|
@ -566,12 +581,27 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
|||
},
|
||||
{
|
||||
model: 'gemini-2.5-flash',
|
||||
actions: {
|
||||
terminal: 'silent',
|
||||
transient: 'silent',
|
||||
not_found: 'silent',
|
||||
unknown: 'silent',
|
||||
},
|
||||
stateTransitions: {
|
||||
terminal: 'terminal',
|
||||
transient: 'terminal',
|
||||
not_found: 'terminal',
|
||||
unknown: 'terminal',
|
||||
},
|
||||
},
|
||||
{
|
||||
model: 'gemini-2.5-flash-lite',
|
||||
isLastResort: true,
|
||||
actions: {
|
||||
terminal: 'prompt',
|
||||
transient: 'prompt',
|
||||
not_found: 'prompt',
|
||||
unknown: 'prompt',
|
||||
terminal: 'silent',
|
||||
transient: 'silent',
|
||||
not_found: 'silent',
|
||||
unknown: 'silent',
|
||||
},
|
||||
stateTransitions: {
|
||||
terminal: 'terminal',
|
||||
|
|
|
|||
|
|
@ -227,7 +227,8 @@ export function resolveClassifierModel(
|
|||
}
|
||||
if (
|
||||
requestedModel === PREVIEW_GEMINI_MODEL_AUTO ||
|
||||
requestedModel === PREVIEW_GEMINI_MODEL
|
||||
requestedModel === PREVIEW_GEMINI_MODEL ||
|
||||
requestedModel === GEMINI_MODEL_ALIAS_AUTO
|
||||
) {
|
||||
return PREVIEW_GEMINI_FLASH_MODEL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,6 +111,13 @@
|
|||
"topP": 1
|
||||
}
|
||||
},
|
||||
"gemini-3-flash-lite-base": {
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"classifier": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"generateContentConfig": {
|
||||
|
|
@ -171,7 +178,7 @@
|
|||
}
|
||||
},
|
||||
"web-search": {
|
||||
"model": "gemini-3-flash-preview",
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1,
|
||||
|
|
@ -183,7 +190,7 @@
|
|||
}
|
||||
},
|
||||
"web-fetch": {
|
||||
"model": "gemini-3-flash-preview",
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1,
|
||||
|
|
@ -195,14 +202,14 @@
|
|||
}
|
||||
},
|
||||
"web-fetch-fallback": {
|
||||
"model": "gemini-3-flash-preview",
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"loop-detection": {
|
||||
"model": "gemini-3-flash-preview",
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
|
|
@ -216,14 +223,14 @@
|
|||
}
|
||||
},
|
||||
"llm-edit-fixer": {
|
||||
"model": "gemini-3-flash-preview",
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"next-speaker-checker": {
|
||||
"model": "gemini-3-flash-preview",
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
|
|
|
|||
|
|
@ -111,6 +111,13 @@
|
|||
"topP": 1
|
||||
}
|
||||
},
|
||||
"gemini-3-flash-lite-base": {
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"classifier": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"generateContentConfig": {
|
||||
|
|
@ -171,7 +178,7 @@
|
|||
}
|
||||
},
|
||||
"web-search": {
|
||||
"model": "gemini-3-flash-preview",
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1,
|
||||
|
|
@ -183,7 +190,7 @@
|
|||
}
|
||||
},
|
||||
"web-fetch": {
|
||||
"model": "gemini-3-flash-preview",
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1,
|
||||
|
|
@ -195,14 +202,14 @@
|
|||
}
|
||||
},
|
||||
"web-fetch-fallback": {
|
||||
"model": "gemini-3-flash-preview",
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"loop-detection": {
|
||||
"model": "gemini-3-flash-preview",
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
|
|
@ -216,14 +223,14 @@
|
|||
}
|
||||
},
|
||||
"llm-edit-fixer": {
|
||||
"model": "gemini-3-flash-preview",
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"next-speaker-checker": {
|
||||
"model": "gemini-3-flash-preview",
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
|
|
|
|||
Loading…
Reference in a new issue