fix(cache): don't promote truncated LLM chunks to the semantic cache as complete#1950
Closed
TPAteeq wants to merge 1 commit into
Closed
fix(cache): don't promote truncated LLM chunks to the semantic cache as complete#1950TPAteeq wants to merge 1 commit into
TPAteeq wants to merge 1 commit into
Conversation
…as complete A chunk whose LLM response is truncated (`finish_reason="length"`) and can't be recovered by splitting, or that hits the adaptive-retry depth cap, returns a partial node set. Today that set is checkpointed and written to the content-hash semantic cache + manifest-stamped as complete, so the incomplete nodes are served forever until the file content changes or `--force`. Truncated give-up results are now tagged with an internal `_partial` marker. `save_semantic_cache` stamps the affected file's entry `partial: True` (detected from the marker or an explicit `partial_source_files` arg), and `load_cached` treats a partial entry as a cache MISS, so the file re-dispatches and retries. The file is also left unstamped in the manifest (like a failed chunk, Graphify-Labs#933) so detect_incremental re-queues it on the next incremental run — not only on a full / `--force` / content-change run. A file sliced across chunks accumulates via a partial-aware `merge_existing` peek (`load_cached(allow_partial=True)`) so a truncated slice is never dropped or silently promoted to complete. Self-heals: a later complete extraction overwrites the same key with a non-partial entry. The marker is stripped after the final save so it never leaks into graph.json.
safishamsi
added a commit
that referenced
this pull request
Jul 16, 2026
…#1950) The _partial item-marker approach couldn't fire on the most common truncation shape: a mid-JSON cut parses to zero items, so a sliced document whose second slice truncated empty was still stamped complete (the PR's own headline case). - The adaptive-retry give-up sites now record the chunk's own source files in a result-level _partial_files list, independent of parsed items; it propagates through _merge_two / the recursion merges / _merge_into so it reaches both the per-chunk checkpoint and the run-level manifest stamp. - _partial_source_files unions _partial_files with the item markers. - save_semantic_cache seeds an empty group for a named partial file with no items so its entry is stamped partial, and carries a partial prev entry's flag forward so a later clean slice merging over it can't re-promote it to complete. - the CLI final save now passes partial_source_files (computed before the save) so an empty-parse file isn't written back as a complete entry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collaborator
|
Landed on v8 (ships in 0.9.18), cherry-picked to preserve your authorship. Thanks @TPAteeq — truncated chunks are no longer cached/stamped as complete. Follow-up added for the empty-parse case: a mid-JSON cut parses to zero items, so the give-up sites now record the chunk own files independently of parsed items (propagated through the merges), the cache stamps a partial file even with no items, and a later clean slice cannot re-promote a partial entry. That closes the sliced-document scenario end to end. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When an LLM chunk is truncated (
finish_reason="length") and can't be recovered by splitting — or the adaptive-retry recursion hits its depth cap — the extractor keeps the partial node set it managed to parse. That partial result was then checkpointed and written to the content-hash semantic cache and manifest-stamped as a complete extraction. Because the cache is keyed by file content, the incomplete node set is served on every subsequent run forever — until the file's content changes or the user passes--force. Half a document's symbols silently masquerade as the whole, with no persisted signal that anything was missing.Fix
A truncated give-up result is tagged with an internal
_partialmarker on each of its items.save_semantic_cachestamps the affected file's cache entrypartial: True(detected either from that marker or from an explicitpartial_source_filesargument), andload_cachedtreats a partial entry as a cache miss, so the file is re-dispatched and retried instead of replayed. To make that re-dispatch actually happen on the common path, the partial file is also left unstamped in the manifest — mirroring the existing failed-chunk handling (#933) — sodetect_incrementalre-queues it on the next incrementalupdate/extract, not only on a full/--force/content-change run.A file split into slices across chunks is handled correctly: the
merge_existingcheckpoint peeks at a partial previous entry (load_cached(allow_partial=True)) so it accumulates all slices and keeps the entry partial, rather than a later clean slice silently dropping a truncated one and promoting the half-file to complete. The whole thing self-heals — a later complete extraction overwrites the same content-hash key with a non-partial entry — and the internal marker is stripped after the final save so it never leaks intograph.json.Tests
tests/test_partial_cache.pyand additions totests/test_chunking.py: partial entry is a load miss; explicitpartial_source_files; self-heal on complete re-extraction; sliced-file accumulation stays partial without losing a slice; manifest excludes partial files; the marker helpers; and an end-to-end truncatedextract_corpus_parallelcheckpoint that reloads as a miss. Full test suite green (3297 passed).