# A DeepSeek vision recipe that retracts its own headline table on the same page

> tonyd2wild/DeepSeek-v4-Flash-Vision-Exp-DSpark-1M-NVFP4-KV-2x-DGX-Spark is a two-node serving recipe for DeepSeek's experimental vision checkpoint, with launcher scripts, patch files and a preflight check. It is unusually honest about itself: the page prints a measurements table and then states the figures are not current, one missing patch silently halves speculative acceptance, and two image-quality deviations are still unfixed.

**tonyd2wild/DeepSeek-v4-Flash-Vision-Exp-DSpark-1M-NVFP4-KV-2x-DGX-Spark** — DeepSeek V4 Flash DSpark 1M NVFP4 KV recipe for 2x DGX Spark

- Repository: https://github.com/tonyd2wild/DeepSeek-v4-Flash-Vision-Exp-DSpark-1M-NVFP4-KV-2x-DGX-Spark
- Stars: 493 · Forks: 60
- Language: Python
- License: MIT
- Published: 2026-09-17 · Updated: 2026-09-17 · Language: en
- Canonical page: https://hysenlabs.com/projects/tonyd2wild-deepseek-v4-flash-vision-exp-dspark-1m-nvfp4-kv-2x-dgx-spark

## The measurements table is retracted by the page that prints it

The most striking thing about this recipe is that its own numbers carry a warning. The table near the top reports a key-value pool of 2,904,519 tokens occupying 18.18 gibibytes, a context of 1,500,000 per request, a vision round trip of 1.03 seconds for a 336 by 336 image with a 26 token answer, and correct colour and position answers in both image orientations. Then the paragraph underneath says the key-value and context figures were measured at the 1.5 million setting and are not current, and points to a separate file for the validated profile.

The later serving section gives the current figures, and they do not move the way the first pair suggests. The validated profile is a context of 1,048,576 with a pool of 2,790,000 tokens, and that pool occupies 19.12 gibibytes, which is fewer tokens in more memory than the retracted table reported. A shorter context reserving fewer tokens per request would not normally cost more memory, so the two figures together are not obviously reconcilable from the page alone.

The page also declares itself non-authoritative twice: it names a separate current-recipe file as the source of truth rather than this page, and it says everything else is reference material with history destined for an archive directory that is described as coming and does not yet exist.

## One missing patch halves draft acceptance without saying so

The recipe has a known silent failure mode, and it has built a script around it. The header carries a preflight instruction to check a fourth patch on both the head and the worker container before trusting any number, and it hands you the command:

```bash
./scripts/check-patch4.sh <head-container> <worker-container>
```

The reason is stated plainly further down: the vision port dropped that patch once and cost roughly half the decode speed.

The sharpest statement of the mechanism is in the speculative decoding section. An earlier version of the branch recommended three speculative tokens. That A/B was run without the fourth patch mounted, and the mount silently halves the acceptance of the drafted tokens. With the patch present, five tokens wins on a count to 300 by a third with code quality neutral. So the earlier recommendation was not merely superseded, it was measured under conditions that invalidated it, and the page says so instead of quietly changing the number.

There is a related loose end the page flags rather than resolves: this release moved the number of prediction layers from one to three, which the author gives as the reason the drafter deserves another look, and then declines to recommend three tokens anyway because of the patch interaction above. That is a change to the draft model with the tuning question left open.

## The vision checkpoint reports the text architecture and carries 316 tensors

The reason a port was needed is specific and checkable. The serving engine's class for this model family is the text-only one. The vision checkpoint reports the same architecture string as the text model, so anything that dispatches on that string sends it to the wrong class, and the checkpoint carries 316 tensors that class has nowhere to put. The documented result is not a degraded load but a failure to load at all.

The approach taken was to run the model's own vision components inside the engine rather than beside it, and the page rules out the two shortcuts explicitly, saying this is not a sidecar and not a vision-language proxy. The model's own 32-block encoder and its aligner run in the same process, so image input goes through the real architecture rather than a translation layer.

The upstream situation is reported as the reason this was necessary: DeepSeek shipped a reference implementation and described it as not being a production serving engine. Which means the gap between what the model publisher tested and what a deployment needs was closed by someone outside the model publisher, and the repository points to its own write-up for a list of twelve blockers along with the port itself.

## Two unfixed deviations affect image quality and neither affects text

The recipe names its own remaining defects, which is worth more than a clean claim would be. Two deviations from the reference implementation are documented and explicitly not fixed: bidirectional attention inside image spans, and a modality-specific routing bias for the mixture-of-experts layers. Both are stated to affect image quality and neither is stated to affect text.

The page goes further than listing them. It tells the reader to read that section before quoting the recipe against benchmarks, which is a direct instruction that the vision numbers should not be treated as a characterisation of the model.

That is consistent with the rest of the document. The text-only checkpoint from July is described as unchanged and fully supported, and the reason the same patch files work for both is that every patch is guarded on a layer count specific to the vision variant. So the text path is the settled one and the vision path is the experimental one, in the repository's own framing, and the two are kept apart by a single conditional rather than by separate files.

The sample answer quoted for an image is correspondingly modest: a two-colour image described with the colours named and their left and right positions stated.

## The runtime is a locally built image running an unreleased engine build

The serving stack is not something you can pull. The runtime image is identified by a long local tag that includes the word probe, and the engine inside it is named as a release candidate followed by a development counter and a commit hash. So the recipe depends on a build of the serving engine that does not correspond to a published release, and the repository ships the shell script that produces that image.

The page is explicit about the consequence: this image family predates the baked-in patches, so the patches have to be supplied from outside it as bind-mounts. In other words the image is the base layer and every behavioural fix arrives at run time from files on the host.

That is a coherent way to iterate on an unreleased engine, and it is also why the recipe cannot be reproduced from a tag alone. You need the local image, the patch files, the model checkpoint pinned to a specific commit, and a compose file that wires them together. The model is pinned by commit hash rather than by name, which is the right call for reproducibility and is one more artefact you have to fetch rather than resolve.

## Bind-mounted files never reach the worker, so both nodes need staging

The mount list is where the operational footgun lives, and the page warns about it before showing it. Each source file has to be staged on both nodes first, because the start script does not copy bind-mounted files to the worker. Skip that and the head container starts with a patched file the worker does not have.

The mounts themselves replace files inside the engine's installed package. One of them puts a scheduler file from the host over the copy in the environment's site-packages directory:

```bash
-v /var/tmp/patch3-scheduler.py:/opt/env/lib/python3.12/site-packages/vllm/v1/core/sched/scheduler.py:r
```

The trailing marker makes it read-only, and the page attributes the file to a recipe overlay path inside the repository. So the fix is not a configuration change; it is a different scheduler module, mounted over the one that shipped.

The read-only marker is worth noting because it is the only thing standing between a stray write and a container whose Python package tree has been mutated. The same staging requirement applies to every other mount, and the preflight script that checks the fourth patch takes both a head container and a worker container as arguments, which at least forces you to name both when you check.

## The throughput figures came from a community checkpoint, and three files claim to be the default

The speed numbers have a provenance worth reading. The official July text checkpoint is reported at 78 tokens per second peak and around 55 typical, and it is stated to require the fourth patch, without which you get roughly half the speed at unchanged output quality. The higher figure, 84.3 tokens per second peak, belongs to a different and unofficial preview checkpoint, and the page says that everything below that section was measured on it and still stands.

So the best number in the document comes from the community checkpoint and the official one is slower, and the text-only baseline sits between those two figures rather than matching either.

The root of the repository tells the same story about process. Three separate documents present themselves as the configuration to follow, one general default file, one vision-specific default file and the current-recipe file that claims to be the source of truth. Alongside them sit dated snapshots as root entries rather than in a changelog: two speed reports from mid and late July, a runtime backoff note from the same late-July date, and a directory whose name is a verification date. Five of the root entries are shell scripts that differ only by a verb, covering logs, smoke test, start, status and stop.

## Conclusion

Use it if you have two of these machines, a local build of an unreleased serving engine, and the appetite to bind-mount patched files over the installed package. Do not quote its numbers without reading the retractions, because the key-value pool and context figures at the top were taken at a setting the page itself marks as no longer current. Before you start, confirm four things: that the preflight script passes on both nodes, since the patch it checks fails silently and halves speculative acceptance when absent; that every bind-mounted file is staged on both nodes, because the start script does not copy them to the worker; that the two documented deviations that affect image quality are acceptable for your use; and that CURRENT.md rather than the README is the version you are following.

## FAQ

### What is the DeepSeek vision recipe in this repository?

A self-contained recipe for serving DeepSeek's experimental vision checkpoint on two DGX Spark machines with a tensor parallel size of two, speculative decoding and an experimental NVFP4 key-value cache. It ships launcher scripts, patch files, a compose file and a preflight script, and it names a separate current-recipe file as its source of truth rather than the README.

### Why does the DeepSeek vision checkpoint need a port to run?

Because the vision checkpoint reports the same architecture string as the text-only model class while carrying 316 tensors that class has nowhere to put, so it fails to load at all. The port runs the model's own 32-block vision encoder and aligner inside the serving engine rather than using a sidecar or a proxy.

### Why does this DeepSeek recipe insist on checking a patch first?

Because a missing mount of that patch fails silently rather than erroring. An earlier comparison of speculative token counts was run without it and silently halved the draft's acceptance, which is why the recipe now specifies five speculative tokens and ships a preflight script that takes both the head and the worker container.

### Are the benchmark numbers in this DeepSeek recipe current?

Not the table near the top. The page states that the key-value pool and context figures there were measured at a 1.5 million token setting and are not current, pointing instead to a 1 million token validated profile, and it separately notes two unfixed deviations from the reference that affect image quality but not text.

## Sources

- [Issues](https://github.com/tonyd2wild/DeepSeek-v4-Flash-Vision-Exp-DSpark-1M-NVFP4-KV-2x-DGX-Spark/issues)
- [License: MIT](https://github.com/tonyd2wild/DeepSeek-v4-Flash-Vision-Exp-DSpark-1M-NVFP4-KV-2x-DGX-Spark/blob/main/LICENSE)
- [README](https://github.com/tonyd2wild/DeepSeek-v4-Flash-Vision-Exp-DSpark-1M-NVFP4-KV-2x-DGX-Spark/blob/main/README.md)
- [tonyd2wild/DeepSeek-v4-Flash-Vision-Exp-DSpark-1M-NVFP4-KV-2x-DGX-Spark on GitHub](https://github.com/tonyd2wild/DeepSeek-v4-Flash-Vision-Exp-DSpark-1M-NVFP4-KV-2x-DGX-Spark)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tonyd2wild-deepseek-v4-flash-vision-exp-dspark-1m-nvfp4-kv-2x-dgx-spark
