Skip to content

fix: StreamingResponseAggregator drops/duplicates function calls in streaming mode - #6568

Open
brucearctor wants to merge 1 commit into
google:mainfrom
brucearctor:fix/streaming-aggregator-infinite-loop
Open

fix: StreamingResponseAggregator drops/duplicates function calls in streaming mode#6568
brucearctor wants to merge 1 commit into
google:mainfrom
brucearctor:fix/streaming-aggregator-infinite-loop

Conversation

@brucearctor

Copy link
Copy Markdown
Contributor

Summary

Fixes two bugs in StreamingResponseAggregator that cause incorrect agent behavior when using StreamingMode.SSE with multi-agent workflows (transfer_to_agent + tools).

Fixes #6566

Problem

Bug 1: Progressive SSE — Duplicate function call parts → infinite loop

close() could emit duplicate FunctionCall parts when streaming chunks overlap. This caused the agent loop in base_llm_flow.run_async to re-execute the same tool call indefinitely (57+ LLM calls instead of 18).

Bug 2: Legacy SSE — Function calls silently dropped → premature exit

close() only emitted text parts. FunctionCall parts received during streaming were silently dropped. The agent loop would exit prematurely without executing tools since is_final_response() saw no function calls.

Fix

streaming_utils.py

  1. Progressive path (close()): Deduplicate FunctionCall parts by their id before building the final LlmResponse.

  2. Legacy path:

    • Added _function_call_parts tracking in process_response()
    • Include tracked FC parts in close() output

Tests (test_streaming_utils.py)

  • test_progressive_close_deduplicates_function_calls: Verifies duplicate FC IDs are collapsed
  • test_legacy_close_preserves_function_calls: Verifies FCs survive legacy aggregation
  • Updated test_pure_function_call_behavior_differs_by_mode for new legacy behavior

Reproduction

Minimal repro script in #6566 — uses two agents + transfer_to_agent + tools to trigger the infinite loop.

…treaming mode

Fixes two issues in StreamingResponseAggregator that cause incorrect
agent behavior when using StreamingMode.SSE with multi-agent workflows
(transfer_to_agent + tools):

1. Progressive SSE path: close() could emit duplicate FunctionCall parts
   when streaming chunks overlap, causing the agent loop to re-execute
   the same tool call indefinitely. Fixed by deduplicating FC parts by
   their id in close().

2. Legacy SSE path: close() only emitted text parts — FunctionCall parts
   received during streaming were silently dropped. This caused the agent
   loop to exit prematurely without executing tools. Fixed by tracking
   FC parts in _function_call_parts and including them in close().

Fixes google#6566
@adk-bot adk-bot added the live [Component] This issue is related to live, voice and video chat label Aug 3, 2026
@wuliang229 wuliang229 added core [Component] This issue is related to the core interface and implementation and removed live [Component] This issue is related to live, voice and video chat labels Aug 3, 2026
@wuliang229 wuliang229 removed their assignment Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core [Component] This issue is related to the core interface and implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

StreamingResponseAggregator causes infinite tool-call loop with transfer_to_agent + streaming

3 participants