EvalSeal
Reproducibility receipts for LLM evals: run it N times, report the score with its noise, and seal what actually ran.
The problem
An eval score from a single run is one sample of a random process. Run the same eval again and borderline items quietly flip from PASS to FAIL, especially when an LLM judge grades them. Reports also name the model you asked for, not the one that answered. EvalSeal measures the flips, records the real provenance, and seals both into a tamper-evident ledger.
Quickstart
pip install evalseal
# The recorded demo (examples + cassette) lives in the repo.
git clone https://github.com/patibandlavenkatamanideep/evalseal && cd evalseal
# Replays the committed cassette: no API key, no network.
evalseal run \
--dataset examples/borderline_judge/dataset.jsonl \
--target-config examples/borderline_judge/target.json \
--scorer-config examples/borderline_judge/scorer.json \
--n 5
Real flip rates
Recorded 2026-09-15 and committed in tests/cassettes/run.json: gemini-2.5-flash as both
the target and the judge, temperature left at the provider default, 20 arguable prompts,
5 runs each. The quickstart above replays exactly this run.
Mean score: 0.92, but 5 of 20 cases did not get the same verdict every time.
| case | prompt | verdicts | mean | 95% CI | flip rate | stability |
|---|---|---|---|---|---|---|
| b01 | Is a hot dog a sandwich? | FPPFP |
0.60 | [0.20, 1.00] | 40% | UNSTABLE |
| b10 | Blockchain for a child in exactly 20 words | FPFPP |
0.60 | [0.20, 1.00] | 40% | UNSTABLE |
| b16 | "Do we only use 10% of our brains?" in a jokey tone | PPFFP |
0.60 | [0.20, 1.00] | 40% | UNSTABLE |
| b05 | A borderline-polite refusal to a coworker | PFPPP |
0.80 | [0.40, 1.00] | 20% | BORDERLINE |
| b19 | A technically accurate haiku about recursion | PPFPP |
0.80 | [0.40, 1.00] | 20% | BORDERLINE |
| 15 others | PPPPP |
1.00 | [1.00, 1.00] | 0% | STABLE |
Treat the k-th repeat of every case as one ordinary single-run eval, and the five "single runs" of this identical eval scored 0.90, 0.95, 0.85, 0.90 and 1.00. A single run can't tell you which of those numbers you got.
The report also flagged TEMPERATURE NOT SET for both the target and the judge, which is
the reason these borderline verdicts can come out differently from run to run.
To re-record with your own key, copy .env.example to .env, add a free
Google AI Studio key, and run the quickstart with
EVALSEAL_RECORD=1. If the free tier rate-limits you, run the same command again later;
responses already recorded are kept.
Commands
| command | what it does | exit code |
|---|---|---|
evalseal run |
Runs each case N times, analyzes variance, seals a record, writes report.json + report.md. |
0 all stable/borderline · 3 any case UNSTABLE · 1 error |
evalseal verify |
Recomputes every hash in .evalseal/ledger.jsonl and checks the chain links. |
0 intact · 1 tampered or broken |
evalseal diff A B |
Compares two ledger runs and says whether the mean moved beyond the noise floor. Use -- for negative indices: evalseal diff -- 0 -1. |
0 |
run takes --concurrency (default 4 requests in flight), --max-retries (default 5, on
HTTP 429/408/5xx and connection errors, honouring Retry-After), and --quiet to drop the
progress bar. Concurrency never changes the result: each response is recorded against its
own (case, repeat) slot, so a 16-worker replay is identical to a serial one.
Stability classes are based on the flip rate, the share of a case's N verdicts that
disagree with its majority: STABLE (0), BORDERLINE (≤ 20%), UNSTABLE (> 20%).
Provenance warnings show up in the report when:
- the served model differs from the requested one (for the target or the judge),
- the endpoint isn't a canonical provider host,
- temperature was left at the provider default,
- the served model or system fingerprint changed partway through the run.
How it works
The executor sends each prompt to the target N times and scores every response. An LLM
judge is itself a target, so its own randomness is measured instead of assumed away.
analyze.py computes the mean, a seeded bootstrap 95% CI, and the flip rate for each case.
Every request goes through a cassette, keyed by the request plus which repeat it belongs
to. In record mode real responses are saved as they arrive; in replay mode, the default and
what CI uses, they are served back by that key, and a missing entry fails loudly. Each run is saved as a RunRecord: its manifest (requested vs.
served model, fingerprint, parameters and whether they were set explicitly, rubric hash,
dataset hash) plus its results. The record is hashed and linked to the previous record's
hash in an append-only JSONL ledger, so editing any past score breaks verify.
Contributing: see CONTRIBUTING.md. See DESIGN.md for what this does and does not prove.
Upgrading to 0.2: cassettes and sealed records written by 0.1.x cannot be read by 0.2,
which keys entries by repeat. The demo cassette in this repo was converted in place, so the
published numbers above are unchanged; your own cassettes need re-recording, and an
existing ledger needs to start fresh. verify names an old record rather than calling it
tampered.
Metadata
Release files for evalseal 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| evalseal-0.2.0.tar.gz | 61.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| evalseal-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 82.6 kB
Release files / evalseal-0.2.0.tar.gz
| Download URL | evalseal-0.2.0.tar.gz |
|---|---|
| Size | 61.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
97aee37efe0969d41fa8259b19adb38392e261093091840e8e13215581329360
|
|
BLAKE2b-256 checksum How to use checksums |
6ba15b50893c297d78d35ce73be4bddd11c39d33953d3bcbe1fad9a303cd7ab2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / evalseal-0.2.0-py3-none-any.whl
| Download URL | evalseal-0.2.0-py3-none-any.whl |
|---|---|
| Size | 21.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6259b9a44189dcf7449524caf2c4fa4f35ba8cba4eb78c2bc3e5ade01c1f776d
|
|
BLAKE2b-256 checksum How to use checksums |
a74986f948a4596167e2457ef98eff626872558fdb44d5612d73dfeb43457c2f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency log