@axlsdk/eval 0.18.0 → 0.18.1
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 +80 -13
- package/dist/{chunk-BWBX5YVQ.js → chunk-66EYSYQS.js} +29 -11
- package/dist/chunk-66EYSYQS.js.map +1 -0
- package/dist/cli.cjs +56 -13
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +31 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +29 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -2
- package/dist/index.d.ts +79 -2
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-BWBX5YVQ.js.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -111,12 +111,14 @@ function round2(n) {
|
|
|
111
111
|
function scorerCounts(items, name) {
|
|
112
112
|
let scored = 0;
|
|
113
113
|
let failed = 0;
|
|
114
|
+
let skipped = 0;
|
|
114
115
|
for (const i of items) {
|
|
115
116
|
if (i.error) continue;
|
|
116
117
|
if (i.scores[name] != null) scored++;
|
|
118
|
+
else if (i.scoreDetails?.[name]?.skipped === true) skipped++;
|
|
117
119
|
else if (i.scoreDetails?.[name]?.duration != null) failed++;
|
|
118
120
|
}
|
|
119
|
-
return { scored, failed };
|
|
121
|
+
return { scored, failed, skipped };
|
|
120
122
|
}
|
|
121
123
|
function evaluateScorerTolerance(scored, failed, type, limit) {
|
|
122
124
|
const attempted = scored + failed;
|
|
@@ -215,17 +217,21 @@ function evalCompare(baseline, candidate, options) {
|
|
|
215
217
|
const threshold = resolveThreshold(name, options, baselineRef.metadata);
|
|
216
218
|
let baselineScored = 0;
|
|
217
219
|
let baselineFailed = 0;
|
|
220
|
+
let baselineSkipped = 0;
|
|
218
221
|
for (const run of baselineRuns) {
|
|
219
222
|
const c = scorerCounts(run.items, name);
|
|
220
223
|
baselineScored += c.scored;
|
|
221
224
|
baselineFailed += c.failed;
|
|
225
|
+
baselineSkipped += c.skipped;
|
|
222
226
|
}
|
|
223
227
|
let candidateScored = 0;
|
|
224
228
|
let candidateFailed = 0;
|
|
229
|
+
let candidateSkipped = 0;
|
|
225
230
|
for (const run of candidateRuns) {
|
|
226
231
|
const c = scorerCounts(run.items, name);
|
|
227
232
|
candidateScored += c.scored;
|
|
228
233
|
candidateFailed += c.failed;
|
|
234
|
+
candidateSkipped += c.skipped;
|
|
229
235
|
}
|
|
230
236
|
const entry = {
|
|
231
237
|
baselineMean: bMean,
|
|
@@ -235,15 +241,17 @@ function evalCompare(baseline, candidate, options) {
|
|
|
235
241
|
baselineScored,
|
|
236
242
|
baselineFailed,
|
|
237
243
|
candidateScored,
|
|
238
|
-
candidateFailed
|
|
244
|
+
candidateFailed,
|
|
245
|
+
baselineSkipped,
|
|
246
|
+
candidateSkipped
|
|
239
247
|
};
|
|
240
248
|
const diffs = pairedDiffs[name];
|
|
249
|
+
entry.n = diffs.length;
|
|
241
250
|
if (diffs.length >= 2) {
|
|
242
251
|
const ci = pairedBootstrapCI(diffs);
|
|
243
252
|
entry.ci = { lower: ci.lower, upper: ci.upper };
|
|
244
253
|
entry.pRegression = ci.pRegression;
|
|
245
254
|
entry.pImprovement = ci.pImprovement;
|
|
246
|
-
entry.n = diffs.length;
|
|
247
255
|
const ciExcludesZero = ci.lower > 0 || ci.upper < 0;
|
|
248
256
|
entry.significant = ciExcludesZero && Math.abs(ci.mean) >= threshold;
|
|
249
257
|
}
|
|
@@ -380,9 +388,11 @@ function evaluateScorerErrorRateGate(comparison, maxScorerErrorRate) {
|
|
|
380
388
|
for (const side of ["baseline", "candidate"]) {
|
|
381
389
|
const scored = (side === "baseline" ? s.baselineScored : s.candidateScored) ?? 0;
|
|
382
390
|
const failed = (side === "baseline" ? s.baselineFailed : s.candidateFailed) ?? 0;
|
|
391
|
+
const skipped = (side === "baseline" ? s.baselineSkipped : s.candidateSkipped) ?? 0;
|
|
383
392
|
const verdict = evaluateScorerTolerance(scored, failed, type, limit);
|
|
384
393
|
if (verdict.zeroSample) {
|
|
385
|
-
|
|
394
|
+
const cause = skipped > 0 ? `every one of its items was N/A (the \`applies\` predicate skipped all ${skipped})` : `its items errored or produced no score`;
|
|
395
|
+
return `scorer "${name}" has no scored items on ${side} (${cause}) \u2014 cannot certify a mean over an empty sample.`;
|
|
386
396
|
}
|
|
387
397
|
if (verdict.exceeds) {
|
|
388
398
|
const limitStr = type === "deterministic" ? "deterministic zero-tolerance" : `${(limit * 100).toFixed(1)}%`;
|
|
@@ -415,6 +425,11 @@ async function scoreItem(item, scorers, scorerConcurrency, scorerContext, signal
|
|
|
415
425
|
if (signal?.aborted) return;
|
|
416
426
|
const scorerStart = Date.now();
|
|
417
427
|
try {
|
|
428
|
+
if (scorer.applies && !scorer.applies(item.output, item.input, item.annotations)) {
|
|
429
|
+
item.scores[scorer.name] = null;
|
|
430
|
+
scoreDetails[scorer.name] = { score: null, skipped: true };
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
418
433
|
const raw = await scorer.score(item.output, item.input, item.annotations, scorerContext);
|
|
419
434
|
const scorerResult = normalizeScorerResult(raw);
|
|
420
435
|
if (scorerResult.cost != null) {
|
|
@@ -606,8 +621,8 @@ async function runEval(config, executeWorkflow, runtime, options) {
|
|
|
606
621
|
const scorerStats = {};
|
|
607
622
|
for (const name of scorerNames) {
|
|
608
623
|
const scores = evalItems.filter((i) => !i.error && i.scores[name] != null).map((i) => i.scores[name]);
|
|
609
|
-
const { scored, failed } = scorerCounts(evalItems, name);
|
|
610
|
-
scorerStats[name] = { ...computeStats(scores), scored, failed };
|
|
624
|
+
const { scored, failed, skipped } = scorerCounts(evalItems, name);
|
|
625
|
+
scorerStats[name] = { ...computeStats(scores), scored, failed, skipped };
|
|
611
626
|
}
|
|
612
627
|
const scorerTypes = {};
|
|
613
628
|
for (const s of config.scorers) {
|
|
@@ -776,8 +791,8 @@ async function rescore(result, scorers, runtime, options) {
|
|
|
776
791
|
const scorerStats = {};
|
|
777
792
|
for (const name of scorerNames) {
|
|
778
793
|
const scores = rescored.filter((i) => !i.error && i.scores[name] != null).map((i) => i.scores[name]);
|
|
779
|
-
const { scored, failed } = scorerCounts(rescored, name);
|
|
780
|
-
scorerStats[name] = { ...computeStats(scores), scored, failed };
|
|
794
|
+
const { scored, failed, skipped } = scorerCounts(rescored, name);
|
|
795
|
+
scorerStats[name] = { ...computeStats(scores), scored, failed, skipped };
|
|
781
796
|
}
|
|
782
797
|
const scorerTypes = {};
|
|
783
798
|
for (const s of scorers) {
|
|
@@ -855,13 +870,15 @@ function aggregateRuns(runs) {
|
|
|
855
870
|
const maxMean = Math.max(...means);
|
|
856
871
|
const scored = runs.reduce((sum, r) => sum + (r.summary.scorers[name]?.scored ?? 0), 0);
|
|
857
872
|
const failed = runs.reduce((sum, r) => sum + (r.summary.scorers[name]?.failed ?? 0), 0);
|
|
873
|
+
const skipped = runs.reduce((sum, r) => sum + (r.summary.scorers[name]?.skipped ?? 0), 0);
|
|
858
874
|
scorers[name] = {
|
|
859
875
|
mean: round2(meanOfMeans),
|
|
860
876
|
std: round2(std(means, meanOfMeans)),
|
|
861
877
|
min: round2(minMean),
|
|
862
878
|
max: round2(maxMean),
|
|
863
879
|
scored,
|
|
864
|
-
failed
|
|
880
|
+
failed,
|
|
881
|
+
skipped
|
|
865
882
|
};
|
|
866
883
|
}
|
|
867
884
|
let timing;
|
|
@@ -1031,10 +1048,11 @@ function refuseToGate(reason) {
|
|
|
1031
1048
|
}
|
|
1032
1049
|
function scorerSampleCounts(entry, name, items) {
|
|
1033
1050
|
if (entry.scored != null && entry.failed != null) {
|
|
1034
|
-
|
|
1051
|
+
const skipped = entry.skipped ?? (items ? scorerCounts(items, name).skipped : 0);
|
|
1052
|
+
return { scored: entry.scored, failed: entry.failed, skipped };
|
|
1035
1053
|
}
|
|
1036
1054
|
if (items) return scorerCounts(items, name);
|
|
1037
|
-
return { scored: entry.scored ?? 0, failed: entry.failed ?? 0 };
|
|
1055
|
+
return { scored: entry.scored ?? 0, failed: entry.failed ?? 0, skipped: entry.skipped ?? 0 };
|
|
1038
1056
|
}
|
|
1039
1057
|
function installAbortHandler(controller) {
|
|
1040
1058
|
let signalled = false;
|
|
@@ -1242,6 +1260,16 @@ Compare: baseline (${comparison.baseline.id.slice(0, 8)}) -> candidate (${compar
|
|
|
1242
1260
|
`[axl-eval] WARNING: scorer "${name}" failed on some items (baseline ${bFailed}/${bAtt}, candidate ${cFailed}/${cAtt}); the means above are computed over the surviving sample.`
|
|
1243
1261
|
);
|
|
1244
1262
|
}
|
|
1263
|
+
const bSkipped = s.baselineSkipped ?? 0;
|
|
1264
|
+
const cSkipped = s.candidateSkipped ?? 0;
|
|
1265
|
+
const pairedN = s.n ?? 0;
|
|
1266
|
+
const minScored = Math.min(s.baselineScored ?? 0, s.candidateScored ?? 0);
|
|
1267
|
+
const skipsDiverge = (bSkipped > 0 || cSkipped > 0) && (bSkipped !== cSkipped || pairedN < minScored);
|
|
1268
|
+
if (skipsDiverge) {
|
|
1269
|
+
console.error(
|
|
1270
|
+
`[axl-eval] NOTE: scorer "${name}" applies to different subsets per side (baseline ${bSkipped} N/A, candidate ${cSkipped} N/A; ${pairedN} item(s) scored on both); the two means cover different applicable subsets, so compare the delta with care.`
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1245
1273
|
}
|
|
1246
1274
|
if (sawThinnedSample && maxScorerErrorRate == null) {
|
|
1247
1275
|
console.error(
|
|
@@ -1352,8 +1380,11 @@ function formatTable(result) {
|
|
|
1352
1380
|
lines.push(" " + "\u2500".repeat(ruleLen));
|
|
1353
1381
|
for (const name of scorerNames) {
|
|
1354
1382
|
const s = result.summary.scorers[name];
|
|
1355
|
-
const { scored, failed } = scorerSampleCounts(s, name, result.items);
|
|
1356
|
-
const
|
|
1383
|
+
const { scored, failed, skipped } = scorerSampleCounts(s, name, result.items);
|
|
1384
|
+
const annotationParts = [];
|
|
1385
|
+
if (failed > 0) annotationParts.push(`${scored}/${scored + failed} scored, ${failed} failed`);
|
|
1386
|
+
if (skipped > 0) annotationParts.push(`${skipped} N/A`);
|
|
1387
|
+
const annotation = annotationParts.length > 0 ? ` (${annotationParts.join(", ")})` : "";
|
|
1357
1388
|
if (scored === 0) {
|
|
1358
1389
|
lines.push(
|
|
1359
1390
|
` ${name.padEnd(maxNameLen)} ${"--".padStart(colWidth)} ${"--".padStart(colWidth)} ${"--".padStart(colWidth)} ${"--".padStart(colWidth)} ${"--".padStart(colWidth)}${annotation}`
|
|
@@ -1437,6 +1468,16 @@ function reportDegraded(result, label) {
|
|
|
1437
1468
|
}
|
|
1438
1469
|
return true;
|
|
1439
1470
|
}
|
|
1471
|
+
function reportFullySkippedScorers(result, label) {
|
|
1472
|
+
for (const [name, entry] of Object.entries(result.summary.scorers)) {
|
|
1473
|
+
const { scored, failed, skipped } = scorerSampleCounts(entry, name, result.items);
|
|
1474
|
+
if (skipped > 0 && scored + failed === 0) {
|
|
1475
|
+
console.error(
|
|
1476
|
+
`[axl-eval] NOTE: ${label} scorer "${name}" was not applicable to any item (${skipped} N/A, 0 scored) \u2014 its mean is not meaningful. Check the scorer's \`applies\` predicate and dataset if you expected it to run.`
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1440
1481
|
function reportTotalWipeout(result, label) {
|
|
1441
1482
|
const { count, failures } = result.summary;
|
|
1442
1483
|
if (count === 0 || failures < count) return false;
|
|
@@ -1674,6 +1715,7 @@ async function runEvalCommand(args, signal) {
|
|
|
1674
1715
|
for (const r of runResults) {
|
|
1675
1716
|
const wipeout = reportTotalWipeout(r, filePath);
|
|
1676
1717
|
const degraded = reportDegraded(r, filePath);
|
|
1718
|
+
reportFullySkippedScorers(r, filePath);
|
|
1677
1719
|
if (wipeout || degraded) anyFailing = true;
|
|
1678
1720
|
}
|
|
1679
1721
|
if (anyFailing && !partial) failedFiles++;
|
|
@@ -1684,6 +1726,7 @@ async function runEvalCommand(args, signal) {
|
|
|
1684
1726
|
console.log("\n" + formatTable(result) + "\n");
|
|
1685
1727
|
const wipeout = reportTotalWipeout(result, filePath);
|
|
1686
1728
|
const degraded = reportDegraded(result, filePath);
|
|
1729
|
+
reportFullySkippedScorers(result, filePath);
|
|
1687
1730
|
if (wipeout || degraded) failedFiles++;
|
|
1688
1731
|
}
|
|
1689
1732
|
} catch (err) {
|