Repository navigation
cuda: splice image rows into the dense engine's prompt chunks - #2768
Merged
Merged
Conversation
This was referenced Nov 10, 2026
Owner
|
Thanks @laudney, this is a careful piece. The text-only variant keeps exactly #2673's declarations, and a test shows text-only bytes and layout unchanged; images are bounded by the request's token budget and each extra lane is charged in the page planner, with tests for both. A few small things:
|
laudney
force-pushed
the
pr-dots-b
branch
2 times, most recently
from
November 11, 2026 08:14
04ea94b to
882648a
Compare
Contributor
Author
|
Thanks, all four are in:
|
Owner
|
Thanks @laudney, all four are in, and each new test fails with its fix removed and passes at this head. Ready from our side.
|
ashhart
added a commit
to laudney/TensorFold
that referenced
this pull request
Nov 12, 2026
14 of 26 tasks
ashhart
added a commit
to laudney/TensorFold
that referenced
this pull request
Nov 12, 2026
This was referenced Nov 14, 2026
The dense engine read text prompts only, so a recipe with an image tower had nowhere to put its rows. A recipe may now declare a Tower, checked at compile time against the contract in recipe.zig; each lane owns a buffer of the request's image tokens, the first one at open and the others at attach, and these bytes join every stream's bytes and fixed part so the page planner shrinks an automatic count. The lane encodes its images in order when it begins, with a cancel check before each, and each prompt chunk copies the image rows that it cuts over its embedding rows. Native(R) is now one shared core with two variants: the text-only one keeps the old decl set exactly, and the tower one adds the vision options, field and bytes that the host reads. Host tests drive the tower variant with a fake tower, also through the host's own offer and options code, and pin that the image span refusal names --vision-image-tokens, the flag the CLI accepts.
Only the words of DenseTowerWidth were tested, so dropping the width check in openTower would pass the host suite. openTower is now public, and a host test opens the fake tower against a decoder one row wider and expects the refusal, closing the tower first if it opens, then opens it at the decoder's width and without --vision.
No test drove a chunk that cuts an image through the backend, so a chunk that passed no injects or a forward pass that skipped the splice would stay green. The fake device now records its device-to-device copies, and a host test runs two chunks cut inside an image on a quiet recipe (no layer work): each must copy that image's rows from the lane's feature rows to the chunk's embedding rows, at the right row, offset and length.
The engine sizes each lane's feature rows by the request's image tokens and copies image.tokens rows an image from them, so the Tower contract now says that encode writes exactly image.tokens rows of the tower's width at out.
Three trailing comments in images_test.zig ran past 120 columns; each now sits on its own line above the code it describes.
Owner
|
Thank you @laudney. This is merged into main with your commits under your own name, so you're listed as a TensorFold contributor. It ships in the next release, credited to you in the changelog and the release notes. |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Tower, checked at compile time (checkTower)Options.vision,Loaded.vision,visionBytesDenseImageSpan(names--vision-image-tokens),DenseServedWithoutVision,DenseTowerWidthWhat this changes
The dense engine read text prompts only, so a recipe with an image tower had nowhere to put its rows. This adds the engine's image path and tests it with a fake tower; no served recipe uses it yet.
zig/src/cuda_dense/recipe.zig: theTowercontract (limits, marks,open/prepare/encode/explainwith their fallibility, an output width equal to the decoder's hidden size), checked bycheckTower.native.zig:Native(R)is one shared core with two variants. The text-only variant keeps exactly cuda: a shared engine for dense decoders, with dots.mocr's text decoder on it #2673's declarations; the tower variant adds the vision options, field and bytes the host reads.images.zig,backend.zig,pass.zig: each lane holds a buffer of the request's image tokens (the first at open, the others at attach). These bytes join every stream's bytes and fixed part, so the planner lowers an automatic lane count and an admitted request never allocates. The lane encodes its images in order when it begins, with a cancel check before each, and each prompt chunk copies the image rows it cuts over its embedding rows.The image span refusal names
--vision-image-tokens, the flag the CLI accepts; a host test fails if the words go back to--image-tokens.Model licence: n/a, no weights are read.
Issue: Refs Native OCR models on HIP: dots.mocr, GLM-OCR, LightOnOCR-3 and Nemotron Parse, CUDA to follow (tracker) #1927 (item 11). Remaining after this stack: Nemotron Parse and GLM-OCR on this engine.
Stacked on: cuda: dots.mocr's vision rotary and patch bias kernels #2767 (piece 1,
4cea19ea3). This PR's commits:5aad7bf24feat(cuda): splice image rows into the dense engine's prompt chunks;61f6e54ectest(cuda): pin the dense engine's tower width refusal;81041a261test(cuda): a prompt chunk through an image copies its rows;d9f9a476ddocs(cuda): state encode's row count in the Tower contract;882648a03style(cuda): move three long trailing comments above their lines. Piece 2 of 7.Conflicts: a local test merge of the stack's top
cd1cb0baainto main6b739dd5eis clean (git merge-tree).Checks on this head
882648a03zig build test-cuda-host,check-native-cuda-host7/7,test-kernel-contract,zig fmt --checkandgit diff --checkpass; lean 386 / 386, 0 new against piece a. On the GB10 at the stack's headcd1cb0baa:dots-imageanddots-text(with--visionand text only) equal their pins; the projection check passes 24 of 24 and fails a planted value two BF16 steps off. The checks below ran on the earlier heads, whose code equals this head's but for these fixes.python3 tools/zig/lean_check.py, on the base (cuda: a shared engine for dense decoders, with dots.mocr's text decoder on it #2673's head05e5e5fdd) and on this head: 386 / 386, 0 new (also 0 new against piece a).zig build test-cuda-host: 378/378 steps, 0 failed (the test binaries this piece changes re-ran; the others are the parent's and passed there).zig build test-cuda-dense-host: 33/33 passed, uncached.zig build test-dots-mocr-cuda-host: 10/10 passed, uncached.zig build check-native-cuda-host: 7/7 steps.zig build test-kernel-contract: 32/32 steps, 0 failed.zig build compile-cuda-tests(the GB10 runner compiles): 10/10 steps.zig fmt --check build.zig zig,git diff --check: pass.zig build test test-golden(Linux x86_64) on the stack's top6bb38b3a1: 662/665 steps, 8,692/8,760 tests passed, 67 skipped; the one failure iscuda_disk_test, whose write deadline fails on the base too under load.New host tests (in
test-cuda-dense-hostandtest-cuda-host): the tower contract with a fake tower and each wrong declaration refused by name; bothNativevariants (the text-only one keeps exactly #2673's 20 declarations); the host's offer and options read through the realcuda_vision_offer.zig; per-lane bytes and image spans; chunk-cut intersections; lane drops on every failure path, each with a red control; the refusal words.Receipt
n/a: no served recipe declares a tower in this piece, so no image reaches inference. The text path's shared core changes; its exactness and speed are measured on the stack's head in piece e's receipt (dots.mocr text unchanged against #2673; Qwen3.8-27B MLX tokens identical and speed within run-to-run spread against main).
Checklist