pairsort

Judges & backends

One interface, many judges

One interface — every backend answers system_one(state, {key: Choice}), and the brief’s functional form judge(state, question, candidates) → probs is a thin wrapper. All answers are cached on disk by content hash, so re-runs and re-analyses are free.

--backend judge notes
openrouter (default) Jev 1.13 via OpenRouter — model typesafe/jev-1.13 Decisions API POST /api/alpha/decisions (or PAIRSORT_JEV_SURFACE=systemone for POST /api/v1/systemone); OPENROUTER_API_KEY; $0.042 / M input tokens, output free. Falls back to llm with a loud warning if Jev is unreachable (--no-fallback to fail instead).
llm[:MODEL] generic LLM-as-judge fallback on OpenRouter, default deepseek/deepseek-v4.1-flash one output token + logprobs → P(letter); routes only to providers that honour logprobs; verbalized-probability fallback for models without them. Not Jev: no typed, RLCD-calibrated decisions — calibrate it.
typesafe[:MODEL] TypeSafe’s hosted Jev directly TYPESAFE_API_KEY, TYPESAFE_BASE_URL
jev-wire:URL[#MODEL] any /v1/systemone server ekzhang/openjev-sglang, decider.serve, Decision-1.0 endpoints, pairsort serve
laya, decider, nanojev, verdict, hf:REPO open Jev reproductions in process see below

Open Jev-style models (compare them on the Jev Decision Index leaderboard):

model size how pairsort runs it
convaiinnovations/laya 421M ModernBERT-large, RLCD, 100+ langs --backend laya (pip install laya, its Router)
llm-semantic-router/Decision-1.0-Kai-0.6B (also Lex, Eos, Sol-2B, Nox-4B, Lux-9B) 0.6B–9B, Apache-2.0 --backend jev-wire:URL#Decision-1.0-Kai-0.6B (SystemOne-compatible endpoint)
Mapika/decider-2b Qwen3.5-2B --backend decider (decider.infer.Decider(...).system_one) or decider.serve + jev-wire
DoccyHealth/Solomon LoRA + typed heads on Qwen3.8-27B its solomon.api.serve behind a /v1/systemone adapter
pngwn/system-one-qwen3.5-4b-scorer-v2b 4B LoRA --backend hf:… + pairsort calibrate for temperature scaling
C-Tianyu/NanoJev 0.6B Qwen3 + attention Choice head --backend nanojev (its DecisionPredictor)
heman10x/rlcd-modernbert-151m (“Verdict”) 151M, <35 ms, WebGPU/ONNX --backend verdict (rlcd.DecisionEngine)
AlexWortega/openjev 0.8B–35B NLI cross-encoders its SGLang server, or ekzhang/openjev-sglang + jev-wire

The in-process adapters wrap each project’s own published Python API (lazy imports, so pairsort itself only needs numpy and httpx); they are thin and have not all been exercised against every checkpoint — issues and PRs welcome. pairsort backends shows what’s ready on your machine.

Serve shim. pairsort serve --backend llm exposes any backend as a TypeSafe-compatible POST /v1/systemone endpoint (Choice, Noul and Score), so the official TypeSafe SDKs work unchanged against an open model or an LLM.

Jev via OpenRouter

OpenRouter serves Jev as typesafe/jev-1.13 (alias ~typesafe/jev-latest) with the same request/response shape as TypeSafe’s own API (OpenRouter’s Jev guide). A pairwise judgment maps onto it like this:

POST https://openrouter.ai/api/alpha/decisions
{ "model": "typesafe/jev-1.13",
  "state": {"objective": "...", "items": {"P01": "...", "P02": "...", "...": "..."}},
  "questions": {
    "evidence|0|1": {"type": "choice",
      "instructions": "Given the stated research objective, which paper provides stronger supporting experimental evidence? ...",
      "criteria": {"A": "the item at `items.P01`", "B": "the item at `items.P02`"}},
    "evidence|1|0": {"type": "choice", "instructions": "...", "criteria": {"A": "the item at `items.P02`", "B": "the item at `items.P01`"}}
  } }
→ {"answers": {"evidence|0|1": {"type": "choice", "choice": "A", "confidence": 0.8, "probabilities": {"A": 0.93, "B": 0.07}}, ...},
   "usage": {"input_tokens": ..., "cost": ...}}

Jev ingests the state once and answers every question against it, so pairsort puts all items in one shared state and batches every pair × question × order into as few calls as possible (up to 64 questions per call) — Jev’s “speculative fan-out” pattern. LLM backends instead get one small state per pair.

Account note: Jev on OpenRouter is served by the TypeSafe provider. If your OpenRouter account restricts providers and you see 404 No allowed providers, allow TypeSafe at https://openrouter.ai/settings/privacy; until then pairsort falls back to the generic LLM judge with a warning.


How it works · Evaluation → · All docs