@axlsdk/eval 0.17.9 → 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 +127 -19
- package/dist/{chunk-NETD4AUB.js → chunk-66EYSYQS.js} +251 -164
- package/dist/chunk-66EYSYQS.js.map +1 -0
- package/dist/cli.cjs +541 -208
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +305 -47
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +322 -176
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +254 -2
- package/dist/index.d.ts +254 -2
- package/dist/index.js +80 -16
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-NETD4AUB.js.map +0 -1
|
@@ -64,10 +64,54 @@ function xorshift32(seed) {
|
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
// src/
|
|
67
|
+
// src/utils.ts
|
|
68
|
+
function computeStats(scores) {
|
|
69
|
+
if (scores.length === 0) return { mean: 0, min: 0, max: 0, p50: 0, p95: 0 };
|
|
70
|
+
const sorted = [...scores].sort((a, b) => a - b);
|
|
71
|
+
const mean = sorted.reduce((a, b) => a + b, 0) / sorted.length;
|
|
72
|
+
const min = sorted[0];
|
|
73
|
+
const max = sorted[sorted.length - 1];
|
|
74
|
+
const p50 = sorted[Math.floor(sorted.length * 0.5)];
|
|
75
|
+
const p95 = sorted[Math.floor(sorted.length * 0.95)];
|
|
76
|
+
return { mean: round2(mean), min: round2(min), max: round2(max), p50: round2(p50), p95: round2(p95) };
|
|
77
|
+
}
|
|
68
78
|
function round2(n) {
|
|
69
79
|
return Math.round(n * 1e3) / 1e3;
|
|
70
80
|
}
|
|
81
|
+
function scorerCounts(items, name) {
|
|
82
|
+
let scored = 0;
|
|
83
|
+
let failed = 0;
|
|
84
|
+
let skipped = 0;
|
|
85
|
+
for (const i of items) {
|
|
86
|
+
if (i.error) continue;
|
|
87
|
+
if (i.scores[name] != null) scored++;
|
|
88
|
+
else if (i.scoreDetails?.[name]?.skipped === true) skipped++;
|
|
89
|
+
else if (i.scoreDetails?.[name]?.duration != null) failed++;
|
|
90
|
+
}
|
|
91
|
+
return { scored, failed, skipped };
|
|
92
|
+
}
|
|
93
|
+
function evaluateScorerTolerance(scored, failed, type, limit) {
|
|
94
|
+
const attempted = scored + failed;
|
|
95
|
+
const rate = attempted === 0 ? 0 : failed / attempted;
|
|
96
|
+
const exceeds = attempted > 0 && (type === "deterministic" ? failed > 0 : rate > limit);
|
|
97
|
+
return { attempted, rate, exceeds, zeroSample: attempted === 0 };
|
|
98
|
+
}
|
|
99
|
+
async function mapWithConcurrency(items, concurrency, task) {
|
|
100
|
+
const results = new Array(items.length);
|
|
101
|
+
const requested = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;
|
|
102
|
+
const workerCount = items.length === 0 ? 0 : Math.min(Math.max(1, requested), items.length);
|
|
103
|
+
let next = 0;
|
|
104
|
+
async function runNext() {
|
|
105
|
+
while (next < items.length) {
|
|
106
|
+
const i = next++;
|
|
107
|
+
results[i] = await task(items[i], i);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
await Promise.all(Array.from({ length: workerCount }, () => runNext()));
|
|
111
|
+
return results;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/compare.ts
|
|
71
115
|
var DEFAULT_LLM_THRESHOLD = 0.05;
|
|
72
116
|
var DEFAULT_DETERMINISTIC_THRESHOLD = 0;
|
|
73
117
|
var LEGACY_THRESHOLD = 0.1;
|
|
@@ -141,19 +185,43 @@ function evalCompare(baseline, candidate, options) {
|
|
|
141
185
|
const delta = round2(deltaRaw);
|
|
142
186
|
const deltaPercent = bMeanRaw > 0 ? round2(deltaRaw / bMeanRaw * 100) : 0;
|
|
143
187
|
const threshold = resolveThreshold(name, options, baselineRef.metadata);
|
|
188
|
+
let baselineScored = 0;
|
|
189
|
+
let baselineFailed = 0;
|
|
190
|
+
let baselineSkipped = 0;
|
|
191
|
+
for (const run of baselineRuns) {
|
|
192
|
+
const c = scorerCounts(run.items, name);
|
|
193
|
+
baselineScored += c.scored;
|
|
194
|
+
baselineFailed += c.failed;
|
|
195
|
+
baselineSkipped += c.skipped;
|
|
196
|
+
}
|
|
197
|
+
let candidateScored = 0;
|
|
198
|
+
let candidateFailed = 0;
|
|
199
|
+
let candidateSkipped = 0;
|
|
200
|
+
for (const run of candidateRuns) {
|
|
201
|
+
const c = scorerCounts(run.items, name);
|
|
202
|
+
candidateScored += c.scored;
|
|
203
|
+
candidateFailed += c.failed;
|
|
204
|
+
candidateSkipped += c.skipped;
|
|
205
|
+
}
|
|
144
206
|
const entry = {
|
|
145
207
|
baselineMean: bMean,
|
|
146
208
|
candidateMean: cMean,
|
|
147
209
|
delta,
|
|
148
|
-
deltaPercent
|
|
210
|
+
deltaPercent,
|
|
211
|
+
baselineScored,
|
|
212
|
+
baselineFailed,
|
|
213
|
+
candidateScored,
|
|
214
|
+
candidateFailed,
|
|
215
|
+
baselineSkipped,
|
|
216
|
+
candidateSkipped
|
|
149
217
|
};
|
|
150
218
|
const diffs = pairedDiffs[name];
|
|
219
|
+
entry.n = diffs.length;
|
|
151
220
|
if (diffs.length >= 2) {
|
|
152
221
|
const ci = pairedBootstrapCI(diffs);
|
|
153
222
|
entry.ci = { lower: ci.lower, upper: ci.upper };
|
|
154
223
|
entry.pRegression = ci.pRegression;
|
|
155
224
|
entry.pImprovement = ci.pImprovement;
|
|
156
|
-
entry.n = diffs.length;
|
|
157
225
|
const ciExcludesZero = ci.lower > 0 || ci.upper < 0;
|
|
158
226
|
entry.significant = ciExcludesZero && Math.abs(ci.mean) >= threshold;
|
|
159
227
|
}
|
|
@@ -281,33 +349,112 @@ function detectPartial(runs) {
|
|
|
281
349
|
if (runs.length >= attempted) return void 0;
|
|
282
350
|
return { completed: runs.length, attempted };
|
|
283
351
|
}
|
|
352
|
+
function evaluateScorerErrorRateGate(comparison, maxScorerErrorRate) {
|
|
353
|
+
const scorerTypes = comparison.baseline.metadata?.scorerTypes ?? {};
|
|
354
|
+
for (const name of Object.keys(comparison.scorers)) {
|
|
355
|
+
const s = comparison.scorers[name];
|
|
356
|
+
const type = scorerTypes[name] === "deterministic" ? "deterministic" : "llm";
|
|
357
|
+
const limit = type === "deterministic" ? 0 : maxScorerErrorRate;
|
|
358
|
+
for (const side of ["baseline", "candidate"]) {
|
|
359
|
+
const scored = (side === "baseline" ? s.baselineScored : s.candidateScored) ?? 0;
|
|
360
|
+
const failed = (side === "baseline" ? s.baselineFailed : s.candidateFailed) ?? 0;
|
|
361
|
+
const skipped = (side === "baseline" ? s.baselineSkipped : s.candidateSkipped) ?? 0;
|
|
362
|
+
const verdict = evaluateScorerTolerance(scored, failed, type, limit);
|
|
363
|
+
if (verdict.zeroSample) {
|
|
364
|
+
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`;
|
|
365
|
+
return `scorer "${name}" has no scored items on ${side} (${cause}) \u2014 cannot certify a mean over an empty sample.`;
|
|
366
|
+
}
|
|
367
|
+
if (verdict.exceeds) {
|
|
368
|
+
const limitStr = type === "deterministic" ? "deterministic zero-tolerance" : `${(limit * 100).toFixed(1)}%`;
|
|
369
|
+
return `scorer "${name}" failed ${(verdict.rate * 100).toFixed(1)}% on ${side} (${failed}/${verdict.attempted}), over the ${limitStr} limit.`;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
284
375
|
|
|
285
376
|
// src/scorer.ts
|
|
286
377
|
function normalizeScorerResult(result) {
|
|
287
378
|
return typeof result === "number" ? { score: result } : result;
|
|
288
379
|
}
|
|
380
|
+
function attachScorerErrorCost(err, cost) {
|
|
381
|
+
if (err && typeof err === "object" && typeof cost === "number" && Number.isFinite(cost) && cost >= 0) {
|
|
382
|
+
err.cost = cost;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function extractScorerErrorCost(err) {
|
|
386
|
+
const v = err && typeof err === "object" ? err.cost : void 0;
|
|
387
|
+
return typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : void 0;
|
|
388
|
+
}
|
|
289
389
|
function scorer(config) {
|
|
290
390
|
return {
|
|
291
391
|
name: config.name,
|
|
292
392
|
description: config.description,
|
|
293
393
|
isLlm: false,
|
|
294
|
-
score: config.score
|
|
394
|
+
score: config.score,
|
|
395
|
+
applies: config.applies
|
|
295
396
|
};
|
|
296
397
|
}
|
|
297
398
|
|
|
298
|
-
// src/
|
|
299
|
-
function
|
|
300
|
-
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
399
|
+
// src/score-item.ts
|
|
400
|
+
async function scoreItem(item, scorers, scorerConcurrency, scorerContext, signal) {
|
|
401
|
+
const scoreDetails = item.scoreDetails ??= {};
|
|
402
|
+
for (const s of scorers) {
|
|
403
|
+
item.scores[s.name] = null;
|
|
404
|
+
scoreDetails[s.name] = { score: null };
|
|
405
|
+
}
|
|
406
|
+
let itemScorerCost = 0;
|
|
407
|
+
const scorerErrorsByName = {};
|
|
408
|
+
await mapWithConcurrency(scorers, scorerConcurrency, async (scorer2) => {
|
|
409
|
+
if (signal?.aborted) return;
|
|
410
|
+
const scorerStart = Date.now();
|
|
411
|
+
try {
|
|
412
|
+
if (scorer2.applies && !scorer2.applies(item.output, item.input, item.annotations)) {
|
|
413
|
+
item.scores[scorer2.name] = null;
|
|
414
|
+
scoreDetails[scorer2.name] = { score: null, skipped: true };
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const raw = await scorer2.score(item.output, item.input, item.annotations, scorerContext);
|
|
418
|
+
const scorerResult = normalizeScorerResult(raw);
|
|
419
|
+
if (scorerResult.cost != null) {
|
|
420
|
+
itemScorerCost += scorerResult.cost;
|
|
421
|
+
}
|
|
422
|
+
const scorerDuration = Date.now() - scorerStart;
|
|
423
|
+
if (!Number.isFinite(scorerResult.score) || scorerResult.score < 0 || scorerResult.score > 1) {
|
|
424
|
+
scorerErrorsByName[scorer2.name] = `Scorer "${scorer2.name}" returned out-of-range score ${scorerResult.score} for input ${JSON.stringify(item.input)}`;
|
|
425
|
+
item.scores[scorer2.name] = null;
|
|
426
|
+
scoreDetails[scorer2.name] = {
|
|
427
|
+
score: null,
|
|
428
|
+
metadata: scorerResult.metadata,
|
|
429
|
+
duration: scorerDuration,
|
|
430
|
+
cost: scorerResult.cost
|
|
431
|
+
};
|
|
432
|
+
} else {
|
|
433
|
+
item.scores[scorer2.name] = round2(scorerResult.score);
|
|
434
|
+
scoreDetails[scorer2.name] = {
|
|
435
|
+
score: round2(scorerResult.score),
|
|
436
|
+
metadata: scorerResult.metadata,
|
|
437
|
+
duration: scorerDuration,
|
|
438
|
+
cost: scorerResult.cost
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
} catch (err) {
|
|
442
|
+
if (err?.name === "AbortError") return;
|
|
443
|
+
const errCost = extractScorerErrorCost(err);
|
|
444
|
+
if (errCost != null) itemScorerCost += errCost;
|
|
445
|
+
scorerErrorsByName[scorer2.name] = `Scorer "${scorer2.name}" threw: ${err instanceof Error ? err.message : String(err)}`;
|
|
446
|
+
item.scores[scorer2.name] = null;
|
|
447
|
+
scoreDetails[scorer2.name] = {
|
|
448
|
+
score: null,
|
|
449
|
+
duration: Date.now() - scorerStart,
|
|
450
|
+
cost: errCost
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
const orderedErrors = scorers.map((s) => scorerErrorsByName[s.name]).filter((e) => e != null);
|
|
455
|
+
if (orderedErrors.length > 0) item.scorerErrors = orderedErrors;
|
|
456
|
+
item.scorerCost = itemScorerCost > 0 ? itemScorerCost : void 0;
|
|
457
|
+
return itemScorerCost;
|
|
311
458
|
}
|
|
312
459
|
|
|
313
460
|
// src/runner.ts
|
|
@@ -350,7 +497,9 @@ async function runEval(config, executeWorkflow, runtime, options) {
|
|
|
350
497
|
const startTime = Date.now();
|
|
351
498
|
const id = randomUUID();
|
|
352
499
|
const items = await config.dataset.getItems();
|
|
500
|
+
const droppedAnnotationKeys = [...config.dataset.droppedAnnotationKeys ?? []];
|
|
353
501
|
const concurrency = config.concurrency ?? 5;
|
|
502
|
+
const scorerConcurrency = config.scorerConcurrency ?? 5;
|
|
354
503
|
const budgetLimit = config.budget ? parseCost(config.budget) : void 0;
|
|
355
504
|
const scorerContext = {
|
|
356
505
|
resolveProvider: (uri) => {
|
|
@@ -439,89 +588,53 @@ async function runEval(config, executeWorkflow, runtime, options) {
|
|
|
439
588
|
if (budgetLimit != null && totalCost > budgetLimit) {
|
|
440
589
|
budgetExceeded = true;
|
|
441
590
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
item.input,
|
|
451
|
-
item.annotations,
|
|
452
|
-
scorerContext
|
|
453
|
-
);
|
|
454
|
-
const scorerResult = normalizeScorerResult(raw);
|
|
455
|
-
if (scorerResult.cost != null) {
|
|
456
|
-
itemScorerCost += scorerResult.cost;
|
|
457
|
-
totalCost += scorerResult.cost;
|
|
458
|
-
}
|
|
459
|
-
const scorerDuration = Date.now() - scorerStart;
|
|
460
|
-
if (!Number.isFinite(scorerResult.score) || scorerResult.score < 0 || scorerResult.score > 1) {
|
|
461
|
-
if (!evalItem.scorerErrors) evalItem.scorerErrors = [];
|
|
462
|
-
evalItem.scorerErrors.push(
|
|
463
|
-
`Scorer "${scorer2.name}" returned out-of-range score ${scorerResult.score} for input ${JSON.stringify(item.input)}`
|
|
464
|
-
);
|
|
465
|
-
evalItem.scores[scorer2.name] = null;
|
|
466
|
-
evalItem.scoreDetails[scorer2.name] = {
|
|
467
|
-
score: null,
|
|
468
|
-
metadata: scorerResult.metadata,
|
|
469
|
-
duration: scorerDuration,
|
|
470
|
-
cost: scorerResult.cost
|
|
471
|
-
};
|
|
472
|
-
} else {
|
|
473
|
-
evalItem.scores[scorer2.name] = round3(scorerResult.score);
|
|
474
|
-
evalItem.scoreDetails[scorer2.name] = {
|
|
475
|
-
score: round3(scorerResult.score),
|
|
476
|
-
metadata: scorerResult.metadata,
|
|
477
|
-
duration: scorerDuration,
|
|
478
|
-
cost: scorerResult.cost
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
} catch (err) {
|
|
482
|
-
const errCost = typeof err?.cost === "number" ? err.cost : void 0;
|
|
483
|
-
if (errCost != null) {
|
|
484
|
-
itemScorerCost += errCost;
|
|
485
|
-
totalCost += errCost;
|
|
486
|
-
}
|
|
487
|
-
if (!evalItem.scorerErrors) evalItem.scorerErrors = [];
|
|
488
|
-
evalItem.scorerErrors.push(
|
|
489
|
-
`Scorer "${scorer2.name}" threw: ${err instanceof Error ? err.message : String(err)}`
|
|
490
|
-
);
|
|
491
|
-
evalItem.scores[scorer2.name] = null;
|
|
492
|
-
evalItem.scoreDetails[scorer2.name] = {
|
|
493
|
-
score: null,
|
|
494
|
-
duration: Date.now() - scorerStart,
|
|
495
|
-
cost: errCost
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
evalItem.scorerCost = itemScorerCost > 0 ? itemScorerCost : void 0;
|
|
591
|
+
const itemScorerCost = await scoreItem(
|
|
592
|
+
evalItem,
|
|
593
|
+
config.scorers,
|
|
594
|
+
scorerConcurrency,
|
|
595
|
+
scorerContext,
|
|
596
|
+
options?.signal
|
|
597
|
+
);
|
|
598
|
+
totalCost += itemScorerCost;
|
|
500
599
|
evalItems[itemIndex] = evalItem;
|
|
501
600
|
options?.onProgress?.({ type: "item_done", itemIndex, totalItems: items.length });
|
|
502
601
|
}
|
|
503
|
-
|
|
504
|
-
async function runNext() {
|
|
505
|
-
while (index < items.length) {
|
|
506
|
-
const currentIndex = index++;
|
|
507
|
-
await processItem(items[currentIndex], currentIndex);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => runNext());
|
|
511
|
-
await Promise.all(workers);
|
|
602
|
+
await mapWithConcurrency(items, concurrency, (item, i) => processItem(item, i));
|
|
512
603
|
const failures = evalItems.filter((i) => i.error).length;
|
|
513
604
|
const scorerNames = config.scorers.map((s) => s.name);
|
|
514
605
|
const scorerStats = {};
|
|
515
606
|
for (const name of scorerNames) {
|
|
516
607
|
const scores = evalItems.filter((i) => !i.error && i.scores[name] != null).map((i) => i.scores[name]);
|
|
517
|
-
|
|
608
|
+
const { scored, failed, skipped } = scorerCounts(evalItems, name);
|
|
609
|
+
scorerStats[name] = { ...computeStats(scores), scored, failed, skipped };
|
|
518
610
|
}
|
|
519
|
-
const durations = evalItems.filter((i) => !i.error && i.duration != null).map((i) => i.duration);
|
|
520
|
-
const timing = durations.length > 0 ? computeStats(durations) : void 0;
|
|
521
611
|
const scorerTypes = {};
|
|
522
612
|
for (const s of config.scorers) {
|
|
523
613
|
scorerTypes[s.name] = s.isLlm ? "llm" : "deterministic";
|
|
524
614
|
}
|
|
615
|
+
let degraded;
|
|
616
|
+
const rawLimit = config.failOnScorerErrorRate;
|
|
617
|
+
if (rawLimit != null) {
|
|
618
|
+
if (!Number.isFinite(rawLimit) || rawLimit < 0 || rawLimit > 1) {
|
|
619
|
+
console.warn(
|
|
620
|
+
`[axl-eval] Ignoring invalid failOnScorerErrorRate (${rawLimit}); expected a number in [0, 1].`
|
|
621
|
+
);
|
|
622
|
+
} else {
|
|
623
|
+
for (const name of scorerNames) {
|
|
624
|
+
const stats = scorerStats[name];
|
|
625
|
+
const scored = stats.scored ?? 0;
|
|
626
|
+
const failed = stats.failed ?? 0;
|
|
627
|
+
const type = scorerTypes[name] === "llm" ? "llm" : "deterministic";
|
|
628
|
+
const limit = type === "deterministic" ? 0 : rawLimit;
|
|
629
|
+
const verdict = evaluateScorerTolerance(scored, failed, type, limit);
|
|
630
|
+
if (verdict.exceeds) {
|
|
631
|
+
(degraded ??= []).push({ scorer: name, rate: verdict.rate, limit, type, scored, failed });
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
const durations = evalItems.filter((i) => !i.error && i.duration != null).map((i) => i.duration);
|
|
637
|
+
const timing = durations.length > 0 ? computeStats(durations) : void 0;
|
|
525
638
|
const totalModelCalls = /* @__PURE__ */ new Map();
|
|
526
639
|
for (const item of evalItems) {
|
|
527
640
|
const itemCounts = item.metadata?.modelCallCounts;
|
|
@@ -580,13 +693,20 @@ async function runEval(config, executeWorkflow, runtime, options) {
|
|
|
580
693
|
...config.metadata,
|
|
581
694
|
scorerTypes,
|
|
582
695
|
...modelsMeta,
|
|
583
|
-
...workflowsMeta
|
|
696
|
+
...workflowsMeta,
|
|
697
|
+
...droppedAnnotationKeys.length > 0 ? { droppedAnnotationKeys } : {}
|
|
584
698
|
},
|
|
585
699
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
586
700
|
totalCost,
|
|
587
701
|
duration: Date.now() - startTime,
|
|
588
702
|
items: evalItems,
|
|
589
|
-
summary: {
|
|
703
|
+
summary: {
|
|
704
|
+
count: items.length,
|
|
705
|
+
failures,
|
|
706
|
+
scorers: scorerStats,
|
|
707
|
+
timing,
|
|
708
|
+
...degraded ? { degraded } : {}
|
|
709
|
+
}
|
|
590
710
|
};
|
|
591
711
|
}
|
|
592
712
|
|
|
@@ -595,6 +715,7 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
595
715
|
async function rescore(result, scorers, runtime, options) {
|
|
596
716
|
const startTime = Date.now();
|
|
597
717
|
const concurrency = options?.concurrency ?? 5;
|
|
718
|
+
const scorerConcurrency = options?.scorerConcurrency ?? 5;
|
|
598
719
|
const scorerContext = {
|
|
599
720
|
resolveProvider: (uri) => {
|
|
600
721
|
if (typeof runtime.resolveProvider !== "function") {
|
|
@@ -638,77 +759,24 @@ async function rescore(result, scorers, runtime, options) {
|
|
|
638
759
|
scores: {},
|
|
639
760
|
scoreDetails: {}
|
|
640
761
|
};
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
totalCost += scorerResult.cost;
|
|
650
|
-
}
|
|
651
|
-
const scorerDuration = Date.now() - scorerStart;
|
|
652
|
-
if (!Number.isFinite(scorerResult.score) || scorerResult.score < 0 || scorerResult.score > 1) {
|
|
653
|
-
if (!item.scorerErrors) item.scorerErrors = [];
|
|
654
|
-
item.scorerErrors.push(
|
|
655
|
-
`Scorer "${scorer2.name}" returned out-of-range score ${scorerResult.score} for input ${JSON.stringify(item.input)}`
|
|
656
|
-
);
|
|
657
|
-
item.scores[scorer2.name] = null;
|
|
658
|
-
item.scoreDetails[scorer2.name] = {
|
|
659
|
-
score: null,
|
|
660
|
-
metadata: scorerResult.metadata,
|
|
661
|
-
duration: scorerDuration,
|
|
662
|
-
cost: scorerResult.cost
|
|
663
|
-
};
|
|
664
|
-
} else {
|
|
665
|
-
item.scores[scorer2.name] = round3(scorerResult.score);
|
|
666
|
-
item.scoreDetails[scorer2.name] = {
|
|
667
|
-
score: round3(scorerResult.score),
|
|
668
|
-
metadata: scorerResult.metadata,
|
|
669
|
-
duration: scorerDuration,
|
|
670
|
-
cost: scorerResult.cost
|
|
671
|
-
};
|
|
672
|
-
}
|
|
673
|
-
} catch (err) {
|
|
674
|
-
const errCost = typeof err?.cost === "number" ? err.cost : void 0;
|
|
675
|
-
if (errCost != null) {
|
|
676
|
-
itemScorerCost += errCost;
|
|
677
|
-
totalCost += errCost;
|
|
678
|
-
}
|
|
679
|
-
if (!item.scorerErrors) item.scorerErrors = [];
|
|
680
|
-
item.scorerErrors.push(
|
|
681
|
-
`Scorer "${scorer2.name}" threw: ${err instanceof Error ? err.message : String(err)}`
|
|
682
|
-
);
|
|
683
|
-
item.scores[scorer2.name] = null;
|
|
684
|
-
item.scoreDetails[scorer2.name] = {
|
|
685
|
-
score: null,
|
|
686
|
-
duration: Date.now() - scorerStart,
|
|
687
|
-
cost: errCost
|
|
688
|
-
};
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
item.scorerCost = itemScorerCost > 0 ? itemScorerCost : void 0;
|
|
762
|
+
const itemScorerCost = await scoreItem(
|
|
763
|
+
item,
|
|
764
|
+
scorers,
|
|
765
|
+
scorerConcurrency,
|
|
766
|
+
scorerContext,
|
|
767
|
+
options?.signal
|
|
768
|
+
);
|
|
769
|
+
totalCost += itemScorerCost;
|
|
692
770
|
rescored[itemIndex] = item;
|
|
693
771
|
}
|
|
694
|
-
|
|
695
|
-
async function runNext() {
|
|
696
|
-
while (index < result.items.length) {
|
|
697
|
-
const currentIndex = index++;
|
|
698
|
-
await rescoreItem(result.items[currentIndex], currentIndex);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
const workers = Array.from(
|
|
702
|
-
{ length: Math.min(concurrency, result.items.length) },
|
|
703
|
-
() => runNext()
|
|
704
|
-
);
|
|
705
|
-
await Promise.all(workers);
|
|
772
|
+
await mapWithConcurrency(result.items, concurrency, (item, i) => rescoreItem(item, i));
|
|
706
773
|
const failures = rescored.filter((i) => i.error).length;
|
|
707
774
|
const scorerNames = scorers.map((s) => s.name);
|
|
708
775
|
const scorerStats = {};
|
|
709
776
|
for (const name of scorerNames) {
|
|
710
777
|
const scores = rescored.filter((i) => !i.error && i.scores[name] != null).map((i) => i.scores[name]);
|
|
711
|
-
|
|
778
|
+
const { scored, failed, skipped } = scorerCounts(rescored, name);
|
|
779
|
+
scorerStats[name] = { ...computeStats(scores), scored, failed, skipped };
|
|
712
780
|
}
|
|
713
781
|
const scorerTypes = {};
|
|
714
782
|
for (const s of scorers) {
|
|
@@ -718,7 +786,11 @@ async function rescore(result, scorers, runtime, options) {
|
|
|
718
786
|
id: randomUUID2(),
|
|
719
787
|
dataset: result.dataset,
|
|
720
788
|
metadata: (() => {
|
|
721
|
-
const
|
|
789
|
+
const rest = { ...result.metadata };
|
|
790
|
+
delete rest.runGroupId;
|
|
791
|
+
delete rest.runIndex;
|
|
792
|
+
delete rest.scorerFiltered;
|
|
793
|
+
delete rest.scorersRun;
|
|
722
794
|
const merged = {
|
|
723
795
|
...rest,
|
|
724
796
|
rescored: true,
|
|
@@ -771,15 +843,26 @@ function aggregateRuns(runs) {
|
|
|
771
843
|
const scorerNames = Object.keys(runs[0].summary.scorers);
|
|
772
844
|
const scorers = {};
|
|
773
845
|
for (const name of scorerNames) {
|
|
774
|
-
const
|
|
846
|
+
const contributing = runs.filter((r) => {
|
|
847
|
+
const sc = r.summary.scorers[name]?.scored;
|
|
848
|
+
return sc == null || sc > 0;
|
|
849
|
+
});
|
|
850
|
+
const meanSource = contributing.length > 0 ? contributing : runs;
|
|
851
|
+
const means = meanSource.map((r) => r.summary.scorers[name]?.mean ?? 0);
|
|
775
852
|
const meanOfMeans = means.reduce((a, b) => a + b, 0) / means.length;
|
|
776
853
|
const minMean = Math.min(...means);
|
|
777
854
|
const maxMean = Math.max(...means);
|
|
855
|
+
const scored = runs.reduce((sum, r) => sum + (r.summary.scorers[name]?.scored ?? 0), 0);
|
|
856
|
+
const failed = runs.reduce((sum, r) => sum + (r.summary.scorers[name]?.failed ?? 0), 0);
|
|
857
|
+
const skipped = runs.reduce((sum, r) => sum + (r.summary.scorers[name]?.skipped ?? 0), 0);
|
|
778
858
|
scorers[name] = {
|
|
779
|
-
mean:
|
|
780
|
-
std:
|
|
781
|
-
min:
|
|
782
|
-
max:
|
|
859
|
+
mean: round2(meanOfMeans),
|
|
860
|
+
std: round2(std(means, meanOfMeans)),
|
|
861
|
+
min: round2(minMean),
|
|
862
|
+
max: round2(maxMean),
|
|
863
|
+
scored,
|
|
864
|
+
failed,
|
|
865
|
+
skipped
|
|
783
866
|
};
|
|
784
867
|
}
|
|
785
868
|
let timing;
|
|
@@ -787,8 +870,8 @@ function aggregateRuns(runs) {
|
|
|
787
870
|
if (timingMeans.length === runs.length && timingMeans.length > 0) {
|
|
788
871
|
const meanTiming = timingMeans.reduce((a, b) => a + b, 0) / timingMeans.length;
|
|
789
872
|
timing = {
|
|
790
|
-
mean:
|
|
791
|
-
std:
|
|
873
|
+
mean: round2(meanTiming),
|
|
874
|
+
std: round2(std(timingMeans, meanTiming))
|
|
792
875
|
};
|
|
793
876
|
}
|
|
794
877
|
return {
|
|
@@ -796,8 +879,8 @@ function aggregateRuns(runs) {
|
|
|
796
879
|
runCount: runs.length,
|
|
797
880
|
workflows,
|
|
798
881
|
dataset,
|
|
799
|
-
totalCost:
|
|
800
|
-
totalDuration:
|
|
882
|
+
totalCost: round2(totalCost),
|
|
883
|
+
totalDuration: round2(totalDuration),
|
|
801
884
|
scorers,
|
|
802
885
|
timing
|
|
803
886
|
};
|
|
@@ -805,11 +888,15 @@ function aggregateRuns(runs) {
|
|
|
805
888
|
|
|
806
889
|
export {
|
|
807
890
|
pairedBootstrapCI,
|
|
891
|
+
scorerCounts,
|
|
892
|
+
evaluateScorerTolerance,
|
|
808
893
|
evalCompare,
|
|
894
|
+
evaluateScorerErrorRateGate,
|
|
809
895
|
normalizeScorerResult,
|
|
896
|
+
attachScorerErrorCost,
|
|
810
897
|
scorer,
|
|
811
898
|
runEval,
|
|
812
899
|
rescore,
|
|
813
900
|
aggregateRuns
|
|
814
901
|
};
|
|
815
|
-
//# sourceMappingURL=chunk-
|
|
902
|
+
//# sourceMappingURL=chunk-66EYSYQS.js.map
|