diff --git a/extensions_built_in/audio_models/ui.tsx b/extensions_built_in/audio_models/ui.tsx
new file mode 100644
index 0000000..a86eec9
--- /dev/null
+++ b/extensions_built_in/audio_models/ui.tsx
@@ -0,0 +1,268 @@
+// UI entries (training form + Generate page) for the models this package
+// registers in AI_TOOLKIT_MODELS. Loaded at runtime by the UI, not bundled:
+// see ui/src/extensions/README.md for the convention and the allowed imports.
+import type { ModelArch } from "@/app/jobs/new/options";
+import {
+ defaultSampleConfig,
+ defaultAudioSampleConfig,
+ defaultYue2SampleConfig,
+} from "@/helpers/defaultSamples";
+
+const defaultNameOrPath = "";
+
+export const AI_TOOLKIT_UI_MODELS: ModelArch[] = [
+ {
+ name: "ace_step_15_xl",
+ label: "ACE-Step 1.5 XL",
+ group: "audio",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "ostris/ace_step_1.5_ComfyUI_files/ace_step_1.5_xl_base_aio.safetensors",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ "config.process[0].sample": [
+ defaultAudioSampleConfig,
+ defaultSampleConfig,
+ ],
+ },
+ sampleTags: {
+ CAPTION: {
+ title: "Audio Prompt",
+ type: "text",
+ full: true,
+ },
+ LYRICS: {
+ title: "Lyrics",
+ type: "multiline",
+ full: true,
+ },
+ BPM: {
+ title: "BPM",
+ type: "number",
+ },
+ KEYSCALE: {
+ title: "Key Scale",
+ type: "text",
+ },
+ TIMESIGNATURE: {
+ title: "Time Signature",
+ type: "text",
+ },
+ DURATION: {
+ title: "Duration (sec)",
+ type: "number",
+ },
+ LANGUAGE: {
+ title: "Language",
+ type: "text",
+ },
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "sample.multi_ctrl_imgs",
+ "model.low_vram",
+ "model.layer_offloading",
+ ],
+ },
+ {
+ name: "yue2",
+ label: "YuE2",
+ group: "audio",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Comfy-Org/YuE2/checkpoints/yue2_3b_int8_convrot.safetensors",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [false, false],
+ "config.process[0].model.low_vram": [false, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["sigmoid", "sigmoid"],
+ // the int8 repack ships convrot8 layers; requesting convrot8 keeps them as-is (no requantization)
+ "config.process[0].model.qtype": ["convrot8", "qfloat8"],
+ "config.process[0].sample": [
+ defaultYue2SampleConfig,
+ defaultSampleConfig,
+ ],
+ "config.process[0].datasets[x].cache_latents_to_disk": [true, true],
+ // audio has no resolution; every bucket would duplicate the whole dataset
+ "config.process[0].datasets[x].resolution": [[512], [512, 768, 1024]],
+ // blank captions break lyric following; the AR must always see the prefix
+ "config.process[0].datasets[x].caption_dropout_rate": [0, 0.05],
+ "config.process[0].model.model_kwargs": [
+ {
+ cot: "full",
+ abc_dropout: 0.5,
+ sample_ar_repetition_penalty: 1.2,
+ ar_kl_weight: 0.2,
+ },
+ {},
+ ],
+ },
+ // native YuE2 prompt: style text, a [Lyrics] line, the lyrics
+ hasMultiLinePrompts: true,
+ modelNotes: (
+
+
+ Experimental. The AR (composition) model memorizes quickly and does
+ not work well on small datasets. A handful of songs is enough for it
+ to learn the exact token sequence of each song; after that it stops
+ generalizing and free-running samples drift away from the training
+ material. Expect to need a large, varied dataset for the AR side to
+ learn a style rather than the songs themselves.
+
+
+ YuE2 is two experts on one backbone. The AR expert reads the
+ style line and lyrics and writes the song as a sequence of semantic
+ codec tokens (25 per second). The NAR expert then renders those
+ tokens into audio latents with flow matching. Training a LoRA here
+ trains both: next-token loss on the AR over the whole song from its
+ start, flow loss on the NAR over a random window.
+
+
+ The AR loss is the one to watch (loss/ar_ce). It starts
+ near 5 and, on a small dataset, falls toward 0 within a few hundred
+ steps, which is memorization. ar_kl_weight in model
+ kwargs anchors the AR to the base model so it cannot collapse onto the
+ training songs; ar_lr_multiplier and a smaller AR rank
+ slow it further. Style comes mostly from the NAR, lyric following from
+ the AR. Do not use caption dropout: a blank prompt breaks lyric
+ following.
+
+
+ The official audio-to-token encoder is unreleased. Training uses the
+ community tokenizer by Kytra (
+
+ @sin_ceriously
+
+ ), a MERT-v2-FullSong head that maps real audio to YuE2 codec tokens:{" "}
+
+ Mothersuperior/yue2-mothersuperior-realaudio-tokenizer-v4
+
+ . It is downloaded on first use. Its tokens are an approximation of
+ the model's own, so rendered samples will not be bit-faithful to the
+ training audio even when the AR replays a song exactly. Though it is
+ extremely close.
+
+
+ ABC generation. Generation is two stages: the AR first writes a
+ lead sheet of the whole song in ABC notation (sections, chords, vocal
+ and instrumental melody), then writes the codec tokens conditioned on
+ that sheet. It can also run without a sheet ("off" mode), which uses a
+ different instruction line. Samples here do the two stages with{" "}
+ cot: full (chords) or cot: melody (melody
+ only), or the single stage with cot: off.
+
+
+ Training for both. Every song is transcribed to an ABC sheet
+ with SheetSage2 and the AR is trained on lyrics to sheet and sheet to
+ tokens. abc_dropout (default 0.5) is the fraction of
+ training items fed without the sheet instead, as an off-mode prompt,
+ so one LoRA works in both modes. Set it to 0 to train the sheet path
+ only, or 1 to train off mode only.
+
+
+ Caching is required. The sheet is produced at latent-cache time
+ and stored with the latents and codec tokens, so Cache Latents to Disk
+ must stay on: without it, SheetSage2 (about 12 s per song), the MERT
+ tokenizer and the VAE would run again on every training step. The
+ cache records which mode built it; changing cot means
+ deleting the dataset's _latent_cache folder so the sheets
+ are rebuilt. Audio has no resolution, so keep a single resolution
+ bucket per dataset or every bucket duplicates the songs.
+
+
+ Prompt format: a style line, then [Lyrics], then the
+ lyrics with bracketed section headers such as [Verse 1]{" "}
+ and [Chorus]. The Qwen3-Omni captioner has a YuE2 preset
+ that writes captions in this layout. Sample length is set by the
+ Duration field in the sample section.
+
+
+ ),
+ // no separate text encoder: the prompt side is the AR expert, covered by the transformer quantization
+ disableSections: ["network.conv", "model.quantize_te"],
+ additionalSections: ["model.low_vram", "sample.duration"],
+ },
+ {
+ name: "ace_step_15",
+ label: "ACE-Step 1.5",
+ group: "audio",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "ostris/ace_step_1.5_ComfyUI_files/ace_step_1.5_base_aio.safetensors",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ "config.process[0].sample": [
+ defaultAudioSampleConfig,
+ defaultSampleConfig,
+ ],
+ },
+ sampleTags: {
+ CAPTION: {
+ title: "Audio Prompt",
+ type: "text",
+ full: true,
+ },
+ LYRICS: {
+ title: "Lyrics",
+ type: "multiline",
+ full: true,
+ },
+ BPM: {
+ title: "BPM",
+ type: "number",
+ },
+ KEYSCALE: {
+ title: "Key Scale",
+ type: "text",
+ },
+ TIMESIGNATURE: {
+ title: "Time Signature",
+ type: "text",
+ },
+ DURATION: {
+ title: "Duration (sec)",
+ type: "number",
+ },
+ LANGUAGE: {
+ title: "Language",
+ type: "text",
+ },
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "sample.multi_ctrl_imgs",
+ "model.low_vram",
+ "model.layer_offloading",
+ ],
+ },
+];
diff --git a/extensions_built_in/diffusion_models/example_model/README.md b/extensions_built_in/diffusion_models/example_model/README.md
index 4e35726..1255013 100644
--- a/extensions_built_in/diffusion_models/example_model/README.md
+++ b/extensions_built_in/diffusion_models/example_model/README.md
@@ -34,9 +34,10 @@ example/
own `AI_TOOLKIT_MODELS` list — see `extensions/z_image_pixel/`.)
3. The class attribute `arch` (e.g. `"example"`) is matched against
`model.arch` in the training config YAML to pick your class.
-4. To expose it in the web UI, add an entry to
- `ui/src/app/jobs/new/options.ts` (search for an existing arch like
- `ideogram4` to copy the shape).
+4. To expose it in the web UI, add an entry to your package's `ui.tsx`
+ (`AI_TOOLKIT_UI_MODELS`, see `ui/src/extensions/README.md`; built-in
+ models live in `extensions_built_in/diffusion_models/ui.tsx`). It is
+ loaded at runtime, so no UI rebuild is needed.
Minimal config YAML to train it:
diff --git a/extensions_built_in/diffusion_models/ui.tsx b/extensions_built_in/diffusion_models/ui.tsx
new file mode 100644
index 0000000..ef4c83f
--- /dev/null
+++ b/extensions_built_in/diffusion_models/ui.tsx
@@ -0,0 +1,1807 @@
+// UI entries (training form + Generate page) for the models this package
+// registers in AI_TOOLKIT_MODELS. Loaded at runtime by the UI, not bundled:
+// see ui/src/extensions/README.md for the convention and the allowed imports.
+import Link from "next/link";
+import type { ModelArch } from "@/app/jobs/new/options";
+import type { JobConfig } from "@/types";
+import {
+ defaultSampleConfig,
+ defaultIdeogramSamplesConfig,
+} from "@/helpers/defaultSamples";
+
+const defaultNameOrPath = "";
+const defaultLinearRank = 32;
+
+export const AI_TOOLKIT_UI_MODELS: ModelArch[] = [
+ {
+ name: "anima",
+ label: "Anima",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "circlestone-labs/Anima-Base-v1.0-Diffusers",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [false, false],
+ "config.process[0].model.quantize_te": [false, false],
+ "config.process[0].model.qtype": ["", "qfloat8"],
+ "config.process[0].model.qtype_te": ["", "qfloat8"],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].sample.neg": [
+ "worst quality, low quality, score_1, score_2, score_3, blurry, jpeg artifacts, sepia, signature, artist name",
+ "",
+ ],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["model.low_vram", "model.layer_offloading"],
+ },
+ {
+ name: "flux",
+ label: "FLUX.1",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "black-forest-labs/FLUX.1-dev",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ },
+ disableSections: ["network.conv"],
+ gateUrl: "https://huggingface.co/black-forest-labs/FLUX.1-dev",
+ },
+ {
+ name: "flux_kontext",
+ label: "FLUX.1-Kontext-dev",
+ group: "instruction",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "black-forest-labs/FLUX.1-Kontext-dev",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["datasets.control_path", "sample.ctrl_img"],
+ gateUrl: "https://huggingface.co/black-forest-labs/FLUX.1-Kontext-dev",
+ },
+ {
+ name: "flex1",
+ label: "Flex.1",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "ostris/Flex.1-alpha",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].train.bypass_guidance_embedding": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ },
+ disableSections: ["network.conv"],
+ },
+ {
+ name: "chroma",
+ label: "Chroma",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "lodestones/Chroma1-Base",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ },
+ disableSections: ["network.conv"],
+ },
+ {
+ name: "zeta_chroma",
+ label: "Zeta Chroma",
+ group: "experimental",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "lodestones/Zeta-Chroma/zeta-chroma-base-x0-pixel-dino-distance.safetensors",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.extras_name_or_path": [
+ "Tongyi-MAI/Z-Image-Turbo",
+ undefined,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ },
+ disableSections: ["network.conv"],
+ },
+ {
+ name: "wan21:1b",
+ label: "Wan 2.1 (1.3B)",
+ group: "video",
+ isVideoModel: true,
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [false, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].sample.num_frames": [41, 1],
+ "config.process[0].sample.fps": [16, 1],
+ "config.process[0].datasets[x].fps": [16, undefined],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "datasets.num_frames",
+ "model.low_vram",
+ "datasets.auto_frame_count",
+ ],
+ },
+ {
+ name: "wan21_i2v:14b480p",
+ label: "Wan 2.1 I2V (14B-480P)",
+ group: "video",
+ isVideoModel: true,
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Wan-AI/Wan2.1-I2V-14B-480P-Diffusers",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].sample.num_frames": [41, 1],
+ "config.process[0].sample.fps": [16, 1],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].datasets[x].fps": [16, undefined],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "sample.ctrl_img",
+ "datasets.num_frames",
+ "model.low_vram",
+ "datasets.auto_frame_count",
+ ],
+ },
+ {
+ name: "wan21_i2v:14b",
+ label: "Wan 2.1 I2V (14B-720P)",
+ group: "video",
+ isVideoModel: true,
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Wan-AI/Wan2.1-I2V-14B-720P-Diffusers",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].sample.num_frames": [41, 1],
+ "config.process[0].sample.fps": [16, 1],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].datasets[x].fps": [16, undefined],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "sample.ctrl_img",
+ "datasets.num_frames",
+ "model.low_vram",
+ "datasets.auto_frame_count",
+ ],
+ },
+ {
+ name: "wan21:14b",
+ label: "Wan 2.1 (14B)",
+ group: "video",
+ isVideoModel: true,
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Wan-AI/Wan2.1-T2V-14B-Diffusers",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].sample.num_frames": [41, 1],
+ "config.process[0].sample.fps": [16, 1],
+ "config.process[0].datasets[x].fps": [16, undefined],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "datasets.num_frames",
+ "model.low_vram",
+ "datasets.auto_frame_count",
+ ],
+ },
+ {
+ name: "wan22_14b:t2v",
+ label: "Wan 2.2 (14B)",
+ group: "video",
+ isVideoModel: true,
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "ai-toolkit/Wan2.2-T2V-A14B-Diffusers-bf16",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].sample.num_frames": [41, 1],
+ "config.process[0].sample.fps": [16, 1],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].datasets[x].fps": [16, undefined],
+ "config.process[0].model.model_kwargs": [
+ {
+ train_high_noise: true,
+ train_low_noise: true,
+ },
+ {},
+ ],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "datasets.num_frames",
+ "model.low_vram",
+ "model.multistage",
+ "model.layer_offloading",
+ "datasets.auto_frame_count",
+ ],
+ accuracyRecoveryAdapters: {
+ // '3 bit with ARA': 'uint3|ostris/accuracy_recovery_adapters/wan22_14b_t2i_torchao_uint3.safetensors',
+ "4 bit with ARA":
+ "uint4|ostris/accuracy_recovery_adapters/wan22_14b_t2i_torchao_uint4.safetensors",
+ },
+ },
+ {
+ name: "wan22_14b_i2v",
+ label: "Wan 2.2 I2V (14B)",
+ group: "video",
+ isVideoModel: true,
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "ai-toolkit/Wan2.2-I2V-A14B-Diffusers-bf16",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].sample.num_frames": [41, 1],
+ "config.process[0].sample.fps": [16, 1],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].datasets[x].fps": [16, undefined],
+ "config.process[0].model.model_kwargs": [
+ {
+ train_high_noise: true,
+ train_low_noise: true,
+ },
+ {},
+ ],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "sample.ctrl_img",
+ "datasets.num_frames",
+ "model.low_vram",
+ "model.multistage",
+ "model.layer_offloading",
+ "datasets.auto_frame_count",
+ ],
+ accuracyRecoveryAdapters: {
+ "4 bit with ARA":
+ "uint4|ostris/accuracy_recovery_adapters/wan22_14b_i2v_torchao_uint4.safetensors",
+ },
+ },
+ {
+ name: "wan22_5b",
+ label: "Wan 2.2 TI2V (5B)",
+ group: "video",
+ isVideoModel: true,
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Wan-AI/Wan2.2-TI2V-5B-Diffusers",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].sample.num_frames": [121, 1],
+ "config.process[0].sample.fps": [24, 1],
+ "config.process[0].sample.width": [768, 1024],
+ "config.process[0].sample.height": [768, 1024],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].datasets[x].do_i2v": [true, undefined],
+ "config.process[0].datasets[x].fps": [24, undefined],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "sample.ctrl_img",
+ "datasets.num_frames",
+ "model.low_vram",
+ "datasets.do_i2v",
+ "datasets.auto_frame_count",
+ ],
+ },
+ {
+ name: "lumina2",
+ label: "Lumina2",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Alpha-VLLM/Lumina-Image-2.0",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [false, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ },
+ disableSections: ["network.conv"],
+ },
+ {
+ name: "qwen_image",
+ label: "Qwen-Image",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Qwen/Qwen-Image",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["model.low_vram", "model.layer_offloading"],
+ accuracyRecoveryAdapters: {
+ "3 bit with ARA":
+ "uint3|ostris/accuracy_recovery_adapters/qwen_image_torchao_uint3.safetensors",
+ },
+ },
+ {
+ name: "qwen_image:2512",
+ label: "Qwen-Image-2512",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Qwen/Qwen-Image-2512",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["model.low_vram", "model.layer_offloading"],
+ // Training an ARA now, the other one will not work
+ accuracyRecoveryAdapters: {
+ "3 bit with ARA":
+ "uint3|ostris/accuracy_recovery_adapters/qwen_image_2512_torchao_uint3.safetensors",
+ "4 bit with ARA":
+ "uint4|ostris/accuracy_recovery_adapters/qwen_image_2512_torchao_uint4.safetensors",
+ },
+ },
+ {
+ name: "qwen_image_edit",
+ label: "Qwen-Image-Edit",
+ group: "instruction",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Qwen/Qwen-Image-Edit",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "datasets.control_path",
+ "sample.ctrl_img",
+ "model.low_vram",
+ "model.layer_offloading",
+ ],
+ accuracyRecoveryAdapters: {
+ "3 bit with ARA":
+ "uint3|ostris/accuracy_recovery_adapters/qwen_image_edit_torchao_uint3.safetensors",
+ },
+ },
+ {
+ name: "qwen_image_edit_plus",
+ label: "Qwen-Image-Edit-2509",
+ group: "instruction",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Qwen/Qwen-Image-Edit-2509",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ "config.process[0].model.model_kwargs": [
+ {
+ match_target_res: false,
+ },
+ {},
+ ],
+ },
+ disableSections: ["network.conv", "train.unload_text_encoder"],
+ additionalSections: [
+ "datasets.multi_control_paths",
+ "sample.multi_ctrl_imgs",
+ "model.low_vram",
+ "model.layer_offloading",
+ "model.qie.match_target_res",
+ ],
+ accuracyRecoveryAdapters: {
+ "3 bit with ARA":
+ "uint3|ostris/accuracy_recovery_adapters/qwen_image_edit_2509_torchao_uint3.safetensors",
+ },
+ },
+ {
+ name: "qwen_image_edit_plus:2511",
+ label: "Qwen-Image-Edit-2511",
+ group: "instruction",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Qwen/Qwen-Image-Edit-2511",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ "config.process[0].model.model_kwargs": [
+ {
+ match_target_res: false,
+ },
+ {},
+ ],
+ },
+ disableSections: ["network.conv", "train.unload_text_encoder"],
+ additionalSections: [
+ "datasets.multi_control_paths",
+ "sample.multi_ctrl_imgs",
+ "model.low_vram",
+ "model.layer_offloading",
+ "model.qie.match_target_res",
+ ],
+ accuracyRecoveryAdapters: {
+ "3 bit with ARA":
+ "uint3|ostris/accuracy_recovery_adapters/qwen_image_edit_2511_torchao_uint3.safetensors",
+ },
+ },
+ {
+ name: "hidream",
+ label: "HiDream",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "HiDream-ai/HiDream-I1-Full",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.lr": [0.0002, 0.0001],
+ "config.process[0].train.timestep_type": ["shift", "sigmoid"],
+ "config.process[0].network.network_kwargs.ignore_if_contains": [
+ ["ff_i.experts", "ff_i.gate"],
+ [],
+ ],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["model.low_vram"],
+ accuracyRecoveryAdapters: {
+ "3 bit with ARA":
+ "uint3|ostris/accuracy_recovery_adapters/hidream_i1_full_torchao_uint3.safetensors",
+ },
+ },
+ {
+ name: "hidream_e1",
+ label: "HiDream E1",
+ group: "instruction",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "HiDream-ai/HiDream-E1-1",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.lr": [0.0001, 0.0001],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].network.network_kwargs.ignore_if_contains": [
+ ["ff_i.experts", "ff_i.gate"],
+ [],
+ ],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "datasets.control_path",
+ "sample.ctrl_img",
+ "model.low_vram",
+ ],
+ },
+ {
+ name: "sdxl",
+ label: "SDXL",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "stabilityai/stable-diffusion-xl-base-1.0",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [false, false],
+ "config.process[0].model.quantize_te": [false, false],
+ "config.process[0].sample.sampler": ["ddpm", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["ddpm", "flowmatch"],
+ "config.process[0].sample.guidance_scale": [6, 4],
+ },
+ disableSections: ["model.quantize", "train.timestep_type"],
+ },
+ {
+ name: "sd15",
+ label: "SD 1.5",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "stable-diffusion-v1-5/stable-diffusion-v1-5",
+ defaultNameOrPath,
+ ],
+ "config.process[0].sample.sampler": ["ddpm", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["ddpm", "flowmatch"],
+ "config.process[0].sample.width": [512, 1024],
+ "config.process[0].sample.height": [512, 1024],
+ "config.process[0].sample.guidance_scale": [6, 4],
+ },
+ disableSections: ["model.quantize", "train.timestep_type"],
+ },
+ {
+ name: "omnigen2",
+ label: "OmniGen2",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "OmniGen2/OmniGen2",
+ defaultNameOrPath,
+ ],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].model.quantize": [false, false],
+ "config.process[0].model.quantize_te": [true, false],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["datasets.control_path", "sample.ctrl_img"],
+ },
+ {
+ name: "flux2",
+ label: "FLUX.2",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "black-forest-labs/FLUX.2-dev",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ "config.process[0].model.model_kwargs": [
+ {
+ match_target_res: false,
+ },
+ {},
+ ],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "datasets.multi_control_paths",
+ "sample.multi_ctrl_imgs",
+ "model.low_vram",
+ "model.layer_offloading",
+ "model.qie.match_target_res",
+ ],
+ gateUrl: "https://huggingface.co/black-forest-labs/FLUX.2-dev",
+ },
+ {
+ name: "zimage:turbo",
+ label: "Z-Image Turbo (w/ Training Adapter)",
+ generateNameOverride: "Z-Image Turbo",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Tongyi-MAI/Z-Image-Turbo",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ "config.process[0].model.assistant_lora_path": [
+ "ostris/zimage_turbo_training_adapter/zimage_turbo_training_adapter_v2.safetensors",
+ undefined,
+ ],
+ "config.process[0].sample.guidance_scale": [1, 4],
+ "config.process[0].sample.sample_steps": [9, 25],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "model.low_vram",
+ "model.layer_offloading",
+ "model.assistant_lora_path",
+ ],
+ },
+ {
+ name: "zimage",
+ label: "Z-Image",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Tongyi-MAI/Z-Image",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ "config.process[0].sample.sample_steps": [30, 25],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["model.low_vram", "model.layer_offloading"],
+ },
+ {
+ name: "zimage:deturbo",
+ label: "Z-Image De-Turbo (De-Distilled)",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "ostris/Z-Image-De-Turbo",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.extras_name_or_path": [
+ "Tongyi-MAI/Z-Image-Turbo",
+ undefined,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ "config.process[0].sample.guidance_scale": [3, 4],
+ "config.process[0].sample.sample_steps": [25, 25],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["model.low_vram", "model.layer_offloading"],
+ },
+ {
+ name: "minimax_h3",
+ label: "MiniMax-H3",
+ group: "video",
+ isVideoModel: true,
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Comfy-Org/MiniMax-H3",
+ defaultNameOrPath,
+ ],
+ // the Comfy-Org weights are pre-quantized (int8 convrot DiT, nvfp4 TE); these
+ // qtypes match the checkpoints exactly, so the load is unchanged. Picking a
+ // different qtype re-quantizes layer by layer into that format.
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.qtype": ["convrot8", "qfloat8"],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.qtype_te": ["nvfp4", "qfloat8"],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.cache_text_embeddings": [true, false],
+ "config.process[0].train.do_guidance_loss": [true, undefined],
+ "config.process[0].train.guidance_loss_target": [3.5, undefined],
+ "config.process[0].model.assistant_lora_path": [
+ "ostris/minimax_h3_training_adapter/minimax_h3_training_adapter_v1.safetensors",
+ undefined,
+ ],
+ "config.process[0].network.linear": [16, defaultLinearRank],
+ "config.process[0].network.linear_alpha": [16, defaultLinearRank],
+ "config.process[0].network.network_kwargs.ignore_if_contains": [
+ ["adaln_proj"],
+ [],
+ ],
+ "config.process[0].sample.num_frames": [107, 1],
+ "config.process[0].sample.fps": [24, 1],
+ "config.process[0].sample.width": [768, 1024],
+ "config.process[0].sample.height": [768, 1024],
+ "config.process[0].sample.guidance_scale": [1, 4],
+ "config.process[0].sample.sample_steps": [28, 25],
+ "config.process[0].train.audio_loss_multiplier": [1.0, undefined],
+ "config.process[0].train.timestep_type": ["shift", "sigmoid"],
+ "config.process[0].datasets[x].do_i2v": [false, undefined],
+ "config.process[0].datasets[x].do_audio": [true, undefined],
+ "config.process[0].datasets[x].cache_latents_to_disk": [true, false],
+ "config.process[0].datasets[x].fps": [24, undefined],
+ "config.process[0].datasets[x].num_frames": [39, undefined],
+ "config.process[0].datasets[x].auto_frame_count": [true, undefined],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "sample.ctrl_img",
+ "datasets.num_frames",
+ "model.layer_offloading",
+ "model.low_vram",
+ "datasets.do_audio",
+ "datasets.audio_normalize",
+ "datasets.audio_preserve_pitch",
+ "datasets.do_i2v",
+ "train.audio_loss_multiplier",
+ "datasets.auto_frame_count",
+ "model.assistant_lora_path",
+ ],
+ customModelSelectOptions: [
+ {
+ label: "Distillation Handling Method",
+ options: [
+ { value: "cg", label: "Contrastive Guidance" },
+ { value: "ta", label: "Training Adapter" },
+ {
+ value: "both",
+ label: "Contrastive Guidance + Training Adapter (default)",
+ },
+ { value: "none", label: "None" },
+ ],
+ getValue: (config: JobConfig) => {
+ const assistantLoraPath =
+ config?.config?.process?.[0]?.model?.assistant_lora_path;
+ const hasAssistantLoraPath =
+ assistantLoraPath && assistantLoraPath.trim() !== "";
+ const hasContrastiveGuidance =
+ config?.config?.process?.[0]?.train?.do_guidance_loss;
+ if (hasAssistantLoraPath && hasContrastiveGuidance) {
+ return "both";
+ }
+ if (hasAssistantLoraPath) {
+ return "ta";
+ }
+ if (hasContrastiveGuidance) {
+ return "cg";
+ }
+ return "none";
+ },
+ onChange: (
+ value: string,
+ config: JobConfig,
+ setJobConfig: (value: any, key: string) => void,
+ ) => {
+ if (value === "cg") {
+ setJobConfig(true, "config.process[0].train.do_guidance_loss");
+ setJobConfig(
+ undefined,
+ "config.process[0].model.assistant_lora_path",
+ );
+ if (!config?.config?.process?.[0]?.train?.guidance_loss_target) {
+ setJobConfig(3.5, "config.process[0].train.guidance_loss_target");
+ }
+ } else if (value === "ta") {
+ setJobConfig(undefined, "config.process[0].train.do_guidance_loss");
+ setJobConfig(
+ undefined,
+ "config.process[0].train.guidance_loss_target",
+ );
+ setJobConfig(
+ "ostris/minimax_h3_training_adapter/minimax_h3_training_adapter_v1.safetensors",
+ "config.process[0].model.assistant_lora_path",
+ );
+ } else if (value === "both") {
+ setJobConfig(true, "config.process[0].train.do_guidance_loss");
+ setJobConfig(
+ "ostris/minimax_h3_training_adapter/minimax_h3_training_adapter_v1.safetensors",
+ "config.process[0].model.assistant_lora_path",
+ );
+ if (!config?.config?.process?.[0]?.train?.guidance_loss_target) {
+ setJobConfig(3.5, "config.process[0].train.guidance_loss_target");
+ }
+ } else if (value === "none") {
+ setJobConfig(undefined, "config.process[0].train.do_guidance_loss");
+ setJobConfig(
+ undefined,
+ "config.process[0].train.guidance_loss_target",
+ );
+ setJobConfig(
+ undefined,
+ "config.process[0].model.assistant_lora_path",
+ );
+ }
+ },
+ doc: {
+ title: "MiniMax-H3 Distillation Handling",
+ description: (
+
+ MiniMax H3 is a guidance distilled model, so training on it
+ directly will make the guidance distillation break down. There are
+ two different ways to train on this model without breaking the
+ guidance distillation: Contrastive Guidance and Training Adapter.
+ Both have their pros and cons. The adapter is faster, but will
+ still break down over a long run. Contrastive Guidance is slower,
+ but is less likely to break down.
+
+ ),
+ },
+ },
+ ],
+ modelNotes: (
+
+
+ Weights load from the{" "}
+
+ Models Folder Path
+ {" "}
+ set in settings. Anything missing is downloaded there from{" "}
+ Comfy-Org/MiniMax-H3 on first load (~43GB total). Files
+ used:
+
+
+ {`/
+├── diffusion_models/
+│ └── minimax_h3_fl2va_pruned_int8_convrot.safetensors
+├── text_encoders/
+│ └── qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors
+└── vae/
+ ├── minimax_h3_video_vae_fp16.safetensors
+ └── minimax_h3_audio_vae_fp32.safetensors`}
+
+
+ The checkpoints are pre-quantized and load directly: int8 ConvRot DiT
+ (~21GB) and nvfp4 Qwen3-VL text encoder (~16GB). The default qtypes (
+ convrot8 / nvfp4) match the files exactly,
+ so nothing is re-quantized on load. Picking a different quantization
+ re-quantizes the pre-quantized layers into that format, one layer at a
+ time.
+
+
+ Supports t2v and first-frame i2v (ctrl img / i2v datasets) with joint
+ audio. The model is guidance-distilled — keep guidance scale at 1.
+ Video is fixed 24 fps and frame counts snap down to the 17n+5 grid (5,
+ 22, 39, 56, ..., 107, 124 ≈ 5s). Image datasets (num_frames 1) train
+ as single frames, and a sample with num_frames 1 renders a single
+ image.
+
+
+ ),
+ },
+ {
+ name: "minimax_h3_ref2va",
+ label: "MiniMax-H3 Ref2V",
+ group: "video",
+ isVideoModel: true,
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Comfy-Org/MiniMax-H3",
+ defaultNameOrPath,
+ ],
+ // pre-quantized weights: matching qtypes keep the load unchanged
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.qtype": ["convrot8", "qfloat8"],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.qtype_te": ["nvfp4", "qfloat8"],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.cache_text_embeddings": [true, false],
+ "config.process[0].train.do_guidance_loss": [true, undefined],
+ "config.process[0].train.guidance_loss_target": [3.5, undefined],
+ "config.process[0].model.assistant_lora_path": [
+ "ostris/minimax_h3_training_adapter/minimax_h3_ref2va_training_adapter_v1.safetensors",
+ undefined,
+ ],
+ "config.process[0].network.linear": [16, defaultLinearRank],
+ "config.process[0].network.linear_alpha": [16, defaultLinearRank],
+ "config.process[0].network.network_kwargs.ignore_if_contains": [
+ ["adaln_proj"],
+ [],
+ ],
+ "config.process[0].sample.num_frames": [107, 1],
+ "config.process[0].sample.fps": [24, 1],
+ "config.process[0].sample.width": [768, 1024],
+ "config.process[0].sample.height": [768, 1024],
+ "config.process[0].sample.guidance_scale": [1, 4],
+ "config.process[0].sample.sample_steps": [28, 25],
+ "config.process[0].train.audio_loss_multiplier": [1.0, undefined],
+ "config.process[0].train.timestep_type": ["shift", "sigmoid"],
+ "config.process[0].datasets[x].do_audio": [true, undefined],
+ "config.process[0].datasets[x].cache_latents_to_disk": [true, false],
+ "config.process[0].datasets[x].fps": [24, undefined],
+ "config.process[0].datasets[x].num_frames": [39, undefined],
+ "config.process[0].datasets[x].auto_frame_count": [true, undefined],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "sample.multi_ctrl_imgs",
+ "datasets.multi_control_paths",
+ "datasets.num_frames",
+ "model.layer_offloading",
+ "model.low_vram",
+ "datasets.do_audio",
+ "datasets.audio_normalize",
+ "datasets.audio_preserve_pitch",
+ "train.audio_loss_multiplier",
+ "datasets.auto_frame_count",
+ "model.assistant_lora_path",
+ ],
+ customModelSelectOptions: [
+ {
+ label: "Distillation Handling Method",
+ options: [
+ { value: "cg", label: "Contrastive Guidance" },
+ { value: "ta", label: "Training Adapter" },
+ {
+ value: "both",
+ label: "Contrastive Guidance + Training Adapter (default)",
+ },
+ { value: "dopsd", label: "D-OPSD" },
+ { value: "none", label: "None" },
+ ],
+ getValue: (config: JobConfig) => {
+ if (config?.config?.process?.[0]?.model?.model_kwargs?.dopsd) {
+ return "dopsd";
+ }
+ const assistantLoraPath =
+ config?.config?.process?.[0]?.model?.assistant_lora_path;
+ const hasAssistantLoraPath =
+ assistantLoraPath && assistantLoraPath.trim() !== "";
+ const hasContrastiveGuidance =
+ config?.config?.process?.[0]?.train?.do_guidance_loss;
+ if (hasAssistantLoraPath && hasContrastiveGuidance) {
+ return "both";
+ }
+ if (hasAssistantLoraPath) {
+ return "ta";
+ }
+ if (hasContrastiveGuidance) {
+ return "cg";
+ }
+ return "none";
+ },
+ onChange: (
+ value: string,
+ config: JobConfig,
+ setJobConfig: (value: any, key: string) => void,
+ ) => {
+ const kwargs = {
+ ...(config?.config?.process?.[0]?.model?.model_kwargs ?? {}),
+ };
+ if (value === "dopsd") {
+ kwargs.dopsd = true;
+ kwargs.dopsd_bleed_strength = 1.0;
+ } else {
+ delete kwargs.dopsd;
+ delete kwargs.dopsd_bleed_strength;
+ }
+ setJobConfig(kwargs, "config.process[0].model.model_kwargs");
+ if (value === "cg") {
+ setJobConfig(true, "config.process[0].train.do_guidance_loss");
+ setJobConfig(
+ undefined,
+ "config.process[0].model.assistant_lora_path",
+ );
+ if (!config?.config?.process?.[0]?.train?.guidance_loss_target) {
+ setJobConfig(3.5, "config.process[0].train.guidance_loss_target");
+ }
+ } else if (value === "ta") {
+ setJobConfig(undefined, "config.process[0].train.do_guidance_loss");
+ setJobConfig(
+ undefined,
+ "config.process[0].train.guidance_loss_target",
+ );
+ setJobConfig(
+ "ostris/minimax_h3_training_adapter/minimax_h3_ref2va_training_adapter_v1.safetensors",
+ "config.process[0].model.assistant_lora_path",
+ );
+ } else if (value === "both") {
+ setJobConfig(true, "config.process[0].train.do_guidance_loss");
+ setJobConfig(
+ "ostris/minimax_h3_training_adapter/minimax_h3_ref2va_training_adapter_v1.safetensors",
+ "config.process[0].model.assistant_lora_path",
+ );
+ if (!config?.config?.process?.[0]?.train?.guidance_loss_target) {
+ setJobConfig(3.5, "config.process[0].train.guidance_loss_target");
+ }
+ } else if (value === "dopsd" || value === "none") {
+ setJobConfig(undefined, "config.process[0].train.do_guidance_loss");
+ setJobConfig(
+ undefined,
+ "config.process[0].train.guidance_loss_target",
+ );
+ setJobConfig(
+ undefined,
+ "config.process[0].model.assistant_lora_path",
+ );
+ }
+ },
+ doc: {
+ title: "MiniMax-H3 Distillation Handling",
+ description: (
+
+ MiniMax H3 is a guidance distilled model, so training on it
+ directly will make the guidance distillation break down. There are
+ two different ways to train on this model without breaking the
+ guidance distillation: Contrastive Guidance and Training Adapter.
+ Both have their pros and cons. The adapter is faster, but will
+ still break down over a long run. Contrastive Guidance is slower,
+ but is less likely to break down. D-OPSD instead self-distills: a
+ no-grad teacher pass sees the training target as its own reference
+ and its prediction becomes the training target for a
+ reference-free pass, baking the reference into your trigger word
+ (or the caption itself when no trigger word is set). Re-caches
+ latents with pixel tensors.
+
+ ),
+ },
+ },
+ {
+ label: "Image Reference Presentation",
+ options: [
+ { value: "picture", label: "Picture (default)" },
+ { value: "video", label: "Static video clip" },
+ ],
+ getValue: (config: JobConfig) => {
+ return config?.config?.process?.[0]?.model?.model_kwargs
+ ?.image_refs_as_video
+ ? "video"
+ : "picture";
+ },
+ onChange: (
+ value: string,
+ config: JobConfig,
+ setJobConfig: (value: any, key: string) => void,
+ ) => {
+ const kwargs = {
+ ...(config?.config?.process?.[0]?.model?.model_kwargs ?? {}),
+ };
+ if (value === "video") {
+ kwargs.image_refs_as_video = true;
+ } else {
+ delete kwargs.image_refs_as_video;
+ delete kwargs.image_ref_video_frames;
+ }
+ setJobConfig(kwargs, "config.process[0].model.model_kwargs");
+ },
+ doc: {
+ title: "MiniMax-H3 Image Reference Presentation",
+ description: (
+
+
+ How still-image references (dataset control images and sample
+ ctrl images) are shown to the model. Video references always use
+ the video path.
+
+
+ Picture : the native ref2va recipe — a
+ single-frame reference block, shown to Qwen3-VL as a{" "}
+ <Picture i> block, scaled down only.
+
+
+ Static video clip : the image is held for 5
+ frames (2 latent frames) and routed through the exact path a
+ reference VIDEO takes — video sizing (matched to the target's
+ pixel area), multi-frame reference block,{" "}
+ <Video k> timestamped presentation. Use this
+ when training on image references but sampling with video
+ references, so the LoRA learns the pathway it will be used
+ through. Adds a handful of rows per reference. Frame count is
+ adjustable with model_kwargs.image_ref_video_frames{" "}
+ (17n+5). Changing this re-caches text embeddings.
+
+
+ ),
+ },
+ },
+ ],
+ modelNotes: (
+
+
+ Reference-to-video: control images and videos condition the output as
+ subject/style references (never as a first frame). References keep
+ their own aspect and are matched to the target's pixel area (images
+ scale down only, never up; a same-aspect video reference is exactly
+ the target size). Each rides into the packed sequence as a reference
+ block, and is also shown to the Qwen3-VL conditioner as a{" "}
+ <Picture i> (image) or timestamped{" "}
+ <Video k> (video) vision block. Training references
+ come from the dataset control path(s); sampling uses the sample ctrl
+ images — always as references. The Image Reference Presentation option
+ can route still images through the video-reference path as short
+ static clips.
+
+
+ Weights load like MiniMax-H3 (see that arch's notes) from the{" "}
+
+ Models Folder Path
+
+ , using{" "}
+
+ diffusion_models/minimax_h3_ref2va_pruned_int8_convrot.safetensors
+ {" "}
+ — the ref2va partition of the same release; text encoder and VAEs are
+ shared with the fl2va arch. Everything else (pre-quantized load, 24
+ fps, 17n+5 frame grid, guidance scale 1, single-image mode) matches
+ MiniMax-H3.
+
+
+ ),
+ },
+ {
+ name: "ltx2",
+ label: "LTX-2",
+ group: "video",
+ isVideoModel: true,
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Lightricks/LTX-2",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].sample.num_frames": [121, 1],
+ "config.process[0].sample.fps": [24, 1],
+ "config.process[0].sample.width": [768, 1024],
+ "config.process[0].sample.height": [768, 1024],
+ "config.process[0].train.audio_loss_multiplier": [1.0, undefined],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].datasets[x].do_i2v": [false, undefined],
+ "config.process[0].datasets[x].do_audio": [true, undefined],
+ "config.process[0].datasets[x].fps": [24, undefined],
+ "config.process[0].datasets[x].auto_frame_count": [false, undefined],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "sample.ctrl_img",
+ "datasets.num_frames",
+ "model.layer_offloading",
+ "model.low_vram",
+ "datasets.do_audio",
+ "datasets.audio_normalize",
+ "datasets.audio_preserve_pitch",
+ "datasets.do_i2v",
+ "train.audio_loss_multiplier",
+ "datasets.auto_frame_count",
+ ],
+ },
+ {
+ name: "ltx2.3",
+ label: "LTX-2.3",
+ group: "video",
+ isVideoModel: true,
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "Lightricks/LTX-2.3/ltx-2.3-22b-dev.safetensors",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].sample.num_frames": [121, 1],
+ "config.process[0].sample.fps": [24, 1],
+ "config.process[0].sample.width": [768, 1024],
+ "config.process[0].sample.height": [768, 1024],
+ "config.process[0].train.audio_loss_multiplier": [1.0, undefined],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].datasets[x].cache_latents_to_disk": [true, false],
+ "config.process[0].datasets[x].do_i2v": [false, undefined],
+ "config.process[0].datasets[x].do_audio": [true, undefined],
+ "config.process[0].datasets[x].fps": [24, undefined],
+ "config.process[0].datasets[x].auto_frame_count": [false, undefined],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "sample.ctrl_img",
+ "datasets.num_frames",
+ "model.layer_offloading",
+ "model.low_vram",
+ "datasets.do_audio",
+ "datasets.audio_normalize",
+ "datasets.audio_preserve_pitch",
+ "datasets.do_i2v",
+ "train.audio_loss_multiplier",
+ "datasets.auto_frame_count",
+ ],
+ },
+ {
+ name: "ltx2.5",
+ label: "LTX-2.5",
+ gateUrl: "https://huggingface.co/Lightricks/LTX-2.5",
+ group: "video",
+ isVideoModel: true,
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ // comfy-style split files resolve from/download to the models folder;
+ // the int8 ConvRot dev transformer is the default
+ "config.process[0].model.name_or_path": [
+ "Lightricks/LTX-2.5",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.qtype": ["convrot8", "qfloat8"],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.qtype_te": ["convrot8", "qfloat8"],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].sample.num_frames": [121, 1],
+ "config.process[0].sample.fps": [24, 1],
+ "config.process[0].sample.width": [768, 1024],
+ "config.process[0].sample.height": [768, 1024],
+ "config.process[0].train.audio_loss_multiplier": [1.0, undefined],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].datasets[x].cache_latents_to_disk": [true, false],
+ "config.process[0].datasets[x].do_i2v": [false, undefined],
+ "config.process[0].datasets[x].do_audio": [true, undefined],
+ "config.process[0].datasets[x].fps": [24, undefined],
+ "config.process[0].datasets[x].auto_frame_count": [false, undefined],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "sample.ctrl_img",
+ "datasets.num_frames",
+ "model.layer_offloading",
+ "model.low_vram",
+ "datasets.do_audio",
+ "datasets.audio_normalize",
+ "datasets.audio_preserve_pitch",
+ "datasets.do_i2v",
+ "train.audio_loss_multiplier",
+ "datasets.auto_frame_count",
+ ],
+ },
+ {
+ name: "flux2_klein_4b",
+ label: "FLUX.2-klein-base-4B",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "black-forest-labs/FLUX.2-klein-base-4B",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ "config.process[0].model.model_kwargs": [
+ {
+ match_target_res: false,
+ },
+ {},
+ ],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "datasets.multi_control_paths",
+ "sample.multi_ctrl_imgs",
+ "model.low_vram",
+ "model.layer_offloading",
+ "model.qie.match_target_res",
+ ],
+ },
+ {
+ name: "ernie_image",
+ label: "ERNIE-Image",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "baidu/ERNIE-Image",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["model.low_vram", "model.layer_offloading"],
+ },
+ {
+ name: "flux2_klein_9b",
+ label: "FLUX.2-klein-base-9B",
+ group: "image",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "black-forest-labs/FLUX.2-klein-base-9B",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].model.qtype": ["qfloat8", "qfloat8"],
+ "config.process[0].model.model_kwargs": [
+ {
+ match_target_res: false,
+ },
+ {},
+ ],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "datasets.multi_control_paths",
+ "sample.multi_ctrl_imgs",
+ "model.low_vram",
+ "model.layer_offloading",
+ "model.qie.match_target_res",
+ ],
+ gateUrl: "https://huggingface.co/black-forest-labs/FLUX.2-klein-base-9B",
+ },
+ {
+ name: "nucleus_image",
+ label: "Nucleus-Image",
+ group: "image",
+ defaults: {
+ "config.process[0].model.name_or_path": [
+ "NucleusAI/Nucleus-Image",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].network.network_kwargs.ignore_if_contains": [
+ ["img_mlp.experts", "img_mlp.gate"],
+ [],
+ ],
+ "config.process[0].network.linear": [128, defaultLinearRank],
+ "config.process[0].network.linear_alpha": [128, defaultLinearRank],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["model.low_vram"],
+ },
+ {
+ name: "hidream_o1",
+ label: "HiDream-O1",
+ group: "image",
+ defaults: {
+ "config.process[0].model.name_or_path": [
+ "HiDream-ai/HiDream-O1-Image",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [false, false],
+ "config.process[0].train.timestep_type": ["weighted", "sigmoid"],
+ "config.process[0].network.conv": [undefined, 16],
+ "config.process[0].network.conv_alpha": [undefined, 16],
+ "config.process[0].train.max_loss": [1.0, undefined],
+ "config.process[0].network.network_kwargs.ignore_if_contains": [
+ ["lm_head", "patch_embed", "visual"],
+ [],
+ ],
+ "config.process[0].network.transformer_only": [false, undefined],
+ "config.process[0].sample.width": [2048, 1024],
+ "config.process[0].sample.height": [2048, 1024],
+ "config.process[0].model.model_kwargs": [
+ {
+ noise_scale_inference: 8.0,
+ noise_scale: 8.0,
+ },
+ {},
+ ],
+ },
+ disableSections: [
+ "network.conv",
+ "model.quantize_te",
+ "train.unload_text_encoder",
+ ],
+ additionalSections: ["model.low_vram", "model.layer_offloading"],
+ },
+ {
+ name: "zimage_l2p",
+ label: "Z-Image L2P (pixel space)",
+ group: "image",
+ defaults: {
+ "config.process[0].model.name_or_path": [
+ "zhen-nan/L2P/model-1k-merge.safetensors",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.extras_name_or_path": [
+ "Tongyi-MAI/Z-Image-Turbo",
+ undefined,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].network.conv": [undefined, 16],
+ "config.process[0].network.conv_alpha": [undefined, 16],
+ "config.process[0].model.low_vram": [true, false],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["model.low_vram", "model.layer_offloading"],
+ },
+ {
+ name: "ideogram4",
+ label: "Ideogram4",
+ group: "experimental",
+ defaults: {
+ "config.process[0].model.name_or_path": [
+ "ideogram-ai/ideogram-4-fp8",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].network.conv": [undefined, 16],
+ "config.process[0].network.conv_alpha": [undefined, 16],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].sample": [
+ defaultIdeogramSamplesConfig,
+ defaultSampleConfig,
+ ],
+ "config.process[0].model.unconditional_lora_path": [
+ "ostris/ideogram_4_unconditional_lora/ideogram_4_unconditional_lora_r16.safetensors",
+ undefined,
+ ],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "model.low_vram",
+ "model.layer_offloading",
+ "ideogram_4_prompt",
+ "model.unconditional_lora_path",
+ ],
+ hasMultiLinePrompts: true,
+ gateUrl: "https://huggingface.co/ideogram-ai/ideogram-4-fp8",
+ },
+ {
+ name: "prx_pixel",
+ label: "PRXPixel (pixel space)",
+ group: "image",
+ defaults: {
+ "config.process[0].model.name_or_path": [
+ "Photoroom/prxpixel-t2i",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].network.conv": [undefined, 16],
+ "config.process[0].network.conv_alpha": [undefined, 16],
+ "config.process[0].model.low_vram": [true, false],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["model.low_vram", "model.layer_offloading"],
+ },
+ {
+ name: "krea2",
+ label: "Krea 2 (raw)",
+ group: "image",
+ gateUrl: "https://huggingface.co/krea/Krea-2-Raw",
+ defaults: {
+ "config.process[0].model.name_or_path": [
+ "krea/Krea-2-Raw",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].network.conv": [undefined, 16],
+ "config.process[0].network.conv_alpha": [undefined, 16],
+ "config.process[0].model.low_vram": [true, false],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["model.low_vram", "model.layer_offloading"],
+ },
+ {
+ name: "krea2:turbo",
+ label: "Krea 2 Turbo (w/ Training Adapter)",
+ generateNameOverride: "Krea 2 Turbo",
+ group: "image",
+ gateUrl: "https://huggingface.co/krea/Krea-2-Turbo",
+ defaults: {
+ "config.process[0].model.name_or_path": [
+ "krea/Krea-2-Turbo",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].network.conv": [undefined, 16],
+ "config.process[0].network.conv_alpha": [undefined, 16],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].model.assistant_lora_path": [
+ "ostris/krea2_turbo_training_adapter/krea2_turbo_training_adapter_v1.safetensors",
+ undefined,
+ ],
+ "config.process[0].sample.guidance_scale": [1, 4],
+ "config.process[0].sample.sample_steps": [9, 25],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: [
+ "model.low_vram",
+ "model.layer_offloading",
+ "model.assistant_lora_path",
+ ],
+ },
+ {
+ name: "krea2:o_edit",
+ label: "Krea 2 (raw) [Edit Training]",
+ gateUrl: "https://huggingface.co/krea/Krea-2-Raw",
+ group: "experimental",
+ defaults: {
+ "config.process[0].model.name_or_path": [
+ "krea/Krea-2-Raw",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].network.conv": [undefined, 16],
+ "config.process[0].network.conv_alpha": [undefined, 16],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].model.model_kwargs": [
+ {
+ edit: true,
+ match_target_res: true,
+ kv_cache: true,
+ },
+ {},
+ ],
+ },
+ disableSections: ["network.conv", "train.unload_text_encoder"],
+ additionalSections: [
+ "datasets.multi_control_paths",
+ "sample.multi_ctrl_imgs",
+ "model.low_vram",
+ "model.layer_offloading",
+ "model.qie.match_target_res",
+ "model.model_kwargs.kv_cache",
+ ],
+ },
+ {
+ name: "krea2:o_edit_turbo",
+ label: "Krea 2 Turbo (w/ Training Adapter) [Edit Training]",
+ generateNameOverride: "Krea 2 Turbo (Edit)",
+ gateUrl: "https://huggingface.co/krea/Krea-2-Turbo",
+ group: "experimental",
+ defaults: {
+ "config.process[0].model.name_or_path": [
+ "krea/Krea-2-Turbo",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].network.conv": [undefined, 16],
+ "config.process[0].network.conv_alpha": [undefined, 16],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].model.assistant_lora_path": [
+ "ostris/krea2_turbo_training_adapter/krea2_turbo_training_adapter_v1.safetensors",
+ undefined,
+ ],
+ "config.process[0].sample.guidance_scale": [1, 4],
+ "config.process[0].sample.sample_steps": [8, 25],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].model.model_kwargs": [
+ {
+ edit: true,
+ match_target_res: true,
+ kv_cache: true,
+ },
+ {},
+ ],
+ },
+ disableSections: ["network.conv", "train.unload_text_encoder"],
+ additionalSections: [
+ "datasets.multi_control_paths",
+ "sample.multi_ctrl_imgs",
+ "model.low_vram",
+ "model.layer_offloading",
+ "model.assistant_lora_path",
+ "model.qie.match_target_res",
+ "model.model_kwargs.kv_cache",
+ ],
+ },
+ {
+ name: "mageflow",
+ label: "Mage-Flow",
+ group: "image",
+ defaults: {
+ "config.process[0].model.name_or_path": [
+ "microsoft/Mage-Flow-Base",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].network.conv": [undefined, 16],
+ "config.process[0].network.conv_alpha": [undefined, 16],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].sample.guidance_scale": [4, 4],
+ "config.process[0].sample.sample_steps": [25, 25],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["model.low_vram", "model.layer_offloading"],
+ },
+ {
+ name: "mageflow_edit",
+ label: "Mage-Flow Edit",
+ group: "instruction",
+ defaults: {
+ "config.process[0].model.name_or_path": [
+ "microsoft/Mage-Flow-Edit-Base",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].network.conv": [undefined, 16],
+ "config.process[0].network.conv_alpha": [undefined, 16],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].sample.guidance_scale": [4, 4],
+ "config.process[0].sample.sample_steps": [25, 25],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ },
+ disableSections: ["network.conv", "train.unload_text_encoder"],
+ additionalSections: [
+ "datasets.multi_control_paths",
+ "sample.multi_ctrl_imgs",
+ "model.low_vram",
+ "model.layer_offloading",
+ ],
+ },
+ {
+ name: "boogu_image",
+ label: "Boogu Image",
+ group: "image",
+ defaults: {
+ "config.process[0].model.name_or_path": [
+ "Boogu/Boogu-Image-0.1-Base",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].network.conv": [undefined, 16],
+ "config.process[0].network.conv_alpha": [undefined, 16],
+ "config.process[0].model.low_vram": [true, false],
+ },
+ disableSections: ["network.conv"],
+ additionalSections: ["model.low_vram", "model.layer_offloading"],
+ },
+ {
+ name: "boogu_image_edit",
+ label: "Boogu Image Edit",
+ group: "instruction",
+ defaults: {
+ "config.process[0].model.name_or_path": [
+ "Boogu/Boogu-Image-0.1-Edit",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].train.timestep_type": ["linear", "sigmoid"],
+ "config.process[0].network.conv": [undefined, 16],
+ "config.process[0].network.conv_alpha": [undefined, 16],
+ "config.process[0].model.low_vram": [true, false],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].model.model_kwargs": [
+ {
+ match_target_res: false,
+ },
+ {},
+ ],
+ },
+ disableSections: ["network.conv", "train.unload_text_encoder"],
+ additionalSections: [
+ "datasets.multi_control_paths",
+ "sample.multi_ctrl_imgs",
+ "model.low_vram",
+ "model.layer_offloading",
+ "model.qie.match_target_res",
+ ],
+ },
+];
diff --git a/extensions_built_in/flex2/ui.tsx b/extensions_built_in/flex2/ui.tsx
new file mode 100644
index 0000000..456bb5a
--- /dev/null
+++ b/extensions_built_in/flex2/ui.tsx
@@ -0,0 +1,40 @@
+// UI entries (training form + Generate page) for the models this package
+// registers in AI_TOOLKIT_MODELS. Loaded at runtime by the UI, not bundled:
+// see ui/src/extensions/README.md for the convention and the allowed imports.
+import type { ModelArch } from "@/app/jobs/new/options";
+
+const defaultNameOrPath = "";
+
+export const AI_TOOLKIT_UI_MODELS: ModelArch[] = [
+ {
+ name: "flex2",
+ label: "Flex.2",
+ group: "image",
+ controls: ["depth", "line", "pose", "inpaint"],
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "ostris/Flex.2-preview",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [true, false],
+ "config.process[0].model.model_kwargs": [
+ {
+ invert_inpaint_mask_chance: 0.2,
+ inpaint_dropout: 0.5,
+ control_dropout: 0.5,
+ inpaint_random_chance: 0.2,
+ do_random_inpainting: true,
+ random_blur_mask: true,
+ random_dialate_mask: true,
+ },
+ {},
+ ],
+ "config.process[0].train.bypass_guidance_embedding": [true, false],
+ "config.process[0].sample.sampler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ },
+ disableSections: ["network.conv"],
+ },
+];
diff --git a/extensions_built_in/llm_models/ui.tsx b/extensions_built_in/llm_models/ui.tsx
new file mode 100644
index 0000000..0954053
--- /dev/null
+++ b/extensions_built_in/llm_models/ui.tsx
@@ -0,0 +1,84 @@
+// UI entries (training form + Generate page) for the models this package
+// registers in AI_TOOLKIT_MODELS. Loaded at runtime by the UI, not bundled:
+// see ui/src/extensions/README.md for the convention and the allowed imports.
+import type { ModelArch } from "@/app/jobs/new/options";
+import {
+ defaultSampleConfig,
+ defaultQwen25OmniSampleConfig,
+} from "@/helpers/defaultSamples";
+
+const defaultNameOrPath = "";
+
+export const AI_TOOLKIT_UI_MODELS: ModelArch[] = [
+ {
+ name: "qwen25_omni",
+ label: "Qwen2.5-Omni",
+ group: "llm",
+ defaults: {
+ // default updates when [selected, unselected] in the UI
+ "config.process[0].model.name_or_path": [
+ "ai-toolkit/Qwen2.5-Omni-7B/qwen2_5_omni_7b_convrot8.safetensors",
+ defaultNameOrPath,
+ ],
+ "config.process[0].model.quantize": [true, false],
+ "config.process[0].model.quantize_te": [false, false],
+ "config.process[0].model.low_vram": [false, false],
+ // the single-file thinker ships convrot8 layers; requesting convrot8 keeps them as-is
+ "config.process[0].model.qtype": ["convrot8", "qfloat8"],
+ "config.process[0].train.unload_text_encoder": [false, false],
+ "config.process[0].train.noise_scheduler": ["flowmatch", "flowmatch"],
+ "config.process[0].train.batch_size": [1, 1],
+ "config.process[0].sample": [
+ defaultQwen25OmniSampleConfig,
+ defaultSampleConfig,
+ ],
+ // media is encoded on the GPU per step; the cache is optional and large (~54 MB per 300 s of audio)
+ "config.process[0].datasets[x].cache_latents_to_disk": [false, true],
+ "config.process[0].datasets[x].resolution": [[512], [512, 768, 1024]],
+ // the caption is the training target; a blank one trains nothing
+ "config.process[0].datasets[x].caption_dropout_rate": [0, 0.05],
+ "config.process[0].model.model_kwargs": [
+ { instruction: "Describe this in detail." },
+ {},
+ ],
+ },
+ disableSections: [
+ "network.conv",
+ "trigger_word",
+ "train.diff_output_preservation",
+ "train.blank_prompt_preservation",
+ "train.unload_text_encoder",
+ "slider",
+ ],
+ additionalSections: [
+ "model.model_kwargs.instruction",
+ "sample.ctrl_img",
+ "datasets.num_frames",
+ ],
+ modelNotes: (
+
+
+ Qwen2.5-Omni 7B thinker as a text-generating model: audio, image or
+ video in, text out. Each dataset item is a media file (mp3, wav, flac,
+ ogg, jpg, png, webp, mp4, ...) with a caption file next to it; the
+ caption is the text the model learns to produce for that media. One
+ dataset can mix all three kinds. Video files are used when Num Frames
+ is above 1 and are seen as frames only (no audio track).
+
+
+ The instruction in model_kwargs.instruction is the user
+ turn for every training item; use the same wording when captioning
+ with the trained LoRA. Samples take a media file per prompt and write
+ the generated text as a .txt file.
+
+
+ Media is encoded by the frozen audio and vision towers on the GPU each
+ step, so Cache Latents to Disk can stay off (the cache is about 54 MB
+ per 300 seconds of audio). Batch size is 1. The LoRA trains the text
+ stack only. Watch loss/ce: next-token cross-entropy on
+ the caption.
+
+
+ ),
+ },
+];
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..bc56cce
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ // type checking + editor support for extension UI modules only (the web
+ // app has its own config in ui/); they are loaded at runtime, not bundled
+ "extends": "./ui/tsconfig.json",
+ "compilerOptions": {
+ "noEmit": true,
+ "incremental": false,
+ "plugins": [],
+ "typeRoots": [
+ "./ui/node_modules/@types"
+ ],
+ "paths": {
+ "@/*": [
+ "./ui/src/*"
+ ],
+ "*": [
+ "./ui/node_modules/@types/*",
+ "./ui/node_modules/*"
+ ]
+ }
+ },
+ "include": [
+ "extensions/*/ui.tsx",
+ "extensions/*/ui.ts",
+ "extensions_built_in/*/ui.tsx",
+ "extensions_built_in/*/ui.ts"
+ ]
+}
\ No newline at end of file
diff --git a/ui/package.json b/ui/package.json
index 0b72a0b..59d5b72 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -11,7 +11,8 @@
"build_and_start": "npm run install_deps && npm run db_build_start",
"lint": "next lint",
"update_db": "npx prisma generate && npx prisma db push",
- "format": "prettier --write \"**/*.{js,jsx,ts,tsx,css,scss}\""
+ "format": "prettier --write \"**/*.{js,jsx,ts,tsx,css,scss}\"",
+ "check_extensions": "tsc -p ../tsconfig.json"
},
"dependencies": {
"@headlessui/react": "^2.2.0",
diff --git a/ui/src/app/api/model_archs/route.ts b/ui/src/app/api/model_archs/route.ts
new file mode 100644
index 0000000..ec24e59
--- /dev/null
+++ b/ui/src/app/api/model_archs/route.ts
@@ -0,0 +1,17 @@
+import { NextResponse } from 'next/server';
+import { listExtensionUiModules } from '@/server/extensionUi';
+
+// Extension UI modules (AI_TOOLKIT_UI_MODELS) as transpiled CommonJS source,
+// evaluated client-side by useModelArchs. Read from disk per request so new
+// models appear without a rebuild.
+export const runtime = 'nodejs';
+export const dynamic = 'force-dynamic';
+
+export async function GET() {
+ try {
+ const result = await listExtensionUiModules();
+ return NextResponse.json(result, { headers: { 'Cache-Control': 'no-store' } });
+ } catch (e: any) {
+ return NextResponse.json({ error: e?.message || String(e) }, { status: 500 });
+ }
+}
diff --git a/ui/src/app/generate/page.tsx b/ui/src/app/generate/page.tsx
index 32e6fd4..fbf5595 100644
--- a/ui/src/app/generate/page.tsx
+++ b/ui/src/app/generate/page.tsx
@@ -36,7 +36,8 @@ import { defaultInferenceJobConfig } from '@/helpers/inferenceJobConfig';
import { encodeFilePathForUrl, objToTags, tagsToObj } from '@/utils/basic';
import { latentToImage, payloadToFloat32, readEngineFrames, PreviewInfo } from '@/utils/engineStream';
import { isMac } from '@/helpers/basic';
-import { modelArchs, getGenerateDefaults, GenerateDefaults } from '@/app/jobs/new/options';
+import { getGenerateDefaults, GenerateDefaults } from '@/app/jobs/new/options';
+import { useModelArchs } from '@/extensions/modelArchs';
import GenerateFooter from '@/components/generate/GenerateFooter';
import LoraBrowserModal, { LoraPick } from '@/components/generate/LoraBrowserModal';
@@ -296,9 +297,10 @@ function GeneratePageInner() {
// ---- models: the same arch list the training UI uses (jobs/new/options.tsx) ----
// text-generating archs have no Generate page path yet
+ const { archs: modelArchs } = useModelArchs();
const archs: GenerateDefaults[] = useMemo(
() => modelArchs.filter(a => a.group !== 'llm').map(getGenerateDefaults),
- [],
+ [modelArchs],
);
const ready = !!engineStatus?.running;
diff --git a/ui/src/app/jobs/new/SimpleJob.tsx b/ui/src/app/jobs/new/SimpleJob.tsx
index 99d2bc3..a03486f 100644
--- a/ui/src/app/jobs/new/SimpleJob.tsx
+++ b/ui/src/app/jobs/new/SimpleJob.tsx
@@ -1,14 +1,13 @@
'use client';
import { useMemo } from 'react';
import {
- modelArchs,
ModelArch,
- groupedModelOptions,
quantizationOptions,
defaultQtype,
jobTypeOptions,
SampleTags,
} from './options';
+import { useModelArchs } from '@/extensions/modelArchs';
import { defaultCompileOptions, defaultDatasetConfig } from './jobConfig';
import { GroupedSelectOption, JobConfig, SelectOption } from '@/types';
import { objectCopy, tagsToObj, objToTags } from '@/utils/basic';
@@ -61,9 +60,10 @@ export default function SimpleJob({
datasetOptions,
isLoading,
}: Props) {
+ const { archs: modelArchs, groupedModelOptions } = useModelArchs();
const modelArch = useMemo(() => {
return modelArchs.find(a => a.name === jobConfig.config.process[0].model.arch) as ModelArch;
- }, [jobConfig.config.process[0].model.arch]);
+ }, [modelArchs, jobConfig.config.process[0].model.arch]);
const jobType = useMemo(() => {
return jobTypeOptions.find(j => j.value === jobConfig.config.process[0].type);
@@ -284,7 +284,7 @@ export default function SimpleJob({
label="Model Architecture"
value={jobConfig.config.process[0].model.arch}
onChange={value => {
- handleModelArchChange(jobConfig.config.process[0].model.arch, value, jobConfig, setJobConfig);
+ handleModelArchChange(modelArchs, jobConfig.config.process[0].model.arch, value, jobConfig, setJobConfig);
}}
options={groupedModelOptions}
/>
diff --git a/ui/src/app/jobs/new/options.tsx b/ui/src/app/jobs/new/options.tsx
index cbbc3ab..29fcbf7 100644
--- a/ui/src/app/jobs/new/options.tsx
+++ b/ui/src/app/jobs/new/options.tsx
@@ -1,14 +1,6 @@
import React from 'react';
-import Link from 'next/link';
import { GroupedSelectOption, SelectOption, JobConfig, ConfigDoc } from '@/types';
import { defaultSliderConfig } from './jobConfig';
-import {
- defaultAudioSampleConfig,
- defaultSampleConfig,
- defaultIdeogramSamplesConfig,
- defaultYue2SampleConfig,
- defaultQwen25OmniSampleConfig,
-} from '@/helpers/defaultSamples';
type Control = 'depth' | 'line' | 'pose' | 'inpaint';
@@ -124,1812 +116,20 @@ export interface ModelArch {
customModelSelectOptions?: CustomModelOption[];
}
-const defaultNameOrPath = '';
-const defaultLinearRank = 32;
-
-export const modelArchs: ModelArch[] = [
- {
- name: 'anima',
- label: 'Anima',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['circlestone-labs/Anima-Base-v1.0-Diffusers', defaultNameOrPath],
- 'config.process[0].model.quantize': [false, false],
- 'config.process[0].model.quantize_te': [false, false],
- 'config.process[0].model.qtype': ['', 'qfloat8'],
- 'config.process[0].model.qtype_te': ['', 'qfloat8'],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].sample.neg': [
- 'worst quality, low quality, score_1, score_2, score_3, blurry, jpeg artifacts, sepia, signature, artist name',
- '',
- ],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram', 'model.layer_offloading'],
- },
- {
- name: 'flux',
- label: 'FLUX.1',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['black-forest-labs/FLUX.1-dev', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- },
- disableSections: ['network.conv'],
- gateUrl: 'https://huggingface.co/black-forest-labs/FLUX.1-dev',
- },
- {
- name: 'flux_kontext',
- label: 'FLUX.1-Kontext-dev',
- group: 'instruction',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['black-forest-labs/FLUX.1-Kontext-dev', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- },
- disableSections: ['network.conv'],
- additionalSections: ['datasets.control_path', 'sample.ctrl_img'],
- gateUrl: 'https://huggingface.co/black-forest-labs/FLUX.1-Kontext-dev',
- },
- {
- name: 'flex1',
- label: 'Flex.1',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['ostris/Flex.1-alpha', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].train.bypass_guidance_embedding': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- },
- disableSections: ['network.conv'],
- },
- {
- name: 'flex2',
- label: 'Flex.2',
- group: 'image',
- controls: ['depth', 'line', 'pose', 'inpaint'],
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['ostris/Flex.2-preview', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.model_kwargs': [
- {
- invert_inpaint_mask_chance: 0.2,
- inpaint_dropout: 0.5,
- control_dropout: 0.5,
- inpaint_random_chance: 0.2,
- do_random_inpainting: true,
- random_blur_mask: true,
- random_dialate_mask: true,
- },
- {},
- ],
- 'config.process[0].train.bypass_guidance_embedding': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- },
- disableSections: ['network.conv'],
- },
- {
- name: 'chroma',
- label: 'Chroma',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['lodestones/Chroma1-Base', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- },
- disableSections: ['network.conv'],
- },
- {
- name: 'zeta_chroma',
- label: 'Zeta Chroma',
- group: 'experimental',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': [
- 'lodestones/Zeta-Chroma/zeta-chroma-base-x0-pixel-dino-distance.safetensors',
- defaultNameOrPath,
- ],
- 'config.process[0].model.extras_name_or_path': ['Tongyi-MAI/Z-Image-Turbo', undefined],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- },
- disableSections: ['network.conv'],
- },
- {
- name: 'wan21:1b',
- label: 'Wan 2.1 (1.3B)',
- group: 'video',
- isVideoModel: true,
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Wan-AI/Wan2.1-T2V-1.3B-Diffusers', defaultNameOrPath],
- 'config.process[0].model.quantize': [false, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].sample.num_frames': [41, 1],
- 'config.process[0].sample.fps': [16, 1],
- 'config.process[0].datasets[x].fps': [16, undefined],
- },
- disableSections: ['network.conv'],
- additionalSections: ['datasets.num_frames', 'model.low_vram', 'datasets.auto_frame_count'],
- },
- {
- name: 'wan21_i2v:14b480p',
- label: 'Wan 2.1 I2V (14B-480P)',
- group: 'video',
- isVideoModel: true,
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Wan-AI/Wan2.1-I2V-14B-480P-Diffusers', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].sample.num_frames': [41, 1],
- 'config.process[0].sample.fps': [16, 1],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].datasets[x].fps': [16, undefined],
- },
- disableSections: ['network.conv'],
- additionalSections: ['sample.ctrl_img', 'datasets.num_frames', 'model.low_vram', 'datasets.auto_frame_count'],
- },
- {
- name: 'wan21_i2v:14b',
- label: 'Wan 2.1 I2V (14B-720P)',
- group: 'video',
- isVideoModel: true,
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Wan-AI/Wan2.1-I2V-14B-720P-Diffusers', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].sample.num_frames': [41, 1],
- 'config.process[0].sample.fps': [16, 1],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].datasets[x].fps': [16, undefined],
- },
- disableSections: ['network.conv'],
- additionalSections: ['sample.ctrl_img', 'datasets.num_frames', 'model.low_vram', 'datasets.auto_frame_count'],
- },
- {
- name: 'wan21:14b',
- label: 'Wan 2.1 (14B)',
- group: 'video',
- isVideoModel: true,
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Wan-AI/Wan2.1-T2V-14B-Diffusers', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].sample.num_frames': [41, 1],
- 'config.process[0].sample.fps': [16, 1],
- 'config.process[0].datasets[x].fps': [16, undefined],
- },
- disableSections: ['network.conv'],
- additionalSections: ['datasets.num_frames', 'model.low_vram', 'datasets.auto_frame_count'],
- },
- {
- name: 'wan22_14b:t2v',
- label: 'Wan 2.2 (14B)',
- group: 'video',
- isVideoModel: true,
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['ai-toolkit/Wan2.2-T2V-A14B-Diffusers-bf16', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].sample.num_frames': [41, 1],
- 'config.process[0].sample.fps': [16, 1],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].datasets[x].fps': [16, undefined],
- 'config.process[0].model.model_kwargs': [
- {
- train_high_noise: true,
- train_low_noise: true,
- },
- {},
- ],
- },
- disableSections: ['network.conv'],
- additionalSections: [
- 'datasets.num_frames',
- 'model.low_vram',
- 'model.multistage',
- 'model.layer_offloading',
- 'datasets.auto_frame_count',
- ],
- accuracyRecoveryAdapters: {
- // '3 bit with ARA': 'uint3|ostris/accuracy_recovery_adapters/wan22_14b_t2i_torchao_uint3.safetensors',
- '4 bit with ARA': 'uint4|ostris/accuracy_recovery_adapters/wan22_14b_t2i_torchao_uint4.safetensors',
- },
- },
- {
- name: 'wan22_14b_i2v',
- label: 'Wan 2.2 I2V (14B)',
- group: 'video',
- isVideoModel: true,
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['ai-toolkit/Wan2.2-I2V-A14B-Diffusers-bf16', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].sample.num_frames': [41, 1],
- 'config.process[0].sample.fps': [16, 1],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].datasets[x].fps': [16, undefined],
- 'config.process[0].model.model_kwargs': [
- {
- train_high_noise: true,
- train_low_noise: true,
- },
- {},
- ],
- },
- disableSections: ['network.conv'],
- additionalSections: [
- 'sample.ctrl_img',
- 'datasets.num_frames',
- 'model.low_vram',
- 'model.multistage',
- 'model.layer_offloading',
- 'datasets.auto_frame_count',
- ],
- accuracyRecoveryAdapters: {
- '4 bit with ARA': 'uint4|ostris/accuracy_recovery_adapters/wan22_14b_i2v_torchao_uint4.safetensors',
- },
- },
- {
- name: 'wan22_5b',
- label: 'Wan 2.2 TI2V (5B)',
- group: 'video',
- isVideoModel: true,
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Wan-AI/Wan2.2-TI2V-5B-Diffusers', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].sample.num_frames': [121, 1],
- 'config.process[0].sample.fps': [24, 1],
- 'config.process[0].sample.width': [768, 1024],
- 'config.process[0].sample.height': [768, 1024],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].datasets[x].do_i2v': [true, undefined],
- 'config.process[0].datasets[x].fps': [24, undefined],
- },
- disableSections: ['network.conv'],
- additionalSections: [
- 'sample.ctrl_img',
- 'datasets.num_frames',
- 'model.low_vram',
- 'datasets.do_i2v',
- 'datasets.auto_frame_count',
- ],
- },
- {
- name: 'lumina2',
- label: 'Lumina2',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Alpha-VLLM/Lumina-Image-2.0', defaultNameOrPath],
- 'config.process[0].model.quantize': [false, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- },
- disableSections: ['network.conv'],
- },
- {
- name: 'qwen_image',
- label: 'Qwen-Image',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Qwen/Qwen-Image', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram', 'model.layer_offloading'],
- accuracyRecoveryAdapters: {
- '3 bit with ARA': 'uint3|ostris/accuracy_recovery_adapters/qwen_image_torchao_uint3.safetensors',
- },
- },
- {
- name: 'qwen_image:2512',
- label: 'Qwen-Image-2512',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Qwen/Qwen-Image-2512', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram', 'model.layer_offloading'],
- // Training an ARA now, the other one will not work
- accuracyRecoveryAdapters: {
- '3 bit with ARA': 'uint3|ostris/accuracy_recovery_adapters/qwen_image_2512_torchao_uint3.safetensors',
- '4 bit with ARA': 'uint4|ostris/accuracy_recovery_adapters/qwen_image_2512_torchao_uint4.safetensors',
- },
- },
- {
- name: 'qwen_image_edit',
- label: 'Qwen-Image-Edit',
- group: 'instruction',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Qwen/Qwen-Image-Edit', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- },
- disableSections: ['network.conv'],
- additionalSections: ['datasets.control_path', 'sample.ctrl_img', 'model.low_vram', 'model.layer_offloading'],
- accuracyRecoveryAdapters: {
- '3 bit with ARA': 'uint3|ostris/accuracy_recovery_adapters/qwen_image_edit_torchao_uint3.safetensors',
- },
- },
- {
- name: 'qwen_image_edit_plus',
- label: 'Qwen-Image-Edit-2509',
- group: 'instruction',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Qwen/Qwen-Image-Edit-2509', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- 'config.process[0].model.model_kwargs': [
- {
- match_target_res: false,
- },
- {},
- ],
- },
- disableSections: ['network.conv', 'train.unload_text_encoder'],
- additionalSections: [
- 'datasets.multi_control_paths',
- 'sample.multi_ctrl_imgs',
- 'model.low_vram',
- 'model.layer_offloading',
- 'model.qie.match_target_res',
- ],
- accuracyRecoveryAdapters: {
- '3 bit with ARA': 'uint3|ostris/accuracy_recovery_adapters/qwen_image_edit_2509_torchao_uint3.safetensors',
- },
- },
- {
- name: 'qwen_image_edit_plus:2511',
- label: 'Qwen-Image-Edit-2511',
- group: 'instruction',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Qwen/Qwen-Image-Edit-2511', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- 'config.process[0].model.model_kwargs': [
- {
- match_target_res: false,
- },
- {},
- ],
- },
- disableSections: ['network.conv', 'train.unload_text_encoder'],
- additionalSections: [
- 'datasets.multi_control_paths',
- 'sample.multi_ctrl_imgs',
- 'model.low_vram',
- 'model.layer_offloading',
- 'model.qie.match_target_res',
- ],
- accuracyRecoveryAdapters: {
- '3 bit with ARA': 'uint3|ostris/accuracy_recovery_adapters/qwen_image_edit_2511_torchao_uint3.safetensors',
- },
- },
- {
- name: 'hidream',
- label: 'HiDream',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['HiDream-ai/HiDream-I1-Full', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.lr': [0.0002, 0.0001],
- 'config.process[0].train.timestep_type': ['shift', 'sigmoid'],
- 'config.process[0].network.network_kwargs.ignore_if_contains': [['ff_i.experts', 'ff_i.gate'], []],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram'],
- accuracyRecoveryAdapters: {
- '3 bit with ARA': 'uint3|ostris/accuracy_recovery_adapters/hidream_i1_full_torchao_uint3.safetensors',
- },
- },
- {
- name: 'hidream_e1',
- label: 'HiDream E1',
- group: 'instruction',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['HiDream-ai/HiDream-E1-1', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.lr': [0.0001, 0.0001],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].network.network_kwargs.ignore_if_contains': [['ff_i.experts', 'ff_i.gate'], []],
- },
- disableSections: ['network.conv'],
- additionalSections: ['datasets.control_path', 'sample.ctrl_img', 'model.low_vram'],
- },
- {
- name: 'sdxl',
- label: 'SDXL',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['stabilityai/stable-diffusion-xl-base-1.0', defaultNameOrPath],
- 'config.process[0].model.quantize': [false, false],
- 'config.process[0].model.quantize_te': [false, false],
- 'config.process[0].sample.sampler': ['ddpm', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['ddpm', 'flowmatch'],
- 'config.process[0].sample.guidance_scale': [6, 4],
- },
- disableSections: ['model.quantize', 'train.timestep_type'],
- },
- {
- name: 'sd15',
- label: 'SD 1.5',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['stable-diffusion-v1-5/stable-diffusion-v1-5', defaultNameOrPath],
- 'config.process[0].sample.sampler': ['ddpm', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['ddpm', 'flowmatch'],
- 'config.process[0].sample.width': [512, 1024],
- 'config.process[0].sample.height': [512, 1024],
- 'config.process[0].sample.guidance_scale': [6, 4],
- },
- disableSections: ['model.quantize', 'train.timestep_type'],
- },
- {
- name: 'omnigen2',
- label: 'OmniGen2',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['OmniGen2/OmniGen2', defaultNameOrPath],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].model.quantize': [false, false],
- 'config.process[0].model.quantize_te': [true, false],
- },
- disableSections: ['network.conv'],
- additionalSections: ['datasets.control_path', 'sample.ctrl_img'],
- },
- {
- name: 'flux2',
- label: 'FLUX.2',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['black-forest-labs/FLUX.2-dev', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- 'config.process[0].model.model_kwargs': [
- {
- match_target_res: false,
- },
- {},
- ],
- },
- disableSections: ['network.conv'],
- additionalSections: [
- 'datasets.multi_control_paths',
- 'sample.multi_ctrl_imgs',
- 'model.low_vram',
- 'model.layer_offloading',
- 'model.qie.match_target_res',
- ],
- gateUrl: 'https://huggingface.co/black-forest-labs/FLUX.2-dev',
- },
- {
- name: 'zimage:turbo',
- label: 'Z-Image Turbo (w/ Training Adapter)',
- generateNameOverride: 'Z-Image Turbo',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Tongyi-MAI/Z-Image-Turbo', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- 'config.process[0].model.assistant_lora_path': [
- 'ostris/zimage_turbo_training_adapter/zimage_turbo_training_adapter_v2.safetensors',
- undefined,
- ],
- 'config.process[0].sample.guidance_scale': [1, 4],
- 'config.process[0].sample.sample_steps': [9, 25],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram', 'model.layer_offloading', 'model.assistant_lora_path'],
- },
- {
- name: 'zimage',
- label: 'Z-Image',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Tongyi-MAI/Z-Image', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- 'config.process[0].sample.sample_steps': [30, 25],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram', 'model.layer_offloading'],
- },
- {
- name: 'zimage:deturbo',
- label: 'Z-Image De-Turbo (De-Distilled)',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['ostris/Z-Image-De-Turbo', defaultNameOrPath],
- 'config.process[0].model.extras_name_or_path': ['Tongyi-MAI/Z-Image-Turbo', undefined],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- 'config.process[0].sample.guidance_scale': [3, 4],
- 'config.process[0].sample.sample_steps': [25, 25],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram', 'model.layer_offloading'],
- },
- {
- name: 'minimax_h3',
- label: 'MiniMax-H3',
- group: 'video',
- isVideoModel: true,
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Comfy-Org/MiniMax-H3', defaultNameOrPath],
- // the Comfy-Org weights are pre-quantized (int8 convrot DiT, nvfp4 TE); these
- // qtypes match the checkpoints exactly, so the load is unchanged. Picking a
- // different qtype re-quantizes layer by layer into that format.
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.qtype': ['convrot8', 'qfloat8'],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.qtype_te': ['nvfp4', 'qfloat8'],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.cache_text_embeddings': [true, false],
- 'config.process[0].train.do_guidance_loss': [true, undefined],
- 'config.process[0].train.guidance_loss_target': [3.5, undefined],
- 'config.process[0].model.assistant_lora_path': [
- 'ostris/minimax_h3_training_adapter/minimax_h3_training_adapter_v1.safetensors',
- undefined,
- ],
- 'config.process[0].network.linear': [16, defaultLinearRank],
- 'config.process[0].network.linear_alpha': [16, defaultLinearRank],
- 'config.process[0].network.network_kwargs.ignore_if_contains': [['adaln_proj'], []],
- 'config.process[0].sample.num_frames': [107, 1],
- 'config.process[0].sample.fps': [24, 1],
- 'config.process[0].sample.width': [768, 1024],
- 'config.process[0].sample.height': [768, 1024],
- 'config.process[0].sample.guidance_scale': [1, 4],
- 'config.process[0].sample.sample_steps': [28, 25],
- 'config.process[0].train.audio_loss_multiplier': [1.0, undefined],
- 'config.process[0].train.timestep_type': ['shift', 'sigmoid'],
- 'config.process[0].datasets[x].do_i2v': [false, undefined],
- 'config.process[0].datasets[x].do_audio': [true, undefined],
- 'config.process[0].datasets[x].cache_latents_to_disk': [true, false],
- 'config.process[0].datasets[x].fps': [24, undefined],
- 'config.process[0].datasets[x].num_frames': [39, undefined],
- 'config.process[0].datasets[x].auto_frame_count': [true, undefined],
- },
- disableSections: ['network.conv'],
- additionalSections: [
- 'sample.ctrl_img',
- 'datasets.num_frames',
- 'model.layer_offloading',
- 'model.low_vram',
- 'datasets.do_audio',
- 'datasets.audio_normalize',
- 'datasets.audio_preserve_pitch',
- 'datasets.do_i2v',
- 'train.audio_loss_multiplier',
- 'datasets.auto_frame_count',
- 'model.assistant_lora_path',
- ],
- customModelSelectOptions: [
- {
- label: 'Distillation Handling Method',
- options: [
- { value: 'cg', label: 'Contrastive Guidance' },
- { value: 'ta', label: 'Training Adapter' },
- { value: 'both', label: 'Contrastive Guidance + Training Adapter (default)' },
- { value: 'none', label: 'None' },
- ],
- getValue: (config: JobConfig) => {
- const assistantLoraPath = config?.config?.process?.[0]?.model?.assistant_lora_path;
- const hasAssistantLoraPath = assistantLoraPath && assistantLoraPath.trim() !== '';
- const hasContrastiveGuidance = config?.config?.process?.[0]?.train?.do_guidance_loss;
- if (hasAssistantLoraPath && hasContrastiveGuidance) {
- return 'both';
- }
- if (hasAssistantLoraPath) {
- return 'ta';
- }
- if (hasContrastiveGuidance) {
- return 'cg';
- }
- return 'none';
- },
- onChange: (value: string, config: JobConfig, setJobConfig: (value: any, key: string) => void) => {
- if (value === 'cg') {
- setJobConfig(true, 'config.process[0].train.do_guidance_loss');
- setJobConfig(undefined, 'config.process[0].model.assistant_lora_path');
- if (!config?.config?.process?.[0]?.train?.guidance_loss_target) {
- setJobConfig(3.5, 'config.process[0].train.guidance_loss_target');
- }
- } else if (value === 'ta') {
- setJobConfig(undefined, 'config.process[0].train.do_guidance_loss');
- setJobConfig(undefined, 'config.process[0].train.guidance_loss_target');
- setJobConfig(
- 'ostris/minimax_h3_training_adapter/minimax_h3_training_adapter_v1.safetensors',
- 'config.process[0].model.assistant_lora_path',
- );
- } else if (value === 'both') {
- setJobConfig(true, 'config.process[0].train.do_guidance_loss');
- setJobConfig(
- 'ostris/minimax_h3_training_adapter/minimax_h3_training_adapter_v1.safetensors',
- 'config.process[0].model.assistant_lora_path',
- );
- if (!config?.config?.process?.[0]?.train?.guidance_loss_target) {
- setJobConfig(3.5, 'config.process[0].train.guidance_loss_target');
- }
- } else if (value === 'none') {
- setJobConfig(undefined, 'config.process[0].train.do_guidance_loss');
- setJobConfig(undefined, 'config.process[0].train.guidance_loss_target');
- setJobConfig(undefined, 'config.process[0].model.assistant_lora_path');
- }
- },
- doc: {
- title: 'MiniMax-H3 Distillation Handling',
- description: (
-
- MiniMax H3 is a guidance distilled model, so training on it directly will make the guidance distillation
- break down. There are two different ways to train on this model without breaking the guidance
- distillation: Contrastive Guidance and Training Adapter. Both have their pros and cons. The adapter is
- faster, but will still break down over a long run. Contrastive Guidance is slower, but is less likely to
- break down.
-
- ),
- },
- },
- ],
- modelNotes: (
-
-
- Weights load from the{' '}
-
- Models Folder Path
- {' '}
- set in settings. Anything missing is downloaded there from Comfy-Org/MiniMax-H3 on first load
- (~43GB total). Files used:
-
-
- {`/
-├── diffusion_models/
-│ └── minimax_h3_fl2va_pruned_int8_convrot.safetensors
-├── text_encoders/
-│ └── qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors
-└── vae/
- ├── minimax_h3_video_vae_fp16.safetensors
- └── minimax_h3_audio_vae_fp32.safetensors`}
-
-
- The checkpoints are pre-quantized and load directly: int8 ConvRot DiT (~21GB) and nvfp4 Qwen3-VL text encoder
- (~16GB). The default qtypes (convrot8 / nvfp4) match the files exactly, so nothing
- is re-quantized on load. Picking a different quantization re-quantizes the pre-quantized layers into that
- format, one layer at a time.
-
-
- Supports t2v and first-frame i2v (ctrl img / i2v datasets) with joint audio. The model is guidance-distilled —
- keep guidance scale at 1. Video is fixed 24 fps and frame counts snap down to the 17n+5 grid (5, 22, 39, 56,
- ..., 107, 124 ≈ 5s). Image datasets (num_frames 1) train as single frames, and a sample with num_frames 1
- renders a single image.
-
-
- ),
- },
- {
- name: 'minimax_h3_ref2va',
- label: 'MiniMax-H3 Ref2V',
- group: 'video',
- isVideoModel: true,
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Comfy-Org/MiniMax-H3', defaultNameOrPath],
- // pre-quantized weights: matching qtypes keep the load unchanged
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.qtype': ['convrot8', 'qfloat8'],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.qtype_te': ['nvfp4', 'qfloat8'],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.cache_text_embeddings': [true, false],
- 'config.process[0].train.do_guidance_loss': [true, undefined],
- 'config.process[0].train.guidance_loss_target': [3.5, undefined],
- 'config.process[0].model.assistant_lora_path': [
- 'ostris/minimax_h3_training_adapter/minimax_h3_ref2va_training_adapter_v1.safetensors',
- undefined,
- ],
- 'config.process[0].network.linear': [16, defaultLinearRank],
- 'config.process[0].network.linear_alpha': [16, defaultLinearRank],
- 'config.process[0].network.network_kwargs.ignore_if_contains': [['adaln_proj'], []],
- 'config.process[0].sample.num_frames': [107, 1],
- 'config.process[0].sample.fps': [24, 1],
- 'config.process[0].sample.width': [768, 1024],
- 'config.process[0].sample.height': [768, 1024],
- 'config.process[0].sample.guidance_scale': [1, 4],
- 'config.process[0].sample.sample_steps': [28, 25],
- 'config.process[0].train.audio_loss_multiplier': [1.0, undefined],
- 'config.process[0].train.timestep_type': ['shift', 'sigmoid'],
- 'config.process[0].datasets[x].do_audio': [true, undefined],
- 'config.process[0].datasets[x].cache_latents_to_disk': [true, false],
- 'config.process[0].datasets[x].fps': [24, undefined],
- 'config.process[0].datasets[x].num_frames': [39, undefined],
- 'config.process[0].datasets[x].auto_frame_count': [true, undefined],
- },
- disableSections: ['network.conv'],
- additionalSections: [
- 'sample.multi_ctrl_imgs',
- 'datasets.multi_control_paths',
- 'datasets.num_frames',
- 'model.layer_offloading',
- 'model.low_vram',
- 'datasets.do_audio',
- 'datasets.audio_normalize',
- 'datasets.audio_preserve_pitch',
- 'train.audio_loss_multiplier',
- 'datasets.auto_frame_count',
- 'model.assistant_lora_path',
- ],
- customModelSelectOptions: [
- {
- label: 'Distillation Handling Method',
- options: [
- { value: 'cg', label: 'Contrastive Guidance' },
- { value: 'ta', label: 'Training Adapter' },
- { value: 'both', label: 'Contrastive Guidance + Training Adapter (default)' },
- { value: 'dopsd', label: 'D-OPSD' },
- { value: 'none', label: 'None' },
- ],
- getValue: (config: JobConfig) => {
- if (config?.config?.process?.[0]?.model?.model_kwargs?.dopsd) {
- return 'dopsd';
- }
- const assistantLoraPath = config?.config?.process?.[0]?.model?.assistant_lora_path;
- const hasAssistantLoraPath = assistantLoraPath && assistantLoraPath.trim() !== '';
- const hasContrastiveGuidance = config?.config?.process?.[0]?.train?.do_guidance_loss;
- if (hasAssistantLoraPath && hasContrastiveGuidance) {
- return 'both';
- }
- if (hasAssistantLoraPath) {
- return 'ta';
- }
- if (hasContrastiveGuidance) {
- return 'cg';
- }
- return 'none';
- },
- onChange: (value: string, config: JobConfig, setJobConfig: (value: any, key: string) => void) => {
- const kwargs = { ...(config?.config?.process?.[0]?.model?.model_kwargs ?? {}) };
- if (value === 'dopsd') {
- kwargs.dopsd = true;
- kwargs.dopsd_bleed_strength = 1.0;
- } else {
- delete kwargs.dopsd;
- delete kwargs.dopsd_bleed_strength;
- }
- setJobConfig(kwargs, 'config.process[0].model.model_kwargs');
- if (value === 'cg') {
- setJobConfig(true, 'config.process[0].train.do_guidance_loss');
- setJobConfig(undefined, 'config.process[0].model.assistant_lora_path');
- if (!config?.config?.process?.[0]?.train?.guidance_loss_target) {
- setJobConfig(3.5, 'config.process[0].train.guidance_loss_target');
- }
- } else if (value === 'ta') {
- setJobConfig(undefined, 'config.process[0].train.do_guidance_loss');
- setJobConfig(undefined, 'config.process[0].train.guidance_loss_target');
- setJobConfig(
- 'ostris/minimax_h3_training_adapter/minimax_h3_ref2va_training_adapter_v1.safetensors',
- 'config.process[0].model.assistant_lora_path',
- );
- } else if (value === 'both') {
- setJobConfig(true, 'config.process[0].train.do_guidance_loss');
- setJobConfig(
- 'ostris/minimax_h3_training_adapter/minimax_h3_ref2va_training_adapter_v1.safetensors',
- 'config.process[0].model.assistant_lora_path',
- );
- if (!config?.config?.process?.[0]?.train?.guidance_loss_target) {
- setJobConfig(3.5, 'config.process[0].train.guidance_loss_target');
- }
- } else if (value === 'dopsd' || value === 'none') {
- setJobConfig(undefined, 'config.process[0].train.do_guidance_loss');
- setJobConfig(undefined, 'config.process[0].train.guidance_loss_target');
- setJobConfig(undefined, 'config.process[0].model.assistant_lora_path');
- }
- },
- doc: {
- title: 'MiniMax-H3 Distillation Handling',
- description: (
-
- MiniMax H3 is a guidance distilled model, so training on it directly will make the guidance distillation
- break down. There are two different ways to train on this model without breaking the guidance
- distillation: Contrastive Guidance and Training Adapter. Both have their pros and cons. The adapter is
- faster, but will still break down over a long run. Contrastive Guidance is slower, but is less likely to
- break down. D-OPSD instead self-distills: a no-grad teacher pass sees the training target as its own
- reference and its prediction becomes the training target for a reference-free pass, baking the reference
- into your trigger word (or the caption itself when no trigger word is set). Re-caches latents with pixel
- tensors.
-
- ),
- },
- },
- {
- label: 'Image Reference Presentation',
- options: [
- { value: 'picture', label: 'Picture (default)' },
- { value: 'video', label: 'Static video clip' },
- ],
- getValue: (config: JobConfig) => {
- return config?.config?.process?.[0]?.model?.model_kwargs?.image_refs_as_video ? 'video' : 'picture';
- },
- onChange: (value: string, config: JobConfig, setJobConfig: (value: any, key: string) => void) => {
- const kwargs = { ...(config?.config?.process?.[0]?.model?.model_kwargs ?? {}) };
- if (value === 'video') {
- kwargs.image_refs_as_video = true;
- } else {
- delete kwargs.image_refs_as_video;
- delete kwargs.image_ref_video_frames;
- }
- setJobConfig(kwargs, 'config.process[0].model.model_kwargs');
- },
- doc: {
- title: 'MiniMax-H3 Image Reference Presentation',
- description: (
-
-
- How still-image references (dataset control images and sample ctrl images) are shown to the model. Video
- references always use the video path.
-
-
- Picture : the native ref2va recipe — a single-frame reference block, shown to Qwen3-VL
- as a <Picture i> block, scaled down only.
-
-
- Static video clip : the image is held for 5 frames (2 latent frames) and routed through
- the exact path a reference VIDEO takes — video sizing (matched to the target's pixel area), multi-frame
- reference block, <Video k> timestamped presentation. Use this when training on image
- references but sampling with video references, so the LoRA learns the pathway it will be used through.
- Adds a handful of rows per reference. Frame count is adjustable with{' '}
- model_kwargs.image_ref_video_frames (17n+5). Changing this re-caches text embeddings.
-
-
- ),
- },
- },
- ],
- modelNotes: (
-
-
- Reference-to-video: control images and videos condition the output as subject/style references (never as a
- first frame). References keep their own aspect and are matched to the target's pixel area (images scale down
- only, never up; a same-aspect video reference is exactly the target size). Each rides into the packed sequence
- as a reference block, and is also shown to the Qwen3-VL conditioner as a <Picture i>{' '}
- (image) or timestamped <Video k> (video) vision block. Training references come from the
- dataset control path(s); sampling uses the sample ctrl images — always as references. The Image Reference
- Presentation option can route still images through the video-reference path as short static clips.
-
-
- Weights load like MiniMax-H3 (see that arch's notes) from the{' '}
-
- Models Folder Path
-
- , using diffusion_models/minimax_h3_ref2va_pruned_int8_convrot.safetensors — the ref2va partition
- of the same release; text encoder and VAEs are shared with the fl2va arch. Everything else (pre-quantized
- load, 24 fps, 17n+5 frame grid, guidance scale 1, single-image mode) matches MiniMax-H3.
-
-
- ),
- },
- {
- name: 'ltx2',
- label: 'LTX-2',
- group: 'video',
- isVideoModel: true,
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Lightricks/LTX-2', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].sample.num_frames': [121, 1],
- 'config.process[0].sample.fps': [24, 1],
- 'config.process[0].sample.width': [768, 1024],
- 'config.process[0].sample.height': [768, 1024],
- 'config.process[0].train.audio_loss_multiplier': [1.0, undefined],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].datasets[x].do_i2v': [false, undefined],
- 'config.process[0].datasets[x].do_audio': [true, undefined],
- 'config.process[0].datasets[x].fps': [24, undefined],
- 'config.process[0].datasets[x].auto_frame_count': [false, undefined],
- },
- disableSections: ['network.conv'],
- additionalSections: [
- 'sample.ctrl_img',
- 'datasets.num_frames',
- 'model.layer_offloading',
- 'model.low_vram',
- 'datasets.do_audio',
- 'datasets.audio_normalize',
- 'datasets.audio_preserve_pitch',
- 'datasets.do_i2v',
- 'train.audio_loss_multiplier',
- 'datasets.auto_frame_count',
- ],
- },
- {
- name: 'ltx2.3',
- label: 'LTX-2.3',
- group: 'video',
- isVideoModel: true,
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['Lightricks/LTX-2.3/ltx-2.3-22b-dev.safetensors', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].sample.num_frames': [121, 1],
- 'config.process[0].sample.fps': [24, 1],
- 'config.process[0].sample.width': [768, 1024],
- 'config.process[0].sample.height': [768, 1024],
- 'config.process[0].train.audio_loss_multiplier': [1.0, undefined],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].datasets[x].cache_latents_to_disk': [true, false],
- 'config.process[0].datasets[x].do_i2v': [false, undefined],
- 'config.process[0].datasets[x].do_audio': [true, undefined],
- 'config.process[0].datasets[x].fps': [24, undefined],
- 'config.process[0].datasets[x].auto_frame_count': [false, undefined],
- },
- disableSections: ['network.conv'],
- additionalSections: [
- 'sample.ctrl_img',
- 'datasets.num_frames',
- 'model.layer_offloading',
- 'model.low_vram',
- 'datasets.do_audio',
- 'datasets.audio_normalize',
- 'datasets.audio_preserve_pitch',
- 'datasets.do_i2v',
- 'train.audio_loss_multiplier',
- 'datasets.auto_frame_count',
- ],
- },
- {
- name: 'ltx2.5',
- label: 'LTX-2.5',
- gateUrl: 'https://huggingface.co/Lightricks/LTX-2.5',
- group: 'video',
- isVideoModel: true,
- defaults: {
- // default updates when [selected, unselected] in the UI
- // comfy-style split files resolve from/download to the models folder;
- // the int8 ConvRot dev transformer is the default
- 'config.process[0].model.name_or_path': ['Lightricks/LTX-2.5', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.qtype': ['convrot8', 'qfloat8'],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.qtype_te': ['convrot8', 'qfloat8'],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].sample.num_frames': [121, 1],
- 'config.process[0].sample.fps': [24, 1],
- 'config.process[0].sample.width': [768, 1024],
- 'config.process[0].sample.height': [768, 1024],
- 'config.process[0].train.audio_loss_multiplier': [1.0, undefined],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].datasets[x].cache_latents_to_disk': [true, false],
- 'config.process[0].datasets[x].do_i2v': [false, undefined],
- 'config.process[0].datasets[x].do_audio': [true, undefined],
- 'config.process[0].datasets[x].fps': [24, undefined],
- 'config.process[0].datasets[x].auto_frame_count': [false, undefined],
- },
- disableSections: ['network.conv'],
- additionalSections: [
- 'sample.ctrl_img',
- 'datasets.num_frames',
- 'model.layer_offloading',
- 'model.low_vram',
- 'datasets.do_audio',
- 'datasets.audio_normalize',
- 'datasets.audio_preserve_pitch',
- 'datasets.do_i2v',
- 'train.audio_loss_multiplier',
- 'datasets.auto_frame_count',
- ],
- },
- {
- name: 'flux2_klein_4b',
- label: 'FLUX.2-klein-base-4B',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['black-forest-labs/FLUX.2-klein-base-4B', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- 'config.process[0].model.model_kwargs': [
- {
- match_target_res: false,
- },
- {},
- ],
- },
- disableSections: ['network.conv'],
- additionalSections: [
- 'datasets.multi_control_paths',
- 'sample.multi_ctrl_imgs',
- 'model.low_vram',
- 'model.layer_offloading',
- 'model.qie.match_target_res',
- ],
- },
- {
- name: 'ernie_image',
- label: 'ERNIE-Image',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['baidu/ERNIE-Image', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram', 'model.layer_offloading'],
- },
- {
- name: 'flux2_klein_9b',
- label: 'FLUX.2-klein-base-9B',
- group: 'image',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': ['black-forest-labs/FLUX.2-klein-base-9B', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].sample.sampler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- 'config.process[0].model.model_kwargs': [
- {
- match_target_res: false,
- },
- {},
- ],
- },
- disableSections: ['network.conv'],
- additionalSections: [
- 'datasets.multi_control_paths',
- 'sample.multi_ctrl_imgs',
- 'model.low_vram',
- 'model.layer_offloading',
- 'model.qie.match_target_res',
- ],
- gateUrl: 'https://huggingface.co/black-forest-labs/FLUX.2-klein-base-9B',
- },
- {
- name: 'ace_step_15_xl',
- label: 'ACE-Step 1.5 XL',
- group: 'audio',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': [
- 'ostris/ace_step_1.5_ComfyUI_files/ace_step_1.5_xl_base_aio.safetensors',
- defaultNameOrPath,
- ],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- 'config.process[0].sample': [defaultAudioSampleConfig, defaultSampleConfig],
- },
- sampleTags: {
- CAPTION: {
- title: 'Audio Prompt',
- type: 'text',
- full: true,
- },
- LYRICS: {
- title: 'Lyrics',
- type: 'multiline',
- full: true,
- },
- BPM: {
- title: 'BPM',
- type: 'number',
- },
- KEYSCALE: {
- title: 'Key Scale',
- type: 'text',
- },
- TIMESIGNATURE: {
- title: 'Time Signature',
- type: 'text',
- },
- DURATION: {
- title: 'Duration (sec)',
- type: 'number',
- },
- LANGUAGE: {
- title: 'Language',
- type: 'text',
- },
- },
- disableSections: ['network.conv'],
- additionalSections: ['sample.multi_ctrl_imgs', 'model.low_vram', 'model.layer_offloading'],
- },
- {
- name: 'qwen25_omni',
- label: 'Qwen2.5-Omni',
- group: 'llm',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': [
- 'ai-toolkit/Qwen2.5-Omni-7B/qwen2_5_omni_7b_convrot8.safetensors',
- defaultNameOrPath,
- ],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [false, false],
- 'config.process[0].model.low_vram': [false, false],
- // the single-file thinker ships convrot8 layers; requesting convrot8 keeps them as-is
- 'config.process[0].model.qtype': ['convrot8', 'qfloat8'],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.batch_size': [1, 1],
- 'config.process[0].sample': [defaultQwen25OmniSampleConfig, defaultSampleConfig],
- // media is encoded on the GPU per step; the cache is optional and large (~54 MB per 300 s of audio)
- 'config.process[0].datasets[x].cache_latents_to_disk': [false, true],
- 'config.process[0].datasets[x].resolution': [[512], [512, 768, 1024]],
- // the caption is the training target; a blank one trains nothing
- 'config.process[0].datasets[x].caption_dropout_rate': [0, 0.05],
- 'config.process[0].model.model_kwargs': [{ instruction: 'Describe this in detail.' }, {}],
- },
- disableSections: [
- 'network.conv',
- 'trigger_word',
- 'train.diff_output_preservation',
- 'train.blank_prompt_preservation',
- 'train.unload_text_encoder',
- 'slider',
- ],
- additionalSections: ['model.model_kwargs.instruction', 'sample.ctrl_img', 'datasets.num_frames'],
- modelNotes: (
-
-
- Qwen2.5-Omni 7B thinker as a text-generating model: audio, image or video in, text out. Each dataset item is a
- media file (mp3, wav, flac, ogg, jpg, png, webp, mp4, ...) with a caption file next to it; the caption is the
- text the model learns to produce for that media. One dataset can mix all three kinds. Video files are used
- when Num Frames is above 1 and are seen as frames only (no audio track).
-
-
- The instruction in model_kwargs.instruction is the user turn for every training item; use the
- same wording when captioning with the trained LoRA. Samples take a media file per prompt and write the
- generated text as a .txt file.
-
-
- Media is encoded by the frozen audio and vision towers on the GPU each step, so Cache Latents to Disk can stay
- off (the cache is about 54 MB per 300 seconds of audio). Batch size is 1. The LoRA trains the text stack only.
- Watch loss/ce: next-token cross-entropy on the caption.
-
-
- ),
- },
- {
- name: 'yue2',
- label: 'YuE2',
- group: 'audio',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': [
- 'Comfy-Org/YuE2/checkpoints/yue2_3b_int8_convrot.safetensors',
- defaultNameOrPath,
- ],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [false, false],
- 'config.process[0].model.low_vram': [false, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['sigmoid', 'sigmoid'],
- // the int8 repack ships convrot8 layers; requesting convrot8 keeps them as-is (no requantization)
- 'config.process[0].model.qtype': ['convrot8', 'qfloat8'],
- 'config.process[0].sample': [defaultYue2SampleConfig, defaultSampleConfig],
- 'config.process[0].datasets[x].cache_latents_to_disk': [true, true],
- // audio has no resolution; every bucket would duplicate the whole dataset
- 'config.process[0].datasets[x].resolution': [[512], [512, 768, 1024]],
- // blank captions break lyric following; the AR must always see the prefix
- 'config.process[0].datasets[x].caption_dropout_rate': [0, 0.05],
- 'config.process[0].model.model_kwargs': [
- { cot: 'full', abc_dropout: 0.5, sample_ar_repetition_penalty: 1.2, ar_kl_weight: 0.2 },
- {},
- ],
- },
- // native YuE2 prompt: style text, a [Lyrics] line, the lyrics
- hasMultiLinePrompts: true,
- modelNotes: (
-
-
- Experimental. The AR (composition) model memorizes quickly and does not work well on small datasets. A handful
- of songs is enough for it to learn the exact token sequence of each song; after that it stops generalizing and
- free-running samples drift away from the training material. Expect to need a large, varied dataset for the AR
- side to learn a style rather than the songs themselves.
-
-
- YuE2 is two experts on one backbone. The AR expert reads the style line and lyrics and writes the song
- as a sequence of semantic codec tokens (25 per second). The NAR expert then renders those tokens into
- audio latents with flow matching. Training a LoRA here trains both: next-token loss on the AR over the whole
- song from its start, flow loss on the NAR over a random window.
-
-
- The AR loss is the one to watch (loss/ar_ce). It starts near 5 and, on a small dataset, falls
- toward 0 within a few hundred steps, which is memorization. ar_kl_weight in model kwargs anchors
- the AR to the base model so it cannot collapse onto the training songs; ar_lr_multiplier and a
- smaller AR rank slow it further. Style comes mostly from the NAR, lyric following from the AR. Do not use
- caption dropout: a blank prompt breaks lyric following.
-
-
- The official audio-to-token encoder is unreleased. Training uses the community tokenizer by Kytra (
-
- @sin_ceriously
-
- ), a MERT-v2-FullSong head that maps real audio to YuE2 codec tokens:{' '}
-
- Mothersuperior/yue2-mothersuperior-realaudio-tokenizer-v4
-
- . It is downloaded on first use. Its tokens are an approximation of the model's own, so rendered samples will
- not be bit-faithful to the training audio even when the AR replays a song exactly. Though it is extremely
- close.
-
-
- ABC generation. Generation is two stages: the AR first writes a lead sheet of the whole song in ABC
- notation (sections, chords, vocal and instrumental melody), then writes the codec tokens conditioned on that
- sheet. It can also run without a sheet ("off" mode), which uses a different instruction line. Samples here do
- the two stages with cot: full (chords) or cot: melody (melody only), or the single
- stage with cot: off.
-
-
- Training for both. Every song is transcribed to an ABC sheet with SheetSage2 and the AR is trained on
- lyrics to sheet and sheet to tokens. abc_dropout (default 0.5) is the fraction of training items
- fed without the sheet instead, as an off-mode prompt, so one LoRA works in both modes. Set it to 0 to train
- the sheet path only, or 1 to train off mode only.
-
-
- Caching is required. The sheet is produced at latent-cache time and stored with the latents and codec
- tokens, so Cache Latents to Disk must stay on: without it, SheetSage2 (about 12 s per song), the MERT
- tokenizer and the VAE would run again on every training step. The cache records which mode built it; changing{' '}
- cot means deleting the dataset's _latent_cache folder so the sheets are rebuilt.
- Audio has no resolution, so keep a single resolution bucket per dataset or every bucket duplicates the songs.
-
-
- Prompt format: a style line, then [Lyrics], then the lyrics with bracketed section headers such
- as [Verse 1] and [Chorus]. The Qwen3-Omni captioner has a YuE2 preset that writes
- captions in this layout. Sample length is set by the Duration field in the sample section.
-
-
- ),
- // no separate text encoder: the prompt side is the AR expert, covered by the transformer quantization
- disableSections: ['network.conv', 'model.quantize_te'],
- additionalSections: ['model.low_vram', 'sample.duration'],
- },
- {
- name: 'ace_step_15',
- label: 'ACE-Step 1.5',
- group: 'audio',
- defaults: {
- // default updates when [selected, unselected] in the UI
- 'config.process[0].model.name_or_path': [
- 'ostris/ace_step_1.5_ComfyUI_files/ace_step_1.5_base_aio.safetensors',
- defaultNameOrPath,
- ],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].train.noise_scheduler': ['flowmatch', 'flowmatch'],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].model.qtype': ['qfloat8', 'qfloat8'],
- 'config.process[0].sample': [defaultAudioSampleConfig, defaultSampleConfig],
- },
- sampleTags: {
- CAPTION: {
- title: 'Audio Prompt',
- type: 'text',
- full: true,
- },
- LYRICS: {
- title: 'Lyrics',
- type: 'multiline',
- full: true,
- },
- BPM: {
- title: 'BPM',
- type: 'number',
- },
- KEYSCALE: {
- title: 'Key Scale',
- type: 'text',
- },
- TIMESIGNATURE: {
- title: 'Time Signature',
- type: 'text',
- },
- DURATION: {
- title: 'Duration (sec)',
- type: 'number',
- },
- LANGUAGE: {
- title: 'Language',
- type: 'text',
- },
- },
- disableSections: ['network.conv'],
- additionalSections: ['sample.multi_ctrl_imgs', 'model.low_vram', 'model.layer_offloading'],
- },
- {
- name: 'nucleus_image',
- label: 'Nucleus-Image',
- group: 'image',
- defaults: {
- 'config.process[0].model.name_or_path': ['NucleusAI/Nucleus-Image', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].network.network_kwargs.ignore_if_contains': [['img_mlp.experts', 'img_mlp.gate'], []],
- 'config.process[0].network.linear': [128, defaultLinearRank],
- 'config.process[0].network.linear_alpha': [128, defaultLinearRank],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram'],
- },
- {
- name: 'hidream_o1',
- label: 'HiDream-O1',
- group: 'image',
- defaults: {
- 'config.process[0].model.name_or_path': ['HiDream-ai/HiDream-O1-Image', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [false, false],
- 'config.process[0].train.timestep_type': ['weighted', 'sigmoid'],
- 'config.process[0].network.conv': [undefined, 16],
- 'config.process[0].network.conv_alpha': [undefined, 16],
- 'config.process[0].train.max_loss': [1.0, undefined],
- 'config.process[0].network.network_kwargs.ignore_if_contains': [['lm_head', 'patch_embed', 'visual'], []],
- 'config.process[0].network.transformer_only': [false, undefined],
- 'config.process[0].sample.width': [2048, 1024],
- 'config.process[0].sample.height': [2048, 1024],
- 'config.process[0].model.model_kwargs': [
- {
- noise_scale_inference: 8.0,
- noise_scale: 8.0,
- },
- {},
- ],
- },
- disableSections: ['network.conv', 'model.quantize_te', 'train.unload_text_encoder'],
- additionalSections: ['model.low_vram', 'model.layer_offloading'],
- },
- {
- name: 'zimage_l2p',
- label: 'Z-Image L2P (pixel space)',
- group: 'image',
- defaults: {
- 'config.process[0].model.name_or_path': ['zhen-nan/L2P/model-1k-merge.safetensors', defaultNameOrPath],
- 'config.process[0].model.extras_name_or_path': ['Tongyi-MAI/Z-Image-Turbo', undefined],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].network.conv': [undefined, 16],
- 'config.process[0].network.conv_alpha': [undefined, 16],
- 'config.process[0].model.low_vram': [true, false],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram', 'model.layer_offloading'],
- },
- {
- name: 'ideogram4',
- label: 'Ideogram4',
- group: 'experimental',
- defaults: {
- 'config.process[0].model.name_or_path': ['ideogram-ai/ideogram-4-fp8', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].network.conv': [undefined, 16],
- 'config.process[0].network.conv_alpha': [undefined, 16],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].sample': [defaultIdeogramSamplesConfig, defaultSampleConfig],
- 'config.process[0].model.unconditional_lora_path': [
- 'ostris/ideogram_4_unconditional_lora/ideogram_4_unconditional_lora_r16.safetensors',
- undefined,
- ],
- },
- disableSections: ['network.conv'],
- additionalSections: [
- 'model.low_vram',
- 'model.layer_offloading',
- 'ideogram_4_prompt',
- 'model.unconditional_lora_path',
- ],
- hasMultiLinePrompts: true,
- gateUrl: 'https://huggingface.co/ideogram-ai/ideogram-4-fp8',
- },
- {
- name: 'prx_pixel',
- label: 'PRXPixel (pixel space)',
- group: 'image',
- defaults: {
- 'config.process[0].model.name_or_path': ['Photoroom/prxpixel-t2i', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].network.conv': [undefined, 16],
- 'config.process[0].network.conv_alpha': [undefined, 16],
- 'config.process[0].model.low_vram': [true, false],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram', 'model.layer_offloading'],
- },
- {
- name: 'krea2',
- label: 'Krea 2 (raw)',
- group: 'image',
- gateUrl: 'https://huggingface.co/krea/Krea-2-Raw',
- defaults: {
- 'config.process[0].model.name_or_path': ['krea/Krea-2-Raw', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].network.conv': [undefined, 16],
- 'config.process[0].network.conv_alpha': [undefined, 16],
- 'config.process[0].model.low_vram': [true, false],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram', 'model.layer_offloading'],
- },
- {
- name: 'krea2:turbo',
- label: 'Krea 2 Turbo (w/ Training Adapter)',
- generateNameOverride: 'Krea 2 Turbo',
- group: 'image',
- gateUrl: 'https://huggingface.co/krea/Krea-2-Turbo',
- defaults: {
- 'config.process[0].model.name_or_path': ['krea/Krea-2-Turbo', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].network.conv': [undefined, 16],
- 'config.process[0].network.conv_alpha': [undefined, 16],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].model.assistant_lora_path': [
- 'ostris/krea2_turbo_training_adapter/krea2_turbo_training_adapter_v1.safetensors',
- undefined,
- ],
- 'config.process[0].sample.guidance_scale': [1, 4],
- 'config.process[0].sample.sample_steps': [9, 25],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram', 'model.layer_offloading', 'model.assistant_lora_path'],
- },
- {
- name: 'krea2:o_edit',
- label: 'Krea 2 (raw) [Edit Training]',
- gateUrl: 'https://huggingface.co/krea/Krea-2-Raw',
- group: 'experimental',
- defaults: {
- 'config.process[0].model.name_or_path': ['krea/Krea-2-Raw', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].network.conv': [undefined, 16],
- 'config.process[0].network.conv_alpha': [undefined, 16],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].model.model_kwargs': [
- {
- edit: true,
- match_target_res: true,
- kv_cache: true,
- },
- {},
- ],
- },
- disableSections: ['network.conv', 'train.unload_text_encoder'],
- additionalSections: [
- 'datasets.multi_control_paths',
- 'sample.multi_ctrl_imgs',
- 'model.low_vram',
- 'model.layer_offloading',
- 'model.qie.match_target_res',
- 'model.model_kwargs.kv_cache',
- ],
- },
- {
- name: 'krea2:o_edit_turbo',
- label: 'Krea 2 Turbo (w/ Training Adapter) [Edit Training]',
- generateNameOverride: 'Krea 2 Turbo (Edit)',
- gateUrl: 'https://huggingface.co/krea/Krea-2-Turbo',
- group: 'experimental',
- defaults: {
- 'config.process[0].model.name_or_path': ['krea/Krea-2-Turbo', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].network.conv': [undefined, 16],
- 'config.process[0].network.conv_alpha': [undefined, 16],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].model.assistant_lora_path': [
- 'ostris/krea2_turbo_training_adapter/krea2_turbo_training_adapter_v1.safetensors',
- undefined,
- ],
- 'config.process[0].sample.guidance_scale': [1, 4],
- 'config.process[0].sample.sample_steps': [8, 25],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].model.model_kwargs': [
- {
- edit: true,
- match_target_res: true,
- kv_cache: true,
- },
- {},
- ],
- },
- disableSections: ['network.conv', 'train.unload_text_encoder'],
- additionalSections: [
- 'datasets.multi_control_paths',
- 'sample.multi_ctrl_imgs',
- 'model.low_vram',
- 'model.layer_offloading',
- 'model.assistant_lora_path',
- 'model.qie.match_target_res',
- 'model.model_kwargs.kv_cache',
- ],
- },
- {
- name: 'mageflow',
- label: 'Mage-Flow',
- group: 'image',
- defaults: {
- 'config.process[0].model.name_or_path': ['microsoft/Mage-Flow-Base', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].network.conv': [undefined, 16],
- 'config.process[0].network.conv_alpha': [undefined, 16],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].sample.guidance_scale': [4, 4],
- 'config.process[0].sample.sample_steps': [25, 25],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram', 'model.layer_offloading'],
- },
- {
- name: 'mageflow_edit',
- label: 'Mage-Flow Edit',
- group: 'instruction',
- defaults: {
- 'config.process[0].model.name_or_path': ['microsoft/Mage-Flow-Edit-Base', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].network.conv': [undefined, 16],
- 'config.process[0].network.conv_alpha': [undefined, 16],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].sample.guidance_scale': [4, 4],
- 'config.process[0].sample.sample_steps': [25, 25],
- 'config.process[0].train.unload_text_encoder': [false, false],
- },
- disableSections: ['network.conv', 'train.unload_text_encoder'],
- additionalSections: [
- 'datasets.multi_control_paths',
- 'sample.multi_ctrl_imgs',
- 'model.low_vram',
- 'model.layer_offloading',
- ],
- },
- {
- name: 'boogu_image',
- label: 'Boogu Image',
- group: 'image',
- defaults: {
- 'config.process[0].model.name_or_path': ['Boogu/Boogu-Image-0.1-Base', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].network.conv': [undefined, 16],
- 'config.process[0].network.conv_alpha': [undefined, 16],
- 'config.process[0].model.low_vram': [true, false],
- },
- disableSections: ['network.conv'],
- additionalSections: ['model.low_vram', 'model.layer_offloading'],
- },
- {
- name: 'boogu_image_edit',
- label: 'Boogu Image Edit',
- group: 'instruction',
- defaults: {
- 'config.process[0].model.name_or_path': ['Boogu/Boogu-Image-0.1-Edit', defaultNameOrPath],
- 'config.process[0].model.quantize': [true, false],
- 'config.process[0].model.quantize_te': [true, false],
- 'config.process[0].train.timestep_type': ['linear', 'sigmoid'],
- 'config.process[0].network.conv': [undefined, 16],
- 'config.process[0].network.conv_alpha': [undefined, 16],
- 'config.process[0].model.low_vram': [true, false],
- 'config.process[0].train.unload_text_encoder': [false, false],
- 'config.process[0].model.model_kwargs': [
- {
- match_target_res: false,
- },
- {},
- ],
- },
- disableSections: ['network.conv', 'train.unload_text_encoder'],
- additionalSections: [
- 'datasets.multi_control_paths',
- 'sample.multi_ctrl_imgs',
- 'model.low_vram',
- 'model.layer_offloading',
- 'model.qie.match_target_res',
- ],
- },
-].sort((a, b) => {
- // Sort by label, case-insensitive
- return a.label.localeCompare(b.label, undefined, { sensitivity: 'base' });
-}) as any;
-
-export const groupedModelOptions: GroupedSelectOption[] = modelArchs.reduce((acc, arch) => {
- const group = acc.find(g => g.label === arch.group);
- if (group) {
- group.options.push({ value: arch.name, label: arch.label });
- } else {
- acc.push({
- label: arch.group,
- options: [{ value: arch.name, label: arch.label }],
- });
- }
- return acc;
-}, [] as GroupedSelectOption[]);
+/** Grouped select options for an arch list (see useModelArchs). */
+export const groupModelOptions = (archs: ModelArch[]): GroupedSelectOption[] =>
+ archs.reduce((acc, arch) => {
+ const group = acc.find(g => g.label === arch.group);
+ if (group) {
+ group.options.push({ value: arch.name, label: arch.label });
+ } else {
+ acc.push({
+ label: arch.group,
+ options: [{ value: arch.name, label: arch.label }],
+ });
+ }
+ return acc;
+ }, [] as GroupedSelectOption[]);
export const quantizationOptions: SelectOption[] = [
{ value: '', label: '- NONE -' },
diff --git a/ui/src/app/jobs/new/utils.ts b/ui/src/app/jobs/new/utils.ts
index 98b2471..6832c3c 100644
--- a/ui/src/app/jobs/new/utils.ts
+++ b/ui/src/app/jobs/new/utils.ts
@@ -1,5 +1,5 @@
import { GroupedSelectOption, JobConfig, SelectOption } from '@/types';
-import { modelArchs, ModelArch } from './options';
+import { ModelArch } from './options';
import { objectCopy } from '@/utils/basic';
const expandDatasetDefaults = (
@@ -22,6 +22,7 @@ const expandDatasetDefaults = (
};
export const handleModelArchChange = (
+ modelArchs: ModelArch[],
currentArchName: string,
newArchName: string,
jobConfig: JobConfig,
diff --git a/ui/src/extensions/README.md b/ui/src/extensions/README.md
new file mode 100644
index 0000000..dc4b5c8
--- /dev/null
+++ b/ui/src/extensions/README.md
@@ -0,0 +1,37 @@
+# Extension UI modules
+
+A model package registers its python classes in `AI_TOOLKIT_MODELS`
+(`extensions//__init__.py`). To show those archs in the web UI (training
+form and Generate page) add a `ui.tsx` next to it exporting the UI entries:
+
+```tsx
+// extensions//ui.tsx
+import type { ModelArch } from '@/app/jobs/new/options';
+
+export const AI_TOOLKIT_UI_MODELS: ModelArch[] = [
+ {
+ name: 'my_arch', // must equal the python class's `arch`
+ label: 'My Model',
+ group: 'image',
+ defaults: {
+ 'config.process[0].model.name_or_path': ['org/my-model', ''],
+ 'config.process[0].model.quantize': [true, false],
+ },
+ },
+];
+```
+
+- The file is read and transpiled by the UI server on every page load
+ (`/api/model_archs`), so adding or editing one needs no rebuild. `.ts`,
+ `.jsx` and `.js` also work.
+- The `ModelArch` shape is in `ui/src/app/jobs/new/options.tsx`; the built-in
+ tables in `extensions_built_in/*/ui.tsx` are the reference examples.
+- Code runs in the browser. Only these imports are available: `react`,
+ `next/link`, `@/helpers/defaultSamples`, `@/components/formInputs`, plus
+ `@/app/jobs/new/options`, and type-only imports from `@/types`.
+- Entries are merged by `name`; `extensions/` loads after
+ `extensions_built_in/`, so an extension can override a built-in entry.
+- A module that fails to compile or evaluate is skipped and logged in the
+ browser console; the others still load.
+- Type check: `cd ui && npm run check_extensions` (uses the repo root
+ `tsconfig.json`, which editors also pick up for these files).
diff --git a/ui/src/extensions/modelArchs.ts b/ui/src/extensions/modelArchs.ts
new file mode 100644
index 0000000..af97cd4
--- /dev/null
+++ b/ui/src/extensions/modelArchs.ts
@@ -0,0 +1,108 @@
+'use client';
+// Runtime-loaded model arch list. Extension packages ship a ui.tsx exporting
+// AI_TOOLKIT_UI_MODELS (see README.md here); /api/model_archs transpiles them
+// and this hook evaluates them in the browser with a fixed set of importable
+// modules. Later modules override earlier ones by arch name, so a package
+// under extensions/ can replace a built-in entry.
+import React, { useEffect, useMemo } from 'react';
+import * as ReactJsxRuntime from 'react/jsx-runtime';
+import * as ReactJsxDevRuntime from 'react/jsx-dev-runtime';
+import Link from 'next/link';
+import { createGlobalState } from 'react-global-hooks';
+import * as defaultSamples from '@/helpers/defaultSamples';
+import * as formInputs from '@/components/formInputs';
+import { apiClient } from '@/utils/api';
+import * as options from '@/app/jobs/new/options';
+import { ModelArch } from '@/app/jobs/new/options';
+import { GroupedSelectOption } from '@/types';
+
+// what `import ... from ''` resolves to inside an extension ui module
+const SHIM: { [name: string]: any } = {
+ react: React,
+ 'react/jsx-runtime': ReactJsxRuntime,
+ 'react/jsx-dev-runtime': ReactJsxDevRuntime,
+ 'next/link': { __esModule: true, default: Link },
+ '@/helpers/defaultSamples': defaultSamples,
+ '@/components/formInputs': formInputs,
+ '@/types': {}, // types only
+ '@/app/jobs/new/options': options,
+};
+
+interface UiModule {
+ id: string;
+ file: string;
+ mtimeMs: number;
+ code: string;
+}
+
+interface ModelArchsState {
+ archs: ModelArch[];
+ errors: string[];
+ isLoaded: boolean;
+}
+
+const state = createGlobalState({ archs: [], errors: [], isLoaded: false });
+let inflight: Promise | null = null;
+
+const evaluateModule = (mod: UiModule): ModelArch[] => {
+ const module = { exports: {} as any };
+ const require = (name: string) => {
+ if (name in SHIM) return SHIM[name];
+ throw new Error(`cannot import "${name}" (available: ${Object.keys(SHIM).join(', ')})`);
+ };
+ new Function('require', 'module', 'exports', mod.code)(require, module, module.exports);
+ const list = module.exports.AI_TOOLKIT_UI_MODELS;
+ if (!Array.isArray(list)) throw new Error('must export an AI_TOOLKIT_UI_MODELS array');
+ list.forEach((a, i) => {
+ if (!a || typeof a.name !== 'string' || typeof a.label !== 'string' || typeof a.group !== 'string') {
+ throw new Error(`AI_TOOLKIT_UI_MODELS[${i}] needs string name, label and group`);
+ }
+ });
+ return list as ModelArch[];
+};
+
+export const loadModelArchs = async (): Promise => {
+ if (inflight) return inflight;
+ inflight = (async () => {
+ try {
+ const res = await apiClient.get('/api/model_archs');
+ const modules: UiModule[] = res.data?.modules || [];
+ const errors: string[] = (res.data?.errors || []).map((e: any) => `${e.id}: ${e.error}`);
+ const byName = new Map();
+ for (const mod of modules) {
+ try {
+ for (const arch of evaluateModule(mod)) byName.set(arch.name, arch);
+ } catch (e: any) {
+ errors.push(`${mod.id}: ${e?.message || e}`);
+ }
+ }
+ for (const err of errors) console.error('[model_archs]', err);
+ const archs = [...byName.values()].sort((a, b) =>
+ a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }),
+ );
+ state.set({ archs, errors, isLoaded: true });
+ } catch (e: any) {
+ const msg = e?.response?.data?.error || e?.message || String(e);
+ console.error('[model_archs]', msg);
+ state.set({ ...state.get(), errors: [msg], isLoaded: true });
+ } finally {
+ inflight = null;
+ }
+ })();
+ return inflight;
+};
+
+/** Model archs for the training form and Generate page. Refetches on every
+ * mount (server side is mtime-cached), so navigating to a page picks up new
+ * extension files; previous data stays until the reload lands. */
+export const useModelArchs = (): ModelArchsState & {
+ groupedModelOptions: GroupedSelectOption[];
+ refresh: () => Promise;
+} => {
+ const [current] = state.use();
+ useEffect(() => {
+ loadModelArchs();
+ }, []);
+ const groupedModelOptions = useMemo(() => options.groupModelOptions(current.archs), [current.archs]);
+ return { ...current, groupedModelOptions, refresh: loadModelArchs };
+};
diff --git a/ui/src/server/extensionUi.ts b/ui/src/server/extensionUi.ts
new file mode 100644
index 0000000..6ee0366
--- /dev/null
+++ b/ui/src/server/extensionUi.ts
@@ -0,0 +1,122 @@
+// Discovers and transpiles extension UI modules at request time.
+//
+// Every package under extensions/ and extensions_built_in/ may ship a
+// `ui.tsx` (or .ts/.jsx/.js) exporting AI_TOOLKIT_UI_MODELS, the UI
+// counterpart of the python AI_TOOLKIT_MODELS list. The files are read from
+// disk on each call (mtime-cached), so a new or edited model shows up on the
+// next page load without a rebuild. Transpiled with the SWC that ships inside
+// Next (native binding, all platforms); evaluation happens in the browser,
+// see src/extensions/modelArchs.ts.
+import fs from 'fs';
+import path from 'path';
+import { TOOLKIT_ROOT } from '@/paths';
+
+const EXTENSION_DIRS = ['extensions_built_in', 'extensions'];
+const UI_FILE_NAMES = ['ui.tsx', 'ui.ts', 'ui.jsx', 'ui.js'];
+
+export interface ExtensionUiModule {
+ id: string; // "/"
+ file: string; // absolute path
+ mtimeMs: number;
+ code: string; // CommonJS source
+}
+
+export interface ExtensionUiError {
+ id: string;
+ file: string;
+ error: string;
+}
+
+const cache = new Map();
+
+// the swc binding is a native module resolved at runtime, outside the bundler
+// graph. webpack rewrites a statically imported createRequire whose base path
+// it cannot evaluate into `(void 0)(...)`, so the builtin is loaded through an
+// import both bundlers are told to leave alone.
+let swcPromise: Promise | null = null;
+const getSwc = () => {
+ if (!swcPromise) {
+ swcPromise = (async () => {
+ const nodeModule = await import(/* webpackIgnore: true */ /* turbopackIgnore: true */ 'module');
+ const req = nodeModule.createRequire(path.join(TOOLKIT_ROOT, 'ui', 'package.json'));
+ return req('next/dist/build/swc');
+ })();
+ }
+ return swcPromise;
+};
+
+const transpile = async (file: string, source: string): Promise => {
+ const swc = await getSwc();
+ const ts = /\.tsx?$/.test(file);
+ const out = await swc.transform(source, {
+ filename: file,
+ jsc: {
+ parser: ts ? { syntax: 'typescript', tsx: file.endsWith('x') } : { syntax: 'ecmascript', jsx: true },
+ transform: { react: { runtime: 'automatic', development: false } },
+ target: 'es2020',
+ },
+ module: { type: 'commonjs' },
+ sourceMaps: false,
+ isModule: true,
+ });
+ return out.code as string;
+};
+
+const findUiFile = (pkgDir: string): string | null => {
+ for (const name of UI_FILE_NAMES) {
+ const file = path.join(pkgDir, name);
+ try {
+ if (fs.statSync(file).isFile()) return file;
+ } catch {
+ /* not present */
+ }
+ }
+ return null;
+};
+
+/** All extension UI modules, built-ins first, packages sorted by name. */
+export const listExtensionUiModules = async (): Promise<{
+ modules: ExtensionUiModule[];
+ errors: ExtensionUiError[];
+}> => {
+ const modules: ExtensionUiModule[] = [];
+ const errors: ExtensionUiError[] = [];
+ const seen = new Set();
+ for (const dir of EXTENSION_DIRS) {
+ const root = path.join(TOOLKIT_ROOT, dir);
+ let entries: fs.Dirent[] = [];
+ try {
+ entries = fs.readdirSync(root, { withFileTypes: true });
+ } catch {
+ continue;
+ }
+ const pkgs = entries
+ .filter(e => e.isDirectory() && !e.name.startsWith('.') && !e.name.startsWith('_'))
+ .map(e => e.name)
+ .sort();
+ for (const pkg of pkgs) {
+ const file = findUiFile(path.join(root, pkg));
+ if (!file) continue;
+ const id = `${dir}/${pkg}`;
+ seen.add(id);
+ try {
+ const mtimeMs = fs.statSync(file).mtimeMs;
+ const cached = cache.get(id);
+ if (cached && cached.file === file && cached.mtimeMs === mtimeMs) {
+ modules.push(cached);
+ continue;
+ }
+ const source = await fs.promises.readFile(file, 'utf8');
+ const code = await transpile(file, source);
+ const mod = { id, file, mtimeMs, code };
+ cache.set(id, mod);
+ modules.push(mod);
+ } catch (e: any) {
+ cache.delete(id);
+ errors.push({ id, file, error: e?.message || String(e) });
+ }
+ }
+ }
+ for (const id of cache.keys()) if (!seen.has(id)) cache.delete(id);
+ return { modules, errors };
+};
diff --git a/version.py b/version.py
index 0156c0c..5c22c0c 100644
--- a/version.py
+++ b/version.py
@@ -1 +1 @@
-VERSION = "0.13.15"
+VERSION = "0.13.16"