Packages

An LLM interaction library that makes you feel like a star

Current section

Files

Jump to
starlet src starlet@openai.erl
Raw

src/starlet@openai.erl

-module(starlet@openai).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/starlet/openai.gleam").
-export([encode_request/2, decode_response/1, new_with_base_url/2, new/1, response_id/1, reasoning_summary/1, with_reasoning/2, continue_from/2, reset_response_id/1, decode_models/1, list_models_with_base_url/2, list_models/1]).
-export_type([decoded_response/0, reasoning_effort/0, ext/0, model/0, output_item/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(
" OpenAI provider for starlet.\n"
"\n"
" Uses the [OpenAI Responses API](https://platform.openai.com/docs/api-reference/responses)\n"
" for chat completions with support for server-side conversation continuation.\n"
"\n"
" ## Usage\n"
"\n"
" ```gleam\n"
" import starlet\n"
" import starlet/openai\n"
"\n"
" let client = openai.new(api_key)\n"
"\n"
" starlet.chat(client, \"gpt-5-nano\")\n"
" |> starlet.user(\"Hello!\")\n"
" |> starlet.send()\n"
" ```\n"
"\n"
" ## Reasoning Models\n"
"\n"
" For reasoning models (o1, o3, gpt-5), you can configure reasoning effort:\n"
"\n"
" ```gleam\n"
" starlet.chat(client, \"gpt-5-nano\")\n"
" |> openai.with_reasoning(openai.ReasoningHigh)\n"
" |> starlet.user(\"Solve this step by step...\")\n"
" |> starlet.send()\n"
" ```\n"
).
-type decoded_response() :: {decoded_response,
starlet:response(),
binary(),
gleam@option:option(binary())}.
-type reasoning_effort() :: reasoning_none |
reasoning_low |
reasoning_medium |
reasoning_high |
reasoning_x_high.
-type ext() :: {ext,
gleam@option:option(binary()),
gleam@option:option(reasoning_effort()),
gleam@option:option(binary())}.
-type model() :: {model, binary(), binary()}.
-type output_item() :: {message_item, binary()} |
{function_call_item, starlet@tool:call()} |
{reasoning_summary_item, binary()} |
skipped_item.
-file("src/starlet/openai.gleam", 179).
-spec decode_error_response(binary()) -> {ok, binary()} | {error, nil}.
decode_error_response(Body) ->
Decoder = begin
gleam@dynamic@decode:field(
<<"error"/utf8>>,
begin
gleam@dynamic@decode:field(
<<"message"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Message) -> gleam@dynamic@decode:success(Message) end
)
end,
fun(Error) -> gleam@dynamic@decode:success(Error) end
)
end,
_pipe = gleam@json:parse(Body, Decoder),
gleam@result:replace_error(_pipe, nil).
-file("src/starlet/openai.gleam", 259).
-spec encode_reasoning_effort(reasoning_effort()) -> gleam@json:json().
encode_reasoning_effort(Effort) ->
case Effort of
reasoning_none ->
gleam@json:string(<<"none"/utf8>>);
reasoning_low ->
gleam@json:string(<<"low"/utf8>>);
reasoning_medium ->
gleam@json:string(<<"medium"/utf8>>);
reasoning_high ->
gleam@json:string(<<"high"/utf8>>);
reasoning_x_high ->
gleam@json:string(<<"xhigh"/utf8>>)
end.
-file("src/starlet/openai.gleam", 269).
-spec build_input(gleam@option:option(binary()), list(starlet:message())) -> list(gleam@json:json()).
build_input(System_prompt, Messages) ->
System_items = case System_prompt of
{some, Prompt} ->
[gleam@json:object(
[{<<"role"/utf8>>, gleam@json:string(<<"system"/utf8>>)},
{<<"content"/utf8>>, gleam@json:string(Prompt)}]
)];
none ->
[]
end,
Message_items = gleam@list:flat_map(Messages, fun(Msg) -> case Msg of
{user_message, Content} ->
[gleam@json:object(
[{<<"role"/utf8>>,
gleam@json:string(<<"user"/utf8>>)},
{<<"content"/utf8>>, gleam@json:string(Content)}]
)];
{assistant_message, Content@1, Tool_calls} ->
case Tool_calls of
[] ->
[gleam@json:object(
[{<<"role"/utf8>>,
gleam@json:string(
<<"assistant"/utf8>>
)},
{<<"content"/utf8>>,
gleam@json:string(Content@1)}]
)];
_ ->
Text_output = case Content@1 of
<<""/utf8>> ->
[];
_ ->
[gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(
<<"text"/utf8>>
)},
{<<"text"/utf8>>,
gleam@json:string(Content@1)}]
)]
end,
Tool_outputs = gleam@list:map(
Tool_calls,
fun(Call) ->
Args_str = gleam@json:to_string(
starlet@tool:dynamic_to_json(
erlang:element(4, Call)
)
),
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(
<<"function_call"/utf8>>
)},
{<<"call_id"/utf8>>,
gleam@json:string(
erlang:element(2, Call)
)},
{<<"name"/utf8>>,
gleam@json:string(
erlang:element(3, Call)
)},
{<<"arguments"/utf8>>,
gleam@json:string(Args_str)}]
)
end
),
lists:append(Text_output, Tool_outputs)
end;
{tool_result_message, Call_id, _, Content@2} ->
[gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(
<<"function_call_output"/utf8>>
)},
{<<"call_id"/utf8>>, gleam@json:string(Call_id)},
{<<"output"/utf8>>,
gleam@json:string(Content@2)}]
)]
end end),
lists:append(System_items, Message_items).
-file("src/starlet/openai.gleam", 337).
-spec build_tools(list(starlet@tool:definition())) -> gleam@option:option(gleam@json:json()).
build_tools(Tools) ->
case Tools of
[] ->
none;
_ ->
{some, gleam@json:array(Tools, fun(T) -> case T of
{function, Name, Description, Parameters} ->
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(
<<"function"/utf8>>
)},
{<<"name"/utf8>>,
gleam@json:string(Name)},
{<<"description"/utf8>>,
gleam@json:string(Description)},
{<<"parameters"/utf8>>, Parameters}]
)
end end)}
end.
-file("src/starlet/openai.gleam", 193).
?DOC(false).
-spec encode_request(starlet:request(), ext()) -> gleam@json:json().
encode_request(Req, Ext) ->
Input = build_input(erlang:element(3, Req), erlang:element(4, Req)),
Tools = build_tools(erlang:element(5, Req)),
Base = [{<<"model"/utf8>>, gleam@json:string(erlang:element(2, Req))},
{<<"input"/utf8>>, gleam@json:array(Input, fun(M) -> M end)}],
Base@1 = case erlang:element(2, Ext) of
{some, Id} ->
lists:append(
Base,
[{<<"previous_response_id"/utf8>>, gleam@json:string(Id)}]
);
none ->
Base
end,
Base@2 = case Tools of
{some, T} ->
lists:append(Base@1, [{<<"tools"/utf8>>, T}]);
none ->
Base@1
end,
Base@3 = case erlang:element(6, Req) of
{some, T@1} ->
lists:append(
Base@2,
[{<<"temperature"/utf8>>, gleam@json:float(T@1)}]
);
none ->
Base@2
end,
Base@4 = case erlang:element(7, Req) of
{some, N} ->
lists:append(
Base@3,
[{<<"max_output_tokens"/utf8>>, gleam@json:int(N)}]
);
none ->
Base@3
end,
Base@5 = case erlang:element(8, Req) of
{some, Schema} ->
lists:append(
Base@4,
[{<<"text"/utf8>>,
gleam@json:object(
[{<<"format"/utf8>>,
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(
<<"json_schema"/utf8>>
)},
{<<"name"/utf8>>,
gleam@json:string(
<<"json_schema"/utf8>>
)},
{<<"schema"/utf8>>, Schema}]
)}]
)}]
);
none ->
Base@4
end,
Base@6 = case erlang:element(3, Ext) of
{some, Effort} ->
lists:append(
Base@5,
[{<<"reasoning"/utf8>>,
gleam@json:object(
[{<<"effort"/utf8>>,
encode_reasoning_effort(Effort)},
{<<"summary"/utf8>>,
gleam@json:string(<<"auto"/utf8>>)}]
)}]
);
none ->
Base@5
end,
gleam@json:object(Base@6).
-file("src/starlet/openai.gleam", 398).
-spec decode_message_item() -> gleam@dynamic@decode:decoder(output_item()).
decode_message_item() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Type_) -> case Type_ of
<<"message"/utf8>> ->
Content_decoder = begin
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Type_@1) -> case Type_@1 of
<<"output_text"/utf8>> ->
gleam@dynamic@decode:field(
<<"text"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Text) ->
gleam@dynamic@decode:success(
Text
)
end
);
_ ->
gleam@dynamic@decode:success(
<<""/utf8>>
)
end end
)
end,
gleam@dynamic@decode:field(
<<"content"/utf8>>,
gleam@dynamic@decode:list(Content_decoder),
fun(Content) ->
Text@1 = gleam@string:join(Content, <<""/utf8>>),
gleam@dynamic@decode:success({message_item, Text@1})
end
);
_ ->
gleam@dynamic@decode:failure(
{message_item, <<""/utf8>>},
<<"message"/utf8>>
)
end end
).
-file("src/starlet/openai.gleam", 420).
-spec decode_function_call_item() -> gleam@dynamic@decode:decoder(output_item()).
decode_function_call_item() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Type_) -> case Type_ of
<<"function_call"/utf8>> ->
gleam@dynamic@decode:field(
<<"call_id"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Call_id) ->
gleam@dynamic@decode:field(
<<"name"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Name) ->
gleam@dynamic@decode:field(
<<"arguments"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Arguments_str) ->
case gleam@json:parse(
Arguments_str,
{decoder,
fun gleam@dynamic@decode:decode_dynamic/1}
) of
{ok, Arguments} ->
gleam@dynamic@decode:success(
{function_call_item,
{call,
Call_id,
Name,
Arguments}}
);
{error, _} ->
gleam@dynamic@decode:failure(
{function_call_item,
{call,
<<""/utf8>>,
<<""/utf8>>,
gleam@dynamic:nil(
)}},
<<"valid JSON arguments"/utf8>>
)
end
end
)
end
)
end
);
_ ->
gleam@dynamic@decode:failure(
{function_call_item,
{call,
<<""/utf8>>,
<<""/utf8>>,
gleam@dynamic:nil()}},
<<"function_call"/utf8>>
)
end end
).
-file("src/starlet/openai.gleam", 447).
-spec decode_reasoning_summary_item() -> gleam@dynamic@decode:decoder(output_item()).
decode_reasoning_summary_item() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Type_) -> case Type_ of
<<"reasoning"/utf8>> ->
gleam@dynamic@decode:field(
<<"summary"/utf8>>,
gleam@dynamic@decode:list(
gleam@dynamic@decode:at(
[<<"text"/utf8>>],
{decoder,
fun gleam@dynamic@decode:decode_string/1}
)
),
fun(Summary) ->
Text = gleam@string:join(Summary, <<"\n"/utf8>>),
gleam@dynamic@decode:success(
{reasoning_summary_item, Text}
)
end
);
_ ->
gleam@dynamic@decode:failure(
{reasoning_summary_item, <<""/utf8>>},
<<"reasoning"/utf8>>
)
end end
).
-file("src/starlet/openai.gleam", 462).
-spec decode_skipped_item() -> gleam@dynamic@decode:decoder(output_item()).
decode_skipped_item() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(_) -> gleam@dynamic@decode:success(skipped_item) end
).
-file("src/starlet/openai.gleam", 467).
-spec extract_text(list(output_item())) -> binary().
extract_text(Items) ->
_pipe = gleam@list:filter_map(Items, fun(Item) -> case Item of
{message_item, Text} ->
{ok, Text};
_ ->
{error, nil}
end end),
gleam@string:join(_pipe, <<""/utf8>>).
-file("src/starlet/openai.gleam", 477).
-spec extract_tool_calls(list(output_item())) -> list(starlet@tool:call()).
extract_tool_calls(Items) ->
gleam@list:filter_map(Items, fun(Item) -> case Item of
{function_call_item, Call} ->
{ok, Call};
_ ->
{error, nil}
end end).
-file("src/starlet/openai.gleam", 486).
-spec extract_reasoning_summary(list(output_item())) -> gleam@option:option(binary()).
extract_reasoning_summary(Items) ->
_pipe = gleam@list:filter_map(Items, fun(Item) -> case Item of
{reasoning_summary_item, Text} ->
{ok, Text};
_ ->
{error, nil}
end end),
(fun(Summaries) -> case Summaries of
[] ->
none;
_ ->
{some, gleam@string:join(Summaries, <<"\n"/utf8>>)}
end end)(_pipe).
-file("src/starlet/openai.gleam", 359).
?DOC(false).
-spec decode_response(binary()) -> {ok, decoded_response()} |
{error, starlet:starlet_error()}.
decode_response(Body) ->
Output_item_decoder = gleam@dynamic@decode:one_of(
decode_message_item(),
[decode_function_call_item(),
decode_reasoning_summary_item(),
decode_skipped_item()]
),
Decoder = begin
gleam@dynamic@decode:field(
<<"id"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Id) ->
gleam@dynamic@decode:field(
<<"output"/utf8>>,
gleam@dynamic@decode:list(Output_item_decoder),
fun(Output) ->
gleam@dynamic@decode:success({Id, Output})
end
)
end
)
end,
case gleam@json:parse(Body, Decoder) of
{ok, {Id@1, Output_items}} ->
Text = extract_text(Output_items),
Tool_calls = extract_tool_calls(Output_items),
Reasoning = extract_reasoning_summary(Output_items),
{ok,
{decoded_response,
{response, Text, Tool_calls},
Id@1,
Reasoning}};
{error, Err} ->
{error,
{decode,
<<"Failed to decode OpenAI response: "/utf8,
(gleam@string:inspect(Err))/binary>>}}
end.
-file("src/starlet/openai.gleam", 109).
-spec send_request(binary(), binary(), starlet:request(), ext()) -> {ok,
{starlet:response(), ext()}} |
{error, starlet:starlet_error()}.
send_request(Api_key, Base_url, Req, Ext) ->
Body = gleam@json:to_string(encode_request(Req, Ext)),
case gleam_stdlib:uri_parse(Base_url) of
{ok, Base_uri} ->
Scheme = case gleam@option:unwrap(
erlang:element(2, Base_uri),
<<"https"/utf8>>
) of
<<"http"/utf8>> ->
http;
_ ->
https
end,
Host = gleam@option:unwrap(
erlang:element(4, Base_uri),
<<"api.openai.com"/utf8>>
),
Base_path = erlang:element(6, Base_uri),
Http_request = begin
_pipe = gleam@http@request:new(),
_pipe@1 = gleam@http@request:set_method(_pipe, post),
_pipe@2 = gleam@http@request:set_scheme(_pipe@1, Scheme),
_pipe@3 = gleam@http@request:set_host(_pipe@2, Host),
_pipe@4 = starlet@internal@http:set_optional_port(
_pipe@3,
erlang:element(5, Base_uri)
),
_pipe@5 = gleam@http@request:set_path(
_pipe@4,
<<Base_path/binary, "/v1/responses"/utf8>>
),
_pipe@6 = gleam@http@request:set_header(
_pipe@5,
<<"content-type"/utf8>>,
<<"application/json"/utf8>>
),
_pipe@7 = gleam@http@request:set_header(
_pipe@6,
<<"authorization"/utf8>>,
<<"Bearer "/utf8, Api_key/binary>>
),
gleam@http@request:set_body(_pipe@7, Body)
end,
Config = begin
_pipe@8 = gleam@httpc:configure(),
gleam@httpc:timeout(_pipe@8, erlang:element(9, Req))
end,
case gleam@httpc:dispatch(Config, Http_request) of
{ok, Response} ->
case erlang:element(2, Response) of
200 ->
case decode_response(erlang:element(4, Response)) of
{ok, Decoded} ->
New_ext = {ext,
{some, erlang:element(3, Decoded)},
erlang:element(3, Ext),
erlang:element(4, Decoded)},
{ok, {erlang:element(2, Decoded), New_ext}};
{error, E} ->
{error, E}
end;
429 ->
Retry_after = starlet@internal@http:parse_retry_after(
erlang:element(3, Response)
),
{error, {rate_limited, Retry_after}};
Status ->
case decode_error_response(
erlang:element(4, Response)
) of
{ok, Msg} ->
{error,
{provider,
<<"openai"/utf8>>,
Msg,
erlang:element(4, Response)}};
{error, _} ->
{error,
{http,
Status,
erlang:element(4, Response)}}
end
end;
{error, Err} ->
{error, {transport, gleam@string:inspect(Err)}}
end;
{error, _} ->
{error, {transport, <<"Invalid base URL: "/utf8, Base_url/binary>>}}
end.
-file("src/starlet/openai.gleam", 99).
?DOC(
" Creates a new OpenAI client with a custom base URL.\n"
" Useful for proxies or Azure OpenAI endpoints.\n"
).
-spec new_with_base_url(binary(), binary()) -> starlet:client(ext()).
new_with_base_url(Api_key, Base_url) ->
Config = {provider_config,
<<"openai"/utf8>>,
Base_url,
fun(Req, Ext) -> send_request(Api_key, Base_url, Req, Ext) end},
Default_ext = {ext, none, none, none},
starlet:from_provider(Config, Default_ext).
-file("src/starlet/openai.gleam", 93).
?DOC(
" Creates a new OpenAI client with the given API key.\n"
" Uses the default base URL: https://api.openai.com\n"
).
-spec new(binary()) -> starlet:client(ext()).
new(Api_key) ->
new_with_base_url(Api_key, <<"https://api.openai.com"/utf8>>).
-file("src/starlet/openai.gleam", 502).
?DOC(" Get the response ID from an OpenAI turn.\n").
-spec response_id(starlet:turn(any(), any(), ext())) -> gleam@option:option(binary()).
response_id(Turn) ->
erlang:element(2, erlang:element(4, Turn)).
-file("src/starlet/openai.gleam", 508).
?DOC(
" Get the reasoning summary from an OpenAI turn (if present).\n"
" Only available for reasoning models (o1, o3, gpt-5).\n"
).
-spec reasoning_summary(starlet:turn(any(), any(), ext())) -> gleam@option:option(binary()).
reasoning_summary(Turn) ->
erlang:element(4, erlang:element(4, Turn)).
-file("src/starlet/openai.gleam", 514).
?DOC(
" Set the reasoning effort for reasoning models (o1, o3, gpt-5).\n"
" When not set, the provider's default applies (medium for reasoning models).\n"
).
-spec with_reasoning(starlet:chat(SYV, SYW, SYX, ext()), reasoning_effort()) -> starlet:chat(SYV, SYW, SYX, ext()).
with_reasoning(Chat, Effort) ->
{chat,
erlang:element(2, Chat),
erlang:element(3, Chat),
erlang:element(4, Chat),
erlang:element(5, Chat),
erlang:element(6, Chat),
erlang:element(7, Chat),
erlang:element(8, Chat),
begin
_record = erlang:element(9, Chat),
{ext,
erlang:element(2, _record),
{some, Effort},
erlang:element(4, _record)}
end,
erlang:element(10, Chat),
erlang:element(11, Chat)}.
-file("src/starlet/openai.gleam", 523).
?DOC(
" Continue a conversation from a previous response ID.\n"
" The server will use its stored conversation state.\n"
).
-spec continue_from(starlet:chat(SZG, SZH, SZI, ext()), binary()) -> starlet:chat(SZG, SZH, SZI, ext()).
continue_from(Chat, Id) ->
{chat,
erlang:element(2, Chat),
erlang:element(3, Chat),
erlang:element(4, Chat),
erlang:element(5, Chat),
erlang:element(6, Chat),
erlang:element(7, Chat),
erlang:element(8, Chat),
begin
_record = erlang:element(9, Chat),
{ext,
{some, Id},
erlang:element(3, _record),
erlang:element(4, _record)}
end,
erlang:element(10, Chat),
erlang:element(11, Chat)}.
-file("src/starlet/openai.gleam", 529).
?DOC(
" Reset the response ID, disabling automatic conversation continuation.\n"
" Use this to start a fresh conversation without the previous context.\n"
).
-spec reset_response_id(starlet:chat(SZR, SZS, SZT, ext())) -> starlet:chat(SZR, SZS, SZT, ext()).
reset_response_id(Chat) ->
{chat,
erlang:element(2, Chat),
erlang:element(3, Chat),
erlang:element(4, Chat),
erlang:element(5, Chat),
erlang:element(6, Chat),
erlang:element(7, Chat),
erlang:element(8, Chat),
begin
_record = erlang:element(9, Chat),
{ext, none, erlang:element(3, _record), erlang:element(4, _record)}
end,
erlang:element(10, Chat),
erlang:element(11, Chat)}.
-file("src/starlet/openai.gleam", 535).
?DOC(false).
-spec decode_models(binary()) -> {ok, list(model())} |
{error, starlet:starlet_error()}.
decode_models(Body) ->
Model_decoder = begin
gleam@dynamic@decode:field(
<<"id"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Id) ->
gleam@dynamic@decode:field(
<<"owned_by"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Owned_by) ->
gleam@dynamic@decode:success({model, Id, Owned_by})
end
)
end
)
end,
Decoder = begin
gleam@dynamic@decode:field(
<<"data"/utf8>>,
gleam@dynamic@decode:list(Model_decoder),
fun(Data) -> gleam@dynamic@decode:success(Data) end
)
end,
_pipe = gleam@json:parse(Body, Decoder),
gleam@result:map_error(
_pipe,
fun(Err) ->
{decode,
<<"Failed to decode OpenAI models: "/utf8,
(gleam@string:inspect(Err))/binary>>}
end
).
-file("src/starlet/openai.gleam", 559).
?DOC(" Lists available models from the OpenAI API with a custom base URL.\n").
-spec list_models_with_base_url(binary(), binary()) -> {ok, list(model())} |
{error, starlet:starlet_error()}.
list_models_with_base_url(Api_key, Base_url) ->
case gleam_stdlib:uri_parse(Base_url) of
{ok, Base_uri} ->
Scheme = case gleam@option:unwrap(
erlang:element(2, Base_uri),
<<"https"/utf8>>
) of
<<"http"/utf8>> ->
http;
_ ->
https
end,
Host = gleam@option:unwrap(
erlang:element(4, Base_uri),
<<"api.openai.com"/utf8>>
),
Base_path = erlang:element(6, Base_uri),
Http_request = begin
_pipe = gleam@http@request:new(),
_pipe@1 = gleam@http@request:set_method(_pipe, get),
_pipe@2 = gleam@http@request:set_scheme(_pipe@1, Scheme),
_pipe@3 = gleam@http@request:set_host(_pipe@2, Host),
_pipe@4 = starlet@internal@http:set_optional_port(
_pipe@3,
erlang:element(5, Base_uri)
),
_pipe@5 = gleam@http@request:set_path(
_pipe@4,
<<Base_path/binary, "/v1/models"/utf8>>
),
gleam@http@request:set_header(
_pipe@5,
<<"authorization"/utf8>>,
<<"Bearer "/utf8, Api_key/binary>>
)
end,
case gleam@httpc:send(Http_request) of
{ok, Response} ->
case erlang:element(2, Response) of
200 ->
decode_models(erlang:element(4, Response));
429 ->
Retry_after = starlet@internal@http:parse_retry_after(
erlang:element(3, Response)
),
{error, {rate_limited, Retry_after}};
Status ->
{error, {http, Status, erlang:element(4, Response)}}
end;
{error, Err} ->
{error, {transport, gleam@string:inspect(Err)}}
end;
{error, _} ->
{error, {transport, <<"Invalid base URL: "/utf8, Base_url/binary>>}}
end.
-file("src/starlet/openai.gleam", 554).
?DOC(" Lists available models from the OpenAI API.\n").
-spec list_models(binary()) -> {ok, list(model())} |
{error, starlet:starlet_error()}.
list_models(Api_key) ->
list_models_with_base_url(Api_key, <<"https://api.openai.com"/utf8>>).