feat(core): Add support for listing experiments (#12495)

This commit is contained in:
Shreya Keshive
2025-11-03 13:51:22 -08:00
committed by GitHub
parent 1c044ba8af
commit be1dc13bb1
7 changed files with 273 additions and 5 deletions

View File

@@ -0,0 +1,55 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { getReleaseChannel } from '../../utils/channel.js';
import type { ClientMetadata, Platform } from './types.js';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Cache all client metadata.
let clientMetadataPromise: Promise<ClientMetadata> | undefined;
function getPlatform(): Platform {
const platform = process.platform;
const arch = process.arch;
if (platform === 'darwin' && arch === 'x64') {
return 'DARWIN_AMD64';
}
if (platform === 'darwin' && arch === 'arm64') {
return 'DARWIN_ARM64';
}
if (platform === 'linux' && arch === 'x64') {
return 'LINUX_AMD64';
}
if (platform === 'linux' && arch === 'arm64') {
return 'LINUX_ARM64';
}
if (platform === 'win32' && arch === 'x64') {
return 'WINDOWS_AMD64';
}
return 'PLATFORM_UNSPECIFIED';
}
/**
* Returns the client metadata.
*
* The client metadata is cached so that it is only computed once per session.
*/
export async function getClientMetadata(): Promise<ClientMetadata> {
if (!clientMetadataPromise) {
clientMetadataPromise = (async () => ({
ide_type: 'GEMINI_CLI',
ide_version: process.env['CLI_VERSION'] || process.version,
platform: getPlatform(),
update_channel: await getReleaseChannel(__dirname),
}))();
}
return await clientMetadataPromise;
}

View File

@@ -0,0 +1,49 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type { CodeAssistServer } from '../server.js';
import { getClientMetadata } from './client_metadata.js';
import type { ListExperimentsResponse, Flag } from './types.js';
export interface Experiments {
flags: Record<string, Flag>;
experimentIds: number[];
}
let experimentsPromise: Promise<Experiments> | undefined;
/**
* Gets the experiments from the server.
*
* The experiments are cached so that they are only fetched once.
*/
export async function getExperiments(
server: CodeAssistServer,
): Promise<Experiments> {
if (experimentsPromise) {
return await experimentsPromise;
}
experimentsPromise = (async () => {
const metadata = await getClientMetadata();
const response = await server.listExperiments(metadata);
return parseExperiments(response);
})();
return await experimentsPromise;
}
function parseExperiments(response: ListExperimentsResponse): Experiments {
const flags: Record<string, Flag> = {};
for (const flag of response.flags ?? []) {
if (flag.name) {
flags[flag.name] = flag;
}
}
return {
flags,
experimentIds: response.experiment_ids ?? [],
};
}

View File

@@ -0,0 +1,58 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
export interface ListExperimentsRequest {
project: string;
metadata?: ClientMetadata;
}
export interface ListExperimentsResponse {
experiment_ids?: number[];
flags?: Flag[];
filtered_flags?: FilteredFlag[];
debug_string?: string;
}
export interface Flag {
name?: string;
bool_value?: boolean;
float_value?: number;
int_value?: string; // int64
string_value?: string;
int32_list_value?: Int32List;
string_list_value?: StringList;
}
export interface Int32List {
values?: number[];
}
export interface StringList {
values?: string[];
}
export interface FilteredFlag {
name?: string;
reason?: string;
}
export interface ClientMetadata {
ide_type?: IdeType;
ide_version?: string;
platform?: Platform;
update_channel?: 'nightly' | 'preview' | 'stable';
duet_project?: string;
}
export type IdeType = 'GEMINI_CLI';
export type Platform =
| 'PLATFORM_UNSPECIFIED'
| 'DARWIN_AMD64'
| 'DARWIN_ARM64'
| 'LINUX_AMD64'
| 'LINUX_ARM64'
| 'WINDOWS_AMD64';