-module(glopenai@file). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/glopenai/file.gleam"). -export([file_expiration_after_anchor_to_json/1, file_expiration_after_anchor_decoder/0, file_expiration_after_to_json/1, file_expiration_after_decoder/0, openai_file_purpose_to_json/1, openai_file_to_json/1, openai_file_purpose_decoder/0, openai_file_decoder/0, new_create_request/3, with_expires_after/2, create_response/1, create_request/3, list_request/1, list_response/1, retrieve_request/2, retrieve_response/1, delete_request/2, delete_response/1, content_request/2, content_response/1]). -export_type([open_ai_file_purpose/0, open_ai_file/0, file_expiration_after_anchor/0, file_expiration_after/0, list_files_response/0, delete_file_response/0, create_file_request/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. -type open_ai_file_purpose() :: assistants | assistants_output | batch | batch_output | fine_tune | fine_tune_results | vision | user_data. -type open_ai_file() :: {open_ai_file, binary(), binary(), integer(), integer(), gleam@option:option(integer()), binary(), open_ai_file_purpose()}. -type file_expiration_after_anchor() :: created_at. -type file_expiration_after() :: {file_expiration_after, file_expiration_after_anchor(), integer()}. -type list_files_response() :: {list_files_response, binary(), list(open_ai_file()), gleam@option:option(binary()), gleam@option:option(binary()), boolean()}. -type delete_file_response() :: {delete_file_response, binary(), binary(), boolean()}. -type create_file_request() :: {create_file_request, binary(), bitstring(), open_ai_file_purpose(), gleam@option:option(file_expiration_after())}. -file("src/glopenai/file.gleam", 46). -spec file_expiration_after_anchor_to_json(file_expiration_after_anchor()) -> gleam@json:json(). file_expiration_after_anchor_to_json(Anchor) -> gleam@json:string(case Anchor of created_at -> <<"created_at"/utf8>> end). -file("src/glopenai/file.gleam", 54). -spec file_expiration_after_anchor_decoder() -> gleam@dynamic@decode:decoder(file_expiration_after_anchor()). file_expiration_after_anchor_decoder() -> gleam@dynamic@decode:then( {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Value) -> case Value of <<"created_at"/utf8>> -> gleam@dynamic@decode:success(created_at); _ -> gleam@dynamic@decode:failure( created_at, <<"FileExpirationAfterAnchor"/utf8>> ) end end ). -file("src/glopenai/file.gleam", 69). -spec file_expiration_after_to_json(file_expiration_after()) -> gleam@json:json(). file_expiration_after_to_json(Expiration) -> gleam@json:object( [{<<"anchor"/utf8>>, file_expiration_after_anchor_to_json( erlang:element(2, Expiration) )}, {<<"seconds"/utf8>>, gleam@json:int(erlang:element(3, Expiration))}] ). -file("src/glopenai/file.gleam", 78). -spec file_expiration_after_decoder() -> gleam@dynamic@decode:decoder(file_expiration_after()). file_expiration_after_decoder() -> gleam@dynamic@decode:field( <<"anchor"/utf8>>, file_expiration_after_anchor_decoder(), fun(Anchor) -> gleam@dynamic@decode:field( <<"seconds"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_int/1}, fun(Seconds) -> gleam@dynamic@decode:success( {file_expiration_after, Anchor, Seconds} ) end ) end ). -file("src/glopenai/file.gleam", 102). -spec openai_file_purpose_to_json(open_ai_file_purpose()) -> gleam@json:json(). openai_file_purpose_to_json(Purpose) -> gleam@json:string(case Purpose of assistants -> <<"assistants"/utf8>>; assistants_output -> <<"assistants_output"/utf8>>; batch -> <<"batch"/utf8>>; batch_output -> <<"batch_output"/utf8>>; fine_tune -> <<"fine-tune"/utf8>>; fine_tune_results -> <<"fine-tune-results"/utf8>>; vision -> <<"vision"/utf8>>; user_data -> <<"user_data"/utf8>> end). -file("src/glopenai/file.gleam", 115). -spec openai_file_to_json(open_ai_file()) -> gleam@json:json(). openai_file_to_json(File) -> gleam@json:object( [{<<"id"/utf8>>, gleam@json:string(erlang:element(2, File))}, {<<"object"/utf8>>, gleam@json:string(erlang:element(3, File))}, {<<"bytes"/utf8>>, gleam@json:int(erlang:element(4, File))}, {<<"created_at"/utf8>>, gleam@json:int(erlang:element(5, File))}, {<<"expires_at"/utf8>>, gleam@json:nullable( erlang:element(6, File), fun gleam@json:int/1 )}, {<<"filename"/utf8>>, gleam@json:string(erlang:element(7, File))}, {<<"purpose"/utf8>>, openai_file_purpose_to_json(erlang:element(8, File))}] ). -file("src/glopenai/file.gleam", 129). -spec openai_file_purpose_decoder() -> gleam@dynamic@decode:decoder(open_ai_file_purpose()). openai_file_purpose_decoder() -> gleam@dynamic@decode:then( {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Value) -> case Value of <<"assistants"/utf8>> -> gleam@dynamic@decode:success(assistants); <<"assistants_output"/utf8>> -> gleam@dynamic@decode:success(assistants_output); <<"batch"/utf8>> -> gleam@dynamic@decode:success(batch); <<"batch_output"/utf8>> -> gleam@dynamic@decode:success(batch_output); <<"fine-tune"/utf8>> -> gleam@dynamic@decode:success(fine_tune); <<"fine-tune-results"/utf8>> -> gleam@dynamic@decode:success(fine_tune_results); <<"vision"/utf8>> -> gleam@dynamic@decode:success(vision); <<"user_data"/utf8>> -> gleam@dynamic@decode:success(user_data); _ -> gleam@dynamic@decode:failure( fine_tune, <<"OpenAiFilePurpose"/utf8>> ) end end ). -file("src/glopenai/file.gleam", 144). -spec openai_file_decoder() -> gleam@dynamic@decode:decoder(open_ai_file()). openai_file_decoder() -> gleam@dynamic@decode:field( <<"id"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Id) -> gleam@dynamic@decode:field( <<"object"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Object) -> gleam@dynamic@decode:field( <<"bytes"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_int/1}, fun(Bytes) -> gleam@dynamic@decode:field( <<"created_at"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_int/1}, fun(Created_at) -> gleam@dynamic@decode:optional_field( <<"expires_at"/utf8>>, none, gleam@dynamic@decode:optional( {decoder, fun gleam@dynamic@decode:decode_int/1} ), fun(Expires_at) -> gleam@dynamic@decode:field( <<"filename"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Filename) -> gleam@dynamic@decode:field( <<"purpose"/utf8>>, openai_file_purpose_decoder( ), fun(Purpose) -> gleam@dynamic@decode:success( {open_ai_file, Id, Object, Bytes, Created_at, Expires_at, Filename, Purpose} ) end ) end ) end ) end ) end ) end ) end ). -file("src/glopenai/file.gleam", 167). -spec list_files_response_decoder() -> gleam@dynamic@decode:decoder(list_files_response()). list_files_response_decoder() -> gleam@dynamic@decode:field( <<"object"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Object) -> gleam@dynamic@decode:field( <<"data"/utf8>>, gleam@dynamic@decode:list(openai_file_decoder()), fun(Data) -> gleam@dynamic@decode:optional_field( <<"first_id"/utf8>>, none, gleam@dynamic@decode:optional( {decoder, fun gleam@dynamic@decode:decode_string/1} ), fun(First_id) -> gleam@dynamic@decode:optional_field( <<"last_id"/utf8>>, none, gleam@dynamic@decode:optional( {decoder, fun gleam@dynamic@decode:decode_string/1} ), fun(Last_id) -> gleam@dynamic@decode:field( <<"has_more"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_bool/1}, fun(Has_more) -> gleam@dynamic@decode:success( {list_files_response, Object, Data, First_id, Last_id, Has_more} ) end ) end ) end ) end ) end ). -file("src/glopenai/file.gleam", 190). -spec delete_file_response_decoder() -> gleam@dynamic@decode:decoder(delete_file_response()). delete_file_response_decoder() -> gleam@dynamic@decode:field( <<"id"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Id) -> gleam@dynamic@decode:field( <<"object"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Object) -> gleam@dynamic@decode:field( <<"deleted"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_bool/1}, fun(Deleted) -> gleam@dynamic@decode:success( {delete_file_response, Id, Object, Deleted} ) end ) end ) end ). -file("src/glopenai/file.gleam", 213). -spec new_create_request(binary(), bitstring(), open_ai_file_purpose()) -> create_file_request(). new_create_request(Filename, File_bytes, Purpose) -> {create_file_request, Filename, File_bytes, Purpose, none}. -file("src/glopenai/file.gleam", 226). -spec with_expires_after(create_file_request(), file_expiration_after()) -> create_file_request(). with_expires_after(Request, Expires_after) -> {create_file_request, erlang:element(2, Request), erlang:element(3, Request), erlang:element(4, Request), {some, Expires_after}}. -file("src/glopenai/file.gleam", 274). -spec create_response(gleam@http@response:response(binary())) -> {ok, open_ai_file()} | {error, glopenai@error:glopenai_error()}. create_response(Response) -> glopenai@internal:parse_response(Response, openai_file_decoder()). -file("src/glopenai/file.gleam", 305). -spec ends_with(binary(), binary()) -> boolean(). ends_with(Haystack, Needle) -> H = gleam_stdlib:identity(Haystack), N = gleam_stdlib:identity(Needle), H_size = erlang:byte_size(H), N_size = erlang:byte_size(N), case H_size >= N_size of false -> false; true -> case gleam_stdlib:bit_array_slice(H, H_size - N_size, N_size) of {ok, Suffix} -> Suffix =:= N; {error, _} -> false end end. -file("src/glopenai/file.gleam", 283). ?DOC( " Best-effort `Content-Type` guess from the filename extension. The OpenAI\n" " API does not strictly enforce this for files, but a sensible value avoids\n" " confusing logs and proxies.\n" ). -spec guess_content_type(binary()) -> binary(). guess_content_type(Filename) -> case gleam_stdlib:identity(Filename) of _ -> case ends_with(Filename, <<".pdf"/utf8>>) of true -> <<"application/pdf"/utf8>>; false -> case ends_with(Filename, <<".jsonl"/utf8>>) of true -> <<"application/jsonl"/utf8>>; false -> case ends_with(Filename, <<".json"/utf8>>) of true -> <<"application/json"/utf8>>; false -> case ends_with(Filename, <<".txt"/utf8>>) of true -> <<"text/plain"/utf8>>; false -> <<"application/octet-stream"/utf8>> end end end end end. -file("src/glopenai/file.gleam", 320). -spec openai_file_purpose_to_string(open_ai_file_purpose()) -> binary(). openai_file_purpose_to_string(Purpose) -> case Purpose of assistants -> <<"assistants"/utf8>>; assistants_output -> <<"assistants_output"/utf8>>; batch -> <<"batch"/utf8>>; batch_output -> <<"batch_output"/utf8>>; fine_tune -> <<"fine-tune"/utf8>>; fine_tune_results -> <<"fine-tune-results"/utf8>>; vision -> <<"vision"/utf8>>; user_data -> <<"user_data"/utf8>> end. -file("src/glopenai/file.gleam", 235). ?DOC( " Build a `POST /files` multipart request. The caller chooses `boundary`;\n" " it must not appear inside `request.file_bytes`.\n" ). -spec create_request(glopenai@config:config(), create_file_request(), binary()) -> gleam@http@request:request(bitstring()). create_request(Config, Request, Boundary) -> Base_parts = [{file_part, <<"file"/utf8>>, erlang:element(2, Request), guess_content_type(erlang:element(2, Request)), erlang:element(3, Request)}, {field_part, <<"purpose"/utf8>>, openai_file_purpose_to_string(erlang:element(4, Request))}], Parts = case erlang:element(5, Request) of none -> Base_parts; {some, Expires} -> [{field_part, <<"expires_after[anchor]"/utf8>>, case erlang:element(2, Expires) of created_at -> <<"created_at"/utf8>> end}, {field_part, <<"expires_after[seconds]"/utf8>>, erlang:integer_to_binary(erlang:element(3, Expires))} | Base_parts] end, glopenai@internal:multipart_request( Config, post, <<"/files"/utf8>>, Parts, Boundary ). -file("src/glopenai/file.gleam", 336). ?DOC(" Build a request to list files.\n"). -spec list_request(glopenai@config:config()) -> gleam@http@request:request(binary()). list_request(Config) -> glopenai@internal:get_request(Config, <<"/files"/utf8>>). -file("src/glopenai/file.gleam", 341). ?DOC(" Parse the response from listing files.\n"). -spec list_response(gleam@http@response:response(binary())) -> {ok, list_files_response()} | {error, glopenai@error:glopenai_error()}. list_response(Response) -> glopenai@internal:parse_response(Response, list_files_response_decoder()). -file("src/glopenai/file.gleam", 348). ?DOC(" Build a request to retrieve metadata for a specific file.\n"). -spec retrieve_request(glopenai@config:config(), binary()) -> gleam@http@request:request(binary()). retrieve_request(Config, File_id) -> glopenai@internal:get_request(Config, <<"/files/"/utf8, File_id/binary>>). -file("src/glopenai/file.gleam", 353). ?DOC(" Parse the response from retrieving a file.\n"). -spec retrieve_response(gleam@http@response:response(binary())) -> {ok, open_ai_file()} | {error, glopenai@error:glopenai_error()}. retrieve_response(Response) -> glopenai@internal:parse_response(Response, openai_file_decoder()). -file("src/glopenai/file.gleam", 360). ?DOC(" Build a request to delete a file.\n"). -spec delete_request(glopenai@config:config(), binary()) -> gleam@http@request:request(binary()). delete_request(Config, File_id) -> glopenai@internal:delete_request(Config, <<"/files/"/utf8, File_id/binary>>). -file("src/glopenai/file.gleam", 365). ?DOC(" Parse the response from deleting a file.\n"). -spec delete_response(gleam@http@response:response(binary())) -> {ok, delete_file_response()} | {error, glopenai@error:glopenai_error()}. delete_response(Response) -> glopenai@internal:parse_response(Response, delete_file_response_decoder()). -file("src/glopenai/file.gleam", 373). ?DOC( " Build a request to download the content of a file.\n" " The response body contains the raw file content.\n" ). -spec content_request(glopenai@config:config(), binary()) -> gleam@http@request:request(binary()). content_request(Config, File_id) -> glopenai@internal:get_request( Config, <<<<"/files/"/utf8, File_id/binary>>/binary, "/content"/utf8>> ). -file("src/glopenai/file.gleam", 379). ?DOC( " Parse the response from downloading file content.\n" " Returns the raw body string on success.\n" ). -spec content_response(gleam@http@response:response(binary())) -> {ok, binary()} | {error, glopenai@error:glopenai_error()}. content_response(Response) -> case (erlang:element(2, Response) >= 200) andalso (erlang:element( 2, Response ) < 300) of true -> {ok, erlang:element(4, Response)}; false -> case gleam@json:parse( erlang:element(4, Response), glopenai@error:wrapped_error_decoder() ) of {ok, Api_error} -> {error, {api_response_error, erlang:element(2, Response), Api_error}}; {error, _} -> {error, {unexpected_response, erlang:element(2, Response), erlang:element(4, Response)}} end end.