Skip to content

HIP: refuse --drafter until the engine runs DFlash2 - #1044

Draft
ThinkOffApp wants to merge 2 commits into
ashhart:mainfrom
ThinkOffApp:hip-refuses-drafter
Draft

ThinkOffApp wants to merge 2 commits into
ashhart:mainfrom
ThinkOffApp:hip-refuses-drafter

Conversation

@ThinkOffApp

Copy link
Copy Markdown
Contributor

For #1001 (1.1.1). Branch ThinkOffApp:hip-refuses-drafter, on main at 76aafcf2. One commit.

What and why

The HIP server accepted --drafter and then did not use it. Qwen3.8-27B ran at about 12.9 tok/s on gfx1151 with DFlash2 and without it, and the receipt could not say whether a drafter had run. Issue #1001 asks for a clear refusal until HIP supports it. On main the HIP engine still has no DFlash2 path: nothing under zig/src/hip* or the Qwen3.5 HIP backend reads Open.drafter. Its own MTP-head drafting (--mtp-drafts) is separate, and this PR does not change it.

  • serve --drafter on HIP is refused before anything loads, on every rank: --drafter: the HIP engine runs no DFlash2 drafter yet; serve without --drafter. The check is first in checkGroup (zig/src/native/hip.zig), which ready() runs before the device, the policy or the group, so a follower rank refuses too.
  • tensorfold benchmark on rocm refuses a named --drafter before any serve child starts: --drafter: the HIP engine runs no DFlash2 drafter yet, so a rocm run takes none; --drafter none runs without it. Nothing is saved, and no .tensorfold directory is made.
  • --drafter auto on rocm passes no drafter. On a checkpoint with its own drafter, such as Qwen3.8-27B, the run says This run passes no drafter: the HIP engine runs no DFlash2 drafter yet., so the receipt's drafter_repo_id is null. On mlx and cuda, auto works as before.
  • Docs: docs/community-benchmarks.md (the --drafter paragraph) and docs/recipes/qwen3.8-27b.md.

When HIP gets DFlash2, removing the refusal line and the rocm case in autoDrafter brings back the old flow. The tests below will then fail on purpose, as a reminder to replace them with tests that the drafter runs.

Tests

Host tests on an M-series Mac, CPU only, Zig 0.17.0, zig build test -Dcpu=apple_m1 --summary all:

Tree Build steps Server unit tests HIP native host tests (zig/src/native/hip.zig)
main 76aafcf2 292/292; 4305 of 4321 passed, 16 skipped 428 3
this branch 920d1348 292/292, all passed 429 (+1) 4 (+1)

Full count on this branch: 4308 of 4324, with the same 16 skips. The server binary runs twice in zig build test. All three PRs for #1001 also build and pass together on one merged tree.

  • hip.test."--drafter is refused before any load, on every rank, until the HIP engine runs a drafter": rank 0, and a follower (--tp 2 --rank 1), are both refused with the message.
  • benchmark_run_test."a rocm run refuses a named --drafter before any serve child, and auto names no drafter on rocm": a repo ID and a directory as --drafter both exit 1 with the message. The fake server gets no request, and no pid file, args file or .tensorfold directory is made. autoDrafter names DFlash2 on cuda and none on rocm or with --serial.

Negative controls. With the checkGroup line removed, the HIP test fails. With the benchmark refusal and the rocm case in autoDrafter removed, the benchmark test fails in both server test binaries. With them back, all tests pass.

GPU receipts

Measured on 18 Oct 2026 on a Bosgame M5 (AMD Ryzen AI MAX+ 395, Radeon 8060S, gfx1151). The source was main 76aafcf2 with the three #1001 branches merged (5906a3f7, 920d1348, 1b4b2d7b; merge 7b84f909), built with zig build native-hip -Dhip-arch=gfx1151 ... -Doptimize=ReleaseFast, Zig 0.17.0. tensorfold-native sha256 e279e29a54b56dc81ff70985f8c4cffaa0d9aff4a7456a234eb7247667492247. Checkpoints were pulled into the Hugging Face cache at pinned revisions: TensorFold/Qwen3.8-27B-MLX-4bit@70ae7fac... and TensorFold/Qwen3.6-35B-A3B-MLX-4bit-MTP@f84b054c.... z-lab/Qwen3.8-27B-DFlash2 is cached at 50307d4c.... The machine's own LLM service was stopped for the run.

Command Exit What happened
tensorfold-native benchmark TensorFold/Qwen3.8-27B-MLX-4bit --drafter z-lab/Qwen3.8-27B-DFlash2 --output ... 1 tensorfold benchmark: --drafter: the HIP engine runs no DFlash2 drafter yet, so a rocm run takes none; --drafter none runs without it. No receipt was written.
tensorfold-native serve TensorFold/Qwen3.8-27B-MLX-4bit --backend rocm --host 127.0.0.1 --port 8093 --drafter z-lab/Qwen3.8-27B-DFlash2 1 tensorfold: --drafter: the HIP engine runs no DFlash2 drafter yet; serve without --drafter. GPU memory in use was 1,129,906,176 bytes before and 1,129,861,120 after, and both commands finished within the same second. Nothing loaded.
tensorfold-native benchmark TensorFold/Qwen3.8-27B-MLX-4bit (--drafter auto, DFlash2 cached) 0 The run printed This run passes no drafter: the HIP engine runs no DFlash2 drafter yet. The receipt has drafter_repo_id: null, and all 20 attempts were ok (8 m 43 s).

A finding about the earlier gfx1151 report in #1001. The draft counts from the draft-counts PR show that the 27B does draft on HIP, with its own MTP head: 262 tokens drafted and 70 accepted (27%) over the 20 attempts. The HIP policy line is mtp drafts 3 under 0.3, and the checkpoint's config has mtp_num_hidden_layers: 1. So in the earlier 1.0.12 runs "with and without DFlash2", both runs were MTP-drafted and --drafter was ignored. That explains why the timings were equal. It also differs from the pinned gfx1151 receipt (docs/receipts/gfx1151-qwen-833bbfcb), which describes the 27B as having no draft head. We have not looked into why.

Full local receipts and logs (27b-auto.*, 27b-dflash2.log, 27b-serve-drafter.log, the run log with GPU-memory stamps), with SHA-256 sums. They can be attached on request.

ThinkOffApp and others added 2 commits October 18, 2026 17:34
The HIP server took --drafter and ignored it: Qwen3.8-27B ran at the same
speed with and without DFlash2 on gfx1151, and the receipt could not say
whether a drafter ran. serve --drafter on HIP is now refused on every rank
before anything loads. tensorfold benchmark on rocm refuses a named --drafter
before it starts, and --drafter auto passes none there and says so, so the
receipt's drafter_repo_id is null.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ashhart

ashhart commented Oct 18, 2026

Copy link
Copy Markdown
Owner

Thanks @ThinkOffApp; we've reviewed this draft early. The constant refusal reaches both ranks before loading, and the benchmark's named-drafter refusal precedes starting a server. Please change the recipe's “decodes plain”: refusing external DFlash2 still permits the engine's own MTP head. Please attach the negative controls and the refusal logs described in the body so we can check the single diagnostic and before-load behavior on our 1.1.2 build. We'll carry this in 1.1.2 after that correction and our gates.

Posted by Ash's AI Assistant Synthena

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants