mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 12:54:07 -07:00
Add support for dynamic model Resolution to ModelConfigService (#22578)
This commit is contained in:
@@ -688,7 +688,7 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
"tier": "pro",
|
"tier": "pro",
|
||||||
"family": "gemini-3",
|
"family": "gemini-3",
|
||||||
"isPreview": true,
|
"isPreview": true,
|
||||||
"dialogLocation": "manual",
|
"isVisible": true,
|
||||||
"features": {
|
"features": {
|
||||||
"thinking": true,
|
"thinking": true,
|
||||||
"multimodalToolUse": true
|
"multimodalToolUse": true
|
||||||
@@ -698,6 +698,7 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
"tier": "pro",
|
"tier": "pro",
|
||||||
"family": "gemini-3",
|
"family": "gemini-3",
|
||||||
"isPreview": true,
|
"isPreview": true,
|
||||||
|
"isVisible": false,
|
||||||
"features": {
|
"features": {
|
||||||
"thinking": true,
|
"thinking": true,
|
||||||
"multimodalToolUse": true
|
"multimodalToolUse": true
|
||||||
@@ -707,7 +708,7 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
"tier": "pro",
|
"tier": "pro",
|
||||||
"family": "gemini-3",
|
"family": "gemini-3",
|
||||||
"isPreview": true,
|
"isPreview": true,
|
||||||
"dialogLocation": "manual",
|
"isVisible": true,
|
||||||
"features": {
|
"features": {
|
||||||
"thinking": true,
|
"thinking": true,
|
||||||
"multimodalToolUse": true
|
"multimodalToolUse": true
|
||||||
@@ -717,7 +718,7 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
"tier": "flash",
|
"tier": "flash",
|
||||||
"family": "gemini-3",
|
"family": "gemini-3",
|
||||||
"isPreview": true,
|
"isPreview": true,
|
||||||
"dialogLocation": "manual",
|
"isVisible": true,
|
||||||
"features": {
|
"features": {
|
||||||
"thinking": false,
|
"thinking": false,
|
||||||
"multimodalToolUse": true
|
"multimodalToolUse": true
|
||||||
@@ -727,7 +728,7 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
"tier": "pro",
|
"tier": "pro",
|
||||||
"family": "gemini-2.5",
|
"family": "gemini-2.5",
|
||||||
"isPreview": false,
|
"isPreview": false,
|
||||||
"dialogLocation": "manual",
|
"isVisible": true,
|
||||||
"features": {
|
"features": {
|
||||||
"thinking": false,
|
"thinking": false,
|
||||||
"multimodalToolUse": false
|
"multimodalToolUse": false
|
||||||
@@ -737,7 +738,7 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
"tier": "flash",
|
"tier": "flash",
|
||||||
"family": "gemini-2.5",
|
"family": "gemini-2.5",
|
||||||
"isPreview": false,
|
"isPreview": false,
|
||||||
"dialogLocation": "manual",
|
"isVisible": true,
|
||||||
"features": {
|
"features": {
|
||||||
"thinking": false,
|
"thinking": false,
|
||||||
"multimodalToolUse": false
|
"multimodalToolUse": false
|
||||||
@@ -747,7 +748,7 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
"tier": "flash-lite",
|
"tier": "flash-lite",
|
||||||
"family": "gemini-2.5",
|
"family": "gemini-2.5",
|
||||||
"isPreview": false,
|
"isPreview": false,
|
||||||
"dialogLocation": "manual",
|
"isVisible": true,
|
||||||
"features": {
|
"features": {
|
||||||
"thinking": false,
|
"thinking": false,
|
||||||
"multimodalToolUse": false
|
"multimodalToolUse": false
|
||||||
@@ -756,6 +757,7 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
"auto": {
|
"auto": {
|
||||||
"tier": "auto",
|
"tier": "auto",
|
||||||
"isPreview": true,
|
"isPreview": true,
|
||||||
|
"isVisible": false,
|
||||||
"features": {
|
"features": {
|
||||||
"thinking": true,
|
"thinking": true,
|
||||||
"multimodalToolUse": false
|
"multimodalToolUse": false
|
||||||
@@ -764,6 +766,7 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
"pro": {
|
"pro": {
|
||||||
"tier": "pro",
|
"tier": "pro",
|
||||||
"isPreview": false,
|
"isPreview": false,
|
||||||
|
"isVisible": false,
|
||||||
"features": {
|
"features": {
|
||||||
"thinking": true,
|
"thinking": true,
|
||||||
"multimodalToolUse": false
|
"multimodalToolUse": false
|
||||||
@@ -772,6 +775,7 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
"flash": {
|
"flash": {
|
||||||
"tier": "flash",
|
"tier": "flash",
|
||||||
"isPreview": false,
|
"isPreview": false,
|
||||||
|
"isVisible": false,
|
||||||
"features": {
|
"features": {
|
||||||
"thinking": false,
|
"thinking": false,
|
||||||
"multimodalToolUse": false
|
"multimodalToolUse": false
|
||||||
@@ -780,6 +784,7 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
"flash-lite": {
|
"flash-lite": {
|
||||||
"tier": "flash-lite",
|
"tier": "flash-lite",
|
||||||
"isPreview": false,
|
"isPreview": false,
|
||||||
|
"isVisible": false,
|
||||||
"features": {
|
"features": {
|
||||||
"thinking": false,
|
"thinking": false,
|
||||||
"multimodalToolUse": false
|
"multimodalToolUse": false
|
||||||
@@ -789,7 +794,7 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
"displayName": "Auto (Gemini 3)",
|
"displayName": "Auto (Gemini 3)",
|
||||||
"tier": "auto",
|
"tier": "auto",
|
||||||
"isPreview": true,
|
"isPreview": true,
|
||||||
"dialogLocation": "main",
|
"isVisible": true,
|
||||||
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-3.1-pro, gemini-3-flash",
|
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-3.1-pro, gemini-3-flash",
|
||||||
"features": {
|
"features": {
|
||||||
"thinking": true,
|
"thinking": true,
|
||||||
@@ -800,7 +805,7 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
"displayName": "Auto (Gemini 2.5)",
|
"displayName": "Auto (Gemini 2.5)",
|
||||||
"tier": "auto",
|
"tier": "auto",
|
||||||
"isPreview": false,
|
"isPreview": false,
|
||||||
"dialogLocation": "main",
|
"isVisible": true,
|
||||||
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash",
|
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash",
|
||||||
"features": {
|
"features": {
|
||||||
"thinking": false,
|
"thinking": false,
|
||||||
@@ -812,6 +817,184 @@ their corresponding top-level category object in your `settings.json` file.
|
|||||||
|
|
||||||
- **Requires restart:** Yes
|
- **Requires restart:** Yes
|
||||||
|
|
||||||
|
- **`modelConfigs.modelIdResolutions`** (object):
|
||||||
|
- **Description:** Rules for resolving requested model names to concrete model
|
||||||
|
IDs based on context.
|
||||||
|
- **Default:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"gemini-3-pro-preview": {
|
||||||
|
"default": "gemini-3-pro-preview",
|
||||||
|
"contexts": [
|
||||||
|
{
|
||||||
|
"condition": {
|
||||||
|
"hasAccessToPreview": false
|
||||||
|
},
|
||||||
|
"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-3": {
|
||||||
|
"default": "gemini-3-pro-preview",
|
||||||
|
"contexts": [
|
||||||
|
{
|
||||||
|
"condition": {
|
||||||
|
"hasAccessToPreview": false
|
||||||
|
},
|
||||||
|
"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": {
|
||||||
|
"default": "gemini-3-pro-preview",
|
||||||
|
"contexts": [
|
||||||
|
{
|
||||||
|
"condition": {
|
||||||
|
"hasAccessToPreview": false
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pro": {
|
||||||
|
"default": "gemini-3-pro-preview",
|
||||||
|
"contexts": [
|
||||||
|
{
|
||||||
|
"condition": {
|
||||||
|
"hasAccessToPreview": false
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"flash": {
|
||||||
|
"default": "gemini-3-flash-preview",
|
||||||
|
"contexts": [
|
||||||
|
{
|
||||||
|
"condition": {
|
||||||
|
"hasAccessToPreview": false
|
||||||
|
},
|
||||||
|
"target": "gemini-2.5-flash"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flash-lite": {
|
||||||
|
"default": "gemini-2.5-flash-lite"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Requires restart:** Yes
|
||||||
|
|
||||||
|
- **`modelConfigs.classifierIdResolutions`** (object):
|
||||||
|
- **Description:** Rules for resolving classifier tiers (flash, pro) to
|
||||||
|
concrete model IDs.
|
||||||
|
- **Default:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"flash": {
|
||||||
|
"default": "gemini-3-flash-preview",
|
||||||
|
"contexts": [
|
||||||
|
{
|
||||||
|
"condition": {
|
||||||
|
"requestedModels": ["auto-gemini-2.5", "gemini-2.5-pro"]
|
||||||
|
},
|
||||||
|
"target": "gemini-2.5-flash"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"condition": {
|
||||||
|
"requestedModels": ["auto-gemini-3", "gemini-3-pro-preview"]
|
||||||
|
},
|
||||||
|
"target": "gemini-3-flash-preview"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pro": {
|
||||||
|
"default": "gemini-3-pro-preview",
|
||||||
|
"contexts": [
|
||||||
|
{
|
||||||
|
"condition": {
|
||||||
|
"requestedModels": ["auto-gemini-2.5", "gemini-2.5-pro"]
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Requires restart:** Yes
|
||||||
|
|
||||||
#### `agents`
|
#### `agents`
|
||||||
|
|
||||||
- **`agents.overrides`** (object):
|
- **`agents.overrides`** (object):
|
||||||
|
|||||||
@@ -1053,6 +1053,34 @@ const SETTINGS_SCHEMA = {
|
|||||||
ref: 'ModelDefinition',
|
ref: 'ModelDefinition',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
modelIdResolutions: {
|
||||||
|
type: 'object',
|
||||||
|
label: 'Model ID Resolutions',
|
||||||
|
category: 'Model',
|
||||||
|
requiresRestart: true,
|
||||||
|
default: DEFAULT_MODEL_CONFIGS.modelIdResolutions,
|
||||||
|
description:
|
||||||
|
'Rules for resolving requested model names to concrete model IDs based on context.',
|
||||||
|
showInDialog: false,
|
||||||
|
additionalProperties: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'ModelResolution',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
classifierIdResolutions: {
|
||||||
|
type: 'object',
|
||||||
|
label: 'Classifier ID Resolutions',
|
||||||
|
category: 'Model',
|
||||||
|
requiresRestart: true,
|
||||||
|
default: DEFAULT_MODEL_CONFIGS.classifierIdResolutions,
|
||||||
|
description:
|
||||||
|
'Rules for resolving classifier tiers (flash, pro) to concrete model IDs.',
|
||||||
|
showInDialog: false,
|
||||||
|
additionalProperties: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'ModelResolution',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -2800,7 +2828,7 @@ export const SETTINGS_SCHEMA_DEFINITIONS: Record<
|
|||||||
tier: { enum: ['pro', 'flash', 'flash-lite', 'custom', 'auto'] },
|
tier: { enum: ['pro', 'flash', 'flash-lite', 'custom', 'auto'] },
|
||||||
family: { type: 'string' },
|
family: { type: 'string' },
|
||||||
isPreview: { type: 'boolean' },
|
isPreview: { type: 'boolean' },
|
||||||
dialogLocation: { enum: ['main', 'manual'] },
|
isVisible: { type: 'boolean' },
|
||||||
dialogDescription: { type: 'string' },
|
dialogDescription: { type: 'string' },
|
||||||
features: {
|
features: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -2811,6 +2839,34 @@ export const SETTINGS_SCHEMA_DEFINITIONS: Record<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ModelResolution: {
|
||||||
|
type: 'object',
|
||||||
|
description: 'Model resolution rule.',
|
||||||
|
properties: {
|
||||||
|
default: { type: 'string' },
|
||||||
|
contexts: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
condition: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
useGemini3_1: { type: 'boolean' },
|
||||||
|
useCustomTools: { type: 'boolean' },
|
||||||
|
hasAccessToPreview: { type: 'boolean' },
|
||||||
|
requestedModels: {
|
||||||
|
type: 'array',
|
||||||
|
items: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
target: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getSettingsSchema(): SettingsSchemaType {
|
export function getSettingsSchema(): SettingsSchemaType {
|
||||||
|
|||||||
@@ -981,6 +981,14 @@ export class Config implements McpContext, AgentLoopContext {
|
|||||||
...DEFAULT_MODEL_CONFIGS.modelDefinitions,
|
...DEFAULT_MODEL_CONFIGS.modelDefinitions,
|
||||||
...modelConfigServiceConfig.modelDefinitions,
|
...modelConfigServiceConfig.modelDefinitions,
|
||||||
};
|
};
|
||||||
|
const mergedModelIdResolutions = {
|
||||||
|
...DEFAULT_MODEL_CONFIGS.modelIdResolutions,
|
||||||
|
...modelConfigServiceConfig.modelIdResolutions,
|
||||||
|
};
|
||||||
|
const mergedClassifierIdResolutions = {
|
||||||
|
...DEFAULT_MODEL_CONFIGS.classifierIdResolutions,
|
||||||
|
...modelConfigServiceConfig.classifierIdResolutions,
|
||||||
|
};
|
||||||
|
|
||||||
modelConfigServiceConfig = {
|
modelConfigServiceConfig = {
|
||||||
// Preserve other user settings like customAliases
|
// Preserve other user settings like customAliases
|
||||||
@@ -992,6 +1000,8 @@ export class Config implements McpContext, AgentLoopContext {
|
|||||||
modelConfigServiceConfig.overrides ?? DEFAULT_MODEL_CONFIGS.overrides,
|
modelConfigServiceConfig.overrides ?? DEFAULT_MODEL_CONFIGS.overrides,
|
||||||
// Use the merged model definitions
|
// Use the merged model definitions
|
||||||
modelDefinitions: mergedModelDefinitions,
|
modelDefinitions: mergedModelDefinitions,
|
||||||
|
modelIdResolutions: mergedModelIdResolutions,
|
||||||
|
classifierIdResolutions: mergedClassifierIdResolutions,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -255,76 +255,81 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
|||||||
tier: 'pro',
|
tier: 'pro',
|
||||||
family: 'gemini-3',
|
family: 'gemini-3',
|
||||||
isPreview: true,
|
isPreview: true,
|
||||||
dialogLocation: 'manual',
|
isVisible: true,
|
||||||
features: { thinking: true, multimodalToolUse: true },
|
features: { thinking: true, multimodalToolUse: true },
|
||||||
},
|
},
|
||||||
'gemini-3.1-pro-preview-customtools': {
|
'gemini-3.1-pro-preview-customtools': {
|
||||||
tier: 'pro',
|
tier: 'pro',
|
||||||
family: 'gemini-3',
|
family: 'gemini-3',
|
||||||
isPreview: true,
|
isPreview: true,
|
||||||
|
isVisible: false,
|
||||||
features: { thinking: true, multimodalToolUse: true },
|
features: { thinking: true, multimodalToolUse: true },
|
||||||
},
|
},
|
||||||
'gemini-3-pro-preview': {
|
'gemini-3-pro-preview': {
|
||||||
tier: 'pro',
|
tier: 'pro',
|
||||||
family: 'gemini-3',
|
family: 'gemini-3',
|
||||||
isPreview: true,
|
isPreview: true,
|
||||||
dialogLocation: 'manual',
|
isVisible: true,
|
||||||
features: { thinking: true, multimodalToolUse: true },
|
features: { thinking: true, multimodalToolUse: true },
|
||||||
},
|
},
|
||||||
'gemini-3-flash-preview': {
|
'gemini-3-flash-preview': {
|
||||||
tier: 'flash',
|
tier: 'flash',
|
||||||
family: 'gemini-3',
|
family: 'gemini-3',
|
||||||
isPreview: true,
|
isPreview: true,
|
||||||
dialogLocation: 'manual',
|
isVisible: true,
|
||||||
features: { thinking: false, multimodalToolUse: true },
|
features: { thinking: false, multimodalToolUse: true },
|
||||||
},
|
},
|
||||||
'gemini-2.5-pro': {
|
'gemini-2.5-pro': {
|
||||||
tier: 'pro',
|
tier: 'pro',
|
||||||
family: 'gemini-2.5',
|
family: 'gemini-2.5',
|
||||||
isPreview: false,
|
isPreview: false,
|
||||||
dialogLocation: 'manual',
|
isVisible: true,
|
||||||
features: { thinking: false, multimodalToolUse: false },
|
features: { thinking: false, multimodalToolUse: false },
|
||||||
},
|
},
|
||||||
'gemini-2.5-flash': {
|
'gemini-2.5-flash': {
|
||||||
tier: 'flash',
|
tier: 'flash',
|
||||||
family: 'gemini-2.5',
|
family: 'gemini-2.5',
|
||||||
isPreview: false,
|
isPreview: false,
|
||||||
dialogLocation: 'manual',
|
isVisible: true,
|
||||||
features: { thinking: false, multimodalToolUse: false },
|
features: { thinking: false, multimodalToolUse: false },
|
||||||
},
|
},
|
||||||
'gemini-2.5-flash-lite': {
|
'gemini-2.5-flash-lite': {
|
||||||
tier: 'flash-lite',
|
tier: 'flash-lite',
|
||||||
family: 'gemini-2.5',
|
family: 'gemini-2.5',
|
||||||
isPreview: false,
|
isPreview: false,
|
||||||
dialogLocation: 'manual',
|
isVisible: true,
|
||||||
features: { thinking: false, multimodalToolUse: false },
|
features: { thinking: false, multimodalToolUse: false },
|
||||||
},
|
},
|
||||||
// Aliases
|
// Aliases
|
||||||
auto: {
|
auto: {
|
||||||
tier: 'auto',
|
tier: 'auto',
|
||||||
isPreview: true,
|
isPreview: true,
|
||||||
|
isVisible: false,
|
||||||
features: { thinking: true, multimodalToolUse: false },
|
features: { thinking: true, multimodalToolUse: false },
|
||||||
},
|
},
|
||||||
pro: {
|
pro: {
|
||||||
tier: 'pro',
|
tier: 'pro',
|
||||||
isPreview: false,
|
isPreview: false,
|
||||||
|
isVisible: false,
|
||||||
features: { thinking: true, multimodalToolUse: false },
|
features: { thinking: true, multimodalToolUse: false },
|
||||||
},
|
},
|
||||||
flash: {
|
flash: {
|
||||||
tier: 'flash',
|
tier: 'flash',
|
||||||
isPreview: false,
|
isPreview: false,
|
||||||
|
isVisible: false,
|
||||||
features: { thinking: false, multimodalToolUse: false },
|
features: { thinking: false, multimodalToolUse: false },
|
||||||
},
|
},
|
||||||
'flash-lite': {
|
'flash-lite': {
|
||||||
tier: 'flash-lite',
|
tier: 'flash-lite',
|
||||||
isPreview: false,
|
isPreview: false,
|
||||||
|
isVisible: false,
|
||||||
features: { thinking: false, multimodalToolUse: false },
|
features: { thinking: false, multimodalToolUse: false },
|
||||||
},
|
},
|
||||||
'auto-gemini-3': {
|
'auto-gemini-3': {
|
||||||
displayName: 'Auto (Gemini 3)',
|
displayName: 'Auto (Gemini 3)',
|
||||||
tier: 'auto',
|
tier: 'auto',
|
||||||
isPreview: true,
|
isPreview: true,
|
||||||
dialogLocation: 'main',
|
isVisible: true,
|
||||||
dialogDescription:
|
dialogDescription:
|
||||||
'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.1-pro, gemini-3-flash',
|
||||||
features: { thinking: true, multimodalToolUse: false },
|
features: { thinking: true, multimodalToolUse: false },
|
||||||
@@ -333,10 +338,117 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
|
|||||||
displayName: 'Auto (Gemini 2.5)',
|
displayName: 'Auto (Gemini 2.5)',
|
||||||
tier: 'auto',
|
tier: 'auto',
|
||||||
isPreview: false,
|
isPreview: false,
|
||||||
dialogLocation: 'main',
|
isVisible: true,
|
||||||
dialogDescription:
|
dialogDescription:
|
||||||
'Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash',
|
'Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash',
|
||||||
features: { thinking: false, multimodalToolUse: false },
|
features: { thinking: false, multimodalToolUse: false },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
modelIdResolutions: {
|
||||||
|
'gemini-3-pro-preview': {
|
||||||
|
default: 'gemini-3-pro-preview',
|
||||||
|
contexts: [
|
||||||
|
{ condition: { hasAccessToPreview: false }, 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-3': {
|
||||||
|
default: 'gemini-3-pro-preview',
|
||||||
|
contexts: [
|
||||||
|
{ condition: { hasAccessToPreview: false }, 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: {
|
||||||
|
default: 'gemini-3-pro-preview',
|
||||||
|
contexts: [
|
||||||
|
{ condition: { hasAccessToPreview: false }, 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',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
pro: {
|
||||||
|
default: 'gemini-3-pro-preview',
|
||||||
|
contexts: [
|
||||||
|
{ condition: { hasAccessToPreview: false }, 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',
|
||||||
|
},
|
||||||
|
flash: {
|
||||||
|
default: 'gemini-3-flash-preview',
|
||||||
|
contexts: [
|
||||||
|
{
|
||||||
|
condition: { hasAccessToPreview: false },
|
||||||
|
target: 'gemini-2.5-flash',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'flash-lite': {
|
||||||
|
default: 'gemini-2.5-flash-lite',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
classifierIdResolutions: {
|
||||||
|
flash: {
|
||||||
|
default: 'gemini-3-flash-preview',
|
||||||
|
contexts: [
|
||||||
|
{
|
||||||
|
condition: { requestedModels: ['auto-gemini-2.5', 'gemini-2.5-pro'] },
|
||||||
|
target: 'gemini-2.5-flash',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
condition: {
|
||||||
|
requestedModels: ['auto-gemini-3', 'gemini-3-pro-preview'],
|
||||||
|
},
|
||||||
|
target: 'gemini-3-flash-preview',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
pro: {
|
||||||
|
default: 'gemini-3-pro-preview',
|
||||||
|
contexts: [
|
||||||
|
{
|
||||||
|
condition: { requestedModels: ['auto-gemini-2.5', 'gemini-2.5-pro'] },
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -60,6 +60,90 @@ describe('Dynamic Configuration Parity', () => {
|
|||||||
'custom-model',
|
'custom-model',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const flagCombos = [
|
||||||
|
{ useGemini3_1: false, useCustomToolModel: false },
|
||||||
|
{ useGemini3_1: true, useCustomToolModel: false },
|
||||||
|
{ useGemini3_1: true, useCustomToolModel: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
it('resolveModel should match legacy behavior when dynamicModelConfiguration flag enabled.', () => {
|
||||||
|
for (const model of modelsToTest) {
|
||||||
|
for (const flags of flagCombos) {
|
||||||
|
for (const hasAccess of [true, false]) {
|
||||||
|
const mockLegacyConfig = {
|
||||||
|
...legacyConfig,
|
||||||
|
getHasAccessToPreviewModel: () => hasAccess,
|
||||||
|
} as unknown as Config;
|
||||||
|
const mockDynamicConfig = {
|
||||||
|
...dynamicConfig,
|
||||||
|
getHasAccessToPreviewModel: () => hasAccess,
|
||||||
|
} as unknown as Config;
|
||||||
|
|
||||||
|
const legacy = resolveModel(
|
||||||
|
model,
|
||||||
|
flags.useGemini3_1,
|
||||||
|
flags.useCustomToolModel,
|
||||||
|
hasAccess,
|
||||||
|
mockLegacyConfig,
|
||||||
|
);
|
||||||
|
const dynamic = resolveModel(
|
||||||
|
model,
|
||||||
|
flags.useGemini3_1,
|
||||||
|
flags.useCustomToolModel,
|
||||||
|
hasAccess,
|
||||||
|
mockDynamicConfig,
|
||||||
|
);
|
||||||
|
expect(dynamic).toBe(legacy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resolveClassifierModel should match legacy behavior.', () => {
|
||||||
|
const classifierTiers = [GEMINI_MODEL_ALIAS_PRO, GEMINI_MODEL_ALIAS_FLASH];
|
||||||
|
const anchorModels = [
|
||||||
|
PREVIEW_GEMINI_MODEL_AUTO,
|
||||||
|
DEFAULT_GEMINI_MODEL_AUTO,
|
||||||
|
PREVIEW_GEMINI_MODEL,
|
||||||
|
DEFAULT_GEMINI_MODEL,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const hasAccess of [true, false]) {
|
||||||
|
const mockLegacyConfig = {
|
||||||
|
...legacyConfig,
|
||||||
|
getHasAccessToPreviewModel: () => hasAccess,
|
||||||
|
} as unknown as Config;
|
||||||
|
const mockDynamicConfig = {
|
||||||
|
...dynamicConfig,
|
||||||
|
getHasAccessToPreviewModel: () => hasAccess,
|
||||||
|
} as unknown as Config;
|
||||||
|
|
||||||
|
for (const tier of classifierTiers) {
|
||||||
|
for (const anchor of anchorModels) {
|
||||||
|
for (const flags of flagCombos) {
|
||||||
|
const legacy = resolveClassifierModel(
|
||||||
|
anchor,
|
||||||
|
tier,
|
||||||
|
flags.useGemini3_1,
|
||||||
|
flags.useCustomToolModel,
|
||||||
|
hasAccess,
|
||||||
|
mockLegacyConfig,
|
||||||
|
);
|
||||||
|
const dynamic = resolveClassifierModel(
|
||||||
|
anchor,
|
||||||
|
tier,
|
||||||
|
flags.useGemini3_1,
|
||||||
|
flags.useCustomToolModel,
|
||||||
|
hasAccess,
|
||||||
|
mockDynamicConfig,
|
||||||
|
);
|
||||||
|
expect(dynamic).toBe(legacy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('getDisplayString should match legacy behavior', () => {
|
it('getDisplayString should match legacy behavior', () => {
|
||||||
for (const model of modelsToTest) {
|
for (const model of modelsToTest) {
|
||||||
const legacy = getDisplayString(model, legacyConfig);
|
const legacy = getDisplayString(model, legacyConfig);
|
||||||
|
|||||||
@@ -4,6 +4,13 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export interface ModelResolutionContext {
|
||||||
|
useGemini3_1?: boolean;
|
||||||
|
useCustomTools?: boolean;
|
||||||
|
hasAccessToPreview?: boolean;
|
||||||
|
requestedModel?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for the ModelConfigService to break circular dependencies.
|
* Interface for the ModelConfigService to break circular dependencies.
|
||||||
*/
|
*/
|
||||||
@@ -20,6 +27,17 @@ export interface IModelConfigService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
|
resolveModelId(
|
||||||
|
requestedModel: string,
|
||||||
|
context?: ModelResolutionContext,
|
||||||
|
): string;
|
||||||
|
|
||||||
|
resolveClassifierModelId(
|
||||||
|
tier: string,
|
||||||
|
requestedModel: string,
|
||||||
|
context?: ModelResolutionContext,
|
||||||
|
): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,7 +99,16 @@ export function resolveModel(
|
|||||||
useGemini3_1: boolean = false,
|
useGemini3_1: boolean = false,
|
||||||
useCustomToolModel: boolean = false,
|
useCustomToolModel: boolean = false,
|
||||||
hasAccessToPreview: boolean = true,
|
hasAccessToPreview: boolean = true,
|
||||||
|
config?: ModelCapabilityContext,
|
||||||
): string {
|
): string {
|
||||||
|
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
|
||||||
|
return config.modelConfigService.resolveModelId(requestedModel, {
|
||||||
|
useGemini3_1,
|
||||||
|
useCustomTools: useCustomToolModel,
|
||||||
|
hasAccessToPreview,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let resolved: string;
|
let resolved: string;
|
||||||
switch (requestedModel) {
|
switch (requestedModel) {
|
||||||
case PREVIEW_GEMINI_MODEL:
|
case PREVIEW_GEMINI_MODEL:
|
||||||
@@ -144,6 +171,9 @@ export function resolveModel(
|
|||||||
*
|
*
|
||||||
* @param requestedModel The current requested model (e.g. auto-gemini-2.5).
|
* @param requestedModel The current requested model (e.g. auto-gemini-2.5).
|
||||||
* @param modelAlias The alias selected by the classifier ('flash' or 'pro').
|
* @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.
|
||||||
|
* @param config Optional config object for dynamic model configuration.
|
||||||
* @returns The resolved concrete model name.
|
* @returns The resolved concrete model name.
|
||||||
*/
|
*/
|
||||||
export function resolveClassifierModel(
|
export function resolveClassifierModel(
|
||||||
@@ -151,7 +181,21 @@ export function resolveClassifierModel(
|
|||||||
modelAlias: string,
|
modelAlias: string,
|
||||||
useGemini3_1: boolean = false,
|
useGemini3_1: boolean = false,
|
||||||
useCustomToolModel: boolean = false,
|
useCustomToolModel: boolean = false,
|
||||||
|
hasAccessToPreview: boolean = true,
|
||||||
|
config?: ModelCapabilityContext,
|
||||||
): string {
|
): string {
|
||||||
|
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
|
||||||
|
return config.modelConfigService.resolveClassifierModelId(
|
||||||
|
modelAlias,
|
||||||
|
requestedModel,
|
||||||
|
{
|
||||||
|
useGemini3_1,
|
||||||
|
useCustomTools: useCustomToolModel,
|
||||||
|
hasAccessToPreview,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (modelAlias === GEMINI_MODEL_ALIAS_FLASH) {
|
if (modelAlias === GEMINI_MODEL_ALIAS_FLASH) {
|
||||||
if (
|
if (
|
||||||
requestedModel === DEFAULT_GEMINI_MODEL_AUTO ||
|
requestedModel === DEFAULT_GEMINI_MODEL_AUTO ||
|
||||||
@@ -169,6 +213,7 @@ export function resolveClassifierModel(
|
|||||||
}
|
}
|
||||||
return resolveModel(requestedModel, useGemini3_1, useCustomToolModel);
|
return resolveModel(requestedModel, useGemini3_1, useCustomToolModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDisplayString(
|
export function getDisplayString(
|
||||||
model: string,
|
model: string,
|
||||||
config?: ModelCapabilityContext,
|
config?: ModelCapabilityContext,
|
||||||
@@ -289,7 +334,7 @@ export function isCustomModel(
|
|||||||
config?: ModelCapabilityContext,
|
config?: ModelCapabilityContext,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
|
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
|
||||||
const resolved = resolveModel(model);
|
const resolved = resolveModel(model, false, false, true, config);
|
||||||
return (
|
return (
|
||||||
config.modelConfigService.getModelDefinition(resolved)?.tier ===
|
config.modelConfigService.getModelDefinition(resolved)?.tier ===
|
||||||
'custom' || !resolved.startsWith('gemini-')
|
'custom' || !resolved.startsWith('gemini-')
|
||||||
|
|||||||
@@ -569,6 +569,9 @@ export class GeminiClient {
|
|||||||
return resolveModel(
|
return resolveModel(
|
||||||
this.config.getActiveModel(),
|
this.config.getActiveModel(),
|
||||||
this.config.getGemini31LaunchedSync?.() ?? false,
|
this.config.getGemini31LaunchedSync?.() ?? false,
|
||||||
|
false,
|
||||||
|
this.config.getHasAccessToPreviewModel?.() ?? true,
|
||||||
|
this.config,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -171,6 +171,9 @@ export async function createContentGenerator(
|
|||||||
config.authType === AuthType.USE_GEMINI ||
|
config.authType === AuthType.USE_GEMINI ||
|
||||||
config.authType === AuthType.USE_VERTEX_AI ||
|
config.authType === AuthType.USE_VERTEX_AI ||
|
||||||
((await gcConfig.getGemini31Launched?.()) ?? false),
|
((await gcConfig.getGemini31Launched?.()) ?? false),
|
||||||
|
false,
|
||||||
|
gcConfig.getHasAccessToPreviewModel?.() ?? true,
|
||||||
|
gcConfig,
|
||||||
);
|
);
|
||||||
const customHeadersEnv =
|
const customHeadersEnv =
|
||||||
process.env['GEMINI_CLI_CUSTOM_HEADERS'] || undefined;
|
process.env['GEMINI_CLI_CUSTOM_HEADERS'] || undefined;
|
||||||
|
|||||||
@@ -525,7 +525,13 @@ export class GeminiChat {
|
|||||||
const useGemini3_1 =
|
const useGemini3_1 =
|
||||||
(await this.context.config.getGemini31Launched?.()) ?? false;
|
(await this.context.config.getGemini31Launched?.()) ?? false;
|
||||||
// Default to the last used model (which respects arguments/availability selection)
|
// Default to the last used model (which respects arguments/availability selection)
|
||||||
let modelToUse = resolveModel(lastModelToUse, useGemini3_1);
|
let modelToUse = resolveModel(
|
||||||
|
lastModelToUse,
|
||||||
|
useGemini3_1,
|
||||||
|
false,
|
||||||
|
this.context.config.getHasAccessToPreviewModel?.() ?? true,
|
||||||
|
this.context.config,
|
||||||
|
);
|
||||||
|
|
||||||
// If the active model has changed (e.g. due to a fallback updating the config),
|
// If the active model has changed (e.g. due to a fallback updating the config),
|
||||||
// we switch to the new active model.
|
// we switch to the new active model.
|
||||||
@@ -533,6 +539,9 @@ export class GeminiChat {
|
|||||||
modelToUse = resolveModel(
|
modelToUse = resolveModel(
|
||||||
this.context.config.getActiveModel(),
|
this.context.config.getActiveModel(),
|
||||||
useGemini3_1,
|
useGemini3_1,
|
||||||
|
false,
|
||||||
|
this.context.config.getHasAccessToPreviewModel?.() ?? true,
|
||||||
|
this.context.config,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ export class PromptProvider {
|
|||||||
const desiredModel = resolveModel(
|
const desiredModel = resolveModel(
|
||||||
context.config.getActiveModel(),
|
context.config.getActiveModel(),
|
||||||
context.config.getGemini31LaunchedSync?.() ?? false,
|
context.config.getGemini31LaunchedSync?.() ?? false,
|
||||||
|
false,
|
||||||
|
context.config.getHasAccessToPreviewModel?.() ?? true,
|
||||||
|
context.config,
|
||||||
);
|
);
|
||||||
const isModernModel = supportsModernFeatures(desiredModel);
|
const isModernModel = supportsModernFeatures(desiredModel);
|
||||||
const activeSnippets = isModernModel ? snippets : legacySnippets;
|
const activeSnippets = isModernModel ? snippets : legacySnippets;
|
||||||
@@ -239,6 +242,9 @@ export class PromptProvider {
|
|||||||
const desiredModel = resolveModel(
|
const desiredModel = resolveModel(
|
||||||
context.config.getActiveModel(),
|
context.config.getActiveModel(),
|
||||||
context.config.getGemini31LaunchedSync?.() ?? false,
|
context.config.getGemini31LaunchedSync?.() ?? false,
|
||||||
|
false,
|
||||||
|
context.config.getHasAccessToPreviewModel?.() ?? true,
|
||||||
|
context.config,
|
||||||
);
|
);
|
||||||
const isModernModel = supportsModernFeatures(desiredModel);
|
const isModernModel = supportsModernFeatures(desiredModel);
|
||||||
const activeSnippets = isModernModel ? snippets : legacySnippets;
|
const activeSnippets = isModernModel ? snippets : legacySnippets;
|
||||||
|
|||||||
@@ -180,6 +180,8 @@ export class ClassifierStrategy implements RoutingStrategy {
|
|||||||
routerResponse.model_choice,
|
routerResponse.model_choice,
|
||||||
useGemini3_1,
|
useGemini3_1,
|
||||||
useCustomToolModel,
|
useCustomToolModel,
|
||||||
|
config.getHasAccessToPreviewModel?.() ?? true,
|
||||||
|
config,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ export class DefaultStrategy implements TerminalStrategy {
|
|||||||
const defaultModel = resolveModel(
|
const defaultModel = resolveModel(
|
||||||
config.getModel(),
|
config.getModel(),
|
||||||
config.getGemini31LaunchedSync?.() ?? false,
|
config.getGemini31LaunchedSync?.() ?? false,
|
||||||
|
false,
|
||||||
|
config.getHasAccessToPreviewModel?.() ?? true,
|
||||||
|
config,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
model: defaultModel,
|
model: defaultModel,
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ export class FallbackStrategy implements RoutingStrategy {
|
|||||||
const resolvedModel = resolveModel(
|
const resolvedModel = resolveModel(
|
||||||
requestedModel,
|
requestedModel,
|
||||||
config.getGemini31LaunchedSync?.() ?? false,
|
config.getGemini31LaunchedSync?.() ?? false,
|
||||||
|
false,
|
||||||
|
config.getHasAccessToPreviewModel?.() ?? true,
|
||||||
|
config,
|
||||||
);
|
);
|
||||||
const service = config.getModelAvailabilityService();
|
const service = config.getModelAvailabilityService();
|
||||||
const snapshot = service.snapshot(resolvedModel);
|
const snapshot = service.snapshot(resolvedModel);
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ export class NumericalClassifierStrategy implements RoutingStrategy {
|
|||||||
modelAlias,
|
modelAlias,
|
||||||
useGemini3_1,
|
useGemini3_1,
|
||||||
useCustomToolModel,
|
useCustomToolModel,
|
||||||
|
config.getHasAccessToPreviewModel?.() ?? true,
|
||||||
|
config,
|
||||||
);
|
);
|
||||||
|
|
||||||
const latencyMs = Date.now() - startTime;
|
const latencyMs = Date.now() - startTime;
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ export class OverrideStrategy implements RoutingStrategy {
|
|||||||
model: resolveModel(
|
model: resolveModel(
|
||||||
overrideModel,
|
overrideModel,
|
||||||
config.getGemini31LaunchedSync?.() ?? false,
|
config.getGemini31LaunchedSync?.() ?? false,
|
||||||
|
false,
|
||||||
|
config.getHasAccessToPreviewModel?.() ?? true,
|
||||||
|
config,
|
||||||
),
|
),
|
||||||
metadata: {
|
metadata: {
|
||||||
source: this.name,
|
source: this.name,
|
||||||
|
|||||||
@@ -59,9 +59,8 @@ export interface ModelDefinition {
|
|||||||
tier?: string; // 'pro' | 'flash' | 'flash-lite' | 'custom' | 'auto'
|
tier?: string; // 'pro' | 'flash' | 'flash-lite' | 'custom' | 'auto'
|
||||||
family?: string; // The gemini family, e.g. 'gemini-3' | 'gemini-2'
|
family?: string; // The gemini family, e.g. 'gemini-3' | 'gemini-2'
|
||||||
isPreview?: boolean;
|
isPreview?: boolean;
|
||||||
// Specifies which view the model should appear in. If unset, the model will
|
// Specifies whether the model should be visible in the dialog.
|
||||||
// not appear in the dialog.
|
isVisible?: boolean;
|
||||||
dialogLocation?: 'main' | 'manual';
|
|
||||||
/** A short description of the model for the dialog. */
|
/** A short description of the model for the dialog. */
|
||||||
dialogDescription?: string;
|
dialogDescription?: string;
|
||||||
features?: {
|
features?: {
|
||||||
@@ -73,12 +72,45 @@ export interface ModelDefinition {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A model resolution is a mapping from a model name to a list of conditions
|
||||||
|
// that can be used to resolve the model to a model ID.
|
||||||
|
export interface ModelResolution {
|
||||||
|
// The default model ID to use when no conditions are met.
|
||||||
|
default: string;
|
||||||
|
// A list of conditions that can be used to resolve the model.
|
||||||
|
contexts?: Array<{
|
||||||
|
// The condition to check for.
|
||||||
|
condition: ResolutionCondition;
|
||||||
|
// The model ID to use when the condition is met.
|
||||||
|
target: string;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The actual state of the current session. */
|
||||||
|
export interface ResolutionContext {
|
||||||
|
useGemini3_1?: boolean;
|
||||||
|
useCustomTools?: boolean;
|
||||||
|
hasAccessToPreview?: boolean;
|
||||||
|
requestedModel?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The requirements defined in the registry. */
|
||||||
|
export interface ResolutionCondition {
|
||||||
|
useGemini3_1?: boolean;
|
||||||
|
useCustomTools?: boolean;
|
||||||
|
hasAccessToPreview?: boolean;
|
||||||
|
/** Matches if the current model is in this list. */
|
||||||
|
requestedModels?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface ModelConfigServiceConfig {
|
export interface ModelConfigServiceConfig {
|
||||||
aliases?: Record<string, ModelConfigAlias>;
|
aliases?: Record<string, ModelConfigAlias>;
|
||||||
customAliases?: Record<string, ModelConfigAlias>;
|
customAliases?: Record<string, ModelConfigAlias>;
|
||||||
overrides?: ModelConfigOverride[];
|
overrides?: ModelConfigOverride[];
|
||||||
customOverrides?: ModelConfigOverride[];
|
customOverrides?: ModelConfigOverride[];
|
||||||
modelDefinitions?: Record<string, ModelDefinition>;
|
modelDefinitions?: Record<string, ModelDefinition>;
|
||||||
|
modelIdResolutions?: Record<string, ModelResolution>;
|
||||||
|
classifierIdResolutions?: Record<string, ModelResolution>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_ALIAS_CHAIN_DEPTH = 100;
|
const MAX_ALIAS_CHAIN_DEPTH = 100;
|
||||||
@@ -121,6 +153,74 @@ export class ModelConfigService {
|
|||||||
return this.config.modelDefinitions ?? {};
|
return this.config.modelDefinitions ?? {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private matches(
|
||||||
|
condition: ResolutionCondition,
|
||||||
|
context: ResolutionContext,
|
||||||
|
): boolean {
|
||||||
|
return Object.entries(condition).every(([key, value]) => {
|
||||||
|
if (value === undefined) return true;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case 'useGemini3_1':
|
||||||
|
return value === context.useGemini3_1;
|
||||||
|
case 'useCustomTools':
|
||||||
|
return value === context.useCustomTools;
|
||||||
|
case 'hasAccessToPreview':
|
||||||
|
return value === context.hasAccessToPreview;
|
||||||
|
case 'requestedModels':
|
||||||
|
return (
|
||||||
|
Array.isArray(value) &&
|
||||||
|
!!context.requestedModel &&
|
||||||
|
value.includes(context.requestedModel)
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolves a model ID to a concrete model ID based on the provided context.
|
||||||
|
resolveModelId(
|
||||||
|
requestedName: string,
|
||||||
|
context: ResolutionContext = {},
|
||||||
|
): string {
|
||||||
|
const resolution = this.config.modelIdResolutions?.[requestedName];
|
||||||
|
if (!resolution) {
|
||||||
|
return requestedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ctx of resolution.contexts ?? []) {
|
||||||
|
if (this.matches(ctx.condition, context)) {
|
||||||
|
return ctx.target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolution.default;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolves a classifier model ID to a concrete model ID based on the provided context.
|
||||||
|
resolveClassifierModelId(
|
||||||
|
tier: string,
|
||||||
|
requestedModel: string,
|
||||||
|
context: ResolutionContext = {},
|
||||||
|
): string {
|
||||||
|
const resolution = this.config.classifierIdResolutions?.[tier];
|
||||||
|
const fullContext: ResolutionContext = { ...context, requestedModel };
|
||||||
|
|
||||||
|
if (!resolution) {
|
||||||
|
// Fallback to regular model resolution if no classifier-specific rule exists
|
||||||
|
return this.resolveModelId(tier, fullContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ctx of resolution.contexts ?? []) {
|
||||||
|
if (this.matches(ctx.condition, fullContext)) {
|
||||||
|
return ctx.target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolution.default;
|
||||||
|
}
|
||||||
|
|
||||||
registerRuntimeModelConfig(aliasName: string, alias: ModelConfigAlias): void {
|
registerRuntimeModelConfig(aliasName: string, alias: ModelConfigAlias): void {
|
||||||
this.runtimeAliases[aliasName] = alias;
|
this.runtimeAliases[aliasName] = alias;
|
||||||
}
|
}
|
||||||
|
|||||||
+404
-20
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user