From ecaaa397a3082a335d47782fb1dbd3077850bfb0 Mon Sep 17 00:00:00 2001 From: Supra4E8C Date: Sun, 22 Feb 2026 12:46:27 +0800 Subject: [PATCH] fix(stale): scope trace cache and ignore stale discovery --- src/pages/AiProvidersCodexEditPage.tsx | 10 +++++++++- src/pages/AiProvidersGeminiEditPage.tsx | 10 +++++++++- src/pages/LogsPage.tsx | 23 ++++++++++++++++++++++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/pages/AiProvidersCodexEditPage.tsx b/src/pages/AiProvidersCodexEditPage.tsx index 85bd4a5..4f99f2b 100644 --- a/src/pages/AiProvidersCodexEditPage.tsx +++ b/src/pages/AiProvidersCodexEditPage.tsx @@ -118,6 +118,7 @@ export function AiProvidersCodexEditPage() { const [modelDiscoverySearch, setModelDiscoverySearch] = useState(''); const [modelDiscoverySelected, setModelDiscoverySelected] = useState>(new Set()); const autoFetchSignatureRef = useRef(''); + const modelDiscoveryRequestIdRef = useRef(0); const hasIndexParam = typeof params.index === 'string'; const editIndex = useMemo(() => parseIndexParam(params.index), [params.index]); @@ -266,6 +267,7 @@ export function AiProvidersCodexEditPage() { ); const fetchCodexModelDiscovery = useCallback(async () => { + const requestId = (modelDiscoveryRequestIdRef.current += 1); setModelDiscoveryFetching(true); setModelDiscoveryError(''); @@ -280,19 +282,25 @@ export function AiProvidersCodexEditPage() { hasCustomAuthorization ? undefined : apiKey, headerObject ); + if (modelDiscoveryRequestIdRef.current !== requestId) return; setDiscoveredModels(list); } catch (err: unknown) { + if (modelDiscoveryRequestIdRef.current !== requestId) return; setDiscoveredModels([]); const message = getErrorMessage(err); setModelDiscoveryError(`${t('ai_providers.codex_models_fetch_error')}: ${message}`); } finally { - setModelDiscoveryFetching(false); + if (modelDiscoveryRequestIdRef.current === requestId) { + setModelDiscoveryFetching(false); + } } }, [form.apiKey, form.baseUrl, form.headers, t]); useEffect(() => { if (!modelDiscoveryOpen) { autoFetchSignatureRef.current = ''; + modelDiscoveryRequestIdRef.current += 1; + setModelDiscoveryFetching(false); return; } diff --git a/src/pages/AiProvidersGeminiEditPage.tsx b/src/pages/AiProvidersGeminiEditPage.tsx index 84fe6e9..d41842e 100644 --- a/src/pages/AiProvidersGeminiEditPage.tsx +++ b/src/pages/AiProvidersGeminiEditPage.tsx @@ -112,6 +112,7 @@ export function AiProvidersGeminiEditPage() { const [modelDiscoverySearch, setModelDiscoverySearch] = useState(''); const [modelDiscoverySelected, setModelDiscoverySelected] = useState>(new Set()); const autoFetchSignatureRef = useRef(''); + const modelDiscoveryRequestIdRef = useRef(0); const hasIndexParam = typeof params.index === 'string'; const editIndex = useMemo(() => parseIndexParam(params.index), [params.index]); @@ -244,6 +245,7 @@ export function AiProvidersGeminiEditPage() { ); const fetchGeminiModelDiscovery = useCallback(async () => { + const requestId = (modelDiscoveryRequestIdRef.current += 1); setModelDiscoveryFetching(true); setModelDiscoveryError(''); const headerObject = buildHeaderObject(form.headers); @@ -253,8 +255,10 @@ export function AiProvidersGeminiEditPage() { form.apiKey.trim() || undefined, headerObject ); + if (modelDiscoveryRequestIdRef.current !== requestId) return; setDiscoveredModels(list); } catch (err: unknown) { + if (modelDiscoveryRequestIdRef.current !== requestId) return; setDiscoveredModels([]); const message = err instanceof Error ? err.message : typeof err === 'string' ? err : ''; const hasCustomXGoogApiKey = Object.keys(headerObject).some( @@ -271,13 +275,17 @@ export function AiProvidersGeminiEditPage() { : ''; setModelDiscoveryError(`${t('ai_providers.gemini_models_fetch_error')}: ${message}${diag}`); } finally { - setModelDiscoveryFetching(false); + if (modelDiscoveryRequestIdRef.current === requestId) { + setModelDiscoveryFetching(false); + } } }, [form.apiKey, form.baseUrl, form.headers, t]); useEffect(() => { if (!modelDiscoveryOpen) { autoFetchSignatureRef.current = ''; + modelDiscoveryRequestIdRef.current += 1; + setModelDiscoveryFetching(false); return; } diff --git a/src/pages/LogsPage.tsx b/src/pages/LogsPage.tsx index 34110f1..bc083d8 100644 --- a/src/pages/LogsPage.tsx +++ b/src/pages/LogsPage.tsx @@ -490,6 +490,9 @@ export function LogsPage() { const { t } = useTranslation(); const { showNotification, showConfirmation } = useNotificationStore(); const connectionStatus = useAuthStore((state) => state.connectionStatus); + const apiBase = useAuthStore((state) => state.apiBase); + const managementKey = useAuthStore((state) => state.managementKey); + const traceScopeKey = `${apiBase}::${managementKey}`; const config = useConfigStore((state) => state.config); const requestLogEnabled = config?.requestLog ?? false; @@ -529,6 +532,7 @@ export function LogsPage() { const pendingFullReloadRef = useRef(false); const traceUsageLoadedAtRef = useRef(0); const traceAuthLoadedAtRef = useRef(0); + const traceScopeKeyRef = useRef(''); // 保存最新时间戳用于增量获取 const latestTimestampRef = useRef(0); @@ -754,6 +758,15 @@ export function LogsPage() { }; const loadTraceUsageDetails = useCallback(async () => { + if (traceScopeKeyRef.current !== traceScopeKey) { + traceScopeKeyRef.current = traceScopeKey; + traceUsageLoadedAtRef.current = 0; + traceAuthLoadedAtRef.current = 0; + setTraceUsageDetails([]); + setTraceAuthFileMap(new Map()); + setTraceError(''); + } + if (traceLoading) return; const now = Date.now(); @@ -801,11 +814,18 @@ export function LogsPage() { } finally { setTraceLoading(false); } - }, [t, traceLoading]); + }, [t, traceLoading, traceScopeKey]); useEffect(() => { if (connectionStatus === 'connected') { latestTimestampRef.current = 0; + traceScopeKeyRef.current = traceScopeKey; + traceUsageLoadedAtRef.current = 0; + traceAuthLoadedAtRef.current = 0; + setTraceUsageDetails([]); + setTraceAuthFileMap(new Map()); + setTraceLoading(false); + setTraceError(''); loadLogs(false); } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -1158,6 +1178,7 @@ export function LogsPage() { const openTraceModal = (line: ParsedLogLine) => { if (!isTraceableRequestPath(line.path)) return; cancelLongPress(); + setTraceError(''); setTraceLogLine(line); void loadTraceUsageDetails(); };