How we reviewed this: on four CPU cores in python:3.12-slim with no GPU anywhere, --network none, with the published weights and the Qwen3-0.6B backbone from a local cache. We ran its HTTP service, its own client, the practical-test script it ships, and its unit test; we planted canaries in the request to see what reaches the model. This is an open model, so everything below is our own run of their model — no Jev calls, and no measurement of Jev.
What it does
A 0.6B decision head on a Qwen3 backbone: a state and typed questions go in, a probability per option comes back, and nothing is decoded. Three primitives — boolean, choice over 2 to 255 candidates, and score over 2 to 10 ordered levels. The weights are on Hugging Face, the code is here, and the two are deliberately separate: the README has you wrap the published model.safetensors into the .pt the server wants, in four lines.
Setup
It is a research tree, and the defaults show it — --checkpoint /root/agentjev/runs/phase4/final.pt, --model-path /root/agentjev/models/Qwen3-0.6B-Base, --device cuda:0. The README’s own invocation overrides all three, and --device cpu is enough to run the whole thing on a laptop:
{"event": "ready", "port": 8149, "model": "AgentJev-0.6B",
"checkpoint_sha256": "f91b276b81f8c865e056eb9cb1a8c6ada4d8baa660aee60a4402bdf0fda6ceda",
"max_choice_candidates": 255, "output_token_decoding": false,
"temperatures": {"boolean": 1.0718, "choice": 1.0353, "score": 1.0718},
"probability_semantics": "model distribution; domain calibration is not guaranteed"}
Two things in that line are rarer than they should be. The service hashes the checkpoint it loaded and publishes the digest, so you can tell two servers apart. And it says in its own health output that the probabilities are a model distribution rather than a calibrated one — while still shipping temperatures.json, which carries the fitted temperature, the number of calibration cases and the soft cross-entropy behind each primitive.
Using it
All three primitives answered on CPU through its own client: a boolean on whether a suite passed, a choice over four next actions, a score over three risk levels. Each returns the full distribution, and choice also returns the margin over the runner-up. Its own run_practical_test.py completes two longer scenarios end to end against the same server.
The contract is the strongest part, and it is strict in the right direction. Every malformed request we sent came back 400 with a sentence naming the rule:
300 options 400 choice requires 2..255 candidates
one option 400 choice requires 2..255 candidates
duplicate question id 400 question IDs must be unique nonempty strings within a state
two identical options 400 candidate descriptions must be distinct
type: "mystery" 400 type must be boolean, choice or score
empty state 400 state must be nonempty text, an object or an array
NaN in the JSON 400 nonfinite JSON
3,016 tokens of state 400 needs 3016 tokens; limit 2048. Shorten the input; nothing was truncated.
That last one is the one to notice: over the limit it refuses, and says it refused, instead of quietly cutting the state in half.
Permissions and data
Nothing leaves the machine. There is no API key, no outbound call, and the service binds loopback only — which we checked rather than read. Running it in a container with the port published, a request from the host is reset while the container’s own loopback request returns 200.
The contract’s claim that “transport IDs never enter semantic model input” also holds. We put distinct canaries in the question id, the option keys, the state, the question text and an option description, then decoded the token paths the model actually sees: the state, the question and the description are there; the question id and the option keys are not.
Maintenance
MIT, weights on Hugging Face, active this week. There are no CI workflows, and the testing situation is the weak spot. The repository’s one unit test cannot be collected on a fresh clone:
jev_service/candidate_v8.py:7: from typed_decisions.agent_completion_v8_run02.canonical import canonicalize
E ModuleNotFoundError: No module named 'typed_decisions.agent_completion_v8_run02'
Only agent_completion_v9 is in the tree; the v8 package the import names is not, and typed_decisions/agent_completion_v9/prepare_mbpp.py imports it too. Someone else reported this the day before we ran it, at the same commit, so it is known and open rather than new. Two other scripts still hardcode C:\Users\ASUS\agentjev_staging\typed_decisions.
The scripts that do run have a smaller problem of the same kind. run_practical_test.py prints its own per-decision timings and then closes with a hardcoded line claiming every decision took 40–80 ms. On our four cores those same decisions took 4.7 to 5.3 seconds, and the summary line would have said 40–80 ms either way, because it is a print rather than a measurement.
One more, for anyone sharing checkpoints: engine.py calls torch.load(..., weights_only=False), which executes pickle from the file. The published checkpoint does not need it — we loaded it with weights_only=True and got all 343 tensors — so the safe flag costs nothing here.
Verdict
The best-behaved open decision service we have run on a laptop: it refuses bad input in a sentence you can act on, never truncates silently, keeps your option keys out of the model, and tells you which checkpoint answered. Use it when you want typed decisions with no account and no GPU, and pin the checkpoint hash it reports.
Treat the repository as the research tree it is: one import short of a working test suite, and one print away from a latency claim it did not make. For the same shape with a server that already speaks the TypeSafe request format, see SemIf and JevForge; for the multilingual end, Laya.
See how it compares with other tools in Best Jev tools, tested hands-on.
Review updated Sep 24, 2026. Numbers quoted from the project are its author's own; we don't publish our own measurements of Jev.