feat(sampling): add noise inversion and composable sampler options
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
"""Verify optional inversion widgets on implementation-backed V3 sampler nodes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, ClassVar
|
||||
|
||||
import pytest
|
||||
|
||||
from simple_syrup.domain.noise_inversion import NoiseInversionOptions
|
||||
from simple_syrup.nodes_v3.legacy_inversion_node_adapter import (
|
||||
LegacyInversionNodeV3Adapter,
|
||||
)
|
||||
from simple_syrup.nodes_v3.legacy_node_wrappers import (
|
||||
DetailSEGSAsRegionsV3,
|
||||
DetailSEGSByScaleFactorTiledDiffusionV3,
|
||||
KSamplerExtrasV3,
|
||||
)
|
||||
|
||||
|
||||
class RecordingImplementation:
|
||||
"""Expose a minimal maintained declaration and the execution boundary."""
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
OUTPUT_TOOLTIPS = ("Result.",)
|
||||
FUNCTION = "run"
|
||||
CATEGORY = "SimpleSyrup/Test"
|
||||
DESCRIPTION = "Records sampling inputs."
|
||||
INPUT_IS_LIST = False
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls) -> dict[str, Any]:
|
||||
"""Keep the persisted input contract independent of inversion widgets."""
|
||||
return {"required": {"text": ("STRING", {"default": "", "tooltip": "Text."})}}
|
||||
|
||||
def run(
|
||||
self, text: object, noise_inversion: NoiseInversionOptions | None = None
|
||||
) -> tuple[object, NoiseInversionOptions | None]:
|
||||
"""Return delegated inputs without invoking Comfy neural execution."""
|
||||
return text, noise_inversion
|
||||
|
||||
|
||||
class RecordingAdapter(LegacyInversionNodeV3Adapter):
|
||||
"""Use the production schema and list-mode normalization implementation."""
|
||||
|
||||
LEGACY_NODE_CLASS: ClassVar[type[Any]] = RecordingImplementation
|
||||
NODE_ID = "SimpleSyrup.Recording"
|
||||
DISPLAY_NAME = "Recording"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("list_mode", [False, True])
|
||||
def test_inversion_controls_normalize_and_default_off(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
list_mode: bool,
|
||||
) -> None:
|
||||
"""Preserve existing execution while constructing the accepted recipe on request."""
|
||||
monkeypatch.setattr(RecordingImplementation, "INPUT_IS_LIST", list_mode)
|
||||
text = ["prompt"] if list_mode else "prompt"
|
||||
output, disabled = RecordingAdapter.execute(text=text)
|
||||
assert output == text and disabled is None
|
||||
_, enabled = RecordingAdapter.execute(
|
||||
text=text,
|
||||
noise_inversion_enabled=[True] if list_mode else True,
|
||||
)
|
||||
assert enabled == NoiseInversionOptions()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"controls",
|
||||
[
|
||||
{"inversion_steps": 0},
|
||||
{"inversion_resolution_scale": 0},
|
||||
{"inversion_method": "fireflow"},
|
||||
{"inversion_switch_fraction": 1},
|
||||
],
|
||||
)
|
||||
def test_invalid_selected_inversion_controls_fail(controls: dict[str, Any]) -> None:
|
||||
"""Apply domain validation instead of handing malformed controls to a sampler."""
|
||||
with pytest.raises(ValueError):
|
||||
RecordingAdapter.execute(
|
||||
text="prompt", noise_inversion_enabled=True, **controls
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"node",
|
||||
[KSamplerExtrasV3, DetailSEGSAsRegionsV3, DetailSEGSByScaleFactorTiledDiffusionV3],
|
||||
)
|
||||
def test_existing_nodes_append_optional_inversion_without_reordering(node: Any) -> None:
|
||||
"""Preserve every serialized socket position before seven optional new controls."""
|
||||
schema = node.define_schema()
|
||||
order = node.WORKFLOW_INPUT_ORDER
|
||||
assert [item.id for item in schema.inputs[: len(order)]] == list(order)
|
||||
new = schema.inputs[len(order) :]
|
||||
assert len(new) == 7 and all(item.optional and item.tooltip for item in new)
|
||||
assert new[0].id == "noise_inversion_enabled" and new[0].default is False
|
||||
@@ -8,7 +8,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, ClassVar
|
||||
|
||||
from simple_syrup.nodes_v3.legacy_node_wrappers import LegacyNodeV3Adapter
|
||||
from simple_syrup.nodes_v3.legacy_node_adapter import LegacyNodeV3Adapter
|
||||
|
||||
|
||||
class _FakeHidden:
|
||||
|
||||
Reference in New Issue
Block a user