fix(parser): harden classic chunker against oversized headers#2569
fix(parser): harden classic chunker against oversized headers#2569FraanW wants to merge 1 commit into
Conversation
`split_document` derived the per-chunk step from `max_tokens - len(header_tokens)`. When the header (first three lines) tokenises to >= `max_tokens`, that step went non-positive and negative list slicing produced an oversized first chunk, an empty chunk, and re-chunked / duplicated body content. This also revives the `duplicate_headers` option, which was a no-op past the first chunk because `header_tokens` was reset to `[]` each iteration. - Reserve header space per chunk with a floor of one body token, so the loop always makes forward progress even when the header alone meets or exceeds `max_tokens`. - Include the header on every chunk when `duplicate_headers` is set, and only on the first chunk otherwise (default behaviour unchanged). Adds regression tests for the large-header edge case (token conservation, no empty or corrupted-oversize chunks) and for both `duplicate_headers` modes.
|
@FraanW is attempting to deploy a commit to the Arc53 Team on Vercel. A member of the Team first needs to authorize it. |
| @@ -50,9 +50,16 @@ def split_document(self, doc: Document) -> List[Document]: | |||
| current_position = 0 | |||
| part_index = 0 | |||
| while current_position < len(body_tokens): | |||
There was a problem hiding this comment.
Nice fix. I think one closely related edge case is still worth covering: when the first three newline-terminated lines are the whole document.
There is a small nuance that in that case separate_header_and_body can put all content into header, leaving body_tokens empty. Since split_document only emits chunks inside while current_position < len(body_tokens), a header-only document that is too large for the pass-through path could still return [] and get dropped by classic_chunk.
Could you, please, add a regression test for a long three-line/header-only document, maybe with trailing newline, and make sure the header content is emitted at least once?
What & why
Chunker.split_document(theclassic_chunkstrategy inapplication/parser/chunking.py) derives its per-chunk step frommax_tokens - len(header_tokens). The header is the document's first three lines (separate_header_and_body), and it is prepended to the first chunk so each chunk stays withinmax_tokens.When the header tokenises to
>= max_tokensthat step becomes non-positive, and Python's negative list slicing then silently corrupts the output:token_count == 0),This is reachable in normal operation because
max_tokensandduplicate_headerscome straight from user config (cfg.chunking.*inapplication/worker.py). A small configured chunk size plus a document whose first three lines are long (long title / metadata / front-matter) is enough to trigger it.While fixing this I also noticed
duplicate_headerswas a no-op past the first chunk:header_tokenswas reset to[]at the end of every iteration, so theself.duplicate_headers or part_index == 0condition could never include the header again.Repro
max_tokens=20, a 36-token header, 61 tokens of body:[81, 0, 20, 20, 17][37, 20, 20, 20]duplicate_headers=True, headerTITLE\nAUTHOR\nDATE\n:The fix
split_documentnow mirrors the robustness already present in the sibling strategies inchunking_strategies.py(which clampmax_tokens = max(1, …)and never emit empty pieces):max_tokens.duplicate_headersis set, and only on the first chunk otherwise — default behaviour (duplicate_headers=False) is unchanged and produces byte-identical chunks to before for the normalheader < max_tokenspath.Tests
Added 4 regression tests in
tests/parser/test_chunking.py(TestSplitDocumentHeaderEdgeCases) covering the large-header edge case (token conservation, no empty / corrupted-oversize chunks) and bothduplicate_headersmodes.The two key tests fail on
mainand pass with this change; the full file stays green:The large-header tests fail against the unpatched code, confirming they pin the bug: