@rulvar/evals 1.81.2 → 1.83.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/dist/index.d.ts +39 -3
- package/dist/index.js +51 -14
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -421,7 +421,8 @@ interface RunBenchmarkOptions {
|
|
|
421
421
|
/**
|
|
422
422
|
* Aggregate debit-only envelope: every target and judge run
|
|
423
423
|
* authorizes its ceiling here BEFORE starting, exactly like the eval
|
|
424
|
-
* runners. A target-run refusal
|
|
424
|
+
* runners. A target-run refusal ends the series monotonically
|
|
425
|
+
* (report.refusal) with every completed repeat preserved; a judge-run
|
|
425
426
|
* refusal rejects that run from scoring as 'judge:refused'.
|
|
426
427
|
*/
|
|
427
428
|
envelope?: SpendEnvelope;
|
|
@@ -520,13 +521,26 @@ interface BenchmarkReport {
|
|
|
520
521
|
/** Every target and judge run, scored or rejected (honest spend). */
|
|
521
522
|
totalCostUsd: number;
|
|
522
523
|
judgeCostUsd: number;
|
|
524
|
+
/**
|
|
525
|
+
* Present when the aggregate envelope refused a TARGET run before it
|
|
526
|
+
* started (cycle 81): the series ends there and every completed
|
|
527
|
+
* repeat stays on the report, mirroring the eval suite's monotone
|
|
528
|
+
* refusal instead of a throw destroying the paid evidence. Judge
|
|
529
|
+
* refusals never appear here; they reject their own run as
|
|
530
|
+
* 'judge:refused'.
|
|
531
|
+
*/
|
|
532
|
+
refusal?: {
|
|
533
|
+
atOrdinal: number;
|
|
534
|
+
detail: string;
|
|
535
|
+
};
|
|
523
536
|
fingerprint: BenchmarkFingerprint;
|
|
524
537
|
}
|
|
525
538
|
/**
|
|
526
539
|
* Runs the spec's repeats sequentially and reports the verified series.
|
|
527
540
|
* Throws only for spec defects (invalid repeats, a throwing grader or
|
|
528
|
-
* extractor)
|
|
529
|
-
*
|
|
541
|
+
* extractor); everything a run does wrong lands in its record, and a
|
|
542
|
+
* target-run envelope refusal ends the series monotonically with the
|
|
543
|
+
* completed repeats preserved (report.refusal).
|
|
530
544
|
*/
|
|
531
545
|
declare function runBenchmark(engine: Engine, spec: BenchmarkSpec, options?: RunBenchmarkOptions): Promise<BenchmarkReport>;
|
|
532
546
|
//#endregion
|
|
@@ -622,6 +636,15 @@ interface SweepCellReport {
|
|
|
622
636
|
*/
|
|
623
637
|
exhaustedRuns?: number;
|
|
624
638
|
/**
|
|
639
|
+
* Count of case results whose TARGET run settled neither ok nor
|
|
640
|
+
* exhausted ('error', 'cancelled', 'suspended'): measurement
|
|
641
|
+
* artifacts, not model quality. Exactly like exhaustedRuns, any such
|
|
642
|
+
* run suppresses the cell's claim: a passRate deflated by a provider
|
|
643
|
+
* failure or a host cancellation must not become a committed model
|
|
644
|
+
* weakness (cycle 81).
|
|
645
|
+
*/
|
|
646
|
+
nonOkRuns?: number;
|
|
647
|
+
/**
|
|
625
648
|
* Count of result rows whose grading stopped on a judge budget event
|
|
626
649
|
* (per-run judge ceiling or envelope refusal of a judge run). The
|
|
627
650
|
* paid target evidence stays on those rows; the cell emits no claim.
|
|
@@ -707,6 +730,15 @@ interface CheckpointCell {
|
|
|
707
730
|
recommended: boolean;
|
|
708
731
|
baseline: CheckpointArm;
|
|
709
732
|
treatment: CheckpointArm;
|
|
733
|
+
/**
|
|
734
|
+
* Either arm carried a measurement artifact (an envelope refusal, an
|
|
735
|
+
* incomplete row, or a target that settled non-ok): the arms are not
|
|
736
|
+
* comparable, the cell can never pass, and criterion 1 fails
|
|
737
|
+
* (cycle 81). Without this, an envelope drained by the baseline left
|
|
738
|
+
* an EMPTY refused treatment arm (n 0, cost 0) that mechanically beat
|
|
739
|
+
* any baseline under the cheaper-at-equal-quality branch.
|
|
740
|
+
*/
|
|
741
|
+
contaminated?: true;
|
|
710
742
|
passed: boolean;
|
|
711
743
|
}
|
|
712
744
|
interface CriterionOneReport {
|
|
@@ -716,11 +748,15 @@ interface CriterionOneReport {
|
|
|
716
748
|
pooledBaseline: CheckpointArm;
|
|
717
749
|
pooledTreatment: CheckpointArm;
|
|
718
750
|
pooledHolds: boolean;
|
|
751
|
+
/** Cells with a contaminated arm; present when nonzero (cycle 81). */
|
|
752
|
+
contaminatedCells?: number;
|
|
719
753
|
passed: boolean;
|
|
720
754
|
}
|
|
721
755
|
interface CriterionTwoReport {
|
|
722
756
|
baseline: CheckpointArm;
|
|
723
757
|
informed: CheckpointArm;
|
|
758
|
+
/** Either arm carried a measurement artifact; the criterion cannot pass. */
|
|
759
|
+
contaminated?: true;
|
|
724
760
|
passed: boolean;
|
|
725
761
|
}
|
|
726
762
|
interface CheckpointReport {
|
package/dist/index.js
CHANGED
|
@@ -760,8 +760,9 @@ function workflowWarningsOf(events) {
|
|
|
760
760
|
/**
|
|
761
761
|
* Runs the spec's repeats sequentially and reports the verified series.
|
|
762
762
|
* Throws only for spec defects (invalid repeats, a throwing grader or
|
|
763
|
-
* extractor)
|
|
764
|
-
*
|
|
763
|
+
* extractor); everything a run does wrong lands in its record, and a
|
|
764
|
+
* target-run envelope refusal ends the series monotonically with the
|
|
765
|
+
* completed repeats preserved (report.refusal).
|
|
765
766
|
*/
|
|
766
767
|
async function runBenchmark(engine, spec, options = {}) {
|
|
767
768
|
if (!Number.isInteger(spec.repeats) || spec.repeats < 1) throw new TypeError(`BenchmarkSpec.repeats must be a positive integer, got ${String(spec.repeats)}`);
|
|
@@ -769,8 +770,18 @@ async function runBenchmark(engine, spec, options = {}) {
|
|
|
769
770
|
let startedAt;
|
|
770
771
|
let totalCostUsd = 0;
|
|
771
772
|
let totalJudgeCostUsd = 0;
|
|
773
|
+
let refusal;
|
|
772
774
|
for (let ordinal = 1; ordinal <= spec.repeats; ordinal += 1) {
|
|
773
|
-
|
|
775
|
+
try {
|
|
776
|
+
options.envelope?.authorize(options.budgetUsd, `benchmark '${spec.name}' run ${String(ordinal)}`);
|
|
777
|
+
} catch (error) {
|
|
778
|
+
if (!(error instanceof SweepBudgetError)) throw error;
|
|
779
|
+
refusal = {
|
|
780
|
+
atOrdinal: ordinal,
|
|
781
|
+
detail: error.message
|
|
782
|
+
};
|
|
783
|
+
break;
|
|
784
|
+
}
|
|
774
785
|
const live = await collectRun(engine.run(spec.workflow, spec.args, {
|
|
775
786
|
name: `benchmark:${spec.name}:${String(ordinal)}`,
|
|
776
787
|
...options.budgetUsd === void 0 ? {} : { budgetUsd: options.budgetUsd }
|
|
@@ -886,6 +897,7 @@ async function runBenchmark(engine, spec, options = {}) {
|
|
|
886
897
|
metrics: metricSeries,
|
|
887
898
|
totalCostUsd,
|
|
888
899
|
judgeCostUsd: totalJudgeCostUsd,
|
|
900
|
+
...refusal === void 0 ? {} : { refusal },
|
|
889
901
|
fingerprint: {
|
|
890
902
|
node: process.version,
|
|
891
903
|
platform: process.platform,
|
|
@@ -958,6 +970,16 @@ function armOf(suite) {
|
|
|
958
970
|
n: suite.results.length
|
|
959
971
|
};
|
|
960
972
|
}
|
|
973
|
+
/**
|
|
974
|
+
* A measurement artifact in an arm invalidates the A/B comparison: an
|
|
975
|
+
* envelope refusal truncated the arm, a judge budget event forced rows
|
|
976
|
+
* to fail, or a target settled non-ok (provider failure, ceiling, host
|
|
977
|
+
* cancellation). Grader failures on ok targets are the HONEST failure
|
|
978
|
+
* signal and never contaminate.
|
|
979
|
+
*/
|
|
980
|
+
function contaminatedArm(suite) {
|
|
981
|
+
return suite.refusal !== void 0 || suite.results.some((result) => result.status !== "ok" || result.incomplete !== void 0);
|
|
982
|
+
}
|
|
961
983
|
function pool(arms) {
|
|
962
984
|
const n = arms.reduce((sum, arm) => sum + arm.n, 0);
|
|
963
985
|
const passed = arms.reduce((sum, arm) => sum + arm.passRate * arm.n, 0);
|
|
@@ -987,9 +1009,15 @@ async function runValueCheckpoint(checkpointPool, options) {
|
|
|
987
1009
|
const treatmentTier = recommendation?.recommendedTier ?? ladder.startTier;
|
|
988
1010
|
const baseMember = ladder.rungs[ladder.startTier];
|
|
989
1011
|
const treatMember = ladder.rungs[treatmentTier];
|
|
990
|
-
if (baseMember === void 0 || treatMember === void 0)
|
|
991
|
-
|
|
992
|
-
|
|
1012
|
+
if (baseMember === void 0 || treatMember === void 0) {
|
|
1013
|
+
const missing = baseMember === void 0 ? ladder.startTier : treatmentTier;
|
|
1014
|
+
throw new Error(`checkpoint: ladder '${ladder.name}' lacks rung ${String(missing)}`);
|
|
1015
|
+
}
|
|
1016
|
+
const baselineSuite = await runEvalSuite(await options.engineFor(baseMember), cases, options.suite ?? {});
|
|
1017
|
+
const treatmentSuite = treatmentTier === ladder.startTier ? baselineSuite : await runEvalSuite(await options.engineFor(treatMember), cases, options.suite ?? {});
|
|
1018
|
+
const baseline = armOf(baselineSuite);
|
|
1019
|
+
const treatment = armOf(treatmentSuite);
|
|
1020
|
+
const contaminated = contaminatedArm(baselineSuite) || contaminatedArm(treatmentSuite);
|
|
993
1021
|
cells.push({
|
|
994
1022
|
ladder: ladder.name,
|
|
995
1023
|
taskClass,
|
|
@@ -998,7 +1026,8 @@ async function runValueCheckpoint(checkpointPool, options) {
|
|
|
998
1026
|
recommended: recommendation !== void 0,
|
|
999
1027
|
baseline,
|
|
1000
1028
|
treatment,
|
|
1001
|
-
|
|
1029
|
+
...contaminated ? { contaminated: true } : {},
|
|
1030
|
+
passed: !contaminated && rungRuleHolds(baseline, treatment)
|
|
1002
1031
|
});
|
|
1003
1032
|
}
|
|
1004
1033
|
const recommendedCells = cells.filter((cell) => cell.recommended);
|
|
@@ -1007,6 +1036,7 @@ async function runValueCheckpoint(checkpointPool, options) {
|
|
|
1007
1036
|
const pooledBaseline = pool(cells.map((cell) => cell.baseline));
|
|
1008
1037
|
const pooledTreatment = pool(cells.map((cell) => cell.treatment));
|
|
1009
1038
|
const pooledHolds = rungRuleHolds(pooledBaseline, pooledTreatment);
|
|
1039
|
+
const contaminatedCells = cells.filter((cell) => cell.contaminated === true).length;
|
|
1010
1040
|
const criterion1 = {
|
|
1011
1041
|
cells,
|
|
1012
1042
|
cellsPassed,
|
|
@@ -1014,18 +1044,23 @@ async function runValueCheckpoint(checkpointPool, options) {
|
|
|
1014
1044
|
pooledBaseline,
|
|
1015
1045
|
pooledTreatment,
|
|
1016
1046
|
pooledHolds,
|
|
1017
|
-
|
|
1047
|
+
...contaminatedCells === 0 ? {} : { contaminatedCells },
|
|
1048
|
+
passed: majorityHolds && pooledHolds && contaminatedCells === 0
|
|
1018
1049
|
};
|
|
1019
1050
|
let criterion2;
|
|
1020
1051
|
if (options.orchestrateEngineFor !== void 0 && options.orchestratedCases !== void 0) {
|
|
1021
1052
|
const cases = options.orchestratedCases.map((entry) => entry.case);
|
|
1022
1053
|
const orchestratedSuite = options.orchestratedSuite ?? options.suite ?? {};
|
|
1023
|
-
const
|
|
1024
|
-
const
|
|
1054
|
+
const baselineSuite = await runEvalSuite(await options.orchestrateEngineFor(false), cases, orchestratedSuite);
|
|
1055
|
+
const informedSuite = await runEvalSuite(await options.orchestrateEngineFor(true), cases, orchestratedSuite);
|
|
1056
|
+
const baseline = armOf(baselineSuite);
|
|
1057
|
+
const informed = armOf(informedSuite);
|
|
1058
|
+
const contaminated = contaminatedArm(baselineSuite) || contaminatedArm(informedSuite);
|
|
1025
1059
|
criterion2 = {
|
|
1026
1060
|
baseline,
|
|
1027
1061
|
informed,
|
|
1028
|
-
|
|
1062
|
+
...contaminated ? { contaminated: true } : {},
|
|
1063
|
+
passed: !contaminated && informed.n > 0 && informed.passRate > 0 && agentTypeRuleHolds(baseline, informed)
|
|
1029
1064
|
};
|
|
1030
1065
|
}
|
|
1031
1066
|
return {
|
|
@@ -1042,9 +1077,9 @@ function renderCheckpointReport(report) {
|
|
|
1042
1077
|
const lines = [
|
|
1043
1078
|
`Measured-value checkpoint (OQ-09) at ${report.observedAt}: ` + (report.passed ? "PASSED" : "FAILED"),
|
|
1044
1079
|
"",
|
|
1045
|
-
`Criterion 1 (rung selection): ${report.criterion1.passed ? "holds" : "fails"} (${String(report.criterion1.cellsPassed)}/${String(report.criterion1.cells.length)} cells, pooled ${report.criterion1.pooledHolds ? "holds" : "fails"})`
|
|
1080
|
+
`Criterion 1 (rung selection): ${report.criterion1.passed ? "holds" : "fails"} (${String(report.criterion1.cellsPassed)}/${String(report.criterion1.cells.filter((cell) => cell.recommended).length)} recommended cells, pooled ${report.criterion1.pooledHolds ? "holds" : "fails"})`
|
|
1046
1081
|
];
|
|
1047
|
-
for (const cell of report.criterion1.cells) lines.push(`* ${cell.ladder} :: ${cell.taskClass}: baseline tier ${String(cell.defaultTier)} ${percent(cell.baseline.passRate)} at ${usd(cell.baseline.totalCostUsd)}; treatment tier ${String(cell.treatmentTier)}${cell.recommended ? "" : " (no recommendation)"} ${percent(cell.treatment.passRate)} at ${usd(cell.treatment.totalCostUsd)}; ${cell.passed ? "pass" : "fail"} (n=${String(cell.baseline.n)})`);
|
|
1082
|
+
for (const cell of report.criterion1.cells) lines.push(`* ${cell.ladder} :: ${cell.taskClass}: baseline tier ${String(cell.defaultTier)} ${percent(cell.baseline.passRate)} at ${usd(cell.baseline.totalCostUsd)}; treatment tier ${String(cell.treatmentTier)}${cell.recommended ? "" : " (no recommendation)"} ${percent(cell.treatment.passRate)} at ${usd(cell.treatment.totalCostUsd)}; ${cell.passed ? "pass" : "fail"}${cell.contaminated === true ? " (contaminated)" : ""} (n=${String(cell.baseline.n)})`);
|
|
1048
1083
|
if (report.criterion2 !== void 0) {
|
|
1049
1084
|
const c2 = report.criterion2;
|
|
1050
1085
|
lines.push("", `Criterion 2 (agentType selection): ${c2.passed ? "holds" : "fails"} (baseline ${percent(c2.baseline.passRate)} at ${usd(c2.baseline.totalCostUsd)}; card-informed ${percent(c2.informed.passRate)} at ${usd(c2.informed.totalCostUsd)}; n=${String(c2.baseline.n)})`);
|
|
@@ -1108,6 +1143,7 @@ async function runSweepMatrix(pool, options) {
|
|
|
1108
1143
|
...options.envelope === void 0 ? {} : { envelope: options.envelope }
|
|
1109
1144
|
});
|
|
1110
1145
|
const exhaustedRuns = suite.results.filter((result) => result.status === "exhausted").length;
|
|
1146
|
+
const nonOkRuns = suite.results.filter((result) => result.status !== "ok" && result.status !== "exhausted").length;
|
|
1111
1147
|
const judgeIncompleteRuns = suite.results.filter((result) => result.incomplete !== void 0).length;
|
|
1112
1148
|
const incompleteReason = suite.refusal !== void 0 ? "envelope-exhausted" : suite.results.find((result) => result.incomplete !== void 0)?.incomplete?.reason;
|
|
1113
1149
|
const cell = {
|
|
@@ -1120,6 +1156,7 @@ async function runSweepMatrix(pool, options) {
|
|
|
1120
1156
|
totalCostUsd: suite.totalCostUsd,
|
|
1121
1157
|
caseNames: suite.results.map((result) => result.name),
|
|
1122
1158
|
...exhaustedRuns === 0 ? {} : { exhaustedRuns },
|
|
1159
|
+
...nonOkRuns === 0 ? {} : { nonOkRuns },
|
|
1123
1160
|
...judgeIncompleteRuns === 0 ? {} : { judgeIncompleteRuns },
|
|
1124
1161
|
...suite.refusal === void 0 ? {} : {
|
|
1125
1162
|
envelopeExhausted: true,
|
|
@@ -1129,7 +1166,7 @@ async function runSweepMatrix(pool, options) {
|
|
|
1129
1166
|
};
|
|
1130
1167
|
cells.push(cell);
|
|
1131
1168
|
const polarity = cell.passRate >= thresholds.strength ? "strength" : cell.passRate <= thresholds.weakness ? "weakness" : void 0;
|
|
1132
|
-
const complete = cell.n === cell.plannedN && exhaustedRuns === 0 && judgeIncompleteRuns === 0 && suite.refusal === void 0;
|
|
1169
|
+
const complete = cell.n === cell.plannedN && exhaustedRuns === 0 && nonOkRuns === 0 && judgeIncompleteRuns === 0 && suite.refusal === void 0;
|
|
1133
1170
|
if (polarity !== void 0 && cell.n > 0 && complete) {
|
|
1134
1171
|
const epoch = options.modelEpochFor?.(member);
|
|
1135
1172
|
claims.push({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/evals",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.83.0",
|
|
4
4
|
"description": "Rulvar evals: eval cases, golden outputs, rubric and judge graders, matrix sweeps, canary fingerprint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@rulvar/
|
|
26
|
-
"@rulvar/
|
|
25
|
+
"@rulvar/testing": "1.83.0",
|
|
26
|
+
"@rulvar/core": "1.83.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^22.20.0",
|