danemery-ai2 commited on
Commit
17162c9
·
unverified ·
1 Parent(s): 8939028

Custom plot legends (#62)

Browse files
assets/ellipse-coral.svg ADDED
assets/ellipse-pink.svg ADDED
assets/ellipse-white.svg ADDED
assets/ellipse-yellow.svg ADDED
assets/five-point-star.svg ADDED
assets/four-point-star.svg ADDED
assets/pareto.svg ADDED
assets/three-point-star.svg ADDED
content.py CHANGED
@@ -656,8 +656,67 @@ span.wrap[tabindex="0"][role="button"][data-editable="false"] {
656
  .info-list {
657
  padding-left: 20px;
658
  }
 
659
  /* Smooth scrolling for the entire page */
660
  html {
661
  scroll-behavior: smooth;
662
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
663
  """
 
656
  .info-list {
657
  padding-left: 20px;
658
  }
659
+
660
  /* Smooth scrolling for the entire page */
661
  html {
662
  scroll-behavior: smooth;
663
  }
664
+
665
+ /* Plot legend styles */
666
+ .plot-legend-container {
667
+ height: 572px;
668
+ background-color: #fff;
669
+ padding: 24px 32px;
670
+ }
671
+
672
+ .dark .plot-legend-container {
673
+ background: rgba(250, 242, 233, 0.1);
674
+ }
675
+
676
+ #plot-legend-logo {
677
+ margin-bottom: 24px;
678
+ }
679
+
680
+ #plot-legend-logo img {
681
+ height: 19px;
682
+ }
683
+
684
+ .plot-legend-category-heading {
685
+ font-size: 16px;
686
+ font-weight: 700;
687
+ }
688
+
689
+ .plot-legend-item {
690
+ margin-top: 8px;
691
+ }
692
+
693
+ .plot-legend-item-text {
694
+ display: inline-block;
695
+ }
696
+
697
+ .plot-legend-item-text .description {
698
+ color: #888;
699
+ font-size: 12px;
700
+ }
701
+
702
+ .plot-legend-item-svg {
703
+ display: inline-block;
704
+ vertical-align: top;
705
+ margin-top: 3px;
706
+ width: 14px;
707
+ height: 14px;
708
+ margin-right: 8px;
709
+ }
710
+
711
+ .plot-legend-tooling-svg {
712
+ height: 16px;
713
+ width: 16px;
714
+ margin-top: 2px;
715
+ }
716
+
717
+ #plot-legend-item-pareto-svg {
718
+ width: 18px;
719
+ height: 18px;
720
+ margin-right: 2px;
721
+ }
722
  """
leaderboard_transformer.py CHANGED
@@ -445,6 +445,7 @@ def _plot_scatter_plotly(
445
  y=frontier_df['y'],
446
  mode='lines',
447
  name='Efficiency Frontier',
 
448
  line=dict(color='#0FCB8C', width=2, dash='dash'),
449
  hoverinfo='skip'
450
  ))
@@ -516,31 +517,6 @@ def _plot_scatter_plotly(
516
  line=dict(width=1, color='deeppink')
517
  )
518
  ))
519
- # ---- Add logic for making the legend -----------
520
- for i, category in enumerate(colors_for_legend):
521
- fig.add_trace(go.Scatter(
522
- x=[None], y=[None],
523
- mode='markers',
524
- name=category,
525
- legendgroup="openness_group",
526
- legendgrouptitle_text="Agent Openness" if i == 0 else None,
527
- marker=dict(
528
- color=color_map.get(category, 'grey'),
529
- symbol='circle',
530
- size=12
531
- )
532
- ))
533
-
534
- # Part B: Dummy traces for the SHAPES ("Agent Tooling")
535
- for i, shape_name in enumerate(shapes_for_legend):
536
- fig.add_trace(go.Scatter(
537
- x=[None], y=[None],
538
- mode='markers',
539
- name=shape_name,
540
- legendgroup="tooling_group",
541
- legendgrouptitle_text="Agent Tooling" if i == 0 else None,
542
- marker=dict(color='black', symbol=shape_map.get(shape_name), size=12)
543
- ))
544
 
545
  # --- Section 8: Configure Layout ---
546
  xaxis_config = dict(title=x_axis_label, rangemode="tozero")
@@ -567,12 +543,13 @@ def _plot_scatter_plotly(
567
  legend=dict(
568
  bgcolor='#FAF2E9',
569
  ),
 
570
  hoverlabel=dict(
571
  bgcolor="#105257",
572
  font_size=12,
573
  font_family="Manrope",
574
  font_color="#d3dedc",
575
- )
576
  )
