Answers appear here, one question at a time.
Decision models in the browser. Text in, calibrated probabilities out — on your GPU, nothing sent anywhere.
Kev is a family of small decision models built on Qwen3.5.
@ai-ecoverse/kev.js runs them on WebGPU through onnxruntime-web, with the same
System One request and response shapes as Kev's own server.
The first load downloads the weights once and keeps them in the browser cache. Kev-0.8B is 822 MB; the bigger models are worth the wait if your connection can take it.
Not loaded.
Answers appear here, one question at a time.
npm install @ai-ecoverse/kev.js onnxruntime-web
Weights are not in the package. They are downloaded from a URL you choose — the bundles published on Hugging Face, or your own host.
import * as ort from "onnxruntime-web/webgpu";
import { loadKev } from "@ai-ecoverse/kev.js";
const kev = await loadKev("https://huggingface.co/ai-ecoverse/kev.js/resolve/main/kev-0.8b", {
ort,
variant: "q8f32",
executionProviders: ["webgpu"],
onProgress: ({ file, loaded, total }) => console.log(file, loaded / total),
});
Run it in a Web Worker: loading takes seconds and every request is tens to hundreds of milliseconds of GPU work. Weight files are cached in Cache Storage, so later loads take a second or two.
One request carries the text to judge (state) and any number of questions. Questions share the
state but cannot read each other, so adding a question never changes another one's answer.
const res = await kev.systemOne({
state: "Shoes arrived two weeks late and in the wrong size. Also I see two charges on my card.",
questions: {
department: { type: "choice", instructions: "Which team should handle this?",
criteria: { returns: "Exchanges, refunds, wrong or damaged items",
shipping: "Delivery status, delays, lost packages",
billing: "Charges, invoices, payment problems" } },
escalate: { type: "noul", instructions: "Does this need urgent human attention?" },
frustration: { type: "score", instructions: "How frustrated is the customer?",
criteria: ["Calm", "Frustrated", "Very angry"] },
},
});
// Kev-4B (kev-4b@4bc64c6) in Chrome:
res.answers.department.choice; // "shipping"
res.answers.department.probabilities; // { returns: 0.42, shipping: 0.43, billing: 0.15 }
res.answers.escalate.noul; // 0.95 probability of yes
res.answers.frustration.score; // 1.46 expected level, 0-based
The ticket is about a late delivery, a wrong size and a double charge, and the department probabilities say
exactly that: returns and shipping are a coin flip, billing is a real possibility. That is what you lose with a
single label, and why routing on confidence beats routing on choice.
| Type | Criteria | Answer |
|---|---|---|
noul | optional descriptions for true and false | noul: probability of yes |
choice | 1–255 named options, each with a description or null | choice, probabilities, confidence |
score | 2–255 level descriptions, lowest first | score (mean level), legend, probabilities, confidence |
state and instructions also accept objects and arrays, which are rendered as labelled
text. Other methods: systemOne(req, { onAnswer }) reports each question as it finishes,
systemOneSeparate(req) runs every question in its own pass, and probs(record) returns
raw probabilities. Pass { dateFacts: true } to append day counts between absolute dates in the
state (Kev's KEV_DATE_FACTS=1). The pointer head applies the checkpoint's fitted temperature
(about 2.1–2.4) unless you set temperature: 1 for the raw logits. The shapes match Kev's
POST /v1/systemone, so the TypeSafe SDK's examples carry over.
| Model | Download | Accuracy / Brier 300 held-out records, new sources | 3-question request |
|---|
Weights are int8 with fp32 activations. Browser probabilities track the full-precision model closely: on 300 labelled records Kev-4B scores 0.773, exactly the full-precision model's accuracy, with a mean probability difference of 0.0028 and not one answer changed. int4 is not offered — it moves probabilities by 0.2–0.8 and flips answers, which defeats the point of a calibrated model. Timings are from an Apple M4 Max; the WASM fallback is roughly 5× slower.
Kev is a Qwen3.5 base with a rank-16 LoRA and a small pointer head. The head scores each option's boundary token against the question's decision token, so the answer is read out of the model's own activations rather than generated as text. Nothing is sampled, and one forward pass answers every question.
Qwen3.5 mixes recurrent Gated DeltaNet layers with attention, so the state runs once and each question runs as a continuation of the state's cache — exact isolation, and the state is never recomputed. kev.js keeps those caches on the GPU between questions and reuses them across requests that share a state.
The README covers the export pipeline, the quantization measurements and how parity with the PyTorch model is verified.