5 releases (2 stable)
Uses new Rust 2024
| 3.0.0 | Mar 3, 2026 |
|---|---|
| 1.0.0 | Feb 9, 2026 |
| 0.9.91 | Feb 9, 2026 |
| 0.9.9 | Feb 9, 2026 |
| 0.1.0 | Feb 7, 2026 |
#1733 in Asynchronous
Used in 5 crates
(4 directly)
190KB
4.5K
SLoC
Capability Layer API
ftooling provides the tool registration and execution runtime for Fiddlesticks.
It is designed to plug into fchat tool loops while staying provider-agnostic by using fprovider tool contracts.
Responsibilities
- Register tools and expose their
ToolDefinitionmetadata - Execute tool calls from model output (
fprovider::ToolCall) - Return tool outputs as structured execution results
- Offer runtime hooks and timeout controls for observability and resilience
ftooling does not:
- Decide when to call tools during a conversation (
fchatowns that) - Persist conversation state (
fmemoryowns that) - Implement model transport/provider adapters (
fproviderowns that)
Add dependency
[dependencies]
ftooling = { path = "../ftooling" }
fprovider = { path = "../fprovider" }
Core types
Tool: trait for executable capabilitiesToolRegistry: registry keyed by tool nameToolRuntime: runtime contract for tool executionDefaultToolRuntime: registry-backed runtime implementationToolExecutionContext: session/trace metadata passed to toolsToolExecutionResult: normalized output payloadToolError: typed error with retryability and optional call metadata
Easiest registration path
You can register tools without implementing a custom struct.
Sync closure
use fprovider::ToolDefinition;
use ftooling::prelude::*;
let mut registry = ToolRegistry::new();
registry.register_sync_fn(
ToolDefinition {
name: "echo".to_string(),
description: "Echo input".to_string(),
input_schema: "{\"type\":\"string\"}".to_string(),
},
|args, _ctx| Ok(args),
);
Async closure
use fprovider::ToolDefinition;
use ftooling::prelude::*;
let mut registry = ToolRegistry::new();
registry.register_fn(
ToolDefinition {
name: "uppercase".to_string(),
description: "Uppercase input".to_string(),
input_schema: "{\"type\":\"string\"}".to_string(),
},
|args, _ctx| async move { Ok(args.to_uppercase()) },
);
Runtime usage
use std::sync::Arc;
use fprovider::ToolCall;
use ftooling::prelude::*;
async fn run_once(registry: ToolRegistry) -> Result<ToolExecutionResult, ToolError> {
let runtime = DefaultToolRuntime::new(Arc::new(registry))
.with_timeout(std::time::Duration::from_secs(2));
runtime
.execute(
ToolCall {
id: "call_1".to_string(),
name: "echo".to_string(),
arguments: "hello".to_string(),
},
ToolExecutionContext::new("session-1").with_trace_id("trace-abc"),
)
.await
}
Argument helper utilities
ftooling exposes lightweight JSON helpers so tool closures do not need to repeat parsing boilerplate:
parse_json_value(args_json)parse_json_object(args_json)required_string(&args, key)
use ftooling::prelude::*;
let args = parse_json_object("{\"query\":\"rust\"}")?;
let query = required_string(&args, "query")?;
let _ = query;
Hooks and timeout
DefaultToolRuntime::with_hooks(...)attaches runtime lifecycle hooksDefaultToolRuntime::with_timeout(...)enforces per-call timeout- Hook events include start/success/failure with elapsed duration
- Hook order contract:
on_execution_start- exactly one of
on_execution_successoron_execution_failure
use std::sync::Arc;
use std::time::Duration;
use ftooling::prelude::*;
let runtime = DefaultToolRuntime::new(Arc::new(ToolRegistry::new()))
.with_hooks(Arc::new(NoopToolRuntimeHooks))
.with_timeout(Duration::from_millis(500));
let _ = runtime;
Error model
ToolErrorKind variants:
NotFoundInvalidArgumentsExecutionTimeoutUnauthorizedOther
ToolError includes:
retryablefor policy decisions- optional
tool_nameandtool_call_idfor richer context - helper methods:
is_retryable(),is_user_error()
When errors are produced by DefaultToolRuntime, tool_name and tool_call_id are populated automatically.
Integration with fchat
fchat can consume ftooling::ToolRuntime directly:
- configure on
ChatServicevia.with_tool_runtime(...) - cap loops with
.with_max_tool_round_trips(...) fchatmapsToolErrortoChatErrorKind::Tooling
Dependencies
~0.8–1.5MB
~29K SLoC