AI.Tools behaviour (fnord v0.9.22)
View SourceThis module defines the behaviour for tool calls. Defining a new tool
requires implementing the spec/0 and call/2 functions.
The spec/0 function should return a map that describes the tool's
capabilities and arguments, using a map to represent the OpenAPI spec.
The call/2 function generates the tool call response. It accepts the
requesting AI.Completion's struct and a map derived from the parsed JSON
provided by the agent, containing the tool call arguments. Note that, because
the arguments are parsed from JSON, the keys will all be strings. Whether
those are converted to symbols is between the tool implementation and the
code it calls. What happens behind closed APIs is none of MY business.
Skeleton Implementation
defmodule AI.Tools.MyNewTool do
@behaviour AI.Tools
@impl AI.Tools
def async?(), do: true
@impl AI.Tools
def ui_note_on_request(_args) do
{"Doing something", "This tool is doing something."}
end
@impl AI.Tools
def ui_note_on_result(_args, _result) do
{"Did something", "This tool did something."}
end
@impl AI.Tools
def read_args(args) do
{:ok, args}
end
@impl AI.Tools
def spec() do
%{
type: "function",
function: %{
name: "something_tool",
description: "This tool does something.",
parameters: %{
additionalProperties: false,
type: "object",
required: ["thing"],
properties: %{
thing: %{
type: "string",
description: "The thing to do."
}
}
}
}
}
end
@impl AI.Tools
def call(args) do
{:ok, "IMPLEMENT ME"}
end
end
Summary
Callbacks
Returns true if the tool is asynchronous, false otherwise. If false, when
the LLM performs a multi-tool call, this tool will be called synchronously,
after all other (asynchronous) tools have been called.
Calls the tool with the provided arguments and returns the response as an :ok tuple.
Returns true if the tool is available for use, false otherwise. This is used to determine whether the tool can be used in the current context, such as whether the tool is available in the current project or if it requires specific conditions to be met (e.g., a project being set, availability of an external tool like ripgrep, etc.).
Reads the arguments and returns a map of the arguments if they are valid.
This is used to validate args before the tool is called. The result is what
is passed to call/2, ui_note_on_request/1, and ui_note_on_result/2.
Returns the OpenAPI spec for the tool as an elixir map.
Returns a message to be displayed when a tool call fails. May return :default, :ignore, a binary message, or a {label, detail} tuple.
Return either a short string or a string tuple of label + detail to be displayed when the tool is called.
Return either a short string or a string tuple of label + detail to be displayed when the tool call is successful.
Functions
Returns a toolbox that includes all tools (basic, read/write, coding, task,
and web tools).
Returns a toolbox that includes all generally available tools and frobs.
Given a list of modules, returns a map from tool_name => module, using each module's spec().function.name value as the key.
Retrieves an argument from the parsed arguments map. Empty strings or nil
values will return an error indicating a missing argument.
Conditionally adds UI tools to the toolbox if the current environment supports it (i.e., if we're running in a TTY and not in quiet mode).
Returns the allowed tool tags for skills. Skills.Runtime uses this to avoid duplicating the list.
Returns the deterministic skill tool tags order, excluding "basic", used when applying tags.
Generate a list of tool specs from a toolbox map.
Adds the coding tools to the toolbox. Coding tools mutate the codebase, but do so in an organized, planned way, rather than directly managing files.
Adds user-defined frobs to the toolbox. Frobs are local tooling (linters, formatters, test runners, kubectl wrappers, etc.) so they should only be given to agents that test, validate, or investigate - not to agents that only produce structured output (e.g. the Patcher).
Adds MCP (Model Context Protocol) tools to the toolbox. MCP tools are externally hosted tool servers enabled at the project or global level. Starts the MCP service lazily on first call.
Adds the review tools to the toolbox. Review tools are read-only agents that perform multi-specialist code review.
Adds the read/write tools to the toolbox. This includes tools that can directly perform file edits, shell commands, and other read/write operations.
Adds the skills tools to the toolbox. Skills are specialized tools that should only be included when explicitly requested.
Adds the task management tools to the toolbox. This includes tools that can create and manage task lists.
Adds interactive UI tools to the toolbox.
Adds the web tools to the toolbox. This includes tools that can access the web, such as web search.
Types
@type args_error() :: missing_arg_error() | invalid_arg_error()
@type entry() :: Store.Project.Entry.t()
@type entry_not_found() :: {:error, :enoent}
@type frob_error() :: {:error, non_neg_integer(), binary()}
@type invalid_arg_error() :: {:error, :invalid_argument, binary()}
@type json_parse_error() :: {:error, Exception.t()}
@type missing_arg_error() :: {:error, :missing_argument, binary()}
@type project() :: Store.Project.t()
@type project_name() :: binary() | nil
@type project_not_found() :: {:error, :project_not_found} | {:error, :project_not_set}
@type raw_tool_result() :: :ok | {:ok, any()} | {:error, any()} | :error | args_error() | frob_error()
@type something_not_found() :: project_not_found() | entry_not_found()
@type tool_error() :: {:error, binary()}
@type tool_name() :: binary()
@type tool_result() :: {:ok, binary()} | unknown_tool_error() | args_error() | tool_error() | frob_error()
@type unknown_tool_error() :: {:error, :unknown_tool, binary()}
@type unparsed_args() :: binary()
Callbacks
@callback async?() :: boolean()
Returns true if the tool is asynchronous, false otherwise. If false, when
the LLM performs a multi-tool call, this tool will be called synchronously,
after all other (asynchronous) tools have been called.
@callback call(args :: map()) :: raw_tool_result()
Calls the tool with the provided arguments and returns the response as an :ok tuple.
@callback is_available?() :: boolean()
Returns true if the tool is available for use, false otherwise. This is used to determine whether the tool can be used in the current context, such as whether the tool is available in the current project or if it requires specific conditions to be met (e.g., a project being set, availability of an external tool like ripgrep, etc.).
@callback read_args(args :: map()) :: {:ok, map()} | args_error()
Reads the arguments and returns a map of the arguments if they are valid.
This is used to validate args before the tool is called. The result is what
is passed to call/2, ui_note_on_request/1, and ui_note_on_result/2.
@callback spec() :: map()
Returns the OpenAPI spec for the tool as an elixir map.
@callback tool_call_failure_message(args :: map(), reason :: any()) :: :default | :ignore | binary() | {binary(), binary()}
Returns a message to be displayed when a tool call fails. May return :default, :ignore, a binary message, or a {label, detail} tuple.
Return either a short string or a string tuple of label + detail to be displayed when the tool is called.
@callback ui_note_on_result(args :: map(), result :: any()) :: {binary(), binary()} | binary() | nil
Return either a short string or a string tuple of label + detail to be displayed when the tool call is successful.
Functions
@spec all_tools() :: toolbox()
Returns a toolbox that includes all tools (basic, read/write, coding, task,
and web tools).
WARNING: all_tools/0 includes mutational tools (file edits, shell commands,
coding tools). For normal runs, prefer basic_tools/0 with selective
with_* merges. Reserve all_tools/0 for cases requiring full lookup
fidelity (e.g., replay, diagnostics).
@spec basic_tools() :: toolbox()
Returns a toolbox that includes all generally available tools and frobs.
Given a list of modules, returns a map from tool_name => module, using each module's spec().function.name value as the key.
@spec get_arg(parsed_args(), atom() | binary()) :: {:ok, any()} | missing_arg_error()
Retrieves an argument from the parsed arguments map. Empty strings or nil
values will return an error indicating a missing argument.
@spec get_entry(binary()) :: {:ok, entry()} | something_not_found()
@spec get_entry(Store.Project.t(), binary()) :: {:ok, entry()} | entry_not_found()
@spec get_file_contents(binary()) :: {:ok, binary()} | something_not_found()
@spec get_project() :: {:ok, project()} | project_not_found()
@spec has_indexed_project() :: boolean()
Conditionally adds UI tools to the toolbox if the current environment supports it (i.e., if we're running in a TTY and not in quiet mode).
@spec perform_tool_call(tool_name(), parsed_args(), toolbox() | nil) :: tool_result()
@spec required_arg_error(binary()) :: missing_arg_error()
@spec skill_tool_tags() :: [String.t()]
Returns the allowed tool tags for skills. Skills.Runtime uses this to avoid duplicating the list.
@spec stable_skill_tool_tag_order() :: [String.t()]
Returns the deterministic skill tool tags order, excluding "basic", used when applying tags.
@spec tool_module(tool_name(), toolbox() | nil) :: {:ok, module()} | unknown_tool_error()
Generate a list of tool specs from a toolbox map.
@spec with_args(tool_name(), parsed_args(), (parsed_args() -> any()), toolbox() | nil) :: any()
Adds the coding tools to the toolbox. Coding tools mutate the codebase, but do so in an organized, planned way, rather than directly managing files.
Adds user-defined frobs to the toolbox. Frobs are local tooling (linters, formatters, test runners, kubectl wrappers, etc.) so they should only be given to agents that test, validate, or investigate - not to agents that only produce structured output (e.g. the Patcher).
Adds MCP (Model Context Protocol) tools to the toolbox. MCP tools are externally hosted tool servers enabled at the project or global level. Starts the MCP service lazily on first call.
Adds the review tools to the toolbox. Review tools are read-only agents that perform multi-specialist code review.
Adds the read/write tools to the toolbox. This includes tools that can directly perform file edits, shell commands, and other read/write operations.
Adds the skills tools to the toolbox. Skills are specialized tools that should only be included when explicitly requested.
Adds the task management tools to the toolbox. This includes tools that can create and manage task lists.
Adds interactive UI tools to the toolbox.
These tools are intended for user-in-the-loop flows where the agent needs to ask the user a question and proceed based on the answer.
Adds the web tools to the toolbox. This includes tools that can access the web, such as web search.