diff --git a/paperforge/worker/ocr_roles.py b/paperforge/worker/ocr_roles.py index 67d4aab7..84b9951f 100644 --- a/paperforge/worker/ocr_roles.py +++ b/paperforge/worker/ocr_roles.py @@ -1128,6 +1128,16 @@ def assign_block_role( evidence=[f"author byline on page 1, assigned as authors: {text[:60]}"], ) + # Explicit correspondence line → frontmatter_support (must run before generic noise rule) + if page_num == 1: + lower_txt = text.lower().strip() + if lower_txt.startswith(("correspondence", "corresponding author", "address for correspondence")): + return RoleAssignment( + role="frontmatter_support", + confidence=0.75, + evidence=[f"page-1 correspondence support: {text[:60]}"], + ) + if re.search(r"(?:correspondence|orcid|@)", text.lower()) and len(text.split()) <= 20 and page_num == 1: return RoleAssignment( role="frontmatter_noise", @@ -1280,6 +1290,14 @@ def assign_block_role( page_num = block.get("page", 1) or 1 still_frontmatter = _page_still_frontmatter(page_blocks, page_num, page_height) + # Explicit page-1 correspondence line → frontmatter_support + if page_num == 1 and lower_txt.startswith(("correspondence", "corresponding author", "address for correspondence")): + return RoleAssignment( + role="frontmatter_support", + confidence=0.75, + evidence=[f"page-1 correspondence support: {text[:60]}"], + ) + # Check for inline table HTML if text.strip().lower().startswith(" assert title_like, "Expected a page-1 paper_title block in DWQQK2YB" assert author_like, "Expected a page-1 authors block in DWQQK2YB" + + +def test_caqnw9q2_page1_correspondence_is_not_frontmatter_noise(tmp_path: Path) -> None: + result = replay_production_pipeline("CAQNW9Q2", tmp_path) + blocks = result["structured_blocks"] + candidates = [ + b for b in blocks + if b.get("page") == 1 and "correspondence" in str(b.get("text") or "").lower() + ] + assert candidates, "Expected a correspondence-related block on CAQNW9Q2 page 1" + assert any(b.get("role") == "frontmatter_support" for b in candidates) + assert not all(b.get("role") == "frontmatter_noise" for b in candidates) diff --git a/tests/test_ocr_roles.py b/tests/test_ocr_roles.py index abb01024..0f66ad7b 100644 --- a/tests/test_ocr_roles.py +++ b/tests/test_ocr_roles.py @@ -1494,3 +1494,18 @@ def test_highlights_label_does_not_swallow_following_body_paragraphs() -> None: assert roles[0].role == "structured_insert_candidate" assert roles[1].role == "body_paragraph" assert roles[2].role == "body_paragraph" + + +def test_page1_explicit_correspondence_line_is_frontmatter_support() -> None: + from paperforge.worker.ocr_roles import assign_block_role + + block = { + "page": 1, + "block_label": "text", + "block_content": "Correspondence: Prof. Smith, Department of Orthopedics, email@example.org", + "block_bbox": [100, 1380, 980, 1450], + "page_width": 1200, + "page_height": 1600, + } + result = assign_block_role(block, [block], page_width=1200, page_height=1600) + assert result.role == "frontmatter_support"