Skip to content

Commit 277084e

Browse files
google-genai-botcopybara-github
authored andcommitted
refactor(tools): Update ToolboxToolset to wrap toolbox-adk
### Description of Change **Problem:** The `ToolboxToolset` was implemented directly within `adk-python`, leading to code duplication and potential drift from the core `toolbox-adk` implementation. **Solution:** Refactored `ToolboxToolset` to act as a rigorous wrapper around the `toolbox-adk` package, which delegates all functionality to `toolbox_adk.ToolboxToolset`. ### Testing Plan **Unit Tests:** - [x] I have added or updated unit tests for my change. - [x] All unit tests pass locally. Summary: - Verified initialization flows through to `toolbox-adk`. - Verified `auth_token_getters` are correctly propagated. - Verified type hints are static-analysis friendly. **Manual End-to-End (E2E) Tests:** Manually verified standard toolbox loading and execution with the new wrapper: ```python from google.adk.tools import ToolboxToolset from toolbox_adk import CredentialStrategy # Loading with toolset_name ts = ToolboxToolset( server_url='http://localhost:8080', toolset_name='calculator', credentials=CredentialStrategy.toolbox_identity() ) tools = await ts.get_tools() print(f'Loaded {len(tools)} tools') ``` ### Checklist - [x] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document. - [x] I have performed a self-review of my own code. - [x] I have commented my code, particularly in hard-to-understand areas. - [x] I have added tests that prove my fix is effective or that my feature works. - [x] New and existing unit tests pass locally with my changes. - [x] I have manually tested my changes end-to-end. - [x] Any dependent changes have been merged and published in downstream modules. PiperOrigin-RevId: 855798474
1 parent ab62b1b commit 277084e

File tree

2 files changed

+56
-50
lines changed

2 files changed

+56
-50
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ extensions = [
157157
"llama-index-readers-file>=0.4.0", # For retrieval using LlamaIndex.
158158
"llama-index-embeddings-google-genai>=0.3.0", # For files retrieval using LlamaIndex.
159159
"lxml>=5.3.0", # For load_web_page tool.
160-
"toolbox-core>=0.1.0", # For tools.toolbox_toolset.ToolboxToolset
160+
"toolbox-adk>=0.1.0", # For tools.toolbox_toolset.ToolboxToolset
161161
]
162162

163163
otel-gcp = ["opentelemetry-instrumentation-google-genai>=0.3b0, <1.0.0"]

src/google/adk/tools/toolbox_toolset.py

Lines changed: 55 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,41 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from __future__ import annotations
16+
1517
from typing import Any
1618
from typing import Callable
1719
from typing import List
1820
from typing import Mapping
1921
from typing import Optional
22+
from typing import TYPE_CHECKING
2023
from typing import Union
2124

22-
import toolbox_core as toolbox
2325
from typing_extensions import override
2426

2527
from ..agents.readonly_context import ReadonlyContext
2628
from .base_tool import BaseTool
2729
from .base_toolset import BaseToolset
28-
from .function_tool import FunctionTool
30+
31+
if TYPE_CHECKING:
32+
from toolbox_adk import CredentialConfig
2933

3034

