@graphorin/evals 0.5.0 → 0.6.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +32 -6
  3. package/dist/index.d.ts +5 -5
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +7 -5
  6. package/dist/index.js.map +1 -1
  7. package/dist/loaders/dmr.js +1 -1
  8. package/dist/loaders/dmr.js.map +1 -1
  9. package/dist/loaders/from-traces.js +1 -1
  10. package/dist/loaders/from-traces.js.map +1 -1
  11. package/dist/loaders/iterable.js.map +1 -1
  12. package/dist/loaders/locomo.d.ts +1 -1
  13. package/dist/loaders/locomo.js +2 -2
  14. package/dist/loaders/locomo.js.map +1 -1
  15. package/dist/loaders/longmemeval.js +1 -1
  16. package/dist/loaders/longmemeval.js.map +1 -1
  17. package/dist/loaders/memory-eval.d.ts +1 -1
  18. package/dist/package.js +6 -0
  19. package/dist/package.js.map +1 -0
  20. package/dist/regression.d.ts.map +1 -1
  21. package/dist/regression.js +29 -7
  22. package/dist/regression.js.map +1 -1
  23. package/dist/reporters/html.js.map +1 -1
  24. package/dist/reporters/markdown.js +1 -1
  25. package/dist/reporters/markdown.js.map +1 -1
  26. package/dist/reporters/terminal.js +1 -1
  27. package/dist/reporters/terminal.js.map +1 -1
  28. package/dist/runner.d.ts.map +1 -1
  29. package/dist/runner.js +25 -1
  30. package/dist/runner.js.map +1 -1
  31. package/dist/scorers/code/exact-match.js.map +1 -1
  32. package/dist/scorers/code/json-path.js.map +1 -1
  33. package/dist/scorers/code/predicate.js.map +1 -1
  34. package/dist/scorers/code/regex.js.map +1 -1
  35. package/dist/scorers/llm/judge.d.ts +3 -3
  36. package/dist/scorers/llm/judge.js +4 -4
  37. package/dist/scorers/llm/judge.js.map +1 -1
  38. package/dist/scorers/trajectory/argument-validity.js.map +1 -1
  39. package/dist/scorers/trajectory/correct-tool-selected.js.map +1 -1
  40. package/dist/scorers/trajectory/final-state-correct.js.map +1 -1
  41. package/dist/scorers/trajectory/recovery-after-error.js.map +1 -1
  42. package/dist/scorers/trajectory/redundant-call-detection.js.map +1 -1
  43. package/dist/scorers/trajectory/types.d.ts +2 -2
  44. package/dist/scorers/trajectory/util.js +1 -1
  45. package/dist/scorers/trajectory/util.js.map +1 -1
  46. package/dist/stats.d.ts +115 -0
  47. package/dist/stats.d.ts.map +1 -0
  48. package/dist/stats.js +180 -0
  49. package/dist/stats.js.map +1 -0
  50. package/dist/types.d.ts +20 -2
  51. package/dist/types.d.ts.map +1 -1
  52. package/package.json +4 -4
@@ -0,0 +1,115 @@
1
+ //#region src/stats.d.ts
2
+ /**
3
+ * Shared eval statistics (audit 2026-07-04, E8; closes the evals-05 /
4
+ * evals-08 gap): confidence intervals, pass^k, and paired significance
5
+ * for benchmark deltas. Everything here is pure and dependency-free so
6
+ * the runner, the regression gate, and the benchmarks can share one
7
+ * implementation instead of each inlining its own mean/stddev.
8
+ *
9
+ * Statistical choices, briefly:
10
+ * - **Wilson score interval** for pass-rate CIs. Eval suites are small
11
+ * (n in the tens) and pass rates hug 0/1, exactly where the normal
12
+ * approximation interval collapses; Wilson stays sane there.
13
+ * - **pass^k** ("pass-hat-k", the fraction of base cases that pass in
14
+ * ALL k repeat iterations) as the stability metric for `iterations>1`
15
+ * runs - a mean pass rate hides a case that flips every other run.
16
+ * - **McNemar's test** (continuity-corrected normal approximation) for
17
+ * "did the pass rate really change vs the baseline": the same cases
18
+ * ran in both reports, so a PAIRED test on the discordant cases has
19
+ * far more power than comparing two independent proportions - and it
20
+ * is sample-size aware, which the fixed-tolerance regression gate is
21
+ * not.
22
+ *
23
+ * @packageDocumentation
24
+ */
25
+ /**
26
+ * Arithmetic mean; `0` for an empty list.
27
+ *
28
+ * @stable
29
+ */
30
+ declare function mean(values: ReadonlyArray<number>): number;
31
+ /**
32
+ * Sample standard deviation (n-1 denominator); `0` for n < 2.
33
+ *
34
+ * @stable
35
+ */
36
+ declare function sampleStddev(values: ReadonlyArray<number>): number;
37
+ /**
38
+ * Wilson score interval for a binomial proportion.
39
+ *
40
+ * @param passed number of successes
41
+ * @param total number of trials
42
+ * @param z normal quantile (default 1.96 = 95% two-sided)
43
+ * @returns `{ lo, hi }` clamped to [0, 1]; `{ lo: 0, hi: 1 }` when `total` is 0
44
+ *
45
+ * @stable
46
+ */
47
+ declare function wilsonInterval(passed: number, total: number, z?: number): {
48
+ readonly lo: number;
49
+ readonly hi: number;
50
+ };
51
+ /**
52
+ * Strip the `-iter-N` disambiguation suffix the runner appends under
53
+ * `iterations > 1` (EB-6), recovering the base case id.
54
+ *
55
+ * @stable
56
+ */
57
+ declare function stripIterationSuffix(caseId: string): string;
58
+ /**
59
+ * pass^k over per-iteration case outcomes: group by base case id and
60
+ * report the fraction of base cases whose EVERY iteration passed.
61
+ *
62
+ * @param outcomes one entry per executed case iteration
63
+ * @returns `k` = the largest group size observed, `baseCases` = number of
64
+ * distinct base cases, `value` = the pass^k fraction (0 when no cases)
65
+ *
66
+ * @stable
67
+ */
68
+ declare function passHatK(outcomes: ReadonlyArray<{
69
+ readonly caseId: string;
70
+ readonly pass: boolean;
71
+ }>): {
72
+ readonly k: number;
73
+ readonly baseCases: number;
74
+ readonly value: number;
75
+ };
76
+ /**
77
+ * Result of {@link pairedPassSignificance}.
78
+ *
79
+ * @stable
80
+ */
81
+ interface PairedSignificance {
82
+ /** Cases present in BOTH runs (the paired sample). */
83
+ readonly pairs: number;
84
+ /** baseline-pass -> current-fail count (the regressions). */
85
+ readonly regressed: number;
86
+ /** baseline-fail -> current-pass count (the improvements). */
87
+ readonly improved: number;
88
+ /**
89
+ * Two-sided p-value from McNemar's test (continuity-corrected normal
90
+ * approximation over the discordant pairs). `1` when there are no
91
+ * discordant pairs - identical outcomes are never "significant".
92
+ */
93
+ readonly pValue: number;
94
+ }
95
+ /**
96
+ * McNemar's paired test on per-case pass outcomes of two eval runs.
97
+ * Only cases present in both maps are compared (by base case id).
98
+ *
99
+ * @stable
100
+ */
101
+ declare function pairedPassSignificance(current: ReadonlyMap<string, boolean>, baseline: ReadonlyMap<string, boolean>): PairedSignificance;
102
+ /**
103
+ * Collapse per-iteration outcomes to per-base-case pass (a base case
104
+ * passes when EVERY iteration passed) - the paired unit for
105
+ * {@link pairedPassSignificance}.
106
+ *
107
+ * @stable
108
+ */
109
+ declare function passByBaseCase(outcomes: ReadonlyArray<{
110
+ readonly caseId: string;
111
+ readonly pass: boolean;
112
+ }>): ReadonlyMap<string, boolean>;
113
+ //#endregion
114
+ export { PairedSignificance, mean, pairedPassSignificance, passByBaseCase, passHatK, sampleStddev, stripIterationSuffix, wilsonInterval };
115
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","names":[],"sources":["../src/stats.ts"],"sourcesContent":[],"mappings":";;AA6BA;AAYA;AAkBA;AAyBA;AAcA;AA0BA;AAqBA;;;;;AA6BA;;;;;;;;;;;;;;;;iBAjJgB,IAAA,SAAa;;;;;;iBAYb,YAAA,SAAqB;;;;;;;;;;;iBAkBrB,cAAA;;;;;;;;;;iBAyBA,oBAAA;;;;;;;;;;;iBAcA,QAAA,WACJ;;;;;;;;;;;;;UAyBK,kBAAA;;;;;;;;;;;;;;;;;;;;iBAqBD,sBAAA,UACL,wCACC,+BACT;;;;;;;;iBA0Ba,cAAA,WACJ;;;KACT"}
package/dist/stats.js ADDED
@@ -0,0 +1,180 @@
1
+ //#region src/stats.ts
2
+ /**
3
+ * Shared eval statistics (audit 2026-07-04, E8; closes the evals-05 /
4
+ * evals-08 gap): confidence intervals, pass^k, and paired significance
5
+ * for benchmark deltas. Everything here is pure and dependency-free so
6
+ * the runner, the regression gate, and the benchmarks can share one
7
+ * implementation instead of each inlining its own mean/stddev.
8
+ *
9
+ * Statistical choices, briefly:
10
+ * - **Wilson score interval** for pass-rate CIs. Eval suites are small
11
+ * (n in the tens) and pass rates hug 0/1, exactly where the normal
12
+ * approximation interval collapses; Wilson stays sane there.
13
+ * - **pass^k** ("pass-hat-k", the fraction of base cases that pass in
14
+ * ALL k repeat iterations) as the stability metric for `iterations>1`
15
+ * runs - a mean pass rate hides a case that flips every other run.
16
+ * - **McNemar's test** (continuity-corrected normal approximation) for
17
+ * "did the pass rate really change vs the baseline": the same cases
18
+ * ran in both reports, so a PAIRED test on the discordant cases has
19
+ * far more power than comparing two independent proportions - and it
20
+ * is sample-size aware, which the fixed-tolerance regression gate is
21
+ * not.
22
+ *
23
+ * @packageDocumentation
24
+ */
25
+ /**
26
+ * Arithmetic mean; `0` for an empty list.
27
+ *
28
+ * @stable
29
+ */
30
+ function mean(values) {
31
+ if (values.length === 0) return 0;
32
+ let sum = 0;
33
+ for (const v of values) sum += v;
34
+ return sum / values.length;
35
+ }
36
+ /**
37
+ * Sample standard deviation (n-1 denominator); `0` for n < 2.
38
+ *
39
+ * @stable
40
+ */
41
+ function sampleStddev(values) {
42
+ if (values.length < 2) return 0;
43
+ const m = mean(values);
44
+ let acc = 0;
45
+ for (const v of values) acc += (v - m) * (v - m);
46
+ return Math.sqrt(acc / (values.length - 1));
47
+ }
48
+ /**
49
+ * Wilson score interval for a binomial proportion.
50
+ *
51
+ * @param passed number of successes
52
+ * @param total number of trials
53
+ * @param z normal quantile (default 1.96 = 95% two-sided)
54
+ * @returns `{ lo, hi }` clamped to [0, 1]; `{ lo: 0, hi: 1 }` when `total` is 0
55
+ *
56
+ * @stable
57
+ */
58
+ function wilsonInterval(passed, total, z = 1.96) {
59
+ if (total <= 0) return {
60
+ lo: 0,
61
+ hi: 1
62
+ };
63
+ const p = passed / total;
64
+ const z2 = z * z;
65
+ const denom = 1 + z2 / total;
66
+ const centre = p + z2 / (2 * total);
67
+ const margin = z * Math.sqrt(p * (1 - p) / total + z2 / (4 * total * total));
68
+ return {
69
+ lo: Math.max(0, (centre - margin) / denom),
70
+ hi: Math.min(1, (centre + margin) / denom)
71
+ };
72
+ }
73
+ const ITERATION_SUFFIX_RE = /-iter-\d+$/;
74
+ /**
75
+ * Strip the `-iter-N` disambiguation suffix the runner appends under
76
+ * `iterations > 1` (EB-6), recovering the base case id.
77
+ *
78
+ * @stable
79
+ */
80
+ function stripIterationSuffix(caseId) {
81
+ return caseId.replace(ITERATION_SUFFIX_RE, "");
82
+ }
83
+ /**
84
+ * pass^k over per-iteration case outcomes: group by base case id and
85
+ * report the fraction of base cases whose EVERY iteration passed.
86
+ *
87
+ * @param outcomes one entry per executed case iteration
88
+ * @returns `k` = the largest group size observed, `baseCases` = number of
89
+ * distinct base cases, `value` = the pass^k fraction (0 when no cases)
90
+ *
91
+ * @stable
92
+ */
93
+ function passHatK(outcomes) {
94
+ const groups = /* @__PURE__ */ new Map();
95
+ for (const o of outcomes) {
96
+ const base = stripIterationSuffix(o.caseId);
97
+ const g = groups.get(base) ?? {
98
+ total: 0,
99
+ passed: 0
100
+ };
101
+ g.total += 1;
102
+ if (o.pass) g.passed += 1;
103
+ groups.set(base, g);
104
+ }
105
+ if (groups.size === 0) return {
106
+ k: 0,
107
+ baseCases: 0,
108
+ value: 0
109
+ };
110
+ let k = 0;
111
+ let allPass = 0;
112
+ for (const g of groups.values()) {
113
+ if (g.total > k) k = g.total;
114
+ if (g.passed === g.total) allPass += 1;
115
+ }
116
+ return {
117
+ k,
118
+ baseCases: groups.size,
119
+ value: allPass / groups.size
120
+ };
121
+ }
122
+ /**
123
+ * McNemar's paired test on per-case pass outcomes of two eval runs.
124
+ * Only cases present in both maps are compared (by base case id).
125
+ *
126
+ * @stable
127
+ */
128
+ function pairedPassSignificance(current, baseline) {
129
+ let pairs = 0;
130
+ let regressed = 0;
131
+ let improved = 0;
132
+ for (const [caseId, basePass] of baseline) {
133
+ const curPass = current.get(caseId);
134
+ if (curPass === void 0) continue;
135
+ pairs += 1;
136
+ if (basePass && !curPass) regressed += 1;
137
+ else if (!basePass && curPass) improved += 1;
138
+ }
139
+ const discordant = regressed + improved;
140
+ if (discordant === 0) return {
141
+ pairs,
142
+ regressed,
143
+ improved,
144
+ pValue: 1
145
+ };
146
+ const z = (Math.abs(regressed - improved) - 1) / Math.sqrt(discordant);
147
+ const pValue = z <= 0 ? 1 : Math.min(1, 2 * (1 - normalCdf(z)));
148
+ return {
149
+ pairs,
150
+ regressed,
151
+ improved,
152
+ pValue
153
+ };
154
+ }
155
+ /**
156
+ * Collapse per-iteration outcomes to per-base-case pass (a base case
157
+ * passes when EVERY iteration passed) - the paired unit for
158
+ * {@link pairedPassSignificance}.
159
+ *
160
+ * @stable
161
+ */
162
+ function passByBaseCase(outcomes) {
163
+ const out = /* @__PURE__ */ new Map();
164
+ for (const o of outcomes) {
165
+ const base = stripIterationSuffix(o.caseId);
166
+ const prev = out.get(base);
167
+ out.set(base, prev === void 0 ? o.pass : prev && o.pass);
168
+ }
169
+ return out;
170
+ }
171
+ /** Standard normal CDF via the Abramowitz-Stegun 7.1.26 erf approximation. */
172
+ function normalCdf(x) {
173
+ const t = 1 / (1 + .3275911 * (Math.abs(x) / Math.SQRT2));
174
+ const erf = 1 - ((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t - .284496736) * t + .254829592) * t * Math.exp(-(x * x) / 2);
175
+ return x >= 0 ? .5 * (1 + erf) : .5 * (1 - erf);
176
+ }
177
+
178
+ //#endregion
179
+ export { mean, pairedPassSignificance, passByBaseCase, passHatK, sampleStddev, stripIterationSuffix, wilsonInterval };
180
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","names":[],"sources":["../src/stats.ts"],"sourcesContent":["/**\n * Shared eval statistics (audit 2026-07-04, E8; closes the evals-05 /\n * evals-08 gap): confidence intervals, pass^k, and paired significance\n * for benchmark deltas. Everything here is pure and dependency-free so\n * the runner, the regression gate, and the benchmarks can share one\n * implementation instead of each inlining its own mean/stddev.\n *\n * Statistical choices, briefly:\n * - **Wilson score interval** for pass-rate CIs. Eval suites are small\n * (n in the tens) and pass rates hug 0/1, exactly where the normal\n * approximation interval collapses; Wilson stays sane there.\n * - **pass^k** (\"pass-hat-k\", the fraction of base cases that pass in\n * ALL k repeat iterations) as the stability metric for `iterations>1`\n * runs - a mean pass rate hides a case that flips every other run.\n * - **McNemar's test** (continuity-corrected normal approximation) for\n * \"did the pass rate really change vs the baseline\": the same cases\n * ran in both reports, so a PAIRED test on the discordant cases has\n * far more power than comparing two independent proportions - and it\n * is sample-size aware, which the fixed-tolerance regression gate is\n * not.\n *\n * @packageDocumentation\n */\n\n/**\n * Arithmetic mean; `0` for an empty list.\n *\n * @stable\n */\nexport function mean(values: ReadonlyArray<number>): number {\n if (values.length === 0) return 0;\n let sum = 0;\n for (const v of values) sum += v;\n return sum / values.length;\n}\n\n/**\n * Sample standard deviation (n-1 denominator); `0` for n < 2.\n *\n * @stable\n */\nexport function sampleStddev(values: ReadonlyArray<number>): number {\n if (values.length < 2) return 0;\n const m = mean(values);\n let acc = 0;\n for (const v of values) acc += (v - m) * (v - m);\n return Math.sqrt(acc / (values.length - 1));\n}\n\n/**\n * Wilson score interval for a binomial proportion.\n *\n * @param passed number of successes\n * @param total number of trials\n * @param z normal quantile (default 1.96 = 95% two-sided)\n * @returns `{ lo, hi }` clamped to [0, 1]; `{ lo: 0, hi: 1 }` when `total` is 0\n *\n * @stable\n */\nexport function wilsonInterval(\n passed: number,\n total: number,\n z = 1.96,\n): { readonly lo: number; readonly hi: number } {\n if (total <= 0) return { lo: 0, hi: 1 };\n const p = passed / total;\n const z2 = z * z;\n const denom = 1 + z2 / total;\n const centre = p + z2 / (2 * total);\n const margin = z * Math.sqrt((p * (1 - p)) / total + z2 / (4 * total * total));\n return {\n lo: Math.max(0, (centre - margin) / denom),\n hi: Math.min(1, (centre + margin) / denom),\n };\n}\n\nconst ITERATION_SUFFIX_RE = /-iter-\\d+$/;\n\n/**\n * Strip the `-iter-N` disambiguation suffix the runner appends under\n * `iterations > 1` (EB-6), recovering the base case id.\n *\n * @stable\n */\nexport function stripIterationSuffix(caseId: string): string {\n return caseId.replace(ITERATION_SUFFIX_RE, '');\n}\n\n/**\n * pass^k over per-iteration case outcomes: group by base case id and\n * report the fraction of base cases whose EVERY iteration passed.\n *\n * @param outcomes one entry per executed case iteration\n * @returns `k` = the largest group size observed, `baseCases` = number of\n * distinct base cases, `value` = the pass^k fraction (0 when no cases)\n *\n * @stable\n */\nexport function passHatK(\n outcomes: ReadonlyArray<{ readonly caseId: string; readonly pass: boolean }>,\n): { readonly k: number; readonly baseCases: number; readonly value: number } {\n const groups = new Map<string, { total: number; passed: number }>();\n for (const o of outcomes) {\n const base = stripIterationSuffix(o.caseId);\n const g = groups.get(base) ?? { total: 0, passed: 0 };\n g.total += 1;\n if (o.pass) g.passed += 1;\n groups.set(base, g);\n }\n if (groups.size === 0) return { k: 0, baseCases: 0, value: 0 };\n let k = 0;\n let allPass = 0;\n for (const g of groups.values()) {\n if (g.total > k) k = g.total;\n if (g.passed === g.total) allPass += 1;\n }\n return { k, baseCases: groups.size, value: allPass / groups.size };\n}\n\n/**\n * Result of {@link pairedPassSignificance}.\n *\n * @stable\n */\nexport interface PairedSignificance {\n /** Cases present in BOTH runs (the paired sample). */\n readonly pairs: number;\n /** baseline-pass -> current-fail count (the regressions). */\n readonly regressed: number;\n /** baseline-fail -> current-pass count (the improvements). */\n readonly improved: number;\n /**\n * Two-sided p-value from McNemar's test (continuity-corrected normal\n * approximation over the discordant pairs). `1` when there are no\n * discordant pairs - identical outcomes are never \"significant\".\n */\n readonly pValue: number;\n}\n\n/**\n * McNemar's paired test on per-case pass outcomes of two eval runs.\n * Only cases present in both maps are compared (by base case id).\n *\n * @stable\n */\nexport function pairedPassSignificance(\n current: ReadonlyMap<string, boolean>,\n baseline: ReadonlyMap<string, boolean>,\n): PairedSignificance {\n let pairs = 0;\n let regressed = 0;\n let improved = 0;\n for (const [caseId, basePass] of baseline) {\n const curPass = current.get(caseId);\n if (curPass === undefined) continue;\n pairs += 1;\n if (basePass && !curPass) regressed += 1;\n else if (!basePass && curPass) improved += 1;\n }\n const discordant = regressed + improved;\n if (discordant === 0) return { pairs, regressed, improved, pValue: 1 };\n // Continuity-corrected normal approximation to the binomial(discordant, 0.5).\n const z = (Math.abs(regressed - improved) - 1) / Math.sqrt(discordant);\n const pValue = z <= 0 ? 1 : Math.min(1, 2 * (1 - normalCdf(z)));\n return { pairs, regressed, improved, pValue };\n}\n\n/**\n * Collapse per-iteration outcomes to per-base-case pass (a base case\n * passes when EVERY iteration passed) - the paired unit for\n * {@link pairedPassSignificance}.\n *\n * @stable\n */\nexport function passByBaseCase(\n outcomes: ReadonlyArray<{ readonly caseId: string; readonly pass: boolean }>,\n): ReadonlyMap<string, boolean> {\n const out = new Map<string, boolean>();\n for (const o of outcomes) {\n const base = stripIterationSuffix(o.caseId);\n const prev = out.get(base);\n out.set(base, prev === undefined ? o.pass : prev && o.pass);\n }\n return out;\n}\n\n/** Standard normal CDF via the Abramowitz-Stegun 7.1.26 erf approximation. */\nfunction normalCdf(x: number): number {\n const t = 1 / (1 + 0.3275911 * (Math.abs(x) / Math.SQRT2));\n const erf =\n 1 -\n ((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t - 0.284496736) * t + 0.254829592) *\n t *\n Math.exp(-(x * x) / 2);\n return x >= 0 ? 0.5 * (1 + erf) : 0.5 * (1 - erf);\n}\n"],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -4,7 +4,7 @@ import { Case as Case$1, Dataset as Dataset$1, EvalCaseResult, EvalReport, EvalR
4
4
 
5
5
  /**
6
6
  * Agent shape consumed by the runner. Anything with a `run(input)`
7
- * method satisfies the contract the framework's own `Agent` type
7
+ * method satisfies the contract - the framework's own `Agent` type
8
8
  * matches by structural typing.
9
9
  *
10
10
  * @stable
@@ -38,7 +38,7 @@ interface RunOptions<I, O> {
38
38
  /**
39
39
  * How to handle `signal` abort. Default (`false`): stop dispatching and
40
40
  * resolve with a **partial** {@link EvalReport} (`aborted: true`) covering
41
- * the cases that finished a long judged run shouldn't lose all completed
41
+ * the cases that finished - a long judged run shouldn't lose all completed
42
42
  * work to a Ctrl+C. Set `true` to throw the abort reason instead.
43
43
  */
44
44
  readonly throwOnAbort?: boolean;
@@ -74,6 +74,18 @@ interface RegressionOptions {
74
74
  * absolute duration gate; leave unset to ignore duration entirely.
75
75
  */
76
76
  readonly maxAvgDurationIncreaseMs?: number;
77
+ /**
78
+ * E8 (evals-05/08): when `true`, a `pass-rate-drop` finding is only kept if
79
+ * McNemar's paired test over the shared cases rejects "no real change" at
80
+ * {@link significanceAlpha} - a fixed percentage tolerance is blind to
81
+ * sample size (a 5pp drop is one case in a 20-case suite). Off by default
82
+ * so existing gates keep their exact behavior; the computed `pValue` is
83
+ * attached to the finding either way whenever both reports carry per-case
84
+ * results.
85
+ */
86
+ readonly requireSignificance?: boolean;
87
+ /** Significance level for {@link requireSignificance}. Default `0.05`. */
88
+ readonly significanceAlpha?: number;
77
89
  }
78
90
  /**
79
91
  * Result of {@link detectRegressions}.
@@ -92,6 +104,12 @@ interface RegressionFinding {
92
104
  readonly scorer?: string;
93
105
  readonly message: string;
94
106
  readonly delta: number;
107
+ /**
108
+ * Two-sided McNemar p-value over the cases shared by both reports
109
+ * (E8; only on `pass-rate-drop` findings, and only when both reports
110
+ * carry per-case results to pair on).
111
+ */
112
+ readonly pValue?: number;
95
113
  }
