How we reviewed this: we ran it in node:24 in Docker with its base URL pointed at a stand-in OpenAI-compatible server of ours that logged every request and answered with whatever probability we told it to. That let us see exactly how many calls leave the machine, what is in them, and how the output changes when the underlying model is certain, uncertain, or broken. We made no calls to any real provider, and none to Jev.
What it is, and what it isn’t
The repository description reads “OpenSource Jev” and the category most people will file it under is open models. It is not one. OpenJev is an Express server plus a small React playground that calls an OpenAI-compatible endpoint — gpt-4o-mini by default, or anything you point OPENAI_BASE_URL at.
The design is the interesting part. Rather than asking one model for one big JSON object, it turns every option into its own yes/no question and asks for a single number:
STATE:
Charged twice again!! Second month in a row.
STATEMENT:
The correct which team should handle this is "billing" (Charges, refunds, invoices).
How likely is the statement true (0–1)?
Each answer comes back as {"p": 0.87} under a strict json_schema, constrained to 32 tokens, and the independent probabilities are turned into a distribution through logits and a softmax. It sidesteps the failure it names — a model dropping keys or inventing labels — because there is no key to drop.
The cost of that is visible on the wire. A three-question form (a 3-way choice, a 4-level score, one noul) over a ten-word state produced eight requests and 800 input tokens, with the state re-sent whole in every one. The grammar of the splice is worth a glance too: your instructions are lower-cased and pasted into a sentence frame, which is how “Which team should handle this?” became “The correct which team should handle this is…”.
Ignorance and unanimity look identical
This is the finding. We ran the same question with two different stand-ins:
| what the model said |
choice |
probabilities |
confidence |
p = 0.5 for all three options |
billing |
0.333 / 0.333 / 0.333 |
0.417 |
p = 1.0 for all three options |
billing |
0.333 / 0.333 / 0.333 |
0.417 |
“I have no idea” and “all three are certainly correct” produce byte-identical answers. That follows from the normalisation — softmax over logits is normalised odds, and any constant cancels — but nothing downstream can tell the two apart, and both report a number called confidence that a reader will take for a probability.
It isn’t one. confidence is 0.5 × top + 0.5 × (gap + 0.5), which has a floor of 0.25: a perfectly uniform three-way answer reads 0.417, a four-way one 0.375. When the model does discriminate the numbers are sensible (0.976 against 0.012 when our stand-in favoured one option), so the machinery works; it is the uninformative end of the scale that is mislabelled.
The tie-break is >, so the winner of a uniform distribution is whichever option appears first in your criteria object. Reordering the three keys, with nothing else changed, moved the answer from billing to technical.
What happens when things go wrong
Two paths swallow failure. If the reply doesn’t parse as JSON, scoreProposition falls back to a regex over the raw text. We answered with prose — “I think billing is most likely, maybe 70 percent” — and the regex matched the 0 inside 70, so that option scored 0.0. Nothing in the response says a parse failed.
In oneshot mode the equivalent line is String(data.choice ?? Object.keys(q.criteria)[0]): a model that omits the key gets you the first option at confidence 0.5, with a probability map assembled on the spot. We saw exactly that — {"billing": 0.5, "technical": 0} from a reply that mentioned neither.
Credit where it is due on the third path: when our stand-in returned HTTP 500 for one of three option calls, the OpenAI SDK’s own retry covered it, and the answer came back complete.
Permissions, keys and the open port
npm run dev binds port 3001 with app.use(cors()) — Access-Control-Allow-Origin: *, no authentication — and the request handler falls back to the key in your .env when the caller doesn’t supply one. We sent a cross-origin request with Origin: https://evil.example and no key, and it was served, using the server’s key. While the playground is running, any page open in your browser can spend it. The key itself is handled decently otherwise: the startup line prints api_key=yes and never the value, and it appears only in the Authorization header to your chosen provider.
Maintenance
Four commits in one day by one author, no tests, no CI, and an empty file named sed2Mso9e committed at the root — the debris of a sed command. The TypeScript is clean (tsc --noEmit passes) and there is a package-lock.json.
There is no licence: no LICENSE file, no license field in package.json, no mention in the README, and "private": true. For a project whose GitHub description is “OpenSource Jev”, that is the thing to fix first — as it stands, nobody can legally reuse it.
Verdict
Read it, don’t deploy it. The per-option idea is worth understanding and the code is short enough to absorb in one sitting, but as a decision engine this multiplies your bill by the number of options, tells you nothing when the model is unsure, and reports 0.417 while doing so. If you want the same interface backed by something that actually runs locally, see Laya for Node or the rest of the open models category.
See how it compares with other tools in Best Jev tools, tested hands-on.
Review updated Sep 21, 2026. Numbers quoted from the project are its author's own; we don't publish our own measurements of Jev.