Skip to content

qwen27: add the pair acceptance and supervision scripts - #1436

Open
akol1 wants to merge 5 commits into
ashhart:mainfrom
akol1:qwen27/pair-tools
Open

akol1 wants to merge 5 commits into
ashhart:mainfrom
akol1:qwen27/pair-tools

Conversation

@akol1

@akol1 akol1 commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

What this changes

Adds three files under tools/qwen27/. qwen38_27b_oracle.json pins four single-host receipts (temperature 0, seed 1234, no drafter, one stream) with the checkpoint revision, and records for chat-256 the one value a pair is accepted at and why it parts from the oracle. pair_accept.py run sends each fixture twice, requires identical replies and the oracle's token_sha, accepts that recorded value for its case, and prints 3/4 … 0 unexpected on a pair; pair_accept.py warm holds a two-turn conversation and requires the second turn to report cached prompt tokens. pair_supervise.sh up|down|status|watch starts rank 1 first and rank 0 second, refuses two installs whose binaries differ unless ALLOW_MISMATCH=1, waits for both /health ports, and in watch stops and restarts both ranks whenever either stops answering. Every location is environment overridable, and the script prints that environment from its usage text.

Refs #1236, the tracker for tensor-parallel ranks: these are the harness and receipts this stack was accepted with, and they do not cover the mixed Metal/CUDA case that issue is about. Refs #808: it asked for two-Mac prompt-splitting speed with drafts and images; this stack splits rounds and refuses both, so nothing here is a retry of that report. Stacked on #1432, #1433, #1434, #1435; the diff against main carries them too, so review this pull request's own commit, 53a0d278.

