ftooling 3.0.0

Tool library for the fiddlesticks agent harness framework
Documentation

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 ToolDefinition metadata
  • 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 (fchat owns that)
  • Persist conversation state (fmemory owns that)
  • Implement model transport/provider adapters (fprovider owns that)

Add dependency

[dependencies]
ftooling = { path = "../ftooling" }
fprovider = { path = "../fprovider" }

Core types

  • Tool: trait for executable capabilities
  • ToolRegistry: registry keyed by tool name
  • ToolRuntime: runtime contract for tool execution
  • DefaultToolRuntime: registry-backed runtime implementation
  • ToolExecutionContext: session/trace metadata passed to tools
  • ToolExecutionResult: normalized output payload
  • ToolError: 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 hooks
  • DefaultToolRuntime::with_timeout(...) enforces per-call timeout
  • Hook events include start/success/failure with elapsed duration
  • Hook order contract:
    1. on_execution_start
    2. exactly one of on_execution_success or on_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:

  • NotFound
  • InvalidArguments
  • Execution
  • Timeout
  • Unauthorized
  • Other

ToolError includes:

  • retryable for policy decisions
  • optional tool_name and tool_call_id for 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 ChatService via .with_tool_runtime(...)
  • cap loops with .with_max_tool_round_trips(...)
  • fchat maps ToolError to ChatErrorKind::Tooling