mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-25 04:54:08 -03:00
IS_CHANGED only receives constant inputs, so a linked text always arrived as None and the node kept serving its cached first expansion. Declare hidden PROMPT/UNIQUE_ID inputs and walk the prompt graph to the upstream node: rerun only when its constants contain dynamic syntax or cannot be statically resolved, keep caching for static linked text. Fixes #1120
184 lines
5.8 KiB
Python
184 lines
5.8 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any, cast
|
|
|
|
from py.nodes.prompt import PromptLM
|
|
from py.nodes.text import TextLM
|
|
|
|
|
|
def test_text_lm_expands_wildcards_before_output(monkeypatch):
|
|
node = TextLM()
|
|
|
|
expand_calls = []
|
|
|
|
class StubService:
|
|
def expand_text(self, text, seed=None):
|
|
expand_calls.append((text, seed))
|
|
return "expanded text"
|
|
|
|
monkeypatch.setattr("py.nodes.text.get_wildcard_service", lambda: StubService())
|
|
|
|
assert node.process("__flower__", seed=9) == ("expanded text",)
|
|
assert expand_calls == [("__flower__", 9)]
|
|
|
|
|
|
def test_prompt_lm_expands_before_appending_trigger_words(monkeypatch):
|
|
node = PromptLM()
|
|
|
|
class StubService:
|
|
def expand_text(self, text, seed=None):
|
|
assert text == "__flower__"
|
|
assert seed == 42
|
|
return "rose"
|
|
|
|
class StubEncoder:
|
|
def encode(self, clip, prompt):
|
|
assert clip == "clip"
|
|
assert prompt == "artist style, rose"
|
|
return ("conditioning",)
|
|
|
|
monkeypatch.setattr("py.nodes.prompt.get_wildcard_service", lambda: StubService())
|
|
monkeypatch.setattr("nodes.CLIPTextEncode", lambda: StubEncoder(), raising=False)
|
|
|
|
result = node.encode("__flower__", "clip", seed=42, trigger_words1="artist style")
|
|
|
|
assert result == ("conditioning", "artist style, rose")
|
|
|
|
|
|
def test_prompt_lm_input_types_expose_input_only_seed():
|
|
input_types = PromptLM.INPUT_TYPES()
|
|
seed_type, seed_options = input_types["optional"]["seed"]
|
|
|
|
assert seed_type == "INT"
|
|
assert seed_options["forceInput"] is True
|
|
assert "wildcard generation" in cast(Any, seed_options)["tooltip"]
|
|
|
|
|
|
def test_text_lm_input_types_expose_input_only_seed():
|
|
input_types = TextLM.INPUT_TYPES()
|
|
seed_type, seed_options = input_types["optional"]["seed"]
|
|
|
|
assert seed_type == "INT"
|
|
assert seed_options["forceInput"] is True
|
|
assert "wildcard generation" in cast(Any, seed_options)["tooltip"]
|
|
|
|
|
|
def test_text_lm_is_changed_forces_rerun_without_seed_when_text_is_dynamic():
|
|
result = TextLM.IS_CHANGED("__flower__", seed=None)
|
|
|
|
assert result != result
|
|
|
|
|
|
def test_text_lm_is_changed_keeps_cache_for_seeded_or_static_text():
|
|
assert TextLM.IS_CHANGED("__flower__", seed=7) is False
|
|
assert TextLM.IS_CHANGED("plain text", seed=None) is False
|
|
assert TextLM.IS_CHANGED("{red|blue}", seed=7) is False
|
|
|
|
|
|
def test_prompt_lm_is_changed_forces_rerun_without_seed_when_text_is_dynamic():
|
|
result = PromptLM.IS_CHANGED("{red|blue}", clip="clip", seed=None)
|
|
|
|
assert result != result
|
|
|
|
|
|
def test_prompt_lm_is_changed_keeps_cache_for_seeded_or_static_text():
|
|
assert PromptLM.IS_CHANGED("__flower__", clip="clip", seed=11) is False
|
|
assert PromptLM.IS_CHANGED("plain text", clip="clip", seed=None) is False
|
|
|
|
|
|
def _linked_prompt(upstream_inputs):
|
|
return {
|
|
"1": {"class_type": "TextMultiline", "inputs": upstream_inputs},
|
|
"2": {
|
|
"class_type": "PromptLM",
|
|
"inputs": {"text": ["1", 0], "clip": ["3", 0]},
|
|
},
|
|
}
|
|
|
|
|
|
def test_prompt_lm_is_changed_forces_rerun_for_linked_dynamic_text():
|
|
prompt = _linked_prompt({"text": "{red|blue|green}"})
|
|
|
|
result = PromptLM.IS_CHANGED(None, clip="clip", seed=None, prompt=prompt, unique_id="2")
|
|
|
|
assert result != result
|
|
|
|
|
|
def test_prompt_lm_is_changed_keeps_cache_for_linked_static_text():
|
|
prompt = _linked_prompt({"text": "a plain static prompt"})
|
|
|
|
assert PromptLM.IS_CHANGED(None, clip="clip", seed=None, prompt=prompt, unique_id="2") is False
|
|
assert PromptLM.IS_CHANGED(None, clip="clip", seed=5, prompt=prompt, unique_id="2") is False
|
|
|
|
|
|
def test_prompt_lm_is_changed_forces_rerun_when_linked_text_unresolvable():
|
|
chained = _linked_prompt({"text": ["9", 0]})
|
|
|
|
result = PromptLM.IS_CHANGED(None, clip="clip", seed=None, prompt=chained, unique_id="2")
|
|
|
|
assert result != result
|
|
assert PromptLM.IS_CHANGED(None, clip="clip", seed=None, prompt=None, unique_id="2") != 0
|
|
missing_upstream = _linked_prompt({"text": "static"})
|
|
missing_upstream["2"]["inputs"]["text"] = ["99", 0]
|
|
assert (
|
|
PromptLM.IS_CHANGED(None, clip="clip", seed=None, prompt=missing_upstream, unique_id="2")
|
|
!= 0
|
|
)
|
|
|
|
|
|
def test_text_lm_is_changed_forces_rerun_for_linked_dynamic_text():
|
|
prompt = _linked_prompt({"text": "__flower__"})
|
|
|
|
result = TextLM.IS_CHANGED(None, seed=None, prompt=prompt, unique_id="2")
|
|
|
|
assert result != result
|
|
|
|
|
|
def test_text_lm_is_changed_keeps_cache_for_linked_static_text():
|
|
prompt = _linked_prompt({"text": "a plain static prompt"})
|
|
|
|
assert TextLM.IS_CHANGED(None, seed=None, prompt=prompt, unique_id="2") is False
|
|
|
|
|
|
def test_text_lm_process_accepts_hidden_inputs(monkeypatch):
|
|
node = TextLM()
|
|
|
|
class StubService:
|
|
def expand_text(self, text, seed=None):
|
|
return text
|
|
|
|
monkeypatch.setattr("py.nodes.text.get_wildcard_service", lambda: StubService())
|
|
|
|
assert node.process("hello", seed=None, prompt={}, unique_id="2") == ("hello",)
|
|
|
|
|
|
def test_prompt_lm_encode_accepts_hidden_inputs(monkeypatch):
|
|
node = PromptLM()
|
|
|
|
class StubService:
|
|
def expand_text(self, text, seed=None):
|
|
return text
|
|
|
|
class StubEncoder:
|
|
def encode(self, clip, prompt):
|
|
return ("conditioning",)
|
|
|
|
monkeypatch.setattr("py.nodes.prompt.get_wildcard_service", lambda: StubService())
|
|
monkeypatch.setattr("nodes.CLIPTextEncode", lambda: StubEncoder(), raising=False)
|
|
|
|
result = node.encode("hello", "clip", seed=None, prompt={}, unique_id="2")
|
|
|
|
assert result == ("conditioning", "hello")
|
|
|
|
|
|
def test_prompt_lm_input_types_declare_hidden_prompt_inputs():
|
|
hidden = PromptLM.INPUT_TYPES()["hidden"]
|
|
|
|
assert hidden == {"prompt": "PROMPT", "unique_id": "UNIQUE_ID"}
|
|
|
|
|
|
def test_text_lm_input_types_declare_hidden_prompt_inputs():
|
|
hidden = TextLM.INPUT_TYPES()["hidden"]
|
|
|
|
assert hidden == {"prompt": "PROMPT", "unique_id": "UNIQUE_ID"}
|