From 5d3a0d5e4ecb31e1f29be824f572a027405aa201 Mon Sep 17 00:00:00 2001 From: Simon Pinfold Date: Tue, 18 Aug 2026 12:25:33 -0700 Subject: [PATCH] Strip the category name rather than the finished tag Normalizing the assembled tag only reaches whitespace at its ends, so a category registered as "vae " was fixed while one registered as " loras" was not: its space lands after "model_type:", where the strip that stored names go through cannot reach it. The entry stayed unmatched and the stale automatic tag it authorises removing survived. Stripping the name before the tag is built covers both, and leaves the vocabulary clean by construction rather than cleaned afterwards. --- app/assets/services/path_utils.py | 10 +++--- .../assets_test/test_semantics_reset.py | 31 ++++++++++++------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/app/assets/services/path_utils.py b/app/assets/services/path_utils.py index 683ed2481..cd47255b1 100644 --- a/app/assets/services/path_utils.py +++ b/app/assets/services/path_utils.py @@ -3,7 +3,6 @@ from pathlib import Path from typing import Literal import folder_paths -from app.assets.helpers import normalize_tags _NON_MODEL_FOLDER_NAMES = frozenset({"configs", "custom_nodes"}) @@ -331,10 +330,11 @@ def get_path_derived_tag_vocabulary() -> set[str]: vocabulary = {"input", "output", "temp", "models"} vocabulary.update(_KNOWN_SUBFOLDER_TAGS) for folder_name, _bases, _extensions in get_comfy_models_folders(): - vocabulary.add(f"model_type:{folder_name}") - # Stored names reach the database through normalize_tags, so an un-normalized - # entry would never match the tag it exists to authorise removing. - return set(normalize_tags(list(vocabulary))) + # Strip the name, not the finished tag: whitespace landing after + # "model_type:" survives the strip that stored names are put through, + # so the entry would never match the tag it authorises removing. + vocabulary.add(f"model_type:{folder_name.strip()}") + return vocabulary def get_name_and_tags_from_asset_path(file_path: str) -> tuple[str, list[str]]: diff --git a/tests-unit/assets_test/test_semantics_reset.py b/tests-unit/assets_test/test_semantics_reset.py index 48fd8b4f9..a866124da 100644 --- a/tests-unit/assets_test/test_semantics_reset.py +++ b/tests-unit/assets_test/test_semantics_reset.py @@ -392,19 +392,26 @@ class TestTagReprojection: tags={"model_type:loras": "automatic"}, ) - with patch( - "app.assets.services.path_utils.get_comfy_models_folders", - return_value=[ - ("checkpoints", [str(comfy_dirs["checkpoints"])], {".safetensors"}), - ("loras ", [str(comfy_dirs["loras"])], {".safetensors"}), - ], - ): - reproject_derived_state() + # Leading whitespace lands after "model_type:", where a strip of the + # finished tag cannot reach it; trailing whitespace a strip would catch. + for registered in (" loras", "loras "): + with patch( + "app.assets.services.path_utils.get_comfy_models_folders", + return_value=[ + ( + "checkpoints", + [str(comfy_dirs["checkpoints"])], + {".safetensors"}, + ), + (registered, [str(comfy_dirs["loras"])], {".safetensors"}), + ], + ): + reproject_derived_state() - assert "model_type:loras" not in _tags(session, "ref-1"), ( - "a category name with stray whitespace must not smuggle an entry " - "past the vocabulary and leave the stale tag in place" - ) + assert "model_type:loras" not in _tags(session, "ref-1"), ( + f"a category registered as {registered!r} must not smuggle an " + "entry past the vocabulary and leave the stale tag in place" + ) def test_automatic_tag_outside_the_vocabulary_is_left_alone( self, session, comfy_dirs