fix: render readable wiki artifact links (#17011)

This commit is contained in:
buua436
2026-07-17 14:28:20 +08:00
committed by GitHub
parent c5cf9b473d
commit b32d5fd86b

View File

@@ -38,6 +38,7 @@ import json
import logging
import re
from typing import Callable, Optional
from urllib.parse import urlsplit
from common.misc_utils import thread_pool_exec
from common.token_utils import num_tokens_from_string
from rag.prompts.generator import gen_json, message_fit_in
@@ -2841,19 +2842,20 @@ async def _wiki_build_source_context(
_WIKILINK_PIPE_RE = re.compile(r"\[\[([^\[\]\|]+?)\|([^\[\]]+?)\]\]")
_WIKILINK_SIMPLE_RE = re.compile(r"\[\[([^\[\]\|]+?)\]\]")
_ARTIFACT_MARKDOWN_LINK_RE = re.compile(r"\[([^\]]+)\]\(([^)]+)\)")
def _wiki_transform_links(content_md: str, kb_id: str) -> tuple[str, list[str]]:
"""Rewrite ``[[slug]]`` / ``[[slug|display]]`` wikilinks to standard
markdown links whose href encodes ``(kb_id, slug)`` so a renderer can
fetch the target page from ES.
def _wiki_transform_links(content_md: str, kb_id: str, page_titles: dict[str, str] | None = None) -> tuple[str, list[str]]:
"""Normalize wiki links and return ``(rendered_md, unique_outlinks)``.
Returns ``(rewritten_md, unique_outlinks)`` — outlinks are slug strings
in first-seen order. The href format is ``artifact/{kb_id}/{slug}`` which is
relative; clients are expected to map this to whatever route serves the
page (e.g. ``/api/v1/artifact/{kb_id}/{slug}``).
Both the canonical ``[[slug]]`` form and Markdown links emitted by an LLM
are accepted. Artifact links are rewritten to the relative
``artifact/{kb_id}/{slug}`` form. When an artifact link uses its slug as
the label, prefer the planned page title; otherwise derive readable text
from the slug.
"""
kb_id_str = str(kb_id)
page_titles = page_titles or {}
seen: set[str] = set()
outlinks: list[str] = []
@@ -2863,6 +2865,38 @@ def _wiki_transform_links(content_md: str, kb_id: str) -> tuple[str, list[str]]:
seen.add(s)
outlinks.append(s)
def _display_text(label: str, slug: str) -> str:
label = label.strip()
if label not in {slug, slug.rsplit("/", 1)[-1]}:
return label
planned_title = page_titles.get(slug)
if planned_title:
return planned_title
readable = slug.rsplit("/", 1)[-1].replace("-", " ").replace("_", " ").strip()
return readable.title() or label
def _artifact_slug(href: str) -> str | None:
parsed = urlsplit(href)
if parsed.scheme or parsed.netloc:
if parsed.netloc != "artifact":
return None
path = parsed.path
else:
path = parsed.path
parts = path.strip("/").split("/")
if parts and parts[0] == "artifact":
parts = parts[1:]
if len(parts) < 2 or parts[0] != kb_id_str:
return None
return "/".join(parts[1:])
def _markdown_artifact(m: re.Match) -> str:
slug = _artifact_slug(m.group(2))
if not slug:
return m.group(0)
_track(slug)
return f"[{_display_text(m.group(1), slug)}](artifact/{kb_id_str}/{slug})"
def _piped(m: re.Match) -> str:
slug = m.group(1).strip()
text = m.group(2).strip()
@@ -2872,9 +2906,10 @@ def _wiki_transform_links(content_md: str, kb_id: str) -> tuple[str, list[str]]:
def _simple(m: re.Match) -> str:
slug = m.group(1).strip()
_track(slug)
return f"[{slug}](artifact/{kb_id_str}/{slug})"
return f"[{_display_text(slug, slug)}](artifact/{kb_id_str}/{slug})"
rewritten = _WIKILINK_PIPE_RE.sub(_piped, content_md or "")
rewritten = _ARTIFACT_MARKDOWN_LINK_RE.sub(_markdown_artifact, content_md or "")
rewritten = _WIKILINK_PIPE_RE.sub(_piped, rewritten)
rewritten = _WIKILINK_SIMPLE_RE.sub(_simple, rewritten)
return rewritten, outlinks
@@ -3362,6 +3397,11 @@ async def wiki_refine_from_plan(
all_claims = plan.get("_claims") or []
# ``all_plan_slugs`` is implicitly deduped now (pages_spec is unique).
all_plan_slugs = [p["slug"] for p in pages_spec]
page_titles = {
str(p["slug"]): str(p.get("title") or "").strip()
for p in pages_spec
if p.get("slug") and str(p.get("title") or "").strip()
}
# Build canonical entity/concept lookups for evidence fallback. When MAP
# produced no claims (a real failure mode we've seen on Chinese / dense
@@ -3483,7 +3523,7 @@ async def wiki_refine_from_plan(
)
# Render artifactlinks once, here, after all LLM transforms.
content_md_rendered, outlinks = _wiki_transform_links(content_md_raw, kb_id)
content_md_rendered, outlinks = _wiki_transform_links(content_md_raw, kb_id, page_titles=page_titles)
source_doc_ids = await _wiki_collect_doc_ids(source_chunk_ids, tenant_id, kb_id)
summary = _wiki_extract_summary(content_md_rendered) or title