feat(ui): add flicker detection and metrics (#10821)

This commit is contained in:
Shreya Keshive
2025-10-10 13:18:38 -07:00
committed by GitHub
parent ab3804d823
commit ae48e964f0
13 changed files with 297 additions and 39 deletions
+1
View File
@@ -115,6 +115,7 @@ export {
recordPerformanceRegression,
recordBaselineComparison,
isPerformanceMonitoringActive,
recordFlickerFrame,
// Performance monitoring types
PerformanceMetricType,
MemoryMetricType,
@@ -91,6 +91,7 @@ describe('Telemetry Metrics', () => {
let recordBaselineComparisonModule: typeof import('./metrics.js').recordBaselineComparison;
let recordGenAiClientTokenUsageModule: typeof import('./metrics.js').recordGenAiClientTokenUsage;
let recordGenAiClientOperationDurationModule: typeof import('./metrics.js').recordGenAiClientOperationDuration;
let recordFlickerFrameModule: typeof import('./metrics.js').recordFlickerFrame;
let recordAgentRunMetricsModule: typeof import('./metrics.js').recordAgentRunMetrics;
beforeEach(async () => {
@@ -131,6 +132,7 @@ describe('Telemetry Metrics', () => {
metricsJsModule.recordGenAiClientTokenUsage;
recordGenAiClientOperationDurationModule =
metricsJsModule.recordGenAiClientOperationDuration;
recordFlickerFrameModule = metricsJsModule.recordFlickerFrame;
recordAgentRunMetricsModule = metricsJsModule.recordAgentRunMetrics;
const otelApiModule = await import('@opentelemetry/api');
@@ -146,6 +148,28 @@ describe('Telemetry Metrics', () => {
mockCreateHistogramFn.mockReturnValue(mockHistogramInstance);
});
describe('recordFlickerFrame', () => {
it('does not record metrics if not initialized', () => {
const config = makeFakeConfig({});
recordFlickerFrameModule(config);
expect(mockCounterAddFn).not.toHaveBeenCalled();
});
it('records a flicker frame event when initialized', () => {
const config = makeFakeConfig({});
initializeMetricsModule(config);
recordFlickerFrameModule(config);
// Called for session, then for flicker
expect(mockCounterAddFn).toHaveBeenCalledTimes(2);
expect(mockCounterAddFn).toHaveBeenNthCalledWith(2, 1, {
'session.id': 'test-session-id',
'installation.id': 'test-installation-id',
'user.email': 'test@example.com',
});
});
});
describe('initializeMetrics', () => {
const mockConfig = {
getSessionId: () => 'test-session-id',
+17
View File
@@ -55,6 +55,7 @@ const REGRESSION_DETECTION = 'gemini_cli.performance.regression';
const REGRESSION_PERCENTAGE_CHANGE =
'gemini_cli.performance.regression.percentage_change';
const BASELINE_COMPARISON = 'gemini_cli.performance.baseline.comparison';
const FLICKER_FRAME_COUNT = 'gemini_cli.ui.flicker.count';
const baseMetricDefinition = {
getCommonAttributes,
@@ -167,6 +168,13 @@ const COUNTER_DEFINITIONS = {
terminate_reason: string;
},
},
[FLICKER_FRAME_COUNT]: {
description:
'Counts UI frames that flicker (render taller than the terminal).',
valueType: ValueType.INT,
assign: (c: Counter) => (flickerFrameCounter = c),
attributes: {} as Record<string, never>,
},
} as const;
const HISTOGRAM_DEFINITIONS = {
@@ -449,6 +457,7 @@ let modelSlashCommandCallCounter: Counter | undefined;
let agentRunCounter: Counter | undefined;
let agentDurationHistogram: Histogram | undefined;
let agentTurnsHistogram: Histogram | undefined;
let flickerFrameCounter: Counter | undefined;
// OpenTelemetry GenAI Semantic Convention Metrics
let genAiClientTokenUsageHistogram: Histogram | undefined;
@@ -606,6 +615,14 @@ export function recordFileOperationMetric(
// --- New Metric Recording Functions ---
/**
* Records a metric for when a UI frame flickers.
*/
export function recordFlickerFrame(config: Config): void {
if (!flickerFrameCounter || !isMetricsInitialized) return;
flickerFrameCounter.add(1, baseMetricDefinition.getCommonAttributes(config));
}
/**
* Records a metric for when an invalid chunk is received from a stream.
*/