@hackerrank/astra-cli 0.1.4 → 0.1.6
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/README.md +13 -2
- package/package.json +1 -1
- package/src/agent.js +30 -1
- package/src/bench.js +12 -3
- package/src/cli.js +102 -20
- package/src/project.js +2 -2
- package/src/report.js +1 -0
package/README.md
CHANGED
|
@@ -204,6 +204,17 @@ astra --resume <id> # resume (continue an interactive chat,
|
|
|
204
204
|
# or inspect/continue an autonomous run)
|
|
205
205
|
```
|
|
206
206
|
|
|
207
|
+
Resume an interrupted benchmark without repeating its task, model, or output
|
|
208
|
+
arguments:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
astra bench --resume <session-id>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
It reopens the original candidate workspace and updates that same run's
|
|
215
|
+
trajectory, metrics row, and report telemetry. Explicit flags override the
|
|
216
|
+
saved setting when needed.
|
|
217
|
+
|
|
207
218
|
Verified working models on the gateway include `claude-sonnet-5`, `claude-opus-5`,
|
|
208
219
|
`gpt-5.6-sol`, `gpt-5.6-terra`, `gemini-3.7-flash`, `grok-4.6`, `kimi-k3`, and others.
|
|
209
220
|
Some routes (e.g. `glm-5.2`) may return HTTP 402 when the underlying provider is out of
|
|
@@ -228,7 +239,7 @@ credits — that's a quota issue, not a bug.
|
|
|
228
239
|
whatever runs already exist on disk, then exit
|
|
229
240
|
-C, --cwd <path> Working directory for commands (default: cwd)
|
|
230
241
|
-o, --output <path> Also write trajectory JSON here (autonomous mode)
|
|
231
|
-
-s, --steps <n>
|
|
242
|
+
-s, --steps <n> Turn limit (unset = no limit)
|
|
232
243
|
-w, --wall <seconds> Wall-clock limit (default: 0 = none)
|
|
233
244
|
--timeout <seconds> Per-command timeout (default: 60)
|
|
234
245
|
--max-output <n> Max chars of command output kept (default: 16000)
|
|
@@ -237,7 +248,7 @@ credits — that's a quota issue, not a bug.
|
|
|
237
248
|
--resume <id> Resume a saved session
|
|
238
249
|
--sessions List saved sessions and exit
|
|
239
250
|
-y, --yolo Auto-run commands (always on in autonomous mode)
|
|
240
|
-
-q, --quiet Do not stream
|
|
251
|
+
-q, --quiet Do not stream turns (autonomous mode)
|
|
241
252
|
-h, --help Show help
|
|
242
253
|
```
|
|
243
254
|
|
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -38,7 +38,7 @@ export class Agent {
|
|
|
38
38
|
this.model = model;
|
|
39
39
|
this.env = env;
|
|
40
40
|
this.mode = opts.mode === "interactive" ? "interactive" : "autonomous";
|
|
41
|
-
this.stepLimit = opts.stepLimit ??
|
|
41
|
+
this.stepLimit = opts.stepLimit ?? 0;
|
|
42
42
|
this.wallTimeLimitSeconds = opts.wallTimeLimitSeconds ?? 0;
|
|
43
43
|
this.maxConsecutiveFormatErrors = opts.maxConsecutiveFormatErrors ?? 3;
|
|
44
44
|
this.outputPath = opts.outputPath ?? null;
|
|
@@ -64,6 +64,7 @@ export class Agent {
|
|
|
64
64
|
this.sessionId = opts.sessionId ?? null;
|
|
65
65
|
this.sessionCreated = opts.sessionCreated ?? new Date().toISOString();
|
|
66
66
|
this.title = opts.title ?? "";
|
|
67
|
+
this.bench = opts.bench ?? null;
|
|
67
68
|
this._saveSession = opts.saveSession ?? null; // (id, doc) => void
|
|
68
69
|
}
|
|
69
70
|
|
|
@@ -240,6 +241,7 @@ export class Agent {
|
|
|
240
241
|
id: this.sessionId ?? undefined,
|
|
241
242
|
created: this.sessionCreated,
|
|
242
243
|
title: this.title || undefined,
|
|
244
|
+
bench: this.bench ?? undefined,
|
|
243
245
|
info: {
|
|
244
246
|
mode: this.mode,
|
|
245
247
|
task: this.task || undefined,
|
|
@@ -248,10 +250,20 @@ export class Agent {
|
|
|
248
250
|
n_steps: this.nSteps,
|
|
249
251
|
model: this.model.model,
|
|
250
252
|
n_calls: this.model.nCalls,
|
|
253
|
+
n_retries: this.model.nRetries,
|
|
254
|
+
counters: {
|
|
255
|
+
commands: this.nCommands,
|
|
256
|
+
failed_commands: this.nFailedCommands,
|
|
257
|
+
format_errors: this.nFormatErrors,
|
|
258
|
+
declined: this.nDeclined,
|
|
259
|
+
},
|
|
251
260
|
elapsed_seconds: Math.round((Date.now() - this.startTime) / 1000),
|
|
252
261
|
tokens: {
|
|
253
262
|
prompt: this.model.totalPromptTokens,
|
|
254
263
|
completion: this.model.totalCompletionTokens,
|
|
264
|
+
cached: this.model.totalCachedTokens,
|
|
265
|
+
cache_write: this.model.totalCacheWriteTokens,
|
|
266
|
+
reasoning: this.model.totalReasoningTokens,
|
|
255
267
|
total: this.model.totalPromptTokens + this.model.totalCompletionTokens,
|
|
256
268
|
last_context: this.lastContextTokens,
|
|
257
269
|
},
|
|
@@ -282,8 +294,25 @@ export class Agent {
|
|
|
282
294
|
this.task = data?.info?.task ?? this.task;
|
|
283
295
|
this.mode = data?.info?.mode === "interactive" ? "interactive" : this.mode;
|
|
284
296
|
this.nSteps = data?.info?.n_steps ?? this.messages.filter((m) => m.role === "assistant").length;
|
|
297
|
+
this.nCommands = data?.info?.counters?.commands ?? 0;
|
|
298
|
+
this.nFailedCommands = data?.info?.counters?.failed_commands ?? 0;
|
|
299
|
+
this.nFormatErrors = data?.info?.counters?.format_errors ?? 0;
|
|
300
|
+
this.nDeclined = data?.info?.counters?.declined ?? 0;
|
|
285
301
|
this.lastContextTokens = data?.info?.tokens?.last_context ?? 0;
|
|
302
|
+
this.model.nCalls = data?.info?.n_calls ?? this.model.nCalls;
|
|
303
|
+
this.model.nRetries = data?.info?.n_retries ?? this.model.nRetries;
|
|
304
|
+
this.model.totalPromptTokens = data?.info?.tokens?.prompt ?? this.model.totalPromptTokens;
|
|
305
|
+
this.model.totalCompletionTokens = data?.info?.tokens?.completion ?? this.model.totalCompletionTokens;
|
|
306
|
+
this.model.totalCachedTokens = data?.info?.tokens?.cached ?? this.model.totalCachedTokens;
|
|
307
|
+
this.model.totalCacheWriteTokens = data?.info?.tokens?.cache_write ?? this.model.totalCacheWriteTokens;
|
|
308
|
+
this.model.totalReasoningTokens = data?.info?.tokens?.reasoning ?? this.model.totalReasoningTokens;
|
|
309
|
+
this.model.totalCostUsd = data?.info?.cost?.usd ?? this.model.totalCostUsd;
|
|
310
|
+
this.model.reportedCostUsd = data?.info?.cost?.reported_usd ?? this.model.reportedCostUsd;
|
|
311
|
+
this.model.estimatedCostUsd = data?.info?.cost?.estimated_usd ?? this.model.estimatedCostUsd;
|
|
312
|
+
this.model.costSource = data?.info?.cost?.source ?? this.model.costSource;
|
|
313
|
+
this.startTime = Date.now() - Number(data?.info?.elapsed_seconds || 0) * 1000;
|
|
286
314
|
this.exitStatus = data?.info?.exit_status || null;
|
|
315
|
+
this.bench = data?.bench ?? this.bench;
|
|
287
316
|
if (data?.id) this.sessionId = data.id;
|
|
288
317
|
if (data?.created) this.sessionCreated = data.created;
|
|
289
318
|
if (data?.title) this.title = data.title;
|
package/src/bench.js
CHANGED
|
@@ -26,6 +26,7 @@ import { Agent } from "./agent.js";
|
|
|
26
26
|
|
|
27
27
|
/** CSV columns, in order, for the metrics table. */
|
|
28
28
|
export const METRIC_COLUMNS = [
|
|
29
|
+
"run_path",
|
|
29
30
|
"timestamp",
|
|
30
31
|
"model",
|
|
31
32
|
"reasoning",
|
|
@@ -211,8 +212,9 @@ export function metricsHeader() {
|
|
|
211
212
|
* the top-level rolled-up index at <root>/metrics.csv.
|
|
212
213
|
*/
|
|
213
214
|
export function writeMetrics({ runDir, root, metrics }) {
|
|
215
|
+
const rowMetrics = { ...metrics, run_path: path.relative(benchRoot(root), runDir) };
|
|
214
216
|
const header = metricsHeader();
|
|
215
|
-
const row = metricsRow(
|
|
217
|
+
const row = metricsRow(rowMetrics);
|
|
216
218
|
|
|
217
219
|
// Per-run file.
|
|
218
220
|
fs.writeFileSync(path.join(runDir, "metrics.csv"), header + "\n" + row + "\n");
|
|
@@ -222,7 +224,14 @@ export function writeMetrics({ runDir, root, metrics }) {
|
|
|
222
224
|
if (!fs.existsSync(indexPath)) {
|
|
223
225
|
fs.writeFileSync(indexPath, header + "\n" + row + "\n");
|
|
224
226
|
} else {
|
|
225
|
-
fs.
|
|
227
|
+
const lines = fs.readFileSync(indexPath, "utf8").trimEnd().split("\n");
|
|
228
|
+
const runPathIndex = lines[0].split(",").indexOf("run_path");
|
|
229
|
+
const existing = runPathIndex < 0
|
|
230
|
+
? -1
|
|
231
|
+
: lines.findIndex((line, index) => index > 0 && line.split(",")[runPathIndex] === rowMetrics.run_path);
|
|
232
|
+
if (existing >= 0) lines[existing] = row;
|
|
233
|
+
else lines.push(row);
|
|
234
|
+
fs.writeFileSync(indexPath, lines.join("\n") + "\n");
|
|
226
235
|
}
|
|
227
236
|
return { runMetrics: path.join(runDir, "metrics.csv"), index: indexPath };
|
|
228
237
|
}
|
|
@@ -282,7 +291,7 @@ export async function runCell({
|
|
|
282
291
|
taskFile,
|
|
283
292
|
projectMetadata,
|
|
284
293
|
root,
|
|
285
|
-
steps =
|
|
294
|
+
steps = 0,
|
|
286
295
|
wall = 0,
|
|
287
296
|
timeout = 60,
|
|
288
297
|
maxOutputChars = 16000,
|
package/src/cli.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* astra -m <model> -f path/to/instruction.md
|
|
18
18
|
* astra -m <model> -p tasks/dummy-slugify bench run from a task directory
|
|
19
19
|
* astra bench --project ./task project-configured bench run
|
|
20
|
+
* astra bench --resume <session-id> continue the same bench attempt
|
|
20
21
|
* astra --resume <session-id> resume a saved session
|
|
21
22
|
* astra --sessions list saved sessions
|
|
22
23
|
*
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
* --bench-root <dir> Root folder for bench runs (default: ./bench)
|
|
46
47
|
* -C, --cwd <path> Working directory for commands (default: cwd)
|
|
47
48
|
* -o, --output <path> Also write trajectory JSON here (bench mode)
|
|
48
|
-
* -s, --steps <n>
|
|
49
|
+
* -s, --steps <n> Turn limit (unset = no limit)
|
|
49
50
|
* -w, --wall <seconds> Wall-clock limit (default: 0 = none)
|
|
50
51
|
* --timeout <seconds> Per-command timeout (default: 60)
|
|
51
52
|
* --max-output <n> Max chars of command output kept (default: 16000)
|
|
@@ -55,7 +56,7 @@
|
|
|
55
56
|
* --sessions List saved sessions and exit
|
|
56
57
|
* -y, --yolo Auto-run commands without confirmation
|
|
57
58
|
* (always on in bench mode)
|
|
58
|
-
* -q, --quiet Do not stream
|
|
59
|
+
* -q, --quiet Do not stream turns (bench mode)
|
|
59
60
|
* -h, --help Show this help
|
|
60
61
|
*
|
|
61
62
|
* API key resolution (first hit wins):
|
|
@@ -113,7 +114,7 @@ function reasoningKwargs(level) {
|
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
function parseArgs(argv) {
|
|
116
|
-
const args = { steps:
|
|
117
|
+
const args = { steps: 0, wall: 0, timeout: 60, quiet: false, "max-output": 16000, _provided: new Set() };
|
|
117
118
|
const alias = {
|
|
118
119
|
"-m": "model", "--model": "model",
|
|
119
120
|
"-t": "task", "--task": "task",
|
|
@@ -198,6 +199,19 @@ async function main() {
|
|
|
198
199
|
// Bench mode uses the autonomous engine; agent mode uses the interactive one.
|
|
199
200
|
let task = args.task;
|
|
200
201
|
if (args["task-file"]) task = fs.readFileSync(args["task-file"], "utf8");
|
|
202
|
+
let resumeBench = resumeDoc?.bench ?? null;
|
|
203
|
+
if (args.bench && args.resume && !resumeBench) {
|
|
204
|
+
console.error("\x1b[31m[astra] request_error: this session is not a bench run.\x1b[0m");
|
|
205
|
+
process.exit(2);
|
|
206
|
+
}
|
|
207
|
+
if (resumeBench) {
|
|
208
|
+
if (!args._provided.has("task") && !args._provided.has("task-file")) task = resumeBench.task;
|
|
209
|
+
if (!args._provided.has("path")) args.path = resumeBench.taskPath;
|
|
210
|
+
if (!args._provided.has("bench-root")) args["bench-root"] = resumeBench.root;
|
|
211
|
+
if (!args._provided.has("steps")) args.steps = resumeBench.steps;
|
|
212
|
+
if (!args._provided.has("wall")) args.wall = resumeBench.wall;
|
|
213
|
+
if (!args._provided.has("timeout")) args.timeout = resumeBench.timeout;
|
|
214
|
+
}
|
|
201
215
|
let project = null;
|
|
202
216
|
if (args.project) {
|
|
203
217
|
try {
|
|
@@ -261,7 +275,7 @@ async function main() {
|
|
|
261
275
|
// 5. hard fallback: glm-5.2 with medium reasoning
|
|
262
276
|
// The chosen values are cached so the next `astra` needs no flags.
|
|
263
277
|
let modelId = args.model || resumeDoc?.info?.model;
|
|
264
|
-
let reasoning = args.reasoning || "";
|
|
278
|
+
let reasoning = args.reasoning || resumeBench?.reasoning || "";
|
|
265
279
|
|
|
266
280
|
if (mode === "autonomous") {
|
|
267
281
|
if (project && !modelId) modelId = project.bench.models.join(",");
|
|
@@ -330,7 +344,21 @@ async function main() {
|
|
|
330
344
|
// workspace under bench/<model-name-reasoning>/run-NN/. The agent's commands
|
|
331
345
|
// run inside that workspace and metrics are recorded when it finishes.
|
|
332
346
|
let benchRun = null;
|
|
333
|
-
if (mode === "autonomous" &&
|
|
347
|
+
if (mode === "autonomous" && resumeBench) {
|
|
348
|
+
if (!fs.existsSync(resumeBench.dir) || !fs.existsSync(resumeBench.workspace)) {
|
|
349
|
+
console.error("\x1b[31m[astra] request_error: saved bench workspace is missing.\x1b[0m");
|
|
350
|
+
process.exit(2);
|
|
351
|
+
}
|
|
352
|
+
benchRun = {
|
|
353
|
+
dir: resumeBench.dir,
|
|
354
|
+
workspace: resumeBench.workspace,
|
|
355
|
+
root: resumeBench.root,
|
|
356
|
+
slug: path.basename(path.dirname(resumeBench.dir)),
|
|
357
|
+
runId: path.basename(resumeBench.dir),
|
|
358
|
+
};
|
|
359
|
+
resumeBench.segments = Array.isArray(resumeBench.segments) ? resumeBench.segments : [];
|
|
360
|
+
resumeBench.segments.push({ started_at: new Date().toISOString(), resumed: true });
|
|
361
|
+
} else if (mode === "autonomous" && !resumeDoc) {
|
|
334
362
|
benchRun = allocateRun({ model: modelId, reasoning, root: args["bench-root"] });
|
|
335
363
|
const seeded = seedWorkspace(benchRun.workspace, {
|
|
336
364
|
taskPath: args.path,
|
|
@@ -341,9 +369,22 @@ async function main() {
|
|
|
341
369
|
// Persist the task text alongside the run for reproducibility.
|
|
342
370
|
fs.writeFileSync(path.join(benchRun.dir, "task.md"), task || "");
|
|
343
371
|
writeProjectMetadata(benchRun.dir, project?.metadata);
|
|
372
|
+
resumeBench = {
|
|
373
|
+
dir: benchRun.dir,
|
|
374
|
+
workspace: benchRun.workspace,
|
|
375
|
+
root: benchRun.root,
|
|
376
|
+
task,
|
|
377
|
+
taskPath: args.path,
|
|
378
|
+
model: modelId,
|
|
379
|
+
reasoning,
|
|
380
|
+
steps: Number(args.steps),
|
|
381
|
+
wall: Number(args.wall),
|
|
382
|
+
timeout: Number(args.timeout),
|
|
383
|
+
segments: [{ started_at: new Date().toISOString(), resumed: false }],
|
|
384
|
+
};
|
|
344
385
|
}
|
|
345
386
|
|
|
346
|
-
const cmdCwd = benchRun
|
|
387
|
+
const cmdCwd = benchRun?.workspace || cwd;
|
|
347
388
|
const env = new LocalEnvironment({
|
|
348
389
|
cwd: cmdCwd,
|
|
349
390
|
timeout: Number(args.timeout),
|
|
@@ -362,12 +403,23 @@ async function main() {
|
|
|
362
403
|
: null,
|
|
363
404
|
sessionId,
|
|
364
405
|
saveSession,
|
|
406
|
+
bench: resumeBench,
|
|
365
407
|
onEvent: quiet || mode === "interactive" ? () => {} : (msg) => printEvent(msg),
|
|
366
408
|
onStep: quiet || mode === "interactive" ? () => {} : (s) => printStep(s),
|
|
367
409
|
});
|
|
368
410
|
|
|
369
411
|
if (resumeDoc) {
|
|
370
412
|
agent.restore(resumeDoc);
|
|
413
|
+
if (resumeBench) {
|
|
414
|
+
agent.exitStatus = null;
|
|
415
|
+
if (!args._provided.has("steps") && Number(resumeBench.steps) > 0) {
|
|
416
|
+
agent.stepLimit = agent.nSteps + Number(resumeBench.steps);
|
|
417
|
+
}
|
|
418
|
+
if (!args._provided.has("wall") && Number(resumeBench.wall) > 0) {
|
|
419
|
+
agent.wallTimeLimitSeconds =
|
|
420
|
+
Math.ceil((Date.now() - agent.startTime) / 1000) + Number(resumeBench.wall);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
371
423
|
console.error(`\x1b[2m[astra] resumed ${sessionId} (${agent.messages.length} messages)\x1b[0m`);
|
|
372
424
|
}
|
|
373
425
|
|
|
@@ -419,20 +471,51 @@ async function main() {
|
|
|
419
471
|
if (benchRun) {
|
|
420
472
|
console.error(
|
|
421
473
|
`\x1b[2m[astra] bench · ${benchRun.slug}/${benchRun.runId} ` +
|
|
422
|
-
`· model=${modelId}${reasoning ? ` reasoning=${reasoning}` : ""}
|
|
474
|
+
`· model=${modelId}${reasoning ? ` reasoning=${reasoning}` : ""}` +
|
|
475
|
+
`${Number(args.steps) > 0 ? ` turns<=${args.steps}` : ""}\x1b[0m`
|
|
423
476
|
);
|
|
424
477
|
console.error(`\x1b[2m[astra] workspace -> ${benchRun.workspace}\x1b[0m`);
|
|
478
|
+
console.error(`\x1b[2m[astra] session -> ${sessionId}\x1b[0m`);
|
|
425
479
|
} else {
|
|
426
|
-
console.error(
|
|
480
|
+
console.error(
|
|
481
|
+
`\x1b[2m[astra] bench · model=${modelId} cwd=${cmdCwd}` +
|
|
482
|
+
`${Number(args.steps) > 0 ? ` turns<=${args.steps}` : ""}\x1b[0m`
|
|
483
|
+
);
|
|
427
484
|
}
|
|
428
485
|
}
|
|
429
486
|
|
|
430
|
-
|
|
431
|
-
|
|
487
|
+
let result;
|
|
488
|
+
try {
|
|
489
|
+
result = resumeDoc ? await continueAutonomous(agent) : await agent.run(task);
|
|
490
|
+
} catch (error) {
|
|
491
|
+
agent.exitStatus = "Error";
|
|
492
|
+
const segment = agent.bench?.segments?.at(-1);
|
|
493
|
+
if (segment) {
|
|
494
|
+
segment.finished_at = new Date().toISOString();
|
|
495
|
+
segment.exit_status = "Error";
|
|
496
|
+
segment.error = String(error?.message || error);
|
|
497
|
+
}
|
|
498
|
+
agent.save();
|
|
499
|
+
if (benchRun) {
|
|
500
|
+
writeMetrics({
|
|
501
|
+
runDir: benchRun.dir,
|
|
502
|
+
root: benchRun.root,
|
|
503
|
+
metrics: collectMetrics({ agent, model, reasoning }),
|
|
504
|
+
});
|
|
505
|
+
try { refreshReport(benchRun.root); } catch {}
|
|
506
|
+
}
|
|
507
|
+
throw error;
|
|
508
|
+
}
|
|
432
509
|
agent.save();
|
|
433
510
|
|
|
434
511
|
// Record benchmark metrics (per-run CSV + rolled-up index).
|
|
435
512
|
if (benchRun) {
|
|
513
|
+
const segment = agent.bench?.segments?.at(-1);
|
|
514
|
+
if (segment) {
|
|
515
|
+
segment.finished_at = new Date().toISOString();
|
|
516
|
+
segment.exit_status = result.exit_status;
|
|
517
|
+
}
|
|
518
|
+
agent.save();
|
|
436
519
|
const metrics = collectMetrics({ agent, model, reasoning });
|
|
437
520
|
const paths = writeMetrics({ runDir: benchRun.dir, root: benchRun.root, metrics });
|
|
438
521
|
if (!quiet) {
|
|
@@ -451,7 +534,7 @@ async function main() {
|
|
|
451
534
|
const pt = model.totalPromptTokens;
|
|
452
535
|
const ct = model.totalCompletionTokens;
|
|
453
536
|
console.error(
|
|
454
|
-
`\n\x1b[1m[astra] exit=${result.exit_status}
|
|
537
|
+
`\n\x1b[1m[astra] exit=${result.exit_status} turns=${agent.nSteps}\x1b[0m`
|
|
455
538
|
);
|
|
456
539
|
console.error(
|
|
457
540
|
`\x1b[1m[astra] tokens: prompt=${fmt(pt)} completion=${fmt(ct)} total=${fmt(pt + ct)} ` +
|
|
@@ -576,7 +659,7 @@ async function runBenchMatrix({ args, apiKey, models, reasonings, repeat, task,
|
|
|
576
659
|
const status = r.error ? `model_error: ${r.error.split("\n")[0]}` : r.status;
|
|
577
660
|
console.error(
|
|
578
661
|
` ${mark} ${String(r.model).padEnd(20)} ${String(r.reasoning).padEnd(8)} ` +
|
|
579
|
-
`${String(status).padEnd(16)} ${String(r.steps).padStart(3)}
|
|
662
|
+
`${String(status).padEnd(16)} ${String(r.steps).padStart(3)} turns · ` +
|
|
580
663
|
`${fmt(r.total_tokens)} tok · ${fmtUsd(r.cost_usd)}` +
|
|
581
664
|
`${r.cost_source === "estimated" ? "~" : ""}`
|
|
582
665
|
);
|
|
@@ -620,7 +703,7 @@ function printLeaderboard(board) {
|
|
|
620
703
|
board.sort((a, b) => b.completion_rate - a.completion_rate || a.avg_steps - b.avg_steps);
|
|
621
704
|
console.error(
|
|
622
705
|
`\n\x1b[1m${"model".padEnd(20)} ${"reason".padEnd(8)} ${"complete".padEnd(8)} ` +
|
|
623
|
-
`${"
|
|
706
|
+
`${"turns".padStart(6)} ${"tokens".padStart(9)} ${"cost".padStart(9)} source\x1b[0m`
|
|
624
707
|
);
|
|
625
708
|
for (const g of board) {
|
|
626
709
|
const completed = `${g.completed}/${g.runs}`;
|
|
@@ -665,23 +748,22 @@ function printSessions() {
|
|
|
665
748
|
|
|
666
749
|
function printEvent(msg) {
|
|
667
750
|
// Bench mode keeps output compact: skip the verbose system/user/assistant
|
|
668
|
-
// message bodies (per-
|
|
751
|
+
// message bodies (per-turn progress is shown by printStep). Only surface a
|
|
669
752
|
// non-empty terminal exit event so the run's submission is still visible.
|
|
670
753
|
if (msg.role !== "exit" || !msg.content?.trim()) return;
|
|
671
754
|
console.error(`\x1b[35m--- SUBMISSION ---\x1b[0m\n${msg.content}\n`);
|
|
672
755
|
}
|
|
673
756
|
|
|
674
|
-
/** Compact per-
|
|
757
|
+
/** Compact per-turn status line with exact token usage from the API. */
|
|
675
758
|
function printStep(s) {
|
|
676
|
-
const limit = s.stepLimit > 0 ? `/${s.stepLimit}` : "";
|
|
677
759
|
const cached = s.usage.cached_tokens ? ` (cached ${fmt(s.usage.cached_tokens)})` : "";
|
|
678
760
|
const cost = s.costUsd != null
|
|
679
|
-
? `
|
|
761
|
+
? ` │ ${fmtUsd(s.costUsd)}${s.costKind === "estimated" ? "~" : ""}`
|
|
680
762
|
: "";
|
|
681
763
|
console.error(
|
|
682
|
-
`\x1b[36m[astra]
|
|
683
|
-
`↑${fmt(s.usage.prompt_tokens)} ↓${fmt(s.usage.completion_tokens)} tok
|
|
684
|
-
`ctx ${fmt(s.contextTokens)}${cached}${cost}
|
|
764
|
+
`\x1b[36m[astra] turn ${s.step} │ ` +
|
|
765
|
+
`↑${fmt(s.usage.prompt_tokens)} ↓${fmt(s.usage.completion_tokens)} tok │ ` +
|
|
766
|
+
`ctx ${fmt(s.contextTokens)}${cached}${cost} │ ${s.elapsedSeconds.toFixed(1)}s\x1b[0m`
|
|
685
767
|
);
|
|
686
768
|
}
|
|
687
769
|
|
package/src/project.js
CHANGED
|
@@ -14,7 +14,7 @@ const DEFAULT_BENCH = {
|
|
|
14
14
|
models: [],
|
|
15
15
|
reasoning: [""],
|
|
16
16
|
repeat: 1,
|
|
17
|
-
steps:
|
|
17
|
+
steps: 0,
|
|
18
18
|
wall_seconds: 0,
|
|
19
19
|
command_timeout_seconds: 60,
|
|
20
20
|
};
|
|
@@ -169,7 +169,7 @@ export function loadProject(projectPath) {
|
|
|
169
169
|
models: stringArray(bench.models, "bench.models", DEFAULT_BENCH.models),
|
|
170
170
|
reasoning: stringArray(bench.reasoning, "bench.reasoning", DEFAULT_BENCH.reasoning),
|
|
171
171
|
repeat: positiveInteger(bench.repeat, "bench.repeat", DEFAULT_BENCH.repeat),
|
|
172
|
-
steps: positiveInteger(bench.steps, "bench.steps", DEFAULT_BENCH.steps),
|
|
172
|
+
steps: positiveInteger(bench.steps, "bench.steps", DEFAULT_BENCH.steps, { allowZero: true }),
|
|
173
173
|
wall: positiveInteger(bench.wall_seconds, "bench.wall_seconds", DEFAULT_BENCH.wall_seconds, { allowZero: true }),
|
|
174
174
|
timeout: positiveInteger(bench.command_timeout_seconds, "bench.command_timeout_seconds", DEFAULT_BENCH.command_timeout_seconds),
|
|
175
175
|
},
|
package/src/report.js
CHANGED
|
@@ -296,6 +296,7 @@ function readRunDir({ rootDir, slug, runId, dir }) {
|
|
|
296
296
|
tokens,
|
|
297
297
|
cost_usd: costSource ? num(metricsRow?.cost_usd ?? traj?.info?.cost?.usd) : null,
|
|
298
298
|
cost_source: costSource || "",
|
|
299
|
+
resume_segments: Array.isArray(traj?.bench?.segments) ? traj.bench.segments : [],
|
|
299
300
|
timeline,
|
|
300
301
|
paths: {
|
|
301
302
|
dir: path.relative(rootDir, dir),
|