Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9fd09a4
fix(ui): improve DyPE field ordering and add 'On' preset option
Pfannkuchensack Jan 27, 2026
e2e9028
Merge branch 'main' into fix/dype-ui-ordering
Pfannkuchensack Jan 27, 2026
fa52310
Chore Ruff check fix
Pfannkuchensack Jan 27, 2026
f862944
Merge branch 'fix/dype-ui-ordering' of https://github.com/Pfannkuchen…
Pfannkuchensack Jan 27, 2026
6d46d6d
fix(flux): remove .value from dype_preset logging
Pfannkuchensack Jan 27, 2026
cc61320
fix(tests): update DyPE tests for Literal type change
Pfannkuchensack Jan 27, 2026
cf830d5
feat(flux): add DyPE scale and exponent controls to Linear UI
Pfannkuchensack Jan 28, 2026
f122a4e
fix(flux): apply DyPE scale/exponent and add metadata recall
Pfannkuchensack Jan 28, 2026
bc6a85e
fix(flux): apply DyPE scale/exponent and add metadata recall
Pfannkuchensack Jan 28, 2026
2f7141d
Merge branch 'fix/dype-ui-ordering' of https://github.com/Pfannkuchen…
Pfannkuchensack Jan 28, 2026
1773a61
feat(ui): show DyPE scale/exponent only when preset is "on"
Pfannkuchensack Jan 28, 2026
1e73ea4
Merge branch 'main' into fix/dype-ui-ordering
JPPhoto Jan 28, 2026
1f65e98
Merge branch 'main' into fix/dype-ui-ordering
JPPhoto Jan 29, 2026
04b35ca
fix(dype): only allow custom scale/exponent with 'on' preset
Pfannkuchensack Jan 29, 2026
db03682
refactor(dype): rename 'on' preset to 'manual'
Pfannkuchensack Jan 29, 2026
eca95c3
refactor(dype): rename 'on' preset to 'manual'
Pfannkuchensack Jan 29, 2026
bd543c5
Merge branch 'fix/dype-ui-ordering' of https://github.com/Pfannkuchen…
Pfannkuchensack Jan 29, 2026
57cd58c
Merge branch 'main' into fix/dype-ui-ordering
JPPhoto Jan 29, 2026
60083bc
Merge branch 'fix/dype-ui-ordering' of https://github.com/Pfannkuchen…
Pfannkuchensack Jan 29, 2026
7931be4
fix(dype): update remaining 'on' references to 'manual'
Pfannkuchensack Jan 29, 2026
8cb8d5f
Merge branch 'main' into fix/dype-ui-ordering
JPPhoto Jan 29, 2026
e934540
Merge branch 'main' into fix/dype-ui-ordering
lstein Jan 30, 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
15 changes: 12 additions & 3 deletions invokeai/app/invocations/flux_denoise.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@
from invokeai.backend.flux.controlnet.instantx_controlnet_flux import InstantXControlNetFlux
from invokeai.backend.flux.controlnet.xlabs_controlnet_flux import XLabsControlNetFlux
from invokeai.backend.flux.denoise import denoise
from invokeai.backend.flux.dype.presets import DyPEPreset, get_dype_config_from_preset
from invokeai.backend.flux.dype.presets import (
DYPE_PRESET_LABELS,
DYPE_PRESET_OFF,
DyPEPreset,
get_dype_config_from_preset,
)
from invokeai.backend.flux.extensions.dype_extension import DyPEExtension
from invokeai.backend.flux.extensions.instantx_controlnet_extension import InstantXControlNetExtension
from invokeai.backend.flux.extensions.kontext_extension import KontextExtension
Expand Down Expand Up @@ -66,7 +71,7 @@
title="FLUX Denoise",
tags=["image", "flux"],
category="image",
version="4.3.0",
version="4.4.0",
)
class FluxDenoiseInvocation(BaseInvocation):
"""Run denoising process with a FLUX transformer model."""
Expand Down Expand Up @@ -170,20 +175,24 @@ class FluxDenoiseInvocation(BaseInvocation):

# DyPE (Dynamic Position Extrapolation) for high-resolution generation
dype_preset: DyPEPreset = InputField(
default=DyPEPreset.OFF,
default=DYPE_PRESET_OFF,
description="DyPE preset for high-resolution generation. 'auto' enables automatically for resolutions > 1536px. '4k' uses optimized settings for 4K output.",
ui_order=100,
ui_choice_labels=DYPE_PRESET_LABELS,
)
dype_scale: Optional[float] = InputField(
default=None,
ge=0.0,
le=8.0,
description="DyPE magnitude (λs). Higher values = stronger extrapolation. Only used when dype_preset is not 'off'.",
ui_order=101,
)
dype_exponent: Optional[float] = InputField(
default=None,
ge=0.0,
le=1000.0,
description="DyPE decay speed (λt). Controls transition from low to high frequency detail. Only used when dype_preset is not 'off'.",
ui_order=102,
)

