From 72a2d4cc18ccb9ff07da471354d972083a114d9d Mon Sep 17 00:00:00 2001 From: dalagadenilesh Date: Sat, 14 Feb 2026 14:37:42 +0530 Subject: [PATCH 1/4] fix: correct include_contents behavior in ADK web (fixes google#3535) --- src/google/adk/flows/llm_flows/contents.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/google/adk/flows/llm_flows/contents.py b/src/google/adk/flows/llm_flows/contents.py index 9b7ef9e121..b623cf9803 100644 --- a/src/google/adk/flows/llm_flows/contents.py +++ b/src/google/adk/flows/llm_flows/contents.py @@ -557,8 +557,9 @@ def _get_current_turn_contents( # Find the latest event that starts the current turn and process from there for i in range(len(events) - 1, -1, -1): event = events[i] - if _should_include_event_in_context(current_branch, event) and ( - event.author == 'user' or _is_other_agent_reply(agent_name, event) + if (_should_include_event_in_context(current_branch, event) + and (event.author == "user" or _is_other_agent_reply(agent_name, event)) + and not _is_direct_transfer(event) ): return _get_contents( current_branch, @@ -570,6 +571,21 @@ def _get_current_turn_contents( return [] +def _is_direct_transfer(event : Event) -> bool: + "Check whether the event is a direct transfer event." + + return bool( + event.actions.transfer_to_agent + or ( + event.content.parts + and any( + p.function_call and p.function_call.name == 'transfer_to_agent' + for p in event.content.parts + ) + ) + ) + + def _is_other_agent_reply(current_agent_name: str, event: Event) -> bool: """Whether the event is a reply from another agent.""" return bool( From ef09566e093ef83f032ec71d9e00f92fbf7f7eb0 Mon Sep 17 00:00:00 2001 From: dalagadenilesh Date: Sat, 14 Feb 2026 15:07:24 +0530 Subject: [PATCH 2/4] fix: Add event.content to #4492 --- src/google/adk/flows/llm_flows/contents.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/google/adk/flows/llm_flows/contents.py b/src/google/adk/flows/llm_flows/contents.py index b623cf9803..9037a37789 100644 --- a/src/google/adk/flows/llm_flows/contents.py +++ b/src/google/adk/flows/llm_flows/contents.py @@ -577,7 +577,8 @@ def _is_direct_transfer(event : Event) -> bool: return bool( event.actions.transfer_to_agent or ( - event.content.parts + event.content + and event.content.parts and any( p.function_call and p.function_call.name == 'transfer_to_agent' for p in event.content.parts From f1cd2d5de8a7def7c9e32318c100e51b28bb4f1f Mon Sep 17 00:00:00 2001 From: dalagadenilesh Date: Thu, 19 Feb 2026 22:09:34 +0530 Subject: [PATCH 3/4] Adding Unittest --- .../flows/llm_flows/test_contents.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/unittests/flows/llm_flows/test_contents.py b/tests/unittests/flows/llm_flows/test_contents.py index b762068d31..2771ffeb80 100644 --- a/tests/unittests/flows/llm_flows/test_contents.py +++ b/tests/unittests/flows/llm_flows/test_contents.py @@ -250,6 +250,57 @@ async def test_include_contents_none_multi_branch_current_turn(): types.Part(text="[sibling_agent] said: Sibling agent response"), ] +@pytest.mark.asyncio +async def test_events_with_transfer_to_agent_are_include(): + """Test that events with transfer_to_agent are included when include_contents='none'""" + agent = Agent(model="gemini-2.5-flash", name="test_agent", include_contents='none') + llm_request = LlmRequest(model="gemini-2.5-flash") + invocation_context = await testing_utils.create_invocation_context( + agent=agent + ) + + events = [ + Event( + invocation_id="inv1", + author="user", + content=types.UserContent("First user message"), + ), + + Event( + invocation_id='inv1', + author='parent', + content=types.Content(parts=[types.Part(function_call=types.FunctionCall(args={'agent_name': 'test_agent'}, + id='call_inv1', + name='transfer_to_agent'))], role='model'), + ), + + Event( + invocation_id='inv1', + author='parent', + content=types.Content(parts=[types.Part(function_response=types.FunctionResponse(id='call_inv1', + name='transfer_to_agent', + response={'result': None}), + ), + ], + role='user' + ), + actions=EventActions(transfer_to_agent='test_agent') + ) + ] + + invocation_context.session.events = events + async for _ in contents.request_processor.run_async( + invocation_context, llm_request + ): + pass + + assert llm_request.contents == [ + types.UserContent('First user message'), + types.Content(parts=[types.Part(text='For context:'), + types.Part(text="[parent] called tool `transfer_to_agent` with parameters: {'agent_name': 'test_agent'}"),], role='user'), + types.Content(parts=[types.Part(text='For context:'), + types.Part(text="[parent] `transfer_to_agent` tool returned result: {'result': None}"),], role='user') + ] @pytest.mark.asyncio async def test_authentication_events_are_filtered(): From fb0b0770eaf863952132dd972dfa2a8a3c3d7aca Mon Sep 17 00:00:00 2001 From: dalagadenilesh Date: Thu, 19 Feb 2026 22:27:18 +0530 Subject: [PATCH 4/4] style: apply autoformat.sh --- src/google/adk/flows/llm_flows/contents.py | 27 +++--- .../flows/llm_flows/test_contents.py | 94 +++++++++++++------ 2 files changed, 79 insertions(+), 42 deletions(-) diff --git a/src/google/adk/flows/llm_flows/contents.py b/src/google/adk/flows/llm_flows/contents.py index 9037a37789..b41dfebe82 100644 --- a/src/google/adk/flows/llm_flows/contents.py +++ b/src/google/adk/flows/llm_flows/contents.py @@ -557,8 +557,9 @@ def _get_current_turn_contents( # Find the latest event that starts the current turn and process from there for i in range(len(events) - 1, -1, -1): event = events[i] - if (_should_include_event_in_context(current_branch, event) - and (event.author == "user" or _is_other_agent_reply(agent_name, event)) + if ( + _should_include_event_in_context(current_branch, event) + and (event.author == 'user' or _is_other_agent_reply(agent_name, event)) and not _is_direct_transfer(event) ): return _get_contents( @@ -571,19 +572,19 @@ def _get_current_turn_contents( return [] -def _is_direct_transfer(event : Event) -> bool: - "Check whether the event is a direct transfer event." - +def _is_direct_transfer(event: Event) -> bool: + 'Check whether the event is a direct transfer event.' + return bool( - event.actions.transfer_to_agent - or ( - event.content - and event.content.parts - and any( - p.function_call and p.function_call.name == 'transfer_to_agent' - for p in event.content.parts + event.actions.transfer_to_agent + or ( + event.content + and event.content.parts + and any( + p.function_call and p.function_call.name == 'transfer_to_agent' + for p in event.content.parts + ) ) - ) ) diff --git a/tests/unittests/flows/llm_flows/test_contents.py b/tests/unittests/flows/llm_flows/test_contents.py index 2771ffeb80..57272858ff 100644 --- a/tests/unittests/flows/llm_flows/test_contents.py +++ b/tests/unittests/flows/llm_flows/test_contents.py @@ -250,10 +250,13 @@ async def test_include_contents_none_multi_branch_current_turn(): types.Part(text="[sibling_agent] said: Sibling agent response"), ] + @pytest.mark.asyncio async def test_events_with_transfer_to_agent_are_include(): """Test that events with transfer_to_agent are included when include_contents='none'""" - agent = Agent(model="gemini-2.5-flash", name="test_agent", include_contents='none') + agent = Agent( + model="gemini-2.5-flash", name="test_agent", include_contents="none" + ) llm_request = LlmRequest(model="gemini-2.5-flash") invocation_context = await testing_utils.create_invocation_context( agent=agent @@ -265,29 +268,41 @@ async def test_events_with_transfer_to_agent_are_include(): author="user", content=types.UserContent("First user message"), ), - Event( - invocation_id='inv1', - author='parent', - content=types.Content(parts=[types.Part(function_call=types.FunctionCall(args={'agent_name': 'test_agent'}, - id='call_inv1', - name='transfer_to_agent'))], role='model'), - ), - - Event( - invocation_id='inv1', - author='parent', - content=types.Content(parts=[types.Part(function_response=types.FunctionResponse(id='call_inv1', - name='transfer_to_agent', - response={'result': None}), - ), - ], - role='user' - ), - actions=EventActions(transfer_to_agent='test_agent') - ) - ] - + invocation_id="inv1", + author="parent", + content=types.Content( + parts=[ + types.Part( + function_call=types.FunctionCall( + args={"agent_name": "test_agent"}, + id="call_inv1", + name="transfer_to_agent", + ) + ) + ], + role="model", + ), + ), + Event( + invocation_id="inv1", + author="parent", + content=types.Content( + parts=[ + types.Part( + function_response=types.FunctionResponse( + id="call_inv1", + name="transfer_to_agent", + response={"result": None}, + ), + ), + ], + role="user", + ), + actions=EventActions(transfer_to_agent="test_agent"), + ), + ] + invocation_context.session.events = events async for _ in contents.request_processor.run_async( invocation_context, llm_request @@ -295,12 +310,33 @@ async def test_events_with_transfer_to_agent_are_include(): pass assert llm_request.contents == [ - types.UserContent('First user message'), - types.Content(parts=[types.Part(text='For context:'), - types.Part(text="[parent] called tool `transfer_to_agent` with parameters: {'agent_name': 'test_agent'}"),], role='user'), - types.Content(parts=[types.Part(text='For context:'), - types.Part(text="[parent] `transfer_to_agent` tool returned result: {'result': None}"),], role='user') - ] + types.UserContent("First user message"), + types.Content( + parts=[ + types.Part(text="For context:"), + types.Part( + text=( + "[parent] called tool `transfer_to_agent` with" + " parameters: {'agent_name': 'test_agent'}" + ) + ), + ], + role="user", + ), + types.Content( + parts=[ + types.Part(text="For context:"), + types.Part( + text=( + "[parent] `transfer_to_agent` tool returned result:" + " {'result': None}" + ) + ), + ], + role="user", + ), + ] + @pytest.mark.asyncio async def test_authentication_events_are_filtered():