fix(library-desk): fix entity linking path cleaning and longest-first matching

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 <noreply@anthropic.com>
This commit is contained in:
2025-12-10 02:09:59 +01:00
co-authored by Claude Opus 4.5
parent 60b0100861
commit 6ea6e4d0ad
@@ -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))