Skip to content

Structural dedup: shared chunks-IO, splitter & parser helpers (~190 fewer lines)#7

Open
shaunpatterson wants to merge 6 commits into
ekimetrics:mainfrom
shaunpatterson:refactor/structural-dedup
Open

Structural dedup: shared chunks-IO, splitter & parser helpers (~190 fewer lines)#7
shaunpatterson wants to merge 6 commits into
ekimetrics:mainfrom
shaunpatterson:refactor/structural-dedup

Conversation

@shaunpatterson

Copy link
Copy Markdown
Contributor

Behavior-preserving structural refactors from the code-quality review — each verified against the test suite added in #6.

Stacked on #6 (which is stacked on the four fix PRs #2#5). It assumes those are merged; once they are, this rebases to just the refactor diff.

What changed (each its own commit)

  1. Splitters dedupregex_splitter() and RecursiveSplitter._split_with_separator() were the same algorithm differing only in error handling; both now delegate to one _split_keep_separator(..., on_error=...). The ~20-line overlap-backtracking block duplicated verbatim in _merge_splits / _merge_small_splits is extracted to _build_overlap().

  2. Shared chunks-parquet IOsplit_oversized_chunks_from_df and merge_small_chunks_from_df were ~95% identical (~110 lines each), and split_documents_from_dir carried a third copy of the records + parquet-merge tail. Extracted:

    • build_chunk_records(){doc: {method: [chunks]}} → per-chunk records
    • upsert_parquet() — write with merge-existing-by-method
    • _regularize_chunks_from_df() — the load/groupby/regularize/write orchestrator

    The two public *_from_df functions are now thin wrappers; split_documents_from_dir reuses the same two helpers.

  3. Parser title-end dedup — the identical title end-offset loop copy-pasted into all four parsers (Azure DI / Excel / Docling / PyMuPDF) is now one pure _assign_title_end_offsets() helper, with a direct unit test.

Net effect

src/ diff: +236 / −423 (~190 net lines deleted), no behavior change.

postprocessing.py  307 → 232 stmts
splitters.py       343 → 290 stmts
split_documents.py 135 →  96 stmts

Verification

Full suite: 214 passed, 1 skipped, 91% coverage on the scoped target (unchanged by the refactor). The driver tests exercise the replace_all_results and merge-existing-by-method paths through the new shared helpers.

Deliberately not included

The greedy table-row-splitter appears in three parser methods (AzureDIParser._table_to_markdown, ExcelParser._split_by_rows, DoclingParser._split_table_markdown) but with genuinely different shapes (DataFrame rebuild vs markdown lines vs captions), and those live in SDK-bound classes that can't be executed/tested here. Unifying them has no safety net, so it's left out.

🤖 Generated with Claude Code

@Avo-k

Avo-k commented Jul 6, 2026

Copy link
Copy Markdown
Member

#2#5 are merged. Same as #6: once #6 is rebased and lands, a rebase here should reduce this to just the refactor diff, and we'll review it then.

shaunpatterson and others added 6 commits July 7, 2026 21:06
Adds pure-logic and mockable tests across splitters, postprocessing
(incl. the *_from_df drivers), metrics (fake embedding model, mocked
sentence-transformers, real sklearn), jina_embedder (mocked httpx),
pipeline (fake parser), and the split/metrics/mentions directory drivers
(fake splitters/models, parquet fixtures).

Adds a coverage config scoping the target to the importable library and
excluding paper/ (research replication scripts) and parsing.py (thin
adapters over heavy optional SDKs: Azure DI / Docling / PyMuPDF). On that
scope coverage is 92% (was ~19%); the remaining gap is the spaCy/maverick
coref internals in metrics.py, which need real models to exercise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
split_documents, compute_metrics, postprocessing (*_from_df) and
extract_mentions all read/write .parquet via pandas, but pandas>=2.2 does
not pull a parquet engine automatically, so these functions (and their
tests) fail at runtime without one. Add pyarrow as an explicit dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- regex_splitter() and RecursiveSplitter._split_with_separator() were the
  same algorithm differing only in error handling; both now delegate to a
  single _split_keep_separator(... on_error='raise'|'return_text').
- The ~20-line overlap-backtracking block duplicated verbatim in
  _merge_splits and _merge_small_splits is extracted to _build_overlap().

Behavior-preserving; verified by the splitter test suite (63 tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
split_oversized_chunks_from_df and merge_small_chunks_from_df were ~95%
identical (~110 lines each), and split_documents_from_dir carried a third
copy of the records-building + parquet-merge tail.

Extract:
  - build_chunk_records(): {doc:{method:[chunks]}} -> per-chunk records
  - upsert_parquet(): write with merge-existing-by-method
  - _regularize_chunks_from_df(): the load/groupby/regularize/write orchestrator
The two public *_from_df functions become thin wrappers (regularize fn +
record type), and split_documents_from_dir reuses the same two helpers.

Behavior-preserving; verified by the postprocessing + split_documents
test suites (59 tests, incl. replace_all_results and merge-existing paths).
Net ~120 fewer lines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The identical title end-offset loop was copy-pasted into all four parsers
(Azure DI / Excel / Docling / PyMuPDF). Extract a single pure helper and
call it from each site (dropping unused local vars). Adds a direct unit
test for the helper (parsing.py is otherwise SDK-bound).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shaunpatterson
shaunpatterson force-pushed the refactor/structural-dedup branch from 2353c6b to 7557715 Compare July 8, 2026 01:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants