asimov 0.1.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -1
- data/Gemfile +12 -1
- data/README.md +1 -0
- data/lib/asimov/api_v1/api_error_translator.rb +70 -28
- data/lib/asimov/api_v1/audio.rb +52 -0
- data/lib/asimov/api_v1/base.rb +71 -18
- data/lib/asimov/api_v1/chat.rb +29 -0
- data/lib/asimov/api_v1/completions.rb +11 -3
- data/lib/asimov/api_v1/edits.rb +11 -4
- data/lib/asimov/api_v1/embeddings.rb +15 -4
- data/lib/asimov/api_v1/files.rb +37 -12
- data/lib/asimov/api_v1/finetunes.rb +35 -9
- data/lib/asimov/api_v1/images.rb +37 -10
- data/lib/asimov/api_v1/models.rb +21 -4
- data/lib/asimov/api_v1/moderations.rb +10 -3
- data/lib/asimov/client.rb +35 -2
- data/lib/asimov/configuration.rb +22 -1
- data/lib/asimov/error.rb +95 -1
- data/lib/asimov/utils/chat_messages_validator.rb +63 -0
- data/lib/asimov/utils/classifications_file_validator.rb +8 -0
- data/lib/asimov/utils/file_manager.rb +15 -3
- data/lib/asimov/utils/jsonl_validator.rb +12 -2
- data/lib/asimov/utils/request_options_validator.rb +13 -0
- data/lib/asimov/utils/text_entry_file_validator.rb +2 -0
- data/lib/asimov/utils/training_file_validator.rb +2 -0
- data/lib/asimov/version.rb +2 -1
- data/lib/asimov.rb +1 -1
- metadata +10 -103
@@ -4,28 +4,54 @@ module Asimov
|
|
4
4
|
# Class interface for API methods in the "/fine-tunes" URI subspace.
|
5
5
|
##
|
6
6
|
class Finetunes < Base
|
7
|
-
|
7
|
+
RESOURCE = "fine-tunes".freeze
|
8
|
+
private_constant :RESOURCE
|
8
9
|
|
10
|
+
##
|
11
|
+
# Lists the set of fine-tuning jobs for this API key and (optionally) organization.
|
12
|
+
##
|
9
13
|
def list
|
10
|
-
|
14
|
+
rest_index(resource: RESOURCE)
|
11
15
|
end
|
12
16
|
|
13
|
-
|
14
|
-
|
17
|
+
##
|
18
|
+
# Creates a new fine-tuning job with the specified parameters.
|
19
|
+
#
|
20
|
+
# @param [String] training_file the id of the training file to use for fine tuning
|
21
|
+
# @param [Hash] parameters the parameters passed with the fine tuning job
|
22
|
+
##
|
23
|
+
def create(training_file:, parameters: {})
|
24
|
+
raise MissingRequiredParameterError.new(:training_file) unless training_file
|
15
25
|
|
16
|
-
|
26
|
+
rest_create_w_json_params(resource: RESOURCE,
|
27
|
+
parameters: parameters.merge(training_file: training_file))
|
17
28
|
end
|
18
29
|
|
30
|
+
##
|
31
|
+
# Retrieves the details of a fine-tuning job with the specified id.
|
32
|
+
#
|
33
|
+
# @param [String] fine_tune_id the id of fine tuning job
|
34
|
+
##
|
19
35
|
def retrieve(fine_tune_id:)
|
20
|
-
|
36
|
+
rest_get(resource: RESOURCE, id: fine_tune_id)
|
21
37
|
end
|
22
38
|
|
39
|
+
##
|
40
|
+
# Cancels the details of a fine-tuning job with the specified id.
|
41
|
+
#
|
42
|
+
# @param [String] fine_tune_id the id of fine tuning job
|
43
|
+
##
|
23
44
|
def cancel(fine_tune_id:)
|
24
|
-
|
45
|
+
rest_create_w_multipart_params(resource: [RESOURCE, fine_tune_id, "cancel"])
|
25
46
|
end
|
26
47
|
|
27
|
-
|
28
|
-
|
48
|
+
##
|
49
|
+
# Lists the events associated with a fine-tuning job with the specified id.
|
50
|
+
#
|
51
|
+
# @param [String] fine_tune_id the id of fine tuning job
|
52
|
+
##
|
53
|
+
def list_events(fine_tune_id:)
|
54
|
+
rest_index(resource: [RESOURCE, fine_tune_id, "events"])
|
29
55
|
end
|
30
56
|
end
|
31
57
|
end
|
data/lib/asimov/api_v1/images.rb
CHANGED
@@ -6,22 +6,49 @@ module Asimov
|
|
6
6
|
# Class interface for API methods in the "/images" URI subspace.
|
7
7
|
##
|
8
8
|
class Images < Base
|
9
|
-
|
9
|
+
RESOURCE = "images".freeze
|
10
|
+
private_constant :RESOURCE
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
##
|
13
|
+
# Creates an image using the specified prompt.
|
14
|
+
#
|
15
|
+
# @param [String] prompt the prompt used to create the image
|
16
|
+
# @param [Hash] parameters additional parameters passed to the API
|
17
|
+
##
|
18
|
+
def create(prompt:, parameters: {})
|
19
|
+
raise MissingRequiredParameterError.new(:prompt) unless prompt
|
13
20
|
|
14
|
-
|
21
|
+
rest_create_w_json_params(resource: [RESOURCE, "generations"],
|
22
|
+
parameters: parameters.merge({ prompt: prompt }))
|
15
23
|
end
|
16
24
|
|
17
|
-
|
18
|
-
|
25
|
+
##
|
26
|
+
# Creates edits of the specified image based on the prompt.
|
27
|
+
#
|
28
|
+
# @param [String] image file name or a File-like object of the base image
|
29
|
+
# @param [String] prompt the prompt used to guide the edit
|
30
|
+
# @param [Hash] parameters additional parameters passed to the API
|
31
|
+
##
|
32
|
+
def create_edit(image:, prompt:, parameters: {})
|
33
|
+
raise MissingRequiredParameterError.new(:prompt) unless prompt
|
19
34
|
|
20
|
-
|
35
|
+
rest_create_w_multipart_params(resource: [RESOURCE, "edits"],
|
36
|
+
parameters: open_files(parameters.merge({
|
37
|
+
image: image,
|
38
|
+
prompt: prompt
|
39
|
+
})))
|
21
40
|
end
|
22
41
|
|
23
|
-
|
24
|
-
|
42
|
+
##
|
43
|
+
# Creates variations of the specified image.
|
44
|
+
#
|
45
|
+
# @param [String] image file name or a File-like object of the base image
|
46
|
+
# @param [Hash] parameters additional parameters passed to the API
|
47
|
+
# @option parameters [String] :mask mask file name or a File-like object
|
48
|
+
##
|
49
|
+
def create_variation(image:, parameters: {})
|
50
|
+
rest_create_w_multipart_params(resource: [RESOURCE, "variations"],
|
51
|
+
parameters: open_files(parameters.merge({ image: image })))
|
25
52
|
end
|
26
53
|
|
27
54
|
private
|
@@ -30,7 +57,7 @@ module Asimov
|
|
30
57
|
raise MissingRequiredParameterError.new(:image) unless parameters[:image]
|
31
58
|
|
32
59
|
parameters = parameters.merge(image: Utils::FileManager.open(parameters[:image]))
|
33
|
-
parameters
|
60
|
+
parameters[:mask] = Utils::FileManager.open(parameters[:mask]) if parameters[:mask]
|
34
61
|
parameters
|
35
62
|
end
|
36
63
|
end
|
data/lib/asimov/api_v1/models.rb
CHANGED
@@ -4,18 +4,35 @@ module Asimov
|
|
4
4
|
# Class interface for API methods in the "/models" URI subspace.
|
5
5
|
##
|
6
6
|
class Models < Base
|
7
|
-
|
7
|
+
RESOURCE = "models".freeze
|
8
|
+
private_constant :RESOURCE
|
8
9
|
|
10
|
+
##
|
11
|
+
# Lists the models accessible to this combination of OpenAI API
|
12
|
+
# key and organization id.
|
13
|
+
##
|
9
14
|
def list
|
10
|
-
|
15
|
+
rest_index(resource: RESOURCE)
|
11
16
|
end
|
12
17
|
|
18
|
+
##
|
19
|
+
# Retrieve information about the model with the specified
|
20
|
+
# model_id.
|
21
|
+
#
|
22
|
+
# @param [String] model_id the id of the model to be retrieved
|
23
|
+
##
|
13
24
|
def retrieve(model_id:)
|
14
|
-
|
25
|
+
rest_get(resource: RESOURCE, id: model_id)
|
15
26
|
end
|
16
27
|
|
28
|
+
##
|
29
|
+
# Deletes the model with the specified model_id. Only
|
30
|
+
# works on models created via fine tuning.
|
31
|
+
#
|
32
|
+
# @param [String] model_id the id of the model to be deleted
|
33
|
+
##
|
17
34
|
def delete(model_id:)
|
18
|
-
|
35
|
+
rest_delete(resource: RESOURCE, id: model_id)
|
19
36
|
end
|
20
37
|
end
|
21
38
|
end
|
@@ -4,10 +4,17 @@ module Asimov
|
|
4
4
|
# Class interface for API methods in the "/moderations" URI subspace.
|
5
5
|
##
|
6
6
|
class Moderations < Base
|
7
|
-
|
8
|
-
|
7
|
+
##
|
8
|
+
# Calls the /moderations POST endpoint with the specified parameters.
|
9
|
+
#
|
10
|
+
# @param [String] input the text being evaluated by the API
|
11
|
+
# @param [Hash] parameters the set of parameters being passed to the API
|
12
|
+
##
|
13
|
+
def create(input:, parameters: {})
|
14
|
+
raise MissingRequiredParameterError.new(:input) unless input
|
9
15
|
|
10
|
-
|
16
|
+
rest_create_w_json_params(resource: "moderations",
|
17
|
+
parameters: parameters.merge({ input: input }))
|
11
18
|
end
|
12
19
|
end
|
13
20
|
end
|
data/lib/asimov/client.rb
CHANGED
@@ -3,6 +3,8 @@ require "httparty"
|
|
3
3
|
require_relative "headers_factory"
|
4
4
|
require_relative "utils/request_options_validator"
|
5
5
|
require_relative "api_v1/base"
|
6
|
+
require_relative "api_v1/audio"
|
7
|
+
require_relative "api_v1/chat"
|
6
8
|
require_relative "api_v1/completions"
|
7
9
|
require_relative "api_v1/edits"
|
8
10
|
require_relative "api_v1/embeddings"
|
@@ -20,7 +22,7 @@ module Asimov
|
|
20
22
|
class Client
|
21
23
|
extend Forwardable
|
22
24
|
|
23
|
-
attr_reader :api_key, :organization_id, :api_version, :request_options
|
25
|
+
attr_reader :api_key, :organization_id, :api_version, :request_options, :openai_api_base
|
24
26
|
|
25
27
|
##
|
26
28
|
# Creates a new Asimov::Client. Includes several optional named parameters:
|
@@ -29,17 +31,36 @@ module Asimov
|
|
29
31
|
# defaults to the application-wide configuration
|
30
32
|
# organization_id - The OpenAI organization identifier that this Asimov::Client instance
|
31
33
|
# will use. If unspecified, defaults to the application-wide configuration.
|
34
|
+
# request_options - HTTParty request options that will be passed to the underlying network
|
35
|
+
# client. Merges (and overrides) global configuration value.
|
36
|
+
# openai_api_base - Custom base URI for the API calls made by this client. Defaults to global
|
37
|
+
# configuration value.
|
32
38
|
##
|
33
39
|
def initialize(api_key: nil, organization_id: HeadersFactory::NULL_ORGANIZATION_ID,
|
34
|
-
request_options: {})
|
40
|
+
request_options: {}, openai_api_base: nil)
|
35
41
|
@headers_factory = HeadersFactory.new(api_key,
|
36
42
|
organization_id)
|
37
43
|
@request_options = Asimov.configuration.request_options
|
38
44
|
.merge(Utils::RequestOptionsValidator.validate(request_options))
|
39
45
|
.freeze
|
46
|
+
initialize_openai_api_base(openai_api_base)
|
40
47
|
end
|
41
48
|
def_delegators :@headers_factory, :api_key, :organization_id, :headers
|
42
49
|
|
50
|
+
##
|
51
|
+
# Use the audio method to access API calls in the /audio URI space.
|
52
|
+
##
|
53
|
+
def audio
|
54
|
+
@audio ||= Asimov::ApiV1::Audio.new(client: self)
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Use the chat method to access API calls in the /chat URI space.
|
59
|
+
##
|
60
|
+
def chat
|
61
|
+
@chat ||= Asimov::ApiV1::Chat.new(client: self)
|
62
|
+
end
|
63
|
+
|
43
64
|
##
|
44
65
|
# Use the completions method to access API calls in the /completions URI space.
|
45
66
|
##
|
@@ -95,5 +116,17 @@ module Asimov
|
|
95
116
|
def moderations
|
96
117
|
@moderations ||= Asimov::ApiV1::Moderations.new(client: self)
|
97
118
|
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def initialize_openai_api_base(openai_api_base)
|
123
|
+
@openai_api_base = openai_api_base || Asimov.configuration.openai_api_base
|
124
|
+
if @openai_api_base
|
125
|
+
@openai_api_base = HTTParty.normalize_base_uri(@openai_api_base)
|
126
|
+
else
|
127
|
+
raise Asimov::MissingBaseUriError,
|
128
|
+
"No API Base URI was provided for this client."
|
129
|
+
end
|
130
|
+
end
|
98
131
|
end
|
99
132
|
end
|
data/lib/asimov/configuration.rb
CHANGED
@@ -9,18 +9,39 @@ module Asimov
|
|
9
9
|
class Configuration
|
10
10
|
attr_accessor :api_key, :organization_id
|
11
11
|
|
12
|
-
attr_reader :request_options
|
12
|
+
attr_reader :request_options, :openai_api_base
|
13
13
|
|
14
|
+
DEFAULT_OPENAI_BASE_URI = "https://api.openai.com/v1".freeze
|
15
|
+
|
16
|
+
##
|
17
|
+
# Initializes the Configuration object and resets it to default values.
|
18
|
+
##
|
14
19
|
def initialize
|
15
20
|
reset
|
16
21
|
end
|
17
22
|
|
23
|
+
##
|
24
|
+
# Reset the configuration to default values. Mostly used for testing.
|
25
|
+
##
|
18
26
|
def reset
|
19
27
|
@api_key = nil
|
20
28
|
@organization_id = nil
|
21
29
|
@request_options = {}
|
30
|
+
@openai_api_base = DEFAULT_OPENAI_BASE_URI
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Sets the openai_api_base on the Configuration. Typically not invoked
|
35
|
+
# directly, but rather through use of `Asimov.configure`.
|
36
|
+
##
|
37
|
+
def openai_api_base=(val)
|
38
|
+
@openai_api_base = val ? HTTParty.normalize_base_uri(val) : nil
|
22
39
|
end
|
23
40
|
|
41
|
+
##
|
42
|
+
# Sets the request_options on the Configuration. Typically not invoked
|
43
|
+
# directly, but rather through use of `Asimov.configure`.
|
44
|
+
##
|
24
45
|
def request_options=(val)
|
25
46
|
@request_options = Utils::RequestOptionsValidator.validate(val)
|
26
47
|
end
|
data/lib/asimov/error.rb
CHANGED
@@ -9,8 +9,18 @@ module Asimov
|
|
9
9
|
##
|
10
10
|
class ConfigurationError < Error; end
|
11
11
|
|
12
|
+
##
|
13
|
+
# Error that occurs when there is no configured
|
14
|
+
# API key for a newly created Asimov::Client.
|
15
|
+
##
|
12
16
|
class MissingApiKeyError < ConfigurationError; end
|
13
17
|
|
18
|
+
##
|
19
|
+
# Error that occurs when there is no configured
|
20
|
+
# base URI for a newly created Asimov::Client.
|
21
|
+
##
|
22
|
+
class MissingBaseUriError < ConfigurationError; end
|
23
|
+
|
14
24
|
##
|
15
25
|
# Errors that occur when making an API request. They
|
16
26
|
# can occur either through local validation or
|
@@ -56,6 +66,18 @@ module Asimov
|
|
56
66
|
##
|
57
67
|
class AuthorizationError < RequestError; end
|
58
68
|
|
69
|
+
##
|
70
|
+
# Error that occurs because the provided API key is not
|
71
|
+
# valid.
|
72
|
+
##
|
73
|
+
class InvalidApiKeyError < AuthorizationError; end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Error that occurs because the provided API key is not
|
77
|
+
# valid.
|
78
|
+
##
|
79
|
+
class InvalidOrganizationError < AuthorizationError; end
|
80
|
+
|
59
81
|
##
|
60
82
|
# Errors that occur because of issues with the
|
61
83
|
# parameters of a request. Typically these correspond
|
@@ -87,26 +109,60 @@ module Asimov
|
|
87
109
|
@system_message = system_message
|
88
110
|
end
|
89
111
|
|
112
|
+
##
|
113
|
+
# Returns the error message based on the file name and the wrapped error.
|
114
|
+
##
|
90
115
|
def message
|
91
116
|
"The file #{@file_name} could not be opened for upload because of the " \
|
92
117
|
"following error - #{@system_message}."
|
93
118
|
end
|
94
119
|
end
|
95
120
|
|
121
|
+
##
|
122
|
+
# Error that occurs when a JSONL file is expected
|
123
|
+
# and it cannot be parsed.
|
124
|
+
##
|
96
125
|
class JsonlFileCannotBeParsedError < FileDataError; end
|
97
126
|
|
127
|
+
##
|
128
|
+
# Error that occurs when an invalid training example
|
129
|
+
# is found in a training file.
|
130
|
+
##
|
98
131
|
class InvalidTrainingExampleError < FileDataError; end
|
99
132
|
|
133
|
+
##
|
134
|
+
# Error that occurs when an invalid text entry
|
135
|
+
# is found in a text entry file.
|
136
|
+
##
|
100
137
|
class InvalidTextEntryError < FileDataError; end
|
101
138
|
|
139
|
+
##
|
140
|
+
# Error that occurs when an invalid classification
|
141
|
+
# is found in a classifications file.
|
142
|
+
##
|
102
143
|
class InvalidClassificationError < FileDataError; end
|
103
144
|
|
145
|
+
##
|
146
|
+
# Error that occurs when the provided chat messages
|
147
|
+
# parameter is not valid.
|
148
|
+
##
|
149
|
+
class InvalidChatMessagesError < RequestError; end
|
150
|
+
|
151
|
+
##
|
152
|
+
# Error that occurs when an invalid value is provided
|
153
|
+
# for an expected parameter in a request.
|
154
|
+
##
|
104
155
|
class InvalidParameterValueError < RequestError; end
|
105
156
|
|
157
|
+
##
|
158
|
+
# Error that occurs when an unexpected parameter is
|
159
|
+
# provided in a request.
|
160
|
+
##
|
106
161
|
class UnsupportedParameterError < RequestError; end
|
107
162
|
|
108
163
|
##
|
109
|
-
#
|
164
|
+
# Error raised when a required parameter is not included
|
165
|
+
# in the request.
|
110
166
|
##
|
111
167
|
class MissingRequiredParameterError < RequestError
|
112
168
|
def initialize(parameter_name)
|
@@ -114,6 +170,9 @@ module Asimov
|
|
114
170
|
@parameter_name = parameter_name
|
115
171
|
end
|
116
172
|
|
173
|
+
##
|
174
|
+
# Returns the error message based on the missing parameter name.
|
175
|
+
##
|
117
176
|
def message
|
118
177
|
"The parameter #{@parameter_name} is required."
|
119
178
|
end
|
@@ -126,4 +185,39 @@ module Asimov
|
|
126
185
|
# an object in the OpenAI system.
|
127
186
|
##
|
128
187
|
class NotFoundError < RequestError; end
|
188
|
+
|
189
|
+
##
|
190
|
+
# Errors that occur because the OpenAI API returned
|
191
|
+
# an HTTP code 429. This typically occurs because
|
192
|
+
# you have hit a rate limit or quota limit, or
|
193
|
+
# because the engine is overloaded.
|
194
|
+
##
|
195
|
+
class TooManyRequestsError < RequestError; end
|
196
|
+
|
197
|
+
##
|
198
|
+
# Error that occurs when the quota for an API key
|
199
|
+
# is exceeded.
|
200
|
+
##
|
201
|
+
class QuotaExceededError < TooManyRequestsError; end
|
202
|
+
|
203
|
+
##
|
204
|
+
# Error that occurs when the rate limit for requests
|
205
|
+
# is exceeded.
|
206
|
+
##
|
207
|
+
class RateLimitError < TooManyRequestsError; end
|
208
|
+
|
209
|
+
##
|
210
|
+
# Error that occurs when the API itself is
|
211
|
+
# overloaded and temporarily cannot accept additional
|
212
|
+
# requests.
|
213
|
+
##
|
214
|
+
class ApiOverloadedError < TooManyRequestsError; end
|
215
|
+
|
216
|
+
##
|
217
|
+
# Raised when a non-false stream parameter is passed
|
218
|
+
# to certain API methods. Processing of server-side
|
219
|
+
# events using the stream parameter is currently not
|
220
|
+
# supported.
|
221
|
+
##
|
222
|
+
class StreamingResponseNotSupportedError < RequestError; end
|
129
223
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Asimov
|
2
|
+
module Utils
|
3
|
+
##
|
4
|
+
# Validator for OpenAI's chat message format
|
5
|
+
##
|
6
|
+
class ChatMessagesValidator
|
7
|
+
def self.validate_and_normalize(messages)
|
8
|
+
new.validate(messages)
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate(messages)
|
12
|
+
raise InvalidChatMessagesError, "Chat messages cannot be nil." if messages.nil?
|
13
|
+
|
14
|
+
unless messages.is_a?(Array)
|
15
|
+
raise InvalidChatMessagesError,
|
16
|
+
"Chat messages must be an array."
|
17
|
+
end
|
18
|
+
|
19
|
+
messages.map do |message|
|
20
|
+
validate_message(normalize_parsed(message))
|
21
|
+
end
|
22
|
+
rescue JSON::ParserError
|
23
|
+
raise InvalidChatMessagesError, "Chat messages must be valid JSON."
|
24
|
+
end
|
25
|
+
|
26
|
+
def validate_message(message)
|
27
|
+
raise InvalidChatMessagesError, "Chat messages must be hashes." unless message.is_a?(Hash)
|
28
|
+
|
29
|
+
validate_role(message["role"])
|
30
|
+
|
31
|
+
content = message["content"]
|
32
|
+
raise InvalidChatMessagesError, "Chat messages must have content." if content.nil?
|
33
|
+
|
34
|
+
validate_keys(message)
|
35
|
+
message
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_keys(json)
|
39
|
+
additional_keys = json.keys - %w[role content]
|
40
|
+
return if additional_keys.empty?
|
41
|
+
|
42
|
+
raise InvalidChatMessagesError,
|
43
|
+
"Chat messages must not have additional keys - #{additional_keys.join(', ')}."
|
44
|
+
end
|
45
|
+
|
46
|
+
ALLOWED_ROLES = %w[assistant system user].freeze
|
47
|
+
def validate_role(role)
|
48
|
+
raise InvalidChatMessagesError, "Chat messages must have a role." if role.nil?
|
49
|
+
|
50
|
+
return true if ALLOWED_ROLES.include?(role)
|
51
|
+
|
52
|
+
raise InvalidChatMessagesError,
|
53
|
+
"The value '#{role}' is not a valid role for a chat message."
|
54
|
+
end
|
55
|
+
|
56
|
+
def normalize_parsed(message)
|
57
|
+
return message if message.is_a?(String)
|
58
|
+
|
59
|
+
JSON.parse(message.respond_to?(:to_json) ? message.to_json : message)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
require "json"
|
2
2
|
|
3
3
|
module Asimov
|
4
|
+
##
|
5
|
+
# Set of utilities, primarily intended for internal library use.
|
6
|
+
##
|
4
7
|
module Utils
|
5
8
|
##
|
6
9
|
# Validates that a file is in the "classifications" format
|
@@ -8,8 +11,13 @@ module Asimov
|
|
8
11
|
# "text" and "label" keys for each line that have string
|
9
12
|
# values and an optional "metadata" key that can have
|
10
13
|
# any value. No other keys are permitted.
|
14
|
+
#
|
15
|
+
# The only method that clients should call on instances
|
16
|
+
# of this class is `validate`
|
11
17
|
##
|
12
18
|
class ClassificationsFileValidator < JsonlValidator
|
19
|
+
private
|
20
|
+
|
13
21
|
def validate_line(line, idx)
|
14
22
|
parsed = JSON.parse(line)
|
15
23
|
validate_classification(parsed, idx)
|
@@ -8,11 +8,23 @@ module Asimov
|
|
8
8
|
# Not intended for client use.
|
9
9
|
##
|
10
10
|
class FileManager
|
11
|
-
|
12
|
-
|
11
|
+
##
|
12
|
+
# Returns the file corresponding to the file_or_path. If the argument is a
|
13
|
+
# file, then just returns the argument. Otherwise calls File.open with the
|
14
|
+
# argument. Raises an error if the file cannot be opened.
|
15
|
+
#
|
16
|
+
# @param [File/String] file_or_path the path to the file to be opened
|
17
|
+
##
|
18
|
+
def self.open(file_or_path)
|
19
|
+
file?(file_or_path) ? file_or_path : File.open(file_or_path)
|
13
20
|
rescue SystemCallError => e
|
14
|
-
raise Asimov::FileCannotBeOpenedError.new(
|
21
|
+
raise Asimov::FileCannotBeOpenedError.new(file_or_path, e.message)
|
15
22
|
end
|
23
|
+
|
24
|
+
def self.file?(object)
|
25
|
+
object.respond_to?(:path) && object.respond_to?(:read)
|
26
|
+
end
|
27
|
+
private_class_method :file?
|
16
28
|
end
|
17
29
|
end
|
18
30
|
end
|
@@ -7,13 +7,23 @@ module Asimov
|
|
7
7
|
# more specific file validators.
|
8
8
|
##
|
9
9
|
class JsonlValidator
|
10
|
-
|
11
|
-
|
10
|
+
##
|
11
|
+
# Validate that the IO object (typically a File) is properly
|
12
|
+
# formatted. Entry method for this class and its subclasses.
|
13
|
+
# Required format will depend on the class.
|
14
|
+
#
|
15
|
+
# @param [IO] io IO object, typically a file, whose contents
|
16
|
+
# are to be format checked.
|
17
|
+
##
|
18
|
+
def validate(io)
|
19
|
+
io.each_line.with_index { |line, idx| validate_line(line, idx) }
|
12
20
|
true
|
13
21
|
rescue JSON::ParserError
|
14
22
|
raise JsonlFileCannotBeParsedError, "Expected file to have the JSONL format."
|
15
23
|
end
|
16
24
|
|
25
|
+
private
|
26
|
+
|
17
27
|
def validate_line(line, idx)
|
18
28
|
JSON.parse(line)
|
19
29
|
rescue JSON::ParserError
|
@@ -14,6 +14,16 @@ module Asimov
|
|
14
14
|
ALLOWED_OPTIONS = %i[timeout open_timeout read_timeout write_timeout local_host local_port
|
15
15
|
verify verify_peer ssl_ca_file ssl_ca_path ssl_version ciphers
|
16
16
|
http_proxyaddr http_proxyport http_proxyuser http_proxypass].freeze
|
17
|
+
|
18
|
+
##
|
19
|
+
# Validates that the options are allowed request
|
20
|
+
# options. Currently checks the keys - both that they are symbols and that
|
21
|
+
# they are allowed options. Does not validate values.
|
22
|
+
#
|
23
|
+
# Only entry point for this class.
|
24
|
+
#
|
25
|
+
# @param [Hash] options the set of request options to validate
|
26
|
+
##
|
17
27
|
def self.validate(options)
|
18
28
|
unless options.is_a?(Hash)
|
19
29
|
raise Asimov::ConfigurationError,
|
@@ -32,6 +42,7 @@ module Asimov
|
|
32
42
|
end
|
33
43
|
unsupported_options
|
34
44
|
end
|
45
|
+
private_class_method :generate_unsupported_options
|
35
46
|
|
36
47
|
def self.check_unsupported_options(unsupported_options)
|
37
48
|
return if unsupported_options.empty?
|
@@ -40,6 +51,7 @@ module Asimov
|
|
40
51
|
raise Asimov::ConfigurationError,
|
41
52
|
"The options #{quoted_keys.join(',')} are not supported."
|
42
53
|
end
|
54
|
+
private_class_method :check_unsupported_options
|
43
55
|
|
44
56
|
def self.supported_option?(key)
|
45
57
|
ALLOWED_OPTIONS.include?(key)
|
@@ -51,6 +63,7 @@ module Asimov
|
|
51
63
|
raise Asimov::ConfigurationError,
|
52
64
|
"Request options keys must be symbols. The key '#{key}' is not a symbol."
|
53
65
|
end
|
66
|
+
private_class_method :check_symbol
|
54
67
|
end
|
55
68
|
end
|
56
69
|
end
|
data/lib/asimov/version.rb
CHANGED