fix: per-tool strict opt-out, buffered tool input - #51
Merged
Conversation
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.
Anthropic gives you one decoding grammar budget across all strict tools in a request. When alphaXiv's toolset blew past those limits, #49 had to turn strict off for everything. Turns out the budget gets eaten by a few fat schemas, not by tool count:
alphaXiv's 15-tool paper-page request has 26 optional properties against a limit of 24, and a single tool accounts for 18 of them. Tagging just that one tool with
anthropicStrict: falsedrops the request to 8 and keeps the other 14 strict — something a request-widestrictToolsflag could never do. So that flag is gone; strict is the default again.eager_input_streamingis now off. It skipped server-side argument validation, and we didn't read a partial block before it closed, so the only thing it bought was theoretical latency savings we weren't using. What it did cost was additional validation. That's where the "Unable to parse tool parameter JSON" failures came from later on thestrict-disabled tools.Malformed arguments no longer kill the run. They get forwarded to the agent, which rejects them and sends that rejection back as a tool result the model can retry from.
A turn whose arguments never parsed now gets replayed as a
tool_useblock wrapping the raw string Anthropic couldn't parse. Before this, it was replayed as-is, which made every subsequent turn unsendable. The run would die one turn after the error the model was supposed to recover from. The other three adapters already handled this viaserializeWrappedToolArguments../anthropic/utilsis now exported so consumers can count their own schemas against the budget instead of finding out they've blown it from a 400 on deploymentThe budget
None of this information seems to be documented by Anthropic.
These numbers come from manual testing, done against
claude-sonnet-4-6,claude-sonnet-5,claude-opus-5, andclaude-opus-4-8. They all behave identically.The two N/A ceilings couldn't be pinned to a number. They shift with schema shape rather than size, and several probes near them just timed out instead of giving a clear answer.
What counts toward the optional budget:
.default()is free. Zod puts defaulted fields inrequired, so they don't count.