modelrig 0.7.0 → 0.8.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/CHANGELOG.md +118 -0
- package/README.md +86 -0
- package/dist/bakeoff/disagreement.d.ts +93 -0
- package/dist/bakeoff/disagreement.d.ts.map +1 -0
- package/dist/bakeoff/disagreement.js +140 -0
- package/dist/bakeoff/engine.d.ts.map +1 -1
- package/dist/bakeoff/engine.js +58 -2
- package/dist/bakeoff/pairwise.d.ts +102 -0
- package/dist/bakeoff/pairwise.d.ts.map +1 -0
- package/dist/bakeoff/pairwise.js +214 -0
- package/dist/bakeoff/replay.d.ts +6 -0
- package/dist/bakeoff/replay.d.ts.map +1 -1
- package/dist/bakeoff/replay.js +6 -2
- package/dist/bakeoff/report.d.ts +7 -0
- package/dist/bakeoff/report.d.ts.map +1 -1
- package/dist/bakeoff/report.js +46 -0
- package/dist/bakeoff/types.d.ts +24 -0
- package/dist/bakeoff/types.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -9
- package/dist/mask/apply.d.ts +80 -0
- package/dist/mask/apply.d.ts.map +1 -0
- package/dist/mask/apply.js +228 -0
- package/dist/mask/detect.d.ts +38 -0
- package/dist/mask/detect.d.ts.map +1 -0
- package/dist/mask/detect.js +167 -0
- package/dist/mask/index.d.ts +21 -0
- package/dist/mask/index.d.ts.map +1 -0
- package/dist/mask/index.js +54 -0
- package/dist/mask/mask-policy-cli.d.ts +11 -0
- package/dist/mask/mask-policy-cli.d.ts.map +1 -0
- package/dist/mask/mask-policy-cli.js +33 -0
- package/dist/mask/policy.d.ts +38 -0
- package/dist/mask/policy.d.ts.map +1 -0
- package/dist/mask/policy.js +187 -0
- package/dist/mask/proof.d.ts +54 -0
- package/dist/mask/proof.d.ts.map +1 -0
- package/dist/mask/proof.js +120 -0
- package/dist/mask/receipt.d.ts +30 -0
- package/dist/mask/receipt.d.ts.map +1 -0
- package/dist/mask/receipt.js +49 -0
- package/dist/mask/seam.d.ts +53 -0
- package/dist/mask/seam.d.ts.map +1 -0
- package/dist/mask/seam.js +120 -0
- package/dist/mask/types.d.ts +81 -0
- package/dist/mask/types.d.ts.map +1 -0
- package/dist/mask/types.js +10 -0
- package/dist/mask/vault.d.ts +50 -0
- package/dist/mask/vault.d.ts.map +1 -0
- package/dist/mask/vault.js +81 -0
- package/dist/receipts/types.d.ts +65 -0
- package/dist/receipts/types.d.ts.map +1 -0
- package/dist/receipts/types.js +134 -0
- package/dist/routes/loader-warnings.d.ts +8 -0
- package/dist/routes/loader-warnings.d.ts.map +1 -1
- package/dist/routes/loader-warnings.js +23 -0
- package/dist/routes/loader.d.ts +1 -1
- package/dist/routes/loader.d.ts.map +1 -1
- package/dist/routes/loader.js +24 -25
- package/dist/routes/mask-loader.d.ts +18 -0
- package/dist/routes/mask-loader.d.ts.map +1 -0
- package/dist/routes/mask-loader.js +39 -0
- package/dist/routes/objective-loader.d.ts.map +1 -1
- package/dist/routes/objective-loader.js +51 -17
- package/dist/routes/screen-loader.d.ts +23 -0
- package/dist/routes/screen-loader.d.ts.map +1 -0
- package/dist/routes/screen-loader.js +79 -0
- package/dist/routes/template.d.ts +10 -0
- package/dist/routes/template.d.ts.map +1 -1
- package/dist/routes/template.js +10 -3
- package/dist/run/accept.d.ts +4 -0
- package/dist/run/accept.d.ts.map +1 -1
- package/dist/run/accept.js +2 -0
- package/dist/run/dispatch.d.ts +35 -0
- package/dist/run/dispatch.d.ts.map +1 -1
- package/dist/run/dispatch.js +33 -0
- package/dist/run/grounding.d.ts +47 -2
- package/dist/run/grounding.d.ts.map +1 -1
- package/dist/run/grounding.js +113 -6
- package/dist/run/raw.d.ts.map +1 -1
- package/dist/run/raw.js +6 -1
- package/dist/run/record-attempt.d.ts.map +1 -1
- package/dist/run/record-attempt.js +12 -1
- package/dist/run/run.d.ts.map +1 -1
- package/dist/run/run.js +19 -16
- package/dist/screen/provenance.d.ts +59 -0
- package/dist/screen/provenance.d.ts.map +1 -0
- package/dist/screen/provenance.js +104 -0
- package/dist/screen/types.d.ts +71 -0
- package/dist/screen/types.d.ts.map +1 -0
- package/dist/screen/types.js +9 -0
- package/dist/status-cli.d.ts +11 -0
- package/dist/status-cli.d.ts.map +1 -0
- package/dist/status-cli.js +21 -0
- package/dist/status-runs.d.ts +38 -0
- package/dist/status-runs.d.ts.map +1 -0
- package/dist/status-runs.js +80 -0
- package/dist/status.d.ts +7 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +11 -2
- package/dist/telemetry/ingest-protocol.d.ts +8 -0
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
- package/dist/telemetry/store-migrations.d.ts.map +1 -1
- package/dist/telemetry/store-migrations.js +11 -0
- package/dist/telemetry/store.d.ts.map +1 -1
- package/dist/telemetry/store.js +12 -3
- package/dist/types/provider.d.ts +11 -0
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/provider.js +9 -0
- package/dist/types/route.d.ts +55 -0
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/telemetry.d.ts +7 -0
- package/dist/types/telemetry.d.ts.map +1 -1
- package/dist/validate-cli.d.ts.map +1 -1
- package/dist/validate-cli.js +13 -0
- package/dist/verify/types.d.ts +6 -0
- package/dist/verify/types.d.ts.map +1 -1
- package/docs/agent-skill/SKILL.md +131 -0
- package/docs/how-it-fits.md +34 -0
- package/docs/instrumentation.md +221 -0
- package/docs/llms.txt +276 -0
- package/docs/migration-playbook.md +512 -0
- package/docs/quickstart.md +339 -0
- package/package.json +18 -2
- package/receipts.d.ts +12 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* N5 — pairwise preference vs the reference arm (near-optimal brief §3.2 N5).
|
|
3
|
+
*
|
|
4
|
+
* A second, complementary read on regret: for each replayed sample, is the SERVED
|
|
5
|
+
* arm's output BETTER than, tied with, or WORSE than the REFERENCE arm's on the
|
|
6
|
+
* same captured input? Aggregated to a `pairwise_win_rate` (win=1, tie=0.5,
|
|
7
|
+
* loss=0) with a seeded bootstrap CI — the RigIndex pairwise idea, scoped to one
|
|
8
|
+
* bake-off's arm-vs-reference comparison.
|
|
9
|
+
*
|
|
10
|
+
* DETERMINISTIC-FIRST (F5), exactly like the regret quality tiers:
|
|
11
|
+
* 1. When BOTH sides carry a per-sample value-accuracy score (eval case /
|
|
12
|
+
* value_checks / Q1 gate), the verdict is the sign of their difference —
|
|
13
|
+
* no model call. This is the free, always-preferred tier.
|
|
14
|
+
* 2. Only when a sample cannot be decided deterministically does an OPT-IN,
|
|
15
|
+
* CAPPED, METERED pairwise judge (`bakeoff.pairwise.judge`) compare the two
|
|
16
|
+
* outputs. Judge model ∈ declared candidates (#223 F3, enforced at load).
|
|
17
|
+
*
|
|
18
|
+
* The aggregation math is PURE and unit-tested directly; the judge reuses the
|
|
19
|
+
* real `run()` loop (no new dispatch path, no run.ts edit) exactly as judge.ts.
|
|
20
|
+
*/
|
|
21
|
+
import type { RunDeps } from "../run/run";
|
|
22
|
+
import type { CandidateSpec, RouteBundle } from "../types/route";
|
|
23
|
+
import type { ReplaySampleResult } from "./replay";
|
|
24
|
+
/** The served arm's standing vs the reference on one sample. `win` = the arm's
|
|
25
|
+
* output is better than the reference's; `loss` = worse; `tie` = indistinguishable. */
|
|
26
|
+
export type PairwiseOutcome = "win" | "tie" | "loss";
|
|
27
|
+
/** How a single verdict was decided (F5): a free deterministic comparison of two
|
|
28
|
+
* value-accuracy scores, or a billed pairwise judge call. */
|
|
29
|
+
export type PairwiseSource = "deterministic" | "judge";
|
|
30
|
+
/** Aggregate pairwise standing of an arm vs the reference (N5). `winRate` counts
|
|
31
|
+
* a win as 1 and a tie as 0.5; the CI is a seeded bootstrap of the per-sample
|
|
32
|
+
* win values, so it is reproducible given the same sample set. */
|
|
33
|
+
export interface PairwiseStats {
|
|
34
|
+
readonly winRate: number;
|
|
35
|
+
readonly winRateCi95: readonly [number, number];
|
|
36
|
+
/** Samples with a verdict (deterministic + judge); the CI's n. */
|
|
37
|
+
readonly nDecided: number;
|
|
38
|
+
readonly wins: number;
|
|
39
|
+
readonly ties: number;
|
|
40
|
+
readonly losses: number;
|
|
41
|
+
/** Paired samples that could NOT be decided (no deterministic score and no
|
|
42
|
+
* judge, or the judge was capped/failed) — excluded from the rate, counted. */
|
|
43
|
+
readonly nUndecided: number;
|
|
44
|
+
/** Where the decided verdicts came from — "deterministic", "judge", or "mixed". */
|
|
45
|
+
readonly source: "deterministic" | "judge" | "mixed";
|
|
46
|
+
}
|
|
47
|
+
/** Seeded so a bake-off's pairwise CI is reproducible (like the regret CI). A
|
|
48
|
+
* distinct seed from the regret bootstrap keeps the two resamplings independent. */
|
|
49
|
+
export declare const PAIRWISE_BOOTSTRAP_REPS = 2000;
|
|
50
|
+
export declare const PAIRWISE_BOOTSTRAP_SEED = 20260824;
|
|
51
|
+
/** The win-value of an outcome (win=1, tie=0.5, loss=0) — the quantity the win
|
|
52
|
+
* rate averages. */
|
|
53
|
+
export declare function winValue(o: PairwiseOutcome): number;
|
|
54
|
+
/**
|
|
55
|
+
* The DETERMINISTIC pairwise verdict (tier 1): the sign of `armQuality −
|
|
56
|
+
* refQuality`. Returns null when EITHER side lacks a value-accuracy score — the
|
|
57
|
+
* caller then falls to the judge. A tie within EPS is a genuine tie.
|
|
58
|
+
*/
|
|
59
|
+
export declare function deterministicPairwise(armQuality: number | null, refQuality: number | null): PairwiseOutcome | null;
|
|
60
|
+
/**
|
|
61
|
+
* Aggregate per-sample verdicts into `PairwiseStats` (N5). `decided` are the
|
|
62
|
+
* verdicts that resolved; `nUndecided` the paired samples that did not. The CI is
|
|
63
|
+
* a seeded bootstrap of the per-sample win values (win=1/tie=0.5/loss=0), so it
|
|
64
|
+
* is deterministic given the verdict list + seed. Pure.
|
|
65
|
+
*/
|
|
66
|
+
export declare function aggregatePairwise(decided: readonly {
|
|
67
|
+
readonly outcome: PairwiseOutcome;
|
|
68
|
+
readonly source: PairwiseSource;
|
|
69
|
+
}[], nUndecided: number, seed?: number, reps?: number): PairwiseStats;
|
|
70
|
+
/**
|
|
71
|
+
* Score ONE arm's pairwise standing vs the reference over the paired replay set
|
|
72
|
+
* (N5). Deterministic-first: each paired sample's verdict is the sign of the two
|
|
73
|
+
* value-accuracy scores; a sample neither side scored falls to the capped judge
|
|
74
|
+
* (when supplied), else it is undecided (counted, excluded from the rate). Pairs
|
|
75
|
+
* by captureId; unpaired samples are ignored. Returns null stats when there are
|
|
76
|
+
* NO paired samples at all. `outcomes` maps captureId → verdict (for N6 mining).
|
|
77
|
+
*/
|
|
78
|
+
export declare function scoreArmPairwise(armSamples: readonly ReplaySampleResult[], refSamples: readonly ReplaySampleResult[], judge: PairwiseJudge | null): Promise<{
|
|
79
|
+
stats: PairwiseStats | null;
|
|
80
|
+
outcomes: Map<string, PairwiseOutcome>;
|
|
81
|
+
}>;
|
|
82
|
+
/** Build the synthetic pairwise-judge route for a base route + judge model. The
|
|
83
|
+
* judge model is declared WITHOUT its trial flag so it serves live here. */
|
|
84
|
+
export declare function buildPairwiseJudgeRoute(baseRoute: RouteBundle, model: CandidateSpec): RouteBundle;
|
|
85
|
+
/** A per-arm pairwise scorer: `compare(armOutput, refOutput, taskInput)` returns
|
|
86
|
+
* the arm's outcome vs the reference, or null when the cap is hit or the judge
|
|
87
|
+
* could not produce a valid verdict. */
|
|
88
|
+
export interface PairwiseJudge {
|
|
89
|
+
compare(armOutput: unknown, refOutput: unknown, taskInput: unknown): Promise<PairwiseOutcome | null>;
|
|
90
|
+
used(): number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Build a pairwise judge over the run loop (mirrors makeJudgeScorer). Each arm
|
|
94
|
+
* gets a FRESH judge so `maxSamples` is per arm. The comparison is presented in a
|
|
95
|
+
* FIXED order (A = served arm, B = reference); position bias is a known limit,
|
|
96
|
+
* noted in verification — a fixed order keeps the judge DETERMINISTIC for tests.
|
|
97
|
+
*/
|
|
98
|
+
export declare function makePairwiseJudge(deps: RunDeps, baseRoute: RouteBundle, judge: {
|
|
99
|
+
readonly model: CandidateSpec;
|
|
100
|
+
readonly maxSamples: number;
|
|
101
|
+
}): PairwiseJudge;
|
|
102
|
+
//# sourceMappingURL=pairwise.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairwise.d.ts","sourceRoot":"","sources":["../../src/bakeoff/pairwise.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD;uFACuF;AACvF,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAErD;6DAC6D;AAC7D,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,OAAO,CAAC;AAEvD;;kEAEkE;AAClE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChD,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;mFAC+E;IAC/E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,GAAG,OAAO,CAAC;CACtD;AAED;oFACoF;AACpF,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAKhD;oBACoB;AACpB,wBAAgB,QAAQ,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,eAAe,GAAG,IAAI,CAKxB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;CAAE,EAAE,EAC1F,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,MAAgC,EACtC,IAAI,GAAE,MAAgC,GACrC,aAAa,CA2Bf;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,SAAS,kBAAkB,EAAE,EACzC,UAAU,EAAE,SAAS,kBAAkB,EAAE,EACzC,KAAK,EAAE,aAAa,GAAG,IAAI,GAC1B,OAAO,CAAC;IAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;CAAE,CAAC,CA8BlF;AAmBD;4EAC4E;AAC5E,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,GAAG,WAAW,CAejG;AAED;;wCAEwC;AACxC,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IACrG,IAAI,IAAI,MAAM,CAAC;CAChB;AAMD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACpE,aAAa,CAmCf"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* N5 — pairwise preference vs the reference arm (near-optimal brief §3.2 N5).
|
|
4
|
+
*
|
|
5
|
+
* A second, complementary read on regret: for each replayed sample, is the SERVED
|
|
6
|
+
* arm's output BETTER than, tied with, or WORSE than the REFERENCE arm's on the
|
|
7
|
+
* same captured input? Aggregated to a `pairwise_win_rate` (win=1, tie=0.5,
|
|
8
|
+
* loss=0) with a seeded bootstrap CI — the RigIndex pairwise idea, scoped to one
|
|
9
|
+
* bake-off's arm-vs-reference comparison.
|
|
10
|
+
*
|
|
11
|
+
* DETERMINISTIC-FIRST (F5), exactly like the regret quality tiers:
|
|
12
|
+
* 1. When BOTH sides carry a per-sample value-accuracy score (eval case /
|
|
13
|
+
* value_checks / Q1 gate), the verdict is the sign of their difference —
|
|
14
|
+
* no model call. This is the free, always-preferred tier.
|
|
15
|
+
* 2. Only when a sample cannot be decided deterministically does an OPT-IN,
|
|
16
|
+
* CAPPED, METERED pairwise judge (`bakeoff.pairwise.judge`) compare the two
|
|
17
|
+
* outputs. Judge model ∈ declared candidates (#223 F3, enforced at load).
|
|
18
|
+
*
|
|
19
|
+
* The aggregation math is PURE and unit-tested directly; the judge reuses the
|
|
20
|
+
* real `run()` loop (no new dispatch path, no run.ts edit) exactly as judge.ts.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.PAIRWISE_BOOTSTRAP_SEED = exports.PAIRWISE_BOOTSTRAP_REPS = void 0;
|
|
24
|
+
exports.winValue = winValue;
|
|
25
|
+
exports.deterministicPairwise = deterministicPairwise;
|
|
26
|
+
exports.aggregatePairwise = aggregatePairwise;
|
|
27
|
+
exports.scoreArmPairwise = scoreArmPairwise;
|
|
28
|
+
exports.buildPairwiseJudgeRoute = buildPairwiseJudgeRoute;
|
|
29
|
+
exports.makePairwiseJudge = makePairwiseJudge;
|
|
30
|
+
const node_crypto_1 = require("node:crypto");
|
|
31
|
+
const bootstrap_1 = require("../rigindex/bootstrap");
|
|
32
|
+
const run_1 = require("../run/run");
|
|
33
|
+
const failures_1 = require("../types/failures");
|
|
34
|
+
/** Seeded so a bake-off's pairwise CI is reproducible (like the regret CI). A
|
|
35
|
+
* distinct seed from the regret bootstrap keeps the two resamplings independent. */
|
|
36
|
+
exports.PAIRWISE_BOOTSTRAP_REPS = 2000;
|
|
37
|
+
exports.PAIRWISE_BOOTSTRAP_SEED = 20260824;
|
|
38
|
+
/** Tie band: value-accuracy scores within EPS are a tie, not a hair-split win. */
|
|
39
|
+
const PAIRWISE_EPS = 1e-9;
|
|
40
|
+
/** The win-value of an outcome (win=1, tie=0.5, loss=0) — the quantity the win
|
|
41
|
+
* rate averages. */
|
|
42
|
+
function winValue(o) {
|
|
43
|
+
return o === "win" ? 1 : o === "tie" ? 0.5 : 0;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The DETERMINISTIC pairwise verdict (tier 1): the sign of `armQuality −
|
|
47
|
+
* refQuality`. Returns null when EITHER side lacks a value-accuracy score — the
|
|
48
|
+
* caller then falls to the judge. A tie within EPS is a genuine tie.
|
|
49
|
+
*/
|
|
50
|
+
function deterministicPairwise(armQuality, refQuality) {
|
|
51
|
+
if (armQuality === null || refQuality === null)
|
|
52
|
+
return null;
|
|
53
|
+
const diff = armQuality - refQuality;
|
|
54
|
+
if (Math.abs(diff) <= PAIRWISE_EPS)
|
|
55
|
+
return "tie";
|
|
56
|
+
return diff > 0 ? "win" : "loss";
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Aggregate per-sample verdicts into `PairwiseStats` (N5). `decided` are the
|
|
60
|
+
* verdicts that resolved; `nUndecided` the paired samples that did not. The CI is
|
|
61
|
+
* a seeded bootstrap of the per-sample win values (win=1/tie=0.5/loss=0), so it
|
|
62
|
+
* is deterministic given the verdict list + seed. Pure.
|
|
63
|
+
*/
|
|
64
|
+
function aggregatePairwise(decided, nUndecided, seed = exports.PAIRWISE_BOOTSTRAP_SEED, reps = exports.PAIRWISE_BOOTSTRAP_REPS) {
|
|
65
|
+
const wins = decided.filter((d) => d.outcome === "win").length;
|
|
66
|
+
const ties = decided.filter((d) => d.outcome === "tie").length;
|
|
67
|
+
const losses = decided.filter((d) => d.outcome === "loss").length;
|
|
68
|
+
const nDecided = decided.length;
|
|
69
|
+
const hasDet = decided.some((d) => d.source === "deterministic");
|
|
70
|
+
const hasJudge = decided.some((d) => d.source === "judge");
|
|
71
|
+
const source = hasDet && hasJudge ? "mixed" : hasJudge ? "judge" : "deterministic";
|
|
72
|
+
if (nDecided === 0) {
|
|
73
|
+
return { winRate: 0, winRateCi95: [0, 0], nDecided: 0, wins: 0, ties: 0, losses: 0, nUndecided, source };
|
|
74
|
+
}
|
|
75
|
+
const values = decided.map((d) => winValue(d.outcome));
|
|
76
|
+
const winRate = values.reduce((a, b) => a + b, 0) / nDecided;
|
|
77
|
+
// Bootstrap the per-sample win values (the pairing is already fixed), take each
|
|
78
|
+
// resample's mean, then the 2.5/97.5 percentiles.
|
|
79
|
+
const rng = (0, bootstrap_1.mulberry32)(seed);
|
|
80
|
+
const means = [];
|
|
81
|
+
for (let b = 0; b < reps; b += 1) {
|
|
82
|
+
let sum = 0;
|
|
83
|
+
for (let i = 0; i < nDecided; i += 1)
|
|
84
|
+
sum += values[Math.floor(rng() * nDecided)];
|
|
85
|
+
means.push(sum / nDecided);
|
|
86
|
+
}
|
|
87
|
+
means.sort((a, b) => a - b);
|
|
88
|
+
const lo = means[Math.floor(0.025 * reps)];
|
|
89
|
+
const hi = means[Math.min(reps - 1, Math.floor(0.975 * reps))];
|
|
90
|
+
return { winRate, winRateCi95: [lo, hi], nDecided, wins, ties, losses, nUndecided, source };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Score ONE arm's pairwise standing vs the reference over the paired replay set
|
|
94
|
+
* (N5). Deterministic-first: each paired sample's verdict is the sign of the two
|
|
95
|
+
* value-accuracy scores; a sample neither side scored falls to the capped judge
|
|
96
|
+
* (when supplied), else it is undecided (counted, excluded from the rate). Pairs
|
|
97
|
+
* by captureId; unpaired samples are ignored. Returns null stats when there are
|
|
98
|
+
* NO paired samples at all. `outcomes` maps captureId → verdict (for N6 mining).
|
|
99
|
+
*/
|
|
100
|
+
async function scoreArmPairwise(armSamples, refSamples, judge) {
|
|
101
|
+
const refByCapture = new Map(refSamples.map((s) => [s.captureId, s]));
|
|
102
|
+
const decided = [];
|
|
103
|
+
const outcomes = new Map();
|
|
104
|
+
let paired = 0;
|
|
105
|
+
let undecided = 0;
|
|
106
|
+
for (const arm of armSamples) {
|
|
107
|
+
const ref = refByCapture.get(arm.captureId);
|
|
108
|
+
if (ref === undefined)
|
|
109
|
+
continue;
|
|
110
|
+
paired += 1;
|
|
111
|
+
const det = deterministicPairwise(arm.valueAccuracy, ref.valueAccuracy);
|
|
112
|
+
if (det !== null) {
|
|
113
|
+
decided.push({ outcome: det, source: "deterministic" });
|
|
114
|
+
outcomes.set(arm.captureId, det);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
// Deterministic can't decide (a side lacks value accuracy). Only a PRODUCED
|
|
118
|
+
// pair can be judged (both need an output); the judge is capped internally.
|
|
119
|
+
if (judge !== null && arm.failureClass === null && ref.failureClass === null) {
|
|
120
|
+
const verdict = await judge.compare(arm.outputText ?? null, ref.outputText ?? null, arm.inputText ?? null);
|
|
121
|
+
if (verdict !== null) {
|
|
122
|
+
decided.push({ outcome: verdict, source: "judge" });
|
|
123
|
+
outcomes.set(arm.captureId, verdict);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
undecided += 1;
|
|
128
|
+
}
|
|
129
|
+
if (paired === 0)
|
|
130
|
+
return { stats: null, outcomes };
|
|
131
|
+
return { stats: aggregatePairwise(decided, undecided), outcomes };
|
|
132
|
+
}
|
|
133
|
+
// ── The opt-in, capped, metered pairwise judge (tier 2) ──────────────────────
|
|
134
|
+
const PAIRWISE_SCHEMA = {
|
|
135
|
+
type: "object",
|
|
136
|
+
properties: { verdict: { type: "string", enum: ["A", "B", "tie"] }, reason: { type: "string" } },
|
|
137
|
+
required: ["verdict"],
|
|
138
|
+
additionalProperties: true,
|
|
139
|
+
};
|
|
140
|
+
const PAIRWISE_RUBRIC = "You are judging PAIRWISE PREFERENCE between two candidate outputs for ONE task — " +
|
|
141
|
+
"which is the better answer for VALUE (correctness and completeness), not merely which " +
|
|
142
|
+
"is better-formed. A is one candidate; B is the other.\n\nTASK INPUT:\n{{task_input}}\n\n" +
|
|
143
|
+
"OUTPUT A:\n{{output_a}}\n\nOUTPUT B:\n{{output_b}}\n\n" +
|
|
144
|
+
'Respond with ONLY compact JSON: {"verdict": "A" | "B" | "tie", "reason": "<one sentence>"}. ' +
|
|
145
|
+
'Answer "A" if A is better, "B" if B is better, "tie" if they are equally good.';
|
|
146
|
+
/** Build the synthetic pairwise-judge route for a base route + judge model. The
|
|
147
|
+
* judge model is declared WITHOUT its trial flag so it serves live here. */
|
|
148
|
+
function buildPairwiseJudgeRoute(baseRoute, model) {
|
|
149
|
+
return Object.freeze({
|
|
150
|
+
route: `__bakeoff_pairwise__:${baseRoute.route}`,
|
|
151
|
+
version: baseRoute.version,
|
|
152
|
+
outputSchema: Object.freeze({ ...PAIRWISE_SCHEMA }),
|
|
153
|
+
candidates: Object.freeze([{ provider: model.provider, model: model.model }]),
|
|
154
|
+
require: Object.freeze([]),
|
|
155
|
+
prefer: Object.freeze([]),
|
|
156
|
+
systemTemplate: PAIRWISE_RUBRIC,
|
|
157
|
+
variables: Object.freeze(["output_a", "output_b", "task_input"]),
|
|
158
|
+
policy: Object.freeze({ retries: Object.freeze({}), timeoutMs: baseRoute.policy.timeoutMs, tier: "standard", json: "json_mode" }),
|
|
159
|
+
variants: Object.freeze([]),
|
|
160
|
+
capture: false,
|
|
161
|
+
fingerprintCell: null,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function asText(v) {
|
|
165
|
+
return typeof v === "string" ? v : JSON.stringify(v ?? null);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Build a pairwise judge over the run loop (mirrors makeJudgeScorer). Each arm
|
|
169
|
+
* gets a FRESH judge so `maxSamples` is per arm. The comparison is presented in a
|
|
170
|
+
* FIXED order (A = served arm, B = reference); position bias is a known limit,
|
|
171
|
+
* noted in verification — a fixed order keeps the judge DETERMINISTIC for tests.
|
|
172
|
+
*/
|
|
173
|
+
function makePairwiseJudge(deps, baseRoute, judge) {
|
|
174
|
+
const judgeRoute = buildPairwiseJudgeRoute(baseRoute, judge.model);
|
|
175
|
+
const routes = {
|
|
176
|
+
get: (r) => (r === judgeRoute.route ? judgeRoute : deps.routes.get(r)),
|
|
177
|
+
list: () => Object.freeze([...deps.routes.list(), judgeRoute]),
|
|
178
|
+
};
|
|
179
|
+
const judgeDeps = { ...deps, routes, captures: null, mode: "live" };
|
|
180
|
+
const envelope = `bakeoff-pairwise:${(0, node_crypto_1.randomUUID)()}`;
|
|
181
|
+
let used = 0;
|
|
182
|
+
return {
|
|
183
|
+
used: () => used,
|
|
184
|
+
async compare(armOutput, refOutput, taskInput) {
|
|
185
|
+
if (used >= judge.maxSamples)
|
|
186
|
+
return null;
|
|
187
|
+
used += 1;
|
|
188
|
+
try {
|
|
189
|
+
const result = await (0, run_1.run)(judgeDeps, judgeRoute.route, {
|
|
190
|
+
input: { output_a: asText(armOutput), output_b: asText(refOutput), task_input: asText(taskInput) },
|
|
191
|
+
// `bakeoff_pairwise` marks the row a pairwise-judge inference (the typed
|
|
192
|
+
// `rung` column is recorder-owned, so a distinct tag key is used); `lane:
|
|
193
|
+
// trial` keeps judge spend off the route's live rollups/odometer.
|
|
194
|
+
tags: { bakeoff_pairwise: "1", lane: "trial" },
|
|
195
|
+
budget: { envelope },
|
|
196
|
+
});
|
|
197
|
+
const verdict = result.output?.verdict;
|
|
198
|
+
if (verdict === "A")
|
|
199
|
+
return "win";
|
|
200
|
+
if (verdict === "B")
|
|
201
|
+
return "loss";
|
|
202
|
+
if (verdict === "tie")
|
|
203
|
+
return "tie";
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
if (!(err instanceof failures_1.RigFailureError))
|
|
208
|
+
throw err;
|
|
209
|
+
// The metered attempt row(s) still stand; this sample gets no verdict.
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
package/dist/bakeoff/replay.d.ts
CHANGED
|
@@ -30,6 +30,12 @@ export interface ReplayRequest {
|
|
|
30
30
|
* sample for the human value review. Off by default (bounds memory + keeps
|
|
31
31
|
* the pure-metrics path clean). */
|
|
32
32
|
readonly keepOutputs?: boolean;
|
|
33
|
+
/** N5: retain `inputText` + `outputText` on each sample for the pairwise judge
|
|
34
|
+
* (arm-vs-reference comparison in the engine), WITHOUT the keepOutputs sink
|
|
35
|
+
* persistence. Set by the engine only when `bakeoff.pairwise.judge` is declared.
|
|
36
|
+
* A superset trigger of `keepOutputs` for retention; the sink stays gated on
|
|
37
|
+
* `keepOutputs` alone so pairwise retention never persists review samples. */
|
|
38
|
+
readonly retainOutputForPairwise?: boolean;
|
|
33
39
|
/** RB-1: corrected/golden expected outputs keyed by captureId (eval-case path).
|
|
34
40
|
* Presence in the map ⇒ this sample has an expected value; `value` may be any
|
|
35
41
|
* JSON (including null). Quality tier 1 (eval case) when present. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/bakeoff/replay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAI1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;uCAEmC;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;yEAEqE;IACrE,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACrE;sFACkF;IAClF,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B;;;;;iFAK6E;IAC7E,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B;wEACoE;IACpE,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC;;;oCAGgC;IAChC,QAAQ,CAAC,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;yDACqD;IACrD,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,oEAAoE;IACpE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;sDACkD;IAClD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,YAAY,EACtB,GAAG,EAAE,aAAa,GACjB,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/bakeoff/replay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAI1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;uCAEmC;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;kFAI8E;IAC9E,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;yEAEqE;IACrE,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACrE;sFACkF;IAClF,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B;;;;;iFAK6E;IAC7E,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B;wEACoE;IACpE,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC;;;oCAGgC;IAChC,QAAQ,CAAC,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;yDACqD;IACrD,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,oEAAoE;IACpE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;sDACkD;IAClD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,YAAY,EACtB,GAAG,EAAE,aAAa,GACjB,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAyIxC"}
|
package/dist/bakeoff/replay.js
CHANGED
|
@@ -49,6 +49,10 @@ async function replayVariant(deps, captures, req) {
|
|
|
49
49
|
envelopeBudgetUsd: req.envelopeUsd,
|
|
50
50
|
mode,
|
|
51
51
|
};
|
|
52
|
+
// Retain per-sample input/output text when the caller asked (keepOutputs sink,
|
|
53
|
+
// or N5 pairwise judge). Same fields either way; the sink persist stays gated
|
|
54
|
+
// on keepOutputs alone (engine.ts).
|
|
55
|
+
const retainText = req.keepOutputs === true || req.retainOutputForPairwise === true;
|
|
52
56
|
const results = [];
|
|
53
57
|
for (const capture of rows) {
|
|
54
58
|
const sampleTag = `${replayId}:${capture.inferenceId}`;
|
|
@@ -81,7 +85,7 @@ async function replayVariant(deps, captures, req) {
|
|
|
81
85
|
repaired = result.meta.repairedBy !== null;
|
|
82
86
|
outputValue = result.output;
|
|
83
87
|
produced = true;
|
|
84
|
-
if (
|
|
88
|
+
if (retainText)
|
|
85
89
|
outputText = JSON.stringify(result.output, null, 2);
|
|
86
90
|
if (req.qualityGate !== undefined) {
|
|
87
91
|
gated = { output: result.output, provider: result.meta.provider, model: result.meta.model, servedVariant: result.meta.servedVariant };
|
|
@@ -145,7 +149,7 @@ async function replayVariant(deps, captures, req) {
|
|
|
145
149
|
pricingMissing,
|
|
146
150
|
latencyMs,
|
|
147
151
|
failureClass,
|
|
148
|
-
...(
|
|
152
|
+
...(retainText
|
|
149
153
|
? { inputText: capture.variablesJson, outputText }
|
|
150
154
|
: {}),
|
|
151
155
|
});
|
package/dist/bakeoff/report.d.ts
CHANGED
|
@@ -36,6 +36,13 @@ export interface CacheAdjustedRow {
|
|
|
36
36
|
* Empty input ⇒ no lines (the section is omitted entirely — back-compat).
|
|
37
37
|
*/
|
|
38
38
|
export declare function formatCacheAdjustedSection(rows: readonly CacheAdjustedRow[]): string[];
|
|
39
|
+
/**
|
|
40
|
+
* N6 disagreement summary lines: the count of mined eval-case candidates (with
|
|
41
|
+
* the honest "of N found" when the list was capped), the per-reason breakdown,
|
|
42
|
+
* and the top candidates as captureId pointers an operator may promote. Pure
|
|
43
|
+
* (returns strings); empty when nothing disagreed. Exported for direct testing.
|
|
44
|
+
*/
|
|
45
|
+
export declare function formatDisagreementSummary(artifact: BakeoffArtifact): string[];
|
|
39
46
|
export declare function printMatrix(artifact: BakeoffArtifact, cacheAdjusted?: readonly CacheAdjustedRow[]): void;
|
|
40
47
|
/**
|
|
41
48
|
* `modelrig bakeoff --from-eval-cases <task> --variants a,b,c` — replay a task's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/bakeoff/report.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/bakeoff/report.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AA2CpD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAexE;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,gFAAgF;IAChF,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAC;CAC3C;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,EAAE,CAgCtF;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,EAAE,CAiB7E;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,eAAe,EAAE,aAAa,GAAE,SAAS,gBAAgB,EAAO,GAAG,IAAI,CAwD5G;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,eAAe,GAAG,SAAS,EACjC,MAAM,EAAE,OAAO,EACf,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC,MAAM,CAAC,CA+CjB"}
|
package/dist/bakeoff/report.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.bakeoffHeadline = bakeoffHeadline;
|
|
10
10
|
exports.formatCacheAdjustedSection = formatCacheAdjustedSection;
|
|
11
|
+
exports.formatDisagreementSummary = formatDisagreementSummary;
|
|
11
12
|
exports.printMatrix = printMatrix;
|
|
12
13
|
exports.runEvalCaseBakeoffCli = runEvalCaseBakeoffCli;
|
|
13
14
|
const node_fs_1 = require("node:fs");
|
|
@@ -35,6 +36,16 @@ function fmtRegret(m) {
|
|
|
35
36
|
function fmtQuality(m) {
|
|
36
37
|
return m.qualityMean === null ? "—" : `${(m.qualityMean * 100).toFixed(0)}% ${m.qualityMetric}`;
|
|
37
38
|
}
|
|
39
|
+
/** N5: an arm's pairwise-vs-reference cell — "63% [51…74] n=30", "ref", or "—". */
|
|
40
|
+
function fmtPairwise(m) {
|
|
41
|
+
if (m.isReference === true)
|
|
42
|
+
return "ref";
|
|
43
|
+
const p = m.pairwise;
|
|
44
|
+
if (p == null)
|
|
45
|
+
return "—";
|
|
46
|
+
const pct = (v) => (v * 100).toFixed(0);
|
|
47
|
+
return `${pct(p.winRate)}% [${pct(p.winRateCi95[0])}…${pct(p.winRateCi95[1])}] n=${p.nDecided}`;
|
|
48
|
+
}
|
|
38
49
|
/** The reference $/1k or per-run ceiling from the objective, formatted, or null
|
|
39
50
|
* when no ceiling is declared. */
|
|
40
51
|
function fmtCeiling(artifact) {
|
|
@@ -107,14 +118,43 @@ function formatCacheAdjustedSection(rows) {
|
|
|
107
118
|
}
|
|
108
119
|
return lines;
|
|
109
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* N6 disagreement summary lines: the count of mined eval-case candidates (with
|
|
123
|
+
* the honest "of N found" when the list was capped), the per-reason breakdown,
|
|
124
|
+
* and the top candidates as captureId pointers an operator may promote. Pure
|
|
125
|
+
* (returns strings); empty when nothing disagreed. Exported for direct testing.
|
|
126
|
+
*/
|
|
127
|
+
function formatDisagreementSummary(artifact) {
|
|
128
|
+
const d = artifact.disagreement;
|
|
129
|
+
if (d === undefined || d.found === 0)
|
|
130
|
+
return [];
|
|
131
|
+
const reasons = Object.entries(d.counts)
|
|
132
|
+
.filter(([, n]) => n > 0)
|
|
133
|
+
.map(([r, n]) => `${r}=${n}`)
|
|
134
|
+
.join(", ");
|
|
135
|
+
const capNote = d.cappedAt !== null ? ` (showing top ${d.candidates.length} of ${d.found} — cap ${d.cappedAt})` : "";
|
|
136
|
+
const lines = [
|
|
137
|
+
`disagreement: ${d.found} eval-case candidate${d.found === 1 ? "" : "s"} mined from ${d.examined} samples${capNote} — ` +
|
|
138
|
+
"NOT auto-promoted; promote with artifact.promoteToEval.",
|
|
139
|
+
];
|
|
140
|
+
if (reasons !== "")
|
|
141
|
+
lines.push(` reasons: ${reasons}`);
|
|
142
|
+
for (const c of d.candidates.slice(0, 5)) {
|
|
143
|
+
lines.push(` ${c.captureId} [${c.arm}] score ${c.score.toFixed(2)} — ${c.reasons.join(", ")}`);
|
|
144
|
+
}
|
|
145
|
+
return lines;
|
|
146
|
+
}
|
|
110
147
|
function printMatrix(artifact, cacheAdjusted = []) {
|
|
111
148
|
console.log(`bakeoff ${artifact.id}\nroute ${artifact.route}@${artifact.routeVersion} · replayed ${artifact.replayN} captured inputs · ${artifact.ranAt}`);
|
|
112
149
|
// RB-1: a reference arm is present ⇒ show quality + regret columns and badge
|
|
113
150
|
// the reference row; otherwise the pre-RB-1 matrix stands byte-for-byte.
|
|
114
151
|
const hasReference = artifact.variants.some((m) => m.isReference === true);
|
|
152
|
+
const hasPairwise = artifact.variants.some((m) => m.pairwise != null);
|
|
115
153
|
const header = ["variant", "n", "conform", "ci95", "repair", "repair$", "lat(ms)", "eff-$/1K-conf"];
|
|
116
154
|
if (hasReference)
|
|
117
155
|
header.push("quality", "regret(pts)");
|
|
156
|
+
if (hasPairwise)
|
|
157
|
+
header.push("pairwise-win");
|
|
118
158
|
const rows = artifact.variants.map((m) => {
|
|
119
159
|
const base = [
|
|
120
160
|
m.isReference === true ? `${m.variant} [ref]` : m.variant,
|
|
@@ -128,6 +168,8 @@ function printMatrix(artifact, cacheAdjusted = []) {
|
|
|
128
168
|
];
|
|
129
169
|
if (hasReference)
|
|
130
170
|
base.push(fmtQuality(m), fmtRegret(m));
|
|
171
|
+
if (hasPairwise)
|
|
172
|
+
base.push(fmtPairwise(m));
|
|
131
173
|
return base;
|
|
132
174
|
});
|
|
133
175
|
const widths = header.map((h, c) => Math.max(h.length, ...rows.map((r) => r[c].length)));
|
|
@@ -153,6 +195,10 @@ function printMatrix(artifact, cacheAdjusted = []) {
|
|
|
153
195
|
else {
|
|
154
196
|
console.log("proposal: none (no variant beats the incumbent's gates)");
|
|
155
197
|
}
|
|
198
|
+
// N6 disagreement summary — printed only when candidates were mined; absent ⇒
|
|
199
|
+
// byte-identical to the pre-N6 matrix output.
|
|
200
|
+
for (const l of formatDisagreementSummary(artifact))
|
|
201
|
+
console.log(l);
|
|
156
202
|
// Cache-adjusted section (spec §5.4) — printed only when the caller supplied
|
|
157
203
|
// simulator rows; absent ⇒ the matrix output is byte-identical to pre-WS-B.
|
|
158
204
|
for (const l of formatCacheAdjustedSection(cacheAdjusted))
|
package/dist/bakeoff/types.d.ts
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
* value accuracy, paired regret vs a reference arm, and an objective-aware gate.
|
|
6
6
|
*/
|
|
7
7
|
import type { ObjectiveMode } from "../types/route";
|
|
8
|
+
import type { PairwiseStats } from "./pairwise";
|
|
9
|
+
import type { DisagreementReport } from "./disagreement";
|
|
10
|
+
import type { MaskArmProof } from "../mask/proof";
|
|
8
11
|
/** Which quality metric an arm's numbers are on (spec §2): value accuracy when a
|
|
9
12
|
* golden / value check / judge scored it, else schema conformance. */
|
|
10
13
|
export type QualityMetric = "value_accuracy" | "conformance";
|
|
@@ -57,6 +60,11 @@ export interface VariantMetrics {
|
|
|
57
60
|
readonly regret?: RegretStats | null;
|
|
58
61
|
/** RB-1: the reference arm hit its envelope — regret is on the paired subset. */
|
|
59
62
|
readonly referencePartial?: boolean;
|
|
63
|
+
/** N5: this arm's pairwise standing vs the reference (win rate + seeded CI).
|
|
64
|
+
* Null on the reference arm and when no pairwise signal was decidable. A
|
|
65
|
+
* REPORTED signal beside regret; gates a proposal only when the route sets
|
|
66
|
+
* `objective.pairwiseAware`. */
|
|
67
|
+
readonly pairwise?: PairwiseStats | null;
|
|
60
68
|
}
|
|
61
69
|
/** RB-1: the resolved objective recorded on the artifact (evidence + report). */
|
|
62
70
|
export interface BakeoffObjective {
|
|
@@ -67,6 +75,9 @@ export interface BakeoffObjective {
|
|
|
67
75
|
readonly usdPerRun?: number;
|
|
68
76
|
} | null;
|
|
69
77
|
readonly lambda: number | null;
|
|
78
|
+
/** N5: the pairwise-aware gate knob (default false). Recorded on the artifact so
|
|
79
|
+
* the report/PR body/evidence say whether pairwise gated the proposal. */
|
|
80
|
+
readonly pairwiseAware?: boolean;
|
|
70
81
|
}
|
|
71
82
|
/** RB-1: the resolved reference arm recorded on the artifact. */
|
|
72
83
|
export interface BakeoffReference {
|
|
@@ -104,5 +115,18 @@ export interface BakeoffArtifact {
|
|
|
104
115
|
readonly reference?: BakeoffReference | null;
|
|
105
116
|
/** RB-1: the metric the bake-off's quality numbers are on. */
|
|
106
117
|
readonly qualityMetric?: QualityMetric;
|
|
118
|
+
/** N6: verdict-disagreement candidates mined from this bake-off's replay set —
|
|
119
|
+
* a ranked list of already-captured pointers an operator MAY promote to eval
|
|
120
|
+
* cases (never auto-promoted). Rides the LOCAL artifact only (captureIds are not
|
|
121
|
+
* mirrored). Absent on a pre-N5/N6 artifact or when no reference/arm signal
|
|
122
|
+
* disagreed. */
|
|
123
|
+
readonly disagreement?: DisagreementReport;
|
|
124
|
+
/** CS-1 Proven Masking (spec §7) — the masked-arm proof for a `--mask-policy`
|
|
125
|
+
* run: the Detector finds no span in the masked arm (`proven: true`), with the
|
|
126
|
+
* unmasked arm's detections as the Δ the mask removed. Counts by type only,
|
|
127
|
+
* never a value. Additive & optional (RB-1 fence — rides the matrix jsonb
|
|
128
|
+
* wholesale, does NOT restructure the replay engine); absent on an ordinary
|
|
129
|
+
* bake-off. The Chunk-B masked-vs-unmasked Δ-value-accuracy arm extends this. */
|
|
130
|
+
readonly maskArm?: MaskArmProof;
|
|
107
131
|
}
|
|
108
132
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/bakeoff/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/bakeoff/types.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;GAgBG;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;GAgBG;AAiDH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACpC;AAeD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAuBxD;AAohBD,wBAAsB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAuCnE"}
|
package/dist/cli.js
CHANGED
|
@@ -30,7 +30,7 @@ const failures_1 = require("./types/failures");
|
|
|
30
30
|
const failures_teaching_1 = require("./types/failures-teaching");
|
|
31
31
|
const config_1 = require("./config");
|
|
32
32
|
const index_1 = require("./index");
|
|
33
|
-
const
|
|
33
|
+
const status_cli_1 = require("./status-cli");
|
|
34
34
|
const engine_1 = require("./bakeoff/engine");
|
|
35
35
|
const samples_1 = require("./bakeoff/samples");
|
|
36
36
|
const report_1 = require("./bakeoff/report");
|
|
@@ -48,6 +48,7 @@ function objectiveOf(flags) {
|
|
|
48
48
|
const cli_run_1 = require("./verify/cli-run");
|
|
49
49
|
const loader_1 = require("./routes/loader");
|
|
50
50
|
const rawonly_1 = require("./run/rawonly");
|
|
51
|
+
const mask_policy_cli_1 = require("./mask/mask-policy-cli");
|
|
51
52
|
const validate_cli_1 = require("./validate-cli");
|
|
52
53
|
const store_1 = require("./telemetry/store");
|
|
53
54
|
const budget_1 = require("./watch/budget");
|
|
@@ -236,6 +237,11 @@ async function cmdBakeoff(flags) {
|
|
|
236
237
|
throw new Error("--budget-usd must be positive");
|
|
237
238
|
return (0, report_1.runEvalCaseBakeoffCli)(config, evalTask, vs, env, keepOutputsMode(flags), flags.bare.has("json"), objectiveOf(flags));
|
|
238
239
|
}
|
|
240
|
+
// CS-1 (spec §7): --mask-policy proves no detected span leaves the masked arm
|
|
241
|
+
// (additive early-return; does not touch the RB-1 replay engine). Handler in mask/.
|
|
242
|
+
const maskPolicyRoute = flags.named.get("mask-policy");
|
|
243
|
+
if (maskPolicyRoute !== undefined)
|
|
244
|
+
return (0, mask_policy_cli_1.runMaskPolicyProofCli)(config, flags, maskPolicyRoute);
|
|
239
245
|
const route = flags.named.get("route");
|
|
240
246
|
const variantsRaw = flags.named.get("variants");
|
|
241
247
|
const lastRaw = flags.named.get("replay-last");
|
|
@@ -486,13 +492,6 @@ async function cmdServe() {
|
|
|
486
492
|
// The server owns the process from here (it holds the listener open).
|
|
487
493
|
return 0;
|
|
488
494
|
}
|
|
489
|
-
/** `modelrig status` — print the runtime posture (§2): export mode, artifact
|
|
490
|
-
* namespace on/off + WHY, content custody, OTLP. Reads config only (no rig, no
|
|
491
|
-
* routes dir), so it works anywhere the env is set. */
|
|
492
|
-
function cmdStatus() {
|
|
493
|
-
console.log((0, status_1.formatStatusReport)((0, status_1.buildRigStatus)((0, config_1.loadConfigFromEnv)())));
|
|
494
|
-
return 0;
|
|
495
|
-
}
|
|
496
495
|
/** `modelrig judge <run>` — E5 V.4: explicitly ask the bounded model-judge to
|
|
497
496
|
* score a run. Goes through the API boundary (T12: the CLI never holds a DB
|
|
498
497
|
* client) — POST /v1/runs/:id/judge on the configured server, authed with a
|
|
@@ -544,6 +543,7 @@ const USAGE = [
|
|
|
544
543
|
" modelrig serve (needs the modelrig-server package)",
|
|
545
544
|
" modelrig bakeoff --route <route> --variants a,b,c --replay-last N [--budget-usd usd] [--objective quality-first|balanced|cost-first] [--keep-outputs[=all]] [--json] [--no-mirror]",
|
|
546
545
|
" modelrig bakeoff --from-proposal <id> [--replay-last N] [--keep-outputs[=all]] [--json]",
|
|
546
|
+
" modelrig bakeoff --mask-policy <route> [--replay-last N] (CS-1: prove the masked arm leaks no detected span)",
|
|
547
547
|
" modelrig watch run [--events fixtures.json] [--json]",
|
|
548
548
|
" modelrig verify-swap --route <route> --captures <fixture.json> [--budget-usd usd] [--out report.md] [--json]",
|
|
549
549
|
" modelrig swap execute <proposalId> --approve [--approved-by name]",
|
|
@@ -564,7 +564,7 @@ async function main(argv) {
|
|
|
564
564
|
const flags = parseArgs(rest);
|
|
565
565
|
switch (command) {
|
|
566
566
|
case "status":
|
|
567
|
-
return
|
|
567
|
+
return (0, status_cli_1.runStatusCli)();
|
|
568
568
|
case "validate":
|
|
569
569
|
return (0, validate_cli_1.runValidateCli)();
|
|
570
570
|
case "bakeoff":
|