Skip to content

Quickstart

Install

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

Try it without any key or GPU using the built-in fake model:

polyjev ask fake/demo '{"ticket": "Everything is down before our demo"}' \
  --noul "urgent=Does the customer need a reply within the hour?" \
  --choice "team=Which team owns this?|billing,outage,feature" \
  --score "tone=How angry is the customer?|calm,annoyed,furious"

Pick a model

A model is a provider/model id, or a name from your config.

you have model id notes
an Anthropic key anthropic/claude-opus-5 ANTHROPIC_API_KEY
an OpenAI key openai/gpt-5.6-luna OPENAI_API_KEY
a Gemini key gemini/gemini-3.8-flash GEMINI_API_KEY
a vLLM server vllm/<served-name> VLLM_BASE_URL=http://host:8000/v1
Ollama ollama/qwen3:8b local on :11434
a GPU in this process hf/Qwen/Qwen3-4B-Instruct-2507 pip install "polyjev[hf]"
any OpenAI-compatible API openai-compatible/<model> set base_url in config

Library

import polyjev as pj
from enum import Enum

class Team(Enum):
    BILLING = "billing"
    OUTAGE = "outage"
    FEATURE = "feature"

judge = pj.Polyjev("vllm/local")
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?", Team, {Team.OUTAGE: "service down"}),
        "tone": pj.Score("How angry is the customer?", ["calm", "annoyed", "furious"]),
        # asked only if "urgent" came out yes, with that answer in its prompt
        "page": pj.Noul("Should we page on-call?", ask_if={"urgent": [True]}),
    },
)

d["urgent"].p             # probability of yes
d["team"].value           # Team.OUTAGE  (Enum in -> Enum out)
d["team"].probabilities   # {Team.BILLING: p1, Team.OUTAGE: p2, Team.FEATURE: p3}, summing to 1
d["tone"].level, d["tone"].expected, d["tone"].confidence
d["page"]                 # None when skipped
d.to_jev()                # the exact Jev response JSON
d.usage, d.diagnostics

Async, batches and images:

async with pj.Polyjev("anthropic/claude-opus-5") as judge:
    d = await judge.adecide(state, questions, images=["photo.jpg"])
    results = await judge.adecide_many(states, questions, concurrency=16,
                                       on_result=lambda i, r: print(i), return_exceptions=True)

Useful options on decide (the same as Jev's request extensions): samples ("auto" or a count), auto_max, auto_threshold, think (a thought budget in tokens), instructions (shared context), ask (answer a subset), sequential, seed, and strategy to force a read strategy.

Server

pip install "polyjev[server]"
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

Or from Python, against any Jev-compatible server:

remote = pj.Remote("http://localhost:8011")
d = remote.decide(state, questions)      # same typed answers

Config file

polyjev.yaml names your models, sets what jev-latest means and configures the server. See examples/polyjev.example.yaml.

default_model: local
models:
  local:
    provider: vllm
    model: local
    base_url: http://gpu-node:8000/v1
    extra_body: {chat_template_kwargs: {enable_thinking: false}}
  claude: {provider: anthropic, model: claude-opus-5, params: {output_config: {effort: low}}}
server:
  api_key: ${POLYJEV_API_KEY:-}
  playground: true
judge = pj.Polyjev(config="polyjev.yaml")      # or set POLYJEV_CONFIG
judge.decide(state, questions, model="claude")