Skip to content

feat(llm-reach): the in-pass split-and-retry for dropped batches — a malformed batch re-issued once as two halves, the recovery carrying its own provenance (#558) - #575

Open
gadievron wants to merge 1 commit into
masterfrom
fix/issue558-split-retry
Open

Conversation

@gadievron

Copy link
Copy Markdown
Collaborator

Summary

The post-lift measurement (this issue's step 1, run 2026-09-09, receipts in the issue comment): 4 malformed batches in ~95 at the DEFAULT_MAX_TOKENS cap — all the end_turn broken-JSON finish class, 0 max_tokens — the cap lift eliminated the truncation-at-cap class; the residual is the model's own structurally-broken finishes, a fresh-roll class. The close-out rule failed → the in-pass recovery proceeds.

The implementation (this issue's direction 3 — split-and-retry, never the JSON corrector: it cannot recover signals the model never emitted, and on the truncation class it would freeze partial batches as reviewed, defeating absence-as-retry):

  • a dropped or truncated batch of ≥2 units re-issues once as two halves — smaller outputs (less likely to break mid-structure) and a fresh roll; bounded to one split level, no recursion — a still-dropped half stays dropped (its units re-run on resume via absence-as-retry);
  • the recovery carries its own provenance (direction 4): batches_split_recovered/batches_split_lost — distinct counters, surfaced in the step summary; the original drop's counters are revised by the attempt's exact applied deltas (the adversarial review's catch: an inferred-shape subtraction drove batches_truncated negative on the max_tokens+prose shape);
  • the usage window spans the whole recovery (the review's second catch: per-attempt tracking reset left the records carrying only the last half's spend — the window now starts once per original batch, the recovered units' records carrying the original + halves' true cost);
  • the recovered halves' units persist checkpoint records; the still-dropped leave none. A provider-exception batch (failed) is deliberately not split — not output-size-shaped; the llm-reachability: provider-exception batches are never counted in batches_dropped/units_not_reviewed — the step report can read success/0/0 for a pass that reviewed nothing #541 counters + the resume own it.

Closes #558.

Test plan

7 new tests (the #558 class): full recovery via halves (the counters revised to 0, the provenance counted); the recovered units persist; a 1-unit batch never splits; the mixed half-recovered/half-lost shape (the coverage truth exact); the max_tokens+prose no-brace shape never drives a counter negative (the deltas pin); the usage spans the recovery; plus the 3 old-behavior pins updated (a still-dropped batch needs the halves to also fail).

Verification evidence

check command result
new + family pytest tests/test_issue532_llr_resume.py tests/test_issue294_reach_batch_diagnostics.py tests/test_llm_reachability.py -q 74 passed
full suite pytest tests/ -q 4022 passed, 2 failed — both pre-existing (SDK pin drift)
static analysis ruff / Semgrep clean / 0
adversarial review combined wave+refute 5 findings, all folded: the negative-counter catch (the deltas mechanism), the usage-window catch, the provenance surfacing, the recovery stderr line, the failed-class note
the measurement the issue comment's receipts (4 malformed in ~95 at the 20000 cap, 0 max_tokens) the decision rule's answer, posted

…malformed batch re-issued once as two halves, the recovery carrying its own provenance (#558)

The post-lift measurement (the issue's step 1, run 2026-09-09): 4
malformed batches in ~95 at the DEFAULT_MAX_TOKENS cap — ALL the
end_turn broken-JSON finish class (0 max_tokens; the cap lift
eliminated the truncation-at-cap class). The close-out rule failed:
the residual is the model's own structurally-broken finishes, a
fresh-roll class — re-generation usually recovers.

The implementation (the issue's direction 3 — split-and-retry, NEVER
the JSON corrector: it cannot recover signals the model never emitted,
and on the truncation class it would freeze partial batches as
reviewed, defeating absence-as-retry):
- a dropped or truncated batch of >= 2 units re-issues ONCE as two
  halves — smaller outputs (less likely to exhaust a cap or break
  mid-structure) AND a fresh roll. Bounded: one split level, no
  recursion; a still-dropped half stays dropped (its units re-run on
  the next resume via absence-as-retry);
- the ORIGINAL drop's counters are REVISED (subtracted) so the halves
  count their own outcomes — the coverage truth never double-counted;
- the recovery's own provenance counters (batches_split_recovered /
  batches_split_lost, direction 4 — a recovered batch never silently
  overwrites the units_not_reviewed count);
- the recovered halves' units persist checkpoint records (they were
  reviewed this pass); the still-dropped leave none.

The batch loop is restructured into a _attempt() helper (call+parse+
counters per sub-batch) — the same behavior for the non-split paths,
with the split at the loop level.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant