Ask a frontier model to click a button in a CRM and it will usually get close. Close is the problem.
We put nine frontier models on 139 held-out screens from a live customer-service CRM and asked each one where to click. To count as correct, the box a model predicted had to genuinely overlap the target, which is the standard object detection has used for a decade. The strongest model in the field, OpenAI's newest at the time, got 44.6% of them right.
Our 4B model got 92.09%.
Twice the accuracy of the best frontier model money can buy, on a task that amounts to clicking the right thing in a business application. It was trained on fewer than a thousand datapoints.
The score is not the part we are proudest of, though. The customer running that model does not train a four-billion-parameter model, does not store one, and does not serve one. They train a small, specific set of weights inside a base model that already exists, and what they end up owning is an 80 MB file. We call it a slice.
Working out which weights belong in that file, and building something a production server can actually run, took three problems worth of work:
Solve all three and a customer's model goes from 8.88 GB to 80 MB, and a hundred of them run on one GPU at once. That is the business.
A 4B vision-language model has hundreds of weight tensors. Fine-tuning all of them gives you a second 8.88 GB model per customer, which is exactly the thing we are trying to avoid. So the first question is simple: which tensors actually need to change?
There is no principled answer in the literature, and the intuitive answers are wrong. Retraining the vision encoder, the conventional prescription for a new visual domain, is close to redundant: the encoder already sees what it needs to. Retraining the language side moves nine tenths of the weights and makes the model worse. The parameters that matter sit in a narrow band where the visual representation crosses into the language model, and you cannot find that band by reasoning about it. We found it by defining a taxonomy of candidate placements and training every one of them, on multiple tasks, across two model families, then measuring which held up.
The answer is a set of layers we call the junction set, between 1.5% and 2% of the model's parameters. We do not repeat that search per customer. Every new specialization trains the same known set of layers, which is why onboarding a customer is a training run rather than a research project.
Where the cut sits matters as much as how big it is. Cut at the junction and the expensive majority of the model remains identical across every customer. Cut a few layers deeper and that shared computation disappears. That placement is not just a training result; it is the architectural decision our inference engine is built around. Find the right 2%, and suddenly hundreds of specialized models can share the other 98%.
Knowing which tensors to take is half of it. Getting them out, and back in, without perturbing the model is the other half, and it is a correctness problem rather than a performance one.
A slice is not a compressed approximation of a fine-tune, and it is not a patch applied on top of the base at inference time. It is the actual trained tensors, extracted directly from the PyTorch model state. Applying one writes those tensors directly into the corresponding parameter buffers with identical shape and dtype: no merge, no reconstruction, no arithmetic. The 8.88 GB base never moves. The ~80 MB specialization does. Restore the original tensors and the model returns bit-for-bit identical to the untouched base. We verify this at the tensor level and across a full base → slice → base decoding cycle.
That invariant is what makes our inference engine possible. The base model stays permanently resident on the GPU; switching customers means swapping ~80 MB of specialization state instead of loading another 4B model. One customer's slice leaves nothing behind for the next, and rolling back a customer means changing an artifact, not redeploying a model.
We test the opposite direction too: a swap must actually change behaviour. Slices trained on deliberately different objectives produce different answers to identical inputs, while restoring the base reproduces its original behaviour. Apply changes behaviour. Restore recovers the base exactly. Either direction takes 16 ms.
This is where most of the engineering went.
Existing inference engines are built to serve one set of model weights extremely efficiently. We needed an engine that could serve hundreds of specialized models as if they were one. So we built our own inference engine on top of vLLM, keeping its high-performance CUDA execution, continuous batching and KV-cache machinery, but changing the fundamental unit of model state from a checkpoint to a slice.
The 8.88 GB base is loaded once and stays resident. Each customer's ~80 MB slice is a versioned, self-describing artifact containing the tensor payload and a manifest pinning its base revision, placement, shapes, dtypes and content hash. Our runtime validates and stages these slices, while a slice-aware scheduler decides which specialization needs to be active for each request. We swap specialization state, not models.
That changes the economics completely. A normal serving stack sees a hundred fine-tunes and thinks a hundred models. Our engine sees one model and a hundred 80 MB pieces of state. The expensive weights, execution engine and GPU memory are shared; only the tiny part that makes the model customer-specific moves.
That is the payoff of building the engine ourselves on top of vLLM: specialization becomes a runtime primitive rather than a deployment primitive. The base stays hot. The slices move. And adding another customer starts looking less like deploying another model and more like scheduling another request.
The benchmark is UI grounding on a real product: the ticket-disposition workflow of a customer-service CRM, captured live while agents worked. One sample is one decision point. The input is a screenshot of the page as it looked immediately before the agent acted, plus an instruction like "open 'Assign To' and pick an agent option" or "type into 'Search sub folder' to filter L2/L3 suggestions". The model answers with JSON: an action, click or fill, and a bounding box on a 0–1000 scale.
These are not big obvious buttons. Twelve distinct intents span 269 distinct selectors, and the held-out set alone contains 42 distinct target boxes across 18 tickets: suggestion chips inside a three-level disposition folder tree, an autocomplete field that only exists after a parent selection, a callback checkbox, the tab that reveals a conversation's email body. Several targets are roughly 29 by 56 units in a 1000-unit space. Landing a cursor somewhere in the right neighbourhood of one is easy to do by accident, which is why we do not score it that way. A prediction counts only if the box it names substantially covers the element it is supposed to hit.
We split by ticket rather than by sample, smallest ticket first, so that no screenshot from a training flow leaks into evaluation as a near-duplicate.
Correct UI targets on the first attempt
139 held-out screens, same 1024px images, same prompt convention. A prediction counts only if its box overlaps the true target by at least half (IoU≥0.5).
| Model | Correct (IoU≥0.5) |
|---|---|
| Runtime Labs SVLM | 92.09% |
| gpt-6-astra | 44.60% |
| GLM-5.3-Flash | 30.22% |
| Kimi-K3 | 28.78% |
| Claude Fable 5.1 | 28.06% |
| GPT-5.6-sol | 25.90% |
| Gemini 3.1 Pro Preview | 21.58% |
The rest of the picture, and it is consistent:
| Model | Correct (IoU≥0.5) | Mean IoU | Tight fit (IoU≥0.75) | Action prec. |
|---|---|---|---|---|
| Runtime Labs SVLM (4B, self-hosted) | 92.09% | 0.882 | 79.86% | 100.0% |
| gpt-6-astra | 44.60% | 0.529 | 34.53% | 82.6% |
| GLM-5.3-Flash | 30.22% | 0.298 | 12.95% | 80.6% |
| Kimi-K3 | 28.78% | 0.251 | 11.51% | 79.3% |
| Claude Fable 5.1 | 28.06% | 0.363 | 13.67% | 81.3% |
| GPT-5.6-sol | 25.90% | 0.374 | 22.30% | 82.0% |
| Gemini 3.1 Pro Preview | 21.58% | 0.341 | 16.55% | 85.3% |
| Claude Opus 4.8, Gemini 2.5 Computer Use and DeepSeek-V4-Flash-0731 could not be scored on overlap: the first two return a click point rather than a box, and the third rejects image input entirely. | ||||
Mean overlap is the number we find most telling. Ours is 0.882, so when our model names a box it is essentially the right box. The best competitor manages 0.529 and most of the field sits between 0.25 and 0.37. A figure around 0.3 describes a model that knows roughly which region of the screen you mean and has no idea where the element ends. For a demo that is fine. For an agent that has to click twenty-three times in a row with nobody watching, it is not.
Action precision points the same way. Ours is 100%: on every response that parsed, the model correctly told "click this" apart from "type into this". The field ran 79% to 85%. One in five actions being the wrong kind is not a rounding error in a workflow that has to run to completion.
The training set is under a thousand labeled screens. That is the whole thing. No synthetic augmentation, no contractor pipeline, no reinforcement learning stage. It is the byproduct of instrumenting a workflow that agents were already performing, captured as a side effect of the product running.
What more data bought
Same model, same training setup, same scoring. Training set scaled roughly 8×.
| Training examples | Correct (IoU≥0.5) |
|---|---|
| ~100 | 62.50% |
| under 1,000 | 92.09% |
Two things stand out. A few hundred examples already beat every hosted model in Figure 1, which means the bar for onboarding a customer is a dataset they can produce in a week of ordinary operation rather than a labeling project. And accuracy was still climbing when we stopped adding data, so we are limited by how many screens we have and not by the size of the model. The fix for the next customer is more of their screens, not a bigger model.
A 4B base sits in 8.88 GB resident. Each customer's slice adds about 80 MB. The base loads once and is never duplicated, so the fleet's memory footprint is set by the model, not by how many customers you have. This is the entire commercial argument, and it only works because the cut is in the right place.
GPU memory as the slice fleet grows
One shared base plus per-customer slices, against one independent fine-tuned model per customer.
| Customers | Shared base + slices | Independent models |
|---|---|---|
| 1 | 8.96 GB | 8.88 GB |
| 10 | 9.68 GB | 88.8 GB |
| 50 | 12.9 GB | 444 GB |
| 100 | 16.9 GB | 888 GB |
Co-residency would be worth having on its own. What makes it fast is the shared remainder: because every slice is cut above the language model, the expensive part of the computation is identical for all of them and runs once for the whole batch. Different customers, different screens, different slices, one pass, different answers. Verified end to end, with nothing swapped between requests.
That is also where the latency goes. Served one at a time, each specialist costs about 1,430 ms. Batched over the shared base, total time barely moves as the batch grows, so per-request latency falls to 156 ms at a batch of ten. Nine customers get served in the time the tenth would have taken alone.
A real disposition task runs about 23 steps. Priced at published rates from token counts captured off live API responses, that is what each model costs and how long it takes.
Cost of one 23-step workflow
Token usage captured from live responses, priced at each provider's published September 2026 rate.
| Model | $ / call | $ / 23-step run |
|---|---|---|
| Runtime Labs SVLM | $0.0000094 | $0.00022 |
| GLM-5.3-Flash | $0.00042 | $0.010 |
| Gemini 2.5 Computer Use | $0.0026 | $0.060 |
| Gemini 3.1 Pro Preview | $0.0051 | $0.117 |
| Claude Opus 4.8 | $0.0113 | $0.260 |
| Kimi-K3 | $0.0123 | $0.282 |
| gpt-6-astra | $0.0135 | $0.310 |
| Claude Fable 5.1 | $0.0222 | $0.510 |
| GPT-5.6-sol | $0.0226 | $0.521 |
Speed follows the same shape, for a different reason. Hosted models burn wall-clock on reasoning tokens: GPT-5.6-sol averages 620 output tokens for a bounding box, GLM 1,517, Kimi 2,028. Ours emits a short JSON object and stops.
| Model | Avg output tokens | Latency / call | 23-step flow |
|---|---|---|---|
| Runtime Labs SVLM | ~30 | 0.40 s | 9.2 s |
| gpt-6-astra | 108 | 4.07 s | 1 min 33 s |
| Gemini 2.5 Computer Use | 24 | 5.78 s | 2 min 12 s |
| Claude Fable 5.1 | 146 | 6.15 s | 2 min 21 s |
| Gemini 3.1 Pro Preview | 227 | 11.83 s | 4 min 32 s |
| GPT-5.6-sol | 620 | 16.44 s | 6 min 18 s |
| GLM-5.3-Flash | 1,517 | 25.2 s | 9 min 40 s |
| Kimi-K3 | 2,028 | 38.9 s | 14 min 55 s |
Nine seconds against a minute and a half is the difference between an agent a human can supervise and a batch job. The two open-weight reasoning models are also wildly variable, with single calls of 58 to 82 seconds sitting next to others under ten.
Because the base is shared, the fixed cost divides across the fleet. This is the one thing per-token pricing structurally cannot do.
| Customers on one card | GPU memory | Cost / customer / month |
|---|---|---|
| 1 | 8.96 GB | $803 |
| 10 | 9.68 GB | $80 |
| 50 | 12.9 GB | $16 |
| 100 | 16.9 GB | $8 |
A hundred customers as independent fine-tuned models would need 888 GB of weights, so twenty cards before anyone gets an answer, or roughly $161 per customer per month against our $8. And that is only the memory bill, with none of the batching economics.