abeval 0.1.0__tar.gz

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.
@@ -0,0 +1 @@
1
+ github: mohammadi-hadi
@@ -0,0 +1,10 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: /
5
+ schedule:
6
+ interval: monthly
7
+ - package-ecosystem: pip
8
+ directory: /
9
+ schedule:
10
+ interval: monthly
@@ -0,0 +1,29 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.10", "3.14"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: ${{ matrix.python-version }}
19
+ - name: Install
20
+ run: pip install -e ".[dev]"
21
+ - name: Lint
22
+ run: ruff check .
23
+ - name: Test
24
+ run: pytest -q
25
+ - name: CLI smoke test
26
+ run: |
27
+ abeval --version
28
+ abeval compare examples/run_a.jsonl examples/run_b.jsonl
29
+ abeval power --baseline 0.75 --delta 0.03 --json
@@ -0,0 +1,18 @@
1
+ cff-version: 1.2.0
2
+ message: "If abeval is useful in your work, please cite this repository."
3
+ title: "abeval: A/B-test statistics for LLM evals"
4
+ type: software
5
+ authors:
6
+ - family-names: Mohammadi
7
+ given-names: Hadi
8
+ version: 0.1.0
9
+ date-released: 2026-08-05
10
+ license: MIT
11
+ repository-code: "https://github.com/mohammadi-hadi/abeval"
12
+ url: "https://mohammadi.cv/abeval/"
13
+ keywords:
14
+ - llm
15
+ - evaluation
16
+ - statistics
17
+ - ab-testing
18
+ - significance
abeval-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hadi Mohammadi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
abeval-0.1.0/Makefile ADDED
@@ -0,0 +1,33 @@
1
+ .PHONY: install lint test demo serve build
2
+
3
+ install:
4
+ python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
5
+
6
+ lint:
7
+ .venv/bin/ruff check .
8
+
9
+ test:
10
+ .venv/bin/python -m pytest -q
11
+
12
+ demo:
13
+ .venv/bin/python -m abeval compare examples/run_a.jsonl examples/run_b.jsonl
14
+ .venv/bin/python -m abeval power --baseline 0.75 --delta 0.03
15
+
16
+ serve:
17
+ .venv/bin/python -m http.server 8765 -d docs
18
+
19
+ # Build sdist + wheel from a clean `git archive` export in a temp directory
20
+ # OUTSIDE the working tree (hatchling walks upward and would otherwise pick up
21
+ # local ignore files), so only committed files can reach a distribution.
22
+ # The artifacts are audited afterwards.
23
+ build:
24
+ rm -rf dist
25
+ @export_dir=$$(mktemp -d) && \
26
+ git archive HEAD | tar -x -C "$$export_dir" && \
27
+ .venv/bin/python -m build --outdir dist "$$export_dir" && \
28
+ rm -rf "$$export_dir"
29
+ @if tar -tzf dist/*.tar.gz | grep -qiE 'claude|gitignore'; then \
30
+ echo "TAINTED SDIST"; exit 1; fi
31
+ @if unzip -l dist/*.whl | grep -qiE 'claude|gitignore'; then \
32
+ echo "TAINTED WHEEL"; exit 1; fi
33
+ @echo "artifacts clean:" && ls dist
abeval-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,201 @@
1
+ Metadata-Version: 2.4
2
+ Name: abeval
3
+ Version: 0.1.0
4
+ Summary: A/B-test statistics for LLM evals: error bars, paired comparisons, and sample-size planning
5
+ Project-URL: Homepage, https://github.com/mohammadi-hadi/abeval
6
+ Project-URL: Calculator, https://mohammadi.cv/abeval/
7
+ Author: Hadi Mohammadi
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: ab-testing,evaluation,llm,sample-size,significance,statistics
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Requires-Python: >=3.10
18
+ Provides-Extra: dev
19
+ Requires-Dist: build; extra == 'dev'
20
+ Requires-Dist: pytest; extra == 'dev'
21
+ Requires-Dist: ruff; extra == 'dev'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # abeval
25
+
26
+ A/B-test statistics for LLM evals.
27
+
28
+ Eval scores are sample estimates, but they're routinely reported as exact
29
+ numbers: "our prompt scores 76.5%, the old one 63.5%, ship it." On 200 items
30
+ much smaller gaps than that are pure noise. abeval treats an eval like an A/B
31
+ test — confidence intervals for a single run, paired significance tests for
32
+ comparing two runs on the same items, and sample-size planning *before* you
33
+ spend on inference. Standard library only, no dependencies.
34
+
35
+ **Sample-size calculator:** https://mohammadi.cv/abeval/
36
+
37
+ ## Install
38
+
39
+ ```
40
+ pip install abeval
41
+ ```
42
+
43
+ Or from source: `git clone https://github.com/mohammadi-hadi/abeval && cd abeval && make install`.
44
+
45
+ ## Is my new prompt actually better?
46
+
47
+ Point `abeval compare` at two JSONL result files that share item ids:
48
+
49
+ ```
50
+ $ abeval compare examples/run_a.jsonl examples/run_b.jsonl
51
+ A: 63.5% B: 76.5% (n=200 paired items)
52
+ B - A: +13.0% [4.5%, 21.5%] (95% CI)
53
+ p (sign-flip permutation): 0.0041 p (paired t): 0.0034
54
+ discordant items: B-only wins 53, A-only wins 27 p (exact McNemar): 0.0049
55
+ verdict: significant at the chosen level
56
+ ```
57
+
58
+ The comparison is **paired**: both runs are scored on the same items, so
59
+ item difficulty cancels out and you detect much smaller differences than two
60
+ independent runs would allow. Items present in only one run are dropped (and
61
+ reported).
62
+
63
+ Every command takes `--json` for machine-readable output, and `--seed` makes
64
+ the resampling reproducible.
65
+
66
+ ## Error bars for one run
67
+
68
+ ```
69
+ $ abeval ci examples/run_a.jsonl
70
+ score: 63.5% [56.6%, 69.9%] (n=200, 95% CI, wilson)
71
+ ```
72
+
73
+ Binary metrics get a Wilson score interval, continuous metrics a t interval
74
+ (or `--bootstrap`). If your items aren't independent — 5 questions generated
75
+ from each source document, say — pass `--cluster-key document` and the
76
+ interval widens to account for within-cluster correlation.
77
+
78
+ ## How many items do I need?
79
+
80
+ Run this *before* the eval, not after:
81
+
82
+ ```
83
+ $ abeval power --baseline 0.75 --delta 0.03
84
+ to detect 3.0% difference from a 75.0% baseline (corr=0.5):
85
+ n = 1568 paired items (80% power, 95% confidence)
86
+ with 100 items you can detect >= 11.9%
87
+ with 200 items you can detect >= 8.4%
88
+ with 500 items you can detect >= 5.3%
89
+ with 1000 items you can detect >= 3.8%
90
+ ```
91
+
92
+ The lesson generalizes: a 200-item eval cannot see a 3-point improvement.
93
+ `--corr` is the item-level correlation between the two runs' outcomes (0.5 is
94
+ a reasonable default for two variants of the same system; higher correlation
95
+ means fewer items needed). There's an interactive version at
96
+ https://mohammadi.cv/abeval/.
97
+
98
+ ## How noisy is my judge?
99
+
100
+ If an LLM judge scores the same item differently on repeat calls, that noise
101
+ eats your statistical power. Feed repeated judgments to `reliability`:
102
+
103
+ ```
104
+ $ abeval reliability examples/judge_repeats.jsonl
105
+ 40 items, 120 judgments (3.0 per item)
106
+ ICC (signal share of variance): 0.764
107
+ between-item sd: 1.708 judge noise sd: 0.9487
108
+ exact agreement across repeats: 15.0%
109
+ repeats per item to push judge noise under 10%: 3
110
+ ```
111
+
112
+ ICC is the share of score variance that is real item signal rather than judge
113
+ noise, from a one-way random-effects decomposition. The last line answers the
114
+ practical question: how many repeat judgments to average per item.
115
+
116
+ ## Data format
117
+
118
+ JSONL, one item per line, any extra fields ignored:
119
+
120
+ ```json
121
+ {"id": "item-001", "score": 1}
122
+ {"id": "item-002", "score": 0, "category": "reasoning"}
123
+ ```
124
+
125
+ `--metric` and `--id-key` rename the fields; booleans are accepted as 0/1.
126
+ The files in `examples/` are generated by `examples/make_fixtures.py`
127
+ (seeded, so they're reproducible).
128
+
129
+ ## Python API
130
+
131
+ Everything the CLI does is a plain function:
132
+
133
+ ```python
134
+ from abeval import paired_compare, proportion_ci, sample_size, sd_diff_from_rates
135
+
136
+ proportion_ci(127, 200) # Wilson interval, level=0.95
137
+ result = paired_compare(scores_a, scores_b, seed=0)
138
+ result.diff, result.ci_lo, result.ci_hi, result.p_permutation
139
+
140
+ sd = sd_diff_from_rates(0.75, 0.78, corr=0.5)
141
+ sample_size(0.03, sd) # -> 1568 paired items
142
+ ```
143
+
144
+ ## What's inside
145
+
146
+ | Question | Method |
147
+ |---|---|
148
+ | CI for a pass rate | Wilson score interval |
149
+ | CI for a mean score | Student t, percentile bootstrap, or cluster-robust t |
150
+ | Is B better than A? | Sign-flip permutation test on paired differences (primary), paired t, exact McNemar on discordant pairs (binary) |
151
+ | How many items? | Normal-approximation power for the paired design, with a Monte-Carlo check (`power_simulated`) |
152
+ | Judge noise | One-way random-effects ICC with unbalanced-design correction |
153
+
154
+ The permutation p-value is the primary test: it's exact under item
155
+ exchangeability and makes no normality assumption. The paired t and McNemar
156
+ values are printed alongside because reviewers ask for them.
157
+
158
+ The approach follows Miller,
159
+ ["Adding Error Bars to Evals"](https://arxiv.org/abs/2411.00640) (2024) —
160
+ report standard errors, use paired designs, plan sample sizes — packaged as a
161
+ tool you can point at result files.
162
+
163
+ ## Honest limitations
164
+
165
+ - The power formulas use the normal approximation; for very small samples or
166
+ rates near 0/1 trust `power_simulated` over the closed form.
167
+ - Cluster-robust intervals are approximate with fewer than ~20 clusters.
168
+ - Multiple-comparison correction is on you: if you compare ten prompts and
169
+ ship the best p-value, it's inflated (a `sweep` command with Bonferroni
170
+ correction is on the roadmap).
171
+ - This is not a stats library. If you need regressions or GLMs, use
172
+ statsmodels; abeval covers the eval loop with zero dependencies.
173
+
174
+ ## Sponsoring
175
+
176
+ abeval is MIT-licensed and dependency-free, and it stays that way. Sponsoring
177
+ funds the roadmap below and the maintenance time to keep the statistics
178
+ trustworthy. Sponsors are credited in release notes and vote on what lands
179
+ next: [GitHub Sponsors](https://github.com/sponsors/mohammadi-hadi).
180
+
181
+ ## Roadmap
182
+
183
+ - `sweep`: compare N runs against a baseline with multiple-comparison
184
+ correction.
185
+ - Confidence intervals for pairwise win rates (Bradley-Terry) for
186
+ head-to-head judge outputs.
187
+ - Variance-reduction guidance: how much power you buy by averaging k judge
188
+ repeats, given your measured ICC.
189
+
190
+ ## Related projects
191
+
192
+ - [judgewatch](https://github.com/mohammadi-hadi/judgewatch) — monthly bias
193
+ audits of LLM judges; abeval tells you whether this month's change is real.
194
+ - [judgekit](https://github.com/mohammadi-hadi/judgekit) — audit your own
195
+ judge pipelines.
196
+ - [trajectory-judge](https://github.com/mohammadi-hadi/trajectory-judge) —
197
+ evaluating agent trajectories with LLM judges.
198
+
199
+ ## License
200
+
201
+ MIT — see [LICENSE](LICENSE).
abeval-0.1.0/README.md ADDED
@@ -0,0 +1,178 @@
1
+ # abeval
2
+
3
+ A/B-test statistics for LLM evals.
4
+
5
+ Eval scores are sample estimates, but they're routinely reported as exact
6
+ numbers: "our prompt scores 76.5%, the old one 63.5%, ship it." On 200 items
7
+ much smaller gaps than that are pure noise. abeval treats an eval like an A/B
8
+ test — confidence intervals for a single run, paired significance tests for
9
+ comparing two runs on the same items, and sample-size planning *before* you
10
+ spend on inference. Standard library only, no dependencies.
11
+
12
+ **Sample-size calculator:** https://mohammadi.cv/abeval/
13
+
14
+ ## Install
15
+
16
+ ```
17
+ pip install abeval
18
+ ```
19
+
20
+ Or from source: `git clone https://github.com/mohammadi-hadi/abeval && cd abeval && make install`.
21
+
22
+ ## Is my new prompt actually better?
23
+
24
+ Point `abeval compare` at two JSONL result files that share item ids:
25
+
26
+ ```
27
+ $ abeval compare examples/run_a.jsonl examples/run_b.jsonl
28
+ A: 63.5% B: 76.5% (n=200 paired items)
29
+ B - A: +13.0% [4.5%, 21.5%] (95% CI)
30
+ p (sign-flip permutation): 0.0041 p (paired t): 0.0034
31
+ discordant items: B-only wins 53, A-only wins 27 p (exact McNemar): 0.0049
32
+ verdict: significant at the chosen level
33
+ ```
34
+
35
+ The comparison is **paired**: both runs are scored on the same items, so
36
+ item difficulty cancels out and you detect much smaller differences than two
37
+ independent runs would allow. Items present in only one run are dropped (and
38
+ reported).
39
+
40
+ Every command takes `--json` for machine-readable output, and `--seed` makes
41
+ the resampling reproducible.
42
+
43
+ ## Error bars for one run
44
+
45
+ ```
46
+ $ abeval ci examples/run_a.jsonl
47
+ score: 63.5% [56.6%, 69.9%] (n=200, 95% CI, wilson)
48
+ ```
49
+
50
+ Binary metrics get a Wilson score interval, continuous metrics a t interval
51
+ (or `--bootstrap`). If your items aren't independent — 5 questions generated
52
+ from each source document, say — pass `--cluster-key document` and the
53
+ interval widens to account for within-cluster correlation.
54
+
55
+ ## How many items do I need?
56
+
57
+ Run this *before* the eval, not after:
58
+
59
+ ```
60
+ $ abeval power --baseline 0.75 --delta 0.03
61
+ to detect 3.0% difference from a 75.0% baseline (corr=0.5):
62
+ n = 1568 paired items (80% power, 95% confidence)
63
+ with 100 items you can detect >= 11.9%
64
+ with 200 items you can detect >= 8.4%
65
+ with 500 items you can detect >= 5.3%
66
+ with 1000 items you can detect >= 3.8%
67
+ ```
68
+
69
+ The lesson generalizes: a 200-item eval cannot see a 3-point improvement.
70
+ `--corr` is the item-level correlation between the two runs' outcomes (0.5 is
71
+ a reasonable default for two variants of the same system; higher correlation
72
+ means fewer items needed). There's an interactive version at
73
+ https://mohammadi.cv/abeval/.
74
+
75
+ ## How noisy is my judge?
76
+
77
+ If an LLM judge scores the same item differently on repeat calls, that noise
78
+ eats your statistical power. Feed repeated judgments to `reliability`:
79
+
80
+ ```
81
+ $ abeval reliability examples/judge_repeats.jsonl
82
+ 40 items, 120 judgments (3.0 per item)
83
+ ICC (signal share of variance): 0.764
84
+ between-item sd: 1.708 judge noise sd: 0.9487
85
+ exact agreement across repeats: 15.0%
86
+ repeats per item to push judge noise under 10%: 3
87
+ ```
88
+
89
+ ICC is the share of score variance that is real item signal rather than judge
90
+ noise, from a one-way random-effects decomposition. The last line answers the
91
+ practical question: how many repeat judgments to average per item.
92
+
93
+ ## Data format
94
+
95
+ JSONL, one item per line, any extra fields ignored:
96
+
97
+ ```json
98
+ {"id": "item-001", "score": 1}
99
+ {"id": "item-002", "score": 0, "category": "reasoning"}
100
+ ```
101
+
102
+ `--metric` and `--id-key` rename the fields; booleans are accepted as 0/1.
103
+ The files in `examples/` are generated by `examples/make_fixtures.py`
104
+ (seeded, so they're reproducible).
105
+
106
+ ## Python API
107
+
108
+ Everything the CLI does is a plain function:
109
+
110
+ ```python
111
+ from abeval import paired_compare, proportion_ci, sample_size, sd_diff_from_rates
112
+
113
+ proportion_ci(127, 200) # Wilson interval, level=0.95
114
+ result = paired_compare(scores_a, scores_b, seed=0)
115
+ result.diff, result.ci_lo, result.ci_hi, result.p_permutation
116
+
117
+ sd = sd_diff_from_rates(0.75, 0.78, corr=0.5)
118
+ sample_size(0.03, sd) # -> 1568 paired items
119
+ ```
120
+
121
+ ## What's inside
122
+
123
+ | Question | Method |
124
+ |---|---|
125
+ | CI for a pass rate | Wilson score interval |
126
+ | CI for a mean score | Student t, percentile bootstrap, or cluster-robust t |
127
+ | Is B better than A? | Sign-flip permutation test on paired differences (primary), paired t, exact McNemar on discordant pairs (binary) |
128
+ | How many items? | Normal-approximation power for the paired design, with a Monte-Carlo check (`power_simulated`) |
129
+ | Judge noise | One-way random-effects ICC with unbalanced-design correction |
130
+
131
+ The permutation p-value is the primary test: it's exact under item
132
+ exchangeability and makes no normality assumption. The paired t and McNemar
133
+ values are printed alongside because reviewers ask for them.
134
+
135
+ The approach follows Miller,
136
+ ["Adding Error Bars to Evals"](https://arxiv.org/abs/2411.00640) (2024) —
137
+ report standard errors, use paired designs, plan sample sizes — packaged as a
138
+ tool you can point at result files.
139
+
140
+ ## Honest limitations
141
+
142
+ - The power formulas use the normal approximation; for very small samples or
143
+ rates near 0/1 trust `power_simulated` over the closed form.
144
+ - Cluster-robust intervals are approximate with fewer than ~20 clusters.
145
+ - Multiple-comparison correction is on you: if you compare ten prompts and
146
+ ship the best p-value, it's inflated (a `sweep` command with Bonferroni
147
+ correction is on the roadmap).
148
+ - This is not a stats library. If you need regressions or GLMs, use
149
+ statsmodels; abeval covers the eval loop with zero dependencies.
150
+
151
+ ## Sponsoring
152
+
153
+ abeval is MIT-licensed and dependency-free, and it stays that way. Sponsoring
154
+ funds the roadmap below and the maintenance time to keep the statistics
155
+ trustworthy. Sponsors are credited in release notes and vote on what lands
156
+ next: [GitHub Sponsors](https://github.com/sponsors/mohammadi-hadi).
157
+
158
+ ## Roadmap
159
+
160
+ - `sweep`: compare N runs against a baseline with multiple-comparison
161
+ correction.
162
+ - Confidence intervals for pairwise win rates (Bradley-Terry) for
163
+ head-to-head judge outputs.
164
+ - Variance-reduction guidance: how much power you buy by averaging k judge
165
+ repeats, given your measured ICC.
166
+
167
+ ## Related projects
168
+
169
+ - [judgewatch](https://github.com/mohammadi-hadi/judgewatch) — monthly bias
170
+ audits of LLM judges; abeval tells you whether this month's change is real.
171
+ - [judgekit](https://github.com/mohammadi-hadi/judgekit) — audit your own
172
+ judge pipelines.
173
+ - [trajectory-judge](https://github.com/mohammadi-hadi/trajectory-judge) —
174
+ evaluating agent trajectories with LLM judges.
175
+
176
+ ## License
177
+
178
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,34 @@
1
+ """abeval: A/B-test statistics for LLM evals.
2
+
3
+ Error bars for a single run, paired significance tests for comparing two
4
+ runs, sample-size planning before you spend on inference, and judge-noise
5
+ reliability. Standard library only.
6
+ """
7
+
8
+ from .ci import Interval, clustered_mean_ci, mean_ci, proportion_ci
9
+ from .compare import Comparison, mcnemar_exact, paired_compare
10
+ from .data import extract, pair, read_jsonl
11
+ from .power import mde, power_simulated, sample_size, sd_diff_from_rates
12
+ from .reliability import Reliability, judge_reliability
13
+
14
+ __version__ = "0.1.0"
15
+
16
+ __all__ = [
17
+ "Comparison",
18
+ "Interval",
19
+ "Reliability",
20
+ "__version__",
21
+ "clustered_mean_ci",
22
+ "extract",
23
+ "judge_reliability",
24
+ "mcnemar_exact",
25
+ "mde",
26
+ "mean_ci",
27
+ "pair",
28
+ "paired_compare",
29
+ "power_simulated",
30
+ "proportion_ci",
31
+ "read_jsonl",
32
+ "sample_size",
33
+ "sd_diff_from_rates",
34
+ ]