Skip to content

Conversation

@sarojrout
Copy link
Contributor

Implements real-time event streaming for AgentTool to propagate sub-agent events, addressing issue #3984.

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

2. Or, if no issue exists, describe the change:

If applicable, please follow the issue templates to provide as much detail as
possible.

Problem:
When a coordinator agent delegates to a sub-agent via AgentTool, the sub-agent's execution acts as a "black box". No events are yielded during sub-agent execution, making the frontend appear unresponsive for the duration of sub-agent execution (which can be 20-30 seconds for complex tasks). Only the final result is returned after sub-agent completes, providing no visibility into intermediate steps, tool calls, or responses.

This creates a poor user experience for hierarchical multi-agent systems where users need real-time feedback about sub-agent progress.

Solution:
Implemented event streaming propagation for AgentTool to yield events from sub-agents in real-time to the parent Runner. This enables:

  1. Real-time visibility: Sub-agent events (model responses, tool calls, tool responses) are streamed immediately as they are generated
  2. Progressive feedback: Frontend receives events progressively, not all at once at the end
  3. Better UX: Users can see intermediate steps and progress, maintaining a responsive UI

Implementation Details:

  • Added run_async_with_events() method to AgentTool that yields events from sub-agent execution
  • Created handle_function_calls_async_with_agent_tool_streaming() helper function in functions.py to handle AgentTool event streaming
  • Modified _postprocess_handle_function_calls_async() in base_llm_flow.py to use the new streaming mechanism
  • Maintains backward compatibility - existing run_async() method still works as before

Testing Plan

Please describe the tests that you ran to verify your changes. This is required
for all PRs that are not small documentation or typo fixes.

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Test Results:

tests/unittests/tools/test_agent_tool_event_streaming.py::test_agent_tool_run_async_with_events_yields_sub_agent_events PASSED
tests/unittests/tools/test_agent_tool_event_streaming.py::test_agent_tool_run_async_with_events_forwards_state_delta PASSED
tests/unittests/tools/test_agent_tool_event_streaming.py::test_agent_tool_event_streaming_in_runner PASSED

======================== 3 passed, 9 warnings in 8.07s =========================

Manual End-to-End (E2E) Tests:

Setup:

cd contributing/samples/agent_tool_event_streaming
adk web .

Test Steps:

  1. In the web UI, select agent_tool_event_streaming from the dropdown
  2. Ask: "Research the history of artificial intelligence"
  3. Observe the Events panel in real-time

Expected Behavior:

  • Coordinator agent's function call to research_agent appears immediately
  • Multiple events from research_agent stream progressively (not all at once)
  • Events include intermediate model responses showing step-by-step progress
  • Final function response event appears after all sub-agent events

Screenshots:
Screenshot 2025-12-20 at 11 20 12 PM
Screenshot 2025-12-20 at 11 20 23 PM
Screenshot 2025-12-20 at 11 21 47 PM

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

Additional context

This PR will have merge conflicts with PR #3848 (streaming-tools-non-live) as both modify:

  • src/google/adk/flows/llm_flows/functions.py
  • src/google/adk/flows/llm_flows/base_llm_flow.py

if we are planning to merge this pr first, then i will have to rebase again and fix the conflicts of #3848

Note: These are separate features (PR #3848 handles streaming tools in non-live mode, while this PR handles AgentTool event streaming), but they touch the same code paths.

Sample Code:
A complete sample demonstrating the feature is available at:
contributing/samples/agent_tool_event_streaming/

Key Benefits:

  • Enables real-time visibility into sub-agent execution progress
  • Improves UX for hierarchical multi-agent systems
  • Maintains backward compatibility

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @sarojrout, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to how AgentTool interacts with sub-agents, enabling real-time event propagation. The primary purpose is to transform the previously opaque execution of sub-agents into a transparent process, allowing users to observe the step-by-step progress of complex multi-agent tasks. This change dramatically improves the responsiveness and user experience of applications built with hierarchical agent systems by providing immediate feedback and visibility into intermediate operations.

Highlights

  • Real-time Event Streaming for AgentTool: Implemented a mechanism to stream events from sub-agents wrapped in AgentTool directly to the parent Runner, providing real-time visibility into sub-agent execution.
  • Improved User Experience: Addresses the "black box" problem of sub-agent execution by yielding intermediate events (model responses, tool calls, tool responses), making the frontend responsive and providing progressive feedback for hierarchical multi-agent systems.
  • Core Implementation Changes: Introduced run_async_with_events() in AgentTool, a new helper handle_function_calls_async_with_agent_tool_streaming() in functions.py, and modified _postprocess_handle_function_calls_async() in base_llm_flow.py to integrate the streaming logic.
  • Backward Compatibility & Testing: The existing run_async() method in AgentTool remains functional, and comprehensive unit and manual end-to-end tests have been added to verify the new streaming behavior.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@adk-bot adk-bot added live [Component] This issue is related to live, voice and video chat tools [Component] This issue is related to tools labels Dec 21, 2025
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a valuable feature for event streaming from AgentTool sub-agents, significantly improving the user experience for hierarchical agent systems by providing real-time feedback. The implementation is well-structured, adding a new run_async_with_events method and a corresponding streaming handler while maintaining backward compatibility. The inclusion of new unit tests and a sample application is commendable. I have a couple of suggestions to improve a test case for better validation and to remove a minor code redundancy.

Comment on lines 261 to 263
if not isinstance(tool_result, dict):
tool_result = {'result': tool_result}
agent_tool_results[function_call.id] = tool_result
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic to wrap tool_result in a dictionary is redundant here. The __build_response_event function, which is called later with this result, already handles wrapping non-dictionary results. Removing this logic will make the code cleaner and more consistent with the non-streaming tool execution path.

      agent_tool_results[function_call.id] = tool_result

@sarojrout
Copy link
Contributor Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a valuable feature for real-time event streaming from AgentTool sub-agents, significantly improving the user experience for hierarchical agent systems. The implementation is well-structured and includes thorough testing. My review focuses on opportunities to reduce code duplication and improve readability, which will enhance the long-term maintainability of this new functionality.

Comment on lines 21 to 22
cd contributing/samples
adk web .
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The command to run the demo appears to be incorrect. The PR description suggests running adk web . from within the agent_tool_event_streaming directory. To align with that and ensure the sample runs correctly, this path should be updated.

Suggested change
cd contributing/samples
adk web .
cd contributing/samples/agent_tool_event_streaming
adk web .

Comment on lines 689 to 697
if (
event.content
and event.content.parts
and any(
part.function_response
for part in event.content.parts
if part.function_response
)
):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This condition to check for a function response can be simplified by using the event.get_function_responses() helper method. This will make the code more concise and readable.

      if event.get_function_responses():

Comment on lines 261 to 262
if not isinstance(tool_result, dict):
tool_result = {'result': tool_result}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This check to ensure tool_result is a dictionary is redundant. The __build_response_event function, which is called later with this result, already performs the same check. Removing this duplicated validation will simplify the code.

@sarojrout
Copy link
Contributor Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a valuable feature for real-time event streaming from AgentTool sub-agents, which significantly improves the user experience for hierarchical agent systems by providing visibility into sub-agent execution. The implementation is well-structured, with a new streaming method in AgentTool, a dedicated handler in functions.py, and comprehensive unit tests and a sample application to validate the changes. My review includes a couple of suggestions to enhance maintainability by reducing code duplication and to improve performance by optimizing a list comprehension.

@ryanaiagent ryanaiagent self-assigned this Dec 22, 2025
@ryanaiagent
Copy link
Collaborator

Hi @sarojrout , Thank you for your contribution! We appreciate you taking the time to submit this pull request.
Can you please fix the failing unit tests before we can proceed with the review.

…patibility

Wrap handle_function_calls_async_with_agent_tool_streaming and agent_tool.run_async_with_events with Aclosing to ensure telemetry test passes.
@sarojrout
Copy link
Contributor Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant improvement by enabling real-time event streaming from sub-agents using AgentTool. The changes are well-structured, including the addition of a new run_async_with_events method, a dedicated handler function, and updates to the base LLM flow. The inclusion of comprehensive unit tests and a new sample application is commendable and greatly helps in understanding and verifying the new feature. My review includes a couple of suggestions to enhance performance and adhere to coding style guidelines.

@sarojrout
Copy link
Contributor Author

sarojrout commented Dec 26, 2025

Hi @sarojrout , Thank you for your contribution! We appreciate you taking the time to submit this pull request. Can you please fix the failing unit tests before we can proceed with the review.

pushed the fix @ryanaiagent. can you pls re-run the ci and one suggestion, can we add workflow_dispatch as part of the python-unit-tests.yml, so that every time we push the pr, ci job runs and we can get to know if there is any ci failures because otherwise we cant run the all the test suites in local to match the full CI run as there are other dependencies and this kind of tests are not able to caught due to that. let me know your thoughts.

@sarojrout
Copy link
Contributor Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a valuable feature for event streaming from AgentTool sub-agents, significantly improving real-time visibility and user experience for hierarchical agent systems. The implementation is well-thought-out, with good refactoring to create helper methods and clear separation of new logic. The changes maintain backward compatibility and are accompanied by thorough unit tests. I have a few minor suggestions to improve code clarity and maintainability.

Comment on lines +693 to +697
and any(
part.function_response
for part in event.content.parts
if part.function_response
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The condition to check for a function_response can be simplified. The if part.function_response clause within the generator expression is redundant because any() correctly handles None values by treating them as False.

            and any(
                part.function_response for part in event.content.parts
            )

@sarojrout
Copy link
Contributor Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces real-time event streaming for sub-agents invoked via AgentTool, significantly improving the user experience for hierarchical multi-agent systems by providing immediate feedback on sub-agent progress. The changes involve modifying base_llm_flow.py to use a new async generator function, handle_function_calls_async_with_agent_tool_streaming, which was added to functions.py. This new function differentiates between AgentTool and regular function calls, streaming events from AgentTool sub-agents using a new run_async_with_events method in agent_tool.py. The agent_tool.py file was refactored to extract common setup logic and result building into helper methods, and run_async_with_events was added to yield sub-agent events and forward state deltas. A new sample demo and dedicated unit tests were added to validate this functionality, and existing AgentTool tests were updated to reflect the new streaming behavior. Review comments suggest simplifying a function response check in base_llm_flow.py and extracting duplicated event generation logic in functions.py into a helper function for better maintainability.

Comment on lines +690 to +698
if (
event.content
and event.content.parts
and any(
part.function_response
for part in event.content.parts
if part.function_response
)
):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The condition to check for a function response event can be simplified by using the existing event.get_function_responses() helper method. This improves readability and maintainability by reusing existing logic.

        if event.get_function_responses():

Comment on lines +217 to +233
if not agent_tool_calls:
function_response_event = await handle_function_calls_async(
invocation_context, function_call_event, tools_dict
)
if function_response_event:
auth_event = generate_auth_event(
invocation_context, function_response_event
)
if auth_event:
yield auth_event
tool_confirmation_event = generate_request_confirmation_event(
invocation_context, function_call_event, function_response_event
)
if tool_confirmation_event:
yield tool_confirmation_event
yield function_response_event
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's some code duplication between this block for handling non-AgentTool calls and the block at the end of the function (lines 308-320) that handles AgentTool call responses. Both blocks generate and yield auth_event, tool_confirmation_event, and the final function_response_event.

To improve maintainability and reduce redundancy, consider extracting this common logic into a helper async generator function. This would centralize the event generation and yielding process.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

live [Component] This issue is related to live, voice and video chat tools [Component] This issue is related to tools

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Support Event Streaming propagation from AgentTool (Sub-agents) to Runner

3 participants