Build a browser extension with Jev

· Towards AI ·

7 min read Original article ↗

How I built Grounds, a Chrome extension that instantly flags whether a page argues its claims or merely asserts them

Carmine De Stefano

Press enter or click to view image in full size

Bust of Aristotle (Palazzo Altemps, Rome), with the TypeSafe logo.

I wanted a browser extension that tells me, at a glance, whether the article on my screen actually argues its case or just asserts things. I built it over a weekend and called it Grounds. The extension plumbing was the easy part. The interesting part was the model behind it, Jev.

What Jev is

Jev is TypeSafe’s “System One” model. A chat model writes free text. Jev does something narrower and, for many tasks, more useful: you give it some input and a set of typed questions, and it returns a typed answer for each question, with calibrated probabilities.

It supports three kinds of decision:

  • a choice from a list of options you define,
  • a score on a rubric you define,
  • a third type called Noul.

Every answer comes back with a confidence value. Calls are fast, around 100 ms, and input is cheap while output is free at the time of writing.

Here is why that matters. A lot of real work is not “write me a paragraph”. It is “classify this”, “rate this”, or “pick one of these”. For those tasks, a model that returns a structured value with a probability is far easier to build on than one that returns prose you then have to parse. Jev never produces free text, so there is nothing to clean up and nothing to hallucinate into your JSON.

A single call

The whole API surface you need for a scoring feature fits in one request:

const res = await fetch("https://api.typesafe.ai/v1/systemone", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "jev-latest",
state: articleText,
questions: {
argument: {
type: "score",
instructions: "Rate how well the article supports its claims.",
criteria: [
"Unsupported assertions or pure rhetoric",
"A few reasons, but loose or mainly rhetorical",
"Clear reasons with a coherent logical structure",
"Rigorous: structured reasoning, real evidence, counterpoints addressed"
]
}
}
})
});

const data = await res.json();
const answer = data.answers.argument; // { score, confidence, ... }

The state is the text you want judged. Each entry in questions is a typed question. For a score, criteria is the rubric, from the lowest level to the highest. Jev returns a probability weighted position across those levels, so the score can land between them, for example 1.6, together with a confidence value. No parsing, no prompt engineering tricks to force a clean output.

The idea behind Grounds

“For an educated man should be able to form a fair judgment as to the goodness or badness of an exposition.” Aristotle, On the Parts of Animals

I did not want a fact checker. Deciding whether an argument is correct is hard for people and for frontier models, so asking a small fast model to do it would be the wrong job. What a model can do reliably is tell whether there is an attempt to argue at all: reasons, structure, evidence, some engagement with the other side.

So that is the axis Grounds measures, and it is worth being precise about it, because “well argued” is easy to misread.

Get Carmine De Stefano’s stories in your inbox

Join Medium for free to get updates from this writer.

Remember me for faster sign in

Well argued does not mean right. Grounds says nothing about whether the claims are true or the reasoning is sound. A page can be rigorously argued and still reach a conclusion you think is wrong, and a weak argument still counts as an argument. What the label reflects is the shape of the writing: does the text lay out reasons, connect them, bring evidence and deal with objections, or does it just state opinions and expect you to agree. The axis is argued against merely asserted, not true against false. An opinion column that builds a case scores high. A confident rant that only asserts scores low, however sure of itself it sounds.

There is one trap here. A fluent, emotional piece can sound persuasive while proving nothing. If you are not careful, a model rewards that tone. The fix is in the prompt, where I tell Jev in plain terms to ignore it:

const ARGUMENT_INSTRUCTIONS =
"Rate how well the article supports its claims with well structured " +
"argumentation: explicit reasons, a clear logical structure, evidence or " +
"examples that genuinely support the point, and engagement with " +
"counterarguments. Do NOT be swayed by rhetorical force, emotive or vivid " +
"language, a confident tone, or examples used only as decoration. A fluent, " +
"persuasive sounding piece that only asserts must score low.";

How the extension works

The flow is short.

1. Get the article text. Running the model on the whole page is a bad idea. A feed or a home page is a pile of unrelated snippets, and the verdict would be meaningless. I use Mozilla Readability, the same engine behind the Reader View in Firefox and Safari, to pull out just the article body:

const article = new Readability(document.cloneNode(true)).parse();
const text = article ? article.textContent : "";

2. Decide if there is even an article. If Readability returns a long, coherent body, the page is an article. If there is very little text, the extension stays quiet and makes no call. When the case is unclear, the same Jev request also asks a choice question, and the badge only appears if Jev confirms it is a single article:

questions.page_type = {
type: "choice",
instructions: "What kind of page is this text taken from?",
options: ["single article", "feed or listing", "other"]
};

3. Turn the score into a badge. The raw score maps to one label, shown with a traffic light colour and Jev’s confidence. Level zero means unsupported, and in that case the badge is hidden, because if a page argues nothing there is no point showing anything:

const LEVELS = [
null, // hidden
{ word: "Loosely argued", color: "#e5484d" }, // red
{ word: "Solidly argued", color: "#e8a33d" }, // amber
{ word: "Rigorously argued", color: "#22a565" } // green
];

The badge itself lives in a shadow DOM so the host page styles cannot break it.

A few decisions that paid off

  • One call per page. My first version scored each sentence separately. It worked, but forty calls per article felt slow. Jev accepts the whole article as state, so one request gives one verdict in about 100 ms.
  • Show the confidence. The label is a judgment, not a fact, so the badge always shows how sure Jev is.
  • Keep the key private. Each user brings their own Jev key. It is stored only in the browser, and the request is made from the background service worker, so the page never sees it.

Press enter or click to view image in full size

Grounds sits in the bottom-right corner of the page.

Try it

The code is on GitHub at github.com/cadeos/jev-grounds. Load it as an unpacked extension, paste your Jev key, and open an article.

The same shape works for a lot of other one glance questions about a page. Is this news or an advertorial. How much does it lean on outrage. How much prior knowledge does it assume. In each case the recipe is the same: pull the real content, ask Jev one typed question, show the answer. That is the part I find worth sharing. Once you stop asking a model for prose and start asking it for a typed decision, a surprising number of small useful tools become a single API call.

That is really all Grounds does. It draws a quiet line between making a case and just stating one.