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@json@decode.erl
Raw

src/claude@json@decode.erl

-module(claude@json@decode).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/claude/json/decode.gleam").
-export([role_decoder/0, stop_reason_decoder/0, usage_decoder/0, citation_decoder/0, message_decoder/0, message/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-file("src/claude/json/decode.gleam", 30).
?DOC(" Map gleam_json's DecodeError to a list of dynamic DecodeErrors.\n").
-spec map_json_error({ok, HNX} | {error, gleam@json:decode_error()}) -> {ok,
HNX} |
{error, list(gleam@dynamic@decode:decode_error())}.
map_json_error(Result) ->
case Result of
{ok, V} ->
{ok, V};
{error, unexpected_end_of_input} ->
{error,
[{decode_error,
<<"valid JSON"/utf8>>,
<<"unexpected end of input"/utf8>>,
[]}]};
{error, {unexpected_byte, B}} ->
{error,
[{decode_error,
<<"valid JSON"/utf8>>,
<<"unexpected byte: "/utf8, B/binary>>,
[]}]};
{error, {unexpected_sequence, S}} ->
{error,
[{decode_error,
<<"valid JSON"/utf8>>,
<<"unexpected sequence: "/utf8, S/binary>>,
[]}]};
{error, {unable_to_decode, Errors}} ->
{error, Errors}
end.
-file("src/claude/json/decode.gleam", 90).
?DOC(" Decoder for Role.\n").
-spec role_decoder() -> gleam@dynamic@decode:decoder(claude@types@message:role()).
role_decoder() ->
gleam@dynamic@decode:then(
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(S) -> case S of
<<"user"/utf8>> ->
gleam@dynamic@decode:success(user);
<<"assistant"/utf8>> ->
gleam@dynamic@decode:success(assistant);
_ ->
gleam@dynamic@decode:failure(user, <<"Role"/utf8>>)
end end
).
-file("src/claude/json/decode.gleam", 100).
?DOC(" Decoder for StopReason.\n").
-spec stop_reason_decoder() -> gleam@dynamic@decode:decoder(claude@types@message:stop_reason()).
stop_reason_decoder() ->
gleam@dynamic@decode:then(
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(S) -> case S of
<<"end_turn"/utf8>> ->
gleam@dynamic@decode:success(end_turn);
<<"tool_use"/utf8>> ->
gleam@dynamic@decode:success(tool_use_stop);
<<"max_tokens"/utf8>> ->
gleam@dynamic@decode:success(max_tokens);
<<"stop_sequence"/utf8>> ->
gleam@dynamic@decode:success(stop_sequence);
<<"pause_turn"/utf8>> ->
gleam@dynamic@decode:success(pause_turn);
<<"refusal"/utf8>> ->
gleam@dynamic@decode:success(refusal);
_ ->
gleam@dynamic@decode:failure(
end_turn,
<<"StopReason"/utf8>>
)
end end
).
-file("src/claude/json/decode.gleam", 114).
?DOC(" Decoder for Usage.\n").
-spec usage_decoder() -> gleam@dynamic@decode:decoder(claude@types@message: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:optional_field(
<<"cache_creation_input_tokens"/utf8>>,
none,
gleam@dynamic@decode:optional(
{decoder, fun gleam@dynamic@decode:decode_int/1}
),
fun(Cache_creation) ->
gleam@dynamic@decode:optional_field(
<<"cache_read_input_tokens"/utf8>>,
none,
gleam@dynamic@decode:optional(
{decoder,
fun gleam@dynamic@decode:decode_int/1}
),
fun(Cache_read) ->
gleam@dynamic@decode:success(
{usage,
Input_tokens,
Output_tokens,
Cache_creation,
Cache_read}
)
end
)
end
)
end
)
end
).
-file("src/claude/json/decode.gleam", 161).
?DOC(" Decoder for Citation — discriminates on the \"type\" field.\n").
-spec citation_decoder() -> gleam@dynamic@decode:decoder(claude@types@content:citation()).
citation_decoder() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Citation_type) -> case Citation_type of
<<"char_location"/utf8>> ->
gleam@dynamic@decode:field(
<<"cited_text"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Cited_text) ->
gleam@dynamic@decode:field(
<<"document_index"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Document_index) ->
gleam@dynamic@decode:field(
<<"start_char_index"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_int/1},
fun(Start_char_index) ->
gleam@dynamic@decode:field(
<<"end_char_index"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_int/1},
fun(End_char_index) ->
gleam@dynamic@decode:success(
{char_location_citation,
Cited_text,
Document_index,
Start_char_index,
End_char_index}
)
end
)
end
)
end
)
end
);
<<"page_location"/utf8>> ->
gleam@dynamic@decode:field(
<<"cited_text"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Cited_text@1) ->
gleam@dynamic@decode:field(
<<"document_index"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Document_index@1) ->
gleam@dynamic@decode:field(
<<"start_page"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_int/1},
fun(Start_page) ->
gleam@dynamic@decode:field(
<<"end_page"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_int/1},
fun(End_page) ->
gleam@dynamic@decode:success(
{page_location_citation,
Cited_text@1,
Document_index@1,
Start_page,
End_page}
)
end
)
end
)
end
)
end
);
<<"content_block_location"/utf8>> ->
gleam@dynamic@decode:field(
<<"cited_text"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Cited_text@2) ->
gleam@dynamic@decode:field(
<<"document_index"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Document_index@2) ->
gleam@dynamic@decode:field(
<<"start_block_index"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_int/1},
fun(Start_block_index) ->
gleam@dynamic@decode:field(
<<"end_block_index"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_int/1},
fun(End_block_index) ->
gleam@dynamic@decode:success(
{content_block_location_citation,
Cited_text@2,
Document_index@2,
Start_block_index,
End_block_index}
)
end
)
end
)
end
)
end
);
<<"web_search_result_location"/utf8>> ->
gleam@dynamic@decode:field(
<<"cited_text"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Cited_text@3) ->
gleam@dynamic@decode:field(
<<"url"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Url) ->
gleam@dynamic@decode:field(
<<"title"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Title) ->
gleam@dynamic@decode:success(
{web_search_result_location_citation,
Cited_text@3,
Url,
Title}
)
end
)
end
)
end
);
_ ->
gleam@dynamic@decode:failure(
{char_location_citation, <<""/utf8>>, 0, 0, 0},
<<"Citation"/utf8>>
)
end end
).
-file("src/claude/json/decode.gleam", 150).
?DOC(" Decoder for Text content block.\n").
-spec text_block_decoder() -> gleam@dynamic@decode:decoder(claude@types@content:content_block()).
text_block_decoder() ->
gleam@dynamic@decode:field(
<<"text"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Text) ->
gleam@dynamic@decode:optional_field(
<<"citations"/utf8>>,
[],
gleam@dynamic@decode:list(citation_decoder()),
fun(Citations) ->
gleam@dynamic@decode:success({text, Text, Citations})
end
)
end
).
-file("src/claude/json/decode.gleam", 224).
?DOC(" Decoder for Thinking content block.\n").
-spec thinking_block_decoder() -> gleam@dynamic@decode:decoder(claude@types@content:content_block()).
thinking_block_decoder() ->
gleam@dynamic@decode:field(
<<"thinking"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Thinking) ->
gleam@dynamic@decode:field(
<<"signature"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Signature) ->
gleam@dynamic@decode:success(
{thinking, Thinking, Signature}
)
end
)
end
).
-file("src/claude/json/decode.gleam", 231).
?DOC(" Decoder for RedactedThinking content block.\n").
-spec redacted_thinking_block_decoder() -> gleam@dynamic@decode:decoder(claude@types@content:content_block()).
redacted_thinking_block_decoder() ->
gleam@dynamic@decode:field(
<<"data"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Data) -> gleam@dynamic@decode:success({redacted_thinking, Data}) end
).
-file("src/claude/json/decode.gleam", 261).
?DOC(" Decoder for WebSearchResult content block.\n").
-spec web_search_result_block_decoder() -> gleam@dynamic@decode:decoder(claude@types@content:content_block()).
web_search_result_block_decoder() ->
gleam@dynamic@decode:field(
<<"id"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Id) ->
gleam@dynamic@decode:field(
<<"title"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Title) ->
gleam@dynamic@decode:field(
<<"url"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Url) ->
gleam@dynamic@decode:field(
<<"encrypted_content"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Encrypted_content) ->
gleam@dynamic@decode:success(
{web_search_result,
Id,
Title,
Url,
Encrypted_content}
)
end
)
end
)
end
)
end
).
-file("src/claude/json/decode.gleam", 276).
?DOC(
" A decoder that returns the raw Dynamic value without transforming it.\n"
" This is used to capture the raw JSON value for ToolUse input.\n"
).
-spec dynamic_decoder() -> gleam@dynamic@decode:decoder(gleam@dynamic:dynamic_()).
dynamic_decoder() ->
gleam@dynamic@decode:new_primitive_decoder(
<<"Dynamic"/utf8>>,
fun(Dyn) -> {ok, Dyn} end
).
-file("src/claude/json/decode.gleam", 238).
?DOC(
" Decoder for ToolUse content block.\n"
" The \"input\" field is an arbitrary JSON object which we re-serialize to a string.\n"
).
-spec tool_use_block_decoder() -> gleam@dynamic@decode:decoder(claude@types@content: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>>,
dynamic_decoder(),
fun(Input_dynamic) ->
Input_string = claude_json_ffi:dynamic_to_json_string(
Input_dynamic
),
gleam@dynamic@decode:success(
{tool_use, Id, Name, Input_string}
)
end
)
end
)
end
).
-file("src/claude/json/decode.gleam", 252).
?DOC(" Decoder for ServerToolUse content block.\n").
-spec server_tool_use_block_decoder() -> gleam@dynamic@decode:decoder(claude@types@content:content_block()).
server_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>>,
dynamic_decoder(),
fun(Input_dynamic) ->
Input_string = claude_json_ffi:dynamic_to_json_string(
Input_dynamic
),
gleam@dynamic@decode:success(
{server_tool_use, Id, Name, Input_string}
)
end
)
end
)
end
).
-file("src/claude/json/decode.gleam", 136).
?DOC(" Decoder for ContentBlock — discriminates on the \"type\" field.\n").
-spec content_block_decoder() -> gleam@dynamic@decode:decoder(claude@types@content: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();
<<"thinking"/utf8>> ->
thinking_block_decoder();
<<"redacted_thinking"/utf8>> ->
redacted_thinking_block_decoder();
<<"tool_use"/utf8>> ->
tool_use_block_decoder();
<<"server_tool_use"/utf8>> ->
server_tool_use_block_decoder();
<<"web_search_result"/utf8>> ->
web_search_result_block_decoder();
_ ->
gleam@dynamic@decode:failure(
{text, <<""/utf8>>, []},
<<"ContentBlock"/utf8>>
)
end end
).
-file("src/claude/json/decode.gleam", 64).
?DOC(" Decoder for the top-level Message type.\n").
-spec message_decoder() -> gleam@dynamic@decode:decoder(claude@types@message:message()).
message_decoder() ->
gleam@dynamic@decode:field(
<<"id"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Id) ->
gleam@dynamic@decode:field(
<<"role"/utf8>>,
role_decoder(),
fun(Role) ->
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(
<<"stop_reason"/utf8>>,
gleam@dynamic@decode:optional(
stop_reason_decoder()
),
fun(Stop_reason) ->
gleam@dynamic@decode:field(
<<"stop_sequence"/utf8>>,
gleam@dynamic@decode:optional(
{decoder,
fun gleam@dynamic@decode:decode_string/1}
),
fun(Stop_sequence) ->
gleam@dynamic@decode:field(
<<"usage"/utf8>>,
usage_decoder(),
fun(Usage) ->
gleam@dynamic@decode:success(
{message,
Id,
Role,
Content,
Model,
Stop_reason,
Stop_sequence,
Usage}
)
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/claude/json/decode.gleam", 22).
?DOC(" Decode a full API response message from a JSON string.\n").
-spec message(binary()) -> {ok, claude@types@message:message()} |
{error, list(gleam@dynamic@decode:decode_error())}.
message(Json_string) ->
_pipe = gleam@json:parse(Json_string, message_decoder()),
map_json_error(_pipe).