Repository navigation
fix(multiagent): avoid false empty output on interleaved tool streams - #385
Merged
Ed1s0nZ merged 1 commit intoNov 8, 2026
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.
When an OpenAI-compatible gateway emits text between parallel tool-call deltas, the pinned eino-ext converter can give different content-block types the same
StreamingMeta.Index.ConcatAgenticMessagesthen fails, and ADK's retry inspection discards the concat error and treats the response as empty. Tool cards can appear before the run reportsempty_model_output/model output rejected by ShouldRetry at attempt 1, even though the model produced tool calls.Fixes #285. Related: #286 and cloudwego/eino-ext#975.
This implements the model-boundary workaround on current
main, with stable ordered remapping and fullContentBlockTypediscrimination. Each source-index/type pair keeps one assigned index across continuation chunks; replacement indices cannot collide with later blocks. The wrapper preserves text, tool IDs/arguments, response metadata, and provider-owned input objects. Valid indices, including gaps and out-of-order arrivals, pass through unchanged. It wraps both OpenAI-compatible and native Claude agentic factories, including models built for failover. Generate and the empty-output retry policy remain unchanged.Regression coverage replays four
TaskCreatecalls followed byexecthrough the real HTTP transport, SDK, converter and native ADK retry/tool loop. The unwrapped backend reproduces the rejection; the repaired path executes five stub calls exactly once, forwards their results to the next model turn, and completes without retries. Additional cases cover reasoning/text collisions, continuation grouping, later-index collisions, repeated streams, metadata, immutability and provider errors.Validation:
go test ./internal/multiagent ./internal/openai ./internal/handler -count=1 -timeout=5mgo test -race ./internal/multiagentcovering the new stream regressions, factories and retry policiesgo vet ./internal/multiagentgo build ./...git diff --checkAll passed. Validation uses deterministic SSE replays and stub tools; no live provider session was exercised. The community screenshot alone does not establish that every occurrence of this error has the same cause.