Skip to content

fix(llm, cli, anthropic): Bound provider rebuild loop and heal turns - #920

Merged
JeanMertz merged 4 commits into
mainfrom
invalid_request_error
Jul 31, 2026
Merged

fix(llm, cli, anthropic): Bound provider rebuild loop and heal turns#920
JeanMertz merged 4 commits into
mainfrom
invalid_request_error

Conversation

@JeanMertz

Copy link
Copy Markdown
Collaborator

Anthropic rejects a thinking block whenever any thinking or redacted-thinking block in the latest assistant message was rewritten, not only when a signature goes stale. The repair now recognizes both rejection messages and, when the offending block sits in the latest assistant turn, downgrades every thinking block in that message in one patch set instead of one block per round. Anthropic requires that message's thinking blocks to come back unchanged as a whole, so patching one block at a time left the message half-rewritten and the next request failed the same way. A conversation already left in that half-rewritten state is now healed by the same patch. Earlier turns keep their narrower, single-block repair since they carry no such constraint.

A provider that keeps asking to rebuild the request without actually fixing anything could previously loop forever, resending the full conversation each time. jp query now refuses a rebuild unless the preceding patch changed the stream in a way guaranteed to make progress (PatchAction::shrinks_stream), and caps consecutive rebuilds within a turn at 10. Hitting either limit aborts the turn with an error instead of looping, after flushing whatever was already streamed so no partial response is lost. The counter resets once a turn streams a successful event, so later, unrelated repairs are not penalized by an earlier one.

Anthropic rejects a thinking block whenever any thinking or
redacted-thinking block in the latest assistant message was rewritten,
not only when a signature goes stale. The repair now recognizes both
rejection messages and, when the offending block sits in the latest
assistant turn, downgrades every thinking block in that message in one
patch set instead of one block per round. Anthropic requires that
message's thinking blocks to come back unchanged as a whole, so
patching one block at a time left the message half-rewritten and the
next request failed the same way. A conversation already left in that
half-rewritten state is now healed by the same patch. Earlier turns
keep their narrower, single-block repair since they carry no such
constraint.

A provider that keeps asking to rebuild the request without actually
fixing anything could previously loop forever, resending the full
conversation each time. `jp query` now refuses a rebuild unless the
preceding patch changed the stream in a way guaranteed to make
progress (`PatchAction::shrinks_stream`), and caps consecutive
rebuilds within a turn at 10. Hitting either limit aborts the turn
with an error instead of looping, after flushing whatever was already
streamed so no partial response is lost. The counter resets once a
turn streams a successful event, so later, unrelated repairs are not
penalized by an earlier one.

Signed-off-by: Jean Mertz <git@jeanmertz.com>
Signed-off-by: Jean Mertz <git@jeanmertz.com>
Signed-off-by: Jean Mertz <git@jeanmertz.com>
Signed-off-by: Jean Mertz <git@jeanmertz.com>
@JeanMertz
JeanMertz merged commit bef6992 into main Jul 31, 2026
16 checks passed
@JeanMertz
JeanMertz deleted the invalid_request_error branch July 31, 2026 12:39
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.

1 participant