NicoLab28 commited on
Commit
4838201
Β·
verified Β·
1 Parent(s): 6b64cf8

README for 0.1.3: named people, the -mlp compatibility trap, corpus sources

Browse files
Files changed (1) hide show
  1. README.md +93 -61
README.md CHANGED
@@ -12,100 +12,117 @@ base_model:
12
  library_name: comfyui
13
  ---
14
 
15
- # ClipProj β€” MiniMax H3 conditioning from a Qwen3-VL-4B
16
 
17
- **Projection matrices that let a Qwen3-VL-4B replace the Qwen3-VL-32B text encoder of MiniMax H3.**
18
 
19
- **15.7 GB β†’ 5.2 GB of VRAM**, with no change to the diffusion model, the VAEs or the sampler.
20
 
21
  > ⚠️ **Proof of concept β€” working, but a proof of concept.** It runs and produces good video, and every number below was measured on real hardware. Built and tested on a single setup (Windows 11, NVIDIA, ComfyUI 0.31.0) with deliberately limited exploration.
22
 
23
  These files are useless on their own. They require the custom node:
24
  **[github.com/nicolab28/ComfyUI-ClipProj](https://github.com/nicolab28/ComfyUI-ClipProj)**
25
 
26
- ## Now in safetensors
27
 
28
- Every matrix is available as `.safetensors`. **Prefer it.** A `.pt` goes through pickle, which can execute arbitrary code the moment it is opened β€” an absurd risk for files that hold nothing but tensors and a handful of scalars. The scalars now live in the safetensors header and the node converts them back on load.
29
 
30
- Reading them needs node **v0.1.2**. The `.pt` files are kept alongside for now so that anyone still on an older node has something that works; they will go away in a later release.
31
 
32
- *Requested by @lucas-ai26, who was right.*
33
-
34
- ## Re-download if you got the first release
35
-
36
- **The first release was missing the attention-sink vector, and short prompts suffered for it.** The first token of a sequence is an attention sink: its direction is constant from one prompt to the next (cosine 1.0000 measured over 1966 prompts) and it carries nothing from the text, yet its norm reaches 16 500 against 291 for a text token. Calibration excluded it, rightly β€” its extreme values would wreck the statistics β€” but the node projected it anyway, through a matrix that had never seen one, producing an arbitrary vector of enormous norm. Invisible on a 200-token prompt where it is 0.5 % of the positions, ruinous on a 7-token one where it is 14 %.
37
-
38
- Every matrix here now carries the measured sink vector and substitutes it at inference. Since the vector is constant, this is not an approximation: it is the value the 32B would have produced.
39
-
40
- Also new in this release: an **8B matrix**, an **int8_convrot matrix**, and **ref2va now works** β€” see below.
41
 
42
  ## What this is
43
 
44
- MiniMax H3 conditions on a Qwen3-VL-32B truncated to 50 layers β€” 15.7 GB in NVFP4 β€” solely to turn a prompt into a `[seq, 5120]` tensor. This repository provides a learned linear map that lets a much smaller Qwen3-VL produce the same conditioning:
45
 
46
  ```
47
  cond = ((h - mean_in) / std_in) @ W * std_out + mean_out
48
  ```
49
 
50
- It works because every Qwen3-VL shares the **same tokenizer** (151936 tokens): a prompt yields the same tokens at the same positions in both models, so a position-by-position mapping between their hidden states can be learned. Calibration is plain **ridge regression** β€” no gradients, no epochs, no learning rate.
 
 
51
 
52
  ## Files
53
 
54
  Put them in `ComfyUI/models/clip_projections/`.
55
 
56
- **Start with `h3_qwen3vl_4b_tap24`.** It is calibrated on eight times more data than anything else here.
57
 
58
- | File | Use it for | Encoder | Corpus | Test cosine |
59
  |---|---|---|---|---|
60
- | `h3_qwen3vl_4b_tap24` | **the default** | any Qwen3-VL-4B | 2 000 prompts | 0.712 |
61
- | `h3_qwen3vl_8b_tap24` | any 8B encoder | any Qwen3-VL-8B | 203 prompts | 0.731 |
62
- | `h3_control_identity` | control β€” run it once | β€” | β€” | β€” |
63
- | `h3_control_zero` | control β€” run it once | β€” | β€” | β€” |
64
- | `h3_qwen3vl_4b_CONDPROJ_tap24` | see below | any Qwen3-VL-4B | 240 prompts | 0.845 * |
65
- | `h3_qwen3vl_8b_CONDPROJ_tap24` | see below | any Qwen3-VL-8B | 203 prompts | 0.860 * |
66
- | `h3_qwen3vl_4b_int8convrot_tap24` | comparison only | any Qwen3-VL-4B | 240 prompts | 0.697 |
67
- | `h3_qwen3vl_4b_tap24_200prompts` | comparison only | any Qwen3-VL-4B | 200 prompts | 0.699 |
 
 
68
 
69
- \* measured in a different space and **not comparable to the other rows**. See below, and do not read it as better.
70
 
71
- Every matrix works on **any variant of its own size**: the measured cosine gap between a bf16-calibrated matrix applied to an abliterated fp8 encoder is 0.0023. You do not need the exact checkpoint a matrix was calibrated on. The 8B matrices need an 8B encoder though, 4096 input dimensions instead of 2560, and the node checks and refuses a mismatch.
72
 
73
- The **8B matrices need an 8B encoder** β€” different input width, 4096 instead of 2560, so they are not interchangeable with the 4B ones. The node checks the width and refuses a mismatch.
74
 
75
- Every matrix here works on **any variant of its own size**: the measured cosine gap between a bf16-calibrated matrix applied to an abliterated fp8 encoder is 0.0023. You do not need the exact checkpoint a matrix was calibrated on.
76
 
77
- ## The CONDPROJ matrices, and why they are not the default
78
 
79
- They are kept because the comparison posted on r/StableDiffusion ran on them, so the links have to keep working. They are **not better**, and the story is worth telling because the mistake was instructive.
80
 
81
- The DiT does not consume the conditioning as it arrives: it first passes it through `condition_proj`, a `Linear(5120 β†’ 5376)` feeding the token refiner. That layer's spectrum is very uneven, a factor of 45 between the top and bottom deciles of its singular values, and 52 % of the energy in 10 % of the directions. Plain ridge regression ignores this and spends as much effort on a direction the DiT will multiply by 0.10 as on one it will multiply by 37. Calibrating against the **output** of that layer instead, then mapping back through the pseudo-inverse, should therefore minimise the error the DiT actually sees. The cosine went from 0.697 to 0.845 on the 4B and 0.731 to 0.860 on the 8B.
 
 
 
82
 
83
- Then I compared what the two matrices actually output, on random inputs:
84
 
85
- ```
86
- 4B CONDPROJ against unweighted, same corpus cosine 0.999998
87
- 8B CONDPROJ against unweighted, same corpus cosine 0.999999
88
- 4B CONDPROJ against the 2000-prompt matrix cosine 0.744034
89
- ```
90
 
91
- They are the same function. The entire gain was an artefact of measuring in a different space: applying an invertible transform to both vectors before taking a cosine flatters agreement on the dominant directions.
92
 
93
- The reason is simple in hindsight. Unregularised least squares is invariant to an invertible linear transform of the targets, so fitting in one space and mapping back recovers the same map. Only the ridge penalty breaks that invariance, and with 37 851 training tokens against Ξ» = 1000 it barely binds. The idea would matter with far less data or far stronger regularisation. It does not matter here.
94
 
95
- *The idea came from u/stddealer on r/StableDiffusion, and it was a good one. The measurement is on me: I published the cosine before checking whether the matrix had changed at all.*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  ## Measured results
98
 
99
- | Corpus | Tokens | Cross-prompt CKA | Test cosine | Test RΒ² |
100
- |---|---|---|---|---|
101
- | 200 prompts | 37 361 | 0.95 | 0.699 | 0.490 |
102
- | 2 000 prompts | 288 608 | 0.92 | **0.712** | 0.507 |
 
 
 
 
 
 
103
 
104
- Eight times the data buys 1.8 % of cosine: the linear projection is **at its ceiling**, not starved of data. Going further needs an MLP, not more prompts.
105
 
106
- A cosine of 0.71 sounds poor and is not β€” the DiT tolerates far more than the metric suggests. What holds up in actual generation: simple prompts, structured multi-shot prompts (four distinct cuts with no bleed between them), fl2va with first and last frame, and ref2va with a reference image.
107
 
108
- Fidelity does **not** collapse on short prompts, contrary to what one might expect from a corpus of long ones: measured per-token cosine goes from 0.937 at 80 words down to 0.908 at 2 words, a 3 % decline, once the sink is handled.
 
 
109
 
110
  ## Run the controls first
111
 
@@ -113,25 +130,40 @@ The two control matrices exist to prove the learned matrix is doing the work rat
113
 
114
  | Matrix | Output for *"a red ball on a wood table"* |
115
  |---|---|
116
- | `h3_control_zero.safetensors` | a countryside landscape β€” the prompt is entirely ignored |
117
- | `h3_control_identity.safetensors` | a golden object in flames β€” unusable |
118
- | `h3_qwen3vl_4b_tap24.safetensors` | the red ball on a wood table |
119
 
120
  `β€–W_identityβ€– = 50.6` against `β€–W_learnedβ€– = 52.4` β€” near-identical energy, so the difference is structural, not a matter of scale.
121
 
122
  **If the identity control ever looks fine, the learned matrix adds nothing β€” and you want to know that before trusting it.**
123
 
124
- ## Known limitations
125
 
126
- **Named references depend on the encoder, not on the projection.** This was measured rather than guessed: asking the encoder to describe a person in plain text β€” which bypasses the matrix entirely β€” shows where the knowledge actually stops. The 4B places Scarlett Johansson correctly as Black Widow but believes she has dark brown hair; the 8B describes her correctly as blonde with blue eyes. So when a proper noun renders as the wrong person, question the encoder first.
127
 
128
- The practical workaround is to describe rather than name: *"the actress X as [role], blonde, ..."* recovers an identity that the bare name loses, on both the 4B and the 8B. A name is a fragile signal carried by two or three tokens; a description spreads it over a dozen redundant ones and the reconstruction error averages out instead of accumulating.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
130
- Note that this does not apply to **ref2va**: there the identity comes from the reference image, so the encoder's factual gaps stop mattering.
131
 
132
- **Quantisation costs facts.** The same test on `int8_convrot` against `bf16` shows two factual errors appearing under quantisation. Fine for general use, worth knowing if your prompts lean on proper nouns.
133
 
134
- **Speech in languages other than English degrades.** Reported and reproduced with French: the 32B pronounces it cleanly, a projected 4B or 8B does not. This is *not* a corpus problem β€” measured on identical English prompts differing only in the quoted line, French tokens reconstruct at 0.8974 against 0.8996 for English, which is noise. A cosine of 0.90 is ample for visual semantics and insufficient for phonetics; the DiT's audio branch is far more demanding than its image branch, and a language the model handles less confidently has less margin to absorb the error.
135
 
136
  ## Required models
137
 
@@ -139,7 +171,7 @@ Note that this does not apply to **ref2va**: there the identity comes from the r
139
  |---|---|
140
  | Diffusion model + VAEs | [Comfy-Org/MiniMax-H3](https://huggingface.co/Comfy-Org/MiniMax-H3) |
141
  | Text encoder, 4B | [Comfy-Org/Krea-2](https://huggingface.co/Comfy-Org/Krea-2) β†’ `text_encoders/qwen3vl_4b_fp8_scaled.safetensors` |
142
- | Text encoder, 8B | any ComfyUI-format Qwen3-VL-8B (the 8B matrix expects 4096 input dims) |
143
 
144
  The 32B text encoder is **no longer needed** β€” that is the entire point.
145
 
@@ -158,4 +190,4 @@ You remain responsible for what you generate and for complying with the licences
158
 
159
  ## Credits
160
 
161
- Vibe-coded with **Anthropic Claude Code (Opus 5)**. Every number quoted was measured on real hardware, not estimated: where a prediction turned out wrong, the measurement won and the text was corrected.
 
12
  library_name: comfyui
13
  ---
14
 
15
+ # ClipProj β€” MiniMax H3 conditioning from a Qwen3-VL-4B or 8B
16
 
17
+ **Projection matrices that let a small Qwen3-VL replace the Qwen3-VL-32B text encoder of MiniMax H3.**
18
 
19
+ **15.7 GB β†’ 4.5 GB of VRAM**, with no change to the diffusion model, the VAEs or the sampler.
20
 
21
  > ⚠️ **Proof of concept β€” working, but a proof of concept.** It runs and produces good video, and every number below was measured on real hardware. Built and tested on a single setup (Windows 11, NVIDIA, ComfyUI 0.31.0) with deliberately limited exploration.
22
 
23
  These files are useless on their own. They require the custom node:
24
  **[github.com/nicolab28/ComfyUI-ClipProj](https://github.com/nicolab28/ComfyUI-ClipProj)**
25
 
26
+ ## Update to node 0.1.3, and re-download
27
 
28
+ Two reasons, one of them silent.
29
 
30
+ **The `-mlp` matrices carry a residual network, and an older node ignores it without saying so.** It reads the matrix, finds keys it does not know, drops them, and applies the linear part alone. Nothing fails, nothing warns, and you end up judging the plain matrix while believing you tested the residual. Node 0.1.3 reads them.
31
 
32
+ **Everything is renamed.** The old `h3_qwen3vl_*` files have moved to `obsolete/` and the `.pt` copies are gone: opening a pickle executes code, which makes no sense for a file holding six tensors. If a workflow of yours names an old file, either point it at `obsolete/` or, better, switch to the new set.
 
 
 
 
 
 
 
 
33
 
34
  ## What this is
35
 
36
+ MiniMax H3 conditions on a Qwen3-VL-32B truncated to 50 layers β€” 15.7 GB in NVFP4 β€” solely to turn a prompt into a `[seq, 5120]` tensor. This repository provides a learned map that lets a much smaller Qwen3-VL produce the same conditioning:
37
 
38
  ```
39
  cond = ((h - mean_in) / std_in) @ W * std_out + mean_out
40
  ```
41
 
42
+ and, in the `-mlp` files, plus the output of a small residual network fed the same standardised input.
43
+
44
+ It works because every Qwen3-VL shares the **same tokenizer** (151936 tokens): a prompt yields the same tokens at the same positions in both models, so a position-by-position mapping between their hidden states can be learned. The matrix is fitted by plain **ridge regression** β€” no gradients, no epochs, no learning rate. The residual network is the only part that is trained.
45
 
46
  ## Files
47
 
48
  Put them in `ComfyUI/models/clip_projections/`.
49
 
50
+ **Start with `mmh3-8b-ClipProj-celeb-mlp` if you have the VRAM, `mmh3-4b-ClipProj-celeb-mlp` otherwise.**
51
 
52
+ | File | Encoder | Names covered | Residual | Test cosine |
53
  |---|---|---|---|---|
54
+ | `mmh3-4b-ClipProj` | any Qwen3-VL-4B | no | no | 0.7169 |
55
+ | `mmh3-4b-ClipProj-mlp` | any Qwen3-VL-4B | no | yes | 0.7944 |
56
+ | `mmh3-4b-ClipProj-celeb` | any Qwen3-VL-4B | **yes** | no | 0.7095 |
57
+ | `mmh3-4b-ClipProj-celeb-mlp` | any Qwen3-VL-4B | **yes** | yes | 0.7930 |
58
+ | `mmh3-8b-ClipProj` | any Qwen3-VL-8B | no | no | 0.7528 |
59
+ | `mmh3-8b-ClipProj-mlp` | any Qwen3-VL-8B | no | yes | 0.7970 |
60
+ | `mmh3-8b-ClipProj-celeb` | any Qwen3-VL-8B | **yes** | no | 0.7466 |
61
+ | `mmh3-8b-ClipProj-celeb-mlp` | any Qwen3-VL-8B | **yes** | yes | **0.8037** |
62
+ | `mmh3-ClipProj-control-zero` | β€” | control, run it once | β€” | β€” |
63
+ | `mmh3-ClipProj-control-identity` | β€” | control, run it once | β€” | β€” |
64
 
65
+ All eight are calibrated on the same general corpus and measured on the same held-out prompts, so the column is comparable across every row.
66
 
67
+ Every matrix works on **any variant of its own size**: the measured cosine gap between a bf16-calibrated matrix applied to an abliterated fp8 encoder is 0.0023. You do not need the exact checkpoint a matrix was calibrated on. The 8B matrices need an 8B encoder though β€” 4096 input dimensions instead of 2560 β€” and the node checks the width and refuses a mismatch.
68
 
69
+ ## Named people
70
 
71
+ **This is what changed in 0.1.3, and it was a corpus problem.**
72
 
73
+ The calibration corpus named a person on about 70 lines out of 8632, roughly 0.02 % of the training tokens. The directions of the hidden space that carry an identity were therefore constrained by nothing at all, and the fit put whatever minimised the error on landscape descriptions there. Named people came out as somebody else.
74
 
75
+ The `-celeb` matrices add 500 people, ranked by popularity, with five short prompts and two long ones each. What it buys and what it costs:
76
 
77
+ | | name tokens | rest of the sentence | general test set |
78
+ |---|---|---|---|
79
+ | without | 0.8265 | 0.9358 | 0.7944 |
80
+ | with | **0.8844** | **0.9516** | 0.7930 |
81
 
82
+ Seven thousandths of cosine on the general corpus, for six points on the tokens that carry an identity. The rest of the sentence improves too, because the celebrity prompts are short and the general corpus had nothing under fifteen words.
83
 
84
+ Two findings that decide how far this is worth pushing.
 
 
 
 
85
 
86
+ **Two contexts per person are enough.** Measured on contexts held out for people the matrix had seen: 0.9875 at two, 0.9945 at five, 0.9986 at twenty. Forty is a waste.
87
 
88
+ **Five hundred names generalise to names never seen.** A held-out band at popularity ranks 501 to 540, absent from every calibration, reconstructs at 0.8795 against 0.8844 for the covered ones. Covering 500 people does not teach 500 names; it teaches the map how to handle that region of the space. Going to several thousand would buy very little.
89
 
90
+ **What still fails is not the corpus.** Characters whose identity is a mask rather than a face come out as a stranger wearing the right costume. People whose fame predates the era when everything was photographed come out wrong or generic. And some names fail on the plain 32B too, so run the reference before blaming the projection β€” that check has overturned three of my own conclusions.
91
+
92
+ ## Where the calibration data comes from
93
+
94
+ The general corpus is [GokuScraper/seedance-2-prompts-datasets](https://huggingface.co/datasets/GokuScraper/seedance-2-prompts-datasets), filtered to prompts of fifteen words or more and deduplicated: 8632 lines, median 128 words. The 500 named people come from a TMDB export published on Kaggle, ranked by popularity, with transliterated names dropped beyond rank 1000.
95
+
96
+ Around each name, five short prompts are generated from templates, and two longer ones in MiniMax H3's section format are written by Mistral Small and Gemini Flash Lite, half each. Everything needed to rebuild the corpus is in the node's `calibration/` folder, including the system prompt the long prompts were written from.
97
+
98
+ ## The residual network
99
+
100
+ The `-mlp` files carry a `d_in β†’ 16384 β†’ 5120` network with a GELU, added to the matrix rather than replacing it. Its last layer is initialised to zero, so at the first step the model reproduces the matrix exactly and can only improve on it. It is worth 0.05 to 0.08 of cosine, four times what multiplying the corpus by eleven buys the linear map.
101
+
102
+ **Which of the two renders better is not settled.** The cosine does not predict it β€” that is the single most repeated lesson of this project. Try both on your own prompts.
103
+
104
+ Two things measured while building it. Width beats depth: at equal parameter count, two hidden layers of 8192 reach 0.7691 against 0.7944 for one layer of 16384. And a residual extrapolates worse than a matrix does β€” outside the corpus it saw, a linear map degrades gracefully while the network collapses.
105
 
106
  ## Measured results
107
 
108
+ | | 4B | 8B |
109
+ |---|---|---|
110
+ | matrix, no names | 0.7169 | 0.7528 |
111
+ | matrix + residual | 0.7944 | 0.7970 |
112
+ | matrix, names covered | 0.7095 | 0.7466 |
113
+ | matrix + residual, names covered | 0.7930 | **0.8037** |
114
+
115
+ A cosine of 0.79 sounds poor and is not β€” the DiT tolerates far more than the metric suggests. What holds up in actual generation: simple prompts, structured multi-shot prompts with several distinct cuts and no bleed between them, fl2va with first and last frame, ref2va with a reference image, and since 0.1.3 ref2va with a reference video.
116
+
117
+ Fidelity does **not** collapse on short prompts: measured per-token cosine goes from 0.937 at 80 words to 0.908 at 2 words, once the attention sink is handled.
118
 
119
+ ## Speech
120
 
121
+ The first release lost non-English speech: a French line came out half Spanish, and the 8B put everything in English. That was the clearest regression and I could not explain it then.
122
 
123
+ With `mmh3-8b-ClipProj-celeb-mlp`, a three-shot clip carrying English, French and Spanish comes out like the 32B does, and the audio level gap measured against the reference has gone from 7.6 dB to 3.5.
124
+
125
+ Part of what was blamed on the projection was not the projection. A line that fills more than about two thirds of its shot comes out slurred whatever encoder produced the conditioning β€” the fix is a longer shot, not a better matrix. And MiniMax H3 expects speech wrapped in `<d>[Language] ...</d>` with a stable speaker id declared beforehand; without that, one voice with one accent is used for the whole clip. Neither of those is documented here because neither is ours, but both cost me a day.
126
 
127
  ## Run the controls first
128
 
 
130
 
131
  | Matrix | Output for *"a red ball on a wood table"* |
132
  |---|---|
133
+ | `mmh3-ClipProj-control-zero` | a countryside landscape β€” the prompt is entirely ignored |
134
+ | `mmh3-ClipProj-control-identity` | a golden object in flames β€” unusable |
135
+ | a learned matrix | the red ball on a wood table |
136
 
137
  `β€–W_identityβ€– = 50.6` against `β€–W_learnedβ€– = 52.4` β€” near-identical energy, so the difference is structural, not a matter of scale.
138
 
139
  **If the identity control ever looks fine, the learned matrix adds nothing β€” and you want to know that before trusting it.**
140
 
141
+ ## What is in obsolete/
142
 
143
+ The previous matrices, kept because a comparison posted on r/StableDiffusion ran on them and the links have to keep working. They have no name coverage and are calibrated on a corpus thirty times smaller. There is no reason to prefer them.
144
 
145
+ Among them, the `CONDPROJ` pair, and the story is worth telling because the mistake was instructive.
146
+
147
+ The DiT does not consume the conditioning as it arrives: it first passes it through `condition_proj`, a `Linear(5120 β†’ 5376)` feeding the token refiner. That layer's spectrum is very uneven β€” a factor of 45 between the top and bottom deciles of its singular values, 52 % of the energy in 10 % of the directions. Plain ridge regression ignores this and spends as much effort on a direction the DiT will multiply by 0.10 as on one it will multiply by 37. Calibrating against the **output** of that layer instead, then mapping back through the pseudo-inverse, should therefore minimise the error the DiT actually sees. The cosine went from 0.697 to 0.845 on the 4B and 0.731 to 0.860 on the 8B.
148
+
149
+ Then I compared what the two matrices actually output:
150
+
151
+ ```
152
+ 4B CONDPROJ against unweighted, same corpus cosine 0.999998
153
+ 8B CONDPROJ against unweighted, same corpus cosine 0.999999
154
+ ```
155
+
156
+ They are the same function. Unregularised least squares is invariant to an invertible linear transform of the targets, so fitting in one space and mapping back recovers the same map; only the ridge penalty breaks that invariance, and with 37 851 training tokens against Ξ» = 1000 it barely binds. The entire gain was an artefact of measuring in a different space.
157
+
158
+ *The idea came from u/stddealer on r/StableDiffusion, and it was a good one. The measurement is on me: I published the cosine before checking whether the matrix had changed at all.*
159
+
160
+ ## Known limitations
161
 
162
+ **Quantisation costs facts.** Comparing `int8_convrot` against `bf16` on factual recall shows errors appearing under quantisation. Fine for general use, worth knowing if your prompts lean on proper nouns.
163
 
164
+ **Masks defeat identity.** A character recognised by a costume rather than a face comes out as an unknown person in the right suit. No corpus fixes that, because the identity is not in the name's representation to begin with.
165
 
166
+ **Counting is unreliable, and not because of the projection.** Ask for three of something and you get four, on the 32B too. Enumerating works better than announcing a number.
167
 
168
  ## Required models
169
 
 
171
  |---|---|
172
  | Diffusion model + VAEs | [Comfy-Org/MiniMax-H3](https://huggingface.co/Comfy-Org/MiniMax-H3) |
173
  | Text encoder, 4B | [Comfy-Org/Krea-2](https://huggingface.co/Comfy-Org/Krea-2) β†’ `text_encoders/qwen3vl_4b_fp8_scaled.safetensors` |
174
+ | Text encoder, 8B | any ComfyUI-format Qwen3-VL-8B (the 8B matrices expect 4096 input dims) |
175
 
176
  The 32B text encoder is **no longer needed** β€” that is the entire point.
177
 
 
190
 
191
  ## Credits
192
 
193
+ Vibe-coded with **Anthropic Claude Code (Opus 5)**. Every number quoted was measured on real hardware, not estimated: where a prediction turned out wrong, the measurement won and the text was corrected. Three claims in the previous version of this file were wrong and are corrected here.