feat(F-12): wiki-compile — grounded concept pages over RAG substrate #2
@@ -284,6 +284,9 @@ def _run_grounding_guard(
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
_INLINE_CODE_RE = re.compile(r"`[^`]+`")
|
||||||
|
|
||||||
|
|
||||||
def _inject_cross_refs(
|
def _inject_cross_refs(
|
||||||
markdown: str,
|
markdown: str,
|
||||||
all_concepts: list[Concept],
|
all_concepts: list[Concept],
|
||||||
@@ -292,8 +295,21 @@ def _inject_cross_refs(
|
|||||||
"""Replace occurrences of other concept titles/aliases with ``[[slug]]`` links.
|
"""Replace occurrences of other concept titles/aliases with ``[[slug]]`` links.
|
||||||
|
|
||||||
Only exact case-insensitive whole-word matches outside of existing
|
Only exact case-insensitive whole-word matches outside of existing
|
||||||
``[[…]]`` blocks or inline code are replaced.
|
``[[…]]`` blocks or inline code spans are replaced.
|
||||||
|
Inline-code spans (`` `…` ``) are temporarily protected by null-byte
|
||||||
|
placeholders and restored after injection.
|
||||||
"""
|
"""
|
||||||
|
# Step 1: protect inline-code spans from replacement
|
||||||
|
placeholders: dict[str, str] = {}
|
||||||
|
|
||||||
|
def _protect(m: re.Match[str]) -> str:
|
||||||
|
key = f"\x00{len(placeholders)}\x00"
|
||||||
|
placeholders[key] = m.group(0)
|
||||||
|
return key
|
||||||
|
|
||||||
|
markdown = _INLINE_CODE_RE.sub(_protect, markdown)
|
||||||
|
|
||||||
|
# Step 2: inject cross-refs on unprotected text
|
||||||
for concept in all_concepts:
|
for concept in all_concepts:
|
||||||
if concept.slug == current_slug:
|
if concept.slug == current_slug:
|
||||||
continue
|
continue
|
||||||
@@ -304,6 +320,11 @@ def _inject_cross_refs(
|
|||||||
pattern = re.compile(rf"(?<!\[\[)\b{escaped}\b(?!\]\])", re.IGNORECASE)
|
pattern = re.compile(rf"(?<!\[\[)\b{escaped}\b(?!\]\])", re.IGNORECASE)
|
||||||
replacement = f"[[{concept.slug}]]"
|
replacement = f"[[{concept.slug}]]"
|
||||||
markdown = pattern.sub(replacement, markdown)
|
markdown = pattern.sub(replacement, markdown)
|
||||||
|
|
||||||
|
# Step 3: restore inline-code spans
|
||||||
|
for key, val in placeholders.items():
|
||||||
|
markdown = markdown.replace(key, val)
|
||||||
|
|
||||||
return markdown
|
return markdown
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -212,6 +212,38 @@ def test_inject_cross_refs_replaces_alias() -> None:
|
|||||||
assert "[[circle-packing]]" in result
|
assert "[[circle-packing]]" in result
|
||||||
|
|
||||||
|
|
||||||
|
def test_inject_cross_refs_skips_inline_code() -> None:
|
||||||
|
"""Concept titles inside backtick inline-code spans are NOT replaced."""
|
||||||
|
concepts = [
|
||||||
|
Concept(
|
||||||
|
slug="circle-packing",
|
||||||
|
title="circle-packing",
|
||||||
|
aliases=[],
|
||||||
|
),
|
||||||
|
FIXTURE_CONCEPT,
|
||||||
|
]
|
||||||
|
# The term "circle-packing" appears both bare (should be replaced)
|
||||||
|
# and inside backticks (should NOT be replaced).
|
||||||
|
text = "Use `circle-packing` for this. See also circle-packing theory."
|
||||||
|
result = _inject_cross_refs(text, concepts, current_slug="lobachevsky-function")
|
||||||
|
assert "`circle-packing`" in result # inline-code preserved
|
||||||
|
assert "[[circle-packing]]" in result # bare occurrence replaced
|
||||||
|
|
||||||
|
|
||||||
|
def test_inject_cross_refs_full_list_even_when_single_concept() -> None:
|
||||||
|
"""Cross-ref injection uses the full concept list, not just the compiled concept."""
|
||||||
|
concepts = [
|
||||||
|
Concept(slug="circle-packing", title="Circle Packing", aliases=[]),
|
||||||
|
Concept(slug="discrete-conformal-map", title="Discrete Conformal Map", aliases=[]),
|
||||||
|
FIXTURE_CONCEPT,
|
||||||
|
]
|
||||||
|
# Compiling lobachevsky-function only; text mentions the other two concepts
|
||||||
|
text = "Discrete Conformal Map is used with Circle Packing."
|
||||||
|
result = _inject_cross_refs(text, concepts, current_slug="lobachevsky-function")
|
||||||
|
assert "[[circle-packing]]" in result
|
||||||
|
assert "[[discrete-conformal-map]]" in result
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# compile_concept (full mock)
|
# compile_concept (full mock)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user