lib/openai/files.rb in ruby-openai-6.5.0 vs lib/openai/files.rb in ruby-openai-7.0.0

- old
+ new

@@ -1,22 +1,33 @@ module OpenAI class Files + PURPOSES = %w[ + assistants + batch + fine-tune + ].freeze + def initialize(client:) @client = client end def list @client.get(path: "/files") end def upload(parameters: {}) - validate(file: parameters[:file]) if parameters[:file].include?(".jsonl") + file_input = parameters[:file] + file = prepare_file_input(file_input: file_input) + validate(file: file, purpose: parameters[:purpose], file_input: file_input) + @client.multipart_post( path: "/files", - parameters: parameters.merge(file: File.open(parameters[:file])) + parameters: parameters.merge(file: file) ) + ensure + file.close if file.is_a?(File) end def retrieve(id:) @client.get(path: "/files/#{id}") end @@ -29,14 +40,35 @@ @client.delete(path: "/files/#{id}") end private - def validate(file:) - File.open(file).each_line.with_index do |line, index| + def prepare_file_input(file_input:) + if file_input.is_a?(String) + File.open(file_input) + elsif file_input.respond_to?(:read) && file_input.respond_to?(:rewind) + file_input + else + raise ArgumentError, "Invalid file - must be a StringIO object or a path to a file." + end + end + + def validate(file:, purpose:, file_input:) + raise ArgumentError, "`file` is required" if file.nil? + unless PURPOSES.include?(purpose) + raise ArgumentError, "`purpose` must be one of `#{PURPOSES.join(',')}`" + end + + validate_jsonl(file: file) if file_input.is_a?(String) && file_input.end_with?(".jsonl") + end + + def validate_jsonl(file:) + file.each_line.with_index do |line, index| JSON.parse(line) rescue JSON::ParserError => e raise JSON::ParserError, "#{e.message} - found on line #{index + 1} of #{file}" end + ensure + file.rewind end end end