Dopp

Every request is training data. Why a proxy is the right shape.

Dopp · 2026-09-24

A fine-tuned model needs examples with answers. You already produce hundreds a day; you just don't keep them. Dopp sits between your app and whatever decision API answers today (Jev, Kev, GLiNER, simple-jev, your own) and keeps every one.


Ask anyone who has tried to fine-tune a classifier what stopped them and the answer is rarely the training. It's the data. You need examples, in your real format, with correct answers, and enough of them. So you start a labelling project, or a spreadsheet, or a script that samples production logs, and three weeks later you have four hundred rows and a Slack thread arguing about edge cases.

Meanwhile your app has been sending its decision API (Jev in this article, but any Jev-compatible endpoint) the exact examples you need, in the exact format, and getting correct answers back, all day.

The shape that fixes it

Dopp is a proxy. Your app sends its request to us; we send it to Jev; Jev's answer comes back to your app unchanged. On the way through, the request and the answer are recorded. That is the whole trick, and it's why the data problem disappears:

One list, not two systems

There is no separate "dataset" in Dopp. There is one list: Requests. Everything on it is a request with a source tag. Your app's traffic, tagged with the key that sent it. A playground run you typed. Rows pasted from a spreadsheet. Rows pulled from a public dataset that looks like your traffic. Rows a model wrote to fill a gap. Same shape, same list, same rights.

Any request on the list can be answered by anyone: Jev, any version of your model, a copy running in your browser, or you. All the answers stay on the request. One of them is what the request trains on: Jev's by default, or the one you pick.

Training then means choosing a cohort from that list. Your app's requests from the last month. Only the ones where your model and Jev disagreed. Only the dataset rows. The count and the price update as you choose.

What this replaces

The old wayWith a proxy
Sample logs, reconstruct requestsThe request is the record
Label by hand or script a teacherJev labels every request as it happens
A dataset folder nobody updatesA list that grows with your product
Eval scriptsA held-out number per version, and live agreement on traffic
Wire the model inChoose who answers, per model, in one switch

What you give up

Nothing you had. Jev still answers. The one thing that changes is the URL, and everything your app receives is the same JSON with one extra field saying who answered.

Questions

Is my data used to train anything except my model?

No. Each model's requests train that model only.

What if Jev is down when a request comes in?

The request is still recorded, marked as waiting for a label, and labelled later with one click.

Can I remove requests?

Yes, individually or by source, and you can leave any request out of training without deleting it.

Can I pick a different answer than Jev's?

Yes. Open the request, pick any answer on it, or set your own. That's what the next training uses.

What Dopp is. Dopp is a drop-in proxy for any Jev-compatible decision API: TypeSafe's Jev, Kev, GLiNER, simple-jev, or your own endpoint that answers typed questions about a state. You change one URL; every request still gets your current API's answer and is recorded as training data. When you have enough, one button trains a small open model on your own requests, measures it against the API it learned from on requests it never saw, and lets you choose who answers: the API, your model with the API as backup, or your model alone, on our servers, or offline, even in your users' browsers.