Skip to content
Merged
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
5 changes: 3 additions & 2 deletions src/google/adk/agents/llm_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,11 @@ async def _convert_tool_union_to_tools(
) -> list[BaseTool]:
if isinstance(tool_union, BaseTool):
return [tool_union]
if isinstance(tool_union, Callable):
if callable(tool_union):
return [FunctionTool(func=tool_union)]

return await tool_union.get_tools(ctx)
# At this point, tool_union must be a BaseToolset
return await tool_union.get_tools_with_prefix(ctx)


class LlmAgent(BaseAgent):
Expand Down
67 changes: 66 additions & 1 deletion src/google/adk/tools/base_toolset.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

from abc import ABC
from abc import abstractmethod
import copy
from typing import final
from typing import List
from typing import Optional
from typing import Protocol
Expand Down Expand Up @@ -58,9 +60,19 @@ class BaseToolset(ABC):
"""

def __init__(
self, *, tool_filter: Optional[Union[ToolPredicate, List[str]]] = None
self,
*,
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
tool_name_prefix: Optional[str] = None,
):
"""Initialize the toolset.

Args:
tool_filter: Filter to apply to tools.
tool_name_prefix: The prefix to prepend to the names of the tools returned by the toolset.
"""
self.tool_filter = tool_filter
self.tool_name_prefix = tool_name_prefix

@abstractmethod
async def get_tools(
Expand All @@ -77,6 +89,59 @@ async def get_tools(
list[BaseTool]: A list of tools available under the specified context.
"""

@final
async def get_tools_with_prefix(
self,
readonly_context: Optional[ReadonlyContext] = None,
) -> list[BaseTool]:
"""Return all tools with optional prefix applied to tool names.

This method calls get_tools() and applies prefixing if tool_name_prefix is provided.

Args:
readonly_context (ReadonlyContext, optional): Context used to filter tools
available to the agent. If None, all tools in the toolset are returned.

Returns:
list[BaseTool]: A list of tools with prefixed names if tool_name_prefix is provided.
"""
tools = await self.get_tools(readonly_context)

if not self.tool_name_prefix:
return tools

prefix = self.tool_name_prefix

# Create copies of tools to avoid modifying original instances
prefixed_tools = []
for tool in tools:
# Create a shallow copy of the tool
tool_copy = copy.copy(tool)

# Apply prefix to the copied tool
prefixed_name = f"{prefix}_{tool.name}"
tool_copy.name = prefixed_name

# Also update the function declaration name if the tool has one
# Use default parameters to capture the current values in the closure
def _create_prefixed_declaration(
original_get_declaration=tool._get_declaration,
prefixed_name=prefixed_name,
):
def _get_prefixed_declaration():
declaration = original_get_declaration()
if declaration is not None:
declaration.name = prefixed_name
return declaration
return None

return _get_prefixed_declaration

tool_copy._get_declaration = _create_prefixed_declaration()
prefixed_tools.append(tool_copy)

return prefixed_tools

async def close(self) -> None:
"""Performs cleanup and releases resources held by the toolset.

Expand Down
Loading