CP Legendre commited on
Commit
e4bbf8f
·
1 Parent(s): 344a63b

Address chart styling feedback

Browse files
Files changed (2) hide show
  1. app.py +63 -120
  2. src/charts.py +98 -50
app.py CHANGED
@@ -65,114 +65,18 @@ API = HfApi(token=TOKEN)
65
  COLOR_BY_CHOICES = ["Model", "Harness"]
66
  COLOR_PALETTE_CHOICES = ["Citrus", "Okabe-Ito", "High contrast", "Rainbow"]
67
  DEFAULT_COLOR_PALETTE = "Citrus"
68
-
69
-
70
- APP_CSS = """
71
- :root,
72
- body,
73
- .gradio-container {
74
- color-scheme: dark;
75
- background: #15110F !important;
76
- color: #F8FAFC !important;
77
- }
78
-
79
- .gradio-container,
80
- .main,
81
- .wrap,
82
- .contain,
83
- #component-0 {
84
- background: #15110F !important;
85
- }
86
-
87
- .markdown-text,
88
- .markdown-text p,
89
- .markdown-text li,
90
- .markdown-text td,
91
- .markdown-text th,
92
- .gradio-container label,
93
- .gradio-container .prose,
94
- .gradio-container .prose * {
95
- color: #E5E7EB !important;
96
- }
97
-
98
- .gradio-container h1,
99
- .gradio-container h2,
100
- .gradio-container h3,
101
- .gradio-container h4 {
102
- color: #F8FAFC !important;
103
- }
104
-
105
- .gradio-container .block,
106
- .gradio-container .form,
107
- .gradio-container .panel,
108
- .gradio-container .tabitem,
109
- .gradio-container .tabs,
110
- .gradio-container .plot-container,
111
- .gradio-container .js-plotly-plot {
112
- background: #27211E !important;
113
- color: #F8FAFC !important;
114
- border-color: rgba(248, 250, 252, 0.14) !important;
115
- }
116
-
117
- .gradio-container input,
118
- .gradio-container textarea,
119
- .gradio-container select,
120
- .gradio-container button,
121
- .gradio-container .wrap-inner {
122
- color: #F8FAFC !important;
123
- }
124
-
125
- /* Radio/dropdown color controls need explicit contrast. Gradio's citrus theme can
126
- otherwise produce light text on light inactive radio buttons. */
127
- .gradio-container .color-control label,
128
- .gradio-container .color-control label span,
129
- .gradio-container .color-control button,
130
- .gradio-container .color-control .wrap label span {
131
- color: #F8FAFC !important;
132
- }
133
-
134
- .gradio-container .color-control label:not(:has(input:checked)),
135
- .gradio-container .color-control button:not([aria-pressed="true"]) {
136
- background: #3B332F !important;
137
- color: #F8FAFC !important;
138
- border-color: rgba(248, 250, 252, 0.18) !important;
139
- }
140
-
141
- .gradio-container .color-control label:has(input:checked),
142
- .gradio-container .color-control label.selected,
143
- .gradio-container .color-control button.selected,
144
- .gradio-container .color-control button[aria-pressed="true"] {
145
- background: #F59E0B !important;
146
- color: #111827 !important;
147
- border-color: #FBBF24 !important;
148
- }
149
-
150
- .gradio-container .color-control label:has(input:checked) span,
151
- .gradio-container .color-control label.selected span,
152
- .gradio-container .color-control button.selected,
153
- .gradio-container .color-control button[aria-pressed="true"] {
154
- color: #111827 !important;
155
- }
156
-
157
- .gradio-container .color-control input[type="radio"] {
158
- accent-color: #F59E0B;
159
- }
160
-
161
- /* Let Plotly consume the available card width instead of keeping a fixed SVG size. */
162
- .gradio-container .plot-container,
163
- .gradio-container .js-plotly-plot,
164
- .gradio-container .plotly,
165
- .gradio-container .svg-container,
166
- .gradio-container .main-svg {
167
- width: 100% !important;
168
- max-width: 100% !important;
169
- }
170
-
171
- .gradio-container .js-plotly-plot,
172
- .gradio-container .svg-container {
173
- min-height: 520px !important;
174
- height: clamp(520px, 58vh, 760px) !important;
175
- }
176
  """
