Skip to content

Commit a417e21

Browse files
authored
feat(inference): known-issue warning boxes for affected configs (#439)
* feat(inference): known-issue warning boxes for affected configs * fix(inference): include overlay series in CSV export issue warnings * feat(inference): show known-issue warnings on GPU date-comparison view
1 parent ac2bb77 commit a417e21

9 files changed

Lines changed: 707 additions & 6 deletions

File tree

packages/app/src/components/inference/ui/ChartDisplay.tsx

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import { ChartShareActions, MetricAssumptionNotes } from '@/components/ui/chart-
2323
import { UnofficialDomainNotice } from '@/components/ui/unofficial-domain-notice';
2424
import { exportToCsv } from '@/lib/csv-export';
2525
import { inferenceChartToCsv } from '@/lib/csv-export-helpers';
26+
import { knownIssueCsvNote, matchKnownConfigIssues } from '@/lib/known-issues';
27+
import { getDisplayLabel } from '@/lib/utils';
2628
import {
2729
Dialog,
2830
DialogContent,
@@ -43,7 +45,7 @@ import {
4345
} from '@/lib/data-mappings';
4446
import { useComparisonChangelogs } from '@/hooks/api/use-comparison-changelogs';
4547
import { useTrendData } from '@/components/inference/hooks/useTrendData';
46-
import { hardwareKeyMatchesAnyBase } from '@/lib/constants';
48+
import { getHardwareConfig, hardwareKeyMatchesAnyBase } from '@/lib/constants';
4749

4850
import ChartControls from './ChartControls';
4951
import ComparisonChangelog from './ComparisonChangelog';
@@ -171,8 +173,14 @@ export default function ChartDisplay() {
171173
track('inference_view_changed', { view: value, chartIndex: index });
172174
};
173175

174-
const { unofficialRunInfo, unofficialRunInfos, runIndexByUrl, getOverlayData, isUnofficialRun } =
175-
useUnofficialRun();
176+
const {
177+
unofficialRunInfo,
178+
unofficialRunInfos,
179+
runIndexByUrl,
180+
getOverlayData,
181+
isUnofficialRun,
182+
activeOverlayHwTypes,
183+
} = useUnofficialRun();
176184

177185
// Compute overlay data for each chart type — must match useChartData processing
178186
const overlayDataByChartType = useMemo(() => {
@@ -383,10 +391,30 @@ export default function ChartDisplay() {
383391
graph.model,
384392
graph.sequence,
385393
);
394+
// Match warnings against the same series the chart annotates,
395+
// including visible unofficial-run overlay series.
396+
const overlay =
397+
graph.chartDefinition.chartType === 'e2e'
398+
? overlayDataByChartType.e2e
399+
: overlayDataByChartType.interactivity;
400+
const visibleOverlayRows = isTimelineMode
401+
? []
402+
: (overlay?.data ?? []).filter(
403+
(p) =>
404+
activeOverlayHwTypes.has(p.hwKey as string) &&
405+
selectedPrecisions.includes(p.precision),
406+
);
407+
const issueNotes = matchKnownConfigIssues(graph.model, [
408+
...visibleData,
409+
...visibleOverlayRows,
410+
]).map((issue) =>
411+
knownIssueCsvNote(issue, getDisplayLabel(getHardwareConfig(issue.hwKey))),
412+
);
386413
exportToCsv(
387414
`InferenceX_${selectedModel}_${graph.chartDefinition.chartType}`,
388415
headers,
389416
rows,
417+
issueNotes,
390418
);
391419
}}
392420
/>

packages/app/src/components/inference/ui/GPUGraph.tsx

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ import {
4242
generateGPUGraphTooltipContent,
4343
getPointLabel,
4444
} from '@/components/inference/utils/tooltipUtils';
45+
import {
46+
type KnownIssueAnnotation,
47+
measureLegendRightInset,
48+
renderKnownIssueAnnotations,
49+
} from '@/components/inference/utils/knownIssueAnnotations';
50+
import { matchKnownConfigIssues } from '@/lib/known-issues';
4551

4652
const CHART_MARGIN = { top: 24, right: 10, bottom: 60, left: 60 };
4753

@@ -58,7 +64,7 @@ function labelTextFor(pts: InferenceData[]): string {
5864
}
5965

6066
const GPUGraph = React.memo(
61-
({ chartId, data, xLabel, yLabel, chartDefinition, caption }: ScatterGraphProps) => {
67+
({ chartId, modelLabel, data, xLabel, yLabel, chartDefinition, caption }: ScatterGraphProps) => {
6268
const {
6369
hardwareConfig,
6470
selectedPrecisions,
@@ -211,6 +217,70 @@ const GPUGraph = React.memo(
211217
return pts;
212218
}, [groupedData, activeDates, hideNonOptimal, optimalPointKeys]);
213219

220+
// Warning annotations for visible series with known upstream issues —
221+
// same treatment the scatter view gets, applied to the date-comparison view.
222+
// Lines here are colored per (gpu, date) pair, so take the first active
223+
// pair's color as the series swatch.
224+
const knownIssueAnnotations = useMemo(
225+
(): KnownIssueAnnotation[] =>
226+
matchKnownConfigIssues(modelLabel, filteredData).map((issue) => {
227+
const cfg = getHardwareConfig(issue.hwKey);
228+
const colorEntry = allGraphs.find(
229+
(entry) => entry.hwKey === issue.hwKey && activeDates.has(entry.id),
230+
);
231+
return {
232+
issue,
233+
label: cfg ? getDisplayLabel(cfg) : issue.hwKey,
234+
color: getCssColor(colorEntry?.color ?? resolveColor(issue.hwKey)),
235+
points: filteredData
236+
.filter(
237+
(p) =>
238+
String(p.hwKey) === issue.hwKey &&
239+
(!issue.precisions || issue.precisions.includes(p.precision)),
240+
)
241+
.map((p) => ({ x: p.x, y: p.y })),
242+
};
243+
}),
244+
[modelLabel, filteredData, allGraphs, activeDates, resolveColor, getCssColor],
245+
);
246+
247+
const drawKnownIssues = (
248+
ctx: RenderContext,
249+
xScale: ContinuousScale,
250+
yScale: ContinuousScale,
251+
) => {
252+
renderKnownIssueAnnotations(ctx.layout.g, ctx.layout.defs, {
253+
chartId,
254+
width: ctx.width,
255+
height: ctx.height,
256+
xScale,
257+
yScale,
258+
annotations: knownIssueAnnotations,
259+
rightInset: measureLegendRightInset(
260+
chartId,
261+
ctx.layout.svg.node(),
262+
ctx.layout.margin.left,
263+
ctx.width,
264+
),
265+
background: getCssColor('--background'),
266+
foreground: getCssColor('--foreground'),
267+
mutedForeground: getCssColor('--muted-foreground'),
268+
onLinkClick: (a) =>
269+
track('inference_known_issue_clicked', {
270+
hwKey: a.issue.hwKey,
271+
issue: a.issue.issueRef,
272+
}),
273+
});
274+
};
275+
const knownIssueLayer: CustomLayerConfig = {
276+
type: 'custom',
277+
key: 'known-issues',
278+
render: (_zoomGroup, ctx) =>
279+
drawKnownIssues(ctx, ctx.xScale as ContinuousScale, ctx.yScale as ContinuousScale),
280+
onZoom: (_zoomGroup, ctx) =>
281+
drawKnownIssues(ctx, ctx.newXScale as ContinuousScale, ctx.newYScale as ContinuousScale),
282+
};
283+
214284
// Compute scale domains
215285
const xExtent = useMemo(() => {
216286
if (filteredData.length === 0) return [0, 100] as [number, number];
@@ -649,6 +719,7 @@ const GPUGraph = React.memo(
649719
},
650720
},
651721
lineLabelLayer,
722+
knownIssueLayer,
652723
]}
653724
zoom={{
654725
enabled: true,

packages/app/src/components/inference/ui/ScatterGraph.tsx

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useUnofficialRun } from '@/components/unofficial-run-provider';
1111
import { computeToggle } from '@/hooks/useTogglableSet';
1212
import { getHardwareConfig, getModelSortIndex } from '@/lib/constants';
1313
import { getChartWatermark, getPrecisionLabel, type Precision } from '@/lib/data-mappings';
14+
import { matchKnownConfigIssues } from '@/lib/known-issues';
1415
import { formatNumber, getDisplayLabel, updateRepoUrl } from '@/lib/utils';
1516
import { D3Chart } from '@/lib/d3-chart/D3Chart';
1617
import type {
@@ -62,6 +63,11 @@ import {
6263
PARETO_LABEL_COLORS,
6364
buildGradientColorMap,
6465
} from '@/components/inference/utils/paretoLabels';
66+
import {
67+
type KnownIssueAnnotation,
68+
measureLegendRightInset,
69+
renderKnownIssueAnnotations,
70+
} from '@/components/inference/utils/knownIssueAnnotations';
6571

6672
// X-shape path for overlay (unofficial) data points
6773
const X_SIZE = 5;
@@ -112,6 +118,7 @@ const lineLabelText = (hwKey: string, precision: string, includePrecision: boole
112118
const ScatterGraph = React.memo(
113119
({
114120
chartId,
121+
modelLabel,
115122
data,
116123
xLabel,
117124
yLabel,
@@ -340,6 +347,35 @@ const ScatterGraph = React.memo(
340347
return overlayData.data.filter((p) => selectedPrecisions.includes(p.precision));
341348
}, [overlayData, selectedPrecisions]);
342349

350+
// Warning annotations for visible series (official + unofficial overlay)
351+
// with known upstream issues. Drawn as an SVG layer (box + arrow to the
352+
// affected line) so PNG exports carry the warning.
353+
const knownIssueAnnotations = useMemo((): KnownIssueAnnotation[] => {
354+
const visibleOverlayPoints = processedOverlayData.filter((p) =>
355+
activeOverlayHwTypes.has(p.hwKey as string),
356+
);
357+
const visiblePoints = [...filteredData, ...visibleOverlayPoints];
358+
return matchKnownConfigIssues(modelLabel, visiblePoints).map((issue) => ({
359+
issue,
360+
label: parseHwKeyToLabel(issue.hwKey).label,
361+
color: getCssColor(resolveColor(issue.hwKey)),
362+
points: visiblePoints
363+
.filter(
364+
(p) =>
365+
String(p.hwKey) === issue.hwKey &&
366+
(!issue.precisions || issue.precisions.includes(p.precision)),
367+
)
368+
.map((p) => ({ x: p.x, y: p.y })),
369+
}));
370+
}, [
371+
modelLabel,
372+
filteredData,
373+
processedOverlayData,
374+
activeOverlayHwTypes,
375+
resolveColor,
376+
getCssColor,
377+
]);
378+
343379
// Combined data for D3 scale domain (includes overlay so scales fit both datasets)
344380
const chartScaleData = useMemo(() => {
345381
if (processedOverlayData.length === 0) return filteredData;
@@ -1760,11 +1796,51 @@ const ScatterGraph = React.memo(
17601796
},
17611797
};
17621798

1799+
// ── Known-issue annotations: warning box + arrow to the affected line ──
1800+
const drawKnownIssues = (
1801+
ctx: RenderContext,
1802+
xScale: ContinuousScale,
1803+
yScale: ContinuousScale,
1804+
) => {
1805+
renderKnownIssueAnnotations(ctx.layout.g, ctx.layout.defs, {
1806+
chartId,
1807+
width: ctx.width,
1808+
height: ctx.height,
1809+
xScale,
1810+
yScale,
1811+
annotations: knownIssueAnnotations,
1812+
rightInset: measureLegendRightInset(
1813+
chartId,
1814+
ctx.layout.svg.node(),
1815+
ctx.layout.margin.left,
1816+
ctx.width,
1817+
),
1818+
background: getCssColor('--background'),
1819+
foreground: getCssColor('--foreground'),
1820+
mutedForeground: getCssColor('--muted-foreground'),
1821+
onLinkClick: (a) =>
1822+
track('inference_known_issue_clicked', {
1823+
hwKey: a.issue.hwKey,
1824+
issue: a.issue.issueRef,
1825+
}),
1826+
});
1827+
};
1828+
const knownIssueLayer: CustomLayerConfig = {
1829+
type: 'custom',
1830+
key: 'known-issues',
1831+
render: (_zoomGroup, ctx) =>
1832+
drawKnownIssues(ctx, ctx.xScale as ContinuousScale, ctx.yScale as ContinuousScale),
1833+
onZoom: (_zoomGroup, ctx) =>
1834+
drawKnownIssues(ctx, ctx.newXScale as ContinuousScale, ctx.newYScale as ContinuousScale),
1835+
};
1836+
17631837
const result: LayerConfig<InferenceData>[] = [rooflineLayer, scatterLayer];
17641838
if (overlayLayer) result.push(overlayLayer);
17651839
result.push(speedOverlayLayer);
1840+
result.push(knownIssueLayer);
17661841
return result;
17671842
}, [
1843+
knownIssueAnnotations,
17681844
rooflines,
17691845
allPointLabelsByKey,
17701846
showGradientLabels,

0 commit comments

Comments
 (0)