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@error:anthropic_error()), binary(), integer()}.-type retry_event() :: {retry_event, binary(), integer(), integer(), integer(), anthropic@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", 188).-spec count_messages(list(any())) -> integer().count_messages(Messages) -> erlang: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} -> erlang:length(T); none -> 0 end.-file("src/anthropic/hooks.gleam", 164).?DOC(" Create a request summary from a CreateMessageRequest\n").-spec summarize_request(anthropic@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) -> erlang:length(Blocks).-file("src/anthropic/hooks.gleam", 176).?DOC(" Create a response summary from a CreateMessageResponse\n").-spec summarize_response(anthropic@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@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", 222).?DOC(" Create default hooks (all disabled)\n").-spec default_hooks() -> hooks().default_hooks() -> {hooks, none, none, none, none}.-file("src/anthropic/hooks.gleam", 232).?DOC(" Create hooks with no callbacks (alias for default_hooks)\n").-spec no_hooks() -> hooks().no_hooks() -> default_hooks().-file("src/anthropic/hooks.gleam", 241).?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", 249).?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", 257).?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", 262).?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", 274).?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", 282).?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", 290).?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", 298).?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", 367).?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", 403).-spec get_timestamp_ms() -> integer().get_timestamp_ms() -> os:system_time() div 1000.-file("src/anthropic/hooks.gleam", 412).-spec random_int(integer(), integer()) -> integer().random_int(Min, Max) -> Min + rand:uniform(Max - Min).-file("src/anthropic/hooks.gleam", 392).?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, (erlang:integer_to_binary(Timestamp))/binary>>/binary, "_"/utf8>>/binary, (erlang:integer_to_binary(Random))/binary>>.-file("src/anthropic/hooks.gleam", 417).?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", 310).?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, (erlang:integer_to_binary( 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, (erlang:integer_to_binary( 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, (erlang:integer_to_binary( 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, (erlang:integer_to_binary( erlang:element(3, Event@2) ))/binary>>/binary, "/"/utf8>>/binary, (erlang:integer_to_binary( erlang:element(4, Event@2) ))/binary>>/binary, " delay_ms="/utf8>>/binary, (erlang:integer_to_binary( erlang:element(5, Event@2) ))/binary>>/binary, " request_id="/utf8>>/binary, (erlang:element(7, Event@2))/binary>> ) end ).-file("src/anthropic/hooks.gleam", 447).-spec combine_callbacks( gleam@option:option(fun((HVL) -> nil)), gleam@option:option(fun((HVL) -> nil))) -> gleam@option:option(fun((HVL) -> 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", 429).?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", 464).?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)).