refactor(ui): Optimize rendering performance (#8239)

This commit is contained in:
Gal Zahavi
2025-09-17 15:37:13 -07:00
committed by GitHub
parent d54cdd8802
commit 6756a8b8a9
13 changed files with 499 additions and 85 deletions
+134 -7
View File
@@ -14,10 +14,129 @@ import {
useEffect,
} from 'react';
import type { SessionMetrics, ModelMetrics } from '@google/gemini-cli-core';
import type {
SessionMetrics,
ModelMetrics,
ToolCallStats,
} from '@google/gemini-cli-core';
import { uiTelemetryService, sessionId } from '@google/gemini-cli-core';
// --- Interface Definitions ---
export enum ToolCallDecision {
ACCEPT = 'accept',
REJECT = 'reject',
MODIFY = 'modify',
AUTO_ACCEPT = 'auto_accept',
}
function areModelMetricsEqual(a: ModelMetrics, b: ModelMetrics): boolean {
if (
a.api.totalRequests !== b.api.totalRequests ||
a.api.totalErrors !== b.api.totalErrors ||
a.api.totalLatencyMs !== b.api.totalLatencyMs
) {
return false;
}
if (
a.tokens.prompt !== b.tokens.prompt ||
a.tokens.candidates !== b.tokens.candidates ||
a.tokens.total !== b.tokens.total ||
a.tokens.cached !== b.tokens.cached ||
a.tokens.thoughts !== b.tokens.thoughts ||
a.tokens.tool !== b.tokens.tool
) {
return false;
}
return true;
}
function areToolCallStatsEqual(a: ToolCallStats, b: ToolCallStats): boolean {
if (
a.count !== b.count ||
a.success !== b.success ||
a.fail !== b.fail ||
a.durationMs !== b.durationMs
) {
return false;
}
if (
a.decisions[ToolCallDecision.ACCEPT] !==
b.decisions[ToolCallDecision.ACCEPT] ||
a.decisions[ToolCallDecision.REJECT] !==
b.decisions[ToolCallDecision.REJECT] ||
a.decisions[ToolCallDecision.MODIFY] !==
b.decisions[ToolCallDecision.MODIFY] ||
a.decisions[ToolCallDecision.AUTO_ACCEPT] !==
b.decisions[ToolCallDecision.AUTO_ACCEPT]
) {
return false;
}
return true;
}
function areMetricsEqual(a: SessionMetrics, b: SessionMetrics): boolean {
if (a === b) return true;
if (!a || !b) return false;
// Compare files
if (
a.files.totalLinesAdded !== b.files.totalLinesAdded ||
a.files.totalLinesRemoved !== b.files.totalLinesRemoved
) {
return false;
}
// Compare tools
const toolsA = a.tools;
const toolsB = b.tools;
if (
toolsA.totalCalls !== toolsB.totalCalls ||
toolsA.totalSuccess !== toolsB.totalSuccess ||
toolsA.totalFail !== toolsB.totalFail ||
toolsA.totalDurationMs !== toolsB.totalDurationMs
) {
return false;
}
// Compare tool decisions
if (
toolsA.totalDecisions[ToolCallDecision.ACCEPT] !==
toolsB.totalDecisions[ToolCallDecision.ACCEPT] ||
toolsA.totalDecisions[ToolCallDecision.REJECT] !==
toolsB.totalDecisions[ToolCallDecision.REJECT] ||
toolsA.totalDecisions[ToolCallDecision.MODIFY] !==
toolsB.totalDecisions[ToolCallDecision.MODIFY] ||
toolsA.totalDecisions[ToolCallDecision.AUTO_ACCEPT] !==
toolsB.totalDecisions[ToolCallDecision.AUTO_ACCEPT]
) {
return false;
}
// Compare tools.byName
const toolsByNameAKeys = Object.keys(toolsA.byName);
const toolsByNameBKeys = Object.keys(toolsB.byName);
if (toolsByNameAKeys.length !== toolsByNameBKeys.length) return false;
for (const key of toolsByNameAKeys) {
const toolA = toolsA.byName[key];
const toolB = toolsB.byName[key];
if (!toolB || !areToolCallStatsEqual(toolA, toolB)) {
return false;
}
}
// Compare models
const modelsAKeys = Object.keys(a.models);
const modelsBKeys = Object.keys(b.models);
if (modelsAKeys.length !== modelsBKeys.length) return false;
for (const key of modelsAKeys) {
if (!b.models[key] || !areModelMetricsEqual(a.models[key], b.models[key])) {
return false;
}
}
return true;
}
export type { SessionMetrics, ModelMetrics };
@@ -80,11 +199,19 @@ export const SessionStatsProvider: React.FC<{ children: React.ReactNode }> = ({
metrics: SessionMetrics;
lastPromptTokenCount: number;
}) => {
setStats((prevState) => ({
...prevState,
metrics,
lastPromptTokenCount,
}));
setStats((prevState) => {
if (
prevState.lastPromptTokenCount === lastPromptTokenCount &&
areMetricsEqual(prevState.metrics, metrics)
) {
return prevState;
}
return {
...prevState,
metrics,
lastPromptTokenCount,
};
});
};
uiTelemetryService.on('update', handleUpdate);