Receipt

  • Environment, with the TensorFold commit, MLX or PyTorch version, machine and GPU, checkpoint and revision: 8c36359d plus this commit (53a0d278); no MLX or PyTorch; macOS 27.0.1 on two Apple M4 Pro Macs (Mac16,11), Metal only (applegpu_g16, no tensor units), Apple metal version 32023.921; checkpoint TensorFold/Qwen3.8-27B-MLX-4bit at revision 22d8d538154e0e5b6f8dcb1fcc74a60a0798104c, 4-bit affine with group 64, on both Macs; the two Macs joined by a Thunderbolt bridge. The three files are not compiled and need no checkpoint to read; they were exercised against sha256 2dda0e50fad3ca10487c215820f5cd9acee83d3914ab474955659333d9e81bdf, which is this stack's native code (later commits add tests, tools and docs only).
  • Exactness, from tools/bench_concurrent.py --alone --serial or token_sha against "draft": false: pair_accept.py run --repeat 2 against one Mac matches all four oracle fixtures on both repeats, chat-256 at a06fab348fbe; against the pair it matches three, twice each, and reports KNOWN chat-256 token_sha=58daf9743077 is the pair value recorded for this case, not the oracle's a06fab348fbe, ending pair_accept: 3/4 cases match the oracle, 1 recorded as different, 0 unexpected. pair_accept.py warm on the one-Mac arm prints warm: turn 1 prefill marked 0; turn 2 cached=158 and PASS warm: the next turn resumed 158 cached prompt tokens. The supervisor confirmed matching installs (binaries match: 2dda0e50fad3), held rank 0's port until the handshake (rank 0's port refused 2 probes while the pair handshake completed), survived kill -9 on rank 0 (restarting both (restart 1), same shas afterwards), and on TERM stopped both ranks with no serve process or listener left on either host.
  • Decode speed before and after, from tools/bench_openai.py: n/a — these tools time nothing. The pair/lifecycle pull request carries the done … tok/s= lines from the same binary: 16.8 to 17.1 tok/s for the pair against 12.3 to 13.6 tok/s for one Mac.
  • Prompt speed before and after: n/a — the fixtures' prompts are 14 and 31 tokens, and warm adds a 187-token conversation over two turns without timing a cold long prompt; the pair/lifecycle pull request owns those numbers.
  • Tests run, with pass, fail and skip counts:zig build test test-golden at this tree: 7116 passed, 45 skipped, 0 failed of 7161, with 535 of 535 build steps; the 45 skips are the image-codec and pixel cases that need libjpeg-turbo 3.1.4.1 or libwebp 1.6.0, plus SKIP qwen27-tp-check: no --model DIR. The three files add no CI test; their own runs at this tree: pair_accept.py run --repeat 2 sent 8 requests per arm — one Mac 4 of 4 cases matched on both repeats (0 failed), the pair 3 matched and 1 recorded and 0 unexpected (0 failed) — pair_accept.py warm passed once with 158 cached tokens, and python3 -m py_compile tools/qwen27/pair_accept.py plus sh -n tools/qwen27/pair_supervise.sh were clean.
  • Not run, and why: the supervisor's SSH path ran between two Macs over one Thunderbolt bridge and no other transport; CUDA, HIP/RDNA, M1-M3/M5 and Windows are absent here; pair_accept.py needs a serving pair, so CI covers none of these three files; and the oracle is four short fixtures, not a corpus, so it is a regression pin rather than a release-wide benchmark.

Checklist

  • Every token still goes through the lane rounds. No serial path, and nothing that needs drafts off. — these tools serve no tokens; but the oracle pins "drafter": false because the pair refuses a drafter, so they cannot check a drafted reply and the drafted path keeps its existing single-host tests.
  • Drafted output equals "draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. — the pair refuses a prompt cache and a second stream by name, so warm checks resume on the one-Mac arm only and concurrency is not exercised; those refusals are the lifecycle pull request's receipt.
  • No precision traded for speed. If the bits change, the description says which sums change and why. — no arithmetic here; the oracle records the pair's one differing value and its two contenders instead of widening a tolerance.
  • A new or changed GPU kernel is registered with the kernel contract (docs/kernel-contract.md); each backend's SUMMARY line is in the receipt. — no kernel in this commit; the pair's four are registered in the first pull request.
  • Prompt processing is no slower than the last release. — n/a: tooling, no prompt path touched; no cold long-prompt timing was taken.
  • The description says which platforms I ran, Metal M1 to M5 and CUDA, and which I could not. — ran Metal on two M4 Pro Macs over Thunderbolt; no CUDA, no HIP, no M1-M3/M5, no Windows (see Not run).
  • New tests fail before the change, pass after it, and skip cleanly without their dependency. — no CI test is added; the confidence is the runs below, and the files do not exist before this commit.
  • Comments and docstrings are one line. No measurements or history in the source. — the Python modules follow tools/bench_openai.py's shape, and the supervisor opens with two comment lines and prints its environment from its usage text instead of a header block; the lean check reports 428 problems, 428 on the base, 0 new.
  • No personal data, machine names, internal hosts or local paths. No AI attribution lines. — the files name no machine, user path, address or serial; the peer, model, settings and binaries all arrive as environment variables, and the only fixed address is the usual 127.0.0.1 loopback default.

akol1 added 2 commits October 26, 2026 18:18
The seam carries one round's partials between two Macs: pair.metal writes a
frame, the host relay owns the exchange instead of spinning on device
counters (which only works while both ranks share a launch group), and
pair_sum completes the partial on the far rank. The payload is f32, the
seam's accumulation, and simd_qmm grows the matching store. pair.zig splits
into pair_layout and pair_loopback so each module stays under the 600-line
rule.

Nothing registered the seam before: three of the four kernels already ran
under the older ABI on the shipped glm_dsa speed-up path, but there was no
record and no spec, so no gate covered the kernels this stack changes. Add
the four launch records, the seam spec with its four probes, the
`metal pair ` route in the limits table, and the fp32-out simd_qmm variant,
so the transport and family changes that follow have a SUMMARY to post.

The glm_dsa call sites and the two Pipeline.load test call sites are forced
by the shared signatures; the family changes themselves come later.
On a pair rank the embedding table is global while the vocabulary in the
split config is the shard's width, so the gather bound took the shard's
number and read the wrong rows; embedBound now takes the whole table and
refuses a bound below it. Rank-sliced projections, the head seam, the GDN
partials and the attention plan follow the same split, and the drafts-off
path keeps the two ranks in lockstep.

The regression test pins the old behaviour (124160) against the new
(248320) and fails when the split width is used again.
akol1 added 3 commits October 26, 2026 20:52
A supervised pair has to survive an unattended night: rank 0 now waits for
rank 1's handshake before it advertises /health, the first turn is warmed
so the client does not pay the load, and either rank exiting takes the
other down instead of leaving it serving alone. The launch script starts
and stops both, with the kill/restart drill recorded in the receipt.

The benchmark and TF_DUMP_IDS receipts in server/ are the support code for
those runs; dump.zig and input.zig are their two halves.
tf-qwen27-tp-check runs rank-1 parity bit for bit and then joins two
rank-local shards through pair.Loopback on one Mac, comparing ids against
the single-host load and the forced replay. It SKIPs without a checkpoint,
so the suite stays clean on hosts that have none. The module registration
is the only build change.
pair_accept.py walks a live pair through the fixture set and compares each
token_sha against the recorded single-host oracle, so the check needs no
second reference machine. pair_supervise.sh starts rank 1 on the peer,
waits for the handshake, starts rank 0, polls both health endpoints,
restarts either rank if it dies, and stops both on SIGTERM. Hosts and paths
come from arguments or environment only.
@akol1
akol1 force-pushed the qwen27/pair-tools branch from 54957a6 to 53a0d27 Compare October 27, 2026 03:58
@akol1
akol1 marked this pull request as ready for review October 27, 2026 04:32
@ashhart

ashhart commented Oct 27, 2026

Copy link
Copy Markdown
Owner
  • The oracle. It says "A paired run must reproduce every token_sha here", but pair_known then accepts 58daf9743077 for chat-256. Please remove the exemption.
  • stop_peer and down kill every process matching tensorfold-native serve.*--speed-up on both Macs, which can include someone else's GLM or Flash Next pair. Kill only the pids the script started, and check them.
  • The default SSH_OPTS turns off StrictHostKeyChecking on a script that starts binaries remotely. Please keep the normal host-key check.

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