mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-18 09:11:55 -07:00
feat(core, cli): Add support for agents in settings.json. (#16433)
This commit is contained in:
@@ -619,6 +619,127 @@ describe('AgentRegistry', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('overrides', () => {
|
||||
it('should skip registration if agent is disabled in settings', async () => {
|
||||
const config = makeFakeConfig({
|
||||
agents: {
|
||||
overrides: {
|
||||
MockAgent: { disabled: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
const registry = new TestableAgentRegistry(config);
|
||||
|
||||
await registry.testRegisterAgent(MOCK_AGENT_V1);
|
||||
|
||||
expect(registry.getDefinition('MockAgent')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should skip remote agent registration if disabled in settings', async () => {
|
||||
const config = makeFakeConfig({
|
||||
agents: {
|
||||
overrides: {
|
||||
RemoteAgent: { disabled: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
const registry = new TestableAgentRegistry(config);
|
||||
|
||||
const remoteAgent: AgentDefinition = {
|
||||
kind: 'remote',
|
||||
name: 'RemoteAgent',
|
||||
description: 'A remote agent',
|
||||
agentCardUrl: 'https://example.com/card',
|
||||
inputConfig: { inputs: {} },
|
||||
};
|
||||
|
||||
await registry.testRegisterAgent(remoteAgent);
|
||||
|
||||
expect(registry.getDefinition('RemoteAgent')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should merge runConfig overrides', async () => {
|
||||
const config = makeFakeConfig({
|
||||
agents: {
|
||||
overrides: {
|
||||
MockAgent: {
|
||||
runConfig: { maxTurns: 50 },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const registry = new TestableAgentRegistry(config);
|
||||
|
||||
await registry.testRegisterAgent(MOCK_AGENT_V1);
|
||||
|
||||
const def = registry.getDefinition('MockAgent') as LocalAgentDefinition;
|
||||
expect(def.runConfig.max_turns).toBe(50);
|
||||
expect(def.runConfig.max_time_minutes).toBe(
|
||||
MOCK_AGENT_V1.runConfig.max_time_minutes,
|
||||
);
|
||||
});
|
||||
|
||||
it('should apply modelConfig overrides', async () => {
|
||||
const config = makeFakeConfig({
|
||||
agents: {
|
||||
overrides: {
|
||||
MockAgent: {
|
||||
modelConfig: {
|
||||
model: 'overridden-model',
|
||||
generateContentConfig: {
|
||||
temperature: 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const registry = new TestableAgentRegistry(config);
|
||||
|
||||
await registry.testRegisterAgent(MOCK_AGENT_V1);
|
||||
|
||||
const resolved = config.modelConfigService.getResolvedConfig({
|
||||
model: getModelConfigAlias(MOCK_AGENT_V1),
|
||||
});
|
||||
|
||||
expect(resolved.model).toBe('overridden-model');
|
||||
expect(resolved.generateContentConfig.temperature).toBe(0.5);
|
||||
// topP should still be MOCK_AGENT_V1.modelConfig.top_p (1) because we merged
|
||||
expect(resolved.generateContentConfig.topP).toBe(1);
|
||||
});
|
||||
|
||||
it('should deep merge generateContentConfig (e.g. thinkingConfig)', async () => {
|
||||
const config = makeFakeConfig({
|
||||
agents: {
|
||||
overrides: {
|
||||
MockAgent: {
|
||||
modelConfig: {
|
||||
generateContentConfig: {
|
||||
thinkingConfig: {
|
||||
thinkingBudget: 16384,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const registry = new TestableAgentRegistry(config);
|
||||
|
||||
await registry.testRegisterAgent(MOCK_AGENT_V1);
|
||||
|
||||
const resolved = config.modelConfigService.getResolvedConfig({
|
||||
model: getModelConfigAlias(MOCK_AGENT_V1),
|
||||
});
|
||||
|
||||
expect(resolved.generateContentConfig.thinkingConfig).toEqual({
|
||||
includeThoughts: true, // Preserved from default
|
||||
thinkingBudget: 16384, // Overridden
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getToolDescription', () => {
|
||||
it('should return default message when no agents are registered', () => {
|
||||
expect(registry.getToolDescription()).toContain(
|
||||
|
||||
@@ -14,7 +14,6 @@ import { CliHelpAgent } from './cli-help-agent.js';
|
||||
import { A2AClientManager } from './a2a-client-manager.js';
|
||||
import { ADCHandler } from './remote-invocation.js';
|
||||
import { type z } from 'zod';
|
||||
import type { GenerateContentConfig } from '@google/genai';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
import {
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
@@ -23,6 +22,10 @@ import {
|
||||
isPreviewModel,
|
||||
isAutoModel,
|
||||
} from '../config/models.js';
|
||||
import {
|
||||
type ModelConfig,
|
||||
ModelConfigService,
|
||||
} from '../services/modelConfigService.js';
|
||||
|
||||
/**
|
||||
* Returns the model config alias for a given agent definition.
|
||||
@@ -226,49 +229,83 @@ export class AgentRegistry {
|
||||
return;
|
||||
}
|
||||
|
||||
const overrides =
|
||||
this.config.getAgentsSettings().overrides?.[definition.name];
|
||||
if (overrides?.disabled) {
|
||||
if (this.config.getDebugMode()) {
|
||||
debugLogger.log(
|
||||
`[AgentRegistry] Skipping disabled agent '${definition.name}'`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.agents.has(definition.name) && this.config.getDebugMode()) {
|
||||
debugLogger.log(`[AgentRegistry] Overriding agent '${definition.name}'`);
|
||||
}
|
||||
|
||||
this.agents.set(definition.name, definition);
|
||||
// TODO(16443): Refactor definition merging logic into a helper.
|
||||
// To do this, we need to align the definition of the internal `Definition`
|
||||
// type with the one exported in settings.json.
|
||||
const mergedDefinition = {
|
||||
...definition,
|
||||
runConfig: {
|
||||
...definition.runConfig,
|
||||
max_time_minutes:
|
||||
overrides?.runConfig?.maxTimeMinutes ??
|
||||
definition.runConfig.max_time_minutes,
|
||||
max_turns:
|
||||
overrides?.runConfig?.maxTurns ?? definition.runConfig.max_turns,
|
||||
},
|
||||
};
|
||||
|
||||
this.agents.set(mergedDefinition.name, mergedDefinition);
|
||||
|
||||
// Register model config. We always create a runtime alias. However,
|
||||
// if the user is using `auto` as a model string then we also create
|
||||
// runtime overrides to ensure the subagent generation settings are
|
||||
// respected regardless of the final model string from routing.
|
||||
// TODO(12916): Migrate sub-agents where possible to static configs.
|
||||
const modelConfig = definition.modelConfig;
|
||||
const modelConfig = mergedDefinition.modelConfig;
|
||||
let model = modelConfig.model;
|
||||
if (model === 'inherit') {
|
||||
model = this.config.getModel();
|
||||
}
|
||||
|
||||
const generateContentConfig: GenerateContentConfig = {
|
||||
temperature: modelConfig.temp,
|
||||
topP: modelConfig.top_p,
|
||||
thinkingConfig: {
|
||||
includeThoughts: true,
|
||||
thinkingBudget: modelConfig.thinkingBudget ?? -1,
|
||||
let agentModelConfig: ModelConfig = {
|
||||
model,
|
||||
generateContentConfig: {
|
||||
temperature: modelConfig.temp,
|
||||
topP: modelConfig.top_p,
|
||||
thinkingConfig: {
|
||||
includeThoughts: true,
|
||||
thinkingBudget: modelConfig.thinkingBudget ?? -1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Apply standardized modelConfig overrides if present.
|
||||
if (overrides?.modelConfig) {
|
||||
agentModelConfig = ModelConfigService.merge(
|
||||
agentModelConfig,
|
||||
overrides.modelConfig,
|
||||
);
|
||||
}
|
||||
|
||||
this.config.modelConfigService.registerRuntimeModelConfig(
|
||||
getModelConfigAlias(definition),
|
||||
getModelConfigAlias(mergedDefinition),
|
||||
{
|
||||
modelConfig: {
|
||||
model,
|
||||
generateContentConfig,
|
||||
},
|
||||
modelConfig: agentModelConfig,
|
||||
},
|
||||
);
|
||||
|
||||
if (isAutoModel(model)) {
|
||||
if (agentModelConfig.model && isAutoModel(agentModelConfig.model)) {
|
||||
this.config.modelConfigService.registerRuntimeModelOverride({
|
||||
match: {
|
||||
overrideScope: definition.name,
|
||||
overrideScope: mergedDefinition.name,
|
||||
},
|
||||
modelConfig: {
|
||||
generateContentConfig,
|
||||
generateContentConfig: agentModelConfig.generateContentConfig,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -292,6 +329,17 @@ export class AgentRegistry {
|
||||
return;
|
||||
}
|
||||
|
||||
const overrides =
|
||||
this.config.getAgentsSettings().overrides?.[definition.name];
|
||||
if (overrides?.disabled) {
|
||||
if (this.config.getDebugMode()) {
|
||||
debugLogger.log(
|
||||
`[AgentRegistry] Skipping disabled remote agent '${definition.name}'`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.agents.has(definition.name) && this.config.getDebugMode()) {
|
||||
debugLogger.log(`[AgentRegistry] Overriding agent '${definition.name}'`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user