reliability-lab 0.1.0a1__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.
- reliability_lab-0.1.0a1/.gitattributes +2 -0
- reliability_lab-0.1.0a1/.github/workflows/ci.yml +50 -0
- reliability_lab-0.1.0a1/.github/workflows/release.yml +65 -0
- reliability_lab-0.1.0a1/.gitignore +28 -0
- reliability_lab-0.1.0a1/.python-version +1 -0
- reliability_lab-0.1.0a1/CHANGELOG.md +26 -0
- reliability_lab-0.1.0a1/LICENSE +28 -0
- reliability_lab-0.1.0a1/PKG-INFO +137 -0
- reliability_lab-0.1.0a1/README.md +107 -0
- reliability_lab-0.1.0a1/docs/design/framing-v2.md +323 -0
- reliability_lab-0.1.0a1/docs/releasing.md +32 -0
- reliability_lab-0.1.0a1/examples/quickstart.py +58 -0
- reliability_lab-0.1.0a1/examples/splits.py +101 -0
- reliability_lab-0.1.0a1/pyproject.toml +107 -0
- reliability_lab-0.1.0a1/src/reliability_lab/__init__.py +62 -0
- reliability_lab-0.1.0a1/src/reliability_lab/_version.py +1 -0
- reliability_lab-0.1.0a1/src/reliability_lab/alpha.py +290 -0
- reliability_lab-0.1.0a1/src/reliability_lab/data.py +280 -0
- reliability_lab-0.1.0a1/src/reliability_lab/design.py +253 -0
- reliability_lab-0.1.0a1/src/reliability_lab/exceptions.py +42 -0
- reliability_lab-0.1.0a1/src/reliability_lab/inference.py +724 -0
- reliability_lab-0.1.0a1/src/reliability_lab/py.typed +0 -0
- reliability_lab-0.1.0a1/src/reliability_lab/report.py +717 -0
- reliability_lab-0.1.0a1/src/reliability_lab/split.py +338 -0
- reliability_lab-0.1.0a1/src/reliability_lab/study.py +824 -0
- reliability_lab-0.1.0a1/tests/fixtures/__init__.py +0 -0
- reliability_lab-0.1.0a1/tests/fixtures/krippendorff2011.py +52 -0
- reliability_lab-0.1.0a1/tests/helpers.py +57 -0
- reliability_lab-0.1.0a1/tests/naive_bootstrap.py +90 -0
- reliability_lab-0.1.0a1/tests/reference.py +73 -0
- reliability_lab-0.1.0a1/tests/test_alpha.py +146 -0
- reliability_lab-0.1.0a1/tests/test_bca.py +83 -0
- reliability_lab-0.1.0a1/tests/test_compare.py +136 -0
- reliability_lab-0.1.0a1/tests/test_data.py +106 -0
- reliability_lab-0.1.0a1/tests/test_design.py +195 -0
- reliability_lab-0.1.0a1/tests/test_inference_units.py +227 -0
- reliability_lab-0.1.0a1/tests/test_kalpha.py +133 -0
- reliability_lab-0.1.0a1/tests/test_parity.py +95 -0
- reliability_lab-0.1.0a1/tests/test_properties.py +130 -0
- reliability_lab-0.1.0a1/tests/test_report.py +75 -0
- reliability_lab-0.1.0a1/tests/test_report_io.py +165 -0
- reliability_lab-0.1.0a1/tests/test_smoke.py +6 -0
- reliability_lab-0.1.0a1/tests/test_split.py +361 -0
- reliability_lab-0.1.0a1/tests/test_status_policies.py +200 -0
- reliability_lab-0.1.0a1/uv.lock +1473 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["**"]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: ${{ matrix.os }} / py${{ matrix.python-version }}
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
16
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v5
|
|
19
|
+
- uses: astral-sh/setup-uv@v6
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
- name: Install
|
|
23
|
+
run: uv sync --all-groups --locked
|
|
24
|
+
- name: Lint
|
|
25
|
+
run: uv run ruff check
|
|
26
|
+
- name: Format
|
|
27
|
+
run: uv run ruff format --check
|
|
28
|
+
- name: Type-check
|
|
29
|
+
run: uv run mypy
|
|
30
|
+
- name: Test
|
|
31
|
+
run: uv run pytest
|
|
32
|
+
|
|
33
|
+
# irrCAC pins scipy==1.12.0, below this package's floor (1.15, for the bootstrap's
|
|
34
|
+
# rng keyword). Its parity test only needs the point estimates, so it runs in a
|
|
35
|
+
# throwaway environment with the package installed without its dependencies.
|
|
36
|
+
parity-irrcac:
|
|
37
|
+
name: irrCAC parity (isolated)
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v5
|
|
41
|
+
- uses: astral-sh/setup-uv@v6
|
|
42
|
+
- name: Build the isolated environment
|
|
43
|
+
run: |
|
|
44
|
+
uv venv .venv-irrcac --python 3.12
|
|
45
|
+
uv pip install --python .venv-irrcac irrcac pytest hypothesis
|
|
46
|
+
uv pip install --python .venv-irrcac --no-deps .
|
|
47
|
+
- name: Run irrCAC parity
|
|
48
|
+
run: >-
|
|
49
|
+
.venv-irrcac/bin/python -m pytest tests/test_parity.py -m parity -k irrcac
|
|
50
|
+
-p no:cacheprovider
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishes on a version tag (v0.1.0a1, v0.1.0, ...) with PyPI trusted publishing:
|
|
4
|
+
# no API tokens are stored. See docs/releasing.md for the one-time setup.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags: ["v*"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
name: Build and check the distributions
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v5
|
|
15
|
+
- uses: astral-sh/setup-uv@v6
|
|
16
|
+
- name: Check the tag matches the package version
|
|
17
|
+
run: |
|
|
18
|
+
version=$(sed -n 's/^__version__ = "\(.*\)"$/\1/p' src/reliability_lab/_version.py)
|
|
19
|
+
if [ "v$version" != "$GITHUB_REF_NAME" ]; then
|
|
20
|
+
echo "tag $GITHUB_REF_NAME does not match package version $version" >&2
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
- name: Build
|
|
24
|
+
run: uv build
|
|
25
|
+
- name: Check metadata
|
|
26
|
+
run: uvx twine check --strict dist/*
|
|
27
|
+
- uses: actions/upload-artifact@v4
|
|
28
|
+
with:
|
|
29
|
+
name: dist
|
|
30
|
+
path: dist/
|
|
31
|
+
|
|
32
|
+
publish-testpypi:
|
|
33
|
+
name: Publish to TestPyPI
|
|
34
|
+
needs: build
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
environment:
|
|
37
|
+
name: testpypi
|
|
38
|
+
url: https://test.pypi.org/p/reliability-lab
|
|
39
|
+
permissions:
|
|
40
|
+
id-token: write
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/download-artifact@v4
|
|
43
|
+
with:
|
|
44
|
+
name: dist
|
|
45
|
+
path: dist/
|
|
46
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
47
|
+
with:
|
|
48
|
+
repository-url: https://test.pypi.org/legacy/
|
|
49
|
+
|
|
50
|
+
publish-pypi:
|
|
51
|
+
name: Publish to PyPI
|
|
52
|
+
needs: publish-testpypi
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
# Give this environment a required reviewer to approve each release by hand.
|
|
55
|
+
environment:
|
|
56
|
+
name: pypi
|
|
57
|
+
url: https://pypi.org/p/reliability-lab
|
|
58
|
+
permissions:
|
|
59
|
+
id-token: write
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/download-artifact@v4
|
|
62
|
+
with:
|
|
63
|
+
name: dist
|
|
64
|
+
path: dist/
|
|
65
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Byte-compiled / cache
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.mypy_cache/
|
|
5
|
+
.pytest_cache/
|
|
6
|
+
.ruff_cache/
|
|
7
|
+
.hypothesis/
|
|
8
|
+
|
|
9
|
+
# Environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
|
|
13
|
+
# Build artefacts
|
|
14
|
+
build/
|
|
15
|
+
dist/
|
|
16
|
+
*.egg-info/
|
|
17
|
+
|
|
18
|
+
# Coverage
|
|
19
|
+
.coverage
|
|
20
|
+
.coverage.*
|
|
21
|
+
htmlcov/
|
|
22
|
+
coverage.xml
|
|
23
|
+
|
|
24
|
+
# Editors / OS
|
|
25
|
+
.vscode/
|
|
26
|
+
.idea/
|
|
27
|
+
.DS_Store
|
|
28
|
+
Thumbs.db
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.1.0a1
|
|
6
|
+
|
|
7
|
+
First public release.
|
|
8
|
+
|
|
9
|
+
- Project scaffold: packaging, BSD-3-Clause licence, CI on Linux, Windows and macOS.
|
|
10
|
+
- Core engine: `RatingFrame` long-form schema with rating status, `CoderSpec` and design labels, Krippendorff's alpha (nominal, ordinal, interval, ratio, custom distances) with white-box diagnostics, and missing-data policies (`as_missing`, `as_category`, `sensitivity`).
|
|
11
|
+
- Bootstrap inference: `AlphaResult.bootstrap(scheme=...)` with a required scheme.
|
|
12
|
+
- `units` resamples units, or groups, optionally within strata, and recomputes D_e per replicate.
|
|
13
|
+
- `kalpha` follows Krippendorff's algorithm, resampling pairs of values with D_e fixed.
|
|
14
|
+
- Percentile intervals are the default; BCa is opt-in and flagged experimental.
|
|
15
|
+
- Failed replicates are counted and reported, the seed is always recorded, and an optional `alpha_min` reports how many replicates fall below it.
|
|
16
|
+
- Paired comparisons: `ReliabilityStudy.compare(a=..., b=...)` places the interval on Δα = α_b − α_a, using the same resampled units for both configurations.
|
|
17
|
+
- Group-safe splits:
|
|
18
|
+
- `ReliabilityStudy.holdout()` and `cv()` take any scikit-learn style splitter. The splitter sees one sample per group, so groups and repeated LLM calls never cross partitions.
|
|
19
|
+
- When a group's units span several strata, `group_stratum` must be given.
|
|
20
|
+
- `SplitManifest` saves partition ids, the splitter, realised sizes and fingerprints; `apply_manifest()` reapplies them to a later log.
|
|
21
|
+
- `assert_no_leakage()` checks that few-shot examples come from the dev partition.
|
|
22
|
+
- Reports:
|
|
23
|
+
- versioned JSON (`to_json`) with provenance for every result type: software versions, ratings fingerprint, declarations, split and known gaps;
|
|
24
|
+
- tidy tables (`tables`) and Parquet output (`to_parquet`, with the `parquet` extra).
|
|
25
|
+
- Tag-triggered release workflow using PyPI trusted publishing.
|
|
26
|
+
- New exceptions and warnings: `BootstrapWarning`, `LeakageError`. The scipy floor is now 1.15.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Sathapana Chawananon
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: reliability-lab
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: Study-design library for the reliability of LLM and human judgments, built on Krippendorff's alpha.
|
|
5
|
+
Project-URL: Repository, https://github.com/Sathapana/reliability-lab
|
|
6
|
+
Project-URL: Issues, https://github.com/Sathapana/reliability-lab/issues
|
|
7
|
+
Author: Sathapana Chawananon
|
|
8
|
+
License-Expression: BSD-3-Clause
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: alpha,annotation,evaluation,inter-rater reliability,krippendorff,llm-as-a-judge
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: numpy>=1.26
|
|
23
|
+
Requires-Dist: pandas>=2.1
|
|
24
|
+
Requires-Dist: scipy>=1.15
|
|
25
|
+
Provides-Extra: parquet
|
|
26
|
+
Requires-Dist: pyarrow>=15; extra == 'parquet'
|
|
27
|
+
Provides-Extra: sklearn
|
|
28
|
+
Requires-Dist: scikit-learn>=1.4; extra == 'sklearn'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# reliability-lab
|
|
32
|
+
|
|
33
|
+
**Status: alpha (0.1.0a1).** The API may still change between releases.
|
|
34
|
+
|
|
35
|
+
reliability-lab is a study-design library for measuring the reliability of LLM and human judgments. It is built on Krippendorff's alpha. Alpha implementations already exist in Python. This package adds the workflow around the coefficient:
|
|
36
|
+
|
|
37
|
+
- a validated long-form ratings schema in which every rating has an explicit status (`observed`, `abstained`, `inapplicable`, `parse_error`, `provider_error`);
|
|
38
|
+
- declared measurement semantics: level, value domain, coder definition and design label;
|
|
39
|
+
- white-box output: observed and expected disagreement, coincidence matrices and per-unit contributions;
|
|
40
|
+
- explicit missing-data policies, because LLM failures are rarely unrelated to the item;
|
|
41
|
+
- bootstrap intervals by a scheme you name: whole units or groups resampled (`scheme="units"`), or Krippendorff's KALPHA algorithm (`scheme="kalpha"`);
|
|
42
|
+
- paired comparisons of two configurations on the same units, with the interval on the difference;
|
|
43
|
+
- group-safe train/test splits that work with any scikit-learn splitter, with a saved manifest that reproduces the split;
|
|
44
|
+
- versioned JSON and Parquet reports with provenance.
|
|
45
|
+
|
|
46
|
+
LLM-specific workflows (a trace sidecar, repeatability and order-swap checks) are next on the roadmap ([design document](docs/design/framing-v2.md)).
|
|
47
|
+
|
|
48
|
+
> **Reliability is not validity.** Coders can agree consistently and still be wrong in the same way, for example because of a flawed rubric or a bias they share. When results come from different designs (repeatability, inter-model agreement, human panels), they are reported as different quantities and are never pooled.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install --pre "reliability-lab[sklearn,parquet]"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The extras are optional. `sklearn` provides the splitters, although any object with a scikit-learn style `split` method works; `parquet` enables `to_parquet`. For development, run `uv sync` and then `uv run pytest`.
|
|
57
|
+
|
|
58
|
+
## Example
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
import pandas as pd
|
|
62
|
+
from reliability_lab import CoderSpec, RatingFrame, ReliabilityStudy
|
|
63
|
+
|
|
64
|
+
df = pd.DataFrame(
|
|
65
|
+
{
|
|
66
|
+
"item": ["u1", "u1", "u2", "u2", "u3", "u3"],
|
|
67
|
+
"annotator": ["ann", "bob", "ann", "bob", "ann", "bob"],
|
|
68
|
+
"label": ["pos", "pos", "neg", "pos", "neg", "neg"],
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
ratings = RatingFrame.from_long(df, unit="item", rater="annotator", value="label")
|
|
73
|
+
study = ReliabilityStudy(
|
|
74
|
+
ratings,
|
|
75
|
+
measurement="nominal",
|
|
76
|
+
values=["neg", "pos"],
|
|
77
|
+
coders=CoderSpec(by="rater_id"),
|
|
78
|
+
design="human_panel",
|
|
79
|
+
)
|
|
80
|
+
result = study.alpha()
|
|
81
|
+
print(result.summary())
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Intervals and comparisons
|
|
85
|
+
|
|
86
|
+
Every interval names its scheme, because the two schemes answer different questions. `scheme` is a required argument.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
boot = result.bootstrap(scheme="units", n_resamples=2000, random_state=7)
|
|
90
|
+
print(boot.summary()) # 95% percentile intervals for every missing-data policy
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
- `scheme="units"` resamples whole units, and whole groups when a `group` column is declared. It can also resample within declared strata (`strata=True`), and it recomputes the expected disagreement in every replicate. `method="bca"` is available as an experimental option.
|
|
94
|
+
- `scheme="kalpha"` reproduces Krippendorff's KALPHA algorithm. It resamples pairs of values and holds the expected disagreement fixed.
|
|
95
|
+
|
|
96
|
+
To compare two configurations of a judge, for example two prompts, select each one's rows. Both must cover the same units. Every replicate resamples the same units for both, and the interval is on the difference:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
diff = study.compare(
|
|
100
|
+
a={"condition_id": "prompt_v1"},
|
|
101
|
+
b={"condition_id": "prompt_v2"},
|
|
102
|
+
n_resamples=2000,
|
|
103
|
+
random_state=7,
|
|
104
|
+
)
|
|
105
|
+
print(diff.summary()) # alpha_b - alpha_a with a paired interval
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Splits and reports
|
|
109
|
+
|
|
110
|
+
Teams tune the judge (prompt, few-shot examples, rubric) and should report reliability on units the tuning never saw. So split before tuning, freeze the judge, then estimate on the test partition.
|
|
111
|
+
|
|
112
|
+
When groups are declared, the splitter sees one sample per group (a conversation, say), so groups and repeated LLM calls never cross partitions:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from sklearn.model_selection import StratifiedGroupKFold
|
|
116
|
+
|
|
117
|
+
dev, test = study.holdout(
|
|
118
|
+
StratifiedGroupKFold(n_splits=4, shuffle=True, random_state=7),
|
|
119
|
+
group_stratum="majority", # needed only when a group's units span several strata
|
|
120
|
+
)
|
|
121
|
+
test.save_manifest("split.json") # the ids of every partition, not just the seed
|
|
122
|
+
|
|
123
|
+
# ... tune and freeze the judge on dev ...
|
|
124
|
+
|
|
125
|
+
result = test.alpha()
|
|
126
|
+
result.to_json("alpha_test.json") # strict JSON with provenance
|
|
127
|
+
result.to_parquet("alpha_test/") # tidy tables
|
|
128
|
+
|
|
129
|
+
dev2, test2 = later_study.apply_manifest("split.json") # same units, new judge runs
|
|
130
|
+
test.manifest.assert_no_leakage(few_shot_units) # raises LeakageError
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
See [`examples/splits.py`](examples/splits.py) for a full run. Releases are described in [docs/releasing.md](docs/releasing.md).
|
|
134
|
+
|
|
135
|
+
## Licence
|
|
136
|
+
|
|
137
|
+
BSD-3-Clause. Alpha is implemented independently from Krippendorff's papers. The GPL-licensed `krippendorff` package is used only in the test suite, as a parity check.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# reliability-lab
|
|
2
|
+
|
|
3
|
+
**Status: alpha (0.1.0a1).** The API may still change between releases.
|
|
4
|
+
|
|
5
|
+
reliability-lab is a study-design library for measuring the reliability of LLM and human judgments. It is built on Krippendorff's alpha. Alpha implementations already exist in Python. This package adds the workflow around the coefficient:
|
|
6
|
+
|
|
7
|
+
- a validated long-form ratings schema in which every rating has an explicit status (`observed`, `abstained`, `inapplicable`, `parse_error`, `provider_error`);
|
|
8
|
+
- declared measurement semantics: level, value domain, coder definition and design label;
|
|
9
|
+
- white-box output: observed and expected disagreement, coincidence matrices and per-unit contributions;
|
|
10
|
+
- explicit missing-data policies, because LLM failures are rarely unrelated to the item;
|
|
11
|
+
- bootstrap intervals by a scheme you name: whole units or groups resampled (`scheme="units"`), or Krippendorff's KALPHA algorithm (`scheme="kalpha"`);
|
|
12
|
+
- paired comparisons of two configurations on the same units, with the interval on the difference;
|
|
13
|
+
- group-safe train/test splits that work with any scikit-learn splitter, with a saved manifest that reproduces the split;
|
|
14
|
+
- versioned JSON and Parquet reports with provenance.
|
|
15
|
+
|
|
16
|
+
LLM-specific workflows (a trace sidecar, repeatability and order-swap checks) are next on the roadmap ([design document](docs/design/framing-v2.md)).
|
|
17
|
+
|
|
18
|
+
> **Reliability is not validity.** Coders can agree consistently and still be wrong in the same way, for example because of a flawed rubric or a bias they share. When results come from different designs (repeatability, inter-model agreement, human panels), they are reported as different quantities and are never pooled.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install --pre "reliability-lab[sklearn,parquet]"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The extras are optional. `sklearn` provides the splitters, although any object with a scikit-learn style `split` method works; `parquet` enables `to_parquet`. For development, run `uv sync` and then `uv run pytest`.
|
|
27
|
+
|
|
28
|
+
## Example
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import pandas as pd
|
|
32
|
+
from reliability_lab import CoderSpec, RatingFrame, ReliabilityStudy
|
|
33
|
+
|
|
34
|
+
df = pd.DataFrame(
|
|
35
|
+
{
|
|
36
|
+
"item": ["u1", "u1", "u2", "u2", "u3", "u3"],
|
|
37
|
+
"annotator": ["ann", "bob", "ann", "bob", "ann", "bob"],
|
|
38
|
+
"label": ["pos", "pos", "neg", "pos", "neg", "neg"],
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
ratings = RatingFrame.from_long(df, unit="item", rater="annotator", value="label")
|
|
43
|
+
study = ReliabilityStudy(
|
|
44
|
+
ratings,
|
|
45
|
+
measurement="nominal",
|
|
46
|
+
values=["neg", "pos"],
|
|
47
|
+
coders=CoderSpec(by="rater_id"),
|
|
48
|
+
design="human_panel",
|
|
49
|
+
)
|
|
50
|
+
result = study.alpha()
|
|
51
|
+
print(result.summary())
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Intervals and comparisons
|
|
55
|
+
|
|
56
|
+
Every interval names its scheme, because the two schemes answer different questions. `scheme` is a required argument.
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
boot = result.bootstrap(scheme="units", n_resamples=2000, random_state=7)
|
|
60
|
+
print(boot.summary()) # 95% percentile intervals for every missing-data policy
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- `scheme="units"` resamples whole units, and whole groups when a `group` column is declared. It can also resample within declared strata (`strata=True`), and it recomputes the expected disagreement in every replicate. `method="bca"` is available as an experimental option.
|
|
64
|
+
- `scheme="kalpha"` reproduces Krippendorff's KALPHA algorithm. It resamples pairs of values and holds the expected disagreement fixed.
|
|
65
|
+
|
|
66
|
+
To compare two configurations of a judge, for example two prompts, select each one's rows. Both must cover the same units. Every replicate resamples the same units for both, and the interval is on the difference:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
diff = study.compare(
|
|
70
|
+
a={"condition_id": "prompt_v1"},
|
|
71
|
+
b={"condition_id": "prompt_v2"},
|
|
72
|
+
n_resamples=2000,
|
|
73
|
+
random_state=7,
|
|
74
|
+
)
|
|
75
|
+
print(diff.summary()) # alpha_b - alpha_a with a paired interval
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Splits and reports
|
|
79
|
+
|
|
80
|
+
Teams tune the judge (prompt, few-shot examples, rubric) and should report reliability on units the tuning never saw. So split before tuning, freeze the judge, then estimate on the test partition.
|
|
81
|
+
|
|
82
|
+
When groups are declared, the splitter sees one sample per group (a conversation, say), so groups and repeated LLM calls never cross partitions:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from sklearn.model_selection import StratifiedGroupKFold
|
|
86
|
+
|
|
87
|
+
dev, test = study.holdout(
|
|
88
|
+
StratifiedGroupKFold(n_splits=4, shuffle=True, random_state=7),
|
|
89
|
+
group_stratum="majority", # needed only when a group's units span several strata
|
|
90
|
+
)
|
|
91
|
+
test.save_manifest("split.json") # the ids of every partition, not just the seed
|
|
92
|
+
|
|
93
|
+
# ... tune and freeze the judge on dev ...
|
|
94
|
+
|
|
95
|
+
result = test.alpha()
|
|
96
|
+
result.to_json("alpha_test.json") # strict JSON with provenance
|
|
97
|
+
result.to_parquet("alpha_test/") # tidy tables
|
|
98
|
+
|
|
99
|
+
dev2, test2 = later_study.apply_manifest("split.json") # same units, new judge runs
|
|
100
|
+
test.manifest.assert_no_leakage(few_shot_units) # raises LeakageError
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
See [`examples/splits.py`](examples/splits.py) for a full run. Releases are described in [docs/releasing.md](docs/releasing.md).
|
|
104
|
+
|
|
105
|
+
## Licence
|
|
106
|
+
|
|
107
|
+
BSD-3-Clause. Alpha is implemented independently from Krippendorff's papers. The GPL-licensed `krippendorff` package is used only in the test suite, as a parity check.
|