A tool the model may call. See spec §5.2 and §15.
Layer A — the struct itself is pure data (:name, :description, :schema,
:metadata, :manual 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 (Phase 18)
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 chat/3 (raw stateless flow).
Default: false — the tool is auto-executed under mode: :auto (today's
behaviour). The flag is silent under mode: :manual whole-loop manual
(spec §12); the whole-loop short-circuit fires before the per-tool
partition runs.
See spec §12.4 (per-tool manual) and Phase 18 design.
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. See spec §5.2 and §12.3 (ask-user).
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).
@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. See spec §5.2 and §12.3 (ask-user).
@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.
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