Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/app/src/components/inference/ui/ChartTooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
'use client';

import { useInference } from '@/components/inference/InferenceContext';
import { getPrecisionDisplayLabel } from '@/lib/constants';

interface TooltipContentProps<TValue, _TName> {
active?: boolean;
payload?: {
payload?: {
hwKey?: string | number;
precision?: string;
tp?: number;
conc?: number;
x?: TValue;
Expand All @@ -25,6 +27,9 @@ export default function ChartTooltip({ active, payload }: TooltipContentProps<nu
return (
<div className="bg-accent p-2 border rounded-sm">
<p>{`GPU: ${hardwareConfig[pointPayload.hwKey as keyof typeof hardwareConfig].gpu}`}</p>
{pointPayload.precision && (
<p>{`Precision: ${getPrecisionDisplayLabel(pointPayload.precision, String(pointPayload.hwKey ?? ''))}`}</p>
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scatter tooltip still generic precision

Medium Severity

Vendor-aware precision was added in ChartTooltip, but the inference scatter chart does not render that component. Hovers still use generateTooltipContent, which uppercases the raw precision field, so legend entries can show labels like NVFP4 while the hover line still shows FP4 or FP4FP8.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8c0d233. Configure here.

<p>{`Total GPUs: ${pointPayload.tp}`}</p>
{(pointPayload.ep !== null && pointPayload.ep !== undefined) ||
(pointPayload.prefill_ep !== null && pointPayload.prefill_ep !== undefined) ? (
Expand Down
103 changes: 73 additions & 30 deletions packages/app/src/components/inference/ui/ScatterGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { pointNearestX } from '@/components/inference/ui/line-label-anchor';
import ChartLegend from '@/components/ui/chart-legend';
import { useUnofficialRun } from '@/components/unofficial-run-provider';
import { computeToggle } from '@/hooks/useTogglableSet';
import { getHardwareConfig, getModelSortIndex } from '@/lib/constants';
import { getChartWatermark, getPrecisionLabel, type Precision } from '@/lib/data-mappings';
import { getHardwareConfig, getModelSortIndex, getPrecisionDisplayLabel } from '@/lib/constants';
import { getChartWatermark, PRECISION_OPTIONS } from '@/lib/data-mappings';
import { matchKnownConfigIssues, pointMatchesIssue } from '@/lib/known-issues';
import { formatNumber, getDisplayLabel, updateRepoUrl } from '@/lib/utils';
import { D3Chart } from '@/lib/d3-chart/D3Chart';
Expand Down Expand Up @@ -110,7 +110,7 @@ const parseHwKeyToLabel = (hwKey: string, model?: string): { name: string; label
};

// Line-label text for a curve. When more than one precision is shown, each curve
// is its own line, so append the precision (e.g. "B200 (vLLM) FP8") to keep the
// is its own line, so append the precision (e.g. "B200 (vLLM) NVFP4") to keep the
// FP4 and FP8 curves of the same hardware distinguishable.
const lineLabelText = (
hwKey: string,
Expand All @@ -119,7 +119,7 @@ const lineLabelText = (
model?: string,
): string => {
const base = parseHwKeyToLabel(hwKey, model).label;
return includePrecision ? `${base} ${getPrecisionLabel(precision as Precision)}` : base;
return includePrecision ? `${base} ${getPrecisionDisplayLabel(precision, hwKey)}` : base;
};

const ScatterGraph = React.memo(
Expand Down Expand Up @@ -357,6 +357,21 @@ const ScatterGraph = React.memo(
[groupedData, selectedPrecisions, effectiveActiveHwTypes],
);

const precisionsPerHw = useMemo(() => {
const map = new Map<string, Set<string>>();
for (const p of data) {
if (!selectedPrecisions.includes(p.precision)) continue;
const hw = p.hwKey as string;
let set = map.get(hw);
if (!set) {
set = new Set();
map.set(hw, set);
}
set.add(p.precision);
}
return map;
}, [data, selectedPrecisions]);

const processedOverlayData = useMemo(() => {
if (!overlayData?.data) return [];
return overlayData.data.filter((p) => selectedPrecisions.includes(p.precision));
Expand Down Expand Up @@ -1109,7 +1124,7 @@ const ScatterGraph = React.memo(
? `✕ ${info.branch || `run ${info.id}`}`
: parseHwKeyToLabel(hwKey, modelLabel).label;
return multiPrecision
? `${base} ${getPrecisionLabel(precision as Precision)}`
? `${base} ${getPrecisionDisplayLabel(precision, hwKey)}`
: base;
};
const sortedOverlay = Object.entries(overlayRooflines)
Expand Down Expand Up @@ -1166,7 +1181,7 @@ const ScatterGraph = React.memo(
? `✕ ${info.branch || `run ${info.id}`}`
: parseHwKeyToLabel(group.hwKey, modelLabel).label;
const labelText = multiPrecision
? `${branchOrHw} ${getPrecisionLabel((group.points[0]?.precision ?? '') as Precision)}`
? `${branchOrHw} ${getPrecisionDisplayLabel(group.points[0]?.precision ?? '', group.hwKey)}`
: branchOrHw;
const labelKey = `overlay-${ovKey}`;
const pt = group.points.at(-1)!;
Expand Down Expand Up @@ -2062,16 +2077,30 @@ const ScatterGraph = React.memo(
...(overlayData && unofficialRunInfos.length > 0
? unofficialRunInfos
.map((info, idx) => {
const hasPoints = overlayData.data.some(
(d) =>
const runPrecs = new Set<string>();
let sampleHwKey = '';
for (const d of overlayData.data) {
if (
overlayRunIndex(d.run_url ?? null, runIndexByUrl) === idx &&
selectedPrecisions.includes(d.precision),
);
if (!hasPoints) return null;
selectedPrecisions.includes(d.precision)
) {
runPrecs.add(d.precision);
if (!sampleHwKey && d.hwKey) sampleHwKey = d.hwKey as string;
}
}
if (runPrecs.size === 0) return null;
const branch = info.branch || `run ${info.id}`;
const precSuffix = ` · ${[...runPrecs]
.toSorted(
(a, b) =>
(PRECISION_OPTIONS as readonly string[]).indexOf(a) -
(PRECISION_OPTIONS as readonly string[]).indexOf(b),
)
.map((p) => getPrecisionDisplayLabel(p, sampleHwKey))
.join(' / ')}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overlay legend wrong vendor label

Low Severity

Unofficial-run legend precision suffixes call getPrecisionDisplayLabel with a single sampleHwKey taken from the first matching overlay point. If one run includes multiple hardware keys, fp4/fp4fp8 labels for every precision use that one vendor, which can mislabel NVIDIA data as MXFP4 or the reverse.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8c0d233. Configure here.

return {
name: `✕ unofficial-run-${info.id}`,
label: `✕ ${branch}`,
label: `✕ ${branch}${precSuffix}`,
color: overlayRunColor(idx),
title: `UNOFFICIAL: ${branch}`,
isHighlighted: true,
Expand Down Expand Up @@ -2105,24 +2134,38 @@ const ScatterGraph = React.memo(
.toSorted(
([a], [b]) => getModelSortIndex(a) - getModelSortIndex(b) || a.localeCompare(b),
)
.map(([key, hwConfig]: [string, any]) => ({
name: hwConfig.name,
label: getDisplayLabel(hwConfig),
color: resolveColor(key),
title: hwConfig.gpu,
isHighlighted: highlightConfigSuffixes.has(key.replaceAll('_', '-')),
hw: key,
isActive: showAllHardwareTypes ? true : effectiveOfficialHwTypes.has(key),
onClick: showAllHardwareTypes
? () => {}
: () => {
handleToggleHwType(key);
track('latency_hw_type_toggled', { hw: key });
},
tooltip: changelog
? formatChangelogDescription(changelog.entries[0].description)
: null,
})),
.map(([key, hwConfig]: [string, any]) => {
const baseLabel = getDisplayLabel(hwConfig);
const precs = precisionsPerHw.get(key);
const precSuffix = precs
? ` · ${[...precs]
.toSorted(
(a, b) =>
(PRECISION_OPTIONS as readonly string[]).indexOf(a) -
(PRECISION_OPTIONS as readonly string[]).indexOf(b),
)
.map((p) => getPrecisionDisplayLabel(p, key))
.join(' / ')}`
: '';
return {
name: hwConfig.name,
label: `${baseLabel}${precSuffix}`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hover tooltip precision not vendor-aware

Medium Severity

This commit adds vendor-aware precision suffixes to chart legend labels via getPrecisionDisplayLabel, but the inference scatter chart still builds hover tooltips through generateTooltipContent, which shows d.precision.toUpperCase() (e.g. FP4). Legend and point tooltip can disagree on the same chart after this change.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 792177c. Configure here.

color: resolveColor(key),
title: hwConfig.gpu,
isHighlighted: highlightConfigSuffixes.has(key.replaceAll('_', '-')),
hw: key,
isActive: showAllHardwareTypes ? true : effectiveOfficialHwTypes.has(key),
onClick: showAllHardwareTypes
? () => {}
: () => {
handleToggleHwType(key);
track('latency_hw_type_toggled', { hw: key });
},
tooltip: changelog
? formatChangelogDescription(changelog.entries[0].description)
: null,
};
}),
]}
disableActiveSort={false}
isLegendExpanded={isLegendExpanded}
Expand Down
44 changes: 44 additions & 0 deletions packages/app/src/lib/constants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
getGpuSpecs,
getHardwareConfig,
getModelSortIndex,
getPrecisionDisplayLabel,
hardwareKeyMatchesAnyBase,
hardwareKeyMatchesBase,
isKnownGpu,
Expand Down Expand Up @@ -259,3 +260,46 @@ describe('getModelSortIndex', () => {
expect(getModelSortIndex('')).toBeGreaterThanOrEqual(9);
});
});

// ===========================================================================
// getPrecisionDisplayLabel
// ===========================================================================
describe('getPrecisionDisplayLabel', () => {
it('returns NVFP4 for fp4 on NVIDIA hardware', () => {
expect(getPrecisionDisplayLabel('fp4', 'b300_vllm')).toBe('NVFP4');
expect(getPrecisionDisplayLabel('fp4', 'h100')).toBe('NVFP4');
expect(getPrecisionDisplayLabel('fp4', 'gb200_dynamo-trt_mtp')).toBe('NVFP4');
});

it('returns MXFP4 for fp4 on AMD hardware', () => {
expect(getPrecisionDisplayLabel('fp4', 'mi355x_vllm')).toBe('MXFP4');
expect(getPrecisionDisplayLabel('fp4', 'mi300x')).toBe('MXFP4');
expect(getPrecisionDisplayLabel('fp4', 'mi325x_atom')).toBe('MXFP4');
});

it('returns generic FP4 for unknown vendor', () => {
expect(getPrecisionDisplayLabel('fp4', 'unknown_x')).toBe('FP4');
expect(getPrecisionDisplayLabel('fp4', '')).toBe('FP4');
});

it('returns vendor-prefixed fp4fp8 labels', () => {
expect(getPrecisionDisplayLabel('fp4fp8', 'b200_vllm')).toBe('NVFP4+FP8');
expect(getPrecisionDisplayLabel('fp4fp8', 'mi355x')).toBe('MXFP4+FP8');
expect(getPrecisionDisplayLabel('fp4fp8', 'unknown')).toBe('FP4+FP8');
});

it('returns generic FP8 regardless of vendor', () => {
expect(getPrecisionDisplayLabel('fp8', 'b300_vllm')).toBe('FP8');
expect(getPrecisionDisplayLabel('fp8', 'mi355x_atom')).toBe('FP8');
expect(getPrecisionDisplayLabel('fp8', 'unknown')).toBe('FP8');
});

it('returns BF16 and INT4 unchanged', () => {
expect(getPrecisionDisplayLabel('bf16', 'h100_vllm')).toBe('BF16');
expect(getPrecisionDisplayLabel('int4', 'mi300x')).toBe('INT4');
});

it('falls back gracefully for unknown precision strings', () => {
expect(getPrecisionDisplayLabel('fp16', 'h100')).toBe('fp16');
});
});
20 changes: 20 additions & 0 deletions packages/app/src/lib/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { HW_REGISTRY, resolveFrameworkPartLabel } from '@semianalysisai/inferencex-constants';

import { getPrecisionLabel, type Precision } from './data-mappings';

/** d3.schemeTableau10 — 10-color categorical palette for tracked configs. */
export const TABLEAU_10 = [
'#4e79a7',
Expand Down Expand Up @@ -34,6 +36,24 @@ export function getGpuSpecs(hwKey: string): GpuSpecs {
return { power: entry.power, costh: entry.costh, costn: entry.costn, costr: entry.costr };
}

// FP4 → NVFP4/MXFP4 by vendor; FP8 stays generic (E4M3 vs MXFP8 is ambiguous in the DB).
export function getPrecisionDisplayLabel(precision: string, hwKey: string): string {
const base = hwKey.split(/[-_]/u)[0];
const vendor = HW_REGISTRY[base]?.vendor;
const fp4 = vendor === 'NVIDIA' ? 'NVFP4' : vendor === 'AMD' ? 'MXFP4' : 'FP4';
switch (precision) {
case 'fp4': {
return fp4;
}
case 'fp4fp8': {
return `${fp4}+FP8`;
}
default: {
return getPrecisionLabel(precision as Precision);
}
}
}

/** Build the vendor prefix string for the `gpu` tooltip field. */
function getVendorPrefix(base: string): string {
const entry = HW_REGISTRY[base];
Expand Down
Loading