-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 ).