Skip to content

feat(minimax-h3): single-file truncated int8-convrot Qwen3-VL text encoder (PR 7) - #146

Open
lstein wants to merge 1 commit into
minimax-h3/06-single-file-int8from
minimax-h3/07-quantized-text-encoder
Open

feat(minimax-h3): single-file truncated int8-convrot Qwen3-VL text encoder (PR 7)#146
lstein wants to merge 1 commit into
minimax-h3/06-single-file-int8from
minimax-h3/07-quantized-text-encoder

Conversation

@lstein

@lstein lstein commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Summary

PR 7 of the MiniMax H3 stack (on top of #145): load Comfy-Org's layer-truncated int8-convrot Qwen3-VL-32B text encoder (qwen3vl_32b_minimax_h3_int8_convrot.safetensors, 25.3 GiB) as a single-file override for the H3 folder's 62 GiB encoder. Tokenizer and processor still come from the folder.

Why: the bf16 encoder is ~64 GB of RAM/VRAM working set — two-thirds of a 96 GB machine by itself, and the reason concurrent H3 sessions thrash. The quantized file is fully resident on one GPU, drops session RAM by ~40 GB, and encodes ~22% faster (50 layers computed instead of 64).

The subtle part: truncation semantics

The file ships 50 of 64 layers, no model.norm, no lm_head, and declares its contract in safetensors metadata: "output": "unnormalized_hidden_after_layer_50". H3 conditioning reads hidden_states[50] unnormalized — but on a truncated stack, transformers tie-replaces the last hidden-states entry with the post-final-norm tensor. The loader therefore installs nn.Identity() as the final norm, making hidden_states[50] bit-identical to the raw last-layer output. Verified two ways:

  • a cross-depth numerical test (full model vs truncated+Identity with copied weights → bit-identical hidden_states[k]), with a negative control showing a surviving real norm corrupts the tensor;
  • the depth guard accepts exactly-50-layers only when the final norm is Identity (and still accepts the full folder encoder).

The absent lm_head is tied to the embeddings (never used — H3 is hidden-states-only conditioning).

Wiring

  • Qwen3VLEncoder_Checkpoint_MiniMaxH3_Config: metadata-primary fingerprint + structural key checks on both paths; cross-probed against Krea-2's 4B encoder configs and PR 6's transformer config in both directions; identification fixture from the real file header. nvfp4 files rejected before the 25 GiB read.
  • Checkpoint loader (int8 machinery shared with PR 6): serves only SubModelType.TextEncoder; bundled Apache-2.0 config (field-identical to the folder's, verified) with layer-count override; strict load asserting the missing set is exactly {lm_head.weight}.
  • minimax_h3_model_loader v1.2.0: optional text_encoder_model field; default-workflow pins bumped; schema regen.
  • Linear UI: second "Text Encoder" override picker in the Advanced group; params persist v6→v7 with migration fixtures; metadata + recall.
  • Krea-2 isolation: all three consumers of the encoder list (picker hook, modelsLoaded, modelSelected auto-select) now use a Krea-2-scoped guard, with an unmocked regression test proving the H3 encoder is never auto-selected for Krea-2.

Known limitations

Testing

Backend 151 passed (H3 suite 46, identification 90 incl. new fixture, workflow-registry 15); ruff 0.11.2 clean. Frontend tsc/eslint/prettier/knip clean, 1722 tests, vite build OK. Real-header dry-run: all 1602 tensors map key-for-key; 350 int8 swaps. Adversarial fresh-context review completed; the one confirmed defect (unscoped Krea-2 auto-select) fixed with regression coverage.

Manual gate: download the encoder file, install by direct path (probes as qwen3_vl_encoder / checkpoint / minimax-h3), select under Advanced → Text Encoder, generate t2v; expect prompt adherence indistinguishable from the folder encoder and session RAM down ~40 GB.

🤖 Generated with Claude Code

…coder support

Loads Comfy-Org's qwen3vl_32b_minimax_h3_{bf16,int8_convrot} single files (25.3 GiB int8 vs
the folder's 62 GiB bf16) as a text-encoder override on the H3 model loader node. The files
are a 50-layer truncation matching H3's conditioning contract (unnormalized hidden state
after layer 50; no final norm, no LM head - the loader installs an Identity norm and ties
the unused head to the embeddings). Quantized layers reuse the PR-6 Int8ConvrotLinear
runtime; tokenizer/processor still come from the diffusers folder. nvfp4 repacks are
rejected from the header before any tensor read.

Probe keys on the file's explicit minimax_h3_te safetensors metadata (structural fallback
for stripped re-uploads); Krea-2's Qwen3VLEncoder pickers and default-encoder selection are
scoped to exclude the H3-truncated encoders. Params persist v6->v7 (adds
minimaxH3TextEncoderModel); loader node v1.2.0; metadata + recall wired.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lstein
lstein force-pushed the minimax-h3/06-single-file-int8 branch from 1f1a342 to ed05671 Compare August 6, 2026 00:28
@lstein
lstein force-pushed the minimax-h3/07-quantized-text-encoder branch from 471df42 to 724ad4d Compare August 6, 2026 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant