mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-08-11 01:16:28 -07:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eb42ff6086 | |||
| a57f33acbc | |||
| f54f63cd4c |
+135
-59
@@ -545,6 +545,24 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"model": "gemini-3-flash-preview"
|
||||
}
|
||||
},
|
||||
"gemini-3.1-pro-preview": {
|
||||
"extends": "chat-base-3",
|
||||
"modelConfig": {
|
||||
"model": "gemini-3.1-pro-preview"
|
||||
}
|
||||
},
|
||||
"gemini-3.1-pro-preview-customtools": {
|
||||
"extends": "chat-base-3",
|
||||
"modelConfig": {
|
||||
"model": "gemini-3.1-pro-preview-customtools"
|
||||
}
|
||||
},
|
||||
"gemini-3.1-flash-lite-preview": {
|
||||
"extends": "chat-base-3",
|
||||
"modelConfig": {
|
||||
"model": "gemini-3.1-flash-lite-preview"
|
||||
}
|
||||
},
|
||||
"gemini-2.5-pro": {
|
||||
"extends": "chat-base-2.5",
|
||||
"modelConfig": {
|
||||
@@ -563,6 +581,18 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"model": "gemini-2.5-flash-lite"
|
||||
}
|
||||
},
|
||||
"gemini-3.1-flash-lite": {
|
||||
"extends": "chat-base-3",
|
||||
"modelConfig": {
|
||||
"model": "gemini-3.1-flash-lite"
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash": {
|
||||
"extends": "chat-base-3",
|
||||
"modelConfig": {
|
||||
"model": "gemini-3.5-flash"
|
||||
}
|
||||
},
|
||||
"gemma-4-31b-it": {
|
||||
"extends": "chat-base-3",
|
||||
"modelConfig": {
|
||||
@@ -587,10 +617,16 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"model": "gemini-3-flash-preview"
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash-base": {
|
||||
"extends": "base",
|
||||
"modelConfig": {
|
||||
"model": "gemini-3.5-flash"
|
||||
}
|
||||
},
|
||||
"classifier": {
|
||||
"extends": "base",
|
||||
"modelConfig": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"maxOutputTokens": 1024,
|
||||
"thinkingConfig": {
|
||||
@@ -602,7 +638,7 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"prompt-completion": {
|
||||
"extends": "base",
|
||||
"modelConfig": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0.3,
|
||||
"maxOutputTokens": 16000,
|
||||
@@ -615,7 +651,7 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"fast-ack-helper": {
|
||||
"extends": "base",
|
||||
"modelConfig": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0.2,
|
||||
"maxOutputTokens": 120,
|
||||
@@ -628,7 +664,7 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"edit-corrector": {
|
||||
"extends": "base",
|
||||
"modelConfig": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"thinkingConfig": {
|
||||
"thinkingBudget": 0
|
||||
@@ -639,7 +675,7 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"summarizer-default": {
|
||||
"extends": "base",
|
||||
"modelConfig": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"maxOutputTokens": 2000
|
||||
}
|
||||
@@ -648,7 +684,7 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"summarizer-shell": {
|
||||
"extends": "base",
|
||||
"modelConfig": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"maxOutputTokens": 2000
|
||||
}
|
||||
@@ -725,7 +761,7 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
},
|
||||
"chat-compression-3.1-flash-lite": {
|
||||
"modelConfig": {
|
||||
"model": "gemini-3.1-flash-lite-preview"
|
||||
"model": "gemini-3.1-flash-lite"
|
||||
}
|
||||
},
|
||||
"chat-compression-2.5-pro": {
|
||||
@@ -779,10 +815,10 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
|
||||
```json
|
||||
{
|
||||
"gemini-3.1-flash-lite-preview": {
|
||||
"gemini-3.1-flash-lite": {
|
||||
"tier": "flash-lite",
|
||||
"family": "gemini-3",
|
||||
"isPreview": true,
|
||||
"isPreview": false,
|
||||
"isVisible": true,
|
||||
"features": {
|
||||
"thinking": false,
|
||||
@@ -829,6 +865,16 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"multimodalToolUse": true
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash": {
|
||||
"tier": "flash",
|
||||
"family": "gemini-3",
|
||||
"isPreview": false,
|
||||
"isVisible": true,
|
||||
"features": {
|
||||
"thinking": false,
|
||||
"multimodalToolUse": true
|
||||
}
|
||||
},
|
||||
"gemini-2.5-pro": {
|
||||
"tier": "pro",
|
||||
"family": "gemini-2.5",
|
||||
@@ -882,9 +928,10 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
}
|
||||
},
|
||||
"auto": {
|
||||
"displayName": "Auto",
|
||||
"tier": "auto",
|
||||
"isPreview": true,
|
||||
"isVisible": false,
|
||||
"isVisible": true,
|
||||
"features": {
|
||||
"thinking": true,
|
||||
"multimodalToolUse": false
|
||||
@@ -918,26 +965,16 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
}
|
||||
},
|
||||
"auto-gemini-3": {
|
||||
"displayName": "Auto (Gemini 3)",
|
||||
"tier": "auto",
|
||||
"family": "gemini-3",
|
||||
"isPreview": true,
|
||||
"isVisible": true,
|
||||
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-3-pro, gemini-3-flash",
|
||||
"features": {
|
||||
"thinking": true,
|
||||
"multimodalToolUse": false
|
||||
}
|
||||
"isVisible": false
|
||||
},
|
||||
"auto-gemini-2.5": {
|
||||
"displayName": "Auto (Gemini 2.5)",
|
||||
"tier": "auto",
|
||||
"family": "gemini-2.5",
|
||||
"isPreview": false,
|
||||
"isVisible": true,
|
||||
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash",
|
||||
"features": {
|
||||
"thinking": false,
|
||||
"multimodalToolUse": false
|
||||
}
|
||||
"isVisible": false
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -990,37 +1027,50 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false
|
||||
"hasAccessToPreview": false,
|
||||
"useGemini3_5Flash": true
|
||||
},
|
||||
"target": "gemini-3.5-flash"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false,
|
||||
"useGemini3_5Flash": false
|
||||
},
|
||||
"target": "gemini-2.5-flash"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gemini-3-pro-preview": {
|
||||
"default": "gemini-3-pro-preview",
|
||||
"gemini-3.5-flash": {
|
||||
"default": "gemini-3.5-flash",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_5Flash": false,
|
||||
"hasAccessToPreview": false
|
||||
},
|
||||
"target": "gemini-2.5-pro"
|
||||
"target": "gemini-2.5-flash"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_1": true,
|
||||
"useCustomTools": true
|
||||
"useGemini3_5Flash": false
|
||||
},
|
||||
"target": "gemini-3.1-pro-preview-customtools"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_1": true
|
||||
},
|
||||
"target": "gemini-3.1-pro-preview"
|
||||
"target": "gemini-3-flash-preview"
|
||||
}
|
||||
]
|
||||
},
|
||||
"auto-gemini-3": {
|
||||
"gemini-2.5-flash": {
|
||||
"default": "gemini-2.5-flash",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_5Flash": true
|
||||
},
|
||||
"target": "gemini-3.5-flash"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gemini-3-pro-preview": {
|
||||
"default": "gemini-3-pro-preview",
|
||||
"contexts": [
|
||||
{
|
||||
@@ -1092,23 +1142,18 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
}
|
||||
]
|
||||
},
|
||||
"auto-gemini-2.5": {
|
||||
"default": "gemini-2.5-pro"
|
||||
},
|
||||
"gemini-3.1-flash-lite-preview": {
|
||||
"default": "gemini-3.1-flash-lite-preview",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_1FlashLite": false
|
||||
},
|
||||
"target": "gemini-2.5-flash-lite"
|
||||
}
|
||||
]
|
||||
"gemini-3.1-flash-lite": {
|
||||
"default": "gemini-3.1-flash-lite"
|
||||
},
|
||||
"flash": {
|
||||
"default": "gemini-3-flash-preview",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_5Flash": true
|
||||
},
|
||||
"target": "gemini-3.5-flash"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false
|
||||
@@ -1118,15 +1163,34 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
]
|
||||
},
|
||||
"flash-lite": {
|
||||
"default": "gemini-2.5-flash-lite",
|
||||
"default": "gemini-3.1-flash-lite"
|
||||
},
|
||||
"auto-gemini-3": {
|
||||
"default": "gemini-3-pro-preview",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_1FlashLite": true
|
||||
"hasAccessToPreview": false
|
||||
},
|
||||
"target": "gemini-3.1-flash-lite-preview"
|
||||
"target": "gemini-2.5-pro"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_1": true,
|
||||
"useCustomTools": true
|
||||
},
|
||||
"target": "gemini-3.1-pro-preview-customtools"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_1": true
|
||||
},
|
||||
"target": "gemini-3.1-pro-preview"
|
||||
}
|
||||
]
|
||||
},
|
||||
"auto-gemini-2.5": {
|
||||
"default": "gemini-2.5-pro"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1145,15 +1209,21 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"requestedModels": ["auto-gemini-2.5", "gemini-2.5-pro"]
|
||||
"useGemini3_5Flash": true
|
||||
},
|
||||
"target": "gemini-3.5-flash"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false
|
||||
},
|
||||
"target": "gemini-2.5-flash"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"requestedModels": ["auto-gemini-3", "gemini-3-pro-preview"]
|
||||
"requestedModels": ["gemini-2.5-pro", "auto-gemini-2.5"]
|
||||
},
|
||||
"target": "gemini-3-flash-preview"
|
||||
"target": "gemini-2.5-flash"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1162,7 +1232,13 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"requestedModels": ["auto-gemini-2.5", "gemini-2.5-pro"]
|
||||
"hasAccessToPreview": false
|
||||
},
|
||||
"target": "gemini-2.5-pro"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"requestedModels": ["gemini-2.5-pro", "auto-gemini-2.5"]
|
||||
},
|
||||
"target": "gemini-2.5-pro"
|
||||
},
|
||||
@@ -1333,7 +1409,7 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
],
|
||||
"lite": [
|
||||
{
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"actions": {
|
||||
"terminal": "silent",
|
||||
"transient": "silent",
|
||||
|
||||
@@ -54,6 +54,11 @@ describe('interactive_commands', () => {
|
||||
suiteName: 'default',
|
||||
suiteType: 'behavioral',
|
||||
name: 'should use non-interactive flags when scaffolding a new app',
|
||||
params: {
|
||||
settings: {
|
||||
plan: false,
|
||||
},
|
||||
},
|
||||
prompt: 'Create a new react application named my-app using vite.',
|
||||
assert: async (rig, result) => {
|
||||
const logs = rig.readToolLogs();
|
||||
|
||||
+24
-4
@@ -76,10 +76,30 @@ export class LLMJudge {
|
||||
|
||||
for (const res of rawResults) {
|
||||
// Remove any punctuation the model might have appended
|
||||
const cleanRes = res.replace(/[^A-Z]/g, '');
|
||||
if (cleanRes.startsWith('YES')) yes++;
|
||||
else if (cleanRes.startsWith('NO')) no++;
|
||||
else other++;
|
||||
const cleanRes = res.toUpperCase().replace(/[^A-Z ]/g, '');
|
||||
if (
|
||||
cleanRes.includes('THE ANSWER IS YES') ||
|
||||
cleanRes.includes('ANSWER IS YES') ||
|
||||
cleanRes.endsWith('YES')
|
||||
) {
|
||||
yes++;
|
||||
} else if (
|
||||
cleanRes.includes('THE ANSWER IS NO') ||
|
||||
cleanRes.includes('ANSWER IS NO') ||
|
||||
cleanRes.endsWith('NO')
|
||||
) {
|
||||
no++;
|
||||
} else if (cleanRes.trim() === 'YES') {
|
||||
yes++;
|
||||
} else if (cleanRes.trim() === 'NO') {
|
||||
no++;
|
||||
} else {
|
||||
// Fallback: look for YES or NO as standalone words or at the end
|
||||
const words = cleanRes.split(/\s+/);
|
||||
if (words.includes('YES')) yes++;
|
||||
else if (words.includes('NO')) no++;
|
||||
else other++;
|
||||
}
|
||||
}
|
||||
|
||||
// Pass if YES > NO and YES > OTHER (plurality)
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
Storage,
|
||||
getProjectHash,
|
||||
SESSION_FILE_PREFIX,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
getErrorMessage,
|
||||
} from '@google/gemini-cli-core';
|
||||
|
||||
@@ -26,8 +25,7 @@ export * from '@google/gemini-cli-test-utils';
|
||||
* The default model used for all evaluations.
|
||||
* Can be overridden by setting the GEMINI_MODEL environment variable.
|
||||
*/
|
||||
export const EVAL_MODEL =
|
||||
process.env['GEMINI_MODEL'] || PREVIEW_GEMINI_FLASH_MODEL;
|
||||
export const EVAL_MODEL = process.env['GEMINI_MODEL'] || 'gemini-3.5-flash';
|
||||
|
||||
// Indicates the consistency expectation for this test.
|
||||
// - ALWAYS_PASSES - Means that the test is expected to pass 100% of the time. These
|
||||
|
||||
Generated
+41
-12
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
@@ -449,7 +449,8 @@
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.11.0.tgz",
|
||||
"integrity": "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==",
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)"
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@bundled-es-modules/cookie": {
|
||||
"version": "2.0.1",
|
||||
@@ -1535,6 +1536,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz",
|
||||
"integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@grpc/proto-loader": "^0.7.13",
|
||||
"@js-sdsl/ordered-map": "^4.4.2"
|
||||
@@ -2212,6 +2214,7 @@
|
||||
"integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^6.0.0",
|
||||
"@octokit/graphql": "^9.0.2",
|
||||
@@ -2392,6 +2395,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
||||
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
@@ -2441,6 +2445,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.0.tgz",
|
||||
"integrity": "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||
},
|
||||
@@ -2815,6 +2820,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.5.0.tgz",
|
||||
"integrity": "sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "2.5.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||
@@ -2848,6 +2854,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.5.0.tgz",
|
||||
"integrity": "sha512-BeJLtU+f5Gf905cJX9vXFQorAr6TAfK3SPvTFqP+scfIpDQEJfRaGJWta7sJgP+m4dNtBf9y3yvBKVAZZtJQVA==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "2.5.0",
|
||||
"@opentelemetry/resources": "2.5.0"
|
||||
@@ -2902,6 +2909,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.5.0.tgz",
|
||||
"integrity": "sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "2.5.0",
|
||||
"@opentelemetry/resources": "2.5.0",
|
||||
@@ -4139,6 +4147,7 @@
|
||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
@@ -4412,6 +4421,7 @@
|
||||
"integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.58.2",
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
@@ -5187,6 +5197,7 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -7304,7 +7315,8 @@
|
||||
"version": "0.0.1581282",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1581282.tgz",
|
||||
"integrity": "sha512-nv7iKtNZQshSW2hKzYNr46nM/Cfh5SEvE2oV0/SEGgc9XupIY5ggf84Cz8eJIkBce7S3bmTAauFD6aysMpnqsQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/dezalgo": {
|
||||
"version": "1.0.4",
|
||||
@@ -7889,6 +7901,7 @@
|
||||
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -8499,6 +8512,7 @@
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.1",
|
||||
@@ -9765,6 +9779,7 @@
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.12.tgz",
|
||||
"integrity": "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
}
|
||||
@@ -10024,6 +10039,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.6.9.tgz",
|
||||
"integrity": "sha512-RL9sSiLQZECnjbmBwjIHOp8yVGdWF7C/uifg7ISv/e+F3nLNsfl7FdUFQs8iZARFMJAYxMFpxW6OW+HSt9drwQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"ansi-escapes": "^7.0.0",
|
||||
"ansi-styles": "^6.2.3",
|
||||
@@ -13799,6 +13815,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
||||
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -13809,6 +13826,7 @@
|
||||
"integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"shell-quote": "^1.6.1",
|
||||
"ws": "^7"
|
||||
@@ -15961,6 +15979,7 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -16183,7 +16202,8 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.20.3",
|
||||
@@ -16191,6 +16211,7 @@
|
||||
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "~0.25.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
@@ -16356,6 +16377,7 @@
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -16423,6 +16445,7 @@
|
||||
"integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.35.0",
|
||||
"@typescript-eslint/types": "8.35.0",
|
||||
@@ -16842,6 +16865,7 @@
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz",
|
||||
"integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -17412,6 +17436,7 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -17424,6 +17449,7 @@
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
|
||||
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/expect": "3.2.4",
|
||||
@@ -18062,6 +18088,7 @@
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
@@ -18077,7 +18104,7 @@
|
||||
},
|
||||
"packages/a2a-server": {
|
||||
"name": "@google/gemini-cli-a2a-server",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"dependencies": {
|
||||
"@a2a-js/sdk": "0.3.11",
|
||||
"@google-cloud/storage": "^7.16.0",
|
||||
@@ -18206,7 +18233,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.16.1",
|
||||
@@ -18354,7 +18381,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@google/gemini-cli-core",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@a2a-js/sdk": "0.3.11",
|
||||
@@ -18498,6 +18525,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz",
|
||||
"integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@grpc/proto-loader": "^0.8.0",
|
||||
"@js-sdsl/ordered-map": "^4.4.2"
|
||||
@@ -18616,6 +18644,7 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -18665,7 +18694,7 @@
|
||||
},
|
||||
"packages/devtools": {
|
||||
"name": "@google/gemini-cli-devtools",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"ws": "^8.16.0"
|
||||
@@ -18680,7 +18709,7 @@
|
||||
},
|
||||
"packages/sdk": {
|
||||
"name": "@google/gemini-cli-sdk",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@google/gemini-cli-core": "file:../core",
|
||||
@@ -18711,7 +18740,7 @@
|
||||
},
|
||||
"packages/test-utils": {
|
||||
"name": "@google/gemini-cli-test-utils",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@google/gemini-cli-core": "file:../core",
|
||||
@@ -18743,7 +18772,7 @@
|
||||
},
|
||||
"packages/vscode-ide-companion": {
|
||||
"name": "gemini-cli-vscode-ide-companion",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"license": "LICENSE",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.23.0",
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
@@ -14,7 +14,7 @@
|
||||
"url": "git+https://github.com/google-gemini/gemini-cli.git"
|
||||
},
|
||||
"config": {
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.42.0-nightly.20260428.g59b2dea0e"
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.42.0-nightly.20260512.gc987b9939"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cross-env NODE_ENV=development node scripts/start.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-a2a-server",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"description": "Gemini CLI A2A Server",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"description": "Gemini CLI",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@@ -27,7 +27,7 @@
|
||||
"dist"
|
||||
],
|
||||
"config": {
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.42.0-nightly.20260428.g59b2dea0e"
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.42.0-nightly.20260512.gc987b9939"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.16.1",
|
||||
|
||||
@@ -19,6 +19,7 @@ import type * as acp from '@agentclientprotocol/sdk';
|
||||
import {
|
||||
AuthType,
|
||||
type Config,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
type MessageBus,
|
||||
type Storage,
|
||||
} from '@google/gemini-cli-core';
|
||||
@@ -208,20 +209,19 @@ describe('AcpSessionManager', () => {
|
||||
expect(response.models?.availableModels).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
modelId: 'auto-gemini-3',
|
||||
modelId: GEMINI_MODEL_ALIAS_AUTO,
|
||||
name: expect.stringContaining('Auto'),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('should include gemini-3.1-flash-lite when useGemini31FlashLite is true', async () => {
|
||||
it('should NOT include retired preview models (none) in available models', async () => {
|
||||
mockConfig.getContentGeneratorConfig = vi.fn().mockReturnValue({
|
||||
apiKey: 'test-key',
|
||||
});
|
||||
mockConfig.getHasAccessToPreviewModel = vi.fn().mockReturnValue(true);
|
||||
mockConfig.getGemini31LaunchedSync = vi.fn().mockReturnValue(true);
|
||||
mockConfig.getGemini31FlashLiteLaunchedSync = vi.fn().mockReturnValue(true);
|
||||
|
||||
const response = await manager.newSession(
|
||||
{
|
||||
@@ -231,14 +231,9 @@ describe('AcpSessionManager', () => {
|
||||
{},
|
||||
);
|
||||
|
||||
expect(response.models?.availableModels).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
modelId: 'gemini-3.1-flash-lite-preview',
|
||||
name: 'gemini-3.1-flash-lite-preview',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
const modelIds =
|
||||
response.models?.availableModels?.map((m) => m.modelId) ?? [];
|
||||
expect(modelIds).not.toContain('none');
|
||||
});
|
||||
|
||||
it('should return modes with plan mode when plan is enabled', async () => {
|
||||
|
||||
@@ -10,8 +10,7 @@ import {
|
||||
type ToolCallConfirmationDetails,
|
||||
Kind,
|
||||
ApprovalMode,
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
@@ -19,10 +18,11 @@ import {
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
getDisplayString,
|
||||
AuthType,
|
||||
ToolConfirmationOutcome,
|
||||
getAutoModelDescription,
|
||||
} from '@google/gemini-cli-core';
|
||||
import type * as acp from '@agentclientprotocol/sdk';
|
||||
import { z } from 'zod';
|
||||
@@ -262,11 +262,10 @@ export function buildAvailableModels(
|
||||
}>;
|
||||
currentModelId: string;
|
||||
} {
|
||||
const preferredModel = config.getModel() || DEFAULT_GEMINI_MODEL_AUTO;
|
||||
const preferredModel = config.getModel() || GEMINI_MODEL_ALIAS_AUTO;
|
||||
const shouldShowPreviewModels = config.getHasAccessToPreviewModel();
|
||||
const useGemini31 = config.getGemini31LaunchedSync?.() ?? false;
|
||||
const useGemini31FlashLite =
|
||||
config.getGemini31FlashLiteLaunchedSync?.() ?? false;
|
||||
const useGemini3_5Flash = config.hasGemini35FlashGAAccess?.() ?? false;
|
||||
const selectedAuthType = settings.merged.security.auth.selectedType;
|
||||
const useCustomToolModel =
|
||||
useGemini31 && selectedAuthType === AuthType.USE_GEMINI;
|
||||
@@ -278,7 +277,7 @@ export function buildAvailableModels(
|
||||
) {
|
||||
const options = config.getModelConfigService().getAvailableModelOptions({
|
||||
useGemini3_1: useGemini31,
|
||||
useGemini3_1FlashLite: useGemini31FlashLite,
|
||||
useGemini3_5Flash,
|
||||
useCustomTools: useCustomToolModel,
|
||||
hasAccessToPreview: shouldShowPreviewModels,
|
||||
});
|
||||
@@ -290,37 +289,35 @@ export function buildAvailableModels(
|
||||
}
|
||||
|
||||
// --- LEGACY PATH ---
|
||||
const defaultFlashModel =
|
||||
config.getDefaultGeminiFlashModel?.() ?? DEFAULT_GEMINI_FLASH_MODEL;
|
||||
const previewFlashModel =
|
||||
config.getPreviewGeminiFlashModel?.() ?? PREVIEW_GEMINI_FLASH_MODEL;
|
||||
|
||||
const mainOptions = [
|
||||
{
|
||||
value: DEFAULT_GEMINI_MODEL_AUTO,
|
||||
title: getDisplayString(DEFAULT_GEMINI_MODEL_AUTO),
|
||||
description:
|
||||
'Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash',
|
||||
value: GEMINI_MODEL_ALIAS_AUTO,
|
||||
title: getDisplayString(GEMINI_MODEL_ALIAS_AUTO, config),
|
||||
description: getAutoModelDescription(
|
||||
shouldShowPreviewModels,
|
||||
useGemini31,
|
||||
useGemini3_5Flash,
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
if (shouldShowPreviewModels) {
|
||||
mainOptions.unshift({
|
||||
value: PREVIEW_GEMINI_MODEL_AUTO,
|
||||
title: getDisplayString(PREVIEW_GEMINI_MODEL_AUTO),
|
||||
description: useGemini31
|
||||
? 'Let Gemini CLI decide the best model for the task: gemini-3.1-pro, gemini-3-flash'
|
||||
: 'Let Gemini CLI decide the best model for the task: gemini-3-pro, gemini-3-flash',
|
||||
});
|
||||
}
|
||||
|
||||
const manualOptions = [
|
||||
{
|
||||
value: DEFAULT_GEMINI_MODEL,
|
||||
title: getDisplayString(DEFAULT_GEMINI_MODEL),
|
||||
title: getDisplayString(DEFAULT_GEMINI_MODEL, config),
|
||||
},
|
||||
{
|
||||
value: DEFAULT_GEMINI_FLASH_MODEL,
|
||||
title: getDisplayString(DEFAULT_GEMINI_FLASH_MODEL),
|
||||
value: defaultFlashModel,
|
||||
title: getDisplayString(defaultFlashModel, config),
|
||||
},
|
||||
{
|
||||
value: DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
title: getDisplayString(DEFAULT_GEMINI_FLASH_LITE_MODEL),
|
||||
title: getDisplayString(DEFAULT_GEMINI_FLASH_LITE_MODEL, config),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -336,18 +333,18 @@ export function buildAvailableModels(
|
||||
const previewOptions = [
|
||||
{
|
||||
value: previewProValue,
|
||||
title: getDisplayString(previewProModel),
|
||||
title: getDisplayString(previewProModel, config),
|
||||
},
|
||||
{
|
||||
value: PREVIEW_GEMINI_FLASH_MODEL,
|
||||
title: getDisplayString(PREVIEW_GEMINI_FLASH_MODEL),
|
||||
value: previewFlashModel,
|
||||
title: getDisplayString(previewFlashModel, config),
|
||||
},
|
||||
];
|
||||
|
||||
if (useGemini31FlashLite) {
|
||||
if (PREVIEW_GEMINI_FLASH_LITE_MODEL !== 'none') {
|
||||
previewOptions.push({
|
||||
value: PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
title: getDisplayString(PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL),
|
||||
value: PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
title: getDisplayString(PREVIEW_GEMINI_FLASH_LITE_MODEL, config),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,14 +12,14 @@ import { waitFor } from '../../test-utils/async.js';
|
||||
import { createMockSettings } from '../../test-utils/settings.js';
|
||||
import {
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
PREVIEW_GEMINI_3_1_MODEL,
|
||||
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
AuthType,
|
||||
} from '@google/gemini-cli-core';
|
||||
import type { Config, ModelSlashCommandEvent } from '@google/gemini-cli-core';
|
||||
@@ -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),
|
||||
@@ -42,7 +47,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
mockModelSlashCommandEvent(model);
|
||||
}
|
||||
},
|
||||
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL: 'gemini-3.1-flash-lite-preview',
|
||||
PREVIEW_GEMINI_FLASH_LITE_MODEL: 'none',
|
||||
};
|
||||
});
|
||||
|
||||
@@ -62,7 +67,6 @@ describe('<ModelDialog />', () => {
|
||||
getHasAccessToPreviewModel: () => boolean;
|
||||
getIdeMode: () => boolean;
|
||||
getGemini31LaunchedSync: () => boolean;
|
||||
getGemini31FlashLiteLaunchedSync: () => boolean;
|
||||
getProModelNoAccess: () => Promise<boolean>;
|
||||
getProModelNoAccessSync: () => boolean;
|
||||
getExperimentalGemma: () => boolean;
|
||||
@@ -83,7 +87,6 @@ describe('<ModelDialog />', () => {
|
||||
getHasAccessToPreviewModel: mockGetHasAccessToPreviewModel,
|
||||
getIdeMode: () => false,
|
||||
getGemini31LaunchedSync: mockGetGemini31LaunchedSync,
|
||||
getGemini31FlashLiteLaunchedSync: mockGetGemini31FlashLiteLaunchedSync,
|
||||
getProModelNoAccess: mockGetProModelNoAccess,
|
||||
getProModelNoAccessSync: mockGetProModelNoAccessSync,
|
||||
getExperimentalGemma: () => false,
|
||||
@@ -93,17 +96,15 @@ describe('<ModelDialog />', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
mockGetModel.mockReturnValue(DEFAULT_GEMINI_MODEL_AUTO);
|
||||
mockGetModel.mockReturnValue(GEMINI_MODEL_ALIAS_AUTO);
|
||||
mockGetHasAccessToPreviewModel.mockReturnValue(false);
|
||||
mockGetGemini31LaunchedSync.mockReturnValue(false);
|
||||
mockGetGemini31FlashLiteLaunchedSync.mockReturnValue(false);
|
||||
mockGetProModelNoAccess.mockResolvedValue(false);
|
||||
mockGetProModelNoAccessSync.mockReturnValue(false);
|
||||
|
||||
// Default implementation for getDisplayString
|
||||
mockGetDisplayString.mockImplementation((val: string) => {
|
||||
if (val === 'auto-gemini-2.5') return 'Auto (Gemini 2.5)';
|
||||
if (val === 'auto-gemini-3') return 'Auto (Preview)';
|
||||
if (val === 'auto') return 'Auto';
|
||||
return val;
|
||||
});
|
||||
});
|
||||
@@ -153,17 +154,13 @@ describe('<ModelDialog />', () => {
|
||||
expect(output).not.toContain(DEFAULT_GEMINI_MODEL);
|
||||
expect(output).not.toContain(PREVIEW_GEMINI_MODEL);
|
||||
|
||||
// Verify order: Flash Preview -> Flash Lite Preview -> Flash -> Flash Lite
|
||||
// Verify order: Flash Preview -> Flash Lite (Preview/Default) -> Flash
|
||||
const flashPreviewIdx = output.indexOf(PREVIEW_GEMINI_FLASH_MODEL);
|
||||
const flashLitePreviewIdx = output.indexOf(
|
||||
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
);
|
||||
const flashIdx = output.indexOf(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
const flashLiteIdx = output.indexOf(DEFAULT_GEMINI_FLASH_LITE_MODEL);
|
||||
const flashIdx = output.indexOf(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
|
||||
expect(flashPreviewIdx).toBeLessThan(flashLitePreviewIdx);
|
||||
expect(flashLitePreviewIdx).toBeLessThan(flashIdx);
|
||||
expect(flashIdx).toBeLessThan(flashLiteIdx);
|
||||
expect(flashPreviewIdx).toBeLessThan(flashLiteIdx);
|
||||
expect(flashLiteIdx).toBeLessThan(flashIdx);
|
||||
|
||||
expect(output).not.toContain('Auto');
|
||||
unmount();
|
||||
@@ -234,7 +231,7 @@ describe('<ModelDialog />', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockSetModel).toHaveBeenCalledWith(
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
true, // Session only by default
|
||||
);
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
@@ -292,7 +289,7 @@ describe('<ModelDialog />', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockSetModel).toHaveBeenCalledWith(
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
false, // Persist enabled
|
||||
);
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
@@ -355,7 +352,7 @@ describe('<ModelDialog />', () => {
|
||||
mockGetModel.mockReturnValue(DEFAULT_GEMINI_MODEL);
|
||||
mockGetDisplayString.mockImplementation((val: string) => {
|
||||
if (val === DEFAULT_GEMINI_MODEL) return 'My Custom Model Display';
|
||||
if (val === 'auto-gemini-2.5') return 'Auto (Gemini 2.5)';
|
||||
if (val === 'auto') return 'Auto';
|
||||
return val;
|
||||
});
|
||||
const { lastFrame, unmount } = await renderComponent();
|
||||
@@ -369,9 +366,9 @@ describe('<ModelDialog />', () => {
|
||||
mockGetHasAccessToPreviewModel.mockReturnValue(true);
|
||||
});
|
||||
|
||||
it('shows Auto (Preview) in main view when access is granted', async () => {
|
||||
it('shows Auto in main view when access is granted', async () => {
|
||||
const { lastFrame, unmount } = await renderComponent();
|
||||
expect(lastFrame()).toContain('Auto (Preview)');
|
||||
expect(lastFrame()).toContain('Auto');
|
||||
unmount();
|
||||
});
|
||||
|
||||
@@ -449,7 +446,7 @@ describe('<ModelDialog />', () => {
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('shows Flash Lite Preview model regardless of tier when flag is enabled', async () => {
|
||||
it('does not show Flash Lite Preview model when it is retired (none) even if flag is enabled', async () => {
|
||||
mockGetProModelNoAccessSync.mockReturnValue(false);
|
||||
mockGetProModelNoAccess.mockResolvedValue(false);
|
||||
mockGetHasAccessToPreviewModel.mockReturnValue(true);
|
||||
@@ -468,7 +465,8 @@ describe('<ModelDialog />', () => {
|
||||
await waitUntilReady();
|
||||
|
||||
const output = lastFrame();
|
||||
expect(output).toContain(PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL);
|
||||
expect(output).not.toContain(PREVIEW_GEMINI_FLASH_LITE_MODEL);
|
||||
expect(output).toContain(DEFAULT_GEMINI_FLASH_LITE_MODEL);
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,12 +13,11 @@ import {
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
PREVIEW_GEMINI_3_1_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
GEMMA_4_31B_IT_MODEL,
|
||||
GEMMA_4_26B_A4B_IT_MODEL,
|
||||
ModelSlashCommandEvent,
|
||||
@@ -27,6 +26,7 @@ import {
|
||||
AuthType,
|
||||
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
|
||||
isProModel,
|
||||
getAutoModelDescription,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { useKeypress } from '../hooks/useKeypress.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
@@ -63,12 +63,11 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
}, [config]);
|
||||
|
||||
// Determine the Preferred Model (read once when the dialog opens).
|
||||
const preferredModel = config?.getModel() || DEFAULT_GEMINI_MODEL_AUTO;
|
||||
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;
|
||||
const useGemini3_5Flash = config?.hasGemini35FlashGAAccess?.() ?? false;
|
||||
const selectedAuthType = settings.merged.security.auth.selectedType;
|
||||
const useCustomToolModel =
|
||||
useGemini31 && selectedAuthType === AuthType.USE_GEMINI;
|
||||
@@ -87,16 +86,21 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
: '';
|
||||
}
|
||||
|
||||
const defaultFlashModel =
|
||||
config?.getDefaultGeminiFlashModel?.() ?? DEFAULT_GEMINI_FLASH_MODEL;
|
||||
const previewFlashModel =
|
||||
config?.getPreviewGeminiFlashModel?.() ?? PREVIEW_GEMINI_FLASH_MODEL;
|
||||
|
||||
const manualModels = [
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
defaultFlashModel,
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
PREVIEW_GEMINI_3_1_MODEL,
|
||||
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
|
||||
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
];
|
||||
PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
previewFlashModel,
|
||||
].filter((m) => m !== 'none');
|
||||
if (manualModels.includes(preferredModel)) {
|
||||
return preferredModel;
|
||||
}
|
||||
@@ -121,7 +125,6 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
},
|
||||
{ isActive: true },
|
||||
);
|
||||
|
||||
const mainOptions = useMemo(() => {
|
||||
// --- DYNAMIC PATH ---
|
||||
if (
|
||||
@@ -132,7 +135,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
.getModelConfigService()
|
||||
.getAvailableModelOptions({
|
||||
useGemini3_1: useGemini31,
|
||||
useGemini3_1FlashLite: useGemini31FlashLite,
|
||||
useGemini3_5Flash,
|
||||
useCustomTools: useCustomToolModel,
|
||||
hasAccessToPreview: shouldShowPreviewModels,
|
||||
hasAccessToProModel,
|
||||
@@ -161,11 +164,14 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
// --- LEGACY PATH ---
|
||||
const list = [
|
||||
{
|
||||
value: DEFAULT_GEMINI_MODEL_AUTO,
|
||||
title: getDisplayString(DEFAULT_GEMINI_MODEL_AUTO),
|
||||
description:
|
||||
'Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash',
|
||||
key: DEFAULT_GEMINI_MODEL_AUTO,
|
||||
value: GEMINI_MODEL_ALIAS_AUTO,
|
||||
title: getDisplayString(GEMINI_MODEL_ALIAS_AUTO),
|
||||
description: getAutoModelDescription(
|
||||
shouldShowPreviewModels,
|
||||
useGemini31,
|
||||
useGemini3_5Flash,
|
||||
),
|
||||
key: GEMINI_MODEL_ALIAS_AUTO,
|
||||
},
|
||||
{
|
||||
value: 'Manual',
|
||||
@@ -177,23 +183,13 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
},
|
||||
];
|
||||
|
||||
if (shouldShowPreviewModels) {
|
||||
list.unshift({
|
||||
value: PREVIEW_GEMINI_MODEL_AUTO,
|
||||
title: getDisplayString(PREVIEW_GEMINI_MODEL_AUTO),
|
||||
description: useGemini31
|
||||
? 'Let Gemini CLI decide the best model for the task: gemini-3.1-pro, gemini-3-flash'
|
||||
: 'Let Gemini CLI decide the best model for the task: gemini-3-pro, gemini-3-flash',
|
||||
key: PREVIEW_GEMINI_MODEL_AUTO,
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}, [
|
||||
config,
|
||||
shouldShowPreviewModels,
|
||||
manualModelSelected,
|
||||
useGemini31,
|
||||
useGemini31FlashLite,
|
||||
useGemini3_5Flash,
|
||||
useCustomToolModel,
|
||||
hasAccessToProModel,
|
||||
]);
|
||||
@@ -208,7 +204,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
.getModelConfigService()
|
||||
.getAvailableModelOptions({
|
||||
useGemini3_1: useGemini31,
|
||||
useGemini3_1FlashLite: useGemini31FlashLite,
|
||||
useGemini3_5Flash,
|
||||
useCustomTools: useCustomToolModel,
|
||||
hasAccessToPreview: shouldShowPreviewModels,
|
||||
hasAccessToProModel,
|
||||
@@ -225,22 +221,29 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
|
||||
// --- LEGACY PATH ---
|
||||
const showGemmaModels = config?.getExperimentalGemma() ?? false;
|
||||
const defaultFlashModel =
|
||||
config?.getDefaultGeminiFlashModel?.() ?? DEFAULT_GEMINI_FLASH_MODEL;
|
||||
const previewFlashModel =
|
||||
config?.getPreviewGeminiFlashModel?.() ?? PREVIEW_GEMINI_FLASH_MODEL;
|
||||
|
||||
const options = [
|
||||
{
|
||||
value: DEFAULT_GEMINI_MODEL,
|
||||
title: getDisplayString(DEFAULT_GEMINI_MODEL),
|
||||
title: getDisplayString(DEFAULT_GEMINI_MODEL, config ?? undefined),
|
||||
key: DEFAULT_GEMINI_MODEL,
|
||||
},
|
||||
{
|
||||
value: DEFAULT_GEMINI_FLASH_MODEL,
|
||||
title: getDisplayString(DEFAULT_GEMINI_FLASH_MODEL),
|
||||
key: DEFAULT_GEMINI_FLASH_MODEL,
|
||||
value: DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
title: getDisplayString(
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
config ?? undefined,
|
||||
),
|
||||
key: DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
},
|
||||
{
|
||||
value: DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
title: getDisplayString(DEFAULT_GEMINI_FLASH_LITE_MODEL),
|
||||
key: DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
value: defaultFlashModel,
|
||||
title: getDisplayString(defaultFlashModel, config ?? undefined),
|
||||
key: defaultFlashModel,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -248,12 +251,15 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
options.push(
|
||||
{
|
||||
value: GEMMA_4_31B_IT_MODEL,
|
||||
title: getDisplayString(GEMMA_4_31B_IT_MODEL),
|
||||
title: getDisplayString(GEMMA_4_31B_IT_MODEL, config ?? undefined),
|
||||
key: GEMMA_4_31B_IT_MODEL,
|
||||
},
|
||||
{
|
||||
value: GEMMA_4_26B_A4B_IT_MODEL,
|
||||
title: getDisplayString(GEMMA_4_26B_A4B_IT_MODEL),
|
||||
title: getDisplayString(
|
||||
GEMMA_4_26B_A4B_IT_MODEL,
|
||||
config ?? undefined,
|
||||
),
|
||||
key: GEMMA_4_26B_A4B_IT_MODEL,
|
||||
},
|
||||
);
|
||||
@@ -271,21 +277,24 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
const previewOptions = [
|
||||
{
|
||||
value: previewProValue,
|
||||
title: getDisplayString(previewProModel),
|
||||
title: getDisplayString(previewProModel, config ?? undefined),
|
||||
key: previewProModel,
|
||||
},
|
||||
{
|
||||
value: PREVIEW_GEMINI_FLASH_MODEL,
|
||||
title: getDisplayString(PREVIEW_GEMINI_FLASH_MODEL),
|
||||
key: PREVIEW_GEMINI_FLASH_MODEL,
|
||||
value: previewFlashModel,
|
||||
title: getDisplayString(previewFlashModel, config ?? undefined),
|
||||
key: previewFlashModel,
|
||||
},
|
||||
];
|
||||
|
||||
if (useGemini31FlashLite) {
|
||||
if (PREVIEW_GEMINI_FLASH_LITE_MODEL !== 'none') {
|
||||
previewOptions.push({
|
||||
value: PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
title: getDisplayString(PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL),
|
||||
key: PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
value: PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
title: getDisplayString(
|
||||
PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
config ?? undefined,
|
||||
),
|
||||
key: PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -301,13 +310,23 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
}, [
|
||||
shouldShowPreviewModels,
|
||||
useGemini31,
|
||||
useGemini31FlashLite,
|
||||
useGemini3_5Flash,
|
||||
useCustomToolModel,
|
||||
hasAccessToProModel,
|
||||
config,
|
||||
]);
|
||||
|
||||
const options = view === 'main' ? mainOptions : manualOptions;
|
||||
const options = useMemo(() => {
|
||||
const rawOptions = view === 'main' ? mainOptions : manualOptions;
|
||||
const seen = new Set<string>();
|
||||
return rawOptions.filter((option) => {
|
||||
if (seen.has(option.value)) {
|
||||
return false;
|
||||
}
|
||||
seen.add(option.value);
|
||||
return true;
|
||||
});
|
||||
}, [view, mainOptions, manualOptions]);
|
||||
|
||||
// Calculate the initial index based on the preferred model.
|
||||
const initialIndex = useMemo(() => {
|
||||
|
||||
@@ -353,6 +353,49 @@ describe('<ModelStatsDisplay />', () => {
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should resolve gemini-3-flash to gemini-3.5-flash via getDisplayString', async () => {
|
||||
const { lastFrame, unmount } = await renderWithMockedStats({
|
||||
models: {
|
||||
'gemini-3-flash': {
|
||||
api: { totalRequests: 1, totalErrors: 0, totalLatencyMs: 100 },
|
||||
tokens: {
|
||||
input: 5,
|
||||
prompt: 10,
|
||||
candidates: 20,
|
||||
total: 30,
|
||||
cached: 5,
|
||||
thoughts: 2,
|
||||
tool: 1,
|
||||
},
|
||||
roles: {},
|
||||
},
|
||||
},
|
||||
tools: {
|
||||
totalCalls: 0,
|
||||
totalSuccess: 0,
|
||||
totalFail: 0,
|
||||
totalDurationMs: 0,
|
||||
totalDecisions: {
|
||||
accept: 0,
|
||||
reject: 0,
|
||||
modify: 0,
|
||||
[ToolCallDecision.AUTO_ACCEPT]: 0,
|
||||
},
|
||||
byName: {},
|
||||
},
|
||||
files: {
|
||||
totalLinesAdded: 0,
|
||||
totalLinesRemoved: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('gemini-3.5-flash');
|
||||
expect(output).not.toContain('gemini-3-flash');
|
||||
expect(output).toMatchSnapshot();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should handle models with long names (gemini-3-*-preview) without layout breaking', async () => {
|
||||
const { lastFrame, unmount } = await renderWithMockedStats(
|
||||
{
|
||||
|
||||
@@ -299,7 +299,7 @@ export const ModelStatsDisplay: React.FC<ModelStatsDisplayProps> = ({
|
||||
},
|
||||
...modelNames.map((name) => ({
|
||||
key: name,
|
||||
header: name,
|
||||
header: getDisplayString(name),
|
||||
flexGrow: 1,
|
||||
renderCell: (row: StatRowData) => {
|
||||
// Don't render anything for section headers in model columns
|
||||
|
||||
+6
@@ -6,6 +6,12 @@ Sort: s Reverse: r First/Last: g/G
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`SessionBrowser Search and Navigation Components > NavigationHelp renders correctly 2`] = `
|
||||
"Navigate: ↑/↓ Resume: Enter Search: / Delete: x Quit: q
|
||||
Sort: s Reverse: r First/Last: g/G
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`SessionBrowser Search and Navigation Components > NoResultsDisplay renders correctly 1`] = `
|
||||
"
|
||||
No sessions found matching 'no match'.
|
||||
|
||||
+6
@@ -6,6 +6,12 @@ exports[`SessionBrowser UI States > SessionBrowserEmpty renders correctly 1`] =
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`SessionBrowser UI States > SessionBrowserEmpty renders correctly 2`] = `
|
||||
" No auto-saved conversations found.
|
||||
Press q to exit
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`SessionBrowser UI States > SessionBrowserError renders correctly 1`] = `
|
||||
" Error: Test error message
|
||||
Press q to exit
|
||||
|
||||
@@ -131,6 +131,33 @@ describe('<StatsDisplay />', () => {
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('resolves gemini-3-flash to gemini-3.5-flash in the model usage table', async () => {
|
||||
const metrics = createTestMetrics({
|
||||
models: {
|
||||
'gemini-3-flash': {
|
||||
api: { totalRequests: 5, totalErrors: 0, totalLatencyMs: 3000 },
|
||||
tokens: {
|
||||
input: 1000,
|
||||
prompt: 2000,
|
||||
candidates: 3000,
|
||||
total: 5000,
|
||||
cached: 500,
|
||||
thoughts: 100,
|
||||
tool: 50,
|
||||
},
|
||||
roles: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { lastFrame } = await renderWithMockedStats(metrics);
|
||||
const output = lastFrame();
|
||||
|
||||
expect(output).toContain('gemini-3.5-flash');
|
||||
expect(output).not.toContain('gemini-3-flash\u0020'); // Avoid matching parts of substrings if not intended
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders role breakdown correctly under models', async () => {
|
||||
const metrics = createTestMetrics({
|
||||
models: {
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
import { computeSessionStats } from '../utils/computeStats.js';
|
||||
import { useSettings } from '../contexts/SettingsContext.js';
|
||||
import type { QuotaStats } from '../types.js';
|
||||
import { LlmRole } from '@google/gemini-cli-core';
|
||||
import { LlmRole, getDisplayString } from '@google/gemini-cli-core';
|
||||
|
||||
// A more flexible and powerful StatRow component
|
||||
interface StatRowProps {
|
||||
@@ -101,7 +101,7 @@ const ModelUsageTable: React.FC<ModelUsageTableProps> = ({ models }) => {
|
||||
Object.entries(models).forEach(([name, metrics]) => {
|
||||
rows.push({
|
||||
name,
|
||||
displayName: name,
|
||||
displayName: getDisplayString(name),
|
||||
requests: metrics.api.totalRequests,
|
||||
cachedTokens: metrics.tokens.cached.toLocaleString(),
|
||||
inputTokens: metrics.tokens.prompt.toLocaleString(),
|
||||
|
||||
+50
@@ -84,6 +84,33 @@ exports[`AlternateBufferQuittingDisplay > renders with history but no pending it
|
||||
|
||||
|
||||
|
||||
Tips for getting started:
|
||||
1. Create GEMINI.md files to customize your interactions
|
||||
2. /help for more information
|
||||
3. Ask coding questions, edit code or run commands
|
||||
4. Be specific for the best results
|
||||
╭──────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ tool1 Description for tool 1 │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────╯
|
||||
╭──────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ tool2 Description for tool 2 │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`AlternateBufferQuittingDisplay > renders with pending items but no history > with_history_no_pending 1`] = `
|
||||
"
|
||||
▝▜▄ ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
|
||||
▝▜▄ █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
|
||||
▗▟▀ ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
|
||||
▝▀ ▀▀▀▀▘▝▀▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
|
||||
|
||||
Gemini CLI v0.10.0
|
||||
|
||||
|
||||
|
||||
Tips for getting started:
|
||||
1. Create GEMINI.md files to customize your interactions
|
||||
2. /help for more information
|
||||
@@ -123,6 +150,29 @@ Tips for getting started:
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`AlternateBufferQuittingDisplay > renders with user and gemini messages > with_confirming_tool 1`] = `
|
||||
"
|
||||
▝▜▄ ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
|
||||
▝▜▄ █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
|
||||
▗▟▀ ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
|
||||
▝▀ ▀▀▀▀▘▝▀▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
|
||||
|
||||
Gemini CLI v0.10.0
|
||||
|
||||
|
||||
|
||||
Tips for getting started:
|
||||
1. Create GEMINI.md files to customize your interactions
|
||||
2. /help for more information
|
||||
3. Ask coding questions, edit code or run commands
|
||||
4. Be specific for the best results
|
||||
|
||||
Action Required (was prompted):
|
||||
|
||||
? confirming_tool Confirming tool description
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`AlternateBufferQuittingDisplay > renders with user and gemini messages > with_user_gemini_messages 1`] = `
|
||||
"
|
||||
▝▜▄ ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="88" viewBox="0 0 920 88">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="88" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#333333" textLength="720" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────╮</text>
|
||||
<text x="0" y="19" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="19" fill="#4796e4" textLength="9" lengthAdjust="spacingAndGlyphs">L</text>
|
||||
<text x="27" y="19" fill="#6688d9" textLength="9" lengthAdjust="spacingAndGlyphs">i</text>
|
||||
<text x="36" y="19" fill="#847ace" textLength="9" lengthAdjust="spacingAndGlyphs">n</text>
|
||||
<text x="45" y="19" fill="#9974b4" textLength="9" lengthAdjust="spacingAndGlyphs">e</text>
|
||||
<text x="63" y="19" fill="#c3677f" textLength="9" lengthAdjust="spacingAndGlyphs">1</text>
|
||||
<text x="711" y="19" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="18" y="36" fill="#ffffff" textLength="54" lengthAdjust="spacingAndGlyphs">Line 2</text>
|
||||
<text x="711" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="53" fill="#333333" textLength="720" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -7,6 +7,13 @@ exports[`Banner > handles newlines in text 1`] = `
|
||||
╰──────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
exports[`Banner > handles newlines in text 2`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Line 1 │
|
||||
│ Line 2 │
|
||||
╰──────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
exports[`Banner > renders in info mode 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Info Message │
|
||||
|
||||
@@ -11,6 +11,17 @@ exports[`<Checklist /> > renders expanded view correctly 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<Checklist /> > renders expanded view correctly 2`] = `
|
||||
"────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Test List 1/3 completed (toggle me)
|
||||
|
||||
✓ Task 1
|
||||
» Task 2
|
||||
☐ Task 3
|
||||
✗ Task 4
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<Checklist /> > renders summary view correctly (collapsed) 1`] = `
|
||||
"────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Test List 1/3 completed (toggle me) » Task 2
|
||||
|
||||
@@ -5,6 +5,11 @@ exports[`<ContextSummaryDisplay /> > should not render empty parts 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<ContextSummaryDisplay /> > should not render empty parts 2`] = `
|
||||
" 1 open file (F4 to view)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<ContextSummaryDisplay /> > should render on a single line on a wide screen 1`] = `
|
||||
" 1 open file (F4 to view) · 1 GEMINI.md file · 1 MCP server · 1 skill
|
||||
"
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`LoopDetectionConfirmation > contains the expected options 1`] = `
|
||||
" ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ? A potential loop was detected │
|
||||
│ │
|
||||
│ This can happen due to repetitive tool calls or other model behavior. Do you want to keep loop │
|
||||
│ detection enabled or disable it for this session? │
|
||||
│ │
|
||||
│ ● 1. Keep loop detection enabled (esc) │
|
||||
│ 2. Disable loop detection for this session │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`LoopDetectionConfirmation > renders correctly 1`] = `
|
||||
" ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ? A potential loop was detected │
|
||||
|
||||
@@ -215,3 +215,26 @@ exports[`<ModelStatsDisplay /> > should render "no API calls" message when there
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<ModelStatsDisplay /> > should resolve gemini-3-flash to gemini-3.5-flash via getDisplayString 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ Model Stats For Nerds │
|
||||
│ │
|
||||
│ │
|
||||
│ Metric gemini-3.5-flash │
|
||||
│ ────────────────────────────────────────────────────────────────────────────────────────────── │
|
||||
│ API │
|
||||
│ Requests 1 │
|
||||
│ Errors 0 (0.0%) │
|
||||
│ Avg Latency 100ms │
|
||||
│ Tokens │
|
||||
│ Total 30 │
|
||||
│ ↳ Input 5 │
|
||||
│ ↳ Cache Reads 5 (50.0%) │
|
||||
│ ↳ Thoughts 2 │
|
||||
│ ↳ Tool 1 │
|
||||
│ ↳ Output 20 │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -16,6 +16,22 @@ exports[`ShortcutsHelp > renders correctly in 'narrow' mode on 'linux' 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ShortcutsHelp > renders correctly in 'narrow' mode on 'linux' 2`] = `
|
||||
"────────────────────────────────────────
|
||||
Shortcuts See /help for more
|
||||
! shell mode
|
||||
@ select file or folder
|
||||
Double Esc clear & rewind
|
||||
Tab focus UI
|
||||
Ctrl+Y YOLO mode
|
||||
Shift+Tab cycle mode
|
||||
Ctrl+V paste images
|
||||
Alt+M raw markdown mode
|
||||
Ctrl+R reverse-search history
|
||||
Ctrl+G open external editor
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ShortcutsHelp > renders correctly in 'narrow' mode on 'mac' 1`] = `
|
||||
"────────────────────────────────────────
|
||||
Shortcuts See /help for more
|
||||
|
||||
@@ -292,3 +292,30 @@ exports[`<StatsDisplay /> > renders role breakdown correctly under models 1`] =
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<StatsDisplay /> > resolves gemini-3-flash to gemini-3.5-flash in the model usage table 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ Session Stats │
|
||||
│ │
|
||||
│ Interaction Summary │
|
||||
│ Session ID: test-session-id │
|
||||
│ Tool Calls: 0 ( ✓ 0 x 0 ) │
|
||||
│ Success Rate: 0.0% │
|
||||
│ │
|
||||
│ Performance │
|
||||
│ Wall Time: 1s │
|
||||
│ Agent Active: 3.0s │
|
||||
│ » API Time: 3.0s (100.0%) │
|
||||
│ » Tool Time: 0s (0.0%) │
|
||||
│ │
|
||||
│ │
|
||||
│ Model Usage │
|
||||
│ Use /model to view model quota information │
|
||||
│ │
|
||||
│ Model Reqs Input Tokens Cache Reads Output Tokens │
|
||||
│ ────────────────────────────────────────────────────────────────────────────────────────────── │
|
||||
│ gemini-3.5-flash 5 2,000 500 3,000 │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -25,6 +25,31 @@ exports[`Initial Theme Selection > should default to a dark theme when terminal
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Initial Theme Selection > should default to a dark theme when terminal background is dark and no theme is set 2`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ > Select Theme Preview │
|
||||
│ ▲ ┌─────────────────────────────────────────────────┐ │
|
||||
│ 1. ANSI Dark │ │ │
|
||||
│ 2. Atom One Dark │ 1 # function │ │
|
||||
│ 3. Ayu Dark │ 2 def fibonacci(n): │ │
|
||||
│ ● 4. Default Dark (Matches terminal) │ 3 a, b = 0, 1 │ │
|
||||
│ 5. Dracula Dark │ 4 for _ in range(n): │ │
|
||||
│ 6. GitHub Dark │ 5 a, b = b, a + b │ │
|
||||
│ 7. GitHub Dark Colorblind Dark │ 6 return a │ │
|
||||
│ 8. Holiday Dark │ │ │
|
||||
│ 9. Shades Of Purple Dark │ 1 - print("Hello, " + name) │ │
|
||||
│ 10. Solarized Dark │ 1 + print(f"Hello, {name}!") │ │
|
||||
│ 11. Tokyo Night Dark │ │ │
|
||||
│ 12. ANSI Light (Incompatible) └─────────────────────────────────────────────────┘ │
|
||||
│ ▼ │
|
||||
│ │
|
||||
│ (Use Enter to select, Tab to configure scope, Esc to close) │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Initial Theme Selection > should default to a light theme when terminal background is light and no theme is set 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
@@ -89,6 +114,20 @@ exports[`ThemeDialog Snapshots > should render correctly in scope selector mode
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ThemeDialog Snapshots > should render correctly in scope selector mode 2`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ > Apply To │
|
||||
│ ● 1. User Settings │
|
||||
│ 2. Workspace Settings │
|
||||
│ 3. System Settings │
|
||||
│ │
|
||||
│ (Use Enter to apply scope, Tab to select theme, Esc to close) │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ThemeDialog Snapshots > should render correctly in theme selection mode (isDevelopment: false) 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
|
||||
@@ -18,3 +18,12 @@ Tips for getting started:
|
||||
3. Be specific for the best results
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tips > 'renders fewer tips when GEMINI.md exi…' 2`] = `
|
||||
"
|
||||
Tips for getting started:
|
||||
1. /help for more information
|
||||
2. Ask coding questions, edit code or run commands
|
||||
3. Be specific for the best results
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -41,6 +41,27 @@ exports[`<ToolStatsDisplay /> > should display stats for multiple tools correctl
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<ToolStatsDisplay /> > should display stats for multiple tools correctly 2`] = `
|
||||
"╭────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ Tool Stats For Nerds │
|
||||
│ │
|
||||
│ Tool Name Calls Success Rate Avg Duration │
|
||||
│ ──────────────────────────────────────────────────────────────── │
|
||||
│ tool-a 2 50.0% 100ms │
|
||||
│ tool-b 1 100.0% 100ms │
|
||||
│ │
|
||||
│ User Decision Summary │
|
||||
│ Total Reviewed Suggestions: 3 │
|
||||
│ » Accepted: 1 │
|
||||
│ » Rejected: 1 │
|
||||
│ » Modified: 1 │
|
||||
│ ──────────────────────────────────────────────────────────────── │
|
||||
│ Overall Agreement Rate: 33.3% │
|
||||
╰────────────────────────────────────────────────────────────────────╯
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<ToolStatsDisplay /> > should handle large values without wrapping or overlapping 1`] = `
|
||||
"╭────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
|
||||
@@ -6,6 +6,12 @@ exports[`ErrorMessage > renders multiline error messages 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ErrorMessage > renders multiline error messages 2`] = `
|
||||
"✕ Error line 1
|
||||
Error line 2
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ErrorMessage > renders with the correct prefix and text 1`] = `
|
||||
"✕ Something went wrong
|
||||
"
|
||||
|
||||
@@ -16,6 +16,13 @@ exports[`<GeminiMessage /> - Raw Markdown Display Snapshots > renders pending st
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<GeminiMessage /> - Raw Markdown Display Snapshots > renders pending state with renderMarkdown=true 2`] = `
|
||||
"✦ Test bold and code markdown
|
||||
|
||||
1 const x = 1;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<GeminiMessage /> - Raw Markdown Display Snapshots > renders with renderMarkdown=false '(raw markdown with syntax highlightin…' 1`] = `
|
||||
"✦ Test **bold** and \`code\` markdown
|
||||
|
||||
@@ -40,3 +47,12 @@ exports[`<GeminiMessage /> - Raw Markdown Display Snapshots > wraps long lines c
|
||||
truncation
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<GeminiMessage /> - Raw Markdown Display Snapshots > wraps long lines correctly in raw markdown mode 2`] = `
|
||||
"✦ This is a long
|
||||
line that should
|
||||
wrap correctly
|
||||
without
|
||||
truncation
|
||||
"
|
||||
`;
|
||||
|
||||
+6
@@ -26,6 +26,12 @@ exports[`ToolGroupMessage Compact Rendering > does not add an extra empty line i
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ToolGroupMessage Compact Rendering > does not add an extra empty line if a compact tool has a dense payload 2`] = `
|
||||
" ✓ ReadFolder Listing files → file1.txt
|
||||
✓ ReadFile Reading file → read file
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ToolGroupMessage Compact Rendering > renders consecutive compact tools without empty lines between them 1`] = `
|
||||
" ✓ ReadFolder Listing files → file1.txt file2.txt
|
||||
✓ ReadFolder Listing files → file3.txt
|
||||
|
||||
+8
@@ -16,6 +16,14 @@ exports[`<ToolMessage /> - Raw Markdown Display Snapshots > renders with renderM
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<ToolMessage /> - Raw Markdown Display Snapshots > renders with renderMarkdown=false, useAlternateBuffer=true '(raw markdown, alternate buffer)' 2`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ test-tool A tool for testing │
|
||||
│ │
|
||||
│ Test **bold** and \`code\` markdown │
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<ToolMessage /> - Raw Markdown Display Snapshots > renders with renderMarkdown=true, useAlternateBuffer=false '(constrained height, regular buffer -…' 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ test-tool A tool for testing │
|
||||
|
||||
@@ -8,6 +8,14 @@ exports[`UserMessage > renders multiline user message 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`UserMessage > renders multiline user message 2`] = `
|
||||
"▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
> Line 1
|
||||
Line 2
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`UserMessage > renders normal user message with correct prefix 1`] = `
|
||||
"▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
> Hello Gemini
|
||||
|
||||
@@ -7,6 +7,13 @@ exports[`WarningMessage > renders multiline warning messages 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`WarningMessage > renders multiline warning messages 2`] = `
|
||||
"
|
||||
⚠ Warning line 1
|
||||
Warning line 2
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`WarningMessage > renders with the correct prefix and text 1`] = `
|
||||
"
|
||||
⚠ Watch out!
|
||||
|
||||
@@ -9,6 +9,15 @@ Note: Newest last, oldest first
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<ChatList /> > handles invalid date formats gracefully 2`] = `
|
||||
"List of saved conversations:
|
||||
|
||||
- bad-date-chat (Invalid Date)
|
||||
|
||||
Note: Newest last, oldest first
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<ChatList /> > renders correctly with a list of chats 1`] = `
|
||||
"List of saved conversations:
|
||||
|
||||
|
||||
@@ -30,6 +30,20 @@ BackgroundTaskDisplay
|
||||
|
||||
|
||||
|
||||
Notifications
|
||||
CopyModeWarning
|
||||
Composer
|
||||
ExitWarning
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<DefaultAppLayout /> > shows BackgroundTaskDisplay when StreamingState is NOT WaitingForConfirmation 2`] = `
|
||||
"MainContent
|
||||
BackgroundTaskDisplay
|
||||
|
||||
|
||||
|
||||
|
||||
Notifications
|
||||
CopyModeWarning
|
||||
Composer
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-core",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"description": "Gemini CLI Core",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
|
||||
@@ -28,6 +28,7 @@ describe('Fallback Integration', () => {
|
||||
getActiveModel: () => PREVIEW_GEMINI_MODEL_AUTO,
|
||||
setActiveModel: vi.fn(),
|
||||
getUserTier: () => undefined,
|
||||
getHasAccessToPreviewModel: () => true,
|
||||
getModelAvailabilityService: () => availabilityService,
|
||||
modelConfigService: undefined as unknown as ModelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
@@ -28,7 +28,6 @@ describe('policyCatalog', () => {
|
||||
const chain = getModelPolicyChain({
|
||||
previewEnabled: true,
|
||||
useGemini31: true,
|
||||
useGemini31FlashLite: false,
|
||||
});
|
||||
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_3_1_MODEL);
|
||||
expect(chain).toHaveLength(2);
|
||||
@@ -39,7 +38,6 @@ describe('policyCatalog', () => {
|
||||
const chain = getModelPolicyChain({
|
||||
previewEnabled: true,
|
||||
useGemini31: true,
|
||||
useGemini31FlashLite: false,
|
||||
useCustomToolModel: true,
|
||||
});
|
||||
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL);
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface ModelPolicyOptions {
|
||||
useGemini31?: boolean;
|
||||
useGemini31FlashLite?: boolean;
|
||||
useCustomToolModel?: boolean;
|
||||
useGemini3_5Flash?: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_ACTIONS: ModelPolicyActionMap = {
|
||||
@@ -93,8 +94,10 @@ export function getModelPolicyChain(
|
||||
const proModel = resolveModel(
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
options.useGemini31,
|
||||
options.useGemini31FlashLite,
|
||||
options.useCustomToolModel,
|
||||
true,
|
||||
undefined,
|
||||
options.useGemini3_5Flash,
|
||||
);
|
||||
return [
|
||||
definePolicy({
|
||||
|
||||
@@ -30,14 +30,17 @@ const createMockConfig = (overrides: Partial<Config> = {}): Config => {
|
||||
getUserTier: () => undefined,
|
||||
getModel: () => 'gemini-2.5-pro',
|
||||
getGemini31LaunchedSync: () => false,
|
||||
getGemini31FlashLiteLaunchedSync: () => false,
|
||||
getUseCustomToolModelSync: () => {
|
||||
const useGemini31 = config.getGemini31LaunchedSync();
|
||||
const authType = config.getContentGeneratorConfig().authType;
|
||||
return useGemini31 && authType === AuthType.USE_GEMINI;
|
||||
},
|
||||
getContentGeneratorConfig: () => ({ authType: undefined }),
|
||||
getHasAccessToPreviewModel: () => true,
|
||||
getMaxAttemptsPerTurn: () => 3,
|
||||
getExperimentalDynamicModelConfiguration: () => false,
|
||||
getReleaseChannel: () => 'preview',
|
||||
modelConfigService: new ModelConfigService(DEFAULT_MODEL_CONFIGS),
|
||||
...overrides,
|
||||
} as unknown as Config;
|
||||
return config;
|
||||
@@ -110,7 +113,7 @@ describe('policyHelpers', () => {
|
||||
});
|
||||
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[0]?.model).toBe('gemini-3.1-flash-lite');
|
||||
expect(chain[1]?.model).toBe('gemini-2.5-flash');
|
||||
expect(chain[2]?.model).toBe('gemini-2.5-pro');
|
||||
});
|
||||
@@ -121,7 +124,7 @@ describe('policyHelpers', () => {
|
||||
});
|
||||
const chain = resolvePolicyChain(config);
|
||||
expect(chain).toHaveLength(3);
|
||||
expect(chain[0]?.model).toBe('gemini-2.5-flash-lite');
|
||||
expect(chain[0]?.model).toBe('gemini-3.1-flash-lite');
|
||||
expect(chain[1]?.model).toBe('gemini-2.5-flash');
|
||||
expect(chain[2]?.model).toBe('gemini-2.5-pro');
|
||||
});
|
||||
@@ -187,6 +190,7 @@ describe('policyHelpers', () => {
|
||||
const testCases = [
|
||||
{ name: 'Default Auto', model: DEFAULT_GEMINI_MODEL_AUTO },
|
||||
{ name: 'Gemini 3 Auto', model: 'auto-gemini-3' },
|
||||
{ name: 'Unified Auto', model: 'auto' },
|
||||
{ name: 'Flash Lite', model: DEFAULT_GEMINI_FLASH_LITE_MODEL },
|
||||
{
|
||||
name: 'Gemini 3 Auto (3.1 Enabled)',
|
||||
@@ -222,9 +226,9 @@ describe('policyHelpers', () => {
|
||||
getExperimentalDynamicModelConfiguration: () => dynamic,
|
||||
getModel: () => model,
|
||||
getGemini31LaunchedSync: () => useGemini31 ?? false,
|
||||
getGemini31FlashLiteLaunchedSync: () => false,
|
||||
getHasAccessToPreviewModel: () => hasAccess ?? true,
|
||||
getContentGeneratorConfig: () => ({ authType }),
|
||||
getReleaseChannel: () => 'preview',
|
||||
modelConfigService: new ModelConfigService(DEFAULT_MODEL_CONFIGS),
|
||||
});
|
||||
|
||||
|
||||
@@ -52,19 +52,21 @@ export function resolvePolicyChain(
|
||||
|
||||
let chain: ModelPolicyChain | undefined;
|
||||
const useGemini31 = config.getGemini31LaunchedSync?.() ?? false;
|
||||
const useGemini31FlashLite =
|
||||
config.getGemini31FlashLiteLaunchedSync?.() ?? false;
|
||||
const useCustomToolModel = config.getUseCustomToolModelSync?.() ?? false;
|
||||
const hasAccessToPreview = config.getHasAccessToPreviewModel?.() ?? true;
|
||||
const hasAccessToPreview = config.getHasAccessToPreviewModel?.() ?? false;
|
||||
const useGemini3_5Flash = config.hasGemini35FlashGAAccess?.() ?? false;
|
||||
|
||||
// Capture the original family intent before any normalization or early downgrade.
|
||||
const isOriginallyGemini3 = isGemini3Model(modelFromConfig, config);
|
||||
|
||||
const resolvedModel = normalizeModelId(
|
||||
resolveModel(
|
||||
modelFromConfig,
|
||||
useGemini31,
|
||||
useGemini31FlashLite,
|
||||
useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
),
|
||||
);
|
||||
const isAutoPreferred = normalizedPreferredModel
|
||||
@@ -75,26 +77,19 @@ export function resolvePolicyChain(
|
||||
// We always wrap around for Gemini 3 chains to ensure maximum availability
|
||||
// between models in the same family (e.g. fallback to Pro if Flash is exhausted).
|
||||
const effectiveWrapsAround =
|
||||
wrapsAround ||
|
||||
isAutoPreferred ||
|
||||
isAutoConfigured ||
|
||||
isGemini3Model(resolvedModel, config);
|
||||
wrapsAround || isAutoPreferred || isAutoConfigured || isOriginallyGemini3;
|
||||
|
||||
// --- DYNAMIC PATH ---
|
||||
if (config.getExperimentalDynamicModelConfiguration?.() === true) {
|
||||
const context = {
|
||||
useGemini3_1: useGemini31,
|
||||
useGemini3_1FlashLite: useGemini31FlashLite,
|
||||
useCustomTools: useCustomToolModel,
|
||||
useGemini3_5Flash,
|
||||
};
|
||||
|
||||
if (resolvedModel === DEFAULT_GEMINI_FLASH_LITE_MODEL) {
|
||||
chain = config.modelConfigService.resolveChain('lite', context);
|
||||
} else if (
|
||||
isGemini3Model(normalizeModelId(resolvedModel), config) ||
|
||||
isAutoPreferred ||
|
||||
isAutoConfigured
|
||||
) {
|
||||
} else if (isOriginallyGemini3 || isAutoPreferred || isAutoConfigured) {
|
||||
// 1. Try to find a chain specifically for the current configured alias
|
||||
if (
|
||||
isAutoConfigured &&
|
||||
@@ -131,15 +126,11 @@ export function resolvePolicyChain(
|
||||
|
||||
if (resolvedModel === DEFAULT_GEMINI_FLASH_LITE_MODEL) {
|
||||
chain = getFlashLitePolicyChain();
|
||||
} else if (
|
||||
isGemini3Model(resolvedModel, config) ||
|
||||
isAutoPreferred ||
|
||||
isAutoConfigured
|
||||
) {
|
||||
} else if (isOriginallyGemini3 || isAutoPreferred || isAutoConfigured) {
|
||||
const isAutoSelection = isAutoPreferred || isAutoConfigured;
|
||||
if (hasAccessToPreview) {
|
||||
const previewEnabled =
|
||||
isGemini3Model(resolvedModel, config) ||
|
||||
isOriginallyGemini3 ||
|
||||
normalizedPreferredModel === PREVIEW_GEMINI_MODEL_AUTO ||
|
||||
configuredModel === PREVIEW_GEMINI_MODEL_AUTO;
|
||||
chain = getModelPolicyChain({
|
||||
@@ -147,8 +138,8 @@ export function resolvePolicyChain(
|
||||
isAutoSelection,
|
||||
userTier: config.getUserTier(),
|
||||
useGemini31,
|
||||
useGemini31FlashLite,
|
||||
useCustomToolModel,
|
||||
useGemini3_5Flash,
|
||||
});
|
||||
} else {
|
||||
// User requested Gemini 3 but has no access. Proactively downgrade
|
||||
@@ -158,8 +149,8 @@ export function resolvePolicyChain(
|
||||
isAutoSelection,
|
||||
userTier: config.getUserTier(),
|
||||
useGemini31,
|
||||
useGemini31FlashLite,
|
||||
useCustomToolModel,
|
||||
useGemini3_5Flash,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from './codeAssist.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
import { LoggingContentGenerator } from '../core/loggingContentGenerator.js';
|
||||
import { ModelMappingContentGenerator } from '../core/modelMappingContentGenerator.js';
|
||||
import { UserTierId } from './types.js';
|
||||
|
||||
// Mock dependencies
|
||||
@@ -22,11 +23,15 @@ vi.mock('./oauth2.js');
|
||||
vi.mock('./setup.js');
|
||||
vi.mock('./server.js');
|
||||
vi.mock('../core/loggingContentGenerator.js');
|
||||
vi.mock('../core/modelMappingContentGenerator.js');
|
||||
|
||||
const mockedGetOauthClient = vi.mocked(getOauthClient);
|
||||
const mockedSetupUser = vi.mocked(setupUser);
|
||||
const MockedCodeAssistServer = vi.mocked(CodeAssistServer);
|
||||
const MockedLoggingContentGenerator = vi.mocked(LoggingContentGenerator);
|
||||
const MockedModelMappingContentGenerator = vi.mocked(
|
||||
ModelMappingContentGenerator,
|
||||
);
|
||||
|
||||
describe('codeAssist', () => {
|
||||
beforeEach(() => {
|
||||
@@ -178,5 +183,47 @@ describe('codeAssist', () => {
|
||||
const server = getCodeAssistServer(mockConfig);
|
||||
expect(server).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should unwrap and return the server if it is wrapped in a ModelMappingContentGenerator', () => {
|
||||
const mockServer = new MockedCodeAssistServer({} as never, '', {});
|
||||
const mockMapper = new MockedModelMappingContentGenerator(
|
||||
{} as never,
|
||||
{},
|
||||
);
|
||||
vi.spyOn(mockMapper, 'getWrapped').mockReturnValue(mockServer);
|
||||
|
||||
const mockConfig = {
|
||||
getContentGenerator: () => mockMapper,
|
||||
} as unknown as Config;
|
||||
|
||||
const server = getCodeAssistServer(mockConfig);
|
||||
expect(server).toBe(mockServer);
|
||||
expect(mockMapper.getWrapped).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should recursively unwrap multiple layers of LoggingContentGenerator and ModelMappingContentGenerator', () => {
|
||||
const mockServer = new MockedCodeAssistServer({} as never, '', {});
|
||||
const mockLogger = new MockedLoggingContentGenerator(
|
||||
{} as never,
|
||||
{} as never,
|
||||
);
|
||||
const mockMapper = new MockedModelMappingContentGenerator(
|
||||
{} as never,
|
||||
{},
|
||||
);
|
||||
|
||||
// Mapper wraps Logger wraps Server
|
||||
vi.spyOn(mockMapper, 'getWrapped').mockReturnValue(mockLogger);
|
||||
vi.spyOn(mockLogger, 'getWrapped').mockReturnValue(mockServer);
|
||||
|
||||
const mockConfig = {
|
||||
getContentGenerator: () => mockMapper,
|
||||
} as unknown as Config;
|
||||
|
||||
const server = getCodeAssistServer(mockConfig);
|
||||
expect(server).toBe(mockServer);
|
||||
expect(mockMapper.getWrapped).toHaveBeenCalled();
|
||||
expect(mockLogger.getWrapped).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import { setupUser } from './setup.js';
|
||||
import { CodeAssistServer, type HttpOptions } from './server.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
import { LoggingContentGenerator } from '../core/loggingContentGenerator.js';
|
||||
import { ModelMappingContentGenerator } from '../core/modelMappingContentGenerator.js';
|
||||
|
||||
export async function createCodeAssistContentGenerator(
|
||||
httpOptions: HttpOptions,
|
||||
@@ -43,9 +44,15 @@ export function getCodeAssistServer(
|
||||
): CodeAssistServer | undefined {
|
||||
let server = config.getContentGenerator();
|
||||
|
||||
// Unwrap LoggingContentGenerator if present
|
||||
if (server instanceof LoggingContentGenerator) {
|
||||
server = server.getWrapped();
|
||||
// Recursively unwrap LoggingContentGenerator and ModelMappingContentGenerator
|
||||
while (true) {
|
||||
if (server instanceof LoggingContentGenerator) {
|
||||
server = server.getWrapped();
|
||||
} else if (server instanceof ModelMappingContentGenerator) {
|
||||
server = server.getWrapped();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(server instanceof CodeAssistServer)) {
|
||||
|
||||
@@ -18,8 +18,8 @@ export const ExperimentFlags = {
|
||||
MASKING_PROTECT_LATEST_TURN: 45758819,
|
||||
GEMINI_3_1_PRO_LAUNCHED: 45760185,
|
||||
PRO_MODEL_NO_ACCESS: 45768879,
|
||||
GEMINI_3_1_FLASH_LITE_LAUNCHED: 45771641,
|
||||
DEFAULT_REQUEST_TIMEOUT: 45773134,
|
||||
GEMINI_3_5_FLASH_GA_LAUNCHED: 45780819,
|
||||
} as const;
|
||||
|
||||
export type ExperimentFlagName =
|
||||
|
||||
@@ -68,7 +68,6 @@ import {
|
||||
PREVIEW_GEMINI_3_1_MODEL,
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
} from './models.js';
|
||||
import { Storage } from './storage.js';
|
||||
import type { AgentLoopContext } from './agent-loop-context.js';
|
||||
@@ -702,20 +701,6 @@ describe('Server Config (config.ts)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getGemini31FlashLiteLaunchedSync', () => {
|
||||
it.each([AuthType.USE_GEMINI, AuthType.USE_VERTEX_AI, AuthType.GATEWAY])(
|
||||
'should return true for %s',
|
||||
async (authType) => {
|
||||
const config = new Config(baseParams);
|
||||
vi.mocked(createContentGeneratorConfig).mockResolvedValue({
|
||||
authType,
|
||||
});
|
||||
await config.refreshAuth(authType);
|
||||
expect(config.getGemini31FlashLiteLaunchedSync()).toBe(true);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('getProModelNoAccessSync', () => {
|
||||
it('should return experiment value for AuthType.LOGIN_WITH_GOOGLE', async () => {
|
||||
vi.mocked(getExperiments).mockResolvedValue({
|
||||
@@ -969,7 +954,7 @@ describe('Server Config (config.ts)', () => {
|
||||
await config.getExperimentsAsync();
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(config.getModel()).toBe(PREVIEW_GEMINI_FLASH_MODEL);
|
||||
expect(config.getModel()).toBe(config.getPreviewGeminiFlashModel());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4331,3 +4316,57 @@ describe('ADKSettings', () => {
|
||||
expect(config.getAgentSessionNoninteractiveEnabled()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasGemini35FlashGAAccess model setting', () => {
|
||||
const baseParams: ConfigParameters = {
|
||||
sessionId: 'test',
|
||||
targetDir: '.',
|
||||
debugMode: false,
|
||||
model: 'test-model',
|
||||
cwd: '.',
|
||||
};
|
||||
|
||||
it('should set DEFAULT_GEMINI_FLASH_MODEL to gemini-3.5-flash and PREVIEW_GEMINI_FLASH_MODEL to gemini-3-flash-preview if hasGemini35FlashGAAccess returns true and authType is USE_GEMINI', () => {
|
||||
const config = new Config(baseParams);
|
||||
config['contentGeneratorConfig'] = { authType: AuthType.USE_GEMINI };
|
||||
|
||||
// Set experiment to return true for GEMINI_3_5_FLASH_GA_LAUNCHED
|
||||
config.setExperiments({
|
||||
experimentIds: [],
|
||||
flags: {
|
||||
[ExperimentFlags.GEMINI_3_5_FLASH_GA_LAUNCHED]: {
|
||||
boolValue: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Call the method
|
||||
const result = config.hasGemini35FlashGAAccess();
|
||||
expect(result).toBe(true);
|
||||
|
||||
expect(config.getDefaultGeminiFlashModel()).toBe('gemini-3.5-flash');
|
||||
expect(config.getPreviewGeminiFlashModel()).toBe('gemini-3-flash-preview');
|
||||
});
|
||||
|
||||
it('should set DEFAULT_GEMINI_FLASH_MODEL and PREVIEW_GEMINI_FLASH_MODEL to gemini-3.5-flash if hasGemini35FlashGAAccess returns true and authType is not USE_GEMINI', () => {
|
||||
const config = new Config(baseParams);
|
||||
config['contentGeneratorConfig'] = { authType: AuthType.LOGIN_WITH_GOOGLE };
|
||||
|
||||
// Set experiment to return true for GEMINI_3_5_FLASH_GA_LAUNCHED
|
||||
config.setExperiments({
|
||||
experimentIds: [],
|
||||
flags: {
|
||||
[ExperimentFlags.GEMINI_3_5_FLASH_GA_LAUNCHED]: {
|
||||
boolValue: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Call the method
|
||||
const result = config.hasGemini35FlashGAAccess();
|
||||
expect(result).toBe(true);
|
||||
|
||||
expect(config.getDefaultGeminiFlashModel()).toBe('gemini-3.5-flash');
|
||||
expect(config.getPreviewGeminiFlashModel()).toBe('gemini-3.5-flash');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -81,14 +81,11 @@ import { tokenLimit } from '../core/tokenLimits.js';
|
||||
import {
|
||||
DEFAULT_GEMINI_EMBEDDING_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
isAutoModel,
|
||||
isPreviewModel,
|
||||
isGemini2Model,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
resolveModel,
|
||||
} from './models.js';
|
||||
import { shouldAttemptBrowserLaunch } from '../utils/browser.js';
|
||||
@@ -184,6 +181,7 @@ import { InjectionService } from './injectionService.js';
|
||||
import { ExecutionLifecycleService } from '../services/executionLifecycleService.js';
|
||||
import { WORKSPACE_POLICY_TIER } from '../policy/config.js';
|
||||
import { loadPoliciesFromToml } from '../policy/toml-loader.js';
|
||||
import { getChannelFromVersion } from '../utils/channel.js';
|
||||
|
||||
import { CheckerRunner } from '../safety/checker-runner.js';
|
||||
import { ContextBuilder } from '../safety/context-builder.js';
|
||||
@@ -762,7 +760,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
private skillManager!: SkillManager;
|
||||
private _sessionId: string;
|
||||
private readonly clientName: string | undefined;
|
||||
private clientVersion: string;
|
||||
private _clientVersion: string;
|
||||
private fileSystemService: FileSystemService;
|
||||
private trackerService?: TrackerService;
|
||||
readonly topicState = new TopicState();
|
||||
@@ -982,7 +980,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
constructor(params: ConfigParameters) {
|
||||
this._sessionId = params.sessionId;
|
||||
this.clientName = params.clientName;
|
||||
this.clientVersion = params.clientVersion ?? 'unknown';
|
||||
this._clientVersion = params.clientVersion ?? 'unknown';
|
||||
this.approvedPlanPath = undefined;
|
||||
this.embeddingModel =
|
||||
params.embeddingModel ?? DEFAULT_GEMINI_EMBEDDING_MODEL;
|
||||
@@ -2013,13 +2011,20 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
return {};
|
||||
}
|
||||
|
||||
const isPreview =
|
||||
model === PREVIEW_GEMINI_MODEL_AUTO ||
|
||||
isPreviewModel(this.getActiveModel(), this);
|
||||
const proModel = isPreview ? PREVIEW_GEMINI_MODEL : DEFAULT_GEMINI_MODEL;
|
||||
const primaryModel = resolveModel(
|
||||
model,
|
||||
this.getGemini31LaunchedSync(),
|
||||
this.getUseCustomToolModelSync(),
|
||||
this.getHasAccessToPreviewModel(),
|
||||
this,
|
||||
this.hasGemini35FlashGAAccess(),
|
||||
);
|
||||
|
||||
const isPreview = isPreviewModel(primaryModel, this);
|
||||
const proModel = primaryModel;
|
||||
const flashModel = isPreview
|
||||
? PREVIEW_GEMINI_FLASH_MODEL
|
||||
: DEFAULT_GEMINI_FLASH_MODEL;
|
||||
? this.getPreviewGeminiFlashModel()
|
||||
: this.getDefaultGeminiFlashModel();
|
||||
|
||||
const proQuota = this.modelQuotas.get(proModel);
|
||||
const flashQuota = this.modelQuotas.get(flashModel);
|
||||
@@ -2049,10 +2054,10 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
const primaryModel = resolveModel(
|
||||
this.getModel(),
|
||||
this.getGemini31LaunchedSync(),
|
||||
this.getGemini31FlashLiteLaunchedSync(),
|
||||
this.getUseCustomToolModelSync(),
|
||||
this.getHasAccessToPreviewModel(),
|
||||
this,
|
||||
this.hasGemini35FlashGAAccess(),
|
||||
);
|
||||
return this.modelQuotas.get(primaryModel)?.remaining;
|
||||
}
|
||||
@@ -2065,10 +2070,10 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
const primaryModel = resolveModel(
|
||||
this.getModel(),
|
||||
this.getGemini31LaunchedSync(),
|
||||
this.getGemini31FlashLiteLaunchedSync(),
|
||||
this.getUseCustomToolModelSync(),
|
||||
this.getHasAccessToPreviewModel(),
|
||||
this,
|
||||
this.hasGemini35FlashGAAccess(),
|
||||
);
|
||||
return this.modelQuotas.get(primaryModel)?.limit;
|
||||
}
|
||||
@@ -2081,10 +2086,10 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
const primaryModel = resolveModel(
|
||||
this.getModel(),
|
||||
this.getGemini31LaunchedSync(),
|
||||
this.getGemini31FlashLiteLaunchedSync(),
|
||||
this.getUseCustomToolModelSync(),
|
||||
this.getHasAccessToPreviewModel(),
|
||||
this,
|
||||
this.hasGemini35FlashGAAccess(),
|
||||
);
|
||||
return this.modelQuotas.get(primaryModel)?.resetTime;
|
||||
}
|
||||
@@ -3441,15 +3446,6 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
return this.getGemini31LaunchedSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether Gemini 3.1 Flash Lite has been launched.
|
||||
* This method is async and ensures that experiments are loaded before returning the result.
|
||||
*/
|
||||
async getGemini31FlashLiteLaunched(): Promise<boolean> {
|
||||
await this.ensureExperimentsLoaded();
|
||||
return this.getGemini31FlashLiteLaunchedSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the custom tool model should be used.
|
||||
*/
|
||||
@@ -3478,6 +3474,47 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
);
|
||||
}
|
||||
|
||||
getReleaseChannel(): string {
|
||||
return getChannelFromVersion(this._clientVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether Gemini 3.5 Flash GA has been launched.
|
||||
*
|
||||
* Note: This method should only be called after startup, once experiments have been loaded.
|
||||
*/
|
||||
hasGemini35FlashGAAccess(): boolean {
|
||||
const authType = this.contentGeneratorConfig?.authType;
|
||||
return (() => {
|
||||
if (this.isGemini31LaunchedForAuthType(authType)) {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
this.experiments?.flags[ExperimentFlags.GEMINI_3_5_FLASH_GA_LAUNCHED]
|
||||
?.boolValue ?? false
|
||||
);
|
||||
})();
|
||||
}
|
||||
|
||||
getPreviewGeminiFlashModel(): string {
|
||||
const authType = this.contentGeneratorConfig?.authType;
|
||||
if (this.hasGemini35FlashGAAccess()) {
|
||||
if (authType === AuthType.USE_GEMINI) {
|
||||
return 'gemini-3-flash-preview';
|
||||
} else {
|
||||
return 'gemini-3.5-flash';
|
||||
}
|
||||
}
|
||||
return 'gemini-3-flash-preview';
|
||||
}
|
||||
|
||||
getDefaultGeminiFlashModel(): string {
|
||||
if (this.hasGemini35FlashGAAccess()) {
|
||||
return 'gemini-3.5-flash';
|
||||
}
|
||||
return 'gemini-2.5-flash';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether Gemini 3.1 has been launched.
|
||||
*
|
||||
@@ -3512,21 +3549,10 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether Gemini 3.1 Flash Lite has been launched.
|
||||
*
|
||||
* Note: This method should only be called after startup, once experiments have been loaded.
|
||||
* If you need to call this during startup or from an async context, use
|
||||
* getGemini31FlashLiteLaunched instead.
|
||||
* Returns the client version.
|
||||
*/
|
||||
getGemini31FlashLiteLaunchedSync(): boolean {
|
||||
const authType = this.contentGeneratorConfig?.authType;
|
||||
if (this.isGemini31LaunchedForAuthType(authType)) {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
this.experiments?.flags[ExperimentFlags.GEMINI_3_1_FLASH_LITE_LAUNCHED]
|
||||
?.boolValue ?? false
|
||||
);
|
||||
get clientVersion(): string {
|
||||
return this._clientVersion;
|
||||
}
|
||||
|
||||
private async ensureExperimentsLoaded(): Promise<void> {
|
||||
|
||||
@@ -71,6 +71,24 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
model: 'gemini-3-flash-preview',
|
||||
},
|
||||
},
|
||||
'gemini-3.1-pro-preview': {
|
||||
extends: 'chat-base-3',
|
||||
modelConfig: {
|
||||
model: 'gemini-3.1-pro-preview',
|
||||
},
|
||||
},
|
||||
'gemini-3.1-pro-preview-customtools': {
|
||||
extends: 'chat-base-3',
|
||||
modelConfig: {
|
||||
model: 'gemini-3.1-pro-preview-customtools',
|
||||
},
|
||||
},
|
||||
'gemini-3.1-flash-lite-preview': {
|
||||
extends: 'chat-base-3',
|
||||
modelConfig: {
|
||||
model: 'gemini-3.1-flash-lite-preview',
|
||||
},
|
||||
},
|
||||
'gemini-2.5-pro': {
|
||||
extends: 'chat-base-2.5',
|
||||
modelConfig: {
|
||||
@@ -89,6 +107,18 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
model: 'gemini-2.5-flash-lite',
|
||||
},
|
||||
},
|
||||
'gemini-3.1-flash-lite': {
|
||||
extends: 'chat-base-3',
|
||||
modelConfig: {
|
||||
model: 'gemini-3.1-flash-lite',
|
||||
},
|
||||
},
|
||||
'gemini-3.5-flash': {
|
||||
extends: 'chat-base-3',
|
||||
modelConfig: {
|
||||
model: 'gemini-3.5-flash',
|
||||
},
|
||||
},
|
||||
'gemma-4-31b-it': {
|
||||
extends: 'chat-base-3',
|
||||
modelConfig: {
|
||||
@@ -115,10 +145,16 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
model: 'gemini-3-flash-preview',
|
||||
},
|
||||
},
|
||||
'gemini-3.5-flash-base': {
|
||||
extends: 'base',
|
||||
modelConfig: {
|
||||
model: 'gemini-3.5-flash',
|
||||
},
|
||||
},
|
||||
classifier: {
|
||||
extends: 'base',
|
||||
modelConfig: {
|
||||
model: 'gemini-2.5-flash-lite',
|
||||
model: 'flash-lite',
|
||||
generateContentConfig: {
|
||||
maxOutputTokens: 1024,
|
||||
thinkingConfig: {
|
||||
@@ -130,7 +166,7 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
'prompt-completion': {
|
||||
extends: 'base',
|
||||
modelConfig: {
|
||||
model: 'gemini-2.5-flash-lite',
|
||||
model: 'flash-lite',
|
||||
generateContentConfig: {
|
||||
temperature: 0.3,
|
||||
maxOutputTokens: 16000,
|
||||
@@ -143,7 +179,7 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
'fast-ack-helper': {
|
||||
extends: 'base',
|
||||
modelConfig: {
|
||||
model: 'gemini-2.5-flash-lite',
|
||||
model: 'flash-lite',
|
||||
generateContentConfig: {
|
||||
temperature: 0.2,
|
||||
maxOutputTokens: 120,
|
||||
@@ -156,7 +192,7 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
'edit-corrector': {
|
||||
extends: 'base',
|
||||
modelConfig: {
|
||||
model: 'gemini-2.5-flash-lite',
|
||||
model: 'flash-lite',
|
||||
generateContentConfig: {
|
||||
thinkingConfig: {
|
||||
thinkingBudget: 0,
|
||||
@@ -167,7 +203,7 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
'summarizer-default': {
|
||||
extends: 'base',
|
||||
modelConfig: {
|
||||
model: 'gemini-2.5-flash-lite',
|
||||
model: 'flash-lite',
|
||||
generateContentConfig: {
|
||||
maxOutputTokens: 2000,
|
||||
},
|
||||
@@ -176,7 +212,7 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
'summarizer-shell': {
|
||||
extends: 'base',
|
||||
modelConfig: {
|
||||
model: 'gemini-2.5-flash-lite',
|
||||
model: 'flash-lite',
|
||||
generateContentConfig: {
|
||||
maxOutputTokens: 2000,
|
||||
},
|
||||
@@ -246,7 +282,7 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
},
|
||||
'chat-compression-3.1-flash-lite': {
|
||||
modelConfig: {
|
||||
model: 'gemini-3.1-flash-lite-preview',
|
||||
model: 'gemini-3.1-flash-lite',
|
||||
},
|
||||
},
|
||||
'chat-compression-2.5-pro': {
|
||||
@@ -287,10 +323,10 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
],
|
||||
modelDefinitions: {
|
||||
// Concrete Models
|
||||
'gemini-3.1-flash-lite-preview': {
|
||||
'gemini-3.1-flash-lite': {
|
||||
tier: 'flash-lite',
|
||||
family: 'gemini-3',
|
||||
isPreview: true,
|
||||
isPreview: false,
|
||||
isVisible: true,
|
||||
features: { thinking: false, multimodalToolUse: true },
|
||||
},
|
||||
@@ -322,6 +358,13 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
isVisible: true,
|
||||
features: { thinking: false, multimodalToolUse: true },
|
||||
},
|
||||
'gemini-3.5-flash': {
|
||||
tier: 'flash',
|
||||
family: 'gemini-3',
|
||||
isPreview: false,
|
||||
isVisible: true,
|
||||
features: { thinking: false, multimodalToolUse: true },
|
||||
},
|
||||
'gemini-2.5-pro': {
|
||||
tier: 'pro',
|
||||
family: 'gemini-2.5',
|
||||
@@ -362,9 +405,10 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
|
||||
// Aliases
|
||||
auto: {
|
||||
displayName: 'Auto',
|
||||
tier: 'auto',
|
||||
isPreview: true,
|
||||
isVisible: false,
|
||||
isVisible: true,
|
||||
features: { thinking: true, multimodalToolUse: false },
|
||||
},
|
||||
pro: {
|
||||
@@ -386,22 +430,16 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
features: { thinking: false, multimodalToolUse: false },
|
||||
},
|
||||
'auto-gemini-3': {
|
||||
displayName: 'Auto (Gemini 3)',
|
||||
tier: 'auto',
|
||||
family: 'gemini-3',
|
||||
isPreview: true,
|
||||
isVisible: true,
|
||||
dialogDescription:
|
||||
'Let Gemini CLI decide the best model for the task: gemini-3-pro, gemini-3-flash',
|
||||
features: { thinking: true, multimodalToolUse: false },
|
||||
isVisible: false,
|
||||
},
|
||||
'auto-gemini-2.5': {
|
||||
displayName: 'Auto (Gemini 2.5)',
|
||||
tier: 'auto',
|
||||
family: 'gemini-2.5',
|
||||
isPreview: false,
|
||||
isVisible: true,
|
||||
dialogDescription:
|
||||
'Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash',
|
||||
features: { thinking: false, multimodalToolUse: false },
|
||||
isVisible: false,
|
||||
},
|
||||
},
|
||||
modelIdResolutions: {
|
||||
@@ -432,26 +470,35 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
default: 'gemini-3-flash-preview',
|
||||
contexts: [
|
||||
{
|
||||
condition: { hasAccessToPreview: false },
|
||||
condition: { hasAccessToPreview: false, useGemini3_5Flash: true },
|
||||
target: 'gemini-3.5-flash',
|
||||
},
|
||||
{
|
||||
condition: { hasAccessToPreview: false, useGemini3_5Flash: false },
|
||||
target: 'gemini-2.5-flash',
|
||||
},
|
||||
],
|
||||
},
|
||||
'gemini-3-pro-preview': {
|
||||
default: 'gemini-3-pro-preview',
|
||||
'gemini-3.5-flash': {
|
||||
default: 'gemini-3.5-flash',
|
||||
contexts: [
|
||||
{ condition: { hasAccessToPreview: false }, target: 'gemini-2.5-pro' },
|
||||
{
|
||||
condition: { useGemini3_1: true, useCustomTools: true },
|
||||
target: 'gemini-3.1-pro-preview-customtools',
|
||||
condition: { useGemini3_5Flash: false, hasAccessToPreview: false },
|
||||
target: 'gemini-2.5-flash',
|
||||
},
|
||||
{
|
||||
condition: { useGemini3_1: true },
|
||||
target: 'gemini-3.1-pro-preview',
|
||||
condition: { useGemini3_5Flash: false },
|
||||
target: 'gemini-3-flash-preview',
|
||||
},
|
||||
],
|
||||
},
|
||||
'auto-gemini-3': {
|
||||
'gemini-2.5-flash': {
|
||||
default: 'gemini-2.5-flash',
|
||||
contexts: [
|
||||
{ condition: { useGemini3_5Flash: true }, target: 'gemini-3.5-flash' },
|
||||
],
|
||||
},
|
||||
'gemini-3-pro-preview': {
|
||||
default: 'gemini-3-pro-preview',
|
||||
contexts: [
|
||||
{ condition: { hasAccessToPreview: false }, target: 'gemini-2.5-pro' },
|
||||
@@ -493,21 +540,13 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
'auto-gemini-2.5': {
|
||||
default: 'gemini-2.5-pro',
|
||||
},
|
||||
'gemini-3.1-flash-lite-preview': {
|
||||
default: 'gemini-3.1-flash-lite-preview',
|
||||
contexts: [
|
||||
{
|
||||
condition: { useGemini3_1FlashLite: false },
|
||||
target: 'gemini-2.5-flash-lite',
|
||||
},
|
||||
],
|
||||
'gemini-3.1-flash-lite': {
|
||||
default: 'gemini-3.1-flash-lite',
|
||||
},
|
||||
flash: {
|
||||
default: 'gemini-3-flash-preview',
|
||||
contexts: [
|
||||
{ condition: { useGemini3_5Flash: true }, target: 'gemini-3.5-flash' },
|
||||
{
|
||||
condition: { hasAccessToPreview: false },
|
||||
target: 'gemini-2.5-flash',
|
||||
@@ -515,28 +554,38 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
],
|
||||
},
|
||||
'flash-lite': {
|
||||
default: 'gemini-2.5-flash-lite',
|
||||
default: 'gemini-3.1-flash-lite',
|
||||
},
|
||||
'auto-gemini-3': {
|
||||
default: 'gemini-3-pro-preview',
|
||||
contexts: [
|
||||
{ condition: { hasAccessToPreview: false }, target: 'gemini-2.5-pro' },
|
||||
{
|
||||
condition: { useGemini3_1FlashLite: true },
|
||||
target: 'gemini-3.1-flash-lite-preview',
|
||||
condition: { useGemini3_1: true, useCustomTools: true },
|
||||
target: 'gemini-3.1-pro-preview-customtools',
|
||||
},
|
||||
{
|
||||
condition: { useGemini3_1: true },
|
||||
target: 'gemini-3.1-pro-preview',
|
||||
},
|
||||
],
|
||||
},
|
||||
'auto-gemini-2.5': {
|
||||
default: 'gemini-2.5-pro',
|
||||
},
|
||||
},
|
||||
classifierIdResolutions: {
|
||||
flash: {
|
||||
default: 'gemini-3-flash-preview',
|
||||
contexts: [
|
||||
{ condition: { useGemini3_5Flash: true }, target: 'gemini-3.5-flash' },
|
||||
{
|
||||
condition: { requestedModels: ['auto-gemini-2.5', 'gemini-2.5-pro'] },
|
||||
condition: { hasAccessToPreview: false },
|
||||
target: 'gemini-2.5-flash',
|
||||
},
|
||||
{
|
||||
condition: {
|
||||
requestedModels: ['auto-gemini-3', 'gemini-3-pro-preview'],
|
||||
},
|
||||
target: 'gemini-3-flash-preview',
|
||||
condition: { requestedModels: ['gemini-2.5-pro', 'auto-gemini-2.5'] },
|
||||
target: 'gemini-2.5-flash',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -544,7 +593,11 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
default: 'gemini-3-pro-preview',
|
||||
contexts: [
|
||||
{
|
||||
condition: { requestedModels: ['auto-gemini-2.5', 'gemini-2.5-pro'] },
|
||||
condition: { hasAccessToPreview: false },
|
||||
target: 'gemini-2.5-pro',
|
||||
},
|
||||
{
|
||||
condition: { requestedModels: ['gemini-2.5-pro', 'auto-gemini-2.5'] },
|
||||
target: 'gemini-2.5-pro',
|
||||
},
|
||||
{
|
||||
@@ -699,7 +752,7 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
||||
],
|
||||
lite: [
|
||||
{
|
||||
model: 'gemini-2.5-flash-lite',
|
||||
model: 'flash-lite',
|
||||
actions: {
|
||||
terminal: 'silent',
|
||||
transient: 'silent',
|
||||
|
||||
@@ -14,9 +14,11 @@ import {
|
||||
supportsModernFeatures,
|
||||
isAutoModel,
|
||||
getDisplayString,
|
||||
getAutoModelDescription,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_3_5_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
supportsMultimodalFunctionResponse,
|
||||
GEMINI_MODEL_ALIAS_PRO,
|
||||
@@ -28,7 +30,7 @@ import {
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
isActiveModel,
|
||||
PREVIEW_GEMINI_3_1_MODEL,
|
||||
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
|
||||
isPreviewModel,
|
||||
isProModel,
|
||||
@@ -66,22 +68,14 @@ describe('Dynamic Configuration Parity', () => {
|
||||
const flagCombos = [
|
||||
{
|
||||
useGemini3_1: false,
|
||||
useGemini3_1FlashLite: false,
|
||||
useCustomToolModel: false,
|
||||
},
|
||||
{
|
||||
useGemini3_1: true,
|
||||
useGemini3_1FlashLite: false,
|
||||
useCustomToolModel: false,
|
||||
},
|
||||
{
|
||||
useGemini3_1: true,
|
||||
useGemini3_1FlashLite: true,
|
||||
useCustomToolModel: false,
|
||||
},
|
||||
{
|
||||
useGemini3_1: true,
|
||||
useGemini3_1FlashLite: true,
|
||||
useCustomToolModel: true,
|
||||
},
|
||||
];
|
||||
@@ -104,7 +98,6 @@ describe('Dynamic Configuration Parity', () => {
|
||||
const legacy = resolveModel(
|
||||
model,
|
||||
flags.useGemini3_1,
|
||||
flags.useGemini3_1FlashLite,
|
||||
flags.useCustomToolModel,
|
||||
hasAccess,
|
||||
mockLegacyConfig,
|
||||
@@ -112,7 +105,6 @@ describe('Dynamic Configuration Parity', () => {
|
||||
const dynamic = resolveModel(
|
||||
model,
|
||||
flags.useGemini3_1,
|
||||
flags.useGemini3_1FlashLite,
|
||||
flags.useCustomToolModel,
|
||||
hasAccess,
|
||||
mockDynamicConfig,
|
||||
@@ -151,7 +143,6 @@ describe('Dynamic Configuration Parity', () => {
|
||||
anchor,
|
||||
tier,
|
||||
flags.useGemini3_1,
|
||||
flags.useGemini3_1FlashLite,
|
||||
flags.useCustomToolModel,
|
||||
hasAccess,
|
||||
mockLegacyConfig,
|
||||
@@ -160,7 +151,6 @@ describe('Dynamic Configuration Parity', () => {
|
||||
anchor,
|
||||
tier,
|
||||
flags.useGemini3_1,
|
||||
flags.useGemini3_1FlashLite,
|
||||
flags.useCustomToolModel,
|
||||
hasAccess,
|
||||
mockDynamicConfig,
|
||||
@@ -228,12 +218,29 @@ describe('Dynamic Configuration Parity', () => {
|
||||
});
|
||||
|
||||
describe('isPreviewModel', () => {
|
||||
it('should return true for preview models', () => {
|
||||
expect(isPreviewModel(PREVIEW_GEMINI_MODEL)).toBe(true);
|
||||
expect(isPreviewModel(PREVIEW_GEMINI_3_1_MODEL)).toBe(true);
|
||||
expect(isPreviewModel(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL)).toBe(true);
|
||||
expect(isPreviewModel(PREVIEW_GEMINI_FLASH_MODEL)).toBe(true);
|
||||
const PREVIEW_MODELS = [
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
PREVIEW_GEMINI_3_1_MODEL,
|
||||
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
];
|
||||
|
||||
it('should return true for active preview models', () => {
|
||||
for (const model of PREVIEW_MODELS) {
|
||||
if (model !== 'none') {
|
||||
expect(isPreviewModel(model)).toBe(true);
|
||||
}
|
||||
}
|
||||
expect(isPreviewModel(PREVIEW_GEMINI_MODEL_AUTO)).toBe(true);
|
||||
expect(isPreviewModel(GEMINI_MODEL_ALIAS_AUTO)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if a preview model is retired (set to none)', () => {
|
||||
const retiredModels = PREVIEW_MODELS.filter((m) => m === 'none');
|
||||
for (const model of retiredModels) {
|
||||
expect(isPreviewModel(model)).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it('should return false for non-preview models', () => {
|
||||
@@ -357,9 +364,9 @@ describe('getDisplayString', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should return PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL for PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL', () => {
|
||||
expect(getDisplayString(PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL)).toBe(
|
||||
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
it('should return PREVIEW_GEMINI_FLASH_LITE_MODEL for PREVIEW_GEMINI_FLASH_LITE_MODEL', () => {
|
||||
expect(getDisplayString(PREVIEW_GEMINI_FLASH_LITE_MODEL)).toBe(
|
||||
PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -404,7 +411,7 @@ describe('resolveModel', () => {
|
||||
});
|
||||
|
||||
it('should return Gemini 3.1 Pro Custom Tools when auto-gemini-3 is requested, useGemini3_1 is true, and useCustomToolModel is true', () => {
|
||||
const model = resolveModel(PREVIEW_GEMINI_MODEL_AUTO, true, false, true);
|
||||
const model = resolveModel(PREVIEW_GEMINI_MODEL_AUTO, true, true);
|
||||
expect(model).toBe(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL);
|
||||
});
|
||||
|
||||
@@ -418,9 +425,9 @@ describe('resolveModel', () => {
|
||||
expect(model).toBe(DEFAULT_GEMINI_FLASH_LITE_MODEL);
|
||||
});
|
||||
|
||||
it('should return the Preview Flash-Lite model when flash-lite is requested and useGemini3_1FlashLite is true', () => {
|
||||
const model = resolveModel(GEMINI_MODEL_ALIAS_FLASH_LITE, false, true);
|
||||
expect(model).toBe(PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL);
|
||||
it('should return the Flash-Lite model when flash-lite is requested', () => {
|
||||
const model = resolveModel(GEMINI_MODEL_ALIAS_FLASH_LITE, false);
|
||||
expect(model).toBe(DEFAULT_GEMINI_FLASH_LITE_MODEL);
|
||||
});
|
||||
|
||||
it('should return the requested model as-is for explicit specific models', () => {
|
||||
@@ -451,45 +458,39 @@ describe('resolveModel', () => {
|
||||
|
||||
describe('hasAccessToPreview logic', () => {
|
||||
it('should return default model when access to preview is false and preview model is requested', () => {
|
||||
expect(
|
||||
resolveModel(PREVIEW_GEMINI_MODEL, false, false, false, false),
|
||||
).toBe(DEFAULT_GEMINI_MODEL);
|
||||
expect(resolveModel(PREVIEW_GEMINI_MODEL, false, false, false)).toBe(
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return default flash model when access to preview is false and preview flash model is requested', () => {
|
||||
expect(
|
||||
resolveModel(PREVIEW_GEMINI_FLASH_MODEL, false, false, false, false),
|
||||
resolveModel(PREVIEW_GEMINI_FLASH_MODEL, false, false, false),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should return default flash lite model when access to preview is false and preview flash lite model is requested', () => {
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
resolveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, false, false, false),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_LITE_MODEL);
|
||||
});
|
||||
|
||||
it('should return default model when access to preview is false and auto-gemini-3 is requested', () => {
|
||||
expect(
|
||||
resolveModel(PREVIEW_GEMINI_MODEL_AUTO, false, false, false, false),
|
||||
).toBe(DEFAULT_GEMINI_MODEL);
|
||||
expect(resolveModel(PREVIEW_GEMINI_MODEL_AUTO, false, false, false)).toBe(
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return default model when access to preview is false and Gemini 3.1 is requested', () => {
|
||||
expect(
|
||||
resolveModel(PREVIEW_GEMINI_MODEL_AUTO, true, false, false, false),
|
||||
).toBe(DEFAULT_GEMINI_MODEL);
|
||||
expect(resolveModel(PREVIEW_GEMINI_MODEL_AUTO, true, false, false)).toBe(
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
);
|
||||
});
|
||||
|
||||
it('should still return default model when access to preview is false and auto-gemini-2.5 is requested', () => {
|
||||
expect(
|
||||
resolveModel(DEFAULT_GEMINI_MODEL_AUTO, false, false, false, false),
|
||||
).toBe(DEFAULT_GEMINI_MODEL);
|
||||
expect(resolveModel(DEFAULT_GEMINI_MODEL_AUTO, false, false, false)).toBe(
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -581,7 +582,6 @@ describe('resolveClassifierModel', () => {
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
GEMINI_MODEL_ALIAS_PRO,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
).toBe(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL);
|
||||
@@ -598,17 +598,14 @@ describe('isActiveModel', () => {
|
||||
it('should return true for Gemma 4 models when experimentalGemma is not provided (defaults to true)', () => {
|
||||
expect(isActiveModel(GEMMA_4_31B_IT_MODEL)).toBe(true);
|
||||
expect(isActiveModel(GEMMA_4_26B_A4B_IT_MODEL)).toBe(true);
|
||||
expect(isActiveModel(GEMMA_4_31B_IT_MODEL, false, false, false, true)).toBe(
|
||||
expect(isActiveModel(GEMMA_4_31B_IT_MODEL, false, false, true)).toBe(true);
|
||||
expect(isActiveModel(GEMMA_4_26B_A4B_IT_MODEL, false, false, true)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
isActiveModel(GEMMA_4_26B_A4B_IT_MODEL, false, false, false, true),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for Gemini 3.1 models when Gemini 3.1 is not launched', () => {
|
||||
expect(isActiveModel(PREVIEW_GEMINI_3_1_MODEL)).toBe(false);
|
||||
expect(isActiveModel(PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true for unknown models and aliases', () => {
|
||||
@@ -624,37 +621,48 @@ describe('isActiveModel', () => {
|
||||
expect(isActiveModel(DEFAULT_GEMINI_MODEL, true)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL only when useGemini3_1FlashLite is true', () => {
|
||||
expect(
|
||||
isActiveModel(PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL, false, true),
|
||||
).toBe(true);
|
||||
expect(isActiveModel(PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL, true, true)).toBe(
|
||||
it('should handle PREVIEW_GEMINI_FLASH_LITE_MODEL activity correctly based on retirement status', () => {
|
||||
if (PREVIEW_GEMINI_FLASH_LITE_MODEL === 'none') {
|
||||
expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, false, true)).toBe(
|
||||
false,
|
||||
);
|
||||
expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, true, true)).toBe(
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, false, true)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, true, true)).toBe(
|
||||
true,
|
||||
);
|
||||
}
|
||||
expect(isActiveModel(DEFAULT_GEMINI_FLASH_LITE_MODEL, false, false)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(isActiveModel(DEFAULT_GEMINI_FLASH_LITE_MODEL, true, true)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(isActiveModel(DEFAULT_GEMINI_FLASH_LITE_MODEL, true, false)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
isActiveModel(PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL, true, false),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should correctly filter Gemini 3.1 models based on useCustomToolModel when useGemini3_1 is true', () => {
|
||||
// When custom tools are preferred, standard 3.1 should be inactive
|
||||
expect(isActiveModel(PREVIEW_GEMINI_3_1_MODEL, true, false, true)).toBe(
|
||||
false,
|
||||
);
|
||||
expect(isActiveModel(PREVIEW_GEMINI_3_1_MODEL, true, true)).toBe(false);
|
||||
expect(
|
||||
isActiveModel(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL, true, false, true),
|
||||
isActiveModel(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL, true, true),
|
||||
).toBe(true);
|
||||
|
||||
// When custom tools are NOT preferred, custom tools 3.1 should be inactive
|
||||
expect(isActiveModel(PREVIEW_GEMINI_3_1_MODEL, true, false, false)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(isActiveModel(PREVIEW_GEMINI_3_1_MODEL, true, false)).toBe(true);
|
||||
expect(
|
||||
isActiveModel(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL, true, false, false),
|
||||
isActiveModel(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL, true, false),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for Gemini 3.1 models when useGemini3_1 and useGemini3_1FlashLite are false', () => {
|
||||
it('should return false for Gemini 3.1 preview models when useGemini3_1 is false', () => {
|
||||
expect(isActiveModel(PREVIEW_GEMINI_3_1_MODEL, false, false, true)).toBe(
|
||||
false,
|
||||
);
|
||||
@@ -667,8 +675,378 @@ describe('isActiveModel', () => {
|
||||
expect(
|
||||
isActiveModel(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL, false, false, false),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isActiveModel(PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL, false, false),
|
||||
).toBe(false);
|
||||
if (PREVIEW_GEMINI_FLASH_LITE_MODEL !== 'none') {
|
||||
expect(isActiveModel(PREVIEW_GEMINI_FLASH_LITE_MODEL, false, false)).toBe(
|
||||
false,
|
||||
);
|
||||
}
|
||||
expect(isActiveModel(DEFAULT_GEMINI_FLASH_LITE_MODEL, false, false)).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Gemini 3.1 Config Resolution', () => {
|
||||
it('PREVIEW_GEMINI_3_1_MODEL should resolve to chat-base-3 config (including thinkingLevel)', () => {
|
||||
const resolved = modelConfigService.getResolvedConfig({
|
||||
model: PREVIEW_GEMINI_3_1_MODEL,
|
||||
isChatModel: true,
|
||||
});
|
||||
expect(
|
||||
resolved.generateContentConfig?.thinkingConfig?.thinkingLevel,
|
||||
).toBeDefined();
|
||||
});
|
||||
|
||||
it('PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL should resolve to chat-base-3 config (including thinkingLevel)', () => {
|
||||
const resolved = modelConfigService.getResolvedConfig({
|
||||
model: PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
|
||||
isChatModel: true,
|
||||
});
|
||||
expect(
|
||||
resolved.generateContentConfig?.thinkingConfig?.thinkingLevel,
|
||||
).toBeDefined();
|
||||
});
|
||||
|
||||
it('PREVIEW_GEMINI_FLASH_LITE_MODEL should resolve to appropriate config based on retirement status', () => {
|
||||
if (PREVIEW_GEMINI_FLASH_LITE_MODEL === 'none') {
|
||||
// If none, it falls back to chat-base which may not have thinkingLevel
|
||||
const resolved = modelConfigService.getResolvedConfig({
|
||||
model: PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
isChatModel: true,
|
||||
});
|
||||
expect(resolved.model).toBe(PREVIEW_GEMINI_FLASH_LITE_MODEL);
|
||||
} else {
|
||||
const resolved = modelConfigService.getResolvedConfig({
|
||||
model: PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
isChatModel: true,
|
||||
});
|
||||
expect(
|
||||
resolved.generateContentConfig?.thinkingConfig?.thinkingLevel,
|
||||
).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-preview');
|
||||
expect(desc).toContain('gemini-3-flash-preview');
|
||||
});
|
||||
|
||||
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-preview');
|
||||
expect(desc).toContain('gemini-3-flash-preview');
|
||||
});
|
||||
|
||||
it('should return Gemini 3.5 Flash description when hasAccessToPreview and useGemini3_5Flash are true', () => {
|
||||
const desc = getAutoModelDescription(true, true, true);
|
||||
expect(desc).toContain('gemini-3.1-pro-preview');
|
||||
expect(desc).toContain(DEFAULT_GEMINI_3_5_FLASH_MODEL);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveModel Gemini 3.5 Flash GA', () => {
|
||||
it('should resolve all but preview flash models to DEFAULT_GEMINI_FLASH_MODEL when useGemini3_5Flash is true (legacy)', () => {
|
||||
expect(
|
||||
resolveModel(
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
expect(
|
||||
resolveModel(
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(PREVIEW_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should resolve all but preview flash models to gemini-3.5-flash when useGemini3_5Flash is true (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveModel(
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3.5-flash');
|
||||
expect(
|
||||
resolveModel(
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3.5-flash');
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe(PREVIEW_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should NOT resolve flash models to DEFAULT_GEMINI_FLASH_MODEL when useGemini3_5Flash is false', () => {
|
||||
expect(
|
||||
resolveModel(
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
false,
|
||||
),
|
||||
).toBe(PREVIEW_GEMINI_FLASH_MODEL);
|
||||
expect(
|
||||
resolveModel(
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
false,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
false,
|
||||
),
|
||||
).toBe(PREVIEW_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should resolve to DEFAULT_GEMINI_FLASH_MODEL when GA is false AND preview access is false (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveModel(
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
false, // No preview access
|
||||
mockDynamicConfig,
|
||||
false, // GA false
|
||||
),
|
||||
).toBe('gemini-2.5-flash');
|
||||
});
|
||||
|
||||
it('should resolve auto to DEFAULT_GEMINI_FLASH_MODEL when useGemini3_5Flash is true and classifier selects flash', () => {
|
||||
expect(
|
||||
resolveClassifierModel(
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should resolve auto to gemini-3.5-flash when useGemini3_5Flash is true and classifier selects flash (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveClassifierModel(
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3.5-flash');
|
||||
});
|
||||
|
||||
describe('Flash model promotion and manual override routing logic', () => {
|
||||
it('should resolve flash alias to DEFAULT_GEMINI_FLASH_MODEL when useGemini3_5Flash is true (static)', () => {
|
||||
expect(
|
||||
resolveModel(
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should resolve flash alias to gemini-3.5-flash when useGemini3_5Flash is true (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveModel(
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3.5-flash');
|
||||
});
|
||||
|
||||
it('should resolve manual selection of gemini-3-flash-preview to gemini-3-flash-preview when useGemini3_5Flash is true and has preview access (static)', () => {
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3-flash-preview');
|
||||
});
|
||||
|
||||
it('should resolve manual selection of gemini-3-flash-preview to gemini-3-flash-preview when useGemini3_5Flash is true and has preview access (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3-flash-preview');
|
||||
});
|
||||
|
||||
it('should resolve manual selection of gemini-3-flash-preview to DEFAULT_GEMINI_FLASH_MODEL when useGemini3_5Flash is true but lacks preview access (static)', () => {
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should resolve manual selection of gemini-3-flash-preview to gemini-3.5-flash when useGemini3_5Flash is true but lacks preview access (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveModel(
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3.5-flash');
|
||||
});
|
||||
|
||||
it('should resolve classifier-selected flash alias to DEFAULT_GEMINI_FLASH_MODEL when useGemini3_5Flash is true (static)', () => {
|
||||
expect(
|
||||
resolveClassifierModel(
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should resolve classifier-selected flash alias to gemini-3.5-flash when useGemini3_5Flash is true (dynamic)', () => {
|
||||
const mockDynamicConfig = {
|
||||
getExperimentalDynamicModelConfiguration: () => true,
|
||||
modelConfigService,
|
||||
} as unknown as Config;
|
||||
|
||||
expect(
|
||||
resolveClassifierModel(
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
mockDynamicConfig,
|
||||
true,
|
||||
),
|
||||
).toBe('gemini-3.5-flash');
|
||||
});
|
||||
|
||||
it('should resolve auto to PREVIEW_GEMINI_MODEL when useGemini3_5Flash is true and has preview access', () => {
|
||||
expect(
|
||||
resolveModel(
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
false,
|
||||
false,
|
||||
true, // hasAccessToPreview
|
||||
undefined,
|
||||
true, // useGemini3_5Flash
|
||||
),
|
||||
).toBe(PREVIEW_GEMINI_MODEL);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
|
||||
export interface ModelResolutionContext {
|
||||
useGemini3_1?: boolean;
|
||||
useGemini3_1FlashLite?: boolean;
|
||||
useGemini3_5Flash?: boolean;
|
||||
useCustomTools?: boolean;
|
||||
hasAccessToPreview?: boolean;
|
||||
requestedModel?: string;
|
||||
releaseChannel?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,18 +49,52 @@ export interface IModelConfigService {
|
||||
export interface ModelCapabilityContext {
|
||||
readonly modelConfigService: IModelConfigService;
|
||||
getExperimentalDynamicModelConfiguration(): boolean;
|
||||
getPreviewGeminiFlashModel?(): string;
|
||||
getDefaultGeminiFlashModel?(): string;
|
||||
}
|
||||
|
||||
export const PREVIEW_GEMINI_MODEL = 'gemini-3-pro-preview';
|
||||
export const PREVIEW_GEMINI_3_1_MODEL = 'gemini-3.1-pro-preview';
|
||||
export const PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL =
|
||||
'gemini-3.1-pro-preview-customtools';
|
||||
// TODO: set to none and const once the experiment for 3_5 flash rollut can be
|
||||
// cleaned up.
|
||||
export const PREVIEW_GEMINI_FLASH_MODEL = 'gemini-3-flash-preview';
|
||||
export const PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL =
|
||||
'gemini-3.1-flash-lite-preview';
|
||||
export const DEFAULT_GEMINI_MODEL = 'gemini-2.5-pro';
|
||||
// TODO: Set to const and update to 'gemini-3.5-flash' once the experiment for
|
||||
// 3_5 flash rollut can be cleaned up.
|
||||
// This is set to either the same as the DEFAULT_GEMINI_3_5_FLASH_MODEL const
|
||||
// OR the SECONDARY_GEMINI_3_5_FLASH_MODEL depending on which is needed for
|
||||
// the user's backend as determined by hasGemini35FlashGAAccess in
|
||||
// packages/core/src/config/config.ts
|
||||
export const DEFAULT_GEMINI_FLASH_MODEL = 'gemini-2.5-flash';
|
||||
export const DEFAULT_GEMINI_FLASH_LITE_MODEL = 'gemini-2.5-flash-lite';
|
||||
export const DEFAULT_GEMINI_3_5_FLASH_MODEL = 'gemini-3.5-flash';
|
||||
// This is resolved to 3.5 flash in backends where it is used,
|
||||
// however those backends do not expect to see the string gemini-3.5-flash
|
||||
// so we need to provide this model as an alternative name in certain instances.
|
||||
export const SECONDARY_GEMINI_3_5_FLASH_MODEL = 'gemini-3-flash';
|
||||
|
||||
export function getPreviewGeminiFlashModel(
|
||||
config?: ModelCapabilityContext,
|
||||
): string {
|
||||
if (config?.getPreviewGeminiFlashModel) {
|
||||
return config.getPreviewGeminiFlashModel();
|
||||
}
|
||||
return PREVIEW_GEMINI_FLASH_MODEL;
|
||||
}
|
||||
|
||||
export function getDefaultGeminiFlashModel(
|
||||
config?: ModelCapabilityContext,
|
||||
): string {
|
||||
if (config?.getDefaultGeminiFlashModel) {
|
||||
return config.getDefaultGeminiFlashModel();
|
||||
}
|
||||
return DEFAULT_GEMINI_FLASH_MODEL;
|
||||
}
|
||||
|
||||
export const DEFAULT_GEMINI_FLASH_LITE_MODEL = 'gemini-3.1-flash-lite';
|
||||
/** @deprecated Gemini 3.1 Flash Lite is now GA. Use DEFAULT_GEMINI_FLASH_LITE_MODEL. */
|
||||
export const PREVIEW_GEMINI_FLASH_LITE_MODEL = 'none';
|
||||
|
||||
export const GEMMA_4_31B_IT_MODEL = 'gemma-4-31b-it';
|
||||
export const GEMMA_4_26B_A4B_IT_MODEL = 'gemma-4-26b-a4b-it';
|
||||
@@ -69,16 +104,20 @@ export const VALID_GEMINI_MODELS = new Set([
|
||||
PREVIEW_GEMINI_3_1_MODEL,
|
||||
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_3_5_FLASH_MODEL,
|
||||
SECONDARY_GEMINI_3_5_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
|
||||
GEMMA_4_31B_IT_MODEL,
|
||||
GEMMA_4_26B_A4B_IT_MODEL,
|
||||
]);
|
||||
|
||||
/** @deprecated Use GEMINI_MODEL_ALIAS_AUTO instead. */
|
||||
export const PREVIEW_GEMINI_MODEL_AUTO = 'auto-gemini-3';
|
||||
/** @deprecated Use GEMINI_MODEL_ALIAS_AUTO instead. */
|
||||
export const DEFAULT_GEMINI_MODEL_AUTO = 'auto-gemini-2.5';
|
||||
|
||||
// Model aliases for user convenience.
|
||||
@@ -92,22 +131,41 @@ export const DEFAULT_GEMINI_EMBEDDING_MODEL = 'gemini-embedding-001';
|
||||
// Cap the thinking at 8192 to prevent run-away thinking loops.
|
||||
export const DEFAULT_THINKING_MODE = 8192;
|
||||
|
||||
export function getAutoModelDescription(
|
||||
hasAccessToPreview: boolean,
|
||||
useGemini3_1: boolean = false,
|
||||
useGemini3_5Flash: boolean = false,
|
||||
) {
|
||||
const proModel = hasAccessToPreview
|
||||
? useGemini3_1
|
||||
? PREVIEW_GEMINI_3_1_MODEL
|
||||
: PREVIEW_GEMINI_MODEL
|
||||
: DEFAULT_GEMINI_MODEL;
|
||||
const flashModel = hasAccessToPreview
|
||||
? useGemini3_5Flash
|
||||
? DEFAULT_GEMINI_3_5_FLASH_MODEL
|
||||
: PREVIEW_GEMINI_FLASH_MODEL
|
||||
: DEFAULT_GEMINI_FLASH_MODEL;
|
||||
return `Let Gemini CLI decide the best model for the task: ${getDisplayString(proModel)}, ${getDisplayString(flashModel)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the requested model alias (e.g., 'auto-gemini-3', 'pro', 'flash', 'flash-lite')
|
||||
* Resolves the requested model alias (e.g., 'auto', 'pro', 'flash', 'flash-lite')
|
||||
* to a concrete model name.
|
||||
*
|
||||
* @param requestedModel The model alias or concrete model name requested by the user.
|
||||
* @param useGemini3_1 Whether to use Gemini 3.1 Pro Preview for auto/pro aliases.
|
||||
* @param useGemini3_5Flash Whether to use Gemini 3.5 Flash GA.
|
||||
* @param hasAccessToPreview Whether the user has access to preview models.
|
||||
* @returns The resolved concrete model name.
|
||||
*/
|
||||
export function resolveModel(
|
||||
requestedModel: string,
|
||||
useGemini3_1: boolean = false,
|
||||
useGemini3_1FlashLite: boolean = false,
|
||||
useCustomToolModel: boolean = false,
|
||||
hasAccessToPreview: boolean = true,
|
||||
config?: ModelCapabilityContext,
|
||||
useGemini3_5Flash: boolean = false,
|
||||
): string {
|
||||
// Defensive check against non-string inputs at runtime
|
||||
const normalizedModel = Array.isArray(requestedModel)
|
||||
@@ -119,9 +177,9 @@ export function resolveModel(
|
||||
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
|
||||
const resolved = config.modelConfigService.resolveModelId(normalizedModel, {
|
||||
useGemini3_1,
|
||||
useGemini3_1FlashLite,
|
||||
useCustomTools: useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
useGemini3_5Flash,
|
||||
});
|
||||
|
||||
if (!hasAccessToPreview && isPreviewModel(resolved, config)) {
|
||||
@@ -140,10 +198,16 @@ export function resolveModel(
|
||||
|
||||
let resolved: string;
|
||||
switch (normalizedModel) {
|
||||
case PREVIEW_GEMINI_MODEL:
|
||||
case PREVIEW_GEMINI_MODEL_AUTO:
|
||||
case GEMINI_MODEL_ALIAS_AUTO:
|
||||
case GEMINI_MODEL_ALIAS_PRO: {
|
||||
if (!hasAccessToPreview) {
|
||||
resolved = DEFAULT_GEMINI_MODEL;
|
||||
break;
|
||||
}
|
||||
// fallthrough
|
||||
}
|
||||
case PREVIEW_GEMINI_MODEL:
|
||||
case PREVIEW_GEMINI_MODEL_AUTO: {
|
||||
if (useGemini3_1) {
|
||||
resolved = useCustomToolModel
|
||||
? PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL
|
||||
@@ -158,13 +222,13 @@ export function resolveModel(
|
||||
break;
|
||||
}
|
||||
case GEMINI_MODEL_ALIAS_FLASH: {
|
||||
resolved = PREVIEW_GEMINI_FLASH_MODEL;
|
||||
resolved = useGemini3_5Flash
|
||||
? getDefaultGeminiFlashModel(config)
|
||||
: getPreviewGeminiFlashModel(config);
|
||||
break;
|
||||
}
|
||||
case GEMINI_MODEL_ALIAS_FLASH_LITE: {
|
||||
resolved = useGemini3_1FlashLite
|
||||
? PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL
|
||||
: DEFAULT_GEMINI_FLASH_LITE_MODEL;
|
||||
resolved = DEFAULT_GEMINI_FLASH_LITE_MODEL;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -173,13 +237,23 @@ export function resolveModel(
|
||||
}
|
||||
}
|
||||
|
||||
if (resolved === 'none') {
|
||||
return DEFAULT_GEMINI_FLASH_LITE_MODEL;
|
||||
}
|
||||
|
||||
if (
|
||||
useGemini3_5Flash &&
|
||||
isFlashModel(resolved) &&
|
||||
normalizedModel !== getPreviewGeminiFlashModel(config)
|
||||
) {
|
||||
return getDefaultGeminiFlashModel(config);
|
||||
}
|
||||
|
||||
if (!hasAccessToPreview && isPreviewModel(resolved)) {
|
||||
// Downgrade to stable models if user lacks preview access.
|
||||
switch (resolved) {
|
||||
case PREVIEW_GEMINI_FLASH_MODEL:
|
||||
return DEFAULT_GEMINI_FLASH_MODEL;
|
||||
case PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL:
|
||||
return DEFAULT_GEMINI_FLASH_LITE_MODEL;
|
||||
case getPreviewGeminiFlashModel(config):
|
||||
return getDefaultGeminiFlashModel(config);
|
||||
case PREVIEW_GEMINI_MODEL:
|
||||
case PREVIEW_GEMINI_3_1_MODEL:
|
||||
case PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL:
|
||||
@@ -190,7 +264,7 @@ export function resolveModel(
|
||||
return DEFAULT_GEMINI_FLASH_LITE_MODEL;
|
||||
}
|
||||
if (resolved.includes('flash')) {
|
||||
return DEFAULT_GEMINI_FLASH_MODEL;
|
||||
return getDefaultGeminiFlashModel(config);
|
||||
}
|
||||
return DEFAULT_GEMINI_MODEL;
|
||||
}
|
||||
@@ -199,10 +273,21 @@ export function resolveModel(
|
||||
return resolved;
|
||||
}
|
||||
|
||||
function isFlashModel(model: string): boolean {
|
||||
return (
|
||||
model === DEFAULT_GEMINI_FLASH_MODEL ||
|
||||
model === PREVIEW_GEMINI_FLASH_MODEL ||
|
||||
model === DEFAULT_GEMINI_3_5_FLASH_MODEL ||
|
||||
model === SECONDARY_GEMINI_3_5_FLASH_MODEL ||
|
||||
model === 'flash' ||
|
||||
model.endsWith('flash')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the appropriate model based on the classifier's decision.
|
||||
*
|
||||
* @param requestedModel The current requested model (e.g. auto-gemini-2.5).
|
||||
* @param requestedModel The current requested model (e.g. auto).
|
||||
* @param modelAlias The alias selected by the classifier ('flash' or 'pro').
|
||||
* @param useGemini3_1 Whether to use Gemini 3.1 Pro Preview.
|
||||
* @param useCustomToolModel Whether to use the custom tool model.
|
||||
@@ -213,10 +298,10 @@ export function resolveClassifierModel(
|
||||
requestedModel: string,
|
||||
modelAlias: string,
|
||||
useGemini3_1: boolean = false,
|
||||
useGemini3_1FlashLite: boolean = false,
|
||||
useCustomToolModel: boolean = false,
|
||||
hasAccessToPreview: boolean = true,
|
||||
config?: ModelCapabilityContext,
|
||||
useGemini3_5Flash: boolean = false,
|
||||
): string {
|
||||
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
|
||||
return config.modelConfigService.resolveClassifierModelId(
|
||||
@@ -224,9 +309,9 @@ export function resolveClassifierModel(
|
||||
requestedModel,
|
||||
{
|
||||
useGemini3_1,
|
||||
useGemini3_1FlashLite,
|
||||
useCustomTools: useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
useGemini3_5Flash,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -236,21 +321,36 @@ export function resolveClassifierModel(
|
||||
requestedModel === DEFAULT_GEMINI_MODEL_AUTO ||
|
||||
requestedModel === DEFAULT_GEMINI_MODEL
|
||||
) {
|
||||
return DEFAULT_GEMINI_FLASH_MODEL;
|
||||
return getDefaultGeminiFlashModel(config);
|
||||
}
|
||||
if (
|
||||
requestedModel === PREVIEW_GEMINI_MODEL_AUTO ||
|
||||
requestedModel === PREVIEW_GEMINI_MODEL
|
||||
requestedModel === PREVIEW_GEMINI_MODEL ||
|
||||
requestedModel === GEMINI_MODEL_ALIAS_AUTO
|
||||
) {
|
||||
return PREVIEW_GEMINI_FLASH_MODEL;
|
||||
if (useGemini3_5Flash) {
|
||||
return getDefaultGeminiFlashModel(config);
|
||||
}
|
||||
return hasAccessToPreview
|
||||
? getPreviewGeminiFlashModel(config)
|
||||
: getDefaultGeminiFlashModel(config);
|
||||
}
|
||||
return resolveModel(GEMINI_MODEL_ALIAS_FLASH);
|
||||
return resolveModel(
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
false,
|
||||
false,
|
||||
hasAccessToPreview,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
);
|
||||
}
|
||||
return resolveModel(
|
||||
requestedModel,
|
||||
useGemini3_1,
|
||||
useGemini3_1FlashLite,
|
||||
useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -266,6 +366,10 @@ export function getDisplayString(
|
||||
}
|
||||
|
||||
switch (model) {
|
||||
case 'gemini-3-flash':
|
||||
return DEFAULT_GEMINI_3_5_FLASH_MODEL;
|
||||
case GEMINI_MODEL_ALIAS_AUTO:
|
||||
return 'Auto';
|
||||
case PREVIEW_GEMINI_MODEL_AUTO:
|
||||
return 'Auto (Gemini 3)';
|
||||
case DEFAULT_GEMINI_MODEL_AUTO:
|
||||
@@ -277,11 +381,11 @@ export function getDisplayString(
|
||||
case GEMINI_MODEL_ALIAS_PRO:
|
||||
return PREVIEW_GEMINI_MODEL;
|
||||
case GEMINI_MODEL_ALIAS_FLASH:
|
||||
return PREVIEW_GEMINI_FLASH_MODEL;
|
||||
return getPreviewGeminiFlashModel(config);
|
||||
case PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL:
|
||||
return PREVIEW_GEMINI_3_1_MODEL;
|
||||
case PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL:
|
||||
return PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL;
|
||||
case PREVIEW_GEMINI_FLASH_LITE_MODEL:
|
||||
return PREVIEW_GEMINI_FLASH_LITE_MODEL;
|
||||
default:
|
||||
return model;
|
||||
}
|
||||
@@ -298,6 +402,9 @@ export function isPreviewModel(
|
||||
model: string,
|
||||
config?: ModelCapabilityContext,
|
||||
): boolean {
|
||||
if (model === 'none') {
|
||||
return false;
|
||||
}
|
||||
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
|
||||
return (
|
||||
config.modelConfigService.getModelDefinition(model)?.isPreview === true
|
||||
@@ -311,7 +418,7 @@ export function isPreviewModel(
|
||||
model === PREVIEW_GEMINI_FLASH_MODEL ||
|
||||
model === PREVIEW_GEMINI_MODEL_AUTO ||
|
||||
model === GEMINI_MODEL_ALIAS_AUTO ||
|
||||
model === PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL
|
||||
model === PREVIEW_GEMINI_FLASH_LITE_MODEL
|
||||
);
|
||||
}
|
||||
|
||||
@@ -345,7 +452,7 @@ export function isGemini3Model(
|
||||
): boolean {
|
||||
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
|
||||
// Legacy behavior resolves the model first.
|
||||
const resolved = resolveModel(model, false, false, false, true, config);
|
||||
const resolved = resolveModel(model, false, false, true, config);
|
||||
return (
|
||||
config.modelConfigService.getModelDefinition(resolved)?.family ===
|
||||
'gemini-3'
|
||||
@@ -380,7 +487,7 @@ export function isCustomModel(
|
||||
config?: ModelCapabilityContext,
|
||||
): boolean {
|
||||
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
|
||||
const resolved = resolveModel(model, false, false, false, true, config);
|
||||
const resolved = resolveModel(model, false, false, true, config);
|
||||
return (
|
||||
config.modelConfigService.getModelDefinition(resolved)?.tier ===
|
||||
'custom' || !resolved.startsWith('gemini-')
|
||||
@@ -453,18 +560,17 @@ export function supportsMultimodalFunctionResponse(
|
||||
export function isActiveModel(
|
||||
model: string,
|
||||
useGemini3_1: boolean = false,
|
||||
useGemini3_1FlashLite: boolean = false,
|
||||
useCustomToolModel: boolean = false,
|
||||
experimentalGemma: boolean = true,
|
||||
): boolean {
|
||||
if (!VALID_GEMINI_MODELS.has(model)) {
|
||||
if (!VALID_GEMINI_MODELS.has(model) || model === 'none') {
|
||||
return false;
|
||||
}
|
||||
if (model === GEMMA_4_31B_IT_MODEL || model === GEMMA_4_26B_A4B_IT_MODEL) {
|
||||
return experimentalGemma;
|
||||
}
|
||||
if (model === PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL) {
|
||||
return useGemini3_1FlashLite;
|
||||
if (model === PREVIEW_GEMINI_FLASH_LITE_MODEL) {
|
||||
return false;
|
||||
}
|
||||
if (useGemini3_1) {
|
||||
if (model === PREVIEW_GEMINI_MODEL) {
|
||||
@@ -482,3 +588,7 @@ export function isActiveModel(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const CCPA_AI_MODEL_MAPPINGS: Record<string, string> = {
|
||||
[DEFAULT_GEMINI_3_5_FLASH_MODEL]: SECONDARY_GEMINI_3_5_FLASH_MODEL,
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
PREVIEW_GEMINI_3_1_MODEL,
|
||||
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_LITE_MODEL,
|
||||
} from '../config/models.js';
|
||||
import { PreCompressTrigger } from '../hooks/types.js';
|
||||
|
||||
@@ -106,14 +106,16 @@ export function modelStringToModelConfigAlias(model: string): string {
|
||||
return 'chat-compression-3-pro';
|
||||
case PREVIEW_GEMINI_FLASH_MODEL:
|
||||
return 'chat-compression-3-flash';
|
||||
case PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL:
|
||||
case PREVIEW_GEMINI_FLASH_LITE_MODEL:
|
||||
// fallthrough
|
||||
case DEFAULT_GEMINI_FLASH_LITE_MODEL:
|
||||
return 'chat-compression-3.1-flash-lite';
|
||||
case 'gemini-2.5-flash-lite':
|
||||
return 'chat-compression-2.5-flash-lite';
|
||||
case DEFAULT_GEMINI_MODEL:
|
||||
return 'chat-compression-2.5-pro';
|
||||
case DEFAULT_GEMINI_FLASH_MODEL:
|
||||
return 'chat-compression-2.5-flash';
|
||||
case DEFAULT_GEMINI_FLASH_LITE_MODEL:
|
||||
return 'chat-compression-2.5-flash-lite';
|
||||
default:
|
||||
return 'chat-compression-default';
|
||||
}
|
||||
|
||||
@@ -604,10 +604,10 @@ export class GeminiClient {
|
||||
return resolveModel(
|
||||
this.config.getActiveModel(),
|
||||
this.config.getGemini31LaunchedSync?.() ?? false,
|
||||
this.config.getGemini31FlashLiteLaunchedSync?.() ?? false,
|
||||
false,
|
||||
this.config.getHasAccessToPreviewModel?.() ?? true,
|
||||
this.config,
|
||||
this.config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,10 +15,13 @@ import { createCodeAssistContentGenerator } from '../code_assist/codeAssist.js';
|
||||
import { GoogleGenAI } from '@google/genai';
|
||||
import type { Config } from '../config/config.js';
|
||||
import { LoggingContentGenerator } from './loggingContentGenerator.js';
|
||||
import { ModelMappingContentGenerator } from './modelMappingContentGenerator.js';
|
||||
import { CCPA_AI_MODEL_MAPPINGS } from '../config/models.js';
|
||||
import { loadApiKey } from './apiKeyCredentialStorage.js';
|
||||
import { FakeContentGenerator } from './fakeContentGenerator.js';
|
||||
import { RecordingContentGenerator } from './recordingContentGenerator.js';
|
||||
import { resetVersionCache } from '../utils/version.js';
|
||||
import type { LlmRole } from '../telemetry/llmRole.js';
|
||||
|
||||
vi.mock('../code_assist/codeAssist.js');
|
||||
vi.mock('@google/genai');
|
||||
@@ -33,6 +36,14 @@ const mockConfig = {
|
||||
getProxy: vi.fn().mockReturnValue(undefined),
|
||||
getUsageStatisticsEnabled: vi.fn().mockReturnValue(true),
|
||||
getClientName: vi.fn().mockReturnValue(undefined),
|
||||
getTelemetryLogPromptsEnabled: vi.fn().mockReturnValue(true),
|
||||
getTelemetryTracesEnabled: vi.fn().mockReturnValue(true),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
refreshUserQuotaIfStale: vi.fn().mockResolvedValue(undefined),
|
||||
setLatestApiRequest: vi.fn(),
|
||||
getContentGeneratorConfig: vi.fn().mockReturnValue({}),
|
||||
isInteractive: vi.fn().mockReturnValue(false),
|
||||
getExperiments: vi.fn().mockReturnValue(undefined),
|
||||
} as unknown as Config;
|
||||
|
||||
describe('createContentGenerator', () => {
|
||||
@@ -100,7 +111,10 @@ describe('createContentGenerator', () => {
|
||||
);
|
||||
expect(createCodeAssistContentGenerator).toHaveBeenCalled();
|
||||
expect(generator).toEqual(
|
||||
new LoggingContentGenerator(mockGenerator, mockConfig),
|
||||
new LoggingContentGenerator(
|
||||
new ModelMappingContentGenerator(mockGenerator, CCPA_AI_MODEL_MAPPINGS),
|
||||
mockConfig,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -117,7 +131,10 @@ describe('createContentGenerator', () => {
|
||||
);
|
||||
expect(createCodeAssistContentGenerator).toHaveBeenCalled();
|
||||
expect(generator).toEqual(
|
||||
new LoggingContentGenerator(mockGenerator, mockConfig),
|
||||
new LoggingContentGenerator(
|
||||
new ModelMappingContentGenerator(mockGenerator, CCPA_AI_MODEL_MAPPINGS),
|
||||
mockConfig,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -851,6 +868,178 @@ describe('createContentGenerator', () => {
|
||||
),
|
||||
).rejects.toThrow('Invalid custom base URL: not-a-url');
|
||||
});
|
||||
|
||||
it('should not apply model mapping for Vertex AI', async () => {
|
||||
const mockModels = {
|
||||
generateContent: vi.fn().mockResolvedValue({}),
|
||||
};
|
||||
const mockGenerator = {
|
||||
models: mockModels,
|
||||
} as unknown as GoogleGenAI;
|
||||
vi.mocked(GoogleGenAI).mockImplementation(() => mockGenerator as never);
|
||||
|
||||
const generator = await createContentGenerator(
|
||||
{
|
||||
apiKey: 'test-api-key',
|
||||
authType: AuthType.USE_VERTEX_AI,
|
||||
vertexai: true,
|
||||
},
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
await generator.generateContent(
|
||||
{
|
||||
model: 'gemini-3-flash',
|
||||
contents: [],
|
||||
},
|
||||
'prompt-id',
|
||||
'user' as LlmRole,
|
||||
);
|
||||
|
||||
expect(mockModels.generateContent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
model: 'gemini-3-flash',
|
||||
}),
|
||||
'prompt-id',
|
||||
'user',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not apply model mapping for Gemini API', async () => {
|
||||
const mockModels = {
|
||||
generateContent: vi.fn().mockResolvedValue({}),
|
||||
};
|
||||
const mockGenerator = {
|
||||
models: mockModels,
|
||||
} as unknown as GoogleGenAI;
|
||||
vi.mocked(GoogleGenAI).mockImplementation(() => mockGenerator as never);
|
||||
|
||||
const generator = await createContentGenerator(
|
||||
{
|
||||
apiKey: 'test-api-key',
|
||||
authType: AuthType.USE_GEMINI,
|
||||
},
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
await generator.generateContent(
|
||||
{
|
||||
model: 'gemini-3-flash',
|
||||
contents: [],
|
||||
},
|
||||
'prompt-id',
|
||||
'user' as LlmRole,
|
||||
);
|
||||
|
||||
expect(mockModels.generateContent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
model: 'gemini-3-flash',
|
||||
}),
|
||||
'prompt-id',
|
||||
'user',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not apply model mapping for GATEWAY', async () => {
|
||||
const mockModels = {
|
||||
generateContent: vi.fn().mockResolvedValue({}),
|
||||
};
|
||||
const mockGenerator = {
|
||||
models: mockModels,
|
||||
} as unknown as GoogleGenAI;
|
||||
vi.mocked(GoogleGenAI).mockImplementation(() => mockGenerator as never);
|
||||
|
||||
const generator = await createContentGenerator(
|
||||
{
|
||||
apiKey: 'test-api-key',
|
||||
authType: AuthType.GATEWAY,
|
||||
},
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
await generator.generateContent(
|
||||
{
|
||||
model: 'gemini-3.5-flash',
|
||||
contents: [],
|
||||
},
|
||||
'prompt-id',
|
||||
'user' as LlmRole,
|
||||
);
|
||||
|
||||
expect(mockModels.generateContent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
model: 'gemini-3.5-flash',
|
||||
}),
|
||||
'prompt-id',
|
||||
'user',
|
||||
);
|
||||
});
|
||||
|
||||
it('should apply model mapping for LOGIN_WITH_GOOGLE', async () => {
|
||||
const mockInnerGenerator = {
|
||||
generateContent: vi.fn().mockResolvedValue({}),
|
||||
} as unknown as ContentGenerator;
|
||||
vi.mocked(createCodeAssistContentGenerator).mockResolvedValue(
|
||||
mockInnerGenerator as never,
|
||||
);
|
||||
|
||||
const generator = await createContentGenerator(
|
||||
{
|
||||
authType: AuthType.LOGIN_WITH_GOOGLE,
|
||||
},
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
await generator.generateContent(
|
||||
{
|
||||
model: 'gemini-3.5-flash',
|
||||
contents: [],
|
||||
},
|
||||
'prompt-id',
|
||||
'user' as LlmRole,
|
||||
);
|
||||
|
||||
expect(mockInnerGenerator.generateContent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
model: 'gemini-3-flash',
|
||||
}),
|
||||
'prompt-id',
|
||||
'user',
|
||||
);
|
||||
});
|
||||
|
||||
it('should apply model mapping for COMPUTE_ADC', async () => {
|
||||
const mockInnerGenerator = {
|
||||
generateContent: vi.fn().mockResolvedValue({}),
|
||||
} as unknown as ContentGenerator;
|
||||
vi.mocked(createCodeAssistContentGenerator).mockResolvedValue(
|
||||
mockInnerGenerator as never,
|
||||
);
|
||||
|
||||
const generator = await createContentGenerator(
|
||||
{
|
||||
authType: AuthType.COMPUTE_ADC,
|
||||
},
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
await generator.generateContent(
|
||||
{
|
||||
model: 'gemini-3.5-flash',
|
||||
contents: [],
|
||||
},
|
||||
'prompt-id',
|
||||
'user' as LlmRole,
|
||||
);
|
||||
|
||||
expect(mockInnerGenerator.generateContent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
model: 'gemini-3-flash',
|
||||
}),
|
||||
'prompt-id',
|
||||
'user',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createContentGeneratorConfig', () => {
|
||||
|
||||
@@ -28,6 +28,8 @@ import { determineSurface } from '../utils/surface.js';
|
||||
import { RecordingContentGenerator } from './recordingContentGenerator.js';
|
||||
import { getVersion, resolveModel } from '../../index.js';
|
||||
import type { LlmRole } from '../telemetry/llmRole.js';
|
||||
import { ModelMappingContentGenerator } from './modelMappingContentGenerator.js';
|
||||
import { CCPA_AI_MODEL_MAPPINGS } from '../config/models.js';
|
||||
|
||||
/**
|
||||
* Interface abstracting the core functionalities for generating content and counting tokens.
|
||||
@@ -205,12 +207,10 @@ export async function createContentGenerator(
|
||||
config.authType === AuthType.USE_GEMINI ||
|
||||
config.authType === AuthType.USE_VERTEX_AI ||
|
||||
((await gcConfig.getGemini31Launched?.()) ?? false),
|
||||
config.authType === AuthType.USE_GEMINI ||
|
||||
config.authType === AuthType.USE_VERTEX_AI ||
|
||||
((await gcConfig.getGemini31FlashLiteLaunched?.()) ?? false),
|
||||
false,
|
||||
gcConfig.getHasAccessToPreviewModel?.() ?? true,
|
||||
gcConfig,
|
||||
gcConfig.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
const customHeadersEnv =
|
||||
process.env['GEMINI_CLI_CUSTOM_HEADERS'] || undefined;
|
||||
@@ -271,11 +271,14 @@ export async function createContentGenerator(
|
||||
) {
|
||||
const httpOptions = { headers: baseHeaders };
|
||||
return new LoggingContentGenerator(
|
||||
await createCodeAssistContentGenerator(
|
||||
httpOptions,
|
||||
config.authType,
|
||||
gcConfig,
|
||||
sessionId,
|
||||
new ModelMappingContentGenerator(
|
||||
await createCodeAssistContentGenerator(
|
||||
httpOptions,
|
||||
config.authType,
|
||||
gcConfig,
|
||||
sessionId,
|
||||
),
|
||||
CCPA_AI_MODEL_MAPPINGS,
|
||||
),
|
||||
gcConfig,
|
||||
);
|
||||
|
||||
@@ -157,6 +157,7 @@ describe('GeminiChat', () => {
|
||||
getTelemetryLogPromptsEnabled: () => true,
|
||||
getTelemetryTracesEnabled: () => false,
|
||||
getUsageStatisticsEnabled: () => true,
|
||||
hasGemini35FlashGAAccess: vi.fn().mockReturnValue(false),
|
||||
getDebugMode: () => false,
|
||||
getContentGeneratorConfig: vi.fn().mockImplementation(() => ({
|
||||
authType: 'oauth-personal',
|
||||
|
||||
@@ -604,19 +604,16 @@ export class GeminiChat {
|
||||
const apiCall = async () => {
|
||||
const useGemini3_1 =
|
||||
(await this.context.config.getGemini31Launched?.()) ?? false;
|
||||
const useGemini3_1FlashLite =
|
||||
(await this.context.config.getGemini31FlashLiteLaunched?.()) ?? false;
|
||||
const hasAccessToPreview =
|
||||
this.context.config.getHasAccessToPreviewModel?.() ?? true;
|
||||
|
||||
// Default to the last used model (which respects arguments/availability selection)
|
||||
let modelToUse = resolveModel(
|
||||
lastModelToUse,
|
||||
useGemini3_1,
|
||||
useGemini3_1FlashLite,
|
||||
false,
|
||||
hasAccessToPreview,
|
||||
this.context.config,
|
||||
this.context.config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
|
||||
// If the active model has changed (e.g. due to a fallback updating the config),
|
||||
@@ -625,10 +622,10 @@ export class GeminiChat {
|
||||
modelToUse = resolveModel(
|
||||
this.context.config.getActiveModel(),
|
||||
useGemini3_1,
|
||||
useGemini3_1FlashLite,
|
||||
false,
|
||||
hasAccessToPreview,
|
||||
this.context.config,
|
||||
this.context.config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -688,10 +685,10 @@ export class GeminiChat {
|
||||
modelToUse = resolveModel(
|
||||
beforeModelResult.modifiedModel,
|
||||
useGemini3_1,
|
||||
useGemini3_1FlashLite,
|
||||
false,
|
||||
hasAccessToPreview,
|
||||
this.context.config,
|
||||
this.context.config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
lastModelToUse = modelToUse;
|
||||
// Re-evaluate contentsToUse based on the new model's feature support
|
||||
|
||||
@@ -98,6 +98,7 @@ describe('GeminiChat Network Retries', () => {
|
||||
getTelemetryLogPromptsEnabled: () => true,
|
||||
getTelemetryTracesEnabled: () => false,
|
||||
getUsageStatisticsEnabled: () => true,
|
||||
hasGemini35FlashGAAccess: vi.fn().mockReturnValue(false),
|
||||
getDebugMode: () => false,
|
||||
getContentGeneratorConfig: vi.fn().mockReturnValue({
|
||||
authType: 'oauth-personal',
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { ModelMappingContentGenerator } from './modelMappingContentGenerator.js';
|
||||
import type { ContentGenerator } from './contentGenerator.js';
|
||||
import { LlmRole } from '../telemetry/llmRole.js';
|
||||
import type { GenerateContentParameters } from '@google/genai';
|
||||
|
||||
describe('ModelMappingContentGenerator', () => {
|
||||
const mockMappings = {
|
||||
'gemini-3.5-flash': 'gemini-3-flash',
|
||||
'gemini-pro': 'gemini-1.5-pro',
|
||||
};
|
||||
|
||||
it('delegates userTier, userTierName, and paidTier properties', () => {
|
||||
const mockWrapped = {
|
||||
userTier: 'free',
|
||||
userTierName: 'Free Tier',
|
||||
paidTier: { id: 'paid' },
|
||||
} as unknown as ContentGenerator;
|
||||
|
||||
const generator = new ModelMappingContentGenerator(
|
||||
mockWrapped,
|
||||
mockMappings,
|
||||
);
|
||||
|
||||
expect(generator.userTier).toBe('free');
|
||||
expect(generator.userTierName).toBe('Free Tier');
|
||||
expect(generator.paidTier).toEqual({ id: 'paid' });
|
||||
});
|
||||
|
||||
it('maps matching model without prefix', async () => {
|
||||
const mockWrapped = {
|
||||
generateContent: vi.fn().mockResolvedValue({}),
|
||||
} as unknown as ContentGenerator;
|
||||
|
||||
const generator = new ModelMappingContentGenerator(
|
||||
mockWrapped,
|
||||
mockMappings,
|
||||
);
|
||||
const req = { model: 'gemini-3.5-flash', contents: [] };
|
||||
|
||||
await generator.generateContent(req, 'prompt-id', LlmRole.MAIN);
|
||||
|
||||
expect(mockWrapped.generateContent).toHaveBeenCalledWith(
|
||||
{ model: 'gemini-3-flash', contents: [] },
|
||||
'prompt-id',
|
||||
LlmRole.MAIN,
|
||||
);
|
||||
});
|
||||
|
||||
it('maps matching model with models/ prefix', async () => {
|
||||
const mockWrapped = {
|
||||
generateContent: vi.fn().mockResolvedValue({}),
|
||||
} as unknown as ContentGenerator;
|
||||
|
||||
const generator = new ModelMappingContentGenerator(
|
||||
mockWrapped,
|
||||
mockMappings,
|
||||
);
|
||||
const req = { model: 'models/gemini-3.5-flash', contents: [] };
|
||||
|
||||
await generator.generateContent(req, 'prompt-id', LlmRole.MAIN);
|
||||
|
||||
expect(mockWrapped.generateContent).toHaveBeenCalledWith(
|
||||
{ model: 'models/gemini-3-flash', contents: [] },
|
||||
'prompt-id',
|
||||
LlmRole.MAIN,
|
||||
);
|
||||
});
|
||||
|
||||
it('leaves unmapped model unchanged', async () => {
|
||||
const mockWrapped = {
|
||||
generateContent: vi.fn().mockResolvedValue({}),
|
||||
} as unknown as ContentGenerator;
|
||||
|
||||
const generator = new ModelMappingContentGenerator(
|
||||
mockWrapped,
|
||||
mockMappings,
|
||||
);
|
||||
const req = { model: 'unknown-model', contents: [] };
|
||||
|
||||
await generator.generateContent(req, 'prompt-id', LlmRole.MAIN);
|
||||
|
||||
expect(mockWrapped.generateContent).toHaveBeenCalledWith(
|
||||
{ model: 'unknown-model', contents: [] },
|
||||
'prompt-id',
|
||||
LlmRole.MAIN,
|
||||
);
|
||||
});
|
||||
|
||||
it('leaves model with prefix unchanged if no match after normalization', async () => {
|
||||
const mockWrapped = {
|
||||
generateContent: vi.fn().mockResolvedValue({}),
|
||||
} as unknown as ContentGenerator;
|
||||
|
||||
const generator = new ModelMappingContentGenerator(
|
||||
mockWrapped,
|
||||
mockMappings,
|
||||
);
|
||||
const req = { model: 'models/unknown-model', contents: [] };
|
||||
|
||||
await generator.generateContent(req, 'prompt-id', LlmRole.MAIN);
|
||||
|
||||
expect(mockWrapped.generateContent).toHaveBeenCalledWith(
|
||||
{ model: 'models/unknown-model', contents: [] },
|
||||
'prompt-id',
|
||||
LlmRole.MAIN,
|
||||
);
|
||||
});
|
||||
|
||||
it('handles missing/undefined model property safely', async () => {
|
||||
const mockWrapped = {
|
||||
generateContent: vi.fn().mockResolvedValue({}),
|
||||
} as unknown as ContentGenerator;
|
||||
|
||||
const generator = new ModelMappingContentGenerator(
|
||||
mockWrapped,
|
||||
mockMappings,
|
||||
);
|
||||
const req = { contents: [] } as unknown as GenerateContentParameters;
|
||||
|
||||
await generator.generateContent(req, 'prompt-id', LlmRole.MAIN);
|
||||
|
||||
expect(mockWrapped.generateContent).toHaveBeenCalledWith(
|
||||
{ contents: [] },
|
||||
'prompt-id',
|
||||
LlmRole.MAIN,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {
|
||||
type CountTokensResponse,
|
||||
type GenerateContentResponse,
|
||||
type GenerateContentParameters,
|
||||
type CountTokensParameters,
|
||||
type EmbedContentResponse,
|
||||
type EmbedContentParameters,
|
||||
} from '@google/genai';
|
||||
import { type ContentGenerator } from './contentGenerator.js';
|
||||
import type { LlmRole } from '../telemetry/llmRole.js';
|
||||
import type { UserTierId, GeminiUserTier } from '../code_assist/types.js';
|
||||
import { normalizeModelId } from '../utils/modelUtils.js';
|
||||
|
||||
export class ModelMappingContentGenerator implements ContentGenerator {
|
||||
constructor(
|
||||
private readonly wrapped: ContentGenerator,
|
||||
private readonly mappings: Record<string, string>,
|
||||
) {}
|
||||
|
||||
getWrapped(): ContentGenerator {
|
||||
return this.wrapped;
|
||||
}
|
||||
|
||||
get userTier(): UserTierId | undefined {
|
||||
return this.wrapped.userTier;
|
||||
}
|
||||
|
||||
get userTierName(): string | undefined {
|
||||
return this.wrapped.userTierName;
|
||||
}
|
||||
|
||||
get paidTier(): GeminiUserTier | undefined {
|
||||
return this.wrapped.paidTier;
|
||||
}
|
||||
|
||||
private mapModel<T extends { model?: string }>(req: T): T {
|
||||
if (req.model) {
|
||||
const normalizedModel = normalizeModelId(req.model);
|
||||
if (this.mappings[normalizedModel]) {
|
||||
return {
|
||||
...req,
|
||||
model: req.model.startsWith('models/')
|
||||
? `models/${this.mappings[normalizedModel]}`
|
||||
: this.mappings[normalizedModel],
|
||||
};
|
||||
}
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
generateContent(
|
||||
request: GenerateContentParameters,
|
||||
userPromptId: string,
|
||||
role: LlmRole,
|
||||
): Promise<GenerateContentResponse> {
|
||||
return this.wrapped.generateContent(
|
||||
this.mapModel(request),
|
||||
userPromptId,
|
||||
role,
|
||||
);
|
||||
}
|
||||
|
||||
generateContentStream(
|
||||
request: GenerateContentParameters,
|
||||
userPromptId: string,
|
||||
role: LlmRole,
|
||||
): Promise<AsyncGenerator<GenerateContentResponse>> {
|
||||
return this.wrapped.generateContentStream(
|
||||
this.mapModel(request),
|
||||
userPromptId,
|
||||
role,
|
||||
);
|
||||
}
|
||||
|
||||
countTokens(request: CountTokensParameters): Promise<CountTokensResponse> {
|
||||
return this.wrapped.countTokens(this.mapModel(request));
|
||||
}
|
||||
|
||||
embedContent(request: EmbedContentParameters): Promise<EmbedContentResponse> {
|
||||
return this.wrapped.embedContent(this.mapModel(request));
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
} from '../config/models.js';
|
||||
import { ApprovalMode } from '../policy/types.js';
|
||||
import { DiscoveredMCPTool } from '../tools/mcp-tool.js';
|
||||
@@ -250,9 +249,7 @@ describe('Core System Prompt (prompts.ts)', () => {
|
||||
});
|
||||
|
||||
it('should use legacy system prompt for non-preview model', () => {
|
||||
vi.mocked(mockConfig.getActiveModel).mockReturnValue(
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
);
|
||||
vi.mocked(mockConfig.getActiveModel).mockReturnValue(DEFAULT_GEMINI_MODEL);
|
||||
const prompt = getCoreSystemPrompt(mockConfig);
|
||||
expect(prompt).toContain(
|
||||
'You are an interactive CLI agent specializing in software engineering tasks.',
|
||||
@@ -267,9 +264,7 @@ describe('Core System Prompt (prompts.ts)', () => {
|
||||
});
|
||||
|
||||
it('should include the TASK MANAGEMENT PROTOCOL in legacy prompt when task tracker is enabled', () => {
|
||||
vi.mocked(mockConfig.getActiveModel).mockReturnValue(
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
);
|
||||
vi.mocked(mockConfig.getActiveModel).mockReturnValue(DEFAULT_GEMINI_MODEL);
|
||||
vi.mocked(mockConfig.isTrackerEnabled).mockReturnValue(true);
|
||||
const prompt = getCoreSystemPrompt(mockConfig);
|
||||
expect(prompt).toContain('# TASK MANAGEMENT PROTOCOL');
|
||||
@@ -662,7 +657,7 @@ describe('Core System Prompt (prompts.ts)', () => {
|
||||
it('should include Windows-specific shell efficiency commands on win32', () => {
|
||||
mockPlatform('win32');
|
||||
vi.mocked(mockConfig.getActiveModel).mockReturnValue(
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
);
|
||||
const prompt = getCoreSystemPrompt(mockConfig);
|
||||
expect(prompt).toContain(
|
||||
@@ -676,7 +671,7 @@ describe('Core System Prompt (prompts.ts)', () => {
|
||||
it('should include generic shell efficiency commands on non-Windows', () => {
|
||||
mockPlatform('linux');
|
||||
vi.mocked(mockConfig.getActiveModel).mockReturnValue(
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
);
|
||||
const prompt = getCoreSystemPrompt(mockConfig);
|
||||
expect(prompt).toContain("using commands like 'grep', 'tail', 'head'");
|
||||
|
||||
@@ -77,6 +77,7 @@ const createMockConfig = (overrides: Partial<Config> = {}): Config =>
|
||||
getModel: vi.fn(() => MOCK_PRO_MODEL),
|
||||
getUserTier: vi.fn(() => undefined),
|
||||
isInteractive: vi.fn(() => false),
|
||||
getHasAccessToPreviewModel: vi.fn(() => true),
|
||||
...overrides,
|
||||
}) as unknown as Config;
|
||||
|
||||
|
||||
@@ -73,10 +73,10 @@ export class PromptProvider {
|
||||
const desiredModel = resolveModel(
|
||||
context.config.getActiveModel(),
|
||||
context.config.getGemini31LaunchedSync?.() ?? false,
|
||||
context.config.getGemini31FlashLiteLaunchedSync?.() ?? false,
|
||||
false,
|
||||
context.config.getHasAccessToPreviewModel?.() ?? true,
|
||||
context.config,
|
||||
context.config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
const isModernModel = supportsModernFeatures(desiredModel);
|
||||
const activeSnippets = isModernModel ? snippets : legacySnippets;
|
||||
@@ -300,10 +300,10 @@ export class PromptProvider {
|
||||
const desiredModel = resolveModel(
|
||||
context.config.getActiveModel(),
|
||||
context.config.getGemini31LaunchedSync?.() ?? false,
|
||||
context.config.getGemini31FlashLiteLaunchedSync?.() ?? false,
|
||||
false,
|
||||
context.config.getHasAccessToPreviewModel?.() ?? true,
|
||||
context.config,
|
||||
context.config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
const isModernModel = supportsModernFeatures(desiredModel);
|
||||
const activeSnippets = isModernModel ? snippets : legacySnippets;
|
||||
|
||||
@@ -43,7 +43,6 @@ describe('ApprovalModeStrategy', () => {
|
||||
getApprovedPlanPath: vi.fn().mockReturnValue(undefined),
|
||||
getPlanModeRoutingEnabled: vi.fn().mockResolvedValue(true),
|
||||
getGemini31Launched: vi.fn().mockResolvedValue(false),
|
||||
getGemini31FlashLiteLaunched: vi.fn().mockResolvedValue(false),
|
||||
getHasAccessToPreviewModel: vi.fn().mockReturnValue(true),
|
||||
getUseCustomToolModel: vi.fn().mockImplementation(async () => {
|
||||
const launched = await mockConfig.getGemini31Launched();
|
||||
@@ -243,4 +242,22 @@ describe('ApprovalModeStrategy', () => {
|
||||
// Should resolve to Preview Flash (3.0) because resolveClassifierModel uses preview variants for Gemini 3
|
||||
expect(decision?.model).toBe(PREVIEW_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
|
||||
it('should route to DEFAULT_GEMINI_FLASH_MODEL when hasGemini35FlashGAAccess is true and plan is approved', async () => {
|
||||
vi.mocked(mockConfig.getModel).mockReturnValue(GEMINI_MODEL_ALIAS_AUTO);
|
||||
mockConfig.hasGemini35FlashGAAccess = vi.fn().mockReturnValue(true);
|
||||
|
||||
vi.mocked(mockConfig.getApprovalMode).mockReturnValue(ApprovalMode.DEFAULT);
|
||||
vi.mocked(mockConfig.getApprovedPlanPath).mockReturnValue(
|
||||
'/path/to/plan.md',
|
||||
);
|
||||
|
||||
const decision = await strategy.route(
|
||||
mockContext,
|
||||
mockConfig,
|
||||
mockBaseLlmClient,
|
||||
);
|
||||
|
||||
expect(decision?.model).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -48,17 +48,13 @@ export class ApprovalModeStrategy implements RoutingStrategy {
|
||||
const approvalMode = config.getApprovalMode();
|
||||
const approvedPlanPath = config.getApprovedPlanPath();
|
||||
|
||||
const [
|
||||
useGemini3_1,
|
||||
useGemini3_1FlashLite,
|
||||
useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
] = await Promise.all([
|
||||
config.getGemini31Launched(),
|
||||
config.getGemini31FlashLiteLaunched(),
|
||||
config.getUseCustomToolModel(),
|
||||
config.getHasAccessToPreviewModel(),
|
||||
]);
|
||||
const [useGemini3_1, useCustomToolModel, hasAccessToPreview] =
|
||||
await Promise.all([
|
||||
config.getGemini31Launched(),
|
||||
config.getUseCustomToolModel(),
|
||||
config.getHasAccessToPreviewModel(),
|
||||
]);
|
||||
const useGemini3_5Flash = config.hasGemini35FlashGAAccess?.() ?? false;
|
||||
|
||||
// 1. Planning Phase: If ApprovalMode === PLAN, explicitly route to the Pro model.
|
||||
if (approvalMode === ApprovalMode.PLAN) {
|
||||
@@ -66,10 +62,10 @@ export class ApprovalModeStrategy implements RoutingStrategy {
|
||||
model,
|
||||
GEMINI_MODEL_ALIAS_PRO,
|
||||
useGemini3_1,
|
||||
useGemini3_1FlashLite,
|
||||
useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
);
|
||||
return {
|
||||
model: proModel,
|
||||
@@ -85,10 +81,10 @@ export class ApprovalModeStrategy implements RoutingStrategy {
|
||||
model,
|
||||
GEMINI_MODEL_ALIAS_FLASH,
|
||||
useGemini3_1,
|
||||
useGemini3_1FlashLite,
|
||||
useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
);
|
||||
return {
|
||||
model: flashModel,
|
||||
|
||||
@@ -60,7 +60,6 @@ describe('ClassifierStrategy', () => {
|
||||
getModel: vi.fn().mockReturnValue(DEFAULT_GEMINI_MODEL_AUTO),
|
||||
getNumericalRoutingEnabled: vi.fn().mockResolvedValue(false),
|
||||
getGemini31Launched: vi.fn().mockResolvedValue(false),
|
||||
getGemini31FlashLiteLaunched: vi.fn().mockResolvedValue(false),
|
||||
getUseCustomToolModel: vi.fn().mockImplementation(async () => {
|
||||
const launched = await mockConfig.getGemini31Launched();
|
||||
const authType = mockConfig.getContentGeneratorConfig().authType;
|
||||
@@ -432,5 +431,27 @@ describe('ClassifierStrategy', () => {
|
||||
|
||||
expect(decision?.model).toBe(PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL);
|
||||
});
|
||||
|
||||
it('should route to DEFAULT_GEMINI_FLASH_MODEL when hasGemini35FlashGAAccess is true', async () => {
|
||||
mockConfig.hasGemini35FlashGAAccess = vi.fn().mockReturnValue(true);
|
||||
vi.mocked(mockConfig.getModel).mockReturnValue(PREVIEW_GEMINI_MODEL_AUTO);
|
||||
|
||||
const mockApiResponse = {
|
||||
reasoning: 'Simple task',
|
||||
model_choice: 'flash',
|
||||
};
|
||||
vi.mocked(mockBaseLlmClient.generateJson).mockResolvedValue(
|
||||
mockApiResponse,
|
||||
);
|
||||
|
||||
const decision = await strategy.route(
|
||||
mockContext,
|
||||
mockConfig,
|
||||
mockBaseLlmClient,
|
||||
mockLocalLiteRtLmClient,
|
||||
);
|
||||
|
||||
expect(decision?.model).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -172,21 +172,20 @@ export class ClassifierStrategy implements RoutingStrategy {
|
||||
|
||||
const reasoning = routerResponse.reasoning;
|
||||
const latencyMs = Date.now() - startTime;
|
||||
const [useGemini3_1, useGemini3_1FlashLite, useCustomToolModel] =
|
||||
await Promise.all([
|
||||
config.getGemini31Launched(),
|
||||
config.getGemini31FlashLiteLaunched(),
|
||||
config.getUseCustomToolModel(),
|
||||
]);
|
||||
const [useGemini3_1, useCustomToolModel] = await Promise.all([
|
||||
config.getGemini31Launched(),
|
||||
config.getUseCustomToolModel(),
|
||||
]);
|
||||
const useGemini3_5Flash = config.hasGemini35FlashGAAccess?.() ?? false;
|
||||
const selectedModel = normalizeModelId(
|
||||
resolveClassifierModel(
|
||||
normalizeModelId(model),
|
||||
routerResponse.model_choice,
|
||||
useGemini3_1,
|
||||
useGemini3_1FlashLite,
|
||||
useCustomToolModel,
|
||||
config.getHasAccessToPreviewModel?.() ?? true,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@ export class DefaultStrategy implements TerminalStrategy {
|
||||
const defaultModel = resolveModel(
|
||||
config.getModel(),
|
||||
config.getGemini31LaunchedSync?.() ?? false,
|
||||
config.getGemini31FlashLiteLaunchedSync?.() ?? false,
|
||||
false,
|
||||
config.getHasAccessToPreviewModel?.() ?? true,
|
||||
config,
|
||||
config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
return {
|
||||
model: defaultModel,
|
||||
|
||||
@@ -28,10 +28,10 @@ export class FallbackStrategy implements RoutingStrategy {
|
||||
const resolvedModel = resolveModel(
|
||||
requestedModel,
|
||||
config.getGemini31LaunchedSync?.() ?? false,
|
||||
config.getGemini31FlashLiteLaunchedSync?.() ?? false,
|
||||
false,
|
||||
config.getHasAccessToPreviewModel?.() ?? true,
|
||||
config,
|
||||
config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
);
|
||||
const service = config.getModelAvailabilityService();
|
||||
const snapshot = service.snapshot(resolvedModel);
|
||||
|
||||
@@ -12,6 +12,7 @@ import type { BaseLlmClient } from '../../core/baseLlmClient.js';
|
||||
import {
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
} from '../../config/models.js';
|
||||
import type { Content } from '@google/genai';
|
||||
import { debugLogger } from '../../utils/debugLogger.js';
|
||||
@@ -39,7 +40,6 @@ describe('GemmaClassifierStrategy', () => {
|
||||
getModel: () => DEFAULT_GEMINI_MODEL,
|
||||
getPreviewFeatures: () => false,
|
||||
getGemini31Launched: vi.fn().mockResolvedValue(false),
|
||||
getGemini31FlashLiteLaunched: vi.fn().mockResolvedValue(false),
|
||||
getUseCustomToolModel: vi.fn().mockResolvedValue(false),
|
||||
getHasAccessToPreviewModel: vi.fn().mockReturnValue(true),
|
||||
} as unknown as Config;
|
||||
@@ -324,4 +324,24 @@ second message
|
||||
|
||||
expect(lastTurn!.parts!.at(0)!.text).toEqual(expectedLastTurn);
|
||||
});
|
||||
|
||||
it('should route to DEFAULT_GEMINI_FLASH_MODEL when hasGemini35FlashGAAccess is true', async () => {
|
||||
mockConfig.hasGemini35FlashGAAccess = vi.fn().mockReturnValue(true);
|
||||
mockConfig.getModel = () => PREVIEW_GEMINI_MODEL_AUTO;
|
||||
|
||||
const mockApiResponse = {
|
||||
reasoning: 'Simple task',
|
||||
model_choice: 'flash',
|
||||
};
|
||||
mockGenerateJson.mockResolvedValue(mockApiResponse);
|
||||
|
||||
const decision = await strategy.route(
|
||||
mockContext,
|
||||
mockConfig,
|
||||
mockBaseLlmClient,
|
||||
mockLocalLiteRtLmClient,
|
||||
);
|
||||
|
||||
expect(decision?.model).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -210,26 +210,22 @@ ${formattedHistory}
|
||||
const reasoning = routerResponse.reasoning;
|
||||
const latencyMs = Date.now() - startTime;
|
||||
|
||||
const [
|
||||
useGemini3_1,
|
||||
useGemini3_1FlashLite,
|
||||
useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
] = await Promise.all([
|
||||
config.getGemini31Launched(),
|
||||
config.getGemini31FlashLiteLaunched(),
|
||||
config.getUseCustomToolModel(),
|
||||
config.getHasAccessToPreviewModel?.() ?? true,
|
||||
]);
|
||||
const [useGemini3_1, useCustomToolModel, hasAccessToPreview] =
|
||||
await Promise.all([
|
||||
config.getGemini31Launched(),
|
||||
config.getUseCustomToolModel(),
|
||||
config.getHasAccessToPreviewModel(),
|
||||
]);
|
||||
const useGemini3_5Flash = config.hasGemini35FlashGAAccess?.() ?? false;
|
||||
|
||||
const selectedModel = resolveClassifierModel(
|
||||
context.requestedModel ?? config.getModel(),
|
||||
routerResponse.model_choice,
|
||||
useGemini3_1,
|
||||
useGemini3_1FlashLite,
|
||||
useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
} from '../../config/models.js';
|
||||
import { promptIdContext } from '../../utils/promptIdContext.js';
|
||||
import type { Content } from '@google/genai';
|
||||
@@ -63,7 +64,6 @@ describe('NumericalClassifierStrategy', () => {
|
||||
getResolvedClassifierThreshold: vi.fn().mockResolvedValue(90),
|
||||
getClassifierThreshold: vi.fn().mockResolvedValue(undefined),
|
||||
getGemini31Launched: vi.fn().mockResolvedValue(false),
|
||||
getGemini31FlashLiteLaunched: vi.fn().mockResolvedValue(false),
|
||||
getUseCustomToolModel: vi.fn().mockImplementation(async () => {
|
||||
const launched = await mockConfig.getGemini31Launched();
|
||||
const authType = mockConfig.getContentGeneratorConfig().authType;
|
||||
@@ -796,5 +796,27 @@ describe('NumericalClassifierStrategy', () => {
|
||||
|
||||
expect(decision?.model).toBe(PREVIEW_GEMINI_3_1_MODEL);
|
||||
});
|
||||
|
||||
it('should route to DEFAULT_GEMINI_FLASH_MODEL when hasGemini35FlashGAAccess is true', async () => {
|
||||
mockConfig.hasGemini35FlashGAAccess = vi.fn().mockReturnValue(true);
|
||||
vi.mocked(mockConfig.getModel).mockReturnValue(PREVIEW_GEMINI_MODEL_AUTO);
|
||||
|
||||
const mockApiResponse = {
|
||||
complexity_reasoning: 'Simple task',
|
||||
complexity_score: 10,
|
||||
};
|
||||
vi.mocked(mockBaseLlmClient.generateJson).mockResolvedValue(
|
||||
mockApiResponse,
|
||||
);
|
||||
|
||||
const decision = await strategy.route(
|
||||
mockContext,
|
||||
mockConfig,
|
||||
mockBaseLlmClient,
|
||||
mockLocalLiteRtLmClient,
|
||||
);
|
||||
|
||||
expect(decision?.model).toBe(DEFAULT_GEMINI_FLASH_MODEL);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -167,21 +167,20 @@ export class NumericalClassifierStrategy implements RoutingStrategy {
|
||||
|
||||
const { threshold, groupLabel, modelAlias } =
|
||||
await this.getRoutingDecision(score, config);
|
||||
const [useGemini3_1, useGemini3_1FlashLite, useCustomToolModel] =
|
||||
await Promise.all([
|
||||
config.getGemini31Launched(),
|
||||
config.getGemini31FlashLiteLaunched(),
|
||||
config.getUseCustomToolModel(),
|
||||
]);
|
||||
const [useGemini3_1, useCustomToolModel] = await Promise.all([
|
||||
config.getGemini31Launched(),
|
||||
config.getUseCustomToolModel(),
|
||||
]);
|
||||
const useGemini3_5Flash = config.hasGemini35FlashGAAccess?.() ?? false;
|
||||
const selectedModel = normalizeModelId(
|
||||
resolveClassifierModel(
|
||||
normalizeModelId(model),
|
||||
modelAlias,
|
||||
useGemini3_1,
|
||||
useGemini3_1FlashLite,
|
||||
useCustomToolModel,
|
||||
config.getHasAccessToPreviewModel?.() ?? true,
|
||||
config,
|
||||
useGemini3_5Flash,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -38,10 +38,10 @@ export class OverrideStrategy implements RoutingStrategy {
|
||||
model: resolveModel(
|
||||
overrideModel,
|
||||
config.getGemini31LaunchedSync?.() ?? false,
|
||||
config.getGemini31FlashLiteLaunchedSync?.() ?? false,
|
||||
false,
|
||||
config.getHasAccessToPreviewModel?.() ?? true,
|
||||
config,
|
||||
config.hasGemini35FlashGAAccess?.() ?? false,
|
||||
),
|
||||
metadata: {
|
||||
source: this.name,
|
||||
|
||||
@@ -1019,6 +1019,79 @@ describe('ModelConfigService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Resolves a model ID to a concrete model ID based on the provided context.
|
||||
describe('resolveModelId', () => {
|
||||
it('should resolve based on useGemini3_5Flash condition', () => {
|
||||
const config: ModelConfigServiceConfig = {
|
||||
modelIdResolutions: {
|
||||
flash: {
|
||||
default: 'gemini-2.0-flash',
|
||||
contexts: [
|
||||
{
|
||||
condition: { useGemini3_5Flash: true },
|
||||
target: 'gemini-3.5-flash',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const service = new ModelConfigService(config);
|
||||
|
||||
expect(service.resolveModelId('flash', { useGemini3_5Flash: true })).toBe(
|
||||
'gemini-3.5-flash',
|
||||
);
|
||||
expect(
|
||||
service.resolveModelId('flash', { useGemini3_5Flash: false }),
|
||||
).toBe('gemini-2.0-flash');
|
||||
expect(service.resolveModelId('flash', {})).toBe('gemini-2.0-flash');
|
||||
});
|
||||
|
||||
it('should resolve based on complex conditions including useGemini3_5Flash', () => {
|
||||
const config: ModelConfigServiceConfig = {
|
||||
modelIdResolutions: {
|
||||
'gemini-flash': {
|
||||
default: 'gemini-3-flash-preview',
|
||||
contexts: [
|
||||
{
|
||||
condition: {
|
||||
useGemini3_5Flash: false,
|
||||
hasAccessToPreview: false,
|
||||
},
|
||||
target: 'gemini-2.5-flash',
|
||||
},
|
||||
{
|
||||
condition: { useGemini3_5Flash: true },
|
||||
target: 'gemini-3.5-flash',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const service = new ModelConfigService(config);
|
||||
|
||||
// Case 1: GA Access granted
|
||||
expect(
|
||||
service.resolveModelId('gemini-flash', { useGemini3_5Flash: true }),
|
||||
).toBe('gemini-3.5-flash');
|
||||
|
||||
// Case 2: GA Access denied, but has preview access
|
||||
expect(
|
||||
service.resolveModelId('gemini-flash', {
|
||||
useGemini3_5Flash: false,
|
||||
hasAccessToPreview: true,
|
||||
}),
|
||||
).toBe('gemini-3-flash-preview');
|
||||
|
||||
// Case 3: GA Access denied AND no preview access
|
||||
expect(
|
||||
service.resolveModelId('gemini-flash', {
|
||||
useGemini3_5Flash: false,
|
||||
hasAccessToPreview: false,
|
||||
}),
|
||||
).toBe('gemini-2.5-flash');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAvailableModelOptions', () => {
|
||||
it('should filter out Pro models when hasAccessToProModel is false', () => {
|
||||
const config: ModelConfigServiceConfig = {
|
||||
|
||||
@@ -9,8 +9,8 @@ import type { ModelPolicy } from '../availability/modelPolicy.js';
|
||||
import {
|
||||
getDisplayString,
|
||||
PREVIEW_GEMINI_3_1_MODEL,
|
||||
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
isProModel,
|
||||
getAutoModelDescription,
|
||||
} from '../config/models.js';
|
||||
|
||||
// The primary key for the ModelConfig is the model string. However, we also
|
||||
@@ -97,6 +97,7 @@ export interface ModelResolution {
|
||||
export interface ResolutionContext {
|
||||
useGemini3_1?: boolean;
|
||||
useGemini3_1FlashLite?: boolean;
|
||||
useGemini3_5Flash?: boolean;
|
||||
useCustomTools?: boolean;
|
||||
hasAccessToPreview?: boolean;
|
||||
hasAccessToProModel?: boolean;
|
||||
@@ -107,6 +108,7 @@ export interface ResolutionContext {
|
||||
export interface ResolutionCondition {
|
||||
useGemini3_1?: boolean;
|
||||
useGemini3_1FlashLite?: boolean;
|
||||
useGemini3_5Flash?: boolean;
|
||||
useCustomTools?: boolean;
|
||||
hasAccessToPreview?: boolean;
|
||||
/** Matches if the current model is in this list. */
|
||||
@@ -155,7 +157,7 @@ export class ModelConfigService {
|
||||
const definitions = this.config.modelDefinitions ?? {};
|
||||
const shouldShowPreviewModels = context.hasAccessToPreview ?? false;
|
||||
const useGemini31 = context.useGemini3_1 ?? false;
|
||||
const useGemini31FlashLite = context.useGemini3_1FlashLite ?? false;
|
||||
const useGemini3_5Flash = context.useGemini3_5Flash ?? false;
|
||||
|
||||
const mainOptions = Object.entries(definitions)
|
||||
.filter(([_, m]) => {
|
||||
@@ -164,18 +166,25 @@ export class ModelConfigService {
|
||||
if (m.tier !== 'auto') return false;
|
||||
return true;
|
||||
})
|
||||
.map(([id, m]) => ({
|
||||
modelId: id,
|
||||
name: m.displayName ?? getDisplayString(id),
|
||||
description:
|
||||
id === 'auto-gemini-3' && useGemini31
|
||||
? (m.dialogDescription ?? '').replace(
|
||||
'gemini-3-pro',
|
||||
'gemini-3.1-pro',
|
||||
)
|
||||
: (m.dialogDescription ?? ''),
|
||||
tier: m.tier ?? 'auto',
|
||||
}));
|
||||
.map(([id, m]) => {
|
||||
let description = m.dialogDescription ?? '';
|
||||
if (id === 'auto') {
|
||||
description = getAutoModelDescription(
|
||||
shouldShowPreviewModels,
|
||||
useGemini31,
|
||||
useGemini3_5Flash,
|
||||
);
|
||||
} else if (id === 'auto-gemini-3' && useGemini31) {
|
||||
description = description.replace('gemini-3-pro', 'gemini-3.1-pro');
|
||||
}
|
||||
|
||||
return {
|
||||
modelId: id,
|
||||
name: m.displayName ?? getDisplayString(id),
|
||||
description,
|
||||
tier: m.tier ?? 'auto',
|
||||
};
|
||||
});
|
||||
|
||||
const manualOptions = Object.entries(definitions)
|
||||
.filter(([id, m]) => {
|
||||
@@ -185,15 +194,12 @@ export class ModelConfigService {
|
||||
if (context.hasAccessToProModel === false && isProModel(id))
|
||||
return false;
|
||||
if (id === PREVIEW_GEMINI_3_1_MODEL && !useGemini31) return false;
|
||||
if (id === PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL && !useGemini31FlashLite)
|
||||
return false;
|
||||
return true;
|
||||
})
|
||||
.map(([id, m]) => {
|
||||
const resolvedId = this.resolveModelId(id, context);
|
||||
const titleId = this.resolveModelId(id, {
|
||||
useGemini3_1: useGemini31,
|
||||
useGemini3_1FlashLite: useGemini31FlashLite,
|
||||
});
|
||||
return {
|
||||
modelId: resolvedId,
|
||||
@@ -248,6 +254,8 @@ export class ModelConfigService {
|
||||
return value === context.useGemini3_1;
|
||||
case 'useGemini3_1FlashLite':
|
||||
return value === context.useGemini3_1FlashLite;
|
||||
case 'useGemini3_5Flash':
|
||||
return value === context.useGemini3_5Flash;
|
||||
case 'useCustomTools':
|
||||
return value === context.useCustomTools;
|
||||
case 'hasAccessToPreview':
|
||||
@@ -337,6 +345,10 @@ export class ModelConfigService {
|
||||
this.runtimeOverrides.push(override);
|
||||
}
|
||||
|
||||
clearRuntimeOverrides(): void {
|
||||
this.runtimeOverrides.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a model configuration by merging settings from aliases and applying overrides.
|
||||
*
|
||||
|
||||
@@ -61,6 +61,42 @@
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-3.1-pro-preview": {
|
||||
"model": "gemini-3.1-pro-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 1,
|
||||
"topP": 0.95,
|
||||
"thinkingConfig": {
|
||||
"includeThoughts": true,
|
||||
"thinkingLevel": "HIGH"
|
||||
},
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-3.1-pro-preview-customtools": {
|
||||
"model": "gemini-3.1-pro-preview-customtools",
|
||||
"generateContentConfig": {
|
||||
"temperature": 1,
|
||||
"topP": 0.95,
|
||||
"thinkingConfig": {
|
||||
"includeThoughts": true,
|
||||
"thinkingLevel": "HIGH"
|
||||
},
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-3.1-flash-lite-preview": {
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 1,
|
||||
"topP": 0.95,
|
||||
"thinkingConfig": {
|
||||
"includeThoughts": true,
|
||||
"thinkingLevel": "HIGH"
|
||||
},
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-2.5-pro": {
|
||||
"model": "gemini-2.5-pro",
|
||||
"generateContentConfig": {
|
||||
@@ -97,6 +133,30 @@
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-3.1-flash-lite": {
|
||||
"model": "gemini-3.1-flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 1,
|
||||
"topP": 0.95,
|
||||
"thinkingConfig": {
|
||||
"includeThoughts": true,
|
||||
"thinkingLevel": "HIGH"
|
||||
},
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash": {
|
||||
"model": "gemini-3.5-flash",
|
||||
"generateContentConfig": {
|
||||
"temperature": 1,
|
||||
"topP": 0.95,
|
||||
"thinkingConfig": {
|
||||
"includeThoughts": true,
|
||||
"thinkingLevel": "HIGH"
|
||||
},
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemma-4-31b-it": {
|
||||
"model": "gemma-4-31b-it",
|
||||
"generateContentConfig": {
|
||||
@@ -135,8 +195,15 @@
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash-base": {
|
||||
"model": "gemini-3.5-flash",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"classifier": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1,
|
||||
@@ -147,7 +214,7 @@
|
||||
}
|
||||
},
|
||||
"prompt-completion": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0.3,
|
||||
"topP": 1,
|
||||
@@ -158,7 +225,7 @@
|
||||
}
|
||||
},
|
||||
"fast-ack-helper": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0.2,
|
||||
"topP": 1,
|
||||
@@ -169,7 +236,7 @@
|
||||
}
|
||||
},
|
||||
"edit-corrector": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1,
|
||||
@@ -179,7 +246,7 @@
|
||||
}
|
||||
},
|
||||
"summarizer-default": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1,
|
||||
@@ -187,7 +254,7 @@
|
||||
}
|
||||
},
|
||||
"summarizer-shell": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1,
|
||||
@@ -273,7 +340,7 @@
|
||||
"generateContentConfig": {}
|
||||
},
|
||||
"chat-compression-3.1-flash-lite": {
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"model": "gemini-3.1-flash-lite",
|
||||
"generateContentConfig": {}
|
||||
},
|
||||
"chat-compression-2.5-pro": {
|
||||
|
||||
@@ -61,6 +61,42 @@
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-3.1-pro-preview": {
|
||||
"model": "gemini-3.1-pro-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 1,
|
||||
"topP": 0.95,
|
||||
"thinkingConfig": {
|
||||
"includeThoughts": true,
|
||||
"thinkingLevel": "HIGH"
|
||||
},
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-3.1-pro-preview-customtools": {
|
||||
"model": "gemini-3.1-pro-preview-customtools",
|
||||
"generateContentConfig": {
|
||||
"temperature": 1,
|
||||
"topP": 0.95,
|
||||
"thinkingConfig": {
|
||||
"includeThoughts": true,
|
||||
"thinkingLevel": "HIGH"
|
||||
},
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-3.1-flash-lite-preview": {
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"generateContentConfig": {
|
||||
"temperature": 1,
|
||||
"topP": 0.95,
|
||||
"thinkingConfig": {
|
||||
"includeThoughts": true,
|
||||
"thinkingLevel": "HIGH"
|
||||
},
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-2.5-pro": {
|
||||
"model": "gemini-2.5-pro",
|
||||
"generateContentConfig": {
|
||||
@@ -97,6 +133,30 @@
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-3.1-flash-lite": {
|
||||
"model": "gemini-3.1-flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 1,
|
||||
"topP": 0.95,
|
||||
"thinkingConfig": {
|
||||
"includeThoughts": true,
|
||||
"thinkingLevel": "HIGH"
|
||||
},
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash": {
|
||||
"model": "gemini-3.5-flash",
|
||||
"generateContentConfig": {
|
||||
"temperature": 1,
|
||||
"topP": 0.95,
|
||||
"thinkingConfig": {
|
||||
"includeThoughts": true,
|
||||
"thinkingLevel": "HIGH"
|
||||
},
|
||||
"topK": 64
|
||||
}
|
||||
},
|
||||
"gemma-4-31b-it": {
|
||||
"model": "gemma-4-31b-it",
|
||||
"generateContentConfig": {
|
||||
@@ -135,8 +195,15 @@
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"gemini-3.5-flash-base": {
|
||||
"model": "gemini-3.5-flash",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1
|
||||
}
|
||||
},
|
||||
"classifier": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1,
|
||||
@@ -147,7 +214,7 @@
|
||||
}
|
||||
},
|
||||
"prompt-completion": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0.3,
|
||||
"topP": 1,
|
||||
@@ -158,7 +225,7 @@
|
||||
}
|
||||
},
|
||||
"fast-ack-helper": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0.2,
|
||||
"topP": 1,
|
||||
@@ -169,7 +236,7 @@
|
||||
}
|
||||
},
|
||||
"edit-corrector": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1,
|
||||
@@ -179,7 +246,7 @@
|
||||
}
|
||||
},
|
||||
"summarizer-default": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1,
|
||||
@@ -187,7 +254,7 @@
|
||||
}
|
||||
},
|
||||
"summarizer-shell": {
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"model": "flash-lite",
|
||||
"generateContentConfig": {
|
||||
"temperature": 0,
|
||||
"topP": 1,
|
||||
@@ -273,7 +340,7 @@
|
||||
"generateContentConfig": {}
|
||||
},
|
||||
"chat-compression-3.1-flash-lite": {
|
||||
"model": "gemini-3.1-flash-lite-preview",
|
||||
"model": "gemini-3.1-flash-lite",
|
||||
"generateContentConfig": {}
|
||||
},
|
||||
"chat-compression-2.5-pro": {
|
||||
|
||||
@@ -968,18 +968,24 @@ export class ShellToolInvocation extends BaseToolInvocation<
|
||||
if (timeoutTimer) clearTimeout(timeoutTimer);
|
||||
signal.removeEventListener('abort', onAbort);
|
||||
timeoutController.signal.removeEventListener('abort', onAbort);
|
||||
if (tempFilePath) {
|
||||
try {
|
||||
await fsPromises.unlink(tempFilePath);
|
||||
} catch {
|
||||
// Ignore errors during unlink
|
||||
|
||||
// Only clean up if NOT running in background.
|
||||
// Background processes need the temp directory and PID file to remain
|
||||
// available until they exit.
|
||||
if (!this.params.is_background) {
|
||||
if (tempFilePath) {
|
||||
try {
|
||||
await fsPromises.unlink(tempFilePath);
|
||||
} catch {
|
||||
// Ignore errors during unlink
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tempDir) {
|
||||
try {
|
||||
await fsPromises.rm(tempDir, { recursive: true, force: true });
|
||||
} catch {
|
||||
// Ignore errors during rm
|
||||
if (tempDir) {
|
||||
try {
|
||||
await fsPromises.rm(tempDir, { recursive: true, force: true });
|
||||
} catch {
|
||||
// Ignore errors during rm
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,5 +55,9 @@ vi.mock('./src/config/storage.js', async (importOriginal) => {
|
||||
(actual.Storage.prototype as any).getProjectIdentifier = vi.fn(
|
||||
() => 'project-slug',
|
||||
);
|
||||
actual.Storage.getUserPoliciesDir = vi.fn(() => '/tmp/mock-user-policies');
|
||||
actual.Storage.getSystemPoliciesDir = vi.fn(
|
||||
() => '/tmp/mock-system-policies',
|
||||
);
|
||||
return actual;
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-devtools",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-sdk",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"description": "Gemini CLI SDK",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-test-utils",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"private": true,
|
||||
"main": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "gemini-cli-vscode-ide-companion",
|
||||
"displayName": "Gemini CLI Companion",
|
||||
"description": "Enable Gemini CLI with direct access to your IDE workspace.",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.42.0-nightly.20260512.gc987b9939",
|
||||
"publisher": "google",
|
||||
"icon": "assets/icon.png",
|
||||
"repository": {
|
||||
|
||||
+276
-124
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user