From 6ea6e4d0adfff34f9afa53148478fc5fa133689d Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Wed, 10 Dec 2025 02:09:59 +0100 Subject: [PATCH] fix(library-desk): fix entity linking path cleaning and longest-first matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two critical bug fixes for entity linking: 1. **Path cleaning**: Replace hardcoded "users/jpmschweitzer/" with regex pattern to handle any user namespace. Now properly cleans paths for all users. 2. **Longest-first matching**: Move protected_ranges computation inside entity loop to recompute after each entity is processed. Prevents nested links like [[Machine](/machine) Learning](/ml) when processing multi-word entities. These fixes ensure entity linking works correctly across all users and prevents nested markdown links when entity names overlap. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../src/routers/entity_linking.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/services/library-desk/src/routers/entity_linking.py b/services/library-desk/src/routers/entity_linking.py index d1ab2f7..008babc 100644 --- a/services/library-desk/src/routers/entity_linking.py +++ b/services/library-desk/src/routers/entity_linking.py @@ -289,14 +289,6 @@ def add_entity_links_to_content( # e.g., "Machine Learning" before "Machine" linkable_entities.sort(key=lambda x: len(x["name"]), reverse=True) - # Find all existing markdown links to protect them - link_pattern = r'\[([^\]]+)\]\([^\)]+\)' - existing_links = list(re.finditer(link_pattern, content)) - - # Create a list of protected ranges (start, end) for ENTIRE links (text + URL) - # This prevents linking entity names inside existing link URLs - protected_ranges = [(m.start(), m.end()) for m in existing_links] - updated_content = content links_added = 0 @@ -309,10 +301,18 @@ def add_entity_links_to_content( continue # Create markdown link - # We'll link to the relative path without the "users/jpmschweitzer/" prefix - clean_path = entity_path.replace("users/jpmschweitzer/", "") + # Remove any "users/{username}/" prefix to get clean relative path + clean_path = re.sub(r'^users/[^/]+/', '', entity_path) markdown_link = f"[{entity_name}](/{clean_path})" + # Find all existing markdown links to protect them (recompute each iteration) + link_pattern = r'\[([^\]]+)\]\([^\)]+\)' + existing_links = list(re.finditer(link_pattern, updated_content)) + + # Create a list of protected ranges (start, end) for ENTIRE links (text + URL) + # This prevents linking entity names inside existing link URLs + protected_ranges = [(m.start(), m.end()) for m in existing_links] + # Find all potential matches pattern = r'\b(' + re.escape(entity_name) + r')\b' matches = list(re.finditer(pattern, updated_content, flags=re.IGNORECASE))