{"slug": "sam-3-found-the-cars-jev-decided-what-they-were-worth", "title": "SAM 3 found the cars. Jev decided what they were worth.", "summary": "A developer built a usage-based pricing system that uses the Jev System One model from TypeSafe to judge how much work an AI answer represents and charge the expected value of a three-level scale rather than a rounded tier. The system also asks the judge whether answers are grounded in sources, multiplying the expected price by the grounding probability, and falls back to rule-based pricing when the judge is unavailable — with the fallback now recorded in the receipt after a 401 error was silently swallowed. The developer argues that charging the mean of the probability distribution preserves calibration information that tier rounding discards.", "body_md": "Most usage-based APIs I have paid for charge by length. Tokens, calls, rows, seconds of video. It is a proxy for value, and it quietly pays a model to pad: the same question answered in four sentences earns more than the one answered in one.\n\nI wanted to see what happens if the price is decided *after* the work instead, by something that reads the result.\n\nThe payment layer already allowed the shape of this: authorize a ceiling before the work starts, settle less after it. What it could not do — what nothing can do — is say **how much less**. That number has to come from somewhere, fast enough to sit inside a request, and it cannot come from the thing that produced the work.\n\nHere is what I built, in the order I got it wrong.\n\nThe judge is [Jev](https://typesafe.ai), a System One model: you send state and typed questions, it answers with probabilities and a confidence, no prose. I asked it one question — how much work does this answer represent, on a three-level scale — and mapped the answer to a tier.\n\nThat was the mistake. `score: 0.99` on a scale whose levels are *lookup / synthesis / investigation* means \"essentially a synthesis\". I floored it to 0 and charged for a lookup.\n\nThe distribution **is** the product. The moment you round it into a bucket you have thrown away the only thing that distinguishes a calibrated model from a general one asked for JSON. The fix is to charge the mean:\n\n``` js\nconst expected = LEVELS.reduce((total, level, i) => total + level.micros * p[i], 0);\n```\n\n12% a synthesis and 88% an investigation, at $0.025 and $0.05, is **$0.047** — not a tier. And it is unbiased in a specific, limited sense: if the probabilities are calibrated, what you charge converges on the expected value of the tier prices — the value function I chose, not some true worth of the work. Rounding to a tier throws that information away and makes the price jump at an arbitrary line; whether the jump favours you or the buyer depends on where the line sits.\n\n`$0.0235` is not a display value, by the way. The ledger holds micros, so that is the amount that settles — which is what lets a price be a mean in the first place.\n\nUncertainty also stops needing a hand-tuned rule. A judge torn between two levels charges between them, automatically.\n\nThe second question I ask is whether the answer is *grounded* — supported by what the service actually read, rather than asserted. Every call came back between 0.07 and 0.54, and I spent a while believing the model was bad at it.\n\nIt was not. I was sending the question and the answer, and asking \"is this supported by the sources?\" — **without sending the sources**. With them in the state, grounded answers score 0.98 and ungrounded ones stay low.\n\nWhat I do with that number is a pricing decision, not a mathematical consequence: I multiply the expected price by the grounding probability, which treats a wholly ungrounded answer as worth nothing and a half-grounded one as worth half. Someone else could reasonably floor it, or refuse to bill at all below 0.5. Whatever you choose, put it in the receipt.\n\nObvious in hindsight. A verification question with nothing to verify against is a vibe check, and it will happily return numbers that look like data.\n\nEvery path through the judge falls back to a rule-based one: no key, timeout, error, unrecognised shape. A trial key that expires should change your prices, not stop your server.\n\nThen I set a real key and everything kept working — priced by rules. The key was a Vercel AI Gateway key (`vck_…`), I was sending it to TypeSafe's own endpoint, the answer was `401`, and my fallback swallowed it into a reason string nobody was reading.\n\n**A fallback in the path of money must be loud.** It is now in the receipt: every response says which judge decided and why, so \"the rules priced it because the judge answered 401\" is visible to the buyer, not just to the logs.\n\nText is the easy case. The sharper one is detection: *\"find every solar panel in this photo\"* has no price until it runs — there might be forty, there might be none — and the model that answers also scores its own answers.\n\nI assumed a segmentation model would sidestep that, because the SAM I remembered was class-agnostic: you clicked, it gave you a mask, it had no idea what the mask was. **SAM 3 is not that model.** It takes a noun phrase, returns every instance of it, and scores each one itself. The self-marking problem does not disappear with segmentation; it arrives with it.\n\nSAM 3 found the three cars in that photo in **2.0 seconds**, scoring them 0.93–0.97. It is equally confident about a crop that is not a car, which is the point.\n\nSo a second opinion decides the money: a vision model says what each crop *shows*, in one sentence, and Jev says whether that sentence is the thing the buyer asked for. Neither of them chose the crop. The price is **$0.01 per accepted detection**, so three cars surviving at `p ≥ 0.9` is **$0.03 of a $0.20 ceiling**.\n\nAsk for a bicycle and SAM returns nothing, so the charge is **$0.00**. The empty photo is the case a flat fee gets wrong, and the one a caller remembers.\n\nThat zero is not an error path I wrote, either. The handler answers `422` and the gate **releases the hold** — the authorization goes back to the caller, unspent, to use elsewhere. Charging nothing has to be as ordinary as charging something, or the seller quietly rounds up.\n\nI would rather have asked the vision model directly and read the probability from its logprobs. Two things stopped me, and I diagnosed the first one wrong:\n\n`logprobs: true` with an image returned `400`. I wrote \"the gateway refuses logprobs alongside images\" in a comment and moved on. The actual error was `max_output_tokens` below the minimum of 16 — my `max_tokens: 1`. Read the error body.\nHence: describe, then decide.\n\nThat is measurable, so I measured it. Seven crops of the same photograph — the three cars, plus a front wheel, a traffic light, a palm tree and bare road — judged three ways.\n\nAll three score **6 / 7**, and all three are wrong about the same crop: a front wheel, described as *\"a car wheel and part of the vehicle's body\"*.\n\nEqual on accuracy. Not equal on what you can do next. Only the graded answer was **unsure** — 0.85, against a confident yes from the other two — and the real cars sit at 0.98 and above. On this tiny sample, 0.9 happens to separate the wheel from the cars, so moving the threshold there makes that column 7 / 7 with nothing else lost. That is a threshold picked by looking at the same seven crops it is scored on, which is not evidence of anything except that the knob exists. A yes/no has no knob at all.\n\nThat is the whole argument for the extra call, and it rests on seven crops of one photograph. It is an anecdote, not a benchmark. But the shape holds: **if your price is graded, your judge has to be.**\n\nNot the judging. Ten questions judged in parallel came back in about **600 ms** for a fraction of a cent, because a System One model bills input tokens and emits no prose.\n\nWhat costs you is the rate limit. Three image crops took **38 seconds**, because the gateway team I was on allows five vision calls a minute. Plan the queue, not the tokens.\n\nAnd the failure that would actually keep me up is neither. It is the provider going quiet between *charge* and *charged*, leaving a charge that is neither — which is the part I did not have to solve. The ledger records the reservation and the settlement separately, and reconciliation asks the provider afterwards what really happened. Once, never twice.\n\nThe judge never sees the image. It sees a sentence a vision model wrote about the image, so its calibration applies to *\"is this sentence a car\"* and not to *\"is this region a car\"*. If the description is wrong, the judge is confidently wrong. The visual-to-language hop is unmeasured, and it is the weakest link in the chain.\n\nTwo ways to close it, neither done: take two descriptions per crop and let disagreement cost the seller, or find a route where the vision model itself returns a distribution.\n\nOutcome pricing is not new — support tools have billed per resolution for years. What I have not seen is the buyer being handed the arithmetic: the distribution the price was averaged over, the grounding score that discounted it, the confidence, the model version that decided, and the threshold it was measured against.\n\n```\n\"pricing\": {\n  \"charged\": \"$0.01739\", \"authorized\": \"$0.05\",\n  \"judgedBy\": \"typesafe-ai/jev\", \"confidence\": 0.82,\n  \"reason\": \"depth 0.00 / 0.12 / 0.88 → $0.047 expected, × 0.37 grounded\"\n}\n```\n\nA price nobody can check is a price nobody trusts. And it is what a refund argument is about later, so it had better be written down at the time.\n\nThe whole route is twelve lines, and three of them are the payment layer:\n\n``` js\napp.post('/detect', tollstile(toll.price(upTo('$0.20'))), async (c) => {\n  const found = await sam3(image, concept);                 // proposes, and scores itself\n  const kept = await secondOpinion(image, concept, found);  // someone else's opinion\n  const payment = c.get('payment');\n\n  if (kept.length === 0) return c.json({ charged: '$0.00' }, 422);  // the hold is released\n\n  await payment.fulfill({ amount: price(kept) });           // settle what survived\n  return c.json({ detections: kept, pricing: explain(kept) });\n});\n```\n\nThe pricing does not care which compatible rail moves the money: x402's `upto` scheme settles the computed amount here, and any other variable-amount rail plugs into the same handler. Fixed-amount rails — MPP's card charges among them — are excluded from a route priced this way, and the gate says so at definition time rather than at settlement. The judge never learns which rail paid, and should not.\n\nEverything above runs, and you can poke it without an account or a wallet: [demo.tollstile.com/jev](https://demo.tollstile.com/jev) prices questions this way, and the detection example is [on GitHub](https://github.com/tollstile/Tollstile/tree/main/examples/detection-pricing).\n\nIf you try it on your own images, measure the thing I could not: how often the judge agrees with a person, over enough cases to mean something. Then publish that number next to your price.", "url": "https://wpnews.pro/news/sam-3-found-the-cars-jev-decided-what-they-were-worth", "canonical_source": "https://dev.to/yosh102/sam-3-found-the-cars-jev-decided-what-they-were-worth-25g7", "published_at": "2026-09-20 07:45:36+00:00", "updated_at": "2026-09-20 07:54:39.233997+00:00", "lang": "en", "topics": ["ai-products", "ai-tools", "ai-agents", "ai-infrastructure"], "entities": ["Jev", "TypeSafe", "Vercel AI Gateway"], "alternates": {"html": "https://wpnews.pro/news/sam-3-found-the-cars-jev-decided-what-they-were-worth", "markdown": "https://wpnews.pro/news/sam-3-found-the-cars-jev-decided-what-they-were-worth.md", "text": "https://wpnews.pro/news/sam-3-found-the-cars-jev-decided-what-they-were-worth.txt", "jsonld": "https://wpnews.pro/news/sam-3-found-the-cars-jev-decided-what-they-were-worth.jsonld"}}