mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 13:22:35 -07:00
feat(core): fallback to chat-base when using unrecognized models for chat (#19016)
This commit is contained in:
@@ -892,7 +892,7 @@ ${JSON.stringify(
|
|||||||
// Assert
|
// Assert
|
||||||
expect(ideContextStore.get).toHaveBeenCalled();
|
expect(ideContextStore.get).toHaveBeenCalled();
|
||||||
expect(mockTurnRunFn).toHaveBeenCalledWith(
|
expect(mockTurnRunFn).toHaveBeenCalledWith(
|
||||||
{ model: 'default-routed-model' },
|
{ model: 'default-routed-model', isChatModel: true },
|
||||||
initialRequest,
|
initialRequest,
|
||||||
expect.any(AbortSignal),
|
expect.any(AbortSignal),
|
||||||
undefined,
|
undefined,
|
||||||
@@ -1722,7 +1722,7 @@ ${JSON.stringify(
|
|||||||
expect(mockConfig.getModelRouterService).toHaveBeenCalled();
|
expect(mockConfig.getModelRouterService).toHaveBeenCalled();
|
||||||
expect(mockRouterService.route).toHaveBeenCalled();
|
expect(mockRouterService.route).toHaveBeenCalled();
|
||||||
expect(mockTurnRunFn).toHaveBeenCalledWith(
|
expect(mockTurnRunFn).toHaveBeenCalledWith(
|
||||||
{ model: 'routed-model' },
|
{ model: 'routed-model', isChatModel: true },
|
||||||
[{ text: 'Hi' }],
|
[{ text: 'Hi' }],
|
||||||
expect.any(AbortSignal),
|
expect.any(AbortSignal),
|
||||||
undefined,
|
undefined,
|
||||||
@@ -1740,7 +1740,7 @@ ${JSON.stringify(
|
|||||||
|
|
||||||
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
||||||
expect(mockTurnRunFn).toHaveBeenCalledWith(
|
expect(mockTurnRunFn).toHaveBeenCalledWith(
|
||||||
{ model: 'routed-model' },
|
{ model: 'routed-model', isChatModel: true },
|
||||||
[{ text: 'Hi' }],
|
[{ text: 'Hi' }],
|
||||||
expect.any(AbortSignal),
|
expect.any(AbortSignal),
|
||||||
undefined,
|
undefined,
|
||||||
@@ -1758,7 +1758,7 @@ ${JSON.stringify(
|
|||||||
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
||||||
// Should stick to the first model
|
// Should stick to the first model
|
||||||
expect(mockTurnRunFn).toHaveBeenCalledWith(
|
expect(mockTurnRunFn).toHaveBeenCalledWith(
|
||||||
{ model: 'routed-model' },
|
{ model: 'routed-model', isChatModel: true },
|
||||||
[{ text: 'Continue' }],
|
[{ text: 'Continue' }],
|
||||||
expect.any(AbortSignal),
|
expect.any(AbortSignal),
|
||||||
undefined,
|
undefined,
|
||||||
@@ -1776,7 +1776,7 @@ ${JSON.stringify(
|
|||||||
|
|
||||||
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
||||||
expect(mockTurnRunFn).toHaveBeenCalledWith(
|
expect(mockTurnRunFn).toHaveBeenCalledWith(
|
||||||
{ model: 'routed-model' },
|
{ model: 'routed-model', isChatModel: true },
|
||||||
[{ text: 'Hi' }],
|
[{ text: 'Hi' }],
|
||||||
expect.any(AbortSignal),
|
expect.any(AbortSignal),
|
||||||
undefined,
|
undefined,
|
||||||
@@ -1798,7 +1798,7 @@ ${JSON.stringify(
|
|||||||
expect(mockRouterService.route).toHaveBeenCalledTimes(2);
|
expect(mockRouterService.route).toHaveBeenCalledTimes(2);
|
||||||
// Should use the newly routed model
|
// Should use the newly routed model
|
||||||
expect(mockTurnRunFn).toHaveBeenCalledWith(
|
expect(mockTurnRunFn).toHaveBeenCalledWith(
|
||||||
{ model: 'new-routed-model' },
|
{ model: 'new-routed-model', isChatModel: true },
|
||||||
[{ text: 'A new topic' }],
|
[{ text: 'A new topic' }],
|
||||||
expect.any(AbortSignal),
|
expect.any(AbortSignal),
|
||||||
undefined,
|
undefined,
|
||||||
@@ -1826,7 +1826,7 @@ ${JSON.stringify(
|
|||||||
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
||||||
expect(mockTurnRunFn).toHaveBeenNthCalledWith(
|
expect(mockTurnRunFn).toHaveBeenNthCalledWith(
|
||||||
1,
|
1,
|
||||||
{ model: 'original-model' },
|
{ model: 'original-model', isChatModel: true },
|
||||||
[{ text: 'Hi' }],
|
[{ text: 'Hi' }],
|
||||||
expect.any(AbortSignal),
|
expect.any(AbortSignal),
|
||||||
undefined,
|
undefined,
|
||||||
@@ -1849,7 +1849,7 @@ ${JSON.stringify(
|
|||||||
expect(mockRouterService.route).toHaveBeenCalledTimes(2);
|
expect(mockRouterService.route).toHaveBeenCalledTimes(2);
|
||||||
expect(mockTurnRunFn).toHaveBeenNthCalledWith(
|
expect(mockTurnRunFn).toHaveBeenNthCalledWith(
|
||||||
2,
|
2,
|
||||||
{ model: 'fallback-model' },
|
{ model: 'fallback-model', isChatModel: true },
|
||||||
[{ text: 'Continue' }],
|
[{ text: 'Continue' }],
|
||||||
expect.any(AbortSignal),
|
expect.any(AbortSignal),
|
||||||
undefined,
|
undefined,
|
||||||
@@ -1935,7 +1935,7 @@ ${JSON.stringify(
|
|||||||
// First call with original request
|
// First call with original request
|
||||||
expect(mockTurnRunFn).toHaveBeenNthCalledWith(
|
expect(mockTurnRunFn).toHaveBeenNthCalledWith(
|
||||||
1,
|
1,
|
||||||
{ model: 'default-routed-model' },
|
{ model: 'default-routed-model', isChatModel: true },
|
||||||
initialRequest,
|
initialRequest,
|
||||||
expect.any(AbortSignal),
|
expect.any(AbortSignal),
|
||||||
undefined,
|
undefined,
|
||||||
@@ -1944,7 +1944,7 @@ ${JSON.stringify(
|
|||||||
// Second call with "Please continue."
|
// Second call with "Please continue."
|
||||||
expect(mockTurnRunFn).toHaveBeenNthCalledWith(
|
expect(mockTurnRunFn).toHaveBeenNthCalledWith(
|
||||||
2,
|
2,
|
||||||
{ model: 'default-routed-model' },
|
{ model: 'default-routed-model', isChatModel: true },
|
||||||
[{ text: 'System: Please continue.' }],
|
[{ text: 'System: Please continue.' }],
|
||||||
expect.any(AbortSignal),
|
expect.any(AbortSignal),
|
||||||
undefined,
|
undefined,
|
||||||
|
|||||||
@@ -656,7 +656,10 @@ export class GeminiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// availability logic
|
// availability logic
|
||||||
const modelConfigKey: ModelConfigKey = { model: modelToUse };
|
const modelConfigKey: ModelConfigKey = {
|
||||||
|
model: modelToUse,
|
||||||
|
isChatModel: true,
|
||||||
|
};
|
||||||
const { model: finalModel } = applyModelSelection(
|
const { model: finalModel } = applyModelSelection(
|
||||||
this.config,
|
this.config,
|
||||||
modelConfigKey,
|
modelConfigKey,
|
||||||
|
|||||||
@@ -729,6 +729,71 @@ describe('ModelConfigService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('fallback behavior', () => {
|
||||||
|
it('should fallback to chat-base if the requested model is completely unknown', () => {
|
||||||
|
const config: ModelConfigServiceConfig = {
|
||||||
|
aliases: {
|
||||||
|
'chat-base': {
|
||||||
|
modelConfig: {
|
||||||
|
model: 'default-fallback-model',
|
||||||
|
generateContentConfig: {
|
||||||
|
temperature: 0.99,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const service = new ModelConfigService(config);
|
||||||
|
const resolved = service.getResolvedConfig({
|
||||||
|
model: 'my-custom-model',
|
||||||
|
isChatModel: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// It preserves the requested model name, but inherits the config from chat-base
|
||||||
|
expect(resolved.model).toBe('my-custom-model');
|
||||||
|
expect(resolved.generateContentConfig).toEqual({
|
||||||
|
temperature: 0.99,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty config if requested model is unknown and chat-base is not defined', () => {
|
||||||
|
const config: ModelConfigServiceConfig = {
|
||||||
|
aliases: {},
|
||||||
|
};
|
||||||
|
const service = new ModelConfigService(config);
|
||||||
|
const resolved = service.getResolvedConfig({
|
||||||
|
model: 'my-custom-model',
|
||||||
|
isChatModel: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resolved.model).toBe('my-custom-model');
|
||||||
|
expect(resolved.generateContentConfig).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT fallback to chat-base if the requested model is completely unknown but isChatModel is false', () => {
|
||||||
|
const config: ModelConfigServiceConfig = {
|
||||||
|
aliases: {
|
||||||
|
'chat-base': {
|
||||||
|
modelConfig: {
|
||||||
|
model: 'default-fallback-model',
|
||||||
|
generateContentConfig: {
|
||||||
|
temperature: 0.99,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const service = new ModelConfigService(config);
|
||||||
|
const resolved = service.getResolvedConfig({
|
||||||
|
model: 'my-custom-model',
|
||||||
|
isChatModel: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resolved.model).toBe('my-custom-model');
|
||||||
|
expect(resolved.generateContentConfig).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('unrecognized models', () => {
|
describe('unrecognized models', () => {
|
||||||
it('should apply overrides to unrecognized model names', () => {
|
it('should apply overrides to unrecognized model names', () => {
|
||||||
const unregisteredModelName = 'my-unregistered-model-v1';
|
const unregisteredModelName = 'my-unregistered-model-v1';
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ export interface ModelConfigKey {
|
|||||||
// This allows overrides to specify different settings (e.g., higher temperature)
|
// This allows overrides to specify different settings (e.g., higher temperature)
|
||||||
// specifically for retry scenarios.
|
// specifically for retry scenarios.
|
||||||
isRetry?: boolean;
|
isRetry?: boolean;
|
||||||
|
|
||||||
|
// Indicates whether this request originates from the primary interactive chat model.
|
||||||
|
// Enables the default fallback configuration to `chat-base` when unknown.
|
||||||
|
isChatModel?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ModelConfig {
|
export interface ModelConfig {
|
||||||
@@ -122,6 +126,7 @@ export class ModelConfigService {
|
|||||||
const { aliasChain, baseModel, resolvedConfig } = this.resolveAliasChain(
|
const { aliasChain, baseModel, resolvedConfig } = this.resolveAliasChain(
|
||||||
context.model,
|
context.model,
|
||||||
allAliases,
|
allAliases,
|
||||||
|
context.isChatModel,
|
||||||
);
|
);
|
||||||
|
|
||||||
const modelToLevel = this.buildModelLevelMap(aliasChain, baseModel);
|
const modelToLevel = this.buildModelLevelMap(aliasChain, baseModel);
|
||||||
@@ -159,6 +164,7 @@ export class ModelConfigService {
|
|||||||
private resolveAliasChain(
|
private resolveAliasChain(
|
||||||
requestedModel: string,
|
requestedModel: string,
|
||||||
allAliases: Record<string, ModelConfigAlias>,
|
allAliases: Record<string, ModelConfigAlias>,
|
||||||
|
isChatModel?: boolean,
|
||||||
): {
|
): {
|
||||||
aliasChain: string[];
|
aliasChain: string[];
|
||||||
baseModel: string | undefined;
|
baseModel: string | undefined;
|
||||||
@@ -206,6 +212,21 @@ export class ModelConfigService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isChatModel) {
|
||||||
|
const fallbackAlias = 'chat-base';
|
||||||
|
if (allAliases[fallbackAlias]) {
|
||||||
|
const fallbackResolution = this.resolveAliasChain(
|
||||||
|
fallbackAlias,
|
||||||
|
allAliases,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
aliasChain: [...fallbackResolution.aliasChain, requestedModel],
|
||||||
|
baseModel: requestedModel,
|
||||||
|
resolvedConfig: fallbackResolution.resolvedConfig,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
aliasChain: [requestedModel],
|
aliasChain: [requestedModel],
|
||||||
baseModel: requestedModel,
|
baseModel: requestedModel,
|
||||||
|
|||||||
Reference in New Issue
Block a user