pairsort
Rank anything with AI judges: reliably, cheaply, and with honest confidence.
Ask a model to score 100 things from 1 to 10 and you get noise on a drifting scale. Ask it "which of these two is better?" and you get its best judgment. pairsort asks many of those small questions, cancels the model's biases, and couples the answers into one ranking with probabilities, using the pairwise-coupling rule of Price, Knerr, Personnaz & Dreyfus (NeurIPS 1994).
- More reliable than any single judgment. Coupling many pairwise answers fixes the contradictions a judge makes (A > B > C > A) and averages out its noise and position bias. Measured against explicit ground truth in eight evals.
- Cheap: not all-vs-all. An active schedule asks only the informative pairs and stops when the ranking settles; 100 items were ranked from 8% of the possible pairs. With Jev as the judge, 4,800 comparisons cost $0.11.
- Probabilities, not vibes. Every item gets a posterior, every pair a calibrated P(A beats B), and pairsort abstains when the top two are too close to call.
- Any judge. TypeSafe's Jev via OpenRouter (default), any OpenRouter LLM via token logprobs, open Jev models, or your own: judges.
- Auditable. Every judgment, in both orders, lands in a JSON audit log.
Try it in 60 seconds
pip install pairsort # PyPI name; you `import pairsort` and run `pairsort` (or `pairsort`)
uvx pairsort demo # or run the CLI without installing anything
export OPENROUTER_API_KEY=sk-or-... # or skip it and bring your own judge function
import pairsort
ideas = ["A CLI that turns any CSV into a chart", "A to-do app on a blockchain", "A tool that ranks PRs by urgency"]
result = pairsort.sort(ideas, "Which side project would developers find most useful?")
result.best # 'A tool that ranks PRs by urgency' (your own objects back, best first)
result.top(2) # the two best
result.scores # {id: probability of being the best}
pairsort.sort(ideas, useful="Which is more useful?", easy="Which is easier to build?") # blend questions
pairsort.compare("draft A", "draft B", "Which is clearer?") # -> P(A is better)
pairsort.sort(ideas, "Which is shorter?", judge=lambda q, a, b: len(a) < len(b)) # any function is a judge
pairsort sort ideas.txt "Which idea has more impact?" # from a file (`uvx pairsort sort ...` works too)
cat ideas.txt | pairsort sort - "Which is funnier?" --top 3 # from stdin
pairsort compare "draft A" "draft B" "Which is clearer?"
Nothing is hidden behind the easy path: budgets, active/referee schedules, meta-judges, calibration profiles and
any backend are all keywords on pairsort.sort or steps on the builder (pairsort.sorter().by(...).budget(60).meta()).
Quickstart (runs in ~5 s: python examples/quickstart.py) ·
Usage & CLI
Summary Showdown
100 of OpenRouter's most-used models each summarized the 1994 paper this library implements. pairsort ranked them on six questions from 400 of 4,950 possible pairs (8%) with a jury of 4 AI judges. Current top 3: GPT-6 Astra, Claude Opus 5, Nemotron 3 Ultra.
Ground truth: none — nobody can say which summary is truly best. The ranking is the AI jury's opinion; we check it against a separate LLM grader (Claude Sonnet 5 + a key-fact rubric), which the verifiable eval shows is itself accurate on exact counts. Humans can vote on the site.
Judge the summaries yourself → · full results + method · leaderboard
Does it work?
Eight evals, each with its ground truth stated up front:
| eval | what gets ranked | ground truth | result | details |
|---|---|---|---|---|
| Verifiable | 72 summaries of 6 fictional documents | exact counts of injected false statements and of facts mentioned, fixed by construction and recountable by a script | Jev: 96% of pairs right on accuracy, 94% on completeness (τ 0.82 / 0.89) for $0.02; weakest judge τ 0.42 | verifiable |
| Market | 80 + 101 stocks over 2 sessions, judged only on their pre-open SEC filings | realized next-day returns (Mon 09-21 and Tue 09-22), which didn't exist before those days | Monday: Jev τ +0.14 (p = 0.035). Tuesday: no judge beat guessing (best p = 0.12). Weak evidence so far | market |
| Degradation ladder | 60 copies of 6 real summaries, damaged one logged step at a time | rung number (each rung = previous + one planted error / deleted / swapped sentence) | Jev: 94% of pairs right, ECE 0.033 | ladder |
| Code runtime | 25 correct implementations of a new function | measured wall-clock time in a sandbox | Jev picks the faster one 97% of the time | runtime |
| Cross-lingual | the same summaries in EN/ES/DE/JA | exact counts, identical in every language by construction | Jev gives the same answer in all 4 languages 96% of the time | cross-lingual |
| Weather | 35 cities, ranked the day before | airport observations of the next day's high and rain; predictions committed first | first round resolves 2026-09-25 | weather |
| Paper sorting | 16 fictional abstracts on 3 questions | 1–5 levels assigned by construction by the dataset author (not expert ratings) | Jev: fused AUC 0.994, Kendall τ 0.86 | evaluation |
| Synthetic | simulated items, flawed simulated judge | known latent order; calibration labels sampled from it | coupling + both orders + temperature: ECE 0.158 → 0.013 | evaluation |
Ground truth in this figure: exact counts built into fictional documents; recount them with
python examples/verifiable_eval.py verify.
Learn more
- How it works: pairwise questions, PKPD Eq. 7, Bradley–Terry, bias guards, blending, Jev as meta-judge and referee
- Judges & backends: Jev via OpenRouter, the LLM fallback, open Jev models, the
/v1/systemoneshim - Usage: CLI, input formats, Python API, calibration, a worked example
- Evals: verifiable · market · ladder · runtime · cross-lingual · weather · synthetic + papers · Summary Showdown · Humans vs judges
MIT licensed. Contributions welcome.
Release files for pairsort 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 | |
|---|---|---|---|
| pairsort-0.2.0.tar.gz | 75.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pairsort-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 155.6 kB
Release files / pairsort-0.2.0.tar.gz
| Download URL | pairsort-0.2.0.tar.gz |
|---|---|
| Size | 75.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e75b42a28deda702e91211a6c4affefef0c280894e9529968fd1249f7f2a2afc
|
|
BLAKE2b-256 checksum How to use checksums |
3a300dd82e62cd31c3d053863bcfae060264bbf386d9787de8b4cfa731725cd6
|
| 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 23, 2026.
Transparency logRelease files / pairsort-0.2.0-py3-none-any.whl
| Download URL | pairsort-0.2.0-py3-none-any.whl |
|---|---|
| Size | 79.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b7d101bf79db44772bf065be34b34c857de40aa9eef326afb2f9f22157a8f9ee
|
|
BLAKE2b-256 checksum How to use checksums |
d7d92d7deef8cb2889f436a6e0213c194b7465fb85b7d8f94979e9cb496114f3
|
| 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 23, 2026.
Transparency log