Skip to content

Commit 0d5752b

Browse files
google-genai-botcopybara-github
authored andcommitted
fix(tools): run AgentTool nested agent in unary mode
`AgentTool.run_async` does not forward the nested run's events to the caller; it only uses the last event's content as the tool response. That content is complete in unary mode and in aggregated streaming, but a caller streaming without aggregation would leave only a partial chunk in the last event. Since the nested events are never surfaced anyway, always run the wrapped agent under `StreamingMode.NONE` (via a per-call RunConfig copy that leaves the caller's config untouched). PiperOrigin-RevId: 970844321
1 parent 97c2af0 commit 0d5752b

2 files changed

Lines changed: 42 additions & 0 deletions

File tree

‎src/google/adk/tools/agent_tool.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from typing_extensions import override
2727

2828
from . import _automatic_function_calling_util
29+
from ..agents._streaming_mode import StreamingMode
2930
from ..agents.common_configs import AgentRefConfig
3031
from ..events._branch_path import _BranchPath
3132
from ..features import FeatureName
@@ -301,6 +302,17 @@ async def run_async(
301302
nested_run_config = nested_run_config.model_copy(
302303
update={'support_cfc': False}
303304
)
305+
if (
306+
nested_run_config is not None
307+
and nested_run_config.streaming_mode != StreamingMode.NONE
308+
):
309+
# The nested run's events are not forwarded to the caller; only the last
310+
# event's content becomes the response. That is complete in unary mode and
311+
# in aggregated streaming, but a caller streaming without aggregation
312+
# would leave only a partial chunk in the last event, so always run unary.
313+
nested_run_config = nested_run_config.model_copy(
314+
update={'streaming_mode': StreamingMode.NONE}
315+
)
304316

305317
last_content = None
306318
last_error_message = None

‎tests/unittests/tools/test_agent_tool.py‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from google.adk.agents.llm_agent import Agent
2525
from google.adk.agents.llm_agent import LlmAgent
2626
from google.adk.agents.run_config import RunConfig
27+
from google.adk.agents.run_config import StreamingMode
2728
from google.adk.agents.sequential_agent import SequentialAgent
2829
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService
2930
from google.adk.events.event import Event
@@ -291,6 +292,35 @@ async def test_agent_tool_does_not_forward_support_cfc(monkeypatch):
291292
assert parent_run_config.support_cfc is True
292293

293294

295+
@mark.asyncio
296+
async def test_agent_tool_forces_unary_nested_run(monkeypatch):
297+
"""The response is read from the last event, so the nested run must be unary."""
298+
parent_run_config = RunConfig(
299+
streaming_mode=StreamingMode.SSE, max_llm_calls=7
300+
)
301+
302+
nested_run_config = await _capture_nested_run_config(
303+
monkeypatch, parent_run_config
304+
)
305+
306+
assert nested_run_config.streaming_mode == StreamingMode.NONE
307+
# Other settings are still forwarded, and the caller is left untouched.
308+
assert nested_run_config.max_llm_calls == 7
309+
assert parent_run_config.streaming_mode == StreamingMode.SSE
310+
311+
312+
@mark.asyncio
313+
async def test_agent_tool_keeps_unary_run_unchanged(monkeypatch):
314+
"""A caller already running unary is forwarded unchanged."""
315+
parent_run_config = RunConfig(streaming_mode=StreamingMode.NONE)
316+
317+
nested_run_config = await _capture_nested_run_config(
318+
monkeypatch, parent_run_config
319+
)
320+
321+
assert nested_run_config.streaming_mode == StreamingMode.NONE
322+
323+
294324
def test_no_schema():
295325
mock_model = testing_utils.MockModel.create(
296326
responses=[

0 commit comments

Comments
 (0)