Packages

A Gleam framework for building AI agents — type-safe, multi-provider, with tool calling, streaming, and simulation testing

Current section

Files

Jump to
glean src glean@providers@anthropic.erl
Raw

src/glean@providers@anthropic.erl

-module(glean@providers@anthropic).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/glean/providers/anthropic.gleam").
-export([from_config/1, new/2]).
-export_type([stream_accumulator/0, streaming_tool_call/0, parsed_response/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(false).
-type stream_accumulator() :: {stream_accumulator,
gleam@string_tree:string_tree(),
list(streaming_tool_call()),
glean@error:finish_reason(),
glean@stream:usage(),
integer(),
binary()}.
-type streaming_tool_call() :: {streaming_tool_call,
binary(),
binary(),
gleam@string_tree:string_tree()}.
-type parsed_response() :: {parsed_response,
list(glean@provider:generated_content()),
glean@error:finish_reason(),
glean@stream:usage()}.
-file("src/glean/providers/anthropic.gleam", 453).
?DOC(false).
-spec update_last_tool_call(list(streaming_tool_call()), binary()) -> list(streaming_tool_call()).
update_last_tool_call(Tool_calls, Args_delta) ->
case lists:reverse(Tool_calls) of
[] ->
[];
[Last | Rest] ->
Updated = {streaming_tool_call,
erlang:element(2, Last),
erlang:element(3, Last),
gleam@string_tree:append(erlang:element(4, Last), Args_delta)},
lists:reverse([Updated | Rest])
end.
-file("src/glean/providers/anthropic.gleam", 569).
?DOC(false).
-spec split_system_messages(list(glean@message:message())) -> {list(binary()),
list(glean@message:message())}.
split_system_messages(Messages) ->
System = gleam@list:filter_map(Messages, fun(M) -> case M of
{system_message, Content} ->
{ok, Content};
_ ->
{error, nil}
end end),
Non_system = gleam@list:filter(Messages, fun(M@1) -> case M@1 of
{system_message, _} ->
false;
_ ->
true
end end),
{System, Non_system}.
-file("src/glean/providers/anthropic.gleam", 630).
?DOC(false).
-spec encode_user_content(glean@message:user_content()) -> gleam@json:json().
encode_user_content(Content) ->
case Content of
{user_text, Text} ->
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(<<"text"/utf8>>)},
{<<"text"/utf8>>, gleam@json:string(Text)}]
);
{user_file, Data, Media_type} ->
case Data of
{base64_data, B64} ->
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(<<"image"/utf8>>)},
{<<"source"/utf8>>,
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(<<"base64"/utf8>>)},
{<<"media_type"/utf8>>,
gleam@json:string(Media_type)},
{<<"data"/utf8>>,
gleam@json:string(B64)}]
)}]
);
{url_data, Url} ->
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(<<"image"/utf8>>)},
{<<"source"/utf8>>,
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(<<"url"/utf8>>)},
{<<"url"/utf8>>, gleam@json:string(Url)}]
)}]
)
end
end.
-file("src/glean/providers/anthropic.gleam", 666).
?DOC(false).
-spec encode_assistant_content(glean@message:assistant_content()) -> gleam@json:json().
encode_assistant_content(Content) ->
case Content of
{assistant_text, Text} ->
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(<<"text"/utf8>>)},
{<<"text"/utf8>>, gleam@json:string(Text)}]
);
{assistant_reasoning, Text@1} ->
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(<<"thinking"/utf8>>)},
{<<"thinking"/utf8>>, gleam@json:string(Text@1)}]
);
{assistant_tool_call, Id, Name, Args_json} ->
Input_json = glean@json_value:parse_to_json(Args_json),
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(<<"tool_use"/utf8>>)},
{<<"id"/utf8>>, gleam@json:string(Id)},
{<<"name"/utf8>>, gleam@json:string(Name)},
{<<"input"/utf8>>, Input_json}]
)
end.
-file("src/glean/providers/anthropic.gleam", 692).
?DOC(false).
-spec encode_tool_result(glean@message:tool_result()) -> gleam@json:json().
encode_tool_result(Result) ->
case Result of
{tool_result_ok, Tool_call_id, _, Output} ->
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(<<"tool_result"/utf8>>)},
{<<"tool_use_id"/utf8>>, gleam@json:string(Tool_call_id)},
{<<"content"/utf8>>, gleam@json:string(Output)}]
);
{tool_result_error, Tool_call_id@1, _, Error_message} ->
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(<<"tool_result"/utf8>>)},
{<<"tool_use_id"/utf8>>, gleam@json:string(Tool_call_id@1)},
{<<"content"/utf8>>,
gleam@json:string(
<<"Error: "/utf8, Error_message/binary>>
)},
{<<"is_error"/utf8>>, gleam@json:bool(true)}]
)
end.
-file("src/glean/providers/anthropic.gleam", 595).
?DOC(false).
-spec encode_message(glean@message:message()) -> list(gleam@json:json()).
encode_message(Msg) ->
case Msg of
{system_message, _} ->
[];
{user_message, Content} ->
[gleam@json:object(
[{<<"role"/utf8>>, gleam@json:string(<<"user"/utf8>>)},
{<<"content"/utf8>>,
gleam@json:preprocessed_array(
gleam@list:map(
Content,
fun encode_user_content/1
)
)}]
)];
{assistant_message, Content@1} ->
[gleam@json:object(
[{<<"role"/utf8>>, gleam@json:string(<<"assistant"/utf8>>)},
{<<"content"/utf8>>,
gleam@json:preprocessed_array(
gleam@list:map(
Content@1,
fun encode_assistant_content/1
)
)}]
)];
{tool_result_message, Results} ->
[gleam@json:object(
[{<<"role"/utf8>>, gleam@json:string(<<"user"/utf8>>)},
{<<"content"/utf8>>,
gleam@json:preprocessed_array(
gleam@list:map(
Results,
fun encode_tool_result/1
)
)}]
)]
end.
-file("src/glean/providers/anthropic.gleam", 591).
?DOC(false).
-spec encode_messages(list(glean@message:message())) -> list(gleam@json:json()).
encode_messages(Messages) ->
gleam@list:flat_map(Messages, fun encode_message/1).
-file("src/glean/providers/anthropic.gleam", 710).
?DOC(false).
-spec encode_tools(list(glean@provider:tool_spec())) -> list(gleam@json:json()).
encode_tools(Tools) ->
gleam@list:map(
Tools,
fun(T) ->
gleam@json:object(
[{<<"name"/utf8>>, gleam@json:string(erlang:element(2, T))},
{<<"description"/utf8>>,
gleam@json:string(erlang:element(3, T))},
{<<"input_schema"/utf8>>,
glean@schema:to_json(erlang:element(4, T))}]
)
end
).
-file("src/glean/providers/anthropic.gleam", 720).
?DOC(false).
-spec encode_tool_choice(glean@provider:tool_choice()) -> gleam@json:json().
encode_tool_choice(Choice) ->
case Choice of
auto ->
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(<<"auto"/utf8>>)}]
);
no_tools ->
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(<<"auto"/utf8>>)}]
);
required ->
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(<<"any"/utf8>>)}]
);
{specific_tool, Name} ->
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(<<"tool"/utf8>>)},
{<<"name"/utf8>>, gleam@json:string(Name)}]
)
end.
-file("src/glean/providers/anthropic.gleam", 153).
?DOC(false).
-spec encode_stream_request(binary(), glean@provider:generate_request()) -> binary().
encode_stream_request(Model, Gen_request) ->
{System_parts, Conversation_messages} = split_system_messages(
erlang:element(2, Gen_request)
),
Anthropic_messages = encode_messages(Conversation_messages),
Fields = [{<<"model"/utf8>>, gleam@json:string(Model)},
{<<"messages"/utf8>>, gleam@json:preprocessed_array(Anthropic_messages)},
{<<"stream"/utf8>>, gleam@json:bool(true)}],
Fields@1 = case System_parts of
[] ->
Fields;
_ ->
lists:append(
Fields,
[{<<"system"/utf8>>,
gleam@json:preprocessed_array(
gleam@list:map(
System_parts,
fun(S) ->
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(
<<"text"/utf8>>
)},
{<<"text"/utf8>>,
gleam@json:string(S)}]
)
end
)
)}]
)
end,
Max_tokens = case erlang:element(2, erlang:element(3, Gen_request)) of
{some, Max} ->
Max;
none ->
4096
end,
Fields@2 = lists:append(
Fields@1,
[{<<"max_tokens"/utf8>>, gleam@json:int(Max_tokens)}]
),
Fields@3 = case erlang:element(5, Gen_request) of
no_tools ->
Fields@2;
Choice ->
case erlang:element(4, Gen_request) of
[] ->
Fields@2;
Tools ->
lists:append(
Fields@2,
[{<<"tools"/utf8>>,
gleam@json:preprocessed_array(
encode_tools(Tools)
)},
{<<"tool_choice"/utf8>>, encode_tool_choice(Choice)}]
)
end
end,
Fields@4 = case erlang:element(3, erlang:element(3, Gen_request)) of
none ->
Fields@3;
{some, Temp} ->
lists:append(
Fields@3,
[{<<"temperature"/utf8>>, gleam@json:float(Temp)}]
)
end,
Fields@5 = case erlang:element(4, erlang:element(3, Gen_request)) of
none ->
Fields@4;
{some, P} ->
lists:append(Fields@4, [{<<"top_p"/utf8>>, gleam@json:float(P)}])
end,
Fields@6 = case erlang:element(5, erlang:element(3, Gen_request)) of
none ->
Fields@5;
{some, K} ->
lists:append(Fields@5, [{<<"top_k"/utf8>>, gleam@json:int(K)}])
end,
Fields@7 = case erlang:element(6, erlang:element(3, Gen_request)) of
none ->
Fields@6;
{some, Stops} ->
lists:append(
Fields@6,
[{<<"stop_sequences"/utf8>>,
gleam@json:array(Stops, fun gleam@json:string/1)}]
)
end,
_pipe = gleam@json:object(Fields@7),
gleam@json:to_string(_pipe).
-file("src/glean/providers/anthropic.gleam", 491).
?DOC(false).
-spec encode_request(binary(), glean@provider:generate_request()) -> binary().
encode_request(Model, Gen_request) ->
{System_parts, Conversation_messages} = split_system_messages(
erlang:element(2, Gen_request)
),
Anthropic_messages = encode_messages(Conversation_messages),
Fields = [{<<"model"/utf8>>, gleam@json:string(Model)},
{<<"messages"/utf8>>, gleam@json:preprocessed_array(Anthropic_messages)}],
Fields@1 = case System_parts of
[] ->
Fields;
_ ->
lists:append(
Fields,
[{<<"system"/utf8>>,
gleam@json:preprocessed_array(
gleam@list:map(
System_parts,
fun(S) ->
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(
<<"text"/utf8>>
)},
{<<"text"/utf8>>,
gleam@json:string(S)}]
)
end
)
)}]
)
end,
Max_tokens = case erlang:element(2, erlang:element(3, Gen_request)) of
{some, Max} ->
Max;
none ->
4096
end,
Fields@2 = lists:append(
Fields@1,
[{<<"max_tokens"/utf8>>, gleam@json:int(Max_tokens)}]
),
Fields@3 = case erlang:element(5, Gen_request) of
no_tools ->
Fields@2;
Choice ->
case erlang:element(4, Gen_request) of
[] ->
Fields@2;
Tools ->
lists:append(
Fields@2,
[{<<"tools"/utf8>>,
gleam@json:preprocessed_array(
encode_tools(Tools)
)},
{<<"tool_choice"/utf8>>, encode_tool_choice(Choice)}]
)
end
end,
Fields@4 = case erlang:element(3, erlang:element(3, Gen_request)) of
none ->
Fields@3;
{some, Temp} ->
lists:append(
Fields@3,
[{<<"temperature"/utf8>>, gleam@json:float(Temp)}]
)
end,
Fields@5 = case erlang:element(4, erlang:element(3, Gen_request)) of
none ->
Fields@4;
{some, P} ->
lists:append(Fields@4, [{<<"top_p"/utf8>>, gleam@json:float(P)}])
end,
Fields@6 = case erlang:element(5, erlang:element(3, Gen_request)) of
none ->
Fields@5;
{some, K} ->
lists:append(Fields@5, [{<<"top_k"/utf8>>, gleam@json:int(K)}])
end,
Fields@7 = case erlang:element(6, erlang:element(3, Gen_request)) of
none ->
Fields@6;
{some, Stops} ->
lists:append(
Fields@6,
[{<<"stop_sequences"/utf8>>,
gleam@json:array(Stops, fun gleam@json:string/1)}]
)
end,
_pipe = gleam@json:object(Fields@7),
gleam@json:to_string(_pipe).
-file("src/glean/providers/anthropic.gleam", 789).
?DOC(false).
-spec text_block_decoder() -> gleam@dynamic@decode:decoder(glean@provider:generated_content()).
text_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>> ->
gleam@dynamic@decode:field(
<<"text"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Text) ->
gleam@dynamic@decode:success({generated_text, Text})
end
);
_ ->
gleam@dynamic@decode:failure(
{generated_text, <<""/utf8>>},
<<"text block"/utf8>>
)
end end
).
-file("src/glean/providers/anthropic.gleam", 800).
?DOC(false).
-spec tool_use_block_decoder() -> gleam@dynamic@decode:decoder(glean@provider:generated_content()).
tool_use_block_decoder() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Block_type) -> case Block_type of
<<"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:field(
<<"input"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_dynamic/1},
fun(Input) ->
Args_json = glean@json_value:dynamic_to_string(
Input
),
gleam@dynamic@decode:success(
{generated_tool_call,
Id,
Name,
Args_json}
)
end
)
end
)
end
);
_ ->
gleam@dynamic@decode:failure(
{generated_tool_call,
<<""/utf8>>,
<<""/utf8>>,
<<""/utf8>>},
<<"tool_use block"/utf8>>
)
end end
).
-file("src/glean/providers/anthropic.gleam", 819).
?DOC(false).
-spec parse_stop_reason(binary()) -> glean@error:finish_reason().
parse_stop_reason(Reason) ->
case Reason of
<<"end_turn"/utf8>> ->
stop;
<<"tool_use"/utf8>> ->
tool_calls;
<<"max_tokens"/utf8>> ->
length;
<<"stop_sequence"/utf8>> ->
stop;
Other ->
{other, Other}
end.
-file("src/glean/providers/anthropic.gleam", 264).
?DOC(false).
-spec process_anthropic_event(
stream_accumulator(),
gleam@option:option(binary()),
binary(),
fun((glean@stream:stream_event()) -> nil)
) -> stream_accumulator().
process_anthropic_event(Acc, Event_type, Data, On_event) ->
case Event_type of
{some, <<"message_start"/utf8>>} ->
Usage_decoder = begin
gleam@dynamic@decode:field(
<<"message"/utf8>>,
begin
gleam@dynamic@decode:field(
<<"usage"/utf8>>,
begin
gleam@dynamic@decode:field(
<<"input_tokens"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_int/1},
fun(Input) ->
gleam@dynamic@decode:optional_field(
<<"output_tokens"/utf8>>,
0,
{decoder,
fun gleam@dynamic@decode:decode_int/1},
fun(Output) ->
gleam@dynamic@decode:success(
{usage, Input, Output}
)
end
)
end
)
end,
fun(Usage) ->
gleam@dynamic@decode:success(Usage)
end
)
end,
fun(Msg) -> gleam@dynamic@decode:success(Msg) end
)
end,
case gleam@json:parse(Data, Usage_decoder) of
{ok, Usage@1} ->
{stream_accumulator,
erlang:element(2, Acc),
erlang:element(3, Acc),
erlang:element(4, Acc),
Usage@1,
erlang:element(6, Acc),
erlang:element(7, Acc)};
{error, _} ->
Acc
end;
{some, <<"content_block_start"/utf8>>} ->
Block_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>>,
begin
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Block_type) ->
gleam@dynamic@decode:optional_field(
<<"id"/utf8>>,
<<""/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Id) ->
gleam@dynamic@decode:optional_field(
<<"name"/utf8>>,
<<""/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Name) ->
gleam@dynamic@decode:success(
{Block_type,
Id,
Name}
)
end
)
end
)
end
)
end,
fun(Block) ->
gleam@dynamic@decode:success({Index, Block})
end
)
end
)
end,
case gleam@json:parse(Data, Block_decoder) of
{ok, {Index@1, {Block_type@1, Id@1, Name@1}}} ->
case Block_type@1 of
<<"text"/utf8>> ->
On_event(
{text_start,
<<"block_"/utf8,
(erlang:integer_to_binary(Index@1))/binary>>}
),
{stream_accumulator,
erlang:element(2, Acc),
erlang:element(3, Acc),
erlang:element(4, Acc),
erlang:element(5, Acc),
Index@1,
<<"text"/utf8>>};
<<"tool_use"/utf8>> ->
On_event(
{tool_call_start,
<<"block_"/utf8,
(erlang:integer_to_binary(Index@1))/binary>>,
Id@1,
Name@1}
),
New_tc = {streaming_tool_call,
Id@1,
Name@1,
gleam@string_tree:new()},
{stream_accumulator,
erlang:element(2, Acc),
lists:append(erlang:element(3, Acc), [New_tc]),
erlang:element(4, Acc),
erlang:element(5, Acc),
Index@1,
<<"tool_use"/utf8>>};
_ ->
Acc
end;
{error, _} ->
Acc
end;
{some, <<"content_block_delta"/utf8>>} ->
Delta_decoder = begin
gleam@dynamic@decode:field(
<<"index"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Index@2) ->
gleam@dynamic@decode:field(
<<"delta"/utf8>>,
begin
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Delta_type) ->
gleam@dynamic@decode:optional_field(
<<"text"/utf8>>,
<<""/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Text) ->
gleam@dynamic@decode:optional_field(
<<"partial_json"/utf8>>,
<<""/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Partial_json) ->
gleam@dynamic@decode:success(
{Delta_type,
Text,
Partial_json}
)
end
)
end
)
end
)
end,
fun(Delta) ->
gleam@dynamic@decode:success({Index@2, Delta})
end
)
end
)
end,
case gleam@json:parse(Data, Delta_decoder) of
{ok, {Index@3, {Delta_type@1, Text@1, Partial_json@1}}} ->
case Delta_type@1 of
<<"text_delta"/utf8>> ->
On_event(
{text_delta,
<<"block_"/utf8,
(erlang:integer_to_binary(Index@3))/binary>>,
Text@1}
),
{stream_accumulator,
gleam@string_tree:append(
erlang:element(2, Acc),
Text@1
),
erlang:element(3, Acc),
erlang:element(4, Acc),
erlang:element(5, Acc),
erlang:element(6, Acc),
erlang:element(7, Acc)};
<<"input_json_delta"/utf8>> ->
Tc_id = case gleam@list:last(erlang:element(3, Acc)) of
{ok, Tc} ->
erlang:element(2, Tc);
{error, _} ->
<<""/utf8>>
end,
On_event(
{tool_call_delta,
<<"block_"/utf8,
(erlang:integer_to_binary(Index@3))/binary>>,
Tc_id,
Partial_json@1}
),
{stream_accumulator,
erlang:element(2, Acc),
update_last_tool_call(
erlang:element(3, Acc),
Partial_json@1
),
erlang:element(4, Acc),
erlang:element(5, Acc),
erlang:element(6, Acc),
erlang:element(7, Acc)};
_ ->
Acc
end;
{error, _} ->
Acc
end;
{some, <<"content_block_stop"/utf8>>} ->
Block_id = <<"block_"/utf8,
(erlang:integer_to_binary(erlang:element(6, Acc)))/binary>>,
case erlang:element(7, Acc) of
<<"text"/utf8>> ->
On_event({text_end, Block_id});
<<"tool_use"/utf8>> ->
Tc_id@1 = case gleam@list:last(erlang:element(3, Acc)) of
{ok, Tc@1} ->
erlang:element(2, Tc@1);
{error, _} ->
<<""/utf8>>
end,
On_event({tool_call_end, Block_id, Tc_id@1});
_ ->
nil
end,
Acc;
{some, <<"message_delta"/utf8>>} ->
Msg_delta_decoder = begin
gleam@dynamic@decode:optional_field(
<<"delta"/utf8>>,
<<""/utf8>>,
begin
gleam@dynamic@decode:optional_field(
<<"stop_reason"/utf8>>,
<<""/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Sr) -> gleam@dynamic@decode:success(Sr) end
)
end,
fun(Stop_reason) ->
gleam@dynamic@decode:optional_field(
<<"usage"/utf8>>,
{usage, 0, 0},
begin
gleam@dynamic@decode:field(
<<"output_tokens"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_int/1},
fun(Output@1) ->
gleam@dynamic@decode:success(
{usage,
erlang:element(
2,
erlang:element(5, Acc)
),
Output@1}
)
end
)
end,
fun(Usage@2) ->
gleam@dynamic@decode:success(
{Stop_reason, Usage@2}
)
end
)
end
)
end,
case gleam@json:parse(Data, Msg_delta_decoder) of
{ok, {Stop_reason@1, Usage@3}} ->
Finish = case Stop_reason@1 of
<<""/utf8>> ->
erlang:element(4, Acc);
Sr@1 ->
parse_stop_reason(Sr@1)
end,
Final_usage = case Usage@3 of
{usage, 0, 0} ->
erlang:element(5, Acc);
U ->
U
end,
{stream_accumulator,
erlang:element(2, Acc),
erlang:element(3, Acc),
Finish,
Final_usage,
erlang:element(6, Acc),
erlang:element(7, Acc)};
{error, _} ->
Acc
end;
_ ->
Acc
end.
-file("src/glean/providers/anthropic.gleam", 230).
?DOC(false).
-spec anthropic_stream_loop(
gleam@erlang@process:subject(httpp@sse:s_s_e_event()),
gleam@erlang@process:subject(httpp@sse:s_s_e_manager_message()),
stream_accumulator(),
fun((glean@stream:stream_event()) -> nil),
integer()
) -> stream_accumulator().
anthropic_stream_loop(Subject, Manager, Acc, On_event, Timeout_ms) ->
case gleam@erlang@process:'receive'(Subject, Timeout_ms) of
{error, _} ->
gleam@erlang@process:send(Manager, shutdown),
Acc;
{ok, closed} ->
Acc;
{ok, {event, Event_type, _, Data}} ->
New_acc = process_anthropic_event(Acc, Event_type, Data, On_event),
case Event_type of
{some, <<"message_stop"/utf8>>} ->
gleam@erlang@process:send(Manager, shutdown),
New_acc;
_ ->
anthropic_stream_loop(
Subject,
Manager,
New_acc,
On_event,
Timeout_ms
)
end
end.
-file("src/glean/providers/anthropic.gleam", 764).
?DOC(false).
-spec anthropic_response_decoder() -> gleam@dynamic@decode:decoder(parsed_response()).
anthropic_response_decoder() ->
Usage_decoder = begin
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
)
end,
Content_block_decoder = gleam@dynamic@decode:one_of(
text_block_decoder(),
[tool_use_block_decoder()]
),
gleam@dynamic@decode:field(
<<"content"/utf8>>,
gleam@dynamic@decode:list(Content_block_decoder),
fun(Content) ->
gleam@dynamic@decode:field(
<<"stop_reason"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Stop_reason) ->
gleam@dynamic@decode:field(
<<"usage"/utf8>>,
Usage_decoder,
fun(Usage) ->
gleam@dynamic@decode:success(
{parsed_response,
Content,
parse_stop_reason(Stop_reason),
Usage}
)
end
)
end
)
end
).
-file("src/glean/providers/anthropic.gleam", 735).
?DOC(false).
-spec decode_response(binary(), binary()) -> {ok,
glean@provider:generate_result()} |
{error, glean@error:glean_error()}.
decode_response(Model_id, Body) ->
Decoder = anthropic_response_decoder(),
case gleam@json:parse(Body, Decoder) of
{error, _} ->
{error,
{json_parse_error,
Body,
<<"Failed to decode Anthropic response"/utf8>>}};
{ok, Parsed} ->
{ok,
{generate_result,
erlang:element(2, Parsed),
erlang:element(3, Parsed),
erlang:element(4, Parsed),
Model_id}}
end.
-file("src/glean/providers/anthropic.gleam", 87).
?DOC(false).
-spec do_stream(
glean@providers@config:provider_config(),
glean@provider:generate_request(),
fun((glean@stream:stream_event()) -> nil)
) -> {ok, glean@provider:generate_result()} | {error, glean@error:glean_error()}.
do_stream(Config, Gen_request, On_event) ->
Body = encode_stream_request(erlang:element(4, Config), Gen_request),
Url = <<(erlang:element(3, Config))/binary, "/messages"/utf8>>,
Headers = [{<<"x-api-key"/utf8>>, erlang:element(2, Config)},
{<<"anthropic-version"/utf8>>, <<"2023-06-01"/utf8>>} |
gleam@list:map(
erlang:element(5, Config),
fun(H) -> {erlang:element(1, H), erlang:element(2, H)} end
)],
case glean@providers@sse:start_stream(
Url,
Headers,
Body,
erlang:element(6, Config)
) of
{error, Msg} ->
{error,
{provider_error,
<<"anthropic"/utf8>>,
erlang:element(4, Config),
0,
<<"Streaming failed: "/utf8, Msg/binary>>}};
{ok, {Event_subject, Manager_subject}} ->
Acc = {stream_accumulator,
gleam@string_tree:new(),
[],
stop,
glean@stream:zero_usage(),
0,
<<""/utf8>>},
Final_acc = anthropic_stream_loop(
Event_subject,
Manager_subject,
Acc,
On_event,
erlang:element(6, Config)
),
Text_content = case unicode:characters_to_binary(
erlang:element(2, Final_acc)
) of
<<""/utf8>> ->
[];
T ->
[{generated_text, T}]
end,
Tool_content = gleam@list:map(
erlang:element(3, Final_acc),
fun(Tc) ->
{generated_tool_call,
erlang:element(2, Tc),
erlang:element(3, Tc),
unicode:characters_to_binary(erlang:element(4, Tc))}
end
),
{ok,
{generate_result,
lists:append(Text_content, Tool_content),
erlang:element(4, Final_acc),
erlang:element(5, Final_acc),
erlang:element(4, Config)}}
end.
-file("src/glean/providers/anthropic.gleam", 472).
?DOC(false).
-spec make_http_request(
glean@providers@config:provider_config(),
binary(),
binary()
) -> {ok, binary()} | {error, glean@error:glean_error()}.
make_http_request(Config, Url, Body) ->
glean@providers@http_utils:send_post(
Config,
<<"anthropic"/utf8>>,
Url,
Body,
[{<<"x-api-key"/utf8>>, erlang:element(2, Config)},
{<<"anthropic-version"/utf8>>, <<"2023-06-01"/utf8>>}]
).
-file("src/glean/providers/anthropic.gleam", 58).
?DOC(false).
-spec do_generate(
glean@providers@config:provider_config(),
glean@provider:generate_request()
) -> {ok, glean@provider:generate_result()} | {error, glean@error:glean_error()}.
do_generate(Config, Gen_request) ->
Body = encode_request(erlang:element(4, Config), Gen_request),
Url = <<(erlang:element(3, Config))/binary, "/messages"/utf8>>,
case make_http_request(Config, Url, Body) of
{error, Err} ->
{error, Err};
{ok, Response_body} ->
decode_response(erlang:element(4, Config), Response_body)
end.
-file("src/glean/providers/anthropic.gleam", 43).
?DOC(false).
-spec from_config(glean@providers@config:provider_config()) -> glean@provider:provider().
from_config(Cfg) ->
Cfg@1 = case erlang:element(3, Cfg) of
<<""/utf8>> ->
glean@providers@config:base_url(
Cfg,
<<"https://api.anthropic.com/v1"/utf8>>
);
_ ->
Cfg
end,
{provider,
<<"anthropic"/utf8>>,
erlang:element(4, Cfg@1),
fun(Request) -> do_generate(Cfg@1, Request) end,
fun(Request@1, On_event) -> do_stream(Cfg@1, Request@1, On_event) end}.
-file("src/glean/providers/anthropic.gleam", 36).
?DOC(false).
-spec new(binary(), binary()) -> glean@provider:provider().
new(Api_key, Model) ->
_pipe = glean@providers@config:new(Api_key, Model),
_pipe@1 = glean@providers@config:base_url(
_pipe,
<<"https://api.anthropic.com/v1"/utf8>>
),
from_config(_pipe@1).