feat(cli): add streamlined gemini gemma local model setup (#25498)

Co-authored-by: Abhijit Balaji <abhijitbalaji@google.com>
Co-authored-by: Samee Zahid <sameez@google.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
Samee Zahid
2026-04-20 16:57:56 -07:00
committed by GitHub
parent 6afc47f81c
commit 1d383a4a8e
31 changed files with 2509 additions and 12 deletions
@@ -61,6 +61,7 @@ import { vimCommand } from '../ui/commands/vimCommand.js';
import { setupGithubCommand } from '../ui/commands/setupGithubCommand.js';
import { terminalSetupCommand } from '../ui/commands/terminalSetupCommand.js';
import { upgradeCommand } from '../ui/commands/upgradeCommand.js';
import { gemmaStatusCommand } from '../ui/commands/gemmaStatusCommand.js';
/**
* Loads the core, hard-coded slash commands that are an integral part
@@ -221,6 +222,7 @@ export class BuiltinCommandLoader implements ICommandLoader {
: [skillsCommand]
: []),
settingsCommand,
gemmaStatusCommand,
tasksCommand,
vimCommand,
setupGithubCommand,
@@ -0,0 +1,68 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import fs from 'node:fs';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { GemmaModelRouterSettings } from '@google/gemini-cli-core';
const mockGetBinaryPath = vi.hoisted(() => vi.fn());
const mockIsServerRunning = vi.hoisted(() => vi.fn());
const mockStartServer = vi.hoisted(() => vi.fn());
vi.mock('../commands/gemma/platform.js', () => ({
getBinaryPath: mockGetBinaryPath,
isServerRunning: mockIsServerRunning,
}));
vi.mock('../commands/gemma/start.js', () => ({
startServer: mockStartServer,
}));
import { LiteRtServerManager } from './liteRtServerManager.js';
describe('LiteRtServerManager', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(fs, 'existsSync').mockReturnValue(true);
mockIsServerRunning.mockResolvedValue(false);
mockStartServer.mockResolvedValue(true);
});
it('uses the configured custom binary path when auto-starting', async () => {
mockGetBinaryPath.mockReturnValue('/user/lit');
const settings: GemmaModelRouterSettings = {
enabled: true,
binaryPath: '/workspace/evil',
classifier: {
host: 'http://localhost:8123',
},
};
await LiteRtServerManager.ensureRunning(settings);
expect(mockGetBinaryPath).toHaveBeenCalledTimes(1);
expect(fs.existsSync).toHaveBeenCalledWith('/user/lit');
expect(mockStartServer).toHaveBeenCalledWith('/user/lit', 8123);
});
it('falls back to the default binary path when no custom path is configured', async () => {
mockGetBinaryPath.mockReturnValue('/default/lit');
const settings: GemmaModelRouterSettings = {
enabled: true,
classifier: {
host: 'http://localhost:9379',
},
};
await LiteRtServerManager.ensureRunning(settings);
expect(mockGetBinaryPath).toHaveBeenCalledTimes(1);
expect(fs.existsSync).toHaveBeenCalledWith('/default/lit');
expect(mockStartServer).toHaveBeenCalledWith('/default/lit', 9379);
});
});
@@ -0,0 +1,59 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import fs from 'node:fs';
import { debugLogger } from '@google/gemini-cli-core';
import type { GemmaModelRouterSettings } from '@google/gemini-cli-core';
import { getBinaryPath, isServerRunning } from '../commands/gemma/platform.js';
import { DEFAULT_PORT } from '../commands/gemma/constants.js';
export class LiteRtServerManager {
static async ensureRunning(
gemmaSettings: GemmaModelRouterSettings | undefined,
): Promise<void> {
if (!gemmaSettings?.enabled) return;
if (gemmaSettings.autoStartServer === false) return;
const binaryPath = getBinaryPath();
if (!binaryPath || !fs.existsSync(binaryPath)) {
debugLogger.log(
'[LiteRtServerManager] Binary not installed, skipping auto-start. Run "gemini gemma setup".',
);
return;
}
const port =
parseInt(
gemmaSettings.classifier?.host?.match(/:(\d+)/)?.[1] ?? '',
10,
) || DEFAULT_PORT;
const running = await isServerRunning(port);
if (running) {
debugLogger.log(
`[LiteRtServerManager] Server already running on port ${port}`,
);
return;
}
debugLogger.log(
`[LiteRtServerManager] Auto-starting LiteRT server on port ${port}...`,
);
try {
const { startServer } = await import('../commands/gemma/start.js');
const started = await startServer(binaryPath, port);
if (started) {
debugLogger.log(`[LiteRtServerManager] Server started on port ${port}`);
} else {
debugLogger.warn(
`[LiteRtServerManager] Server may not have started correctly on port ${port}`,
);
}
} catch (error) {
debugLogger.warn('[LiteRtServerManager] Auto-start failed:', error);
}
}
}