Skip to content

Commit fc98549

Browse files
haranrkcopybara-github
authored andcommitted
feat(agents): wrap single_turn non-LlmAgent sub-agents as inline tools
Relax the parent `LlmAgent` auto-wrapping loop so any sub-agent declaring `mode='single_turn'` is exposed as an inline `_SingleTurnAgentTool`, not just `LlmAgent` sub-agents. This lets a `ManagedAgent` with `mode='single_turn'` be called like a tool while staying excluded from LLM-transfer targets. `LlmAgent` sub-agents still default to `mode='chat'` (unchanged), and sub-agents that do not declare `mode` are never wrapped. Co-authored-by: Haran Rajkumar <haranrk@google.com> PiperOrigin-RevId: 945960582
1 parent a680cea commit fc98549

2 files changed

Lines changed: 62 additions & 12 deletions

File tree

src/google/adk/agents/llm_agent.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,18 +1119,19 @@ def model_post_init(self, __context: Any) -> None:
11191119

11201120
if self.sub_agents:
11211121
for sub_agent in self.sub_agents:
1122-
if isinstance(sub_agent, LlmAgent):
1123-
mode = getattr(sub_agent, 'mode', None)
1124-
if mode is None:
1125-
try:
1126-
sub_agent.mode = 'chat'
1127-
mode = 'chat'
1128-
except (AttributeError, TypeError):
1129-
continue
1130-
if mode == 'single_turn':
1131-
self.tools.append(_SingleTurnAgentTool(sub_agent))
1132-
elif mode == 'task':
1133-
self.tools.append(_TaskAgentTool(sub_agent))
1122+
# `mode` is defined by whichever agent classes declare the field; any
1123+
# agent that defines `mode` participates here. A sub-agent that does not
1124+
# declare `mode` returns None and is never wrapped (it stays an
1125+
# LLM-transfer target).
1126+
mode = getattr(sub_agent, 'mode', None)
1127+
# LlmAgent sub-agents default to chat mode (unchanged behavior).
1128+
if isinstance(sub_agent, LlmAgent) and mode is None:
1129+
sub_agent.mode = 'chat'
1130+
mode = 'chat'
1131+
if mode == 'single_turn':
1132+
self.tools.append(_SingleTurnAgentTool(sub_agent))
1133+
elif mode == 'task':
1134+
self.tools.append(_TaskAgentTool(sub_agent))
11341135

11351136
@classmethod
11361137
@experimental(FeatureName.AGENT_CONFIG)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from google.adk.agents import LlmAgent
16+
17+
18+
def test_single_turn_managed_agent_is_wrapped_as_tool():
19+
from google.adk.agents import ManagedAgent
20+
from google.adk.tools.agent_tool import _SingleTurnAgentTool
21+
22+
managed = ManagedAgent(name='m', agent_id='a', mode='single_turn')
23+
coordinator = LlmAgent(name='c', sub_agents=[managed])
24+
25+
assert any(
26+
isinstance(t, _SingleTurnAgentTool) and t.agent is managed
27+
for t in coordinator.tools
28+
)
29+
30+
31+
def test_managed_agent_without_mode_is_not_wrapped():
32+
from google.adk.agents import ManagedAgent
33+
from google.adk.tools.agent_tool import _SingleTurnAgentTool
34+
35+
managed = ManagedAgent(name='m', agent_id='a') # mode defaults to None
36+
coordinator = LlmAgent(name='c', sub_agents=[managed])
37+
38+
assert not any(isinstance(t, _SingleTurnAgentTool) for t in coordinator.tools)
39+
40+
41+
def test_single_turn_managed_agent_excluded_from_transfer_targets():
42+
from google.adk.agents import ManagedAgent
43+
from google.adk.flows.llm_flows.agent_transfer import _get_transfer_targets
44+
45+
managed = ManagedAgent(name='m', agent_id='a', mode='single_turn')
46+
coordinator = LlmAgent(name='c', sub_agents=[managed])
47+
48+
targets = _get_transfer_targets(coordinator)
49+
assert managed not in targets

0 commit comments

Comments
 (0)