diff --git a/core/workflow/analysis.py b/core/workflow/analysis.py index 34f24e8..7b26142 100644 --- a/core/workflow/analysis.py +++ b/core/workflow/analysis.py @@ -159,14 +159,14 @@ def _get_promoted_widget_context_cache_key( for node in nodes: if not isinstance(node, dict): continue - widgets_values = node.get("widgets_values", []) context_node = { - **node, - "widgets_values": ( - list(widgets_values) - if isinstance(widgets_values, list) - else [] - ), + # Inner widget values are part of each node fingerprint + # and must not invalidate the whole promoted-context + # snapshot. The current node is reanalyzed when its value + # changes, while promotion topology is rebuilt below. + key: value + for key, value in node.items() + if key != "widgets_values" } context_nodes.append( _normalize_workflow_analysis_value(context_node) diff --git a/tests/test_workflow_analyzer.py b/tests/test_workflow_analyzer.py index ce29e92..4a28a25 100644 --- a/tests/test_workflow_analyzer.py +++ b/tests/test_workflow_analyzer.py @@ -1626,6 +1626,58 @@ class WorkflowModelInventoryCacheTests(unittest.TestCase): self.assertEqual(6, get_node_info.call_count) + def test_inner_subgraph_model_change_reuses_unchanged_nodes(self): + def build_workflow(inner_value): + return { + "nodes": [ + { + "id": 1, + "type": "subgraph-1", + "widgets_values": [], + "properties": {}, + "inputs": [], + "outputs": [], + } + ], + "definitions": { + "subgraphs": [ + { + "id": "subgraph-1", + "name": "Model subgraph", + "inputs": [], + "nodes": [ + { + **_workflow_with_model(inner_value)["nodes"][0], + "id": 10, + }, + { + **_workflow_with_model( + "unchanged.safetensors" + )["nodes"][0], + "id": 11, + }, + ], + } + ] + }, + } + + with ( + patch( + "core.scanner.get_model_files", + return_value=[], + ), + patch.object( + references, + "get_node_model_info", + wraps=references.get_node_model_info, + ) as get_node_info, + ): + get_workflow_model_inventory(build_workflow("first.safetensors")) + get_workflow_model_inventory(build_workflow("second.safetensors")) + + self.assertEqual(4, get_node_info.call_count) + def test_scanner_invalidation_clears_shared_inventory(self): workflow = _workflow_with_model("invalidated.safetensors")