diff --git a/paperforge/worker/ocr_figure_reader.py b/paperforge/worker/ocr_figure_reader.py index 76ea5666..1d636673 100644 --- a/paperforge/worker/ocr_figure_reader.py +++ b/paperforge/worker/ocr_figure_reader.py @@ -190,7 +190,7 @@ def _materialize_hold_outcome( "caption_text": caption_text, "visual_groups": [], "consumed_caption_block_ids": ( - [legend_block_id] if reader_visible and legend_block_id is not None and caption_text else [] + [{"page": page, "block_id": legend_block_id}] if reader_visible and legend_block_id is not None and caption_text else [] ), "consumed_asset_block_ids": [], "debug_refs": {"candidate_asset_ids": list(candidate_asset_ids), "hold_visibility": hold_visibility}, @@ -231,8 +231,8 @@ def _materialize_reader_figure( "rendered_as_representative": True, } ], - "consumed_caption_block_ids": [legend_block_id] if legend_block_id is not None else [], - "consumed_asset_block_ids": asset_ids, + "consumed_caption_block_ids": [{"page": normalized_item.get("page"), "block_id": legend_block_id}] if legend_block_id is not None else [], + "consumed_asset_block_ids": [{"page": normalized_item.get("page"), "block_id": aid} for aid in asset_ids], "debug_refs": {}, } @@ -260,7 +260,7 @@ def _materialize_reader_figure( "rendered_as_representative": False, } ], - "consumed_caption_block_ids": [legend_block_id] if legend_block_id is not None else [], + "consumed_caption_block_ids": [{"page": normalized_item.get("page"), "block_id": legend_block_id}] if legend_block_id is not None else [], "consumed_asset_block_ids": [], "debug_refs": {"candidate_asset_ids": candidate_asset_ids}, } @@ -278,7 +278,7 @@ def _materialize_reader_figure( "caption_block_id": legend_block_id, "caption_text": caption_text, "visual_groups": [], - "consumed_caption_block_ids": [legend_block_id] if legend_block_id is not None else [], + "consumed_caption_block_ids": [{"page": normalized_item.get("page"), "block_id": legend_block_id}] if legend_block_id is not None else [], "consumed_asset_block_ids": [], "debug_refs": {}, } @@ -298,7 +298,7 @@ def _materialize_reader_figure( "caption_block_id": legend_block_id, "caption_text": caption_text, "visual_groups": [], - "consumed_caption_block_ids": [legend_block_id] if legend_block_id is not None else [], + "consumed_caption_block_ids": [{"page": normalized_item.get("page"), "block_id": legend_block_id}] if legend_block_id is not None else [], "consumed_asset_block_ids": [], "debug_refs": {}, } @@ -327,7 +327,7 @@ def _materialize_reader_figure( } ], "consumed_caption_block_ids": [], - "consumed_asset_block_ids": asset_ids, + "consumed_asset_block_ids": [{"page": normalized_item.get("page"), "block_id": aid} for aid in asset_ids], "debug_refs": {}, } @@ -361,25 +361,25 @@ def _passes_salient_visual_group_gate(item: dict) -> bool: def _collect_reader_eligible_inputs(normalized: dict) -> list[dict]: eligible: list[dict] = [] - seen_legends: set[int | str] = set() + seen_legends: set[tuple[int | str | None, int | str | None]] = set() for source_name in ("matched_figures", "held_figures", "ambiguous_figures"): for item in normalized.get(source_name, []): - legend_block_id = item.get("legend_block_id") - if legend_block_id is None or legend_block_id in seen_legends: + legend_id = (item.get("page"), item.get("legend_block_id")) + if legend_id in seen_legends: continue if not _passes_formal_legend_gate(item): continue - seen_legends.add(legend_block_id) + seen_legends.add(legend_id) eligible.append({"kind": "legend", "source": source_name, "item": item}) for item in normalized.get("unmatched_legends", []): - legend_block_id = item.get("legend_block_id") - if legend_block_id is None or legend_block_id in seen_legends: + legend_id = (item.get("page"), item.get("legend_block_id")) + if legend_id in seen_legends: continue if not _passes_formal_legend_gate(item): continue - seen_legends.add(legend_block_id) + seen_legends.add(legend_id) eligible.append({"kind": "legend", "source": "unmatched_legends", "item": item}) for item in normalized.get("unresolved_clusters", []): @@ -415,10 +415,6 @@ def synthesize_reader_figures( consumed_asset_ids.extend(materialized.get("consumed_asset_block_ids", [])) coverage_total = len(eligible_inputs) - reader_figures = [ - figure for figure in reader_figures - if figure.get("visual_groups") - ] return { "normalized_inputs": normalized, "reader_figures": reader_figures, diff --git a/paperforge/worker/ocr_render.py b/paperforge/worker/ocr_render.py index 3547e65e..8d540c37 100644 --- a/paperforge/worker/ocr_render.py +++ b/paperforge/worker/ocr_render.py @@ -674,11 +674,6 @@ def _emit_page_objects( for fig in figures_by_page.get(page, []): if str(fig['figure_id']).startswith("unmatched_legend_"): continue - caption = str(fig.get("caption") or "").strip() - if caption and caption not in emitted_figure_captions: - lines.append(caption) - lines.append("") - emitted_figure_captions.add(caption) lines.append(f"![[render/figures/{fig['figure_id']}.md]]") lines.append("") for cluster_id in unresolved_clusters_by_page.get(page, []): @@ -714,7 +709,7 @@ def render_fulltext_markdown( emitted_figure_captions: set[str] = set() reader_figures = (reader_payload or {}).get("reader_figures", []) - consumed_caption_block_ids = set((reader_payload or {}).get("consumed_caption_block_ids", [])) + consumed_caption_keys = {(item.get("page"), item.get("block_id")) for item in (reader_payload or {}).get("consumed_caption_block_ids", []) if item.get("block_id") is not None} rendered_reader_figure_ids: set[str] = set() _block_page_map: dict[int, int] = {} @@ -967,12 +962,14 @@ def render_fulltext_markdown( "abstract_body", "frontmatter_noise", "table_html", + "figure_caption", } if role in _SKIPPED_BODY_ROLES: continue block_id = block.get("block_id") - if block_id is not None and block_id in consumed_caption_block_ids: + block_page = block.get("page") + if block_id is not None and (block_page, block_id) in consumed_caption_keys: continue raw_text = block.get("text", "") @@ -980,7 +977,6 @@ def render_fulltext_markdown( text = re.sub(r"