Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions src/google/adk/tools/agent_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@

if TYPE_CHECKING:
from ..agents.base_agent import BaseAgent
from ..sessions.base_session_service import BaseSessionService
from ..sessions.session import Session


def _part_to_text(part: types.Part) -> str:
Expand Down Expand Up @@ -104,6 +106,54 @@ def _get_output_schema(agent: BaseAgent) -> Optional[SchemaType]:
return None


async def _inject_conversation_history(
tool_context: ToolContext,
session_service: BaseSessionService,
session: Session,
) -> None:
Comment on lines +109 to +113

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the optional max_history_messages parameter to the history injection helper function.

Suggested change
async def _inject_conversation_history(
tool_context: ToolContext,
session_service: BaseSessionService,
session: Session,
) -> None:
async def _inject_conversation_history(
tool_context: ToolContext,
session_service: BaseSessionService,
session: Session,
max_history_messages: Optional[int] = None,
) -> None:

"""Injects parent conversation events into the child session.

Copies user and model text events from the parent session into the child,
giving the tool agent multi-turn conversational awareness.

Function-call and function-response events are excluded because they
reference tools that only exist in the parent runner. A new Event object
is created for each injected turn to prevent state_delta values from being
replayed and corrupting the child session state.

Args:
tool_context: The parent tool context containing the session to read
from.
session_service: The child runner's session service for appending
events.
session: The child session to inject events into.
Comment on lines +124 to +129

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update max_history_messages argument in the helper function's docstring.

Suggested change
Args:
tool_context: The parent tool context containing the session to read
from.
session_service: The child runner's session service for appending
events.
session: The child session to inject events into.
Args:
tool_context: The parent tool context containing the session to read from.
session_service: The child runner's session service for appending events.
session: The child session to inject events into.
max_history_messages: The maximum number of recent conversation turns to inject.

"""
from ..events.event import Event

for parent_event in tool_context.session.events:
if not parent_event.content or not parent_event.content.parts:
continue
if parent_event.content.role not in ('user', 'model'):
continue
text_parts = [
part
for part in parent_event.content.parts
if part.text and not part.thought
]
if not text_parts:
continue
await session_service.append_event(
session,
Event(
author=parent_event.author,
content=types.Content(
role=parent_event.content.role,
parts=text_parts,
),
),
)
Comment on lines +133 to +154

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gather and filter the eligible history events first, then slice them to match the configured limit before injecting.

Suggested change
for parent_event in tool_context.session.events:
if not parent_event.content or not parent_event.content.parts:
continue
if parent_event.content.role not in ('user', 'model'):
continue
text_parts = [
part
for part in parent_event.content.parts
if part.text and not part.thought
]
if not text_parts:
continue
await session_service.append_event(
session,
Event(
author=parent_event.author,
content=types.Content(
role=parent_event.content.role,
parts=text_parts,
),
),
)
eligible_events = []
for parent_event in tool_context.session.events:
if not parent_event.content or not parent_event.content.parts:
continue
if parent_event.content.role not in ('user', 'model'):
continue
text_parts = [
part
for part in parent_event.content.parts
if part.text and not part.thought
]
if not text_parts:
continue
eligible_events.append((parent_event, text_parts))
if max_history_messages is not None:
eligible_events = eligible_events[-max_history_messages:]
for parent_event, text_parts in eligible_events:
await session_service.append_event(
session,
Event(
author=parent_event.author,
content=types.Content(
role=parent_event.content.role,
parts=text_parts,
),
),
)



class AgentTool(BaseTool):
"""A tool that wraps an agent.

Expand All @@ -118,6 +168,10 @@ class AgentTool(BaseTool):
to the agent's runner. When True (default), the agent will inherit all
plugins from its parent. Set to False to run the agent with an isolated
plugin environment.
propagate_conversation_history: Whether to inject parent session's user
and model text events into the child session before execution. When
True, the child agent receives the full conversational context. Default
is False (isolated session with no history).
"""

def __init__(
Expand All @@ -127,11 +181,13 @@ def __init__(
*,
include_plugins: bool = True,
propagate_grounding_metadata: bool = False,
propagate_conversation_history: bool = False,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accept max_history_messages as an optional configuration keyword argument in the AgentTool constructor.

Suggested change
propagate_conversation_history: bool = False,
propagate_conversation_history: bool = False,
max_history_messages: Optional[int] = None,

):
self.agent = agent
self.skip_summarization: bool = skip_summarization
self.include_plugins = include_plugins
self.propagate_grounding_metadata = propagate_grounding_metadata
self.propagate_conversation_history = propagate_conversation_history

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Store max_history_messages as an instance variable on the AgentTool.

Suggested change
self.propagate_conversation_history = propagate_conversation_history
self.propagate_conversation_history = propagate_conversation_history
self.max_history_messages = max_history_messages


super().__init__(name=agent.name, description=agent.description)

Expand Down Expand Up @@ -267,6 +323,11 @@ async def run_async(
state=state_dict,
)

if self.propagate_conversation_history:
await _inject_conversation_history(
tool_context, runner.session_service, session
)
Comment on lines +326 to +329

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass the configured "max_history_messages" limit down into the helper function when injecting the history.

Suggested change
if self.propagate_conversation_history:
await _inject_conversation_history(
tool_context, runner.session_service, session
)
if self.propagate_conversation_history:
await _inject_conversation_history(
tool_context,
runner.session_service,
session,
max_history_messages=self.max_history_messages,
)


last_content = None
last_grounding_metadata = None
async with Aclosing(
Expand Down Expand Up @@ -385,12 +446,14 @@ def __init__(
*,
include_plugins: bool = True,
propagate_grounding_metadata: bool = False,
propagate_conversation_history: bool = False,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expose the max_history_messages parameter on the _TaskAgentTool subclass constructor.

Suggested change
propagate_conversation_history: bool = False,
propagate_conversation_history: bool = False,
max_history_messages: Optional[int] = None,

):
super().__init__(
agent,
skip_summarization,
include_plugins=include_plugins,
propagate_grounding_metadata=propagate_grounding_metadata,
propagate_conversation_history=propagate_conversation_history,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forward the max_history_messages argument to the base AgentTool constructor via super().init.

Suggested change
propagate_conversation_history=propagate_conversation_history,
propagate_conversation_history=propagate_conversation_history,
max_history_messages=max_history_messages,

)
self._defers_response = True

Expand Down
Loading