Skip to main content

polyjev

Typed, calibrated decisions from any LLM.
Ask yes/no, multiple-choice, rating and extraction questions; get back typed answers with honest probabilities,
from Claude, GPT, Gemini, or open models on your own GPUs.

CI Apache-2.0 Python 3.10+ Open in Colab

Across 7 open models, raw confidence sits far above accuracy; after calibration it lands next to it

LLMs are great judges, classifiers and extractors, but they give you text, and when they sound sure they are often wrong. polyjev turns any model into a decision model:

  • Always valid. Answers are read as probabilities over your options, so there is nothing to parse and no answer outside your schema.
  • Honest numbers. Every answer has a probability distribution and a confidence. Option shuffling reduces position bias, and one-command calibration brings "90% sure" close to right 90% of the time (benchmark).
  • Grounded extraction. Span answers are always substrings of the input, with character offsets. The model cannot invent a value.
  • Any model. Anthropic, OpenAI, Gemini, OpenRouter and every OpenAI-compatible server (vLLM, SGLang, Ollama, llama.cpp), or a Hugging Face model loaded straight onto your GPU. One string switches between them.
  • A drop-in Jev API. polyjev serve speaks POST /v1/systemone, the API of TypeSafe's Jev, so Jev clients work against your own models.
import polyjev as pj

judge = pj.Polyjev("vllm/local")   # or "anthropic/claude-opus-5", "openai/gpt-5.6-luna", "hf/Qwen/Qwen3-4B-Instruct-2507"
d = judge.decide(
    {"ticket": "Everything is down and we have a demo at noon."},
    {
        "urgent": pj.Noul("Does the customer need a reply within the hour?"),
        "team": pj.Choice("Which team owns this?", {"billing": None, "outage": "service down", "feature": None}),
        "tone": pj.Score("How angry is the customer?", ["calm", "annoyed", "furious"]),
        "order": pj.Span("the order number, if the customer gives one"),
    },
)
d["urgent"].p              # 0.999
d["team"].value            # 'outage'   (pass an Enum class and you get Enum members back)
d["team"].probabilities    # {'billing': 8e-14, 'outage': 1.0, 'feature': 2e-12}
d["tone"].level            # 'annoyed'
d["order"].found           # False: there is no order number, and polyjev will not invent one

Raw output of Qwen3-4B-Instruct-2507 on vLLM (one RTX 5000 Ada): about 60 ms for all four questions. Instruction-tuned models are this sure of themselves; the benchmark shows how often they should be.

Install

pip install "polyjev[all]"     # library + server + Claude + Gemini
pip install "polyjev[hf]"      # + in-process Hugging Face models (torch)

No key or GPU? fake/demo is a deterministic stand-in model, and the Colab notebook runs a real model on a free GPU.

polyjev ask fake/demo "Invoice #A-1042, total due \$1,234.56 by 2024-03-15" \
  --noul "overdue=Is the invoice overdue?" --span "id=the invoice id" --spans "dates=every date"

What people use it for

example
LLM-as-a-judge that knows when it is unsure: auto-accept confident grades, send the rest to a human examples/llm_judge.py
Guardrails: classify, then redact personal data by exact offsets examples/guardrails.py
Routing requests to tools or agents, and asking a clarifying question below a confidence threshold examples/router.py
RAG: is the answer in the context? Extract it with a highlightable citation examples/rag_answerable.py
Triage and tagging at scale, with decide_many and bounded concurrency examples/async_batch.py
Vision: questions about images and webcam frames examples/images.py

Questions and answers

type you ask you get
Noul a yes/no question p = P(yes), value, confidence
Choice one of 2–26 options (strings, a dict with descriptions, or an Enum) value, probabilities over every option, confidence
Score a level on an ordered scale level, expected (probability-weighted level), probabilities
Span / Spans one value / every value of a kind to copy from the input text, start, end, confidence, or found=False

Questions can depend on each other (depends_on), be asked only when an earlier answer matches (ask_if), and take images. Extra reads (samples) average over shuffled option orders; think lets the model reason first. See how it works.

Honest probabilities: the benchmark

polyjev-bench runs 1,777 questions from six public datasets (BoolQ, RTE, MNLI, AG News, MMLU, SST-5) through 7 open models on one GPU, calibrating on one half and reporting on the other:

model accuracy mean confidence ECE raw → calibrated decisions / s
Gemma-4-12B-it 0.810 0.974 0.173 → 0.064 30
Qwen3-14B (FP8) 0.787 0.971 0.189 → 0.070 18
Qwen3-8B 0.766 0.973 0.223 → 0.084 58
Qwen3-4B-Instruct-2507 0.763 0.980 0.222 → 0.079 107
Phi-4-mini-instruct 0.706 0.821 0.115 → 0.060 85
Granite-3.3-8B-instruct 0.704 0.922 0.235 → 0.087 42
OLMo-2-13B-Instruct (FP8) 0.675 0.685 0.083 → 0.094 27

What we learned:

  • Models are over-confident, and one fitted temperature fixes most of it.
  • Reading logprobs beats asking a model for probabilities. Verbalized confidence cost Qwen3-8B 6 points of accuracy and ran ~10× slower.
  • Shuffling the options cut raw calibration error by a third.
  • polyjev's logprob reading matches the model's exact logits: the same answer on 99.5% of items.

Full leaderboard · charts · rerun it with bench/run.py.

Calibrate your own model on your own data:

polyjev calibrate vllm/local --data labelled.jsonl --out calibration.json   # then `calibration: calibration.json` in polyjev.yaml

Any model

