A tool the model may call — Layer A serializable data plus an optional
runtime :handler.
The struct itself is pure data (:name, :description, :schema,
:metadata, :manual, :compact, :summary are all serializable),
but :handler may be an anonymous function. A tool with a fn handler
is not safe to persist via :erlang.term_to_binary/1; persist
either :handler | nil and re-attach at load time, or use a
{Module, :function} tuple.
Per-tool manual mode
Setting manual: true declares this tool will not be auto-executed by
ALLM.chat/3 even when the call uses mode: :auto. When the assistant
emits a tool call for a manual: true tool, the chat orchestrator halts
the loop with halted_reason: :manual_tool_calls and surfaces the call
in metadata.manual_tool_calls. The caller resolves the tool result
either via ALLM.Session.submit_tool_result/3 (when running through the
Session API) or by appending a :tool message to the thread and
re-issuing ALLM.chat/3 (raw stateless flow).
Default: false — the tool is auto-executed under mode: :auto. The
flag is silent under whole-loop mode: :manual; the whole-loop
short-circuit fires before the per-tool partition runs.
Compact tools
Setting compact: true asks the chat loop to send this tool to the model
as a one-line stub (a summary, the argument names, and a bare
{"type": "object"} schema) instead of its full description and JSON
Schema, alongside a built-in tool_help tool the model can call to read
the full definition on demand. Execution always uses the full tool. The
projection and the meta-tool are provided by ALLM.ToolHelp.
:summary optionally overrides the one-line summary a stub carries;
when nil (the default), the summary is derived from :description.
It must be nil or a string — ALLM.Validate.tool/1 rejects anything
else with {:summary, :not_a_string}.
Default: compact: false — the tool is sent with its full description
and schema. Opting in is per tool, so the most-used tools can stay full
while a long tail is compacted.
See also guides/tools.md.
Summary
Types
Tool handler — called with parsed arguments (arity 1) or with arguments plus a caller-supplied context keyword list (arity 2).
Legal returns from a tool handler.
Types
@type handler() :: (map() -> handler_result()) | (map(), keyword() -> handler_result())
Tool handler — called with parsed arguments (arity 1) or with arguments plus a caller-supplied context keyword list (arity 2).
The arity-2 keyword list carries call context. Standard keys provided by
ALLM.ToolExecutor.Default:
| Key | Type | Notes |
|---|---|---|
:context | term() | The opaque value passed via ALLM.chat(engine, thread, context: ...) or Session.reply(session, msg, context: ...). Caller-defined shape. |
:session_id | String.t() | nil | The %Session{}.id when invoked through the Session API; nil for stateless chat/3 / step/3. |
:tool_call | %ALLM.ToolCall{} | The exact tool call the assistant emitted (:id, :name, :arguments). |
:engine | %ALLM.Engine{} | The engine driving the call — handlers needing to issue downstream LLM calls reuse it via ALLM.generate/3 etc. |
:request_id | String.t() | nil | Telemetry-correlation id from the parent span. |
Custom keys in :context are passed through unchanged. The arity-1 form
is preferred when handlers don't need context; arity-2 is detected at
dispatch time via :erlang.fun_info(handler, :arity).
@type handler_result() :: {:ok, term()} | {:error, term()} | {:ask_user, String.t()} | {:ask_user, String.t(), keyword()} | {:halt, atom(), term()}
Legal returns from a tool handler.
@type schema() :: map()
Functions
Build a %Tool{} from keyword opts.
:name, :description, and :schema are required; omitting any raises
ArgumentError via struct!/2. :handler is optional — a tool may be
declared without a handler when the caller handles tool calls manually.
:manual is optional and defaults to false. It MUST be a boolean
passing :manual with a non-boolean value (including nil) raises
ArgumentError. The defstruct default protects against omission, but
struct!/2 accepts an explicit nil (silently overwriting the default),
so this constructor adds an explicit guard.
:compact is optional, defaults to false, and is guarded the same way:
a non-boolean value (including nil) raises ArgumentError. :summary
is optional, defaults to nil, and is not guarded here — a non-string
summary is reported by ALLM.Validate.tool/1.
Examples
iex> tool = ALLM.Tool.new(name: "weather", description: "weather by city", schema: %{"type" => "object"})
iex> tool.name
"weather"
iex> tool.handler
nil
iex> tool.manual
false
iex> tool = ALLM.Tool.new(name: "charge", description: "charge a card", schema: %{"type" => "object"}, manual: true)
iex> tool.manual
true
iex> tool = ALLM.Tool.new(name: "search", description: "search the web", schema: %{"type" => "object"}, compact: true)
iex> {tool.compact, tool.summary}
{true, nil}