Skip to content

Commit a33ddf9

Browse files
committed
Handle unknown file types more gracefully
1 parent 89d6435 commit a33ddf9

File tree

4 files changed

+49
-29
lines changed

4 files changed

+49
-29
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
### Changed
1616

1717
- [BREAKING] Try to JSON parse everything. If it fails, always fall back to returning the raw response. Thank you to [@gregszero](https://github.com/gregszero) and the many others who raised this issue.
18+
- [BREAKING] An unknown file type will no longer prevent file upload, but instead raise a warning.
19+
- Add `user_data` and `evals` as options for known File types - thank you to [jontec](https://github.com/jontec) for this fix!
1820

1921
### Removed
2022

lib/openai.rb

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
require "faraday"
22
require "faraday/multipart" if Gem::Version.new(Faraday::VERSION) >= Gem::Version.new("2.0")
3-
43
require_relative "openai/http"
54
require_relative "openai/client"
65
require_relative "openai/files"
@@ -31,13 +30,7 @@ def call(env)
3130
@app.call(env)
3231
rescue Faraday::Error => e
3332
raise e unless e.response.is_a?(Hash)
34-
35-
logger = Logger.new($stdout)
36-
logger.formatter = proc do |_severity, _datetime, _progname, msg|
37-
"\033[31mOpenAI HTTP Error (spotted in ruby-openai #{VERSION}): #{msg}\n\033[0m"
38-
end
39-
logger.error(e.response[:body])
40-
33+
OpenAI.log_message("OpenAI HTTP Error", e.response[:body], :error)
4134
raise e
4235
end
4336
end
@@ -73,25 +66,37 @@ def initialize
7366

7467
class << self
7568
attr_writer :configuration
76-
end
7769

78-
def self.configuration
79-
@configuration ||= OpenAI::Configuration.new
80-
end
70+
def configuration
71+
@configuration ||= OpenAI::Configuration.new
72+
end
8173

82-
def self.configure
83-
yield(configuration)
84-
end
74+
def configure
75+
yield(configuration)
76+
end
8577

86-
# Estimate the number of tokens in a string, using the rules of thumb from OpenAI:
87-
# https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them
88-
def self.rough_token_count(content = "")
89-
raise ArgumentError, "rough_token_count requires a string" unless content.is_a? String
90-
return 0 if content.empty?
78+
# Estimate the number of tokens in a string, using the rules of thumb from OpenAI:
79+
# https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them
80+
def rough_token_count(content = "")
81+
raise ArgumentError, "rough_token_count requires a string" unless content.is_a? String
82+
return 0 if content.empty?
83+
count_by_chars = content.size / 4.0
84+
count_by_words = content.split.size * 4.0 / 3
85+
estimate = ((count_by_chars + count_by_words) / 2.0).round
86+
[1, estimate].max
87+
end
9188

92-
count_by_chars = content.size / 4.0
93-
count_by_words = content.split.size * 4.0 / 3
94-
estimate = ((count_by_chars + count_by_words) / 2.0).round
95-
[1, estimate].max
89+
# Log a message with appropriate formatting
90+
# @param prefix [String] Prefix to add to the message
91+
# @param message [String] The message to log
92+
# @param level [Symbol] The log level (:error, :warn, etc.)
93+
def log_message(prefix, message, level = :warn)
94+
color = level == :error ? "\033[31m" : "\033[33m"
95+
logger = Logger.new($stdout)
96+
logger.formatter = proc do |_severity, _datetime, _progname, msg|
97+
"#{color}#{prefix} (spotted in ruby-openai #{VERSION}): #{msg}\n\033[0m"
98+
end
99+
logger.send(level, message)
100+
end
96101
end
97102
end

lib/openai/files.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ def list(parameters: {})
2020
def upload(parameters: {})
2121
file_input = parameters[:file]
2222
file = prepare_file_input(file_input: file_input)
23-
2423
validate(file: file, purpose: parameters[:purpose], file_input: file_input)
25-
2624
@client.multipart_post(
2725
path: "/files",
2826
parameters: parameters.merge(file: file)
@@ -57,8 +55,12 @@ def prepare_file_input(file_input:)
5755

5856
def validate(file:, purpose:, file_input:)
5957
raise ArgumentError, "`file` is required" if file.nil?
58+
6059
unless PURPOSES.include?(purpose)
61-
raise ArgumentError, "`purpose` must be one of `#{PURPOSES.join(',')}`"
60+
filename = file_input.is_a?(String) ? File.basename(file_input) : "uploaded file"
61+
message = "The purpose '#{purpose}' for file '#{filename}' is not in the known purpose "
62+
message += "list: #{PURPOSES.join(', ')}."
63+
OpenAI.log_message("Warning", message, :warn)
6264
end
6365

6466
validate_jsonl(file: file) if file_input.is_a?(String) && file_input.end_with?(".jsonl")

spec/openai/client/files_spec.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,21 @@
2121
end
2222

2323
context "with an invalid purpose" do
24-
let(:cassette_label) { "unused" }
24+
let(:cassette_label) { "invalid purpose" }
2525
let(:upload_purpose) { "invalid" }
2626

27-
it { expect { upload }.to raise_error(ArgumentError) }
27+
it "logs a warning" do
28+
expected_message = "The purpose 'invalid' for file 'sentiment.jsonl' is not in the known "
29+
expected_message += "purpose list: #{OpenAI::Files::PURPOSES.join(', ')}."
30+
31+
expect(OpenAI).to receive(:log_message)
32+
.with("Warning", expected_message, :warn)
33+
.and_call_original
34+
35+
allow_any_instance_of(OpenAI::Client).to receive(:multipart_post).and_return({})
36+
37+
upload
38+
end
2839
end
2940

3041
context "with a `File` instance content" do

0 commit comments

Comments
 (0)