feat(model): register minimax h3 as a text2image base model

Reference entries for the bf16 repo and the sdnq uint4 quant load the
modular pipeline through the standard dispatch. Image tabs run the
model in still mode with audio off; the video tab keeps its own
overrides through the shared per-generation hook. Detailer is not
supported and is disabled with a warning.
This commit is contained in:
CalamitousFelicitousness
2026-08-07 02:08:06 +01:00
parent c9e1398c71
commit 6375b42ff7
8 changed files with 63 additions and 1 deletions
+8
View File
@@ -374,6 +374,14 @@
"size": 75.64,
"date": "2025 September"
},
"MiniMaxAI MiniMax-H3": {
"path": "MiniMaxAI/MiniMax-H3",
"preview": "MiniMaxAI--MiniMax-H3.jpg",
"desc": "MiniMax-H3 generates video with synchronized stereo audio in a single denoising pass through a 33B single-stream transformer with a Qwen3-VL conditioner. In image tabs the model runs in experimental still mode, keeping the first frame of a minimal generation.",
"extras": "sampler: None",
"size": 134,
"date": "2026 August"
},
"Freepik F-Lite": {
"path": "Freepik/F-Lite",
"preview": "Freepik--F-Lite.jpg",
+8
View File
@@ -79,6 +79,14 @@
"date": "2025 October",
"size": 23.53
},
"MiniMaxAI MiniMax-H3 sdnq-uint4": {
"path": "OzzyGT/MiniMax_H3_sdnq_dynamic_4bit",
"preview": "MiniMaxAI--MiniMax-H3.jpg",
"desc": "Quantization of MiniMaxAI/MiniMax-H3 using SDNQ: dynamic 4-bit uint. Video with synchronized audio; in image tabs the model runs in experimental still mode.",
"extras": "sampler: None",
"size": 51,
"date": "2026 August"
},
"Z-Image-Turbo sdnq-svd-uint4": {
"path": "Disty0/Z-Image-Turbo-SDNQ-uint4-svd-r32",
"preview": "Disty0--Z-Image-Turbo-SDNQ-uint4-svd-r32.jpg",
+2
View File
@@ -156,6 +156,8 @@ def get_model_type(pipe):
model_type = 'mochivideo'
elif "Allegro" in name:
model_type = 'allegrovideo'
elif 'MiniMaxH3' in name:
model_type = 'minimaxh3'
# cloud models
elif 'GoogleVeo' in name:
model_type = 'veo3'
+8 -1
View File
@@ -540,6 +540,13 @@ def update_pipeline(sd_model, p: processing.StableDiffusionProcessing):
log.warning('Processing: op=update model not loaded')
return None
updated_model = sd_model
if 'MiniMaxH3' in sd_model.__class__.__name__ and not isinstance(p, processing.StableDiffusionProcessingVideo):
# image tabs run the model in still mode; the video tab applies its own overrides
from modules.video_models import video_modular
video_modular.apply_minimax_overrides(p, sd_model, still=True, audio=False)
if getattr(p, 'detailer_enabled', False):
log.warning(f'Processing: cls={sd_model.__class__.__name__} detailer not supported')
p.detailer_enabled = False
if sd_models.get_diffusers_task(sd_model) == sd_models.DiffusersTaskType.INPAINTING and getattr(p, 'image_mask', None) is None and p.task_args.get('image_mask', None) is None and getattr(p, 'mask', None) is None:
log.warning('Processing: mode=inpaint mask=None')
updated_model = sd_models.set_diffuser_pipe(sd_model, sd_models.DiffusersTaskType.IMAGE_2_IMAGE)
@@ -569,7 +576,7 @@ def validate_pipeline(p: processing.StableDiffusionProcessing):
if m.custom is not None:
models_cls.append(m.custom)
is_video_model = shared.sd_model.__class__.__name__ in models_cls
override_video_pipelines = ['WanPipeline', 'WanImageToVideoPipeline', 'WanVACEPipeline']
override_video_pipelines = ['WanPipeline', 'WanImageToVideoPipeline', 'WanVACEPipeline', 'MiniMaxH3ModularPipeline']
is_video_pipeline = ('video' in p.__class__.__name__.lower()) or (shared.sd_model.__class__.__name__ in override_video_pipelines)
if is_video_model and not is_video_pipeline:
log.error(f'Mismatch: type={shared.sd_model_type} cls={shared.sd_model.__class__.__name__} request={p.__class__.__name__} video model with non-video pipeline')
+2
View File
@@ -115,6 +115,8 @@ def guess_by_name(fn, current_guess):
new_guess = 'Cosmos'
elif 'f-lite' in fn.lower():
new_guess = 'FLite'
elif 'minimax' in fn.lower():
new_guess = 'MiniMaxH3'
elif 'wan' in fn.lower():
new_guess = 'WanAI'
if 'chronoedit' in fn.lower():
+4
View File
@@ -493,6 +493,10 @@ def load_diffuser_force(detected_model_type: str, checkpoint_info: CheckpointInf
from pipelines.model_wanai import load_wan
sd_model = load_wan(checkpoint_info, diffusers_load_config)
allow_post_quant = False
elif model_type in ['MiniMaxH3']:
from pipelines.model_minimax import load_minimax
sd_model = load_minimax(checkpoint_info, diffusers_load_config)
allow_post_quant = False
elif model_type in ['ChronoEdit']:
from pipelines.model_chrono import load_chrono
sd_model = load_chrono(checkpoint_info, diffusers_load_config)
+1
View File
@@ -55,6 +55,7 @@ pipelines = {
'PixArtAlpha': getattr(diffusers, 'PixArtAlphaPipeline', None),
'PixArtSigma': getattr(diffusers, 'PixArtSigmaPipeline', None),
'PRXPixel': getattr(diffusers, 'PRXPixelPipeline', None),
'MiniMaxH3': getattr(diffusers, 'MiniMaxH3ModularPipeline', None),
'Qwen': getattr(diffusers, 'QwenImagePipeline', None),
'Sana': getattr(diffusers, 'SanaPipeline', None),
'WanAI': getattr(diffusers, 'WanPipeline', None),
+30
View File
@@ -0,0 +1,30 @@
import diffusers
from modules import shared, devices, sd_models
from modules.logger import log
def load_minimax(checkpoint_info, diffusers_load_config=None): # pylint: disable=unused-argument
from modules.video_models import video_modular, video_load
repo_id = sd_models.path_to_repo(checkpoint_info)
sd_models.hf_auth_check(checkpoint_info)
if repo_id is None or repo_id.lower() == 'none':
return None
offline_args = {'local_files_only': True} if shared.opts.offline_mode else {}
log.debug(f'Load model: type=MiniMaxH3 repo="{repo_id}" offload={shared.opts.diffusers_offload_mode} dtype={devices.dtype}')
pipe = video_modular.load_modular_pipe(
getattr(diffusers, 'MiniMaxH3ModularPipeline', None),
repo_id,
workflow='fl2va',
offline_args=offline_args,
)
if pipe is None:
return None
video_modular.install_state_hook(pipe)
video_load.loaded_model = None # image-path load invalidates the video tab's name cache
if hasattr(pipe, 'vae') and hasattr(pipe.vae, 'enable_tiling'):
pipe.vae.enable_tiling()
devices.torch_gc()
return pipe