Current section
Files
Jump to
Current section
Files
src/gopenai@chat.erl
-module(gopenai@chat).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([completion_from_json/1, request_to_json/1, completion/2]).
-export_type([role/0, message/0, model_message/0, request/0, finish_reason/0, choice/0, usage/0, completion_response/0, chat_error/0]).
-type role() :: system | assistant | user | function.
-type message() :: {message, role(), binary()}.
-type model_message() :: {model_message,
role(),
gleam@option:option(binary()),
gleam@option:option(list(gopenai@tool:tool_call()))}.
-type request() :: {request,
gopenai@model:model(),
list(message()),
float(),
gleam@option:option(list(gopenai@tool:tool()))}.
-type finish_reason() :: stop |
length |
content_filter |
tool_calls |
function_call.
-type choice() :: {choice, finish_reason(), integer(), model_message()}.
-type usage() :: {usage, integer(), integer(), integer()}.
-type completion_response() :: {completion_response,
binary(),
list(choice()),
integer(),
binary(),
binary(),
binary(),
usage()}.
-type chat_error() :: {dynamic_error, gleam@dynamic:dynamic_()} |
{json_decode_error, gleam@json:decode_error()}.
-spec role_to_json(role()) -> gleam@json:json().
role_to_json(Role) ->
_pipe = case Role of
system ->
<<"system"/utf8>>;
assistant ->
<<"assistant"/utf8>>;
user ->
<<"user"/utf8>>;
function ->
<<"function"/utf8>>
end,
gleam@json:string(_pipe).
-spec role_from_json(gleam@dynamic:dynamic_()) -> {ok, role()} |
{error, list(gleam@dynamic:decode_error())}.
role_from_json(Data) ->
case gleam@dynamic:string(Data) of
{ok, Str} ->
case Str of
<<"system"/utf8>> ->
{ok, system};
<<"assistant"/utf8>> ->
{ok, assistant};
<<"user"/utf8>> ->
{ok, user};
<<"function"/utf8>> ->
{ok, function};
Default ->
{error,
[{decode_error, <<"valid role"/utf8>>, Default, []}]}
end;
{error, E} ->
{error, E}
end.
-spec message_to_json(message()) -> gleam@json:json().
message_to_json(Message) ->
gleam@json:object(
[{<<"role"/utf8>>, role_to_json(erlang:element(2, Message))},
{<<"content"/utf8>>, gleam@json:string(erlang:element(3, Message))}]
).
-spec finish_reason_from_json(gleam@dynamic:dynamic_()) -> {ok, finish_reason()} |
{error, list(gleam@dynamic:decode_error())}.
finish_reason_from_json(Data) ->
case gleam@dynamic:string(Data) of
{ok, Str} ->
case Str of
<<"stop"/utf8>> ->
{ok, stop};
<<"length"/utf8>> ->
{ok, length};
<<"content_filter"/utf8>> ->
{ok, content_filter};
<<"tool_calls"/utf8>> ->
{ok, tool_calls};
<<"function_call"/utf8>> ->
{ok, function_call};
Default ->
{error,
[{decode_error,
<<"valid finish reason"/utf8>>,
Default,
[]}]}
end;
{error, E} ->
{error, E}
end.
-spec completion_from_json(binary()) -> {ok, completion_response()} |
{error, gleam@json:decode_error()}.
completion_from_json(Data) ->
_pipe = Data,
gleam@json:decode(
_pipe,
gleam@dynamic:decode7(
fun(Field@0, Field@1, Field@2, Field@3, Field@4, Field@5, Field@6) -> {completion_response, Field@0, Field@1, Field@2, Field@3, Field@4, Field@5, Field@6} end,
gleam@dynamic:field(<<"id"/utf8>>, fun gleam@dynamic:string/1),
gleam@dynamic:field(
<<"choices"/utf8>>,
gleam@dynamic:list(
gleam@dynamic:decode3(
fun(Field@0, Field@1, Field@2) -> {choice, Field@0, Field@1, Field@2} end,
gleam@dynamic:field(
<<"finish_reason"/utf8>>,
fun finish_reason_from_json/1
),
gleam@dynamic:field(
<<"index"/utf8>>,
fun gleam@dynamic:int/1
),
gleam@dynamic:field(
<<"message"/utf8>>,
gleam@dynamic:decode3(
fun(Field@0, Field@1, Field@2) -> {model_message, Field@0, Field@1, Field@2} end,
gleam@dynamic:field(
<<"role"/utf8>>,
fun role_from_json/1
),
gleam@dynamic:optional_field(
<<"content"/utf8>>,
fun gleam@dynamic:string/1
),
gleam@dynamic:optional_field(
<<"tool_calls"/utf8>>,
gleam@dynamic:list(
gleam@dynamic:decode3(
fun(Field@0, Field@1, Field@2) -> {tool_call, Field@0, Field@1, Field@2} end,
gleam@dynamic:field(
<<"id"/utf8>>,
fun gleam@dynamic:string/1
),
gleam@dynamic:field(
<<"type"/utf8>>,
fun gopenai@tool:tool_type_from_json/1
),
gleam@dynamic:field(
<<"function"/utf8>>,
fun gopenai@tool:function_call_from_json/1
)
)
)
)
)
)
)
)
),
gleam@dynamic:field(<<"created"/utf8>>, fun gleam@dynamic:int/1),
gleam@dynamic:field(<<"model"/utf8>>, fun gleam@dynamic:string/1),
gleam@dynamic:field(
<<"system_fingerprint"/utf8>>,
fun gleam@dynamic:string/1
),
gleam@dynamic:field(<<"object"/utf8>>, fun gleam@dynamic:string/1),
gleam@dynamic:field(
<<"usage"/utf8>>,
gleam@dynamic:decode3(
fun(Field@0, Field@1, Field@2) -> {usage, Field@0, Field@1, Field@2} end,
gleam@dynamic:field(
<<"prompt_tokens"/utf8>>,
fun gleam@dynamic:int/1
),
gleam@dynamic:field(
<<"completion_tokens"/utf8>>,
fun gleam@dynamic:int/1
),
gleam@dynamic:field(
<<"total_tokens"/utf8>>,
fun gleam@dynamic:int/1
)
)
)
)
).
-spec request_to_json(request()) -> binary().
request_to_json(Req) ->
_pipe = gleam@json:object(
[{<<"model"/utf8>>, gopenai@model:model_to_json(erlang:element(2, Req))},
{<<"messages"/utf8>>,
gleam@json:array(erlang:element(3, Req), fun message_to_json/1)},
{<<"temperature"/utf8>>, gleam@json:float(erlang:element(4, Req))},
{<<"tools"/utf8>>,
gopenai@tool:tools_to_json(erlang:element(5, Req))}]
),
gleam@json:to_string(_pipe).
-spec completion(gopenai@client:client(), request()) -> {ok,
completion_response()} |
{error, chat_error()}.
completion(Client, Req) ->
_assert_subject = gleam@http@request:to(
gleam@string:concat(
[erlang:element(2, Client),
<<"/v1/"/utf8>>,
begin
_pipe = completions,
gopenai@client:endpoint_to_str(_pipe)
end]
)
),
{ok, Request} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"gopenai/chat"/utf8>>,
function => <<"completion"/utf8>>,
line => 200})
end,
_pipe@1 = Request,
_pipe@2 = gleam@http@request:set_method(_pipe@1, post),
_pipe@3 = gleam@http@request:prepend_header(
_pipe@2,
<<"Content-Type"/utf8>>,
<<"application/json"/utf8>>
),
_pipe@4 = gleam@http@request:prepend_header(
_pipe@3,
<<"Authorization"/utf8>>,
<<"Bearer "/utf8, (erlang:element(3, Client))/binary>>
),
_pipe@6 = gleam@http@request:set_body(
_pipe@4,
begin
_pipe@5 = Req,
request_to_json(_pipe@5)
end
),
_pipe@7 = gleam@httpc:send(_pipe@6),
_pipe@8 = gleam@result:map_error(_pipe@7, fun(E) -> {dynamic_error, E} end),
_pipe@10 = gleam@result:map(
_pipe@8,
fun(Response) ->
_pipe@9 = completion_from_json(erlang:element(4, Response)),
gleam@result:map_error(
_pipe@9,
fun(E@1) -> {json_decode_error, E@1} end
)
end
),
gleam@result:flatten(_pipe@10).