@axlsdk/eval 0.13.8 → 0.15.0
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 +46 -2
- package/dist/{chunk-CMWRFU3P.js → chunk-CV6VE5VT.js} +137 -15
- package/dist/chunk-CV6VE5VT.js.map +1 -0
- package/dist/cli.cjs +160 -21
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +25 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +136 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -5
- package/dist/index.d.ts +72 -5
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-CMWRFU3P.js.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -311,7 +311,36 @@ function parseCost(cost) {
|
|
|
311
311
|
if (!match) throw new Error(`Invalid cost format: "${cost}"`);
|
|
312
312
|
return parseFloat(match[1]);
|
|
313
313
|
}
|
|
314
|
-
|
|
314
|
+
function extractUserCost(result, label = "executeWorkflow") {
|
|
315
|
+
if (result === null || typeof result !== "object") return void 0;
|
|
316
|
+
const raw = result.cost;
|
|
317
|
+
if (raw === void 0) return void 0;
|
|
318
|
+
if (typeof raw === "number" && Number.isFinite(raw) && raw >= 0) return raw;
|
|
319
|
+
console.warn(
|
|
320
|
+
`[axl-eval] Ignoring invalid \`cost\` from ${label} return: expected non-negative finite number, got ${typeof raw === "number" ? String(raw) : typeof raw}. Falling back to tracked cost (or undefined).`
|
|
321
|
+
);
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
function extractUserMetadata(result, label = "executeWorkflow") {
|
|
325
|
+
if (result === null || typeof result !== "object") return void 0;
|
|
326
|
+
const meta = result.metadata;
|
|
327
|
+
if (meta === void 0) return void 0;
|
|
328
|
+
if (meta === null || typeof meta !== "object" || Array.isArray(meta)) {
|
|
329
|
+
console.warn(
|
|
330
|
+
`[axl-eval] Ignoring invalid \`metadata\` from ${label} return: expected plain object, got ${meta === null ? "null" : Array.isArray(meta) ? "array" : typeof meta}.`
|
|
331
|
+
);
|
|
332
|
+
return void 0;
|
|
333
|
+
}
|
|
334
|
+
const proto = Object.getPrototypeOf(meta);
|
|
335
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
336
|
+
console.warn(
|
|
337
|
+
`[axl-eval] Ignoring invalid \`metadata\` from ${label} return: expected plain object, got ${proto?.constructor?.name ?? "exotic object"}.`
|
|
338
|
+
);
|
|
339
|
+
return void 0;
|
|
340
|
+
}
|
|
341
|
+
return meta;
|
|
342
|
+
}
|
|
343
|
+
async function runEval(config, executeWorkflow, runtime, options) {
|
|
315
344
|
const startTime = Date.now();
|
|
316
345
|
const id = (0, import_node_crypto.randomUUID)();
|
|
317
346
|
const items = await config.dataset.getItems();
|
|
@@ -331,6 +360,17 @@ async function runEval(config, executeWorkflow, runtime) {
|
|
|
331
360
|
let totalCost = 0;
|
|
332
361
|
let budgetExceeded = false;
|
|
333
362
|
async function processItem(item, itemIndex) {
|
|
363
|
+
if (options?.signal?.aborted) {
|
|
364
|
+
evalItems[itemIndex] = {
|
|
365
|
+
input: item.input,
|
|
366
|
+
annotations: item.annotations,
|
|
367
|
+
output: null,
|
|
368
|
+
error: "Cancelled",
|
|
369
|
+
scores: {}
|
|
370
|
+
};
|
|
371
|
+
options?.onProgress?.({ type: "item_done", itemIndex, totalItems: items.length });
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
334
374
|
if (budgetExceeded) {
|
|
335
375
|
evalItems[itemIndex] = {
|
|
336
376
|
input: item.input,
|
|
@@ -339,6 +379,7 @@ async function runEval(config, executeWorkflow, runtime) {
|
|
|
339
379
|
error: "Budget exceeded",
|
|
340
380
|
scores: {}
|
|
341
381
|
};
|
|
382
|
+
options?.onProgress?.({ type: "item_done", itemIndex, totalItems: items.length });
|
|
342
383
|
return;
|
|
343
384
|
}
|
|
344
385
|
const evalItem = {
|
|
@@ -349,18 +390,43 @@ async function runEval(config, executeWorkflow, runtime) {
|
|
|
349
390
|
};
|
|
350
391
|
const itemStart = Date.now();
|
|
351
392
|
try {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
393
|
+
if (options?.captureTraces) {
|
|
394
|
+
const tracked = await runtime.trackExecution(
|
|
395
|
+
async () => executeWorkflow(item.input, runtime),
|
|
396
|
+
{ captureTraces: true }
|
|
397
|
+
);
|
|
398
|
+
evalItem.duration = Date.now() - itemStart;
|
|
399
|
+
evalItem.output = tracked.result.output;
|
|
400
|
+
evalItem.cost = extractUserCost(tracked.result) ?? tracked.cost;
|
|
401
|
+
evalItem.metadata = extractUserMetadata(tracked.result) ?? tracked.metadata;
|
|
402
|
+
if (tracked.traces && tracked.traces.length > 0) {
|
|
403
|
+
evalItem.traces = tracked.traces;
|
|
404
|
+
}
|
|
405
|
+
if (evalItem.cost != null) {
|
|
406
|
+
totalCost += evalItem.cost;
|
|
407
|
+
}
|
|
408
|
+
} else {
|
|
409
|
+
const result = await executeWorkflow(item.input, runtime);
|
|
410
|
+
evalItem.duration = Date.now() - itemStart;
|
|
411
|
+
evalItem.output = result.output;
|
|
412
|
+
evalItem.cost = extractUserCost(result);
|
|
413
|
+
const meta = extractUserMetadata(result);
|
|
414
|
+
if (meta) evalItem.metadata = meta;
|
|
415
|
+
if (evalItem.cost != null) {
|
|
416
|
+
totalCost += evalItem.cost;
|
|
417
|
+
}
|
|
359
418
|
}
|
|
360
419
|
} catch (err) {
|
|
361
420
|
evalItem.duration = Date.now() - itemStart;
|
|
362
421
|
evalItem.error = err instanceof Error ? err.message : String(err);
|
|
422
|
+
if (options?.captureTraces) {
|
|
423
|
+
const captured = err.axlCapturedTraces;
|
|
424
|
+
if (Array.isArray(captured) && captured.length > 0) {
|
|
425
|
+
evalItem.traces = captured;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
363
428
|
evalItems[itemIndex] = evalItem;
|
|
429
|
+
options?.onProgress?.({ type: "item_done", itemIndex, totalItems: items.length });
|
|
364
430
|
return;
|
|
365
431
|
}
|
|
366
432
|
if (budgetLimit != null && totalCost > budgetLimit) {
|
|
@@ -369,6 +435,7 @@ async function runEval(config, executeWorkflow, runtime) {
|
|
|
369
435
|
evalItem.scoreDetails = {};
|
|
370
436
|
let itemScorerCost = 0;
|
|
371
437
|
for (const scorer of config.scorers) {
|
|
438
|
+
if (options?.signal?.aborted) break;
|
|
372
439
|
const scorerStart = Date.now();
|
|
373
440
|
try {
|
|
374
441
|
const raw = await scorer.score(
|
|
@@ -424,6 +491,7 @@ async function runEval(config, executeWorkflow, runtime) {
|
|
|
424
491
|
}
|
|
425
492
|
evalItem.scorerCost = itemScorerCost > 0 ? itemScorerCost : void 0;
|
|
426
493
|
evalItems[itemIndex] = evalItem;
|
|
494
|
+
options?.onProgress?.({ type: "item_done", itemIndex, totalItems: items.length });
|
|
427
495
|
}
|
|
428
496
|
let index = 0;
|
|
429
497
|
async function runNext() {
|
|
@@ -470,14 +538,42 @@ async function runEval(config, executeWorkflow, runtime) {
|
|
|
470
538
|
modelsMeta.models = sorted.map(([m]) => m);
|
|
471
539
|
modelsMeta.modelCounts = Object.fromEntries(sorted);
|
|
472
540
|
}
|
|
541
|
+
const totalWorkflowCalls = /* @__PURE__ */ new Map();
|
|
542
|
+
for (const item of evalItems) {
|
|
543
|
+
const itemCounts = item.metadata?.workflowCallCounts;
|
|
544
|
+
if (itemCounts && typeof itemCounts === "object") {
|
|
545
|
+
for (const [w, count] of Object.entries(itemCounts)) {
|
|
546
|
+
if (typeof count === "number")
|
|
547
|
+
totalWorkflowCalls.set(w, (totalWorkflowCalls.get(w) ?? 0) + count);
|
|
548
|
+
}
|
|
549
|
+
} else {
|
|
550
|
+
const itemWorkflows = item.metadata?.workflows;
|
|
551
|
+
if (Array.isArray(itemWorkflows)) {
|
|
552
|
+
for (const w of itemWorkflows) {
|
|
553
|
+
if (typeof w === "string")
|
|
554
|
+
totalWorkflowCalls.set(w, (totalWorkflowCalls.get(w) ?? 0) + 1);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
const workflowsMeta = {};
|
|
560
|
+
if (totalWorkflowCalls.size > 0) {
|
|
561
|
+
const sorted = [...totalWorkflowCalls.entries()].sort((a, b) => b[1] - a[1]);
|
|
562
|
+
workflowsMeta.workflows = sorted.map(([w]) => w);
|
|
563
|
+
workflowsMeta.workflowCounts = Object.fromEntries(sorted);
|
|
564
|
+
} else if (config.workflow) {
|
|
565
|
+
workflowsMeta.workflows = [config.workflow];
|
|
566
|
+
workflowsMeta.workflowCounts = { [config.workflow]: items.length };
|
|
567
|
+
}
|
|
568
|
+
options?.onProgress?.({ type: "run_done", totalItems: items.length, failures });
|
|
473
569
|
return {
|
|
474
570
|
id,
|
|
475
|
-
workflow: config.workflow,
|
|
476
571
|
dataset: config.dataset.name,
|
|
477
572
|
metadata: {
|
|
478
573
|
...config.metadata,
|
|
479
574
|
scorerTypes,
|
|
480
|
-
...modelsMeta
|
|
575
|
+
...modelsMeta,
|
|
576
|
+
...workflowsMeta
|
|
481
577
|
},
|
|
482
578
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
483
579
|
totalCost,
|
|
@@ -520,6 +616,7 @@ async function rescore(result, scorers, runtime, options) {
|
|
|
520
616
|
annotations: original.annotations,
|
|
521
617
|
output: original.output,
|
|
522
618
|
metadata: original.metadata,
|
|
619
|
+
traces: original.traces,
|
|
523
620
|
scores: {},
|
|
524
621
|
scoreDetails: {}
|
|
525
622
|
};
|
|
@@ -601,11 +698,23 @@ async function rescore(result, scorers, runtime, options) {
|
|
|
601
698
|
}
|
|
602
699
|
return {
|
|
603
700
|
id: (0, import_node_crypto2.randomUUID)(),
|
|
604
|
-
workflow: result.workflow,
|
|
605
701
|
dataset: result.dataset,
|
|
606
702
|
metadata: (() => {
|
|
607
703
|
const { runGroupId: _, runIndex: __, ...rest } = result.metadata;
|
|
608
|
-
|
|
704
|
+
const merged = {
|
|
705
|
+
...rest,
|
|
706
|
+
rescored: true,
|
|
707
|
+
originalId: result.id,
|
|
708
|
+
scorerTypes
|
|
709
|
+
};
|
|
710
|
+
if (!Array.isArray(merged.workflows)) {
|
|
711
|
+
const legacyWorkflow = result.workflow;
|
|
712
|
+
if (typeof legacyWorkflow === "string" && legacyWorkflow) {
|
|
713
|
+
merged.workflows = [legacyWorkflow];
|
|
714
|
+
merged.workflowCounts = { [legacyWorkflow]: result.items.length };
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return merged;
|
|
609
718
|
})(),
|
|
610
719
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
611
720
|
totalCost,
|
|
@@ -625,7 +734,20 @@ function std(values, mean) {
|
|
|
625
734
|
function aggregateRuns(runs) {
|
|
626
735
|
if (runs.length === 0) throw new Error("Cannot aggregate zero runs");
|
|
627
736
|
const runGroupId = runs[0].metadata.runGroupId ?? (0, import_node_crypto3.randomUUID)();
|
|
628
|
-
const {
|
|
737
|
+
const { dataset } = runs[0];
|
|
738
|
+
const seenWorkflows = /* @__PURE__ */ new Set();
|
|
739
|
+
const workflows = [];
|
|
740
|
+
for (const run of runs) {
|
|
741
|
+
const list = run.metadata.workflows;
|
|
742
|
+
if (Array.isArray(list)) {
|
|
743
|
+
for (const w of list) {
|
|
744
|
+
if (typeof w === "string" && !seenWorkflows.has(w)) {
|
|
745
|
+
seenWorkflows.add(w);
|
|
746
|
+
workflows.push(w);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
629
751
|
const totalCost = runs.reduce((sum, r) => sum + r.totalCost, 0);
|
|
630
752
|
const totalDuration = runs.reduce((sum, r) => sum + r.duration, 0);
|
|
631
753
|
const scorerNames = Object.keys(runs[0].summary.scorers);
|
|
@@ -654,7 +776,7 @@ function aggregateRuns(runs) {
|
|
|
654
776
|
return {
|
|
655
777
|
runGroupId,
|
|
656
778
|
runCount: runs.length,
|
|
657
|
-
|
|
779
|
+
workflows,
|
|
658
780
|
dataset,
|
|
659
781
|
totalCost: round3(totalCost),
|
|
660
782
|
totalDuration: round3(totalDuration),
|
|
@@ -733,7 +855,8 @@ var KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
733
855
|
"--conditions",
|
|
734
856
|
"--fail-on-regression",
|
|
735
857
|
"--threshold",
|
|
736
|
-
"--runs"
|
|
858
|
+
"--runs",
|
|
859
|
+
"--capture-traces"
|
|
737
860
|
]);
|
|
738
861
|
async function main() {
|
|
739
862
|
const args = process.argv.slice(2);
|
|
@@ -745,6 +868,10 @@ Usage:
|
|
|
745
868
|
axl-eval <path> --output <file> Save results to JSON
|
|
746
869
|
axl-eval <path> --config <file> Use config file for runtime
|
|
747
870
|
axl-eval <path> --conditions <list> Node.js import conditions (comma-separated)
|
|
871
|
+
axl-eval <path> --capture-traces Populate EvalItem.traces on every item
|
|
872
|
+
(success + failure). Adds memory
|
|
873
|
+
overhead proportional to dataset size
|
|
874
|
+
x turns x agents; off by default.
|
|
748
875
|
axl-eval rescore <results> <eval-file> Re-run scorers on saved outputs
|
|
749
876
|
axl-eval compare <a> <b> Compare two eval result files
|
|
750
877
|
axl-eval compare <a> <b> --threshold <v> Set regression threshold (global or per-scorer)
|
|
@@ -911,12 +1038,18 @@ function collectEvalFiles(p) {
|
|
|
911
1038
|
return [resolved];
|
|
912
1039
|
}
|
|
913
1040
|
}
|
|
1041
|
+
function formatWorkflows(workflows) {
|
|
1042
|
+
if (!Array.isArray(workflows) || workflows.length === 0) return "(unknown)";
|
|
1043
|
+
return workflows.filter((w) => typeof w === "string").join(", ");
|
|
1044
|
+
}
|
|
914
1045
|
function formatTable(result) {
|
|
915
1046
|
const lines = [];
|
|
916
1047
|
const scorerNames = Object.keys(result.summary.scorers);
|
|
917
1048
|
const maxNameLen = Math.max(...scorerNames.map((n) => n.length), "Scorer".length);
|
|
918
1049
|
const colWidth = 8;
|
|
919
|
-
lines.push(
|
|
1050
|
+
lines.push(
|
|
1051
|
+
`Eval: ${formatWorkflows(result.metadata.workflows)} x ${result.dataset} (${result.summary.count} items)`
|
|
1052
|
+
);
|
|
920
1053
|
lines.push(
|
|
921
1054
|
` ${"Scorer".padEnd(maxNameLen)} ${"Mean".padStart(colWidth)} ${"Min".padStart(colWidth)} ${"Max".padStart(colWidth)} ${"p50".padStart(colWidth)} ${"p95".padStart(colWidth)}`
|
|
922
1055
|
);
|
|
@@ -968,7 +1101,9 @@ function formatMultiRunTable(summary) {
|
|
|
968
1101
|
const scorerNames = Object.keys(summary.scorers);
|
|
969
1102
|
const maxNameLen = Math.max(...scorerNames.map((n) => n.length), "Scorer".length);
|
|
970
1103
|
const colWidth = 16;
|
|
971
|
-
lines.push(
|
|
1104
|
+
lines.push(
|
|
1105
|
+
`Eval: ${formatWorkflows(summary.workflows)} x ${summary.dataset} \u2014 ${summary.runCount} runs`
|
|
1106
|
+
);
|
|
972
1107
|
lines.push(
|
|
973
1108
|
` ${"Scorer".padEnd(maxNameLen)} ${"Mean \xB1 Std".padStart(colWidth)} ${"Min".padStart(8)} ${"Max".padStart(8)}`
|
|
974
1109
|
);
|
|
@@ -1057,6 +1192,7 @@ function parseEvalArgs(args) {
|
|
|
1057
1192
|
let configArg;
|
|
1058
1193
|
let conditions = [];
|
|
1059
1194
|
let runs = 1;
|
|
1195
|
+
let captureTraces = false;
|
|
1060
1196
|
const paths = [];
|
|
1061
1197
|
for (let i = 0; i < args.length; i++) {
|
|
1062
1198
|
const arg = args[i];
|
|
@@ -1071,6 +1207,8 @@ function parseEvalArgs(args) {
|
|
|
1071
1207
|
else if (arg === "--runs") runs = Math.max(1, parseInt(value, 10) || 1);
|
|
1072
1208
|
else
|
|
1073
1209
|
conditions = value.split(",").map((c) => c.trim()).filter(Boolean);
|
|
1210
|
+
} else if (arg === "--capture-traces") {
|
|
1211
|
+
captureTraces = true;
|
|
1074
1212
|
} else if (arg.startsWith("--")) {
|
|
1075
1213
|
if (!KNOWN_FLAGS.has(arg)) {
|
|
1076
1214
|
console.error(`Unknown flag: ${arg}`);
|
|
@@ -1080,10 +1218,10 @@ function parseEvalArgs(args) {
|
|
|
1080
1218
|
paths.push(arg);
|
|
1081
1219
|
}
|
|
1082
1220
|
}
|
|
1083
|
-
return { outputPath, configArg, conditions, runs, paths };
|
|
1221
|
+
return { outputPath, configArg, conditions, runs, captureTraces, paths };
|
|
1084
1222
|
}
|
|
1085
1223
|
async function runEvalCommand(args) {
|
|
1086
|
-
const { outputPath, configArg, conditions, runs, paths } = parseEvalArgs(args);
|
|
1224
|
+
const { outputPath, configArg, conditions, runs, captureTraces, paths } = parseEvalArgs(args);
|
|
1087
1225
|
if (paths.length === 0) {
|
|
1088
1226
|
console.error("Error: No eval file path provided");
|
|
1089
1227
|
process.exit(1);
|
|
@@ -1138,13 +1276,14 @@ async function runEvalCommand(args) {
|
|
|
1138
1276
|
);
|
|
1139
1277
|
executeWorkflow = async (input) => ({ output: input });
|
|
1140
1278
|
}
|
|
1279
|
+
const runOptions = captureTraces ? { captureTraces: true } : void 0;
|
|
1141
1280
|
if (runs > 1) {
|
|
1142
1281
|
const { randomUUID: randomUUID4 } = await import("crypto");
|
|
1143
1282
|
const runGroupId = randomUUID4();
|
|
1144
1283
|
const runResults = [];
|
|
1145
1284
|
for (let r = 0; r < runs; r++) {
|
|
1146
1285
|
console.error(`[axl-eval] Run ${r + 1}/${runs}...`);
|
|
1147
|
-
const result = await runEval(evalConfig, executeWorkflow, runtime);
|
|
1286
|
+
const result = await runEval(evalConfig, executeWorkflow, runtime, runOptions);
|
|
1148
1287
|
result.metadata.runGroupId = runGroupId;
|
|
1149
1288
|
result.metadata.runIndex = r;
|
|
1150
1289
|
runResults.push(result);
|
|
@@ -1153,7 +1292,7 @@ async function runEvalCommand(args) {
|
|
|
1153
1292
|
const summary = aggregateRuns(runResults);
|
|
1154
1293
|
console.log("\n" + formatMultiRunTable(summary) + "\n");
|
|
1155
1294
|
} else {
|
|
1156
|
-
const result = await runEval(evalConfig, executeWorkflow, runtime);
|
|
1295
|
+
const result = await runEval(evalConfig, executeWorkflow, runtime, runOptions);
|
|
1157
1296
|
results.push(result);
|
|
1158
1297
|
console.log("\n" + formatTable(result) + "\n");
|
|
1159
1298
|
}
|