Update chart and table styling

#1
by JonnaMat - opened
app.js CHANGED
@@ -351,6 +351,10 @@ function buildChart(filtered) {
351
  const gRows = filtered.filter(r => String(r[GROUP_BY]) === String(groupVal));
352
  if (!gRows.length) return;
353
 
 
 
 
 
354
  // If no scenarios configured, show one chart with all rows
355
  const scenarioList = scenarios.length
356
  ? scenarios
@@ -464,7 +468,7 @@ function buildTables(filtered, chartsShown) {
464
 
465
  // Build column list: Model + visible display cols + metrics
466
  const colDefs = [
467
- { key: MODEL_COL, label: "Model", isModel: true },
468
  ...visibleDisplay.map(dc => ({ key: dc.column, label: dc.label, description: dc.description || "" })),
469
  ...config.metrics.map(m => ({ key: m.column, label: m.short || m.column, isMetric: true, description: m.description || "" })),
470
  ];
@@ -519,7 +523,7 @@ function buildTables(filtered, chartsShown) {
519
  const firstMetricIdx = colDefs.findIndex(c => c.isMetric);
520
  html += colDefs.map((c, i) => {
521
  const tip = c.description ? ` data-tip="${c.description.replace(/"/g, '"')}"` : '';
522
- const cls = i === firstMetricIdx ? ' class="first-metric"' : '';
523
  return `<th${tip}${cls}>${c.label}</th>`;
524
  }).join("");
525
  html += `</tr></thead><tbody>`;
@@ -557,13 +561,13 @@ function buildTables(filtered, chartsShown) {
557
  html += `<tr class="${rowClass}">`;
558
  colDefs.forEach((c, i) => {
559
  const val = r[c.key];
560
- const fmCls = i === firstMetricIdx ? ' class="first-metric"' : '';
561
  if (c.isModel) {
562
  const hfUrl = LINK_PREFIX + val;
563
  const modelColor = MODEL_COLORS[val]?.border || '#888';
564
  html += `<td class="model-cell"><span class="model-dot" style="background:${modelColor}"></span><a href="${hfUrl}" target="_blank" rel="noopener" style="color:${modelColor}">${val}</a></td>`;
565
  } else if (oom) {
566
- html += `<td${fmCls}><span class="oom">OOM</span></td>`;
567
  } else if (c.isMetric) {
568
  const sg = groupRowKey(r);
569
  const isBest = val !== null && val !== undefined && val === bestByGroup[sg]?.[c.key];
@@ -629,7 +633,7 @@ function render() {
629
  });
630
 
631
  buildChart(filtered);
632
- const chartsShown = filters[GROUP_BY] !== "all";
633
  // Toggle metric selector visibility
634
  const metricEl = document.getElementById("filter-metric");
635
  if (metricEl) {
 
351
  const gRows = filtered.filter(r => String(r[GROUP_BY]) === String(groupVal));
352
  if (!gRows.length) return;
353
 
354
+ // Don't show chart when there is only a single model
355
+ const uniqueModels = new Set(gRows.map(r => r[MODEL_COL]));
356
+ if (uniqueModels.size <= 1) return;
357
+
358
  // If no scenarios configured, show one chart with all rows
359
  const scenarioList = scenarios.length
360
  ? scenarios
 
468
 
469
  // Build column list: Model + visible display cols + metrics
470
  const colDefs = [
471
+ { key: MODEL_COL, label: "MODEL", isModel: true },
472
  ...visibleDisplay.map(dc => ({ key: dc.column, label: dc.label, description: dc.description || "" })),
473
  ...config.metrics.map(m => ({ key: m.column, label: m.short || m.column, isMetric: true, description: m.description || "" })),
474
  ];
 
523
  const firstMetricIdx = colDefs.findIndex(c => c.isMetric);
524
  html += colDefs.map((c, i) => {
525
  const tip = c.description ? ` data-tip="${c.description.replace(/"/g, '&quot;')}"` : '';
526
+ const cls = i === firstMetricIdx ? ' class="first-metric metric-cell"' : (c.isMetric ? ' class="metric-cell"' : '');
527
  return `<th${tip}${cls}>${c.label}</th>`;
528
  }).join("");
529
  html += `</tr></thead><tbody>`;
 
561
  html += `<tr class="${rowClass}">`;
562
  colDefs.forEach((c, i) => {
563
  const val = r[c.key];
564
+ const fmCls = i === firstMetricIdx ? ' class="first-metric metric-cell"' : ' class="metric-cell"';
565
  if (c.isModel) {
566
  const hfUrl = LINK_PREFIX + val;
567
  const modelColor = MODEL_COLORS[val]?.border || '#888';
568
  html += `<td class="model-cell"><span class="model-dot" style="background:${modelColor}"></span><a href="${hfUrl}" target="_blank" rel="noopener" style="color:${modelColor}">${val}</a></td>`;
569
  } else if (oom) {
570
+ html += `<td${c.isMetric ? fmCls : ''}><span class="oom">OOM</span></td>`;
571
  } else if (c.isMetric) {
572
  const sg = groupRowKey(r);
573
  const isBest = val !== null && val !== undefined && val === bestByGroup[sg]?.[c.key];
 
633
  });
634
 
635
  buildChart(filtered);
636
+ const chartsShown = charts.length > 0;
637
  // Toggle metric selector visibility
638
  const metricEl = document.getElementById("filter-metric");
639
  if (metricEl) {
config.json CHANGED
@@ -37,21 +37,21 @@
37
  {
38
  "column": "tpot",
39
  "label": "Time per Output Token (ms)",
40
- "short": "TPOT ↓",
41
  "higher_is_better": false,
42
  "description": "Time per output token in ms (lower is better). Average time (in milliseconds) required to generate one output token during decoding. Computed as TPOT = (last_token_ts - first_token_ts) / total_output_tokens."
43
  },
44
  {
45
  "column": "ttft",
46
  "label": "Time to First Token (ms)",
47
- "short": "TTFT ↓",
48
  "higher_is_better": false,
49
  "description": "Time to first token in ms (lower is better). Time from request submission to generation of the first output token. This includes vision encoding, prompt prefill, KV cache initialization."
50
  },
51
  {
52
  "column": "e2e",
53
  "label": "End-to-End Latency (sec)",
54
- "short": "E2E ↓",
55
  "higher_is_better": false,
56
  "description": "End-to-end latency in seconds (lower is better). Total time from request submission to completion of the full generated response. This reflects real user-perceived latency."
57
  }
@@ -59,7 +59,7 @@
59
  "display_columns": [
60
  {
61
  "column": "res",
62
- "label": "Resolution",
63
  "visible_when": {
64
  "type": [
65
  "video",
@@ -128,12 +128,12 @@
128
  "table_group_by": "model",
129
  "model_families": {
130
  "Cosmos-Reason2-2B": {
131
- "data_file": "data/cosmos-reason2.csv",
132
  "table_group_by": ["res", "fps"],
133
  "experiment_setup": {
134
  "agx_thor": "Measurement setup: NVIDIA vLLM 26.01, 256 tokens generated, 10 warm-up runs, averaged over 25 runs.",
135
- "agx_orin": "Measurement setup: NVIDIA vLLM 0.14.0 for Jetson, 256 tokens generated, 10 warm-up runs, averaged over 25 runs.",
136
- "orin_nano": "Measurement setup: NVIDIA vLLM 0.14.0 for Jetson, 256 tokens generated, 10 warm-up runs, averaged over 25 runs."
137
  }
138
  }
139
  }
 
37
  {
38
  "column": "tpot",
39
  "label": "Time per Output Token (ms)",
40
+ "short": "TPOT(ms) ↓",
41
  "higher_is_better": false,
42
  "description": "Time per output token in ms (lower is better). Average time (in milliseconds) required to generate one output token during decoding. Computed as TPOT = (last_token_ts - first_token_ts) / total_output_tokens."
43
  },
44
  {
45
  "column": "ttft",
46
  "label": "Time to First Token (ms)",
47
+ "short": "TTFT(ms) ↓",
48
  "higher_is_better": false,
49
  "description": "Time to first token in ms (lower is better). Time from request submission to generation of the first output token. This includes vision encoding, prompt prefill, KV cache initialization."
50
  },
51
  {
52
  "column": "e2e",
53
  "label": "End-to-End Latency (sec)",
54
+ "short": "E2E(s) ↓",
55
  "higher_is_better": false,
56
  "description": "End-to-end latency in seconds (lower is better). Total time from request submission to completion of the full generated response. This reflects real user-perceived latency."
57
  }
 
59
  "display_columns": [
60
  {
61
  "column": "res",
62
+ "label": "RESOLUTION",
63
  "visible_when": {
64
  "type": [
65
  "video",
 
128
  "table_group_by": "model",
129
  "model_families": {
130
  "Cosmos-Reason2-2B": {
131
+ "data_file": "data/Cosmos-Reason2.csv",
132
  "table_group_by": ["res", "fps"],
133
  "experiment_setup": {
134
  "agx_thor": "Measurement setup: NVIDIA vLLM 26.01, 256 tokens generated, 10 warm-up runs, averaged over 25 runs.",
135
+ "agx_orin": "Measurement setup: NVIDIA AI IoT vLLM 0.14.0 tegra, 256 tokens generated, 10 warm-up runs, averaged over 25 runs.",
136
+ "orin_nano": "Measurement setup: NVIDIA AI IoT vLLM 0.14.0 tegra, 256 tokens generated, 10 warm-up runs, averaged over 25 runs."
137
  }
138
  }
139
  }
data/{cosmos-reason2.csv β†’ Cosmos-Reason2.csv} RENAMED
File without changes
style.css CHANGED
@@ -183,7 +183,6 @@ code {
183
  display: block;
184
  font-size: 0.8rem;
185
  font-weight: 600;
186
- text-transform: uppercase;
187
  letter-spacing: 0.08em;
188
  color: var(--text-dim);
189
  margin-bottom: 0.25rem;
@@ -300,7 +299,6 @@ thead th {
300
  text-align: left;
301
  font-weight: 600;
302
  font-size: 0.8rem;
303
- text-transform: uppercase;
304
  letter-spacing: 0.05em;
305
  color: var(--text-dim);
306
  padding: 0.5rem 0.75rem;
@@ -315,6 +313,11 @@ tbody td {
315
  color: var(--text-muted);
316
  }
317
 
 
 
 
 
 
318
  tbody tr:last-child td {
319
  border-bottom: 1px solid var(--border);
320
  }
 
183
  display: block;
184
  font-size: 0.8rem;
185
  font-weight: 600;
 
186
  letter-spacing: 0.08em;
187
  color: var(--text-dim);
188
  margin-bottom: 0.25rem;
 
299
  text-align: left;
300
  font-weight: 600;
301
  font-size: 0.8rem;
 
302
  letter-spacing: 0.05em;
303
  color: var(--text-dim);
304
  padding: 0.5rem 0.75rem;
 
313
  color: var(--text-muted);
314
  }
315
 
316
+ th.metric-cell,
317
+ td.metric-cell {
318
+ text-align: right;
319
+ }
320
+
321
  tbody tr:last-child td {
322
  border-bottom: 1px solid var(--border);
323
  }