Spaces:
Running
Running
openhands openhands commited on
Commit ·
4f4eb00
1
Parent(s): 76b9525
Refactor Winners by Category to single unified table
Browse files- Changed from multiple category cards to a single horizontal table
- Format: | [emoji] Category | Score | ... for each category
- Display only model name (removed SDK version)
- Deduplicate by model name, keeping highest score
- Updated CSS for new table layout with responsive design
Co-authored-by: openhands <openhands@all-hands.dev>
- content.py +78 -121
- ui_components.py +67 -67
content.py
CHANGED
|
@@ -966,187 +966,144 @@ h3 .header-link-icon {
|
|
| 966 |
|
| 967 |
/* ====== Winners by Category Section ====== */
|
| 968 |
.winners-by-category-container {
|
| 969 |
-
display: grid;
|
| 970 |
-
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
| 971 |
-
gap: 20px;
|
| 972 |
margin: 20px 0;
|
|
|
|
| 973 |
}
|
| 974 |
|
| 975 |
-
.
|
|
|
|
|
|
|
|
|
|
| 976 |
background: #fff;
|
| 977 |
border: 1px solid #032629;
|
| 978 |
border-radius: 12px;
|
| 979 |
-
|
| 980 |
-
transition: box-shadow 0.2s ease;
|
| 981 |
}
|
| 982 |
|
| 983 |
-
.dark .
|
| 984 |
background: rgba(250, 242, 233, 0.05);
|
| 985 |
border-color: #9fead1;
|
| 986 |
}
|
| 987 |
|
| 988 |
-
.
|
| 989 |
-
|
| 990 |
-
}
|
| 991 |
-
|
| 992 |
-
.dark .winner-category-card:hover {
|
| 993 |
-
box-shadow: 0 4px 12px rgba(159, 234, 209, 0.2);
|
| 994 |
-
}
|
| 995 |
-
|
| 996 |
-
.winner-category-header {
|
| 997 |
-
display: flex;
|
| 998 |
-
align-items: center;
|
| 999 |
-
gap: 12px;
|
| 1000 |
-
margin-bottom: 16px;
|
| 1001 |
-
padding-bottom: 12px;
|
| 1002 |
-
border-bottom: 2px solid #F0529C;
|
| 1003 |
-
}
|
| 1004 |
-
|
| 1005 |
-
.dark .winner-category-header {
|
| 1006 |
-
border-bottom-color: #0FCB8C;
|
| 1007 |
}
|
| 1008 |
|
| 1009 |
-
.
|
| 1010 |
-
|
| 1011 |
-
height: 32px;
|
| 1012 |
}
|
| 1013 |
|
| 1014 |
-
.
|
| 1015 |
-
|
| 1016 |
-
|
| 1017 |
font-weight: 700;
|
|
|
|
| 1018 |
color: #032629;
|
|
|
|
|
|
|
|
|
|
| 1019 |
}
|
| 1020 |
|
| 1021 |
-
.dark .
|
| 1022 |
color: #fff;
|
|
|
|
|
|
|
| 1023 |
}
|
| 1024 |
|
| 1025 |
-
.
|
| 1026 |
-
|
| 1027 |
-
border-collapse: collapse;
|
| 1028 |
-
font-size: 13px;
|
| 1029 |
-
}
|
| 1030 |
-
|
| 1031 |
-
.winner-table thead tr {
|
| 1032 |
-
border-bottom: 1px solid #ddd;
|
| 1033 |
-
}
|
| 1034 |
-
|
| 1035 |
-
.dark .winner-table thead tr {
|
| 1036 |
-
border-bottom-color: #4a5a5a;
|
| 1037 |
-
}
|
| 1038 |
-
|
| 1039 |
-
.winner-table th {
|
| 1040 |
-
padding: 8px 4px;
|
| 1041 |
-
text-align: left;
|
| 1042 |
-
font-weight: 600;
|
| 1043 |
-
color: #667876;
|
| 1044 |
-
font-size: 11px;
|
| 1045 |
-
text-transform: uppercase;
|
| 1046 |
}
|
| 1047 |
|
| 1048 |
-
.
|
| 1049 |
-
|
|
|
|
|
|
|
|
|
|
| 1050 |
}
|
| 1051 |
|
| 1052 |
-
.
|
| 1053 |
-
padding:
|
| 1054 |
vertical-align: middle;
|
| 1055 |
border-bottom: 1px solid #eee;
|
| 1056 |
}
|
| 1057 |
|
| 1058 |
-
.dark .
|
| 1059 |
border-bottom-color: #2a3a3a;
|
| 1060 |
}
|
| 1061 |
|
| 1062 |
-
.
|
| 1063 |
border-bottom: none;
|
| 1064 |
}
|
| 1065 |
|
| 1066 |
-
.
|
| 1067 |
-
|
| 1068 |
-
text-align: center;
|
| 1069 |
-
font-size: 14px;
|
| 1070 |
}
|
| 1071 |
|
| 1072 |
-
.
|
| 1073 |
-
|
| 1074 |
-
text-align: right;
|
| 1075 |
-
color: #F0529C;
|
| 1076 |
-
font-size: 14px;
|
| 1077 |
-
}
|
| 1078 |
-
|
| 1079 |
-
.dark .winner-table .score-col {
|
| 1080 |
-
color: #0FCB8C;
|
| 1081 |
-
}
|
| 1082 |
-
|
| 1083 |
-
.system-info {
|
| 1084 |
-
display: flex;
|
| 1085 |
-
align-items: center;
|
| 1086 |
-
gap: 10px;
|
| 1087 |
}
|
| 1088 |
|
| 1089 |
-
.
|
| 1090 |
-
width: 20px;
|
| 1091 |
-
height: 20px;
|
| 1092 |
-
flex-shrink: 0;
|
| 1093 |
-
}
|
| 1094 |
-
|
| 1095 |
-
.system-details {
|
| 1096 |
-
display: flex;
|
| 1097 |
-
flex-direction: column;
|
| 1098 |
-
overflow: hidden;
|
| 1099 |
-
}
|
| 1100 |
-
|
| 1101 |
-
.sdk-name {
|
| 1102 |
-
font-weight: 600;
|
| 1103 |
-
color: #032629;
|
| 1104 |
-
font-size: 13px;
|
| 1105 |
white-space: nowrap;
|
| 1106 |
-
|
| 1107 |
-
|
|
|
|
| 1108 |
}
|
| 1109 |
|
| 1110 |
-
.dark .
|
| 1111 |
color: #fff;
|
| 1112 |
}
|
| 1113 |
|
| 1114 |
-
.
|
| 1115 |
-
|
| 1116 |
-
|
| 1117 |
-
|
| 1118 |
-
|
| 1119 |
-
|
|
|
|
| 1120 |
}
|
| 1121 |
|
| 1122 |
-
.dark .
|
| 1123 |
-
color: #
|
|
|
|
| 1124 |
}
|
| 1125 |
|
| 1126 |
-
.
|
| 1127 |
-
|
| 1128 |
-
|
| 1129 |
-
|
| 1130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1131 |
}
|
| 1132 |
|
| 1133 |
/* Responsive adjustments for winners section */
|
| 1134 |
-
@media (max-width:
|
| 1135 |
-
.winners-
|
| 1136 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1137 |
}
|
| 1138 |
|
| 1139 |
-
.winner-category-
|
| 1140 |
-
|
|
|
|
|
|
|
| 1141 |
}
|
| 1142 |
|
| 1143 |
-
.
|
| 1144 |
-
|
| 1145 |
-
font-size: 12px;
|
| 1146 |
}
|
| 1147 |
|
| 1148 |
-
.
|
| 1149 |
-
|
|
|
|
|
|
|
| 1150 |
}
|
| 1151 |
|
| 1152 |
.model-name {
|
|
|
|
| 966 |
|
| 967 |
/* ====== Winners by Category Section ====== */
|
| 968 |
.winners-by-category-container {
|
|
|
|
|
|
|
|
|
|
| 969 |
margin: 20px 0;
|
| 970 |
+
overflow-x: auto;
|
| 971 |
}
|
| 972 |
|
| 973 |
+
.winners-unified-table {
|
| 974 |
+
width: 100%;
|
| 975 |
+
border-collapse: collapse;
|
| 976 |
+
font-size: 13px;
|
| 977 |
background: #fff;
|
| 978 |
border: 1px solid #032629;
|
| 979 |
border-radius: 12px;
|
| 980 |
+
overflow: hidden;
|
|
|
|
| 981 |
}
|
| 982 |
|
| 983 |
+
.dark .winners-unified-table {
|
| 984 |
background: rgba(250, 242, 233, 0.05);
|
| 985 |
border-color: #9fead1;
|
| 986 |
}
|
| 987 |
|
| 988 |
+
.winners-unified-table thead tr {
|
| 989 |
+
background: linear-gradient(to right, rgba(240, 82, 156, 0.1), rgba(15, 203, 140, 0.1));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 990 |
}
|
| 991 |
|
| 992 |
+
.dark .winners-unified-table thead tr {
|
| 993 |
+
background: linear-gradient(to right, rgba(240, 82, 156, 0.2), rgba(15, 203, 140, 0.2));
|
|
|
|
| 994 |
}
|
| 995 |
|
| 996 |
+
.winners-unified-table .category-header {
|
| 997 |
+
padding: 12px 8px;
|
| 998 |
+
text-align: center;
|
| 999 |
font-weight: 700;
|
| 1000 |
+
font-size: 13px;
|
| 1001 |
color: #032629;
|
| 1002 |
+
border-bottom: 2px solid #F0529C;
|
| 1003 |
+
border-right: 1px solid #eee;
|
| 1004 |
+
white-space: nowrap;
|
| 1005 |
}
|
| 1006 |
|
| 1007 |
+
.dark .winners-unified-table .category-header {
|
| 1008 |
color: #fff;
|
| 1009 |
+
border-bottom-color: #0FCB8C;
|
| 1010 |
+
border-right-color: #2a3a3a;
|
| 1011 |
}
|
| 1012 |
|
| 1013 |
+
.winners-unified-table .category-header:last-child {
|
| 1014 |
+
border-right: none;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1015 |
}
|
| 1016 |
|
| 1017 |
+
.winner-category-icon-small {
|
| 1018 |
+
width: 18px;
|
| 1019 |
+
height: 18px;
|
| 1020 |
+
vertical-align: middle;
|
| 1021 |
+
margin-right: 6px;
|
| 1022 |
}
|
| 1023 |
|
| 1024 |
+
.winners-unified-table td {
|
| 1025 |
+
padding: 8px 6px;
|
| 1026 |
vertical-align: middle;
|
| 1027 |
border-bottom: 1px solid #eee;
|
| 1028 |
}
|
| 1029 |
|
| 1030 |
+
.dark .winners-unified-table td {
|
| 1031 |
border-bottom-color: #2a3a3a;
|
| 1032 |
}
|
| 1033 |
|
| 1034 |
+
.winners-unified-table tbody tr:last-child td {
|
| 1035 |
border-bottom: none;
|
| 1036 |
}
|
| 1037 |
|
| 1038 |
+
.winners-unified-table tbody tr:hover {
|
| 1039 |
+
background: rgba(240, 82, 156, 0.05);
|
|
|
|
|
|
|
| 1040 |
}
|
| 1041 |
|
| 1042 |
+
.dark .winners-unified-table tbody tr:hover {
|
| 1043 |
+
background: rgba(15, 203, 140, 0.1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1044 |
}
|
| 1045 |
|
| 1046 |
+
.winners-unified-table .model-cell {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1047 |
white-space: nowrap;
|
| 1048 |
+
color: #032629;
|
| 1049 |
+
font-weight: 500;
|
| 1050 |
+
border-right: none;
|
| 1051 |
}
|
| 1052 |
|
| 1053 |
+
.dark .winners-unified-table .model-cell {
|
| 1054 |
color: #fff;
|
| 1055 |
}
|
| 1056 |
|
| 1057 |
+
.winners-unified-table .score-cell {
|
| 1058 |
+
text-align: right;
|
| 1059 |
+
font-weight: 700;
|
| 1060 |
+
color: #F0529C;
|
| 1061 |
+
padding-right: 12px;
|
| 1062 |
+
border-right: 1px solid #eee;
|
| 1063 |
+
min-width: 50px;
|
| 1064 |
}
|
| 1065 |
|
| 1066 |
+
.dark .winners-unified-table .score-cell {
|
| 1067 |
+
color: #0FCB8C;
|
| 1068 |
+
border-right-color: #2a3a3a;
|
| 1069 |
}
|
| 1070 |
|
| 1071 |
+
.winners-unified-table td:nth-last-child(1) {
|
| 1072 |
+
border-right: none;
|
| 1073 |
+
}
|
| 1074 |
+
|
| 1075 |
+
.company-logo-tiny {
|
| 1076 |
+
width: 16px;
|
| 1077 |
+
height: 16px;
|
| 1078 |
+
vertical-align: middle;
|
| 1079 |
+
margin-right: 6px;
|
| 1080 |
}
|
| 1081 |
|
| 1082 |
/* Responsive adjustments for winners section */
|
| 1083 |
+
@media (max-width: 900px) {
|
| 1084 |
+
.winners-unified-table {
|
| 1085 |
+
font-size: 11px;
|
| 1086 |
+
}
|
| 1087 |
+
|
| 1088 |
+
.winners-unified-table .category-header {
|
| 1089 |
+
font-size: 11px;
|
| 1090 |
+
padding: 8px 4px;
|
| 1091 |
}
|
| 1092 |
|
| 1093 |
+
.winner-category-icon-small {
|
| 1094 |
+
width: 14px;
|
| 1095 |
+
height: 14px;
|
| 1096 |
+
margin-right: 4px;
|
| 1097 |
}
|
| 1098 |
|
| 1099 |
+
.winners-unified-table td {
|
| 1100 |
+
padding: 6px 4px;
|
|
|
|
| 1101 |
}
|
| 1102 |
|
| 1103 |
+
.company-logo-tiny {
|
| 1104 |
+
width: 14px;
|
| 1105 |
+
height: 14px;
|
| 1106 |
+
margin-right: 4px;
|
| 1107 |
}
|
| 1108 |
|
| 1109 |
.model-name {
|
ui_components.py
CHANGED
|
@@ -426,14 +426,15 @@ CATEGORY_DEFINITIONS = [
|
|
| 426 |
|
| 427 |
def get_winners_by_category(df: pd.DataFrame, top_n: int = 5) -> dict:
|
| 428 |
"""
|
| 429 |
-
Extract the top N
|
|
|
|
| 430 |
|
| 431 |
Args:
|
| 432 |
df: The full leaderboard DataFrame with all scores
|
| 433 |
-
top_n: Number of top
|
| 434 |
|
| 435 |
Returns:
|
| 436 |
-
Dictionary mapping category name to list of top
|
| 437 |
"""
|
| 438 |
winners = {}
|
| 439 |
|
|
@@ -452,72 +453,87 @@ def get_winners_by_category(df: pd.DataFrame, top_n: int = 5) -> dict:
|
|
| 452 |
winners[cat_name] = []
|
| 453 |
continue
|
| 454 |
|
| 455 |
-
#
|
| 456 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 457 |
|
| 458 |
# Extract relevant info
|
| 459 |
-
|
| 460 |
for rank, (_, row) in enumerate(cat_df.iterrows(), 1):
|
| 461 |
-
|
| 462 |
"rank": rank,
|
| 463 |
-
"sdk_version": row.get("SDK Version", "Unknown"),
|
| 464 |
"language_model": row.get("Language Model", "Unknown"),
|
| 465 |
"score": row[score_col],
|
| 466 |
}
|
| 467 |
-
|
| 468 |
|
| 469 |
-
winners[cat_name] =
|
| 470 |
|
| 471 |
return winners
|
| 472 |
|
| 473 |
|
| 474 |
def create_winners_by_category_html(df: pd.DataFrame, top_n: int = 5) -> str:
|
| 475 |
"""
|
| 476 |
-
Create
|
|
|
|
| 477 |
|
| 478 |
Args:
|
| 479 |
df: The full leaderboard DataFrame
|
| 480 |
-
top_n: Number of top
|
| 481 |
|
| 482 |
Returns:
|
| 483 |
HTML string with the winners table
|
| 484 |
"""
|
| 485 |
winners = get_winners_by_category(df, top_n)
|
| 486 |
|
| 487 |
-
# Build
|
| 488 |
html_parts = ['<div class="winners-by-category-container">']
|
|
|
|
| 489 |
|
| 490 |
-
#
|
|
|
|
| 491 |
for cat_def in CATEGORY_DEFINITIONS:
|
| 492 |
cat_name = cat_def["name"]
|
| 493 |
icon_file = cat_def["icon"]
|
| 494 |
icon_uri = get_svg_as_data_uri(f"assets/{icon_file}")
|
| 495 |
-
|
| 496 |
-
top_systems = winners.get(cat_name, [])
|
| 497 |
-
|
| 498 |
html_parts.append(f'''
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
</div>
|
| 504 |
-
<table class="winner-table">
|
| 505 |
-
<thead>
|
| 506 |
-
<tr>
|
| 507 |
-
<th class="rank-col">#</th>
|
| 508 |
-
<th class="system-col">System</th>
|
| 509 |
-
<th class="score-col">Score</th>
|
| 510 |
-
</tr>
|
| 511 |
-
</thead>
|
| 512 |
-
<tbody>
|
| 513 |
''')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 514 |
|
| 515 |
-
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 521 |
|
| 522 |
# Get company logo for the model
|
| 523 |
company_info = get_company_from_model(language_model)
|
|
@@ -526,46 +542,30 @@ def create_winners_by_category_html(df: pd.DataFrame, top_n: int = 5) -> str:
|
|
| 526 |
# Format model name - clean it if it's a list
|
| 527 |
if isinstance(language_model, list):
|
| 528 |
language_model = language_model[0] if language_model else "Unknown"
|
| 529 |
-
model_display = str(language_model).split('/')[-1]
|
| 530 |
|
| 531 |
# Add medal emoji for top 3
|
| 532 |
-
|
| 533 |
if rank == 1:
|
| 534 |
-
|
| 535 |
elif rank == 2:
|
| 536 |
-
|
| 537 |
elif rank == 3:
|
| 538 |
-
|
| 539 |
|
| 540 |
html_parts.append(f'''
|
| 541 |
-
<
|
| 542 |
-
<
|
| 543 |
-
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
<div class="system-details">
|
| 547 |
-
<span class="sdk-name">{sdk_version}</span>
|
| 548 |
-
<span class="model-name">{model_display}</span>
|
| 549 |
-
</div>
|
| 550 |
-
</div>
|
| 551 |
-
</td>
|
| 552 |
-
<td class="score-col"><strong>{score:.1f}</strong></td>
|
| 553 |
-
</tr>
|
| 554 |
''')
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
<tr>
|
| 558 |
-
<td colspan="3" class="no-data">No submissions yet</td>
|
| 559 |
-
</tr>
|
| 560 |
-
''')
|
| 561 |
|
| 562 |
-
html_parts.append(''
|
| 563 |
-
</tbody>
|
| 564 |
-
</table>
|
| 565 |
-
</div>
|
| 566 |
-
''')
|
| 567 |
|
| 568 |
-
html_parts.append('</div>')
|
| 569 |
|
| 570 |
return ''.join(html_parts)
|
| 571 |
|
|
|
|
| 426 |
|
| 427 |
def get_winners_by_category(df: pd.DataFrame, top_n: int = 5) -> dict:
|
| 428 |
"""
|
| 429 |
+
Extract the top N models for each category based on their score.
|
| 430 |
+
Deduplicates by model name, keeping only the highest score per model.
|
| 431 |
|
| 432 |
Args:
|
| 433 |
df: The full leaderboard DataFrame with all scores
|
| 434 |
+
top_n: Number of top models to return per category (default: 5)
|
| 435 |
|
| 436 |
Returns:
|
| 437 |
+
Dictionary mapping category name to list of top models with their scores
|
| 438 |
"""
|
| 439 |
winners = {}
|
| 440 |
|
|
|
|
| 453 |
winners[cat_name] = []
|
| 454 |
continue
|
| 455 |
|
| 456 |
+
# Clean model names for deduplication
|
| 457 |
+
def clean_model_name(model):
|
| 458 |
+
if isinstance(model, list):
|
| 459 |
+
model = model[0] if model else "Unknown"
|
| 460 |
+
return str(model).split('/')[-1]
|
| 461 |
+
|
| 462 |
+
cat_df['_clean_model'] = cat_df['Language Model'].apply(clean_model_name)
|
| 463 |
+
|
| 464 |
+
# Deduplicate by model name, keeping highest score
|
| 465 |
+
cat_df = cat_df.sort_values(score_col, ascending=False)
|
| 466 |
+
cat_df = cat_df.drop_duplicates(subset=['_clean_model'], keep='first')
|
| 467 |
+
|
| 468 |
+
# Take top N after dedup
|
| 469 |
+
cat_df = cat_df.head(top_n)
|
| 470 |
|
| 471 |
# Extract relevant info
|
| 472 |
+
top_models = []
|
| 473 |
for rank, (_, row) in enumerate(cat_df.iterrows(), 1):
|
| 474 |
+
model_info = {
|
| 475 |
"rank": rank,
|
|
|
|
| 476 |
"language_model": row.get("Language Model", "Unknown"),
|
| 477 |
"score": row[score_col],
|
| 478 |
}
|
| 479 |
+
top_models.append(model_info)
|
| 480 |
|
| 481 |
+
winners[cat_name] = top_models
|
| 482 |
|
| 483 |
return winners
|
| 484 |
|
| 485 |
|
| 486 |
def create_winners_by_category_html(df: pd.DataFrame, top_n: int = 5) -> str:
|
| 487 |
"""
|
| 488 |
+
Create a single HTML table displaying the top N winners for each category side by side.
|
| 489 |
+
Format: | [emoji] Category | Score | [emoji] Category | Score | ...
|
| 490 |
|
| 491 |
Args:
|
| 492 |
df: The full leaderboard DataFrame
|
| 493 |
+
top_n: Number of top models to show per category
|
| 494 |
|
| 495 |
Returns:
|
| 496 |
HTML string with the winners table
|
| 497 |
"""
|
| 498 |
winners = get_winners_by_category(df, top_n)
|
| 499 |
|
| 500 |
+
# Build a single unified table
|
| 501 |
html_parts = ['<div class="winners-by-category-container">']
|
| 502 |
+
html_parts.append('<table class="winners-unified-table">')
|
| 503 |
|
| 504 |
+
# Header row with category icons and names
|
| 505 |
+
html_parts.append('<thead><tr>')
|
| 506 |
for cat_def in CATEGORY_DEFINITIONS:
|
| 507 |
cat_name = cat_def["name"]
|
| 508 |
icon_file = cat_def["icon"]
|
| 509 |
icon_uri = get_svg_as_data_uri(f"assets/{icon_file}")
|
|
|
|
|
|
|
|
|
|
| 510 |
html_parts.append(f'''
|
| 511 |
+
<th class="category-header" colspan="2">
|
| 512 |
+
<img src="{icon_uri}" alt="{cat_name}" class="winner-category-icon-small">
|
| 513 |
+
{cat_name}
|
| 514 |
+
</th>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 515 |
''')
|
| 516 |
+
html_parts.append('</tr></thead>')
|
| 517 |
+
|
| 518 |
+
# Body rows - one row per rank position
|
| 519 |
+
html_parts.append('<tbody>')
|
| 520 |
+
for rank in range(1, top_n + 1):
|
| 521 |
+
html_parts.append('<tr>')
|
| 522 |
|
| 523 |
+
for cat_def in CATEGORY_DEFINITIONS:
|
| 524 |
+
cat_name = cat_def["name"]
|
| 525 |
+
top_models = winners.get(cat_name, [])
|
| 526 |
+
|
| 527 |
+
# Find the model at this rank
|
| 528 |
+
model_at_rank = None
|
| 529 |
+
for m in top_models:
|
| 530 |
+
if m["rank"] == rank:
|
| 531 |
+
model_at_rank = m
|
| 532 |
+
break
|
| 533 |
+
|
| 534 |
+
if model_at_rank:
|
| 535 |
+
language_model = model_at_rank["language_model"]
|
| 536 |
+
score = model_at_rank["score"]
|
| 537 |
|
| 538 |
# Get company logo for the model
|
| 539 |
company_info = get_company_from_model(language_model)
|
|
|
|
| 542 |
# Format model name - clean it if it's a list
|
| 543 |
if isinstance(language_model, list):
|
| 544 |
language_model = language_model[0] if language_model else "Unknown"
|
| 545 |
+
model_display = str(language_model).split('/')[-1]
|
| 546 |
|
| 547 |
# Add medal emoji for top 3
|
| 548 |
+
rank_prefix = ""
|
| 549 |
if rank == 1:
|
| 550 |
+
rank_prefix = "🥇 "
|
| 551 |
elif rank == 2:
|
| 552 |
+
rank_prefix = "🥈 "
|
| 553 |
elif rank == 3:
|
| 554 |
+
rank_prefix = "🥉 "
|
| 555 |
|
| 556 |
html_parts.append(f'''
|
| 557 |
+
<td class="model-cell">
|
| 558 |
+
<img src="{logo_uri}" alt="{company_info['name']}" class="company-logo-tiny">
|
| 559 |
+
{rank_prefix}{model_display}
|
| 560 |
+
</td>
|
| 561 |
+
<td class="score-cell">{score:.1f}</td>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 562 |
''')
|
| 563 |
+
else:
|
| 564 |
+
html_parts.append('<td class="model-cell">-</td><td class="score-cell">-</td>')
|
|
|
|
|
|
|
|
|
|
|
|
|
| 565 |
|
| 566 |
+
html_parts.append('</tr>')
|
|
|
|
|
|
|
|
|
|
|
|
|
| 567 |
|
| 568 |
+
html_parts.append('</tbody></table></div>')
|
| 569 |
|
| 570 |
return ''.join(html_parts)
|
| 571 |
|