@@ -157,6 +157,9 @@ async def load_tool(
157
157
for execution. The specific arguments and behavior of the callable
158
158
depend on the tool itself.
159
159
160
+ Raises:
161
+ ValueError: If the loaded tool instance fails to utilize at least
162
+ one provided parameter or auth token (if any provided).
160
163
"""
161
164
# Resolve client headers
162
165
resolved_headers = {
@@ -174,55 +177,130 @@ async def load_tool(
174
177
if name not in manifest .tools :
175
178
# TODO: Better exception
176
179
raise Exception (f"Tool '{ name } ' not found!" )
177
- tool , _ , _ = self .__parse_tool (
180
+ tool , used_auth_keys , used_bound_keys = self .__parse_tool (
178
181
name ,
179
182
manifest .tools [name ],
180
183
auth_token_getters ,
181
184
bound_params ,
182
185
self .__client_headers ,
183
186
)
184
187
188
+ provided_auth_keys = set (auth_token_getters .keys ())
189
+ provided_bound_keys = set (bound_params .keys ())
190
+
191
+ unused_auth = provided_auth_keys - used_auth_keys
192
+ unused_bound = provided_bound_keys - used_bound_keys
193
+
194
+ if unused_auth or unused_bound :
195
+ error_messages = []
196
+ if unused_auth :
197
+ error_messages .append (f"unused auth tokens: { ', ' .join (unused_auth )} " )
198
+ if unused_bound :
199
+ error_messages .append (
200
+ f"unused bound parameters: { ', ' .join (unused_bound )} "
201
+ )
202
+ raise ValueError (
203
+ f"Validation failed for tool '{ name } ': { '; ' .join (error_messages ) } ."
204
+ )
205
+
185
206
return tool
186
207
187
208
async def load_toolset (
188
209
self ,
189
210
name : Optional [str ] = None ,
190
211
auth_token_getters : dict [str , Callable [[], str ]] = {},
191
212
bound_params : Mapping [str , Union [Callable [[], Any ], Any ]] = {},
213
+ strict : bool = False ,
192
214
) -> list [ToolboxTool ]:
193
215
"""
194
216
Asynchronously fetches a toolset and loads all tools defined within it.
195
217
196
218
Args:
197
- name: Name of the toolset to load tools .
219
+ name: Name of the toolset to load. If None, loads the default toolset .
198
220
auth_token_getters: A mapping of authentication service names to
199
221
callables that return the corresponding authentication token.
200
222
bound_params: A mapping of parameter names to bind to specific values or
201
223
callables that are called to produce values as needed.
224
+ strict: If True, raises an error if *any* loaded tool instance fails
225
+ to utilize at least one provided parameter or auth token (if any
226
+ provided). If False (default), raises an error only if a
227
+ user-provided parameter or auth token cannot be applied to *any*
228
+ loaded tool across the set.
202
229
203
230
Returns:
204
231
list[ToolboxTool]: A list of callables, one for each tool defined
205
232
in the toolset.
233
+
234
+ Raises:
235
+ ValueError: If validation fails based on the `strict` flag.
206
236
"""
237
+
207
238
# Resolve client headers
208
239
original_headers = self .__client_headers
209
240
resolved_headers = {
210
241
header_name : await resolve_value (original_headers [header_name ])
211
242
for header_name in original_headers
212
243
}
213
- # Request the definition of the tool from the server
244
+ # Request the definition of the toolset from the server
214
245
url = f"{ self .__base_url } /api/toolset/{ name or '' } "
215
246
async with self .__session .get (url , headers = resolved_headers ) as response :
216
247
json = await response .json ()
217
248
manifest : ManifestSchema = ManifestSchema (** json )
218
249
219
- # parse each tools name and schema into a list of ToolboxTools
220
- tools = [
221
- self .__parse_tool (
222
- n , s , auth_token_getters , bound_params , self .__client_headers
223
- )[0 ]
224
- for n , s in manifest .tools .items ()
225
- ]
250
+ tools : list [ToolboxTool ] = []
251
+ overall_used_auth_keys : set [str ] = set ()
252
+ overall_used_bound_params : set [str ] = set ()
253
+ provided_auth_keys = set (auth_token_getters .keys ())
254
+ provided_bound_keys = set (bound_params .keys ())
255
+
256
+ # parse each tool's name and schema into a list of ToolboxTools
257
+ for tool_name , schema in manifest .tools .items ():
258
+ tool , used_auth_keys , used_bound_keys = self .__parse_tool (
259
+ tool_name ,
260
+ schema ,
261
+ auth_token_getters ,
262
+ bound_params ,
263
+ self .__client_headers ,
264
+ )
265
+ tools .append (tool )
266
+
267
+ if strict :
268
+ unused_auth = provided_auth_keys - used_auth_keys
269
+ unused_bound = provided_bound_keys - used_bound_keys
270
+ if unused_auth or unused_bound :
271
+ error_messages = []
272
+ if unused_auth :
273
+ error_messages .append (
274
+ f"unused auth tokens: { ', ' .join (unused_auth )} "
275
+ )
276
+ if unused_bound :
277
+ error_messages .append (
278
+ f"unused bound parameters: { ', ' .join (unused_bound )} "
279
+ )
280
+ raise ValueError (
281
+ f"Validation failed for tool '{ tool_name } ': { '; ' .join (error_messages ) } ."
282
+ )
283
+ else :
284
+ overall_used_auth_keys .update (used_auth_keys )
285
+ overall_used_bound_params .update (used_bound_keys )
286
+
287
+ unused_auth = provided_auth_keys - overall_used_auth_keys
288
+ unused_bound = provided_bound_keys - overall_used_bound_params
289
+
290
+ if unused_auth or unused_bound :
291
+ error_messages = []
292
+ if unused_auth :
293
+ error_messages .append (
294
+ f"unused auth tokens could not be applied to any tool: { ', ' .join (unused_auth )} "
295
+ )
296
+ if unused_bound :
297
+ error_messages .append (
298
+ f"unused bound parameters could not be applied to any tool: { ', ' .join (unused_bound )} "
299
+ )
300
+ raise ValueError (
301
+ f"Validation failed for toolset '{ name or 'default' } ': { '; ' .join (error_messages ) } ."
302
+ )
303
+
226
304
return tools
227
305
228
306
async def add_headers (
0 commit comments