where the model runs provider ids how probabilities are read
your GPU, in-process hf exact label-token logits
your GPU server vllm, sglang, llamacpp, ollama, lmstudio top-20 logprobs at the answer position
hosted APIs anthropic, openai, gemini, openrouter, together, groq, deepseek, fireworks, mistral, xai, openai-compatible logprobs where the API has them, otherwise probabilities from structured output ("verbalized")

strategy: auto probes each model once and falls back on its own. Adding a provider is one small class (docs). The open-model paths (vLLM, in-process Hugging Face) have run on real GPUs. The hosted-API adapters are tested offline: every request is checked against the vendor SDK's real signature, at both the newest and the oldest supported SDK version. The benchmark so far covers open models; hosted results are welcome as PRs.

Serve it

polyjev serve --model vllm/local --playground            # http://localhost:8011
curl -s localhost:8011/v1/systemone -H 'content-type: application/json' -d @examples/requests/readme_example.json

The request and response shapes, extensions and error bodies follow Jev and djev, so existing clients only change the URL. pj.Remote(url) is the typed Python client for any such server. The built-in playground has a JSON editor, image upload and webcam capture, and probability bars. Server docs.

Run open models on your own GPUs

  • Workstation: docker compose --profile gpu up (deploy/) runs vLLM and polyjev together.
  • HPC / SLURM:
    • setup-env.sh installs the vLLM/torch build that matches your driver.
    • polyjev.sbatch serves a model and prints the SSH tunnel command.
    • Both were tested on a university cluster, including the gotchas: CUDA 12 drivers, GPUs shared with a display, compute nodes without nvcc, and home-directory quotas.
  • Inside any GPU job: provider: hf, no server.

Deployment guide, with a GPU-memory → model table.

How it compares

polyjev JSON mode / structured-output libraries Jev (TypeSafe) AnyJev
answer always valid yes yes yes yes
probabilities for every option yes no, one label yes yes
calibration tooling yes no trained in yes
hosted models (Claude, GPT, Gemini) yes yes no, one model no
open models on your GPUs yes yes no yes
grounded extraction with offsets yes no no (Noul/Choice/Score) no
Jev-compatible HTTP API yes no yes no
open source Apache-2.0 varies no Apache-2.0

FAQ

Is this Jev? No. polyjev is independent and not affiliated with TypeSafe; "Jev" is their trademark. polyjev implements the publicly documented /v1/systemone request and response shapes, so it can stand in for Jev with models you choose. Jev itself is one trained model with built-in calibration.

Why not just ask the model for JSON? You get a label but not how sure the model is. The numbers a model writes about itself are less reliable than its token probabilities (see the benchmark), and a bare label cannot tell you when to escalate to a human.

Does it work with Claude? Yes. Claude has no logprobs, so polyjev asks for a probability per option through structured output ("verbalized"), and prompt caching keeps the repeated state cheap. The adapter is tested offline against Anthropic's SDK, but Claude has not been benchmarked here yet. Calibrate it on a few hundred of your own examples.

How fast is it? With an open model on vLLM, one read is a few output tokens: about 100 ms per decision, and 20–100 decisions per second on one workstation GPU (see the benchmark). Hosted APIs take their usual round trip per question, asked in parallel.

What does samples: "auto" do? It reads once, and re-reads with shuffled options only when the first answer is uncertain. Confident questions cost one call.

Project layout

src/polyjev/   library: types, validation, read strategies, engine, spans, client, backends/, server/, CLI
bench/         polyjev-bench: tasks, runner, report, results, SLURM scripts
deploy/        Dockerfile, compose (+ vLLM), SLURM recipes
docs/          documentation site (mkdocs)
examples/      runnable use cases, configs, Colab notebook
tests/         offline tests with every provider SDK faked, plus a real-model test for `hf`

Credits

polyjev follows the API of TypeSafe's Jev and the details of Matt Mastracci's djev / djev-spark (validation rules, samples: "auto", dependencies, span semantics). Its option-shift debiasing follows AnyJev's cyclic shifts.

If polyjev helps your work, a ⭐ helps others find it, and you can cite it.

Roadmap

  • Benchmark results for hosted models (Claude, GPT-5.x, Gemini)
  • pj.Form: decision schemas declared as classes
  • Joint mode: every question in one call, for APIs that bill per request
  • A TypeScript client
  • Hidden-state probes for in-process models (AnyJev-style)

Contributing

Bug reports, new providers, benchmark results for more models, and docs fixes are very welcome. See CONTRIBUTING.md.

License

Apache-2.0

Metadata

Release files for polyjev 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for polyjev 0.1.0
File Size Uploaded
polyjev-0.1.0.tar.gz 97.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for polyjev 0.1.0
File Interpreter ABI Platform
polyjev-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 184.3 kB

Release files / polyjev-0.1.0.tar.gz

Download URL polyjev-0.1.0.tar.gz
Size 97.6 kB
Tags Source
SHA-256 checksum
How to use checksums
2c0ecb5c18f769c6bf2bde99404fc110213948e70bce7712893336913e6d9d0e
BLAKE2b-256 checksum
How to use checksums
bd6ee5c2eb9d41410de0903b676e1b7d613dd26e6a6fec2c6a9b05f677918bf3
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 27, 2026.

Transparency log

Release files / polyjev-0.1.0-py3-none-any.whl

Download URL polyjev-0.1.0-py3-none-any.whl
Size 86.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f66686b0445e9146750be36909c3d835230bfb22125e9461d1bd9b1a51853c5b
BLAKE2b-256 checksum
How to use checksums
df06c7c3837f3c36b7b63abc667262300a51237de74aa3769c67f3b0c831cb68
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 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page