mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-19 17:50:37 -07:00
Add ModelChain support to ModelConfigService and make ModelDialog dynamic (#22914)
This commit is contained in:
@@ -19,6 +19,8 @@ import {
|
||||
PREVIEW_GEMINI_3_1_MODEL,
|
||||
} from '../config/models.js';
|
||||
import { AuthType } from '../core/contentGenerator.js';
|
||||
import { ModelConfigService } from '../services/modelConfigService.js';
|
||||
import { DEFAULT_MODEL_CONFIGS } from '../config/defaultModelConfigs.js';
|
||||
|
||||
const createMockConfig = (overrides: Partial<Config> = {}): Config => {
|
||||
const config = {
|
||||
@@ -163,6 +165,66 @@ describe('policyHelpers', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolvePolicyChain behavior is identical between dynamic and legacy implementations', () => {
|
||||
const testCases = [
|
||||
{ name: 'Default Auto', model: DEFAULT_GEMINI_MODEL_AUTO },
|
||||
{ name: 'Gemini 3 Auto', model: 'auto-gemini-3' },
|
||||
{ name: 'Flash Lite', model: DEFAULT_GEMINI_FLASH_LITE_MODEL },
|
||||
{
|
||||
name: 'Gemini 3 Auto (3.1 Enabled)',
|
||||
model: 'auto-gemini-3',
|
||||
useGemini31: true,
|
||||
},
|
||||
{
|
||||
name: 'Gemini 3 Auto (3.1 + Custom Tools)',
|
||||
model: 'auto-gemini-3',
|
||||
useGemini31: true,
|
||||
authType: AuthType.USE_GEMINI,
|
||||
},
|
||||
{
|
||||
name: 'Gemini 3 Auto (No Access)',
|
||||
model: 'auto-gemini-3',
|
||||
hasAccess: false,
|
||||
},
|
||||
{ name: 'Concrete Model (2.5 Pro)', model: 'gemini-2.5-pro' },
|
||||
{ name: 'Custom Model', model: 'my-custom-model' },
|
||||
{
|
||||
name: 'Wrap Around',
|
||||
model: DEFAULT_GEMINI_MODEL_AUTO,
|
||||
wrapsAround: true,
|
||||
},
|
||||
];
|
||||
|
||||
testCases.forEach(
|
||||
({ name, model, useGemini31, hasAccess, authType, wrapsAround }) => {
|
||||
it(`achieves parity for: ${name}`, () => {
|
||||
const createBaseConfig = (dynamic: boolean) =>
|
||||
createMockConfig({
|
||||
getExperimentalDynamicModelConfiguration: () => dynamic,
|
||||
getModel: () => model,
|
||||
getGemini31LaunchedSync: () => useGemini31 ?? false,
|
||||
getHasAccessToPreviewModel: () => hasAccess ?? true,
|
||||
getContentGeneratorConfig: () => ({ authType }),
|
||||
modelConfigService: new ModelConfigService(DEFAULT_MODEL_CONFIGS),
|
||||
});
|
||||
|
||||
const legacyChain = resolvePolicyChain(
|
||||
createBaseConfig(false),
|
||||
model,
|
||||
wrapsAround,
|
||||
);
|
||||
const dynamicChain = resolvePolicyChain(
|
||||
createBaseConfig(true),
|
||||
model,
|
||||
wrapsAround,
|
||||
);
|
||||
|
||||
expect(dynamicChain).toEqual(legacyChain);
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('buildFallbackPolicyContext', () => {
|
||||
it('returns remaining candidates after the failed model', () => {
|
||||
const chain = [
|
||||
|
||||
@@ -53,12 +53,57 @@ export function resolvePolicyChain(
|
||||
useGemini31,
|
||||
useCustomToolModel,
|
||||
hasAccessToPreview,
|
||||
config,
|
||||
);
|
||||
const isAutoPreferred = preferredModel
|
||||
? isAutoModel(preferredModel, config)
|
||||
: false;
|
||||
const isAutoConfigured = isAutoModel(configuredModel, config);
|
||||
|
||||
// --- DYNAMIC PATH ---
|
||||
if (config.getExperimentalDynamicModelConfiguration?.() === true) {
|
||||
const context = {
|
||||
useGemini3_1: useGemini31,
|
||||
useCustomTools: useCustomToolModel,
|
||||
};
|
||||
|
||||
if (resolvedModel === DEFAULT_GEMINI_FLASH_LITE_MODEL) {
|
||||
chain = config.modelConfigService.resolveChain('lite', context);
|
||||
} else if (
|
||||
isGemini3Model(resolvedModel, config) ||
|
||||
isAutoModel(preferredModel ?? '', config) ||
|
||||
isAutoModel(configuredModel, config)
|
||||
) {
|
||||
// 1. Try to find a chain specifically for the current configured alias
|
||||
if (
|
||||
isAutoModel(configuredModel, config) &&
|
||||
config.modelConfigService.getModelChain(configuredModel)
|
||||
) {
|
||||
chain = config.modelConfigService.resolveChain(
|
||||
configuredModel,
|
||||
context,
|
||||
);
|
||||
}
|
||||
// 2. Fallback to family-based auto-routing
|
||||
if (!chain) {
|
||||
const previewEnabled =
|
||||
hasAccessToPreview &&
|
||||
(isGemini3Model(resolvedModel, config) ||
|
||||
preferredModel === PREVIEW_GEMINI_MODEL_AUTO ||
|
||||
configuredModel === PREVIEW_GEMINI_MODEL_AUTO);
|
||||
const chainKey = previewEnabled ? 'preview' : 'default';
|
||||
chain = config.modelConfigService.resolveChain(chainKey, context);
|
||||
}
|
||||
}
|
||||
if (!chain) {
|
||||
// No matching modelChains found, default to single model chain
|
||||
chain = createSingleModelChain(modelFromConfig);
|
||||
}
|
||||
return applyDynamicSlicing(chain, resolvedModel, wrapsAround);
|
||||
}
|
||||
|
||||
// --- LEGACY PATH ---
|
||||
|
||||
if (resolvedModel === DEFAULT_GEMINI_FLASH_LITE_MODEL) {
|
||||
chain = getFlashLitePolicyChain();
|
||||
} else if (
|
||||
@@ -90,7 +135,17 @@ export function resolvePolicyChain(
|
||||
} else {
|
||||
chain = createSingleModelChain(modelFromConfig);
|
||||
}
|
||||
return applyDynamicSlicing(chain, resolvedModel, wrapsAround);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies active-index slicing and wrap-around logic to a chain template.
|
||||
*/
|
||||
function applyDynamicSlicing(
|
||||
chain: ModelPolicy[],
|
||||
resolvedModel: string,
|
||||
wrapsAround: boolean,
|
||||
): ModelPolicyChain {
|
||||
const activeIndex = chain.findIndex(
|
||||
(policy) => policy.model === resolvedModel,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user