openhands openhands commited on
Commit
76b9525
·
1 Parent(s): c14a283

Update visualization styling to match existing charts

Browse files

- Use company logos as markers instead of colored dots
- Apply standard layout configuration (template, height, hoverlabel)
- Use domain coordinates for logo positioning
- Add model name labels above each point
- Match hover text formatting with existing charts

Co-authored-by: openhands <openhands@all-hands.dev>

Files changed (1) hide show
  1. visualizations.py +293 -131
visualizations.py CHANGED
@@ -5,12 +5,40 @@ import pandas as pd
5
  import plotly.graph_objects as go
6
  import plotly.express as px
7
  from datetime import datetime
 
 
8
  import aliases
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  def create_evolution_over_time_chart(df: pd.DataFrame) -> go.Figure:
12
  """
13
- Create a line chart showing model performance evolution over release dates.
 
14
 
15
  Args:
16
  df: DataFrame with columns including 'release_date' or 'Release_Date', 'Language Model', 'average score', 'openness'
@@ -31,8 +59,9 @@ def create_evolution_over_time_chart(df: pd.DataFrame) -> go.Figure:
31
  text="No release date data available",
32
  xref="paper", yref="paper",
33
  x=0.5, y=0.5, showarrow=False,
34
- font=dict(size=16)
35
  )
 
36
  return fig
37
 
38
  # Filter out rows without release dates
@@ -44,8 +73,9 @@ def create_evolution_over_time_chart(df: pd.DataFrame) -> go.Figure:
44
  text="No release date data available",
45
  xref="paper", yref="paper",
46
  x=0.5, y=0.5, showarrow=False,
47
- font=dict(size=16)
48
  )
 
49
  return fig
50
 
51
  # Convert release_date to datetime (normalize column name)
@@ -73,8 +103,9 @@ def create_evolution_over_time_chart(df: pd.DataFrame) -> go.Figure:
73
  text="No score data available",
74
  xref="paper", yref="paper",
75
  x=0.5, y=0.5, showarrow=False,
76
- font=dict(size=16)
77
  )
 
78
  return fig
79
 
80
  # Get model name column
@@ -86,46 +117,9 @@ def create_evolution_over_time_chart(df: pd.DataFrame) -> go.Figure:
86
  if model_col is None:
87
  model_col = 'Language Model' # Default
88
 
89
- # Map openness to colors
90
- color_map = {
91
- aliases.CANONICAL_OPENNESS_OPEN: "#F0529C", # Pink for open
92
- aliases.CANONICAL_OPENNESS_CLOSED: "#FFD700", # Yellow/gold for closed
93
- }
94
- for canonical_openness, openness_aliases in aliases.OPENNESS_ALIASES.items():
95
- for openness_alias in openness_aliases:
96
- color_map[openness_alias] = color_map[canonical_openness]
97
-
98
  fig = go.Figure()
99
 
100
- # Add scatter points for each model
101
- for _, row in plot_df.iterrows():
102
- openness = row.get('Openness', row.get('openness', 'unknown'))
103
- color = color_map.get(openness, '#888888')
104
- model_name = row.get(model_col, 'Unknown')
105
-
106
- fig.add_trace(go.Scatter(
107
- x=[row['release_date']],
108
- y=[row[score_col]],
109
- mode='markers+text',
110
- marker=dict(
111
- size=12,
112
- color=color,
113
- line=dict(width=1, color='#333333')
114
- ),
115
- text=[model_name],
116
- textposition='top center',
117
- textfont=dict(size=10),
118
- name=model_name,
119
- hovertemplate=(
120
- f"<b>{model_name}</b><br>"
121
- f"Release: %{{x|%Y-%m-%d}}<br>"
122
- f"Score: %{{y:.1f}}<br>"
123
- f"<extra></extra>"
124
- ),
125
- showlegend=False
126
- ))
127
-
128
- # Add trend line
129
  if len(plot_df) > 1:
130
  fig.add_trace(go.Scatter(
131
  x=plot_df['release_date'],
@@ -137,54 +131,133 @@ def create_evolution_over_time_chart(df: pd.DataFrame) -> go.Figure:
137
  showlegend=False
138
  ))
139
 
140
- # Update layout
141
- fig.update_layout(
142
- title=dict(
143
- text="Model Performance Evolution Over Time",
144
- font=dict(size=18)
145
- ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  xaxis=dict(
147
  title="Model Release Date",
148
- showgrid=True,
149
- gridcolor='rgba(128,128,128,0.2)'
150
  ),
151
  yaxis=dict(
152
  title="Average Score",
153
- showgrid=True,
154
- gridcolor='rgba(128,128,128,0.2)'
155
  ),
156
- plot_bgcolor='rgba(0,0,0,0)',
157
- paper_bgcolor='rgba(0,0,0,0)',
158
- hovermode='closest',
159
- margin=dict(l=60, r=40, t=60, b=60),
160
- height=400
161
  )
162
 
163
- # Add legend for openness
164
- fig.add_trace(go.Scatter(
165
- x=[None], y=[None],
166
- mode='markers',
167
- marker=dict(size=10, color='#F0529C'),
168
- name='Open Weights',
169
- showlegend=True
170
- ))
171
- fig.add_trace(go.Scatter(
172
- x=[None], y=[None],
173
- mode='markers',
174
- marker=dict(size=10, color='#FFD700'),
175
- name='Closed',
176
- showlegend=True
177
- ))
178
 
179
- fig.update_layout(
180
- legend=dict(
181
- orientation="h",
182
- yanchor="bottom",
183
- y=1.02,
184
- xanchor="right",
185
- x=1
186
- )
187
- )
188
 
189
  return fig
190
 
@@ -192,6 +265,7 @@ def create_evolution_over_time_chart(df: pd.DataFrame) -> go.Figure:
192
  def create_accuracy_by_size_chart(df: pd.DataFrame) -> go.Figure:
193
  """
194
  Create a scatter plot showing accuracy vs parameter count for open-weights models.
 
195
 
196
  Args:
197
  df: DataFrame with columns including 'parameter_count_b' or 'Parameter_Count_B',
@@ -201,6 +275,8 @@ def create_accuracy_by_size_chart(df: pd.DataFrame) -> go.Figure:
201
  Returns:
202
  Plotly figure showing accuracy vs model size
203
  """
 
 
204
  # Handle different column name formats for parameter count
205
  param_col = None
206
  for col in ['parameter_count_b', 'Parameter_Count_B', 'Parameter Count B']:
@@ -220,8 +296,9 @@ def create_accuracy_by_size_chart(df: pd.DataFrame) -> go.Figure:
220
  text="No parameter count data available",
221
  xref="paper", yref="paper",
222
  x=0.5, y=0.5, showarrow=False,
223
- font=dict(size=16)
224
  )
 
225
  return fig
226
 
227
  # Filter to only open-weights models with parameter data
@@ -241,8 +318,9 @@ def create_accuracy_by_size_chart(df: pd.DataFrame) -> go.Figure:
241
  text="No open-weights models with parameter data available",
242
  xref="paper", yref="paper",
243
  x=0.5, y=0.5, showarrow=False,
244
- font=dict(size=16)
245
  )
 
246
  return fig
247
 
248
  # Get the score column (handle different naming conventions)
@@ -263,8 +341,9 @@ def create_accuracy_by_size_chart(df: pd.DataFrame) -> go.Figure:
263
  text="No score data available",
264
  xref="paper", yref="paper",
265
  x=0.5, y=0.5, showarrow=False,
266
- font=dict(size=16)
267
  )
 
268
  return fig
269
 
270
  # Get model name column
@@ -278,8 +357,13 @@ def create_accuracy_by_size_chart(df: pd.DataFrame) -> go.Figure:
278
 
279
  fig = go.Figure()
280
 
281
- # Determine if we should use active params (for MoE models) or total params
282
- # Use active params if available, otherwise total params
 
 
 
 
 
283
  for _, row in plot_df.iterrows():
284
  total_params = row[param_col]
285
  active_params = row.get(active_param_col) if active_param_col else None
@@ -289,66 +373,144 @@ def create_accuracy_by_size_chart(df: pd.DataFrame) -> go.Figure:
289
  # Use active params for x-axis if available (more meaningful for MoE)
290
  x_val = active_params if pd.notna(active_params) else total_params
291
 
292
- # Create hover text
293
- hover_text = f"<b>{model_name}</b><br>"
294
- hover_text += f"Total Params: {total_params:.0f}B<br>"
295
- if pd.notna(active_params):
296
- hover_text += f"Active Params: {active_params:.0f}B<br>"
297
- hover_text += f"Score: {score:.1f}<br>"
298
 
299
- # Marker size based on total params (larger models = larger markers)
300
- marker_size = 10 + (total_params / 100) # Scale marker size
301
- marker_size = min(marker_size, 30) # Cap at 30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
 
303
- fig.add_trace(go.Scatter(
304
- x=[x_val],
305
- y=[score],
306
- mode='markers+text',
307
- marker=dict(
308
- size=marker_size,
309
- color='#F0529C', # Pink for open models
310
- line=dict(width=1, color='#333333'),
311
- opacity=0.8
312
- ),
313
- text=[model_name],
314
- textposition='top center',
315
- textfont=dict(size=10),
316
- name=model_name,
317
- hovertemplate=hover_text + "<extra></extra>",
318
- showlegend=False
319
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
 
321
- # Update layout
322
- fig.update_layout(
323
- title=dict(
324
- text="Open Model Accuracy by Size",
325
- font=dict(size=18)
326
- ),
327
  xaxis=dict(
328
  title="Active Parameters (Billions)",
329
- showgrid=True,
330
- gridcolor='rgba(128,128,128,0.2)',
331
- type='log' # Log scale for better visualization
332
  ),
333
  yaxis=dict(
334
  title="Average Score",
335
- showgrid=True,
336
- gridcolor='rgba(128,128,128,0.2)'
337
  ),
338
- plot_bgcolor='rgba(0,0,0,0)',
339
- paper_bgcolor='rgba(0,0,0,0)',
340
- hovermode='closest',
341
- margin=dict(l=60, r=40, t=60, b=60),
342
- height=400
343
  )
344
 
 
 
 
 
 
 
345
  # Add annotation explaining marker size
346
  fig.add_annotation(
347
- text="Marker size indicates total parameter count",
348
  xref="paper", yref="paper",
349
- x=0.02, y=-0.12,
350
  showarrow=False,
351
- font=dict(size=10, color='gray'),
352
  align='left'
353
  )
354
 
 
5
  import plotly.graph_objects as go
6
  import plotly.express as px
7
  from datetime import datetime
8
+ import os
9
+ import base64
10
  import aliases
11
 
12
+ # Import company logo mapping from ui_components
13
+ from ui_components import get_company_from_model, get_svg_as_data_uri
14
+
15
+ # Standard layout configuration matching existing charts
16
+ STANDARD_LAYOUT = dict(
17
+ template="plotly_white",
18
+ height=572,
19
+ hoverlabel=dict(
20
+ bgcolor="#105257",
21
+ font_size=12,
22
+ font_family="Manrope",
23
+ font_color="#d3dedc",
24
+ ),
25
+ legend=dict(
26
+ bgcolor='#FAF2E9',
27
+ ),
28
+ )
29
+
30
+ # Standard font for annotations
31
+ STANDARD_FONT = dict(
32
+ size=10,
33
+ color='#032629',
34
+ family='Manrope'
35
+ )
36
+
37
 
38
  def create_evolution_over_time_chart(df: pd.DataFrame) -> go.Figure:
39
  """
40
+ Create a chart showing model performance evolution over release dates.
41
+ Uses company logos as markers to match the existing chart styling.
42
 
43
  Args:
44
  df: DataFrame with columns including 'release_date' or 'Release_Date', 'Language Model', 'average score', 'openness'
 
59
  text="No release date data available",
60
  xref="paper", yref="paper",
61
  x=0.5, y=0.5, showarrow=False,
62
+ font=STANDARD_FONT
63
  )
64
+ fig.update_layout(**STANDARD_LAYOUT)
65
  return fig
66
 
67
  # Filter out rows without release dates
 
73
  text="No release date data available",
74
  xref="paper", yref="paper",
75
  x=0.5, y=0.5, showarrow=False,
76
+ font=STANDARD_FONT
77
  )
78
+ fig.update_layout(**STANDARD_LAYOUT)
79
  return fig
80
 
81
  # Convert release_date to datetime (normalize column name)
 
103
  text="No score data available",
104
  xref="paper", yref="paper",
105
  x=0.5, y=0.5, showarrow=False,
106
+ font=STANDARD_FONT
107
  )
108
+ fig.update_layout(**STANDARD_LAYOUT)
109
  return fig
110
 
111
  # Get model name column
 
117
  if model_col is None:
118
  model_col = 'Language Model' # Default
