Skip to content

Resolve Issue #158: Page-Memory PR4 modules, Excel table pathing, evidence renderer optimization - #159

Merged
EricNGOntos merged 5 commits into
mainfrom
feat/wuchengke/page-memory-page-mode
Jun 17, 2026
Merged

EricNGOntos merged 5 commits into
mainfrom
feat/wuchengke/page-memory-page-mode

Conversation

@EricNGOntos

Copy link
Copy Markdown
Contributor

Closes #158

Changes Overview

This PR delivers four independent but cohesive improvements across the document parsing and retrieval pipeline. All page-memory changes are gated behind RETRIEVAL_PAGE_MEMORY_ENABLED=false (default) and parse_track='chunk' (default), ensuring zero impact on existing production parsing.

1. Page-Memory Per-Page Parsing Pipeline (C1–C7)

New package apps/worker/app/services/page_memory/ with complete per-page processing:

Module Purpose
page_renderer.py Renders page images + extracts raw text via PyMuPDF
page_plan.py Derives per-page processing strategy from document profile labels
page_tagger.py VLM-based page summarization with budget-aware tagging and JSON retry
skeleton_extractor.py Extracts section skeleton from anatomy map for heading→page mapping
page_section_mapper.py Maps each page to its hierarchical section path
memory_service.py Orchestrates the full C1→C7 pipeline, producing standard DataFrame output

Gating: RETRIEVAL_PAGE_MEMORY_ENABLED=false in API → 400 rejection. documents.parse_track defaults to chunk → Worker never enters page path.

2. Document Agent: Page Locate Stage + Lazy Loading

  • hierarchy_locator.py: Multi-strategy heading-to-page resolver (strict anchoring → grep → VLM verify)
  • page_locate_agent.py / page_locate_subagent.py: Bounded ReAct loop for residual headings that strict anchoring cannot resolve
  • page_locate_tools.py: grep.title_pages and verify.section_page tool implementations
  • coordinator.py: Added page_locate budget stage envelope (min=0, cap=0 — inactive by default)
  • __init__.py: ProfileAgent converted to lazy import via __getattr__ to avoid heavy dependency loading

3. Excel Table Parsing Enhancement

  • Hierarchical pathing: Table paths now follow filename/sheet_name/subtable_title instead of flat tables/table-name.html
  • Subtable metadata: DataFrame attrs propagate sheet_name, subtable_index, subtable_count, subtable_title
  • Content normalization: Table chunks now store prettified HTML directly in content field (was reference string)
  • doc_nav fallback: When a document has only table chunks (e.g., pure Excel), tables participate in the section tree via table_section_candidates

4. Agentic Evidence Renderer Optimization

  • Large table truncation: Tables exceeding RETRIEVAL_AGENTIC_INLINE_TABLE_CHAR_LIMIT (default 10,000 chars) are replaced with a compact summary (asset URL, path, summary, keywords)
  • Table rendering enrichment: Table chunks now include asset URL and file path references in evidence text
  • New config: RETRIEVAL_AGENTIC_INLINE_TABLE_CHAR_LIMIT in AIConfig (env-configurable)

5. Supporting Changes

  • zip_chunk_schema.py: Added page chunk type counting and formatting
  • zip_doc_navigation.py: Page chunks participate in section tree; table fallback for text-empty documents
  • zip_result_resources.py: New utility for page result resources
  • Contract tests for evidence renderer and Excel parser

Testing

  • make check passes: 0 lint errors, 0 type errors
  • Contract tests: test_agentic_evidence_renderer_contract.py, test_excel_parser_contract.py
  • Impact analysis confirms PDF chunk-track parsing is completely unaffected (parse_service.py and pdf/parser.py have zero diffs)

Risk Assessment

Change Risk Rationale
Page-memory modules None Double-gated: API flag + DB default
ProfileAgent lazy loading Minimal Behavioral no-op, only changes import timing
Excel table pathing Low Only affects Excel parsing output format
Evidence renderer Low Positive UX improvement, configurable threshold
doc_nav table fallback Low Only triggers when no text chunks exist

… ProfileAgent.

- Added "page_locate" to BudgetStage for improved budget management.
- Introduced lazy loading for ProfileAgent to optimize imports in document_agent.
- Updated tools initialization to include page_locate functionality.
New page_memory modules:
- C1 page_renderer: PNG + thumbnail + raw_text per page
- C2 page_plan: rule-based vlm_lite/text_only/skip_tagging strategy
- C3 page_tagger: VLM per-page annotation with JSON retry + blurry degradation
- C6 page_section_mapper: skeleton × tagger → section_path (primary/spans/inherited)
- C7 memory_service: unified page/shard_page builder via full C1-C7 pipeline

shared-python GAP fixes:
- GAP-1: zip_chunk_schema recognizes 'page' chunk type (no collapse to text)
- GAP-2: zip_result_resources collects pages/ directory
- GAP-3: zip_doc_navigation counts page_chunks in stats
Field changes:
- content = raw PyMuPDF text only (no [SUMMARY]/[RAW] markers)
- summary = VLM or LLM-generated summary (metadata only)
- keywords = VLM or summary-full LLM extracted (semicolon-separated)
- kind = PageLabel.kind from Profile Agent (not plan.reason)
- observed_titles = from C4 skeleton primary titles (not VLM)
- Remove thumb_uri (only page_image_uri kept)
- Remove status field (strategy_used covers quality info)

Strategy changes:
- text_only: calls existing summary-full LLM for summary+keywords
- skip_tagging: preserves raw text content, marks EMPTY if blank
- vlm_lite: outputs summary+keywords (no observed_titles)
- Mapper no longer depends on PageTagResult
… support, and optimized evidence rendering with configurable character limits
@EricNGOntos EricNGOntos added the page-memory-pipeline Page-based memory parsing pipeline and supporting infrastructure label Jun 17, 2026
@EricNGOntos EricNGOntos self-assigned this Jun 17, 2026
@EricNGOntos
EricNGOntos merged commit 438f079 into main Jun 17, 2026
4 of 5 checks passed
@EricNGOntos
EricNGOntos deleted the feat/wuchengke/page-memory-page-mode branch June 17, 2026 07:00
actual_max_depth = max_title_depth(nodes)
emit_depth = min(actual_max_depth, self.config.max_emit_depth)
emit_depth = max(emit_depth, self.config.min_emit_depth)
selected_nodes = nodes
subtable_index = int(attrs.get("subtable_index") or 1)
except (TypeError, ValueError):
subtable_count = 1
subtable_index = 1
suguanYang added a commit that referenced this pull request Jun 17, 2026
Resolve Issue #158: Page-Memory PR4 modules, Excel table pathing, evidence renderer optimization (#159)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

page-memory-pipeline Page-based memory parsing pipeline and supporting infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Page-Memory Pipeline PR4: Per-Page Parsing Modules, Excel Table Pathing, and Evidence Renderer Optimization

2 participants