3135
class ToolboxToolset(BaseToolset):
3236
"""A class that provides access to toolbox toolsets.
3337
38+
This class acts as a bridge to the `toolbox-adk` package.
39+
You must install `toolbox-adk` to use this class.
40+
3441
Example:
3542
```python
36-
toolbox_toolset = ToolboxToolset("http://127.0.0.1:5000",
37-
toolset_name="my-toolset")
43+
from toolbox_adk import CredentialStrategy
44+
45+
toolbox_toolset = ToolboxToolset(
46+
server_url="http://127.0.0.1:5000",
47+
# toolset_name and tool_names are optional. If omitted, all tools are
48+
loaded.
49+
credentials=CredentialStrategy.toolbox_identity()
3850
)
3951
```
4052
"""
@@ -44,64 +56,58 @@ def __init__(
4456
server_url: str,
4557
toolset_name: Optional[str] = None,
4658
tool_names: Optional[List[str]] = None,
47-
auth_token_getters: Optional[dict[str, Callable[[], str]]] = None,
59+
auth_token_getters: Optional[Mapping[str, Callable[[], str]]] = None,
4860
bound_params: Optional[
4961
Mapping[str, Union[Callable[[], Any], Any]]
5062
] = None,
63+
credentials: Optional[CredentialConfig] = None,
64+
additional_headers: Optional[Mapping[str, str]] = None,
65+
**kwargs,
5166
):
5267
"""Args:
5368
54-
server_url: The URL of the toolbox server.
55-
toolset_name: The name of the toolbox toolset to load.
56-
tool_names: The names of the tools to load.
57-
auth_token_getters: A mapping of authentication service names to
58-
callables that return the corresponding authentication token. see:
59-
https://github.com/googleapis/mcp-toolbox-sdk-python/tree/main/packages/toolbox-core#authenticating-tools
60-
for details.
61-
bound_params: A mapping of parameter names to bind to specific values or
62-
callables that are called to produce values as needed. see:
63-
https://github.com/googleapis/mcp-toolbox-sdk-python/tree/main/packages/toolbox-core#binding-parameter-values
64-
for details.
65-
The resulting ToolboxToolset will contain both tools loaded by tool_names
66-
and toolset_name.
69+
server_url: The URL of the toolbox server.
70+
toolset_name: The name of the toolbox toolset to load.
71+
tool_names: The names of the tools to load.
72+
auth_token_getters: (Deprecated) Map of auth token getters.
73+
bound_params: Parameters to bind to the tools.
74+
credentials: (Optional) toolbox_adk.CredentialConfig object.
75+
additional_headers: (Optional) Static headers dictionary.
76+
**kwargs: Additional arguments passed to the underlying
77+
toolbox_adk.ToolboxToolset.
6778
"""
68-
if not tool_names and not toolset_name:
69-
raise ValueError("tool_names and toolset_name cannot both be None")
79+
if not toolset_name and not tool_names:
80+
raise ValueError(
81+
"Either 'toolset_name' or 'tool_names' must be provided."
82+
)
83+
84+
try:
85+
from toolbox_adk import ToolboxToolset as RealToolboxToolset # pylint: disable=import-outside-toplevel
86+
except ImportError as exc:
87+
raise ImportError(
88+
"ToolboxToolset requires the 'toolbox-adk' package. "
89+
"Please install it using `pip install toolbox-adk`."
90+
) from exc
91+
7092
super().__init__()
71-
self._server_url = server_url
72-
self._toolbox_client = toolbox.ToolboxClient(server_url)
73-
self._toolset_name = toolset_name
74-
self._tool_names = tool_names
75-
self._auth_token_getters = auth_token_getters or {}
76-
self._bound_params = bound_params or {}
93+
94+
self._delegate = RealToolboxToolset(
95+
server_url=server_url,
96+
toolset_name=toolset_name,
97+
tool_names=tool_names,
98+
credentials=credentials,
99+
additional_headers=additional_headers,
100+
bound_params=bound_params,
101+
auth_token_getters=auth_token_getters,
102+
**kwargs,
103+
)
77104

78105
@override
79106
async def get_tools(
80107
self, readonly_context: Optional[ReadonlyContext] = None
81108
) -> list[BaseTool]:
82-
tools = []
83-
if self._toolset_name:
84-
tools.extend([
85-
FunctionTool(tool)
86-
for tool in await self._toolbox_client.load_toolset(
87-
self._toolset_name,
88-
auth_token_getters=self._auth_token_getters,
89-
bound_params=self._bound_params,
90-
)
91-
])
92-
if self._tool_names:
93-
tools.extend([
94-
FunctionTool(
95-
await self._toolbox_client.load_tool(
96-
tool_name,
97-
auth_token_getters=self._auth_token_getters,
98-
bound_params=self._bound_params,
99-
)
100-
)
101-
for tool_name in self._tool_names
102-
])
103-
return tools
109+
return await self._delegate.get_tools(readonly_context)
104110

105111
@override
106112
async def close(self):
107-
self._toolbox_client.close()
113+
await self._delegate.close()

0 commit comments

Comments
 (0)