[Gemma x Gemini CLI] Add an Experimental Gemma Router that uses a LiteRT-LM shim into the Composite Model Classifier Strategy (#17231)

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Allen Hutchison <adh@google.com>
This commit is contained in:
Siddharth Diwan
2026-02-26 15:43:43 -08:00
committed by GitHub
parent 6dc9d5ff11
commit 9b7852f11c
29 changed files with 1456 additions and 58 deletions
+60
View File
@@ -2765,6 +2765,66 @@ describe('loadCliConfig approval mode', () => {
});
});
describe('loadCliConfig gemmaModelRouter', () => {
beforeEach(() => {
vi.resetAllMocks();
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
vi.spyOn(ExtensionManager.prototype, 'getExtensions').mockReturnValue([]);
});
afterEach(() => {
vi.unstubAllEnvs();
vi.restoreAllMocks();
});
it('should have gemmaModelRouter disabled by default', async () => {
process.argv = ['node', 'script.js'];
const argv = await parseArguments(createTestMergedSettings());
const settings = createTestMergedSettings();
const config = await loadCliConfig(settings, 'test-session', argv);
expect(config.getGemmaModelRouterEnabled()).toBe(false);
});
it('should load gemmaModelRouter settings from merged settings', async () => {
process.argv = ['node', 'script.js'];
const argv = await parseArguments(createTestMergedSettings());
const settings = createTestMergedSettings({
experimental: {
gemmaModelRouter: {
enabled: true,
classifier: {
host: 'http://custom:1234',
model: 'custom-gemma',
},
},
},
});
const config = await loadCliConfig(settings, 'test-session', argv);
expect(config.getGemmaModelRouterEnabled()).toBe(true);
const gemmaSettings = config.getGemmaModelRouterSettings();
expect(gemmaSettings.classifier?.host).toBe('http://custom:1234');
expect(gemmaSettings.classifier?.model).toBe('custom-gemma');
});
it('should handle partial gemmaModelRouter settings', async () => {
process.argv = ['node', 'script.js'];
const argv = await parseArguments(createTestMergedSettings());
const settings = createTestMergedSettings({
experimental: {
gemmaModelRouter: {
enabled: true,
},
},
});
const config = await loadCliConfig(settings, 'test-session', argv);
expect(config.getGemmaModelRouterEnabled()).toBe(true);
const gemmaSettings = config.getGemmaModelRouterSettings();
expect(gemmaSettings.classifier?.host).toBe('http://localhost:9379');
expect(gemmaSettings.classifier?.model).toBe('gemma3-1b-gpu-custom');
});
});
describe('loadCliConfig fileFiltering', () => {
const originalArgv = process.argv;
+1
View File
@@ -856,6 +856,7 @@ export async function loadCliConfig(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
format: (argv.outputFormat ?? settings.output?.format) as OutputFormat,
},
gemmaModelRouter: settings.experimental?.gemmaModelRouter,
fakeResponses: argv.fakeResponses,
recordResponses: argv.recordResponses,
retryFetchErrors: settings.general?.retryFetchErrors,
@@ -444,6 +444,60 @@ describe('SettingsSchema', () => {
expect(hookItemProperties.description).toBeDefined();
expect(hookItemProperties.description.type).toBe('string');
});
it('should have gemmaModelRouter setting in schema', () => {
const gemmaModelRouter =
getSettingsSchema().experimental.properties.gemmaModelRouter;
expect(gemmaModelRouter).toBeDefined();
expect(gemmaModelRouter.type).toBe('object');
expect(gemmaModelRouter.category).toBe('Experimental');
expect(gemmaModelRouter.default).toEqual({});
expect(gemmaModelRouter.requiresRestart).toBe(true);
expect(gemmaModelRouter.showInDialog).toBe(true);
expect(gemmaModelRouter.description).toBe(
'Enable Gemma model router (experimental).',
);
const enabled = gemmaModelRouter.properties.enabled;
expect(enabled).toBeDefined();
expect(enabled.type).toBe('boolean');
expect(enabled.category).toBe('Experimental');
expect(enabled.default).toBe(false);
expect(enabled.requiresRestart).toBe(true);
expect(enabled.showInDialog).toBe(true);
expect(enabled.description).toBe(
'Enable the Gemma Model Router. Requires a local endpoint serving Gemma via the Gemini API using LiteRT-LM shim.',
);
const classifier = gemmaModelRouter.properties.classifier;
expect(classifier).toBeDefined();
expect(classifier.type).toBe('object');
expect(classifier.category).toBe('Experimental');
expect(classifier.default).toEqual({});
expect(classifier.requiresRestart).toBe(true);
expect(classifier.showInDialog).toBe(false);
expect(classifier.description).toBe('Classifier configuration.');
const host = classifier.properties.host;
expect(host).toBeDefined();
expect(host.type).toBe('string');
expect(host.category).toBe('Experimental');
expect(host.default).toBe('http://localhost:9379');
expect(host.requiresRestart).toBe(true);
expect(host.showInDialog).toBe(false);
expect(host.description).toBe('The host of the classifier.');
const model = classifier.properties.model;
expect(model).toBeDefined();
expect(model.type).toBe('string');
expect(model.category).toBe('Experimental');
expect(model.default).toBe('gemma3-1b-gpu-custom');
expect(model.requiresRestart).toBe(true);
expect(model.showInDialog).toBe(false);
expect(model.description).toBe(
'The model to use for the classifier. Only tested on `gemma3-1b-gpu-custom`.',
);
});
});
it('has JSON schema definitions for every referenced ref', () => {
+57 -2
View File
@@ -1787,6 +1787,57 @@ const SETTINGS_SCHEMA = {
'Enable web fetch behavior that bypasses LLM summarization.',
showInDialog: true,
},
gemmaModelRouter: {
type: 'object',
label: 'Gemma Model Router',
category: 'Experimental',
requiresRestart: true,
default: {},
description: 'Enable Gemma model router (experimental).',
showInDialog: true,
properties: {
enabled: {
type: 'boolean',
label: 'Enable Gemma Model Router',
category: 'Experimental',
requiresRestart: true,
default: false,
description:
'Enable the Gemma Model Router. Requires a local endpoint serving Gemma via the Gemini API using LiteRT-LM shim.',
showInDialog: true,
},
classifier: {
type: 'object',
label: 'Classifier',
category: 'Experimental',
requiresRestart: true,
default: {},
description: 'Classifier configuration.',
showInDialog: false,
properties: {
host: {
type: 'string',
label: 'Host',
category: 'Experimental',
requiresRestart: true,
default: 'http://localhost:9379',
description: 'The host of the classifier.',
showInDialog: false,
},
model: {
type: 'string',
label: 'Model',
category: 'Experimental',
requiresRestart: true,
default: 'gemma3-1b-gpu-custom',
description:
'The model to use for the classifier. Only tested on `gemma3-1b-gpu-custom`.',
showInDialog: false,
},
},
},
},
},
},
},
@@ -2532,7 +2583,9 @@ type InferSettings<T extends SettingsSchema> = {
: T[K]['default']
: T[K]['default'] extends boolean
? boolean
: T[K]['default'];
: T[K]['default'] extends string
? string
: T[K]['default'];
};
type InferMergedSettings<T extends SettingsSchema> = {
@@ -2544,7 +2597,9 @@ type InferMergedSettings<T extends SettingsSchema> = {
: T[K]['default']
: T[K]['default'] extends boolean
? boolean
: T[K]['default'];
: T[K]['default'] extends string
? string
: T[K]['default'];
};
export type Settings = InferSettings<SettingsSchemaType>;