fix(tools): defensively recover concatenated streamed tool arguments#66
Open
walcz-de wants to merge 1 commit into
Open
fix(tools): defensively recover concatenated streamed tool arguments#66walcz-de wants to merge 1 commit into
walcz-de wants to merge 1 commit into
Conversation
When streamed tool-call argument deltas are accumulated into Function.Arguments, some
providers (e.g. an OpenAI-compat proxy that maps Anthropic/Gemini tool-call streaming into
OpenAI deltas) re-emit the full arguments object, so the accumulated string becomes a
concatenation like {"a":1}{"a":1}. A plain json.Unmarshal then fails with
'invalid character { after top-level value' and the tool loop cannot proceed — agentic
tool calling through such providers is unusable, while local backends with correct
incremental deltas are unaffected.
Recover the FIRST complete top-level JSON object via json.Decoder on that specific failure;
the happy path keeps the direct Unmarshal. Adds parseStreamedToolArgs + a regression test
(plain / concatenated-duplicate / concatenated-different / malformed) and routes both the
streaming and non-streaming parse sites through it.
Signed-off-by: stefanwalcz <stefan.walcz@walcz.de>
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.
Problem
When streamed tool-call argument deltas are accumulated into
Function.Arguments, some providers re-emit the full arguments object rather than incremental fragments — most notably an OpenAI-compatible proxy that maps Anthropic/Gemini tool-call streaming into OpenAIdelta.tool_calls. The accumulated string then becomes a concatenation, e.g.{"a":1}{"a":1}.json.Unmarshalrejects that withinvalid character '{' after top-level value, sodecisionWithStreaming/ the non-streaming path setallParsed = falseand the tool loop cannot proceed. Result: agentic tool-calling is unusable through such providers, while local backends that emit correct incremental deltas are unaffected.Fix
Add
parseStreamedToolArgswhich keeps the directjson.Unmarshalfast path and, only on failure, recovers the first complete top-level JSON object viajson.Decoder. Both parse sites (streaming + non-streaming) route through it. Truly malformed input still returns an error (no silent garbage).Test
parse_defensive_test.gocovers: plain-valid, concatenated-duplicate, concatenated-different (first wins), malformed (errors).Signed-off-by included (DCO).