diff --git a/src/google/adk/flows/llm_flows/agent_transfer.py b/src/google/adk/flows/llm_flows/agent_transfer.py index f4c68671db..050f2ae8a3 100644 --- a/src/google/adk/flows/llm_flows/agent_transfer.py +++ b/src/google/adk/flows/llm_flows/agent_transfer.py @@ -193,7 +193,10 @@ def _get_transfer_targets(agent: LlmAgent) -> list[BaseAgent]: peer_agent for peer_agent in agent.parent_agent.sub_agents if peer_agent.name != agent.name - and peer_agent.mode not in ('single_turn', 'task') + and ( + not hasattr(peer_agent, 'mode') + or peer_agent.mode not in ('single_turn', 'task') + ) ]) return result diff --git a/tests/unittests/flows/llm_flows/test_get_transfer_targets.py b/tests/unittests/flows/llm_flows/test_get_transfer_targets.py new file mode 100644 index 0000000000..8ba08d4d9a --- /dev/null +++ b/tests/unittests/flows/llm_flows/test_get_transfer_targets.py @@ -0,0 +1,63 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for _get_transfer_targets in agent_transfer module.""" + +from google.adk.agents.llm_agent import Agent +from google.adk.agents.loop_agent import LoopAgent +from google.adk.flows.llm_flows.agent_transfer import _get_transfer_targets + +from ... import testing_utils + + +def test_loop_agent_peer_does_not_raise_attribute_error(): + """LoopAgent has no 'mode' attribute; _get_transfer_targets must not crash. + + Regression test for https://github.com/google/adk-python/issues/5863. + """ + mock_model = testing_utils.MockModel.create(responses=['response']) + + loop_peer = LoopAgent(name='loop_peer', sub_agents=[]) + llm_agent = Agent(name='llm_agent', model=mock_model) + + root = Agent( + name='root', + model=mock_model, + sub_agents=[llm_agent, loop_peer], + ) + + # Before the fix this raised: + # AttributeError: 'LoopAgent' object has no attribute 'mode' + targets = _get_transfer_targets(llm_agent) + + target_names = [t.name for t in targets] + # loop_peer should be included as a valid peer transfer target + assert 'loop_peer' in target_names + + +def test_loop_agent_sub_agent_does_not_raise_attribute_error(): + """LoopAgent as a sub-agent of the current agent should also not crash.""" + mock_model = testing_utils.MockModel.create(responses=['response']) + + loop_sub = LoopAgent(name='loop_sub', sub_agents=[]) + llm_agent = Agent( + name='llm_agent', + model=mock_model, + sub_agents=[loop_sub], + ) + + targets = _get_transfer_targets(llm_agent) + + target_names = [t.name for t in targets] + assert 'loop_sub' in target_names