Decision 2.0 is a full fine-tune of Qwen3-0.6B-Base with a candidate head on the final hidden states. Qwen3 is a plain transformer, so the backbone and the head were exported together with torch.onnx (fp32, eager attention), then weight-quantized with ONNX Runtime's MatMulNBits quantizer (embeddings 4-bit, matmuls 8-bit).
Build one prompt per question as the package's decision_model.segments / encode do (Context: … Task type: … Question: … Options:, one <option> segment per option holding {"description": …, "key": …} as sorted, compact JSON, then the Decision: suffix), tokenizing each segment separately; option_pos is the last token of each option segment and answer_pos the last token. Add decision2.score_bias[L] from config.json to the logits of an L-level score question (Kai ships offsets for 5 levels), then softmax (temperature 1).
q8 here is 8-bit block quantization (MatMulNBits, bits=8), not the usual dynamic int8 export; it uses the q8 slot because Transformers.js has no q8f16.
Against the packaged runtime (decision2.api.Decision2, exact path, CPU fp32), on 11 questions over 6 states: largest difference in any option's probability and how many top answers changed.
Runtime
Max difference
Top answer changed
q8, ONNX Runtime CPU (fixtures)
0.017
0 of 11
q8, WebGPU (Chrome, M3 Pro)
0.017
0 of 11
q8 on 30 typed-decisions questions
0.013
2 of 30 (near-ties)
fp32 export
< 0.0001
0 of 11
open-jev in Node reproduces the Python run of the same graph exactly (same token ids, option positions and score offsets).
On a MacBook Pro (M3 Pro, Chrome, WebGPU), on the 400 states × 5 questions of typed-decisions (test split; top answer = annotator label, as in the open-jev demo's benchmark):