Fix IS_CHANGED for marking node as not changed

This commit is contained in:
Kent Mewhort 2025-06-04 06:46:59 -04:00
parent 47d55b8b45
commit 2e5316232e
2 changed files with 46 additions and 4 deletions

View File

@ -93,11 +93,18 @@ class CacheKeySetInputSignature(CacheKeySet):
self.subcache_keys[node_id] = (node_id, node["class_type"])
def get_node_signature(self, dynprompt, node_id):
node = dynprompt.get_node(node_id)
class_type = node["class_type"]
class_def = nodes.NODE_CLASS_MAPPINGS[class_type]
signature = []
ancestors, order_mapping = self.get_ordered_ancestry(dynprompt, node_id)
signature.append(self.get_immediate_node_signature(dynprompt, node_id, order_mapping))
for ancestor_id in ancestors:
signature.append(self.get_immediate_node_signature(dynprompt, ancestor_id, order_mapping))
if hasattr(class_def, "IS_CHANGED"):
signature.append(self.get_immediate_node_signature(dynprompt, node_id, {}))
else:
ancestors, order_mapping = self.get_ordered_ancestry(dynprompt, node_id)
signature.append(self.get_immediate_node_signature(dynprompt, node_id, order_mapping))
for ancestor_id in ancestors:
signature.append(self.get_immediate_node_signature(dynprompt, ancestor_id, order_mapping))
return to_hashable(signature)
def get_immediate_node_signature(self, dynprompt, node_id, ancestor_order_mapping):
@ -108,8 +115,13 @@ class CacheKeySetInputSignature(CacheKeySet):
class_type = node["class_type"]
class_def = nodes.NODE_CLASS_MAPPINGS[class_type]
signature = [class_type, self.is_changed_cache.get(node_id)]
if self.include_node_id_in_input() or (hasattr(class_def, "NOT_IDEMPOTENT") and class_def.NOT_IDEMPOTENT) or include_unique_id_in_input(class_type):
signature.append(node_id)
if hasattr(class_def, "IS_CHANGED"):
return signature
inputs = node["inputs"]
for key in sorted(inputs.keys()):
if is_link(inputs[key]):

View File

@ -497,6 +497,36 @@ class TestExecution:
assert numpy.array(images[0]).min() == 63 and numpy.array(images[0]).max() == 63, "Image should have value 0.25"
assert not result.did_run(test_node), "The execution should have been cached"
# This tests that a node does not re-execute when IS_CHANGED returs the same value (even if
# inputs change)
def test_is_changed_overrides_default_checks(self, client: ComfyClient, builder: GraphBuilder):
g = builder
changing_input = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
test_node = g.node("TestCustomIsChanged", should_change=False, image=changing_input.out(0))
g.node("SaveImage", images=test_node.out(0))
result1 = client.run(g)
assert result1.did_run(test_node), "Node should run on first execution"
assert result1.did_run(changing_input), "Input should run on first execution"
changing_input.set_input('content', "WHITE") # Change input content
result2 = client.run(g)
assert result2.did_run(changing_input), "Input should re-execute due to content change"
assert not result2.did_run(test_node), "Node with IS_CHANGED should NOT re-execute when IS_CHANGED is consistent"
# verify the behavior when IS_CHANGED actually changes
test_node.set_input('should_change', True) # This should cause IS_CHANGED to return different value
result3 = client.run(g)
assert result3.did_run(test_node), "Node should re-execute when IS_CHANGED value actually changes"
changing_input.set_input('content', "BLACK") # Change input back
result4 = client.run(g)
assert result4.did_run(changing_input), "Input should re-execute due to content change"
assert result4.did_run(test_node), "Node should re-execute because IS_CHANGED returns changing value (NaN)"
# This tests that nodes with OUTPUT_IS_LIST function correctly when they receive an ExecutionBlocker
# as input. We also test that when that list (containing an ExecutionBlocker) is passed to a node,
# only that one entry in the list is blocked.