@hackerrank/astra-cli 0.1.19 → 0.1.21
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.
- package/package.json +1 -1
- package/src/agent.js +15 -0
- package/src/model.js +11 -1
- package/src/prompts.js +7 -0
- package/src/report.html +83 -132
- package/src/report.js +17 -2
- package/src/verifier-runner.js +1 -1
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
buildSystemPrompt,
|
|
27
27
|
INSTANCE_TEMPLATE,
|
|
28
28
|
FORMAT_ERROR,
|
|
29
|
+
TRUNCATED_COMMAND,
|
|
29
30
|
OBSERVATION_TEMPLATE,
|
|
30
31
|
render,
|
|
31
32
|
} from "./prompts.js";
|
|
@@ -162,6 +163,14 @@ export class Agent {
|
|
|
162
163
|
if (this.mode === "interactive" && err.code === "NO_COMMAND") {
|
|
163
164
|
return { kind: "chat", content };
|
|
164
165
|
}
|
|
166
|
+
if (isTruncatedShellResponse(content, usage)) {
|
|
167
|
+
// A provider length limit can cut off a large heredoc before the
|
|
168
|
+
// closing fence. No command has run, so recover with smaller writes
|
|
169
|
+
// rather than consuming the terminal format-error budget.
|
|
170
|
+
this.formatErrorStreak = 0;
|
|
171
|
+
this.add("user", TRUNCATED_COMMAND);
|
|
172
|
+
return { kind: "format_error", error: "truncated shell response" };
|
|
173
|
+
}
|
|
165
174
|
this.formatErrorStreak++;
|
|
166
175
|
this.nFormatErrors++;
|
|
167
176
|
if (
|
|
@@ -352,6 +361,12 @@ export function parseCommand(text, { allowMultiple = false } = {}) {
|
|
|
352
361
|
return cmd;
|
|
353
362
|
}
|
|
354
363
|
|
|
364
|
+
function isTruncatedShellResponse(content, usage) {
|
|
365
|
+
if (usage?.finish_reason !== "length") return false;
|
|
366
|
+
const fences = String(content || "").match(/^```(?:bash|sh|shell|zsh)?[ \t]*$/gim) || [];
|
|
367
|
+
return fences.length % 2 === 1;
|
|
368
|
+
}
|
|
369
|
+
|
|
355
370
|
/** Returns the submission string if the sentinel was echoed, else null. */
|
|
356
371
|
export function checkSubmitted(output) {
|
|
357
372
|
if (output.returncode !== 0) return null;
|
package/src/model.js
CHANGED
|
@@ -48,7 +48,7 @@ export class GatewayModel {
|
|
|
48
48
|
this.maxTokens = maxTokens;
|
|
49
49
|
this.baseUrl = (baseUrl || process.env.ASTRA_GATEWAY_BASE_URL || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
50
50
|
this.apiKey = apiKey || process.env.ASTRA_GATEWAY_API_KEY || "";
|
|
51
|
-
this.modelKwargs = modelKwargs;
|
|
51
|
+
this.modelKwargs = sanitizeModelKwargs(model, modelKwargs);
|
|
52
52
|
this.maxRetries = maxRetries;
|
|
53
53
|
this.requestTimeoutMs = requestTimeoutMs;
|
|
54
54
|
this.onRetry = onRetry || (() => {});
|
|
@@ -144,6 +144,7 @@ export class GatewayModel {
|
|
|
144
144
|
this.nCalls++;
|
|
145
145
|
const content = data?.choices?.[0]?.message?.content ?? "";
|
|
146
146
|
const usage = normalizeUsage(data?.usage);
|
|
147
|
+
usage.finish_reason = data?.choices?.[0]?.finish_reason ?? null;
|
|
147
148
|
this.totalPromptTokens += usage.prompt_tokens;
|
|
148
149
|
this.totalCompletionTokens += usage.completion_tokens;
|
|
149
150
|
this.totalReasoningTokens += usage.reasoning_tokens || 0;
|
|
@@ -338,3 +339,12 @@ export function normalizeUsage(usage) {
|
|
|
338
339
|
function num(v) {
|
|
339
340
|
return Number.isFinite(v) ? v : 0;
|
|
340
341
|
}
|
|
342
|
+
|
|
343
|
+
function sanitizeModelKwargs(model, modelKwargs) {
|
|
344
|
+
const kwargs = { ...(modelKwargs || {}) };
|
|
345
|
+
// Gemini's OpenAI-compatible chat endpoint rejects this Anthropic-style
|
|
346
|
+
// cache-routing field. Keep the gateway client safe even when a caller or
|
|
347
|
+
// persisted configuration passes provider-specific kwargs through.
|
|
348
|
+
if (/^gemini(?:-|$)/i.test(model)) delete kwargs.prompt_cache_key;
|
|
349
|
+
return kwargs;
|
|
350
|
+
}
|
package/src/prompts.js
CHANGED
|
@@ -39,6 +39,9 @@ Autonomous mode:
|
|
|
39
39
|
environment variables, and config files needed to complete the task.
|
|
40
40
|
- In autonomous mode, you may provide multiple fenced bash blocks when they
|
|
41
41
|
form one coherent sequence; Astra executes them together in order.
|
|
42
|
+
- Keep each command response small enough to finish completely (roughly 6,000
|
|
43
|
+
characters). Do not write a large source file in one heredoc: create it in
|
|
44
|
+
small appended chunks, then verify it.
|
|
42
45
|
- When completely finished, run this and NOTHING else:
|
|
43
46
|
\`echo COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT\`
|
|
44
47
|
After that command you cannot continue.`;
|
|
@@ -78,6 +81,10 @@ Respond again with exactly ONE bash code block containing ONE command, like:
|
|
|
78
81
|
your_command_here
|
|
79
82
|
\`\`\``;
|
|
80
83
|
|
|
84
|
+
export const TRUNCATED_COMMAND = `Your previous response was cut off before its shell block closed, so no command was run.
|
|
85
|
+
|
|
86
|
+
Do not repeat a large file write in one response. Continue with exactly one complete bash block containing a small command (under roughly 6,000 characters); append files in chunks or make a smaller focused change.`;
|
|
87
|
+
|
|
81
88
|
export const OBSERVATION_TEMPLATE = `{{exception}}<returncode>{{returncode}}</returncode>
|
|
82
89
|
<output>
|
|
83
90
|
{{output}}
|
package/src/report.html
CHANGED
|
@@ -8,16 +8,15 @@
|
|
|
8
8
|
<style>
|
|
9
9
|
:root { --ink:#11131a; --muted:#69707d; --wash:#f5f6f8; --line:#e4e7eb; --lime:#a7ff63; --blue:#5285f7; --purple:#9160d8; --green:#39ae76; --orange:#f3a021; --red:#d85668; font-family:Satoshi,"Avenir Next",Arial,sans-serif; }
|
|
10
10
|
* { box-sizing:border-box; } body { margin:0; background:#fff; color:var(--ink); font-size:14px; line-height:1.45; } .wrap { max-width:1240px; margin:auto; padding:28px 32px 68px; }
|
|
11
|
-
header.top { display:flex; justify-content:space-between; gap:20px; align-items:flex-start; padding-bottom:28px; border-bottom:1px solid var(--line); } header.top h1 { font-size:32px; letter-spacing:-.045em; line-height:1; margin:8px 0 0; font-weight:700; } .eyebrow { display:inline-block; padding:5px 14px; border-radius:99px; background:var(--lime); font-size:12px; font-weight:700; } header.top .meta { color:var(--muted); font-size:12px; text-align:
|
|
11
|
+
header.top { display:flex; justify-content:space-between; gap:20px; align-items:flex-start; padding-bottom:28px; border-bottom:1px solid var(--line); } header.top h1 { font-size:32px; letter-spacing:-.045em; line-height:1; margin:8px 0 0; font-weight:700; } .eyebrow { display:inline-block; padding:5px 14px; border-radius:99px; background:var(--lime); font-size:12px; font-weight:700; } header.top .meta { color:var(--muted); font-size:12px; text-align:left; }
|
|
12
12
|
.kpis { display:grid; grid-template-columns:repeat(5,1fr); margin:24px 0 52px; } .kpi { min-height:96px; padding:8px 18px; border-left:1px solid var(--line); } .kpi:first-child { border:0; padding-left:0; } .kpi .v { font-size:28px; letter-spacing:-.045em; font-weight:700; } .kpi .l { color:var(--muted); font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; margin-top:8px; }
|
|
13
|
-
section { margin:56px 0; } section > h2 { font-size:20px; letter-spacing:-.035em; margin:0 0 22px; }
|
|
13
|
+
section { margin:56px 0; } section > h2 { font-size:20px; letter-spacing:-.035em; margin:0 0 22px; } .grid2 { display:grid; grid-template-columns:1.1fr 1fr; gap:44px; } .panel { padding:0; } .panel h3 { color:var(--muted); letter-spacing:.07em; text-transform:uppercase; font-size:12px; margin:0 0 16px; }
|
|
14
14
|
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
15
15
|
th, td { text-align: left; padding: 10px 8px; border-bottom: 1px solid var(--line); white-space: nowrap; }
|
|
16
16
|
th { color: var(--muted); font-weight: 600; font-size: 11.5px; text-transform: uppercase; letter-spacing: .03em; cursor: pointer; user-select: none; }
|
|
17
17
|
th:hover { color: var(--text); }
|
|
18
18
|
th .arrow { opacity: .5; font-size: 10px; margin-left: 3px; }
|
|
19
19
|
tbody tr:hover { background: rgba(255,255,255,0.02); }
|
|
20
|
-
tbody tr.run-row { cursor: pointer; }
|
|
21
20
|
.pill { display: inline-block; padding: 1px 8px; border-radius: 100px; font-size: 11px; font-weight: 600; }
|
|
22
21
|
.pill.ok { background: rgba(62,207,142,.15); color: var(--good); }
|
|
23
22
|
.pill.bad { background: rgba(239,90,111,.15); color: var(--bad); }
|
|
@@ -33,17 +32,6 @@
|
|
|
33
32
|
.legend .sw { display: inline-block; width: 9px; height: 9px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; }
|
|
34
33
|
.heat-table td { text-align: center; font-variant-numeric: tabular-nums; }
|
|
35
34
|
.heat-cell { display: inline-block; min-width: 44px; padding: 3px 6px; border-radius: 5px; font-size: 12px; }
|
|
36
|
-
details.run-detail { margin: 0; }
|
|
37
|
-
details.run-detail > summary { display: none; }
|
|
38
|
-
.timeline { margin: 0 0 18px; background:var(--wash); padding:0 14px; overflow: hidden; }
|
|
39
|
-
.timeline .step { padding: 10px 14px; border-bottom: 1px solid var(--border); }
|
|
40
|
-
.timeline .step:last-child { border-bottom: none; }
|
|
41
|
-
.timeline .step .head { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; margin-bottom: 4px; }
|
|
42
|
-
.timeline .step .n { color: var(--accent); font-weight: 700; font-size: 12px; }
|
|
43
|
-
.timeline .step .stats { color: var(--muted); font-size: 11.5px; }
|
|
44
|
-
.timeline .step .thought { color: var(--muted); font-size: 12.5px; margin-bottom: 6px; }
|
|
45
|
-
.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; }
|
|
46
|
-
.rc0 { color: var(--good); } .rcN { color: var(--bad); }
|
|
47
35
|
.toolbar { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; align-items: center; }
|
|
48
36
|
.toolbar select, .toolbar input { background: var(--wash); border:0; color:var(--ink); border-radius:4px; padding:7px 9px; font-size:12px; } .model-filter { align-items:end; } .model-filter > span { color:var(--muted); font-size:11px; } .model-menu { position:relative; } .model-menu summary { cursor:pointer; list-style:none; min-width:220px; padding:9px 32px 9px 11px; background:var(--wash); border-radius:5px; font-size:12px; position:relative; } .model-menu summary::-webkit-details-marker { display:none; } .model-menu summary::after { content:"⌄"; position:absolute; right:11px; color:var(--muted); } .model-options { position:absolute; z-index:2; top:calc(100% + 6px); left:0; width:260px; max-height:240px; overflow:auto; padding:6px; background:#fff; border:1px solid var(--line); box-shadow:0 10px 24px rgba(17,19,26,.1); } .model-option { display:flex; gap:9px; align-items:center; padding:8px; font-size:12px; cursor:pointer; } .model-option:hover { background:var(--wash); } .model-option input { accent-color:var(--blue); }
|
|
49
37
|
.toolbar label { color: var(--muted); font-size: 12px; }
|
|
@@ -61,6 +49,17 @@
|
|
|
61
49
|
.result-badge.passed { background:rgba(57,174,118,.14); color:#16734a; }
|
|
62
50
|
.result-badge.failed { background:rgba(216,86,104,.14); color:#ad3042; }
|
|
63
51
|
.result-badge.unscored { background:var(--wash); color:var(--muted); }
|
|
52
|
+
.score-chart-scroll { overflow-x:auto; padding:4px 0 12px; }
|
|
53
|
+
.score-chart { position:relative; height:280px; padding:18px 12px 68px 44px; display:flex; align-items:end; gap:4px; background:repeating-linear-gradient(to top, transparent 0, transparent 25%, rgba(228,231,235,.8) 25.2%, transparent 25.5%); }
|
|
54
|
+
.score-axis { position:absolute; left:0; color:var(--muted); font-size:10px; font-variant-numeric:tabular-nums; }
|
|
55
|
+
.score-axis.top { top:12px; } .score-axis.mid { top:50%; } .score-axis.zero { bottom:62px; }
|
|
56
|
+
.score-item { position:relative; height:100%; min-width:40px; flex:1 0 40px; display:flex; align-items:end; }
|
|
57
|
+
.score-bar { width:100%; min-height:2px; position:relative; border-radius:4px 4px 0 0; background:var(--blue); }
|
|
58
|
+
.score-bar.unscored { height:3px !important; }
|
|
59
|
+
.score-value { position:absolute; left:0; right:0; bottom:7px; color:#fff; text-align:center; font-size:10px; font-weight:700; font-variant-numeric:tabular-nums; }
|
|
60
|
+
.score-bar.unscored .score-value { color:var(--muted); bottom:7px; }
|
|
61
|
+
.score-label { position:absolute; top:calc(100% + 8px); left:0; width:40px; color:var(--muted); font-size:8px; line-height:1.2; overflow-wrap:anywhere; text-align:center; white-space:normal; transform:none; }
|
|
62
|
+
@media(max-width:800px){ .score-chart { height:260px; padding-bottom:64px; } .score-axis.zero { bottom:58px; } }
|
|
64
63
|
footer { color: var(--muted); font-size: 11.5px; margin-top: 40px; text-align: center; }
|
|
65
64
|
svg text { fill: var(--muted); font-size: 10.5px; }
|
|
66
65
|
.axis line, .axis path { stroke: var(--line); }
|
|
@@ -70,15 +69,19 @@
|
|
|
70
69
|
<body>
|
|
71
70
|
<div class="wrap">
|
|
72
71
|
<header class="top">
|
|
73
|
-
<div><h1>Benchmark report</h1><div class="meta" id="generated"></div
|
|
72
|
+
<div><h1>Benchmark report</h1><div class="meta" id="generated"></div></div>
|
|
74
73
|
</header>
|
|
75
74
|
|
|
76
75
|
<div class="kpis" id="kpis"></div>
|
|
77
76
|
|
|
77
|
+
<section id="section-score-distribution">
|
|
78
|
+
<h2>Score distribution</h2>
|
|
79
|
+
<div class="score-chart-scroll"><div id="chart-score-distribution" role="img" aria-label="Verified score by model and reasoning configuration"></div></div>
|
|
80
|
+
</section>
|
|
81
|
+
|
|
78
82
|
<section>
|
|
79
|
-
<h2>Run outcomes
|
|
83
|
+
<h2>Run outcomes</h2>
|
|
80
84
|
<div class="toolbar model-filter">
|
|
81
|
-
<span>Models</span>
|
|
82
85
|
<details class="model-menu">
|
|
83
86
|
<summary id="model-filter-label">All models</summary>
|
|
84
87
|
<div id="leaderboard-model-options" class="model-options"></div>
|
|
@@ -90,22 +93,15 @@
|
|
|
90
93
|
</section>
|
|
91
94
|
|
|
92
95
|
<section>
|
|
93
|
-
<h2>Test-case results
|
|
96
|
+
<h2>Test-case results</h2>
|
|
94
97
|
<div class="toolbar"><label for="verifier-model-select">Model</label><select id="verifier-model-select"></select></div>
|
|
95
98
|
<div class="panel">
|
|
96
99
|
<table id="tbl-verifier-matrix"></table>
|
|
97
100
|
</div>
|
|
98
101
|
</section>
|
|
99
102
|
|
|
100
|
-
<section>
|
|
101
|
-
<h2>Run details <span class="sub">generation and benchmarking status for each attempt</span></h2>
|
|
102
|
-
<div class="panel">
|
|
103
|
-
<table id="tbl-runs"></table>
|
|
104
|
-
</div>
|
|
105
|
-
</section>
|
|
106
|
-
|
|
107
103
|
<section id="section-series">
|
|
108
|
-
<h2>Trajectory
|
|
104
|
+
<h2>Trajectory</h2>
|
|
109
105
|
<div class="grid2">
|
|
110
106
|
<div class="panel">
|
|
111
107
|
<h3>Prompt tokens (context) vs. step</h3>
|
|
@@ -150,6 +146,21 @@
|
|
|
150
146
|
}
|
|
151
147
|
|
|
152
148
|
function fmt(n) { return Number(n || 0).toLocaleString("en-US"); }
|
|
149
|
+
function fmtDuration(seconds) {
|
|
150
|
+
var total = Math.max(0, Math.round(Number(seconds) || 0));
|
|
151
|
+
if (total < 60) return total + "s";
|
|
152
|
+
var minutes = Math.floor(total / 60);
|
|
153
|
+
var remainder = total % 60;
|
|
154
|
+
if (minutes < 60) return minutes + "m " + String(remainder).padStart(2, "0") + "s";
|
|
155
|
+
var hours = Math.floor(minutes / 60);
|
|
156
|
+
return hours + "h " + String(minutes % 60).padStart(2, "0") + "m";
|
|
157
|
+
}
|
|
158
|
+
function fmtTokens(tokens) {
|
|
159
|
+
var value = Math.max(0, Number(tokens) || 0);
|
|
160
|
+
if (value < 1000) return fmt(Math.round(value));
|
|
161
|
+
if (value < 1000000) return (value / 1000).toFixed(1).replace(/\.0$/, "") + "K";
|
|
162
|
+
return (value / 1000000).toFixed(1).replace(/\.0$/, "") + "M";
|
|
163
|
+
}
|
|
153
164
|
function fmt1(n) { return (Number(n) || 0).toFixed(1); }
|
|
154
165
|
function pct(n) { return Math.round((Number(n) || 0) * 100) + "%"; }
|
|
155
166
|
function fmtUsd(n) {
|
|
@@ -179,19 +190,17 @@
|
|
|
179
190
|
|
|
180
191
|
// ---------------------------------------------------------------- header
|
|
181
192
|
document.getElementById("generated").textContent = DATA.generated_at
|
|
182
|
-
? "Generated " + new Date(DATA.generated_at).toLocaleString("en-US", { dateStyle: "medium", timeStyle: "short" })
|
|
193
|
+
? "Generated on " + new Date(DATA.generated_at).toLocaleString("en-US", { dateStyle: "medium", timeStyle: "short" })
|
|
183
194
|
: "";
|
|
184
|
-
document.getElementById("schema").textContent = DATA.schema || "";
|
|
185
|
-
|
|
186
195
|
// ---------------------------------------------------------------- KPIs
|
|
187
196
|
(function renderKpis() {
|
|
188
197
|
var k = DATA.kpis || {};
|
|
189
198
|
var cards = [
|
|
190
|
-
["
|
|
199
|
+
["Model–reasoning configurations", fmt(k.models)],
|
|
191
200
|
["Average score", k.average_score == null ? "n/a" : fmt1(k.average_score) + "%"],
|
|
192
|
-
["
|
|
201
|
+
["Average duration", fmtDuration(k.runs ? k.elapsed_seconds / k.runs : 0)],
|
|
193
202
|
["Total cost", fmtUsd(k.cost_usd) + (k.cost_source === "estimated" ? "~" : "")],
|
|
194
|
-
["Tokens used",
|
|
203
|
+
["Tokens used", fmtTokens(k.tokens)],
|
|
195
204
|
];
|
|
196
205
|
var host = document.getElementById("kpis");
|
|
197
206
|
cards.forEach(function (c) {
|
|
@@ -199,6 +208,37 @@
|
|
|
199
208
|
});
|
|
200
209
|
})();
|
|
201
210
|
|
|
211
|
+
// ---------------------------------------------------------------- score distribution
|
|
212
|
+
(function renderScoreDistribution() {
|
|
213
|
+
var host = document.getElementById("chart-score-distribution");
|
|
214
|
+
if (!host) return;
|
|
215
|
+
var rows = (DATA.leaderboard || []).map(function (row) {
|
|
216
|
+
var verified = typeof row.average_score === "number";
|
|
217
|
+
return { slug: row.slug, score: verified ? Math.max(0, Math.min(100, row.average_score)) : null, verified: verified };
|
|
218
|
+
}).sort(function (a, b) {
|
|
219
|
+
if (a.verified !== b.verified) return a.verified ? -1 : 1;
|
|
220
|
+
return (b.score || 0) - (a.score || 0) || a.slug.localeCompare(b.slug);
|
|
221
|
+
});
|
|
222
|
+
if (!rows.length) {
|
|
223
|
+
document.getElementById("section-score-distribution").style.display = "none";
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
host.className = "score-chart";
|
|
227
|
+
host.style.minWidth = Math.max(760, rows.length * 45) + "px";
|
|
228
|
+
[["top", "100"], ["mid", "50"], ["zero", "0"]].forEach(function (axis) {
|
|
229
|
+
host.appendChild(el("span", { class: "score-axis " + axis[0] }, axis[1]));
|
|
230
|
+
});
|
|
231
|
+
rows.forEach(function (row) {
|
|
232
|
+
var item = el("div", { class: "score-item" });
|
|
233
|
+
var bar = el("div", { class: "score-bar", title: row.slug + ": " + (row.verified ? fmt1(row.score) + "%" : "n/a") });
|
|
234
|
+
if (row.verified) bar.style.height = Math.max(2, row.score) + "%";
|
|
235
|
+
bar.appendChild(el("span", { class: "score-value" }, row.verified ? fmt1(row.score) + "%" : "n/a"));
|
|
236
|
+
item.appendChild(bar);
|
|
237
|
+
item.appendChild(el("span", { class: "score-label" }, esc(row.slug)));
|
|
238
|
+
host.appendChild(item);
|
|
239
|
+
});
|
|
240
|
+
})();
|
|
241
|
+
|
|
202
242
|
// ---------------------------------------------------------------- sortable table helper
|
|
203
243
|
function sortableTable(container, columns, rows, opts) {
|
|
204
244
|
opts = opts || {};
|
|
@@ -206,6 +246,10 @@
|
|
|
206
246
|
function draw() {
|
|
207
247
|
var sorted = rows.slice().sort(function (a, b) {
|
|
208
248
|
var va = a[state.key], vb = b[state.key];
|
|
249
|
+
if (va == null || vb == null) {
|
|
250
|
+
if (va == null && vb == null) return 0;
|
|
251
|
+
return va == null ? 1 : -1;
|
|
252
|
+
}
|
|
209
253
|
if (typeof va === "string" || typeof vb === "string") {
|
|
210
254
|
return state.dir * String(va).localeCompare(String(vb));
|
|
211
255
|
}
|
|
@@ -291,13 +335,13 @@
|
|
|
291
335
|
{ key: "runs", label: "Runs", render: function (r) { return fmt(r.runs); } },
|
|
292
336
|
{ key: "average_score", label: "Score", render: function (r) { return r.average_score == null ? '<span class="n-a">n/a</span>' : fmt1(r.average_score) + "%"; } },
|
|
293
337
|
{ key: "sum_steps", label: "Total steps", render: function (r) { return fmt(r.sum_steps); } },
|
|
294
|
-
{ key: "sum_elapsed_seconds", label: "Time taken", render: function (r) { return
|
|
295
|
-
{ key: "sum_tokens", label: "Tokens", render: function (r) { return
|
|
338
|
+
{ key: "sum_elapsed_seconds", label: "Time taken", render: function (r) { return fmtDuration(r.sum_elapsed_seconds); } },
|
|
339
|
+
{ key: "sum_tokens", label: "Tokens", render: function (r) { return fmtTokens(r.sum_tokens); } },
|
|
296
340
|
{ key: "cost_usd", label: "Cost", render: function (r) { return fmtUsd(r.cost_usd) + (r.cost_source === "estimated" ? "~" : ""); } },
|
|
297
341
|
];
|
|
298
342
|
(DATA.leaderboard || []).forEach(function (r) {
|
|
299
343
|
var option = el("label", { class: "model-option" });
|
|
300
|
-
option.appendChild(el("input", { type: "checkbox", value: r.slug }));
|
|
344
|
+
option.appendChild(el("input", { type: "checkbox", value: r.slug, checked: "checked" }));
|
|
301
345
|
option.appendChild(document.createTextNode(r.model + " · " + r.reasoning));
|
|
302
346
|
options.appendChild(option);
|
|
303
347
|
});
|
|
@@ -305,7 +349,7 @@
|
|
|
305
349
|
var selected = Array.prototype.slice.call(options.querySelectorAll("input:checked")).map(function (o) { return o.value; });
|
|
306
350
|
var rows = selected.length ? (DATA.leaderboard || []).filter(function (r) { return selected.indexOf(r.slug) !== -1; }) : DATA.leaderboard || [];
|
|
307
351
|
filterLabel.textContent = selected.length ? selected.length + " model" + (selected.length === 1 ? "" : "s") + " selected" : "All models";
|
|
308
|
-
sortableTable(container, columns, rows, { defaultKey: "
|
|
352
|
+
sortableTable(container, columns, rows, { defaultKey: "average_score" });
|
|
309
353
|
}
|
|
310
354
|
options.addEventListener("change", draw);
|
|
311
355
|
draw();
|
|
@@ -389,7 +433,7 @@
|
|
|
389
433
|
if (w > 0) track.appendChild(el("div", { class: "bar-seg", style: "width:" + w + "%;background:" + colors[k] }));
|
|
390
434
|
});
|
|
391
435
|
row.appendChild(track);
|
|
392
|
-
row.appendChild(el("div", { class: "bar-val" },
|
|
436
|
+
row.appendChild(el("div", { class: "bar-val" }, fmtTokens(total)));
|
|
393
437
|
host.appendChild(row);
|
|
394
438
|
});
|
|
395
439
|
keys.forEach(function (k) {
|
|
@@ -414,7 +458,7 @@
|
|
|
414
458
|
[0, 0.25, 0.5, 0.75, 1].forEach(function (t) {
|
|
415
459
|
var ty = y(maxDuration * t);
|
|
416
460
|
var lab = svgEl("text", { x: pad.l - 8, y: ty + 3, "text-anchor": "end" });
|
|
417
|
-
lab.textContent =
|
|
461
|
+
lab.textContent = fmtDuration(maxDuration * t);
|
|
418
462
|
svg.appendChild(lab);
|
|
419
463
|
});
|
|
420
464
|
var lab2 = svgEl("text", { x: W - pad.r, y: H - pad.b + 20, "text-anchor": "end" });
|
|
@@ -510,7 +554,7 @@
|
|
|
510
554
|
document.getElementById("section-series").style.display = "none";
|
|
511
555
|
return;
|
|
512
556
|
}
|
|
513
|
-
lineChart(document.getElementById("chart-line-tokens"), series, function (p) { return p.prompt_tokens.p50; }, { fmt:
|
|
557
|
+
lineChart(document.getElementById("chart-line-tokens"), series, function (p) { return p.prompt_tokens.p50; }, { fmt: fmtTokens });
|
|
514
558
|
lineChart(document.getElementById("chart-line-cost"), series, function (p) { return p.cum_cost_usd.mean; }, { fmt: fmtUsd });
|
|
515
559
|
var legend = document.getElementById("legend-series");
|
|
516
560
|
series.forEach(function (s) {
|
|
@@ -553,99 +597,6 @@
|
|
|
553
597
|
container.innerHTML = thead + tbody;
|
|
554
598
|
})();
|
|
555
599
|
|
|
556
|
-
// ---------------------------------------------------------------- runs table + filters + expandable timeline
|
|
557
|
-
(function () {
|
|
558
|
-
var runs = DATA.runs || [];
|
|
559
|
-
var container = document.getElementById("tbl-runs");
|
|
560
|
-
var fModel = document.getElementById("filter-model");
|
|
561
|
-
var fTask = document.getElementById("filter-task");
|
|
562
|
-
var fOutcome = document.getElementById("filter-outcome");
|
|
563
|
-
if (!container) return;
|
|
564
|
-
|
|
565
|
-
function uniq(fn) {
|
|
566
|
-
var seen = {}, out = [];
|
|
567
|
-
runs.forEach(function (r) { var v = fn(r); if (v && !seen[v]) { seen[v] = 1; out.push(v); } });
|
|
568
|
-
return out.sort();
|
|
569
|
-
}
|
|
570
|
-
if (fModel) uniq(function (r) { return r.slug; }).forEach(function (v) { fModel.appendChild(el("option", { value: v }, esc(v))); });
|
|
571
|
-
if (fTask) uniq(function (r) { return r.task_id; }).forEach(function (v) { fTask.appendChild(el("option", { value: v }, esc(v))); });
|
|
572
|
-
if (fOutcome) uniq(function (r) { return r.status; }).forEach(function (v) { fOutcome.appendChild(el("option", { value: v }, esc(v))); });
|
|
573
|
-
|
|
574
|
-
var columns = [
|
|
575
|
-
{ key: "slug", label: "Model", render: function (r) { return '<span class="mono">' + esc(r.slug) + "</span>"; } },
|
|
576
|
-
{ key: "status", label: "generation_status", render: function (r) {
|
|
577
|
-
var cls = r.completed ? "ok" : (r.status === "request_error" ? "bad" : "warn");
|
|
578
|
-
return '<span class="pill ' + cls + '">' + esc(r.status || "Unknown") + "</span>";
|
|
579
|
-
} },
|
|
580
|
-
{ key: "verifier_status", label: "benchmarking_status", render: function (r) {
|
|
581
|
-
var cls = r.verifier_status === "passed" ? "ok" : (r.verifier_status === "not_configured" ? "warn" : "bad");
|
|
582
|
-
return '<span class="pill ' + cls + '">' + esc(r.verifier_status || "NA") + "</span>";
|
|
583
|
-
} },
|
|
584
|
-
{ key: "solved_score", label: "Solved", render: function (r) { return typeof r.solved_score === "number" ? fmt1(r.solved_score) + "%" : '<span class="n-a">NA</span>'; } },
|
|
585
|
-
{ key: "steps", label: "Steps", render: function (r) { return fmt(r.steps); } },
|
|
586
|
-
{ key: "elapsed_seconds", label: "Time", render: function (r) { return fmt(r.elapsed_seconds) + "s"; } },
|
|
587
|
-
{ key: "tokens", label: "Tokens", render: function (r) { return fmt(r.tokens.total); } },
|
|
588
|
-
{ key: "cost_usd", label: "Cost", render: function (r) { return fmtUsd(r.cost_usd) + (r.cost_source === "estimated" ? "~" : ""); } },
|
|
589
|
-
{ key: "n_failed_commands", label: "Fails", render: function (r) { return fmt(r.n_failed_commands); } },
|
|
590
|
-
];
|
|
591
|
-
|
|
592
|
-
function renderTimeline(run) {
|
|
593
|
-
var box = el("div", { class: "timeline" });
|
|
594
|
-
(run.timeline || []).forEach(function (s) {
|
|
595
|
-
var rcClass = s.returncode === 0 ? "rc0" : (s.returncode == null ? "muted" : "rcN");
|
|
596
|
-
var step = el("div", { class: "step" });
|
|
597
|
-
step.innerHTML =
|
|
598
|
-
'<div class="head"><span class="n">step ' + s.step + '</span>' +
|
|
599
|
-
'<span class="stats">' +
|
|
600
|
-
fmt(s.tokens.total) + " tok" +
|
|
601
|
-
(s.cost_usd != null ? " \u00b7 " + fmtUsd(s.cost_usd) : "") +
|
|
602
|
-
(s.returncode != null ? ' \u00b7 rc=<span class="' + rcClass + '">' + s.returncode + "</span>" : "") +
|
|
603
|
-
"</span></div>" +
|
|
604
|
-
(s.thought ? '<div class="thought">' + esc(s.thought) + "</div>" : "") +
|
|
605
|
-
(s.command ? "<pre>$ " + esc(s.command) + "</pre>" : "") +
|
|
606
|
-
(s.output_preview ? "<pre>" + esc(s.output_preview) + "</pre>" : "");
|
|
607
|
-
box.appendChild(step);
|
|
608
|
-
});
|
|
609
|
-
return box;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
function draw() {
|
|
613
|
-
var filtered = runs.filter(function (r) {
|
|
614
|
-
if (fModel && fModel.value && r.slug !== fModel.value) return false;
|
|
615
|
-
if (fTask && fTask.value && r.task_id !== fTask.value) return false;
|
|
616
|
-
if (fOutcome && fOutcome.value && r.status !== fOutcome.value) return false;
|
|
617
|
-
return true;
|
|
618
|
-
});
|
|
619
|
-
sortableTable(container, columns, filtered, {
|
|
620
|
-
defaultKey: "slug",
|
|
621
|
-
defaultDir: 1,
|
|
622
|
-
rowAttrs: function (r, i) { return 'class="run-row" data-idx="' + i + '"'; },
|
|
623
|
-
afterDraw: function (containerEl, sorted) {
|
|
624
|
-
containerEl.querySelectorAll("tbody tr").forEach(function (tr) {
|
|
625
|
-
var idx = Number(tr.getAttribute("data-idx"));
|
|
626
|
-
var run = sorted[idx];
|
|
627
|
-
tr.addEventListener("click", function () {
|
|
628
|
-
var next = tr.nextElementSibling;
|
|
629
|
-
if (next && next.classList.contains("detail-row")) {
|
|
630
|
-
next.remove();
|
|
631
|
-
return;
|
|
632
|
-
}
|
|
633
|
-
containerEl.querySelectorAll(".detail-row").forEach(function (d) { d.remove(); });
|
|
634
|
-
var detailRow = document.createElement("tr");
|
|
635
|
-
detailRow.className = "detail-row";
|
|
636
|
-
var td = document.createElement("td");
|
|
637
|
-
td.colSpan = columns.length;
|
|
638
|
-
td.appendChild(renderTimeline(run));
|
|
639
|
-
detailRow.appendChild(td);
|
|
640
|
-
tr.parentNode.insertBefore(detailRow, tr.nextSibling);
|
|
641
|
-
});
|
|
642
|
-
});
|
|
643
|
-
},
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
[fModel, fTask, fOutcome].filter(Boolean).forEach(function (elm) { elm.addEventListener("change", draw); });
|
|
647
|
-
draw();
|
|
648
|
-
})();
|
|
649
600
|
})();
|
|
650
601
|
</script>
|
|
651
602
|
</body>
|
package/src/report.js
CHANGED
|
@@ -148,7 +148,13 @@ export function buildSummary(runs) {
|
|
|
148
148
|
avg_n_retries: avg(rs, (r) => r.n_retries),
|
|
149
149
|
};
|
|
150
150
|
});
|
|
151
|
-
leaderboard.sort((a, b) =>
|
|
151
|
+
leaderboard.sort((a, b) => {
|
|
152
|
+
const aScored = a.average_score != null;
|
|
153
|
+
const bScored = b.average_score != null;
|
|
154
|
+
if (aScored !== bScored) return aScored ? -1 : 1;
|
|
155
|
+
return (b.average_score ?? 0) - (a.average_score ?? 0)
|
|
156
|
+
|| a.slug.localeCompare(b.slug);
|
|
157
|
+
});
|
|
152
158
|
|
|
153
159
|
const matrix = groupBy(runs, (r) => `${r.slug}\u0000${r.task_id}`).map(([, rs]) => {
|
|
154
160
|
const completed = rs.filter((r) => r.completed).length;
|
|
@@ -260,7 +266,16 @@ export function buildSummary(runs) {
|
|
|
260
266
|
export function renderReportHtml(summary) {
|
|
261
267
|
const tplPath = new URL("./report.html", import.meta.url);
|
|
262
268
|
const tpl = fs.readFileSync(tplPath, "utf8");
|
|
263
|
-
|
|
269
|
+
// The summary includes model trajectory text, which can contain arbitrary
|
|
270
|
+
// shell output. Escape HTML-sensitive characters before embedding JSON in a
|
|
271
|
+
// script block so a literal `</script>` cannot terminate it early.
|
|
272
|
+
const data = JSON.stringify(summary)
|
|
273
|
+
.replace(/</g, "\\u003c")
|
|
274
|
+
.replace(/>/g, "\\u003e")
|
|
275
|
+
.replace(/&/g, "\\u0026")
|
|
276
|
+
.replace(/[\u2028\u2029]/g, (character) =>
|
|
277
|
+
character === "\u2028" ? "\\u2028" : "\\u2029",
|
|
278
|
+
);
|
|
264
279
|
if (!tpl.includes("/*__ASTRA_DATA__*/")) {
|
|
265
280
|
throw new Error("src/report.html is missing the /*__ASTRA_DATA__*/ injection marker");
|
|
266
281
|
}
|
package/src/verifier-runner.js
CHANGED
|
@@ -153,7 +153,7 @@ export async function runVerifier({ project, cell, runDir, candidateDir, config,
|
|
|
153
153
|
if (!validation.ok) return { status: "error", failureOwner: "infrastructure", solvedScore: "NA", durationSeconds, error: validation.error, exitCode: commandResult.code };
|
|
154
154
|
return {
|
|
155
155
|
status: raw.status,
|
|
156
|
-
failureOwner: raw.status === "failed" ? "candidate" : raw.status === "passed" ? null : "infrastructure",
|
|
156
|
+
failureOwner: raw.status === "failed" || raw.status === "blocked" ? "candidate" : raw.status === "passed" ? null : "infrastructure",
|
|
157
157
|
score: raw.score,
|
|
158
158
|
solvedScore: raw.score?.percentage ?? "NA",
|
|
159
159
|
criteria: raw.criteria,
|