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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d49b0bc867c3e318501548e7fcf45a3dd48bc469b05f959b5839144e3f9d472
|
4
|
+
data.tar.gz: 1193a9a5eea75c3d3b57d09ca10ccd5e50ee01d44ecce648c985ac2bcfd5cded
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 349259c93c833021ebfc46f7912f8c36109dc51693c3828972d263282f514adee94b5416d9e89c2dc1cc1d868d01cabce20ef1df01e83279eac80d156b1ded61
|
7
|
+
data.tar.gz: 67deac4b3306ffdc5ab36aa49b7ab66093dc4938131b3264ce2ef2cd6a628840cd04d3474f39fe7bc1f70dc5c0d9dba7bd136c28c46c58b776ff2494ae825030
|
data/CHANGELOG.md
CHANGED
@@ -5,9 +5,42 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## Unreleased
|
9
|
+
|
10
|
+
## [1.1.0] - 2023-03-01
|
11
|
+
|
12
|
+
### Added
|
13
|
+
|
14
|
+
- Made API base URI configurable to support services that proxy API calls (like Helicone)
|
15
|
+
- Added support for the chat and audio endpoints
|
16
|
+
|
17
|
+
## [1.0.0] - 2023-01-24
|
18
|
+
|
19
|
+
This version has complete coverage of the OpenAI API (except for stream: true behavior), has
|
20
|
+
no known errors and has full test coverage. At this point there are no anticipated changes
|
21
|
+
to existing endpoints.
|
22
|
+
|
23
|
+
### Fixed
|
24
|
+
|
25
|
+
- Fixed handling of authentication errors
|
26
|
+
|
27
|
+
### Changed
|
28
|
+
|
29
|
+
- Properly distinguished public and private methods to ensure proper documentation.
|
30
|
+
- Renamed events to list_events for consistency
|
31
|
+
- Updated file arguments to take path strings or File-like objects
|
32
|
+
- Adjusted endpoints to make required parameters more explicit
|
33
|
+
|
34
|
+
### Added
|
35
|
+
|
36
|
+
- Code level documentation for all public classes and methods.
|
37
|
+
- Error for the unsupported stream: true case
|
38
|
+
- Error mapping for 409 and 429 errors
|
39
|
+
- Specs for authentication
|
40
|
+
|
8
41
|
## [0.1.0] - 2023-01-14
|
9
42
|
|
10
|
-
This initial version of the gem is now suitable for use. API endpoint method naming may shift slightly
|
43
|
+
This initial version of the gem is now suitable for use. API endpoint method naming may shift slightly.
|
11
44
|
|
12
45
|
### Changed
|
13
46
|
|
data/Gemfile
CHANGED
@@ -2,4 +2,15 @@ source "https://rubygems.org"
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
-
|
5
|
+
group :development, :test do
|
6
|
+
gem "faker"
|
7
|
+
gem "rake", "~> 13.0"
|
8
|
+
gem "rspec", "~> 3.12"
|
9
|
+
gem "rubocop", "~> 1.44"
|
10
|
+
gem "rubocop-performance"
|
11
|
+
gem "rubocop-rake"
|
12
|
+
gem "rubocop-rspec"
|
13
|
+
gem "simplecov", require: false
|
14
|
+
gem "vcr", "~> 6.1.0"
|
15
|
+
gem "webmock", "~> 3.18.1"
|
16
|
+
end
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Asimov
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/asimov)
|
3
4
|
[](https://github.com/petergoldstein/asimov/blob/main/LICENSE.txt)
|
4
5
|
[](https://github.com/petergoldstein/asimov/actions/workflows/ci.yml)
|
5
6
|
|
@@ -8,26 +8,47 @@ module Asimov
|
|
8
8
|
# fragile.
|
9
9
|
##
|
10
10
|
class ApiErrorTranslator
|
11
|
+
##
|
12
|
+
# This method raises an appropriate Asimov::RequestError
|
13
|
+
# subclass if the response corresponds to an HTTP error.
|
14
|
+
#
|
15
|
+
# @param [HTTParty::Response] resp the response (or fragment) object
|
16
|
+
# encapsulating the server response.
|
17
|
+
##
|
11
18
|
def self.translate(resp)
|
19
|
+
return if resp.code == 200
|
20
|
+
|
12
21
|
match_400(resp)
|
13
22
|
match_401(resp)
|
14
23
|
match_404(resp)
|
24
|
+
match_409(resp)
|
25
|
+
match_429(resp)
|
26
|
+
|
27
|
+
raise Asimov::RequestError, error_message(resp)
|
15
28
|
end
|
16
29
|
|
17
30
|
# rubocop:disable Naming/VariableNumber
|
18
31
|
# rubocop:disable Metrics/MethodLength
|
32
|
+
|
33
|
+
# Prefix for OpenAI error message when an invalid key is provided.
|
19
34
|
INVALID_API_KEY_PREFIX = "Incorrect API key provided: ".freeze
|
35
|
+
|
36
|
+
# Prefix for OpenAI error message when an organization cannot be found.
|
20
37
|
INVALID_ORGANIZATION_PREFIX = "No such organization: ".freeze
|
21
38
|
def self.match_401(resp)
|
22
39
|
return unless resp.code == 401
|
23
40
|
|
24
41
|
msg = error_message(resp)
|
25
42
|
raise Asimov::InvalidApiKeyError, msg if msg.start_with?(INVALID_API_KEY_PREFIX)
|
26
|
-
raise Asimov::InvalidOrganizationError, msg if msg.start_with?(
|
43
|
+
raise Asimov::InvalidOrganizationError, msg if msg.start_with?(INVALID_ORGANIZATION_PREFIX)
|
27
44
|
|
28
|
-
raise Asimov::
|
45
|
+
raise Asimov::AuthorizationError
|
29
46
|
end
|
47
|
+
private_class_method :match_401
|
48
|
+
private_constant :INVALID_API_KEY_PREFIX
|
49
|
+
private_constant :INVALID_ORGANIZATION_PREFIX
|
30
50
|
|
51
|
+
# Prefix for OpenAI error message when training file format cannot be validated.
|
31
52
|
INVALID_TRAINING_EXAMPLE_PREFIX = "Expected file to have JSONL format with " \
|
32
53
|
"prompt/completion keys. Missing".freeze
|
33
54
|
ADDITIONAL_PROPERTIES_ERROR_PREFIX = "Additional properties are not allowed".freeze
|
@@ -39,28 +60,6 @@ module Asimov
|
|
39
60
|
return unless resp.code == 400
|
40
61
|
|
41
62
|
msg = error_message(resp)
|
42
|
-
# 400
|
43
|
-
# {"error"=>{"message"=>"'moose' is not one of ['fine-tune', 'answers', 'search', " \
|
44
|
-
# "'classifications'] - 'purpose'", "type"=>"invalid_request_error",
|
45
|
-
# "param"=>nil, "code"=>nil}}
|
46
|
-
# {"error"=>{"code"=>nil, "message"=>"'8x8' is not one of ['256x256', '512x512', " \
|
47
|
-
# "'1024x1024'] - 'size'", "param"=>nil,
|
48
|
-
# "type"=>"invalid_request_error"}}
|
49
|
-
# {"error"=>{"message"=>"Incorrect format for purpose=classifications. Please check " \
|
50
|
-
# "the openai documentation and try again",
|
51
|
-
# "type"=>"invalid_request_error", "param"=>nil, "code"=>nil}}
|
52
|
-
# {"error"=>{"message"=>"Expected file to have the JSONL format with 'text' key " \
|
53
|
-
# "and (optional) 'metadata' key.",
|
54
|
-
# "type"=>"invalid_request_error", "param"=>nil, "code"=>nil}}
|
55
|
-
# {"error"=>{"message"=>"Additional properties are not allowed ('moose' was unexpected)",
|
56
|
-
# "type"=>"invalid_request_error", "param"=>nil, "code"=>nil}}
|
57
|
-
# {"error"=>{"message"=>"Additional properties are not allowed ('moose', 'squirrel' were " \
|
58
|
-
# "unexpected)",
|
59
|
-
# "type"=>"invalid_request_error", "param"=>nil, "code"=>nil}}
|
60
|
-
# {"error"=>{"code"=>nil, "message"=>"-1 is less than the minimum of 1 - 'n'",
|
61
|
-
# "param"=>nil, "type"=>"invalid_request_error"}}
|
62
|
-
# {"error"=>{"code"=>nil, "message"=>"20 is greater than the maximum of 10 - 'n'",
|
63
|
-
# "param"=>nil, "type"=>"invalid_request_error"}}
|
64
63
|
|
65
64
|
if msg.start_with?(INVALID_TRAINING_EXAMPLE_PREFIX)
|
66
65
|
raise Asimov::InvalidTrainingExampleError,
|
@@ -78,6 +77,50 @@ module Asimov
|
|
78
77
|
|
79
78
|
raise Asimov::RequestError, msg
|
80
79
|
end
|
80
|
+
private_class_method :match_400
|
81
|
+
private_constant :INVALID_TRAINING_EXAMPLE_PREFIX
|
82
|
+
private_constant :ADDITIONAL_PROPERTIES_ERROR_PREFIX
|
83
|
+
private_constant :INVALID_PARAMETER_VALUE_STRING
|
84
|
+
private_constant :INVALID_PARAMETER_VALUE_PREFIX_2
|
85
|
+
private_constant :BELOW_MINIMUM_STRING
|
86
|
+
private_constant :ABOVE_MAXIMUM_STRING
|
87
|
+
|
88
|
+
def self.match_409(resp)
|
89
|
+
return unless resp.code == 409
|
90
|
+
|
91
|
+
raise Asimov::RequestError, error_message(resp)
|
92
|
+
end
|
93
|
+
private_class_method :match_409
|
94
|
+
|
95
|
+
QUOTA_EXCEEDED_MESSAGE = "You exceeded your current quota".freeze
|
96
|
+
RATE_LIMIT_REACHED_MESSAGE = "Rate limit reached".freeze
|
97
|
+
ENGINE_OVERLOADED_MESSAGE = "The engine is currently overloaded".freeze
|
98
|
+
def self.match_429(resp)
|
99
|
+
return unless resp.code == 429
|
100
|
+
|
101
|
+
msg = error_message(resp)
|
102
|
+
|
103
|
+
if msg.start_with?(QUOTA_EXCEEDED_MESSAGE)
|
104
|
+
raise Asimov::QuotaExceededError,
|
105
|
+
msg
|
106
|
+
end
|
107
|
+
|
108
|
+
if msg.start_with?(RATE_LIMIT_REACHED_MESSAGE)
|
109
|
+
raise Asimov::RateLimitError,
|
110
|
+
msg
|
111
|
+
end
|
112
|
+
|
113
|
+
if msg.start_with?(ENGINE_OVERLOADED_MESSAGE)
|
114
|
+
raise Asimov::ApiOverloadedError,
|
115
|
+
msg
|
116
|
+
end
|
117
|
+
|
118
|
+
raise Asimov::TooManyRequestsError, msg
|
119
|
+
end
|
120
|
+
private_class_method :match_429
|
121
|
+
private_constant :QUOTA_EXCEEDED_MESSAGE
|
122
|
+
private_constant :RATE_LIMIT_REACHED_MESSAGE
|
123
|
+
private_constant :ENGINE_OVERLOADED_MESSAGE
|
81
124
|
|
82
125
|
def self.match_invalid_parameter_value?(msg)
|
83
126
|
msg.include?(INVALID_PARAMETER_VALUE_STRING) ||
|
@@ -85,17 +128,15 @@ module Asimov
|
|
85
128
|
msg.include?(ABOVE_MAXIMUM_STRING) ||
|
86
129
|
msg.start_with?(INVALID_PARAMETER_VALUE_PREFIX_2)
|
87
130
|
end
|
131
|
+
private_class_method :match_invalid_parameter_value?
|
88
132
|
|
89
133
|
def self.match_404(resp)
|
90
134
|
return unless resp.code == 404
|
91
135
|
|
92
136
|
msg = error_message(resp)
|
93
|
-
# {"error"=>{"message"=>"That model does not exist", "type"=>"invalid_request_error",
|
94
|
-
# "param"=>"model", "code"=>nil}}
|
95
|
-
# {"error"=>{"message"=>"No such File object: file-BWp1k9EVJRq5Ybjr3Mb0tDXW",
|
96
|
-
# "type"=>"invalid_request_error", "param"=>"id", "code"=>nil}}
|
97
137
|
raise Asimov::NotFoundError, msg
|
98
138
|
end
|
139
|
+
private_class_method :match_404
|
99
140
|
|
100
141
|
# rubocop:enable Naming/VariableNumber
|
101
142
|
# rubocop:enable Metrics/MethodLength
|
@@ -110,6 +151,7 @@ module Asimov
|
|
110
151
|
|
111
152
|
pr["error"]["message"] || ""
|
112
153
|
end
|
154
|
+
private_class_method :error_message
|
113
155
|
end
|
114
156
|
end
|
115
157
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative "../utils/chat_messages_validator"
|
2
|
+
|
3
|
+
module Asimov
|
4
|
+
module ApiV1
|
5
|
+
##
|
6
|
+
# Class interface for API methods in the "/audio" URI subspace.
|
7
|
+
##
|
8
|
+
class Audio < Base
|
9
|
+
RESOURCE = "audio".freeze
|
10
|
+
|
11
|
+
##
|
12
|
+
# Creates a transcription request with the specified parameters.
|
13
|
+
#
|
14
|
+
# @param [String] model the model to use for the completion
|
15
|
+
# @param [Hash] parameters the set of parameters being passed to the API
|
16
|
+
##
|
17
|
+
def create_transcription(file:, model:, parameters: {})
|
18
|
+
raise MissingRequiredParameterError.new(:model) unless model
|
19
|
+
|
20
|
+
rest_create_w_multipart_params(resource: [RESOURCE, "transcriptions"],
|
21
|
+
parameters:
|
22
|
+
open_file(parameters.merge({
|
23
|
+
file: file, model: model
|
24
|
+
})))
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Creates a transcription request with the specified parameters.
|
29
|
+
#
|
30
|
+
# @param [String] model the model to use for the completion
|
31
|
+
# @param [Hash] parameters the set of parameters being passed to the API
|
32
|
+
##
|
33
|
+
def create_translation(file:, model:, parameters: {})
|
34
|
+
raise MissingRequiredParameterError.new(:model) unless model
|
35
|
+
|
36
|
+
rest_create_w_multipart_params(resource: [RESOURCE, "translations"],
|
37
|
+
parameters:
|
38
|
+
open_file(parameters.merge({
|
39
|
+
file: file, model: model
|
40
|
+
})))
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def open_file(parameters)
|
46
|
+
raise MissingRequiredParameterError.new(:file) unless parameters[:file]
|
47
|
+
|
48
|
+
parameters.merge(file: Utils::FileManager.open(parameters[:file]))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/asimov/api_v1/base.rb
CHANGED
@@ -2,6 +2,10 @@ require_relative "./api_error_translator"
|
|
2
2
|
require_relative "./network_error_translator"
|
3
3
|
|
4
4
|
module Asimov
|
5
|
+
##
|
6
|
+
# Classes and method associated with the requests, responses, and
|
7
|
+
# errors associated with v1 of the OpenAI API.
|
8
|
+
##
|
5
9
|
module ApiV1
|
6
10
|
##
|
7
11
|
# Base class for API interface implementations. Currently
|
@@ -11,65 +15,115 @@ module Asimov
|
|
11
15
|
extend Forwardable
|
12
16
|
include HTTParty
|
13
17
|
|
14
|
-
base_uri "https://api.openai.com/v1"
|
15
|
-
|
16
18
|
def initialize(client: nil)
|
17
19
|
@client = client
|
18
20
|
end
|
19
|
-
def_delegators :@client, :headers, :request_options
|
21
|
+
def_delegators :@client, :headers, :request_options, :openai_api_base
|
20
22
|
|
21
|
-
|
23
|
+
##
|
24
|
+
# Executes a REST index for the specified resource
|
25
|
+
#
|
26
|
+
# @param [String] resource the pluralized resource name
|
27
|
+
##
|
28
|
+
def rest_index(resource:)
|
29
|
+
wrap_response_with_error_handling do
|
30
|
+
self.class.get(
|
31
|
+
absolute_path("/#{Array(resource).join('/')}"),
|
32
|
+
{ headers: headers }.merge!(request_options)
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Executes a REST delete on the specified resource.
|
39
|
+
#
|
40
|
+
# @param [String] resource the pluralized resource name
|
41
|
+
# @param [String] id the id of the resource to delete
|
42
|
+
##
|
43
|
+
def rest_delete(resource:, id:)
|
22
44
|
wrap_response_with_error_handling do
|
23
45
|
self.class.delete(
|
24
|
-
|
46
|
+
absolute_path("/#{resource}/#{CGI.escape(id)}"),
|
25
47
|
{ headers: headers }.merge!(request_options)
|
26
48
|
)
|
27
49
|
end
|
28
50
|
end
|
29
51
|
|
30
|
-
|
52
|
+
##
|
53
|
+
# Executes a REST get on the specified resource.
|
54
|
+
#
|
55
|
+
# @param [String] resource the pluralized resource name
|
56
|
+
# @param [String] id the id of the resource get
|
57
|
+
##
|
58
|
+
def rest_get(resource:, id:)
|
31
59
|
wrap_response_with_error_handling do
|
32
60
|
self.class.get(
|
33
|
-
|
61
|
+
absolute_path("/#{resource}/#{CGI.escape(id)}"),
|
34
62
|
{ headers: headers }.merge!(request_options)
|
35
63
|
)
|
36
64
|
end
|
37
65
|
end
|
38
66
|
|
39
|
-
|
67
|
+
##
|
68
|
+
# Executes a REST create with JSON-encoded parameters for the specified
|
69
|
+
# resource.
|
70
|
+
#
|
71
|
+
# @param [String] the resource to be created.
|
72
|
+
# @param [Hash] parameters the parameters to include with the request
|
73
|
+
# to create the resource
|
74
|
+
##
|
75
|
+
def rest_create_w_json_params(resource:, parameters:)
|
40
76
|
wrap_response_with_error_handling do
|
41
77
|
self.class.post(
|
42
|
-
|
78
|
+
absolute_path("/#{Array(resource).join('/')}"),
|
43
79
|
{ headers: headers,
|
44
80
|
body: parameters&.to_json }.merge!(request_options)
|
45
81
|
)
|
46
82
|
end
|
47
83
|
end
|
48
84
|
|
49
|
-
|
85
|
+
##
|
86
|
+
# Executes a REST create with multipart-encoded parameters for the specified
|
87
|
+
# resource.
|
88
|
+
#
|
89
|
+
# @param [String] the resource to be created.
|
90
|
+
# @param [Hash] parameters the optional parameters to include with the request
|
91
|
+
# to create the resource
|
92
|
+
##
|
93
|
+
def rest_create_w_multipart_params(resource:, parameters: nil)
|
50
94
|
wrap_response_with_error_handling do
|
51
95
|
self.class.post(
|
52
|
-
|
96
|
+
absolute_path("/#{Array(resource).join('/')}"),
|
53
97
|
{ headers: headers("multipart/form-data"),
|
54
98
|
body: parameters }.merge!(request_options)
|
55
99
|
)
|
56
100
|
end
|
57
101
|
end
|
58
102
|
|
59
|
-
|
60
|
-
|
103
|
+
##
|
104
|
+
# Executes an REST get on the specified path, streaming the resulting body
|
105
|
+
# to the writer in case of success.
|
106
|
+
#
|
107
|
+
# @param [Array] resource the resource path elements as an array
|
108
|
+
# @param [Writer] writer an object, typically a File, that responds to a `write` method
|
109
|
+
##
|
110
|
+
def rest_get_streamed_download(resource:, writer:)
|
111
|
+
self.class.get(absolute_path("/#{Array(resource).join('/')}"),
|
61
112
|
{ headers: headers,
|
62
113
|
stream_body: true }.merge!(request_options)) do |fragment|
|
63
114
|
fragment.code == 200 ? writer.write(fragment) : check_for_api_error(fragment)
|
64
115
|
end
|
65
|
-
rescue Asimov::RequestError => e
|
66
|
-
# Raise any translated API errors
|
67
|
-
raise e
|
68
116
|
rescue StandardError => e
|
69
|
-
#
|
117
|
+
# Any error raised by the HTTP call is a network error
|
70
118
|
NetworkErrorTranslator.translate(e)
|
71
119
|
end
|
72
120
|
|
121
|
+
private
|
122
|
+
|
123
|
+
def absolute_path(path)
|
124
|
+
"#{openai_api_base}#{path}"
|
125
|
+
end
|
126
|
+
|
73
127
|
def wrap_response_with_error_handling
|
74
128
|
resp = begin
|
75
129
|
yield
|
@@ -84,7 +138,6 @@ module Asimov
|
|
84
138
|
return if resp.code == 200
|
85
139
|
|
86
140
|
ApiErrorTranslator.translate(resp)
|
87
|
-
resp
|
88
141
|
end
|
89
142
|
end
|
90
143
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "../utils/chat_messages_validator"
|
2
|
+
|
3
|
+
module Asimov
|
4
|
+
module ApiV1
|
5
|
+
##
|
6
|
+
# Class interface for API methods in the "/chat" URI subspace.
|
7
|
+
##
|
8
|
+
class Chat < Base
|
9
|
+
RESOURCE = "chat".freeze
|
10
|
+
|
11
|
+
##
|
12
|
+
# Creates a completion request with the specified parameters.
|
13
|
+
#
|
14
|
+
# @param [String] model the model to use for the completion
|
15
|
+
# @param [Hash] parameters the set of parameters being passed to the API
|
16
|
+
##
|
17
|
+
def create_completions(model:, messages:, parameters: {})
|
18
|
+
raise MissingRequiredParameterError.new(:model) unless model
|
19
|
+
raise MissingRequiredParameterError.new(:messages) unless messages
|
20
|
+
raise StreamingResponseNotSupportedError if parameters[:stream]
|
21
|
+
|
22
|
+
messages = Utils::ChatMessagesValidator.validate_and_normalize(messages)
|
23
|
+
rest_create_w_json_params(resource: [RESOURCE, "completions"],
|
24
|
+
parameters: parameters.merge({ model: model,
|
25
|
+
messages: messages }))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -4,10 +4,18 @@ module Asimov
|
|
4
4
|
# Class interface for API methods in the "/completions" URI subspace.
|
5
5
|
##
|
6
6
|
class Completions < Base
|
7
|
-
|
8
|
-
|
7
|
+
##
|
8
|
+
# Creates a completion request with the specified parameters.
|
9
|
+
#
|
10
|
+
# @param [String] model the model to use for the completion
|
11
|
+
# @param [Hash] parameters the set of parameters being passed to the API
|
12
|
+
##
|
13
|
+
def create(model:, parameters: {})
|
14
|
+
raise MissingRequiredParameterError.new(:model) unless model
|
15
|
+
raise StreamingResponseNotSupportedError if parameters[:stream]
|
9
16
|
|
10
|
-
|
17
|
+
rest_create_w_json_params(resource: "completions",
|
18
|
+
parameters: parameters.merge({ model: model }))
|
11
19
|
end
|
12
20
|
end
|
13
21
|
end
|
data/lib/asimov/api_v1/edits.rb
CHANGED
@@ -4,11 +4,18 @@ module Asimov
|
|
4
4
|
# Class interface for API methods in the "/edits" URI subspace.
|
5
5
|
##
|
6
6
|
class Edits < Base
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
##
|
8
|
+
# Creates an edit resource with the specified parameters.
|
9
|
+
#
|
10
|
+
# @param [Hash] parameters the set of parameters being passed to the API
|
11
|
+
##
|
12
|
+
def create(model:, instruction:, parameters: {})
|
13
|
+
raise MissingRequiredParameterError.new(:model) unless model
|
14
|
+
raise MissingRequiredParameterError.new(:instruction) unless instruction
|
10
15
|
|
11
|
-
|
16
|
+
rest_create_w_json_params(resource: "edits",
|
17
|
+
parameters: parameters.merge({ model: model,
|
18
|
+
instruction: instruction }))
|
12
19
|
end
|
13
20
|
end
|
14
21
|
end
|
@@ -4,11 +4,22 @@ module Asimov
|
|
4
4
|
# Class interface for API methods in the "/embeddings" URI subspace.
|
5
5
|
##
|
6
6
|
class Embeddings < Base
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
##
|
8
|
+
# Creates an embedding resource with the specified parameters.
|
9
|
+
#
|
10
|
+
# @param [String] model the id for the model used to create the embedding
|
11
|
+
# @param [String] parameters the (optional) additional parameters being
|
12
|
+
# provided to inform embedding creation.
|
13
|
+
# @param [Hash] parameters the set of parameters being passed to the API
|
14
|
+
##
|
15
|
+
def create(model:, input:, parameters: {})
|
16
|
+
raise MissingRequiredParameterError.new(:model) unless model
|
17
|
+
raise MissingRequiredParameterError.new(:input) unless input
|
10
18
|
|
11
|
-
|
19
|
+
rest_create_w_json_params(resource: "embeddings",
|
20
|
+
parameters: parameters.merge({
|
21
|
+
model: model, input: input
|
22
|
+
}))
|
12
23
|
end
|
13
24
|
end
|
14
25
|
end
|
data/lib/asimov/api_v1/files.rb
CHANGED
@@ -10,37 +10,62 @@ module Asimov
|
|
10
10
|
# Class interface for API methods in the "/files" URI subspace.
|
11
11
|
##
|
12
12
|
class Files < Base
|
13
|
-
|
13
|
+
RESOURCE = "files".freeze
|
14
|
+
private_constant :RESOURCE
|
14
15
|
|
15
16
|
##
|
16
17
|
# Lists files that have been uploaded to OpenAI
|
17
18
|
##
|
18
19
|
def list
|
19
|
-
|
20
|
+
rest_index(resource: RESOURCE)
|
20
21
|
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
##
|
24
|
+
# Uploads a file to the /files endpoint with the specified parameters.
|
25
|
+
#
|
26
|
+
# @param [String] file file name or a File-like object to be uploaded
|
27
|
+
# @param [Hash] parameters the set of parameters being passed to the API
|
28
|
+
##
|
29
|
+
def upload(file:, purpose:, parameters: {})
|
30
|
+
raise MissingRequiredParameterError.new(:file) unless file
|
31
|
+
raise MissingRequiredParameterError.new(:purpose) unless purpose
|
25
32
|
|
26
|
-
validate(
|
33
|
+
validate(file, purpose)
|
27
34
|
|
28
|
-
|
29
|
-
|
30
|
-
parameters: parameters.merge(file: Utils::FileManager.open(
|
35
|
+
rest_create_w_multipart_params(
|
36
|
+
resource: RESOURCE,
|
37
|
+
parameters: parameters.merge(file: Utils::FileManager.open(file), purpose: purpose)
|
31
38
|
)
|
32
39
|
end
|
33
40
|
|
41
|
+
##
|
42
|
+
# Retrieves the file with the specified file_id from OpenAI.
|
43
|
+
#
|
44
|
+
# @param [String] file_id the id of the file to be retrieved
|
45
|
+
##
|
34
46
|
def retrieve(file_id:)
|
35
|
-
|
47
|
+
rest_get(resource: RESOURCE, id: file_id)
|
36
48
|
end
|
37
49
|
|
50
|
+
##
|
51
|
+
# Deletes the file with the specified file_id from OpenAI.
|
52
|
+
#
|
53
|
+
# @param [String] file_id the id of the file to be deleted
|
54
|
+
##
|
38
55
|
def delete(file_id:)
|
39
|
-
|
56
|
+
rest_delete(resource: RESOURCE, id: file_id)
|
40
57
|
end
|
41
58
|
|
59
|
+
##
|
60
|
+
# Retrieves the contents of the file with the specified file_id from OpenAI
|
61
|
+
# and passes those contents to the writer in a chunked manner.
|
62
|
+
#
|
63
|
+
# @param [String] file_id the id of the file to be retrieved
|
64
|
+
# @param [Writer] writer the Writer that will process the chunked content
|
65
|
+
# as it is received from the API
|
66
|
+
##
|
42
67
|
def content(file_id:, writer:)
|
43
|
-
|
68
|
+
rest_get_streamed_download(resource: [RESOURCE, file_id, "content"], writer: writer)
|
44
69
|
end
|
45
70
|
|
46
71
|
private
|