Skip to content
This repository was archived by the owner on Oct 4, 2026. It is now read-only.

feat(minimax-h3): single-file pruned/int8-convrot transformer support (PR 6) - #61

Merged
lstein merged 3 commits into
mainfrom
minimax-h3/06-single-file-int8
Aug 15, 2026
Merged

lstein merged 3 commits into
mainfrom
minimax-h3/06-single-file-int8

Conversation

@lstein

@lstein lstein commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Note

Port of lstein#145 to the InvokeAI-7 tree. Mechanical replay of the original commits onto v7 main (4a48a37967); openapi.json/schema.ts regenerated and verified drift-free at every level, and the dependencies.py auto-merge verified by hand. All changes target the backend and the existing frontend/web app - webv2 is untouched (H3 support in the new UI is future work). Review history and test notes live on the original PR.

Summary

PR 6 of the MiniMax H3 stack (on top of lstein#144): load Comfy-Org single-file H3 transformers — including the pruned int8-convrot fl2va (19.5 GiB) — as a checkpoint-format main model that overrides the transformer while the diffusers folder keeps providing the text encoder, tokenizer/processor, and both VAEs.

Why: the bf16 folder transformer is 62 GiB — on a 48 GB card only ~53% stays resident and every denoise step streams ~30 GiB from RAM. The pruned int8 file is fully resident with room to spare, and has ~40% fewer FLOPs (the AdaLN branches are replaced by a precomputed rank-8 time-embedding curve).

Format support (semantics verified against Comfy-Org/comfy-quants, comfy-kitchen, and ComfyUI's model code — reimplemented, not copied)

  • int8_tensorwise: per-output-channel symmetric int8 + fp32 scales, declared by <layer>.comfy_quant JSON markers. Weights stay int8 in VRAM (Int8ConvrotLinear buffers); dequant happens per forward in the compute dtype (~620 MB transient at the largest layer).
  • convrot: grouped-256 regular-Hadamard rotation folded into the stored weights. The Hadamard is symmetric orthonormal, so derotation is the same grouped matmul — no runtime activation rotation. Roundtrip pinned by tests against a reference quantizer.
  • Pruned "AdaLN curves": adaln_t_table [1025, 8] lerped at t∈[0,1] replaces the time embedder; rank-8 AdaLN projections, no silu, fp32 modulation. Implemented as a subclass of the vendored transformer (vendored file untouched). Timestep-domain equivalence with ComfyUI verified in review (t = 1−σ, keyframe pin 0.999, unique-t dedup).
  • The full bf16 and pruned bf16 single files load through the same path (time-embedder renames verified against the real HF file header via range request; the no-marker path skips the int8 swap).
  • fp8-scaled files are rejected early (header-only marker check) with a clear unsupported-quantization error. GGUF: out of scope. ref2va files are rejected by filename (keys are indistinguishable from fl2va).

Wiring

  • Main_Checkpoint_MiniMaxH3_Config (fingerprint: audio_patch_proj.weight + video_patch_proj.weight + blocks.0.attn.qkv_proj.weight — checked for collisions against every other family's single-file fingerprint, both directions); identification fixture included.
  • The checkpoint loader serves ONLY SubModelType.Transformer (Wan-GGUF precedent) and errors helpfully for anything else; shape-inferred config (verified key-for-key/shape-for-shape against the real 19.5 GiB header).
  • minimax_h3_model_loader v1.1.0: optional transformer_model field (checkpoint-format H3 mains); default workflows' pinned versions bumped.
  • Linear UI: "Transformer (single file)" combobox in Generation Settings → Advanced (gated on H3); checkpoint mains hidden from the main model picker; metadata recall for the override (skips silently if the model was uninstalled); params persist v5→v6 with migration fixtures.

Known limitations (review-documented)

  • Int8 layers are not autocast-wrapped: residency is all-or-nothing, so the single file needs ~20 GiB free VRAM. Graceful partial-load degradation remains the folder model's job.
  • ⚠ Version-collision ledger: upstream main is still params _version 4; this stack claims v5 (PR 4) and v6 (this PR), and the flux2 branch also claims v5 — whichever lands second renumbers.
  • A pre-existing global gap (all estimators): user-set max_cache_vram_gb bypasses working-memory reservations.

Testing

  • Backend: H3 suite 40 tests + identification (incl. new fixture) + workflow-registry — 144 passed; ruff (CI-pinned 0.11.2) clean.
  • Frontend: tsc / eslint / prettier / knip clean; 1720 tests passed; vite build OK. Schema regen committed (node field).
  • Adversarial fresh-context review completed; all findings fixed (full-file claim-then-crash, early fp8 rejection, dequant transient, modulation dtype, recall handler) or documented (all-or-nothing residency).
  • Manual gate: install the single file by direct path (probes as minimax-h3 / checkpoint / fl2va, pruned), select it under Advanced → Transformer (single file), run t2v; expect ~31 GiB VRAM steady state and no per-step streaming.

🤖 Generated with Claude Code

Loads MiniMax's single-file FL2VA transformer repacks (Comfy-Org layout):
bf16 or Comfy int8_tensorwise(+convrot) quantized, full or AdaLN-pruned.
The 19.5 GiB pruned-int8 file stays int8-resident in VRAM (fits a 45 GiB
card outright, vs ~53% partial load for the 62 GiB bf16 folder transformer).

- Int8ConvrotLinear: int8 weight + per-channel scale buffers, dequantized
  and derotated per forward. ConvRot is a grouped-256 regular-Hadamard
  rotation folded into the stored weights; H is symmetric orthonormal, so
  derotation is the same grouped matmul (semantics verified against
  Comfy-Org/comfy-quants + comfy-kitchen; reimplemented, not copied).
- MiniMaxH3PrunedTransformer3DModel: 'adaln curves' variant - the timestep
  MLP and full-width AdaLN inputs are replaced by a [1025, 8] fp32 curve
  table (lerp lookup, SiLU-free fp32 AdaLN projections), matching ComfyUI's
  reference semantics. Vendored transformer file untouched.
- State-dict converter: remote-code -> vendored diffusers key layout,
  incl. fused-qkv row splitting (scales split with their rows) and
  comfy_quant marker fan-out. Verified key-for-key + shape-for-shape
  against the real 19.5 GiB checkpoint header (dry run, no tensor data).
- Probe: Main_Checkpoint_MiniMaxH3_Config fingerprints on the H3-unique
  audio+video patch projections; ref2va excluded by filename; pruned
  detected via adaln_t_table. Stripped identification fixture generated
  from the real file's header.
- Loader: transformer-only (Wan-GGUF precedent); shape-inferred config;
  strict load after swapping quantized linears.
- Node: minimax_h3_model_loader v1.1.0 gains an optional 'Transformer
  (single file)' override; folder main keeps serving encoders + VAEs.
  Default-workflow pins bumped to match.
- UI: checkpoint-format H3 mains hidden from the main picker and offered
  in a MiniMax H3 advanced section combobox; params persist v5 -> v6
  (minimaxH3TransformerModel), migration + fixtures updated; wired through
  buildMiniMaxH3Graph + metadata.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
torch's CPU bf16 kernels dispatch on the host ISA, and the Windows CI
fleet is mixed hardware: on some runners the dequant matmul in this test
crashes the interpreter with an illegal instruction (0xC000001D). The
same commit passes on a re-run that lands on a different runner, and the
identical test passes on other PRs in this stack, so it is the machine
rather than the code - but at roughly one crash in three Windows runs it
is an ongoing re-run tax that reads like a real fault every time.

The bf16 rounding contract stays covered on Linux and macOS, and the
dequant path only ever runs on a GPU in practice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Base automatically changed from minimax-h3/05-starter-and-workflows to main August 15, 2026 19:07
Same persisted-state collision the linear-UI sync hit, one version on: this
branch added its MiniMax H3 single-file transformer override as v5 -> v6, and
main now also has a v5 -> v6 (the H3 duration/output-mode fields). paramsSlice.ts
and types.ts both auto-merged without a conflict, leaving two consecutive
`if (state._version === 5)` blocks that each assign 6 — the second never runs, so
minimaxH3TransformerModel is never backfilled, zParamsState.parse() throws on the
missing key and redux-persist wipes the whole params slice on upgrade.

The transformer-override step therefore becomes v6 -> v7, with
zParamsState._version and getInitialParamsState bumped to 7.

The v5 -> v6 H3 seeds are also made conditional (`?? 5` / `?? 'video'`) to match
every other step in this migration. They were unconditional, so migrating a blob
that already carried a duration reset it to the default — which is what this
branch's own v5 test caught once the two steps were chained.

Other conflicts were keep-both: modelsByType.ts (isMiniMaxH3CheckpointMainModelConfig
alongside main's isMistralEncoderModelConfig) and the migration tests. schema.ts
was regenerated from the merged source rather than hand-merged, with openapi.json
alongside.

Vendored check: the five minimax_h3 files still differ from
huggingface/diffusers@abc5e9bf71 by import lines only.

Verified: 892 passed / 91 skipped backend, 1739 frontend tests across 144 files,
tsc clean, ruff check + format clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lstein
lstein merged commit e78ffe5 into main Aug 15, 2026
19 checks passed
@lstein
lstein deleted the minimax-h3/06-single-file-int8 branch August 15, 2026 20:43
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant