@trazum/core 1.50.6 → 1.50.8
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/experiment.d.ts +134 -0
- package/dist/experiment.d.ts.map +1 -0
- package/dist/experiment.js +168 -0
- package/dist/experiment.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/quality-gate.d.ts +134 -0
- package/dist/quality-gate.d.ts.map +1 -0
- package/dist/quality-gate.js +185 -0
- package/dist/quality-gate.js.map +1 -0
- package/package.json +1 -1
- package/src/experiment.ts +275 -0
- package/src/index.ts +18 -0
- package/src/quality-gate.ts +253 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two arms on real traffic, and the three things that stop it being theatre.
|
|
3
|
+
*
|
|
4
|
+
* `eval` compares two prompts on cases somebody wrote; `route` compares two
|
|
5
|
+
* models on the same. Both measure agreement in a laboratory. The traffic is
|
|
6
|
+
* the only place the real question gets answered — and the moment a comparison
|
|
7
|
+
* runs on real traffic, three failures become available that a laboratory does
|
|
8
|
+
* not have.
|
|
9
|
+
*
|
|
10
|
+
* ## 1. A winner where there is none
|
|
11
|
+
*
|
|
12
|
+
* Two arms always produce two numbers, and one of them is always larger. An
|
|
13
|
+
* A/B report that names a winner from that is a coin flip with a dashboard.
|
|
14
|
+
* The verdict here is **three-valued**, the way `verify`'s has been since
|
|
15
|
+
* 1.39: A wins, B wins, or **not separable on this traffic** — and the third
|
|
16
|
+
* one comes with the number of outcomes per arm that *would* separate them, so
|
|
17
|
+
* "run it longer" is a quantified instruction rather than a shrug.
|
|
18
|
+
*
|
|
19
|
+
* ## 2. Peeking
|
|
20
|
+
*
|
|
21
|
+
* A test stopped on the first afternoon it looked good is not a test. The
|
|
22
|
+
* stopping rule is declared **before** the experiment starts, and the report
|
|
23
|
+
* says whether it was honoured. It cannot enforce that — nobody can stop
|
|
24
|
+
* somebody reading a number early — but it can make an early stop *visible*
|
|
25
|
+
* to whoever reads the result later, which is the part that matters.
|
|
26
|
+
*
|
|
27
|
+
* ## 3. Quality and cost judged apart, then together
|
|
28
|
+
*
|
|
29
|
+
* The interesting arm is almost never better *and* cheaper. It is better and
|
|
30
|
+
* dearer, and the decision needs one figure nobody computes: **what an extra
|
|
31
|
+
* success costs**. That is the marginal figure — the difference in spend over
|
|
32
|
+
* the difference in successes — and it is the number a product decision
|
|
33
|
+
* actually turns on, printed rather than left as an exercise.
|
|
34
|
+
*
|
|
35
|
+
* ## The statistics are shown, not asserted
|
|
36
|
+
*
|
|
37
|
+
* Wilson score intervals per arm and Newcombe's interval on the difference,
|
|
38
|
+
* because both behave at the small samples this will actually see. The
|
|
39
|
+
* intervals are returned, not just the verdict: a reader who disagrees with
|
|
40
|
+
* the threshold can see the numbers it was applied to, which is the same
|
|
41
|
+
* discipline `eval` established by running the original twice before judging
|
|
42
|
+
* anything against it.
|
|
43
|
+
*/
|
|
44
|
+
import type { OutcomeTally, OutcomeVocabulary } from './outcome.js';
|
|
45
|
+
/**
|
|
46
|
+
* The declaration, made before the experiment runs.
|
|
47
|
+
*
|
|
48
|
+
* Its whole purpose is to exist *earlier* than the result. A stopping rule
|
|
49
|
+
* invented after looking at the numbers is not a stopping rule.
|
|
50
|
+
*/
|
|
51
|
+
export interface ExperimentDeclaration {
|
|
52
|
+
/** Two arms. More would need a multiple-comparison correction nobody asked for. */
|
|
53
|
+
arms: [string, string];
|
|
54
|
+
/**
|
|
55
|
+
* Outcomes each arm must record before the result may be read.
|
|
56
|
+
*
|
|
57
|
+
* Declared as a count rather than a duration, because a duration is a proxy
|
|
58
|
+
* for a count and the proxy breaks the week traffic doubles.
|
|
59
|
+
*/
|
|
60
|
+
minOutcomesPerArm: number;
|
|
61
|
+
}
|
|
62
|
+
/** What one arm actually did. */
|
|
63
|
+
export interface ExperimentArm {
|
|
64
|
+
name: string;
|
|
65
|
+
tally: OutcomeTally;
|
|
66
|
+
/** Everything this arm spent, recorded outcome or not. */
|
|
67
|
+
totalUsd: number;
|
|
68
|
+
}
|
|
69
|
+
export interface ArmResult {
|
|
70
|
+
name: string;
|
|
71
|
+
successes: number;
|
|
72
|
+
/** Calls carrying a *declared* outcome — the denominator. */
|
|
73
|
+
recorded: number;
|
|
74
|
+
/** Successes over recorded, or null when nothing was recorded. */
|
|
75
|
+
rate: number | null;
|
|
76
|
+
/** Wilson score interval on that rate, or null. */
|
|
77
|
+
interval: {
|
|
78
|
+
low: number;
|
|
79
|
+
high: number;
|
|
80
|
+
} | null;
|
|
81
|
+
/** Spend on calls carrying a declared outcome. */
|
|
82
|
+
recordedUsd: number;
|
|
83
|
+
}
|
|
84
|
+
export type Separation = 'a-wins' | 'b-wins' | 'not-separable';
|
|
85
|
+
export type NotSeparableReason = 'interval-includes-zero' | 'no-difference-observed' | 'nothing-recorded';
|
|
86
|
+
export interface Marginal {
|
|
87
|
+
/**
|
|
88
|
+
* What one extra success costs, going from the worse arm to the better one.
|
|
89
|
+
*
|
|
90
|
+
* `(dearer spend − cheaper spend) / (more successes − fewer successes)`,
|
|
91
|
+
* both per call so arms of different sizes compare. Null when the better arm
|
|
92
|
+
* is also the cheaper one, because then nothing is being bought and a
|
|
93
|
+
* "cost per extra success" would be a negative number people would quote.
|
|
94
|
+
*/
|
|
95
|
+
usdPerExtraSuccess: number | null;
|
|
96
|
+
/** The arm that resolved more, by rate. */
|
|
97
|
+
better: string;
|
|
98
|
+
/** Whether that arm also costs more per call. */
|
|
99
|
+
dearer: boolean;
|
|
100
|
+
}
|
|
101
|
+
export interface ExperimentResult {
|
|
102
|
+
a: ArmResult;
|
|
103
|
+
b: ArmResult;
|
|
104
|
+
separation: Separation;
|
|
105
|
+
/** Why, when not separable. A refusal never arrives bare. */
|
|
106
|
+
notSeparable: NotSeparableReason | null;
|
|
107
|
+
/** Newcombe's interval on (rate A − rate B), or null. */
|
|
108
|
+
difference: {
|
|
109
|
+
point: number;
|
|
110
|
+
low: number;
|
|
111
|
+
high: number;
|
|
112
|
+
} | null;
|
|
113
|
+
/**
|
|
114
|
+
* Outcomes **per arm** that would separate the observed difference, or null.
|
|
115
|
+
*
|
|
116
|
+
* Null when the arms recorded the same rate: no sample size separates a
|
|
117
|
+
* difference of zero, and returning a very large number would read as "keep
|
|
118
|
+
* going" when the honest answer is "there is nothing here to find".
|
|
119
|
+
*/
|
|
120
|
+
outcomesNeededPerArm: number | null;
|
|
121
|
+
stopping: {
|
|
122
|
+
declared: number;
|
|
123
|
+
/** Whether both arms cleared the declared minimum. */
|
|
124
|
+
honoured: boolean;
|
|
125
|
+
/** The arm that has not, when one has not. */
|
|
126
|
+
short: string | null;
|
|
127
|
+
};
|
|
128
|
+
marginal: Marginal | null;
|
|
129
|
+
}
|
|
130
|
+
export declare function runExperiment(declaration: ExperimentDeclaration, arms: {
|
|
131
|
+
a: ExperimentArm;
|
|
132
|
+
b: ExperimentArm;
|
|
133
|
+
}, vocabulary: OutcomeVocabulary | null): ExperimentResult;
|
|
134
|
+
//# sourceMappingURL=experiment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"experiment.d.ts","sourceRoot":"","sources":["../src/experiment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAMpE;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,mFAAmF;IACnF,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvB;;;;;OAKG;IACH,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,iCAAiC;AACjC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,YAAY,CAAC;IACpB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,mDAAmD;IACnD,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/C,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,eAAe,CAAC;AAE/D,MAAM,MAAM,kBAAkB,GAC1B,wBAAwB,GACxB,wBAAwB,GACxB,kBAAkB,CAAC;AAEvB,MAAM,WAAW,QAAQ;IACvB;;;;;;;OAOG;IACH,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,SAAS,CAAC;IACb,CAAC,EAAE,SAAS,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,6DAA6D;IAC7D,YAAY,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACxC,yDAAyD;IACzD,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChE;;;;;;OAMG;IACH,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,sDAAsD;QACtD,QAAQ,EAAE,OAAO,CAAC;QAClB,8CAA8C;QAC9C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;KACtB,CAAC;IACF,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC3B;AAmCD,wBAAgB,aAAa,CAC3B,WAAW,EAAE,qBAAqB,EAClC,IAAI,EAAE;IAAE,CAAC,EAAE,aAAa,CAAC;IAAC,CAAC,EAAE,aAAa,CAAA;CAAE,EAC5C,UAAU,EAAE,iBAAiB,GAAG,IAAI,GACnC,gBAAgB,CAkGlB"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two arms on real traffic, and the three things that stop it being theatre.
|
|
3
|
+
*
|
|
4
|
+
* `eval` compares two prompts on cases somebody wrote; `route` compares two
|
|
5
|
+
* models on the same. Both measure agreement in a laboratory. The traffic is
|
|
6
|
+
* the only place the real question gets answered — and the moment a comparison
|
|
7
|
+
* runs on real traffic, three failures become available that a laboratory does
|
|
8
|
+
* not have.
|
|
9
|
+
*
|
|
10
|
+
* ## 1. A winner where there is none
|
|
11
|
+
*
|
|
12
|
+
* Two arms always produce two numbers, and one of them is always larger. An
|
|
13
|
+
* A/B report that names a winner from that is a coin flip with a dashboard.
|
|
14
|
+
* The verdict here is **three-valued**, the way `verify`'s has been since
|
|
15
|
+
* 1.39: A wins, B wins, or **not separable on this traffic** — and the third
|
|
16
|
+
* one comes with the number of outcomes per arm that *would* separate them, so
|
|
17
|
+
* "run it longer" is a quantified instruction rather than a shrug.
|
|
18
|
+
*
|
|
19
|
+
* ## 2. Peeking
|
|
20
|
+
*
|
|
21
|
+
* A test stopped on the first afternoon it looked good is not a test. The
|
|
22
|
+
* stopping rule is declared **before** the experiment starts, and the report
|
|
23
|
+
* says whether it was honoured. It cannot enforce that — nobody can stop
|
|
24
|
+
* somebody reading a number early — but it can make an early stop *visible*
|
|
25
|
+
* to whoever reads the result later, which is the part that matters.
|
|
26
|
+
*
|
|
27
|
+
* ## 3. Quality and cost judged apart, then together
|
|
28
|
+
*
|
|
29
|
+
* The interesting arm is almost never better *and* cheaper. It is better and
|
|
30
|
+
* dearer, and the decision needs one figure nobody computes: **what an extra
|
|
31
|
+
* success costs**. That is the marginal figure — the difference in spend over
|
|
32
|
+
* the difference in successes — and it is the number a product decision
|
|
33
|
+
* actually turns on, printed rather than left as an exercise.
|
|
34
|
+
*
|
|
35
|
+
* ## The statistics are shown, not asserted
|
|
36
|
+
*
|
|
37
|
+
* Wilson score intervals per arm and Newcombe's interval on the difference,
|
|
38
|
+
* because both behave at the small samples this will actually see. The
|
|
39
|
+
* intervals are returned, not just the verdict: a reader who disagrees with
|
|
40
|
+
* the threshold can see the numbers it was applied to, which is the same
|
|
41
|
+
* discipline `eval` established by running the original twice before judging
|
|
42
|
+
* anything against it.
|
|
43
|
+
*/
|
|
44
|
+
import { judgeOutcome } from './outcome.js';
|
|
45
|
+
/** z for a two-sided 95% interval, and for 80% power. */
|
|
46
|
+
const Z_95 = 1.959963984540054;
|
|
47
|
+
const Z_POWER_80 = 0.8416212335729143;
|
|
48
|
+
/** Wilson score interval — behaves at the sample sizes this will actually see. */
|
|
49
|
+
function wilson(successes, n, z = Z_95) {
|
|
50
|
+
if (n <= 0)
|
|
51
|
+
return null;
|
|
52
|
+
const p = successes / n;
|
|
53
|
+
const denominator = 1 + (z * z) / n;
|
|
54
|
+
const centre = (p + (z * z) / (2 * n)) / denominator;
|
|
55
|
+
const half = (z * Math.sqrt((p * (1 - p)) / n + (z * z) / (4 * n * n))) / denominator;
|
|
56
|
+
return { low: Math.max(0, centre - half), high: Math.min(1, centre + half) };
|
|
57
|
+
}
|
|
58
|
+
function resultOf(arm, vocabulary) {
|
|
59
|
+
const declared = vocabulary ?? { values: [], success: [] };
|
|
60
|
+
let successes = 0;
|
|
61
|
+
let recorded = 0;
|
|
62
|
+
let recordedUsd = 0;
|
|
63
|
+
for (const entry of arm.tally.byValue) {
|
|
64
|
+
// Undeclared values are out of both halves, as everywhere since 1.50.4: a
|
|
65
|
+
// typo in an exporter must not decide an experiment.
|
|
66
|
+
if (judgeOutcome(entry.value, declared) === 'undeclared')
|
|
67
|
+
continue;
|
|
68
|
+
recorded += entry.calls;
|
|
69
|
+
recordedUsd += entry.usd;
|
|
70
|
+
if (declared.success.includes(entry.value))
|
|
71
|
+
successes += entry.calls;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
name: arm.name,
|
|
75
|
+
successes,
|
|
76
|
+
recorded,
|
|
77
|
+
rate: recorded > 0 ? successes / recorded : null,
|
|
78
|
+
interval: wilson(successes, recorded),
|
|
79
|
+
recordedUsd,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function runExperiment(declaration, arms, vocabulary) {
|
|
83
|
+
const a = resultOf(arms.a, vocabulary);
|
|
84
|
+
const b = resultOf(arms.b, vocabulary);
|
|
85
|
+
const stopping = {
|
|
86
|
+
declared: declaration.minOutcomesPerArm,
|
|
87
|
+
honoured: a.recorded >= declaration.minOutcomesPerArm && b.recorded >= declaration.minOutcomesPerArm,
|
|
88
|
+
short: a.recorded < declaration.minOutcomesPerArm
|
|
89
|
+
? a.name
|
|
90
|
+
: b.recorded < declaration.minOutcomesPerArm
|
|
91
|
+
? b.name
|
|
92
|
+
: null,
|
|
93
|
+
};
|
|
94
|
+
const bare = (reason) => ({
|
|
95
|
+
a,
|
|
96
|
+
b,
|
|
97
|
+
separation: 'not-separable',
|
|
98
|
+
notSeparable: reason,
|
|
99
|
+
difference: null,
|
|
100
|
+
outcomesNeededPerArm: null,
|
|
101
|
+
stopping,
|
|
102
|
+
marginal: null,
|
|
103
|
+
});
|
|
104
|
+
if (a.rate === null || b.rate === null || a.interval === null || b.interval === null) {
|
|
105
|
+
return bare('nothing-recorded');
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Newcombe's interval on the difference, built from the two Wilson
|
|
109
|
+
* intervals rather than from a normal approximation to the difference.
|
|
110
|
+
*
|
|
111
|
+
* The naive interval is symmetric and can run past 0 or 1, which at the
|
|
112
|
+
* sample sizes a real experiment starts with is not an edge case — it is
|
|
113
|
+
* most of the first week.
|
|
114
|
+
*/
|
|
115
|
+
const point = a.rate - b.rate;
|
|
116
|
+
const low = point - Math.sqrt((a.rate - a.interval.low) ** 2 + (b.interval.high - b.rate) ** 2);
|
|
117
|
+
const high = point + Math.sqrt((a.interval.high - a.rate) ** 2 + (b.rate - b.interval.low) ** 2);
|
|
118
|
+
const difference = { point, low, high };
|
|
119
|
+
/**
|
|
120
|
+
* How many outcomes per arm would separate the difference *observed so far*.
|
|
121
|
+
*
|
|
122
|
+
* A standard two-proportion power calculation at 95% confidence and 80%
|
|
123
|
+
* power. It is an estimate about a difference that may itself be noise, and
|
|
124
|
+
* it is offered as "how much longer" rather than as a promise — but a number
|
|
125
|
+
* somebody can act on beats "not significant", which tells a reader nothing
|
|
126
|
+
* about whether to wait a day or abandon the idea.
|
|
127
|
+
*/
|
|
128
|
+
const spread = Math.abs(point);
|
|
129
|
+
const outcomesNeededPerArm = spread === 0
|
|
130
|
+
? null
|
|
131
|
+
: Math.ceil((((Z_95 + Z_POWER_80) ** 2) * (a.rate * (1 - a.rate) + b.rate * (1 - b.rate))) /
|
|
132
|
+
(spread * spread));
|
|
133
|
+
const perCallA = arms.a.tally.parsed > 0 ? arms.a.totalUsd / arms.a.tally.parsed : 0;
|
|
134
|
+
const perCallB = arms.b.tally.parsed > 0 ? arms.b.totalUsd / arms.b.tally.parsed : 0;
|
|
135
|
+
const better = a.rate >= b.rate ? a : b;
|
|
136
|
+
const worse = better === a ? b : a;
|
|
137
|
+
const betterPerCall = better === a ? perCallA : perCallB;
|
|
138
|
+
const worsePerCall = better === a ? perCallB : perCallA;
|
|
139
|
+
const rateGap = better.rate - worse.rate;
|
|
140
|
+
const marginal = {
|
|
141
|
+
/**
|
|
142
|
+
* Per call on both sides, so arms that took different shares of the
|
|
143
|
+
* traffic compare. Dividing raw totals would report a marginal cost that
|
|
144
|
+
* moves when the split changes and the behaviour does not.
|
|
145
|
+
*/
|
|
146
|
+
usdPerExtraSuccess: betterPerCall > worsePerCall && rateGap > 0
|
|
147
|
+
? (betterPerCall - worsePerCall) / rateGap
|
|
148
|
+
: null,
|
|
149
|
+
better: better.name,
|
|
150
|
+
dearer: betterPerCall > worsePerCall,
|
|
151
|
+
};
|
|
152
|
+
if (spread === 0)
|
|
153
|
+
return { ...bare('no-difference-observed'), difference, marginal };
|
|
154
|
+
if (low <= 0 && high >= 0) {
|
|
155
|
+
return { ...bare('interval-includes-zero'), difference, outcomesNeededPerArm, marginal };
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
a,
|
|
159
|
+
b,
|
|
160
|
+
separation: point > 0 ? 'a-wins' : 'b-wins',
|
|
161
|
+
notSeparable: null,
|
|
162
|
+
difference,
|
|
163
|
+
outcomesNeededPerArm,
|
|
164
|
+
stopping,
|
|
165
|
+
marginal,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=experiment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"experiment.js","sourceRoot":"","sources":["../src/experiment.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -19,6 +19,10 @@ export type { PlanAction, PlanActionKind, PlanAssumption, PlanDocument } from '.
|
|
|
19
19
|
export { verifyPlan } from './verify.js';
|
|
20
20
|
export { buildHistory, storedReportFrom, MIN_RUN } from './history.js';
|
|
21
21
|
export { outcomeReport, judgeOutcome, OUTCOME_UNLOCKS } from './outcome.js';
|
|
22
|
+
export { runExperiment } from './experiment.js';
|
|
23
|
+
export { qualityGate, MIN_OUTCOMES_EACH_SIDE, MAX_MODEL_MIX_DRIFT, MAX_VOLUME_RATIO, MAX_COVERAGE_DRIFT, } from './quality-gate.js';
|
|
24
|
+
export type { Confounder, GateSide, GateUnknown, GateVerdict, QualityGate } from './quality-gate.js';
|
|
25
|
+
export type { ArmResult, ExperimentArm, ExperimentDeclaration, ExperimentResult, Marginal, NotSeparableReason, Separation, } from './experiment.js';
|
|
22
26
|
export { ladderArithmetic, ladderPosition, validateLadder, MIN_CALLS_FOR_LADDER, BREAK_EVEN_BAND, } from './ladder.js';
|
|
23
27
|
export type { LadderArithmetic, LadderPolicy, LadderPosition, LadderProblem, LadderUnknown, LadderVerdict, } from './ladder.js';
|
|
24
28
|
export { perOutcome, rankPerOutcome, MIN_OUTCOMES_FOR_RATE, MIN_COVERAGE_FOR_RATE, } from './per-outcome.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,8 @@ export { buildPlan, planLabelName, parsePlanDocument, PLAN_ACTION_KINDS } from '
|
|
|
22
22
|
export { verifyPlan } from './verify.js';
|
|
23
23
|
export { buildHistory, storedReportFrom, MIN_RUN } from './history.js';
|
|
24
24
|
export { outcomeReport, judgeOutcome, OUTCOME_UNLOCKS } from './outcome.js';
|
|
25
|
+
export { runExperiment } from './experiment.js';
|
|
26
|
+
export { qualityGate, MIN_OUTCOMES_EACH_SIDE, MAX_MODEL_MIX_DRIFT, MAX_VOLUME_RATIO, MAX_COVERAGE_DRIFT, } from './quality-gate.js';
|
|
25
27
|
export { ladderArithmetic, ladderPosition, validateLadder, MIN_CALLS_FOR_LADDER, BREAK_EVEN_BAND, } from './ladder.js';
|
|
26
28
|
export { perOutcome, rankPerOutcome, MIN_OUTCOMES_FOR_RATE, MIN_COVERAGE_FOR_RATE, } from './per-outcome.js';
|
|
27
29
|
export { gatewayDecision, usageFromResponse, FAILURE_POLICIES } from './gateway.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The failure that actually matters, and the four ways of getting it wrong.
|
|
3
|
+
*
|
|
4
|
+
* CI has been able to fail a build for tokens since 1.4 and for dollars since
|
|
5
|
+
* 1.21. A prompt edit that quietly made the product worse has never been
|
|
6
|
+
* gateable — which means every cost saving this tool has ever recommended went
|
|
7
|
+
* into a repository with its most important consequence unmeasured.
|
|
8
|
+
*
|
|
9
|
+
* ## This is a before-and-after, not an experiment
|
|
10
|
+
*
|
|
11
|
+
* Worth saying in the first paragraph, because the arithmetic is the same as
|
|
12
|
+
* `experiment`'s and the epistemics are not. An experiment splits traffic
|
|
13
|
+
* randomly, so the two arms differ only in the thing under test. A
|
|
14
|
+
* before-and-after splits it by **time**, and everything else that changed at
|
|
15
|
+
* the same time is in the difference too.
|
|
16
|
+
*
|
|
17
|
+
* So this module spends most of its code looking for reasons *not* to blame the
|
|
18
|
+
* prompt, and it reports `cannot-tell` far more readily than a randomised
|
|
19
|
+
* comparison would. That is not timidity. A gate that blames the prompt because
|
|
20
|
+
* the prompt is the thing it can see will be switched off within a month, and a
|
|
21
|
+
* switched-off gate catches nothing at all.
|
|
22
|
+
*
|
|
23
|
+
* ## The three confounders it can actually see
|
|
24
|
+
*
|
|
25
|
+
* **The model moved.** If the mix of models shifted across the boundary, the
|
|
26
|
+
* prompt is not the only variable, and the drop may be entirely somebody else's
|
|
27
|
+
* migration.
|
|
28
|
+
*
|
|
29
|
+
* **The volume moved.** A workload whose traffic tripled is usually a workload
|
|
30
|
+
* whose *population* changed — a new surface, a new customer, a marketing
|
|
31
|
+
* campaign — and the questions being asked are not the questions from before.
|
|
32
|
+
*
|
|
33
|
+
* **The coverage moved.** The subtle one, and the one nobody thinks of: if the
|
|
34
|
+
* share of calls recording an outcome changed, the two rates describe different
|
|
35
|
+
* populations. A team that started instrumenting its hard cases will see its
|
|
36
|
+
* measured rate fall without anything getting worse.
|
|
37
|
+
*
|
|
38
|
+
* Any of them present, and the verdict is `cannot-tell` with the confounder
|
|
39
|
+
* **named**. Not a hedge attached to a blame — a refusal to blame.
|
|
40
|
+
*
|
|
41
|
+
* ## What it cannot see, and says so
|
|
42
|
+
*
|
|
43
|
+
* Everything else deployed that day. This module has one label's numbers and no
|
|
44
|
+
* knowledge of the world, and it never pretends otherwise: a `dropped` verdict
|
|
45
|
+
* is a statement that the rate fell and the three things it can check did not
|
|
46
|
+
* move, which is a smaller claim than "the prompt did it" and is the largest
|
|
47
|
+
* claim the evidence supports.
|
|
48
|
+
*/
|
|
49
|
+
import type { ArmResult, ExperimentArm } from './experiment.js';
|
|
50
|
+
import type { OutcomeVocabulary } from './outcome.js';
|
|
51
|
+
/**
|
|
52
|
+
* Outcomes each side of the change needs before a verdict is offered.
|
|
53
|
+
*
|
|
54
|
+
* A hundred rather than the ten used for a rate elsewhere, because this one
|
|
55
|
+
* fails builds. The cost of a wrong `dropped` is somebody reverting a good
|
|
56
|
+
* change and losing the saving; the cost of a wrong `cannot-tell` is waiting a
|
|
57
|
+
* day. Those are not symmetric and the threshold is not either.
|
|
58
|
+
*/
|
|
59
|
+
export declare const MIN_OUTCOMES_EACH_SIDE = 100;
|
|
60
|
+
/** How far the model mix may move before the comparison stops being about the prompt. */
|
|
61
|
+
export declare const MAX_MODEL_MIX_DRIFT = 0.1;
|
|
62
|
+
/**
|
|
63
|
+
* How far the call volume may move, as a ratio.
|
|
64
|
+
*
|
|
65
|
+
* Half again, either way. Ordinary week-to-week traffic moves less than that;
|
|
66
|
+
* a workload that doubled is usually a workload whose population changed.
|
|
67
|
+
*/
|
|
68
|
+
export declare const MAX_VOLUME_RATIO = 1.5;
|
|
69
|
+
/** How far outcome coverage may move before the two rates describe different populations. */
|
|
70
|
+
export declare const MAX_COVERAGE_DRIFT = 0.1;
|
|
71
|
+
export type Confounder = {
|
|
72
|
+
kind: 'model-mix-moved';
|
|
73
|
+
drift: number;
|
|
74
|
+
model: string;
|
|
75
|
+
} | {
|
|
76
|
+
kind: 'volume-moved';
|
|
77
|
+
beforeCalls: number;
|
|
78
|
+
afterCalls: number;
|
|
79
|
+
ratio: number;
|
|
80
|
+
} | {
|
|
81
|
+
kind: 'coverage-moved';
|
|
82
|
+
before: number;
|
|
83
|
+
after: number;
|
|
84
|
+
};
|
|
85
|
+
export type GateVerdict = 'held' | 'dropped' | 'cannot-tell';
|
|
86
|
+
export type GateUnknown = 'too-few-before' | 'too-few-after' | 'confounded' | 'no-vocabulary' | 'not-separable';
|
|
87
|
+
/** One side of the boundary, as the caller measured it. */
|
|
88
|
+
export interface GateSide {
|
|
89
|
+
arm: ExperimentArm;
|
|
90
|
+
/** Every call in the window, outcome or not — the coverage denominator. */
|
|
91
|
+
calls: number;
|
|
92
|
+
/** Spend per model, for the mix comparison. */
|
|
93
|
+
usdByModel: Array<{
|
|
94
|
+
model: string;
|
|
95
|
+
usd: number;
|
|
96
|
+
}>;
|
|
97
|
+
}
|
|
98
|
+
export interface QualityGate {
|
|
99
|
+
verdict: GateVerdict;
|
|
100
|
+
/** Why, when the verdict is `cannot-tell`. A refusal never arrives bare. */
|
|
101
|
+
unknown: GateUnknown | null;
|
|
102
|
+
before: ArmResult;
|
|
103
|
+
after: ArmResult;
|
|
104
|
+
/** The 95% interval on (after − before), or null. */
|
|
105
|
+
difference: {
|
|
106
|
+
point: number;
|
|
107
|
+
low: number;
|
|
108
|
+
high: number;
|
|
109
|
+
} | null;
|
|
110
|
+
/**
|
|
111
|
+
* Everything found that means the prompt is not the only variable.
|
|
112
|
+
*
|
|
113
|
+
* Non-empty forces `cannot-tell`. Reported even on a `held` verdict, because
|
|
114
|
+
* a rate that held while the model changed underneath is not evidence that
|
|
115
|
+
* the prompt is fine either.
|
|
116
|
+
*/
|
|
117
|
+
confounders: Confounder[];
|
|
118
|
+
/**
|
|
119
|
+
* The cost half — measured, and kept beside the quality half rather than
|
|
120
|
+
* merged with it. The sentence teams argue about needs both.
|
|
121
|
+
*/
|
|
122
|
+
cost: {
|
|
123
|
+
beforeUsdPerCall: number;
|
|
124
|
+
afterUsdPerCall: number;
|
|
125
|
+
deltaUsdPerCall: number;
|
|
126
|
+
} | null;
|
|
127
|
+
/** Outcomes behind the comparison, so the claim carries its own sample size. */
|
|
128
|
+
outcomes: {
|
|
129
|
+
before: number;
|
|
130
|
+
after: number;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
export declare function qualityGate(before: GateSide, after: GateSide, vocabulary: OutcomeVocabulary | null): QualityGate;
|
|
134
|
+
//# sourceMappingURL=quality-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality-gate.d.ts","sourceRoot":"","sources":["../src/quality-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,yFAAyF;AACzF,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,6FAA6F;AAC7F,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChF;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,aAAa,CAAC;AAE7D,MAAM,MAAM,WAAW,GACnB,gBAAgB,GAChB,eAAe,GACf,YAAY,GACZ,eAAe,GACf,eAAe,CAAC;AAEpB,2DAA2D;AAC3D,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,aAAa,CAAC;IACnB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,UAAU,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,WAAW,CAAC;IACrB,4EAA4E;IAC5E,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,SAAS,CAAC;IACjB,qDAAqD;IACrD,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChE;;;;;;OAMG;IACH,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B;;;OAGG;IACH,IAAI,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5F,gFAAgF;IAChF,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAwBD,wBAAgB,WAAW,CACzB,MAAM,EAAE,QAAQ,EAChB,KAAK,EAAE,QAAQ,EACf,UAAU,EAAE,iBAAiB,GAAG,IAAI,GACnC,WAAW,CAqGb"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The failure that actually matters, and the four ways of getting it wrong.
|
|
3
|
+
*
|
|
4
|
+
* CI has been able to fail a build for tokens since 1.4 and for dollars since
|
|
5
|
+
* 1.21. A prompt edit that quietly made the product worse has never been
|
|
6
|
+
* gateable — which means every cost saving this tool has ever recommended went
|
|
7
|
+
* into a repository with its most important consequence unmeasured.
|
|
8
|
+
*
|
|
9
|
+
* ## This is a before-and-after, not an experiment
|
|
10
|
+
*
|
|
11
|
+
* Worth saying in the first paragraph, because the arithmetic is the same as
|
|
12
|
+
* `experiment`'s and the epistemics are not. An experiment splits traffic
|
|
13
|
+
* randomly, so the two arms differ only in the thing under test. A
|
|
14
|
+
* before-and-after splits it by **time**, and everything else that changed at
|
|
15
|
+
* the same time is in the difference too.
|
|
16
|
+
*
|
|
17
|
+
* So this module spends most of its code looking for reasons *not* to blame the
|
|
18
|
+
* prompt, and it reports `cannot-tell` far more readily than a randomised
|
|
19
|
+
* comparison would. That is not timidity. A gate that blames the prompt because
|
|
20
|
+
* the prompt is the thing it can see will be switched off within a month, and a
|
|
21
|
+
* switched-off gate catches nothing at all.
|
|
22
|
+
*
|
|
23
|
+
* ## The three confounders it can actually see
|
|
24
|
+
*
|
|
25
|
+
* **The model moved.** If the mix of models shifted across the boundary, the
|
|
26
|
+
* prompt is not the only variable, and the drop may be entirely somebody else's
|
|
27
|
+
* migration.
|
|
28
|
+
*
|
|
29
|
+
* **The volume moved.** A workload whose traffic tripled is usually a workload
|
|
30
|
+
* whose *population* changed — a new surface, a new customer, a marketing
|
|
31
|
+
* campaign — and the questions being asked are not the questions from before.
|
|
32
|
+
*
|
|
33
|
+
* **The coverage moved.** The subtle one, and the one nobody thinks of: if the
|
|
34
|
+
* share of calls recording an outcome changed, the two rates describe different
|
|
35
|
+
* populations. A team that started instrumenting its hard cases will see its
|
|
36
|
+
* measured rate fall without anything getting worse.
|
|
37
|
+
*
|
|
38
|
+
* Any of them present, and the verdict is `cannot-tell` with the confounder
|
|
39
|
+
* **named**. Not a hedge attached to a blame — a refusal to blame.
|
|
40
|
+
*
|
|
41
|
+
* ## What it cannot see, and says so
|
|
42
|
+
*
|
|
43
|
+
* Everything else deployed that day. This module has one label's numbers and no
|
|
44
|
+
* knowledge of the world, and it never pretends otherwise: a `dropped` verdict
|
|
45
|
+
* is a statement that the rate fell and the three things it can check did not
|
|
46
|
+
* move, which is a smaller claim than "the prompt did it" and is the largest
|
|
47
|
+
* claim the evidence supports.
|
|
48
|
+
*/
|
|
49
|
+
import { runExperiment } from './experiment.js';
|
|
50
|
+
/**
|
|
51
|
+
* Outcomes each side of the change needs before a verdict is offered.
|
|
52
|
+
*
|
|
53
|
+
* A hundred rather than the ten used for a rate elsewhere, because this one
|
|
54
|
+
* fails builds. The cost of a wrong `dropped` is somebody reverting a good
|
|
55
|
+
* change and losing the saving; the cost of a wrong `cannot-tell` is waiting a
|
|
56
|
+
* day. Those are not symmetric and the threshold is not either.
|
|
57
|
+
*/
|
|
58
|
+
export const MIN_OUTCOMES_EACH_SIDE = 100;
|
|
59
|
+
/** How far the model mix may move before the comparison stops being about the prompt. */
|
|
60
|
+
export const MAX_MODEL_MIX_DRIFT = 0.1;
|
|
61
|
+
/**
|
|
62
|
+
* How far the call volume may move, as a ratio.
|
|
63
|
+
*
|
|
64
|
+
* Half again, either way. Ordinary week-to-week traffic moves less than that;
|
|
65
|
+
* a workload that doubled is usually a workload whose population changed.
|
|
66
|
+
*/
|
|
67
|
+
export const MAX_VOLUME_RATIO = 1.5;
|
|
68
|
+
/** How far outcome coverage may move before the two rates describe different populations. */
|
|
69
|
+
export const MAX_COVERAGE_DRIFT = 0.1;
|
|
70
|
+
function mixDrift(before, after) {
|
|
71
|
+
const share = (rows) => {
|
|
72
|
+
const total = rows.reduce((sum, row) => sum + row.usd, 0);
|
|
73
|
+
const out = new Map();
|
|
74
|
+
if (total <= 0)
|
|
75
|
+
return out;
|
|
76
|
+
for (const row of rows)
|
|
77
|
+
out.set(row.model, (out.get(row.model) ?? 0) + row.usd / total);
|
|
78
|
+
return out;
|
|
79
|
+
};
|
|
80
|
+
const a = share(before);
|
|
81
|
+
const b = share(after);
|
|
82
|
+
let worst = null;
|
|
83
|
+
for (const model of new Set([...a.keys(), ...b.keys()])) {
|
|
84
|
+
const drift = Math.abs((b.get(model) ?? 0) - (a.get(model) ?? 0));
|
|
85
|
+
if (worst === null || drift > worst.drift)
|
|
86
|
+
worst = { model, drift };
|
|
87
|
+
}
|
|
88
|
+
if (worst === null || worst.drift <= MAX_MODEL_MIX_DRIFT)
|
|
89
|
+
return null;
|
|
90
|
+
return { kind: 'model-mix-moved', drift: worst.drift, model: worst.model };
|
|
91
|
+
}
|
|
92
|
+
export function qualityGate(before, after, vocabulary) {
|
|
93
|
+
/**
|
|
94
|
+
* The statistics are `experiment`'s, deliberately.
|
|
95
|
+
*
|
|
96
|
+
* One implementation of a two-proportion comparison, so a gate and a
|
|
97
|
+
* deliberate experiment can never disagree about the same two numbers — the
|
|
98
|
+
* failure mode where a team runs both and gets two answers, then trusts
|
|
99
|
+
* whichever one they liked.
|
|
100
|
+
*/
|
|
101
|
+
const stats = runExperiment({ arms: ['before', 'after'], minOutcomesPerArm: MIN_OUTCOMES_EACH_SIDE }, { a: { ...after.arm, name: 'after' }, b: { ...before.arm, name: 'before' } }, vocabulary);
|
|
102
|
+
const outcomes = { before: stats.b.recorded, after: stats.a.recorded };
|
|
103
|
+
const cost = before.calls > 0 && after.calls > 0
|
|
104
|
+
? {
|
|
105
|
+
beforeUsdPerCall: before.arm.totalUsd / before.calls,
|
|
106
|
+
afterUsdPerCall: after.arm.totalUsd / after.calls,
|
|
107
|
+
deltaUsdPerCall: after.arm.totalUsd / after.calls - before.arm.totalUsd / before.calls,
|
|
108
|
+
}
|
|
109
|
+
: null;
|
|
110
|
+
const confounders = [];
|
|
111
|
+
const mix = mixDrift(before.usdByModel, after.usdByModel);
|
|
112
|
+
if (mix !== null)
|
|
113
|
+
confounders.push(mix);
|
|
114
|
+
if (before.calls > 0 && after.calls > 0) {
|
|
115
|
+
const ratio = after.calls / before.calls;
|
|
116
|
+
if (ratio > MAX_VOLUME_RATIO || ratio < 1 / MAX_VOLUME_RATIO) {
|
|
117
|
+
confounders.push({
|
|
118
|
+
kind: 'volume-moved',
|
|
119
|
+
beforeCalls: before.calls,
|
|
120
|
+
afterCalls: after.calls,
|
|
121
|
+
ratio,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Coverage, the one nobody thinks of.
|
|
126
|
+
*
|
|
127
|
+
* A team that starts instrumenting its hard cases sees its measured rate
|
|
128
|
+
* fall without anything having got worse. Comparing two rates over
|
|
129
|
+
* differently-selected populations is the most convincing wrong answer
|
|
130
|
+
* this module could produce.
|
|
131
|
+
*/
|
|
132
|
+
const coverageBefore = outcomes.before / before.calls;
|
|
133
|
+
const coverageAfter = outcomes.after / after.calls;
|
|
134
|
+
if (Math.abs(coverageAfter - coverageBefore) > MAX_COVERAGE_DRIFT) {
|
|
135
|
+
confounders.push({ kind: 'coverage-moved', before: coverageBefore, after: coverageAfter });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const shell = {
|
|
139
|
+
before: stats.b,
|
|
140
|
+
after: stats.a,
|
|
141
|
+
difference: stats.difference,
|
|
142
|
+
confounders,
|
|
143
|
+
cost,
|
|
144
|
+
outcomes,
|
|
145
|
+
};
|
|
146
|
+
if (vocabulary === null || vocabulary.success.length === 0) {
|
|
147
|
+
return { ...shell, verdict: 'cannot-tell', unknown: 'no-vocabulary' };
|
|
148
|
+
}
|
|
149
|
+
if (outcomes.before < MIN_OUTCOMES_EACH_SIDE) {
|
|
150
|
+
return { ...shell, verdict: 'cannot-tell', unknown: 'too-few-before' };
|
|
151
|
+
}
|
|
152
|
+
if (outcomes.after < MIN_OUTCOMES_EACH_SIDE) {
|
|
153
|
+
return { ...shell, verdict: 'cannot-tell', unknown: 'too-few-after' };
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* A confounder outranks the statistics.
|
|
157
|
+
*
|
|
158
|
+
* Checked after the sample sizes and before the verdict, so a build is never
|
|
159
|
+
* failed on a difference that something else could equally explain. A gate
|
|
160
|
+
* that blames the prompt because the prompt is the thing it can see gets
|
|
161
|
+
* switched off within a month, and a switched-off gate catches nothing.
|
|
162
|
+
*/
|
|
163
|
+
if (confounders.length > 0) {
|
|
164
|
+
return { ...shell, verdict: 'cannot-tell', unknown: 'confounded' };
|
|
165
|
+
}
|
|
166
|
+
if (stats.separation === 'not-separable') {
|
|
167
|
+
/**
|
|
168
|
+
* Not separable is `cannot-tell`, never `held`.
|
|
169
|
+
*
|
|
170
|
+
* "The rate did not measurably fall" and "the rate held" are different
|
|
171
|
+
* claims, and a gate that spells them the same way passes a real
|
|
172
|
+
* regression it merely lacked the power to see. `verify` has held this
|
|
173
|
+
* line since 1.39 and it matters more here, because this one is wired to
|
|
174
|
+
* an exit code.
|
|
175
|
+
*/
|
|
176
|
+
return { ...shell, verdict: 'cannot-tell', unknown: 'not-separable' };
|
|
177
|
+
}
|
|
178
|
+
// `a` is the *after* arm, so `a-wins` means the rate went up.
|
|
179
|
+
return {
|
|
180
|
+
...shell,
|
|
181
|
+
verdict: stats.separation === 'a-wins' ? 'held' : 'dropped',
|
|
182
|
+
unknown: null,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=quality-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality-gate.js","sourceRoot":"","sources":["../src/quality-gate.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trazum/core",
|
|
3
|
-
"version": "1.50.
|
|
3
|
+
"version": "1.50.8",
|
|
4
4
|
"description": "Trazum core: priced advisories for LLM prompts (caching, model tier, batching, schemas), plus deterministic trimming, token counting and pricing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "David Mu\u00f1oz Rey",
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two arms on real traffic, and the three things that stop it being theatre.
|
|
3
|
+
*
|
|
4
|
+
* `eval` compares two prompts on cases somebody wrote; `route` compares two
|
|
5
|
+
* models on the same. Both measure agreement in a laboratory. The traffic is
|
|
6
|
+
* the only place the real question gets answered — and the moment a comparison
|
|
7
|
+
* runs on real traffic, three failures become available that a laboratory does
|
|
8
|
+
* not have.
|
|
9
|
+
*
|
|
10
|
+
* ## 1. A winner where there is none
|
|
11
|
+
*
|
|
12
|
+
* Two arms always produce two numbers, and one of them is always larger. An
|
|
13
|
+
* A/B report that names a winner from that is a coin flip with a dashboard.
|
|
14
|
+
* The verdict here is **three-valued**, the way `verify`'s has been since
|
|
15
|
+
* 1.39: A wins, B wins, or **not separable on this traffic** — and the third
|
|
16
|
+
* one comes with the number of outcomes per arm that *would* separate them, so
|
|
17
|
+
* "run it longer" is a quantified instruction rather than a shrug.
|
|
18
|
+
*
|
|
19
|
+
* ## 2. Peeking
|
|
20
|
+
*
|
|
21
|
+
* A test stopped on the first afternoon it looked good is not a test. The
|
|
22
|
+
* stopping rule is declared **before** the experiment starts, and the report
|
|
23
|
+
* says whether it was honoured. It cannot enforce that — nobody can stop
|
|
24
|
+
* somebody reading a number early — but it can make an early stop *visible*
|
|
25
|
+
* to whoever reads the result later, which is the part that matters.
|
|
26
|
+
*
|
|
27
|
+
* ## 3. Quality and cost judged apart, then together
|
|
28
|
+
*
|
|
29
|
+
* The interesting arm is almost never better *and* cheaper. It is better and
|
|
30
|
+
* dearer, and the decision needs one figure nobody computes: **what an extra
|
|
31
|
+
* success costs**. That is the marginal figure — the difference in spend over
|
|
32
|
+
* the difference in successes — and it is the number a product decision
|
|
33
|
+
* actually turns on, printed rather than left as an exercise.
|
|
34
|
+
*
|
|
35
|
+
* ## The statistics are shown, not asserted
|
|
36
|
+
*
|
|
37
|
+
* Wilson score intervals per arm and Newcombe's interval on the difference,
|
|
38
|
+
* because both behave at the small samples this will actually see. The
|
|
39
|
+
* intervals are returned, not just the verdict: a reader who disagrees with
|
|
40
|
+
* the threshold can see the numbers it was applied to, which is the same
|
|
41
|
+
* discipline `eval` established by running the original twice before judging
|
|
42
|
+
* anything against it.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import { judgeOutcome } from './outcome.js';
|
|
46
|
+
import type { OutcomeTally, OutcomeVocabulary } from './outcome.js';
|
|
47
|
+
|
|
48
|
+
/** z for a two-sided 95% interval, and for 80% power. */
|
|
49
|
+
const Z_95 = 1.959963984540054;
|
|
50
|
+
const Z_POWER_80 = 0.8416212335729143;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The declaration, made before the experiment runs.
|
|
54
|
+
*
|
|
55
|
+
* Its whole purpose is to exist *earlier* than the result. A stopping rule
|
|
56
|
+
* invented after looking at the numbers is not a stopping rule.
|
|
57
|
+
*/
|
|
58
|
+
export interface ExperimentDeclaration {
|
|
59
|
+
/** Two arms. More would need a multiple-comparison correction nobody asked for. */
|
|
60
|
+
arms: [string, string];
|
|
61
|
+
/**
|
|
62
|
+
* Outcomes each arm must record before the result may be read.
|
|
63
|
+
*
|
|
64
|
+
* Declared as a count rather than a duration, because a duration is a proxy
|
|
65
|
+
* for a count and the proxy breaks the week traffic doubles.
|
|
66
|
+
*/
|
|
67
|
+
minOutcomesPerArm: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** What one arm actually did. */
|
|
71
|
+
export interface ExperimentArm {
|
|
72
|
+
name: string;
|
|
73
|
+
tally: OutcomeTally;
|
|
74
|
+
/** Everything this arm spent, recorded outcome or not. */
|
|
75
|
+
totalUsd: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface ArmResult {
|
|
79
|
+
name: string;
|
|
80
|
+
successes: number;
|
|
81
|
+
/** Calls carrying a *declared* outcome — the denominator. */
|
|
82
|
+
recorded: number;
|
|
83
|
+
/** Successes over recorded, or null when nothing was recorded. */
|
|
84
|
+
rate: number | null;
|
|
85
|
+
/** Wilson score interval on that rate, or null. */
|
|
86
|
+
interval: { low: number; high: number } | null;
|
|
87
|
+
/** Spend on calls carrying a declared outcome. */
|
|
88
|
+
recordedUsd: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type Separation = 'a-wins' | 'b-wins' | 'not-separable';
|
|
92
|
+
|
|
93
|
+
export type NotSeparableReason =
|
|
94
|
+
| 'interval-includes-zero'
|
|
95
|
+
| 'no-difference-observed'
|
|
96
|
+
| 'nothing-recorded';
|
|
97
|
+
|
|
98
|
+
export interface Marginal {
|
|
99
|
+
/**
|
|
100
|
+
* What one extra success costs, going from the worse arm to the better one.
|
|
101
|
+
*
|
|
102
|
+
* `(dearer spend − cheaper spend) / (more successes − fewer successes)`,
|
|
103
|
+
* both per call so arms of different sizes compare. Null when the better arm
|
|
104
|
+
* is also the cheaper one, because then nothing is being bought and a
|
|
105
|
+
* "cost per extra success" would be a negative number people would quote.
|
|
106
|
+
*/
|
|
107
|
+
usdPerExtraSuccess: number | null;
|
|
108
|
+
/** The arm that resolved more, by rate. */
|
|
109
|
+
better: string;
|
|
110
|
+
/** Whether that arm also costs more per call. */
|
|
111
|
+
dearer: boolean;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface ExperimentResult {
|
|
115
|
+
a: ArmResult;
|
|
116
|
+
b: ArmResult;
|
|
117
|
+
separation: Separation;
|
|
118
|
+
/** Why, when not separable. A refusal never arrives bare. */
|
|
119
|
+
notSeparable: NotSeparableReason | null;
|
|
120
|
+
/** Newcombe's interval on (rate A − rate B), or null. */
|
|
121
|
+
difference: { point: number; low: number; high: number } | null;
|
|
122
|
+
/**
|
|
123
|
+
* Outcomes **per arm** that would separate the observed difference, or null.
|
|
124
|
+
*
|
|
125
|
+
* Null when the arms recorded the same rate: no sample size separates a
|
|
126
|
+
* difference of zero, and returning a very large number would read as "keep
|
|
127
|
+
* going" when the honest answer is "there is nothing here to find".
|
|
128
|
+
*/
|
|
129
|
+
outcomesNeededPerArm: number | null;
|
|
130
|
+
stopping: {
|
|
131
|
+
declared: number;
|
|
132
|
+
/** Whether both arms cleared the declared minimum. */
|
|
133
|
+
honoured: boolean;
|
|
134
|
+
/** The arm that has not, when one has not. */
|
|
135
|
+
short: string | null;
|
|
136
|
+
};
|
|
137
|
+
marginal: Marginal | null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Wilson score interval — behaves at the sample sizes this will actually see. */
|
|
141
|
+
function wilson(successes: number, n: number, z = Z_95): { low: number; high: number } | null {
|
|
142
|
+
if (n <= 0) return null;
|
|
143
|
+
const p = successes / n;
|
|
144
|
+
const denominator = 1 + (z * z) / n;
|
|
145
|
+
const centre = (p + (z * z) / (2 * n)) / denominator;
|
|
146
|
+
const half = (z * Math.sqrt((p * (1 - p)) / n + (z * z) / (4 * n * n))) / denominator;
|
|
147
|
+
return { low: Math.max(0, centre - half), high: Math.min(1, centre + half) };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function resultOf(arm: ExperimentArm, vocabulary: OutcomeVocabulary | null): ArmResult {
|
|
151
|
+
const declared = vocabulary ?? { values: [], success: [] };
|
|
152
|
+
let successes = 0;
|
|
153
|
+
let recorded = 0;
|
|
154
|
+
let recordedUsd = 0;
|
|
155
|
+
for (const entry of arm.tally.byValue) {
|
|
156
|
+
// Undeclared values are out of both halves, as everywhere since 1.50.4: a
|
|
157
|
+
// typo in an exporter must not decide an experiment.
|
|
158
|
+
if (judgeOutcome(entry.value, declared) === 'undeclared') continue;
|
|
159
|
+
recorded += entry.calls;
|
|
160
|
+
recordedUsd += entry.usd;
|
|
161
|
+
if (declared.success.includes(entry.value)) successes += entry.calls;
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
name: arm.name,
|
|
165
|
+
successes,
|
|
166
|
+
recorded,
|
|
167
|
+
rate: recorded > 0 ? successes / recorded : null,
|
|
168
|
+
interval: wilson(successes, recorded),
|
|
169
|
+
recordedUsd,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function runExperiment(
|
|
174
|
+
declaration: ExperimentDeclaration,
|
|
175
|
+
arms: { a: ExperimentArm; b: ExperimentArm },
|
|
176
|
+
vocabulary: OutcomeVocabulary | null,
|
|
177
|
+
): ExperimentResult {
|
|
178
|
+
const a = resultOf(arms.a, vocabulary);
|
|
179
|
+
const b = resultOf(arms.b, vocabulary);
|
|
180
|
+
|
|
181
|
+
const stopping = {
|
|
182
|
+
declared: declaration.minOutcomesPerArm,
|
|
183
|
+
honoured: a.recorded >= declaration.minOutcomesPerArm && b.recorded >= declaration.minOutcomesPerArm,
|
|
184
|
+
short:
|
|
185
|
+
a.recorded < declaration.minOutcomesPerArm
|
|
186
|
+
? a.name
|
|
187
|
+
: b.recorded < declaration.minOutcomesPerArm
|
|
188
|
+
? b.name
|
|
189
|
+
: null,
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const bare = (reason: NotSeparableReason): ExperimentResult => ({
|
|
193
|
+
a,
|
|
194
|
+
b,
|
|
195
|
+
separation: 'not-separable',
|
|
196
|
+
notSeparable: reason,
|
|
197
|
+
difference: null,
|
|
198
|
+
outcomesNeededPerArm: null,
|
|
199
|
+
stopping,
|
|
200
|
+
marginal: null,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
if (a.rate === null || b.rate === null || a.interval === null || b.interval === null) {
|
|
204
|
+
return bare('nothing-recorded');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Newcombe's interval on the difference, built from the two Wilson
|
|
209
|
+
* intervals rather than from a normal approximation to the difference.
|
|
210
|
+
*
|
|
211
|
+
* The naive interval is symmetric and can run past 0 or 1, which at the
|
|
212
|
+
* sample sizes a real experiment starts with is not an edge case — it is
|
|
213
|
+
* most of the first week.
|
|
214
|
+
*/
|
|
215
|
+
const point = a.rate - b.rate;
|
|
216
|
+
const low = point - Math.sqrt((a.rate - a.interval.low) ** 2 + (b.interval.high - b.rate) ** 2);
|
|
217
|
+
const high = point + Math.sqrt((a.interval.high - a.rate) ** 2 + (b.rate - b.interval.low) ** 2);
|
|
218
|
+
const difference = { point, low, high };
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* How many outcomes per arm would separate the difference *observed so far*.
|
|
222
|
+
*
|
|
223
|
+
* A standard two-proportion power calculation at 95% confidence and 80%
|
|
224
|
+
* power. It is an estimate about a difference that may itself be noise, and
|
|
225
|
+
* it is offered as "how much longer" rather than as a promise — but a number
|
|
226
|
+
* somebody can act on beats "not significant", which tells a reader nothing
|
|
227
|
+
* about whether to wait a day or abandon the idea.
|
|
228
|
+
*/
|
|
229
|
+
const spread = Math.abs(point);
|
|
230
|
+
const outcomesNeededPerArm =
|
|
231
|
+
spread === 0
|
|
232
|
+
? null
|
|
233
|
+
: Math.ceil(
|
|
234
|
+
(((Z_95 + Z_POWER_80) ** 2) * (a.rate * (1 - a.rate) + b.rate * (1 - b.rate))) /
|
|
235
|
+
(spread * spread),
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
const perCallA = arms.a.tally.parsed > 0 ? arms.a.totalUsd / arms.a.tally.parsed : 0;
|
|
239
|
+
const perCallB = arms.b.tally.parsed > 0 ? arms.b.totalUsd / arms.b.tally.parsed : 0;
|
|
240
|
+
const better = a.rate >= b.rate ? a : b;
|
|
241
|
+
const worse = better === a ? b : a;
|
|
242
|
+
const betterPerCall = better === a ? perCallA : perCallB;
|
|
243
|
+
const worsePerCall = better === a ? perCallB : perCallA;
|
|
244
|
+
const rateGap = (better.rate as number) - (worse.rate as number);
|
|
245
|
+
|
|
246
|
+
const marginal: Marginal = {
|
|
247
|
+
/**
|
|
248
|
+
* Per call on both sides, so arms that took different shares of the
|
|
249
|
+
* traffic compare. Dividing raw totals would report a marginal cost that
|
|
250
|
+
* moves when the split changes and the behaviour does not.
|
|
251
|
+
*/
|
|
252
|
+
usdPerExtraSuccess:
|
|
253
|
+
betterPerCall > worsePerCall && rateGap > 0
|
|
254
|
+
? (betterPerCall - worsePerCall) / rateGap
|
|
255
|
+
: null,
|
|
256
|
+
better: better.name,
|
|
257
|
+
dearer: betterPerCall > worsePerCall,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
if (spread === 0) return { ...bare('no-difference-observed'), difference, marginal };
|
|
261
|
+
if (low <= 0 && high >= 0) {
|
|
262
|
+
return { ...bare('interval-includes-zero'), difference, outcomesNeededPerArm, marginal };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
a,
|
|
267
|
+
b,
|
|
268
|
+
separation: point > 0 ? 'a-wins' : 'b-wins',
|
|
269
|
+
notSeparable: null,
|
|
270
|
+
difference,
|
|
271
|
+
outcomesNeededPerArm,
|
|
272
|
+
stopping,
|
|
273
|
+
marginal,
|
|
274
|
+
};
|
|
275
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -50,6 +50,24 @@ export type { PlanAction, PlanActionKind, PlanAssumption, PlanDocument } from '.
|
|
|
50
50
|
export { verifyPlan } from './verify.js';
|
|
51
51
|
export { buildHistory, storedReportFrom, MIN_RUN } from './history.js';
|
|
52
52
|
export { outcomeReport, judgeOutcome, OUTCOME_UNLOCKS } from './outcome.js';
|
|
53
|
+
export { runExperiment } from './experiment.js';
|
|
54
|
+
export {
|
|
55
|
+
qualityGate,
|
|
56
|
+
MIN_OUTCOMES_EACH_SIDE,
|
|
57
|
+
MAX_MODEL_MIX_DRIFT,
|
|
58
|
+
MAX_VOLUME_RATIO,
|
|
59
|
+
MAX_COVERAGE_DRIFT,
|
|
60
|
+
} from './quality-gate.js';
|
|
61
|
+
export type { Confounder, GateSide, GateUnknown, GateVerdict, QualityGate } from './quality-gate.js';
|
|
62
|
+
export type {
|
|
63
|
+
ArmResult,
|
|
64
|
+
ExperimentArm,
|
|
65
|
+
ExperimentDeclaration,
|
|
66
|
+
ExperimentResult,
|
|
67
|
+
Marginal,
|
|
68
|
+
NotSeparableReason,
|
|
69
|
+
Separation,
|
|
70
|
+
} from './experiment.js';
|
|
53
71
|
export {
|
|
54
72
|
ladderArithmetic,
|
|
55
73
|
ladderPosition,
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The failure that actually matters, and the four ways of getting it wrong.
|
|
3
|
+
*
|
|
4
|
+
* CI has been able to fail a build for tokens since 1.4 and for dollars since
|
|
5
|
+
* 1.21. A prompt edit that quietly made the product worse has never been
|
|
6
|
+
* gateable — which means every cost saving this tool has ever recommended went
|
|
7
|
+
* into a repository with its most important consequence unmeasured.
|
|
8
|
+
*
|
|
9
|
+
* ## This is a before-and-after, not an experiment
|
|
10
|
+
*
|
|
11
|
+
* Worth saying in the first paragraph, because the arithmetic is the same as
|
|
12
|
+
* `experiment`'s and the epistemics are not. An experiment splits traffic
|
|
13
|
+
* randomly, so the two arms differ only in the thing under test. A
|
|
14
|
+
* before-and-after splits it by **time**, and everything else that changed at
|
|
15
|
+
* the same time is in the difference too.
|
|
16
|
+
*
|
|
17
|
+
* So this module spends most of its code looking for reasons *not* to blame the
|
|
18
|
+
* prompt, and it reports `cannot-tell` far more readily than a randomised
|
|
19
|
+
* comparison would. That is not timidity. A gate that blames the prompt because
|
|
20
|
+
* the prompt is the thing it can see will be switched off within a month, and a
|
|
21
|
+
* switched-off gate catches nothing at all.
|
|
22
|
+
*
|
|
23
|
+
* ## The three confounders it can actually see
|
|
24
|
+
*
|
|
25
|
+
* **The model moved.** If the mix of models shifted across the boundary, the
|
|
26
|
+
* prompt is not the only variable, and the drop may be entirely somebody else's
|
|
27
|
+
* migration.
|
|
28
|
+
*
|
|
29
|
+
* **The volume moved.** A workload whose traffic tripled is usually a workload
|
|
30
|
+
* whose *population* changed — a new surface, a new customer, a marketing
|
|
31
|
+
* campaign — and the questions being asked are not the questions from before.
|
|
32
|
+
*
|
|
33
|
+
* **The coverage moved.** The subtle one, and the one nobody thinks of: if the
|
|
34
|
+
* share of calls recording an outcome changed, the two rates describe different
|
|
35
|
+
* populations. A team that started instrumenting its hard cases will see its
|
|
36
|
+
* measured rate fall without anything getting worse.
|
|
37
|
+
*
|
|
38
|
+
* Any of them present, and the verdict is `cannot-tell` with the confounder
|
|
39
|
+
* **named**. Not a hedge attached to a blame — a refusal to blame.
|
|
40
|
+
*
|
|
41
|
+
* ## What it cannot see, and says so
|
|
42
|
+
*
|
|
43
|
+
* Everything else deployed that day. This module has one label's numbers and no
|
|
44
|
+
* knowledge of the world, and it never pretends otherwise: a `dropped` verdict
|
|
45
|
+
* is a statement that the rate fell and the three things it can check did not
|
|
46
|
+
* move, which is a smaller claim than "the prompt did it" and is the largest
|
|
47
|
+
* claim the evidence supports.
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
import { runExperiment } from './experiment.js';
|
|
51
|
+
import type { ArmResult, ExperimentArm } from './experiment.js';
|
|
52
|
+
import type { OutcomeVocabulary } from './outcome.js';
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Outcomes each side of the change needs before a verdict is offered.
|
|
56
|
+
*
|
|
57
|
+
* A hundred rather than the ten used for a rate elsewhere, because this one
|
|
58
|
+
* fails builds. The cost of a wrong `dropped` is somebody reverting a good
|
|
59
|
+
* change and losing the saving; the cost of a wrong `cannot-tell` is waiting a
|
|
60
|
+
* day. Those are not symmetric and the threshold is not either.
|
|
61
|
+
*/
|
|
62
|
+
export const MIN_OUTCOMES_EACH_SIDE = 100;
|
|
63
|
+
|
|
64
|
+
/** How far the model mix may move before the comparison stops being about the prompt. */
|
|
65
|
+
export const MAX_MODEL_MIX_DRIFT = 0.1;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* How far the call volume may move, as a ratio.
|
|
69
|
+
*
|
|
70
|
+
* Half again, either way. Ordinary week-to-week traffic moves less than that;
|
|
71
|
+
* a workload that doubled is usually a workload whose population changed.
|
|
72
|
+
*/
|
|
73
|
+
export const MAX_VOLUME_RATIO = 1.5;
|
|
74
|
+
|
|
75
|
+
/** How far outcome coverage may move before the two rates describe different populations. */
|
|
76
|
+
export const MAX_COVERAGE_DRIFT = 0.1;
|
|
77
|
+
|
|
78
|
+
export type Confounder =
|
|
79
|
+
| { kind: 'model-mix-moved'; drift: number; model: string }
|
|
80
|
+
| { kind: 'volume-moved'; beforeCalls: number; afterCalls: number; ratio: number }
|
|
81
|
+
| { kind: 'coverage-moved'; before: number; after: number };
|
|
82
|
+
|
|
83
|
+
export type GateVerdict = 'held' | 'dropped' | 'cannot-tell';
|
|
84
|
+
|
|
85
|
+
export type GateUnknown =
|
|
86
|
+
| 'too-few-before'
|
|
87
|
+
| 'too-few-after'
|
|
88
|
+
| 'confounded'
|
|
89
|
+
| 'no-vocabulary'
|
|
90
|
+
| 'not-separable';
|
|
91
|
+
|
|
92
|
+
/** One side of the boundary, as the caller measured it. */
|
|
93
|
+
export interface GateSide {
|
|
94
|
+
arm: ExperimentArm;
|
|
95
|
+
/** Every call in the window, outcome or not — the coverage denominator. */
|
|
96
|
+
calls: number;
|
|
97
|
+
/** Spend per model, for the mix comparison. */
|
|
98
|
+
usdByModel: Array<{ model: string; usd: number }>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface QualityGate {
|
|
102
|
+
verdict: GateVerdict;
|
|
103
|
+
/** Why, when the verdict is `cannot-tell`. A refusal never arrives bare. */
|
|
104
|
+
unknown: GateUnknown | null;
|
|
105
|
+
before: ArmResult;
|
|
106
|
+
after: ArmResult;
|
|
107
|
+
/** The 95% interval on (after − before), or null. */
|
|
108
|
+
difference: { point: number; low: number; high: number } | null;
|
|
109
|
+
/**
|
|
110
|
+
* Everything found that means the prompt is not the only variable.
|
|
111
|
+
*
|
|
112
|
+
* Non-empty forces `cannot-tell`. Reported even on a `held` verdict, because
|
|
113
|
+
* a rate that held while the model changed underneath is not evidence that
|
|
114
|
+
* the prompt is fine either.
|
|
115
|
+
*/
|
|
116
|
+
confounders: Confounder[];
|
|
117
|
+
/**
|
|
118
|
+
* The cost half — measured, and kept beside the quality half rather than
|
|
119
|
+
* merged with it. The sentence teams argue about needs both.
|
|
120
|
+
*/
|
|
121
|
+
cost: { beforeUsdPerCall: number; afterUsdPerCall: number; deltaUsdPerCall: number } | null;
|
|
122
|
+
/** Outcomes behind the comparison, so the claim carries its own sample size. */
|
|
123
|
+
outcomes: { before: number; after: number };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function mixDrift(
|
|
127
|
+
before: Array<{ model: string; usd: number }>,
|
|
128
|
+
after: Array<{ model: string; usd: number }>,
|
|
129
|
+
): Confounder | null {
|
|
130
|
+
const share = (rows: Array<{ model: string; usd: number }>): Map<string, number> => {
|
|
131
|
+
const total = rows.reduce((sum, row) => sum + row.usd, 0);
|
|
132
|
+
const out = new Map<string, number>();
|
|
133
|
+
if (total <= 0) return out;
|
|
134
|
+
for (const row of rows) out.set(row.model, (out.get(row.model) ?? 0) + row.usd / total);
|
|
135
|
+
return out;
|
|
136
|
+
};
|
|
137
|
+
const a = share(before);
|
|
138
|
+
const b = share(after);
|
|
139
|
+
let worst: { model: string; drift: number } | null = null;
|
|
140
|
+
for (const model of new Set([...a.keys(), ...b.keys()])) {
|
|
141
|
+
const drift = Math.abs((b.get(model) ?? 0) - (a.get(model) ?? 0));
|
|
142
|
+
if (worst === null || drift > worst.drift) worst = { model, drift };
|
|
143
|
+
}
|
|
144
|
+
if (worst === null || worst.drift <= MAX_MODEL_MIX_DRIFT) return null;
|
|
145
|
+
return { kind: 'model-mix-moved', drift: worst.drift, model: worst.model };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function qualityGate(
|
|
149
|
+
before: GateSide,
|
|
150
|
+
after: GateSide,
|
|
151
|
+
vocabulary: OutcomeVocabulary | null,
|
|
152
|
+
): QualityGate {
|
|
153
|
+
/**
|
|
154
|
+
* The statistics are `experiment`'s, deliberately.
|
|
155
|
+
*
|
|
156
|
+
* One implementation of a two-proportion comparison, so a gate and a
|
|
157
|
+
* deliberate experiment can never disagree about the same two numbers — the
|
|
158
|
+
* failure mode where a team runs both and gets two answers, then trusts
|
|
159
|
+
* whichever one they liked.
|
|
160
|
+
*/
|
|
161
|
+
const stats = runExperiment(
|
|
162
|
+
{ arms: ['before', 'after'], minOutcomesPerArm: MIN_OUTCOMES_EACH_SIDE },
|
|
163
|
+
{ a: { ...after.arm, name: 'after' }, b: { ...before.arm, name: 'before' } },
|
|
164
|
+
vocabulary,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const outcomes = { before: stats.b.recorded, after: stats.a.recorded };
|
|
168
|
+
const cost =
|
|
169
|
+
before.calls > 0 && after.calls > 0
|
|
170
|
+
? {
|
|
171
|
+
beforeUsdPerCall: before.arm.totalUsd / before.calls,
|
|
172
|
+
afterUsdPerCall: after.arm.totalUsd / after.calls,
|
|
173
|
+
deltaUsdPerCall: after.arm.totalUsd / after.calls - before.arm.totalUsd / before.calls,
|
|
174
|
+
}
|
|
175
|
+
: null;
|
|
176
|
+
|
|
177
|
+
const confounders: Confounder[] = [];
|
|
178
|
+
const mix = mixDrift(before.usdByModel, after.usdByModel);
|
|
179
|
+
if (mix !== null) confounders.push(mix);
|
|
180
|
+
|
|
181
|
+
if (before.calls > 0 && after.calls > 0) {
|
|
182
|
+
const ratio = after.calls / before.calls;
|
|
183
|
+
if (ratio > MAX_VOLUME_RATIO || ratio < 1 / MAX_VOLUME_RATIO) {
|
|
184
|
+
confounders.push({
|
|
185
|
+
kind: 'volume-moved',
|
|
186
|
+
beforeCalls: before.calls,
|
|
187
|
+
afterCalls: after.calls,
|
|
188
|
+
ratio,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Coverage, the one nobody thinks of.
|
|
193
|
+
*
|
|
194
|
+
* A team that starts instrumenting its hard cases sees its measured rate
|
|
195
|
+
* fall without anything having got worse. Comparing two rates over
|
|
196
|
+
* differently-selected populations is the most convincing wrong answer
|
|
197
|
+
* this module could produce.
|
|
198
|
+
*/
|
|
199
|
+
const coverageBefore = outcomes.before / before.calls;
|
|
200
|
+
const coverageAfter = outcomes.after / after.calls;
|
|
201
|
+
if (Math.abs(coverageAfter - coverageBefore) > MAX_COVERAGE_DRIFT) {
|
|
202
|
+
confounders.push({ kind: 'coverage-moved', before: coverageBefore, after: coverageAfter });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const shell = {
|
|
207
|
+
before: stats.b,
|
|
208
|
+
after: stats.a,
|
|
209
|
+
difference: stats.difference,
|
|
210
|
+
confounders,
|
|
211
|
+
cost,
|
|
212
|
+
outcomes,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
if (vocabulary === null || vocabulary.success.length === 0) {
|
|
216
|
+
return { ...shell, verdict: 'cannot-tell', unknown: 'no-vocabulary' };
|
|
217
|
+
}
|
|
218
|
+
if (outcomes.before < MIN_OUTCOMES_EACH_SIDE) {
|
|
219
|
+
return { ...shell, verdict: 'cannot-tell', unknown: 'too-few-before' };
|
|
220
|
+
}
|
|
221
|
+
if (outcomes.after < MIN_OUTCOMES_EACH_SIDE) {
|
|
222
|
+
return { ...shell, verdict: 'cannot-tell', unknown: 'too-few-after' };
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* A confounder outranks the statistics.
|
|
226
|
+
*
|
|
227
|
+
* Checked after the sample sizes and before the verdict, so a build is never
|
|
228
|
+
* failed on a difference that something else could equally explain. A gate
|
|
229
|
+
* that blames the prompt because the prompt is the thing it can see gets
|
|
230
|
+
* switched off within a month, and a switched-off gate catches nothing.
|
|
231
|
+
*/
|
|
232
|
+
if (confounders.length > 0) {
|
|
233
|
+
return { ...shell, verdict: 'cannot-tell', unknown: 'confounded' };
|
|
234
|
+
}
|
|
235
|
+
if (stats.separation === 'not-separable') {
|
|
236
|
+
/**
|
|
237
|
+
* Not separable is `cannot-tell`, never `held`.
|
|
238
|
+
*
|
|
239
|
+
* "The rate did not measurably fall" and "the rate held" are different
|
|
240
|
+
* claims, and a gate that spells them the same way passes a real
|
|
241
|
+
* regression it merely lacked the power to see. `verify` has held this
|
|
242
|
+
* line since 1.39 and it matters more here, because this one is wired to
|
|
243
|
+
* an exit code.
|
|
244
|
+
*/
|
|
245
|
+
return { ...shell, verdict: 'cannot-tell', unknown: 'not-separable' };
|
|
246
|
+
}
|
|
247
|
+
// `a` is the *after* arm, so `a-wins` means the rate went up.
|
|
248
|
+
return {
|
|
249
|
+
...shell,
|
|
250
|
+
verdict: stats.separation === 'a-wins' ? 'held' : 'dropped',
|
|
251
|
+
unknown: null,
|
|
252
|
+
};
|
|
253
|
+
}
|