96
114
  //#endregion
97
115
  export { AgentLike, type Case$1 as Case, type Dataset$1 as Dataset, type EvalCaseResult, type EvalReport$1 as EvalReport, ProgressEvent, RegressionFinding, RegressionOptions, RegressionReport, type RunEvalOptions, RunOptions, type ScoreResult$1 as ScoreResult, type Scorer$1 as Scorer };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAiD4C,UAnB3B,SAmB2B,CAAA,CAAA,EAAA,CAAA,CAAA,CAAA;EAAV,GAAA,CAAA,KAAA,EAlBrB,CAkBqB,EAAA,GAKd,CALc,EAAA;IAAd,MAAA,CAAA,EAlBa,WAkBb;EAKA,CAAA,CAAA,EAvB6B,OAuB7B,CAvBqC,CAuBrC,CAAA;;;AAgBpB;AAaA;AAoBA;;AAEqB,UAlEJ,UAkEI,CAAA,CAAA,EAAA,CAAA,CAAA,CAAA;EACU,SAAA,KAAA,EAlEb,SAkEa,CAlEH,CAkEG,EAlEA,CAkEA,CAAA;EAAG,SAAA,OAAA,EAAA;IAAd,SAAA,KAAA,EAhEA,aAgEA,CAAA;MACY,SAAA,EAAA,CAAA,EAAA,MAAA;MAAG,SAAA,KAAA,EA/Db,CA+Da;MAAd,SAAA,QAAA,CAAA,EA9DK,CA8DL;MAAU,SAAA,QAAA,CAAA,EA7DL,QA6DK,CA7DI,MA6DJ,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;IAId,CAAA,CAAA;;oBA9DG,cAAc,OAAO,GAAG;;;;;oBAKxB;;;;;;;;;;;;gCAYY;;;UAIf,aAAA;;;;;;;;;;;;UAaA,iBAAA;;;;;;;;;;;;;;;;;;;UAoBA;;qBAEI,cAAc;oBACf,WAAW,GAAG;qBACb,WAAW,GAAG;;;UAIlB,iBAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAiD4C,UAnB3B,SAmB2B,CAAA,CAAA,EAAA,CAAA,CAAA,CAAA;EAAV,GAAA,CAAA,KAAA,EAlBrB,CAkBqB,EAAA,GAKd,CALc,EAAA;IAAd,MAAA,CAAA,EAlBa,WAkBb;EAKA,CAAA,CAAA,EAvB6B,OAuB7B,CAvBqC,CAuBrC,CAAA;;;AAgBpB;AAaA;AAgCA;;AAEqB,UA9EJ,UA8EI,CAAA,CAAA,EAAA,CAAA,CAAA,CAAA;EACU,SAAA,KAAA,EA9Eb,SA8Ea,CA9EH,CA8EG,EA9EA,CA8EA,CAAA;EAAG,SAAA,OAAA,EAAA;IAAd,SAAA,KAAA,EA5EA,aA4EA,CAAA;MACY,SAAA,EAAA,CAAA,EAAA,MAAA;MAAG,SAAA,KAAA,EA3Eb,CA2Ea;MAAd,SAAA,QAAA,CAAA,EA1EK,CA0EL;MAAU,SAAA,QAAA,CAAA,EAzEL,QAyEK,CAzEI,MAyEJ,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;IAId,CAAA,CAAA;;oBA1EG,cAAc,OAAO,GAAG;;;;;oBAKxB;;;;;;;;;;;;gCAYY;;;UAIf,aAAA;;;;;;;;;;;;UAaA,iBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAgCA;;qBAEI,cAAc;oBACf,WAAW,GAAG;qBACb,WAAW,GAAG;;;UAIlB,iBAAA"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@graphorin/evals",
