Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions packages/cli/src/commands/gemma/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { loadSettings } from '../../config/settings.js';
import fs from 'node:fs';
import path from 'node:path';
import { execFileSync } from 'node:child_process';
Expand All @@ -22,6 +23,34 @@ export interface PlatformInfo {
binaryName: string;
}

export interface GemmaConfigStatus {
settingsEnabled: boolean;
configuredPort: number;
}

/**
* Resolves the Gemma configuration from the workspace settings.
*/
export function resolveGemmaConfig(fallbackPort: number): GemmaConfigStatus {
let settingsEnabled = false;
let configuredPort = fallbackPort;
try {
const settings = loadSettings(process.cwd());
const gemmaSettings = settings.merged.experimental?.gemmaModelRouter;
settingsEnabled = gemmaSettings?.enabled === true;
const hostStr = gemmaSettings?.classifier?.host;
if (hostStr) {
const match = hostStr.match(/:(\d+)/);
if (match) {
configuredPort = parseInt(match[1], 10);
}
}
} catch {
// Settings may fail to load in some contexts; treat as not enabled.
}
return { settingsEnabled, configuredPort };
}

/**
* Detects the current platform and resolves the corresponding LiteRT-LM binary name.
* Returns null if the platform is unsupported.
Expand Down
12 changes: 10 additions & 2 deletions packages/cli/src/commands/gemma/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
getBinaryPath,
isBinaryInstalled,
isServerRunning,
resolveGemmaConfig,
} from './platform.js';

/**
Expand Down Expand Up @@ -78,11 +79,18 @@ export const startCommand: CommandModule = {
builder: (yargs) =>
yargs.option('port', {
type: 'number',
default: DEFAULT_PORT,
description: 'Port for the LiteRT server',
}),
handler: async (argv) => {
const port = Number(argv['port']);
let port: number | undefined;
if (argv['port'] !== undefined) {
port = Number(argv['port']);
}

if (!port) {
const { configuredPort } = resolveGemmaConfig(DEFAULT_PORT);
port = configuredPort;
}
Comment thread
Abhijit-2592 marked this conversation as resolved.

if (!isBinaryInstalled()) {
debugLogger.error(
Expand Down
21 changes: 8 additions & 13 deletions packages/cli/src/commands/gemma/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import type { CommandModule } from 'yargs';
import chalk from 'chalk';
import { loadSettings } from '../../config/settings.js';
import { DEFAULT_PORT, GEMMA_MODEL_NAME } from './constants.js';
import {
detectPlatform,
Expand All @@ -16,6 +15,7 @@ import {
isServerRunning,
readServerPid,
isProcessRunning,
resolveGemmaConfig,
} from './platform.js';
import { exitCli } from '../utils.js';

Expand All @@ -38,7 +38,9 @@ export interface GemmaStatusResult {
export async function checkGemmaStatus(
port?: number,
): Promise<GemmaStatusResult> {
const effectivePort = port ?? DEFAULT_PORT;
const { settingsEnabled, configuredPort } = resolveGemmaConfig(DEFAULT_PORT);

const effectivePort = port ?? configuredPort;
Comment thread
Abhijit-2592 marked this conversation as resolved.
const binaryPath = getBinaryPath();
const binaryInstalled = isBinaryInstalled();
const modelDownloaded =
Expand All @@ -47,15 +49,6 @@ export async function checkGemmaStatus(
const pid = readServerPid();
const serverPid = pid && isProcessRunning(pid) ? pid : null;

let settingsEnabled = false;
try {
const settings = loadSettings(process.cwd());
const gemmaSettings = settings.merged.experimental?.gemmaModelRouter;
settingsEnabled = gemmaSettings?.enabled === true;
} catch {
// Settings may fail to load in some contexts; treat as not enabled.
}

const allPassing =
binaryInstalled && modelDownloaded && serverRunning && settingsEnabled;

Expand Down Expand Up @@ -167,11 +160,13 @@ export const statusCommand: CommandModule = {
builder: (yargs) =>
yargs.option('port', {
type: 'number',
default: DEFAULT_PORT,
description: 'Port to check for the LiteRT server',
}),
handler: async (argv) => {
const port = Number(argv['port']);
let port: number | undefined;
if (argv['port'] !== undefined) {
port = Number(argv['port']);
}
const status = await checkGemmaStatus(port);
const output = formatGemmaStatus(status);
// Use process.stdout directly for consistent output in non-interactive mode.
Expand Down
16 changes: 12 additions & 4 deletions packages/cli/src/commands/gemma/stop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
readServerPid,
isProcessRunning,
isServerRunning,
resolveGemmaConfig,
} from './platform.js';

/**
Expand Down Expand Up @@ -66,18 +67,25 @@ export async function stopServer(): Promise<boolean> {

return true;
}

export const stopCommand: CommandModule = {
command: 'stop',
describe: 'Stop the LiteRT-LM server',
builder: (yargs) =>
yargs.option('port', {
type: 'number',
default: DEFAULT_PORT,
description: 'Port the server is running on',
description: 'Port where the LiteRT server is running',
}),
handler: async (argv) => {
const port = Number(argv['port']);
let port: number | undefined;
if (argv['port'] !== undefined) {
port = Number(argv['port']);
}

if (!port) {
const { configuredPort } = resolveGemmaConfig(DEFAULT_PORT);
port = configuredPort;
}

const pid = readServerPid();

if (pid !== null && isProcessRunning(pid)) {
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/core/localLiteRtLmClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LocalLiteRtLmClient } from './localLiteRtLmClient.js';
import type { Config } from '../config/config.js';
import { GoogleGenAI } from '@google/genai';

const mockGenerateContent = vi.fn();

vi.mock('@google/genai', () => {
Expand Down Expand Up @@ -44,6 +46,14 @@ describe('LocalLiteRtLmClient', () => {
const result = await client.generateJson([], 'test-instruction');

expect(result).toEqual({ key: 'value' });
expect(GoogleGenAI).toHaveBeenCalledWith(
expect.objectContaining({
apiVersion: 'v1beta',
httpOptions: expect.objectContaining({
baseUrl: 'http://test-host:1234',
}),
}),
);
expect(mockGenerateContent).toHaveBeenCalledWith(
expect.objectContaining({
model: 'gemma:latest',
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/core/localLiteRtLmClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export class LocalLiteRtLmClient {
this.client = new GoogleGenAI({
// The LiteRT-LM server does not require an API key, but the SDK requires one to be set even for local endpoints. This is a dummy value and is not used for authentication.
apiKey: 'no-api-key-needed',
apiVersion: 'v1beta',
vertexai: false,
httpOptions: {
baseUrl: this.host,
// If the LiteRT-LM server is started but the wrong port is set, there will be a lengthy TCP timeout (here fixed to be 10 seconds).
Expand Down
Loading