Skip to content

Commit 935bcc2

Browse files
committed
feat(agent-tool): Make include_plugins keyword-only with default True
- Change include_plugins default from False to True (matches existing behavior) - Make only include_plugins keyword-only (preserves backward compatibility) - Enhance docstring to explain plugin propagation behavior - Add include_plugins field to AgentToolConfig - Update from_config to pass include_plugins parameter Tests: - Add test_include_plugins_default_true (verifies default propagation) - Add test_include_plugins_explicit_true (verifies explicit True) - Add test_include_plugins_false (verifies opt-out behavior) - All 18 tests pass (6 new test variants for GOOGLE_AI and VERTEX)
1 parent d8b2504 commit 935bcc2

2 files changed

Lines changed: 130 additions & 5 deletions

File tree

src/google/adk/tools/agent_tool.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,19 @@ class AgentTool(BaseTool):
4545
Attributes:
4646
agent: The agent to wrap.
4747
skip_summarization: Whether to skip summarization of the agent output.
48-
include_plugins: Whether to include plugins from parent runner context
48+
include_plugins: Whether to propagate plugins from the parent runner context
49+
to the agent's runner. When True (default), the agent will inherit all
50+
plugins from its parent. Set to False to run the agent with an isolated
51+
plugin environment.
4952
"""
5053

51-
def __init__(self, *, agent: BaseAgent, skip_summarization: bool = False, include_plugins: bool = False):
54+
def __init__(
55+
self,
56+
agent: BaseAgent,
57+
skip_summarization: bool = False,
58+
*,
59+
include_plugins: bool = True,
60+
):
5261
self.agent = agent
5362
self.skip_summarization: bool = skip_summarization
5463
self.include_plugins = include_plugins
@@ -127,15 +136,19 @@ async def run_async(
127136
role='user',
128137
parts=[types.Part.from_text(text=args['request'])],
129138
)
130-
plugins = tool_context._invocation_context.plugin_manager.plugins if self.include_plugins else None
139+
plugins = (
140+
tool_context._invocation_context.plugin_manager.plugins
141+
if self.include_plugins
142+
else None
143+
)
131144
runner = Runner(
132145
app_name=self.agent.name,
133146
agent=self.agent,
134147
artifact_service=ForwardingArtifactService(tool_context),
135148
session_service=InMemorySessionService(),
136149
memory_service=InMemoryMemoryService(),
137150
credential_service=tool_context._invocation_context.credential_service,
138-
plugins=plugins
151+
plugins=plugins,
139152
)
140153
session = await runner.session_service.create_session(
141154
app_name=self.agent.name,
@@ -180,7 +193,9 @@ def from_config(
180193
agent_tool_config.agent, config_abs_path
181194
)
182195
return cls(
183-
agent=agent, skip_summarization=agent_tool_config.skip_summarization
196+
agent=agent,
197+
skip_summarization=agent_tool_config.skip_summarization,
198+
include_plugins=agent_tool_config.include_plugins,
184199
)
185200

186201

@@ -192,3 +207,6 @@ class AgentToolConfig(BaseToolConfig):
192207

193208
skip_summarization: bool = False
194209
"""Whether to skip summarization of the agent output."""
210+
211+
include_plugins: bool = True
212+
"""Whether to include plugins from parent runner context."""

tests/unittests/tools/test_agent_tool.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from google.adk.agents.callback_context import CallbackContext
1616
from google.adk.agents.llm_agent import Agent
1717
from google.adk.agents.sequential_agent import SequentialAgent
18+
from google.adk.plugins.base_plugin import BasePlugin
1819
from google.adk.tools.agent_tool import AgentTool
1920
from google.adk.utils.variant_utils import GoogleLLMVariant
2021
from google.genai import types
@@ -355,3 +356,109 @@ class CustomInput(BaseModel):
355356
# Should have string response schema for VERTEX_AI when no output_schema
356357
assert declaration.response is not None
357358
assert declaration.response.type == types.Type.STRING
359+
360+
361+
def test_include_plugins_default_true():
362+
"""Test that plugins are propagated by default (include_plugins=True)."""
363+
364+
# Create a test plugin that tracks callbacks
365+
class TrackingPlugin(BasePlugin):
366+
def __init__(self, name: str):
367+
super().__init__(name)
368+
self.before_agent_calls = 0
369+
370+
async def before_agent_callback(self, **kwargs):
371+
self.before_agent_calls += 1
372+
373+
tracking_plugin = TrackingPlugin(name='tracking')
374+
375+
mock_model = testing_utils.MockModel.create(
376+
responses=[function_call_no_schema, 'response1', 'response2']
377+
)
378+
379+
tool_agent = Agent(
380+
name='tool_agent',
381+
model=mock_model,
382+
)
383+
384+
root_agent = Agent(
385+
name='root_agent',
386+
model=mock_model,
387+
tools=[AgentTool(agent=tool_agent)], # Default include_plugins=True
388+
)
389+
390+
runner = testing_utils.InMemoryRunner(root_agent, plugins=[tracking_plugin])
391+
runner.run('test1')
392+
393+
# Plugin should be called for both root_agent and tool_agent
394+
assert tracking_plugin.before_agent_calls == 2
395+
396+
397+
def test_include_plugins_explicit_true():
398+
"""Test that plugins are propagated when include_plugins=True."""
399+
400+
class TrackingPlugin(BasePlugin):
401+
def __init__(self, name: str):
402+
super().__init__(name)
403+
self.before_agent_calls = 0
404+
405+
async def before_agent_callback(self, **kwargs):
406+
self.before_agent_calls += 1
407+
408+
tracking_plugin = TrackingPlugin(name='tracking')
409+
410+
mock_model = testing_utils.MockModel.create(
411+
responses=[function_call_no_schema, 'response1', 'response2']
412+
)
413+
414+
tool_agent = Agent(
415+
name='tool_agent',
416+
model=mock_model,
417+
)
418+
419+
root_agent = Agent(
420+
name='root_agent',
421+
model=mock_model,
422+
tools=[AgentTool(agent=tool_agent, include_plugins=True)],
423+
)
424+
425+
runner = testing_utils.InMemoryRunner(root_agent, plugins=[tracking_plugin])
426+
runner.run('test1')
427+
428+
# Plugin should be called for both root_agent and tool_agent
429+
assert tracking_plugin.before_agent_calls == 2
430+
431+
432+
def test_include_plugins_false():
433+
"""Test that plugins are NOT propagated when include_plugins=False."""
434+
435+
class TrackingPlugin(BasePlugin):
436+
def __init__(self, name: str):
437+
super().__init__(name)
438+
self.before_agent_calls = 0
439+
440+
async def before_agent_callback(self, **kwargs):
441+
self.before_agent_calls += 1
442+
443+
tracking_plugin = TrackingPlugin(name='tracking')
444+
445+
mock_model = testing_utils.MockModel.create(
446+
responses=[function_call_no_schema, 'response1', 'response2']
447+
)
448+
449+
tool_agent = Agent(
450+
name='tool_agent',
451+
model=mock_model,
452+
)
453+
454+
root_agent = Agent(
455+
name='root_agent',
456+
model=mock_model,
457+
tools=[AgentTool(agent=tool_agent, include_plugins=False)],
458+
)
459+
460+
runner = testing_utils.InMemoryRunner(root_agent, plugins=[tracking_plugin])
461+
runner.run('test1')
462+
463+
# Plugin should only be called for root_agent, not tool_agent
464+
assert tracking_plugin.before_agent_calls == 1

0 commit comments

Comments
 (0)