fix: stop paper_title pollution, text-to-heading, table HTML leak, author recovery

This commit is contained in:
Research Assistant 2026-06-05 15:02:00 +08:00
parent 27511b6df6
commit 009be8c958
5 changed files with 45 additions and 19 deletions

View file

@ -25,7 +25,7 @@ def build_structured_blocks(raw_blocks: list[dict]) -> list[dict]:
index_default = True
if role.role in {"noise", "page_header", "page_footer", "frontmatter_noise"}:
render_default = False
if role.role in {"noise", "frontmatter_noise"}:
if role.role in {"noise", "frontmatter_noise", "table_html"}:
index_default = False
row = {
"paper_id": block["paper_id"],

View file

@ -78,29 +78,27 @@ def resolve_metadata(
# --- authors ---
zotero_authors = source_metadata.get("authors", [])
ocr_authors_text = frontmatter_candidates.get("authors_text", "")
ocr_author_list = [a.strip() for a in ocr_authors_text.split(",") if a.strip()] if ocr_authors_text else []
if isinstance(zotero_authors, list) and len(zotero_authors) > 0:
resolved["authors"] = {
"value": zotero_authors,
"source": "zotero",
"confidence": 0.99,
}
elif ocr_author_list:
resolved["authors"] = {
"value": ocr_author_list,
"source": "ocr_frontmatter",
"confidence": 0.6,
}
else:
resolved["authors"] = {
"value": [],
"source": "unknown",
"confidence": 0.3,
}
ocr_authors_text = frontmatter_candidates.get("authors_text", "")
if ocr_authors_text:
ocr_author_list = [a.strip() for a in ocr_authors_text.split(",") if a.strip()]
if ocr_author_list and ocr_author_list != zotero_authors:
resolved["authors"]["alternatives"] = [
{
"value": ocr_author_list,
"source": "ocr_frontmatter",
"confidence": 0.6,
}
]
# --- year ---
zotero_year = source_metadata.get("year", 0)

View file

@ -92,10 +92,12 @@ def render_fulltext_markdown(
if not block.get("render_default", True):
continue
role = block.get("role", "")
if role in ("abstract_heading", "abstract_body", "figure_caption", "table_caption", "frontmatter_noise"):
if role in ("abstract_heading", "abstract_body", "figure_caption", "table_caption", "frontmatter_noise", "table_html"):
continue
text = _normalize_latex(block.get("text", ""))
if text.strip().lower().startswith("<table"):
continue
block_page = block.get("page")
if block_page is not None and block_page != current_page:

View file

@ -12,7 +12,7 @@ class RoleAssignment:
_HEADING_NUMBER_PATTERN = re.compile(
r"^\d+(?:\.\d+)*\s+\w",
r"^\d+(?:\.\d+)*\s+[A-Z]",
)
_FIGURE_PREFIX_PATTERN = re.compile(
@ -141,10 +141,17 @@ def assign_block_role(
confidence=0.85,
evidence=[f"paragraph_title label with numbering: {text[:60]}"],
)
page_num = block.get("page", 1) or 1
if page_num <= 1:
return RoleAssignment(
role="paper_title",
confidence=0.7,
evidence=[f"unnumbered paragraph_title on page 1, likely paper title: {text[:60]}"],
)
return RoleAssignment(
role="paper_title",
confidence=0.7,
evidence=[f"unnumbered paragraph_title, likely paper title: {text[:60]}"],
role="section_heading",
confidence=0.6,
evidence=[f"unnumbered paragraph_title on page {page_num}, treated as heading: {text[:60]}"],
)
if raw_label == "figure_title":
@ -189,6 +196,13 @@ def assign_block_role(
evidence=["abstract label from Paddle OCR"],
)
if raw_label == "reference_content":
return RoleAssignment(
role="reference_item",
confidence=0.85,
evidence=[f"reference content label: {text[:60]}"],
)
# text with reference-like pattern
if _looks_like_reference(text):
return RoleAssignment(
@ -202,6 +216,14 @@ def assign_block_role(
stripped = text.strip().lstrip("*•·-—_")
lower_txt = stripped.lower()
# Check for inline table HTML
if text.strip().lower().startswith("<table"):
return RoleAssignment(
role="table_html",
confidence=0.95,
evidence=[f"inline table HTML"],
)
# Check for abstract heading (may appear as text block, not paragraph_title)
if lower_txt.startswith("abstract") and len(text) < 30:
return RoleAssignment(
@ -290,7 +312,11 @@ def assign_block_role(
confidence=0.7,
evidence=[f"frontmatter noise text: {text[:60]}"],
)
if _has_heading_numbering(text):
if (
_has_heading_numbering(text)
and len(text) < 80
and ". " not in text
):
return RoleAssignment(
role="section_heading" if re.match(r"^\d+\s", text) else "subsection_heading",
confidence=0.65,

View file

@ -123,7 +123,7 @@ def test_stabilize_role_classifies_paper_title() -> None:
from paperforge.worker.ocr_roles import assign_block_role
assignment = assign_block_role(
block={"block_label": "paragraph_title", "block_content": "Test Paper About Science"},
block={"block_label": "paragraph_title", "block_content": "Test Paper About Science", "page": 1},
page_blocks=[],
)