mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-02-01 14:44:29 +00:00
add metrics for masking
This commit is contained in:
@@ -32,6 +32,7 @@ describe('ObservationMaskingService', () => {
|
||||
storage: {
|
||||
getHistoryDir: () => '/mock/history',
|
||||
},
|
||||
getUsageStatisticsEnabled: () => false,
|
||||
} as unknown as Config;
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
@@ -10,6 +10,8 @@ import * as fsPromises from 'node:fs/promises';
|
||||
import { estimateTokenCountSync } from '../utils/tokenCalculation.js';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
import { logObservationMasking } from '../telemetry/loggers.js';
|
||||
import { ObservationMaskingEvent } from '../telemetry/types.js';
|
||||
|
||||
export const TOOL_PROTECTION_THRESHOLD = 50_000;
|
||||
export const HYSTERESIS_THRESHOLD = 30_000;
|
||||
@@ -176,11 +178,23 @@ export class ObservationMaskingService {
|
||||
`[ObservationMasking] Masked ${prunableParts.length} tool outputs. Saved ~${actualTokensSaved.toLocaleString()} tokens.`,
|
||||
);
|
||||
|
||||
return {
|
||||
const result = {
|
||||
newHistory,
|
||||
maskedCount: prunableParts.length,
|
||||
tokensSaved: actualTokensSaved,
|
||||
};
|
||||
|
||||
logObservationMasking(
|
||||
config,
|
||||
new ObservationMaskingEvent({
|
||||
tokens_before: totalPrunableTokens,
|
||||
tokens_after: totalPrunableTokens - actualTokensSaved,
|
||||
masked_count: prunableParts.length,
|
||||
total_prunable_tokens: totalPrunableTokens,
|
||||
}),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private getObservationContent(part: Part): string | null {
|
||||
|
||||
@@ -44,6 +44,7 @@ import type {
|
||||
HookCallEvent,
|
||||
ApprovalModeSwitchEvent,
|
||||
ApprovalModeDurationEvent,
|
||||
ObservationMaskingEvent,
|
||||
} from '../types.js';
|
||||
import { EventMetadataKey } from './event-metadata-key.js';
|
||||
import type { Config } from '../../config/config.js';
|
||||
@@ -104,6 +105,7 @@ export enum EventNames {
|
||||
HOOK_CALL = 'hook_call',
|
||||
APPROVAL_MODE_SWITCH = 'approval_mode_switch',
|
||||
APPROVAL_MODE_DURATION = 'approval_mode_duration',
|
||||
OBSERVATION_MASKING = 'observation_masking',
|
||||
}
|
||||
|
||||
export interface LogResponse {
|
||||
@@ -1201,8 +1203,40 @@ export class ClearcutLogger {
|
||||
},
|
||||
];
|
||||
|
||||
const logEvent = this.createLogEvent(
|
||||
EventNames.TOOL_OUTPUT_TRUNCATED,
|
||||
data,
|
||||
);
|
||||
this.enqueueLogEvent(logEvent);
|
||||
this.flushIfNeeded();
|
||||
}
|
||||
|
||||
logObservationMaskingEvent(event: ObservationMaskingEvent): void {
|
||||
const data: EventValue[] = [
|
||||
{
|
||||
gemini_cli_key:
|
||||
EventMetadataKey.GEMINI_CLI_OBSERVATION_MASKING_TOKENS_BEFORE,
|
||||
value: event.tokens_before.toString(),
|
||||
},
|
||||
{
|
||||
gemini_cli_key:
|
||||
EventMetadataKey.GEMINI_CLI_OBSERVATION_MASKING_TOKENS_AFTER,
|
||||
value: event.tokens_after.toString(),
|
||||
},
|
||||
{
|
||||
gemini_cli_key:
|
||||
EventMetadataKey.GEMINI_CLI_OBSERVATION_MASKING_MASKED_COUNT,
|
||||
value: event.masked_count.toString(),
|
||||
},
|
||||
{
|
||||
gemini_cli_key:
|
||||
EventMetadataKey.GEMINI_CLI_OBSERVATION_MASKING_TOTAL_PRUNABLE_TOKENS,
|
||||
value: event.total_prunable_tokens.toString(),
|
||||
},
|
||||
];
|
||||
|
||||
this.enqueueLogEvent(
|
||||
this.createLogEvent(EventNames.TOOL_OUTPUT_TRUNCATED, data),
|
||||
this.createLogEvent(EventNames.OBSERVATION_MASKING, data),
|
||||
);
|
||||
this.flushIfNeeded();
|
||||
}
|
||||
|
||||
@@ -542,4 +542,20 @@ export enum EventMetadataKey {
|
||||
|
||||
// Logs the duration spent in an approval mode in milliseconds.
|
||||
GEMINI_CLI_APPROVAL_MODE_DURATION_MS = 143,
|
||||
|
||||
// ==========================================================================
|
||||
// Observation Masking Event Keys
|
||||
// ==========================================================================
|
||||
|
||||
// Logs the total tokens in the prunable block before masking.
|
||||
GEMINI_CLI_OBSERVATION_MASKING_TOKENS_BEFORE = 144,
|
||||
|
||||
// Logs the total tokens in the masked remnants after masking.
|
||||
GEMINI_CLI_OBSERVATION_MASKING_TOKENS_AFTER = 145,
|
||||
|
||||
// Logs the number of tool outputs masked in this operation.
|
||||
GEMINI_CLI_OBSERVATION_MASKING_MASKED_COUNT = 146,
|
||||
|
||||
// Logs the total prunable tokens identified at the trigger point.
|
||||
GEMINI_CLI_OBSERVATION_MASKING_TOTAL_PRUNABLE_TOKENS = 147,
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ import type {
|
||||
HookCallEvent,
|
||||
StartupStatsEvent,
|
||||
LlmLoopCheckEvent,
|
||||
ObservationMaskingEvent,
|
||||
} from './types.js';
|
||||
import {
|
||||
recordApiErrorMetrics,
|
||||
@@ -159,6 +160,21 @@ export function logToolOutputTruncated(
|
||||
});
|
||||
}
|
||||
|
||||
export function logObservationMasking(
|
||||
config: Config,
|
||||
event: ObservationMaskingEvent,
|
||||
): void {
|
||||
ClearcutLogger.getInstance(config)?.logObservationMaskingEvent(event);
|
||||
bufferTelemetryEvent(() => {
|
||||
const logger = logs.getLogger(SERVICE_NAME);
|
||||
const logRecord: LogRecord = {
|
||||
body: event.toLogBody(),
|
||||
attributes: event.toOpenTelemetryAttributes(config),
|
||||
};
|
||||
logger.emit(logRecord);
|
||||
});
|
||||
}
|
||||
|
||||
export function logFileOperation(
|
||||
config: Config,
|
||||
event: FileOperationEvent,
|
||||
|
||||
@@ -1350,6 +1350,49 @@ export class ToolOutputTruncatedEvent implements BaseTelemetryEvent {
|
||||
}
|
||||
}
|
||||
|
||||
export const EVENT_OBSERVATION_MASKING = 'gemini_cli.observation_masking';
|
||||
|
||||
export class ObservationMaskingEvent implements BaseTelemetryEvent {
|
||||
'event.name': 'observation_masking';
|
||||
'event.timestamp': string;
|
||||
tokens_before: number;
|
||||
tokens_after: number;
|
||||
masked_count: number;
|
||||
total_prunable_tokens: number;
|
||||
|
||||
constructor(details: {
|
||||
tokens_before: number;
|
||||
tokens_after: number;
|
||||
masked_count: number;
|
||||
total_prunable_tokens: number;
|
||||
}) {
|
||||
this['event.name'] = 'observation_masking';
|
||||
this['event.timestamp'] = new Date().toISOString();
|
||||
this.tokens_before = details.tokens_before;
|
||||
this.tokens_after = details.tokens_after;
|
||||
this.masked_count = details.masked_count;
|
||||
this.total_prunable_tokens = details.total_prunable_tokens;
|
||||
}
|
||||
|
||||
toOpenTelemetryAttributes(config: Config): LogAttributes {
|
||||
return {
|
||||
...getCommonAttributes(config),
|
||||
'event.name': EVENT_OBSERVATION_MASKING,
|
||||
'event.timestamp': this['event.timestamp'],
|
||||
tokens_before: this.tokens_before,
|
||||
tokens_after: this.tokens_after,
|
||||
masked_count: this.masked_count,
|
||||
total_prunable_tokens: this.total_prunable_tokens,
|
||||
};
|
||||
}
|
||||
|
||||
toLogBody(): string {
|
||||
return `Observation masking (Masked ${this.masked_count} tool outputs. Saved ${
|
||||
this.tokens_before - this.tokens_after
|
||||
} tokens)`;
|
||||
}
|
||||
}
|
||||
|
||||
export const EVENT_EXTENSION_UNINSTALL = 'gemini_cli.extension_uninstall';
|
||||
export class ExtensionUninstallEvent implements BaseTelemetryEvent {
|
||||
'event.name': 'extension_uninstall';
|
||||
@@ -1576,6 +1619,7 @@ export type TelemetryEvent =
|
||||
| LlmLoopCheckEvent
|
||||
| StartupStatsEvent
|
||||
| WebFetchFallbackAttemptEvent
|
||||
| ObservationMaskingEvent
|
||||
| EditStrategyEvent
|
||||
| EditCorrectionEvent;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user