Packages

Gleam SDK for the Anthropic Claude API with agentic tool-use loop and BEAM concurrency

Current section

Files

Jump to
claude_gleam src claude@types@tool.erl
Raw

src/claude@types@tool.erl

-module(claude@types@tool).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/claude/types/tool.gleam").
-export([registry/0, register/2, dispatch/3, to_json/1, unregister/2, is_empty/1]).
-export_type([tool/1, erased_tool/0, registry/0, tool_choice/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.
-type tool(HII) :: {tool,
binary(),
binary(),
glon:json_schema(HII),
fun((HII) -> {ok, binary()} | {error, binary()})}.
-type erased_tool() :: {erased_tool,
binary(),
binary(),
gleam@json:json(),
fun((binary()) -> {ok, binary()} | {error, binary()})}.
-opaque registry() :: {registry, gleam@dict:dict(binary(), erased_tool())}.
-type tool_choice() :: {auto, boolean()} |
{any, boolean()} |
{specific_tool, binary(), boolean()} |
no_tools.
-file("src/claude/types/tool.gleam", 28).
?DOC(" Erase a typed tool into the internal representation.\n").
-spec erase(tool(any())) -> erased_tool().
erase(Tool) ->
{erased_tool,
erlang:element(2, Tool),
erlang:element(3, Tool),
glon:to_json(erlang:element(4, Tool)),
fun(Input) -> case glon:decode(erlang:element(4, Tool), Input) of
{ok, Params} ->
(erlang:element(5, Tool))(Params);
{error, _} ->
{error,
<<"Failed to decode tool input for: "/utf8,
(erlang:element(2, Tool))/binary>>}
end end}.
-file("src/claude/types/tool.gleam", 48).
?DOC(" Create an empty tool registry.\n").
-spec registry() -> registry().
registry() ->
{registry, maps:new()}.
-file("src/claude/types/tool.gleam", 54).
?DOC(
" Register a typed tool. Erases the type parameter internally.\n"
" Pipeable: `registry() |> register(weather_tool()) |> register(search_tool())`\n"
).
-spec register(registry(), tool(any())) -> registry().
register(Reg, Tool) ->
T = erase(Tool),
{registry,
gleam@dict:insert(erlang:element(2, Reg), erlang:element(2, T), T)}.
-file("src/claude/types/tool.gleam", 66).
?DOC(
" Dispatch a tool call by name. Used by the tool runner.\n"
"\n"
" The input string is parsed from JSON on each call. This is a known\n"
" trade-off: tool input is stored as a JSON string for conversation history\n"
" serialization, requiring a parse-serialize-parse cycle when the API response\n"
" is decoded and then dispatched. A future optimization could pass the raw\n"
" Dynamic value directly to avoid the redundant JSON round-trip.\n"
).
-spec dispatch(registry(), binary(), binary()) -> {ok, binary()} |
{error, binary()}.
dispatch(Reg, Name, Input) ->
case gleam_stdlib:map_get(erlang:element(2, Reg), Name) of
{ok, Tool} ->
(erlang:element(5, Tool))(Input);
{error, _} ->
{error, <<"Unknown tool: "/utf8, Name/binary>>}
end.
-file("src/claude/types/tool.gleam", 78).
?DOC(" Encode all tools as a JSON array for the API request.\n").
-spec to_json(registry()) -> gleam@json:json().
to_json(Reg) ->
_pipe = maps:values(erlang:element(2, Reg)),
gleam@json:array(
_pipe,
fun(T) ->
gleam@json:object(
[{<<"name"/utf8>>, gleam@json:string(erlang:element(2, T))},
{<<"description"/utf8>>,
gleam@json:string(erlang:element(3, T))},
{<<"input_schema"/utf8>>, erlang:element(4, T)}]
)
end
).
-file("src/claude/types/tool.gleam", 90).
?DOC(" Remove a tool by name.\n").
-spec unregister(registry(), binary()) -> registry().
unregister(Reg, Name) ->
{registry, gleam@dict:delete(erlang:element(2, Reg), Name)}.
-file("src/claude/types/tool.gleam", 95).
?DOC(" Check if the registry is empty.\n").
-spec is_empty(registry()) -> boolean().
is_empty(Reg) ->
gleam@dict:is_empty(erlang:element(2, Reg)).