-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.