pub struct ToolRegistry<Ctx = ()>{ /* private fields */ }Expand description
A registry of tool handlers, indexed by name.
Generic over context type Ctx which is passed to tool handlers on
execution. Default is () for backwards compatibility.
Provides validation of tool call arguments against their schemas and parallel execution of multiple tool calls.
§Interceptors
Tool execution can be wrapped with interceptors for cross-cutting concerns like logging, approval gates, or rate limiting:
use llm_stack::{ToolRegistry, tool_fn};
use llm_stack::intercept::{InterceptorStack, ToolExec, Approval, ApprovalDecision};
let mut registry: ToolRegistry<()> = ToolRegistry::new()
.with_interceptors(
InterceptorStack::<ToolExec<()>>::new()
.with(Approval::new(|req| {
if req.name.starts_with("dangerous_") {
ApprovalDecision::Deny("Not allowed".into())
} else {
ApprovalDecision::Allow
}
}))
);Implementations§
Source§impl<Ctx: Send + Sync + 'static> ToolRegistry<Ctx>
impl<Ctx: Send + Sync + 'static> ToolRegistry<Ctx>
Sourcepub fn register(
&mut self,
handler: impl ToolHandler<Ctx> + 'static,
) -> &mut Self
pub fn register( &mut self, handler: impl ToolHandler<Ctx> + 'static, ) -> &mut Self
Registers a tool handler.
If a handler with the same name already exists, it is replaced.
Registers a shared tool handler.
Sourcepub fn get(&self, name: &str) -> Option<&Arc<dyn ToolHandler<Ctx>>>
pub fn get(&self, name: &str) -> Option<&Arc<dyn ToolHandler<Ctx>>>
Returns the handler for the given tool name.
Sourcepub fn contains(&self, name: &str) -> bool
pub fn contains(&self, name: &str) -> bool
Returns whether a tool with the given name is registered.
Sourcepub fn definitions(&self) -> Vec<ToolDefinition>
pub fn definitions(&self) -> Vec<ToolDefinition>
Returns the definitions of all registered tools.
Pass this to ChatParams::tools to tell the model which
tools are available.
Sourcepub fn without<'a>(&self, names: impl IntoIterator<Item = &'a str>) -> Self
pub fn without<'a>(&self, names: impl IntoIterator<Item = &'a str>) -> Self
Returns a new registry excluding the named tools.
Useful for creating scoped registries in Master/Worker patterns
where workers should not have access to certain tools (e.g., spawn_task).
§Example
use llm_stack::ToolRegistry;
let master_registry: ToolRegistry<()> = ToolRegistry::new();
// ... register tools ...
// Workers can't spawn or use admin tools
let worker_registry = master_registry.without(["spawn_task", "admin_tool"]);Sourcepub fn only<'a>(&self, names: impl IntoIterator<Item = &'a str>) -> Self
pub fn only<'a>(&self, names: impl IntoIterator<Item = &'a str>) -> Self
Returns a new registry with only the named tools.
Useful for creating minimal registries with specific capabilities.
§Example
use llm_stack::ToolRegistry;
let full_registry: ToolRegistry<()> = ToolRegistry::new();
// ... register tools ...
// Read-only registry with just search tools
let search_registry = full_registry.only(["search_docs", "search_web"]);Sourcepub fn with_interceptors(
self,
interceptors: InterceptorStack<ToolExec<Ctx>>,
) -> Self
pub fn with_interceptors( self, interceptors: InterceptorStack<ToolExec<Ctx>>, ) -> Self
Sets the interceptor stack for all tool executions.
Interceptors run in the order added (first = outermost). They can inspect, modify, or block tool calls before they reach the handler.
§Example
use llm_stack::{ToolRegistry, tool_fn};
use llm_stack::intercept::{InterceptorStack, ToolExec, Approval, ApprovalDecision, Retry};
let registry: ToolRegistry<()> = ToolRegistry::new()
.with_interceptors(
InterceptorStack::<ToolExec<()>>::new()
.with(Approval::new(|req| {
if req.name == "dangerous" {
ApprovalDecision::Deny("Not allowed".into())
} else {
ApprovalDecision::Allow
}
}))
.with(Retry::default())
);Sourcepub async fn execute(&self, call: &ToolCall, ctx: &Ctx) -> ToolResult
pub async fn execute(&self, call: &ToolCall, ctx: &Ctx) -> ToolResult
Executes a single tool call with schema validation and optional retry.
- Looks up the handler by
ToolCall::name - Validates arguments against the tool’s parameter schema
- Runs the call through interceptors (if any)
- Invokes the handler with the provided context
- If the tool has retry configuration and execution fails, retries with exponential backoff
Returns a ToolResult (always succeeds at the outer level).
Execution errors are captured in ToolResult::is_error.
Sourcepub async fn execute_all(
&self,
calls: &[ToolCall],
ctx: &Ctx,
parallel: bool,
) -> Vec<ToolResult>
pub async fn execute_all( &self, calls: &[ToolCall], ctx: &Ctx, parallel: bool, ) -> Vec<ToolResult>
Executes multiple tool calls, preserving order.
When parallel is true, all calls run concurrently via
futures::future::join_all. When false, they run sequentially.