177
 
178
 
@@ -186,21 +90,33 @@ BENCHMARK_RUN_DF = get_benchmark_run_df()
186
  SCORE_VS_COST_DF = get_score_vs_cost_df()
187
 
188
 
189
- def render_leaderboard_benchmark_plot(benchmark_name, color_by, color_palette=DEFAULT_COLOR_PALETTE):
 
 
 
 
 
190
  return create_leaderboard_benchmark_plot(
191
  BENCHMARK_RUN_DF,
192
  benchmark_name=benchmark_name,
193
  color_by=color_by,
194
  palette_name=color_palette,
 
195
  )
196
 
197
 
198
- def render_score_vs_cost_plot(benchmark_name, color_by, color_palette=DEFAULT_COLOR_PALETTE):
 
 
 
 
 
199
  return create_score_vs_cost_plot(
200
  SCORE_VS_COST_DF,
201
  benchmark_name=benchmark_name,
202
  color_by=color_by,
203
  palette_name=color_palette,
 
204
  )
205
 
206
 
@@ -273,7 +189,7 @@ def init_benchmark_runs(dataframe):
273
  )
274
 
275
 
276
- demo = gr.Blocks(theme="citrus", css=APP_CSS)
277
  with demo:
278
  gr.HTML(build_header_html(BENCHMARK_RUN_DF))
279
  gr.Markdown(INTRODUCTION_TEXT, elem_classes="markdown-text")
@@ -294,24 +210,35 @@ with demo:
294
  label="Color palette",
295
  elem_classes="color-control",
296
  )
 
 
 
 
 
 
297
 
298
  leaderboard_plots = []
299
  for benchmark_name in BENCHMARK_NAMES:
300
  gr.Markdown(f"#### {benchmark_name}")
301
  plot = gr.Plot(
302
- value=render_leaderboard_benchmark_plot(benchmark_name, "Model", DEFAULT_COLOR_PALETTE),
 
 
 
 
 
303
  show_label=False,
304
  elem_classes="responsive-plot",
305
  )
306
  leaderboard_plots.append((benchmark_name, plot))
307
 
308
  for benchmark_name, plot in leaderboard_plots:
309
- for control in (leaderboard_color_by, leaderboard_palette):
310
  control.change(
311
- fn=lambda color_by, palette, name=benchmark_name: render_leaderboard_benchmark_plot(
312
- name, color_by, palette
313
  ),
314
- inputs=[leaderboard_color_by, leaderboard_palette],
315
  outputs=plot,
316
  )
317
 
@@ -334,24 +261,40 @@ with demo:
334
  label="Color palette",
335
  elem_classes="color-control",
336
  )
 
 
 
 
 
 
337
  score_vs_cost_plot = gr.Plot(
338
- value=render_score_vs_cost_plot(DEFAULT_BENCHMARK, "Model", DEFAULT_COLOR_PALETTE),
 
 
 
 
 
339
  show_label=False,
340
  elem_classes="responsive-plot",
341
  )
342
  cost_benchmark.change(
343
  fn=render_score_vs_cost_plot,
344
- inputs=[cost_benchmark, cost_color_by, cost_palette],
345
  outputs=score_vs_cost_plot,
346
  )
347
  cost_color_by.change(
348
  fn=render_score_vs_cost_plot,
349
- inputs=[cost_benchmark, cost_color_by, cost_palette],
350
  outputs=score_vs_cost_plot,
351
  )
352
  cost_palette.change(
353
  fn=render_score_vs_cost_plot,
354
- inputs=[cost_benchmark, cost_color_by, cost_palette],
 
 
 
 
 
355
  outputs=score_vs_cost_plot,
356
  )
357
 
 
65
  COLOR_BY_CHOICES = ["Model", "Harness"]
66
  COLOR_PALETTE_CHOICES = ["Citrus", "Okabe-Ito", "High contrast", "Rainbow"]
67
  DEFAULT_COLOR_PALETTE = "Citrus"
