From 67008d4643e331a4b9181d12927c82f08fe58597 Mon Sep 17 00:00:00 2001 From: Sandy Tao Date: Tue, 22 Jul 2025 17:31:57 -0700 Subject: [PATCH] Log when flash model decided to continue (#4698) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- packages/core/src/core/client.test.ts | 1 + packages/core/src/core/client.ts | 5 +++++ .../clearcut-logger/clearcut-logger.ts | 20 +++++++++++++++++++ packages/core/src/telemetry/types.ts | 15 +++++++++++++- 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/core/src/core/client.test.ts b/packages/core/src/core/client.test.ts index 2d75637c..44b19f56 100644 --- a/packages/core/src/core/client.test.ts +++ b/packages/core/src/core/client.test.ts @@ -198,6 +198,7 @@ describe('Gemini Client (client.ts)', () => { getQuotaErrorOccurred: vi.fn().mockReturnValue(false), setQuotaErrorOccurred: vi.fn(), getNoBrowser: vi.fn().mockReturnValue(false), + getUsageStatisticsEnabled: vi.fn().mockReturnValue(true), getIdeMode: vi.fn().mockReturnValue(false), getGeminiClient: vi.fn(), }; diff --git a/packages/core/src/core/client.ts b/packages/core/src/core/client.ts index 340b3dae..d8507ffb 100644 --- a/packages/core/src/core/client.ts +++ b/packages/core/src/core/client.ts @@ -43,6 +43,8 @@ import { ProxyAgent, setGlobalDispatcher } from 'undici'; import { DEFAULT_GEMINI_FLASH_MODEL } from '../config/models.js'; import { LoopDetectionService } from '../services/loopDetectionService.js'; import { ideContext } from '../services/ideContext.js'; +import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js'; +import { FlashDecidedToContinueEvent } from '../telemetry/types.js'; function isThinkingSupported(model: string) { if (model.startsWith('gemini-2.5')) return true; @@ -386,6 +388,9 @@ export class GeminiClient { signal, ); if (nextSpeakerCheck?.next_speaker === 'model') { + ClearcutLogger.getInstance(this.config)?.logFlashDecidedToContinueEvent( + new FlashDecidedToContinueEvent(prompt_id), + ); const nextRequest = [{ text: 'Please continue.' }]; // This recursive call's events will be yielded out, but the final // turn object will be from the top-level call. diff --git a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts index 5addd99e..ba47e7a0 100644 --- a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts +++ b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts @@ -18,6 +18,7 @@ import { ApiErrorEvent, FlashFallbackEvent, LoopDetectedEvent, + FlashDecidedToContinueEvent, } from '../types.js'; import { EventMetadataKey } from './event-metadata-key.js'; import { Config } from '../../config/config.js'; @@ -37,6 +38,7 @@ const api_error_event_name = 'api_error'; const end_session_event_name = 'end_session'; const flash_fallback_event_name = 'flash_fallback'; const loop_detected_event_name = 'loop_detected'; +const flash_decided_to_continue_event_name = 'flash_decided_to_continue'; export interface LogResponse { nextRequestWaitMs?: number; @@ -492,6 +494,24 @@ export class ClearcutLogger { this.flushIfNeeded(); } + logFlashDecidedToContinueEvent(event: FlashDecidedToContinueEvent): void { + const data = [ + { + gemini_cli_key: EventMetadataKey.GEMINI_CLI_PROMPT_ID, + value: JSON.stringify(event.prompt_id), + }, + { + gemini_cli_key: EventMetadataKey.GEMINI_CLI_SESSION_ID, + value: this.config?.getSessionId() ?? '', + }, + ]; + + this.enqueueLogEvent( + this.createLogEvent(flash_decided_to_continue_event_name, data), + ); + this.flushIfNeeded(); + } + logEndSessionEvent(event: EndSessionEvent): void { const data = [ { diff --git a/packages/core/src/telemetry/types.ts b/packages/core/src/telemetry/types.ts index 9420a29e..268457b5 100644 --- a/packages/core/src/telemetry/types.ts +++ b/packages/core/src/telemetry/types.ts @@ -264,6 +264,18 @@ export class LoopDetectedEvent { } } +export class FlashDecidedToContinueEvent { + 'event.name': 'flash_decided_to_continue'; + 'event.timestamp': string; // ISO 8601 + prompt_id: string; + + constructor(prompt_id: string) { + this['event.name'] = 'flash_decided_to_continue'; + this['event.timestamp'] = new Date().toISOString(); + this.prompt_id = prompt_id; + } +} + export type TelemetryEvent = | StartSessionEvent | EndSessionEvent @@ -273,4 +285,5 @@ export type TelemetryEvent = | ApiErrorEvent | ApiResponseEvent | FlashFallbackEvent - | LoopDetectedEvent; + | LoopDetectedEvent + | FlashDecidedToContinueEvent;