Skip to content

Commit 56bef0b

Browse files
feat(z-image): add scheduler selection for Z-Image models
Add support for alternative diffusers Flow Matching schedulers for Z-Image: - Euler (default) - 1st order, optimized for Z-Image-Turbo (8 steps) - Heun (2nd order) - Better quality, 2x slower - LCM - Optimized for few-step generation Backend: - Extend schedulers.py with Z-Image scheduler types and mapping - Add scheduler InputField to z_image_denoise invocation (v1.3.0) - Refactor denoising loop to support diffusers schedulers Frontend: - Add zImageScheduler to Redux state in paramsSlice - Create ParamZImageScheduler component for Linear UI - Add scheduler to buildZImageGraph for generation
1 parent 99fc124 commit 56bef0b

File tree

10 files changed

+326
-78
lines changed

10 files changed

+326
-78
lines changed

invokeai/app/invocations/z_image_denoise.py

Lines changed: 224 additions & 73 deletions
Large diffs are not rendered by default.

invokeai/backend/flux/schedulers.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
"""Flux scheduler definitions and mapping.
1+
"""Flow Matching scheduler definitions and mapping.
22
3-
This module provides the scheduler types and mapping for Flux models,
4-
supporting multiple Flow Matching schedulers from the diffusers library.
3+
This module provides the scheduler types and mapping for Flow Matching models
4+
(Flux and Z-Image), supporting multiple schedulers from the diffusers library.
55
"""
66

77
from typing import Literal, Type
@@ -38,3 +38,25 @@
3838

3939
if _HAS_LCM:
4040
FLUX_SCHEDULER_MAP["lcm"] = FlowMatchLCMScheduler
41+
42+
43+
# Z-Image scheduler types (same schedulers as Flux, both use Flow Matching)
44+
# Note: Z-Image-Turbo is optimized for ~8 steps with Euler, but other schedulers
45+
# can be used for experimentation.
46+
ZIMAGE_SCHEDULER_NAME_VALUES = Literal["euler", "heun", "lcm"]
47+
48+
# Human-readable labels for the UI
49+
ZIMAGE_SCHEDULER_LABELS: dict[str, str] = {
50+
"euler": "Euler",
51+
"heun": "Heun (2nd order)",
52+
"lcm": "LCM",
53+
}
54+
55+
# Mapping from scheduler names to scheduler classes (same as Flux)
56+
ZIMAGE_SCHEDULER_MAP: dict[str, Type[SchedulerMixin]] = {
57+
"euler": FlowMatchEulerDiscreteScheduler,
58+
"heun": FlowMatchHeunDiscreteScheduler,
59+
}
60+
61+
if _HAS_LCM:
62+
ZIMAGE_SCHEDULER_MAP["lcm"] = FlowMatchLCMScheduler

invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ const slice = createSlice({
7272
setFluxScheduler: (state, action: PayloadAction<'euler' | 'heun' | 'lcm'>) => {
7373
state.fluxScheduler = action.payload;
7474
},
75+
setZImageScheduler: (state, action: PayloadAction<'euler' | 'heun' | 'lcm'>) => {
76+
state.zImageScheduler = action.payload;
77+
},
7578
setUpscaleScheduler: (state, action: PayloadAction<ParameterScheduler>) => {
7679
state.upscaleScheduler = action.payload;
7780
},
@@ -453,6 +456,7 @@ export const {
453456
setGuidance,
454457
setScheduler,
455458
setFluxScheduler,
459+
setZImageScheduler,
456460
setUpscaleScheduler,
457461
setUpscaleCfgScale,
458462
setSeed,
@@ -593,6 +597,7 @@ export const selectModelSupportsOptimizedDenoising = createSelector(
593597
);
594598
export const selectScheduler = createParamsSelector((params) => params.scheduler);
595599
export const selectFluxScheduler = createParamsSelector((params) => params.fluxScheduler);
600+
export const selectZImageScheduler = createParamsSelector((params) => params.zImageScheduler);
596601
export const selectSeamlessXAxis = createParamsSelector((params) => params.seamlessXAxis);
597602
export const selectSeamlessYAxis = createParamsSelector((params) => params.seamlessYAxis);
598603
export const selectSeed = createParamsSelector((params) => params.seed);

invokeai/frontend/web/src/features/controlLayers/store/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
zParameterStrength,
2525
zParameterT5EncoderModel,
2626
zParameterVAEModel,
27+
zParameterZImageScheduler,
2728
} from 'features/parameters/types/parameterSchemas';
2829
import type { JsonObject } from 'type-fest';
2930
import { z } from 'zod';
@@ -598,6 +599,7 @@ export const zParamsState = z.object({
598599
iterations: z.number(),
599600
scheduler: zParameterScheduler,
600601
fluxScheduler: zParameterFluxScheduler,
602+
zImageScheduler: zParameterZImageScheduler,
601603
upscaleScheduler: zParameterScheduler,
602604
upscaleCfgScale: zParameterCFGScale,
603605
seed: zParameterSeed,
@@ -653,6 +655,7 @@ export const getInitialParamsState = (): ParamsState => ({
653655
iterations: 1,
654656
scheduler: 'dpmpp_3m_k',
655657
fluxScheduler: 'euler',
658+
zImageScheduler: 'euler',
656659
upscaleScheduler: 'kdpm_2',
657660
upscaleCfgScale: 2,
658661
seed: 0,

invokeai/frontend/web/src/features/nodes/types/common.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ export type SchedulerField = z.infer<typeof zSchedulerField>;
6767

6868
// Flux-specific scheduler options (Flow Matching schedulers)
6969
export const zFluxSchedulerField = z.enum(['euler', 'heun', 'lcm']);
70+
71+
// Z-Image scheduler options (Flow Matching schedulers, same as Flux)
72+
export const zZImageSchedulerField = z.enum(['euler', 'heun', 'lcm']);
7073
// #endregion
7174

7275
// #region Model-related schemas

invokeai/frontend/web/src/features/nodes/util/graph/generation/buildZImageGraph.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export const buildZImageGraph = async (arg: GraphBuilderArg): Promise<GraphBuild
5353

5454
// Z-Image-Turbo uses guidance_scale (stored as cfgScale), defaults to 1.0 for no CFG
5555
// (1.0 means no CFG effect, matching FLUX convention)
56-
const { cfgScale: guidance_scale, steps } = params;
56+
const { cfgScale: guidance_scale, steps, zImageScheduler } = params;
5757

5858
const prompts = selectPresetModifiedPrompts(state);
5959

@@ -113,6 +113,7 @@ export const buildZImageGraph = async (arg: GraphBuilderArg): Promise<GraphBuild
113113
id: getPrefixedId('denoise_latents'),
114114
guidance_scale,
115115
steps,
116+
scheduler: zImageScheduler,
116117
});
117118
const l2i = g.addNode({
118119
type: 'z_image_l2i',
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
2+
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
3+
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
4+
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
5+
import { selectZImageScheduler, setZImageScheduler } from 'features/controlLayers/store/paramsSlice';
6+
import { isParameterZImageScheduler } from 'features/parameters/types/parameterSchemas';
7+
import { memo, useCallback, useMemo } from 'react';
8+
import { useTranslation } from 'react-i18next';
9+
10+
// Z-Image scheduler options (Flow Matching schedulers, same as Flux)
11+
const ZIMAGE_SCHEDULER_OPTIONS: ComboboxOption[] = [
12+
{ value: 'euler', label: 'Euler' },
13+
{ value: 'heun', label: 'Heun (2nd order)' },
14+
{ value: 'lcm', label: 'LCM' },
15+
];
16+
17+
const ParamZImageScheduler = () => {
18+
const dispatch = useAppDispatch();
19+
const { t } = useTranslation();
20+
const zImageScheduler = useAppSelector(selectZImageScheduler);
21+
22+
const onChange = useCallback<ComboboxOnChange>(
23+
(v) => {
24+
if (!isParameterZImageScheduler(v?.value)) {
25+
return;
26+
}
27+
dispatch(setZImageScheduler(v.value));
28+
},
29+
[dispatch]
30+
);
31+
32+
const value = useMemo(() => ZIMAGE_SCHEDULER_OPTIONS.find((o) => o.value === zImageScheduler), [zImageScheduler]);
33+
34+
return (
35+
<FormControl>
36+
<InformationalPopover feature="paramScheduler">
37+
<FormLabel>{t('parameters.scheduler')}</FormLabel>
38+
</InformationalPopover>
39+
<Combobox value={value} options={ZIMAGE_SCHEDULER_OPTIONS} onChange={onChange} />
40+
</FormControl>
41+
);
42+
};
43+
44+
export default memo(ParamZImageScheduler);

invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { NUMPY_RAND_MAX } from 'app/constants';
22
import { roundToMultiple } from 'common/util/roundDownToMultiple';
33
import { buildZodTypeGuard } from 'common/util/zodUtils';
4-
import { zFluxSchedulerField, zModelIdentifierField, zSchedulerField } from 'features/nodes/types/common';
4+
import {
5+
zFluxSchedulerField,
6+
zModelIdentifierField,
7+
zSchedulerField,
8+
zZImageSchedulerField,
9+
} from 'features/nodes/types/common';
510
import { z } from 'zod';
611

712
/**
@@ -66,6 +71,11 @@ export const [zParameterFluxScheduler, isParameterFluxScheduler] = buildParamete
6671
export type ParameterFluxScheduler = z.infer<typeof zParameterFluxScheduler>;
6772
// #endregion
6873

74+
// #region Z-Image Scheduler
75+
export const [zParameterZImageScheduler, isParameterZImageScheduler] = buildParameter(zZImageSchedulerField);
76+
export type ParameterZImageScheduler = z.infer<typeof zParameterZImageScheduler>;
77+
// #endregion
78+
6979
// #region seed
7080
export const [zParameterSeed, isParameterSeed] = buildParameter(z.number().int().min(0).max(NUMPY_RAND_MAX));
7181
export type ParameterSeed = z.infer<typeof zParameterSeed>;

invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import ParamFluxScheduler from 'features/parameters/components/Core/ParamFluxSch
1212
import ParamGuidance from 'features/parameters/components/Core/ParamGuidance';
1313
import ParamScheduler from 'features/parameters/components/Core/ParamScheduler';
1414
import ParamSteps from 'features/parameters/components/Core/ParamSteps';
15+
import ParamZImageScheduler from 'features/parameters/components/Core/ParamZImageScheduler';
1516
import { MainModelPicker } from 'features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker';
1617
import { useExpanderToggle } from 'features/settingsAccordions/hooks/useExpanderToggle';
1718
import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/useStandaloneAccordionToggle';
@@ -70,6 +71,7 @@ export const GenerationSettingsAccordion = memo(() => {
7071
<FormControlGroup formLabelProps={formLabelProps}>
7172
{!isFLUX && !isSD3 && !isCogView4 && !isZImage && <ParamScheduler />}
7273
{isFLUX && <ParamFluxScheduler />}
74+
{isZImage && <ParamZImageScheduler />}
7375
<ParamSteps />
7476
{isFLUX && modelConfig && !isFluxFillMainModelModelConfig(modelConfig) && <ParamGuidance />}
7577
{!isFLUX && <ParamCFGScale />}

invokeai/frontend/web/src/services/api/schema.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25555,6 +25555,13 @@ export type components = {
2555525555
* @default null
2555625556
*/
2555725557
vae?: components["schemas"]["VAEField"] | null;
25558+
/**
25559+
* Scheduler
25560+
* @description Scheduler (sampler) for the denoising process. Euler is the default and recommended for Z-Image-Turbo. Heun is 2nd-order (better quality, 2x slower). LCM is optimized for few steps.
25561+
* @default euler
25562+
* @enum {string}
25563+
*/
25564+
scheduler?: "euler" | "heun" | "lcm";
2555825565
/**
2555925566
* type
2556025567
* @default z_image_denoise

0 commit comments

Comments
 (0)