68
+ PLOT_BACKGROUND_CHOICES = ["Dark", "White"]
69
+ DEFAULT_PLOT_BACKGROUND = "Dark"
70
+ FORCE_DARK_MODE_HEAD = """
71
+ <script>
72
+ (() => {
73
+ const url = new URL(window.location.href);
74
+ if (!url.searchParams.has("__theme")) {
75
+ url.searchParams.set("__theme", "dark");
76
+ window.location.replace(url.toString());
77
+ }
78
+ })();
79
+ </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  """
81
 
82
 
 
90
  SCORE_VS_COST_DF = get_score_vs_cost_df()
91
 
92
 
93
+ def render_leaderboard_benchmark_plot(
94
+ benchmark_name,
95
+ color_by,
96
+ color_palette=DEFAULT_COLOR_PALETTE,
97
+ plot_background=DEFAULT_PLOT_BACKGROUND,
98
+ ):
99
  return create_leaderboard_benchmark_plot(
100
  BENCHMARK_RUN_DF,
101
  benchmark_name=benchmark_name,
102
  color_by=color_by,
103
  palette_name=color_palette,
104
+ background_name=plot_background,
105
  )
106
 
107
 
108
+ def render_score_vs_cost_plot(
109
+ benchmark_name,
110
+ color_by,
111
+ color_palette=DEFAULT_COLOR_PALETTE,
112
+ plot_background=DEFAULT_PLOT_BACKGROUND,
113
+ ):
114
  return create_score_vs_cost_plot(
115
  SCORE_VS_COST_DF,
116
  benchmark_name=benchmark_name,
117
  color_by=color_by,
118
  palette_name=color_palette,
119
+ background_name=plot_background,
120
  )
121
 
122
 
 
189
  )
190
 
191
 
192
+ demo = gr.Blocks(theme="citrus", head=FORCE_DARK_MODE_HEAD)
193
  with demo:
194
  gr.HTML(build_header_html(BENCHMARK_RUN_DF))
195
  gr.Markdown(INTRODUCTION_TEXT, elem_classes="markdown-text")
 
210
  label="Color palette",
211
  elem_classes="color-control",
212
  )
213
+ leaderboard_background = gr.Dropdown(
214
+ choices=PLOT_BACKGROUND_CHOICES,
215
+ value=DEFAULT_PLOT_BACKGROUND,
216
+ label="Image background",
217
+ elem_classes="color-control",
218
+ )
219
 
220
  leaderboard_plots = []
221
  for benchmark_name in BENCHMARK_NAMES:
222
  gr.Markdown(f"#### {benchmark_name}")
223
  plot = gr.Plot(
224
+ value=render_leaderboard_benchmark_plot(
225
+ benchmark_name,
226
+ "Model",
227
+ DEFAULT_COLOR_PALETTE,
228
+ DEFAULT_PLOT_BACKGROUND,
229
+ ),
230
  show_label=False,
231
  elem_classes="responsive-plot",
232
  )
233
  leaderboard_plots.append((benchmark_name, plot))
234
 
235
  for benchmark_name, plot in leaderboard_plots:
236
+ for control in (leaderboard_color_by, leaderboard_palette, leaderboard_background):
237
  control.change(
238
+ fn=lambda color_by, palette, background, name=benchmark_name: render_leaderboard_benchmark_plot(
239
+ name, color_by, palette, background
240
  ),
241
+ inputs=[leaderboard_color_by, leaderboard_palette, leaderboard_background],
242
  outputs=plot,
243
  )
244
 
 
261
  label="Color palette",
262
  elem_classes="color-control",
263
  )
264
+ cost_background = gr.Dropdown(
265
+ choices=PLOT_BACKGROUND_CHOICES,
266
+ value=DEFAULT_PLOT_BACKGROUND,
267
+ label="Image background",
268
+ elem_classes="color-control",
269
+ )
270
  score_vs_cost_plot = gr.Plot(
271
+ value=render_score_vs_cost_plot(
272
+ DEFAULT_BENCHMARK,
273
+ "Model",
274
+ DEFAULT_COLOR_PALETTE,
275
+ DEFAULT_PLOT_BACKGROUND,
276
+ ),
277
  show_label=False,
278
  elem_classes="responsive-plot",
279
  )
