Skip to content

Commit 64aa5a8

Browse files
authored
feat(toolbox-llamaindex)!: Base toolbox-llamaindex over toolbox-core (#244)
* chore: Add toolbox-core as a dependency Also add to pyproject.toml along with removing unnecessary files. * docs: Update README * feat(toolbox-llamaindex)!: Update tool and client codebase * fix: Add llamaindex as a dependency * chore: Delint * chore: Fix unit tests * chore: Fix e2e tests * chore: Fix e2e test * chore: Pin toolbox-core version
1 parent ec423ea commit 64aa5a8

File tree

16 files changed

+1529
-1820
lines changed

16 files changed

+1529
-1820
lines changed

packages/toolbox-llamaindex/README.md

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,27 @@ applications, enabling advanced orchestration and interaction with GenAI models.
99
## Table of Contents
1010
<!-- TOC -->
1111

12-
- [MCP Toolbox LlamaIndex SDK](#mcp-toolbox-llamaindex-sdk)
13-
- [Installation](#installation)
14-
- [Quickstart](#quickstart)
15-
- [TODO: add link](#todo-add-link)
16-
- [Usage](#usage)
17-
- [Loading Tools](#loading-tools)
18-
- [Load a toolset](#load-a-toolset)
19-
- [Load a single tool](#load-a-single-tool)
20-
- [Use with LlamaIndex](#use-with-llamaindex)
21-
- [Maintain state](#maintain-state)
22-
- [Manual usage](#manual-usage)
23-
- [Authenticating Tools](#authenticating-tools)
24-
- [Supported Authentication Mechanisms](#supported-authentication-mechanisms)
25-
- [Configure Tools](#configure-tools)
26-
- [Configure SDK](#configure-sdk)
27-
- [Add Authentication to a Tool](#add-authentication-to-a-tool)
28-
- [Add Authentication While Loading](#add-authentication-while-loading)
29-
- [Complete Example](#complete-example)
30-
- [Binding Parameter Values](#binding-parameter-values)
31-
- [Binding Parameters to a Tool](#binding-parameters-to-a-tool)
32-
- [Binding Parameters While Loading](#binding-parameters-while-loading)
33-
- [Binding Dynamic Values](#binding-dynamic-values)
34-
- [Asynchronous Usage](#asynchronous-usage)
12+
- [Installation](#installation)
13+
- [Quickstart](#quickstart)
14+
- [Usage](#usage)
15+
- [Loading Tools](#loading-tools)
16+
- [Load a toolset](#load-a-toolset)
17+
- [Load a single tool](#load-a-single-tool)
18+
- [Use with LlamaIndex](#use-with-llamaindex)
19+
- [Maintain state](#maintain-state)
20+
- [Manual usage](#manual-usage)
21+
- [Authenticating Tools](#authenticating-tools)
22+
- [Supported Authentication Mechanisms](#supported-authentication-mechanisms)
23+
- [Configure Tools](#configure-tools)
24+
- [Configure SDK](#configure-sdk)
25+
- [Add Authentication to a Tool](#add-authentication-to-a-tool)
26+
- [Add Authentication While Loading](#add-authentication-while-loading)
27+
- [Complete Example](#complete-example)
28+
- [Binding Parameter Values](#binding-parameter-values)
29+
- [Binding Parameters to a Tool](#binding-parameters-to-a-tool)
30+
- [Binding Parameters While Loading](#binding-parameters-while-loading)
31+
- [Binding Dynamic Values](#binding-dynamic-values)
32+
- [Asynchronous Usage](#asynchronous-usage)
3533

3634
<!-- /TOC -->
3735

@@ -44,8 +42,7 @@ pip install toolbox-llamaindex
4442
## Quickstart
4543

4644
Here's a minimal example to get you started using
47-
# TODO: add link
48-
[LlamaIndex]():
45+
[LlamaIndex](https://docs.llamaindex.ai/en/stable/#getting-started):
4946

5047
```py
5148
import asyncio
@@ -111,7 +108,7 @@ available to your LLM agent.
111108

112109
## Use with LlamaIndex
113110

114-
LangChain's agents can dynamically choose and execute tools based on the user
111+
LlamaIndex's agents can dynamically choose and execute tools based on the user
115112
input. Include tools loaded from the Toolbox SDK in the agent's toolkit:
116113

117114
```py
@@ -165,7 +162,7 @@ print(response)
165162
Execute a tool manually using the `call` method:
166163

167164
```py
168-
result = tools[0].call({"name": "Alice", "age": 30})
165+
result = tools[0].call(name="Alice", age=30)
169166
```
170167

171168
This is useful for testing tools or when you need precise control over tool
@@ -210,21 +207,21 @@ async def get_auth_token():
210207
toolbox = ToolboxClient("http://127.0.0.1:5000")
211208
tools = toolbox.load_toolset()
212209

213-
auth_tool = tools[0].add_auth_token("my_auth", get_auth_token) # Single token
210+
auth_tool = tools[0].add_auth_token_getter("my_auth", get_auth_token) # Single token
214211

215-
multi_auth_tool = tools[0].add_auth_tokens({"my_auth", get_auth_token}) # Multiple tokens
212+
multi_auth_tool = tools[0].add_auth_token_getters({"auth_1": get_auth_1}, {"auth_2": get_auth_2}) # Multiple tokens
216213

217214
# OR
218215

219-
auth_tools = [tool.add_auth_token("my_auth", get_auth_token) for tool in tools]
216+
auth_tools = [tool.add_auth_token_getter("my_auth", get_auth_token) for tool in tools]
220217
```
221218

222219
#### Add Authentication While Loading
223220

224221
```py
225-
auth_tool = toolbox.load_tool(auth_tokens={"my_auth": get_auth_token})
222+
auth_tool = toolbox.load_tool(auth_token_getters={"my_auth": get_auth_token})
226223

227-
auth_tools = toolbox.load_toolset(auth_tokens={"my_auth": get_auth_token})
224+
auth_tools = toolbox.load_toolset(auth_token_getters={"my_auth": get_auth_token})
228225
```
229226

230227
> [!NOTE]
@@ -245,8 +242,8 @@ async def get_auth_token():
245242
toolbox = ToolboxClient("http://127.0.0.1:5000")
246243
tool = toolbox.load_tool("my-tool")
247244

248-
auth_tool = tool.add_auth_token("my_auth", get_auth_token)
249-
result = auth_tool.call({"input": "some input"})
245+
auth_tool = tool.add_auth_token_getter("my_auth", get_auth_token)
246+
result = auth_tool.call(input="some input")
250247
print(result)
251248
```
252249

packages/toolbox-llamaindex/integration.cloudbuild.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515
steps:
1616
- id: Install library requirements
1717
name: 'python:${_VERSION}'
18+
dir: 'packages/toolbox-llamaindex'
1819
args:
1920
- install
2021
- '-r'
21-
- 'packages/toolbox-llamaindex/requirements.txt'
22+
- 'requirements.txt'
2223
- '--user'
2324
entrypoint: pip
2425
- id: Install test requirements

packages/toolbox-llamaindex/pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[project]
2-
name = "toolbox-llamindex"
2+
name = "toolbox-llamaindex"
33
dynamic = ["version"]
44
readme = "README.md"
55
description = "Python SDK for interacting with the Toolbox service with LlamaIndex"
@@ -9,6 +9,8 @@ authors = [
99
{name = "Google LLC", email = "[email protected]"}
1010
]
1111
dependencies = [
12+
# TODO: Bump toolbox-core version to 0.2.0
13+
"toolbox-core==0.1.0",
1214
"llama-index>=0.12.0,<1.0.0",
1315
"PyYAML>=6.0.1,<7.0.0",
1416
"pydantic>=2.8.0,<3.0.0",

packages/toolbox-llamaindex/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
-e ../toolbox-core
12
llama-index==0.12.33
23
PyYAML==6.0.2
34
pydantic==2.11.4

packages/toolbox-llamaindex/src/toolbox_llamaindex/async_client.py

Lines changed: 77 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
from warnings import warn
1717

1818
from aiohttp import ClientSession
19+
from toolbox_core.client import ToolboxClient as ToolboxCoreClient
1920

20-
from .tools import AsyncToolboxTool
21-
from .utils import ManifestSchema, _load_manifest
21+
from .async_tools import AsyncToolboxTool
2222

2323

2424
# This class is an internal implementation detail and is not exposed to the
@@ -38,67 +38,72 @@ def __init__(
3838
url: The base URL of the Toolbox service.
3939
session: An HTTP client session.
4040
"""
41-
self.__url = url
42-
self.__session = session
41+
self.__core_client = ToolboxCoreClient(url=url, session=session)
4342

4443
async def aload_tool(
4544
self,
4645
tool_name: str,
47-
auth_tokens: dict[str, Callable[[], str]] = {},
46+
auth_token_getters: dict[str, Callable[[], str]] = {},
47+
auth_tokens: Optional[dict[str, Callable[[], str]]] = None,
4848
auth_headers: Optional[dict[str, Callable[[], str]]] = None,
4949
bound_params: dict[str, Union[Any, Callable[[], Any]]] = {},
50-
strict: bool = True,
5150
) -> AsyncToolboxTool:
5251
"""
5352
Loads the tool with the given tool name from the Toolbox service.
5453
5554
Args:
5655
tool_name: The name of the tool to load.
57-
auth_tokens: An optional mapping of authentication source names to
58-
functions that retrieve ID tokens.
59-
auth_headers: Deprecated. Use `auth_tokens` instead.
56+
auth_token_getters: An optional mapping of authentication source
57+
names to functions that retrieve ID tokens.
58+
auth_tokens: Deprecated. Use `auth_token_getters` instead.
59+
auth_headers: Deprecated. Use `auth_token_getters` instead.
6060
bound_params: An optional mapping of parameter names to their
6161
bound values.
62-
strict: If True, raises a ValueError if any of the given bound
63-
parameters are missing from the schema or require
64-
authentication. If False, only issues a warning.
6562
6663
Returns:
6764
A tool loaded from the Toolbox.
6865
"""
66+
if auth_tokens:
67+
if auth_token_getters:
68+
warn(
69+
"Both `auth_token_getters` and `auth_tokens` are provided. `auth_tokens` is deprecated, and `auth_token_getters` will be used.",
70+
DeprecationWarning,
71+
)
72+
else:
73+
warn(
74+
"Argument `auth_tokens` is deprecated. Use `auth_token_getters` instead.",
75+
DeprecationWarning,
76+
)
77+
auth_token_getters = auth_tokens
78+
6979
if auth_headers:
70-
if auth_tokens:
80+
if auth_token_getters:
7181
warn(
72-
"Both `auth_tokens` and `auth_headers` are provided. `auth_headers` is deprecated, and `auth_tokens` will be used.",
82+
"Both `auth_token_getters` and `auth_headers` are provided. `auth_headers` is deprecated, and `auth_token_getters` will be used.",
7383
DeprecationWarning,
7484
)
7585
else:
7686
warn(
77-
"Argument `auth_headers` is deprecated. Use `auth_tokens` instead.",
87+
"Argument `auth_headers` is deprecated. Use `auth_token_getters` instead.",
7888
DeprecationWarning,
7989
)
80-
auth_tokens = auth_headers
81-
82-
url = f"{self.__url}/api/tool/{tool_name}"
83-
manifest: ManifestSchema = await _load_manifest(url, self.__session)
84-
85-
return AsyncToolboxTool(
86-
tool_name,
87-
manifest.tools[tool_name],
88-
self.__url,
89-
self.__session,
90-
auth_tokens,
91-
bound_params,
92-
strict,
90+
auth_token_getters = auth_headers
91+
92+
core_tool = await self.__core_client.load_tool(
93+
name=tool_name,
94+
auth_token_getters=auth_token_getters,
95+
bound_params=bound_params,
9396
)
97+
return AsyncToolboxTool(core_tool=core_tool)
9498

9599
async def aload_toolset(
96100
self,
97101
toolset_name: Optional[str] = None,
98-
auth_tokens: dict[str, Callable[[], str]] = {},
102+
auth_token_getters: dict[str, Callable[[], str]] = {},
103+
auth_tokens: Optional[dict[str, Callable[[], str]]] = None,
99104
auth_headers: Optional[dict[str, Callable[[], str]]] = None,
100105
bound_params: dict[str, Union[Any, Callable[[], Any]]] = {},
101-
strict: bool = True,
106+
strict: bool = False,
102107
) -> list[AsyncToolboxTool]:
103108
"""
104109
Loads tools from the Toolbox service, optionally filtered by toolset
@@ -107,65 +112,76 @@ async def aload_toolset(
107112
Args:
108113
toolset_name: The name of the toolset to load. If not provided,
109114
all tools are loaded.
110-
auth_tokens: An optional mapping of authentication source names to
111-
functions that retrieve ID tokens.
112-
auth_headers: Deprecated. Use `auth_tokens` instead.
115+
auth_token_getters: An optional mapping of authentication source
116+
names to functions that retrieve ID tokens.
117+
auth_tokens: Deprecated. Use `auth_token_getters` instead.
118+
auth_headers: Deprecated. Use `auth_token_getters` instead.
113119
bound_params: An optional mapping of parameter names to their
114120
bound values.
115-
strict: If True, raises a ValueError if any of the given bound
116-
parameters are missing from the schema or require
117-
authentication. If False, only issues a warning.
121+
strict: If True, raises an error if *any* loaded tool instance fails
122+
to utilize at least one provided parameter or auth token (if any
123+
provided). If False (default), raises an error only if a
124+
user-provided parameter or auth token cannot be applied to *any*
125+
loaded tool across the set.
118126
119127
Returns:
120128
A list of all tools loaded from the Toolbox.
121129
"""
122-
if auth_headers:
123-
if auth_tokens:
130+
if auth_tokens:
131+
if auth_token_getters:
124132
warn(
125-
"Both `auth_tokens` and `auth_headers` are provided. `auth_headers` is deprecated, and `auth_tokens` will be used.",
133+
"Both `auth_token_getters` and `auth_tokens` are provided. `auth_tokens` is deprecated, and `auth_token_getters` will be used.",
126134
DeprecationWarning,
127135
)
128136
else:
129137
warn(
130-
"Argument `auth_headers` is deprecated. Use `auth_tokens` instead.",
138+
"Argument `auth_tokens` is deprecated. Use `auth_token_getters` instead.",
131139
DeprecationWarning,
132140
)
133-
auth_tokens = auth_headers
134-
135-
url = f"{self.__url}/api/toolset/{toolset_name or ''}"
136-
manifest: ManifestSchema = await _load_manifest(url, self.__session)
137-
tools: list[AsyncToolboxTool] = []
138-
139-
for tool_name, tool_schema in manifest.tools.items():
140-
tools.append(
141-
AsyncToolboxTool(
142-
tool_name,
143-
tool_schema,
144-
self.__url,
145-
self.__session,
146-
auth_tokens,
147-
bound_params,
148-
strict,
141+
auth_token_getters = auth_tokens
142+
143+
if auth_headers:
144+
if auth_token_getters:
145+
warn(
146+
"Both `auth_token_getters` and `auth_headers` are provided. `auth_headers` is deprecated, and `auth_token_getters` will be used.",
147+
DeprecationWarning,
148+
)
149+
else:
150+
warn(
151+
"Argument `auth_headers` is deprecated. Use `auth_token_getters` instead.",
152+
DeprecationWarning,
149153
)
150-
)
154+
auth_token_getters = auth_headers
155+
156+
core_tools = await self.__core_client.load_toolset(
157+
name=toolset_name,
158+
auth_token_getters=auth_token_getters,
159+
bound_params=bound_params,
160+
strict=strict,
161+
)
162+
163+
tools = []
164+
for core_tool in core_tools:
165+
tools.append(AsyncToolboxTool(core_tool=core_tool))
151166
return tools
152167

153168
def load_tool(
154169
self,
155170
tool_name: str,
156-
auth_tokens: dict[str, Callable[[], str]] = {},
171+
auth_token_getters: dict[str, Callable[[], str]] = {},
172+
auth_tokens: Optional[dict[str, Callable[[], str]]] = None,
157173
auth_headers: Optional[dict[str, Callable[[], str]]] = None,
158174
bound_params: dict[str, Union[Any, Callable[[], Any]]] = {},
159-
strict: bool = True,
160175
) -> AsyncToolboxTool:
161176
raise NotImplementedError("Synchronous methods not supported by async client.")
162177

163178
def load_toolset(
164179
self,
165180
toolset_name: Optional[str] = None,
166-
auth_tokens: dict[str, Callable[[], str]] = {},
181+
auth_token_getters: dict[str, Callable[[], str]] = {},
182+
auth_tokens: Optional[dict[str, Callable[[], str]]] = None,
167183
auth_headers: Optional[dict[str, Callable[[], str]]] = None,
168184
bound_params: dict[str, Union[Any, Callable[[], Any]]] = {},
169-
strict: bool = True,
185+
strict: bool = False,
170186
) -> list[AsyncToolboxTool]:
171187
raise NotImplementedError("Synchronous methods not supported by async client.")

0 commit comments

Comments
 (0)