Spaces:
Running
Running
Fix: Scatter plot zoom and 'Show all labels' not working
#29
by juan-all-hands - opened
- app.py +2 -147
- content.py +5 -8
- leaderboard_transformer.py +173 -181
- simple_data_loader.py +1 -2
- ui_components.py +11 -25
- visualizations.py +1 -11
app.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
| 2 |
import logging
|
| 3 |
import sys
|
| 4 |
import os
|
| 5 |
-
import json
|
| 6 |
|
| 7 |
from constants import FONT_FAMILY_SHORT
|
| 8 |
|
|
@@ -77,29 +76,6 @@ redirect_script = """
|
|
| 77 |
if (window.location.pathname === '/') { window.location.replace('/home'); }
|
| 78 |
</script>
|
| 79 |
"""
|
| 80 |
-
hf_space_fetch_credentials_script = """
|
| 81 |
-
<script>
|
| 82 |
-
(function() {
|
| 83 |
-
const hfSpaceApiPrefix = "https://openhands-openhands-index.hf.space/gradio_api/";
|
| 84 |
-
const originalFetch = window.fetch.bind(window);
|
| 85 |
-
|
| 86 |
-
window.fetch = function(input, init) {
|
| 87 |
-
const url = typeof input === "string" ? input : input?.url;
|
| 88 |
-
|
| 89 |
-
if (url && url.startsWith(hfSpaceApiPrefix)) {
|
| 90 |
-
if (input instanceof Request) {
|
| 91 |
-
input = new Request(input, { ...(init || {}), credentials: "omit" });
|
| 92 |
-
init = undefined;
|
| 93 |
-
} else {
|
| 94 |
-
init = { ...(init || {}), credentials: "omit" };
|
| 95 |
-
}
|
| 96 |
-
}
|
| 97 |
-
|
| 98 |
-
return originalFetch(input, init);
|
| 99 |
-
};
|
| 100 |
-
})();
|
| 101 |
-
</script>
|
| 102 |
-
"""
|
| 103 |
|
| 104 |
# JavaScript to fix navigation links to use relative paths (avoids domain mismatch when behind proxy)
|
| 105 |
fix_nav_links_script = """
|
|
@@ -376,15 +352,7 @@ logger.info("Creating Gradio application")
|
|
| 376 |
demo = gr.Blocks(
|
| 377 |
theme=theme,
|
| 378 |
css=final_css,
|
| 379 |
-
head=
|
| 380 |
-
hf_space_fetch_credentials_script
|
| 381 |
-
+ posthog_script
|
| 382 |
-
+ scroll_script
|
| 383 |
-
+ redirect_script
|
| 384 |
-
+ fix_nav_links_script
|
| 385 |
-
+ tooltip_script
|
| 386 |
-
+ dark_mode_script
|
| 387 |
-
),
|
| 388 |
title="OpenHands Index",
|
| 389 |
)
|
| 390 |
|
|
@@ -436,119 +404,6 @@ class RootRedirectMiddleware(BaseHTTPMiddleware):
|
|
| 436 |
return await call_next(request)
|
| 437 |
|
| 438 |
|
| 439 |
-
class StringifiedGradioJSONMiddleware:
|
| 440 |
-
"""Normalize custom-domain Gradio requests before they reach Gradio.
|
| 441 |
-
|
| 442 |
-
Requests sent through index.openhands.dev can arrive at Gradio as a JSON
|
| 443 |
-
string containing the real request object, which makes FastAPI validation
|
| 444 |
-
reject interactive callbacks with 422. Direct HF Space traffic already sends
|
| 445 |
-
proper JSON objects, so this only rewrites bodies that decode to strings.
|
| 446 |
-
|
| 447 |
-
The custom domain also loads the Gradio frontend from Vercel while
|
| 448 |
-
window.gradio_config.root points to the hf.space runtime. Chrome therefore
|
| 449 |
-
requires successful credentialed CORS preflights for queue and heartbeat
|
| 450 |
-
endpoints.
|
| 451 |
-
"""
|
| 452 |
-
|
| 453 |
-
CUSTOM_DOMAIN_ORIGIN = "https://index.openhands.dev"
|
| 454 |
-
|
| 455 |
-
def __init__(self, app):
|
| 456 |
-
self.app = app
|
| 457 |
-
|
| 458 |
-
async def __call__(self, scope, receive, send):
|
| 459 |
-
origin = None
|
| 460 |
-
if scope["type"] == "http":
|
| 461 |
-
headers = {
|
| 462 |
-
key.decode("latin-1").lower(): value.decode("latin-1")
|
| 463 |
-
for key, value in scope.get("headers", [])
|
| 464 |
-
}
|
| 465 |
-
origin = headers.get("origin")
|
| 466 |
-
|
| 467 |
-
should_apply_cors = (
|
| 468 |
-
scope["type"] == "http"
|
| 469 |
-
and scope.get("path", "").startswith("/gradio_api/")
|
| 470 |
-
and origin == self.CUSTOM_DOMAIN_ORIGIN
|
| 471 |
-
)
|
| 472 |
-
|
| 473 |
-
if should_apply_cors:
|
| 474 |
-
cors_headers = [
|
| 475 |
-
(b"access-control-allow-origin", self.CUSTOM_DOMAIN_ORIGIN.encode("latin-1")),
|
| 476 |
-
(b"access-control-allow-credentials", b"true"),
|
| 477 |
-
(b"access-control-allow-methods", b"DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT"),
|
| 478 |
-
(b"access-control-allow-headers", b"*"),
|
| 479 |
-
(b"access-control-expose-headers", b"*"),
|
| 480 |
-
(b"vary", b"Origin"),
|
| 481 |
-
]
|
| 482 |
-
|
| 483 |
-
if scope.get("method") == "OPTIONS":
|
| 484 |
-
await send({
|
| 485 |
-
"type": "http.response.start",
|
| 486 |
-
"status": 200,
|
| 487 |
-
"headers": cors_headers,
|
| 488 |
-
})
|
| 489 |
-
await send({"type": "http.response.body", "body": b""})
|
| 490 |
-
return
|
| 491 |
-
|
| 492 |
-
async def cors_send(message):
|
| 493 |
-
if message["type"] == "http.response.start":
|
| 494 |
-
message["headers"] = [
|
| 495 |
-
(key, value)
|
| 496 |
-
for key, value in message.get("headers", [])
|
| 497 |
-
if not key.lower().startswith(b"access-control-")
|
| 498 |
-
and key.lower() != b"vary"
|
| 499 |
-
] + cors_headers
|
| 500 |
-
await send(message)
|
| 501 |
-
else:
|
| 502 |
-
cors_send = send
|
| 503 |
-
|
| 504 |
-
if (
|
| 505 |
-
scope["type"] == "http"
|
| 506 |
-
and scope.get("method") == "POST"
|
| 507 |
-
and scope.get("path", "").startswith("/gradio_api/")
|
| 508 |
-
):
|
| 509 |
-
content_type = headers.get("content-type", "")
|
| 510 |
-
if "application/json" not in content_type:
|
| 511 |
-
return await self.app(scope, receive, cors_send)
|
| 512 |
-
|
| 513 |
-
body_parts = []
|
| 514 |
-
while True:
|
| 515 |
-
message = await receive()
|
| 516 |
-
if message["type"] != "http.request":
|
| 517 |
-
break
|
| 518 |
-
body_parts.append(message.get("body", b""))
|
| 519 |
-
if not message.get("more_body", False):
|
| 520 |
-
break
|
| 521 |
-
|
| 522 |
-
body = b"".join(body_parts)
|
| 523 |
-
replacement_body = body
|
| 524 |
-
try:
|
| 525 |
-
decoded = json.loads(body)
|
| 526 |
-
except json.JSONDecodeError:
|
| 527 |
-
decoded = None
|
| 528 |
-
|
| 529 |
-
if isinstance(decoded, str):
|
| 530 |
-
stripped = decoded.strip()
|
| 531 |
-
if stripped.startswith(("{", "[")):
|
| 532 |
-
replacement_body = stripped.encode("utf-8")
|
| 533 |
-
|
| 534 |
-
sent = False
|
| 535 |
-
|
| 536 |
-
async def replay_receive():
|
| 537 |
-
nonlocal sent
|
| 538 |
-
if sent:
|
| 539 |
-
return {"type": "http.request", "body": b"", "more_body": False}
|
| 540 |
-
sent = True
|
| 541 |
-
return {
|
| 542 |
-
"type": "http.request",
|
| 543 |
-
"body": replacement_body,
|
| 544 |
-
"more_body": False,
|
| 545 |
-
}
|
| 546 |
-
|
| 547 |
-
return await self.app(scope, replay_receive, cors_send)
|
| 548 |
-
|
| 549 |
-
return await self.app(scope, receive, cors_send)
|
| 550 |
-
|
| 551 |
-
|
| 552 |
# Create a parent FastAPI app with redirect_slashes=False to prevent
|
| 553 |
# automatic trailing slash redirects that cause issues with Gradio
|
| 554 |
root_app = FastAPI(redirect_slashes=False)
|
|
@@ -560,7 +415,6 @@ root_app.mount("/api", api_app)
|
|
| 560 |
|
| 561 |
# Mount Gradio app at root path
|
| 562 |
app = gr.mount_gradio_app(root_app, demo, path="/")
|
| 563 |
-
app = StringifiedGradioJSONMiddleware(app)
|
| 564 |
logger.info("REST API mounted at /api, Gradio app mounted at /")
|
| 565 |
|
| 566 |
|
|
@@ -573,3 +427,4 @@ if __name__ == "__main__":
|
|
| 573 |
logger.info(f"Launching app on {host}:{port}")
|
| 574 |
uvicorn.run(app, host=host, port=port)
|
| 575 |
logger.info("App launched successfully")
|
|
|
|
|
|
| 2 |
import logging
|
| 3 |
import sys
|
| 4 |
import os
|
|
|
|
| 5 |
|
| 6 |
from constants import FONT_FAMILY_SHORT
|
| 7 |
|
|
|
|
| 76 |
if (window.location.pathname === '/') { window.location.replace('/home'); }
|
| 77 |
</script>
|
| 78 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
# JavaScript to fix navigation links to use relative paths (avoids domain mismatch when behind proxy)
|
| 81 |
fix_nav_links_script = """
|
|
|
|
| 352 |
demo = gr.Blocks(
|
| 353 |
theme=theme,
|
| 354 |
css=final_css,
|
| 355 |
+
head=posthog_script + scroll_script + redirect_script + fix_nav_links_script + tooltip_script + dark_mode_script,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 356 |
title="OpenHands Index",
|
| 357 |
)
|
| 358 |
|
|
|
|
| 404 |
return await call_next(request)
|
| 405 |
|
| 406 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 407 |
# Create a parent FastAPI app with redirect_slashes=False to prevent
|
| 408 |
# automatic trailing slash redirects that cause issues with Gradio
|
| 409 |
root_app = FastAPI(redirect_slashes=False)
|
|
|
|
| 415 |
|
| 416 |
# Mount Gradio app at root path
|
| 417 |
app = gr.mount_gradio_app(root_app, demo, path="/")
|
|
|
|
| 418 |
logger.info("REST API mounted at /api, Gradio app mounted at /")
|
| 419 |
|
| 420 |
|
|
|
|
| 427 |
logger.info(f"Launching app on {host}:{port}")
|
| 428 |
uvicorn.run(app, host=host, port=port)
|
| 429 |
logger.info("App launched successfully")
|
| 430 |
+
|
content.py
CHANGED
|
@@ -547,20 +547,19 @@ span.wrap[tabindex="0"][role="button"][data-editable="false"] {
|
|
| 547 |
width: 100% !important;
|
| 548 |
align-items: center;
|
| 549 |
}
|
| 550 |
-
.nav-holder nav a[href*="alternative-agents"] {
|
| 551 |
-
grid-row: 1 !important;
|
| 552 |
-
grid-column: 7 !important;
|
| 553 |
-
white-space: nowrap !important;
|
| 554 |
-
}
|
| 555 |
.nav-holder nav a[href*="about"] {
|
| 556 |
grid-row: 1 !important;
|
| 557 |
-
grid-column:
|
| 558 |
}
|
| 559 |
.nav-holder nav a[href*="submit"] {
|
| 560 |
grid-row: 1 !important;
|
| 561 |
grid-column: 8 !important;
|
| 562 |
white-space: nowrap !important;
|
| 563 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
|
| 565 |
/* Divider line between header and category nav */
|
| 566 |
.nav-holder nav::after {
|
|
@@ -599,7 +598,6 @@ span.wrap[tabindex="0"][role="button"][data-editable="false"] {
|
|
| 599 |
.nav-holder nav a[href*="discovery"] { grid-column: 4 !important; }
|
| 600 |
|
| 601 |
/* Navigation hover styles */
|
| 602 |
-
.nav-holder nav a[href*="alternative-agents"]:hover,
|
| 603 |
.nav-holder nav a[href*="about"]:hover,
|
| 604 |
.nav-holder nav a[href*="submit"]:hover,
|
| 605 |
.nav-holder nav a[href*="literature-understanding"]:hover,
|
|
@@ -609,7 +607,6 @@ span.wrap[tabindex="0"][role="button"][data-editable="false"] {
|
|
| 609 |
background-color: #FDF9F4;
|
| 610 |
}
|
| 611 |
|
| 612 |
-
.dark .nav-holder nav a[href*="alternative-agents"]:hover,
|
| 613 |
.dark .nav-holder nav a[href*="about"]:hover,
|
| 614 |
.dark .nav-holder nav a[href*="submit"]:hover,
|
| 615 |
.dark .nav-holder nav a[href*="literature-understanding"]:hover,
|
|
|
|
| 547 |
width: 100% !important;
|
| 548 |
align-items: center;
|
| 549 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 550 |
.nav-holder nav a[href*="about"] {
|
| 551 |
grid-row: 1 !important;
|
| 552 |
+
grid-column: 7 !important;
|
| 553 |
}
|
| 554 |
.nav-holder nav a[href*="submit"] {
|
| 555 |
grid-row: 1 !important;
|
| 556 |
grid-column: 8 !important;
|
| 557 |
white-space: nowrap !important;
|
| 558 |
}
|
| 559 |
+
/* Hide the Alternative Agents page from the top-level nav for now. */
|
| 560 |
+
.nav-holder nav a[href*="alternative-agents"] {
|
| 561 |
+
display: none !important;
|
| 562 |
+
}
|
| 563 |
|
| 564 |
/* Divider line between header and category nav */
|
| 565 |
.nav-holder nav::after {
|
|
|
|
| 598 |
.nav-holder nav a[href*="discovery"] { grid-column: 4 !important; }
|
| 599 |
|
| 600 |
/* Navigation hover styles */
|
|
|
|
| 601 |
.nav-holder nav a[href*="about"]:hover,
|
| 602 |
.nav-holder nav a[href*="submit"]:hover,
|
| 603 |
.nav-holder nav a[href*="literature-understanding"]:hover,
|
|
|
|
| 607 |
background-color: #FDF9F4;
|
| 608 |
}
|
| 609 |
|
|
|
|
| 610 |
.dark .nav-holder nav a[href*="about"]:hover,
|
| 611 |
.dark .nav-holder nav a[href*="submit"]:hover,
|
| 612 |
.dark .nav-holder nav a[href*="literature-understanding"]:hover,
|
leaderboard_transformer.py
CHANGED
|
@@ -12,38 +12,6 @@ from constants import FONT_FAMILY, FONT_FAMILY_SHORT
|
|
| 12 |
|
| 13 |
logger = logging.getLogger(__name__)
|
| 14 |
|
| 15 |
-
_DATA_URI_CACHE: dict[str, str] = {}
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def get_asset_data_uri(path: str) -> Optional[str]:
|
| 19 |
-
"""Return a cached data URI for a local image asset."""
|
| 20 |
-
if path in _DATA_URI_CACHE:
|
| 21 |
-
return _DATA_URI_CACHE[path]
|
| 22 |
-
|
| 23 |
-
if not os.path.exists(path):
|
| 24 |
-
_DATA_URI_CACHE[path] = ""
|
| 25 |
-
return None
|
| 26 |
-
|
| 27 |
-
try:
|
| 28 |
-
with open(path, "rb") as f:
|
| 29 |
-
encoded = base64.b64encode(f.read()).decode("utf-8")
|
| 30 |
-
except Exception as e:
|
| 31 |
-
logger.warning(f"Could not load image asset {path}: {e}")
|
| 32 |
-
_DATA_URI_CACHE[path] = ""
|
| 33 |
-
return None
|
| 34 |
-
|
| 35 |
-
ext = os.path.splitext(path)[1].lower()
|
| 36 |
-
if ext == ".svg":
|
| 37 |
-
mime = "image/svg+xml"
|
| 38 |
-
elif ext == ".png":
|
| 39 |
-
mime = "image/png"
|
| 40 |
-
else:
|
| 41 |
-
mime = "application/octet-stream"
|
| 42 |
-
|
| 43 |
-
uri = f"data:{mime};base64,{encoded}"
|
| 44 |
-
_DATA_URI_CACHE[path] = uri
|
| 45 |
-
return uri
|
| 46 |
-
|
| 47 |
# Company logo mapping for graphs - maps model name patterns to company logo files
|
| 48 |
COMPANY_LOGO_MAP = {
|
| 49 |
"anthropic": {"path": "assets/logo-anthropic.svg", "name": "Anthropic"},
|
|
@@ -141,34 +109,42 @@ def get_openhands_logo_images():
|
|
| 141 |
images = []
|
| 142 |
|
| 143 |
# Light mode logo (visible in light mode, hidden in dark mode)
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
|
| 158 |
# Dark mode logo (hidden in light mode, visible in dark mode)
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
|
| 173 |
return images
|
| 174 |
|
|
@@ -535,50 +511,54 @@ def create_scatter_chart(
|
|
| 535 |
marker_info = get_marker_icon(model_name, openness, mark_by)
|
| 536 |
logo_path = marker_info['path']
|
| 537 |
|
| 538 |
-
|
| 539 |
-
|
| 540 |
-
|
| 541 |
-
|
| 542 |
-
|
| 543 |
-
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
|
| 561 |
-
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 582 |
|
| 583 |
# Add labels for frontier points only
|
| 584 |
for row in frontier_rows:
|
|
@@ -991,7 +971,7 @@ def _plot_scatter_plotly(
|
|
| 991 |
name: Optional[str] = None,
|
| 992 |
plot_type: str = 'cost', # 'cost' or 'runtime'
|
| 993 |
mark_by: Optional[str] = None, # 'Company', 'Openness', or 'Country'
|
| 994 |
-
show_all_labels: bool = False
|
| 995 |
) -> go.Figure:
|
| 996 |
from constants import MARK_BY_DEFAULT
|
| 997 |
if mark_by is None:
|
|
@@ -1213,8 +1193,25 @@ def _plot_scatter_plotly(
|
|
| 1213 |
y_min = min_score - 5 if min_score > 5 else 0
|
| 1214 |
y_max = max_score + 5
|
| 1215 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1216 |
def _encode_logo(path: str) -> Optional[str]:
|
| 1217 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1218 |
|
| 1219 |
# Composite markers: on the Alternative Agents page the dataframe carries
|
| 1220 |
# an "Agent" column (Claude Code / Codex / Gemini CLI / OpenHands Sub-agents),
|
|
@@ -1271,107 +1268,93 @@ def _plot_scatter_plotly(
|
|
| 1271 |
domain_x = max(0, min(1, domain_x))
|
| 1272 |
domain_y = max(0, min(1, domain_y))
|
| 1273 |
|
| 1274 |
-
# Convert to data coordinates
|
| 1275 |
-
# For log scale x: use log10(x) to match the axis type
|
| 1276 |
-
x_log = np.log10(x_val) if x_val > 0 else x_min_log
|
| 1277 |
-
|
| 1278 |
if harness_uri is not None:
|
| 1279 |
-
# Composite: stack model on top, harness on bottom
|
| 1280 |
-
#
|
| 1281 |
-
|
|
|
|
|
|
|
| 1282 |
layout_images.append(dict(
|
| 1283 |
source=model_logo_uri,
|
| 1284 |
-
xref="x", yref="y",
|
| 1285 |
-
x=
|
| 1286 |
-
sizex=STACKED_SIZE_X
|
| 1287 |
-
sizey=STACKED_SIZE_Y * (y_max - y_min),
|
| 1288 |
xanchor="center", yanchor="middle",
|
| 1289 |
layer="above",
|
| 1290 |
))
|
| 1291 |
layout_images.append(dict(
|
| 1292 |
source=harness_uri,
|
| 1293 |
-
xref="x", yref="y",
|
| 1294 |
-
x=
|
| 1295 |
-
sizex=STACKED_SIZE_X
|
| 1296 |
-
sizey=STACKED_SIZE_Y * (y_max - y_min),
|
| 1297 |
xanchor="center", yanchor="middle",
|
| 1298 |
layer="above",
|
| 1299 |
))
|
| 1300 |
else:
|
| 1301 |
-
# Single marker
|
|
|
|
|
|
|
|
|
|
| 1302 |
layout_images.append(dict(
|
| 1303 |
source=model_logo_uri,
|
| 1304 |
-
xref="x", yref="y",
|
| 1305 |
-
x=
|
| 1306 |
-
sizex=SINGLE_SIZE_X
|
| 1307 |
-
sizey=SINGLE_SIZE_Y * (y_max - y_min),
|
| 1308 |
xanchor="center", yanchor="middle",
|
| 1309 |
layer="above",
|
| 1310 |
))
|
| 1311 |
|
| 1312 |
-
# --- Section 7: Add Model Name Labels ---
|
| 1313 |
-
|
| 1314 |
-
|
| 1315 |
-
# Label all data points
|
| 1316 |
-
labels_data = []
|
| 1317 |
-
for _, row in data_plot.iterrows():
|
| 1318 |
-
x_val = row[x_col_to_use]
|
| 1319 |
-
y_val = row[y_col_to_use]
|
| 1320 |
-
|
| 1321 |
-
model_name = row.get('Language Model', '')
|
| 1322 |
-
if isinstance(model_name, list):
|
| 1323 |
-
model_name = model_name[0] if model_name else ''
|
| 1324 |
-
model_name = str(model_name).split('/')[-1]
|
| 1325 |
-
if len(model_name) > 25:
|
| 1326 |
-
model_name = model_name[:22] + '...'
|
| 1327 |
-
|
| 1328 |
-
labels_data.append({'x': x_val, 'y': y_val, 'label': model_name})
|
| 1329 |
-
elif frontier_rows:
|
| 1330 |
-
# Label only Pareto frontier points
|
| 1331 |
-
labels_data = []
|
| 1332 |
|
| 1333 |
for row in frontier_rows:
|
| 1334 |
x_val = row[x_col_to_use]
|
| 1335 |
y_val = row[y_col_to_use]
|
| 1336 |
|
|
|
|
| 1337 |
model_name = row.get('Language Model', '')
|
| 1338 |
if isinstance(model_name, list):
|
| 1339 |
model_name = model_name[0] if model_name else ''
|
|
|
|
| 1340 |
model_name = str(model_name).split('/')[-1]
|
|
|
|
| 1341 |
if len(model_name) > 25:
|
| 1342 |
model_name = model_name[:22] + '...'
|
| 1343 |
|
| 1344 |
-
|
| 1345 |
-
|
| 1346 |
-
|
| 1347 |
-
|
| 1348 |
-
|
| 1349 |
-
# For log scale x-axis, annotations need log10(x) coordinates (Plotly issue #2580)
|
| 1350 |
-
for item in labels_data:
|
| 1351 |
-
x_val = item['x']
|
| 1352 |
-
y_val = item['y']
|
| 1353 |
-
label = item['label']
|
| 1354 |
-
|
| 1355 |
-
# Transform x to log10 for annotation positioning on log scale
|
| 1356 |
-
if x_val > 0:
|
| 1357 |
-
x_log = np.log10(x_val)
|
| 1358 |
-
else:
|
| 1359 |
-
x_log = x_min_log
|
| 1360 |
|
| 1361 |
-
|
| 1362 |
-
|
| 1363 |
-
|
| 1364 |
-
|
| 1365 |
-
|
| 1366 |
-
|
| 1367 |
-
|
| 1368 |
-
|
| 1369 |
-
|
| 1370 |
-
|
| 1371 |
-
|
| 1372 |
-
|
| 1373 |
-
|
| 1374 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1375 |
|
| 1376 |
# --- Section 8: Configure Layout ---
|
| 1377 |
# Use the same axis ranges as calculated for domain coordinates
|
|
@@ -1490,38 +1473,47 @@ def format_score_column(df: pd.DataFrame, score_col_name: str) -> pd.DataFrame:
|
|
| 1490 |
return df.assign(**{score_col_name: df[score_col_name].apply(apply_formatting)})
|
| 1491 |
|
| 1492 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1493 |
def format_runtime_column(df: pd.DataFrame, runtime_col_name: str) -> pd.DataFrame:
|
| 1494 |
"""
|
| 1495 |
Applies custom formatting to a runtime column based on its corresponding score column.
|
| 1496 |
- If runtime is not null, formats as time with 's' suffix.
|
| 1497 |
- If runtime is null but score is not, it becomes "Missing".
|
| 1498 |
- If both runtime and score are null, it becomes "Not Submitted".
|
|
|
|
| 1499 |
Args:
|
| 1500 |
df: The DataFrame to modify.
|
| 1501 |
runtime_col_name: The name of the runtime column to format (e.g., "Average Runtime").
|
| 1502 |
Returns:
|
| 1503 |
The DataFrame with the formatted runtime column.
|
| 1504 |
"""
|
| 1505 |
-
# Find the corresponding score column by replacing "Runtime" with "Score"
|
| 1506 |
score_col_name = runtime_col_name.replace("Runtime", "Score")
|
| 1507 |
|
| 1508 |
-
# Ensure the score column actually exists to avoid errors
|
| 1509 |
if score_col_name not in df.columns:
|
| 1510 |
-
return df
|
| 1511 |
|
| 1512 |
def apply_formatting_logic(row):
|
| 1513 |
runtime_value = row[runtime_col_name]
|
| 1514 |
score_value = row[score_col_name]
|
| 1515 |
status_color = "#ec4899"
|
|
|
|
|
|
|
| 1516 |
|
| 1517 |
if pd.notna(runtime_value) and isinstance(runtime_value, (int, float)):
|
| 1518 |
-
return f"{runtime_value:.0f}s"
|
| 1519 |
elif pd.notna(score_value):
|
| 1520 |
-
return f'<span style="color: {status_color};">Missing</span>'
|
| 1521 |
else:
|
| 1522 |
-
return f'<span style="color: {status_color};">Not Submitted</span>'
|
| 1523 |
|
| 1524 |
-
# Apply the logic to the specified runtime column and update the DataFrame
|
| 1525 |
df[runtime_col_name] = df.apply(apply_formatting_logic, axis=1)
|
| 1526 |
|
| 1527 |
return df
|
|
|
|
| 12 |
|
| 13 |
logger = logging.getLogger(__name__)
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
# Company logo mapping for graphs - maps model name patterns to company logo files
|
| 16 |
COMPANY_LOGO_MAP = {
|
| 17 |
"anthropic": {"path": "assets/logo-anthropic.svg", "name": "Anthropic"},
|
|
|
|
| 109 |
images = []
|
| 110 |
|
| 111 |
# Light mode logo (visible in light mode, hidden in dark mode)
|
| 112 |
+
if os.path.exists(OPENHANDS_LOGO_PATH_LIGHT):
|
| 113 |
+
try:
|
| 114 |
+
with open(OPENHANDS_LOGO_PATH_LIGHT, "rb") as f:
|
| 115 |
+
logo_data = base64.b64encode(f.read()).decode('utf-8')
|
| 116 |
+
images.append(dict(
|
| 117 |
+
source=f"data:image/png;openhands=lightlogo;base64,{logo_data}",
|
| 118 |
+
xref="paper",
|
| 119 |
+
yref="paper",
|
| 120 |
+
x=0,
|
| 121 |
+
y=-0.15,
|
| 122 |
+
sizex=0.15,
|
| 123 |
+
sizey=0.15,
|
| 124 |
+
xanchor="left",
|
| 125 |
+
yanchor="bottom",
|
| 126 |
+
))
|
| 127 |
+
except Exception:
|
| 128 |
+
pass
|
| 129 |
|
| 130 |
# Dark mode logo (hidden in light mode, visible in dark mode)
|
| 131 |
+
if os.path.exists(OPENHANDS_LOGO_PATH_DARK):
|
| 132 |
+
try:
|
| 133 |
+
with open(OPENHANDS_LOGO_PATH_DARK, "rb") as f:
|
| 134 |
+
logo_data = base64.b64encode(f.read()).decode('utf-8')
|
| 135 |
+
images.append(dict(
|
| 136 |
+
source=f"data:image/png;openhands=darklogo;base64,{logo_data}",
|
| 137 |
+
xref="paper",
|
| 138 |
+
yref="paper",
|
| 139 |
+
x=0,
|
| 140 |
+
y=-0.15,
|
| 141 |
+
sizex=0.15,
|
| 142 |
+
sizey=0.15,
|
| 143 |
+
xanchor="left",
|
| 144 |
+
yanchor="bottom",
|
| 145 |
+
))
|
| 146 |
+
except Exception:
|
| 147 |
+
pass
|
| 148 |
|
| 149 |
return images
|
| 150 |
|
|
|
|
| 511 |
marker_info = get_marker_icon(model_name, openness, mark_by)
|
| 512 |
logo_path = marker_info['path']
|
| 513 |
|
| 514 |
+
if os.path.exists(logo_path):
|
| 515 |
+
try:
|
| 516 |
+
with open(logo_path, 'rb') as f:
|
| 517 |
+
encoded_logo = base64.b64encode(f.read()).decode('utf-8')
|
| 518 |
+
logo_uri = f"data:image/svg+xml;base64,{encoded_logo}"
|
| 519 |
+
|
| 520 |
+
if x_type == "date":
|
| 521 |
+
# For date axes, use data coordinates directly
|
| 522 |
+
layout_images.append(dict(
|
| 523 |
+
source=logo_uri,
|
| 524 |
+
xref="x",
|
| 525 |
+
yref="y",
|
| 526 |
+
x=x_val,
|
| 527 |
+
y=y_val,
|
| 528 |
+
sizex=15 * 24 * 60 * 60 * 1000, # ~15 days in milliseconds
|
| 529 |
+
sizey=3, # score units
|
| 530 |
+
xanchor="center",
|
| 531 |
+
yanchor="middle",
|
| 532 |
+
layer="above"
|
| 533 |
+
))
|
| 534 |
+
else:
|
| 535 |
+
# For log axes, use domain coordinates (0-1 range)
|
| 536 |
+
if x_type == "log" and x_val > 0:
|
| 537 |
+
log_x = np.log10(x_val)
|
| 538 |
+
domain_x = (log_x - x_range_log[0]) / (x_range_log[1] - x_range_log[0])
|
| 539 |
+
else:
|
| 540 |
+
domain_x = 0.5
|
| 541 |
+
|
| 542 |
+
domain_y = (y_val - y_range[0]) / (y_range[1] - y_range[0]) if (y_range[1] - y_range[0]) > 0 else 0.5
|
| 543 |
+
|
| 544 |
+
# Clamp to valid range
|
| 545 |
+
domain_x = max(0, min(1, domain_x))
|
| 546 |
+
domain_y = max(0, min(1, domain_y))
|
| 547 |
+
|
| 548 |
+
layout_images.append(dict(
|
| 549 |
+
source=logo_uri,
|
| 550 |
+
xref="x domain",
|
| 551 |
+
yref="y domain",
|
| 552 |
+
x=domain_x,
|
| 553 |
+
y=domain_y,
|
| 554 |
+
sizex=0.04,
|
| 555 |
+
sizey=0.06,
|
| 556 |
+
xanchor="center",
|
| 557 |
+
yanchor="middle",
|
| 558 |
+
layer="above"
|
| 559 |
+
))
|
| 560 |
+
except Exception:
|
| 561 |
+
pass
|
| 562 |
|
| 563 |
# Add labels for frontier points only
|
| 564 |
for row in frontier_rows:
|
|
|
|
| 971 |
name: Optional[str] = None,
|
| 972 |
plot_type: str = 'cost', # 'cost' or 'runtime'
|
| 973 |
mark_by: Optional[str] = None, # 'Company', 'Openness', or 'Country'
|
| 974 |
+
show_all_labels: bool = False
|
| 975 |
) -> go.Figure:
|
| 976 |
from constants import MARK_BY_DEFAULT
|
| 977 |
if mark_by is None:
|
|
|
|
| 1193 |
y_min = min_score - 5 if min_score > 5 else 0
|
| 1194 |
y_max = max_score + 5
|
| 1195 |
|
| 1196 |
+
# Cache base64-encoded logos across rows — every Claude model on the
|
| 1197 |
+
# Alternative Agents page points at the same assets/harness-claude-code.svg,
|
| 1198 |
+
# so decoding once per path is ~N× cheaper than once per point.
|
| 1199 |
+
_logo_cache: dict[str, str] = {}
|
| 1200 |
def _encode_logo(path: str) -> Optional[str]:
|
| 1201 |
+
if path in _logo_cache:
|
| 1202 |
+
return _logo_cache[path]
|
| 1203 |
+
if not os.path.exists(path):
|
| 1204 |
+
return None
|
| 1205 |
+
try:
|
| 1206 |
+
with open(path, "rb") as f:
|
| 1207 |
+
encoded = base64.b64encode(f.read()).decode("utf-8")
|
| 1208 |
+
except Exception as e:
|
| 1209 |
+
logger.warning(f"Could not load logo {path}: {e}")
|
| 1210 |
+
return None
|
| 1211 |
+
mime = "svg+xml" if path.lower().endswith(".svg") else "png"
|
| 1212 |
+
uri = f"data:image/{mime};base64,{encoded}"
|
| 1213 |
+
_logo_cache[path] = uri
|
| 1214 |
+
return uri
|
| 1215 |
|
| 1216 |
# Composite markers: on the Alternative Agents page the dataframe carries
|
| 1217 |
# an "Agent" column (Claude Code / Codex / Gemini CLI / OpenHands Sub-agents),
|
|
|
|
| 1268 |
domain_x = max(0, min(1, domain_x))
|
| 1269 |
domain_y = max(0, min(1, domain_y))
|
| 1270 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1271 |
if harness_uri is not None:
|
| 1272 |
+
# Composite: stack model on top, harness on bottom, clamping
|
| 1273 |
+
# each half to the plot area so markers near the edges don't
|
| 1274 |
+
# drift off-canvas.
|
| 1275 |
+
model_y = min(1, domain_y + STACKED_Y_OFFSET)
|
| 1276 |
+
harness_y = max(0, domain_y - STACKED_Y_OFFSET)
|
| 1277 |
layout_images.append(dict(
|
| 1278 |
source=model_logo_uri,
|
| 1279 |
+
xref="x domain", yref="y domain",
|
| 1280 |
+
x=domain_x, y=model_y,
|
| 1281 |
+
sizex=STACKED_SIZE_X, sizey=STACKED_SIZE_Y,
|
|
|
|
| 1282 |
xanchor="center", yanchor="middle",
|
| 1283 |
layer="above",
|
| 1284 |
))
|
| 1285 |
layout_images.append(dict(
|
| 1286 |
source=harness_uri,
|
| 1287 |
+
xref="x domain", yref="y domain",
|
| 1288 |
+
x=domain_x, y=harness_y,
|
| 1289 |
+
sizex=STACKED_SIZE_X, sizey=STACKED_SIZE_Y,
|
|
|
|
| 1290 |
xanchor="center", yanchor="middle",
|
| 1291 |
layer="above",
|
| 1292 |
))
|
| 1293 |
else:
|
| 1294 |
+
# Single marker (canonical OpenHands pages, or Alternative Agents
|
| 1295 |
+
# rows with an unknown harness name — the latter shouldn't happen
|
| 1296 |
+
# in practice since HARNESS_LOGO_PATHS covers every agent_name the
|
| 1297 |
+
# push-to-index script emits).
|
| 1298 |
layout_images.append(dict(
|
| 1299 |
source=model_logo_uri,
|
| 1300 |
+
xref="x domain", yref="y domain",
|
| 1301 |
+
x=domain_x, y=domain_y,
|
| 1302 |
+
sizex=SINGLE_SIZE_X, sizey=SINGLE_SIZE_Y,
|
|
|
|
| 1303 |
xanchor="center", yanchor="middle",
|
| 1304 |
layer="above",
|
| 1305 |
))
|
| 1306 |
|
| 1307 |
+
# --- Section 7: Add Model Name Labels to Frontier Points ---
|
| 1308 |
+
if frontier_rows:
|
| 1309 |
+
frontier_labels_data = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1310 |
|
| 1311 |
for row in frontier_rows:
|
| 1312 |
x_val = row[x_col_to_use]
|
| 1313 |
y_val = row[y_col_to_use]
|
| 1314 |
|
| 1315 |
+
# Get the model name for the label
|
| 1316 |
model_name = row.get('Language Model', '')
|
| 1317 |
if isinstance(model_name, list):
|
| 1318 |
model_name = model_name[0] if model_name else ''
|
| 1319 |
+
# Clean the model name (remove path prefixes)
|
| 1320 |
model_name = str(model_name).split('/')[-1]
|
| 1321 |
+
# Truncate long names
|
| 1322 |
if len(model_name) > 25:
|
| 1323 |
model_name = model_name[:22] + '...'
|
| 1324 |
|
| 1325 |
+
frontier_labels_data.append({
|
| 1326 |
+
'x': x_val,
|
| 1327 |
+
'y': y_val,
|
| 1328 |
+
'label': model_name
|
| 1329 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1330 |
|
| 1331 |
+
# Add annotations for each frontier label
|
| 1332 |
+
# For log scale x-axis, annotations need log10(x) coordinates (Plotly issue #2580)
|
| 1333 |
+
for item in frontier_labels_data:
|
| 1334 |
+
x_val = item['x']
|
| 1335 |
+
y_val = item['y']
|
| 1336 |
+
label = item['label']
|
| 1337 |
+
|
| 1338 |
+
# Transform x to log10 for annotation positioning on log scale
|
| 1339 |
+
if x_val > 0:
|
| 1340 |
+
x_log = np.log10(x_val)
|
| 1341 |
+
else:
|
| 1342 |
+
x_log = x_min_log
|
| 1343 |
+
|
| 1344 |
+
fig.add_annotation(
|
| 1345 |
+
x=x_log,
|
| 1346 |
+
y=y_val,
|
| 1347 |
+
text=label,
|
| 1348 |
+
showarrow=False,
|
| 1349 |
+
yshift=25, # Move label higher above the icon
|
| 1350 |
+
font=dict(
|
| 1351 |
+
size=10,
|
| 1352 |
+
color='#0D0D0F', # neutral-950
|
| 1353 |
+
family=FONT_FAMILY_SHORT
|
| 1354 |
+
),
|
| 1355 |
+
xanchor='center',
|
| 1356 |
+
yanchor='bottom'
|
| 1357 |
+
)
|
| 1358 |
|
| 1359 |
# --- Section 8: Configure Layout ---
|
| 1360 |
# Use the same axis ranges as calculated for domain coordinates
|
|
|
|
| 1473 |
return df.assign(**{score_col_name: df[score_col_name].apply(apply_formatting)})
|
| 1474 |
|
| 1475 |
|
| 1476 |
+
def _hidden_runtime_sort_key(runtime_value: float | int | None, score_value: float | int | None) -> str:
|
| 1477 |
+
"""Build a hidden prefix so Gradio's string-based runtime sorting behaves numerically."""
|
| 1478 |
+
if pd.notna(runtime_value) and isinstance(runtime_value, (int, float)):
|
| 1479 |
+
return f"{float(runtime_value):020.6f}"
|
| 1480 |
+
if pd.notna(score_value):
|
| 1481 |
+
return "99999999999999999998"
|
| 1482 |
+
return "99999999999999999999"
|
| 1483 |
+
|
| 1484 |
+
|
| 1485 |
def format_runtime_column(df: pd.DataFrame, runtime_col_name: str) -> pd.DataFrame:
|
| 1486 |
"""
|
| 1487 |
Applies custom formatting to a runtime column based on its corresponding score column.
|
| 1488 |
- If runtime is not null, formats as time with 's' suffix.
|
| 1489 |
- If runtime is null but score is not, it becomes "Missing".
|
| 1490 |
- If both runtime and score are null, it becomes "Not Submitted".
|
| 1491 |
+
- Adds a hidden, zero-padded numeric prefix so Gradio sorts the column numerically.
|
| 1492 |
Args:
|
| 1493 |
df: The DataFrame to modify.
|
| 1494 |
runtime_col_name: The name of the runtime column to format (e.g., "Average Runtime").
|
| 1495 |
Returns:
|
| 1496 |
The DataFrame with the formatted runtime column.
|
| 1497 |
"""
|
|
|
|
| 1498 |
score_col_name = runtime_col_name.replace("Runtime", "Score")
|
| 1499 |
|
|
|
|
| 1500 |
if score_col_name not in df.columns:
|
| 1501 |
+
return df
|
| 1502 |
|
| 1503 |
def apply_formatting_logic(row):
|
| 1504 |
runtime_value = row[runtime_col_name]
|
| 1505 |
score_value = row[score_col_name]
|
| 1506 |
status_color = "#ec4899"
|
| 1507 |
+
sort_key = _hidden_runtime_sort_key(runtime_value, score_value)
|
| 1508 |
+
hidden_sort_prefix = f'<span style="display:none">{sort_key}</span>'
|
| 1509 |
|
| 1510 |
if pd.notna(runtime_value) and isinstance(runtime_value, (int, float)):
|
| 1511 |
+
return f"{hidden_sort_prefix}{runtime_value:.0f}s"
|
| 1512 |
elif pd.notna(score_value):
|
| 1513 |
+
return f'{hidden_sort_prefix}<span style="color: {status_color};">Missing</span>'
|
| 1514 |
else:
|
| 1515 |
+
return f'{hidden_sort_prefix}<span style="color: {status_color};">Not Submitted</span>'
|
| 1516 |
|
|
|
|
| 1517 |
df[runtime_col_name] = df.apply(apply_formatting_logic, axis=1)
|
| 1518 |
|
| 1519 |
return df
|
simple_data_loader.py
CHANGED
|
@@ -245,6 +245,7 @@ class SimpleLeaderboardViewer:
|
|
| 245 |
'acp-claude': 'Claude Code',
|
| 246 |
'acp-codex': 'Codex',
|
| 247 |
'acp-gemini': 'Gemini CLI',
|
|
|
|
| 248 |
}
|
| 249 |
alt_dir = self.config_path / "alternative_agents"
|
| 250 |
if alt_dir.exists():
|
|
@@ -252,8 +253,6 @@ class SimpleLeaderboardViewer:
|
|
| 252 |
if not type_dir.is_dir():
|
| 253 |
continue
|
| 254 |
default_name = agent_type_default_name.get(type_dir.name)
|
| 255 |
-
if default_name is None:
|
| 256 |
-
continue # skip unlisted agent types (e.g. openhands_subagents)
|
| 257 |
for agent_dir in type_dir.iterdir():
|
| 258 |
if not agent_dir.is_dir():
|
| 259 |
continue
|
|
|
|
| 245 |
'acp-claude': 'Claude Code',
|
| 246 |
'acp-codex': 'Codex',
|
| 247 |
'acp-gemini': 'Gemini CLI',
|
| 248 |
+
'openhands_subagents': 'OpenHands Sub-agents',
|
| 249 |
}
|
| 250 |
alt_dir = self.config_path / "alternative_agents"
|
| 251 |
if alt_dir.exists():
|
|
|
|
| 253 |
if not type_dir.is_dir():
|
| 254 |
continue
|
| 255 |
default_name = agent_type_default_name.get(type_dir.name)
|
|
|
|
|
|
|
| 256 |
for agent_dir in type_dir.iterdir():
|
| 257 |
if not agent_dir.is_dir():
|
| 258 |
continue
|
ui_components.py
CHANGED
|
@@ -43,8 +43,6 @@ from content import (
|
|
| 43 |
api = HfApi()
|
| 44 |
os.makedirs(EXTRACTED_DATA_DIR, exist_ok=True)
|
| 45 |
|
| 46 |
-
_SVG_DATA_URI_CACHE: dict[str, str] = {}
|
| 47 |
-
|
| 48 |
|
| 49 |
def get_company_logo_html(model_name: str) -> str:
|
| 50 |
"""
|
|
@@ -83,18 +81,12 @@ OPENNESS_SVG_MAP = {
|
|
| 83 |
|
| 84 |
def get_svg_as_data_uri(path: str) -> str:
|
| 85 |
"""Reads an SVG file and returns it as a base64-encoded data URI."""
|
| 86 |
-
if path in _SVG_DATA_URI_CACHE:
|
| 87 |
-
return _SVG_DATA_URI_CACHE[path]
|
| 88 |
-
|
| 89 |
try:
|
| 90 |
with open(path, "rb") as svg_file:
|
| 91 |
encoded_svg = base64.b64encode(svg_file.read()).decode("utf-8")
|
| 92 |
-
|
| 93 |
-
_SVG_DATA_URI_CACHE[path] = uri
|
| 94 |
-
return uri
|
| 95 |
except FileNotFoundError:
|
| 96 |
print(f"Warning: SVG file not found at {path}")
|
| 97 |
-
_SVG_DATA_URI_CACHE[path] = ""
|
| 98 |
return ""
|
| 99 |
|
| 100 |
|
|
@@ -962,7 +954,7 @@ def create_leaderboard_display(
|
|
| 962 |
if not new_df.empty:
|
| 963 |
new_transformer = DataTransformer(new_df, new_tag_map)
|
| 964 |
new_df_view_full, _ = new_transformer.view(tag=category_name, use_plotly=True)
|
| 965 |
-
|
| 966 |
# Prepare both complete and all entries versions
|
| 967 |
if 'Categories Attempted' in new_df_view_full.columns:
|
| 968 |
new_df_view_complete = new_df_view_full[new_df_view_full['Categories Attempted'] == '5/5'].copy()
|
|
@@ -1022,22 +1014,16 @@ def create_leaderboard_display(
|
|
| 1022 |
|
| 1023 |
# Connect the timer to the refresh function
|
| 1024 |
if show_incomplete_checkbox is not None:
|
|
|
|
| 1025 |
if show_open_only_checkbox is not None:
|
| 1026 |
-
|
| 1027 |
-
|
| 1028 |
-
|
| 1029 |
-
|
| 1030 |
-
|
| 1031 |
-
|
| 1032 |
-
|
| 1033 |
-
|
| 1034 |
-
def _timer_refresh_no_open(show_incomplete, mark_by, show_all_labels):
|
| 1035 |
-
return check_and_refresh_data(show_incomplete, False, mark_by, show_all_labels)
|
| 1036 |
-
refresh_timer.tick(
|
| 1037 |
-
fn=_timer_refresh_no_open,
|
| 1038 |
-
inputs=[show_incomplete_checkbox, mark_by_dropdown, show_all_labels_checkbox],
|
| 1039 |
-
outputs=[dataframe_component, cost_plot_component, runtime_plot_component]
|
| 1040 |
-
)
|
| 1041 |
else:
|
| 1042 |
# If no incomplete checkbox, always show all data (but still filter by open if needed)
|
| 1043 |
def check_and_refresh_all(show_open_only=False, mark_by=MARK_BY_DEFAULT, show_all_labels=False):
|
|
|
|
| 43 |
api = HfApi()
|
| 44 |
os.makedirs(EXTRACTED_DATA_DIR, exist_ok=True)
|
| 45 |
|
|
|
|
|
|
|
| 46 |
|
| 47 |
def get_company_logo_html(model_name: str) -> str:
|
| 48 |
"""
|
|
|
|
| 81 |
|
| 82 |
def get_svg_as_data_uri(path: str) -> str:
|
| 83 |
"""Reads an SVG file and returns it as a base64-encoded data URI."""
|
|
|
|
|
|
|
|
|
|
| 84 |
try:
|
| 85 |
with open(path, "rb") as svg_file:
|
| 86 |
encoded_svg = base64.b64encode(svg_file.read()).decode("utf-8")
|
| 87 |
+
return f"data:image/svg+xml;base64,{encoded_svg}"
|
|
|
|
|
|
|
| 88 |
except FileNotFoundError:
|
| 89 |
print(f"Warning: SVG file not found at {path}")
|
|
|
|
| 90 |
return ""
|
| 91 |
|
| 92 |
|
|
|
|
| 954 |
if not new_df.empty:
|
| 955 |
new_transformer = DataTransformer(new_df, new_tag_map)
|
| 956 |
new_df_view_full, _ = new_transformer.view(tag=category_name, use_plotly=True)
|
| 957 |
+
|
| 958 |
# Prepare both complete and all entries versions
|
| 959 |
if 'Categories Attempted' in new_df_view_full.columns:
|
| 960 |
new_df_view_complete = new_df_view_full[new_df_view_full['Categories Attempted'] == '5/5'].copy()
|
|
|
|
| 1014 |
|
| 1015 |
# Connect the timer to the refresh function
|
| 1016 |
if show_incomplete_checkbox is not None:
|
| 1017 |
+
timer_inputs = [show_incomplete_checkbox]
|
| 1018 |
if show_open_only_checkbox is not None:
|
| 1019 |
+
timer_inputs.append(show_open_only_checkbox)
|
| 1020 |
+
timer_inputs.append(mark_by_dropdown) # Always include mark_by
|
| 1021 |
+
timer_inputs.append(show_all_labels_checkbox)
|
| 1022 |
+
refresh_timer.tick(
|
| 1023 |
+
fn=check_and_refresh_data,
|
| 1024 |
+
inputs=timer_inputs,
|
| 1025 |
+
outputs=[dataframe_component, cost_plot_component, runtime_plot_component]
|
| 1026 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1027 |
else:
|
| 1028 |
# If no incomplete checkbox, always show all data (but still filter by open if needed)
|
| 1029 |
def check_and_refresh_all(show_open_only=False, mark_by=MARK_BY_DEFAULT, show_all_labels=False):
|
visualizations.py
CHANGED
|
@@ -108,17 +108,7 @@ def create_accuracy_by_size_chart(df: pd.DataFrame, mark_by: str = None) -> go.F
|
|
| 108 |
open_aliases = [aliases.CANONICAL_OPENNESS_OPEN] + list(
|
| 109 |
aliases.OPENNESS_ALIASES.get(aliases.CANONICAL_OPENNESS_OPEN, [])
|
| 110 |
)
|
| 111 |
-
openness_col =
|
| 112 |
-
if openness_col is None:
|
| 113 |
-
fig = go.Figure()
|
| 114 |
-
fig.add_annotation(
|
| 115 |
-
text="No openness data available",
|
| 116 |
-
xref="paper", yref="paper",
|
| 117 |
-
x=0.5, y=0.5, showarrow=False,
|
| 118 |
-
font=STANDARD_FONT
|
| 119 |
-
)
|
| 120 |
-
fig.update_layout(**STANDARD_LAYOUT, title="Open Model Accuracy by Size")
|
| 121 |
-
return fig
|
| 122 |
|
| 123 |
plot_df = df[
|
| 124 |
(df[param_col].notna()) &
|
|
|
|
| 108 |
open_aliases = [aliases.CANONICAL_OPENNESS_OPEN] + list(
|
| 109 |
aliases.OPENNESS_ALIASES.get(aliases.CANONICAL_OPENNESS_OPEN, [])
|
| 110 |
)
|
| 111 |
+
openness_col = 'Openness' if 'Openness' in df.columns else 'openness'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
|
| 113 |
plot_df = df[
|
| 114 |
(df[param_col].notna()) &
|