Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/get-started/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,10 +361,10 @@
}
}
},
"gemini-3-pro-preview": {

Check warning on line 364 in docs/get-started/configuration.md

View workflow job for this annotation

GitHub Actions / Lint

Found sensitive keyword "gemini-3". Please make sure this change is appropriate to submit.
"extends": "chat-base-3",
"modelConfig": {
"model": "gemini-3-pro-preview"

Check warning on line 367 in docs/get-started/configuration.md

View workflow job for this annotation

GitHub Actions / Lint

Found sensitive keyword "gemini-3". Please make sure this change is appropriate to submit.
}
},
"gemini-2.5-pro": {
Expand Down Expand Up @@ -494,6 +494,11 @@
}
```

- **`modelConfigs.customAliases`** (object):
- **Description:** Custom named presets for model configs. These are merged
with (and override) the built-in aliases.
- **Default:** `{}`

- **`modelConfigs.overrides`** (array):
- **Description:** Apply specific configuration overrides based on matches,
with a primary key of model (or alias). The most specific match will be
Expand Down
10 changes: 10 additions & 0 deletions packages/cli/src/config/settingsSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,16 @@ const SETTINGS_SCHEMA = {
'Named presets for model configs. Can be used in place of a model name and can inherit from other aliases using an `extends` property.',
showInDialog: false,
},
customAliases: {
type: 'object',
label: 'Custom Model Config Aliases',
category: 'Model',
requiresRestart: false,
default: {},
description:
'Custom named presets for model configs. These are merged with (and override) the built-in aliases.',
showInDialog: false,
},
overrides: {
type: 'array',
label: 'Model Config Overrides',
Expand Down
121 changes: 121 additions & 0 deletions packages/core/src/services/modelConfigService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -576,4 +576,125 @@ describe('ModelConfigService', () => {
});
});
});

describe('custom aliases', () => {
it('should resolve a custom alias', () => {
const config: ModelConfigServiceConfig = {
aliases: {},
customAliases: {
'my-custom-alias': {
modelConfig: {
model: 'gemini-custom',
generateContentConfig: {
temperature: 0.9,
},
},
},
},
overrides: [],
};
const service = new ModelConfigService(config);
const resolved = service.getResolvedConfig({ model: 'my-custom-alias' });

expect(resolved.model).toBe('gemini-custom');
expect(resolved.generateContentConfig).toEqual({
temperature: 0.9,
});
});

it('should allow custom aliases to override built-in aliases', () => {
const config: ModelConfigServiceConfig = {
aliases: {
'standard-alias': {
modelConfig: {
model: 'gemini-standard',
generateContentConfig: {
temperature: 0.5,
},
},
},
},
customAliases: {
'standard-alias': {
modelConfig: {
model: 'gemini-custom-override',
generateContentConfig: {
temperature: 0.1,
},
},
},
},
overrides: [],
};
const service = new ModelConfigService(config);
const resolved = service.getResolvedConfig({ model: 'standard-alias' });

expect(resolved.model).toBe('gemini-custom-override');
expect(resolved.generateContentConfig).toEqual({
temperature: 0.1,
});
});
});

describe('unrecognized models', () => {
it('should apply overrides to unrecognized model names', () => {
const unregisteredModelName = 'my-unregistered-model-v1';
const config: ModelConfigServiceConfig = {
aliases: {}, // No aliases defined
overrides: [
{
match: { model: unregisteredModelName },
modelConfig: {
generateContentConfig: {
temperature: 0.01,
},
},
},
],
};
const service = new ModelConfigService(config);

// Request the unregistered model directly
const resolved = service.getResolvedConfig({
model: unregisteredModelName,
});

// It should preserve the model name and apply the override
expect(resolved.model).toBe(unregisteredModelName);
expect(resolved.generateContentConfig).toEqual({
temperature: 0.01,
});
});

it('should apply scoped overrides to unrecognized model names', () => {
const unregisteredModelName = 'my-unregistered-model-v1';
const config: ModelConfigServiceConfig = {
aliases: {},
overrides: [
{
match: {
model: unregisteredModelName,
overrideScope: 'special-agent',
},
modelConfig: {
generateContentConfig: {
temperature: 0.99,
},
},
},
],
};
const service = new ModelConfigService(config);

const resolved = service.getResolvedConfig({
model: unregisteredModelName,
overrideScope: 'special-agent',
});

expect(resolved.model).toBe(unregisteredModelName);
expect(resolved.generateContentConfig).toEqual({
temperature: 0.99,
});
});
});
});
9 changes: 7 additions & 2 deletions packages/core/src/services/modelConfigService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface ModelConfigAlias {

export interface ModelConfigServiceConfig {
aliases?: Record<string, ModelConfigAlias>;
customAliases?: Record<string, ModelConfigAlias>;
overrides?: ModelConfigOverride[];
}

Expand Down Expand Up @@ -104,8 +105,12 @@ export class ModelConfigService {
generateContentConfig: GenerateContentConfig;
} {
const config = this.config || {};
const { aliases = {}, overrides = [] } = config;
const allAliases = { ...aliases, ...this.runtimeAliases };
const { aliases = {}, customAliases = {}, overrides = [] } = config;
const allAliases = {
...aliases,
...customAliases,
...this.runtimeAliases,
};
let baseModel: string | undefined = context.model;
let resolvedConfig: GenerateContentConfig = {};

Expand Down
8 changes: 8 additions & 0 deletions schemas/settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@
"modelConfigs": {
"title": "Model Configs",
"description": "Model configurations.",
"markdownDescription": "Model configurations.\n\n- Category: `Model`\n- Requires restart: `no`\n- Default: `{\n \"aliases\": {\n \"base\": {\n \"modelConfig\": {\n \"generateContentConfig\": {\n \"temperature\": 0,\n \"topP\": 1\n }\n }\n },\n \"chat-base\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"generateContentConfig\": {\n \"thinkingConfig\": {\n \"includeThoughts\": true\n },\n \"temperature\": 1,\n \"topP\": 0.95,\n \"topK\": 64\n }\n }\n },\n \"chat-base-2.5\": {\n \"extends\": \"chat-base\",\n \"modelConfig\": {\n \"generateContentConfig\": {\n \"thinkingConfig\": {\n \"thinkingBudget\": 8192\n }\n }\n }\n },\n \"chat-base-3\": {\n \"extends\": \"chat-base\",\n \"modelConfig\": {\n \"generateContentConfig\": {\n \"thinkingConfig\": {\n \"thinkingLevel\": \"HIGH\"\n }\n }\n }\n },\n \"gemini-3-pro-preview\": {\n \"extends\": \"chat-base-3\",\n \"modelConfig\": {\n \"model\": \"gemini-3-pro-preview\"\n }\n },\n \"gemini-2.5-pro\": {\n \"extends\": \"chat-base-2.5\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-pro\"\n }\n },\n \"gemini-2.5-flash\": {\n \"extends\": \"chat-base-2.5\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash\"\n }\n },\n \"gemini-2.5-flash-lite\": {\n \"extends\": \"chat-base-2.5\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash-lite\"\n }\n },\n \"gemini-2.5-flash-base\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash\"\n }\n },\n \"classifier\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash-lite\",\n \"generateContentConfig\": {\n \"maxOutputTokens\": 1024,\n \"thinkingConfig\": {\n \"thinkingBudget\": 512\n }\n }\n }\n },\n \"prompt-completion\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash-lite\",\n \"generateContentConfig\": {\n \"temperature\": 0.3,\n \"maxOutputTokens\": 16000,\n \"thinkingConfig\": {\n \"thinkingBudget\": 0\n }\n }\n }\n },\n \"edit-corrector\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash-lite\",\n \"generateContentConfig\": {\n \"thinkingConfig\": {\n \"thinkingBudget\": 0\n }\n }\n }\n },\n \"summarizer-default\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash-lite\",\n \"generateContentConfig\": {\n \"maxOutputTokens\": 2000\n }\n }\n },\n \"summarizer-shell\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash-lite\",\n \"generateContentConfig\": {\n \"maxOutputTokens\": 2000\n }\n }\n },\n \"web-search\": {\n \"extends\": \"gemini-2.5-flash-base\",\n \"modelConfig\": {\n \"generateContentConfig\": {\n \"tools\": [\n {\n \"googleSearch\": {}\n }\n ]\n }\n }\n },\n \"web-fetch\": {\n \"extends\": \"gemini-2.5-flash-base\",\n \"modelConfig\": {\n \"generateContentConfig\": {\n \"tools\": [\n {\n \"urlContext\": {}\n }\n ]\n }\n }\n },\n \"web-fetch-fallback\": {\n \"extends\": \"gemini-2.5-flash-base\",\n \"modelConfig\": {}\n },\n \"loop-detection\": {\n \"extends\": \"gemini-2.5-flash-base\",\n \"modelConfig\": {}\n },\n \"loop-detection-double-check\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-pro\"\n }\n },\n \"llm-edit-fixer\": {\n \"extends\": \"gemini-2.5-flash-base\",\n \"modelConfig\": {}\n },\n \"next-speaker-checker\": {\n \"extends\": \"gemini-2.5-flash-base\",\n \"modelConfig\": {}\n }\n }\n}`",

Check warning on line 439 in schemas/settings.schema.json

View workflow job for this annotation

GitHub Actions / Lint

Found sensitive keyword "gemini-3". Please make sure this change is appropriate to submit.

Check warning on line 439 in schemas/settings.schema.json

View workflow job for this annotation

GitHub Actions / Lint

Found sensitive keyword "gemini-3". Please make sure this change is appropriate to submit.
"default": {
"aliases": {
"base": {
Expand Down Expand Up @@ -480,10 +480,10 @@
}
}
},
"gemini-3-pro-preview": {

Check warning on line 483 in schemas/settings.schema.json

View workflow job for this annotation

GitHub Actions / Lint

Found sensitive keyword "gemini-3". Please make sure this change is appropriate to submit.
"extends": "chat-base-3",
"modelConfig": {
"model": "gemini-3-pro-preview"

Check warning on line 486 in schemas/settings.schema.json

View workflow job for this annotation

GitHub Actions / Lint

Found sensitive keyword "gemini-3". Please make sure this change is appropriate to submit.
}
},
"gemini-2.5-pro": {
Expand Down Expand Up @@ -617,7 +617,7 @@
"aliases": {
"title": "Model Config Aliases",
"description": "Named presets for model configs. Can be used in place of a model name and can inherit from other aliases using an `extends` property.",
"markdownDescription": "Named presets for model configs. Can be used in place of a model name and can inherit from other aliases using an `extends` property.\n\n- Category: `Model`\n- Requires restart: `no`\n- Default: `{\n \"base\": {\n \"modelConfig\": {\n \"generateContentConfig\": {\n \"temperature\": 0,\n \"topP\": 1\n }\n }\n },\n \"chat-base\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"generateContentConfig\": {\n \"thinkingConfig\": {\n \"includeThoughts\": true\n },\n \"temperature\": 1,\n \"topP\": 0.95,\n \"topK\": 64\n }\n }\n },\n \"chat-base-2.5\": {\n \"extends\": \"chat-base\",\n \"modelConfig\": {\n \"generateContentConfig\": {\n \"thinkingConfig\": {\n \"thinkingBudget\": 8192\n }\n }\n }\n },\n \"chat-base-3\": {\n \"extends\": \"chat-base\",\n \"modelConfig\": {\n \"generateContentConfig\": {\n \"thinkingConfig\": {\n \"thinkingLevel\": \"HIGH\"\n }\n }\n }\n },\n \"gemini-3-pro-preview\": {\n \"extends\": \"chat-base-3\",\n \"modelConfig\": {\n \"model\": \"gemini-3-pro-preview\"\n }\n },\n \"gemini-2.5-pro\": {\n \"extends\": \"chat-base-2.5\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-pro\"\n }\n },\n \"gemini-2.5-flash\": {\n \"extends\": \"chat-base-2.5\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash\"\n }\n },\n \"gemini-2.5-flash-lite\": {\n \"extends\": \"chat-base-2.5\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash-lite\"\n }\n },\n \"gemini-2.5-flash-base\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash\"\n }\n },\n \"classifier\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash-lite\",\n \"generateContentConfig\": {\n \"maxOutputTokens\": 1024,\n \"thinkingConfig\": {\n \"thinkingBudget\": 512\n }\n }\n }\n },\n \"prompt-completion\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash-lite\",\n \"generateContentConfig\": {\n \"temperature\": 0.3,\n \"maxOutputTokens\": 16000,\n \"thinkingConfig\": {\n \"thinkingBudget\": 0\n }\n }\n }\n },\n \"edit-corrector\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash-lite\",\n \"generateContentConfig\": {\n \"thinkingConfig\": {\n \"thinkingBudget\": 0\n }\n }\n }\n },\n \"summarizer-default\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash-lite\",\n \"generateContentConfig\": {\n \"maxOutputTokens\": 2000\n }\n }\n },\n \"summarizer-shell\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-flash-lite\",\n \"generateContentConfig\": {\n \"maxOutputTokens\": 2000\n }\n }\n },\n \"web-search\": {\n \"extends\": \"gemini-2.5-flash-base\",\n \"modelConfig\": {\n \"generateContentConfig\": {\n \"tools\": [\n {\n \"googleSearch\": {}\n }\n ]\n }\n }\n },\n \"web-fetch\": {\n \"extends\": \"gemini-2.5-flash-base\",\n \"modelConfig\": {\n \"generateContentConfig\": {\n \"tools\": [\n {\n \"urlContext\": {}\n }\n ]\n }\n }\n },\n \"web-fetch-fallback\": {\n \"extends\": \"gemini-2.5-flash-base\",\n \"modelConfig\": {}\n },\n \"loop-detection\": {\n \"extends\": \"gemini-2.5-flash-base\",\n \"modelConfig\": {}\n },\n \"loop-detection-double-check\": {\n \"extends\": \"base\",\n \"modelConfig\": {\n \"model\": \"gemini-2.5-pro\"\n }\n },\n \"llm-edit-fixer\": {\n \"extends\": \"gemini-2.5-flash-base\",\n \"modelConfig\": {}\n },\n \"next-speaker-checker\": {\n \"extends\": \"gemini-2.5-flash-base\",\n \"modelConfig\": {}\n }\n}`",

Check warning on line 620 in schemas/settings.schema.json

View workflow job for this annotation

GitHub Actions / Lint

Found sensitive keyword "gemini-3". Please make sure this change is appropriate to submit.

Check warning on line 620 in schemas/settings.schema.json

View workflow job for this annotation

GitHub Actions / Lint

Found sensitive keyword "gemini-3". Please make sure this change is appropriate to submit.
"default": {
"base": {
"modelConfig": {
Expand Down Expand Up @@ -660,10 +660,10 @@
}
}
},
"gemini-3-pro-preview": {

Check warning on line 663 in schemas/settings.schema.json

View workflow job for this annotation

GitHub Actions / Lint

Found sensitive keyword "gemini-3". Please make sure this change is appropriate to submit.
"extends": "chat-base-3",
"modelConfig": {
"model": "gemini-3-pro-preview"

Check warning on line 666 in schemas/settings.schema.json

View workflow job for this annotation

GitHub Actions / Lint

Found sensitive keyword "gemini-3". Please make sure this change is appropriate to submit.
}
},
"gemini-2.5-pro": {
Expand Down Expand Up @@ -794,6 +794,14 @@
"type": "object",
"additionalProperties": true
},
"customAliases": {
"title": "Custom Model Config Aliases",
"description": "Custom named presets for model configs. These are merged with (and override) the built-in aliases.",
"markdownDescription": "Custom named presets for model configs. These are merged with (and override) the built-in aliases.\n\n- Category: `Model`\n- Requires restart: `no`\n- Default: `{}`",
"default": {},
"type": "object",
"additionalProperties": true
},
"overrides": {
"title": "Model Config Overrides",
"description": "Apply specific configuration overrides based on matches, with a primary key of model (or alias). The most specific match will be used.",
Expand Down