577
  fig.add_layout_image(
578
  dict(
 
445
  y=frontier_df['y'],
446
  mode='lines',
447
  name='Efficiency Frontier',
448
+ showlegend=False,
449
  line=dict(color='#0FCB8C', width=2, dash='dash'),
450
  hoverinfo='skip'
451
  ))
 
517
  line=dict(width=1, color='deeppink')
518
  )
519
  ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
 
521
  # --- Section 8: Configure Layout ---
522
  xaxis_config = dict(title=x_axis_label, rangemode="tozero")
 
543
  legend=dict(
544
  bgcolor='#FAF2E9',
545
  ),
546
+ height=572,
547
  hoverlabel=dict(
548
  bgcolor="#105257",
549
  font_size=12,
550
  font_family="Manrope",
551
  font_color="#d3dedc",
552
+ ),
553
  )
554
  fig.add_layout_image(
555
  dict(
ui_components.py CHANGED
@@ -76,15 +76,36 @@ for canonical_openness, openness_aliases in aliases.OPENNESS_ALIASES.items():
76
 
77
 
78
  OPENNESS_SVG_MAP = {
79
- "Open Source + Open Weights": "assets/os-ow-legend.svg",
80
- "Open Source + Closed Weights": "assets/os-legend.svg",
81
- "API Available": "assets/api-legend.svg",
82
- "Closed Source & UI only": "assets/c-legend.svg",
 
 
 
 
 
 
 
 
 
 
 
 
83
  }
84
  TOOLING_SVG_MAP = {
85
- "Standard": "assets/standard-legend.svg",
86
- "Custom Interface": "assets/equivalent-legend.svg",
87
- "Fully Custom": "assets/custom-legend.svg",
 
 
 
 
 
 
 
 
 
88
  }
89
 
90
  def get_svg_as_data_uri(path: str) -> str:
@@ -129,8 +150,13 @@ def create_svg_html(value, svg_map):
129
  return ""
130
 
131
  path_info = svg_map[value]
 
 
 
 
 
132
 
133
- src = get_svg_as_data_uri(path_info)
134
  # Generate the HTML for the single icon, with NO text.
135
  if src:
136
  return f'<img src="{src}" style="width: 16px; height: 16px; vertical-align: middle;" alt="{value}" title="{value}">'
@@ -147,11 +173,10 @@ def build_openness_tooltip_content() -> str:
147
  "Closed Source + UI Only": "No access to code or API; UI access only",
148
  }
149
  html_items = []
150
- for name, path in OPENNESS_SVG_MAP.items():
151
- uri = get_svg_as_data_uri(path)
152
  desc = descriptions.get(name, "")
153
 
154
- # Create the HTML for a single row in the tooltip legend
155
  html_items.append(f"""
156
  <div class="tooltip-legend-item">
157
  <img src="{uri}" alt="{name}">
@@ -196,8 +221,8 @@ def build_tooling_tooltip_content() -> str:
196
  </ul>
197
  """
198
  html_items = []
199
- for name, path in TOOLING_SVG_MAP.items():
200
- uri = get_svg_as_data_uri(path)
201
  desc = descriptions.get(name, "")
202
 
203
  # Check if this is the special case that needs a sub-list
@@ -266,8 +291,8 @@ openness_html = " ".join([create_svg_html(name, OPENNESS_SVG_MAP) for name in OP
266
  tooling_html = " ".join([create_svg_html(name, TOOLING_SVG_MAP) for name in TOOLING_SVG_MAP])
267
  # Create HTML for the "Openness" legend items
268
  openness_html_items = []
269
- for name, path in OPENNESS_SVG_MAP.items():
270
- uri = get_svg_as_data_uri(path)
271
  # Each item is now its own flexbox container to guarantee alignment
272
  openness_html_items.append(
273
  f'<div style="display: flex; align-items: center; white-space: nowrap;">'
@@ -279,8 +304,8 @@ openness_html = " ".join(openness_html_items)
279
 
280
  # Create HTML for the "Tooling" legend items
281
  tooling_html_items = []
282
- for name, path in TOOLING_SVG_MAP.items():
283
- uri = get_svg_as_data_uri(path)
284
  tooling_html_items.append(
285
  f'<div style="display: flex; align-items: center; white-space: nowrap;">'
286
  f'<img src="{uri}" alt="{name}" title="{name}" style="width:16px; height:16px; margin-right: 4px; flex-shrink: 0;">'
@@ -355,6 +380,68 @@ def create_legend_markdown(which_table: str) -> str:
355
  """
356
  return legend_markdown
357
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  # --- Global State for Viewers (simple caching) ---
359
  CACHED_VIEWERS = {}
360
  CACHED_TAG_MAPS = {}
@@ -505,11 +592,16 @@ def create_leaderboard_display(
505
  # 5. Combine all the lists to create the final, fully dynamic list.
506
  final_column_widths = fixed_start_widths + dynamic_widths + fixed_end_widths
507
 
508
- plot_component = gr.Plot(
509
- value=scatter_plot,
510
- show_label=False
511
- )
 
 
 
 
512
  gr.Markdown(value=SCATTER_DISCLAIMER, elem_id="scatter-disclaimer")
 
513
  # Put table and key into an accordion
514
  with gr.Accordion("Show / Hide Table View", open=True, elem_id="leaderboard-accordion"):
515
  dataframe_component = gr.DataFrame(
@@ -668,8 +760,13 @@ def create_benchmark_details_display(
668
  agent_col="Agent",
669
  name=benchmark_name
670
  )
671
- gr.Plot(value=benchmark_plot, show_label=False)
 
 
 
 
672
  gr.Markdown(value=SCATTER_DISCLAIMER, elem_id="scatter-disclaimer")
 
673
  # Put table and key into an accordion
674
  with gr.Accordion("Show / Hide Table View", open=True, elem_id="leaderboard-accordion"):
675
  gr.DataFrame(
 
76
 
77
 
78
  OPENNESS_SVG_MAP = {
79
+ "Open source & open weights": {
80
+ "path": "assets/ellipse-pink.svg",
81
+ "description": "Code and models are open"
82
+ },
83
+ "Open source & closed weights": {
84
+ "path": "assets/ellipse-coral.svg",
85
+ "description": "Code is open but uses closed-weight models"
86
+ },
87
+ "Closed source & API available": {
88
+ "path": "assets/ellipse-yellow.svg",
89
+ "description": "No access to code; API access only"
90
+ },
91
+ "Closed source & UI only": {
92
+ "path": "assets/ellipse-white.svg",
93
+ "description": "No access to code or API; UI access only"
94
+ },
95
  }
96
  TOOLING_SVG_MAP = {
97
+ "Standard": {
98
+ "path": "assets/five-point-star.svg",
99
+ "description": "Uses only tools explicitly provided in state.tools"
100
+ },
101
+ "Custom interface": {
102
+ "path": "assets/four-point-star.svg",
103
+ "description": "Custom tools for accessing an equivalent underlying environment"
104
+ },
105
+ "Fully custom": {
106
+ "path": "assets/three-point-star.svg",
107
+ "description": "Uses tools beyond constraints of Standard or Custom interface"
108
+ },
109
  }
110
 
111
  def get_svg_as_data_uri(path: str) -> str:
 
150
  return ""
151
 
152
  path_info = svg_map[value]
153
+ # Handle both old string format and new object format
154
+ if isinstance(path_info, dict):
155
+ path = path_info["path"]
156
+ else:
157
+ path = path_info
158
 
159
+ src = get_svg_as_data_uri(path)
160
  # Generate the HTML for the single icon, with NO text.
161
  if src:
162
  return f'<img src="{src}" style="width: 16px; height: 16px; vertical-align: middle;" alt="{value}" title="{value}">'
 
173
  "Closed Source + UI Only": "No access to code or API; UI access only",
174
  }
175
  html_items = []
176
+ for name, info in OPENNESS_SVG_MAP.items():
177
+ uri = get_svg_as_data_uri(info["path"])
178
  desc = descriptions.get(name, "")
179
 
 
180
  html_items.append(f"""
181
  <div class="tooltip-legend-item">
182
  <img src="{uri}" alt="{name}">
 
221
  </ul>
222
  """
223
  html_items = []
224
+ for name, info in TOOLING_SVG_MAP.items():
225
+ uri = get_svg_as_data_uri(info["path"])
226
  desc = descriptions.get(name, "")
227
 
228
  # Check if this is the special case that needs a sub-list
 
291
  tooling_html = " ".join([create_svg_html(name, TOOLING_SVG_MAP) for name in TOOLING_SVG_MAP])
292
  # Create HTML for the "Openness" legend items
293
  openness_html_items = []
294
+ for name, info in OPENNESS_SVG_MAP.items():
295
+ uri = get_svg_as_data_uri(info["path"])
296
  # Each item is now its own flexbox container to guarantee alignment
297
  openness_html_items.append(
298
  f'<div style="display: flex; align-items: center; white-space: nowrap;">'
 
304
 
305
  # Create HTML for the "Tooling" legend items
306
  tooling_html_items = []
307
+ for name, info in TOOLING_SVG_MAP.items():
308
+ uri = get_svg_as_data_uri(info["path"])
309
  tooling_html_items.append(
310
  f'<div style="display: flex; align-items: center; white-space: nowrap;">'
311
  f'<img src="{uri}" alt="{name}" title="{name}" style="width:16px; height:16px; margin-right: 4px; flex-shrink: 0;">'
 
380
  """
381
  return legend_markdown
382
 
383
+ # Create HTML for plot legend with SVG icons and keys
384
+ openness_legend_items = []
385
+ for name, info in OPENNESS_SVG_MAP.items():
386
+ uri = get_svg_as_data_uri(info["path"])
387
+ if uri:
388
+ openness_legend_items.append(
389
+ f'<div class="plot-legend-item">'
390
+ f'<img class="plot-legend-item-svg" src="{uri}" alt="{name}" title="{name}">'
391
+ f'<div class="plot-legend-item-text">'
392
+ f'<div>'
393
+ f'<span>{name}</span>'
394
+ f'</div>'
395
+ f'<span class="description">{info["description"]}</span>'
396
+ f'</div>'
397
+ f'</div>'
398
+ )
399
+
400
+ tooling_legend_items = []
401
+ for name, info in TOOLING_SVG_MAP.items():
402
+ uri = get_svg_as_data_uri(info["path"])
403
+ if uri:
404
+ tooling_legend_items.append(
405
+ f'<div class="plot-legend-item">'
406
+ f'<img class="plot-legend-item-svg plot-legend-tooling-svg" src="{uri}" alt="{name}" title="{name}">'
407
+ f'<div class="plot-legend-item-text">'
408
+ f'<div>'
409
+ f'<span>{name}</span>'
410
+ f'</div>'
411
+ f'<span class="description">{info["description"]}</span>'
412
+ f'</div>'
413
+ f'</div>'
414
+ )
415
+
416
+ plot_legend_html = f"""
417
+ <div class="plot-legend-container">
418
+ <div id="plot-legend-logo">
419
+ <img src="{get_svg_as_data_uri("assets/logo.svg")}">
420
+ </div>
421
+ <div style="margin-bottom: 16px;">
422
+ <span class="plot-legend-category-heading">Pareto</span>
423
+ <div style="margin-top: 8px;">
424
+ <div class="plot-legend-item">
425
+ <img id="plot-legend-item-pareto-svg" class="plot-legend-item-svg" src="{get_svg_as_data_uri("assets/pareto.svg")}">
426
+ <span>On frontier</span>
427
+ </div>
428
+ </div>
429
+ </div>
430
+ <div style="margin-bottom: 16px;">
431
+ <span class="plot-legend-category-heading">Agent Openness</span>
432
+ <div style="margin-top: 8px;">
433
+ {''.join(openness_legend_items)}
434
+ </div>
435
+ </div>
436
+ <div>
437
+ <span class="plot-legend-category-heading">Agent Tooling</span>
438
+ <div style="margin-top: 8px;">
439
+ {''.join(tooling_legend_items)}
440
+ </div>
441
+ </div>
442
+ </div>
443
+ """;
444
+
445
  # --- Global State for Viewers (simple caching) ---
446
  CACHED_VIEWERS = {}
447
  CACHED_TAG_MAPS = {}
 
592
  # 5. Combine all the lists to create the final, fully dynamic list.
593
  final_column_widths = fixed_start_widths + dynamic_widths + fixed_end_widths
594
 
595
+ with gr.Row():
596
+ with gr.Column(scale=3):
597
+ plot_component = gr.Plot(
598
+ value=scatter_plot,
599
+ show_label=False
600
+ )
601
+ with gr.Column(scale=1):
602
+ gr.HTML(value=plot_legend_html)
603
  gr.Markdown(value=SCATTER_DISCLAIMER, elem_id="scatter-disclaimer")
604
+
605
  # Put table and key into an accordion
606
  with gr.Accordion("Show / Hide Table View", open=True, elem_id="leaderboard-accordion"):
607
  dataframe_component = gr.DataFrame(
 
760
  agent_col="Agent",
761
  name=benchmark_name
762
  )
763
+ with gr.Row():
764
+ with gr.Column(scale=3):
765
+ gr.Plot(value=benchmark_plot, show_label=False)
766
+ with gr.Column(scale=1):
767
+ gr.HTML(value=plot_legend_html)
768
  gr.Markdown(value=SCATTER_DISCLAIMER, elem_id="scatter-disclaimer")
769
+
770
  # Put table and key into an accordion
771
  with gr.Accordion("Show / Hide Table View", open=True, elem_id="leaderboard-accordion"):
772
  gr.DataFrame(