asimov 0.1.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/asimov.svg)](https://badge.fury.io/rb/asimov)
|
3
4
|
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/petergoldstein/asimov/blob/main/LICENSE.txt)
|
4
5
|
[![Tests](https://github.com/petergoldstein/asimov/actions/workflows/ci.yml/badge.svg)](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
|