Repository navigation
Validate streamed tool calls before execution - #73
Merged
Merged
Conversation
zhanghanduo
force-pushed
the
codex/streamed-tool-validation
branch
2 times, most recently
from
October 7, 2026 09:32
1c0fecb to
79a8c41
Compare
zhanghanduo
force-pushed
the
codex/streamed-tool-validation
branch
from
October 7, 2026 09:37
79a8c41 to
8be5e34
Compare
- Add LoopConfig.tool_argument_validation ("structural" default, "strict",
"off"). Structural checks JSON object shape and top-level required
properties only, so tool-side type coercion keeps working.
- Treat blank/whitespace arguments as {} so zero-argument tools (Anthropic
streaming encodes them as "") run without a wasted retry.
- Block only provider-native calls at execution; text-mode calls are never
rejected by schema validation. Drop the private parser keys that leaked
to observers and derive diagnostics from response.tool_calls instead.
- Skip validation for tools whose schema is not valid JSON Schema instead
of raising; cache compiled validators.
- Generate ids for named native calls that lack one instead of failing the
LLM call; nameless calls keep their dropped-call handling.
- Keep the legacy empty-arguments trigger and its attempt reasons
(stream_empty_tool_arguments / stream_empty_args_replay) as aliases.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Summary
LoopConfig.stream_transportandcall_llm(stream=...)so a complete response can use streaming transport without a delta observer. Existing automatic selection andstream_llm_tokensremain compatible.How tool calls are checked
LoopConfig.tool_argument_validationselects the level. It is keyword-only, and the shared text-mode parsers are untouched."structural"(default)requiredproperty{}, and property types are left to the tool"strict""off"Property types are deliberately not enforced in the default mode: tools coerce values themselves, and text-mode parameters are JSON-decoded (a
600519parameter arrives as an int), so a strict type check would reject calls that work today. A tool schema that is not itself valid JSON Schema never fails a call — validation is skipped for that tool with a warning.At execution time, only provider-native calls are checked. Text-mode calls keep their lenient handling, and an observer's
rewrite_argsalways wins over validation.Recovery and diagnostics
stream_empty_tool_argumentsattempt reason (andstream_empty_args_replaywhen the retry fails), plus thestream_empty_args_*response metadata keys. Other invalid calls usestream_invalid_tool_call. The retry'srecovery_actionisretry_streaming.invalid_tool_callsdiagnostics, raw arguments included, and the attempt isaccepted_degraded.Compatibility
stream_transportis keyword-only, so positionalLoopConfigconstruction is unaffected, and leaving it unset retains the legacy selection. Existingstream_empty_args_*response metadata remains available for calls matching the prior condition.Two behaviour changes are intentional and downstream-facing: the retry uses streaming rather than a non-streaming replay, and an invalid call that survives the retry is returned as
accepted_degradedwith an error tool result rather than executing. Consumers asserting the old non-streaming replay or its event names must update those assertions when they adopt this release.Verification
2306 passed, 2 skipped.git diff --checkpass on the changed runtime code.