Skip to content

Commit 1392807

Browse files
functionstackxclaudegithub-actions[bot]adibarra
authored
feat(inference): rework point-label toggle and default line labels on (#474)
* feat(inference): rework point-label toggle and default line labels on - Rename the "Hide Labels" toggle to "Labels" and invert it: off by default now means point labels are hidden; turning it on shows them. State renamed hidePointLabels -> showPointLabels; URL param i_nolabel -> i_label (=1 shows labels). - Enable "Line Labels" by default. URL encodes the non-default OFF state as i_linelabel=0 (on, the default, is stripped from share links). - Turning "Parallelism Labels" on now auto-enables "Labels" if it's off, so the parallelism text actually renders instead of being hidden. Applied to both the latency Scatter chart and the GPU chart, including the unofficial-run overlay label path. Updated mock context, the line-labels e2e specs (default-on + i_linelabel=0 coverage), and docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(inference): keep legacy ?i_nolabel=1 and ?i_advlabel=1 share links working - Re-add `i_nolabel` as a read-only legacy alias so pre-rename share links explicitly hide point labels (survives future default changes). - Hydrate `showPointLabels=true` when `?i_advlabel=1` is present and `?i_nolabel=1` is not, mirroring the toggle's auto-enable side-effect so parallelism labels actually render on legacy links. - Add Cypress coverage for the three URL combinations. Co-authored-by: Alec Ibarra <adibarra@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Alec Ibarra <adibarra@users.noreply.github.com>
1 parent 7ec952a commit 1392807

8 files changed

Lines changed: 143 additions & 67 deletions

File tree

docs/state-ownership.md

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ Depends on: `GlobalFilterProvider` (reads all filter state and availability, inc
7373

7474
- `selectedYAxisMetric` (`i_metric`), `selectedXAxisMetric` (`i_xmetric`), `selectedE2eXAxisMetric` (`i_e2e_xmetric`)
7575
- `scaleType``auto | linear | log` (`i_scale`)
76-
- `hideNonOptimal` (`i_optimal`), `hidePointLabels` (`i_nolabel`), `logScale` (`i_log`)
76+
- `hideNonOptimal` (`i_optimal`), `showPointLabels` (`i_label`), `logScale` (`i_log`)
7777
- `highContrast` (`i_hc`), `isLegendExpanded` (`i_legend`)
7878
- `useAdvancedLabels` (`i_advlabel`), `showGradientLabels` (`i_gradlabel`)
7979
- `colorShuffleSeed` — no URL param; ephemeral
@@ -260,34 +260,35 @@ Historical Trends and TCO Calculator share the inference tab's URL path (`/infer
260260

261261
### Full parameter list
262262

263-
| Param | Owner | Default |
264-
| --------------- | ------------------- | -------------------------------- |
265-
| `g_model` | GlobalFilterContext | `DeepSeek-R1-0528` |
266-
| `g_rundate` | GlobalFilterContext | `''` |
267-
| `g_runid` | GlobalFilterContext | `''` |
268-
| `i_seq` | GlobalFilterContext | `8k/1k` |
269-
| `i_prec` | GlobalFilterContext | `fp4` |
270-
| `i_metric` | InferenceProvider | `y_tpPerGpu` |
271-
| `i_xmetric` | InferenceProvider | `p99_ttft` |
272-
| `i_e2e_xmetric` | InferenceProvider | `''` |
273-
| `i_scale` | InferenceProvider | `auto` |
274-
| `i_gpus` | InferenceProvider | `''` |
275-
| `i_dates` | InferenceProvider | `''` |
276-
| `i_dstart` | InferenceProvider | `''` |
277-
| `i_dend` | InferenceProvider | `''` |
278-
| `i_optimal` | InferenceProvider | `''` (truthy = hide non-optimal) |
279-
| `i_nolabel` | InferenceProvider | `''` |
280-
| `i_hc` | InferenceProvider | `''` |
281-
| `i_log` | InferenceProvider | `''` |
282-
| `i_legend` | InferenceProvider | `''` |
283-
| `i_advlabel` | InferenceProvider | `''` |
284-
| `i_gradlabel` | InferenceProvider | `''` |
285-
| `e_rundate` | EvaluationProvider | `''` |
286-
| `e_bench` | EvaluationProvider | `''` |
287-
| `e_hc` | EvaluationProvider | `''` |
288-
| `e_labels` | EvaluationProvider | `''` |
289-
| `e_legend` | EvaluationProvider | `''` |
290-
| `r_range` | ReliabilityProvider | `last-3-months` |
291-
| `r_pct` | ReliabilityProvider | `''` |
292-
| `r_hc` | ReliabilityProvider | `''` |
293-
| `r_legend` | ReliabilityProvider | `''` |
263+
| Param | Owner | Default |
264+
| --------------- | ------------------- | --------------------------------- |
265+
| `g_model` | GlobalFilterContext | `DeepSeek-R1-0528` |
266+
| `g_rundate` | GlobalFilterContext | `''` |
267+
| `g_runid` | GlobalFilterContext | `''` |
268+
| `i_seq` | GlobalFilterContext | `8k/1k` |
269+
| `i_prec` | GlobalFilterContext | `fp4` |
270+
| `i_metric` | InferenceProvider | `y_tpPerGpu` |
271+
| `i_xmetric` | InferenceProvider | `p99_ttft` |
272+
| `i_e2e_xmetric` | InferenceProvider | `''` |
273+
| `i_scale` | InferenceProvider | `auto` |
274+
| `i_gpus` | InferenceProvider | `''` |
275+
| `i_dates` | InferenceProvider | `''` |
276+
| `i_dstart` | InferenceProvider | `''` |
277+
| `i_dend` | InferenceProvider | `''` |
278+
| `i_optimal` | InferenceProvider | `''` (truthy = hide non-optimal) |
279+
| `i_label` | InferenceProvider | `''` (truthy = show point labels) |
280+
| `i_nolabel` | InferenceProvider | `''` (legacy, read-only) |
281+
| `i_hc` | InferenceProvider | `''` |
282+
| `i_log` | InferenceProvider | `''` |
283+
| `i_legend` | InferenceProvider | `''` |
284+
| `i_advlabel` | InferenceProvider | `''` |
285+
| `i_gradlabel` | InferenceProvider | `''` |
286+
| `e_rundate` | EvaluationProvider | `''` |
287+
| `e_bench` | EvaluationProvider | `''` |
288+
| `e_hc` | EvaluationProvider | `''` |
289+
| `e_labels` | EvaluationProvider | `''` |
290+
| `e_legend` | EvaluationProvider | `''` |
291+
| `r_range` | ReliabilityProvider | `last-3-months` |
292+
| `r_pct` | ReliabilityProvider | `''` |
293+
| `r_hc` | ReliabilityProvider | `''` |
294+
| `r_legend` | ReliabilityProvider | `''` |

packages/app/cypress/e2e/line-labels.cy.ts

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,22 @@ describe('Line Labels Toggle', () => {
1515
cy.get('label[for="scatter-line-labels"]').should('contain.text', 'Line Labels');
1616
});
1717

18-
it('Line Labels toggle is off by default', () => {
19-
cy.get('#scatter-line-labels').should('have.attr', 'data-state', 'unchecked');
18+
it('Line Labels toggle is on by default', () => {
19+
cy.get('#scatter-line-labels').should('have.attr', 'data-state', 'checked');
20+
21+
// Line labels render without any interaction
22+
cy.get('[data-testid="scatter-graph"] svg g.line-label').should('have.length.greaterThan', 0);
2023
});
2124

22-
it('toggling Line Labels on renders label elements on the chart', () => {
25+
it('toggling Line Labels off then back on removes and restores label elements', () => {
26+
// On by default — turn it off first.
2327
cy.get('#scatter-line-labels').click();
24-
cy.get('#scatter-line-labels').should('have.attr', 'data-state', 'checked');
28+
cy.get('#scatter-line-labels').should('have.attr', 'data-state', 'unchecked');
29+
cy.get('[data-testid="scatter-graph"] svg g.line-label').should('have.length', 0);
2530

26-
// Line label groups should appear in the SVG
31+
// Turn it back on — labels return.
32+
cy.get('#scatter-line-labels').click();
33+
cy.get('#scatter-line-labels').should('have.attr', 'data-state', 'checked');
2734
cy.get('[data-testid="scatter-graph"] svg g.line-label').should('have.length.greaterThan', 0);
2835
});
2936

@@ -148,6 +155,53 @@ describe('Line Labels Toggle', () => {
148155
cy.get('[data-testid="scatter-graph"] svg g.line-label').should('have.length.greaterThan', 0);
149156
});
150157

158+
it('URL param i_linelabel=0 disables line labels on load', () => {
159+
cy.visit('/inference?i_linelabel=0', {
160+
onBeforeLoad(win) {
161+
win.localStorage.setItem('inferencex-star-modal-dismissed', String(Date.now()));
162+
},
163+
});
164+
cy.get('[data-testid="scatter-graph"]').should('be.visible');
165+
cy.get('#scatter-line-labels').should('have.attr', 'data-state', 'unchecked');
166+
167+
// Labels should not be rendered
168+
cy.get('[data-testid="scatter-graph"] svg g.line-label').should('have.length', 0);
169+
});
170+
171+
it('legacy URL param i_nolabel=1 keeps point labels hidden on load', () => {
172+
cy.visit('/inference?i_nolabel=1', {
173+
onBeforeLoad(win) {
174+
win.localStorage.setItem('inferencex-star-modal-dismissed', String(Date.now()));
175+
},
176+
});
177+
cy.get('[data-testid="scatter-graph"]').should('be.visible');
178+
cy.get('#scatter-point-labels').should('have.attr', 'data-state', 'unchecked');
179+
});
180+
181+
it('legacy URL param i_advlabel=1 auto-enables Labels so advanced labels render', () => {
182+
cy.visit('/inference?i_advlabel=1', {
183+
onBeforeLoad(win) {
184+
win.localStorage.setItem('inferencex-star-modal-dismissed', String(Date.now()));
185+
},
186+
});
187+
cy.get('[data-testid="scatter-graph"]').should('be.visible');
188+
cy.get('#scatter-parallelism-labels').should('have.attr', 'data-state', 'checked');
189+
// Labels toggle is auto-enabled by the URL hydration so the advanced
190+
// (parallelism) point labels actually render.
191+
cy.get('#scatter-point-labels').should('have.attr', 'data-state', 'checked');
192+
});
193+
194+
it('legacy URL combo i_advlabel=1&i_nolabel=1 keeps point labels hidden (i_nolabel wins)', () => {
195+
cy.visit('/inference?i_advlabel=1&i_nolabel=1', {
196+
onBeforeLoad(win) {
197+
win.localStorage.setItem('inferencex-star-modal-dismissed', String(Date.now()));
198+
},
199+
});
200+
cy.get('[data-testid="scatter-graph"]').should('be.visible');
201+
cy.get('#scatter-parallelism-labels').should('have.attr', 'data-state', 'checked');
202+
cy.get('#scatter-point-labels').should('have.attr', 'data-state', 'unchecked');
203+
});
204+
151205
it('appends the precision to each line label when multiple precisions are selected', () => {
152206
// Pair the FP4+FP8 selection with a model that has both precisions in the
153207
// fixtures. The default model (DeepSeek-V4-Pro) only has FP4, so

packages/app/cypress/support/mock-data.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ export function createMockInferenceContext(
199199
setIsLegendExpanded: namedStub('setIsLegendExpanded'),
200200
hideNonOptimal: false,
201201
setHideNonOptimal: namedStub('setHideNonOptimal'),
202-
hidePointLabels: false,
203-
setHidePointLabels: namedStub('setHidePointLabels'),
202+
showPointLabels: false,
203+
setShowPointLabels: namedStub('setShowPointLabels'),
204204
highContrast: false,
205205
setHighContrast: namedStub('setHighContrast'),
206206
logScale: false,

packages/app/src/components/inference/InferenceContext.tsx

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,25 @@ export function InferenceProvider({
157157
});
158158

159159
const [hideNonOptimal, setHideNonOptimal] = useState(() => getUrlParam('i_optimal') !== '0');
160-
const [hidePointLabels, setHidePointLabels] = useState(() => getUrlParam('i_nolabel') === '1');
160+
const [showPointLabels, setShowPointLabels] = useState(() => {
161+
// Legacy `?i_nolabel=1` from before the rename: keep hiding point labels
162+
// explicitly so the share link's intent survives future default changes.
163+
if (getUrlParam('i_nolabel') === '1') return false;
164+
if (getUrlParam('i_label') === '1') return true;
165+
// Old share links set `?i_advlabel=1` while keeping the labels default
166+
// (shown). Mirror the toggle's auto-enable side-effect on load so those
167+
// links still render advanced labels under the new default-off behavior.
168+
if (getUrlParam('i_advlabel') === '1') return true;
169+
return false;
170+
});
161171
const [logScale, setLogScale] = useState(() => getUrlParam('i_log') === '1');
162172
const [useAdvancedLabels, setUseAdvancedLabels] = useState(
163173
() => getUrlParam('i_advlabel') === '1',
164174
);
165175
const [showGradientLabels, setShowGradientLabels] = useState(
166176
() => getUrlParam('i_gradlabel') === '1',
167177
);
168-
const [showLineLabels, setShowLineLabels] = useState(() => getUrlParam('i_linelabel') === '1');
178+
const [showLineLabels, setShowLineLabels] = useState(() => getUrlParam('i_linelabel') !== '0');
169179
const [showSpeedOverlay, setShowSpeedOverlay] = useState(() => getUrlParam('i_speed') === '1');
170180
const [showMinecraftOverlay, setShowMinecraftOverlay] = useState(
171181
() => getUrlParam('i_mc') === '1',
@@ -821,7 +831,7 @@ export function InferenceProvider({
821831
i_dstart: selectedDateRange.startDate,
822832
i_dend: selectedDateRange.endDate,
823833
i_optimal: hideNonOptimal ? '' : '0',
824-
i_nolabel: hidePointLabels ? '1' : '',
834+
i_label: showPointLabels ? '1' : '',
825835
i_hc: highContrast ? '1' : '',
826836
i_log: logScale ? '1' : '',
827837
i_xmetric: selectedXAxisMetric || '',
@@ -830,7 +840,7 @@ export function InferenceProvider({
830840
i_legend: isLegendExpanded ? '' : '0',
831841
i_advlabel: useAdvancedLabels ? '1' : '',
832842
i_gradlabel: showGradientLabels ? '1' : '',
833-
i_linelabel: showLineLabels ? '1' : '',
843+
i_linelabel: showLineLabels ? '' : '0',
834844
i_speed: showSpeedOverlay ? '1' : '',
835845
i_mc: showMinecraftOverlay ? '1' : '',
836846
i_active: iActiveStr,
@@ -844,7 +854,7 @@ export function InferenceProvider({
844854
selectedDates,
845855
selectedDateRange,
846856
hideNonOptimal,
847-
hidePointLabels,
857+
showPointLabels,
848858
highContrast,
849859
logScale,
850860
isLegendExpanded,
@@ -989,8 +999,8 @@ export function InferenceProvider({
989999
setIsLegendExpanded,
9901000
hideNonOptimal,
9911001
setHideNonOptimal,
992-
hidePointLabels,
993-
setHidePointLabels,
1002+
showPointLabels,
1003+
setShowPointLabels,
9941004
highContrast,
9951005
setHighContrast,
9961006
logScale,
@@ -1089,7 +1099,7 @@ export function InferenceProvider({
10891099
availableSequences,
10901100
availableModels,
10911101
hideNonOptimal,
1092-
hidePointLabels,
1102+
showPointLabels,
10931103
highContrast,
10941104
logScale,
10951105
isLegendExpanded,

packages/app/src/components/inference/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,8 +645,8 @@ export interface InferenceChartContextType {
645645
isLegendExpanded: boolean;
646646
hideNonOptimal: boolean;
647647
setHideNonOptimal: (hide: boolean) => void;
648-
hidePointLabels: boolean;
649-
setHidePointLabels: (hide: boolean) => void;
648+
showPointLabels: boolean;
649+
setShowPointLabels: (show: boolean) => void;
650650
highContrast: boolean;
651651
setHighContrast: (highContrast: boolean) => void;
652652
logScale: boolean;

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ const GPUGraph = React.memo(
9292
activeDates,
9393
hideNonOptimal,
9494
setHideNonOptimal,
95-
hidePointLabels,
96-
setHidePointLabels,
95+
showPointLabels,
96+
setShowPointLabels,
9797
logScale,
9898
setLogScale,
9999
isLegendExpanded,
@@ -754,7 +754,7 @@ const GPUGraph = React.memo(
754754
data: filteredData,
755755
config: {
756756
getColor,
757-
hideLabels: hidePointLabels,
757+
hideLabels: !showPointLabels,
758758
getLabelText: (d) => (useAdvancedLabels ? getPointLabel(d) : String(d.tp)),
759759
foreground: 'var(--foreground)',
760760
dataAttrs: {
@@ -876,12 +876,12 @@ const GPUGraph = React.memo(
876876
},
877877
},
878878
{
879-
id: 'gpu-hide-point-labels',
880-
label: 'Hide Labels',
881-
checked: hidePointLabels,
879+
id: 'gpu-point-labels',
880+
label: 'Labels',
881+
checked: showPointLabels,
882882
onCheckedChange: (c) => {
883-
setHidePointLabels(c);
884-
track('interactivity_hide_point_labels_toggled', { enabled: c });
883+
setShowPointLabels(c);
884+
track('interactivity_point_labels_toggled', { enabled: c });
885885
},
886886
},
887887
{
@@ -891,6 +891,9 @@ const GPUGraph = React.memo(
891891
onCheckedChange: (c) => {
892892
setUseAdvancedLabels(c);
893893
track('interactivity_advanced_labels_toggled', { enabled: c });
894+
// Parallelism labels are point labels; turning them on is
895+
// pointless if labels are hidden, so auto-enable Labels.
896+
if (c && !showPointLabels) setShowPointLabels(true);
894897
},
895898
},
896899
{

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

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,8 @@ const ScatterGraph = React.memo(
152152
selectedRunId,
153153
hideNonOptimal,
154154
setHideNonOptimal,
155-
hidePointLabels,
156-
setHidePointLabels,
155+
showPointLabels,
156+
setShowPointLabels,
157157
selectAllHwTypes,
158158
highContrast,
159159
setHighContrast,
@@ -1486,7 +1486,7 @@ const ScatterGraph = React.memo(
14861486
getCssColor(resolveColor(d.hwKey as string)),
14871487
getOpacity: (d) => (isPointVisible(d) ? 1 : 0),
14881488
getPointerEvents: (d) => (isPointVisible(d) ? 'auto' : 'none'),
1489-
hideLabels: hidePointLabels || showGradientLabels,
1489+
hideLabels: !showPointLabels || showGradientLabels,
14901490
getLabelText: (d) => (useAdvancedLabels ? getPointLabel(d) : String(d.tp)),
14911491
foreground: 'var(--foreground)',
14921492
dataAttrs: {
@@ -1585,7 +1585,7 @@ const ScatterGraph = React.memo(
15851585
);
15861586

15871587
// Labels
1588-
const showLabels = !hidePointLabels && !showGradientLabels;
1588+
const showLabels = showPointLabels && !showGradientLabels;
15891589
overlayPoints.each(function (d) {
15901590
d3.select(this)
15911591
.selectAll<SVGTextElement, boolean>('.overlay-label')
@@ -1864,7 +1864,7 @@ const ScatterGraph = React.memo(
18641864
resolveColor,
18651865
pointsData,
18661866
isPointVisible,
1867-
hidePointLabels,
1867+
showPointLabels,
18681868
useAdvancedLabels,
18691869
buildPointConfigId,
18701870
overlayData,
@@ -2154,12 +2154,12 @@ const ScatterGraph = React.memo(
21542154
},
21552155
},
21562156
{
2157-
id: 'scatter-hide-point-labels',
2158-
label: 'Hide Labels',
2159-
checked: hidePointLabels,
2157+
id: 'scatter-point-labels',
2158+
label: 'Labels',
2159+
checked: showPointLabels,
21602160
onCheckedChange: (checked: boolean) => {
2161-
setHidePointLabels(checked);
2162-
track('latency_hide_point_labels_toggled', { enabled: checked });
2161+
setShowPointLabels(checked);
2162+
track('latency_point_labels_toggled', { enabled: checked });
21632163
},
21642164
},
21652165
{
@@ -2178,6 +2178,9 @@ const ScatterGraph = React.memo(
21782178
onCheckedChange: (checked: boolean) => {
21792179
setUseAdvancedLabels(checked);
21802180
track('latency_advanced_labels_toggled', { enabled: checked });
2181+
// Parallelism labels are point labels; turning them on is
2182+
// pointless if labels are hidden, so auto-enable Labels.
2183+
if (checked && !showPointLabels) setShowPointLabels(true);
21812184
if (checked && !showGradientLabels) {
21822185
window.dispatchEvent(
21832186
new CustomEvent(GRADIENT_NUDGE_EVENT, {

packages/app/src/lib/url-state.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ const URL_STATE_KEYS = [
3030
'i_dstart',
3131
'i_dend',
3232
'i_optimal',
33+
'i_label',
34+
// Legacy alias of `i_label` with inverted semantics — read-only on load so
35+
// pre-rename share links (?i_nolabel=1) keep hiding point labels even if the
36+
// default flips again later. New code only writes `i_label`.
3337
'i_nolabel',
3438
'i_hc',
3539
'i_log',
@@ -77,6 +81,7 @@ export const PARAM_DEFAULTS: Record<UrlStateKey, string> = {
7781
i_dstart: '',
7882
i_dend: '',
7983
i_optimal: '',
84+
i_label: '',
8085
i_nolabel: '',
8186
i_hc: '',
8287
i_log: '',

0 commit comments

Comments
 (0)