diff options
| author | Jerop Kipruto <[email protected]> | 2025-06-05 16:04:25 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-06-05 13:04:25 -0700 |
| commit | 2ebf2fbc82ba998f0369f17b56f99d0b25680cb4 (patch) | |
| tree | f2cfb0991e4781f99618d5b2fbff607f52aead81 /packages/cli/src | |
| parent | d3e43437a00cfe64790cc60c9c8aa82c85f520c3 (diff) | |
OpenTelemetry Integration & Telemetry Control Flag (#762)
Diffstat (limited to 'packages/cli/src')
| -rw-r--r-- | packages/cli/src/config/config.test.ts | 67 | ||||
| -rw-r--r-- | packages/cli/src/config/config.ts | 9 | ||||
| -rw-r--r-- | packages/cli/src/config/settings.test.ts | 56 | ||||
| -rw-r--r-- | packages/cli/src/config/settings.ts | 1 | ||||
| -rw-r--r-- | packages/cli/src/ui/hooks/useGeminiStream.ts | 5 | ||||
| -rw-r--r-- | packages/cli/src/ui/hooks/useReactToolScheduler.ts | 25 |
6 files changed, 163 insertions, 0 deletions
diff --git a/packages/cli/src/config/config.test.ts b/packages/cli/src/config/config.test.ts index a39278bc..6356b624 100644 --- a/packages/cli/src/config/config.test.ts +++ b/packages/cli/src/config/config.test.ts @@ -53,6 +53,7 @@ vi.mock('@gemini-code/core', async () => { getGeminiMdFileCount: () => params.geminiMdFileCount, getVertexAI: () => params.vertexai, getShowMemoryUsage: () => params.showMemoryUsage, // Added for the test + getTelemetry: () => params.telemetry, // Add any other methods that are called on the config object setUserMemory: vi.fn(), setGeminiMdFileCount: vi.fn(), @@ -108,6 +109,72 @@ describe('loadCliConfig', () => { }); }); +describe('loadCliConfig telemetry', () => { + const originalArgv = process.argv; + const originalEnv = { ...process.env }; + + beforeEach(() => { + vi.resetAllMocks(); + vi.mocked(os.homedir).mockReturnValue(MOCK_HOME_DIR); + process.env.GEMINI_API_KEY = 'test-api-key'; + }); + + afterEach(() => { + process.argv = originalArgv; + process.env = originalEnv; + vi.restoreAllMocks(); + }); + + it('should set telemetry to false by default when no flag or setting is present', async () => { + process.argv = ['node', 'script.js']; + const settings: Settings = {}; + const result = await loadCliConfig(settings); + expect(result.config.getTelemetry()).toBe(false); + }); + + it('should set telemetry to true when --telemetry flag is present', async () => { + process.argv = ['node', 'script.js', '--telemetry']; + const settings: Settings = {}; + const result = await loadCliConfig(settings); + expect(result.config.getTelemetry()).toBe(true); + }); + + it('should set telemetry to false when --no-telemetry flag is present', async () => { + process.argv = ['node', 'script.js', '--no-telemetry']; + const settings: Settings = {}; + const result = await loadCliConfig(settings); + expect(result.config.getTelemetry()).toBe(false); + }); + + it('should use telemetry value from settings if CLI flag is not present (settings true)', async () => { + process.argv = ['node', 'script.js']; + const settings: Settings = { telemetry: true }; + const result = await loadCliConfig(settings); + expect(result.config.getTelemetry()).toBe(true); + }); + + it('should use telemetry value from settings if CLI flag is not present (settings false)', async () => { + process.argv = ['node', 'script.js']; + const settings: Settings = { telemetry: false }; + const result = await loadCliConfig(settings); + expect(result.config.getTelemetry()).toBe(false); + }); + + it('should prioritize --telemetry CLI flag (true) over settings (false)', async () => { + process.argv = ['node', 'script.js', '--telemetry']; + const settings: Settings = { telemetry: false }; + const result = await loadCliConfig(settings); + expect(result.config.getTelemetry()).toBe(true); + }); + + it('should prioritize --no-telemetry CLI flag (false) over settings (true)', async () => { + process.argv = ['node', 'script.js', '--no-telemetry']; + const settings: Settings = { telemetry: true }; + const result = await loadCliConfig(settings); + expect(result.config.getTelemetry()).toBe(false); + }); +}); + describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => { beforeEach(() => { vi.resetAllMocks(); diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index 387325d7..e363587f 100644 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -45,6 +45,7 @@ interface CliArgs { all_files: boolean | undefined; show_memory_usage: boolean | undefined; yolo: boolean | undefined; + telemetry: boolean | undefined; } async function parseArguments(): Promise<CliArgs> { @@ -89,6 +90,10 @@ async function parseArguments(): Promise<CliArgs> { 'Automatically accept all actions (aka YOLO mode, see https://www.youtube.com/watch?v=xvFZjo5PgG0 for more details)?', default: false, }) + .option('telemetry', { + type: 'boolean', + description: 'Enable telemetry?', + }) .version() // This will enable the --version flag based on package.json .help() .alias('h', 'help') @@ -214,6 +219,10 @@ export async function loadCliConfig( argv.show_memory_usage || settings.showMemoryUsage || false, geminiIgnorePatterns, accessibility: settings.accessibility, + telemetry: + argv.telemetry !== undefined + ? argv.telemetry + : (settings.telemetry ?? false), // Git-aware file filtering settings fileFilteringRespectGitIgnore: settings.fileFiltering?.respectGitIgnore, fileFilteringAllowBuildArtifacts: diff --git a/packages/cli/src/config/settings.test.ts b/packages/cli/src/config/settings.test.ts index 4d61ed8b..c6943dbe 100644 --- a/packages/cli/src/config/settings.test.ts +++ b/packages/cli/src/config/settings.test.ts @@ -243,6 +243,62 @@ describe('Settings Loading and Merging', () => { expect(settings.merged.contextFileName).toBeUndefined(); }); + it('should load telemetry setting from user settings', () => { + (mockFsExistsSync as Mock).mockImplementation( + (p: fs.PathLike) => p === USER_SETTINGS_PATH, + ); + const userSettingsContent = { telemetry: true }; + (fs.readFileSync as Mock).mockImplementation( + (p: fs.PathOrFileDescriptor) => { + if (p === USER_SETTINGS_PATH) + return JSON.stringify(userSettingsContent); + return '{}'; + }, + ); + const settings = loadSettings(MOCK_WORKSPACE_DIR); + expect(settings.merged.telemetry).toBe(true); + }); + + it('should load telemetry setting from workspace settings', () => { + (mockFsExistsSync as Mock).mockImplementation( + (p: fs.PathLike) => p === MOCK_WORKSPACE_SETTINGS_PATH, + ); + const workspaceSettingsContent = { telemetry: false }; + (fs.readFileSync as Mock).mockImplementation( + (p: fs.PathOrFileDescriptor) => { + if (p === MOCK_WORKSPACE_SETTINGS_PATH) + return JSON.stringify(workspaceSettingsContent); + return '{}'; + }, + ); + const settings = loadSettings(MOCK_WORKSPACE_DIR); + expect(settings.merged.telemetry).toBe(false); + }); + + it('should prioritize workspace telemetry setting over user setting', () => { + (mockFsExistsSync as Mock).mockReturnValue(true); + const userSettingsContent = { telemetry: true }; + const workspaceSettingsContent = { telemetry: false }; + (fs.readFileSync as Mock).mockImplementation( + (p: fs.PathOrFileDescriptor) => { + if (p === USER_SETTINGS_PATH) + return JSON.stringify(userSettingsContent); + if (p === MOCK_WORKSPACE_SETTINGS_PATH) + return JSON.stringify(workspaceSettingsContent); + return '{}'; + }, + ); + const settings = loadSettings(MOCK_WORKSPACE_DIR); + expect(settings.merged.telemetry).toBe(false); + }); + + it('should have telemetry as undefined if not in any settings file', () => { + (mockFsExistsSync as Mock).mockReturnValue(false); // No settings files exist + (fs.readFileSync as Mock).mockReturnValue('{}'); + const settings = loadSettings(MOCK_WORKSPACE_DIR); + expect(settings.merged.telemetry).toBeUndefined(); + }); + it('should handle JSON parsing errors gracefully', () => { (mockFsExistsSync as Mock).mockReturnValue(true); (fs.readFileSync as Mock).mockImplementation( diff --git a/packages/cli/src/config/settings.ts b/packages/cli/src/config/settings.ts index f0993ad7..db5dabb6 100644 --- a/packages/cli/src/config/settings.ts +++ b/packages/cli/src/config/settings.ts @@ -36,6 +36,7 @@ export interface Settings { showMemoryUsage?: boolean; contextFileName?: string; accessibility?: AccessibilitySettings; + telemetry?: boolean; // Git-aware file filtering settings fileFiltering?: { diff --git a/packages/cli/src/ui/hooks/useGeminiStream.ts b/packages/cli/src/ui/hooks/useGeminiStream.ts index 64e39e68..0b42c161 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.ts +++ b/packages/cli/src/ui/hooks/useGeminiStream.ts @@ -17,6 +17,7 @@ import { Config, MessageSenderType, ToolCallRequestInfo, + logUserPrompt, } from '@gemini-code/core'; import { type PartListUnion } from '@google/genai'; import { @@ -178,6 +179,10 @@ export const useGeminiStream = ( if (typeof query === 'string') { const trimmedQuery = query.trim(); + logUserPrompt(config, { + prompt: trimmedQuery, + prompt_char_count: trimmedQuery.length, + }); onDebugMessage(`User query: '${trimmedQuery}'`); await logger?.logMessage(MessageSenderType.USER, trimmedQuery); diff --git a/packages/cli/src/ui/hooks/useReactToolScheduler.ts b/packages/cli/src/ui/hooks/useReactToolScheduler.ts index e681e972..8e027ade 100644 --- a/packages/cli/src/ui/hooks/useReactToolScheduler.ts +++ b/packages/cli/src/ui/hooks/useReactToolScheduler.ts @@ -20,6 +20,7 @@ import { Tool, ToolCall, Status as CoreStatus, + logToolCall, } from '@gemini-code/core'; import { useCallback, useEffect, useState, useRef } from 'react'; import { @@ -108,6 +109,30 @@ export function useReactToolScheduler( const allToolCallsCompleteHandler: AllToolCallsCompleteHandler = ( completedToolCalls, ) => { + completedToolCalls.forEach((call) => { + let success = false; + let errorMessage: string | undefined; + let duration = 0; + + if (call.status === 'success') { + success = true; + } + if ( + call.status === 'error' && + typeof call.response.resultDisplay === 'string' + ) { + errorMessage = call.response.resultDisplay; + } + duration = call.durationMs || 0; + + logToolCall({ + function_name: call.request.name, + function_args: call.request.args, + duration_ms: duration, + success, + error: errorMessage, + }); + }); onComplete(completedToolCalls); }; |
