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@streaming@decoder.erl
-module(anthropic@streaming@decoder).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/anthropic/streaming/decoder.gleam").
-export([decode_event/1]).
-export_type([decode_error/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(
" JSON decoder for streaming events\n"
"\n"
" This module decodes JSON data from SSE events into typed StreamEvent values.\n"
" It handles all Anthropic streaming event types including message_start,\n"
" content_block_start, content_block_delta, content_block_stop,\n"
" message_delta, message_stop, ping, and error events.\n"
).
-type decode_error() :: {json_parse_error, binary()} |
{unknown_event_type, binary()} |
{missing_field, binary()} |
{invalid_field_value, binary(), binary()}.
-file("src/anthropic/streaming/decoder.gleam", 150).
?DOC(" Decoder for content block in content_block_start\n").
-spec content_block_start_decoder() -> gleam@dynamic@decode:decoder(anthropic@types@message:content_block()).
content_block_start_decoder() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Block_type) -> case Block_type of
<<"text"/utf8>> ->
gleam@dynamic@decode:optional_field(
<<"text"/utf8>>,
<<""/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Text) ->
gleam@dynamic@decode:success({text_block, Text})
end
);
<<"tool_use"/utf8>> ->
gleam@dynamic@decode:field(
<<"id"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Id) ->
gleam@dynamic@decode:field(
<<"name"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Name) ->
gleam@dynamic@decode:success(
{tool_use_block, Id, Name, <<""/utf8>>}
)
end
)
end
);
_ ->
gleam@dynamic@decode:success({text_block, <<""/utf8>>})
end end
).
-file("src/anthropic/streaming/decoder.gleam", 193).
?DOC(" Decoder for delta content\n").
-spec delta_decoder() -> gleam@dynamic@decode:decoder(anthropic@types@streaming:content_block_delta()).
delta_decoder() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Delta_type) -> case Delta_type of
<<"text_delta"/utf8>> ->
gleam@dynamic@decode:field(
<<"text"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Text) ->
gleam@dynamic@decode:success(
{text_content_delta, {text_delta, Text}}
)
end
);
<<"input_json_delta"/utf8>> ->
gleam@dynamic@decode:field(
<<"partial_json"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Partial_json) ->
gleam@dynamic@decode:success(
{input_json_content_delta,
{input_json_delta, Partial_json}}
)
end
);
_ ->
gleam@dynamic@decode:success(
{text_content_delta, {text_delta, <<""/utf8>>}}
)
end end
).
-file("src/anthropic/streaming/decoder.gleam", 273).
?DOC(" Decoder for message delta usage\n").
-spec message_delta_usage_decoder() -> gleam@dynamic@decode:decoder(anthropic@types@streaming:message_delta_usage()).
message_delta_usage_decoder() ->
gleam@dynamic@decode:field(
<<"output_tokens"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Output_tokens) ->
gleam@dynamic@decode:success({message_delta_usage, Output_tokens})
end
).
-file("src/anthropic/streaming/decoder.gleam", 293).
?DOC(" Decoder for error inner content\n").
-spec error_inner_decoder() -> gleam@dynamic@decode:decoder(anthropic@types@streaming:stream_error()).
error_inner_decoder() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Error_type) ->
gleam@dynamic@decode:field(
<<"message"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Message) ->
gleam@dynamic@decode:success(
{stream_error, Error_type, Message}
)
end
)
end
).
-file("src/anthropic/streaming/decoder.gleam", 304).
?DOC(" Decoder for Usage\n").
-spec usage_decoder() -> gleam@dynamic@decode:decoder(anthropic@types@request:usage()).
usage_decoder() ->
gleam@dynamic@decode:field(
<<"input_tokens"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Input_tokens) ->
gleam@dynamic@decode:optional_field(
<<"output_tokens"/utf8>>,
0,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Output_tokens) ->
gleam@dynamic@decode:success(
{usage, Input_tokens, Output_tokens}
)
end
)
end
).
-file("src/anthropic/streaming/decoder.gleam", 318).
?DOC(" Parse role from string\n").
-spec parse_role(binary()) -> anthropic@types@message:role().
parse_role(Str) ->
case Str of
<<"assistant"/utf8>> ->
assistant;
_ ->
assistant
end.
-file("src/anthropic/streaming/decoder.gleam", 105).
?DOC(" Decoder for MessageStart within the message field\n").
-spec message_start_decoder() -> gleam@dynamic@decode:decoder(anthropic@types@streaming:message_start()).
message_start_decoder() ->
gleam@dynamic@decode:field(
<<"id"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Id) ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Message_type) ->
gleam@dynamic@decode:field(
<<"role"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Role_str) ->
gleam@dynamic@decode:field(
<<"model"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Model) ->
gleam@dynamic@decode:field(
<<"usage"/utf8>>,
usage_decoder(),
fun(Usage) ->
Role = parse_role(Role_str),
gleam@dynamic@decode:success(
{message_start,
Id,
Message_type,
Role,
Model,
Usage}
)
end
)
end
)
end
)
end
)
end
).
-file("src/anthropic/streaming/decoder.gleam", 326).
?DOC(" Parse stop reason from string\n").
-spec parse_stop_reason(binary()) -> gleam@option:option(anthropic@types@request:stop_reason()).
parse_stop_reason(Str) ->
case Str of
<<"end_turn"/utf8>> ->
{some, end_turn};
<<"max_tokens"/utf8>> ->
{some, max_tokens};
<<"stop_sequence"/utf8>> ->
{some, stop_sequence};
<<"tool_use"/utf8>> ->
{some, tool_use};
_ ->
none
end.
-file("src/anthropic/streaming/decoder.gleam", 254).
?DOC(" Decoder for message delta inner content\n").
-spec message_delta_inner_decoder() -> gleam@dynamic@decode:decoder(anthropic@types@streaming:message_delta()).
message_delta_inner_decoder() ->
gleam@dynamic@decode:optional_field(
<<"stop_reason"/utf8>>,
none,
begin
_pipe = {decoder, fun gleam@dynamic@decode:decode_string/1},
gleam@dynamic@decode:map(_pipe, fun(S) -> parse_stop_reason(S) end)
end,
fun(Stop_reason) ->
gleam@dynamic@decode:optional_field(
<<"stop_sequence"/utf8>>,
none,
begin
_pipe@1 = {decoder,
fun gleam@dynamic@decode:decode_string/1},
gleam@dynamic@decode:map(
_pipe@1,
fun(Field@0) -> {some, Field@0} end
)
end,
fun(Stop_sequence) ->
gleam@dynamic@decode:success(
{message_delta, Stop_reason, Stop_sequence}
)
end
)
end
).
-file("src/anthropic/streaming/decoder.gleam", 337).
?DOC(" Format decode errors to string\n").
-spec format_errors(list(gleam@dynamic@decode:decode_error())) -> binary().
format_errors(Errors) ->
_pipe = Errors,
_pipe@1 = gleam@list:map(
_pipe,
fun(E) ->
<<<<<<"expected "/utf8, (erlang:element(2, E))/binary>>/binary,
", got "/utf8>>/binary,
(erlang:element(3, E))/binary>>
end
),
gleam@string:join(_pipe@1, <<"; "/utf8>>).
-file("src/anthropic/streaming/decoder.gleam", 89).
?DOC(" Decode a message_start event\n").
-spec decode_message_start(gleam@dynamic:dynamic_()) -> {ok,
anthropic@types@streaming:stream_event()} |
{error, decode_error()}.
decode_message_start(Json) ->
Decoder = begin
gleam@dynamic@decode:field(
<<"message"/utf8>>,
message_start_decoder(),
fun(Message) -> gleam@dynamic@decode:success(Message) end
)
end,
case gleam@dynamic@decode:run(Json, Decoder) of
{ok, Msg} ->
{ok, {message_start_event, Msg}};
{error, Errors} ->
{error,
{json_parse_error,
<<"Failed to decode message_start: "/utf8,
(format_errors(Errors))/binary>>}}
end.
-file("src/anthropic/streaming/decoder.gleam", 124).
?DOC(" Decode a content_block_start event\n").
-spec decode_content_block_start(gleam@dynamic:dynamic_()) -> {ok,
anthropic@types@streaming:stream_event()} |
{error, decode_error()}.
decode_content_block_start(Json) ->
Decoder = begin
gleam@dynamic@decode:field(
<<"index"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Index) ->
gleam@dynamic@decode:field(
<<"content_block"/utf8>>,
content_block_start_decoder(),
fun(Content_block) ->
gleam@dynamic@decode:success({Index, Content_block})
end
)
end
)
end,
case gleam@dynamic@decode:run(Json, Decoder) of
{ok, {Index@1, Block}} ->
{ok,
{content_block_start_event,
{content_block_start, Index@1, Block}}};
{error, Errors} ->
{error,
{json_parse_error,
<<"Failed to decode content_block_start: "/utf8,
(format_errors(Errors))/binary>>}}
end.
-file("src/anthropic/streaming/decoder.gleam", 168).
?DOC(" Decode a content_block_delta event\n").
-spec decode_content_block_delta(gleam@dynamic:dynamic_()) -> {ok,
anthropic@types@streaming:stream_event()} |
{error, decode_error()}.
decode_content_block_delta(Json) ->
Decoder = begin
gleam@dynamic@decode:field(
<<"index"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Index) ->
gleam@dynamic@decode:field(
<<"delta"/utf8>>,
delta_decoder(),
fun(Delta) ->
gleam@dynamic@decode:success({Index, Delta})
end
)
end
)
end,
case gleam@dynamic@decode:run(Json, Decoder) of
{ok, {Index@1, Delta@1}} ->
{ok,
{content_block_delta_event_variant,
{content_block_delta_event, Index@1, Delta@1}}};
{error, Errors} ->
{error,
{json_parse_error,
<<"Failed to decode content_block_delta: "/utf8,
(format_errors(Errors))/binary>>}}
end.
-file("src/anthropic/streaming/decoder.gleam", 212).
?DOC(" Decode a content_block_stop event\n").
-spec decode_content_block_stop(gleam@dynamic:dynamic_()) -> {ok,
anthropic@types@streaming:stream_event()} |
{error, decode_error()}.
decode_content_block_stop(Json) ->
Decoder = begin
gleam@dynamic@decode:field(
<<"index"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Index) -> gleam@dynamic@decode:success(Index) end
)
end,
case gleam@dynamic@decode:run(Json, Decoder) of
{ok, Index@1} ->
{ok, {content_block_stop_event, {content_block_stop, Index@1}}};
{error, Errors} ->
{error,
{json_parse_error,
<<"Failed to decode content_block_stop: "/utf8,
(format_errors(Errors))/binary>>}}
end.
-file("src/anthropic/streaming/decoder.gleam", 231).
?DOC(" Decode a message_delta event\n").
-spec decode_message_delta(gleam@dynamic:dynamic_()) -> {ok,
anthropic@types@streaming:stream_event()} |
{error, decode_error()}.
decode_message_delta(Json) ->
Decoder = begin
gleam@dynamic@decode:field(
<<"delta"/utf8>>,
message_delta_inner_decoder(),
fun(Delta) ->
gleam@dynamic@decode:field(
<<"usage"/utf8>>,
message_delta_usage_decoder(),
fun(Usage) ->
gleam@dynamic@decode:success({Delta, Usage})
end
)
end
)
end,
case gleam@dynamic@decode:run(Json, Decoder) of
{ok, {Delta@1, Usage@1}} ->
{ok,
{message_delta_event_variant,
{message_delta_event, Delta@1, Usage@1}}};
{error, Errors} ->
{error,
{json_parse_error,
<<"Failed to decode message_delta: "/utf8,
(format_errors(Errors))/binary>>}}
end.
-file("src/anthropic/streaming/decoder.gleam", 279).
?DOC(" Decode an error event\n").
-spec decode_error(gleam@dynamic:dynamic_()) -> {ok,
anthropic@types@streaming:stream_event()} |
{error, decode_error()}.
decode_error(Json) ->
Decoder = begin
gleam@dynamic@decode:field(
<<"error"/utf8>>,
error_inner_decoder(),
fun(Error) -> gleam@dynamic@decode:success(Error) end
)
end,
case gleam@dynamic@decode:run(Json, Decoder) of
{ok, Error@1} ->
{ok, {error_event, Error@1}};
{error, Errors} ->
{error,
{json_parse_error,
<<"Failed to decode error: "/utf8,
(format_errors(Errors))/binary>>}}
end.
-file("src/anthropic/streaming/decoder.gleam", 69).
?DOC(" Decode a typed event from parsed JSON\n").
-spec decode_typed_event(binary(), gleam@dynamic:dynamic_()) -> {ok,
anthropic@types@streaming:stream_event()} |
{error, decode_error()}.
decode_typed_event(Event_type, Json) ->
case Event_type of
<<"message_start"/utf8>> ->
decode_message_start(Json);
<<"content_block_start"/utf8>> ->
decode_content_block_start(Json);
<<"content_block_delta"/utf8>> ->
decode_content_block_delta(Json);
<<"content_block_stop"/utf8>> ->
decode_content_block_stop(Json);
<<"message_delta"/utf8>> ->
decode_message_delta(Json);
<<"error"/utf8>> ->
decode_error(Json);
_ ->
{error, {unknown_event_type, Event_type}}
end.
-file("src/anthropic/streaming/decoder.gleam", 58).
?DOC(" Decode an event that has JSON data\n").
-spec decode_data_event(binary(), binary()) -> {ok,
anthropic@types@streaming:stream_event()} |
{error, decode_error()}.
decode_data_event(Event_type, Data) ->
case gleam_json_ffi:decode(Data) of
{error, _} ->
{error,
{json_parse_error,
<<"Failed to parse JSON: "/utf8, Data/binary>>}};
{ok, Json} ->
decode_typed_event(Event_type, Json)
end.
-file("src/anthropic/streaming/decoder.gleam", 47).
?DOC(" Decode an SSE event into a StreamEvent\n").
-spec decode_event(anthropic@streaming@sse:sse_event()) -> {ok,
anthropic@types@streaming:stream_event()} |
{error, decode_error()}.
decode_event(Sse_event) ->
Event_type = anthropic@streaming@sse:get_event_type(Sse_event),
case Event_type of
<<"ping"/utf8>> ->
{ok, ping_event};
<<"message_stop"/utf8>> ->
{ok, message_stop_event};
_ ->
decode_data_event(
Event_type,
anthropic@streaming@sse:get_data(Sse_event)
)
end.