diff options
| author | Sandy Tao <[email protected]> | 2025-07-14 21:44:07 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-07-15 04:44:07 +0000 |
| commit | 886faa2990b191a0dbd01dc58a88e09e342886b5 (patch) | |
| tree | b22e1c3b95efe0678b86bfa1d959f17c9032b28e /packages/core/src/telemetry | |
| parent | 734da8b9d24ab7e2cfcfe80e43f061734b7ae6ba (diff) | |
Log the 2 types of loop detection (#4193)
Diffstat (limited to 'packages/core/src/telemetry')
4 files changed, 61 insertions, 1 deletions
diff --git a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts index 07c40c86..7beacb9b 100644 --- a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts +++ b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts @@ -15,6 +15,7 @@ import { ApiResponseEvent, ApiErrorEvent, FlashFallbackEvent, + LoopDetectedEvent, } from '../types.js'; import { EventMetadataKey } from './event-metadata-key.js'; import { Config } from '../../config/config.js'; @@ -33,6 +34,7 @@ const api_response_event_name = 'api_response'; 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'; export interface LogResponse { nextRequestWaitMs?: number; @@ -448,6 +450,18 @@ export class ClearcutLogger { }); } + logLoopDetectedEvent(event: LoopDetectedEvent): void { + const data = [ + { + gemini_cli_key: EventMetadataKey.GEMINI_CLI_LOOP_DETECTED_TYPE, + value: JSON.stringify(event.loop_type), + }, + ]; + + this.enqueueLogEvent(this.createLogEvent(loop_detected_event_name, data)); + this.flushIfNeeded(); + } + logEndSessionEvent(event: EndSessionEvent): void { const data = [ { diff --git a/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts b/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts index 8e9d53f5..4a47488a 100644 --- a/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts +++ b/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts @@ -150,6 +150,13 @@ export enum EventMetadataKey { // Logs the total number of Google accounts ever used. GEMINI_CLI_GOOGLE_ACCOUNTS_COUNT = 37, + + // ========================================================================== + // Loop Detected Event Keys + // =========================================================================== + + // Logs the type of loop detected. + GEMINI_CLI_LOOP_DETECTED_TYPE = 38, } export function getEventMetadataKey( diff --git a/packages/core/src/telemetry/loggers.ts b/packages/core/src/telemetry/loggers.ts index 3cf3794b..25a35b5d 100644 --- a/packages/core/src/telemetry/loggers.ts +++ b/packages/core/src/telemetry/loggers.ts @@ -25,6 +25,7 @@ import { ToolCallEvent, UserPromptEvent, FlashFallbackEvent, + LoopDetectedEvent, } from './types.js'; import { recordApiErrorMetrics, @@ -288,3 +289,23 @@ export function logApiResponse(config: Config, event: ApiResponseEvent): void { ); recordTokenUsageMetrics(config, event.model, event.tool_token_count, 'tool'); } + +export function logLoopDetected( + config: Config, + event: LoopDetectedEvent, +): void { + ClearcutLogger.getInstance(config)?.logLoopDetectedEvent(event); + if (!isTelemetrySdkInitialized()) return; + + const attributes: LogAttributes = { + ...getCommonAttributes(config), + ...event, + }; + + const logger = logs.getLogger(SERVICE_NAME); + const logRecord: LogRecord = { + body: `Loop detected. Type: ${event.loop_type}.`, + attributes, + }; + logger.emit(logRecord); +} diff --git a/packages/core/src/telemetry/types.ts b/packages/core/src/telemetry/types.ts index d04756e2..54da0214 100644 --- a/packages/core/src/telemetry/types.ts +++ b/packages/core/src/telemetry/types.ts @@ -246,6 +246,23 @@ export class FlashFallbackEvent { } } +export enum LoopType { + CONSECUTIVE_IDENTICAL_TOOL_CALLS = 'consecutive_identical_tool_calls', + CHANTING_IDENTICAL_SENTENCES = 'chanting_identical_sentences', +} + +export class LoopDetectedEvent { + 'event.name': 'loop_detected'; + 'event.timestamp': string; // ISO 8601 + loop_type: LoopType; + + constructor(loop_type: LoopType) { + this['event.name'] = 'loop_detected'; + this['event.timestamp'] = new Date().toISOString(); + this.loop_type = loop_type; + } +} + export type TelemetryEvent = | StartSessionEvent | EndSessionEvent @@ -254,4 +271,5 @@ export type TelemetryEvent = | ApiRequestEvent | ApiErrorEvent | ApiResponseEvent - | FlashFallbackEvent; + | FlashFallbackEvent + | LoopDetectedEvent; |
