From 21e89fa7de2964c1147c42e1293760aceb9a5dd0 Mon Sep 17 00:00:00 2001 From: Will Miao Date: Fri, 19 Jun 2026 16:42:09 +0800 Subject: [PATCH] fix(tags): normalize tag case on save and make filtering case-insensitive (#727) - save_metadata_updates now trims/lowercases/dedupes tags on write - ModelFilterSet tag matching is now case-insensitive (both include/exclude) - Removed redundant .lower() calls in tag_update_service.py --- py/services/metadata_sync_service.py | 13 ++++++++++++- py/services/model_query.py | 26 +++++++++++++++++--------- py/services/tag_update_service.py | 4 ++-- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/py/services/metadata_sync_service.py b/py/services/metadata_sync_service.py index fbb95768..4438fcbf 100644 --- a/py/services/metadata_sync_service.py +++ b/py/services/metadata_sync_service.py @@ -427,7 +427,18 @@ class MetadataSyncService: metadata = await metadata_loader(metadata_path) for key, value in updates.items(): - if isinstance(value, dict) and isinstance(metadata.get(key), dict): + if key == "tags" and isinstance(value, list): + # Normalize tags: trim, lowercase, deduplicate + normalized = [] + seen = set() + for tag in value: + if isinstance(tag, str): + t = tag.strip().lower() + if t and t not in seen: + normalized.append(t) + seen.add(t) + metadata[key] = normalized + elif isinstance(value, dict) and isinstance(metadata.get(key), dict): metadata[key].update(value) else: metadata[key] = value diff --git a/py/services/model_query.py b/py/services/model_query.py index 34c542d4..73386cc4 100644 --- a/py/services/model_query.py +++ b/py/services/model_query.py @@ -294,12 +294,14 @@ class ModelFilterSet: for tag, state in tag_filters.items(): if not tag: continue + # Normalize to lowercase for case-insensitive matching + normalized = tag.strip().lower() if state == "exclude": - exclude_tags.add(tag) + exclude_tags.add(normalized) else: - include_tags.add(tag) + include_tags.add(normalized) else: - include_tags = {tag for tag in tag_filters if tag} + include_tags = {tag.strip().lower() for tag in tag_filters if tag} if include_tags: tag_logic = criteria.tag_logic.lower() if criteria.tag_logic else "any" @@ -318,13 +320,17 @@ class ModelFilterSet: return True # Otherwise, check if all non-special tags match if non_special_tags: - return all(tag in (item_tags or []) for tag in non_special_tags) + # Case-insensitive: normalize item tags too + normalized_item_tags = {t.strip().lower() for t in (item_tags or []) if isinstance(t, str)} + return all(tag in normalized_item_tags for tag in non_special_tags) return True - # Normal case: all tags must match - return all(tag in (item_tags or []) for tag in non_special_tags) + # Normal case: all tags must match (case-insensitive) + normalized_item_tags = {t.strip().lower() for t in (item_tags or []) if isinstance(t, str)} + return all(tag in normalized_item_tags for tag in non_special_tags) else: - # OR logic (default): item must have ANY include tag - return any(tag in include_tags for tag in (item_tags or [])) + # OR logic (default): item must have ANY include tag (case-insensitive) + normalized_item_tags = {t.strip().lower() for t in (item_tags or []) if isinstance(t, str)} + return bool(normalized_item_tags & include_tags) items = [item for item in items if matches_include(item.get("tags"))] @@ -333,7 +339,9 @@ class ModelFilterSet: def matches_exclude(item_tags): if not item_tags and "__no_tags__" in exclude_tags: return True - return any(tag in exclude_tags for tag in (item_tags or [])) + # Case-insensitive: normalize item tags + normalized_item_tags = {t.strip().lower() for t in (item_tags or []) if isinstance(t, str)} + return bool(normalized_item_tags & exclude_tags) items = [ item for item in items if not matches_exclude(item.get("tags")) diff --git a/py/services/tag_update_service.py b/py/services/tag_update_service.py index 373fed6d..d2a9a7c1 100644 --- a/py/services/tag_update_service.py +++ b/py/services/tag_update_service.py @@ -36,9 +36,9 @@ class TagUpdateService: if isinstance(tag, str) and tag.strip(): # Convert all tags to lowercase to avoid case sensitivity issues on Windows normalized = tag.strip().lower() - if normalized.lower() not in existing_lower: + if normalized not in existing_lower: existing_tags.append(normalized) - existing_lower.append(normalized.lower()) + existing_lower.append(normalized) tags_added.append(normalized) metadata["tags"] = existing_tags