import { useId } from "react"; import { Gpu } from "lucide-react"; import { useTranslation } from "react-i18next"; import type { ServerMetrics } from "@/main-axios"; import type { GpuDevice, GpuProcess } from "@/types/stats-widgets"; import { MiniStat, RadialGauge, Sparkline, StatRow } from "@/components/charts"; import { MetricCard } from "@/components/metric-card"; import type { GpuHistories, GpuHistory } from "./gpu-history"; const NA = "N/A"; const EMPTY_HISTORY: GpuHistory = { utilization: [], memory: [] }; function formatGiB(mib: number | null): string { return mib === null ? NA : (mib / 1024).toFixed(1); } function formatVram(gpu: GpuDevice): string { return gpu.memoryUsedMiB === null || gpu.memoryTotalMiB === null ? NA : `${formatGiB(gpu.memoryUsedMiB)}/${formatGiB(gpu.memoryTotalMiB)}G`; } function formatWhole(value: number | null, unit: string): string { return value === null ? NA : `${Math.round(value)}${unit}`; } function formatPower(gpu: GpuDevice): string { if (gpu.powerDrawWatts === null && gpu.powerLimitWatts === null) return NA; return `${formatWhole(gpu.powerDrawWatts, "")}/${formatWhole(gpu.powerLimitWatts, "")}W`; } function processVram(process: GpuProcess): string { const vram = process.memoryUsedMiB === null ? NA : `${formatGiB(process.memoryUsedMiB)}G`; return process.gpuIndex === null ? vram : `${vram} · GPU ${process.gpuIndex}`; } function executableName(path: string): string { return path.split(/[\\/]/).pop() || path; } /** A captioned 0-100% sparkline, so the two trends under each GPU are told apart. */ function TrendSparkline({ caption, data, colorClassName, }: { caption: string; data: number[]; colorClassName?: string; }) { const captionId = useId(); return (
{caption}
); } function GpuDeviceRow({ gpu, history, }: { gpu: GpuDevice; history: GpuHistory; }) { const { t } = useTranslation(); return (
#{gpu.index} {gpu.name}
); } export function GpuCard({ metrics, gpuHistories, }: { metrics: ServerMetrics | null; gpuHistories: GpuHistories; }) { const { t } = useTranslation(); const gpus = metrics?.gpu?.gpus ?? []; const processes = metrics?.gpu?.processes ?? []; const driverVersion = gpus[0]?.driverVersion; return ( } action={ driverVersion ? ( {t("hostMetrics.gpu.driver", { version: driverVersion })} ) : undefined } scroll={gpus.length > 2 || processes.length > 6} scrollMax={520} > {gpus.length === 0 ? ( {t("hostMetrics.gpu.noDevice")} ) : (
{gpus.map((gpu) => ( ))}
{t("hostMetrics.gpu.processes")} {processes.length === 0 ? ( {t("hostMetrics.gpu.noProcesses")} ) : (
{processes.map((process) => ( {executableName(process.name)} {process.pid} } value={processVram(process)} mono /> ))}
)}
)}
); }