280
  cost_benchmark.change(
281
  fn=render_score_vs_cost_plot,
282
+ inputs=[cost_benchmark, cost_color_by, cost_palette, cost_background],
283
  outputs=score_vs_cost_plot,
284
  )
285
  cost_color_by.change(
286
  fn=render_score_vs_cost_plot,
287
+ inputs=[cost_benchmark, cost_color_by, cost_palette, cost_background],
288
  outputs=score_vs_cost_plot,
289
  )
290
  cost_palette.change(
291
  fn=render_score_vs_cost_plot,
292
+ inputs=[cost_benchmark, cost_color_by, cost_palette, cost_background],
293
+ outputs=score_vs_cost_plot,
294
+ )
295
+ cost_background.change(
296
+ fn=render_score_vs_cost_plot,
297
+ inputs=[cost_benchmark, cost_color_by, cost_palette, cost_background],
298
  outputs=score_vs_cost_plot,
299
  )
300
 
src/charts.py CHANGED
@@ -10,16 +10,18 @@ from plotly.graph_objs._figure import Figure
10
 
11
  ColorBy = Literal["Model", "Harness"]
12
  PaletteName = Literal["Citrus", "Okabe-Ito", "High contrast", "Rainbow"]
 
13
  DEFAULT_PALETTE: PaletteName = "Citrus"
 
14
 
15
  # Separate categorical palettes for each grouping dimension.
16
  # Model and harness colors intentionally start from different hue families so
17
  # switching "Color by" remains visually obvious.
18
  MODEL_COLORS: dict[str, str] = {
19
- "GPT 5.5 - high": "#F59E0B", # amber
20
- "Opus 4.8": "#84CC16", # lime
21
  "RedHatAI/Qwen3.6-35B-A3B-NVFP4": "#F97316", # orange
22
- "Sonnet 4.6": "#22C55E", # green
23
  }
24
 
25
  HARNESS_COLORS: dict[str, str] = {
@@ -33,16 +35,16 @@ HARNESS_COLORS: dict[str, str] = {
33
  }
34
 
35
  MODEL_FALLBACK_PALETTE = [
36
- "#F59E0B", # amber
37
- "#84CC16", # lime
38
- "#F97316", # orange
39
- "#22C55E", # green
40
- "#EAB308", # yellow
41
- "#FB7185", # rose
42
- "#A3E635", # light lime
43
- "#FACC15", # gold
44
- "#F472B6", # pink
45
- "#2DD4BF", # teal
46
  ]
47
 
48
  HARNESS_FALLBACK_PALETTE = [
@@ -66,6 +68,30 @@ TEXT_MUTED = "#CBD5E1"
66
  GRID_COLOR = "rgba(248,250,252,0.14)"
67
  ZERO_LINE_COLOR = "rgba(248,250,252,0.24)"
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  def clean_markdown_link(value: object) -> str:
70
  """Return human-readable text from Markdown links used in leaderboard tables."""
71
  text = str(value).replace("<sup>*</sup>", "")
@@ -102,6 +128,18 @@ def normalize_palette_name(palette_name: str | None) -> PaletteName:
102
  return DEFAULT_PALETTE
103
 
104
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  def stable_color(name: str, color_by: ColorBy, palette_name: str | None = DEFAULT_PALETTE) -> str:
106
  palette_key = normalize_palette_name(palette_name)
107
  palettes = MODEL_PALETTES if color_by == "Model" else HARNESS_PALETTES
@@ -158,7 +196,8 @@ def color_map_for(
158
  }
159
 
160
 
161
- def empty_figure(message: str) -> Figure:
 
162
  fig = go.Figure()
163
  fig.add_annotation(
164
  text=message,
@@ -167,50 +206,52 @@ def empty_figure(message: str) -> Figure:
167
  y=0.5,
168
  xref="paper",
169
  yref="paper",
170
- font={"size": 14, "color": TEXT_MUTED},
171
  )
172
- return apply_plot_theme(fig)
173
 
174
 
175
- def apply_plot_theme(fig: Figure) -> Figure:
 
176
  fig.update_layout(
177
- template="plotly_dark",
178
  autosize=True,
179
- paper_bgcolor="rgba(0,0,0,0)",
180
- plot_bgcolor=DARK_PLOT,
181
- font={"color": TEXT_PRIMARY},
182
- title={"font": {"color": TEXT_PRIMARY}},
183
- margin={"t": 64, "b": 72, "l": 72, "r": 36},
184
  legend={
185
  "orientation": "h",
186
- "yanchor": "bottom",
187
- "y": 1.02,
188
  "xanchor": "center",
189
  "x": 0.5,
190
- "font": {"color": TEXT_MUTED},
191
  },
192
  )
193
  # Remove fixed dimensions so Gradio/Plotly can size to the browser/container.
194
  fig.update_layout(width=None, height=None)
195
  fig.update_xaxes(
196
- color=TEXT_MUTED,
197
- gridcolor=GRID_COLOR,
198
- zerolinecolor=ZERO_LINE_COLOR,
199
- linecolor=GRID_COLOR,
200
- title_font={"color": TEXT_MUTED},
201
- tickfont={"color": TEXT_MUTED},
 
202
  )
203
  fig.update_yaxes(
204
- color=TEXT_MUTED,
205
- gridcolor=GRID_COLOR,
206
- zerolinecolor=ZERO_LINE_COLOR,
207
- linecolor=GRID_COLOR,
208
- title_font={"color": TEXT_MUTED},
209
- tickfont={"color": TEXT_MUTED},
 
210
  )
211
  return fig
212
 
213
-
214
  def prepare_benchmark_run_plot_df(dataframe: pd.DataFrame) -> pd.DataFrame:
215
  plot_df = dataframe.copy()
216
  plot_df["Model Label"] = plot_df["Model"].map(clean_markdown_link)
@@ -226,19 +267,21 @@ def create_leaderboard_benchmark_plot(
226
  benchmark_name: str,
227
  color_by: ColorBy = "Model",
228
  palette_name: str | None = DEFAULT_PALETTE,
 
229
  ) -> Figure:
230
  if dataframe is None or dataframe.empty:
231
- return empty_figure("No benchmark data available.")
232
 
233
  plot_df = prepare_benchmark_run_plot_df(dataframe)
234
  plot_df = plot_df[plot_df["Benchmark Label"] == benchmark_name].dropna(subset=["Score"])
235
  plot_df = plot_df.sort_values("Score", ascending=False)
236
 
237
  if plot_df.empty:
238
- return empty_figure(f"No results available for {benchmark_name}.")
239
 
240
  color_source = "Model Label" if color_by == "Model" else "Harness Label"
241
  colors = color_map_for(plot_df[color_source], color_by, palette_name)
 
242
  fig = go.Figure()
243
 
244
  for group, group_df in plot_df.groupby(color_source, sort=True):
@@ -247,7 +290,10 @@ def create_leaderboard_benchmark_plot(
247
  x=group_df["Run Label"],
248
  y=group_df["Score"],
249
  name=str(group),
250
- marker={"color": colors[str(group)]},
 
 
 
251
  text=group_df["Score"].map(lambda score: f"{score:.1f}"),
252
  textposition="outside",
253
  customdata=group_df[["Model Label", "Harness Label", "Score"]],
@@ -261,14 +307,14 @@ def create_leaderboard_benchmark_plot(
261
  )
262
 
263
  fig.update_layout(
264
- title={"text": benchmark_name, "font": {"size": 18}},
265
  xaxis={"title": "Model / Harness", "categoryorder": "array", "categoryarray": plot_df["Run Label"].tolist()},
266
  yaxis={"title": "Score (%)", "range": [0, 105]},
267
  legend_title_text=color_by,
268
  bargap=0.28,
269
  )
270
  fig.update_xaxes(tickangle=-28)
271
- return apply_plot_theme(fig)
272
 
273
 
274
  def create_score_vs_cost_plot(
@@ -276,12 +322,13 @@ def create_score_vs_cost_plot(
276
  benchmark_name: str | None,
277
  color_by: ColorBy = "Model",
278
  palette_name: str | None = DEFAULT_PALETTE,
 
279
  ) -> Figure:
280
  if dataframe is None or dataframe.empty:
281
- return empty_figure("No cost data available.")
282
 
283
  if not benchmark_name:
284
- return empty_figure("Select a benchmark to view cost data.")
285
 
286
  plot_df = dataframe.copy()
287
  plot_df = plot_df[plot_df["Benchmark"] == benchmark_name]
@@ -290,9 +337,10 @@ def create_score_vs_cost_plot(
290
  plot_df = plot_df.dropna(subset=["Score", "Cost Per Task (USD)"])
291
 
292
  if plot_df.empty:
293
- return empty_figure(f"No cost data available for {benchmark_name}.")
294
 
295
  colors = color_map_for(plot_df[color_by], color_by, palette_name)
 
296
  fig = go.Figure()
297
 
298
  for group, group_df in plot_df.groupby(color_by, sort=True):
@@ -307,7 +355,7 @@ def create_score_vs_cost_plot(
307
  marker={
308
  "size": 15,
309
  "color": colors[str(group)],
310
- "line": {"width": 1, "color": DARK_PAPER},
311
  },
312
  customdata=group_df[["Model", "Harness", "Benchmark", "Score", "Cost Per Task (USD)"]],
313
  hovertemplate=(
@@ -322,9 +370,9 @@ def create_score_vs_cost_plot(
322
  )
323
 
324
  fig.update_layout(
325
- title={"text": f"{benchmark_name}: Cost vs Performance", "font": {"size": 18}},
326
  xaxis={"title": "Cost per task (USD)", "tickprefix": "$", "tickformat": ".2f"},
327
  yaxis={"title": "Score (%)", "range": [0, 105]},
328
  legend_title_text=color_by,
329
  )
330
- return apply_plot_theme(fig)
 
10
 
11
  ColorBy = Literal["Model", "Harness"]
12
  PaletteName = Literal["Citrus", "Okabe-Ito", "High contrast", "Rainbow"]
13
+ PlotBackground = Literal["Dark", "White"]
14
  DEFAULT_PALETTE: PaletteName = "Citrus"
15
+ DEFAULT_BACKGROUND: PlotBackground = "Dark"
16
 
17
  # Separate categorical palettes for each grouping dimension.
18
  # Model and harness colors intentionally start from different hue families so
19
  # switching "Color by" remains visually obvious.
20
  MODEL_COLORS: dict[str, str] = {
21
+ "GPT 5.5 - high": "#F8FAFC", # white
22
+ "Opus 4.8": "#FEF3C7", # cream
23
  "RedHatAI/Qwen3.6-35B-A3B-NVFP4": "#F97316", # orange
24
+ "Sonnet 4.6": "#DC2626", # red
25
  }
26
 
27
  HARNESS_COLORS: dict[str, str] = {
 
35
  }
36
 
37
  MODEL_FALLBACK_PALETTE = [
38
+ "#F8FAFC", # white
39
+ "#FEF3C7", # cream
40
+ "#FACC15", # yellow
41
+ "#FB923C", # orange
42
+ "#DC2626", # red
43
+ "#93C5FD", # blue fallback
44
+ "#22C55E", # green fallback
45
+ "#C084FC", # violet fallback
46
+ "#F472B6", # pink fallback
47
+ "#14B8A6", # teal fallback
48
  ]
49
 
50
  HARNESS_FALLBACK_PALETTE = [
 
68
  GRID_COLOR = "rgba(248,250,252,0.14)"
69
  ZERO_LINE_COLOR = "rgba(248,250,252,0.24)"
70
 
71
+ PLOT_BACKGROUNDS: dict[PlotBackground, dict[str, str]] = {
72
+ "Dark": {
73
+ "template": "plotly_dark",
74
+ "paper_bgcolor": DARK_CARD,
75
+ "plot_bgcolor": DARK_PLOT,
76
+ "text_primary": TEXT_PRIMARY,
77
+ "text_muted": TEXT_MUTED,
78
+ "grid_color": GRID_COLOR,
79
+ "zero_line_color": ZERO_LINE_COLOR,
80
+ "marker_line_color": DARK_PAPER,
81
+ },
82
+ "White": {
83
+ "template": "plotly_white",
84
+ "paper_bgcolor": "#FFFFFF",
85
+ "plot_bgcolor": "#FFFFFF",
86
+ "text_primary": "#0F172A",
87
+ "text_muted": "#475569",
88
+ "grid_color": "rgba(15,23,42,0.12)",
89
+ "zero_line_color": "rgba(15,23,42,0.25)",
90
+ "marker_line_color": "#334155",
91
+ },
92
+ }
93
+
94
+
95
  def clean_markdown_link(value: object) -> str:
96
  """Return human-readable text from Markdown links used in leaderboard tables."""
97
  text = str(value).replace("<sup>*</sup>", "")
 
128
  return DEFAULT_PALETTE
129
 
130
 
131
+ def normalize_background_name(background_name: str | None) -> PlotBackground:
132
+ if background_name == "Current":
133
+ return "Dark"
134
+ if background_name in PLOT_BACKGROUNDS:
135
+ return background_name # type: ignore[return-value]
136
+ return DEFAULT_BACKGROUND
137
+
138
+
139
+ def get_plot_background(background_name: str | None = DEFAULT_BACKGROUND) -> dict[str, str]:
140
+ return PLOT_BACKGROUNDS[normalize_background_name(background_name)]
141
+
142
+
143
  def stable_color(name: str, color_by: ColorBy, palette_name: str | None = DEFAULT_PALETTE) -> str:
144
  palette_key = normalize_palette_name(palette_name)
145
  palettes = MODEL_PALETTES if color_by == "Model" else HARNESS_PALETTES
 
196
  }
197
 
198
 
199
+ def empty_figure(message: str, background_name: str | None = DEFAULT_BACKGROUND) -> Figure:
200
+ theme = get_plot_background(background_name)
201
  fig = go.Figure()
202
  fig.add_annotation(
203
  text=message,
 
206
  y=0.5,
207
  xref="paper",
208
  yref="paper",
209
+ font={"size": 14, "color": theme["text_muted"]},
210
  )
211
+ return apply_plot_theme(fig, background_name)
212
 
213
 
214
+ def apply_plot_theme(fig: Figure, background_name: str | None = DEFAULT_BACKGROUND) -> Figure:
215
+ theme = get_plot_background(background_name)
216
  fig.update_layout(
217
+ template=theme["template"],
218
  autosize=True,
219
+ paper_bgcolor=theme["paper_bgcolor"],
220
+ plot_bgcolor=theme["plot_bgcolor"],
221
+ font={"color": theme["text_primary"]},
222
+ title={"font": {"color": theme["text_primary"]}},
223
+ margin={"t": 0, "b": 0, "l": 0, "r": 0},
224
  legend={
225
  "orientation": "h",
226
+ "yanchor": "top",
227
+ "y": 1,
228
  "xanchor": "center",
229
  "x": 0.5,
230
+ "font": {"color": theme["text_muted"]},
231
  },
232
  )
233
  # Remove fixed dimensions so Gradio/Plotly can size to the browser/container.
234
  fig.update_layout(width=None, height=None)
235
  fig.update_xaxes(
236
+ automargin=True,
237
+ color=theme["text_muted"],
238
+ gridcolor=theme["grid_color"],
239
+ zerolinecolor=theme["zero_line_color"],
240
+ linecolor=theme["grid_color"],
241
+ title_font={"color": theme["text_muted"]},
242
+ tickfont={"color": theme["text_muted"]},
243
  )
244
  fig.update_yaxes(
245
+ automargin=True,
246
+ color=theme["text_muted"],
247
+ gridcolor=theme["grid_color"],
248
+ zerolinecolor=theme["zero_line_color"],
249
+ linecolor=theme["grid_color"],
250
+ title_font={"color": theme["text_muted"]},
251
+ tickfont={"color": theme["text_muted"]},
252
  )
253
  return fig
254
 
 
255
  def prepare_benchmark_run_plot_df(dataframe: pd.DataFrame) -> pd.DataFrame:
256
  plot_df = dataframe.copy()
257
  plot_df["Model Label"] = plot_df["Model"].map(clean_markdown_link)
 
267
  benchmark_name: str,
268
  color_by: ColorBy = "Model",
269
  palette_name: str | None = DEFAULT_PALETTE,
270
+ background_name: str | None = DEFAULT_BACKGROUND,
271
  ) -> Figure:
272
  if dataframe is None or dataframe.empty:
273
+ return empty_figure("No benchmark data available.", background_name)
274
 
275
  plot_df = prepare_benchmark_run_plot_df(dataframe)
276
  plot_df = plot_df[plot_df["Benchmark Label"] == benchmark_name].dropna(subset=["Score"])
277
  plot_df = plot_df.sort_values("Score", ascending=False)
278
 
279
  if plot_df.empty:
280
+ return empty_figure(f"No results available for {benchmark_name}.", background_name)
281
 
282
  color_source = "Model Label" if color_by == "Model" else "Harness Label"
283
  colors = color_map_for(plot_df[color_source], color_by, palette_name)
284
+ theme = get_plot_background(background_name)
285
  fig = go.Figure()
286
 
287
  for group, group_df in plot_df.groupby(color_source, sort=True):
 
290
  x=group_df["Run Label"],
291
  y=group_df["Score"],
292
  name=str(group),
293
+ marker={
294
+ "color": colors[str(group)],
295
+ "line": {"width": 1, "color": theme["marker_line_color"]},
296
+ },
297
  text=group_df["Score"].map(lambda score: f"{score:.1f}"),
298
  textposition="outside",
299
  customdata=group_df[["Model Label", "Harness Label", "Score"]],
 
307
  )
308
 
309
  fig.update_layout(
310
+ title=None,
311
  xaxis={"title": "Model / Harness", "categoryorder": "array", "categoryarray": plot_df["Run Label"].tolist()},
312
  yaxis={"title": "Score (%)", "range": [0, 105]},
313
  legend_title_text=color_by,
314
  bargap=0.28,
315
  )
316
  fig.update_xaxes(tickangle=-28)
317
+ return apply_plot_theme(fig, background_name)
318
 
319
 
320
  def create_score_vs_cost_plot(
 
322
  benchmark_name: str | None,
323
  color_by: ColorBy = "Model",
324
  palette_name: str | None = DEFAULT_PALETTE,
325
+ background_name: str | None = DEFAULT_BACKGROUND,
326
  ) -> Figure:
327
  if dataframe is None or dataframe.empty:
328
+ return empty_figure("No cost data available.", background_name)
329
 
330
  if not benchmark_name:
331
+ return empty_figure("Select a benchmark to view cost data.", background_name)
332
 
333
  plot_df = dataframe.copy()
334
  plot_df = plot_df[plot_df["Benchmark"] == benchmark_name]
 
337
  plot_df = plot_df.dropna(subset=["Score", "Cost Per Task (USD)"])
338
 
339
  if plot_df.empty:
340
+ return empty_figure(f"No cost data available for {benchmark_name}.", background_name)
341
 
342
  colors = color_map_for(plot_df[color_by], color_by, palette_name)
343
+ theme = get_plot_background(background_name)
344
  fig = go.Figure()
345
 
346
  for group, group_df in plot_df.groupby(color_by, sort=True):
 
355
  marker={
356
  "size": 15,
357
  "color": colors[str(group)],
358
+ "line": {"width": 1, "color": theme["marker_line_color"]},
359
  },
360
  customdata=group_df[["Model", "Harness", "Benchmark", "Score", "Cost Per Task (USD)"]],
361
  hovertemplate=(
 
370
  )
371
 
372
  fig.update_layout(
373
+ title=None,
374
  xaxis={"title": "Cost per task (USD)", "tickprefix": "$", "tickformat": ".2f"},
375
  yaxis={"title": "Score (%)", "range": [0, 105]},
376
  legend_title_text=color_by,
377
  )
378
+ return apply_plot_theme(fig, background_name)