Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
99fc124
feat(flux): add scheduler selection for Flux models
Pfannkuchensack Dec 26, 2025
56bef0b
feat(z-image): add scheduler selection for Z-Image models
Pfannkuchensack Dec 26, 2025
bd678b1
fix ruff check
Pfannkuchensack Dec 26, 2025
eb516e1
Merge branch 'main' into feature/zimage-scheduler-support
JPPhoto Dec 27, 2025
0956ce0
Merge branch 'main' into feature/zimage-scheduler-support
Pfannkuchensack Dec 27, 2025
b4f05d3
Merge branch 'main' into feature/zimage-scheduler-support
lstein Dec 28, 2025
16fedfb
fix(schedulers): prevent progress percentage overflow with LCM scheduler
Pfannkuchensack Dec 28, 2025
bc47830
Merge branch 'main' into feature/zimage-scheduler-support
Pfannkuchensack Dec 28, 2025
9617140
Merge branch 'feature/zimage-scheduler-support' of https://github.com…
Pfannkuchensack Dec 28, 2025
0f830dd
Ruff format
Pfannkuchensack Dec 28, 2025
8d880ef
fix(schedulers): remove initial step-0 callback for consistent step c…
Pfannkuchensack Dec 29, 2025
6c3ce8e
Merge branch 'main' into feature/zimage-scheduler-support
lstein Jan 2, 2026
e7233ef
Merge branch 'main' into feature/zimage-scheduler-support
JPPhoto Jan 2, 2026
132a484
feat(z-image): add scheduler support with metadata recall
Pfannkuchensack Jan 3, 2026
1bcf589
feat(z-image): add Z-Image Denoise + Metadata node
Pfannkuchensack Jan 3, 2026
7847cce
fix typegen
Pfannkuchensack Jan 3, 2026
252794d
ruff fix
Pfannkuchensack Jan 3, 2026
d99707f
fix(ui): fix z-image scheduler recall by reordering metadata handlers
Pfannkuchensack Jan 3, 2026
81d83d5
Merge branch 'main' into z-image_metadata_node
Pfannkuchensack Jan 3, 2026
2ccadd1
Merge branch 'main' into z-image_metadata_node
lstein Jan 4, 2026
1ca589e
Merge branch 'main' into z-image_metadata_node
Pfannkuchensack Jan 4, 2026
f29820a
feat(ui): improve Z-Image model selector UX with auto-clearing conflicts
Pfannkuchensack Jan 4, 2026
a05a626
Fix typegen
Pfannkuchensack Jan 5, 2026
384a1a6
Merge branch 'main' into z-image_metadata_node
Pfannkuchensack Jan 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion invokeai/app/invocations/flux_denoise.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
pack,
unpack,
)
from invokeai.backend.flux.schedulers import FLUX_SCHEDULER_LABELS, FLUX_SCHEDULER_MAP, FLUX_SCHEDULER_NAME_VALUES
from invokeai.backend.flux.text_conditioning import FluxReduxConditioning, FluxTextConditioning
from invokeai.backend.model_manager.taxonomy import BaseModelType, FluxVariantType, ModelFormat, ModelType
from invokeai.backend.patches.layer_patcher import LayerPatcher
Expand All @@ -63,7 +64,7 @@
title="FLUX Denoise",
tags=["image", "flux"],
category="image",
version="4.1.0",
version="4.2.0",
)
class FluxDenoiseInvocation(BaseInvocation):
"""Run denoising process with a FLUX transformer model."""
Expand Down Expand Up @@ -132,6 +133,12 @@ class FluxDenoiseInvocation(BaseInvocation):
num_steps: int = InputField(
default=4, description="Number of diffusion steps. Recommended values are schnell: 4, dev: 50."
)
scheduler: FLUX_SCHEDULER_NAME_VALUES = InputField(
default="euler",
description="Scheduler (sampler) for the denoising process. 'euler' is fast and standard. "
"'heun' is 2nd-order (better quality, 2x slower). 'lcm' is optimized for few steps.",
ui_choice_labels=FLUX_SCHEDULER_LABELS,
)
guidance: float = InputField(
default=4.0,
description="The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell.",
Expand Down Expand Up @@ -242,6 +249,12 @@ def _run_diffusion(
shift=not is_schnell,
)

# Create scheduler if not using default euler
scheduler = None
if self.scheduler in FLUX_SCHEDULER_MAP:
scheduler_class = FLUX_SCHEDULER_MAP[self.scheduler]
scheduler = scheduler_class(num_train_timesteps=1000)

# Clip the timesteps schedule based on denoising_start and denoising_end.
timesteps = clip_timestep_schedule_fractional(timesteps, self.denoising_start, self.denoising_end)

Expand Down Expand Up @@ -426,6 +439,7 @@ def _run_diffusion(
img_cond=img_cond,
img_cond_seq=img_cond_seq,
img_cond_seq_ids=img_cond_seq_ids,
scheduler=scheduler,
)

x = unpack(x.float(), self.height, self.width)
Expand Down
47 changes: 47 additions & 0 deletions invokeai/app/invocations/metadata_linked.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
)
from invokeai.app.invocations.scheduler import SchedulerOutput
from invokeai.app.invocations.t2i_adapter import T2IAdapterField, T2IAdapterInvocation
from invokeai.app.invocations.z_image_denoise import ZImageDenoiseInvocation
from invokeai.app.services.shared.invocation_context import InvocationContext
from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType, SubModelType
from invokeai.backend.stable_diffusion.schedulers.schedulers import SCHEDULER_NAME_VALUES
Expand Down Expand Up @@ -729,6 +730,52 @@ def _loras_to_json(obj: Union[Any, list[Any]]):
return LatentsMetaOutput(**params, metadata=MetadataField.model_validate(md))


@invocation(
"z_image_denoise_meta",
title=f"{ZImageDenoiseInvocation.UIConfig.title} + Metadata",
tags=["z-image", "latents", "denoise", "txt2img", "t2i", "t2l", "img2img", "i2i", "l2l"],
category="latents",
version="1.0.0",
)
class ZImageDenoiseMetaInvocation(ZImageDenoiseInvocation, WithMetadata):
"""Run denoising process with a Z-Image transformer model + metadata."""

def invoke(self, context: InvocationContext) -> LatentsMetaOutput:
def _loras_to_json(obj: Union[Any, list[Any]]):
if not isinstance(obj, list):
obj = [obj]

output: list[dict[str, Any]] = []
for item in obj:
output.append(
LoRAMetadataField(
model=item.lora,
weight=item.weight,
).model_dump(exclude_none=True, exclude={"id", "type", "is_intermediate", "use_cache"})
)
return output

obj = super().invoke(context)

md: Dict[str, Any] = {} if self.metadata is None else self.metadata.root
md.update({"width": obj.width})
md.update({"height": obj.height})
md.update({"steps": self.steps})
md.update({"guidance": self.guidance_scale})
md.update({"denoising_start": self.denoising_start})
md.update({"denoising_end": self.denoising_end})
md.update({"scheduler": self.scheduler})
md.update({"model": self.transformer.transformer})
md.update({"seed": self.seed})
if len(self.transformer.loras) > 0:
md.update({"loras": _loras_to_json(self.transformer.loras)})

params = obj.__dict__.copy()
del params["type"]

return LatentsMetaOutput(**params, metadata=MetadataField.model_validate(md))


@invocation(
"metadata_to_vae",
title="Metadata To VAE",
Expand Down
Loading