Skip to content

Commit c5c699c

Browse files
authored
fix(toolbox-sdk)!: deprecate 'add_auth_headers' in favor of 'add_auth_tokens' (#170)
Client code should not be concerned with how authentication tokens are handled internally. This commit refactors method and parameter names to use the more abstract term "token" instead of "header," improving the developer experience and hiding implementation details.
1 parent dc47da9 commit c5c699c

File tree

7 files changed

+179
-43
lines changed

7 files changed

+179
-43
lines changed

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ configuring tools for authenticated parameters.
204204

205205
### Configure SDK for Authentication
206206

207-
Provide the `auth_headers` parameter to the `load_tool` or `load_toolset` calls
207+
Provide the `auth_tokens` parameter to the `load_tool` or `load_toolset` calls
208208
with a dictionary. The keys of this dictionary should match the names of the
209209
authentication sources configured in your tools file (e.g., `my_auth_service`),
210210
and the values should be callable functions (e.g., lambdas or regular functions)
@@ -213,30 +213,30 @@ that return the ID token of the logged-in user.
213213
Here's an example:
214214

215215
```py
216-
def get_auth_header():
216+
def get_auth_token():
217217
# ... Logic to retrieve ID token (e.g., from local storage, OAuth flow)
218218
# This example just returns a placeholder. Replace with your actual token retrieval.
219219
return "YOUR_ID_TOKEN"
220220

221221
toolbox = ToolboxClient("http://localhost:5000")
222222

223-
tools = toolbox.load_toolset(auth_headers={ "my_auth_service": get_auth_header })
223+
tools = toolbox.load_toolset(auth_tokens={ "my_auth_service": get_auth_token })
224224

225225
# OR
226226

227-
tool = toolbox.load_tool("my_tool", auth_headers={ "my_auth_service": get_auth_header })
227+
tool = toolbox.load_tool("my_tool", auth_tokens={ "my_auth_service": get_auth_token })
228228
```
229229

230-
Alternatively, you can call the `add_auth_header` method to configure
230+
Alternatively, you can call the `add_auth_token` method to configure
231231
authentication separately.
232232

233233
```py
234-
toolbox.add_auth_header("my_auth_service", get_auth_header)
234+
toolbox.add_auth_token("my_auth_service", get_auth_token)
235235
```
236236

237237
> [!NOTE]
238-
> Authentication headers added via `load_tool`, `load_toolset`, or
239-
> `add_auth_header` apply to all subsequent tool invocations, regardless of when
238+
> Authentication tokens added via `load_tool`, `load_toolset`, or
239+
> `add_auth_token` apply to all subsequent tool invocations, regardless of when
240240
> the tool was loaded. This ensures a consistent authentication context.
241241
242242
### Complete Example
@@ -245,7 +245,7 @@ toolbox.add_auth_header("my_auth_service", get_auth_header)
245245
import asyncio
246246
from toolbox_langchain_sdk import ToolboxClient
247247

248-
async def get_auth_header():
248+
async def get_auth_token():
249249
# Replace with your actual ID token retrieval logic.
250250
# For example, using a library like google-auth
251251
# from google.oauth2 import id_token
@@ -257,7 +257,7 @@ async def get_auth_header():
257257

258258
async def main():
259259
toolbox = ToolboxClient("http://localhost:5000")
260-
toolbox.add_auth_header("my_auth_service", get_auth_header)
260+
toolbox.add_auth_token("my_auth_service", get_auth_token)
261261
tools = await toolbox.load_toolset()
262262
result = await tools[0].arun({"input": "some input"})
263263
print(result)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ dependencies = [
1212
"PyYAML>=6.0.1,<7.0.0",
1313
"pydantic>=2.7.0,<3.0.0",
1414
"aiohttp>=3.8.6,<4.0.0",
15+
"deprecated>=1.1.0,<2.0.0",
1516
]
1617

1718
classifiers = [

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ langchain-core==0.3.21
22
PyYAML==6.0.2
33
pydantic==2.10.2
44
aiohttp==3.11.7
5+
deprecated==1.2.15

src/toolbox_langchain_sdk/client.py

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import asyncio
2-
import warnings
32
from typing import Any, Callable, Optional, Type
3+
from warnings import warn
44

55
from aiohttp import ClientSession
6+
from deprecated import deprecated
67
from langchain_core.tools import StructuredTool
78
from pydantic import BaseModel
89

@@ -183,13 +184,17 @@ def _process_auth_params(self, manifest: ManifestSchema) -> None:
183184
# If none of the permitted auth sources of a parameter are
184185
# registered, raise a warning message to the user.
185186
if not self._validate_auth(tool_name):
186-
warnings.warn(
187+
warn(
187188
f"Some parameters of tool {tool_name} require authentication, but no valid auth sources are registered. Please register the required sources before use."
188189
)
189190

191+
@deprecated("Please use `add_auth_token` instead.")
190192
def add_auth_header(
191193
self, auth_source: str, get_id_token: Callable[[], str]
192194
) -> None:
195+
self.add_auth_token(auth_source, get_id_token)
196+
197+
def add_auth_token(self, auth_source: str, get_id_token: Callable[[], str]) -> None:
193198
"""
194199
Registers a function to retrieve an ID token for a given authentication
195200
source.
@@ -201,23 +206,39 @@ def add_auth_header(
201206
self._id_token_getters[auth_source] = get_id_token
202207

203208
async def load_tool(
204-
self, tool_name: str, auth_headers: dict[str, Callable[[], str]] = {}
209+
self,
210+
tool_name: str,
211+
auth_tokens: dict[str, Callable[[], str]] = {},
212+
auth_headers: Optional[dict[str, Callable[[], str]]] = None,
205213
) -> StructuredTool:
206214
"""
207215
Loads the tool, with the given tool name, from the Toolbox service.
208216
209217
Args:
210218
tool_name: The name of the tool to load.
211-
auth_headers: A mapping of authentication source names to
219+
auth_tokens: A mapping of authentication source names to
212220
functions that retrieve ID tokens. If provided, these will
213221
override or be added to the existing ID token getters.
214222
Default: Empty.
215223
216224
Returns:
217225
A tool loaded from the Toolbox
218226
"""
219-
for auth_source, get_id_token in auth_headers.items():
220-
self.add_auth_header(auth_source, get_id_token)
227+
if auth_headers:
228+
if auth_tokens:
229+
warn(
230+
"Both `auth_tokens` and `auth_headers` are provided. `auth_headers` is deprecated, and `auth_tokens` will be used.",
231+
DeprecationWarning,
232+
)
233+
else:
234+
warn(
235+
"Argument `auth_headers` is deprecated. Use `auth_tokens` instead.",
236+
DeprecationWarning,
237+
)
238+
auth_tokens = auth_headers
239+
240+
for auth_source, get_id_token in auth_tokens.items():
241+
self.add_auth_token(auth_source, get_id_token)
221242

222243
manifest: ManifestSchema = await self._load_tool_manifest(tool_name)
223244

@@ -228,7 +249,8 @@ async def load_tool(
228249
async def load_toolset(
229250
self,
230251
toolset_name: Optional[str] = None,
231-
auth_headers: dict[str, Callable[[], str]] = {},
252+
auth_tokens: dict[str, Callable[[], str]] = {},
253+
auth_headers: Optional[dict[str, Callable[[], str]]] = None,
232254
) -> list[StructuredTool]:
233255
"""
234256
Loads tools from the Toolbox service, optionally filtered by toolset
@@ -237,16 +259,29 @@ async def load_toolset(
237259
Args:
238260
toolset_name: The name of the toolset to load.
239261
Default: None. If not provided, then all the tools are loaded.
240-
auth_headers: A mapping of authentication source names to
262+
auth_tokens: A mapping of authentication source names to
241263
functions that retrieve ID tokens. If provided, these will
242264
override or be added to the existing ID token getters.
243265
Default: Empty.
244266
245267
Returns:
246268
A list of all tools loaded from the Toolbox.
247269
"""
248-
for auth_source, get_id_token in auth_headers.items():
249-
self.add_auth_header(auth_source, get_id_token)
270+
if auth_headers:
271+
if auth_tokens:
272+
warn(
273+
"Both `auth_tokens` and `auth_headers` are provided. `auth_headers` is deprecated, and `auth_tokens` will be used.",
274+
DeprecationWarning,
275+
)
276+
else:
277+
warn(
278+
"Argument `auth_headers` is deprecated. Use `auth_tokens` instead.",
279+
DeprecationWarning,
280+
)
281+
auth_tokens = auth_headers
282+
283+
for auth_source, get_id_token in auth_tokens.items():
284+
self.add_auth_token(auth_source, get_id_token)
250285

251286
tools: list[StructuredTool] = []
252287
manifest: ManifestSchema = await self._load_toolset_manifest(toolset_name)

src/toolbox_langchain_sdk/utils.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import json
22
import warnings
33
from typing import Any, Callable, Optional, Type, cast
4+
from warnings import warn
45

56
from aiohttp import ClientSession
7+
from deprecated import deprecated
68
from pydantic import BaseModel, Field, create_model
79

810

@@ -100,22 +102,27 @@ def _parse_type(type_: str) -> Any:
100102
raise ValueError(f"Unsupported schema type: {type_}")
101103

102104

105+
@deprecated("Please use `_get_auth_tokens` instead.")
103106
def _get_auth_headers(id_token_getters: dict[str, Callable[[], str]]) -> dict[str, str]:
107+
return _get_auth_tokens(id_token_getters)
108+
109+
110+
def _get_auth_tokens(id_token_getters: dict[str, Callable[[], str]]) -> dict[str, str]:
104111
"""
105112
Gets id tokens for the given auth sources in the getters map and returns
106-
headers to be included in tool invocation.
113+
tokens to be included in tool invocation.
107114
108115
Args:
109116
id_token_getters: A dict that maps auth source names to the functions
110117
that return its ID token.
111118
112119
Returns:
113-
A dictionary of headers to be included in the tool invocation.
120+
A dictionary of tokens to be included in the tool invocation.
114121
"""
115-
auth_headers = {}
122+
auth_tokens = {}
116123
for auth_source, get_id_token in id_token_getters.items():
117-
auth_headers[f"{auth_source}_token"] = get_id_token()
118-
return auth_headers
124+
auth_tokens[f"{auth_source}_token"] = get_id_token()
125+
return auth_tokens
119126

120127

121128
async def _invoke_tool(
@@ -141,20 +148,20 @@ async def _invoke_tool(
141148
invocation.
142149
"""
143150
url = f"{url}/api/tool/{tool_name}/invoke"
144-
auth_headers = _get_auth_headers(id_token_getters)
151+
auth_tokens = _get_auth_tokens(id_token_getters)
145152

146153
# ID tokens contain sensitive user information (claims). Transmitting these
147154
# over HTTP exposes the data to interception and unauthorized access. Always
148155
# use HTTPS to ensure secure communication and protect user privacy.
149-
if auth_headers and not url.startswith("https://"):
150-
warnings.warn(
156+
if auth_tokens and not url.startswith("https://"):
157+
warn(
151158
"Sending ID token over HTTP. User data may be exposed. Use HTTPS for secure communication."
152159
)
153160

154161
async with session.post(
155162
url,
156163
json=_convert_none_to_empty_string(data),
157-
headers=auth_headers,
164+
headers=auth_tokens,
158165
) as response:
159166
response.raise_for_status()
160167
return await response.json()

0 commit comments

Comments
 (0)