Packages

A well-typed, idiomatic Gleam client for Anthropic's Claude API with streaming support and tool use

Current section

Files

Jump to
anthropic_gleam src anthropic@internal@decoder.erl
Raw

src/anthropic@internal@decoder.erl

-module(anthropic@internal@decoder).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/anthropic/internal/decoder.gleam").
-export([parse_json/1, text_block_decoder/0, usage_decoder/0, parse_role/1, parse_stop_reason/1, decode_errors_to_string/1, input_decoder/0, tool_use_block_decoder/0, content_block_decoder/0, response_decoder/0, decode_response/1, parse_response_body/1, parse_error_body/1, parse_api_error/3]).
-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(
" Shared JSON response decoders for Anthropic API\n"
"\n"
" This module provides common JSON decoders used across the library for\n"
" parsing API responses. Both the high-level `api.gleam` and the sans-io\n"
" `http.gleam` modules use these shared decoders.\n"
"\n"
" ## Internal Module\n"
"\n"
" This module is primarily for internal use. Most users should use the\n"
" higher-level functions in `anthropic/api` or `anthropic/http` instead.\n"
).
-file("src/anthropic/internal/decoder.gleam", 32).
?DOC(" Parse a JSON string to Dynamic\n").
-spec parse_json(binary()) -> {ok, gleam@dynamic:dynamic_()} | {error, nil}.
parse_json(Json) ->
gleam_json_ffi:decode(Json).
-file("src/anthropic/internal/decoder.gleam", 119).
?DOC(" Decoder for text blocks\n").
-spec text_block_decoder() -> gleam@dynamic@decode:decoder(anthropic@message:content_block()).
text_block_decoder() ->
gleam@dynamic@decode:field(
<<"text"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Text) -> gleam@dynamic@decode:success({text_block, Text}) end
).
-file("src/anthropic/internal/decoder.gleam", 142).
?DOC(" Decoder for Usage\n").
-spec usage_decoder() -> gleam@dynamic@decode:decoder(anthropic@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:field(
<<"output_tokens"/utf8>>,
{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/internal/decoder.gleam", 157).
?DOC(" Parse a role string to Role type\n").
-spec parse_role(binary()) -> anthropic@message:role().
parse_role(Str) ->
case Str of
<<"user"/utf8>> ->
user;
<<"assistant"/utf8>> ->
assistant;
_ ->
assistant
end.
-file("src/anthropic/internal/decoder.gleam", 166).
?DOC(" Parse a stop reason string to Option(StopReason)\n").
-spec parse_stop_reason(binary()) -> gleam@option:option(anthropic@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/internal/decoder.gleam", 177).
?DOC(" Convert decode errors to a human-readable string\n").
-spec decode_errors_to_string(list(gleam@dynamic@decode:decode_error())) -> binary().
decode_errors_to_string(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/internal/decoder.gleam", 188).
?DOC(" Convert a dynamic value to a JSON string\n").
-spec dynamic_to_json_string(gleam@dynamic:dynamic_()) -> binary().
dynamic_to_json_string(Value) ->
Iodata = json:encode(Value),
erlang:iolist_to_binary(Iodata).
-file("src/anthropic/internal/decoder.gleam", 134).
?DOC(" Decoder for tool input (converts dynamic to JSON string)\n").
-spec input_decoder() -> gleam@dynamic@decode:decoder(binary()).
input_decoder() ->
gleam@dynamic@decode:new_primitive_decoder(
<<"Object"/utf8>>,
fun(Data) ->
Json_str = dynamic_to_json_string(Data),
{ok, Json_str}
end
).
-file("src/anthropic/internal/decoder.gleam", 125).
?DOC(" Decoder for tool use blocks\n").
-spec tool_use_block_decoder() -> gleam@dynamic@decode:decoder(anthropic@message:content_block()).
tool_use_block_decoder() ->
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:field(
<<"input"/utf8>>,
input_decoder(),
fun(Input) ->
gleam@dynamic@decode:success(
{tool_use_block, Id, Name, Input}
)
end
)
end
)
end
).
-file("src/anthropic/internal/decoder.gleam", 104).
?DOC(" Decoder for ContentBlock\n").
-spec content_block_decoder() -> gleam@dynamic@decode:decoder(anthropic@message:content_block()).
content_block_decoder() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Block_type) -> case Block_type of
<<"text"/utf8>> ->
text_block_decoder();
<<"tool_use"/utf8>> ->
tool_use_block_decoder();
_ ->
gleam@dynamic@decode:success(
{text_block,
<<<<"[Unknown content type: "/utf8,
Block_type/binary>>/binary,
"]"/utf8>>}
)
end end
).
-file("src/anthropic/internal/decoder.gleam", 63).
?DOC(" Decoder for CreateMessageResponse\n").
-spec response_decoder() -> gleam@dynamic@decode:decoder(anthropic@request:create_message_response()).
response_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(Response_type) ->
gleam@dynamic@decode:field(
<<"role"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Role_str) ->
gleam@dynamic@decode:field(
<<"content"/utf8>>,
gleam@dynamic@decode:list(
content_block_decoder()
),
fun(Content) ->
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) ->
gleam@dynamic@decode:optional_field(
<<"stop_reason"/utf8>>,
none,
gleam@dynamic@decode:optional(
{decoder,
fun gleam@dynamic@decode:decode_string/1}
),
fun(Stop_reason_opt) ->
Stop_reason = case Stop_reason_opt of
{some, S} ->
parse_stop_reason(
S
);
none ->
none
end,
gleam@dynamic@decode:optional_field(
<<"stop_sequence"/utf8>>,
none,
gleam@dynamic@decode:optional(
{decoder,
fun gleam@dynamic@decode:decode_string/1}
),
fun(
Stop_sequence
) ->
Role = parse_role(
Role_str
),
gleam@dynamic@decode:success(
{create_message_response,
Id,
Response_type,
Role,
Content,
Model,
Stop_reason,
Stop_sequence,
Usage}
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/anthropic/internal/decoder.gleam", 45).
?DOC(" Decode a dynamic value into CreateMessageResponse\n").
-spec decode_response(gleam@dynamic:dynamic_()) -> {ok,
anthropic@request:create_message_response()} |
{error, anthropic@error:anthropic_error()}.
decode_response(Value) ->
Decoder = response_decoder(),
case gleam@dynamic@decode:run(Value, Decoder) of
{ok, Response} ->
{ok, Response};
{error, Errors} ->
{error,
anthropic@error:json_error(
<<"Failed to decode response: "/utf8,
(decode_errors_to_string(Errors))/binary>>
)}
end.
-file("src/anthropic/internal/decoder.gleam", 35).
?DOC(" Parse response body JSON and decode into CreateMessageResponse\n").
-spec parse_response_body(binary()) -> {ok,
anthropic@request:create_message_response()} |
{error, anthropic@error:anthropic_error()}.
parse_response_body(Body) ->
case gleam_json_ffi:decode(Body) of
{ok, Dyn} ->
decode_response(Dyn);
{error, _} ->
{error,
anthropic@error:json_error(
<<"Failed to parse response JSON"/utf8>>
)}
end.
-file("src/anthropic/internal/decoder.gleam", 261).
?DOC(" Decoder for the inner error details\n").
-spec error_details_decoder() -> gleam@dynamic@decode:decoder({anthropic@error:api_error_type(),
binary(),
gleam@option:option(binary()),
gleam@option:option(binary())}).
error_details_decoder() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Error_type_str) ->
gleam@dynamic@decode:field(
<<"message"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Message) ->
gleam@dynamic@decode:optional_field(
<<"param"/utf8>>,
none,
begin
_pipe = {decoder,
fun gleam@dynamic@decode:decode_string/1},
gleam@dynamic@decode:map(
_pipe,
fun(Field@0) -> {some, Field@0} end
)
end,
fun(Param) ->
gleam@dynamic@decode:optional_field(
<<"code"/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(Code) ->
Error_type = anthropic@error:api_error_type_from_string(
Error_type_str
),
gleam@dynamic@decode:success(
{Error_type, Message, Param, Code}
)
end
)
end
)
end
)
end
).
-file("src/anthropic/internal/decoder.gleam", 253).
?DOC(" Decoder for the error wrapper object\n").
-spec error_wrapper_decoder() -> gleam@dynamic@decode:decoder({anthropic@error:api_error_type(),
binary(),
gleam@option:option(binary()),
gleam@option:option(binary())}).
error_wrapper_decoder() ->
gleam@dynamic@decode:field(
<<"error"/utf8>>,
error_details_decoder(),
fun(Details) -> gleam@dynamic@decode:success(Details) end
).
-file("src/anthropic/internal/decoder.gleam", 239).
?DOC(
" Parse error body JSON into error details tuple\n"
"\n"
" Returns a tuple of (ApiErrorType, message, optional param, optional code)\n"
).
-spec parse_error_body(binary()) -> {ok,
{anthropic@error:api_error_type(),
binary(),
gleam@option:option(binary()),
gleam@option:option(binary())}} |
{error, nil}.
parse_error_body(Body) ->
case gleam_json_ffi:decode(Body) of
{ok, Dyn} ->
case gleam@dynamic@decode:run(Dyn, error_wrapper_decoder()) of
{ok, Details} ->
{ok, Details};
{error, _} ->
{error, nil}
end;
{error, _} ->
{error, nil}
end.
-file("src/anthropic/internal/decoder.gleam", 220).
?DOC(
" Parse an API error response body into an AnthropicError\n"
"\n"
" This function attempts to parse the Anthropic API error format:\n"
" ```json\n"
" {\n"
" \"type\": \"error\",\n"
" \"error\": {\n"
" \"type\": \"invalid_request_error\",\n"
" \"message\": \"...\"\n"
" }\n"
" }\n"
" ```\n"
"\n"
" If parsing fails, it falls back to creating an error with the default type\n"
" and the raw body as the message.\n"
).
-spec parse_api_error(integer(), binary(), anthropic@error:api_error_type()) -> anthropic@error:anthropic_error().
parse_api_error(Status_code, Body, Default_type) ->
case parse_error_body(Body) of
{ok, {Error_type, Message, Param, Code}} ->
anthropic@error:api_error(
Status_code,
anthropic@error:api_error_details_full(
Error_type,
Message,
Param,
Code
)
);
{error, _} ->
anthropic@error:api_error(
Status_code,
anthropic@error:api_error_details(Default_type, Body)
)
end.