@torch.no_grad()
Expand Down
15 changes: 14 additions & 1 deletion invokeai/backend/flux/dype/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,24 @@

from invokeai.backend.flux.dype.base import DyPEConfig
from invokeai.backend.flux.dype.embed import DyPEEmbedND
from invokeai.backend.flux.dype.presets import DyPEPreset, get_dype_config_for_resolution
from invokeai.backend.flux.dype.presets import (
DYPE_PRESET_4K,
DYPE_PRESET_AUTO,
DYPE_PRESET_LABELS,
DYPE_PRESET_OFF,
DYPE_PRESET_ON,
DyPEPreset,
get_dype_config_for_resolution,
)

__all__ = [
"DyPEConfig",
"DyPEEmbedND",
"DyPEPreset",
"DYPE_PRESET_OFF",
"DYPE_PRESET_ON",
"DYPE_PRESET_AUTO",
"DYPE_PRESET_4K",
"DYPE_PRESET_LABELS",
"get_dype_config_for_resolution",
]
45 changes: 34 additions & 11 deletions invokeai/backend/flux/dype/presets.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
"""DyPE presets and automatic configuration."""

from dataclasses import dataclass
from enum import Enum
from typing import Literal

from invokeai.backend.flux.dype.base import DyPEConfig


class DyPEPreset(str, Enum):
"""Predefined DyPE configurations."""

OFF = "off" # DyPE disabled
AUTO = "auto" # Automatically enable based on resolution
PRESET_4K = "4k" # Optimized for 3840x2160 / 4096x2160
# DyPE preset type - using Literal for proper frontend dropdown support
DyPEPreset = Literal["off", "on", "auto", "4k"]

# Constants for preset values
DYPE_PRESET_OFF: DyPEPreset = "off"
DYPE_PRESET_ON: DyPEPreset = "on"
DYPE_PRESET_AUTO: DyPEPreset = "auto"
DYPE_PRESET_4K: DyPEPreset = "4k"

# Human-readable labels for the UI
DYPE_PRESET_LABELS: dict[str, str] = {
"off": "Off",
"on": "On",
"auto": "Auto (>1536px)",
"4k": "4K Optimized",
}


@dataclass
Expand All @@ -27,7 +36,7 @@ class DyPEPresetConfig:

