@@ -121,6 +121,59 @@ describe('inferenceChartToCsv', () => {
121121 expect ( rows ) . toHaveLength ( 0 ) ;
122122 } ) ;
123123
124+ it ( 'only includes data matching selected precisions when pre-filtered (mirrors ChartDisplay export)' , ( ) => {
125+ const fp4Point = makePoint ( { hwKey : 'h100-sxm-sglang' , precision : 'fp4' } ) ;
126+ const fp8Point = makePoint ( { hwKey : 'h100-sxm-sglang' , precision : 'fp8' } ) ;
127+ const allData = [ fp4Point , fp8Point ] ;
128+
129+ // Simulate ChartDisplay.tsx onExportCsv filter
130+ const activeHwTypes = new Set ( [ 'h100-sxm-sglang' ] ) ;
131+ const selectedPrecisions = [ 'fp4' ] ;
132+ const visibleData = allData . filter (
133+ ( d ) => activeHwTypes . has ( d . hwKey as string ) && selectedPrecisions . includes ( d . precision ) ,
134+ ) ;
135+
136+ const { headers, rows } = inferenceChartToCsv ( visibleData , 'llama-3.1-405b' , '1k/1k' ) ;
137+ expect ( rows ) . toHaveLength ( 1 ) ;
138+ expect ( rows [ 0 ] [ headers . indexOf ( 'Precision' ) ] ) . toBe ( 'fp4' ) ;
139+ } ) ;
140+
141+ it ( 'filters by both GPU and precision (mirrors ChartDisplay export)' , ( ) => {
142+ const data = [
143+ makePoint ( { hwKey : 'h100-sxm-sglang' , precision : 'fp4' } ) ,
144+ makePoint ( { hwKey : 'h100-sxm-sglang' , precision : 'fp8' } ) ,
145+ makePoint ( { hwKey : 'b200-sxm-sglang' , precision : 'fp4' } ) ,
146+ makePoint ( { hwKey : 'b200-sxm-sglang' , precision : 'fp8' } ) ,
147+ ] ;
148+
149+ const activeHwTypes = new Set ( [ 'h100-sxm-sglang' ] ) ;
150+ const selectedPrecisions = [ 'fp4' ] ;
151+ const visibleData = data . filter (
152+ ( d ) => activeHwTypes . has ( d . hwKey as string ) && selectedPrecisions . includes ( d . precision ) ,
153+ ) ;
154+
155+ const { headers, rows } = inferenceChartToCsv ( visibleData , 'llama-3.1-405b' , '1k/1k' ) ;
156+ expect ( rows ) . toHaveLength ( 1 ) ;
157+ expect ( rows [ 0 ] [ headers . indexOf ( 'Hardware Key' ) ] ) . toBe ( 'h100-sxm-sglang' ) ;
158+ expect ( rows [ 0 ] [ headers . indexOf ( 'Precision' ) ] ) . toBe ( 'fp4' ) ;
159+ } ) ;
160+
161+ it ( 'includes multiple precisions when all are selected' , ( ) => {
162+ const data = [
163+ makePoint ( { hwKey : 'h100-sxm-sglang' , precision : 'fp4' } ) ,
164+ makePoint ( { hwKey : 'h100-sxm-sglang' , precision : 'fp8' } ) ,
165+ ] ;
166+
167+ const activeHwTypes = new Set ( [ 'h100-sxm-sglang' ] ) ;
168+ const selectedPrecisions = [ 'fp4' , 'fp8' ] ;
169+ const visibleData = data . filter (
170+ ( d ) => activeHwTypes . has ( d . hwKey as string ) && selectedPrecisions . includes ( d . precision ) ,
171+ ) ;
172+
173+ const { rows } = inferenceChartToCsv ( visibleData , 'llama-3.1-405b' , '1k/1k' ) ;
174+ expect ( rows ) . toHaveLength ( 2 ) ;
175+ } ) ;
176+
124177 it ( 'uses empty string for missing optional fields' , ( ) => {
125178 // Minimal point — most AggDataEntry fields are optional via Partial
126179 const data = [
@@ -153,7 +206,7 @@ describe('inferenceChartToCsv', () => {
153206 } ) ;
154207} ) ;
155208
156- describe ( 'reliabilityChartToCsv' , ( ) => {
209+ describe ( 'reliabilityChartToCsv (mirrors ReliabilityChartDisplay export) ' , ( ) => {
157210 it ( 'exports reliability data with correct headers and values' , ( ) => {
158211 const data = [
159212 { model : 'h100-sxm' , modelLabel : 'H100 SXM' , successRate : 99.5 , n_success : 199 , total : 200 } ,
@@ -179,9 +232,22 @@ describe('reliabilityChartToCsv', () => {
179232 expect ( headers ) . toHaveLength ( 5 ) ;
180233 expect ( rows ) . toHaveLength ( 0 ) ;
181234 } ) ;
235+
236+ it ( 'exports all visible GPUs from chartData (no extra filtering needed)' , ( ) => {
237+ // ReliabilityChartDisplay passes chartData directly — no precision/GPU filter
238+ const chartData = [
239+ { model : 'h100-sxm' , modelLabel : 'H100 SXM' , successRate : 99.5 , n_success : 199 , total : 200 } ,
240+ { model : 'a100-sxm' , modelLabel : 'A100 SXM' , successRate : 95.0 , n_success : 95 , total : 100 } ,
241+ ] ;
242+
243+ const { rows } = reliabilityChartToCsv ( chartData ) ;
244+ expect ( rows ) . toHaveLength ( 2 ) ;
245+ expect ( rows [ 0 ] [ 0 ] ) . toBe ( 'H100 SXM' ) ;
246+ expect ( rows [ 1 ] [ 0 ] ) . toBe ( 'A100 SXM' ) ;
247+ } ) ;
182248} ) ;
183249
184- describe ( 'evaluationChartToCsv' , ( ) => {
250+ describe ( 'evaluationChartToCsv (mirrors EvaluationChartDisplay export) ' , ( ) => {
185251 const makeEvalPoint = (
186252 overrides : Partial < {
187253 configLabel : string ;
@@ -245,9 +311,49 @@ describe('evaluationChartToCsv', () => {
245311 expect ( rows [ 0 ] [ headers . indexOf ( 'Min Score' ) ] ) . toBe ( '' ) ;
246312 expect ( rows [ 0 ] [ headers . indexOf ( 'Max Score' ) ] ) . toBe ( '' ) ;
247313 } ) ;
314+
315+ it ( 'exports all chartData entries directly (no extra filtering needed)' , ( ) => {
316+ // EvaluationChartDisplay passes chartData directly from context
317+ const data = [
318+ makeEvalPoint ( { hwKey : 'h100-sxm-vllm' , precision : 'fp8' } ) ,
319+ makeEvalPoint ( { hwKey : 'b200-sxm-sglang' , precision : 'fp4' } ) ,
320+ ] ;
321+
322+ const { headers, rows } = evaluationChartToCsv ( data ) ;
323+ expect ( rows ) . toHaveLength ( 2 ) ;
324+ expect ( rows [ 0 ] [ headers . indexOf ( 'Hardware Key' ) ] ) . toBe ( 'h100-sxm-vllm' ) ;
325+ expect ( rows [ 1 ] [ headers . indexOf ( 'Hardware Key' ) ] ) . toBe ( 'b200-sxm-sglang' ) ;
326+ expect ( rows [ 0 ] [ headers . indexOf ( 'Precision' ) ] ) . toBe ( 'fp8' ) ;
327+ expect ( rows [ 1 ] [ headers . indexOf ( 'Precision' ) ] ) . toBe ( 'fp4' ) ;
328+ } ) ;
329+
330+ it ( 'includes Benchmark column reflecting the selected eval type (pre-filtered by context)' , ( ) => {
331+ // EvaluationContext filters rawData by selectedBenchmark before building chartData,
332+ // so all rows in the export share the same benchmark value
333+ const data = [
334+ makeEvalPoint ( { benchmark : 'mmlu' , hwKey : 'h100-sxm-vllm' } ) ,
335+ makeEvalPoint ( { benchmark : 'mmlu' , hwKey : 'b200-sxm-sglang' } ) ,
336+ ] ;
337+
338+ const { headers, rows } = evaluationChartToCsv ( data ) ;
339+ expect ( headers ) . toContain ( 'Benchmark' ) ;
340+ expect ( rows [ 0 ] [ headers . indexOf ( 'Benchmark' ) ] ) . toBe ( 'mmlu' ) ;
341+ expect ( rows [ 1 ] [ headers . indexOf ( 'Benchmark' ) ] ) . toBe ( 'mmlu' ) ;
342+ } ) ;
343+
344+ it ( 'only contains data for one benchmark at a time (context filters by selectedBenchmark)' , ( ) => {
345+ // Simulates that context already filtered to only 'humaneval' — no 'mmlu' rows leak through
346+ const data = [
347+ makeEvalPoint ( { benchmark : 'humaneval' , hwKey : 'h100-sxm-vllm' } ) ,
348+ makeEvalPoint ( { benchmark : 'humaneval' , hwKey : 'b200-sxm-sglang' } ) ,
349+ ] ;
350+
351+ const { headers, rows } = evaluationChartToCsv ( data ) ;
352+ expect ( rows . every ( ( r ) => r [ headers . indexOf ( 'Benchmark' ) ] === 'humaneval' ) ) . toBe ( true ) ;
353+ } ) ;
248354} ) ;
249355
250- describe ( 'calculatorChartToCsv' , ( ) => {
356+ describe ( 'calculatorChartToCsv (mirrors ThroughputCalculatorDisplay export) ' , ( ) => {
251357 it ( 'exports calculator results with target interactivity' , ( ) => {
252358 const results = [
253359 {
@@ -318,9 +424,48 @@ describe('calculatorChartToCsv', () => {
318424 expect ( headers ) . toHaveLength ( 14 ) ;
319425 expect ( rows ) . toHaveLength ( 0 ) ;
320426 } ) ;
427+
428+ it ( 'exports results with label resolver (mirrors ThroughputCalculatorDisplay export)' , ( ) => {
429+ // ThroughputCalculatorDisplay uses a getLabel that resolves hwKey → display name
430+ const results = [
431+ {
432+ resultKey : 'h100-sxm-sglang' ,
433+ hwKey : 'h100-sxm-sglang' ,
434+ precision : 'FP8' ,
435+ value : 1200 ,
436+ cost : 0.52 ,
437+ concurrency : 16 ,
438+ } ,
439+ {
440+ resultKey : 'b200-sxm-sglang' ,
441+ hwKey : 'b200-sxm-sglang' ,
442+ precision : 'FP4' ,
443+ value : 2000 ,
444+ cost : 0.35 ,
445+ concurrency : 32 ,
446+ } ,
447+ ] ;
448+
449+ const labelMap : Record < string , string > = {
450+ 'h100-sxm-sglang' : 'H100 SXM (SGLang)' ,
451+ 'b200-sxm-sglang' : 'B200 SXM (SGLang)' ,
452+ } ;
453+
454+ const { headers, rows } = calculatorChartToCsv (
455+ results ,
456+ 125 ,
457+ ( hwKey ) => labelMap [ hwKey ] ?? hwKey ,
458+ ) ;
459+ expect ( rows ) . toHaveLength ( 2 ) ;
460+ expect ( rows [ 0 ] [ headers . indexOf ( 'GPU' ) ] ) . toBe ( 'H100 SXM (SGLang)' ) ;
461+ expect ( rows [ 0 ] [ headers . indexOf ( 'Precision' ) ] ) . toBe ( 'FP8' ) ;
462+ expect ( rows [ 0 ] [ headers . indexOf ( 'Cost per Million Total Tokens ($)' ) ] ) . toBe ( 0.52 ) ;
463+ expect ( rows [ 1 ] [ headers . indexOf ( 'GPU' ) ] ) . toBe ( 'B200 SXM (SGLang)' ) ;
464+ expect ( rows [ 1 ] [ headers . indexOf ( 'Precision' ) ] ) . toBe ( 'FP4' ) ;
465+ } ) ;
321466} ) ;
322467
323- describe ( 'historicalTrendToCsv' , ( ) => {
468+ describe ( 'historicalTrendToCsv (mirrors HistoricalTrendsDisplay export) ' , ( ) => {
324469 it ( 'flattens trend lines into rows with GPU labels' , ( ) => {
325470 const trendLines = new Map ( [
326471 [
@@ -378,4 +523,33 @@ describe('historicalTrendToCsv', () => {
378523 expect ( headers ) . toHaveLength ( 8 ) ;
379524 expect ( rows ) . toHaveLength ( 0 ) ;
380525 } ) ;
526+
527+ it ( 'exports with dynamic metric label and target interactivity (mirrors HistoricalTrendsDisplay export)' , ( ) => {
528+ // HistoricalTrendsDisplay passes currentYLabel and targetInteractivity
529+ const trendLines = new Map ( [
530+ [
531+ 'h100-sxm-sglang' ,
532+ [
533+ { date : '2025-01-10' , value : 800 , x : 50 } ,
534+ { date : '2025-01-15' , value : 950 , x : 50 } ,
535+ ] ,
536+ ] ,
537+ ] ) ;
538+
539+ const lineConfigs = [ { id : 'h100-sxm-sglang' , label : 'H100 SXM (SGLang)' , precision : 'fp8' } ] ;
540+
541+ const { headers, rows } = historicalTrendToCsv (
542+ trendLines ,
543+ lineConfigs ,
544+ 'Cost per Million Tokens ($)' ,
545+ 50 ,
546+ ) ;
547+
548+ expect ( headers ) . toContain ( 'Cost per Million Tokens ($)' ) ;
549+ expect ( headers ) . toContain ( 'Target Interactivity (tok/s/user)' ) ;
550+ expect ( rows ) . toHaveLength ( 2 ) ;
551+ expect ( rows [ 0 ] [ headers . indexOf ( 'Cost per Million Tokens ($)' ) ] ) . toBe ( 800 ) ;
552+ expect ( rows [ 0 ] [ headers . indexOf ( 'Target Interactivity (tok/s/user)' ) ] ) . toBe ( 50 ) ;
553+ expect ( rows [ 1 ] [ headers . indexOf ( 'Date' ) ] ) . toBe ( '2025-01-15' ) ;
554+ } ) ;
381555} ) ;
0 commit comments