ruby-openai 1.3.1 → 1.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b8fe14b8e08b05f4743569e5c94f92552032802bf0eb4fd39fd8168ea6e1105
4
- data.tar.gz: 70933cec4140ffe73c52e0bc25e5afc2bb65d4348b1c8138f95acc8b7ccdb500
3
+ metadata.gz: 54bd37da173d97fdb58db219e057eec6997a3694c463d79cdc6e04fce86713fd
4
+ data.tar.gz: 051e5bd9b7dfe58040e0c1107c96da85f9a8c8d655987a7214dc3ce9f4663829
5
5
  SHA512:
6
- metadata.gz: 58572f5ea0262e16d9b6a916e5a1ae04e786804cdb04771e443ce8c6010b4d6ba2956e1cb5c82a03d743d83e8511f91e9510b6ac5b36ab257435c5cd02dc41f2
7
- data.tar.gz: b655c9054c7d89c8dfb8b1f364acc7e70a8aece46bdaf4c365a693d81dc97db83f8e6fe325e8e7eabdec12c079aca9c12e402ac909fe485e721cf8613d46f403
6
+ metadata.gz: bcf13d22abf4a3f46a82622d6f34be59cab1fd068d1fe29709da89fa434611b5a0ada0397cc3dc0e091406174b5ad8472c6647c553d2bdf6b8fcfe5d1d547c81
7
+ data.tar.gz: 6ed690d12edb3d1aac43894f6704365ed1535b124286fd6b048dbe8606499d28e474f89096bba8780b8eadbebc201918a6e6a754cc03d942221bb31dad4de8c0
data/CHANGELOG.md CHANGED
@@ -5,6 +5,15 @@ 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
+ ## [1.4.0] - 2021-12-11
9
+
10
+ ### Added
11
+
12
+ - Add Client#engines endpoints to list and query available engines.
13
+ - Add Client#finetunes endpoints to create and use fine-tuned models.
14
+ - Add Client#embeddings endpoint to get vector representations of inputs.
15
+ - Add tests and examples for more engines.
16
+
8
17
  ## [1.3.1] - 2021-07-14
9
18
 
10
19
  ### Changed
data/Gemfile CHANGED
@@ -6,6 +6,6 @@ gemspec
6
6
  gem "byebug", "~> 11.1.3"
7
7
  gem "rake", "~> 13.0"
8
8
  gem "rspec", "~> 3.10"
9
- gem "rubocop", "~> 1.18.3"
9
+ gem "rubocop", "~> 1.23.0"
10
10
  gem "vcr", "~> 6.0.0"
11
- gem "webmock", "~> 3.13.0"
11
+ gem "webmock", "~> 3.14.0"
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-openai (1.3.1)
4
+ ruby-openai (1.4.0)
5
5
  dotenv (~> 2.7.6)
