An executable tool for use in agent turns.
Extends Planck.AI.Tool with an execute_fn — the function called when the
LLM requests a tool invocation. The AI tool schema (name, description, parameters)
is extracted when building Planck.AI.Context; execute_fn stays agent-side.
Summary
Types
The function invoked when the LLM requests this tool.
A fully-specified tool: schema fields understood by the LLM plus the
execute_fn that runs when the model calls it.
A UI side effect attached to a tool result — see execute_fn's 3-element
return form. Deliberately not just "the widget to open": what shows up in
the chat transcript to represent this side effect is a different thing
from what the widget itself contains (Planck.Agent.Widget.render/1), and
not every UI side effect involves a widget at all.
Functions
Build a Planck.Agent.Tool from keyword options.
Convert to a Planck.AI.Tool for use in Planck.AI.Context (drops execute_fn).
Validate args against the tool's JSON schema using ExJsonSchema.
Types
@type execute_fn() :: (agent_id :: String.t(), id :: String.t(), args :: map() -> {:ok, String.t()} | {:ok, String.t(), %{ui: ui_content()}} | {:error, String.t()})
The function invoked when the LLM requests this tool.
Receives the calling agent_id, the tool call id (an opaque string from
the provider, used to correlate results), and args (the JSON-decoded
arguments map).
Returns one of:
{:ok, result}— the common case.resultis placed directly into the model's context as tool result text.{:ok, result, %{ui: ui_content()}}— same, plus a UI side effect invisible to the model. The third element is a map, not a keyword list. The%{ui: ...}wrapper is stripped before the text reaches the LLM — seePlanck.Agent's tool-result handling.{:error, reason}—reasonis placed into the model's context the same wayresultwould be.
All text values must be strings. Exceptions and exit signals are caught by the agent and converted to error strings automatically.
@type t() :: %Planck.Agent.Tool{ description: String.t(), execute_fn: execute_fn(), name: String.t(), parameters: map(), widget: module() | nil }
A fully-specified tool: schema fields understood by the LLM plus the
execute_fn that runs when the model calls it.
:name— identifier the model uses to call the tool; must be unique within an agent's tool set:description— natural-language description sent to the model; quality here directly affects how reliably the model uses the tool:parameters— JSON Schema object describing the accepted arguments:execute_fn— the function called with the agent id, tool call id, and decoded args:widget— optional module implementingPlanck.Agent.Widget, pairing this tool with a UI widget.nilfor most tools. SeePlanck.Agent.Sidecar.list_widgets/0, which derives the sidecar's widget list from tools that set this field.
@type ui_content() :: %{kind: :text, text: String.t()} | %{kind: :widget, label: String.t(), widget: String.t(), data: term()}
A UI side effect attached to a tool result — see execute_fn's 3-element
return form. Deliberately not just "the widget to open": what shows up in
the chat transcript to represent this side effect is a different thing
from what the widget itself contains (Planck.Agent.Widget.render/1), and
not every UI side effect involves a widget at all.
%{kind: :text, text: text}— a UI-only note shown in the chat, invisible to the LLM, no widget involved.%{kind: :widget, label: label, widget: widget_id, data: data}— a button in the chat labeledlabel(sidecar-authored and human-readable — the tool decides what invites the click; the widget decides what's inside once opened).widget_idnames aPlanck.Agent.Widget(itsid/0, paired via aPlanck.Agent.Tool's:widgetfield).datais an opaque initial snapshot for that widget's first paint, avoiding a round-trip throughrender/1to open it — optional, may benil.
Functions
Build a Planck.Agent.Tool from keyword options.
Examples
iex> Tool.new(
...> name: "read_file",
...> description: "Read a file",
...> parameters: %{"type" => "object", "properties" => %{"path" => %{"type" => "string"}}, "required" => ["path"]},
...> execute_fn: fn _agent_id, _id, %{"path" => path} -> File.read(path) end
...> )
%Planck.Agent.Tool{name: "read_file", ...}
@spec to_ai_tool(t()) :: Planck.AI.Tool.t()
Convert to a Planck.AI.Tool for use in Planck.AI.Context (drops execute_fn).
Validate args against the tool's JSON schema using ExJsonSchema.
Returns :ok or {:error, message} suitable for returning directly from an
execute_fn. Called automatically by the agent before invoking execute_fn,
so individual tools do not need to duplicate this check.