Packages
A well-typed, idiomatic Gleam client for Anthropic's Claude API with streaming support and tool use
Current section
Files
Jump to
Current section
Files
src/anthropic@hooks.erl
-module(anthropic@hooks).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/anthropic/hooks.gleam").
-export([summarize_request/1, summarize_response/1, default_hooks/0, no_hooks/0, with_on_request_start/2, with_on_request_end/2, with_on_retry/2, with_on_stream_event/2, emit_request_start/2, emit_request_end/2, emit_retry/2, emit_stream_event/2, metrics_hooks/1, generate_request_id/0, simple_logging_hooks/0, combine_hooks/2, has_hooks/1]).
-export_type([request_start_event/0, request_end_event/0, retry_event/0, stream_event/0, stream_event_type/0, request_summary/0, response_summary/0, hooks/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(
" Logging and telemetry hooks for observability\n"
"\n"
" This module provides optional hooks for monitoring API interactions,\n"
" including request lifecycle events, errors, and streaming events.\n"
"\n"
" ## Hook Points\n"
"\n"
" - `on_request_start`: Called before sending a request\n"
" - `on_request_end`: Called after receiving a response (success or error)\n"
" - `on_retry`: Called before each retry attempt\n"
" - `on_stream_event`: Called for each streaming event\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let hooks = default_hooks()\n"
" |> with_on_request_start(fn(event) {\n"
" io.println(\"Starting request to \" <> event.endpoint)\n"
" })\n"
" |> with_on_request_end(fn(event) {\n"
" io.println(\"Request completed in \" <> int.to_string(event.duration_ms) <> \"ms\")\n"
" })\n"
"\n"
" let client = new_client_with_hooks(config, hooks)\n"
" ```\n"
).
-type request_start_event() :: {request_start_event,
binary(),
request_summary(),
integer(),
binary()}.
-type request_end_event() :: {request_end_event,
binary(),
integer(),
boolean(),
gleam@option:option(response_summary()),
gleam@option:option(anthropic@types@error:anthropic_error()),
binary(),
integer()}.
-type retry_event() :: {retry_event,
binary(),
integer(),
integer(),
integer(),
anthropic@types@error:anthropic_error(),
binary()}.
-type stream_event() :: {stream_event, stream_event_type(), binary(), integer()}.
-type stream_event_type() :: stream_opened |
message_start |
{content_block_start, integer()} |
{content_block_delta, integer(), binary()} |
{content_block_stop, integer()} |
message_delta |
message_stop |
stream_closed |
{stream_error, binary()}.
-type request_summary() :: {request_summary,
binary(),
integer(),
integer(),
boolean(),
integer(),
boolean()}.
-type response_summary() :: {response_summary,
binary(),
binary(),
gleam@option:option(binary()),
integer(),
integer(),
integer()}.
-type hooks() :: {hooks,
gleam@option:option(fun((request_start_event()) -> nil)),
gleam@option:option(fun((request_end_event()) -> nil)),
gleam@option:option(fun((retry_event()) -> nil)),
gleam@option:option(fun((stream_event()) -> nil))}.
-file("src/anthropic/hooks.gleam", 207).
-spec do_list_length(list(any()), integer()) -> integer().
do_list_length(List, Acc) ->
case List of
[] ->
Acc;
[_ | Rest] ->
do_list_length(Rest, Acc + 1)
end.
-file("src/anthropic/hooks.gleam", 203).
-spec list_length(list(any())) -> integer().
list_length(List) ->
do_list_length(List, 0).
-file("src/anthropic/hooks.gleam", 188).
-spec count_messages(list(any())) -> integer().
count_messages(Messages) ->
list_length(Messages).
-file("src/anthropic/hooks.gleam", 192).
-spec count_tools(gleam@option:option(list(any()))) -> integer().
count_tools(Tools) ->
case Tools of
{some, T} ->
list_length(T);
none ->
0
end.
-file("src/anthropic/hooks.gleam", 164).
?DOC(" Create a request summary from a CreateMessageRequest\n").
-spec summarize_request(anthropic@types@request:create_message_request()) -> request_summary().
summarize_request(Request) ->
{request_summary,
erlang:element(2, Request),
count_messages(erlang:element(3, Request)),
erlang:element(4, Request),
gleam@option:unwrap(erlang:element(10, Request), false),
count_tools(erlang:element(12, Request)),
gleam@option:is_some(erlang:element(5, Request))}.
-file("src/anthropic/hooks.gleam", 199).
-spec count_content_blocks(list(any())) -> integer().
count_content_blocks(Blocks) ->
list_length(Blocks).
-file("src/anthropic/hooks.gleam", 176).
?DOC(" Create a response summary from a CreateMessageResponse\n").
-spec summarize_response(anthropic@types@request:create_message_response()) -> response_summary().
summarize_response(Response) ->
{response_summary,
erlang:element(2, Response),
erlang:element(6, Response),
gleam@option:map(
erlang:element(7, Response),
fun anthropic@types@request:stop_reason_to_string/1
),
erlang:element(2, erlang:element(9, Response)),
erlang:element(3, erlang:element(9, Response)),
count_content_blocks(erlang:element(5, Response))}.
-file("src/anthropic/hooks.gleam", 233).
?DOC(" Create default hooks (all disabled)\n").
-spec default_hooks() -> hooks().
default_hooks() ->
{hooks, none, none, none, none}.
-file("src/anthropic/hooks.gleam", 243).
?DOC(" Create hooks with no callbacks (alias for default_hooks)\n").
-spec no_hooks() -> hooks().
no_hooks() ->
default_hooks().
-file("src/anthropic/hooks.gleam", 252).
?DOC(" Set the on_request_start hook\n").
-spec with_on_request_start(hooks(), fun((request_start_event()) -> nil)) -> hooks().
with_on_request_start(Hooks, Callback) ->
{hooks,
{some, Callback},
erlang:element(3, Hooks),
erlang:element(4, Hooks),
erlang:element(5, Hooks)}.
-file("src/anthropic/hooks.gleam", 260).
?DOC(" Set the on_request_end hook\n").
-spec with_on_request_end(hooks(), fun((request_end_event()) -> nil)) -> hooks().
with_on_request_end(Hooks, Callback) ->
{hooks,
erlang:element(2, Hooks),
{some, Callback},
erlang:element(4, Hooks),
erlang:element(5, Hooks)}.
-file("src/anthropic/hooks.gleam", 268).
?DOC(" Set the on_retry hook\n").
-spec with_on_retry(hooks(), fun((retry_event()) -> nil)) -> hooks().
with_on_retry(Hooks, Callback) ->
{hooks,
erlang:element(2, Hooks),
erlang:element(3, Hooks),
{some, Callback},
erlang:element(5, Hooks)}.
-file("src/anthropic/hooks.gleam", 273).
?DOC(" Set the on_stream_event hook\n").
-spec with_on_stream_event(hooks(), fun((stream_event()) -> nil)) -> hooks().
with_on_stream_event(Hooks, Callback) ->
{hooks,
erlang:element(2, Hooks),
erlang:element(3, Hooks),
erlang:element(4, Hooks),
{some, Callback}}.
-file("src/anthropic/hooks.gleam", 285).
?DOC(" Emit a request start event\n").
-spec emit_request_start(hooks(), request_start_event()) -> nil.
emit_request_start(Hooks, Event) ->
case erlang:element(2, Hooks) of
{some, Callback} ->
Callback(Event);
none ->
nil
end.
-file("src/anthropic/hooks.gleam", 293).
?DOC(" Emit a request end event\n").
-spec emit_request_end(hooks(), request_end_event()) -> nil.
emit_request_end(Hooks, Event) ->
case erlang:element(3, Hooks) of
{some, Callback} ->
Callback(Event);
none ->
nil
end.
-file("src/anthropic/hooks.gleam", 301).
?DOC(" Emit a retry event\n").
-spec emit_retry(hooks(), retry_event()) -> nil.
emit_retry(Hooks, Event) ->
case erlang:element(4, Hooks) of
{some, Callback} ->
Callback(Event);
none ->
nil
end.
-file("src/anthropic/hooks.gleam", 309).
?DOC(" Emit a stream event\n").
-spec emit_stream_event(hooks(), stream_event()) -> nil.
emit_stream_event(Hooks, Event) ->
case erlang:element(5, Hooks) of
{some, Callback} ->
Callback(Event);
none ->
nil
end.
-file("src/anthropic/hooks.gleam", 378).
?DOC(" Create a metrics-focused hook (counts and timings only)\n").
-spec metrics_hooks(fun((binary(), integer()) -> nil)) -> hooks().
metrics_hooks(On_metric) ->
_pipe = default_hooks(),
with_on_request_end(
_pipe,
fun(Event) ->
On_metric(<<"request_duration_ms"/utf8>>, erlang:element(3, Event)),
case erlang:element(4, Event) of
true ->
On_metric(<<"request_success"/utf8>>, 1);
false ->
On_metric(<<"request_failure"/utf8>>, 1)
end,
On_metric(<<"retry_count"/utf8>>, erlang:element(8, Event)),
case erlang:element(5, Event) of
{some, Resp} ->
On_metric(<<"input_tokens"/utf8>>, erlang:element(5, Resp)),
On_metric(<<"output_tokens"/utf8>>, erlang:element(6, Resp));
none ->
nil
end
end
).
-file("src/anthropic/hooks.gleam", 414).
-spec get_timestamp_ms() -> integer().
get_timestamp_ms() ->
os:system_time() div 1000.
-file("src/anthropic/hooks.gleam", 423).
-spec random_int(integer(), integer()) -> integer().
random_int(Min, Max) ->
Min + rand:uniform(Max - Min).
-file("src/anthropic/hooks.gleam", 428).
?DOC(" Simple int to string conversion\n").
-spec int_to_string(integer()) -> binary().
int_to_string(N) ->
erlang:integer_to_binary(N).
-file("src/anthropic/hooks.gleam", 403).
?DOC(" Generate a unique request ID\n").
-spec generate_request_id() -> binary().
generate_request_id() ->
Timestamp = get_timestamp_ms(),
Random = random_int(0, 999999),
<<<<<<"req_"/utf8, (int_to_string(Timestamp))/binary>>/binary, "_"/utf8>>/binary,
(int_to_string(Random))/binary>>.
-file("src/anthropic/hooks.gleam", 436).
?DOC(" Simple logging function\n").
-spec log_message(binary(), binary()) -> nil.
log_message(Level, Message) ->
io:format(<<"[~s] ~s~n"/utf8>>, [Level, Message]).
-file("src/anthropic/hooks.gleam", 321).
?DOC(" Create a simple logging hook that prints to stdout\n").
-spec simple_logging_hooks() -> hooks().
simple_logging_hooks() ->
_pipe = default_hooks(),
_pipe@1 = with_on_request_start(
_pipe,
fun(Event) ->
log_message(
<<"REQUEST_START"/utf8>>,
<<<<<<<<<<<<<<"endpoint="/utf8,
(erlang:element(2, Event))/binary>>/binary,
" model="/utf8>>/binary,
(erlang:element(2, erlang:element(3, Event)))/binary>>/binary,
" messages="/utf8>>/binary,
(int_to_string(
erlang:element(3, erlang:element(3, Event))
))/binary>>/binary,
" request_id="/utf8>>/binary,
(erlang:element(5, Event))/binary>>
)
end
),
_pipe@2 = with_on_request_end(
_pipe@1,
fun(Event@1) -> case erlang:element(4, Event@1) of
true ->
log_message(
<<"REQUEST_END"/utf8>>,
<<<<<<<<<<"endpoint="/utf8,
(erlang:element(2, Event@1))/binary>>/binary,
" duration_ms="/utf8>>/binary,
(int_to_string(erlang:element(3, Event@1)))/binary>>/binary,
" success=true request_id="/utf8>>/binary,
(erlang:element(7, Event@1))/binary>>
);
false ->
log_message(
<<"REQUEST_END"/utf8>>,
<<<<<<<<<<"endpoint="/utf8,
(erlang:element(2, Event@1))/binary>>/binary,
" duration_ms="/utf8>>/binary,
(int_to_string(erlang:element(3, Event@1)))/binary>>/binary,
" success=false request_id="/utf8>>/binary,
(erlang:element(7, Event@1))/binary>>
)
end end
),
with_on_retry(
_pipe@2,
fun(Event@2) ->
log_message(
<<"RETRY"/utf8>>,
<<<<<<<<<<<<<<<<<<"endpoint="/utf8,
(erlang:element(2, Event@2))/binary>>/binary,
" attempt="/utf8>>/binary,
(int_to_string(
erlang:element(3, Event@2)
))/binary>>/binary,
"/"/utf8>>/binary,
(int_to_string(erlang:element(4, Event@2)))/binary>>/binary,
" delay_ms="/utf8>>/binary,
(int_to_string(erlang:element(5, Event@2)))/binary>>/binary,
" request_id="/utf8>>/binary,
(erlang:element(7, Event@2))/binary>>
)
end
).
-file("src/anthropic/hooks.gleam", 466).
-spec combine_callbacks(
gleam@option:option(fun((GOG) -> nil)),
gleam@option:option(fun((GOG) -> nil))
) -> gleam@option:option(fun((GOG) -> nil)).
combine_callbacks(First, Second) ->
case {First, Second} of
{none, none} ->
none;
{{some, F}, none} ->
{some, F};
{none, {some, S}} ->
{some, S};
{{some, F@1}, {some, S@1}} ->
{some,
fun(Event) ->
F@1(Event),
S@1(Event)
end}
end.
-file("src/anthropic/hooks.gleam", 448).
?DOC(" Combine two hooks, running both callbacks when events occur\n").
-spec combine_hooks(hooks(), hooks()) -> hooks().
combine_hooks(First, Second) ->
{hooks,
combine_callbacks(erlang:element(2, First), erlang:element(2, Second)),
combine_callbacks(erlang:element(3, First), erlang:element(3, Second)),
combine_callbacks(erlang:element(4, First), erlang:element(4, Second)),
combine_callbacks(erlang:element(5, First), erlang:element(5, Second))}.
-file("src/anthropic/hooks.gleam", 483).
?DOC(" Check if any hooks are configured\n").
-spec has_hooks(hooks()) -> boolean().
has_hooks(Hooks) ->
((gleam@option:is_some(erlang:element(2, Hooks)) orelse gleam@option:is_some(
erlang:element(3, Hooks)
))
orelse gleam@option:is_some(erlang:element(4, Hooks)))
orelse gleam@option:is_some(erlang:element(5, Hooks)).