@rulvar/evals 1.0.0 → 1.1.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 +88 -2
- package/dist/index.js +142 -2
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CompiledWorkflow, Effort, Engine, EvidenceRef, Json, JsonSchema, ModelClaim, ModelKnowledgeStore, ModelRef, ModelSpec, RunOutcome, SchemaSpec, TaskClass, Usage, WireError, Workflow } from "@rulvar/core";
|
|
1
|
+
import { CompiledWorkflow, DeclaredLadder, Effort, Engine, EvidenceRef, Json, JsonSchema, KnowledgeSnapshot, ModelClaim, ModelKnowledgeStore, ModelRef, ModelSpec, RunOutcome, SchemaSpec, TaskClass, Usage, WireError, Workflow } from "@rulvar/core";
|
|
2
2
|
|
|
3
3
|
//#region src/case.d.ts
|
|
4
4
|
/**
|
|
@@ -332,4 +332,90 @@ declare const SWEEP_THRESHOLD_DEFAULTS: SweepThresholds;
|
|
|
332
332
|
*/
|
|
333
333
|
declare function runSweepMatrix(pool: SweepPool, options: RunSweepOptions): Promise<SweepReport>;
|
|
334
334
|
//#endregion
|
|
335
|
-
|
|
335
|
+
//#region src/checkpoint.d.ts
|
|
336
|
+
/** One declared checkpoint ladder: rungs are concrete pool members. */
|
|
337
|
+
interface CheckpointLadder extends DeclaredLadder {
|
|
338
|
+
name: string;
|
|
339
|
+
startTier: number;
|
|
340
|
+
rungs: SweepModel[];
|
|
341
|
+
}
|
|
342
|
+
interface CheckpointPool {
|
|
343
|
+
ladders: CheckpointLadder[];
|
|
344
|
+
/** The measurement half; the seeding sweep MUST NOT have seen these. */
|
|
345
|
+
evalCases: SweepCase[];
|
|
346
|
+
}
|
|
347
|
+
interface OrchestratedCase {
|
|
348
|
+
/** The workflow drives an orchestrate-role run; graders judge its outcome. */
|
|
349
|
+
case: EvalCase;
|
|
350
|
+
}
|
|
351
|
+
interface RunCheckpointOptions {
|
|
352
|
+
/** The claims snapshot produced by the seeding sweep (disjoint cases). */
|
|
353
|
+
snapshot: KnowledgeSnapshot;
|
|
354
|
+
/** ISO date of the evaluation (recorded in the report; no wall clock inside). */
|
|
355
|
+
observedAt: string;
|
|
356
|
+
/** An engine per concrete pool member (the caller owns adapters and budgets). */
|
|
357
|
+
engineFor: (member: SweepModel) => Engine | Promise<Engine>;
|
|
358
|
+
/**
|
|
359
|
+
* Criterion 2 engines: withKnowledge true configures the SAME store
|
|
360
|
+
* snapshot behind stores.modelKnowledge; false omits it entirely.
|
|
361
|
+
*/
|
|
362
|
+
orchestrateEngineFor?: (withKnowledge: boolean) => Engine | Promise<Engine>;
|
|
363
|
+
orchestratedCases?: OrchestratedCase[];
|
|
364
|
+
suite?: RunEvalSuiteOptions;
|
|
365
|
+
/**
|
|
366
|
+
* Orchestrated runs need room for the orchestrator cap math (the
|
|
367
|
+
* run ceiling must host the finalize reserve; docs/07, 12.2): their
|
|
368
|
+
* suite options default to `suite` but usually carry a larger
|
|
369
|
+
* budgetUsd.
|
|
370
|
+
*/
|
|
371
|
+
orchestratedSuite?: RunEvalSuiteOptions;
|
|
372
|
+
}
|
|
373
|
+
interface CheckpointArm {
|
|
374
|
+
passRate: number;
|
|
375
|
+
totalCostUsd: number;
|
|
376
|
+
n: number;
|
|
377
|
+
}
|
|
378
|
+
interface CheckpointCell {
|
|
379
|
+
ladder: string;
|
|
380
|
+
taskClass: TaskClass;
|
|
381
|
+
defaultTier: number;
|
|
382
|
+
/** The tier the treatment arm ran at (default when no recommendation). */
|
|
383
|
+
treatmentTier: number;
|
|
384
|
+
recommended: boolean;
|
|
385
|
+
baseline: CheckpointArm;
|
|
386
|
+
treatment: CheckpointArm;
|
|
387
|
+
passed: boolean;
|
|
388
|
+
}
|
|
389
|
+
interface CriterionOneReport {
|
|
390
|
+
cells: CheckpointCell[];
|
|
391
|
+
cellsPassed: number;
|
|
392
|
+
majorityHolds: boolean;
|
|
393
|
+
pooledBaseline: CheckpointArm;
|
|
394
|
+
pooledTreatment: CheckpointArm;
|
|
395
|
+
pooledHolds: boolean;
|
|
396
|
+
passed: boolean;
|
|
397
|
+
}
|
|
398
|
+
interface CriterionTwoReport {
|
|
399
|
+
baseline: CheckpointArm;
|
|
400
|
+
informed: CheckpointArm;
|
|
401
|
+
passed: boolean;
|
|
402
|
+
}
|
|
403
|
+
interface CheckpointReport {
|
|
404
|
+
observedAt: string;
|
|
405
|
+
criterion1: CriterionOneReport;
|
|
406
|
+
criterion2?: CriterionTwoReport;
|
|
407
|
+
/** Both criteria (criterion 2 counts as failed when unmeasured). */
|
|
408
|
+
passed: boolean;
|
|
409
|
+
}
|
|
410
|
+
/** The OQ-09 cell rule (shared by the per-cell and pooled verdicts). */
|
|
411
|
+
declare function rungRuleHolds(baseline: CheckpointArm, treatment: CheckpointArm): boolean;
|
|
412
|
+
/**
|
|
413
|
+
* Runs the checkpoint over the fixed pool. Sequential in declaration
|
|
414
|
+
* order (deterministic cassette consumption when recorded); every cell
|
|
415
|
+
* runs baseline then treatment.
|
|
416
|
+
*/
|
|
417
|
+
declare function runValueCheckpoint(checkpointPool: CheckpointPool, options: RunCheckpointOptions): Promise<CheckpointReport>;
|
|
418
|
+
/** The deterministic render for the M12 gate docs amendment. */
|
|
419
|
+
declare function renderCheckpointReport(report: CheckpointReport): string;
|
|
420
|
+
//#endregion
|
|
421
|
+
export { type CanaryDriftReport, type CanaryProbeSet, type CheckpointArm, type CheckpointCell, type CheckpointLadder, type CheckpointPool, type CheckpointReport, type CriterionOneReport, type CriterionTwoReport, type EvalCase, type EvalCaseResult, type EvalCommitterOptions, EvalJudgeError, type EvalMatrixReport, type EvalSuiteResult, type GoldenGraderOptions, type Grader, type GraderContext, type GraderVerdict, JUDGE_VERDICT_SCHEMA, type JudgeGraderOptions, type JudgeSpec, type MatrixCell, type MatrixCellReport, type MeasuredClaimInput, type OrchestratedCase, type RubricCriterion, type RubricGraderOptions, type RunCheckpointOptions, type RunEvalCaseOptions, type RunEvalSuiteOptions, type RunSweepOptions, SWEEP_THRESHOLD_DEFAULTS, type SweepCase, type SweepCellReport, type SweepModel, type SweepPool, type SweepReport, type SweepThresholds, canaryFingerprint, commitEvalMeasured, evalMeasuredClaim, flipStaleOnCanaryDrift, goldenGrader, judgeGrader, normalizeCanaryOutput, renderCheckpointReport, rubricGrader, runEvalCase, runEvalMatrix, runEvalSuite, runSweepMatrix, runValueCheckpoint, rungRuleHolds };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KnowledgeCasError, claimExpiry, defineWorkflow } from "@rulvar/core";
|
|
1
|
+
import { KnowledgeCasError, claimExpiry, compileVerifiedLayer, defineWorkflow } from "@rulvar/core";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
//#region src/case.ts
|
|
4
4
|
/**
|
|
@@ -394,6 +394,146 @@ async function flipStaleOnCanaryDrift(store, model, freshFingerprint, options) {
|
|
|
394
394
|
throw lastCas ?? /* @__PURE__ */ new Error("flipStaleOnCanaryDrift: unreachable");
|
|
395
395
|
}
|
|
396
396
|
//#endregion
|
|
397
|
+
//#region src/checkpoint.ts
|
|
398
|
+
/**
|
|
399
|
+
* The phases 1-2 measured-value checkpoint (M12-T01; docs/05, section
|
|
400
|
+
* "Phases and placement"; the quantitative criteria of OQ-09,
|
|
401
|
+
* 14-open-questions.md, closed at M11-T06). The M12 gate: kb_propose
|
|
402
|
+
* and the proposal loop ship ONLY if the knowledge card demonstrably
|
|
403
|
+
* improves tier and agentType selection on eval cases.
|
|
404
|
+
*
|
|
405
|
+
* Two experiments, both A/B under identical fixed pools:
|
|
406
|
+
*
|
|
407
|
+
* 1. RUNG SELECTION, per (ladder, taskClass) cell: the baseline arm
|
|
408
|
+
* runs every eval case at the ladder's DEFAULT start tier; the
|
|
409
|
+
* treatment arm runs at the tier recommended by
|
|
410
|
+
* compileVerifiedLayer over the store's claims (default when no
|
|
411
|
+
* recommendation). A cell passes when the treatment reaches a pass
|
|
412
|
+
* rate at least equal to the baseline at no more than 90 percent
|
|
413
|
+
* of its cost, OR at least 5 points above it at no more than its
|
|
414
|
+
* cost. Criterion 1 holds when a MAJORITY of cells pass AND the
|
|
415
|
+
* pooled aggregate passes the same rule.
|
|
416
|
+
*
|
|
417
|
+
* 2. AGENTTYPE SELECTION, pooled: the same orchestrate-role cases run
|
|
418
|
+
* with and without the knowledge store configured (the card docks
|
|
419
|
+
* into the spawn tool description when configured). Criterion 2
|
|
420
|
+
* holds when the card-informed arm matches or beats the baseline
|
|
421
|
+
* pass rate at no more than 105 percent of its cost.
|
|
422
|
+
*
|
|
423
|
+
* The checkpoint PASSES only when both criteria hold. Methodology
|
|
424
|
+
* guard: the claims the treatment consumes MUST come from a seeding
|
|
425
|
+
* sweep over a DISJOINT case set (the seed/eval split is the caller's
|
|
426
|
+
* pool contract), or the measurement is leakage.
|
|
427
|
+
*/
|
|
428
|
+
/** IEEE754 guard for the rule boundaries (0.8 + 0.05 exceeds 0.85). */
|
|
429
|
+
const EPSILON = 1e-9;
|
|
430
|
+
/** The OQ-09 cell rule (shared by the per-cell and pooled verdicts). */
|
|
431
|
+
function rungRuleHolds(baseline, treatment) {
|
|
432
|
+
const equalOrBetterCheaper = treatment.passRate >= baseline.passRate - EPSILON && treatment.totalCostUsd <= .9 * baseline.totalCostUsd + EPSILON;
|
|
433
|
+
const clearlyBetterAtCost = treatment.passRate >= baseline.passRate + .05 - EPSILON && treatment.totalCostUsd <= baseline.totalCostUsd + EPSILON;
|
|
434
|
+
return equalOrBetterCheaper || clearlyBetterAtCost;
|
|
435
|
+
}
|
|
436
|
+
function armOf(suite) {
|
|
437
|
+
return {
|
|
438
|
+
passRate: suite.passRate,
|
|
439
|
+
totalCostUsd: suite.totalCostUsd,
|
|
440
|
+
n: suite.results.length
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function pool(arms) {
|
|
444
|
+
const n = arms.reduce((sum, arm) => sum + arm.n, 0);
|
|
445
|
+
const passed = arms.reduce((sum, arm) => sum + arm.passRate * arm.n, 0);
|
|
446
|
+
const cost = arms.reduce((sum, arm) => sum + arm.totalCostUsd, 0);
|
|
447
|
+
return {
|
|
448
|
+
passRate: n === 0 ? 0 : passed / n,
|
|
449
|
+
totalCostUsd: cost,
|
|
450
|
+
n
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Runs the checkpoint over the fixed pool. Sequential in declaration
|
|
455
|
+
* order (deterministic cassette consumption when recorded); every cell
|
|
456
|
+
* runs baseline then treatment.
|
|
457
|
+
*/
|
|
458
|
+
async function runValueCheckpoint(checkpointPool, options) {
|
|
459
|
+
const recommendations = compileVerifiedLayer(options.snapshot.claims.filter((claim) => claim.status === "active"), checkpointPool.ladders);
|
|
460
|
+
const byTaskClass = /* @__PURE__ */ new Map();
|
|
461
|
+
for (const entry of checkpointPool.evalCases) {
|
|
462
|
+
const bucket = byTaskClass.get(entry.taskClass) ?? [];
|
|
463
|
+
bucket.push(entry.case);
|
|
464
|
+
byTaskClass.set(entry.taskClass, bucket);
|
|
465
|
+
}
|
|
466
|
+
const cells = [];
|
|
467
|
+
for (const ladder of checkpointPool.ladders) for (const [taskClass, cases] of byTaskClass) {
|
|
468
|
+
const recommendation = recommendations.find((row) => row.ladder === ladder.name && row.taskClass === taskClass);
|
|
469
|
+
const treatmentTier = recommendation?.recommendedTier ?? ladder.startTier;
|
|
470
|
+
const baseMember = ladder.rungs[ladder.startTier];
|
|
471
|
+
const treatMember = ladder.rungs[treatmentTier];
|
|
472
|
+
if (baseMember === void 0 || treatMember === void 0) throw new Error(`checkpoint: ladder '${ladder.name}' lacks rung ${String(treatmentTier)}`);
|
|
473
|
+
const baseline = armOf(await runEvalSuite(await options.engineFor(baseMember), cases, options.suite ?? {}));
|
|
474
|
+
const treatment = treatmentTier === ladder.startTier ? baseline : armOf(await runEvalSuite(await options.engineFor(treatMember), cases, options.suite ?? {}));
|
|
475
|
+
cells.push({
|
|
476
|
+
ladder: ladder.name,
|
|
477
|
+
taskClass,
|
|
478
|
+
defaultTier: ladder.startTier,
|
|
479
|
+
treatmentTier,
|
|
480
|
+
recommended: recommendation !== void 0,
|
|
481
|
+
baseline,
|
|
482
|
+
treatment,
|
|
483
|
+
passed: rungRuleHolds(baseline, treatment)
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
const recommendedCells = cells.filter((cell) => cell.recommended);
|
|
487
|
+
const cellsPassed = recommendedCells.filter((cell) => cell.passed).length;
|
|
488
|
+
const majorityHolds = recommendedCells.length > 0 && cellsPassed * 2 > recommendedCells.length;
|
|
489
|
+
const pooledBaseline = pool(cells.map((cell) => cell.baseline));
|
|
490
|
+
const pooledTreatment = pool(cells.map((cell) => cell.treatment));
|
|
491
|
+
const pooledHolds = rungRuleHolds(pooledBaseline, pooledTreatment);
|
|
492
|
+
const criterion1 = {
|
|
493
|
+
cells,
|
|
494
|
+
cellsPassed,
|
|
495
|
+
majorityHolds,
|
|
496
|
+
pooledBaseline,
|
|
497
|
+
pooledTreatment,
|
|
498
|
+
pooledHolds,
|
|
499
|
+
passed: majorityHolds && pooledHolds
|
|
500
|
+
};
|
|
501
|
+
let criterion2;
|
|
502
|
+
if (options.orchestrateEngineFor !== void 0 && options.orchestratedCases !== void 0) {
|
|
503
|
+
const cases = options.orchestratedCases.map((entry) => entry.case);
|
|
504
|
+
const orchestratedSuite = options.orchestratedSuite ?? options.suite ?? {};
|
|
505
|
+
const baseline = armOf(await runEvalSuite(await options.orchestrateEngineFor(false), cases, orchestratedSuite));
|
|
506
|
+
const informed = armOf(await runEvalSuite(await options.orchestrateEngineFor(true), cases, orchestratedSuite));
|
|
507
|
+
criterion2 = {
|
|
508
|
+
baseline,
|
|
509
|
+
informed,
|
|
510
|
+
passed: informed.n > 0 && informed.passRate > 0 && informed.passRate >= baseline.passRate && informed.totalCostUsd <= 1.05 * baseline.totalCostUsd + EPSILON
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
return {
|
|
514
|
+
observedAt: options.observedAt,
|
|
515
|
+
criterion1,
|
|
516
|
+
...criterion2 === void 0 ? {} : { criterion2 },
|
|
517
|
+
passed: criterion1.passed && criterion2 !== void 0 && criterion2.passed
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
const percent = (rate) => `${(rate * 100).toFixed(1)}%`;
|
|
521
|
+
const usd = (value) => `$${value.toFixed(4)}`;
|
|
522
|
+
/** The deterministic render for the M12 gate docs amendment. */
|
|
523
|
+
function renderCheckpointReport(report) {
|
|
524
|
+
const lines = [
|
|
525
|
+
`Measured-value checkpoint (OQ-09) at ${report.observedAt}: ` + (report.passed ? "PASSED" : "FAILED"),
|
|
526
|
+
"",
|
|
527
|
+
`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"})`
|
|
528
|
+
];
|
|
529
|
+
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)})`);
|
|
530
|
+
if (report.criterion2 !== void 0) {
|
|
531
|
+
const c2 = report.criterion2;
|
|
532
|
+
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)})`);
|
|
533
|
+
} else lines.push("", "Criterion 2 (agentType selection): NOT MEASURED (counts as failed)");
|
|
534
|
+
return lines.join("\n");
|
|
535
|
+
}
|
|
536
|
+
//#endregion
|
|
397
537
|
//#region src/sweeps.ts
|
|
398
538
|
const SWEEP_THRESHOLD_DEFAULTS = {
|
|
399
539
|
strength: .9,
|
|
@@ -485,4 +625,4 @@ async function runSweepMatrix(pool, options) {
|
|
|
485
625
|
return report;
|
|
486
626
|
}
|
|
487
627
|
//#endregion
|
|
488
|
-
export { EvalJudgeError, JUDGE_VERDICT_SCHEMA, SWEEP_THRESHOLD_DEFAULTS, canaryFingerprint, commitEvalMeasured, evalMeasuredClaim, flipStaleOnCanaryDrift, goldenGrader, judgeGrader, normalizeCanaryOutput, rubricGrader, runEvalCase, runEvalMatrix, runEvalSuite, runSweepMatrix };
|
|
628
|
+
export { EvalJudgeError, JUDGE_VERDICT_SCHEMA, SWEEP_THRESHOLD_DEFAULTS, canaryFingerprint, commitEvalMeasured, evalMeasuredClaim, flipStaleOnCanaryDrift, goldenGrader, judgeGrader, normalizeCanaryOutput, renderCheckpointReport, rubricGrader, runEvalCase, runEvalMatrix, runEvalSuite, runSweepMatrix, runValueCheckpoint, rungRuleHolds };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/evals",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.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/core": "1.1.0",
|
|
26
|
+
"@rulvar/testing": "1.1.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^22.20.0",
|