fix: bisect extraction chunks on timeout - #2872
Conversation
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 3 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Adds timeout-driven bisection to _extract_with_adaptive_retry via a new _looks_like_timeout helper that classifies subprocess.TimeoutExpired plus optional SDK timeout classes (openai/anthropic/botocore), so slow chunks split and retry instead of failing outright. Threads the timeout vs. context-overflow distinction through all the retry log messages. Covers the new path with tests for classifier matching, multi-file/single-slice/single-file splits, and the max-depth cap.
Worth a look
- Timeouts are converted into successful empty extraction results —
graphify/llm.py:2164· Escalate · high- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
- Timeout classifier can mask the original exception when an installed SDK lacks APITimeoutError —
graphify/llm.py:1985· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
- Timeout handler logs raw subprocess command arguments —
graphify/llm.py:2164· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 735 functions depend on the 265 functions this change touches.
Health — this change adds coupling hotspots:
- new:
deduplicate_entities()— 63 callers, 21 callees - new:
build_merge()— 46 callers, 14 callees - new:
extract_files_direct()— 17 callers, 20 callees - new:
_call_claude_cli()— 31 callers, 9 callees - new:
extract_corpus_parallel()— 26 callers, 10 callees - new:
dispatch_command()— 2 callers, 117 callees - new:
_call_llm()— 11 callers, 17 callees - new:
_call_openai_compat()— 23 callers, 8 callees - …and 15 more — each is listed as a finding
Verification — 735 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 466 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_extract\_with\_adaptive\_retry.
The verifier did not have enough to check \_extract\_with\_adaptive\_retry, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
· 23 more finding(s) on lines outside this diff (see the check run).
|
Shipped in v0.9.47 via authorship-preserving cherry-pick. Thanks @hopstreax! Release: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.47 |
Summary
Fixes #2866.
claude-cliextraction chunks that exceedGRAPHIFY_API_TIMEOUTwere previously treated as unrecoverable failures. This was especially problematic for dense chunks that could take longer to generate their semantic output and time out before returningfinish_reason="length".This change routes recognized timeout exceptions through the existing adaptive bisection path.
Root Cause
_extract_with_adaptive_retry()already bisects chunks when it encounters:finish_reason == "length"However,
claude-cliusessubprocess.run(..., timeout=...), which raisessubprocess.TimeoutExpiredwhen a chunk exceeds the configured timeout.That exception was not recognized as a bisectable failure, so it propagated and the entire chunk failed without being split.
Changes
_looks_like_timeout()to classify concrete timeout exceptions from supported extraction backends:subprocess.TimeoutExpiredopenai.APITimeoutErroranthropic.APITimeoutErrorbotocore.exceptions.ReadTimeoutErrorbotocore.exceptions.ConnectTimeoutErrormax_depthand unsplittable-file safeguards.FileSlicebisectionmax_depthhandlingVerification
##Also verified with:
No whitespace errors.