Repository navigation
dots.mocr on HIP: the publisher's BF16 OCR checkpoint, token-exact against its model card - #1938
Conversation
|
Thanks @laudney. We've reviewed this independently, the code against your account, since we have no AMD GPU. What the review established:
Before it lands:
Ordering: the body says the last seven commits depend on #1929, #1930 and #1933. #1929 and #1930 are on main now, and #1933 lands shortly. Once it's in, a rebase leaves only this family's commits. |
|
Thanks @ashhart. All three points are answered below, and the code changes are two new commits on top (
Pinned sources at e539fbb52 (SHA-256)
Device receipt (PR head
|
| identity | value |
|---|---|
| candidate | 459512e31e51 (PR head); tensorfold-native sha256 89a6d579d596cfd173245f0714ee25b20465803b427eeed7dbe3e223c47bdc3e |
| base | 71932143f95a (upstream/main); tensorfold-native sha256 2e17004a34f084c3e2d1f84557cdb0c5f2ede94ff806913e94acf974b9806e33 |
| build (both) | zig build native-hip -Dhip=true -Dhip-arch=gfx1201 -Dhipcc=/opt/rocm/core/bin/hipcc -Dhip-device-lib=/opt/rocm/core/lib/llvm/amdgcn/bitcode -Doptimize=ReleaseFast -j8 |
| toolchain | Zig 0.17.0; ROCm core 10.1.0; hipcc/HIP 7.16.26385; AMD clang 24.0.0git |
| libraries the server loaded | rocBLAS librocblas.so.5.7 75645cbd1f2dd32f; HIP libamdhip64.so.7.16.26385 bddbf3553ee64ae5; libhipblaslt.so.1.4 3d414626f84ecc43; libhsa-runtime64.so.1.21.0 0457249a68424615; libjpeg-turbo 3.1.4.1 (official .deb 3.1.4.1-20260327, via TENSORFOLD_LIBTURBOJPEG) b6a5f4e6066da95c; libwebp 1.6.0 decoder a9d75f4997d79878 |
| checkpoint | dots-studio/dots.mocr @ e539fbb52280393adc081b289ec597430a0f9031; shard 1 d8f4bc75340279da003609fe93f2eb02cc1a77087f5dfb6ba46c0980e1b4da81, shard 2 0101b36fe6620ba135d4bb8efbbf275fc27b4363b10b7f632c058c0955f3dc4d (equal to the HF LFS ids) |
dots_mocr_reference.py sources |
14 files, e.g. config 41d5c9aad72c730b, modeling_dots_ocr.py 274b5bfa35f624f1, modeling_dots_vision.py f22d6251a05a03c7, tokenizer.json 386545eb05f08c51, chat_template.json c8d629ba2b313337; all 9 files in the HF run manifests are equal |
| assets | demo_image1.jpg (rednote-hilab/dots.mocr @ 23f3e561) 90345584ccc2c4a8; square.png 56f28596c8b92fcc; wide.png e8f59ece1a61661b |
| HF reference | torch 2.10.0+git8514f05, transformers 4.57.6, flash-attn 2.8.3 (Triton AMD backend), qwen-vl-utils 0.0.10, HIP 7.2.53211; greedy decoding, flash_attention_2; max_new_tokens 24000 (demo) and 128 (square, two-image) |
| server | serve <ckpt> --host 127.0.0.1 --port 18896 --name dots-mocr --vision --context 16384 --parallel 2 --max-tokens 8192 --temperature 0 --no-update-check (recipe flags); v1.2.1, 10,693,404,224 tracked device bytes; SIGINT exit 0 |
| requests | model-card prompt, data URLs, temperature 0, return_token_ids: true; max_tokens 64 / 8192 (demo), 128 (square, two-image) |
| check | native | HF / expected | equal |
|---|---|---|---|
before: base serve, same flags |
exit 1: the native HIP engine has no backend for dots_ocr checkpoints yet; serve with --engine python |
refusal before weight load (device memory unchanged) | n/a |
demo prompt /tokenize, 5096 ids |
A e523420cba1e851e R feef1012bc620281 |
same | yes |
| square prompt, 280 ids / two-image prompt, 330 ids | A c1386e1fe8f59690 / A b4dee8bffad44e22 |
same / same | yes |
demo, full, plain, 4634 ids, last 151673 (EOS), stop |
A baddbe15d181fcad R 25ea393e611c83c5 |
same | yes |
| demo, full, drafted (1288/3280 accepted) | A baddbe15d181fcad R 25ea393e611c83c5 |
same | yes |
| two-image, plain, 108 ids, last 151673 (EOS) | A 8e9c4f1aa6a208d6 R a402ec9a718f471d |
same | yes |
| square, plain, 40 ids, last 151673 (EOS) | A f6acbea11c225fc6, bbox [15,44,131,64] |
A c2b79c3c8d0da748, bbox [16,45,130,63] |
no: 4 bbox ids differ, text ALPHA 123 is equal (a known difference that the PR discloses) |
| demo, 64 ids: fresh (cached 0) / resumed (cached 4864) / drafted | A d8c09779b3ce16b8 ×3 |
HF first 64: A d8c09779b3ce16b8 |
yes |
| two-image: fresh (0) / resumed (256) / drafted (9/28 accepted) | A 8e9c4f1aa6a208d6 ×3 |
solo plain | yes |
| square: fresh (0) / resumed (256) / drafted | A f6acbea11c225fc6 ×3 |
solo plain | yes |
| concurrent: demo 64 plain ‖ two-image drafted | A d8c09779b3ce16b8 / A 8e9c4f1aa6a208d6 |
solo | yes |
| seeded sampling, two-image, T 0.7, top-k 32, top-p 0.9, seed 9281, 128 ids: solo plain, solo drafted (36/41), concurrent plain ‖ concurrent drafted | A 2aa5a1927fd3f35d R 9824c9bc8d76d17b ×4 |
equal across all 4 cells; differs from greedy | yes |
client closes in prefill (0 output tokens, client-left), then square |
A f6acbea11c225fc6 |
solo square | yes |
client closes in decode (1 output token, client-left), then square |
A f6acbea11c225fc6 |
solo square | yes |
| invalid image bytes | 400 image 1 is not PNG, JPEG or WebP; send one of those |
400 | yes |
excess context (max_tokens 50000) |
400 context_length_exceeded |
400 | yes |
video_url part |
400 content parts must be text or image_url; audio and video inputs are unsupported |
400 | yes |
In all replies, the OCR text is in message.content and there is no reasoning_content. The 64-id demo and square drafted requests made zero copy proposals, so their drafted = plain result is trivial. The full demo, two-image and seeded rows do use drafts.
Kernel contract and limits (PR head kernels; the two new commits change no kernel)
upstream/main |
this PR | |
|---|---|---|
| gfx1201 contract | 52 kernels pass gates 1-4, 5/5 controls red | 63 kernels pass gates 1-4, 9/9 controls red |
| Limit problems (live gfx1201 / offline, 4 chips) | 124 / 503 | 124 / 503, the same entries |
Ordering: #1929 and #1930 are now on main as your versions, so this branch shows conflicts with main in build.zig, zig/build/hip.zig, zig/src/core/prompt_rules.zig and zig/src/hip/root.zig. They are the copies of those two PRs. #1933 is merged too, so a rebase onto main now leaves only the dots.mocr commits. We do not force-push open PRs. If you prefer, we can push a merge of main instead.
|
Thanks @laudney: all three asks are answered (the oracle pins the revision and the SHA-256 of all 14 non-weight sources, refuses a differing checkpoint, and has a GPU-free sources command). Please go ahead with the rebase you offered. Its four conflicting files are the copies of #1929/#1930, which are on main now, so dropping those commits should leave only the dots.mocr ones. We'll land it then. #1940 is landing ahead of it, so rebase after that if you can. |
459512e to
7e5b5c4
Compare
|
Rebased on main
Conflicts. Each one is a file where #1940 and this PR both add an entry, and both entries are kept:
Checks on Device record (gfx1201, Radeon AI PRO R9700): the same driver and server flags as the receipt in this thread, on binary sha256
25 of 26 checks pass; the one failure is the known square bbox difference, with the same positions and hashes as before the rebase. |
|
Thanks @laudney. The rebase is clean: 9 commits, all dots, on current main, and each resolution keeps both GLM-OCR's
With the pinned sources, the attention and bias-order answers, and the registry assertion already in, everything we asked for is done, so this is ready to land as it is. One heads-up for #1941: both PRs add |
7e5b5c4 to
b0b62f3
Compare
dots.mocr support starts by checking what the native engine will load before any GPU work: the released BF16 config, all 643 tensor layouts and exact shard bytes, and image patches prepared byte for byte like the pinned Qwen2VL processor. Fixtures pin synthetic processor hashes and the Hugging Face demo image's pixel hashes, and one offline Python script records those fixtures, runs the model card's example as an oracle with scores streamed to disk, and later compares a native greedy trace against it.
The native HIP engine needs every dots.mocr tensor resident before it can run the graph, so the family places all model tensors in one admitted device buffer, uploads them from the checked shards, and frees them on every failure path. Host tests refuse impossible layouts and budgets without GPU calls, and a device test built by build-dots-hip-weights-test verifies complete upload bytes and cleanup against a local checkpoint.
The checked weights now drive a native HIP graph for the dots.mocr vision tower and Qwen2 text decoder: BF16 column, vision and dense text kernels with FP32 sums, rocBLAS projections through hip.blas, and contract records and controls for the recorded RDNA chips. The tf-dots-hip-check tool runs the graph on a local checkpoint and image with checked memory, and the demo fixture records that the complete example's 4634 native greedy tokens match the Hugging Face output exactly. Host steps cover operator admission and graph refusals.
Core prompt-cache snapshots must keep every dots.mocr attention head while a lane's cache and its snapshot reserve different row counts. A checked physical KV prefix copy moves only the live rows between capacities and refuses mismatched geometry, so snapshots preserve each head without copying unused reservations. A device test built by build-dots-hip-cache-test proves the copied rows on a GPU.
Serving lanes add persistent buffers and KV reservations on top of the weights, so a lane count that does not fit must fail before any upload. A serving plan counts those bytes against the admitted GPU budget: a fixed lane count that does not fit refuses safely, and an automatic count shrinks until it fits. Host tests check the budget arithmetic in debug and release modes without a GPU.
Serving decodes image prompts in windows and keeps only accepted prefixes, so those paths must match plain single-token decoding. tf-dots-hip-check now compares real image-prompt windows with single-token steps and restores accepted prefixes into poisoned caches, which shows that future rows never leak into kept results.
dots.mocr becomes a native HIP engine behind the shared lane host: the BF16 image and text graphs run with owned pixel staging, used only when the plan admits images, bounded KV slots, core sampling and prompt snapshots, and the family declares replies without reasoning, so OCR text stays in answer content. Warm passes yield between chunks, and a new opt-in Rules.warm_usable flag, set only by dots, keeps a warm pass's end only at a resumable row; other families keep their behaviour. Unsupported hardware and graph policies are refused, and a new recipe documents the checked setup.
The registry test from ashhart#1933 checks the hook on two anonymous namespaces, so it does not name dots.mocr. The dots refusal test now asserts that the registered dots family declares no reasoning block, which keeps its OCR text in message content. Only the dots assertion is added, so later OCR families do not conflict with it.
The oracle recorded source hashes but accepted any checkpoint, so a changed snapshot could pass as the pinned revision. It now holds the SHA-256 of every non-weight source at e539fbb52, the model code, configuration, tokenizer, template, processor assets and model card, and refuses a differing checkpoint before the oracle or processor run. A new sources command checks a checkpoint and prints the manifest without loading the model, so reviewers can verify it on any host.
b0b62f3 to
2d16c82
Compare
|
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. |
|
Thanks @laudney! This is in TensorFold 1.2.3, out now: https://github.com/ashhart/TensorFold/releases/tag/v1.2.3 |
Part of #1927.
This serves the publisher's BF16
dots-studio/dots.mocr(revisione539fbb52280393adc081b289ec597430a0f9031) natively on HIP. Its template, tokenizer, image processor, weights and three EOS IDs come from the checkpoint unchanged. It is the checkpoint other engines publish numbers for and the one users download first, so serving the publisher's weights matters for benchmarking and adoption. The image tower and the Qwen2 decoder run in Zig and HIP, with no Python in the served engine, behind/v1/chat/completionswith image inputs, shared lanes, drafted verification and the prompt cache.dots-studio/dots.mocr(BF16) on HIPtools/zig/for checksPrerequisites #1929, #1930 and #1933 are merged. This branch still carries their earlier copies at its base, so a rebase onto main leaves only the dots.mocr commits,
de07fc7a9^..459512e31.Commits
tools/zig/dots_mocr_reference.py:oracleruns the model card's own example and saves IDs, scores and seams.processorregenerates the fixtures.comparechecks a native greedy trace against the oracle.cu_seqlensspans), and the merger LayerNorm/GELU.columns,dense_text,vision) with kernel-contract cases and launch records.attention/prefill.hipas a non-causal instantiation. A NaN score makes its row NaN, as in Torch. The existing causal instantiations compile to the same code and keep their contract results.--tp, MTP drafts, unsupported policies), warm passes that yield to foreground requests, and the recipedocs/recipes/dots-mocr-hip.md.!reasoning(dots.native), and the oracle pins the SHA-256 of all 14 non-weight sources ate539fbb52, with asourcescommand that checks a checkpoint without a GPU.Shared changes (other models unchanged)
pub const reasoning = false;(Server: one engine flag for models whose replies carry no reasoning block #1933), so OCR text is always answer content. Its chat template does not readenable_thinking, so the prompt is the same either way.native/hip.zig: three optional family hooks (refusal,openVision,attach). A family without them takes the existing path. The dots refusal test asserts that dots.mocr declares no reasoning block; Server: one engine flag for models whose replies carry no reasoning block #1933's test checks the hook itself.Rules.warm_usable(defaultfalse): with it, a warm pass keeps its end only whereStore.usableresumes it. With it off, both call sites inlane_host_warm.zigreduce to the existing condition.lane_host_warm_test.zigcovers both settings.build.ziggains one line afterconst test_step. core: a GGUF reader and an exact repack of GGUF's quantized blocks (#1920, steps 1-2) #1926 and Native OCR models on HIP: dots.mocr, GLM-OCR, LightOnOCR-3 and Nemotron Parse, CUDA to follow (tracker) #1927's GLM-OCR PR add a line at the same place, so whichever lands later keeps both lines.Gates
SUMMARY gfx1201 63 kernels: 63 pass gates 1-4, 0 fail, 2 known findings; 9 controls: 9 red as planted, 0 missed; 124 limit problems.upstream/mainbase reports52 kernels ... 124 limit problemson the same GPU, and the same limit problems appear on both. So the 124 problems are inherited, and none is a new launch.31040 non-finite and 460480 finite outputs match the float64 classes, five byte-identical runs, 8 shapes within the float64 bound, andexact sums at k 57600.upstream/main, and no new problem.test-hip-blas-host,test-dots-mocr-host,test-dots-hip-host,test-dots-hip-weights-host,test-dots-native-hostandtest-lane-api-hostpass.zig build test: 469/469 steps;zig build test -Dhip=true: 502/502 steps.native-hipfor gfx1201.