Skip to content

fix: per-tool strict opt-out, buffered tool input - #51

Merged
Im-Beast merged 1 commit into
mainfrom
mateusz/anthropic-strict-per-tool
Aug 5, 2026
Merged

fix: per-tool strict opt-out, buffered tool input#51
Im-Beast merged 1 commit into
mainfrom
mateusz/anthropic-strict-per-tool

Conversation

@Im-Beast

@Im-Beast Im-Beast commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

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: false drops the request to 8 and keeps the other 14 strict — something a request-wide strictTools flag could never do. So that flag is gone; strict is the default again.

  • eager_input_streaming is 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 the strict-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_use block 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 via serializeWrappedToolArguments.

  • ./anthropic/utils is now exported so consumers can count their own schemas against the budget instead of finding out they've blown it from a 400 on deployment

The 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, and claude-opus-4-8. They all behave identically.

Limit Value Scope
Strict tools 20 per request
Optional properties 24 per request, summed across all strict tools
Union-typed properties 16 per request, summed across all strict tools
"The compiled grammar is too large" N/A per request
"Schema is too complex for compilation" N/A per tool schema; can fire even below the counted limits

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:

  • Nesting doesn't help. Optionals are counted recursively, 25 of them buried inside one required object still reports 25, and so does an array whose items carry them.
  • .default() is free. Zod puts defaulted fields in required, so they don't count.
  • Non-strict tools do not count into the limits. That's why opting out a single tool can be worth 18 properties.
  • Enums and descriptions don't count either

@Im-Beast
Im-Beast merged commit bcf1b3c into main Aug 5, 2026
1 check passed
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