119
 
 
 
 
 
 
 
 
 
 
120
  fig = go.Figure()
121
 
122
+ # Add trend line first (so it's behind the markers)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  if len(plot_df) > 1:
124
  fig.add_trace(go.Scatter(
125
  x=plot_df['release_date'],
 
131
  showlegend=False
132
  ))
133
 
134
+ # Calculate axis ranges for domain coordinate conversion
135
+ min_date = plot_df['release_date'].min()
136
+ max_date = plot_df['release_date'].max()
137
+ date_range = (max_date - min_date).total_seconds() if max_date != min_date else 1
138
+
139
+ min_score = plot_df[score_col].min()
140
+ max_score = plot_df[score_col].max()
141
+ y_min = min_score - 5 if min_score > 5 else 0
142
+ y_max = max_score + 10 # Extra space for labels
143
+
144
+ # Build hover text for each point
145
+ hover_texts = []
146
+ for _, row in plot_df.iterrows():
147
+ model_name = row.get(model_col, 'Unknown')
148
+ openness = row.get('Openness', row.get('openness', 'unknown'))
149
+ h_pad = " "
150
+ hover_text = f"<br>{h_pad}<b>{model_name}</b>{h_pad}<br>"
151
+ hover_text += f"{h_pad}Release: <b>{row['release_date'].strftime('%Y-%m-%d')}</b>{h_pad}<br>"
152
+ hover_text += f"{h_pad}Average Score: <b>{row[score_col]:.1f}</b>{h_pad}<br>"
153
+ hover_text += f"{h_pad}Openness: <b>{openness}</b>{h_pad}<br>"
154
+ hover_texts.append(hover_text)
155
+
156
+ plot_df['hover_text'] = hover_texts
157
+
158
+ # Add invisible markers for hover functionality
159
+ fig.add_trace(go.Scatter(
160
+ x=plot_df['release_date'],
161
+ y=plot_df[score_col],
162
+ mode='markers',
163
+ name='Models',
164
+ showlegend=False,
165
+ text=plot_df['hover_text'],
166
+ hoverinfo='text',
167
+ marker=dict(
168
+ color='rgba(0,0,0,0)', # Invisible markers
169
+ size=25, # Large enough for hover detection
170
+ opacity=0
171
+ )
172
+ ))
173
+
174
+ # Add company logo images for each data point
175
+ layout_images = []
176
+ frontier_labels_data = []
177
+
178
+ for _, row in plot_df.iterrows():
179
+ model_name = row.get(model_col, '')
180
+ company_info = get_company_from_model(model_name)
181
+ logo_path = company_info['path']
182
+
183
+ # Read the SVG file and encode as base64 data URI
184
+ if os.path.exists(logo_path):
185
+ try:
186
+ with open(logo_path, 'rb') as f:
187
+ encoded_logo = base64.b64encode(f.read()).decode('utf-8')
188
+ logo_uri = f"data:image/svg+xml;base64,{encoded_logo}"
189
+
190
+ x_val = row['release_date']
191
+ y_val = row[score_col]
192
+
193
+ # Convert to domain coordinates (0-1 range)
194
+ if date_range > 0:
195
+ domain_x = (x_val - min_date).total_seconds() / date_range
196
+ else:
197
+ domain_x = 0.5
198
+
199
+ domain_y = (y_val - y_min) / (y_max - y_min) if (y_max - y_min) > 0 else 0.5
200
+
201
+ # Clamp to valid range
202
+ domain_x = max(0.02, min(0.98, domain_x))
203
+ domain_y = max(0.02, min(0.98, domain_y))
204
+
205
+ layout_images.append(dict(
206
+ source=logo_uri,
207
+ xref="x domain",
208
+ yref="y domain",
209
+ x=domain_x,
210
+ y=domain_y,
211
+ sizex=0.04,
212
+ sizey=0.06,
213
+ xanchor="center",
214
+ yanchor="middle",
215
+ layer="above"
216
+ ))
217
+
218
+ # Store label data for annotation
219
+ frontier_labels_data.append({
220
+ 'x': domain_x,
221
+ 'y': domain_y,
222
+ 'label': model_name
223
+ })
224
+ except Exception:
225
+ pass
226
+
227
+ # Add model name labels above each point
228
+ for item in frontier_labels_data:
229
+ fig.add_annotation(
230
+ x=item['x'],
231
+ y=item['y'],
232
+ xref="x domain",
233
+ yref="y domain",
234
+ text=item['label'],
235
+ showarrow=False,
236
+ yshift=20,
237
+ font=STANDARD_FONT,
238
+ xanchor='center',
239
+ yanchor='bottom'
240
+ )
241
+
242
+ # Build layout configuration
243
+ layout_config = dict(
244
+ **STANDARD_LAYOUT,
245
+ title="Model Performance Evolution Over Time",
246
  xaxis=dict(
247
  title="Model Release Date",
248
+ range=[min_date - pd.Timedelta(days=15), max_date + pd.Timedelta(days=15)]
 
249
  ),
250
  yaxis=dict(
251
  title="Average Score",
252
+ range=[y_min, y_max]
 
253
  ),
 
 
 
 
 
254
  )
255
 
256
+ # Add company logo images to the layout
257
+ if layout_images:
258
+ layout_config['images'] = layout_images
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
+ fig.update_layout(**layout_config)
 
 
 
 
 
 
 
 
261
 
262
  return fig
263
 
 
265
  def create_accuracy_by_size_chart(df: pd.DataFrame) -> go.Figure:
266
  """
267
  Create a scatter plot showing accuracy vs parameter count for open-weights models.
268
+ Uses company logos as markers to match the existing chart styling.
269
 
270
  Args:
271
  df: DataFrame with columns including 'parameter_count_b' or 'Parameter_Count_B',
 
275
  Returns:
276
  Plotly figure showing accuracy vs model size
277
  """
278
+ import numpy as np
279
+
280
  # Handle different column name formats for parameter count
281
  param_col = None
282
  for col in ['parameter_count_b', 'Parameter_Count_B', 'Parameter Count B']:
 
296
  text="No parameter count data available",
297
  xref="paper", yref="paper",
298
  x=0.5, y=0.5, showarrow=False,
299
+ font=STANDARD_FONT
300
  )
301
+ fig.update_layout(**STANDARD_LAYOUT)
302
  return fig
303
 
304
  # Filter to only open-weights models with parameter data
 
318
  text="No open-weights models with parameter data available",
319
  xref="paper", yref="paper",
320
  x=0.5, y=0.5, showarrow=False,
321
+ font=STANDARD_FONT
322
  )
323
+ fig.update_layout(**STANDARD_LAYOUT)
324
  return fig
325
 
326
  # Get the score column (handle different naming conventions)
 
341
  text="No score data available",
342
  xref="paper", yref="paper",
343
  x=0.5, y=0.5, showarrow=False,
344
+ font=STANDARD_FONT
345
  )
346
+ fig.update_layout(**STANDARD_LAYOUT)
347
  return fig
348
 
349
  # Get model name column
 
357
 
358
  fig = go.Figure()
359
 
360
+ # Prepare data for plotting
361
+ x_values = []
362
+ y_values = []
363
+ hover_texts = []
364
+ model_names = []
365
+ total_params_list = []
366
+
367
  for _, row in plot_df.iterrows():
368
  total_params = row[param_col]
369
  active_params = row.get(active_param_col) if active_param_col else None
 
373
  # Use active params for x-axis if available (more meaningful for MoE)
374
  x_val = active_params if pd.notna(active_params) else total_params
375
 
376
+ x_values.append(x_val)
377
+ y_values.append(score)
378
+ model_names.append(model_name)
379
+ total_params_list.append(total_params)
 
 
380
 
