Ask once.
Branch on a value.
jev_decide is a tool for Pi
agents that turns a judgement call into a typed result. Ask
TypeSafe's Jev System One a question and you get back a
choice, a score or a noul — a number your
code can compare. Jev never returns prose. The call runs through
Pi's builtin classifier API with your TYPESAFE_API_KEY, so its
usage lands in your session cost. If Jev is unavailable, rate-limited,
out of credit or too slow, the same question is handed silently to your session model and the
result is marked fallback.
Why
Typed values.
Not paragraphs to re-parse.
A decision you cannot act on is just more text. Everything here is aimed at making the result something your code — and your model — can use immediately.
Three typed answers
choice picks a label, score places something on your own 2–10 level
scale, noul returns a calibrated probability. Same shape every time.
Silent fallback
Jev down, rate-limited, out of credit, too slow or unreachable? The identical question goes
to your session model and the result carries source: "fallback".
Bounded, then a breaker
A 30 s deadline on every request (configurable), plus a circuit breaker so a struggling endpoint is not retried on every turn — one probe per cooldown window.
Answers are validated
Every answer is checked against the question that was asked — a missing answer, an unknown
choice, a non-numeric score or a noul outside 0..1
fails loudly instead of reaching your model.
Self-describing results
Each result ships a legend explaining its own fields, so the format travels with
the data instead of relying on the model remembering it.
Bad input never ships
Arguments are normalized and re-validated before a request exists — malformed payloads fail locally with a message that says what was actually received.
How it works
One call, one value.
Ask a typed question
Pass the context as state and 1–8 questions keyed by a short identifier.
The questions are a nested JSON object — never a string.
Jev returns a value
Not prose — a labelled field per question, with the distribution behind the answer so you can apply your own threshold.
Branch on it
Compare against a threshold in code, or hand the typed result back to the model as the basis for its next action.
Reading the answer
Every field, spelled out.
The result carries a legend restating this at the moment of use —
this table is the reference version.
| field | meaning |
|---|---|
| answers[k].type | mirrors the question type: choice, score or noul |
| answers[k].noul | P(yes) in 0..1 — there is deliberately no confidence field; the probability is the certainty |
| answers[k].choice | the selected label, with probabilities giving the full distribution over your criteria keys |
| answers[k].score | position on your own scale and may be fractional (1.57) — its legend (always present, rebuilt from your criteria) maps level index to your label |
| answers[k].confidence | 0..1 for choice and score; absent for noul |
| source | jev or fallback — treat fallback answers as less calibrated second opinions |
| legend | top-level: this documentation note, not part of any answer |
Two things that trip people up
A noul answer has no confidence field — by design.
The probability is the confidence, so asking for a second one is asking the same
number twice.
A score is not an integer. It is a position on the rubric you
supplied, so 1.57 means "a little past medium" — read it against the answer's
own legend rather than rounding it yourself.
When Jev cannot answer
It falls back. You keep working.
Every failure mode has a defined outcome — a stall or an outage never becomes a dead agent turn.
| condition | what happens |
|---|---|
| provider error | auth, invalid request, quota or upstream failure — fallback, and the circuit opens so the endpoint is not hammered; malformed questions are still rejected before the call |
| timeout | fallback after JEVMODEL_TIMEOUT_MS (default 30 s), circuit opens |
| no credentials | fallback with a stated preflight reason — the session model answers |
| bad questions | rejected before any request is sent |
| bad answers | error — a malformed answer never reaches your model silently |
Privacy: what leaves your machine
state is sent verbatim to the endpoint behind your classifier credential —
TypeSafe's System One with TYPESAFE_API_KEY, or whichever Jev-capable provider
you configured in Pi. Do not pass material your organisation forbids sending off-box.
With no classifier credential, no request is made at all: the session model answers.
Install
Three commands.
Pi loads TypeScript directly — there is no build step.
From npm
published package, listed in the Pi gallery
Pi package
from GitHub
Local checkout
for development
| environment | purpose |
|---|---|
| TYPESAFE_API_KEY | required — Pi's TypeSafe credential (or another Jev-capable provider); without any classifier credential every call falls back to the session model |
| JEVMODEL_TIMEOUT_MS | max wait before handing off, default 30000 |
| JEVMODEL_FALLBACK_MODEL | pin the fallback to provider/modelId, otherwise the session model answers |
Model selection: the first credentialed
classifier in Pi's registry, preferring typesafe/jev-latest; override with the
tool's model argument as provider/id.
Unofficial
An independent integration. Not affiliated with, endorsed by, or supported by TypeSafe or the Jev project. Licensed MIT.
Source, tests and the full README live at
github.com/rioliu/pi-jev-extension.