From f9164b3d155d4b3e1c09a426b81fc9800fbd8342 Mon Sep 17 00:00:00 2001 From: paisley <8197966+su8su@users.noreply.github.com> Date: Wed, 18 Mar 2026 18:39:21 +0800 Subject: [PATCH] fix token cost (#579) --- src/pages/Models/index.tsx | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/pages/Models/index.tsx b/src/pages/Models/index.tsx index a57291d5c..473364fad 100644 --- a/src/pages/Models/index.tsx +++ b/src/pages/Models/index.tsx @@ -37,9 +37,18 @@ export function Models() { const [usageWindow, setUsageWindow] = useState('7d'); const [usagePage, setUsagePage] = useState(1); const [selectedUsageEntry, setSelectedUsageEntry] = useState(null); + const [usageFetchDoneKey, setUsageFetchDoneKey] = useState(null); const usageFetchTimerRef = useRef | null>(null); const usageFetchGenerationRef = useRef(0); + // Stable key derived from the effect's dependencies — changes whenever a new + // fetch cycle should start. Comparing this to `usageFetchDoneKey` lets us + // derive the loading state without calling setState in the effect body or + // reading refs during render. + const usageFetchKey = isGatewayRunning + ? `${gatewayStatus.pid ?? 'na'}:${gatewayStatus.connectedAt ?? 'na'}:${usageFetchMaxAttempts}` + : null; + useEffect(() => { trackUiEvent('models.page_viewed'); }, []); @@ -50,8 +59,11 @@ export function Models() { usageFetchTimerRef.current = null; } - if (!isGatewayRunning) return; + if (!isGatewayRunning) { + return; + } + const fetchKey = `${gatewayStatus.pid ?? 'na'}:${gatewayStatus.connectedAt ?? 'na'}:${usageFetchMaxAttempts}`; const generation = usageFetchGenerationRef.current + 1; usageFetchGenerationRef.current = generation; const restartMarker = `${gatewayStatus.pid ?? 'na'}:${gatewayStatus.connectedAt ?? 'na'}`; @@ -90,13 +102,16 @@ export function Models() { usageFetchTimerRef.current = setTimeout(() => { void fetchUsageHistoryWithRetry(attempt + 1); }, USAGE_FETCH_RETRY_DELAY_MS); - } else if (normalized.length === 0) { - trackUiEvent('models.token_usage_fetch_exhausted', { - generation, - attempt, - reason: 'empty', - restartMarker, - }); + } else { + if (normalized.length === 0) { + trackUiEvent('models.token_usage_fetch_exhausted', { + generation, + attempt, + reason: 'empty', + restartMarker, + }); + } + setUsageFetchDoneKey(fetchKey); } } catch (error) { if (usageFetchGenerationRef.current !== generation) return; @@ -119,6 +134,7 @@ export function Models() { return; } setUsageHistory([]); + setUsageFetchDoneKey(fetchKey); trackUiEvent('models.token_usage_fetch_exhausted', { generation, attempt, @@ -145,7 +161,7 @@ export function Models() { const usageTotalPages = Math.max(1, Math.ceil(filteredUsageHistory.length / usagePageSize)); const safeUsagePage = Math.min(usagePage, usageTotalPages); const pagedUsageHistory = filteredUsageHistory.slice((safeUsagePage - 1) * usagePageSize, safeUsagePage * usagePageSize); - const usageLoading = isGatewayRunning && visibleUsageHistory.length === 0; + const usageLoading = isGatewayRunning && usageFetchDoneKey !== usageFetchKey; return (