amisima commited on
Commit
24d1416
·
verified ·
1 Parent(s): 24f0fef

Upload README.md

Browse files
Files changed (1) hide show
  1. README.md +64 -154
README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: MiniMax-H3 · reference · Custom lora + CivitAI links, GPU cost, profiles, clip stitching
3
  emoji: 🎭
4
  colorFrom: pink
5
  colorTo: purple
@@ -7,7 +7,7 @@ sdk: gradio
7
  sdk_version: 6.20.0
8
  app_file: app.py
9
  pinned: true
10
- short_description: Video + soundtrack, your lora + CivitAI, cost, profiles
11
  suggested_hardware: zero-a10g
12
  tags:
13
  - video
@@ -26,27 +26,49 @@ audio references, at **bfloat16 with no quantization anywhere**.
26
 
27
  This Space is the denoising half of the `ref2va` task: the 61.73 GiB `transformer_ref` partition and the two
28
  autoencoders. The 62.14 GiB Qwen3-VL conditioner runs in
29
- [`qwen3vl-conditioner`](https://huggingface.co/spaces/multimodalart/qwen3vl-conditioner), which this
30
- Space calls over the gradio API for every request — the same conditioner Space, and the same resident weights, that
31
- the keyframe half [`minimax-h3`](https://huggingface.co/spaces/multimodalart/minimax-h3)
32
- uses.
33
 
34
  ## What this fork adds
35
 
36
- **Custom lora, three slots.** A Hugging Face repo, a file inside one, a **CivitAI download link**, or an uploaded
 
 
 
 
 
 
 
 
 
37
  file. Adapters have to be trained against the `transformer_ref/` partition — a `transformer/` adapter is a different
38
  partition and will not match.
39
 
40
- **kohya / CivitAI files are converted on the fly.** Most of what CivitAI carries is kohya-named, and it differs from
41
- diffusers in ways that quietly ruin a result rather than raise: flat underscored names, a fused QKV interleaved *per
42
- attention head* (a plain three-way split hands q's rows to k), a gated MLP whose two halves sit in the opposite
43
- order, and an `alpha` that sets the scale. All four are handled at load time, and `.pt` files are read as well as
44
- `.safetensors`.
45
-
46
- **Turbo presets.** Few-step distillations that render joint video + soundtrack in 4–8 steps instead of the usual
47
- ~28. Picking one fills a free slot at strength `1.0` and moves the steps slider to the count it was distilled for.
48
-
49
- **Clip stitching, soundtrack included.** Queue several clips and they are joined into a single file; tick the box and
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  every new generation joins on its own. It runs on the CPU, so it costs nothing from the GPU allowance. Everything is
51
  scaled to the first clip's frame, and a clip without audio gets silence rather than breaking the join.
52
 
@@ -58,10 +80,6 @@ so a request that will not fit is visible as a number rather than as a failed ru
58
  **Randomize seed**, drawn fresh per press and written back into the box, so the number shown is always the one the
59
  clip was made with.
60
 
61
- Set `CIVITAI_TOKEN` under *Settings → Variables and secrets* for gated CivitAI models: the Space downloads on its own
62
- machine, so being signed in to CivitAI in a browser does not authenticate it — a gated model answers a server with an
63
- HTML login page, which is caught and reported rather than saved as weights.
64
-
65
  ## Why split
66
 
67
  MiniMax-H3 is 195.9 GiB in bfloat16 and a ZeroGPU Space is evicted at **150 GB of storage**. An unquantized single
@@ -80,9 +98,7 @@ reads them**. The order is semantic: it numbers the labels of MiniMax-H3's promp
80
  `<Video 1>`, `<Audio 1>`) and it advances the shared audio/video rotary clock, so the same references in a different
81
  order are a different request. This demo lays the slots out as one tab per modality in reading order — images, then
82
  audio, then video — and assembles the request that way. The Images tab opens with two slots and **+ Add another
83
- image** reveals the rest, up to the model's own nine; the audio and video tabs hold one each. A reference left in a
84
- tab that is not the open one is still part of the request; the tabs lay the slots out, they do not choose between
85
- them.
86
 
87
  Rules the model imposes, enforced here before anything is uploaded:
88
 
@@ -92,108 +108,23 @@ Rules the model imposes, enforced here before anything is uploaded:
92
  which is why the duration slider disappears when a single reference can set it, and comes back when two can or
93
  when the one that could is out of range.
94
 
95
- ## Example assets
96
-
97
- `examples/subject.png` is [a studio portrait by Oliver
98
- Dohrn](https://www.pexels.com/photo/casual-portrait-of-smiling-woman-in-studio-31747504/) cropped to head and
99
- shoulders — Pexels licence, free to use. `examples/motion.mp4` is a synthetic clip from the parity fixtures, and
100
- `examples/voice.wav` is utterance `1462-170145-0022` of
101
- [LibriSpeech](https://www.openslr.org/12) `dev-clean` — CC BY 4.0, read from a public-domain LibriVox recording. It
102
- is 16 kHz mono on purpose: the audio VAE wants 32 kHz, so the example exercises the `torchaudio` resample the
103
- `ref2va` path needs.
104
-
105
- ## How the split is expressed
106
-
107
- `MiniMaxH3Blocks` is one `SequentialPipelineBlocks` whose branches are picked per request — and per `workflow=` —
108
- from the inputs, `ref2va` being the branch `references` selects:
109
-
110
- ```
111
- setup -> text_encoder -> reference_encoder -> denoise -> after_denoise -> decode
112
- ```
113
-
114
- where `denoise` is itself `prepare_layout -> prepare_latents -> set_timesteps -> denoise`, against the
115
- `transformer_ref` partition.
116
-
117
- `h3_split_blocks.py` subclasses it with the `text_encoder` step removed. Dropping the step drops the three
118
- components it declares, so `load_components` resolves `transformer_ref` / `vae` / `audio_vae` / the two schedulers
119
- out of the shared `modular_model_index.json` and never fetches the conditioner — and `prompt_embeds` and
120
- `text_token_tags` become ordinary required inputs of the pipeline call:
121
-
122
- ```py
123
- pipe = MiniMaxH3Ref2VAGeneratorBlocks().init_pipeline("MiniMaxAI/MiniMax-H3")
124
- pipe.load_components(dtype=torch.bfloat16)
125
- state = pipe(prompt_embeds=..., text_token_tags=..., references=[...], height=544, width=960, num_frames=124,
126
- num_inference_steps=28)
127
- ```
128
-
129
- Only **text** encoding is remote. `reference_encoder` is the `ref2va` branch's own encoder step — it runs the video
130
- VAE over the image and video references and the audio VAE over the soundtracks, and it is where the references'
131
- latent geometry is resolved — so it stays on this side, next to the autoencoders the conditioner Space does not hold.
132
-
133
- The wire format is the same two tensors as the keyframe half: `(1, num_text_tokens, 5120)` bfloat16 and
134
- `(num_text_tokens,)` int64, carried as one safetensors file with the resolved `height` / `width` / `num_frames` in
135
- its metadata header. What differs is only what the conditioner is shown, so the references travel to it as files:
136
- `ref2va`'s presentation puts a vision block in front of the prompt for every image and every merged video frame
137
- pair. An audio reference contributes its `"<Audio j>: "` label and nothing else — a waveform never reaches the
138
- conditioner — but it still goes over, because a single audio-bearing reference is what resolves `num_frames` when
139
- the request leaves it open.
140
-
141
- The `setup` step runs on **both** halves. It owns no component (PIL, PyAV-decoded media and arithmetic) and it
142
- resolves the canvas, the `17 * n + 5` frame count and the references prepared at their own resolutions. It is
143
- deterministic over the same files, and the conditioner returns the plan it resolved so this Space pins the same
144
- canvas and frame count rather than re-deriving them.
145
-
146
- ## AoTI-compiled blocks
147
-
148
- With `H3_AOTI=1` the 50 repeated transformer blocks run from a compiled package,
149
- [`multimodalart/minimax-h3-aoti`](https://huggingface.co/multimodalart/minimax-h3-aoti)`:bf16/torch2.11/sm120/dynamic` — a single dynamic-sequence artifact that serves
150
- every canvas, duration, reference set and prompt length.
151
-
152
- It is the **same package the `transformer/` partition runs**, and nothing about it is partition-specific. The two
153
- `config.json` files are identical field for field, and the package carries no weights at all: `LazyAOTIModel` binds
154
- each block's own live `state_dict()` by name on its first forward. Patching it in is startup CPU work and costs no
155
- GPU time.
156
-
157
- It removes a near-constant ~0.5 s/step — 50 blocks' worth of kernel-launch overhead plus the norm / rotary / AdaLN
158
- epilogues around the matmuls — and cannot touch the matmuls themselves, so it pays best where the block is not
159
- compute bound. `ref2va` packs the reference rows in front of the generated ones, which makes the sequence longer
160
- than a keyframe request at the same canvas and moves it further toward compute bound.
161
-
162
- ## Nothing is paid for with GPU time
163
-
164
- The 77.3 GB download and the load happen at **startup**: `import spaces` at module top patches `torch.cuda` before
165
- any GPU is attached, so nothing about the load needs a card. The conditioner round trip is a network call on this
166
- Space's CPU. A `@spaces.GPU` call is therefore only the placement (once), the two reference encoders, the denoise
167
- loop and the two decoders.
168
-
169
- One thing does *not* happen at startup: the move onto the card. `spaces`' startup `torch.pack()` writes every
170
- startup-resident CUDA tensor to a second copy on disk before deleting the downloaded originals, and 77.3 GB of
171
- weights plus a 77.3 GB pack is 154.6 GB against a 150 GB quota — the Space is evicted mid-pack with `OSError:
172
- [Errno 28] No space left on device`. Placement therefore happens on the **first GPU call**, `PIPE.to("cuda")` at the
173
- top of the `@spaces.GPU` function: about 10 s of PCIe once, then a no-op walk, and the denoise loop runs with
174
- everything resident and no offloading at all.
175
-
176
- The references are decoded inside that call too, from their paths rather than as decoded media. A `@spaces.GPU`
177
- argument crosses a process boundary by pickling, and a 5 s 1344x768 reference video is 370 MB of frames once PyAV
178
- has expanded it.
179
-
180
  ## Generation constraints
181
 
182
  Fixed by the checkpoint: 24 fps, a 768 pixel short edge, 5 to 15 s, `num_frames` snapped up to the next `17 * n + 5`,
183
  no CFG and no negative prompt (it is guidance-distilled, so every step is one forward pass). The duration slider
184
- stops at 14 s because it is the *snapped* count that has to hold for the ceiling: 15 s is 360 frames, which rounds
185
- up to 362, i.e. 15.083 s, and is refused.
186
 
187
  ## GPU time is reserved per request, not per Space
188
 
189
- MiniMax-H3 attends over one packed sequence, so what a step costs is a function of that sequence's length alone —
190
- and on this half the references dominate it. A single 1344x768 image reference is ~7168 conditioning rows plus the
191
- vision block it puts in front of the prompt; a 2.5 s video reference is another ~17000. The same 960x544, 124-frame
192
- request runs 2.4 s/step with no references and 16 s/step with an image and a video.
193
 
194
- `get_duration` prices that before the call instead of reserving a flat ceiling for everything. It takes the
195
- arguments of the `@spaces.GPU` function, so it has the conditioner's own `text_token_tags` (exact) and the reference
196
- files (measured from metadata, no decode), and evaluates
197
 
198
  ```
199
  S = text rows + reference rows + target rows
@@ -204,8 +135,12 @@ fitted on the `t2va` half and checked against live `ref2va` requests to about 10
204
  pool reserves whatever number it is given, and a flat 900 s is what makes a busy account fail admission with *"You
205
  have too many ZeroGPU credits allocated to running tasks."* A typical single-image request now reserves ~460 s.
206
 
207
- Every request carries the full placement allowance, because nothing on this side knows whether the worker it lands
208
- on is cold and a cold one pays the lazy 72.16 GiB `PIPE.to("cuda")` inside its first GPU call.
 
 
 
 
209
 
210
  ## Space variables
211
 
@@ -215,47 +150,22 @@ on is cold and a cold one pays the lazy 72.16 GiB `PIPE.to("cuda")` inside its f
215
  | `H3_MODEL_REPO` | `MiniMaxAI/MiniMax-H3` | The diffusers-layout checkpoint. Public. |
216
  | `H3_AOTI` | `0` | `1` loads the compiled block package. |
217
  | `H3_PLACEMENT` | `lazy` | `lazy` moves all 72.16 GiB onto the card on the first GPU call and leaves it there; `offload` hands placement to `ComponentsManager.enable_auto_cpu_offload` instead. |
218
- | `H3_ATTENTION` | `_native_cudnn` | cuDNN's fused kernel, 10–20% faster than the SDPA default and needs nothing installed. flash-attention 3 is sm90-only and this pool is sm120. The two VAEs are pinned to torch SDPA instead: they are float32, which cuDNN has no kernel for. |
219
  | `H3_GPU_DURATION_MIN` / `_MAX` | `120` / `1500` | Bounds on what `get_duration` may reserve. |
220
  | `H3_PLACEMENT_ALLOWANCE` | `90` | Seconds of the reservation set aside for a cold worker's placement. |
221
  | `H3_GPU_SIZE` | `xlarge` | ZeroGPU allocation size. `large` does not fit. |
222
- | `CIVITAI_TOKEN` | unset | Appended to CivitAI download links, for models that are not open. |
223
-
224
- ## Whose GPU quota pays
225
-
226
- Two cards are booked per request — this Space's denoise loop and the conditioner's forward — and both are billed to the
227
- requesting user, with nothing here arranging it: `gradio_client` attaches the caller's own `x-ip-token` to every
228
- outgoing call, reading it off gradio's `LocalContext` inside the event listener (`Client.send_data` ->
229
- `add_zero_gpu_headers`), and ZeroGPU charges the booking to whatever that token identifies.
230
-
231
- A caller with no token to forward — a `gradio_client` script rather than a browser — leaves the conditioner's booking
232
- attributed to this Space's pod IP and its small shared quota. An unattributed caller may book at most 120 credits at a
233
- time and an `xlarge` booking costs twice its seconds, so the conditioner books the encode (45 s) and a prompt upsample
234
- (60 s) as two separate calls, each within that ceiling.
235
-
236
- ## Secrets
237
-
238
- None are required for generation. Everything this Space downloads is public — the
239
- [`MiniMaxAI/MiniMax-H3`](https://huggingface.co/MiniMaxAI/MiniMax-H3) checkpoint and the
240
- [`multimodalart/minimax-h3-aoti`](https://huggingface.co/multimodalart/minimax-h3-aoti) packages — and the conditioner
241
- is a public Space called on the requesting user's own ZeroGPU token, never on an org token. `CIVITAI_TOKEN` is the one
242
- optional addition, and only for a gated adapter.
243
 
244
  ## Where diffusers comes from
245
 
246
  MiniMax-H3 is modular-only and not in a released `diffusers`, so `requirements.txt` installs it from the canonical
247
  pull request, [huggingface/diffusers#14371](https://github.com/huggingface/diffusers/pull/14371), pinned to the commit
248
- `665f5782` (`refs/pull/14371/head`) rather than to the moving `minimax-h3-refactor` branch.
249
-
250
- That PR is a WIP, so it needs re-pinning whenever it updates, and `h3_split_blocks.py` — which subclasses its block
251
- classes to cut the pipeline in two — has to be re-checked against the new head at the same time.
252
-
253
- Two of those are `ref2va`-only and easy to miss. PyAV decodes a reference video or audio file as the reference is
254
- built, and **`torchaudio`** resamples a soundtrack that is not already at the audio VAE's 32 kHz — a 32 kHz
255
- reference skips the resample entirely, so the dependency only shows up once someone brings audio at another rate:
256
 
257
- ```
258
- ImportError: Resampling a MiniMax-H3 reference soundtrack from 24000 Hz to 32000 Hz needs `torchaudio`.
259
- ```
260
 
261
- The conditioner Space needs it as well: its `setup` step normalizes the very same waveforms this one does.
 
 
1
  ---
2
+ title: MiniMax-H3 · reference video + audio · Custom lora
3
  emoji: 🎭
4
  colorFrom: pink
5
  colorTo: purple
 
7
  sdk_version: 6.20.0
8
  app_file: app.py
9
  pinned: true
10
+ short_description: Video + soundtrack, your lora + CivitAI, structured prompts
11
  suggested_hardware: zero-a10g
12
  tags:
13
  - video
 
26
 
27
  This Space is the denoising half of the `ref2va` task: the 61.73 GiB `transformer_ref` partition and the two
28
  autoencoders. The 62.14 GiB Qwen3-VL conditioner runs in
29
+ [`qwen3vl-conditioner`](https://huggingface.co/spaces/multimodalart/qwen3vl-conditioner), which this Space calls over
30
+ the gradio API for every request — the same conditioner Space, and the same resident weights, that the keyframe half
31
+ [`minimax-h3`](https://huggingface.co/spaces/multimodalart/minimax-h3) uses.
 
32
 
33
  ## What this fork adds
34
 
35
+ **A structured prompt builder.** H3 was trained on the output of H3-Context-IR, a preprocessor that rewrites a plain
36
+ request into labelled sections, and MiniMax's own model card calls that structure *critical to the quality of the
37
+ final output*. Nothing in this pipeline adds it — the string reaches the transformer as typed. The builder writes
38
+ those sections for you: the reference line naming `<Picture 1>`, `<Picture 2>` in connection order, then
39
+ `integrated_multimodal_description` with the shot type, the camera move written as type + amplitude + speed, and the
40
+ dialogue verbatim inside `<d>` tags, then `overall_soundscape` and `non_diegetic_music`. Speech is generated together
41
+ with the picture, so naming that someone speaks without giving the words produces correct mouth shapes with nothing
42
+ in them; the builder makes that hard to get wrong.
43
+
44
+ **Custom lora, five slots.** A Hugging Face repo, a file inside one, a **CivitAI download link**, or an uploaded
45
  file. Adapters have to be trained against the `transformer_ref/` partition — a `transformer/` adapter is a different
46
  partition and will not match.
47
 
48
+ **CivitAI, searched from inside the Space.** Type a word, get the model's title, version, file, size, downloads and
49
+ trigger words, and drop the one you want straight into a slot. A link that is already in a slot can be named with
50
+ one press, so a row reads *Hairy dad butt · v1.0 · hairy.safetensors* rather than a number. Gated entries need
51
+ `CIVITAI_TOKEN` under *Settings Variables and secrets*: the Space downloads on its own machine, so being signed in
52
+ to CivitAI in a browser does not authenticate it — a gated model answers a server with an HTML login page, which is
53
+ caught and reported rather than saved as weights.
54
+
55
+ **kohya, CivitAI and LoKr files are converted on the fly.** Most of what CivitAI carries is kohya-named, and it
56
+ differs from diffusers in ways that quietly ruin a result rather than raise: flat underscored names, a fused QKV
57
+ interleaved *per attention head* (a plain three-way split hands q's rows to k), a gated MLP whose two halves sit in
58
+ the opposite order, and an `alpha` that sets the scale. LyCORIS **LoKr** goes further and stores each layer as the
59
+ Kronecker product of two small factors, which PEFT cannot load at all; it is rebuilt into an ordinary low-rank pair
60
+ at load time — exactly, since the SVD of a Kronecker product is the outer product of the factors' SVDs, so nothing
61
+ the size of the full 7168×7168 layer is ever built. `.pt` files are read as well as `.safetensors`.
62
+
63
+ **Turbo presets.** Few-step distillations that render joint video + soundtrack in 4–8 steps instead of the usual ~28.
64
+ Picking one fills a free slot at that build's own strength and moves the steps slider to the count it was distilled
65
+ for.
66
+
67
+ **Continue the scene.** One press takes the last frame of the clip you have, makes it the first reference, generates
68
+ again with the settings untouched, and joins the two into a single file — soundtrack included. Press again for a
69
+ third clip. Each press costs one normal generation; the joining is free.
70
+
71
+ **Clip stitching, soundtrack included.** Queue several clips and they are joined into one file; tick the box and
72
  every new generation joins on its own. It runs on the CPU, so it costs nothing from the GPU allowance. Everything is
73
  scaled to the first clip's frame, and a clip without audio gets silence rather than breaking the join.
74
 
 
80
  **Randomize seed**, drawn fresh per press and written back into the box, so the number shown is always the one the
81
  clip was made with.
82
 
 
 
 
 
83
  ## Why split
84
 
85
  MiniMax-H3 is 195.9 GiB in bfloat16 and a ZeroGPU Space is evicted at **150 GB of storage**. An unquantized single
 
98
  `<Video 1>`, `<Audio 1>`) and it advances the shared audio/video rotary clock, so the same references in a different
99
  order are a different request. This demo lays the slots out as one tab per modality in reading order — images, then
100
  audio, then video — and assembles the request that way. The Images tab opens with two slots and **+ Add another
101
+ image** reveals the rest, up to the model's own nine.
 
 
102
 
103
  Rules the model imposes, enforced here before anything is uploaded:
104
 
 
108
  which is why the duration slider disappears when a single reference can set it, and comes back when two can or
109
  when the one that could is out of range.
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  ## Generation constraints
112
 
113
  Fixed by the checkpoint: 24 fps, a 768 pixel short edge, 5 to 15 s, `num_frames` snapped up to the next `17 * n + 5`,
114
  no CFG and no negative prompt (it is guidance-distilled, so every step is one forward pass). The duration slider
115
+ stops at 14 s because it is the *snapped* count that has to hold for the ceiling: 15 s is 360 frames, which rounds up
116
+ to 362, i.e. 15.083 s, and is refused.
117
 
118
  ## GPU time is reserved per request, not per Space
119
 
120
+ MiniMax-H3 attends over one packed sequence, so what a step costs is a function of that sequence's length alone — and
121
+ on this half the references dominate it. A single 1344x768 image reference is ~7168 conditioning rows plus the vision
122
+ block it puts in front of the prompt; a 2.5 s video reference is another ~17000. The same 960x544, 124-frame request
123
+ runs 2.4 s/step with no references and 16 s/step with an image and a video.
124
 
125
+ `get_duration` prices that before the call instead of reserving a flat ceiling for everything. It takes the arguments
126
+ of the `@spaces.GPU` function, so it has the conditioner's own `text_token_tags` (exact) and the reference files
127
+ (measured from metadata, no decode), and evaluates
128
 
129
  ```
130
  S = text rows + reference rows + target rows
 
135
  pool reserves whatever number it is given, and a flat 900 s is what makes a busy account fail admission with *"You
136
  have too many ZeroGPU credits allocated to running tasks."* A typical single-image request now reserves ~460 s.
137
 
138
+ ## Nothing is paid for with GPU time that does not have to be
139
+
140
+ The 77.3 GB download and the load happen at **startup**: `import spaces` at module top patches `torch.cuda` before
141
+ any GPU is attached, so nothing about the load needs a card. The conditioner round trip is a network call on this
142
+ Space's CPU. So is every lora download, every CivitAI lookup, and all clip stitching. A `@spaces.GPU` call is only
143
+ the placement (once), the two reference encoders, the denoise loop and the two decoders.
144
 
145
  ## Space variables
146
 
 
150
  | `H3_MODEL_REPO` | `MiniMaxAI/MiniMax-H3` | The diffusers-layout checkpoint. Public. |
151
  | `H3_AOTI` | `0` | `1` loads the compiled block package. |
152
  | `H3_PLACEMENT` | `lazy` | `lazy` moves all 72.16 GiB onto the card on the first GPU call and leaves it there; `offload` hands placement to `ComponentsManager.enable_auto_cpu_offload` instead. |
153
+ | `H3_ATTENTION` | `_native_cudnn` | cuDNN's fused kernel, 10–20% faster than the SDPA default. flash-attention 3 is sm90-only and this pool is sm120. |
154
  | `H3_GPU_DURATION_MIN` / `_MAX` | `120` / `1500` | Bounds on what `get_duration` may reserve. |
155
  | `H3_PLACEMENT_ALLOWANCE` | `90` | Seconds of the reservation set aside for a cold worker's placement. |
156
  | `H3_GPU_SIZE` | `xlarge` | ZeroGPU allocation size. `large` does not fit. |
157
+ | `H3_LOKR_RANK` | `32` | Rank the LoKr converter targets. Raise it if the log reports a weak layer. |
158
+ | `CIVITAI_TOKEN` | unset | Used for CivitAI downloads and search, and required for gated or adult entries. |
159
+ | `CIVITAI_API_HOST` | unset | Pins the search host; otherwise `civitai.red` is asked first and `civitai.com` is the fallback. |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
  ## Where diffusers comes from
162
 
163
  MiniMax-H3 is modular-only and not in a released `diffusers`, so `requirements.txt` installs it from the canonical
164
  pull request, [huggingface/diffusers#14371](https://github.com/huggingface/diffusers/pull/14371), pinned to the commit
165
+ `665f5782` rather than to the moving `minimax-h3-refactor` branch. That PR is a WIP, so it needs re-pinning whenever
166
+ it updates, and `h3_split_blocks.py` has to be re-checked against the new head at the same time.
 
 
 
 
 
 
167
 
168
+ ## Attribution
 
 
169
 
170
+ An optimized derivative of [`multimodalart/minimax-h3`](https://huggingface.co/spaces/multimodalart/minimax-h3).
171
+ MiniMax-H3 weights remain governed by the MiniMax-H3 Community License Agreement.