feat: Add User-Agent to API requests

This change introduces a User-Agent header to all API requests made by the Gemini CLI.

The User-Agent string includes the CLI version, Node.js version, operating system, and architecture. This will help in tracking usage and identifying potential issues.

Fixes https://b.corp.google.com/issues/416353675

Signed-off-by: Gemini
This commit is contained in:
Taylor Mullen 2025-05-11 14:28:21 -07:00 committed by N. Taylor Mullen
parent 2970f0a06c
commit 8537aabba4
3 changed files with 26 additions and 1 deletions

View File

@ -13,6 +13,7 @@ import {
createServerConfig, createServerConfig,
} from '@gemini-code/server'; } from '@gemini-code/server';
import { Settings } from './settings.js'; import { Settings } from './settings.js';
import { readPackageUp } from 'read-package-up';
const DEFAULT_GEMINI_MODEL = 'gemini-2.5-pro-preview-05-06'; const DEFAULT_GEMINI_MODEL = 'gemini-2.5-pro-preview-05-06';
@ -80,6 +81,8 @@ export async function loadCliConfig(settings: Settings): Promise<Config> {
// Parse CLI arguments // Parse CLI arguments
const argv = await parseArguments(); const argv = await parseArguments();
const userAgent = await createUserAgent();
// Create config using factory from server package // Create config using factory from server package
return createServerConfig( return createServerConfig(
process.env.GEMINI_API_KEY, process.env.GEMINI_API_KEY,
@ -92,5 +95,12 @@ export async function loadCliConfig(settings: Settings): Promise<Config> {
settings.toolDiscoveryCommand, settings.toolDiscoveryCommand,
settings.toolCallCommand, settings.toolCallCommand,
settings.mcpServerCommand, settings.mcpServerCommand,
userAgent,
); );
} }
async function createUserAgent(): Promise<string> {
const packageJsonInfo = await readPackageUp({ cwd: import.meta.url });
const cliVersion = packageJsonInfo?.packageJson.version || 'unknown';
return `GeminiCLI/${cliVersion} Node.js/${process.version} (${process.platform}; ${process.arch})`;
}

View File

@ -34,6 +34,7 @@ export class Config {
private readonly toolDiscoveryCommand: string | undefined, private readonly toolDiscoveryCommand: string | undefined,
private readonly toolCallCommand: string | undefined, private readonly toolCallCommand: string | undefined,
private readonly mcpServerCommand: string | undefined, private readonly mcpServerCommand: string | undefined,
private readonly userAgent: string,
) { ) {
// toolRegistry still needs initialization based on the instance // toolRegistry still needs initialization based on the instance
this.toolRegistry = createToolRegistry(this); this.toolRegistry = createToolRegistry(this);
@ -81,6 +82,10 @@ export class Config {
getMcpServerCommand(): string | undefined { getMcpServerCommand(): string | undefined {
return this.mcpServerCommand; return this.mcpServerCommand;
} }
getUserAgent(): string {
return this.userAgent;
}
} }
function findEnvFile(startDir: string): string | null { function findEnvFile(startDir: string): string | null {
@ -117,6 +122,7 @@ export function createServerConfig(
toolDiscoveryCommand?: string, toolDiscoveryCommand?: string,
toolCallCommand?: string, toolCallCommand?: string,
mcpServerCommand?: string, mcpServerCommand?: string,
userAgent?: string,
): Config { ): Config {
return new Config( return new Config(
apiKey, apiKey,
@ -129,6 +135,7 @@ export function createServerConfig(
toolDiscoveryCommand, toolDiscoveryCommand,
toolCallCommand, toolCallCommand,
mcpServerCommand, mcpServerCommand,
userAgent ?? 'GeminiCLI/unknown', // Default user agent
); );
} }

View File

@ -34,7 +34,15 @@ export class GeminiClient {
private readonly MAX_TURNS = 100; private readonly MAX_TURNS = 100;
constructor(private config: Config) { constructor(private config: Config) {
this.client = new GoogleGenAI({ apiKey: config.getApiKey() }); const userAgent = config.getUserAgent();
this.client = new GoogleGenAI({
apiKey: config.getApiKey(),
httpOptions: {
headers: {
'User-Agent': userAgent,
},
},
});
this.model = config.getModel(); this.model = config.getModel();
} }