Packages

A Gleam library for building and orchestrating agents on the BEAM.

Current section

Files

Jump to
pig src pig@tool.erl
Raw

src/pig@tool.erl

-module(pig@tool).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/pig/tool.gleam").
-export([call_id/1, tool_name/1, error_message/1, new_registry/0, register/2, lookup/2, execute_tool/2, list_definitions/1, list_tool_prompts/1]).
-export_type([tool_call_context/0, tool_call_batch_error/0, tool_error/0, tool/0, tool_registry/0, tool_prompt/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Tool types and registry for the pig agent library.\n"
"\n"
" A `Tool` pairs a `ToolDefinition` (schema for the LLM) with a handler\n"
" function. The handler receives library-owned immutable invocation context\n"
" and parsed `dynamic.Dynamic` arguments, and returns a `json.Json` result\n"
" or structured `ToolError`.\n"
).
-opaque tool_call_context() :: {tool_call_context, binary(), binary()}.
-type tool_call_batch_error() :: {empty_tool_call_id, integer()} |
{duplicate_tool_call_id, binary()}.
-type tool_error() :: {tool_error, binary()} |
{invalid_tool_call_batch, tool_call_batch_error()}.
-type tool() :: {tool,
pig_protocol@tool_definition:tool_definition(),
fun((tool_call_context(), gleam@dynamic:dynamic_()) -> {ok,
gleam@json:json()} |
{error, tool_error()})}.
-type tool_registry() :: {tool_registry, gleam@dict:dict(binary(), tool())}.
-type tool_prompt() :: {tool_prompt, binary(), binary()}.
-file("src/pig/tool.gleam", 26).
?DOC(" Return the protocol call ID for this invocation.\n").
-spec call_id(tool_call_context()) -> binary().
call_id(Context) ->
erlang:element(2, Context).
-file("src/pig/tool.gleam", 31).
?DOC(" Return the requested tool name for this invocation.\n").
-spec tool_name(tool_call_context()) -> binary().
tool_name(Context) ->
erlang:element(3, Context).
-file("src/pig/tool.gleam", 48).
?DOC(" Render a structured tool error for tool-result messages and users.\n").
-spec error_message(tool_error()) -> binary().
error_message(Error) ->
case Error of
{tool_error, Message} ->
Message;
{invalid_tool_call_batch, {empty_tool_call_id, Index}} ->
<<"invalid tool call batch: empty call ID at index "/utf8,
(erlang:integer_to_binary(Index))/binary>>;
{invalid_tool_call_batch, {duplicate_tool_call_id, Call_id}} ->
<<<<"invalid tool call batch: duplicate call ID \""/utf8,
Call_id/binary>>/binary,
"\""/utf8>>
end.
-file("src/pig/tool.gleam", 73).
?DOC(" Create an empty tool registry.\n").
-spec new_registry() -> tool_registry().
new_registry() ->
{tool_registry, maps:new()}.
-file("src/pig/tool.gleam", 79).
?DOC(
" Register a tool in the registry. If a tool with the same name already\n"
" exists, it is overwritten.\n"
).
-spec register(tool_registry(), tool()) -> tool_registry().
register(Registry, Tool) ->
{tool_registry,
gleam@dict:insert(
erlang:element(2, Registry),
erlang:element(2, erlang:element(2, Tool)),
Tool
)}.
-file("src/pig/tool.gleam", 88).
?DOC(" Look up a tool by name. Returns `Error(Nil)` if not found.\n").
-spec lookup(tool_registry(), binary()) -> {ok, tool()} | {error, nil}.
lookup(Registry, Name) ->
gleam_stdlib:map_get(erlang:element(2, Registry), Name).
-file("src/pig/tool.gleam", 97).
?DOC(
" Execute a protocol tool call through its registry-selected handler.\n"
"\n"
" The call name selects the registered tool; that same call is decoded and\n"
" supplies the immutable context passed to its handler. Returns structured\n"
" errors for unknown tools, malformed arguments, or handler failures.\n"
).
-spec execute_tool(tool_registry(), pig_protocol@message:tool_call()) -> {ok,
gleam@json:json()} |
{error, tool_error()}.
execute_tool(Registry, Call) ->
case lookup(Registry, erlang:element(3, Call)) of
{ok, Tool} ->
case gleam@json:parse(
erlang:element(4, Call),
{decoder, fun gleam@dynamic@decode:decode_dynamic/1}
) of
{ok, Arguments} ->
(erlang:element(3, Tool))(
{tool_call_context,
erlang:element(2, Call),
erlang:element(3, Call)},
Arguments
);
{error, _} ->
{error,
{tool_error,
<<<<"invalid JSON arguments for tool \""/utf8,
(erlang:element(3, Call))/binary>>/binary,
"\""/utf8>>}}
end;
{error, nil} ->
{error,
{tool_error,
<<<<"unknown tool \""/utf8,
(erlang:element(3, Call))/binary>>/binary,
"\""/utf8>>}}
end.
-file("src/pig/tool.gleam", 121).
?DOC(" List all tool definitions in the registry.\n").
-spec list_definitions(tool_registry()) -> list(pig_protocol@tool_definition:tool_definition()).
list_definitions(Registry) ->
_pipe = erlang:element(2, Registry),
_pipe@1 = maps:values(_pipe),
gleam@list:map(_pipe@1, fun(T) -> erlang:element(2, T) end).
-file("src/pig/tool.gleam", 134).
?DOC(
" Extract name and description from each tool in the registry.\n"
" Used to auto-compose an \"Available tools\" section in the system prompt.\n"
).
-spec list_tool_prompts(tool_registry()) -> list(tool_prompt()).
list_tool_prompts(Registry) ->
_pipe = erlang:element(2, Registry),
_pipe@1 = maps:values(_pipe),
gleam@list:map(
_pipe@1,
fun(T) ->
{tool_prompt,
erlang:element(2, erlang:element(2, T)),
erlang:element(3, erlang:element(2, T))}
end
).