@hackerrank/astra-cli 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,569 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>astra bench report</title>
7
+ <style>
8
+ :root {
9
+ --bg: #0f1115;
10
+ --panel: #171a21;
11
+ --panel-2: #1d212b;
12
+ --border: #262b36;
13
+ --text: #e6e8ee;
14
+ --muted: #8b93a7;
15
+ --accent: #5b9dff;
16
+ --good: #3ecf8e;
17
+ --bad: #ef5a6f;
18
+ --warn: #e8b339;
19
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
20
+ }
21
+ * { box-sizing: border-box; }
22
+ body { margin: 0; background: var(--bg); color: var(--text); font-size: 14px; line-height: 1.45; }
23
+ a { color: var(--accent); }
24
+ .wrap { max-width: 1180px; margin: 0 auto; padding: 28px 20px 64px; }
25
+ header.top { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 4px; flex-wrap: wrap; gap: 8px; }
26
+ header.top h1 { font-size: 20px; margin: 0; font-weight: 650; letter-spacing: .2px; }
27
+ header.top .meta { color: var(--muted); font-size: 12.5px; }
28
+ .kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px; margin: 20px 0 28px; }
29
+ .kpi { background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; }
30
+ .kpi .v { font-size: 22px; font-weight: 700; }
31
+ .kpi .l { color: var(--muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: .06em; margin-top: 2px; }
32
+ section { margin: 34px 0; }
33
+ section > h2 { font-size: 15px; margin: 0 0 12px; font-weight: 650; color: var(--text); display: flex; align-items: center; gap: 8px; }
34
+ section > h2 .sub { color: var(--muted); font-weight: 400; font-size: 12px; }
35
+ .grid2 { display: grid; grid-template-columns: 1.1fr 1fr; gap: 16px; }
36
+ .grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
37
+ @media (max-width: 900px) { .grid2, .grid3 { grid-template-columns: 1fr; } }
38
+ .panel { background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 14px 16px; }
39
+ .panel h3 { margin: 0 0 10px; font-size: 12.5px; color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: .05em; }
40
+ table { width: 100%; border-collapse: collapse; font-size: 13px; }
41
+ th, td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--border); white-space: nowrap; }
42
+ th { color: var(--muted); font-weight: 600; font-size: 11.5px; text-transform: uppercase; letter-spacing: .03em; cursor: pointer; user-select: none; }
43
+ th:hover { color: var(--text); }
44
+ th .arrow { opacity: .5; font-size: 10px; margin-left: 3px; }
45
+ tbody tr:hover { background: rgba(255,255,255,0.02); }
46
+ tbody tr.run-row { cursor: pointer; }
47
+ .pill { display: inline-block; padding: 1px 8px; border-radius: 100px; font-size: 11px; font-weight: 600; }
48
+ .pill.ok { background: rgba(62,207,142,.15); color: var(--good); }
49
+ .pill.bad { background: rgba(239,90,111,.15); color: var(--bad); }
50
+ .pill.warn { background: rgba(232,179,57,.15); color: var(--warn); }
51
+ .mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; }
52
+ .muted { color: var(--muted); }
53
+ .bar-row { display: flex; align-items: center; gap: 8px; margin: 6px 0; }
54
+ .bar-row .lbl { width: 150px; flex: 0 0 150px; font-size: 12px; overflow: hidden; text-overflow: ellipsis; }
55
+ .bar-track { flex: 1; height: 14px; background: var(--panel-2); border-radius: 6px; overflow: hidden; display: flex; }
56
+ .bar-seg { height: 100%; }
57
+ .bar-val { width: 64px; flex: 0 0 64px; text-align: right; font-size: 12px; font-variant-numeric: tabular-nums; }
58
+ .legend { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 10px; font-size: 11.5px; color: var(--muted); }
59
+ .legend .sw { display: inline-block; width: 9px; height: 9px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; }
60
+ .heat-table td { text-align: center; font-variant-numeric: tabular-nums; }
61
+ .heat-cell { display: inline-block; min-width: 44px; padding: 3px 6px; border-radius: 5px; font-size: 12px; }
62
+ details.run-detail { margin: 0; }
63
+ details.run-detail > summary { display: none; }
64
+ .timeline { margin: 0 0 18px; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
65
+ .timeline .step { padding: 10px 14px; border-bottom: 1px solid var(--border); }
66
+ .timeline .step:last-child { border-bottom: none; }
67
+ .timeline .step .head { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; margin-bottom: 4px; }
68
+ .timeline .step .n { color: var(--accent); font-weight: 700; font-size: 12px; }
69
+ .timeline .step .stats { color: var(--muted); font-size: 11.5px; }
70
+ .timeline .step .thought { color: var(--muted); font-size: 12.5px; margin-bottom: 6px; }
71
+ .timeline .step pre { background: var(--panel-2); border-radius: 6px; padding: 8px 10px; margin: 4px 0; font-size: 12px; overflow-x: auto; white-space: pre-wrap; word-break: break-word; }
72
+ .rc0 { color: var(--good); } .rcN { color: var(--bad); }
73
+ .toolbar { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; align-items: center; }
74
+ .toolbar select, .toolbar input { background: var(--panel-2); border: 1px solid var(--border); color: var(--text); border-radius: 6px; padding: 5px 9px; font-size: 12.5px; }
75
+ .toolbar label { color: var(--muted); font-size: 12px; }
76
+ footer { color: var(--muted); font-size: 11.5px; margin-top: 40px; text-align: center; }
77
+ svg text { fill: var(--muted); font-size: 10.5px; }
78
+ .axis line, .axis path { stroke: var(--border); }
79
+ .n-a { color: var(--muted); }
80
+ </style>
81
+ </head>
82
+ <body>
83
+ <div class="wrap">
84
+ <header class="top">
85
+ <h1>astra bench report</h1>
86
+ <div class="meta" id="generated"></div>
87
+ </header>
88
+
89
+ <div class="kpis" id="kpis"></div>
90
+
91
+ <section>
92
+ <h2>Leaderboard <span class="sub">one row per model · reasoning</span></h2>
93
+ <div class="grid2">
94
+ <div class="panel">
95
+ <h3>Solve rate</h3>
96
+ <div id="chart-solve"></div>
97
+ </div>
98
+ <div class="panel">
99
+ <h3>Outcomes</h3>
100
+ <div id="chart-outcomes"></div>
101
+ <div class="legend" id="legend-outcomes"></div>
102
+ </div>
103
+ </div>
104
+ <div class="panel" style="margin-top:16px;">
105
+ <table id="tbl-leaderboard"></table>
106
+ </div>
107
+ </section>
108
+
109
+ <section>
110
+ <h2>Cost &amp; tokens</h2>
111
+ <div class="grid2">
112
+ <div class="panel">
113
+ <h3>Cost vs. solve rate</h3>
114
+ <div id="chart-scatter"></div>
115
+ </div>
116
+ <div class="panel">
117
+ <h3>Token mix (prompt / completion / reasoning / cached)</h3>
118
+ <div id="chart-tokens"></div>
119
+ <div class="legend" id="legend-tokens"></div>
120
+ </div>
121
+ </div>
122
+ </section>
123
+
124
+ <section id="section-series">
125
+ <h2>Per-step trend <span class="sub">median tokens &amp; cumulative cost across the trajectory</span></h2>
126
+ <div class="grid2">
127
+ <div class="panel">
128
+ <h3>Prompt tokens (context) vs. step</h3>
129
+ <div id="chart-line-tokens"></div>
130
+ </div>
131
+ <div class="panel">
132
+ <h3>Cumulative cost vs. step</h3>
133
+ <div id="chart-line-cost"></div>
134
+ </div>
135
+ </div>
136
+ <div class="legend" id="legend-series"></div>
137
+ </section>
138
+
139
+ <section id="section-matrix">
140
+ <h2>Model &times; task <span class="sub">pass@k</span></h2>
141
+ <div class="panel">
142
+ <table id="tbl-matrix" class="heat-table"></table>
143
+ </div>
144
+ </section>
145
+
146
+ <section>
147
+ <h2>Runs <span class="sub">every attempt · click a row to expand the command timeline</span></h2>
148
+ <div class="toolbar">
149
+ <label>Model
150
+ <select id="filter-model"><option value="">all</option></select>
151
+ </label>
152
+ <label>Task
153
+ <select id="filter-task"><option value="">all</option></select>
154
+ </label>
155
+ <label>Outcome
156
+ <select id="filter-outcome"><option value="">all</option></select>
157
+ </label>
158
+ </div>
159
+ <div class="panel">
160
+ <table id="tbl-runs"></table>
161
+ </div>
162
+ </section>
163
+
164
+ <footer>generated by <span class="mono">astra bench --report</span> · schema <span id="schema"></span></footer>
165
+ </div>
166
+
167
+ <script>
168
+ /*__ASTRA_DATA__*/
169
+ </script>
170
+ <script>
171
+ (function () {
172
+ var DATA = window.DATA || { kpis: {}, leaderboard: [], matrix: [], runs: [], step_series: [] };
173
+ var PALETTE = ["#5b9dff", "#3ecf8e", "#e8b339", "#ef5a6f", "#b57bff", "#33c2c2", "#ff8a5b", "#a3d977"];
174
+ var colorCache = {};
175
+ function colorFor(key) {
176
+ if (!colorCache[key]) {
177
+ var n = Object.keys(colorCache).length;
178
+ colorCache[key] = PALETTE[n % PALETTE.length];
179
+ }
180
+ return colorCache[key];
181
+ }
182
+
183
+ function fmt(n) { return Number(n || 0).toLocaleString("en-US"); }
184
+ function fmt1(n) { return (Number(n) || 0).toFixed(1); }
185
+ function pct(n) { return Math.round((Number(n) || 0) * 100) + "%"; }
186
+ function fmtUsd(n) {
187
+ if (n == null) return '<span class="n-a">n/a</span>';
188
+ var v = Number(n) || 0;
189
+ if (v === 0) return "$0";
190
+ if (v < 0.01) return "$" + v.toFixed(5);
191
+ if (v < 1) return "$" + v.toFixed(4);
192
+ return "$" + v.toFixed(2);
193
+ }
194
+ function esc(s) {
195
+ return String(s == null ? "" : s).replace(/[&<>"']/g, function (c) {
196
+ return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[c];
197
+ });
198
+ }
199
+ function el(tag, attrs, html) {
200
+ var e = document.createElement(tag);
201
+ for (var k in attrs || {}) e.setAttribute(k, attrs[k]);
202
+ if (html != null) e.innerHTML = html;
203
+ return e;
204
+ }
205
+ function svgEl(tag, attrs) {
206
+ var e = document.createElementNS("http://www.w3.org/2000/svg", tag);
207
+ for (var k in attrs || {}) e.setAttribute(k, attrs[k]);
208
+ return e;
209
+ }
210
+
211
+ // ---------------------------------------------------------------- header
212
+ document.getElementById("generated").textContent = "generated " + (DATA.generated_at || "");
213
+ document.getElementById("schema").textContent = DATA.schema || "";
214
+
215
+ // ---------------------------------------------------------------- KPIs
216
+ (function renderKpis() {
217
+ var k = DATA.kpis || {};
218
+ var cards = [
219
+ ["Runs", fmt(k.runs)],
220
+ ["Models", fmt(k.models)],
221
+ ["Tasks", fmt(k.tasks)],
222
+ ["Solved", fmt(k.solved) + " / " + fmt(k.runs)],
223
+ ["Solve rate", pct(k.solved_rate)],
224
+ ["Total tokens", fmt(k.tokens)],
225
+ ["Total cost", fmtUsd(k.cost_usd) + (k.cost_source === "estimated" ? "~" : "")],
226
+ ["Elapsed", fmt(k.elapsed_seconds) + "s"],
227
+ ];
228
+ var host = document.getElementById("kpis");
229
+ cards.forEach(function (c) {
230
+ host.appendChild(el("div", { class: "kpi" }, '<div class="v">' + c[1] + '</div><div class="l">' + esc(c[0]) + "</div>"));
231
+ });
232
+ })();
233
+
234
+ // ---------------------------------------------------------------- sortable table helper
235
+ function sortableTable(container, columns, rows, opts) {
236
+ opts = opts || {};
237
+ var state = { key: opts.defaultKey || columns[0].key, dir: opts.defaultDir || -1 };
238
+ function draw() {
239
+ var sorted = rows.slice().sort(function (a, b) {
240
+ var va = a[state.key], vb = b[state.key];
241
+ if (typeof va === "string" || typeof vb === "string") {
242
+ return state.dir * String(va).localeCompare(String(vb));
243
+ }
244
+ return state.dir * ((Number(va) || 0) - (Number(vb) || 0));
245
+ });
246
+ var thead = "<thead><tr>" + columns.map(function (c) {
247
+ var arrow = c.key === state.key ? (state.dir === 1 ? "\u2191" : "\u2193") : "";
248
+ return '<th data-key="' + c.key + '">' + esc(c.label) + (arrow ? ' <span class="arrow">' + arrow + "</span>" : "") + "</th>";
249
+ }).join("") + "</tr></thead>";
250
+ var tbody = "<tbody>" + sorted.map(function (row, i) {
251
+ var cells = columns.map(function (c) { return "<td>" + c.render(row) + "</td>"; }).join("");
252
+ var extra = opts.rowAttrs ? opts.rowAttrs(row, i) : "";
253
+ return "<tr " + extra + ">" + cells + "</tr>";
254
+ }).join("") + "</tbody>";
255
+ container.innerHTML = thead + tbody;
256
+ container.querySelectorAll("th").forEach(function (th) {
257
+ th.addEventListener("click", function () {
258
+ var key = th.getAttribute("data-key");
259
+ if (state.key === key) state.dir *= -1;
260
+ else { state.key = key; state.dir = -1; }
261
+ draw();
262
+ });
263
+ });
264
+ if (opts.afterDraw) opts.afterDraw(container, sorted);
265
+ }
266
+ draw();
267
+ }
268
+
269
+ // ---------------------------------------------------------------- leaderboard chart: solve rate bars
270
+ (function () {
271
+ var host = document.getElementById("chart-solve");
272
+ (DATA.leaderboard || []).forEach(function (g) {
273
+ var row = el("div", { class: "bar-row" });
274
+ row.appendChild(el("div", { class: "lbl mono" }, esc(g.slug)));
275
+ var track = el("div", { class: "bar-track" });
276
+ var seg = el("div", {
277
+ class: "bar-seg",
278
+ style: "width:" + Math.round((g.solved_rate || 0) * 100) + "%;background:" + colorFor(g.slug),
279
+ });
280
+ track.appendChild(seg);
281
+ row.appendChild(track);
282
+ row.appendChild(el("div", { class: "bar-val" }, pct(g.solved_rate) + " (" + g.solved + "/" + g.runs + ")"));
283
+ host.appendChild(row);
284
+ });
285
+ })();
286
+
287
+ // ---------------------------------------------------------------- outcomes stacked bar
288
+ (function () {
289
+ var host = document.getElementById("chart-outcomes");
290
+ var legend = document.getElementById("legend-outcomes");
291
+ var outcomeColors = { Submitted: "#3ecf8e", LimitsExceeded: "#e8b339", TimeExceeded: "#e8b339", RepeatedFormatError: "#ef5a6f", ContextWindowExceeded: "#ef5a6f", Error: "#ef5a6f", Unknown: "#8b93a7" };
292
+ function colorForOutcome(k) { return outcomeColors[k] || "#8b93a7"; }
293
+ var allOutcomes = {};
294
+ (DATA.leaderboard || []).forEach(function (g) {
295
+ var row = el("div", { class: "bar-row" });
296
+ row.appendChild(el("div", { class: "lbl mono" }, esc(g.slug)));
297
+ var track = el("div", { class: "bar-track" });
298
+ var total = g.runs || 1;
299
+ Object.keys(g.outcomes || {}).forEach(function (k) {
300
+ allOutcomes[k] = true;
301
+ var w = (g.outcomes[k] / total) * 100;
302
+ track.appendChild(el("div", { class: "bar-seg", style: "width:" + w + "%;background:" + colorForOutcome(k) }));
303
+ });
304
+ row.appendChild(track);
305
+ row.appendChild(el("div", { class: "bar-val" }, g.runs + " runs"));
306
+ host.appendChild(row);
307
+ });
308
+ Object.keys(allOutcomes).forEach(function (k) {
309
+ legend.appendChild(el("span", {}, '<span class="sw" style="background:' + colorForOutcome(k) + '"></span>' + esc(k)));
310
+ });
311
+ })();
312
+
313
+ // ---------------------------------------------------------------- leaderboard table
314
+ (function () {
315
+ var container = document.getElementById("tbl-leaderboard");
316
+ var columns = [
317
+ { key: "model", label: "Model", render: function (r) { return esc(r.model); } },
318
+ { key: "reasoning", label: "Reasoning", render: function (r) { return esc(r.reasoning); } },
319
+ { key: "runs", label: "Runs", render: function (r) { return fmt(r.runs); } },
320
+ { key: "solved_rate", label: "Solved", render: function (r) { return r.solved + "/" + r.runs + " (" + pct(r.solved_rate) + ")"; } },
321
+ { key: "avg_steps", label: "Avg steps", render: function (r) { return fmt1(r.avg_steps); } },
322
+ { key: "avg_elapsed_seconds", label: "Avg time", render: function (r) { return fmt1(r.avg_elapsed_seconds) + "s"; } },
323
+ { key: "avg_tokens", label: "Avg tokens", render: function (r) { return fmt(Math.round(r.avg_tokens)); } },
324
+ { key: "cost_usd", label: "Cost", render: function (r) { return fmtUsd(r.cost_usd) + (r.cost_source === "estimated" ? "~" : ""); } },
325
+ { key: "avg_n_failed_commands", label: "Fail/run", render: function (r) { return fmt1(r.avg_n_failed_commands); } },
326
+ ];
327
+ sortableTable(container, columns, DATA.leaderboard || [], { defaultKey: "solved_rate" });
328
+ })();
329
+
330
+ // ---------------------------------------------------------------- token mix stacked bar
331
+ (function () {
332
+ var host = document.getElementById("chart-tokens");
333
+ var legend = document.getElementById("legend-tokens");
334
+ var keys = ["prompt", "completion", "reasoning", "cached"];
335
+ var colors = { prompt: "#5b9dff", completion: "#3ecf8e", reasoning: "#b57bff", cached: "#8b93a7" };
336
+ var maxTotal = Math.max(1, Math.max.apply(null, (DATA.leaderboard || []).map(function (g) {
337
+ return keys.reduce(function (s, k) { return s + (g.tokens[k] || 0); }, 0);
338
+ })));
339
+ (DATA.leaderboard || []).forEach(function (g) {
340
+ var row = el("div", { class: "bar-row" });
341
+ row.appendChild(el("div", { class: "lbl mono" }, esc(g.slug)));
342
+ var track = el("div", { class: "bar-track" });
343
+ var total = keys.reduce(function (s, k) { return s + (g.tokens[k] || 0); }, 0);
344
+ keys.forEach(function (k) {
345
+ var w = (g.tokens[k] || 0) / maxTotal * 100;
346
+ if (w > 0) track.appendChild(el("div", { class: "bar-seg", style: "width:" + w + "%;background:" + colors[k] }));
347
+ });
348
+ row.appendChild(track);
349
+ row.appendChild(el("div", { class: "bar-val" }, fmt(total)));
350
+ host.appendChild(row);
351
+ });
352
+ keys.forEach(function (k) {
353
+ legend.appendChild(el("span", {}, '<span class="sw" style="background:' + colors[k] + '"></span>' + k));
354
+ });
355
+ })();
356
+
357
+ // ---------------------------------------------------------------- scatter: cost vs solve rate
358
+ (function () {
359
+ var host = document.getElementById("chart-scatter");
360
+ var W = host.clientWidth || 460, H = 260, pad = { l: 42, r: 16, t: 14, b: 30 };
361
+ var svg = svgEl("svg", { width: "100%", height: H, viewBox: "0 0 " + W + " " + H });
362
+ var data = (DATA.leaderboard || []).filter(function (g) { return g.cost_usd != null; });
363
+ var maxCost = Math.max(0.001, Math.max.apply(null, data.map(function (g) { return g.cost_usd; }).concat([0.001])));
364
+ function x(v) { return pad.l + (v / maxCost) * (W - pad.l - pad.r); }
365
+ function y(v) { return H - pad.b - v * (H - pad.t - pad.b); }
366
+ // axes
367
+ svg.appendChild(svgEl("line", { x1: pad.l, x2: W - pad.r, y1: H - pad.b, y2: H - pad.b, stroke: "#262b36" }));
368
+ svg.appendChild(svgEl("line", { x1: pad.l, x2: pad.l, y1: pad.t, y2: H - pad.b, stroke: "#262b36" }));
369
+ [0, 0.25, 0.5, 0.75, 1].forEach(function (t) {
370
+ var ty = y(t);
371
+ var lab = svgEl("text", { x: pad.l - 8, y: ty + 3, "text-anchor": "end" });
372
+ lab.textContent = Math.round(t * 100) + "%";
373
+ svg.appendChild(lab);
374
+ });
375
+ var lab2 = svgEl("text", { x: W - pad.r, y: H - pad.b + 20, "text-anchor": "end" });
376
+ lab2.textContent = "cost \u2192 (max " + fmtUsd(maxCost) + ")";
377
+ svg.appendChild(lab2);
378
+ data.forEach(function (g) {
379
+ var cx = x(g.cost_usd), cy = y(g.solved_rate);
380
+ var r = 6 + Math.min(10, (g.avg_tokens || 0) / 4000);
381
+ var c = svgEl("circle", { cx: cx, cy: cy, r: r, fill: colorFor(g.slug), "fill-opacity": 0.85 });
382
+ svg.appendChild(c);
383
+ var t = svgEl("text", { x: cx + r + 4, y: cy + 3 });
384
+ t.textContent = g.slug;
385
+ svg.appendChild(t);
386
+ });
387
+ host.appendChild(svg);
388
+ })();
389
+
390
+ // ---------------------------------------------------------------- per-step line charts
391
+ function lineChart(host, series, valueFn, opts) {
392
+ opts = opts || {};
393
+ var W = host.clientWidth || 460, H = 260, pad = { l: 46, r: 16, t: 14, b: 26 };
394
+ var svg = svgEl("svg", { width: "100%", height: H, viewBox: "0 0 " + W + " " + H });
395
+ var maxStep = 1, maxVal = 0.0001;
396
+ series.forEach(function (s) {
397
+ s.points.forEach(function (p) {
398
+ if (p.step > maxStep) maxStep = p.step;
399
+ var v = valueFn(p);
400
+ if (v > maxVal) maxVal = v;
401
+ });
402
+ });
403
+ function x(v) { return pad.l + (v - 1) / Math.max(1, maxStep - 1) * (W - pad.l - pad.r); }
404
+ function y(v) { return H - pad.b - (v / maxVal) * (H - pad.t - pad.b); }
405
+ svg.appendChild(svgEl("line", { x1: pad.l, x2: W - pad.r, y1: H - pad.b, y2: H - pad.b, stroke: "#262b36" }));
406
+ svg.appendChild(svgEl("line", { x1: pad.l, x2: pad.l, y1: pad.t, y2: H - pad.b, stroke: "#262b36" }));
407
+ [0, 0.5, 1].forEach(function (t) {
408
+ var ty = y(maxVal * t);
409
+ var lab = svgEl("text", { x: pad.l - 6, y: ty + 3, "text-anchor": "end" });
410
+ lab.textContent = opts.fmt ? opts.fmt(maxVal * t) : Math.round(maxVal * t);
411
+ svg.appendChild(lab);
412
+ });
413
+ series.forEach(function (s) {
414
+ var pts = s.points.slice().sort(function (a, b) { return a.step - b.step; });
415
+ var d = pts.map(function (p, i) { return (i === 0 ? "M" : "L") + x(p.step) + "," + y(valueFn(p)); }).join(" ");
416
+ svg.appendChild(svgEl("path", { d: d, fill: "none", stroke: colorFor(s.slug), "stroke-width": 2 }));
417
+ pts.forEach(function (p) {
418
+ svg.appendChild(svgEl("circle", { cx: x(p.step), cy: y(valueFn(p)), r: 2.5, fill: colorFor(s.slug) }));
419
+ });
420
+ });
421
+ host.appendChild(svg);
422
+ }
423
+
424
+ (function () {
425
+ var bySlug = {};
426
+ (DATA.step_series || []).forEach(function (r) {
427
+ if (!bySlug[r.slug]) bySlug[r.slug] = [];
428
+ bySlug[r.slug].push(r);
429
+ });
430
+ var series = Object.keys(bySlug).map(function (slug) { return { slug: slug, points: bySlug[slug] }; });
431
+ if (series.length === 0) {
432
+ document.getElementById("section-series").style.display = "none";
433
+ return;
434
+ }
435
+ lineChart(document.getElementById("chart-line-tokens"), series, function (p) { return p.prompt_tokens.p50; }, { fmt: fmt });
436
+ lineChart(document.getElementById("chart-line-cost"), series, function (p) { return p.cum_cost_usd.mean; }, { fmt: fmtUsd });
437
+ var legend = document.getElementById("legend-series");
438
+ series.forEach(function (s) {
439
+ legend.appendChild(el("span", {}, '<span class="sw" style="background:' + colorFor(s.slug) + '"></span>' + esc(s.slug)));
440
+ });
441
+ })();
442
+
443
+ // ---------------------------------------------------------------- model x task matrix (heatmap table)
444
+ (function () {
445
+ var matrix = DATA.matrix || [];
446
+ var container = document.getElementById("tbl-matrix");
447
+ if (matrix.length === 0) {
448
+ document.getElementById("section-matrix").style.display = "none";
449
+ return;
450
+ }
451
+ var tasks = [];
452
+ var slugs = [];
453
+ var cell = {};
454
+ matrix.forEach(function (m) {
455
+ if (tasks.indexOf(m.task_id) === -1) tasks.push(m.task_id);
456
+ if (slugs.indexOf(m.slug) === -1) slugs.push(m.slug);
457
+ cell[m.slug + "\u0000" + m.task_id] = m;
458
+ });
459
+ function heatColor(rate) {
460
+ if (rate == null) return "transparent";
461
+ var g = Math.round(60 + rate * 140);
462
+ var r = Math.round(220 - rate * 160);
463
+ return "rgba(" + r + "," + g + ",120,0.35)";
464
+ }
465
+ var thead = "<thead><tr><th>Model</th>" + tasks.map(function (t) { return "<th>" + esc(t) + "</th>"; }).join("") + "</tr></thead>";
466
+ var tbody = "<tbody>" + slugs.map(function (slug) {
467
+ var cells = tasks.map(function (t) {
468
+ var m = cell[slug + "\u0000" + t];
469
+ if (!m) return "<td>&mdash;</td>";
470
+ return '<td><span class="heat-cell" style="background:' + heatColor(m.pass_at_k) + '">' + m.passed + "/" + m.k + "<br>" + pct(m.pass_at_k) + "</span></td>";
471
+ }).join("");
472
+ return "<tr><td class=\"mono\">" + esc(slug) + "</td>" + cells + "</tr>";
473
+ }).join("") + "</tbody>";
474
+ container.innerHTML = thead + tbody;
475
+ })();
476
+
477
+ // ---------------------------------------------------------------- runs table + filters + expandable timeline
478
+ (function () {
479
+ var runs = DATA.runs || [];
480
+ var container = document.getElementById("tbl-runs");
481
+ var fModel = document.getElementById("filter-model");
482
+ var fTask = document.getElementById("filter-task");
483
+ var fOutcome = document.getElementById("filter-outcome");
484
+
485
+ function uniq(fn) {
486
+ var seen = {}, out = [];
487
+ runs.forEach(function (r) { var v = fn(r); if (v && !seen[v]) { seen[v] = 1; out.push(v); } });
488
+ return out.sort();
489
+ }
490
+ uniq(function (r) { return r.slug; }).forEach(function (v) { fModel.appendChild(el("option", { value: v }, esc(v))); });
491
+ uniq(function (r) { return r.task_id; }).forEach(function (v) { fTask.appendChild(el("option", { value: v }, esc(v))); });
492
+ uniq(function (r) { return r.exit_status; }).forEach(function (v) { fOutcome.appendChild(el("option", { value: v }, esc(v))); });
493
+
494
+ var columns = [
495
+ { key: "slug", label: "Model", render: function (r) { return '<span class="mono">' + esc(r.slug) + "</span>"; } },
496
+ { key: "task_id", label: "Task", render: function (r) { return esc(r.task_id); } },
497
+ { key: "run_id", label: "Run", render: function (r) { return esc(r.run_id); } },
498
+ { key: "exit_status", label: "Outcome", render: function (r) {
499
+ var cls = r.resolved ? "ok" : (r.exit_status === "Error" ? "bad" : "warn");
500
+ return '<span class="pill ' + cls + '">' + esc(r.exit_status || "Unknown") + "</span>";
501
+ } },
502
+ { key: "steps", label: "Steps", render: function (r) { return fmt(r.steps); } },
503
+ { key: "elapsed_seconds", label: "Time", render: function (r) { return fmt(r.elapsed_seconds) + "s"; } },
504
+ { key: "tokens", label: "Tokens", render: function (r) { return fmt(r.tokens.total); } },
505
+ { key: "cost_usd", label: "Cost", render: function (r) { return fmtUsd(r.cost_usd) + (r.cost_source === "estimated" ? "~" : ""); } },
506
+ { key: "n_failed_commands", label: "Fails", render: function (r) { return fmt(r.n_failed_commands); } },
507
+ ];
508
+
509
+ function renderTimeline(run) {
510
+ var box = el("div", { class: "timeline" });
511
+ (run.timeline || []).forEach(function (s) {
512
+ var rcClass = s.returncode === 0 ? "rc0" : (s.returncode == null ? "muted" : "rcN");
513
+ var step = el("div", { class: "step" });
514
+ step.innerHTML =
515
+ '<div class="head"><span class="n">step ' + s.step + '</span>' +
516
+ '<span class="stats">' +
517
+ fmt(s.tokens.total) + " tok" +
518
+ (s.cost_usd != null ? " \u00b7 " + fmtUsd(s.cost_usd) : "") +
519
+ (s.returncode != null ? ' \u00b7 rc=<span class="' + rcClass + '">' + s.returncode + "</span>" : "") +
520
+ "</span></div>" +
521
+ (s.thought ? '<div class="thought">' + esc(s.thought) + "</div>" : "") +
522
+ (s.command ? "<pre>$ " + esc(s.command) + "</pre>" : "") +
523
+ (s.output_preview ? "<pre>" + esc(s.output_preview) + "</pre>" : "");
524
+ box.appendChild(step);
525
+ });
526
+ return box;
527
+ }
528
+
529
+ function draw() {
530
+ var filtered = runs.filter(function (r) {
531
+ if (fModel.value && r.slug !== fModel.value) return false;
532
+ if (fTask.value && r.task_id !== fTask.value) return false;
533
+ if (fOutcome.value && r.exit_status !== fOutcome.value) return false;
534
+ return true;
535
+ });
536
+ sortableTable(container, columns, filtered, {
537
+ defaultKey: "slug",
538
+ defaultDir: 1,
539
+ rowAttrs: function (r, i) { return 'class="run-row" data-idx="' + i + '"'; },
540
+ afterDraw: function (containerEl, sorted) {
541
+ containerEl.querySelectorAll("tbody tr").forEach(function (tr) {
542
+ var idx = Number(tr.getAttribute("data-idx"));
543
+ var run = sorted[idx];
544
+ tr.addEventListener("click", function () {
545
+ var next = tr.nextElementSibling;
546
+ if (next && next.classList.contains("detail-row")) {
547
+ next.remove();
548
+ return;
549
+ }
550
+ containerEl.querySelectorAll(".detail-row").forEach(function (d) { d.remove(); });
551
+ var detailRow = document.createElement("tr");
552
+ detailRow.className = "detail-row";
553
+ var td = document.createElement("td");
554
+ td.colSpan = columns.length;
555
+ td.appendChild(renderTimeline(run));
556
+ detailRow.appendChild(td);
557
+ tr.parentNode.insertBefore(detailRow, tr.nextSibling);
558
+ });
559
+ });
560
+ },
561
+ });
562
+ }
563
+ [fModel, fTask, fOutcome].forEach(function (elm) { elm.addEventListener("change", draw); });
564
+ draw();
565
+ })();
566
+ })();
567
+ </script>
568
+ </body>
569
+ </html>