381
+ # Create hover text matching existing chart style
382
+ h_pad = " "
383
+ hover_text = f"<br>{h_pad}<b>{model_name}</b>{h_pad}<br>"
384
+ hover_text += f"{h_pad}Total Params: <b>{total_params:.0f}B</b>{h_pad}<br>"
385
+ if pd.notna(active_params):
386
+ hover_text += f"{h_pad}Active Params: <b>{active_params:.0f}B</b>{h_pad}<br>"
387
+ hover_text += f"{h_pad}Average Score: <b>{score:.1f}</b>{h_pad}<br>"
388
+ hover_texts.append(hover_text)
389
+
390
+ # Calculate axis ranges for domain coordinate conversion
391
+ min_x = min(x_values)
392
+ max_x = max(x_values)
393
+ x_min_log = np.log10(min_x * 0.5) if min_x > 0 else 0
394
+ x_max_log = np.log10(max_x * 1.5) if max_x > 0 else 3
395
+
396
+ min_score = min(y_values)
397
+ max_score = max(y_values)
398
+ y_min = min_score - 5 if min_score > 5 else 0
399
+ y_max = max_score + 10 # Extra space for labels
400
+
401
+ # Add invisible markers for hover functionality
402
+ fig.add_trace(go.Scatter(
403
+ x=x_values,
404
+ y=y_values,
405
+ mode='markers',
406
+ name='Models',
407
+ showlegend=False,
408
+ text=hover_texts,
409
+ hoverinfo='text',
410
+ marker=dict(
411
+ color='rgba(0,0,0,0)', # Invisible markers
412
+ size=25, # Large enough for hover detection
413
+ opacity=0
414
+ )
415
+ ))
416
+
417
+ # Add company logo images for each data point
418
+ layout_images = []
419
+ frontier_labels_data = []
420
+
421
+ for i, (x_val, y_val, model_name, total_params) in enumerate(zip(x_values, y_values, model_names, total_params_list)):
422
+ company_info = get_company_from_model(model_name)
423
+ logo_path = company_info['path']
424
 
425
+ # Read the SVG file and encode as base64 data URI
426
+ if os.path.exists(logo_path):
427
+ try:
428
+ with open(logo_path, 'rb') as f:
429
+ encoded_logo = base64.b64encode(f.read()).decode('utf-8')
430
+ logo_uri = f"data:image/svg+xml;base64,{encoded_logo}"
431
+
432
+ # Convert to domain coordinates (0-1 range) for log scale x-axis
433
+ if x_val > 0:
434
+ log_x = np.log10(x_val)
435
+ domain_x = (log_x - x_min_log) / (x_max_log - x_min_log)
436
+ else:
437
+ domain_x = 0
438
+
439
+ domain_y = (y_val - y_min) / (y_max - y_min) if (y_max - y_min) > 0 else 0.5
440
+
441
+ # Clamp to valid range
442
+ domain_x = max(0.02, min(0.98, domain_x))
443
+ domain_y = max(0.02, min(0.98, domain_y))
444
+
445
+ # Scale logo size based on total params
446
+ size_scale = 0.03 + (total_params / 2000) # Larger models = larger logos
447
+ size_scale = min(size_scale, 0.06) # Cap size
448
+
449
+ layout_images.append(dict(
450
+ source=logo_uri,
451
+ xref="x domain",
452
+ yref="y domain",
453
+ x=domain_x,
454
+ y=domain_y,
455
+ sizex=size_scale,
456
+ sizey=size_scale * 1.5,
457
+ xanchor="center",
458
+ yanchor="middle",
459
+ layer="above"
460
+ ))
461
+
462
+ # Store label data for annotation
463
+ frontier_labels_data.append({
464
+ 'x': domain_x,
465
+ 'y': domain_y,
466
+ 'label': model_name
467
+ })
468
+ except Exception:
469
+ pass
470
+
471
+ # Add model name labels above each point
472
+ for item in frontier_labels_data:
473
+ fig.add_annotation(
474
+ x=item['x'],
475
+ y=item['y'],
476
+ xref="x domain",
477
+ yref="y domain",
478
+ text=item['label'],
479
+ showarrow=False,
480
+ yshift=25,
481
+ font=STANDARD_FONT,
482
+ xanchor='center',
483
+ yanchor='bottom'
484
+ )
485
 
486
+ # Build layout configuration
487
+ layout_config = dict(
488
+ **STANDARD_LAYOUT,
489
+ title="Open Model Accuracy by Size",
 
 
490
  xaxis=dict(
491
  title="Active Parameters (Billions)",
492
+ type="log",
493
+ range=[x_min_log, x_max_log]
 
494
  ),
495
  yaxis=dict(
496
  title="Average Score",
497
+ range=[y_min, y_max]
 
498
  ),
 
 
 
 
 
499
  )
500
 
501
+ # Add company logo images to the layout
502
+ if layout_images:
503
+ layout_config['images'] = layout_images
504
+
505
+ fig.update_layout(**layout_config)
506
+
507
  # Add annotation explaining marker size
508
  fig.add_annotation(
509
+ text="Logo size indicates total parameter count",
510
  xref="paper", yref="paper",
511
+ x=0.02, y=-0.08,
512
  showarrow=False,
513
+ font=STANDARD_FONT,
514
  align='left'
515
  )
516