# Predefined preset configurations
DYPE_PRESETS: dict[DyPEPreset, DyPEPresetConfig] = {
DyPEPreset.PRESET_4K: DyPEPresetConfig(
DYPE_PRESET_4K: DyPEPresetConfig(
base_resolution=1024,
method="vision_yarn",
dype_scale=2.0,
Expand Down Expand Up @@ -98,7 +107,7 @@ def get_dype_config_from_preset(
Returns:
DyPEConfig if DyPE should be enabled, None otherwise
"""
if preset == DyPEPreset.OFF:
if preset == DYPE_PRESET_OFF:
# Check if custom values are provided even with preset=OFF
if custom_scale is not None:
return DyPEConfig(
Expand All @@ -111,7 +120,21 @@ def get_dype_config_from_preset(
)
return None

if preset == DyPEPreset.AUTO:
if preset == DYPE_PRESET_ON:
# Always enable DyPE with dynamic settings based on resolution
max_dim = max(width, height)
scale = max_dim / 1024
dynamic_dype_scale = min(2.0 * scale, 8.0)
return DyPEConfig(
enable_dype=True,
base_resolution=1024,
method="vision_yarn",
dype_scale=custom_scale if custom_scale is not None else dynamic_dype_scale,
dype_exponent=custom_exponent if custom_exponent is not None else 2.0,
dype_start_sigma=1.0,
)

if preset == DYPE_PRESET_AUTO:
config = get_dype_config_for_resolution(
width=width,
height=height,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const slice = createSlice({
setFluxScheduler: (state, action: PayloadAction<'euler' | 'heun' | 'lcm'>) => {
state.fluxScheduler = action.payload;
},
setFluxDypePreset: (state, action: PayloadAction<'off' | 'auto' | '4k'>) => {
setFluxDypePreset: (state, action: PayloadAction<'off' | 'on' | 'auto' | '4k'>) => {
state.fluxDypePreset = action.payload;
},
setZImageScheduler: (state, action: PayloadAction<'euler' | 'heun' | 'lcm'>) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks';
import { isNil } from 'es-toolkit/compat';
import { useInvocationNodeContext } from 'features/nodes/components/flow/nodes/Invocation/context';
import type { FieldInputTemplate } from 'features/nodes/types/field';
import { isSingleOrCollection, isStatefulFieldType } from 'features/nodes/types/field';
import { useMemo } from 'react';

/**
* Sort input fields: unordered fields first (preserving original order),
* then explicitly ordered fields sorted by ui_order ascending.
*/
const sortInputFields = (fields: FieldInputTemplate[]): string[] => {
const visibleFields = fields.filter((field) => !field.ui_hidden);

const unorderedFields = visibleFields.filter((f) => isNil(f.ui_order));
const orderedFields = visibleFields
.filter((f) => !isNil(f.ui_order))
.sort((a, b) => (a.ui_order ?? 0) - (b.ui_order ?? 0));

return unorderedFields
.concat(orderedFields)
.map((f) => f.name)
.filter((fieldName) => fieldName !== 'is_intermediate');
};

const isConnectionInputField = (field: FieldInputTemplate) => {
return (field.input === 'connection' && !isSingleOrCollection(field.type)) || !isStatefulFieldType(field.type);
};
Expand Down Expand Up @@ -34,13 +53,13 @@ export const useInputFieldNamesAnyOrDirect = () => {
const selector = useMemo(
() =>
createSelector([ctx.selectNodeTemplateSafe], (template) => {
const fieldNames: string[] = [];
for (const [fieldName, fieldTemplate] of Object.entries(template?.inputs ?? {})) {
const fields: FieldInputTemplate[] = [];
for (const fieldTemplate of Object.values(template?.inputs ?? {})) {
if (isAnyOrDirectInputField(fieldTemplate)) {
fieldNames.push(fieldName);
fields.push(fieldTemplate);
}
}
return fieldNames;
return sortInputFields(fields);
}),
[ctx]
);
Expand All @@ -52,13 +71,13 @@ export const useInputFieldNamesConnection = () => {
const selector = useMemo(
() =>
createSelector([ctx.selectNodeTemplateSafe], (template) => {
const fieldNames: string[] = [];
for (const [fieldName, fieldTemplate] of Object.entries(template?.inputs ?? {})) {
const fields: FieldInputTemplate[] = [];
for (const fieldTemplate of Object.values(template?.inputs ?? {})) {
if (isConnectionInputField(fieldTemplate)) {
fieldNames.push(fieldName);
fields.push(fieldTemplate);
}
}
return fieldNames;
return sortInputFields(fields);
}),
[ctx]
);
Expand Down
2 changes: 1 addition & 1 deletion invokeai/frontend/web/src/features/nodes/types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const zFluxSchedulerField = z.enum(['euler', 'heun', 'lcm']);
export const zZImageSchedulerField = z.enum(['euler', 'heun', 'lcm']);

// Flux DyPE (Dynamic Position Extrapolation) preset options for high-resolution generation
export const zFluxDypePresetField = z.enum(['off', 'auto', '4k']);
export const zFluxDypePresetField = z.enum(['off', 'on', 'auto', '4k']);
// #endregion

// #region Model-related schemas
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useTranslation } from 'react-i18next';
// DyPE (Dynamic Position Extrapolation) preset options for high-resolution generation
const FLUX_DYPE_PRESET_OPTIONS: ComboboxOption[] = [
{ value: 'off', label: 'Off' },
{ value: 'on', label: 'On' },
{ value: 'auto', label: 'Auto (> 1536px)' },
{ value: '4k', label: '4K Optimized' },
];
Expand Down
14 changes: 6 additions & 8 deletions invokeai/frontend/web/src/services/api/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6990,12 +6990,6 @@ export type components = {
*/
download_path: string;
};
/**
* DyPEPreset
* @description Predefined DyPE configurations.
* @enum {string}
*/
DyPEPreset: "off" | "auto" | "4k";
/**
* Dynamic Prompt
* @description Parses a prompt using adieyal/dynamicprompts' random or combinatorial generator
Expand Down Expand Up @@ -8748,10 +8742,12 @@ export type components = {
*/
kontext_conditioning?: components["schemas"]["FluxKontextConditioningField"] | components["schemas"]["FluxKontextConditioningField"][] | null;
/**
* Dype Preset
* @description DyPE preset for high-resolution generation. 'auto' enables automatically for resolutions > 1536px. '4k' uses optimized settings for 4K output.
* @default off
* @enum {string}
*/
dype_preset?: components["schemas"]["DyPEPreset"];
dype_preset?: "off" | "on" | "auto" | "4k";
/**
* Dype Scale
* @description DyPE magnitude (λs). Higher values = stronger extrapolation. Only used when dype_preset is not 'off'.
Expand Down Expand Up @@ -8940,10 +8936,12 @@ export type components = {
*/
kontext_conditioning?: components["schemas"]["FluxKontextConditioningField"] | components["schemas"]["FluxKontextConditioningField"][] | null;
/**
* Dype Preset
* @description DyPE preset for high-resolution generation. 'auto' enables automatically for resolutions > 1536px. '4k' uses optimized settings for 4K output.
* @default off
* @enum {string}
*/
dype_preset?: components["schemas"]["DyPEPreset"];
dype_preset?: "off" | "on" | "auto" | "4k";
/**
* Dype Scale
* @description DyPE magnitude (λs). Higher values = stronger extrapolation. Only used when dype_preset is not 'off'.
Expand Down
Loading