Fix telemetry shutdown noise and improve token usage diagnostics (#444)

Co-authored-by: zuolingxuan <zuolingxuan@bytedance.com>
This commit is contained in:
Lingxuan Zuo
2026-03-13 13:57:49 +08:00
committed by GitHub
Unverified
parent 01adc828b5
commit 995a7f070d
21 changed files with 923 additions and 116 deletions

View File

@@ -12,25 +12,15 @@ import { hostApiFetch } from '@/lib/host-api';
import { trackUiEvent } from '@/lib/telemetry';
import { ProvidersSettings } from '@/components/settings/ProvidersSettings';
import { FeedbackState } from '@/components/common/FeedbackState';
type UsageHistoryEntry = {
timestamp: string;
sessionId: string;
agentId: string;
model?: string;
provider?: string;
content?: string;
inputTokens: number;
outputTokens: number;
cacheReadTokens: number;
cacheWriteTokens: number;
totalTokens: number;
costUsd?: number;
};
type UsageWindow = '7d' | '30d' | 'all';
type UsageGroupBy = 'model' | 'day';
const USAGE_FETCH_MAX_ATTEMPTS = 6;
import {
filterUsageHistoryByWindow,
groupUsageHistory,
type UsageGroupBy,
type UsageHistoryEntry,
type UsageWindow,
} from './usage-history';
const DEFAULT_USAGE_FETCH_MAX_ATTEMPTS = 6;
const WINDOWS_USAGE_FETCH_MAX_ATTEMPTS = 10;
const USAGE_FETCH_RETRY_DELAY_MS = 1500;
export function Models() {
@@ -38,6 +28,9 @@ export function Models() {
const gatewayStatus = useGatewayStore((state) => state.status);
const devModeUnlocked = useSettingsStore((state) => state.devModeUnlocked);
const isGatewayRunning = gatewayStatus.state === 'running';
const usageFetchMaxAttempts = window.electron.platform === 'win32'
? WINDOWS_USAGE_FETCH_MAX_ATTEMPTS
: DEFAULT_USAGE_FETCH_MAX_ATTEMPTS;
const [usageHistory, setUsageHistory] = useState<UsageHistoryEntry[]>([]);
const [usageGroupBy, setUsageGroupBy] = useState<UsageGroupBy>('model');
@@ -87,7 +80,7 @@ export function Models() {
restartMarker,
});
if (normalized.length === 0 && attempt < USAGE_FETCH_MAX_ATTEMPTS) {
if (normalized.length === 0 && attempt < usageFetchMaxAttempts) {
trackUiEvent('models.token_usage_fetch_retry_scheduled', {
generation,
attempt,
@@ -113,7 +106,7 @@ export function Models() {
restartMarker,
message: error instanceof Error ? error.message : String(error),
});
if (attempt < USAGE_FETCH_MAX_ATTEMPTS) {
if (attempt < usageFetchMaxAttempts) {
trackUiEvent('models.token_usage_fetch_retry_scheduled', {
generation,
attempt,
@@ -143,7 +136,7 @@ export function Models() {
usageFetchTimerRef.current = null;
}
};
}, [isGatewayRunning, gatewayStatus.connectedAt, gatewayStatus.pid]);
}, [isGatewayRunning, gatewayStatus.connectedAt, gatewayStatus.pid, usageFetchMaxAttempts]);
const visibleUsageHistory = isGatewayRunning ? usageHistory : [];
const filteredUsageHistory = filterUsageHistoryByWindow(visibleUsageHistory, usageWindow);
@@ -383,84 +376,6 @@ function formatUsageTimestamp(timestamp: string): string {
}).format(date);
}
function groupUsageHistory(
entries: UsageHistoryEntry[],
groupBy: UsageGroupBy,
): Array<{
label: string;
totalTokens: number;
inputTokens: number;
outputTokens: number;
cacheTokens: number;
sortKey: number | string;
}> {
const grouped = new Map<string, {
label: string;
totalTokens: number;
inputTokens: number;
outputTokens: number;
cacheTokens: number;
sortKey: number | string;
}>();
for (const entry of entries) {
const label = groupBy === 'model'
? (entry.model || 'Unknown')
: formatUsageDay(entry.timestamp);
const current = grouped.get(label) ?? {
label,
totalTokens: 0,
inputTokens: 0,
outputTokens: 0,
cacheTokens: 0,
sortKey: groupBy === 'day' ? getUsageDaySortKey(entry.timestamp) : label.toLowerCase(),
};
current.totalTokens += entry.totalTokens;
current.inputTokens += entry.inputTokens;
current.outputTokens += entry.outputTokens;
current.cacheTokens += entry.cacheReadTokens + entry.cacheWriteTokens;
grouped.set(label, current);
}
return Array.from(grouped.values())
.sort((a, b) => {
if (groupBy === 'day') {
return Number(a.sortKey) - Number(b.sortKey);
}
return b.totalTokens - a.totalTokens;
})
.slice(0, 8);
}
function formatUsageDay(timestamp: string): string {
const date = new Date(timestamp);
if (Number.isNaN(date.getTime())) return timestamp;
return new Intl.DateTimeFormat(undefined, {
month: 'short',
day: 'numeric',
}).format(date);
}
function getUsageDaySortKey(timestamp: string): number {
const date = new Date(timestamp);
if (Number.isNaN(date.getTime())) return 0;
date.setHours(0, 0, 0, 0);
return date.getTime();
}
function filterUsageHistoryByWindow(entries: UsageHistoryEntry[], window: UsageWindow): UsageHistoryEntry[] {
if (window === 'all') return entries;
const now = Date.now();
const days = window === '7d' ? 7 : 30;
const cutoff = now - days * 24 * 60 * 60 * 1000;
return entries.filter((entry) => {
const timestamp = Date.parse(entry.timestamp);
return Number.isFinite(timestamp) && timestamp >= cutoff;
});
}
function UsageBarChart({
groups,
emptyLabel,