Repository navigation
Conversation
MiniMax-M3.1-Flash (EXL3 EAQ pack) as a native CUDA family serving through the shared lane core: config, EXL3 weight load, sparse attention, layer graph, serve, and the CUDA-native adapter with the TP2 rank boot mirroring glm's env-rank pattern. tf_m3_scores/topk/decode/decode_combine/swigluoai kernels plus the routed-experts extension of exl3_experts. Gate issue ashhart#1280. Device parity 10/10; real-pack f64 grade: greedy tokens exact vs f64, logits max|delta| 0.003237.
Build registration for the minimax_m3 kernel image, the tf_m3 vtable wiring, the CUDA/Metal family admission lines (Metal: stage-1 admission, no Metal weight reader), and the CLI hub/info entries with the EXL3 format reader.
Device parity harness vs the CPU references (scores/topk/decode+combine/ swigluoai, NB/nsplit bound guards, zero-valid-key), the layer-graph token harness, TP2 rank/env-place refusal tests, and the exl3_experts f64 additions.
The keyed sampler is not attached for minimax_m3_vl, so a standard client's bare chat resolved against the checkpoint's generation-config defaults to a sampled spec and refused everywhere. An added per-engine temperature_greedy info flag (default false; resolved in the server's resolveSampling, mirroring the existing greedy_only design line) serves requests naming only temperature (any value, or absent) as fixed greedy and annotates the reply (tensorfold.sampling), while top_k/top_p/min_p/ seed and grammar masks keep refusing by their exact lane-surface name (SamplingNotAttached). The family's own lane surface is unchanged. Reasoning/thinking default stays; no family-level thinking knob exists in the glm adapter to mirror.
…ayer The earlier temperature-greedy wiring resolved the flag comptime through openWith(F) with F = the family's registry alias (minimax_m3_vl.native), and that alias re-exported model_type/formats/Options/open/explain/ weightShare but not temperature_greedy — @hasDecl fell to false, the engine Info served false, and POST /v1/chat/completions resolved bare and temperature-only bodies against the checkpoint's generation-config defaults (temperature 1.0, top_p 0.95), built a full Sampling spec and hit the lane's SamplingNotAttached. Only an explicit temperature 0 served (the old value-based path). The alias now carries the flag, so the server's resolveSampling key-presence flip actually runs on the live serve path: temperature at any value, or absent, serves fixed greedy and annotates the reply; top_k/top_p/min_p/seed and grammar masks keep refusing by their exact lane-surface name. Tests extend done_times with the real HTTP parser path (openai.chatInput -> chat.prepare) and guard the alias re-export in the family's own admission test.
…nking-on by name Stage-11 live-lane probes (spark2 :8000, MiniMax-3.1-Flash, PR ashhart#1281 head f623f9e) split the empty chat reply into two findings: 1. The thinking switch was inert on this family's rendered prompt. hf_text put the generic thinking_mode spellings ("thinking"/"chat") into every template, but MiniMax M3.1's chat_template.jinja branches on "enabled"/"disabled"/"adaptive"; both spellings fell through to its adaptive branch, so chat_template_kwargs enable_thinking was accepted (served, echoed) yet changed nothing in the prompt ids: thinking on and off rendered the identical 156 rows. thinkingModeValue now reads the template's own dialect from its source, so thinking-off renders disabled (the </mm:think> assistant prefix) and templates without the dialect keep the generic spellings. 2. The request's own enable_thinking body field was ignored: the server read the switch only through chat_template_kwargs. thinkingFields now honors the body's field when the kwargs switch is absent (kwargs wins), so the field is honored wherever it names the switch. 3. The stage knob: minimax_m3_vl declares thinking_off (mirroring temperature_greedy's registry-alias pattern: family const + openWith(F)'s Info read + a family guard test). chat.prepare runs thinking-off engines without the reasoning header whatever the server config says, and an explicit thinking request (chat_template_kwargs or the body's field) refuses by exact name (ThinkingNotAttached) — the native lane has no thinking reserve attached, so thinking cannot be served honestly. Host suites on the canonical tree (zig 0.17.0): test-server-cpu 618/618, test-done-times-host 20/20 (new HTTP parser-level test: bare bodies prepare thinking=false; kwargs and body-level enable_thinking true refuse naming ThinkingNotAttached; body-level false prepares thinking=false on a thinking-capable engine), test-cuda-host 177/177 steps 451/452 tests passed (1 pre-existing recorded skip); lean_check vs merge-base 7938815 introduces no new problems; git diff --check clean. Lane-level boundary (measured on the live pair, receipts §4b): the greedy first draw at the assistant turn is the EOS token itself under every honest render, so chat content staying empty is a decode-fidelity defect of the native EXL3 row path on the real pack, upstream of the template — receipts report it verbatim; this commit carries the honest knob.
|
Thanks @styles01. This is a large and careful bring-up: kernel parity 10/10, tokenizer parity 19/19, and TP=2 boot over the lane core. New families are welcome when they're off by default and leave the rest of the engine byte-identical. Before we can land it as a served family:
Once those are in, we'll run it on our GB10 pair. - Synthema, Ash's AI Assistant. |
…act (act_mode 2) The MiniMax M3.1 routed experts' gate/up epilogue ran the generic act_mode-1 silu path; this family's contract is the OAI swiglu (alpha 1.702 sigmoid form; fp16 gate/up rows and an fp16 activation row before the down-side rotation — tf_m3_swigluoai's shape). The decode epilogue (tf_exl3_gateup_epilogue), the prompt-side tgateup and that path's eligible() gate now carry act_mode 2, and the family's experts upload names it. Layer-3 boundary rows against the f64 mirror: relmax 9.7e-2 -> 4.3e-4 (~200x); layers 0-2 bit-identical pre/post (footprint exact). Full-depth serving on the rebuilt binary now completes 'The capital of France is' (temperature 0, greedy) with real content where the pre-fix lane drew a forced first-draw EOS with empty content.
|
Root cause found and fixed in a493d13: the family's routed-experts upload ran the generic exl3 gate/up epilogue at act_mode 1 (plain SiLU) where this model's contract is the OAI swiglu (alpha 1.702 sigmoid form). The decode epilogue, the prompt-side gateup path, and its eligibility check now carry act_mode 2. Layer-3 boundary rows now match an f64 mirror to ~4e-4 relative (from ~1e-1), with layers 0-2 bit-identical before/after. Greedy first draws at full depth read real content ('The capital of France is' -> ' Paris') instead of the forced-EOS draw, and the host test suites pass. |
ashhart
left a comment
There was a problem hiding this comment.
Thanks @styles01, a lot of work here. We rebased it onto current main (build-glue and fields.zig conflicts, both sides kept), and Linux CUDA compiles on x86_64 and aarch64. Not ready for a device run yet:
Shared code and exactness
- GLM's EXL3 kernels gain new runtime branches: f32 input in
tf_exl3_experts_rot_in,act_mode 2intf_exl3_gateup_epilogueandtgateup. Please move them into family-owned entry points (or template instantiations), so the existing kernels' SASS stays byte-identical. __expf(approximate) is used inexl3_experts.cu,exl3_prompt.cuhandminimax_m3.cu. Please useexpf.exl3_experts_f64_tests.zigmoved the f64 checks from GLM'sact_mode 0to mode 1, leaving mode 0 checked on one pattern only, and there's no f64 check for mode 2. Please restore mode 0 on all IDs and add mode 2.tf_exl3_plan_finishwould also treat mode 2 as plain SiLU, soexl3_groupedshould refuse it.- The
tf_m3_*kernels need to be registered with the CUDA kernel contract (a standalone parity runner doesn't replace the SUMMARY). - The parity, f64 and TP results predate e1076a50, which changed the experts' arithmetic (9.7e-2 to 4.3e-4 relative error), so they need rerunning.
Serving
6. temperature_greedy serves a temperature-0.8 request greedy. Please drop it, and refuse sampled requests (greedy_only) until the keyed sampler is attached.
7. The decode is still a host smoke graph (per-layer syncs, norm and RoPE on the CPU, one stream, one-token prompt feed, drafts refused), and graph.zig caps keys at 4096 while the default context is 1,048,576, so a plain open fails. We land families once they serve through the lane core.
8. setenv on NCCL variables changes the whole process. Please pass them through the TP setup instead.
Out of scope here (they change every family): hf_text.zig's thinking_mode detection by quoted words, and honouring a body-level enable_thinking for all families in fields.zig. The Metal list entry for minimax_m3_vl should go too, since Metal can't open it.
Rules: lean reports 460 new findings, mostly multi-line comments; five new files exceed 600 lines (graph.zig 1061, serve.zig 1106, cuda_native.zig 793 and two test files), and build/cuda.zig and server/chat.zig go over 600 too. Commit fbf132f0's message names a machine; please reword it. The hub.zig test should use std.testing.tmpDir instead of fixed .zig-cache/tmp paths. A -> in a parameter comment in minimax_m3.cu crashes our launch-argument scanner, and zig fmt reports 10 files.
With #1235 (Metal): one family directory and name (minimax_m3), one strict config reader that accepts both the published text_config form and the converted root form (with rope_theta read, not hard-coded), quantization metadata read once through the core readers, #1235's markers/tool parser/thinking-off path, and one host reference each for the MSA selection and swigluoai that both backends test against. Please also give the checkpoint's full revision.
|
Hi @styles01, following Synthema's ask above about one family. We're bringing MiniMax-M3.1-Flash to CUDA as the One thought from reading your branch: once the family exists with the shared config and loaders, your EXL3 experts Happy to coordinate here or on #1280. |
What this changes
Adds the
minimax_m3_vlmodel family: MiniMax-M3.1-Flash (Mia-AiLab/MiniMax-M3.1-Flash-EXL3-EAQ-v1-3.06-bpw) running natively on CUDA, one change — the new family plus its minimal core glue. Fixes #1280 (the new-family gate issue; proposed gate and verification records summarized there).zig/src/families/minimax_m3_vl/— config, EXL3 weight load/unpack, sparse attention, layer graph, serve, CUDA-native adapter. Serves through the existing shared lane core (TP1) and over TP2 on 2 ranks (boot pattern mirrors glm's env-rank adapter incuda_native.zig; no scheduler, no parallel lane machinery).zig/kernels/cuda/minimax_m3.cu(tf_m3_scores/topk/decode/decode_combine/swigluoai) and additive changes toexl3_experts.cufor the family's routed experts.cuda.zig,cuda_kernels.zig), kernel vtable wiring (src/cuda/kernels.zig,src/native/cuda.zig), Metal-side an earlier gate task admission line (metal.zig, no Metal weight reader), CLI hub/info entries.ServeTextNeedsTokenizeruntil a tokenizer is provided; the ids path is bit-exact vs Hugging Facetokenizers0.22.2 (19/19 encode parity).open()— env-rank boot (TF_MINIMAXM3_RANK/WORLD), the row-control buffer that re-keys mirror rows on cache-length changes outside a put, and the weightShare shim letting one open lane serve the rank-0 HTTP path over the split pack.:8000answers/v1/modelswithMiniMax-3.1-Flashlive at fixture scale; the full-depth real-pack TP2 server transcript is run outside CI and lands as a follow-up comment on the issue.top_k/top_p/min_p/seed) or a grammar mask keeps refusing by exact name (SamplingNotAttached) rather than serve an ungraded draw. The reply documents itself:tensorfold.sampling: "greedy-deterministic (keyed sampler not attached; temperature is served as fixed greedy)". Implemented as an additive per-enginetemperature_greedyinfo flag (default false, glm/flashnext behavior untouched) resolved inresolveSampling, honoring the existinggreedy_onlydesign line. Reasoning/thinking default left as-is: there is no family-level thinking knob in the glm CUDA adapter to mirror (thinking switches are server-side config/request fields), so the default reasoning behavior stands and is noted here.verification
Mia-AiLab/MiniMax-M3.1-Flash-EXL3-EAQ-v1-3.06-bpw@ snapshotc33e7a66…; development trees at upstreammain7938815.max|delta|0e0 scores/marks/swigluoai, decode+combine 1.5e-5 = one fp16 ulp); real-pack f64 grade (first 2/60 layers, 35 rows): greedy tokens EXACT vs the independent f64 reference, logitsmax|delta|0.003237 (quant tolerance), 0 NaN/inf; TP1 vs TP2 lane-surface tokens EXACT (88638 88638 52236 40265, and smoke ids bar reproduced); TP2 weight split tiling verified (75.79/79.68 GiB, plan sum 166,928,433,556 bytes).n/a— new family bring-up; no previous release serves this checkpoint. Served decode on the real pack at fixture scale runs through the regular lane surface (drafts on).n/a— same reason; cold-prefill verification records to follow as a separate performance PR once the family lands.zig build test-cuda-hoston the submit tree): exit 0, 177/177 steps, 158/159 passed, 1 pre-existing recorded skip. On GB10 CUDA:tf-cuda-test minimax-m3parity 12/12 PASS;tf-cuda-test minimax-m3-serve fixture lcgids bar EXACT vs the the token bars. New tests: device parity harness + graph tests + TP2 rank tests (zig/tests/cuda/minimax_m3*.zig,minimax_tp2_tests.zig), incl. env-place refusal tests for the TP2 boot (TF_MINIMAXM3_RANK/WORLDvalidation) and the tokenizer-free-text refusal.:8000—/v1/modelslive; will land as a follow-up comment).Checklist
"draft": falseon the graded depths (token-for-token vs the f64 reference); resumed/fresh and concurrent/solo equalities verified on the lane surface.SUMMARYlines: parity 10/10 PASS (see verification records) — Metal has no kernels and no summary.n/afor bring-up (no baseline exists for this checkpoint).cuda_nativeadmission requires a CUDA module; refusal tests run device-free).Overlap note (ATC)
atc overlap --jsonat submit time: the working set overlaps open PRs sharing common build glue (zig/build/cuda.zig,zig/src/native/metal.zig+ kernel registry): #1235 (Metal minimax_m3 — different family, same checkpoint), #1009, #1268, #1271, #1272, #1241, #1254, #1240. All conflicts are at additive family/build registration points; happy to rebase when any of those land.