diff --git a/packages/app/cypress/component/scatter-graph.cy.tsx b/packages/app/cypress/component/scatter-graph.cy.tsx index 692f6a77..edfe55d0 100644 --- a/packages/app/cypress/component/scatter-graph.cy.tsx +++ b/packages/app/cypress/component/scatter-graph.cy.tsx @@ -917,6 +917,8 @@ describe('ChartDisplay engine comparison guard', () => { hw: 'Official SGLang', model: Model.DeepSeek_V4_Pro, precision: Precision.FP4, + p75_ttft: 0.75, + p75_intvty: 75, }); const runUrl = 'https://github.com/x/y/actions/runs/456'; const overlayRow = createMockInferenceData({ @@ -924,6 +926,8 @@ describe('ChartDisplay engine comparison guard', () => { hw: 'Unofficial vLLM', model: Model.DeepSeek_V4_Pro, precision: Precision.FP4, + p75_ttft: 0.85, + p75_intvty: 65, run_url: runUrl, }); const exclusion = buildExclusion([ @@ -968,6 +972,7 @@ describe('ChartDisplay engine comparison guard', () => { selectedModel: Model.DeepSeek_V4_Pro, selectedSequence: Sequence.AgenticTraces, selectedXAxisMode: 'interactivity', + selectedPercentile: 'p75', activeHwTypes: new Set(['b200_sglang']), hwTypesWithData: new Set(['b200_sglang']), resolveComparisonSelection: resolveSelection, @@ -992,6 +997,15 @@ describe('ChartDisplay engine comparison guard', () => { cy.get('[data-testid="inference-results-table"] tbody tr').should('have.length', 2); cy.get('[data-testid="inference-results-table"] tbody').contains('vLLM').should('exist'); cy.get('[data-testid="inference-results-table"] tbody').contains('SGLang').should('exist'); + cy.get('[data-testid="inference-results-table"] thead') + .should('contain.text', 'P75 TTFT (ms)') + .and('contain.text', 'P75 Interactivity (tok/s)') + .and('not.contain.text', 'Median TTFT'); + cy.get('[data-testid="inference-results-table"] tbody') + .should('contain.text', '750') + .and('contain.text', '850') + .and('contain.text', '75.0') + .and('contain.text', '65.0'); // The reconciliation effect must not strip the run's hw types from the provider. cy.get('@setActiveOverlayHwTypes').should('not.have.been.called'); }); diff --git a/packages/app/src/components/inference/ui/ChartDisplay.tsx b/packages/app/src/components/inference/ui/ChartDisplay.tsx index 76d3e6f4..8d660297 100644 --- a/packages/app/src/components/inference/ui/ChartDisplay.tsx +++ b/packages/app/src/components/inference/ui/ChartDisplay.tsx @@ -871,6 +871,7 @@ export default function ChartDisplay() { data={[...officialRows, ...overlayRows]} chartDefinition={graph.chartDefinition} selectedYAxisMetric={selectedYAxisMetric} + percentile={isAgenticSequence ? selectedPercentile : 'median'} /> ); diff --git a/packages/app/src/components/inference/ui/InferenceTable.tsx b/packages/app/src/components/inference/ui/InferenceTable.tsx index 5bdb713c..2812251f 100644 --- a/packages/app/src/components/inference/ui/InferenceTable.tsx +++ b/packages/app/src/components/inference/ui/InferenceTable.tsx @@ -13,6 +13,7 @@ interface InferenceTableProps { data: InferenceData[]; chartDefinition: ChartDefinition; selectedYAxisMetric: string; + percentile?: string; } /** Format a number for table display — picks sensible precision based on magnitude. */ @@ -28,10 +29,14 @@ export default function InferenceTable({ data, chartDefinition, selectedYAxisMetric, + percentile = 'median', }: InferenceTableProps) { const yPath = chartDefinition[selectedYAxisMetric as keyof ChartDefinition] as string | undefined; const yLabel = chartDefinition[`${selectedYAxisMetric}_label` as keyof ChartDefinition] as string; const xLabel = chartDefinition.x_label; + const percentileLabel = percentile === 'median' ? 'Median' : percentile.toUpperCase(); + const ttftKey = `${percentile}_ttft` as keyof InferenceData; + const interactivityKey = `${percentile}_intvty` as keyof InferenceData; const rooflineDir = chartDefinition[ `${selectedYAxisMetric}_roofline` as keyof ChartDefinition @@ -97,21 +102,21 @@ export default function InferenceTable({ className: 'tabular-nums', }, { - header: 'Median TTFT (ms)', + header: `${percentileLabel} TTFT (ms)`, align: 'right', - cell: (row) => fmt((row.median_ttft ?? 0) * 1000, 0), - sortValue: (row) => row.median_ttft ?? 0, + cell: (row) => fmt(((row[ttftKey] as number | undefined) ?? 0) * 1000, 0), + sortValue: (row) => (row[ttftKey] as number | undefined) ?? 0, className: 'tabular-nums', }, { - header: 'Median Interactivity (tok/s)', + header: `${percentileLabel} Interactivity (tok/s)`, align: 'right', - cell: (row) => fmt(row.median_intvty ?? 0, 1), - sortValue: (row) => row.median_intvty ?? 0, + cell: (row) => fmt((row[interactivityKey] as number | undefined) ?? 0, 1), + sortValue: (row) => (row[interactivityKey] as number | undefined) ?? 0, className: 'tabular-nums', }, ], - [yPath, yLabel, xLabel], + [yPath, yLabel, xLabel, percentileLabel, ttftKey, interactivityKey], ); return (