From a7c8d8bb79e910e374792642f1e23254cc5bbd49 Mon Sep 17 00:00:00 2001 From: RazorCrest00 <146749797+RazorCrest00@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:50:09 -0700 Subject: [PATCH] [bugfix]: preserve and label stale GPU telemetry --- .../src/components/system/GpuGrid.test.tsx | 28 +++++++++- .../src/components/system/GpuGrid.tsx | 55 +++++++++++++++---- 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/apps/fastvideo_studio/src/components/system/GpuGrid.test.tsx b/apps/fastvideo_studio/src/components/system/GpuGrid.test.tsx index d1f413ed6..4fd8f0dbf 100644 --- a/apps/fastvideo_studio/src/components/system/GpuGrid.test.tsx +++ b/apps/fastvideo_studio/src/components/system/GpuGrid.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import GpuGrid from './GpuGrid'; @@ -73,4 +73,30 @@ describe('GpuGrid', () => { await screen.findByText(/Could not reach the API server/), ).toBeInTheDocument(); }); + + it('keeps the last snapshot visible and warns when a refresh fails', async () => { + vi.useFakeTimers(); + try { + vi.mocked(getGpus) + .mockResolvedValueOnce(SNAPSHOT) + .mockRejectedValueOnce(new Error('network down')); + + render(); + await act(async () => { + await vi.advanceTimersByTimeAsync(0); + }); + expect(screen.getAllByText('NVIDIA B200')).toHaveLength(2); + + await act(async () => { + await vi.advanceTimersByTimeAsync(3000); + }); + + expect( + screen.getByText(/values below may be stale/), + ).toBeInTheDocument(); + expect(screen.getAllByText('NVIDIA B200')).toHaveLength(2); + } finally { + vi.useRealTimers(); + } + }); }); diff --git a/apps/fastvideo_studio/src/components/system/GpuGrid.tsx b/apps/fastvideo_studio/src/components/system/GpuGrid.tsx index b5d39f1e7..e13645031 100644 --- a/apps/fastvideo_studio/src/components/system/GpuGrid.tsx +++ b/apps/fastvideo_studio/src/components/system/GpuGrid.tsx @@ -1,7 +1,9 @@ 'use client'; import * as React from 'react'; +import { AlertTriangle } from 'lucide-react'; +import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { getGpus, type GpuInfo, type GpuSnapshot } from '@/lib/api'; import { cn } from '@/lib/utils'; @@ -97,7 +99,8 @@ function GpuCard({ gpu }: { gpu: GpuInfo }) { export default function GpuGrid() { const [snapshot, setSnapshot] = React.useState(null); - const [fetchError, setFetchError] = React.useState(false); + const [fetchError, setFetchError] = React.useState(null); + const [retryToken, setRetryToken] = React.useState(0); React.useEffect(() => { let mounted = true; @@ -110,10 +113,14 @@ export default function GpuGrid() { const next = await getGpus(); if (mounted) { setSnapshot(next); - setFetchError(false); + setFetchError(null); } } catch { - if (mounted) setFetchError(true); + if (mounted) { + setFetchError( + 'GPU status could not be refreshed. The values below may be stale.', + ); + } } finally { inFlight = false; } @@ -125,14 +132,27 @@ export default function GpuGrid() { mounted = false; clearInterval(interval); }; - }, []); + }, [retryToken]); if (fetchError && !snapshot) { return ( - - Could not reach the API server. GPU status needs the studio API server - running. - + + + + Could not reach the API server. GPU status needs the Studio API server + running. + + setRetryToken((token) => token + 1)} + > + Try Again + + ); } if (!snapshot) { @@ -150,9 +170,22 @@ export default function GpuGrid() { return ( {fetchError && ( - - Lost contact with the API server — showing the last known values. - + + + {fetchError} + setRetryToken((token) => token + 1)} + > + Refresh Now + + )} {snapshot.gpus.map((gpu) => (
- Could not reach the API server. GPU status needs the studio API server - running. -
+ Could not reach the API server. GPU status needs the Studio API server + running. +
- Lost contact with the API server — showing the last known values. -