6
- httparty (~> 0.18.1)
6
+ httparty (>= 0.18.1, < 0.21.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
@@ -17,15 +17,15 @@ GEM
17
17
  diff-lcs (1.4.4)
18
18
  dotenv (2.7.6)
19
19
  hashdiff (1.0.1)
20
- httparty (0.18.1)
20
+ httparty (0.20.0)
21
21
  mime-types (~> 3.0)
22
22
  multi_xml (>= 0.5.2)
23
23
  mime-types (3.3.1)
24
24
  mime-types-data (~> 3.2015)
25
- mime-types-data (3.2021.0704)
25
+ mime-types-data (3.2021.0901)
26
26
  multi_xml (0.6.0)
27
- parallel (1.20.1)
28
- parser (3.0.1.1)
27
+ parallel (1.21.0)
28
+ parser (3.0.2.0)
29
29
  ast (~> 2.4.1)
30
30
  public_suffix (4.0.6)
31
31
  rainbow (3.0.0)
@@ -45,22 +45,22 @@ GEM
45
45
  diff-lcs (>= 1.2.0, < 2.0)
46
46
  rspec-support (~> 3.10.0)
47
47
  rspec-support (3.10.1)
48
- rubocop (1.18.3)
48
+ rubocop (1.23.0)
49
49
  parallel (~> 1.10)
50
50
  parser (>= 3.0.0.0)
51
51
  rainbow (>= 2.2.2, < 4.0)
52
52
  regexp_parser (>= 1.8, < 3.0)
53
53
  rexml
54
- rubocop-ast (>= 1.7.0, < 2.0)
54
+ rubocop-ast (>= 1.12.0, < 2.0)
55
55
  ruby-progressbar (~> 1.7)
56
56
  unicode-display_width (>= 1.4.0, < 3.0)
57
- rubocop-ast (1.7.0)
57
+ rubocop-ast (1.13.0)
58
58
  parser (>= 3.0.1.1)
59
59
  ruby-progressbar (1.11.0)
60
- unicode-display_width (2.0.0)
60
+ unicode-display_width (2.1.0)
61
61
  vcr (6.0.0)
62
- webmock (3.13.0)
63
- addressable (>= 2.3.6)
62
+ webmock (3.14.0)
63
+ addressable (>= 2.8.0)
64
64
  crack (>= 0.3.2)
65
65
  hashdiff (>= 0.4.0, < 2.0.0)
66
66
 
@@ -71,10 +71,10 @@ DEPENDENCIES
71
71
  byebug (~> 11.1.3)
72
72
  rake (~> 13.0)
73
73
  rspec (~> 3.10)
74
- rubocop (~> 1.18.3)
74
+ rubocop (~> 1.23.0)
75
75
  ruby-openai!
76
76
  vcr (~> 6.0.0)
77
- webmock (~> 3.13.0)
77
+ webmock (~> 3.14.0)
78
78
 
79
79
  BUNDLED WITH
80
- 2.2.3
80
+ 2.2.20
data/README.md CHANGED
@@ -9,19 +9,29 @@ A simple Ruby wrapper for the [OpenAI GPT-3 API](https://openai.com/blog/openai-
9
9
 
10
10
  ## Installation
11
11
 
12
+ ### Bundler
13
+
12
14
  Add this line to your application's Gemfile:
13
15
 
14
16
  ```ruby
15
- gem 'ruby-openai'
17
+ gem 'ruby-openai'
16
18
  ```
17
19
 
18
20
  And then execute:
19
21
 
20
- $ bundle install
22
+ $ bundle install
23
+
24
+ ### Gem install
25
+
26
+ Or install with:
21
27
 
22
- Or install it yourself as:
28
+ $ gem install ruby-openai
23
29
 
24
- $ gem install ruby-openai
30
+ and require with:
31
+
32
+ ```ruby
33
+ require "ruby/openai"
34
+ ```
25
35
 
26
36
  ## Usage
27
37
 
@@ -37,7 +47,7 @@ If you're using [dotenv](https://github.com/motdotla/dotenv), you can add your s
37
47
 
38
48
  And create a client:
39
49
 
40
- ```
50
+ ```ruby
41
51
  client = OpenAI::Client.new
42
52
  ```
43
53
 
@@ -45,15 +55,42 @@ And create a client:
45
55
 
46
56
  Alternatively you can pass your key directly to a new client:
47
57
 
48
- ```
58
+ ```ruby
49
59
  client = OpenAI::Client.new(access_token: "access_token_goes_here")
50
60
  ```
51
61
 
52
- ### Completions
62
+ ### Engines
53
63
 
54
- The engine options are currently "ada", "babbage", "curie" and "davinci". Hit the OpenAI API for a completion:
64
+ There are different engines that can be used to generate text. For a full list and to retrieve information about a single engine:
55
65
 
66
+ ```ruby
67
+ client.engines.list
68
+ client.engines.retrieve(id: 'ada')
56
69
  ```
70
+
71
+ #### Examples
72
+
73
+ - [Base](https://beta.openai.com/docs/engines/base-series)
74
+ - ada
75
+ - babbage
76
+ - curie
77
+ - davinci
78
+ - [Instruct](https://beta.openai.com/docs/engines/instruct-series-beta)
79
+ - ada-instruct-beta
80
+ - babbage-instruct-beta
81
+ - curie-instruct-beta-v2
82
+ - davinci-instruct-beta-v3
83
+ - [Codex (private beta)](https://beta.openai.com/docs/engines/codex-series-private-beta)
84
+ - davinci-codex
85
+ - cushman-codex
86
+ - [Content Filter](https://beta.openai.com/docs/engines/content-filter)
87
+ - content-filter-alpha
88
+
89
+ ### Completions
90
+
91
+ Hit the OpenAI API for a completion:
92
+
93
+ ```ruby
57
94
  response = client.completions(engine: "davinci", parameters: { prompt: "Once upon a time", max_tokens: 5 })
58
95
  puts response.parsed_response['choices'].map{ |c| c["text"] }
59
96
  => [", there lived a great"]
@@ -63,14 +100,14 @@ The engine options are currently "ada", "babbage", "curie" and "davinci". Hit th
63
100
 
64
101
  Put your data in a `.jsonl` file like this:
65
102
 
66
- ```
103
+ ```json
67
104
  {"text": "puppy A is happy", "metadata": "emotional state of puppy A"}
68
105
  {"text": "puppy B is sad", "metadata": "emotional state of puppy B"}
69
106
  ```
70
107
 
71
108
  and pass the path to `client.files.upload` to upload it to OpenAI, and then interact with it:
72
109
 
73
- ```
110
+ ```ruby
74
111
  client.files.upload(parameters: { file: 'path/to/puppy.jsonl', purpose: 'search' })
75
112
  client.files.list
76
113
  client.files.retrieve(id: 123)
@@ -81,7 +118,7 @@ and pass the path to `client.files.upload` to upload it to OpenAI, and then inte
81
118
 
82
119
  Pass documents and a query string to get semantic search scores against each document:
83
120
 
84
- ```
121
+ ```ruby
85
122
  response = client.search(engine: "ada", parameters: { documents: %w[washington hospital school], query: "president" })
86
123
  puts response["data"].map { |d| d["score"] }
87
124
  => [202.0, 48.052, 19.247]
@@ -89,7 +126,7 @@ Pass documents and a query string to get semantic search scores against each doc
89
126
 
90
127
  You can alternatively search using the ID of a file you've uploaded:
91
128
 
92
- ```
129
+ ```ruby
93
130
  client.search(engine: "ada", parameters: { file: "abc123", query: "happy" })
94
131
  ```
95
132
 
@@ -97,7 +134,7 @@ You can alternatively search using the ID of a file you've uploaded:
97
134
 
98
135
  Pass documents, a question string, and an example question/response to get an answer to a question:
99
136
 
100
- ```
137
+ ```ruby
101
138
  response = client.answers(parameters: {
102
139
  documents: ["Puppy A is happy.", "Puppy B is sad."],
103
140
  question: "which puppy is happy?",
@@ -109,7 +146,7 @@ Pass documents, a question string, and an example question/response to get an an
109
146
 
110
147
  Or use the ID of a file you've uploaded:
111
148
 
112
- ```
149
+ ```ruby
113
150
  response = client.answers(parameters: {
114
151
  file: "123abc",
115
152
  question: "which puppy is happy?",
@@ -123,7 +160,7 @@ Or use the ID of a file you've uploaded:
123
160
 
124
161
  Pass examples and a query to predict the most likely labels:
125
162
 
126
- ```
163
+ ```ruby
127
164
  response = client.classifications(parameters: {
128
165
  examples: [
129
166
  ["A happy moment", "Positive"],
@@ -137,7 +174,7 @@ Pass examples and a query to predict the most likely labels:
137
174
 
138
175
  Or use the ID of a file you've uploaded:
139
176
 
140
- ```
177
+ ```ruby
141
178
  response = client.classifications(parameters: {
142
179
  file: "123abc,
143
180
  query: "It is a raining day :(",
@@ -145,6 +182,74 @@ Or use the ID of a file you've uploaded:
145
182
  })
146
183
  ```
147
184
 
185
+ ### Fine-tunes
186
+
187
+ Put your fine-tuning data in a `.jsonl` file like this:
188
+
189
+ ```json
190
+ {"prompt":"Overjoyed with my new phone! ->", "completion":" positive"}
191
+ {"prompt":"@lakers disappoint for a third straight night ->", "completion":" negative"}
192
+ ```
193
+
194
+ and pass the path to `client.files.upload` to upload it to OpenAI and get its ID:
195
+
196
+ ```ruby
197
+ response = client.files.upload(parameters: { file: 'path/to/sentiment.jsonl', purpose: 'fine-tune' })
198
+ file_id = JSON.parse(response.body)["id"]
199
+ ```
200
+
201
+ You can then use this file ID to create a fine-tune model:
202
+
203
+ ```ruby
204
+ response = client.finetunes.create(
205
+ parameters: {
206
+ training_file: file_id,
207
+ model: "ada"
208
+ })
209
+ fine_tune_id = JSON.parse(response.body)["id"]
210
+ ```
211
+
212
+ That will give you the fine-tune ID. If you made a mistake you can cancel the fine-tune model before it is processed:
213
+
214
+ ```ruby
215
+ client.finetunes.cancel(id: fine_tune_id)
216
+ ```
217
+
218
+ You may need to wait a short time for processing to complete. Once processed, you can use list or retrieve to get the name of the fine-tuned model:
219
+
220
+ ```ruby
221
+ client.finetunes.list
222
+ response = client.finetunes.retrieve(id: fine_tune_id)
223
+ fine_tuned_model = JSON.parse(response.body)["fine_tuned_model"]
224
+ ```
225
+
226
+ This fine-tuned model name can then be used in classifications:
227
+
228
+ ```ruby
229
+ response = client.completions(
230
+ parameters: {
231
+ model: fine_tuned_model,
232
+ prompt: "I love Mondays!"
233
+ }
234
+ )
235
+ JSON.parse(response.body)["choices"].map { |c| c["text"] }
236
+ ```
237
+
238
+ Do not pass the engine parameter when using a fine-tuned model.
239
+
240
+ ### Embeddings
241
+
242
+ You can use the embeddings endpoint to get a vector of numbers representing an input. You can then compare these vectors for different inputs to efficiently check how similar the inputs are.
243
+
244
+ ```ruby
245
+ client.embeddings(
246
+ engine: "babbage-similarity",
247
+ parameters: {
248
+ input: "The food was delicious and the waiter..."
249
+ }
250
+ )
251
+ ```
252
+
148
253
  ## Development
149
254
 
150
255
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -15,14 +15,30 @@ module OpenAI
15
15
  post(url: "/#{version}/classifications", parameters: parameters)
16
16
  end
17
17
 
18
- def completions(engine:, version: default_version, parameters: {})
19
- post(url: "/#{version}/engines/#{engine}/completions", parameters: parameters)
18
+ def completions(engine: nil, version: default_version, parameters: {})
19
+ if engine
20
+ post(url: "/#{version}/engines/#{engine}/completions", parameters: parameters)
21
+ else
22
+ post(url: "/#{version}/completions", parameters: parameters)
23
+ end
24
+ end
25
+
26
+ def embeddings(engine:, version: default_version, parameters: {})
27
+ post(url: "/#{version}/engines/#{engine}/embeddings", parameters: parameters)
28
+ end
29
+
30
+ def engines
31
+ @engines ||= OpenAI::Engines.new(access_token: @access_token)
20
32
  end
21
33
 
22
34
  def files
23
35
  @files ||= OpenAI::Files.new(access_token: @access_token)
24
36
  end
25
37
 
38
+ def finetunes
39
+ @finetunes ||= OpenAI::Finetunes.new(access_token: @access_token)
40
+ end
41
+
26
42
  # rubocop:disable Layout/LineLength
27
43
  # rubocop:disable Metrics/ParameterLists
28
44
  def search(engine:, query: nil, documents: nil, file: nil, version: default_version, parameters: {})
@@ -0,0 +1,36 @@
1
+ module OpenAI
2
+ class Engines
3
+ include HTTParty
4
+ base_uri "https://api.openai.com"
5
+
6
+ def initialize(access_token: nil)
7
+ @access_token = access_token || ENV["OPENAI_ACCESS_TOKEN"]
8
+ end
9
+
10
+ def list(version: default_version)
11
+ self.class.get(
12
+ "/#{version}/engines",
13
+ headers: {
14
+ "Content-Type" => "application/json",
15
+ "Authorization" => "Bearer #{@access_token}"
16
+ }
17
+ )
18
+ end
19
+
20
+ def retrieve(id:, version: default_version)
21
+ self.class.get(
22
+ "/#{version}/engines/#{id}",
23
+ headers: {
24
+ "Content-Type" => "application/json",
25
+ "Authorization" => "Bearer #{@access_token}"
26
+ }
27
+ )
28
+ end
29
+
30
+ private
31
+
32
+ def default_version
33
+ "v1".freeze
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,67 @@
1
+ module OpenAI
2
+ class Finetunes
3
+ include HTTParty
4
+ base_uri "https://api.openai.com"
5
+
6
+ def initialize(access_token: nil)
7
+ @access_token = access_token || ENV["OPENAI_ACCESS_TOKEN"]
8
+ end
9
+
10
+ def list(version: default_version)
11
+ self.class.get(
12
+ "/#{version}/fine-tunes",
13
+ headers: {
14
+ "Content-Type" => "application/json",
15
+ "Authorization" => "Bearer #{@access_token}"
16
+ }
17
+ )
18
+ end
19
+
20
+ def create(version: default_version, parameters: {})
21
+ self.class.post(
22
+ "/#{version}/fine-tunes",
23
+ headers: {
24
+ "Content-Type" => "application/json",
25
+ "Authorization" => "Bearer #{@access_token}"
26
+ },
27
+ body: parameters.to_json
28
+ )
29
+ end
30
+
31
+ def retrieve(id:, version: default_version)
32
+ self.class.get(
33
+ "/#{version}/fine-tunes/#{id}",
34
+ headers: {
35
+ "Content-Type" => "application/json",
36
+ "Authorization" => "Bearer #{@access_token}"
37
+ }
38
+ )
39
+ end
40
+
41
+ def cancel(id:, version: default_version)
42
+ self.class.post(
43
+ "/#{version}/fine-tunes/#{id}/cancel",
44
+ headers: {
45
+ "Content-Type" => "application/json",
46
+ "Authorization" => "Bearer #{@access_token}"
47
+ }
48
+ )
49
+ end
50
+
51
+ def events(id:, version: default_version)
52
+ self.class.get(
53
+ "/#{version}/fine-tunes/#{id}/events",
54
+ headers: {
55
+ "Content-Type" => "application/json",
56
+ "Authorization" => "Bearer #{@access_token}"
57
+ }
58
+ )
59
+ end
60
+
61
+ private
62
+
63
+ def default_version
64
+ "v1".freeze
65
+ end
66
+ end
67
+ end
@@ -1,5 +1,5 @@
1
1
  module Ruby
2
2
  module OpenAI
3
- VERSION = "1.3.1".freeze
3
+ VERSION = "1.4.0".freeze
4
4
  end
5
5
  end
data/lib/ruby/openai.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require "httparty"
2
+ require "ruby/openai/engines"
2
3
  require "ruby/openai/files"
4
+ require "ruby/openai/finetunes"
3
5
  require "ruby/openai/client"
4
6
  require "ruby/openai/version"
5
7
  require "dotenv/load"
data/ruby-openai.gemspec CHANGED
@@ -14,6 +14,7 @@ Gem::Specification.new do |spec|
14
14
  spec.metadata["homepage_uri"] = spec.homepage
15
15
  spec.metadata["source_code_uri"] = "https://github.com/alexrudall/ruby-openai"
16
16
  spec.metadata["changelog_uri"] = "https://github.com/alexrudall/ruby-openai/blob/main/CHANGELOG.md"
17
+ spec.metadata["rubygems_mfa_required"] = "true"
17
18
 
18
19
  # Specify which files should be added to the gem when it is released.
19
20
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -25,5 +26,5 @@ Gem::Specification.new do |spec|
25
26
  spec.require_paths = ["lib"]
26
27
 
27
28
  spec.add_dependency "dotenv", "~> 2.7.6"
28
- spec.add_dependency "httparty", "~> 0.18.1"
29
+ spec.add_dependency "httparty", ">= 0.18.1", "< 0.21.0"
29
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-openai
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-14 00:00:00.000000000 Z
11
+ date: 2021-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dotenv
@@ -28,16 +28,22 @@ dependencies:
28
28
  name: httparty
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 0.18.1
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: 0.21.0
34
37
  type: :runtime
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
38
- - - "~>"
41
+ - - ">="
39
42
  - !ruby/object:Gem::Version
40
43
  version: 0.18.1
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: 0.21.0
41
47
  description:
42
48
  email:
43
49
  - alexrudall@users.noreply.github.com
@@ -65,7 +71,9 @@ files:
65
71
  - bin/setup
66
72
  - lib/ruby/openai.rb
67
73
  - lib/ruby/openai/client.rb
74
+ - lib/ruby/openai/engines.rb
68
75
  - lib/ruby/openai/files.rb
76
+ - lib/ruby/openai/finetunes.rb
69
77
  - lib/ruby/openai/version.rb
70
78
  - pull_request_template.md
71
79
  - ruby-openai.gemspec
@@ -76,6 +84,7 @@ metadata:
76
84
  homepage_uri: https://github.com/alexrudall/ruby-openai
77
85
  source_code_uri: https://github.com/alexrudall/ruby-openai
78
86
  changelog_uri: https://github.com/alexrudall/ruby-openai/blob/main/CHANGELOG.md
87
+ rubygems_mfa_required: 'true'
79
88
  post_install_message:
80
89
  rdoc_options: []
81
90
  require_paths:
@@ -91,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
100
  - !ruby/object:Gem::Version
92
101
  version: '0'
93
102
  requirements: []
94
- rubygems_version: 3.2.3
103
+ rubygems_version: 3.1.6
95
104
  signing_key:
96
105
  specification_version: 4
97
106
  summary: A Ruby gem for the OpenAI GPT-3 API