Commit Graph
3 Commits
Author SHA1 Message Date
Chris CoutinhoandClaude Opus 4.8 9ffe0645b8 fix: close pypdfium2 page handle on error + cover classifier/OCR edge cases
Follow-up to the tiered document processor (#858), landing the round-4 review
nits the reviewer approved without:

- pypdfium2_fast: free the page handle in an outer finally so a corrupt page
  that makes get_textpage() raise can't orphan it.
- test: classify_from_text junk-text-layer path (non-zero chars, low quality,
  high ocr_frac) flags bad_text_layer -- the hot-path coverage gap.
- test: build_ocr_backend raises ValueError when the gateway M2M client_id is
  set without its token_url/secret.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 03:21:10 +02:00
Chris CoutinhoandClaude Opus 4.8 bc957b5bc7 fix(review): _enum_fields validation, gate classify_from_text flags, OCR warnings
Address PR #858 review round 3:

- document_tier1_engine / document_ocr_provider now validate + normalize via
  Settings.__post_init__ _enum_fields (the repo's canonical opt-in-enum pattern;
  case-insensitive) instead of dynaconf Validators. A typo now raises ValueError
  at load and "Gateway" normalizes to "gateway".
- classify_from_text gates no_text_layer/bad_text_layer on ocr_frac >=
  OCR_PAGE_FRACTION, matching classify_pdf -- a "fast"-routed doc with a few junk
  pages no longer emits a misleading flag (keeps the shadow vs hot-path
  classification metrics consistent).
- build_ocr_backend warns when an EXPLICIT provider is misconfigured
  (gateway without EMBEDDING_GATEWAY_URL, mistral without MISTRAL_API_KEY)
  instead of silently returning None.
- Pypdfium2FastProcessor.health_check probes the import; documented why
  OcrProcessor.health_check is unconditionally True (lazy per-tenant backends).
- Removed the leftover per-boundary / per-chunk debug logging loops.

Tests: enum normalization + rejection for the two new settings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 02:35:26 +02:00
Chris CoutinhoandClaude Opus 4.8 c48a797896 feat: tiered PDF processor with pypdfium2 fast path (deprecate pymupdf4llm)
Replaces single-engine pymupdf4llm extraction with a tiered pipeline (Deck #205,
follows the tier-0 classifier #855). pypdfium2 becomes the default and only
hot-path PDF extractor; pymupdf4llm is deprecated to a rollback toggle.

Why: pymupdf4llm's O(n^2) find_tables drove the OOM (#852) and the form-PDF
parse timeouts (#856), carries AGPL/commercial licensing liability, and -- per
the benchmarks -- recovers near-zero usable tables on the real corpus. pypdfium2
(Apache/BSD) extracts the same text far faster (Student 1a.pdf: 120s timeout ->
0.2s) with no table-detection bomb.

- document_processors/pypdfium2_fast.py: tier-1 "fast" processor emitting text +
  exact page_boundaries (the pdf_highlighter contract). pymupdf processor is now
  tier "structured" (the rollback engine), registered but not default.
- registry: tiered routing in ProcessorRegistry. tier-1 fast extracts, then
  classification is DERIVED from that text (classifier.classify_from_text -- no
  PDF re-open), records the classification metrics, and escalates scanned /
  no-text-layer docs to the "ocr" tier when document_ocr_enabled (default off;
  no provider yet, so fast is terminal). Wires record_document_escalation + the
  real "escalated" span attribute (was hardcoded False).
- Removes the separate _shadow_classify pass from vector/processor.py -- it
  re-opened every PDF and re-extracted text (~0.5-1.3s/doc of pure duplicated
  CPU that lowered throughput); classification now rides the tier-1 extraction.
- Settings: document_tier1_engine ("pypdfium2" default | "pymupdf" rollback,
  enum-validated), document_ocr_enabled (default false).

Tests: pypdfium2 extractor, registry tiering (fast routing, rollback, classify
recording, OCR escalation on/off), classify_from_text. Full unit suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 01:32:14 +02:00