-
Notifications
You must be signed in to change notification settings - Fork 280
feat(ds4v): dflash vision serving runtime with bounded IQ85 conversion #722
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
131 commits
Select commit
Hold shift + click to select a range
af84f44
feat(ds4v): MIX converter for Vision-Exp safetensors plus codec test
marcelormendes a53acc9
test(ds4v): reproduce collapsed fitter and repair clean-build test wi…
marcelormendes 9e403cd
fix(ds4v): separate collapsed BF16 codebook levels and stamp repairs
marcelormendes 07e3284
test(ds4v): replay an individual source expert through calibration
marcelormendes fd5a994
feat(ds4v): export lossless vision projector GGUF
marcelormendes ef64f62
chore(ds4v): trim exporter test commentary
marcelormendes c563fbf
feat(ds4v): implement isolated BF16 vision tower runtime and CPU probes
marcelormendes dfa9719
fix(ds4v): reserve measured scratch before graph allocation
marcelormendes 076de6e
test(ds4v): compare parent stages and reject malformed projectors
marcelormendes 8758e09
fix(ds4v): pin diagnostic snapshots independently of tensor views
marcelormendes 8bac576
test(ds4v): isolate block kernels with native inputs and BF16 ULP met…
marcelormendes d26f1c9
test(ds4v): verify transactional reload and strict tensor table rejec…
marcelormendes da3fa6e
test(ds4v): measure parent BF16 Math SDPA sensitivity
marcelormendes 1cf2b26
test(ds4v): identify actual parent SDPA dispatch and parser rejection
marcelormendes 4573248
fix(ds4v): preserve parent Math SDPA operand scaling order
marcelormendes 2bfba86
test(ds4v): cover multihead attention and BF16 halfway rounding
marcelormendes a23d7cb
docs(ds4v): record CPU qualification and remaining numerical limits
marcelormendes 8e28cfb
test(ds4v): isolate vendored GGUF reader import
marcelormendes 782bf15
Revert "test(ds4v): isolate vendored GGUF reader import"
marcelormendes 5845ed5
test(ds4v): cover complete image token budget
marcelormendes 5bf705e
fix(ds4v): bound the complete image block before encoding
marcelormendes e30c1b0
test(ds4v): specify image routing and raw visibility policy
marcelormendes a238e8e
feat(ds4v): add exact RGB vision preprocessing
marcelormendes 60ce979
feat(ds4v): add bounded image routing and raw visibility helpers
marcelormendes 0306a3f
test(ds4v): specify bounded image data URL transport
marcelormendes e09f8da
feat(ds4v): parse bounded image data URLs and preserve content order
marcelormendes 5da9272
docs(ds4v): record image policy contracts and CPU proof
marcelormendes 4acc20d
docs(ds4v): record image transport contract and proof
marcelormendes 1fa0a2e
feat(ds4v): add bounded JPEG and PNG decode gate
marcelormendes 127bb61
build(ds4v): prepare explicit HIP tower qualification probe
marcelormendes 2af9862
test(ds4v): bound image message nesting before JSON copy
marcelormendes 60af19f
fix(ds4v): isolate libjpeg probe build
marcelormendes 25f6105
fix(ds4v): validate message depth and placeholders before copying
marcelormendes 724e4ef
chore(ds4v): keep codec probe dependency focused
marcelormendes 4bf7270
fix(ds4v): reuse server HIP compatibility definitions in probe
marcelormendes 6a2b381
test(ds4v): cover RGB conversion and EXIF handling
marcelormendes a28abfd
test(ds4v): capture decoder review regressions
marcelormendes c24e823
test(ds4v): specify transactional image prompt preparation
marcelormendes 6d28845
fix(ds4v): bound PNG inflate and match source decoding
marcelormendes a2489a4
feat(ds4v): prepare owned image prompts with checked expansion
marcelormendes 859f2f7
test(ds4v): prove prompt ownership and document CPU qualification
marcelormendes edb3b0e
docs(ds4v): correct LodePNG notice
marcelormendes 52c6ba7
feat(ds4v): compose verified prompt and preprocessing units
marcelormendes 101f69c
feat(ds4v): compose verified image transport
marcelormendes 20ae935
feat(ds4v): compose verified image routing policy
marcelormendes 1096fab
test(ds4v): specify CPU image preparation composition
marcelormendes a5aa8a3
test(ds4v): link existing Jinja unicode helper in composition probe
marcelormendes 15a10c5
feat(ds4v): compose accepted image preparation in CPU probe
marcelormendes 0065158
test(ds4v): freeze bounded CPU preparation composition proof
marcelormendes a353f1e
test(ds4v): isolate biased linear BF16 boundary regression
marcelormendes 43652c1
test(ds4v): allocate probe scratch after size preflight
marcelormendes 8bec967
fix(ds4v): retain biased linear products until final BF16 round
marcelormendes be8b0f1
fix(ds4v): scope biased product workaround to GPU backends
marcelormendes 983be86
test(ds4v): compare shared vision linears to frozen HIP source fixtures
marcelormendes ee085f3
fix(ds4v): use explicit HIP Lt fused BF16 bias operation
marcelormendes b3ca801
fix(ggml): align RPC ABI guard and reject HIP op before CPU dispatch
marcelormendes 969318c
test(ds4v): enforce local HIP op contract and preserved CPU graph
marcelormendes add215f
test(ds4v): verify full-tower Lt dispatch and retained workspace acco…
marcelormendes 6137f43
test(ds4v): require explicit HIP capability in full qualification probe
marcelormendes 3191e7e
fix: reserve buffers in DS4V source probe before allocation
marcelormendes f64d945
fix: match source HIP math for unbiased vision projections
marcelormendes 53bf07d
test: retain the generic linear rounding oracle
marcelormendes 4e601d0
Match original DS4V HIP vision normalization arithmetic
marcelormendes ed661d0
Verify both HIP vision dispatch counters in source probes
marcelormendes 4102da0
Match HIP vision rotary tables to source GPU math
marcelormendes 686285f
Match HIP vision softmax and attention products to source order
marcelormendes e1ccfbe
Merge qualified DS4V HIP vision encoder into image integration
marcelormendes edc61af
test(ds4): expose BF16 norm affine compatibility regression
marcelormendes c574604
fix(ds4): widen BF16 norm vectors at the affine boundary
marcelormendes 03edb38
feat(ds4v): integrate bounded image prompts into HIP serving
marcelormendes 4f388d1
Fix DS4V startup accounting for reclaimable HIP memory pools
marcelormendes 14754e4
Revert "Fix DS4V startup accounting for reclaimable HIP memory pools"
marcelormendes 91cc90a
Reclaim copied mmap source pages during hybrid GPU loading
marcelormendes 439c8c3
Release copied model file cache during hybrid loading
marcelormendes 6914f9d
Stage HIP dense uploads through bounded host scratch
marcelormendes 5dc0fdd
Release cached GPU temporaries before image admission
marcelormendes dbaeb1e
Trim cached GPU pools before bulk heterogeneous prefill
marcelormendes 5296836
Release bulk prefill arenas before ordinary hybrid decode
marcelormendes 5fe8713
Bound paired decode attention cache to the current shape
marcelormendes be48d1b
fix(deepseek4): trim returned pool blocks between long prefill chunks
marcelormendes c76edfa
fix(deepseek4): acknowledge every HC worker job generation
marcelormendes 7e851cb
feat(ds4v): add bounded IQ85 quantization from original weights
marcelormendes 39c0791
docs(ds4v): record vision continuation status, decision log and IQ85 …
marcelormendes 6d762d8
Merge upstream/main (2f0eff05) into ds4v/quant85
marcelormendes c6030da
chore(ds4v): ship the vision qualification kit and reference receipts
marcelormendes 344713d
Merge main (e0048e019) into ds4v/quant85
94856ca
build(ds4v): make hipBLASLt optional for the HIP backend
6f784d4
chore(ds4v): drop research logs and evidence receipts
7651512
fix(ds4v): keep the text path identical to main without --mmproj
f248dfd
chore(ds4v): drop the rejected IQ85 recipe
212b3a6
test(ds4v): run the image unit tests in the main build, drop standalo…
c42b579
fix(ggml): correct the RPC op-count guard and shorten the vendor note
fa7ff8f
docs(ds4v): state the real verification status of image serving
633df96
fix(ds4v): scan for image markers only with a projector loaded; use m…
5c81edc
fix(server): leave image parts alone on backends without image input
02b510f
test(ds4): follow the generalised image-bias loader message
89fd666
fix(server): run image extraction and redaction only for an image-cap…
f9cc9e3
fix(ds4v): refuse --mmproj at startup when the build has no vision ops
37dfa4d
fix(ds4v): ignore the MTP block's image bias in the loader check; dro…
7d4d7ac
test(ds4): the loader no longer pins the DS4V layer count and vocabulary
4196a70
fix(ds4v): check for the vision ops before loading weights; report th…
13a5dae
build(ds4v): pass DOWNLOAD_EXTRACT_TIMESTAMP only on CMake >= 3.24
84591eb
build(ds4v): build the MIX converter only when its source is in the tree
a6e4e72
refactor(vision): move the model-independent image pieces out of deep…
57fe97d
feat(ds4v): load the image router bias from published GGUFs
8544d76
feat(ds4): load llama.cpp-converted DeepSeek4 GGUFs without deepseek4…
b91eea9
fix(ds4v): count the GPU driver's page pool in the image memory admis…
f023a8a
build(docker): build the DS4V vision ops in the ROCm image; keep the …
49cabab
feat(ds4v): image input on one GPU holding the whole model
b3409fc
fix(ds4v): review fixes - images outside the last prefill chunk, 16-b…
38e0cfd
Merge remote-tracking branch 'lucebox/main' into pr722-clean
12a0b29
build(vision): vendor lodepng instead of downloading it at configure …
d1d5be7
refactor(vision): share the Pillow-exact resize, add a projector file…
b52c981
feat(qwen35): image input for Qwen3.5 / Qwen3.8 through --mmproj
47e9259
Merge main: engine rename from dflash to luce
cbf8691
fix: review findings in the page pool estimate and the MIX converter
f18750f
fix(qwen35): write the request-visible vision config once, never snap…
8cca0e9
fix(vision): review findings in the Qwen image path, the projector re…
27b08e7
fix(vision): release the projector before its backend, smaller review…
47e4703
perf(qwen35): fused attention in the vision tower
901ef2d
docs(vision): Q8_0 projector measurement
c202867
perf(tools): quantize experts on every core in the MIX converter
6e5a0ca
fix(deepseek4): grouped output projection only for a quantized tensor
0a7ed37
fix(tools): name the MIX file after its checkpoint
61c07ff
docs(vision): DS4V on our own ROCMFP MIX conversion
6461136
feat(tools): write the MIX file with the shipped DeepSeek-V4-Flash re…
0d349b8
feat(tools): per-expert importance and embedded codebooks in the MIX …
a098ecd
docs(vision): DS4V MIX results with the shipped recipe
25735a3
docs(vision): Qwen3.8 results with the Lucebox model file and a Q8_0 …
b35966e
build(vision): fetch lodepng at a pinned commit instead of vendoring it
34d0b06
docs(vision): quick start from the published Lucebox files, LUCE_MMPR…
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # DeepSeek-V4 vision projector export | ||
|
|
||
| `server/tools/export_ds4v_mmproj.py` extracts the vision tower and aligner from | ||
| the verified DeepSeek-V4 parent model without loading or changing tensor values. | ||
| It requires only Python's standard library. The output is published as | ||
| [`DeepSeek-V4-Flash-Vision-Exp-mmproj-BF16.gguf`](https://huggingface.co/Lucebox/DeepSeek-V4-Flash-0731-ROCmFP3); | ||
| run the exporter only to rebuild it. | ||
|
|
||
| ```bash | ||
| python3 server/tools/export_ds4v_mmproj.py \ | ||
| /models/DeepSeek-V4-Flash-Vision-Uncensored \ | ||
| /models/ds4v-mmproj.gguf | ||
| ``` | ||
|
|
||
| The output path must not exist. The exporter validates `config.json`, the | ||
| safetensors index, all 267 required names and shapes, BF16 payload lengths, | ||
| source bounds, and shard paths before it writes. It publishes the finished file | ||
| atomically and removes its temporary file after any pre-publication failure. | ||
|
|
||
| The GGUF keeps every `vision.*`, `aligner.*`, and `image_*` name, shape, dtype, | ||
| and payload byte unchanged. `general.architecture` is `deepseek4_vision`. | ||
| Required `deepseek4.vision.*` metadata records the 32-block, width-1024, | ||
| 16-head tower; head width 64; patch size 14; intermediate width 2816; 2D RoPE | ||
| layout and theta 10000; ratio-3, width-9216 aligner; language width 4096; | ||
| vocabulary 129280; RMS epsilon `1e-6`; image token/pixel/aspect bounds; RGB | ||
| mean/std `0.5`; channel-major patches; N-layout recipe version 1 with | ||
| compression alignment 4; bottom/right aligner padding; channel-first unfold; | ||
| and exact GELU. | ||
|
|
||
| Run the focused synthetic suite with: | ||
|
|
||
| ```bash | ||
| python3 -m unittest -v server.tests.test_export_ds4v_mmproj | ||
| ``` | ||
|
|
||
| When NumPy is available, the suite also opens the result with llama.cpp's | ||
| vendored `GGUFReader`, independently of the exporter's writer. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,300 @@ | ||
| # Image input | ||
|
|
||
| The server accepts JPEG and PNG images through OpenAI chat completions when a | ||
| model is started with its vision projector, `--mmproj <file>`. Without | ||
| `--mmproj` nothing in the text serving path changes. | ||
|
|
||
| | Model | Decoder | Projector | Runs on | | ||
| | --- | --- | --- | --- | | ||
| | Qwen3.8-27B | [`Qwen3.8-27B-IQ4_XS-pure.gguf`](https://huggingface.co/Lucebox/Qwen3.8-27B-IQ4_XS-fast-GGUF) (any Qwen3.5 / Qwen3.8 dense GGUF works) | [`Qwen3.8-27B-mmproj-Q8_0.gguf`](https://huggingface.co/Lucebox/Qwen3.8-27B-IQ4_XS-fast-GGUF), or any published `qwen3vl_merger` mmproj | one GPU, any backend | | ||
| | DeepSeek V4 Flash Vision (DS4V) | [`DeepSeek-V4-Flash-Vision-Exp-ROCMFPX-MIX-STRIX.gguf`](https://huggingface.co/Lucebox/DeepSeek-V4-Flash-0731-ROCmFP3) | [`DeepSeek-V4-Flash-Vision-Exp-mmproj-BF16.gguf`](https://huggingface.co/Lucebox/DeepSeek-V4-Flash-0731-ROCmFP3) | HIP: a Strix Halo alone, or R9700 + Strix Halo | | ||
|
|
||
| **Status: experimental.** Both models answer image questions correctly end to | ||
| end; see each model's verification notes for what has and has not been | ||
| measured. | ||
|
|
||
| ## Quick start | ||
|
|
||
| Build the server as in the [README](../README.md#run-the-server). DS4V also | ||
| needs hipBLASLt at build time (`hipblaslt-dev` on ROCm; CMake prints | ||
| `hipBLASLt found: building the DS4V vision ops`). | ||
|
|
||
| ### Qwen3.8-27B on one GPU (R9700) | ||
|
|
||
| ```bash | ||
| hf download Lucebox/Qwen3.8-27B-IQ4_XS-fast-GGUF \ | ||
| Qwen3.8-27B-IQ4_XS-pure.gguf Qwen3.8-27B-mmproj-Q8_0.gguf --local-dir models | ||
| hf download Lucebox/Qwen3.8-27B-DFlash2-GGUF \ | ||
| Qwen3.8-27B-DFlash2-Q8_0.gguf --local-dir models | ||
|
|
||
| ./server/build-hip/luce_server models/Qwen3.8-27B-IQ4_XS-pure.gguf \ | ||
| --target-device hip:0 \ | ||
| --draft models/Qwen3.8-27B-DFlash2-Q8_0.gguf --draft-device hip:0 \ | ||
| --draft-block-size 16 --max-ctx 32768 \ | ||
| --cache-type-k q8_0 --cache-type-v q8_0 \ | ||
| --mmproj models/Qwen3.8-27B-mmproj-Q8_0.gguf \ | ||
| --port 8216 | ||
| ``` | ||
|
|
||
| About 21 GiB of VRAM at the peak of an image request. Text requests keep the | ||
| DFlash2 drafter; image requests decode without it. | ||
|
|
||
| ### DeepSeek V4 Flash Vision on a Strix Halo | ||
|
|
||
| ```bash | ||
| hf download Lucebox/DeepSeek-V4-Flash-0731-ROCmFP3 \ | ||
| DeepSeek-V4-Flash-Vision-Exp-ROCMFPX-MIX-STRIX.gguf \ | ||
| DeepSeek-V4-Flash-Vision-Exp-mmproj-BF16.gguf --local-dir models | ||
| hf download Lucebox/DeepSeek-V4-Flash-0731-DSpark-GGUF \ | ||
| DeepSeek-V4-Flash-0731-DSpark-draft-Q4RMFP4-denseF16.gguf --local-dir models | ||
|
|
||
| LUCE_DS4_SPEC=1 \ | ||
| LUCE_DS4_DRAFT=models/DeepSeek-V4-Flash-0731-DSpark-draft-Q4RMFP4-denseF16.gguf \ | ||
| LUCE_DS4_SPARSE_DECODE_FLASH=1 \ | ||
| ./server/build-hip/luce_server models/DeepSeek-V4-Flash-Vision-Exp-ROCMFPX-MIX-STRIX.gguf \ | ||
| --target-device hip:0 --max-ctx 131072 --chunk 8192 \ | ||
| --cache-type-k q4_0 --cache-type-v q4_0 \ | ||
| --ds4-fused-decode --ds4-fused-verify-f16-kv \ | ||
| --ds4-expert-top-k 6 --ds4-prefill sparse \ | ||
| --mmproj models/DeepSeek-V4-Flash-Vision-Exp-mmproj-BF16.gguf \ | ||
| --port 8216 | ||
| ``` | ||
|
|
||
| `hip:0` must be the Strix Halo; on a host with a discrete GPU too, expose the | ||
| Strix Halo alone with `HIP_VISIBLE_DEVICES`. This is the text model's published | ||
| launch plus `--mmproj`: the Vision file replaces | ||
| `DeepSeek-V4-Flash-0731-ROCMFPX-MIX-STRIX.gguf` for text as well and decodes | ||
| at least as fast (numbers below). For R9700 + Strix Halo see [DS4V](#ds4v) below. | ||
|
|
||
| ### Send an image | ||
|
|
||
| ```bash | ||
| IMG=$(base64 < photo.png | tr -d '\n') | ||
| curl -s http://127.0.0.1:8216/v1/chat/completions \ | ||
| -H 'Content-Type: application/json' \ | ||
| -d '{"messages":[{"role":"user","content":[ | ||
| {"type":"text","text":"What does this chart show?"}, | ||
| {"type":"image_url","image_url":{"url":"data:image/png;base64,'"$IMG"'"}}]}], | ||
| "max_tokens":256}' | ||
| ``` | ||
|
|
||
| `GET /props` reports `capabilities.image_input_supported: true` once the | ||
| projector has loaded. In the Docker images, set `LUCE_MMPROJ` to the projector | ||
| path inside the container. | ||
|
|
||
| ## Request contract | ||
|
|
||
| Use `POST /v1/chat/completions` with user-message content parts in display order: | ||
|
|
||
| ```json | ||
| { | ||
| "messages": [{ | ||
| "role": "user", | ||
| "content": [ | ||
| {"type": "text", "text": "Describe this image."}, | ||
| {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}} | ||
| ] | ||
| }], | ||
| "max_tokens": 128 | ||
| } | ||
| ``` | ||
|
|
||
| Only base64 JPEG/PNG data URLs are supported. Remote URLs, images outside user | ||
| content arrays, and image parts through other API formats are rejected. A | ||
| request carries at most four images, 16 MiB encoded each and 32 MiB combined. | ||
| Decoder pixel and aspect limits also apply. A model's image marker cannot be supplied | ||
| as ordinary text. | ||
|
|
||
| The server expands image markers after final rendering and tokenization, and | ||
| the expanded image tokens count toward context and usage. Image requests use | ||
| plain autoregressive decoding and bypass the token-keyed prefix, disk and | ||
| agent-turn caches and prompt compression: tokens alone do not identify an | ||
| image. Text requests on the same server keep speculative decoding and caching. | ||
|
|
||
| Layer or tensor splitting across GPUs, remote target shards, concurrent | ||
| sequence scheduling (`--max-concurrency`) and upstream forwarding do not | ||
| support images. `/props` reports the effective capability in | ||
| `capabilities.image_input_supported` after backend initialization. | ||
|
|
||
| ## Qwen3.5 / Qwen3.8 | ||
|
|
||
| The launch is in the [quick start](#qwen38-27b-on-one-gpu-r9700). | ||
|
|
||
| The projector is read directly from the published `clip` file, BF16, F16 or | ||
| Q8_0; Q8_0 is recommended (below). Projectors with | ||
| deepstack branches (Qwen3-VL) are refused. An image is resized the way the | ||
| model was trained (bicubic, both sides to a multiple of 32 pixels) and costs | ||
| one token per 32x32 pixels, between 64 and 1,024 tokens; larger images are | ||
| scaled down to the cap. | ||
|
|
||
| Image tokens take two-dimensional rotary positions, so positions run behind | ||
| token counts after an image. Prefill handles that in its normal chunk loop; | ||
| decoding carries the offset for the rest of the request. | ||
|
|
||
| ### Verification status | ||
|
|
||
| Covered by `test_qwen35_image`: target sizes against the model's reference | ||
| resize rule, the tower's patch order and position table sampling, marker | ||
| expansion, rotary positions, and image rows that straddle prefill chunks. | ||
|
|
||
| Measured on an R9700 alone with the DFlash2 drafter, thinking off. With the | ||
| Lucebox `Qwen3.8-27B-IQ4_XS-pure` file and a Q8_0 projector: | ||
|
|
||
| - 220 seeded questions from `lmms-lab/ai2d` and `lmms-lab/ChartQA` with | ||
| lmms-eval prompts: AI2D 90/100, ChartQA relaxed accuracy 56/60 (augmented) | ||
| and 42/60 (human). Image prompts prefill in 0.56 s on average; one to four | ||
| images per request all answer correctly (four images, 2,495 tokens: 3.2 s). | ||
| - Text decodes at 56 to 117 tok/s on 256-token answers (84 on average); image | ||
| requests decode without the drafter at about 36 tok/s. | ||
|
|
||
| With unsloth's UD-IQ4_XS file and the published BF16 projector: | ||
|
|
||
| - 220 seeded questions from `lmms-lab/ai2d` and `lmms-lab/ChartQA` with | ||
| lmms-eval prompts: AI2D 85/100, ChartQA relaxed accuracy 55/60 (augmented) | ||
| and 43/60 (human), no errors. Image prompts average 448 tokens and prefill in | ||
| 0.71 s (largest 1,068 tokens, 1.8 s); decode runs at 31 to 35 tok/s. | ||
| - A projector with its weight matrices in Q8_0 (rows that are not a multiple | ||
| of 32 stay F16) encodes a 975-token image in 443 ms instead of 677 ms with | ||
| the BF16 file, with the same scores on the 220 questions and 216 identical | ||
| answers. Prefer one when available. | ||
| - llama.cpp (HIP build, `-fa on`, same GGUF, projector and image cap) answers | ||
| the same on every test image; on a 1,012-token image prompt it prefills in | ||
| 1.65 s to our 1.68 s, on a 323-token one in 0.61 s to our 0.50 s. | ||
| - An image inside a 6,271-token prompt, two images in one request, and a | ||
| follow-up turn after an image all answer correctly. | ||
| - Text requests are byte-identical to a build without image support, at the | ||
| same speed, with or without a projector loaded (five prompts up to 19.6K | ||
| tokens). The projector adds 0.9 GiB of VRAM; the peak during image requests | ||
| was 21.6 GiB against 20.8 GiB for text. | ||
| - The same requests answer correctly on a Strix Halo alone, where a | ||
| 1,012-token image prompt prefills in 4.6 s and decodes at 14 tok/s. | ||
|
|
||
| Not yet established: a comparison against the reference implementation on the | ||
| same questions, and CUDA. The tower uses only standard ggml | ||
| operators, so nothing in it is HIP specific. | ||
|
|
||
| ## DS4V | ||
|
|
||
| The server must be built with hipBLASLt available (the `hipblaslt-dev` package | ||
| on ROCm). CMake reports `hipBLASLt found: building the DS4V vision ops`; a build | ||
| without it refuses `--mmproj` for this model at startup. | ||
|
|
||
| Image input needs Linux HIP, a DeepSeek4 decoder whose GGUF carries the image | ||
| router biases, `--ds4-prefill sparse`, and `--mmproj` pointing at the published projector (or one [exported with our | ||
| tool](ds4v-mmproj.md)). Two layouts work: | ||
|
|
||
| - **One GPU holding the whole model** (for example a Strix Halo): nothing else | ||
| to set. The projector is loaded after the weights and must fit beside them. | ||
| - **Two GPUs splitting the experts in process** (for example R9700 + Strix | ||
| Halo): `LUCE_DS4_MOE_TP=1`, `LUCE_DS4_MOE_TP_INPROC=1`, and | ||
| `LUCE_DS4_MOE_TP_GPU` selecting the second device, with `--target-device` | ||
| on the first. Device ordinals must match the host's actual topology. | ||
|
|
||
| Remote expert IPC, all-on-secondary placement, experts kept on the CPU and | ||
| dense prefill do not support images. | ||
|
|
||
| Published llama.cpp conversions of the decoder load directly (image router | ||
| bias named `blk.N.exp_probs_b_vl.bias`, no `deepseek4.vocab_size` key). Split | ||
| GGUF files and llama.cpp's `clip` projector files are not read yet. | ||
|
|
||
| A decoder in our own ROCMFP MIX format comes from `tools/ds4_mix_converter` | ||
| run on the Vision-Exp checkpoint. It follows the shipped DeepSeek-V4-Flash | ||
| recipe: routed gate and up experts in fp2, down experts in fp2 on the shipped | ||
| layer set and fp3 elsewhere, dense projections in ROCmFP4, the token embedding | ||
| in Q6_K, codebooks embedded in the GGUF (one file, about 100 GB). It keeps the | ||
| image router biases. Pass `--imatrix` with an importance matrix (llama.cpp's | ||
| per-expert layout is used expert by expert; the community publishes one for | ||
| this model) or `--absmax-only`. The converter uses every core: about 40 minutes | ||
| for this checkpoint on 32 cores. | ||
|
|
||
| One image request may be outstanding per backend. Its admission lease remains | ||
| with the immutable payload through queueing and generation; another image | ||
| request is rejected until that payload is released. This bounds simultaneous | ||
| preprocessing and prepared-image memory. Text requests retain the normal queue. | ||
|
|
||
| The server expands image markers after final rendering and tokenization. | ||
| Expanded image tokens count toward context and usage. Image blocks remain | ||
| whole during prefill, image rows use their learned routing bias, and raw | ||
| attention is bidirectional within each image's visible span. The projector's | ||
| tile permutation is applied once when assembling rows with named sentinel | ||
| embeddings. All chunks are capped at 1,024 tokens while a projector is loaded. | ||
|
|
||
| The image payload survives request copies and retry paths. Failed or cancelled | ||
| multi-image encoding publishes no partial embedding matrices. | ||
|
|
||
| ### Memory | ||
|
|
||
| The projector is validated and loaded before expert placement. Admission counts | ||
| actual selected owner tensor sizes, allocation alignment, MIX tables, copy | ||
| staging, future KV, and explicit execution reserves. Host and integrated-device | ||
| charges share one physical-memory budget. Before image decoding, the server | ||
| checks host availability; before encoding, it synchronizes and releases | ||
| disposable decoder, owner, and draft graphs and checks live device/host | ||
| availability again. KV, saved snapshots, and draft weights remain reflected in | ||
| that live measurement. Reservations are conservative policy, not a guarantee | ||
| against unrelated concurrent allocations. | ||
|
|
||
| ### Verification status | ||
|
|
||
| Covered by unit tests in the main build: image transport and request policy, | ||
| prompt expansion and ownership, embedding assembly and cancellation, image | ||
| spans and the expert budget, plus the decoder loader and image-batch admission | ||
| tests in `test_deepseek4_unit`. | ||
|
|
||
| Measured with the public `DeepSeek-V4-Flash-Vision-Exp` Q2_K_S decoder and | ||
| the exported projector, on a Strix Halo alone and on R9700 + Strix Halo, 220 | ||
| seeded questions from `lmms-lab/ai2d` and `lmms-lab/ChartQA` with lmms-eval | ||
| prompts: AI2D 85/100, ChartQA relaxed accuracy 55/60 (augmented) and 43/60 | ||
| (human). Both layouts score the same and give word-identical answers on 213 of | ||
| 220 questions. An image request prefills in about 4 s and decodes at about | ||
| 23 tok/s. | ||
|
|
||
| With our own ROCMFP MIX conversion of the same checkpoint (per-expert | ||
| importance matrix, the shipped recipe above), on a Strix Halo alone at top-k 6: | ||
|
|
||
| - Against the MXFP4 reference (native FP4 experts) on 8,176 wikitext-2 tokens: | ||
| KL 0.464 mean, 0.102 median, top-1 agreement 78.4%, perplexity 4.14 against | ||
| 2.82. The community Q2_K_S scores KL 0.511 in our engine (0.523 in | ||
| llama.cpp) and perplexity 4.22. | ||
| - AI2D 84/100, ChartQA 54/60 and 40/60 (the Q2_K_S: 85, 55, 43); the sanity | ||
| and one-to-four-image sets are all correct. | ||
| - With the published DSpark drafter and fused decode and verify, text decodes | ||
| at 25 to 37 tok/s on 256-token answers (30 mean), as fast as the shipped | ||
| text model; image requests decode without the drafter at about 22 tok/s. | ||
|
|
||
| Not yet established: | ||
|
|
||
| - The vision tower misses the fixed 0.9995 feature-cosine gate against the | ||
| reference implementation: 0.99906 on the Radeon RX 7900 XT it was developed | ||
| on, 0.99823 on CPU. Embeddings pass; features do not. | ||
| - No comparison against the reference implementation on the same questions. | ||
|
|
||
| ## Code layout | ||
|
|
||
| Shared by every model: | ||
|
|
||
| | Piece | Where | | ||
| | --- | --- | | ||
| | Reading images out of a request, limits, redaction | `server/src/server/image_input.*` | | ||
| | JPEG and PNG decoding to RGB | `server/src/common/vision/image_decode.*`, codecs in `server/cmake/ImageCodecs.cmake` | | ||
| | Bicubic resizing that matches Pillow byte for byte | `server/src/common/vision/image_resize.*` | | ||
| | Reading a published `clip`-format projector file | `server/src/common/vision/mmproj_file.*` | | ||
| | Image positions in a prompt, batches that keep an image whole | `server/src/common/vision/image_spans.h` | | ||
| | The backend contract | `supports_images`, `image_placeholder`, `prepare_images` in `server/src/common/model_backend.h`; `GenerateRequest::images` in `server/src/common/generation_types.h` | | ||
|
|
||
| DS4V only, all under `server/src/deepseek4/`: resizing and patching | ||
| (`deepseek4_vision_preprocess`), the vision tower (`deepseek4_vision`), marker | ||
| expansion and embedding assembly (`deepseek4_image_prompt`, | ||
| `deepseek4_image_assembly`), attention visibility and expert routing for image | ||
| rows (`deepseek4_image_policy`), and memory admission | ||
| (`deepseek4_image_admission`). | ||
|
|
||
| Qwen3.5 / Qwen3.8 only, all under `server/src/qwen35/`: the vision tower and | ||
| its preprocessing (`qwen35_vision`), marker expansion and rotary positions | ||
| (`qwen35_image_prompt`), what a request carries (`qwen35_image_request.h`), and | ||
| the backend's three contract methods (`qwen35_backend_images.cpp`). Prefill and | ||
| decode changes are a few lines in `qwen35_backend.cpp`. | ||
|
|
||
| Another model needs its own preprocessing, tower and prompt expansion, and its | ||
| backend implements the three contract methods. Nothing in the HTTP server or in | ||
| `common/vision` names a model. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3: This quick start presents
LUCE_DS4_SPARSE_DECODE_FLASH=1as a plain part of the launch, but the rest of the repo documents it as an experimental single-HIP opt-in that can change generated tokens (ENVIRONMENT.md, DS4.md, RECOMMENDED_SETUPS.md all say so). Users copying the canonical snippet will silently enable a non-default, output-changing verifier path, which conflicts with the 'published launch plus --mmproj' framing. Add the same experimental/single-HIP/may-change-tokens comment used in RECOMMENDED_SETUPS.md.Prompt for AI agents