3
- "version": "0.5.0",
4
- "description": "Eval framework for the Graphorin framework. Ships scorer libraries (`code/` exact-match, regex, JSON path, predicates; `llm/` LLM-judge; `prebuilt/` toxicity / factuality / helpfulness), dataset loaders (JSONL, CSV, from-traces, generic iterable), reporters (terminal, markdown, JSON, JUnit XML), a parallel runner with `concurrency` config, and regression detection that compares the current run against a stored baseline. Builds on the eval primitives shipped from `@graphorin/observability` (RB-17 / DEC-152 full orchestrator lives here, post-MVP, decoupled from the core observability bundle so consumers do not pay the dataset / reporter cost when only the inline runner is needed). Created and maintained by Oleksiy Stepurenko.",
3
+ "version": "0.6.1",
4
+ "description": "Eval framework for the Graphorin framework. Ships scorer libraries (`code/` exact-match, regex, JSON path, predicates; `llm/` LLM-judge; `prebuilt/` toxicity / factuality / helpfulness), dataset loaders (JSONL, CSV, from-traces, generic iterable), reporters (terminal, markdown, JSON, JUnit XML), a parallel runner with `concurrency` config, and regression detection that compares the current run against a stored baseline. Builds on the eval primitives shipped from `@graphorin/observability` (RB-17 / DEC-152 - full orchestrator lives here, post-MVP, decoupled from the core observability bundle so consumers do not pay the dataset / reporter cost when only the inline runner is needed). Created and maintained by Oleksiy Stepurenko.",
5
5
  "license": "MIT",
6
6
  "author": "Oleksiy Stepurenko",
7
7
  "homepage": "https://github.com/o-stepper/graphorin/tree/main/packages/evals",
@@ -64,8 +64,8 @@
64
64
  "LICENSE"
65
65
  ],
66
66
  "dependencies": {
67
- "@graphorin/core": "0.5.0",
68
- "@graphorin/observability": "0.5.0"
67
+ "@graphorin/core": "0.6.1",
68
+ "@graphorin/observability": "0.6.1"
69
69
  },
70
70
  "publishConfig": {
71
71
  "access": "public",