DataAgent-evals / site /viewer.html
AdithyaSK's picture
AdithyaSK HF Staff
viz: bigger fonts, desktop-only gate, benchmark toggle + info popover, dense details panel with MΓ—H/MΓ—D/HΓ—D matrices
865e161
Raw
History Blame Contribute Delete
65.5 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Eval Sweep β€” Pass@k Dashboard</title>
<style>
:root {
--bg: #0e1116;
--panel: #161b22;
--panel2: #1e242c;
--border: #2c333d;
--text: #d7dadf;
--muted: #8a9099;
--accent: #59a6ff;
--pass: #3ec97a;
--fail: #e25c5c;
--warn: #e6b455;
--gray: #565d68;
}
* { box-sizing: border-box; }
* { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.18) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.16); border-radius: 4px;
border: 2px solid transparent; background-clip: content-box; }
*::-webkit-scrollbar-thumb:hover { background-color: rgba(255,255,255,0.32); }
*::-webkit-scrollbar-corner { background: transparent; }
html, body { height: 100%; margin: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;
background: var(--bg); color: var(--text); font-size: 14px; line-height: 1.5;
display: flex; flex-direction: column; min-height: 100vh; overflow: hidden; }
header { background: var(--panel); border-bottom: 1px solid var(--border); flex: 0 0 auto; }
.hdr-bar { padding: 12px 20px; display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.hdr-bar h1 { margin: 0; font-size: 16px; font-weight: 600; }
.hdr-bar .sub { color: var(--muted); font-size: 12px; }
.hdr-bar .pills { display: flex; gap: 7px; flex-wrap: wrap; margin-left: auto; }
.hdr-bar .pill-stat { background: var(--panel2); padding: 4px 11px; border-radius: 4px;
font-size: 12px; color: var(--text); border: 1px solid var(--border); }
.hdr-bar .pill-stat b { color: var(--accent); font-weight: 600; }
/* Dataset segmented control β€” sits next to the H1, switches the whole UI. */
.ds-tabs { display: inline-flex; gap: 0; background: var(--panel2); border: 1px solid var(--border);
border-radius: 6px; overflow: hidden; }
.ds-tabs button { background: transparent; color: var(--muted); border: 0; padding: 6px 14px;
font: inherit; font-size: 13px; cursor: pointer; transition: all 0.15s; }
.ds-tabs button:hover { color: var(--text); }
.ds-tabs button.active { background: var(--accent); color: #fff; font-weight: 600; }
/* Info icon next to the benchmark toggle β€” clicks open a popover with the benchmark's
description + source links. */
.info-btn { background: var(--panel2); color: var(--muted); border: 1px solid var(--border);
width: 26px; height: 26px; border-radius: 50%; cursor: pointer; display: inline-flex;
align-items: center; justify-content: center; font-size: 13px; font-weight: 600;
padding: 0; line-height: 1; transition: all 0.15s; }
.info-btn:hover { background: var(--border); color: var(--text); }
.info-popover { position: absolute; top: 100%; left: 0; margin-top: 6px; max-width: 420px;
background: var(--panel); border: 1px solid var(--border); border-radius: 6px;
padding: 14px 16px; box-shadow: 0 8px 24px rgba(0,0,0,0.5); z-index: 100;
font-size: 13px; line-height: 1.55; }
.info-popover.hidden { display: none; }
.info-popover h4 { margin: 0 0 8px 0; font-size: 14px; color: var(--text); }
.info-popover p { margin: 0 0 10px 0; color: var(--muted); }
.info-popover a { color: var(--accent); text-decoration: none; }
.info-popover a:hover { text-decoration: underline; }
.info-popover .src-list { display: flex; flex-direction: column; gap: 4px; }
.info-popover .src-list .lbl { color: var(--muted); font-size: 11px; text-transform: uppercase;
letter-spacing: 0.5px; margin-right: 6px; }
.bench-toggle-wrap { position: relative; display: inline-flex; align-items: center; gap: 8px; }
/* Full-page loading overlay β€” shown during initial fetch + on dataset switch. */
.loader-overlay { position: fixed; inset: 0; background: rgba(15, 17, 21, 0.85); z-index: 9999;
display: flex; flex-direction: column; align-items: center; justify-content: center;
gap: 14px; transition: opacity 0.2s; }
.loader-overlay.hidden { opacity: 0; pointer-events: none; }
.loader-spinner { width: 38px; height: 38px; border: 3px solid var(--panel2);
border-top-color: var(--accent); border-radius: 50%;
animation: spin 0.8s linear infinite; }
.loader-msg { color: var(--muted); font-size: 12px; letter-spacing: 0.4px; }
@keyframes spin { to { transform: rotate(360deg); } }
.hdr-bar .toggle { background: var(--panel2); color: var(--text); border: 1px solid var(--border);
padding: 4px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; }
.hdr-bar .toggle:hover { background: var(--border); }
/* Details panel β€” caps its own height + scrolls internally so the rest of the
page (filters, heatmap) stays in view. Without max-height it pushes the
heatmap off-screen and the page becomes unscrollable. */
.hdr-expanded { padding: 4px 18px 14px; border-top: 1px solid var(--border);
max-height: 70vh; overflow-y: auto; }
.hdr-expanded.hidden { display: none; }
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 8px; margin-top: 10px; }
.stat { background: var(--panel2); padding: 8px 12px; border-radius: 6px; border: 1px solid var(--border); }
.stat .num { font-size: 18px; font-weight: 600; }
.stat .lbl { color: var(--muted); font-size: 11px; margin-top: 2px; }
.agg-tables { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 12px; }
.agg-table { background: var(--panel2); padding: 10px; border-radius: 6px; border: 1px solid var(--border); }
.agg-table h4 { margin: 0 0 6px; font-size: 12px; color: var(--muted); }
.agg-table table { width: 100%; border-collapse: collapse; font-size: 11px; }
.agg-table th { text-align: left; padding: 3px 5px; color: var(--muted); border-bottom: 1px solid var(--border); }
.agg-table td { padding: 3px 5px; border-bottom: 1px solid var(--border); font-family: ui-monospace, SF Mono, Menlo, monospace; }
.agg-table .pct { color: var(--accent); }
/* Bar-chart blocks inside the details panel. Pure CSS β€” pass rate per
model / harness / difficulty, with one bar per row + a small numeric label. */
.charts { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 12px; }
.chart { background: var(--panel2); padding: 12px; border-radius: 6px; border: 1px solid var(--border); }
.chart h4 { margin: 0 0 10px; font-size: 12px; color: var(--muted); display: flex; justify-content: space-between; }
.chart h4 .sub { color: var(--gray); font-weight: 400; font-size: 10px; }
.chart .row { display: grid; grid-template-columns: 110px 1fr 64px; gap: 8px; align-items: center; margin-bottom: 5px; }
.chart .row:last-child { margin-bottom: 0; }
.chart .row .lbl { font-size: 11px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-family: ui-monospace, SF Mono, Menlo, monospace; }
.chart .row .bar-wrap { height: 14px; background: rgba(255,255,255,0.04); border-radius: 3px; overflow: hidden; position: relative; }
.chart .row .bar { height: 100%; background: linear-gradient(90deg, var(--accent), #7fc1ff); border-radius: 3px; transition: width 0.3s; }
.chart .row .num { font-size: 11px; color: var(--muted); font-family: ui-monospace, SF Mono, Menlo, monospace; text-align: right; }
.chart .row .num b { color: var(--accent); font-weight: 600; }
/* Stacked-bar variant for pass@k progression */
.chart .row .stacked { display: flex; height: 14px; border-radius: 3px; overflow: hidden;
background: rgba(255,255,255,0.04); }
.chart .row .stacked > span { height: 100%; }
.chart .row .stacked .s1 { background: rgba(62,201,122,0.85); }
.chart .row .stacked .s2 { background: rgba(62,201,122,0.62); }
.chart .row .stacked .s3 { background: rgba(62,201,122,0.42); }
.chart .row .stacked .s4 { background: rgba(62,201,122,0.26); }
.chart-legend { display: flex; gap: 8px; font-size: 10px; color: var(--muted); margin-top: 6px; }
.chart-legend .swatch { width: 10px; height: 10px; display: inline-block; vertical-align: middle; margin-right: 3px; border-radius: 2px; }
/* ─── Dense details panel ──────────────────────────────────────────────── */
/* Compact KPI cards along the top of the panel. */
.stats { grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 8px; margin-top: 10px; }
.stat { padding: 8px 12px; }
.stat .num { font-size: 17px; }
.stat .lbl { font-size: 12px; margin-top: 2px; }
/* Marginal tables (per-model, per-harness) with @1/@2/@3/@4 heat-tinted cells. */
.marg-card { grid-column: 1 / -1; background: var(--panel2); padding: 12px 14px;
border-radius: 6px; border: 1px solid var(--border); }
.marg-card h4 { margin: 0 0 10px; font-size: 13px; color: var(--muted);
display: flex; justify-content: space-between; }
.marg-card h4 .sub { color: var(--gray); font-weight: 400; font-size: 11px; }
.marg-wrap { overflow-x: auto; }
.marg-table { border-collapse: collapse; font-size: 13px; width: 100%;
font-family: ui-monospace, SF Mono, Menlo, monospace; }
.marg-table th { text-align: left; padding: 6px 10px; color: var(--muted);
font-weight: 500; border-bottom: 1px solid var(--border); white-space: nowrap;
font-size: 12px; }
.marg-table th.kc { text-align: center; }
.marg-table td { padding: 6px 10px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.marg-table td.lbl { color: var(--text); }
.marg-table td.num { text-align: right; color: var(--muted); }
.marg-table td.kc { text-align: center; min-width: 90px; position: relative; padding: 6px 4px; }
.marg-table td.kc .kc-num { position: relative; font-weight: 600; font-size: 13px; color: var(--text); }
.marg-table td.kc .kc-sub { position: relative; font-size: 10px; color: var(--muted); }
/* Tiny stacked sparkline column showing @1β†’@k progression */
.marg-table td.spark { padding: 6px 8px; min-width: 100px; }
.marg-table td.spark .sparkbar { display: flex; height: 10px; border-radius: 2px; overflow: hidden;
background: rgba(255,255,255,0.04); }
.marg-table td.spark .sparkbar > span { height: 100%; }
.marg-table td.spark .sparkbar .s1 { background: rgba(62,201,122,0.85); }
.marg-table td.spark .sparkbar .s2 { background: rgba(62,201,122,0.62); }
.marg-table td.spark .sparkbar .s3 { background: rgba(62,201,122,0.42); }
.marg-table td.spark .sparkbar .s4 { background: rgba(62,201,122,0.26); }
/* Heat-tinted matrices β€” one cell per (axis-A, axis-B) pair. */
.matrices { display: grid; grid-template-columns: repeat(auto-fit, minmax(420px, 1fr));
gap: 12px; grid-column: 1 / -1; }
.matrix-card { background: var(--panel2); padding: 12px 14px;
border-radius: 6px; border: 1px solid var(--border); }
.matrix-card h4 { margin: 0 0 10px; font-size: 13px; color: var(--muted);
display: flex; justify-content: space-between; }
.matrix-card h4 .sub { color: var(--gray); font-weight: 400; font-size: 11px; }
.matrix-card .matrix-wrap { overflow-x: auto; }
.matrix-card table { border-collapse: collapse; font-size: 12px;
font-family: ui-monospace, SF Mono, Menlo, monospace; }
.matrix-card th, .matrix-card td { padding: 6px 9px; border: 1px solid var(--border);
text-align: center; white-space: nowrap; }
.matrix-card th { background: var(--panel); color: var(--muted); font-weight: 500; font-size: 12px; }
.matrix-card td.lbl { background: var(--panel); color: var(--text); text-align: left; }
.matrix-card td.cell { color: var(--text); }
.matrix-card td.cell b { font-size: 13px; }
.matrix-card td.cell .det { color: rgba(255,255,255,0.6); font-size: 11px; }
.matrix-card td.empty { background: transparent; color: var(--gray); }
/* ─── Desktop only β€” show a friendly gate on small viewports ─────────────
The dashboard is data-dense (multi-column tables, heatmaps, side panels) and
does not condense gracefully below ~900px. Rather than mangle layout on
phones, we show a single message and hide the app. */
.desktop-only-gate { display: none; position: fixed; inset: 0; z-index: 99999;
background: var(--bg); color: var(--text); flex-direction: column;
align-items: center; justify-content: center; gap: 16px;
padding: 28px; text-align: center; font-size: 15px; line-height: 1.55; }
.desktop-only-gate .icon { font-size: 42px; }
.desktop-only-gate h2 { margin: 0; font-size: 18px; color: var(--text); }
.desktop-only-gate p { margin: 0; color: var(--muted); max-width: 360px; }
@media (max-width: 900px) {
.desktop-only-gate { display: flex; }
/* Hide everything else underneath; the gate fully covers the page. */
body > header, body > main, body > .filters, body > .loader-overlay { display: none !important; }
}
.filters { padding: 8px 18px; background: var(--panel); border-bottom: 1px solid var(--border);
display: flex; gap: 10px; flex-wrap: wrap; align-items: center; flex: 0 0 auto; }
.filters input, .filters select { background: var(--panel2); color: var(--text);
border: 1px solid var(--border); padding: 4px 8px; border-radius: 4px; font-size: 12px; }
.filters input[type=search] { width: 240px; }
.filters label { color: var(--muted); font-size: 12px; }
.filters input, .filters select { font-size: 13px; padding: 5px 10px; }
.filters input[type=search] { width: 280px; }
.view-tabs { display: flex; gap: 0; border: 1px solid var(--border); border-radius: 4px; overflow: hidden; }
.view-tabs button { background: var(--panel2); color: var(--muted); border: 0; padding: 4px 12px; cursor: pointer; font-size: 11px; }
.view-tabs button.active { background: var(--accent); color: white; }
main { flex: 1 1 auto; display: flex; flex-direction: row; min-height: 0; overflow: hidden; }
.view-pane { flex: 1 1 auto; overflow: auto; min-width: 0; min-height: 0; }
.view-pane.hidden { display: none; }
.side-pane { flex: 0 0 640px; overflow: hidden; border-left: 1px solid var(--border);
background: var(--panel); display: flex; flex-direction: column; min-height: 0;
position: relative; }
.side-resizer { position: absolute; left: -3px; top: 0; bottom: 0; width: 7px; cursor: col-resize;
z-index: 10; background: transparent; transition: background 0.15s; }
.side-resizer:hover, .side-resizer.active { background: rgba(89,166,255,0.35); }
body.resizing { cursor: col-resize !important; user-select: none; }
body.resizing * { user-select: none; pointer-events: none; }
body.resizing .side-resizer { pointer-events: auto; }
.side-pane.hidden { display: none; }
.side-head { padding: 8px 14px; background: var(--panel2); border-bottom: 1px solid var(--border);
display: flex; gap: 10px; align-items: center; flex: 0 0 auto; }
.side-head h2 { font-size: 13px; margin: 0; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side-head button { background: var(--panel); color: var(--text); border: 1px solid var(--border);
padding: 3px 10px; border-radius: 3px; cursor: pointer; font-size: 11px; }
.side-head button:hover { background: var(--border); }
.side-body { padding: 12px 16px; overflow-y: auto; flex: 1 1 auto; min-height: 0; }
.heatmap-wrap { padding: 0; }
table.heatmap { border-collapse: separate; border-spacing: 0; font-size: 12px;
table-layout: fixed; width: max-content; }
table.heatmap col.cg-task { width: 320px; }
table.heatmap col.cg-h { width: 90px; }
table.heatmap col.cg-cell { width: 64px; }
table.heatmap th, table.heatmap td { border-right: 1px solid var(--border); border-bottom: 1px solid var(--border);
padding: 0; vertical-align: middle; overflow: hidden; }
table.heatmap thead th { position: sticky; top: 0; z-index: 3; background: var(--panel); padding: 4px 6px;
text-align: center; font-weight: 600; color: var(--text); font-size: 10.5px;
white-space: nowrap; }
table.heatmap th.col-task, table.heatmap td.col-task {
position: sticky; left: 0; z-index: 5;
background-color: #0e1116;
text-align: left; padding: 4px 8px;
border-right: 2px solid var(--border);
}
table.heatmap thead th.col-task { z-index: 7; background-color: #161b22; }
table.heatmap th.col-h, table.heatmap td.col-h {
position: sticky; left: 320px; z-index: 5;
background-color: #0e1116;
padding: 0 8px;
font-family: ui-monospace, monospace; font-size: 10.5px;
border-right: 2px solid var(--border); color: var(--muted); text-align: left;
}
table.heatmap thead th.col-h { z-index: 7; background-color: #161b22; }
table.heatmap td.col-task { font-size: 12px; line-height: 1.4; vertical-align: middle; }
table.heatmap td.col-task .tid { font-family: ui-monospace, monospace; font-size: 11px; color: var(--muted); }
table.heatmap td.col-task .q { color: var(--text); margin-top: 2px;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
table.heatmap td.col-task .grad-mini { font-size: 9.5px; padding: 1px 5px; border-radius: 3px; margin-right: 4px; }
.gm-yes { background: rgba(62,201,122,0.18); color: var(--pass); }
.gm-partial { background: rgba(230,180,85,0.18); color: var(--warn); }
.gm-no { background: rgba(226,92,92,0.18); color: var(--fail); }
table.heatmap td.cell { width: 64px; height: 26px; cursor: pointer; text-align: center;
font-family: ui-monospace, monospace; font-size: 11px;
transition: filter 0.1s; }
table.heatmap td.cell:hover { filter: brightness(1.4); outline: 1px solid var(--accent); outline-offset: -1px; }
td.cell.empty { background: transparent; color: var(--gray); cursor: default; }
td.cell.empty:hover { filter: none; outline: 0; }
/* Pass@k color gradient β€” green darker β†’ lighter as k grows */
td.cell.pass-1 { background: rgba(62,201,122,0.55); color: white; font-weight: 600; }
td.cell.pass-2 { background: rgba(62,201,122,0.40); color: white; font-weight: 600; }
td.cell.pass-3 { background: rgba(62,201,122,0.28); color: white; font-weight: 600; }
td.cell.pass-4 { background: rgba(62,201,122,0.18); color: white; font-weight: 600; }
td.cell.fail { background: rgba(226,92,92,0.25); color: rgba(255,255,255,0.85); }
td.cell.no_answer { background: rgba(226,92,92,0.12); color: rgba(226,92,92,0.7); }
td.cell.err { background: rgba(230,180,85,0.18); color: var(--warn); }
tr.task-spacer td { border: 0 !important; background-color: #0e1116; height: 8px; padding: 0; }
tr.selected td.col-task, tr.selected td.col-h { background: rgba(89,166,255,0.10); }
tr.selected td.col-task { border-left: 3px solid var(--accent); padding-left: 5px; }
/* List view */
#view-list { padding: 0; }
.task-row { padding: 8px 14px; border-bottom: 1px solid var(--border); cursor: pointer;
display: grid; grid-template-columns: 28px 1fr auto; gap: 10px; align-items: center; }
.task-row:hover { background: var(--panel); }
.task-row.selected { background: var(--panel2); border-left: 3px solid var(--accent); padding-left: 11px; }
.diff { font-size: 10px; text-transform: uppercase; font-weight: 600; padding: 1px 6px;
border-radius: 3px; letter-spacing: 0.5px; display: inline-block; }
.diff.easy { background: rgba(62, 201, 122, 0.15); color: var(--pass); }
.diff.medium { background: rgba(230, 180, 85, 0.15); color: var(--warn); }
.diff.hard { background: rgba(226, 92, 92, 0.15); color: var(--fail); }
.task-row .id { font-family: ui-monospace, monospace; font-size: 11px; color: var(--muted); }
.task-row .q { color: var(--text); margin-top: 2px; }
.grad-badge { font-size: 10px; padding: 2px 7px; border-radius: 3px; font-weight: 600; white-space: nowrap; }
.grad-yes { background: rgba(62, 201, 122, 0.2); color: var(--pass); }
.grad-partial { background: rgba(230, 180, 85, 0.2); color: var(--warn); }
.grad-no { background: rgba(226, 92, 92, 0.2); color: var(--fail); }
/* Detail */
.side-body h2 { font-size: 14px; margin: 0 0 6px; }
.side-body .question { background: var(--panel2); padding: 10px; border-radius: 5px;
border-left: 3px solid var(--accent); margin-bottom: 10px; }
.side-body .meta { color: var(--muted); font-size: 11px; margin-bottom: 8px; }
.side-body .meta b { color: var(--text); }
.side-body .gold { background: rgba(62,201,122,0.08); padding: 6px 10px; border-radius: 4px;
font-family: ui-monospace, monospace; font-size: 12px; margin-bottom: 10px;
border-left: 3px solid var(--pass); }
.trial-table { width: 100%; border-collapse: collapse; font-size: 11px; margin-top: 6px; }
.trial-table th { text-align: left; padding: 5px 6px; color: var(--muted); font-weight: 500;
border-bottom: 1px solid var(--border); font-size: 10px; text-transform: uppercase; }
.trial-table td { padding: 5px 6px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.trial-table tr:hover { background: var(--panel2); }
.trial-table .k { font-family: ui-monospace, monospace; font-weight: 600; }
.trial-table .reward.pass { color: var(--pass); font-weight: 600; }
.trial-table .reward.fail { color: var(--fail); }
.trial-table .view-btn { background: var(--panel2); color: var(--accent); border: 1px solid var(--border);
padding: 2px 8px; border-radius: 3px; cursor: pointer; font-size: 10px; }
.trial-table .view-btn:hover { background: var(--border); }
.agent-pill { font-size: 10px; padding: 1px 5px; border-radius: 3px; background: var(--panel2);
border: 1px solid var(--border); font-family: ui-monospace, monospace; }
.msg { margin-bottom: 8px; border-radius: 5px; overflow: hidden; }
.msg .msg-head { padding: 4px 10px; font-size: 10px; text-transform: uppercase; font-weight: 600;
letter-spacing: 0.5px; }
.msg .msg-body { padding: 8px 12px; white-space: pre-wrap; word-break: break-word;
font-family: ui-monospace, SF Mono, Menlo, monospace; font-size: 11.5px; line-height: 1.5; }
.msg.system .msg-head { background: #2c3a4f; color: #b6c4e2; }
.msg.system .msg-body { background: rgba(44, 58, 79, 0.3); }
.msg.user .msg-head { background: #1f3a4d; color: #9bcce8; }
.msg.user .msg-body { background: rgba(31, 58, 77, 0.3); }
.msg.assistant .msg-head { background: #2e4030; color: #a8d5af; }
.msg.assistant .msg-body { background: rgba(46, 64, 48, 0.25); }
.msg.tool .msg-head { background: #4a3e23; color: #e6c98a; }
.msg.tool .msg-body { background: rgba(74, 62, 35, 0.25); }
.tool-call { margin-top: 6px; border: 1px solid var(--border); border-radius: 4px; overflow: hidden; background: var(--panel2); }
.tool-call .tc-head { padding: 5px 10px; background: rgba(89,166,255,0.08); display: flex; gap: 10px;
align-items: center; font-size: 11px; flex-wrap: wrap; border-bottom: 1px solid var(--border); }
.tool-call .tc-name { color: var(--accent); font-weight: 600; font-family: ui-monospace, monospace; }
.tool-call .tc-id { color: var(--gray); font-size: 9.5px; font-family: ui-monospace, monospace; }
.tool-call .tc-meta { color: var(--muted); font-size: 10.5px; font-family: ui-monospace, monospace;
margin-left: auto; }
.tool-call .tc-code { margin: 0; padding: 8px 12px; background: #0a0d12; font-family: ui-monospace, SF Mono, Menlo, monospace;
font-size: 11.5px; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
max-height: 360px; overflow: auto; color: #e8eaef; border: 0; }
.tool-call .tc-code.lang-python { border-left: 3px solid #4584b6; }
.tool-call .tc-code.lang-bash { border-left: 3px solid #4eaa25; }
.tool-call .tc-code.lang-json { border-left: 3px solid #cb7d29; }
.tool-output { margin: 0; padding: 8px 12px; background: rgba(0,0,0,0.35);
font-family: ui-monospace, SF Mono, Menlo, monospace; font-size: 11.5px; line-height: 1.5;
white-space: pre-wrap; word-break: break-word; max-height: 360px; overflow: auto;
color: #c9d0d8; border-top: 1px solid var(--border); }
.tool-output.empty { color: var(--gray); font-style: italic; }
.tool-output-head { padding: 4px 10px; background: rgba(74,62,35,0.25); color: #e6c98a;
font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;
display: flex; gap: 8px; }
.tool-output-head .rc { margin-left: auto; color: var(--muted); }
.turn { border: 1px solid var(--border); border-radius: 6px; padding: 0; margin-bottom: 10px;
background: var(--panel); overflow: hidden; }
.turn-head { padding: 4px 12px; background: var(--panel2); color: var(--muted); font-size: 10px;
text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600;
border-bottom: 1px solid var(--border); }
.turn-thinking { padding: 8px 12px; color: #a8d5af; white-space: pre-wrap; word-break: break-word;
font-size: 12px; line-height: 1.55; }
.pill { display: inline-block; padding: 1px 6px; border-radius: 3px; font-size: 10px;
background: var(--panel2); color: var(--muted); border: 1px solid var(--border); margin-right: 4px; }
.pill.k { font-family: ui-monospace, monospace; }
.pill.ok { background: rgba(62,201,122,0.15); color: var(--pass); border-color: transparent; }
.pill.err { background: rgba(226,92,92,0.15); color: var(--fail); border-color: transparent; }
.pill.warn { background: rgba(230,180,85,0.15); color: var(--warn); border-color: transparent; }
.grader-box { background: var(--panel2); padding: 8px 10px; border-radius: 4px; font-family: ui-monospace, monospace;
font-size: 11px; white-space: pre-wrap; color: var(--muted); margin-bottom: 8px; }
details { margin: 10px 0; }
details summary { cursor: pointer; color: var(--muted); padding: 4px 0; }
details > pre { background: var(--panel2); padding: 8px; border-radius: 4px; overflow-x: auto;
font-size: 11px; max-height: 300px; }
.legend { display: inline-flex; gap: 8px; align-items: center; font-size: 10.5px; color: var(--muted); margin-left: 8px; }
.legend .swatch { display: inline-block; width: 14px; height: 12px; border-radius: 2px; margin-right: 3px; vertical-align: middle; }
</style>
</head>
<body>
<!-- Desktop-only gate β€” shown under 900px viewport, hides everything else. -->
<div class="desktop-only-gate">
<div class="icon">πŸ–₯️</div>
<h2>Please view on desktop</h2>
<p>This dashboard is data-dense β€” heatmaps, multi-column tables, and side-by-side trajectory panels. It's optimized for a wider screen. Open this page on a laptop or larger display to use it.</p>
</div>
<!-- Full-page loader, removed once the first dataset's summary lands. -->
<div class="loader-overlay" id="loader">
<div class="loader-spinner"></div>
<div class="loader-msg" id="loader-msg">loading…</div>
</div>
<header>
<div class="hdr-bar">
<div class="bench-toggle-wrap">
<div class="ds-tabs" id="ds-tabs"></div>
<button class="info-btn" id="info-btn" title="benchmark info" onclick="toggleInfoPopover()">β“˜</button>
<div class="info-popover hidden" id="info-popover"></div>
</div>
<div class="pills" id="hdr-pills"></div>
<button class="toggle" onclick="toggleHeader()" id="toggle-btn">β–Ό details</button>
</div>
<div class="hdr-expanded hidden" id="hdr-expanded">
<div class="stats" id="stats"></div>
<div class="charts" id="charts"></div>
</div>
</header>
<div class="filters">
<input type="search" id="search" placeholder="Search task id, question, kaggle…" />
<label>Difficulty</label>
<select id="diff">
<option value="">all</option>
<option value="0">L0</option><option value="1">L1</option>
<option value="2">L2</option><option value="3">L3</option>
<option value="4">L4</option>
</select>
<label>Status</label>
<select id="status"><option value="">all</option><option value="grad">graduated (β‰₯2 harnesses)</option><option value="partial">any pass</option><option value="fail">all failed</option></select>
<label>Pass on harness</label>
<select id="harness"><option value="">any</option></select>
<div class="view-tabs" style="margin-left:auto">
<button id="tab-heatmap" class="active" onclick="setView('heatmap')">heatmap</button>
<button id="tab-list" onclick="setView('list')">list</button>
</div>
<span class="legend">
<span><span class="swatch" style="background:rgba(62,201,122,0.55)"></span>pass@1</span>
<span><span class="swatch" style="background:rgba(62,201,122,0.40)"></span>@2</span>
<span><span class="swatch" style="background:rgba(62,201,122,0.28)"></span>@3</span>
<span><span class="swatch" style="background:rgba(62,201,122,0.18)"></span>@4</span>
<span><span class="swatch" style="background:rgba(226,92,92,0.25)"></span>fail</span>
<span><span class="swatch" style="background:transparent;border:1px solid var(--border)"></span>not run</span>
</span>
<span id="count" style="color:var(--muted);font-size:11px"></span>
</div>
<main>
<div class="view-pane" id="view-heatmap">
<div class="heatmap-wrap" id="heatmap-wrap"></div>
</div>
<div class="view-pane hidden" id="view-list"></div>
<aside class="side-pane hidden" id="side-pane">
<div class="side-resizer" id="side-resizer" title="drag to resize"></div>
<div class="side-head">
<button id="side-back" onclick="sideBack()" style="display:none">← back</button>
<h2 id="side-title"></h2>
<button onclick="sideClose()">close βœ•</button>
</div>
<div class="side-body" id="side-body"></div>
</aside>
</main>
<script>
let DATA = null;
let selectedTaskIdx = null;
let currentView = 'heatmap';
function fmtCost(c) { return '$' + (c||0).toFixed(4); }
function fmtT(n) { if(!n) return '0'; if(n>=1e6) return (n/1e6).toFixed(1)+'M'; if(n>=1e3) return (n/1e3).toFixed(1)+'k'; return ''+n; }
function fmtDur(s) { s = s||0; if(s<60) return s.toFixed(0)+'s'; return (s/60).toFixed(1)+'m'; }
function escape(s) { return String(s == null ? '' : s)
.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
.replace(/"/g,'&quot;'); }
function gradeClass(t) { if (t.graduated) return 'grad-yes'; if (t.any_pass) return 'grad-partial'; return 'grad-no'; }
function gradeLabel(t) {
if (t.graduated) return `βœ“ graduated @k${t.best_passed_at}`;
if (t.any_pass) return `~ partial (${t.passing_harnesses.length} harness)`;
return 'βœ— all failed';
}
function gradeMiniClass(t) { if (t.graduated) return 'gm-yes'; if (t.any_pass) return 'gm-partial'; return 'gm-no'; }
function gradeMiniLabel(t) {
if (t.graduated) return 'βœ“' + (t.best_passed_at != null ? `@${t.best_passed_at}` : '');
if (t.any_pass) return '~';
return 'βœ—';
}
function renderHeaderStats() {
const s = DATA.summary;
document.getElementById('hdr-pills').innerHTML = [
['Tasks', s.tasks_total],
['Any pass', s.tasks_any_pass],
['Graduated', s.tasks_graduated],
['Failed', s.tasks_failed],
['Attempts', s.attempts_total],
['Tok in/out', fmtT(s.tokens_prompt) + '/' + fmtT(s.tokens_completion)],
].map(([l,v]) => `<span class="pill-stat">${l} <b>${v}</b></span>`).join('');
const stats = [
['Tasks', s.tasks_total],
['Graduated (β‰₯2 harnesses)', s.tasks_graduated],
['Any pass (β‰₯1)', s.tasks_any_pass],
['Failed (0)', s.tasks_failed],
['Attempts', s.attempts_total],
['Attempts passed', s.attempts_passed],
['Tokens (in/out)', `${fmtT(s.tokens_prompt)} / ${fmtT(s.tokens_completion)}`],
];
document.getElementById('stats').innerHTML = stats.map(([l,n]) =>
`<div class="stat"><div class="num">${n}</div><div class="lbl">${l}</div></div>`
).join('');
const byModel = DATA.by_model.slice();
const byAgent = DATA.by_agent.slice();
renderCharts(s, byModel, byAgent);
// populate harness dropdown
const hsel = document.getElementById('harness');
for (const a of DATA.agents_order) {
const o = document.createElement('option');
o.value = a; o.textContent = a;
hsel.appendChild(o);
}
}
function toggleHeader() {
const ex = document.getElementById('hdr-expanded');
ex.classList.toggle('hidden');
document.getElementById('toggle-btn').textContent =
ex.classList.contains('hidden') ? 'β–Ό details' : 'β–² hide details';
}
// ─── Dense details panel ─────────────────────────────────────────────────
// Compact, no-repeat layout:
// 1. KPI strip (rendered in renderHeaderStats above)
// 2. Per-model marginal table β€” Cells | @1 | @2 | @3 | @4 | spark | Tok i/o
// 3. Per-harness marginal table β€” same shape
// 4. Three matrices: ModelΓ—Harness, ModelΓ—Difficulty, HarnessΓ—Difficulty
function renderCharts(s, byModel, byAgent) {
const charts = document.getElementById('charts');
if (!charts) return;
// ── Compute @1..@k_max counts per (model, agent) by walking task cells ────
// by_model / by_agent only carry @1 and @k_max; we want all four. Also need
// joint counts for the three matrices. Single pass over DATA.tasks β†’ cells.
const KMAX = s.k_max || 4;
const harnessOrder = (DATA.agents_order && DATA.agents_order.length)
? DATA.agents_order.slice()
: byAgent.map(a => a.agent);
const agentLabel = {};
for (const a of byAgent) agentLabel[a.agent] = a.agent_label || a.agent;
const blank = () => ({cells: 0, k: Array(KMAX).fill(0)}); // k[i] = # cells that passed at attempt ≀ i+1
const modelAgg = {};
const agentAgg = {};
const diffAgg = {};
const mxhAgg = {}; // model β†’ agent β†’ blank
const mxdAgg = {}; // model β†’ diff β†’ blank
const hxdAgg = {}; // agent β†’ diff β†’ blank
const diffOrder = []; // preserve first-seen order from tasks
for (const t of (DATA.tasks || [])) {
const dkey = (t.difficulty || ('L' + (t.difficulty_level ?? 0))).toString();
if (!diffAgg[dkey]) { diffAgg[dkey] = blank(); diffOrder.push(dkey); }
for (const ck of Object.keys(t.cells || {})) {
const c = t.cells[ck];
const m = c.model, a = c.agent;
modelAgg[m] = modelAgg[m] || blank();
agentAgg[a] = agentAgg[a] || blank();
mxhAgg[m] = mxhAgg[m] || {}; mxhAgg[m][a] = mxhAgg[m][a] || blank();
mxdAgg[m] = mxdAgg[m] || {}; mxdAgg[m][dkey] = mxdAgg[m][dkey] || blank();
hxdAgg[a] = hxdAgg[a] || {}; hxdAgg[a][dkey] = hxdAgg[a][dkey] || blank();
const slots = [modelAgg[m], agentAgg[a], diffAgg[dkey],
mxhAgg[m][a], mxdAgg[m][dkey], hxdAgg[a][dkey]];
for (const sl of slots) sl.cells += 1;
// c.passed_at = attempt at which the cell first passed (1..k_max) or null.
const pa = c.passed_at;
if (pa != null && pa >= 1 && pa <= KMAX) {
for (const sl of slots) for (let k = pa - 1; k < KMAX; k++) sl.k[k] += 1;
}
}
}
// Heat color helper — red→amber→green by rate.
const heatBg = (pct) => {
if (pct == null || isNaN(pct)) return 'transparent';
const p = Math.max(0, Math.min(1, pct / 100));
const r = Math.round(226 + (62 - 226) * p);
const g = Math.round(92 + (201 - 92) * p);
const b = Math.round(92 + (122 - 92) * p);
const a = 0.18 + p * 0.55;
return `rgba(${r},${g},${b},${a.toFixed(3)})`;
};
// ── Per-(model|harness) marginal table cells: heat-tinted @k cell + count ──
function kcell(agg, k) {
if (!agg || !agg.cells) return `<td class="kc empty">β€”</td>`;
const cnt = agg.k[k - 1] || 0;
const pct = cnt / agg.cells * 100;
return `<td class="kc" style="background:${heatBg(pct)}">
<div class="kc-num">${pct.toFixed(0)}%</div>
<div class="kc-sub">${cnt}/${agg.cells}</div>
</td>`;
}
function sparkCell(agg) {
if (!agg || !agg.cells) return '<td class="spark"></td>';
const denom = agg.cells;
const bands = [];
let prev = 0;
for (let i = 0; i < KMAX; i++) {
const inc = (agg.k[i] - prev) / denom * 100;
bands.push(`<span class="s${Math.min(i+1, 4)}" style="width:${Math.max(0, inc)}%" title="@${i+1} +${inc.toFixed(0)}%"></span>`);
prev = agg.k[i];
}
return `<td class="spark"><div class="sparkbar">${bands.join('')}</div></td>`;
}
function tokCell(model_or_agent) {
return `<td class="num">${fmtT(model_or_agent.tokens_in)}/${fmtT(model_or_agent.tokens_out)}</td>`;
}
const kHeads = Array.from({length: KMAX}, (_, i) =>
`<th class="kc">@${i + 1}</th>`).join('');
const modelTable = `
<div class="marg-card">
<h4>By model <span class="sub">pass@k heat-tinted Β· spark = @1β†’@${KMAX} progression</span></h4>
<div class="marg-wrap">
<table class="marg-table">
<thead><tr><th>Model</th><th class="num">Cells</th>${kHeads}<th>Ξ”@1β†’@${KMAX}</th><th class="num">Tok i/o</th></tr></thead>
<tbody>
${byModel.map(m => {
const agg = modelAgg[m.model];
return `<tr>
<td class="lbl">${escape(m.model_short)}</td>
<td class="num">${m.cells}</td>
${Array.from({length: KMAX}, (_,i) => kcell(agg, i+1)).join('')}
${sparkCell(agg)}
${tokCell(m)}
</tr>`;
}).join('')}
</tbody>
</table>
</div>
</div>`;
const agentTable = `
<div class="marg-card">
<h4>By harness <span class="sub">pass@k heat-tinted Β· spark = @1β†’@${KMAX} progression</span></h4>
<div class="marg-wrap">
<table class="marg-table">
<thead><tr><th>Harness</th><th class="num">Cells</th>${kHeads}<th>Ξ”@1β†’@${KMAX}</th><th class="num">Tok i/o</th></tr></thead>
<tbody>
${byAgent.map(a => {
const agg = agentAgg[a.agent];
return `<tr>
<td class="lbl">${escape(a.agent_label)}</td>
<td class="num">${a.cells}</td>
${Array.from({length: KMAX}, (_,i) => kcell(agg, i+1)).join('')}
${sparkCell(agg)}
${tokCell(a)}
</tr>`;
}).join('')}
</tbody>
</table>
</div>
</div>`;
// ── Three matrices (MΓ—H, MΓ—D, HΓ—D) β€” pass@k_max % heat-tinted ────────────
function matrix(rowKeys, rowLabel, colKeys, colLabel, agg) {
const head = `<tr><th></th>${colKeys.map(c =>
`<th>${escape(colLabel(c))}</th>`).join('')}</tr>`;
const body = rowKeys.map(rk => {
const cells = colKeys.map(ck => {
const slot = (agg[rk] || {})[ck];
if (!slot || !slot.cells) return '<td class="empty">β€”</td>';
const cnt = slot.k[KMAX - 1] || 0;
const pct = cnt / slot.cells * 100;
return `<td class="cell" style="background:${heatBg(pct)}">
<b>${pct.toFixed(0)}%</b><div class="det">${cnt}/${slot.cells}</div>
</td>`;
}).join('');
return `<tr><td class="lbl">${escape(rowLabel(rk))}</td>${cells}</tr>`;
}).join('');
return `<table><thead>${head}</thead><tbody>${body}</tbody></table>`;
}
const modelKeys = byModel.map(m => m.model);
const modelLbl = (k) => (byModel.find(m => m.model === k) || {model_short: k}).model_short;
const harnessLbl = (k) => agentLabel[k] || k;
const matrices = `
<div class="matrices">
<div class="matrix-card">
<h4>Model Γ— Harness <span class="sub">pass@${KMAX}</span></h4>
<div class="matrix-wrap">${matrix(modelKeys, modelLbl, harnessOrder, harnessLbl, mxhAgg)}</div>
</div>
<div class="matrix-card">
<h4>Model Γ— Difficulty <span class="sub">pass@${KMAX}</span></h4>
<div class="matrix-wrap">${matrix(modelKeys, modelLbl, diffOrder, (d) => d, mxdAgg)}</div>
</div>
<div class="matrix-card">
<h4>Harness Γ— Difficulty <span class="sub">pass@${KMAX}</span></h4>
<div class="matrix-wrap">${matrix(harnessOrder, harnessLbl, diffOrder, (d) => d, hxdAgg)}</div>
</div>
</div>`;
charts.innerHTML = modelTable + agentTable + matrices;
}
function applyFilters(tasks) {
const q = document.getElementById('search').value.toLowerCase();
const diff = document.getElementById('diff').value;
const status = document.getElementById('status').value;
const harness = document.getElementById('harness').value;
return tasks.filter(t => {
if (q && !(t.id.toLowerCase().includes(q)
|| (t.question||'').toLowerCase().includes(q)
|| (t.kaggle||'').toLowerCase().includes(q))) return false;
if (diff !== "" && String(t.difficulty_level) !== diff) return false;
if (status === 'grad' && !t.graduated) return false;
if (status === 'partial' && (t.graduated || !t.any_pass)) return false;
if (status === 'fail' && t.any_pass) return false;
if (harness && !t.passing_harnesses.includes(harness)) return false;
return true;
});
}
function cellClass(c) {
if (!c || c.n_attempts === 0) return 'empty';
if (c.passed_at != null) return 'pass-' + Math.min(c.passed_at, 4);
// never passed β€” inspect the last attempt's error_kind for a finer color
const last = c.attempts[c.attempts.length - 1];
if (last && last.error_kind === 'no_answer') return 'no_answer';
if (last && last.error_kind && last.error_kind !== 'ok' && last.error_kind !== 'wrong_answer') return 'err';
return 'fail';
}
function cellSymbol(c) {
if (!c || c.n_attempts === 0) return 'Β·';
if (c.passed_at != null) return String(c.passed_at);
const last = c.attempts[c.attempts.length - 1];
if (last && last.error_kind === 'no_answer') return 'βˆ…';
if (last && last.error_kind && last.error_kind !== 'ok' && last.error_kind !== 'wrong_answer') return '⚠';
return 'βœ—';
}
function renderHeatmap() {
const visible = applyFilters(DATA.tasks);
document.getElementById('count').textContent = `${visible.length} of ${DATA.tasks.length} tasks`;
const models = DATA.models_order;
const agents = DATA.agents_order;
let cgroup = `<col class="cg-task"><col class="cg-h">`;
for (const _ of models) cgroup += `<col class="cg-cell">`;
let head = `<tr><th class="col-task">Task</th><th class="col-h">Harness</th>`;
for (const m of models) {
head += `<th>${escape(m)}</th>`;
}
head += `</tr>`;
const nCols = 2 + models.length;
let body = '';
visible.forEach((t, ti) => {
if (ti > 0) body += `<tr class="task-spacer"><td colspan="${nCols}"></td></tr>`;
const realIdx = DATA.tasks.indexOf(t);
const selCls = realIdx === selectedTaskIdx ? 'selected' : '';
agents.forEach((a, ai) => {
const isFirst = ai === 0;
let row = `<tr class="${selCls}" data-task="${realIdx}">`;
if (isFirst) {
row += `<td class="col-task" rowspan="${agents.length}" onclick="selectTask(${realIdx})" style="cursor:pointer">
<div><span class="diff ${escape(t.difficulty_class)}">${t.difficulty || ("L" + t.difficulty_level)}</span>
<span class="grad-mini ${gradeMiniClass(t)}">${gradeMiniLabel(t)}</span>
<span class="tid">${escape(t.id)}</span></div>
<div class="q">${escape(t.question)}</div>
</td>`;
}
row += `<td class="col-h">${escape(a)}</td>`;
for (const m of models) {
const key = (t.cell_map[a] || {})[m];
const c = key ? t.cells[key] : null;
const cls = cellClass(c);
const sym = cellSymbol(c);
if (c) {
const passLabel = c.passed_at != null ? `pass@${c.passed_at}` : 'never passed';
const lastAtt = c.attempts[c.attempts.length - 1];
const tooltip = `${m} / ${a}\n${passLabel} (${c.n_attempts} attempt${c.n_attempts===1?'':'s'})\n${lastAtt ? 'last: ' + lastAtt.error_kind + ' $' + lastAtt.cost.toFixed(4) : ''}`;
row += `<td class="cell ${cls}" title="${escape(tooltip)}" onclick="openCellDetail(${realIdx},'${escape(key)}')">${sym}</td>`;
} else {
row += `<td class="cell empty">Β·</td>`;
}
}
row += `</tr>`;
body += row;
});
});
document.getElementById('heatmap-wrap').innerHTML =
`<table class="heatmap"><colgroup>${cgroup}</colgroup><thead>${head}</thead><tbody>${body}</tbody></table>`;
}
function renderList() {
const visible = applyFilters(DATA.tasks);
document.getElementById('count').textContent = `${visible.length} of ${DATA.tasks.length} tasks`;
const harnessOrder = DATA.agents_order;
const html = visible.map(t => {
const realIdx = DATA.tasks.indexOf(t);
const harness = harnessOrder.map(a => {
// Best cell across models for this harness
let bestPa = null; let bestModel = null;
for (const m of DATA.models_order) {
const key = (t.cell_map[a] || {})[m];
if (!key) continue;
const c = t.cells[key];
if (c && c.passed_at != null && (bestPa == null || c.passed_at < bestPa)) {
bestPa = c.passed_at; bestModel = m;
}
}
const has = harnessOrder.some(_ => (t.cell_map[a] || {})[Object.keys(t.cell_map[a]||{})[0] || ''] != null) || Object.keys(t.cell_map[a] || {}).length > 0;
if (bestPa != null) {
return `<span class="pill ok" title="${escape(bestModel)}">${a} βœ“@${bestPa}</span>`;
}
if (!has) return `<span class="pill">${a} β€”</span>`;
return `<span class="pill err">${a} βœ—</span>`;
}).join('');
return `<div class="task-row ${realIdx===selectedTaskIdx?'selected':''}" onclick="selectTask(${realIdx})">
<span class="diff ${escape(t.difficulty_class)}">${t.difficulty || ("L" + t.difficulty_level)}</span>
<div>
<div class="id">${escape(t.id)}</div>
<div class="q">${escape(t.question).slice(0,160)}${(t.question||'').length>160?'…':''}</div>
<div style="margin-top:5px">${harness}</div>
</div>
<div style="text-align:right">
<div class="grad-badge ${gradeClass(t)}">${gradeLabel(t)}</div>
<div style="color:var(--muted);font-size:10px;margin-top:4px">${fmtCost(t.total_cost)} Β· ${t.total_attempts} attempts</div>
</div>
</div>`;
}).join('');
document.getElementById('view-list').innerHTML = html || `<div style="padding:20px;color:var(--muted)">No tasks match filters.</div>`;
}
function renderCurrent() {
if (currentView === 'heatmap') renderHeatmap();
else renderList();
}
function setView(v) {
currentView = v;
document.getElementById('view-heatmap').classList.toggle('hidden', v !== 'heatmap');
document.getElementById('view-list').classList.toggle('hidden', v !== 'list');
document.getElementById('tab-heatmap').classList.toggle('active', v === 'heatmap');
document.getElementById('tab-list').classList.toggle('active', v === 'list');
renderCurrent();
}
let sideStack = [];
function openSide() { document.getElementById('side-pane').classList.remove('hidden'); }
function sideClose() {
document.getElementById('side-pane').classList.add('hidden');
sideStack = [];
selectedTaskIdx = null;
document.getElementById('side-back').style.display = 'none';
renderCurrent();
}
function sideBack() {
if (sideStack.length <= 1) { sideClose(); return; }
sideStack.pop();
applySideTop();
}
function applySideTop() {
const top = sideStack[sideStack.length - 1];
document.getElementById('side-back').style.display = sideStack.length > 1 ? '' : 'none';
if (top.kind === 'task') renderTaskDetail(DATA.tasks[top.taskIdx]);
else if (top.kind === 'cell') renderCellDetail(top.taskIdx, top.cellKey);
else if (top.kind === 'trace') renderTraceSide(top.taskIdx, top.cellKey, top.attemptIdx);
}
function selectTask(idx) {
selectedTaskIdx = idx;
sideStack = [{kind: 'task', taskIdx: idx}];
openSide();
applySideTop();
renderCurrent();
}
function openCellDetail(taskIdx, cellKey) {
selectedTaskIdx = taskIdx;
if (sideStack.length === 0) sideStack.push({kind: 'task', taskIdx});
sideStack.push({kind: 'cell', taskIdx, cellKey});
openSide();
applySideTop();
renderCurrent();
}
function openTrace(taskIdx, cellKey, attemptIdx) {
selectedTaskIdx = taskIdx;
if (sideStack.length === 0) sideStack.push({kind: 'task', taskIdx});
sideStack.push({kind: 'trace', taskIdx, cellKey, attemptIdx});
openSide();
applySideTop();
renderCurrent();
}
function renderTaskDetail(t) {
const taskIdx = DATA.tasks.indexOf(t);
// Build per-cell rows (one per (model, agent) cell with attempts)
const cellRows = Object.entries(t.cells).map(([key, c]) => {
const passLabel = c.passed_at != null ? `<span class="pill ok">pass@${c.passed_at}</span>` : `<span class="pill err">βœ—</span>`;
const totalCost = c.attempts.reduce((s, a) => s + a.cost, 0);
return `<tr onclick="openCellDetail(${taskIdx}, '${escape(key)}')" style="cursor:pointer">
<td><span class="pill k">${escape(c.model)}</span></td>
<td><span class="agent-pill">${escape(c.agent)}</span></td>
<td>${passLabel}</td>
<td>${c.n_attempts}</td>
<td>${fmtCost(totalCost)}</td>
<td><button class="view-btn">view β†’</button></td>
</tr>`;
}).join('');
document.getElementById('side-title').textContent = t.id;
document.getElementById('side-body').innerHTML = `
<div class="meta">
<span class="diff ${escape(t.difficulty_class)}">${t.difficulty || ("L" + t.difficulty_level)}</span> Β·
<b>Kaggle:</b> ${escape(t.kaggle || 'β€”')} Β·
<b>Reward mode:</b> ${escape(t.reward_mode || '?')}
</div>
<div class="question">${escape(t.question)}</div>
<div class="gold"><b>Gold:</b> ${escape(t.answer)}</div>
<div class="meta">
<span class="grad-badge ${gradeClass(t)}">${gradeLabel(t)}</span>
&nbsp; <b>Passing harnesses:</b> ${t.passing_harnesses.join(', ') || 'β€”'}
&nbsp; <b>Cost:</b> ${fmtCost(t.total_cost)} &nbsp; <b>Attempts:</b> ${t.total_attempts}
</div>
<table class="trial-table">
<thead><tr><th>Model</th><th>Harness</th><th>Result</th><th>Attempts</th><th>Cost</th><th></th></tr></thead>
<tbody>${cellRows || '<tr><td colspan="6" style="color:var(--muted)">(no cells)</td></tr>'}</tbody>
</table>
`;
}
function renderCellDetail(taskIdx, cellKey) {
const t = DATA.tasks[taskIdx];
const c = t.cells[cellKey];
const passLabel = c.passed_at != null
? `<span class="pill ok">PASS @k${c.passed_at}</span>`
: `<span class="pill err">FAIL after ${c.n_attempts} attempts</span>`;
document.getElementById('side-title').innerHTML =
`${escape(t.id)} Β· ${escape(c.model)} Β· ${escape(c.agent)} ${passLabel}`;
const attRows = c.attempts.map((a, i) => {
const rcls = a.reward >= 1 ? 'pass' : 'fail';
const sym = a.reward >= 1 ? 'βœ“' : 'βœ—';
const errPill = a.error_kind && a.error_kind !== 'ok'
? `<span class="pill err">${escape(a.error_kind)}</span>` : '';
return `<tr onclick="openTrace(${taskIdx}, '${escape(cellKey)}', ${i})" style="cursor:pointer">
<td><span class="pill k">k=${a.attempt}</span></td>
<td class="reward ${rcls}">${sym} ${a.reward.toFixed(2)}</td>
<td>${fmtCost(a.cost)}</td>
<td>${fmtT(a.prompt_tokens)}/${fmtT(a.completion_tokens)}</td>
<td>${fmtDur(a.elapsed_sec)}</td>
<td>${errPill}</td>
<td><button class="view-btn">trace β†’</button></td>
</tr>`;
}).join('');
document.getElementById('side-body').innerHTML = `
<div class="meta">
<b>Kaggle:</b> ${escape(t.kaggle || 'β€”')} Β·
<b>Question:</b> ${escape(t.question)}
</div>
<div class="gold"><b>Gold:</b> ${escape(t.answer)}</div>
<table class="trial-table">
<thead><tr><th>k</th><th>Reward</th><th>Cost</th><th>Tok i/o</th><th>Dur</th><th>Error</th><th></th></tr></thead>
<tbody>${attRows}</tbody>
</table>
`;
}
function renderTraceSide(taskIdx, cellKey, attemptIdx) {
const t = DATA.tasks[taskIdx];
const c = t.cells[cellKey];
const a = c.attempts[attemptIdx];
const passClass = a.reward >= 1 ? 'ok' : 'err';
document.getElementById('side-title').innerHTML =
`${escape(t.id)} Β· k=${a.attempt} Β· ${escape(c.model)} Β· ${escape(c.agent)}
<span class="pill ${passClass}">${a.reward >= 1 ? 'PASS' : 'FAIL'} ${a.reward.toFixed(2)}</span>
<span class="pill">${fmtCost(a.cost)} Β· ${fmtT(a.prompt_tokens)}/${fmtT(a.completion_tokens)} tok</span>`;
const logPathHtml = a.log_path
? `<div class="meta"><b>Sweep log:</b> <code style="font-size:10px">${escape(a.log_path)}</code></div>` : '';
document.getElementById('side-body').innerHTML = logPathHtml +
'<div style="color:var(--muted);padding:10px">loading trace…</div>';
fetch('api/' + CURRENT_DS + '/trace/' + a.tid).then(r => r.json()).then(tr => {
const msgs = renderTrajectory(tr.trajectory || []);
const graderHtml = tr.grader_text
? `<h4 style="margin:6px 0;color:var(--muted);font-size:11px;text-transform:uppercase">Grader output</h4>
<div class="grader-box">${escape(tr.grader_text)}</div>` : '';
const ocHtml = tr.opencode_log
? `<details><summary>opencode.txt (${tr.opencode_log.length} chars)</summary>
<pre>${escape(tr.opencode_log)}</pre></details>` : '';
const logHtml = tr.trial_log_tail
? `<details><summary>trial.log (last 80 lines)</summary>
<pre>${escape(tr.trial_log_tail)}</pre></details>` : '';
document.getElementById('side-body').innerHTML = logPathHtml + graderHtml + msgs + ocHtml + logHtml;
}).catch(e => {
document.getElementById('side-body').innerHTML = logPathHtml +
'<div style="color:#f88;padding:10px">failed to load trace: ' + escape(e) + '</div>';
});
}
function renderTrajectory(traj) {
const resultsById = {};
for (const m of traj) {
if (m.role === 'tool' && m.tool_call_id) resultsById[m.tool_call_id] = m;
}
let out = '';
let turnIdx = 0;
for (let i = 0; i < traj.length; i++) {
const m = traj[i];
if (m.role === 'tool') continue;
if (m.role === 'system' || m.role === 'user') {
out += renderSimpleMessage(m);
} else if (m.role === 'assistant') {
turnIdx += 1;
out += renderAssistantTurn(m, resultsById, turnIdx);
} else {
out += renderSimpleMessage(m);
}
}
return out || `<div style="color:var(--muted);padding:10px">(no trajectory captured)</div>`;
}
function renderSimpleMessage(m) {
const body = escape(m.content || '(empty)');
return `<div class="msg ${m.role}">
<div class="msg-head">${m.role}</div>
<div class="msg-body">${body}</div>
</div>`;
}
function renderAssistantTurn(m, resultsById, idx) {
const thinking = (m.content || '').trim();
const calls = m.tool_calls || [];
if (!calls.length) {
return `<div class="msg assistant">
<div class="msg-head">assistant Β· turn ${idx}</div>
<div class="msg-body">${escape(thinking || '(no content)')}</div>
</div>`;
}
const thinkingHtml = thinking ? `<div class="turn-thinking">${escape(thinking)}</div>` : '';
const callsHtml = calls.map(tc => renderToolCall(tc, resultsById[tc.id])).join('');
return `<div class="turn">
<div class="turn-head">β–Ά turn ${idx} Β· assistant</div>
${thinkingHtml}
${callsHtml}
</div>`;
}
function renderToolCall(tc, resultMsg) {
let parsed = null;
try { parsed = JSON.parse(tc.arguments || '{}'); }
catch { parsed = null; }
let codeBody = '';
let langHint = '';
let metaParts = [];
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
if (typeof parsed.code === 'string') { codeBody = parsed.code; langHint = 'python'; }
else if (typeof parsed.command === 'string') { codeBody = parsed.command; langHint = 'bash'; }
else if (typeof parsed.cell === 'string') { codeBody = parsed.cell; langHint = 'python'; }
else if (typeof parsed.answer === 'string') { codeBody = parsed.answer; langHint = ''; }
const bodyKeys = new Set(['code', 'command', 'cell', 'answer']);
for (const [k, v] of Object.entries(parsed)) {
if (bodyKeys.has(k)) continue;
let val;
if (typeof v === 'string') val = v.length > 120 ? v.slice(0,120) + '…' : v;
else val = JSON.stringify(v);
if (val != null && val !== '') metaParts.push(`${k}=${val}`);
}
if (!codeBody && Object.keys(parsed).length === 0) { /* empty */ }
else if (!codeBody) {
codeBody = JSON.stringify(parsed, null, 2);
langHint = 'json';
}
} else {
codeBody = tc.arguments || '';
langHint = 'json';
}
const meta = metaParts.length ? `<span class="tc-meta">${escape(metaParts.join(' Β· '))}</span>` : '';
const idLabel = tc.id ? `<span class="tc-id">${escape(tc.id.slice(0,16))}</span>` : '';
const codeBlock = codeBody
? `<pre class="tc-code lang-${langHint}">${escape(codeBody)}</pre>`
: `<div style="padding:8px 12px;color:var(--gray);font-style:italic">(no arguments)</div>`;
let resultHtml = '';
if (resultMsg) {
const content = resultMsg.content || '';
const lines = content.split('\n');
const lineInfo = `${lines.length} line${lines.length===1?'':'s'} Β· ${content.length} char${content.length===1?'':'s'}`;
const truncMatch = content.match(/\[truncated, full length (\d+)\]/);
const truncated = truncMatch ? `<span style="color:var(--warn)">truncated Β· full ${truncMatch[1]}ch</span>` : '';
const empty = content.trim() === '' ? ' empty' : '';
resultHtml = `
<div class="tool-output-head">output <span class="rc">${lineInfo} ${truncated}</span></div>
<pre class="tool-output${empty}">${empty ? '(empty)' : escape(content)}</pre>
`;
} else {
resultHtml = `<div class="tool-output-head" style="color:var(--gray)">(no matching tool result)</div>`;
}
return `<div class="tool-call">
<div class="tc-head">
<span class="tc-name">β†’ ${escape(tc.name || '?')}()</span>
${idLabel}
${meta}
</div>
${codeBlock}
${resultHtml}
</div>`;
}
['search','diff','status','harness'].forEach(id => document.getElementById(id).addEventListener('input', renderCurrent));
document.addEventListener('keydown', e => { if (e.key==='Escape') sideClose(); });
const SIDE_MIN = 380;
const SIDE_MAX_FRAC = 0.85;
function sideMax() { return Math.max(SIDE_MIN + 100, Math.floor(window.innerWidth * SIDE_MAX_FRAC)); }
function clampSideWidth(w) { return Math.min(sideMax(), Math.max(SIDE_MIN, w)); }
function applySideWidth(w) {
const sp = document.getElementById('side-pane');
sp.style.flex = `0 0 ${clampSideWidth(w)}px`;
}
try {
const stored = parseInt(localStorage.getItem('sidePaneWidth') || '', 10);
if (stored && !isNaN(stored)) applySideWidth(stored);
} catch {}
(function setupResizer() {
const handle = document.getElementById('side-resizer');
const sp = document.getElementById('side-pane');
let dragging = false, startX = 0, startW = 0;
handle.addEventListener('mousedown', e => {
dragging = true; startX = e.clientX; startW = sp.getBoundingClientRect().width;
document.body.classList.add('resizing');
handle.classList.add('active');
e.preventDefault();
});
document.addEventListener('mousemove', e => {
if (!dragging) return;
const newW = startW + (startX - e.clientX);
applySideWidth(newW);
});
document.addEventListener('mouseup', () => {
if (!dragging) return;
dragging = false;
document.body.classList.remove('resizing');
handle.classList.remove('active');
try { localStorage.setItem('sidePaneWidth', String(sp.getBoundingClientRect().width)); } catch {}
});
window.addEventListener('resize', () => {
const cur = sp.getBoundingClientRect().width;
applySideWidth(cur);
});
})();
// ─── Benchmark + sweep selector + bootstrap ────────────────────────────────
// `BENCHMARKS` is loaded from /api/benchmarks at boot. Each entry has:
// { label, description, source: {harbor, hf_dataset, paper, ...}, sweeps: [<key>] }
let BENCHMARKS = {};
let CURRENT_BENCH = null; // benchmark key
let CURRENT_DS = null; // sweep key (the actual data key /api/<ds>/summary)
// Display order preference; unknown benchmarks appended alphabetically.
const BENCH_ORDER = ['data-agent-bench', 'dabstep-bench'];
function setLoader(visible, msg) {
const el = document.getElementById('loader');
if (msg) document.getElementById('loader-msg').textContent = msg;
el.classList.toggle('hidden', !visible);
}
function orderBenchmarks(keys) {
const known = BENCH_ORDER.filter(k => keys.includes(k));
const extra = keys.filter(k => !BENCH_ORDER.includes(k)).sort();
return [...known, ...extra];
}
function renderBenchTabs() {
const wrap = document.getElementById('ds-tabs');
const keys = orderBenchmarks(Object.keys(BENCHMARKS));
wrap.innerHTML = keys.map(k => {
const label = (BENCHMARKS[k] && BENCHMARKS[k].label) || k;
const cls = k === CURRENT_BENCH ? 'active' : '';
return `<button class="${cls}" data-bench="${k}" onclick="switchBenchmark('${k}')">${escape(label)}</button>`;
}).join('');
}
// Switching benchmark: defaults to that benchmark's first sweep.
function switchBenchmark(benchKey) {
if (benchKey === CURRENT_BENCH) return;
const bench = BENCHMARKS[benchKey];
if (!bench || !bench.sweeps || !bench.sweeps.length) return;
CURRENT_BENCH = benchKey;
CURRENT_DS = bench.sweeps[0];
document.querySelectorAll('#ds-tabs button').forEach(b =>
b.classList.toggle('active', b.dataset.bench === benchKey));
// Hide stale side pane + info popover.
const side = document.getElementById('side-pane'); if (side) side.classList.add('hidden');
document.getElementById('info-popover').classList.add('hidden');
loadSummary(CURRENT_DS);
}
function loadSummary(ds) {
setLoader(true, `loading ${(BENCHMARKS[CURRENT_BENCH] && BENCHMARKS[CURRENT_BENCH].label) || ds}…`);
return fetch(`api/${ds}/summary`)
.then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); })
.then(d => {
DATA = d;
renderHeaderStats();
renderCurrent();
setLoader(false);
})
.catch(e => {
setLoader(true, `Failed to load ${ds}: ${e}`);
});
}
// ─── Info popover β€” shows benchmark description + source links ─────────────
function toggleInfoPopover() {
const pop = document.getElementById('info-popover');
if (!CURRENT_BENCH || !BENCHMARKS[CURRENT_BENCH]) return;
if (!pop.classList.contains('hidden')) {
pop.classList.add('hidden');
return;
}
const b = BENCHMARKS[CURRENT_BENCH];
const src = b.source || {};
const SRC_LABELS = {
harbor: 'Harbor task suite',
hf_dataset: 'Hugging Face dataset',
paper: 'Paper',
leaderboard: 'Leaderboard',
};
const links = Object.keys(src).map(k => {
const url = src[k].startsWith('http') ? src[k]
: (k === 'harbor' || k === 'hf_dataset')
? `https://huggingface.co/datasets/${src[k]}`
: src[k];
return `<div><span class="lbl">${SRC_LABELS[k] || k}:</span><a href="${url}" target="_blank" rel="noopener">${escape(src[k])}</a></div>`;
}).join('');
pop.innerHTML =
`<h4>${escape(b.label || CURRENT_BENCH)}</h4>
<p>${escape(b.description || '')}</p>
<div class="src-list">${links}</div>`;
pop.classList.remove('hidden');
}
// Click-outside-to-close for the popover.
document.addEventListener('click', e => {
const pop = document.getElementById('info-popover');
const btn = document.getElementById('info-btn');
if (pop && !pop.contains(e.target) && e.target !== btn) {
pop.classList.add('hidden');
}
});
// Bootstrap: fetch benchmark registry, pick first preferred, load its first sweep.
fetch('api/benchmarks').then(r => r.json()).then(reg => {
BENCHMARKS = reg || {};
const keys = orderBenchmarks(Object.keys(BENCHMARKS));
if (!keys.length) {
setLoader(true, 'No benchmarks found on server.');
return;
}
CURRENT_BENCH = keys[0];
const sweeps = BENCHMARKS[CURRENT_BENCH].sweeps || [];
if (!sweeps.length) {
setLoader(true, `Benchmark ${CURRENT_BENCH} has no sweeps yet.`);
return;
}
CURRENT_DS = sweeps[0];
renderBenchTabs();
loadSummary(CURRENT_DS);
}).catch(e => {
setLoader(true, `Failed to load benchmark registry: ${e}`);
});
</script>
</body>
</html>