ruby-openai 2.2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b9c4dc42598004908a840d7f014259502f876df499c2bcfc93170e4fc44f729
4
- data.tar.gz: 4149a872b11219223412e9a1c8ce0e8f4c25706b75e30acb8c41ae80144cfd83
3
+ metadata.gz: 50fc900251b1e7c0c1a9f1f18c779871cba3691c9e3cd32a022286f6ee5978f2
4
+ data.tar.gz: '015428f6b586a0c504f839c6b38eb6842bd7c964805049ec8af706bf18fe8307'
5
5
  SHA512:
6
- metadata.gz: b2dbfdb269b5cee46758307b32ad149ed198a994aa79274d1790e12c13c5505e6b7a126647725d8169204bea69b16b82f37e42a69903e20af9d3f5f5b07f5b51
7
- data.tar.gz: 5c75f720b3f329f4ef4d37ed7b768c7edc46b97de843687543e382b49db98fc0df730c3dbab068f05862e765be59a75c7efadc405f9864554db84246efe61ece
6
+ metadata.gz: ab310b17e7282ba6885b468373b10d27f33ef50effff6b61d07d5d677e2b4153346ca355c66803521dc55bed87c8799a1da1513dd04ff8e4d36a53ec382b9d77
7
+ data.tar.gz: f29975d47364d66ca01ffebdf6adfeb2b660b808ec4cf704f35aaa775be73bfb0aff09673a3b0ee540dd8c35e70c2af24e909ba336c8cdbe22a2f9c6bd868781
data/.rubocop.yml CHANGED
@@ -12,9 +12,7 @@ Layout/LineLength:
12
12
 
13
13
  Metrics/BlockLength:
14
14
  Exclude:
15
- - "Rakefile"
16
- - "**/*.rake"
17
- - "spec/**/*.rb"
15
+ - "spec/**/*"
18
16
 
19
17
  Style/StringLiterals:
20
18
  EnforcedStyle: double_quotes
data/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ 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
+ ## [3.0.0] - 2022-12-26
9
+
10
+ ### Added
11
+
12
+ - Add ability to set access_token via gem configuration.
13
+ - Thanks [@grjones](https://github.com/grjones) and [@aquaflamingo](https://github.com/aquaflamingo) for raising this and [@feministy](https://github.com/feministy) for the [excellent guide](https://github.com/feministy/lizabinante.com/blob/stable/source/2016-01-30-creating-a-configurable-ruby-gem.markdown#configuration-block-the-end-goal) to adding config to a gem.
14
+
15
+ ### Removed
16
+
17
+ - [BREAKING] Remove ability to include access_token directly via ENV vars.
18
+ - [BREAKING] Remove deprecated answers, classifications, embeddings, engines and search endpoints.
19
+ - [BREAKING] Remove ability to pass engine to completions and embeddings outside of the parameters hash.
20
+ - [BREAKING] Remove ability to pass API version directly to endpoints.
21
+
22
+ ## [2.3.0] - 2022-12-23
23
+
24
+ ### Added
25
+
26
+ - Add Images#edit and Images#variations endpoint to modify images with DALL·E.
27
+
8
28
  ## [2.2.0] - 2022-12-15
9
29
 
10
30
  ### Added
data/Gemfile CHANGED
@@ -4,8 +4,9 @@ source "https://rubygems.org"
4
4
  gemspec
5
5
 
6
6
  gem "byebug", "~> 11.1.3"
7
+ gem "dotenv", "~> 2.8.1"
7
8
  gem "rake", "~> 13.0"
8
9
  gem "rspec", "~> 3.12"
9
- gem "rubocop", "~> 1.40.0"
10
+ gem "rubocop", "~> 1.41.1"
10
11
  gem "vcr", "~> 6.1.0"
11
12
  gem "webmock", "~> 3.18.1"
data/Gemfile.lock CHANGED
@@ -1,8 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-openai (2.2.0)
5
- dotenv (>= 2.7.6, < 2.9.0)
4
+ ruby-openai (3.0.0)
6
5
  httparty (>= 0.18.1, < 0.21.0)
7
6
 
8
7
  GEM
@@ -46,7 +45,7 @@ GEM
46
45
  diff-lcs (>= 1.2.0, < 2.0)
47
46
  rspec-support (~> 3.12.0)
48
47
  rspec-support (3.12.0)
49
- rubocop (1.40.0)
48
+ rubocop (1.41.1)
50
49
  json (~> 2.3)
51
50
  parallel (~> 1.10)
52
51
  parser (>= 3.1.2.1)
@@ -71,9 +70,10 @@ PLATFORMS
71
70
 
72
71
  DEPENDENCIES
73
72
  byebug (~> 11.1.3)
73
+ dotenv (~> 2.8.1)
74
74
  rake (~> 13.0)
75
75
  rspec (~> 3.12)
76
- rubocop (~> 1.40.0)
76
+ rubocop (~> 1.41.1)
77
77
  ruby-openai!
78
78
  vcr (~> 6.1.0)
79
79
  webmock (~> 3.18.1)
data/README.md CHANGED
@@ -5,7 +5,9 @@
5
5
  [![CircleCI Build Status](https://circleci.com/gh/alexrudall/ruby-openai.svg?style=shield)](https://circleci.com/gh/alexrudall/ruby-openai)
6
6
  [![Maintainability](https://api.codeclimate.com/v1/badges/a99a88d28ad37a79dbf6/maintainability)](https://codeclimate.com/github/codeclimate/codeclimate/maintainability)
7
7
 
8
- Use the [OpenAI GPT-3 API](https://openai.com/blog/openai-api/) with Ruby! 🤖❤️
8
+ Use the [OpenAI API](https://openai.com/blog/openai-api/) with Ruby! 🤖❤️
9
+
10
+ Generate text with GPT-3, create images with DALL·E, or write code with Codex...
9
11
 
10
12
  ## Installation
11
13
 
@@ -38,30 +40,29 @@ and require with:
38
40
  - Get your API key from [https://beta.openai.com/account/api-keys](https://beta.openai.com/account/api-keys)
39
41
  - If you belong to multiple organizations, you can get your Organization ID from [https://beta.openai.com/account/org-settings](https://beta.openai.com/account/org-settings)
40
42
 
41
- ### With dotenv
43
+ ### Quickstart
42
44
 
43
- If you're using [dotenv](https://github.com/motdotla/dotenv), you can add your secret keys to your .env file:
45
+ For a quick test you can pass your token directly to a new client:
44
46
 
45
- ```
46
- OPENAI_ACCESS_TOKEN=access_token_goes_here
47
- OPENAI_ORGANIZATION_ID=organization_id_goes_here # Optional.
47
+ ```ruby
48
+ client = OpenAI::Client.new(access_token: "access_token_goes_here")
48
49
  ```
49
50
 
50
- And create a client:
51
+ ### With Config
52
+
53
+ For a more robust setup, you can configure the gem with your API keys, for example in an `openai.rb` initializer file. Never hardcode secrets into your codebase - instead use something like [dotenv](https://github.com/motdotla/dotenv) to pass the keys safely into your environments.
51
54
 
52
55
  ```ruby
53
- client = OpenAI::Client.new
56
+ Ruby::OpenAI.configure do |config|
57
+ config.access_token = ENV.fetch('OPENAI_ACCESS_TOKEN')
58
+ config.organization_id = ENV.fetch('OPENAI_ORGANIZATION_ID') # Optional.
59
+ end
54
60
  ```
55
61
 
56
- ### Without dotenv
57
-
58
- Alternatively you can pass your key directly to a new client:
62
+ Then you can create a client like this:
59
63
 
60
64
  ```ruby
61
- client = OpenAI::Client.new(
62
- access_token: "access_token_goes_here",
63
- organization_id: "organization_id_goes_here"
64
- )
65
+ client = OpenAI::Client.new
65
66
  ```
66
67
 
67
68
  ### Models
@@ -199,7 +200,7 @@ This fine-tuned model name can then be used in classifications:
199
200
  JSON.parse(response.body)["choices"].map { |c| c["text"] }
200
201
  ```
201
202
 
202
- ### Images
203
+ ### Image Generation
203
204
 
204
205
  Generate an image using DALL·E!
205
206
 
@@ -209,7 +210,32 @@ Generate an image using DALL·E!
209
210
  => "https://oaidalleapiprodscus.blob.core.windows.net/private/org-Rf437IxKhh..."
210
211
  ```
211
212
 
212
- ![Otter Chef](https://oaidalleapiprodscus.blob.core.windows.net/private/org-Rf437IxKhhQPMiIQ0Es8OwrH/user-jxM65ijkZc1qRfHC0IJ8mOIc/img-UrDvFC4tDnuhTieF7TrTJ2gq.png?st=2022-11-13T15%3A55%3A34Z&se=2022-11-13T17%3A55%3A34Z&sp=r&sv=2021-08-06&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2022-11-13T01%3A32%3A30Z&ske=2022-11-14T01%3A32%3A30Z&sks=b&skv=2021-08-06&sig=tLdggckHl20CnnpCleoeiAEQjy4zMjuZJiUdovmkoF0%3D)
213
+ ![Ruby](https://i.ibb.co/6y4HJFx/img-d-Tx-Rf-RHj-SO5-Gho-Cbd8o-LJvw3.png)
214
+
215
+ ### Image Edit
216
+
217
+ Fill in the transparent part of an image, or upload a mask with transparent sections to indicate the parts of an image that can be changed according to your prompt...
218
+
219
+ ```ruby
220
+ response = client.images.edit(parameters: { prompt: "A solid red Ruby on a blue background", image: "image.png", mask: "mask.png" })
221
+ puts response.dig("data", 0, "url")
222
+ => "https://oaidalleapiprodscus.blob.core.windows.net/private/org-Rf437IxKhh..."
223
+ ```
224
+
225
+ ![Ruby](https://i.ibb.co/sWVh3BX/dalle-ruby.png)
226
+
227
+ ### Image Variations
228
+
229
+ Create n variations of an image.
230
+
231
+ ```ruby
232
+ response = client.images.variations(parameters: { image: "image.png", n: 2 })
233
+ puts response.dig("data", 0, "url")
234
+ => "https://oaidalleapiprodscus.blob.core.windows.net/private/org-Rf437IxKhh..."
235
+ ```
236
+
237
+ ![Ruby](https://i.ibb.co/TWJLP2y/img-miu-Wk-Nl0-QNy-Xtj-Lerc3c0l-NW.png)
238
+ ![Ruby](https://i.ibb.co/ScBhDGB/img-a9-Be-Rz-Au-Xwd-AV0-ERLUTSTGdi.png)
213
239
 
214
240
  ### Moderations
215
241
 
@@ -1,80 +1,111 @@
1
1
  module OpenAI
2
2
  class Client
3
- include HTTParty
4
- base_uri "https://api.openai.com"
3
+ URI_BASE = "https://api.openai.com/".freeze
5
4
 
6
5
  def initialize(access_token: nil, organization_id: nil)
7
- @access_token = access_token || ENV.fetch("OPENAI_ACCESS_TOKEN")
8
- @organization_id = organization_id || ENV.fetch("OPENAI_ORGANIZATION_ID", nil)
6
+ Ruby::OpenAI.configuration.access_token = access_token if access_token
7
+ Ruby::OpenAI.configuration.organization_id = organization_id if organization_id
9
8
  end
10
9
 
11
- def answers(version: default_version, parameters: {})
10
+ def answers(parameters: {})
12
11
  warn "[DEPRECATION WARNING] [ruby-openai] `Client#answers` is deprecated and will
13
12
  be removed from the OpenAI API on 3 December 2022 and from ruby-openai v3.0.
14
13
  More information: https://help.openai.com/en/articles/6233728-answers-transition-guide"
15
14
 
16
- post(url: "/#{version}/answers", parameters: parameters)
15
+ OpenAI::Client.post(path: "/answers", parameters: parameters)
17
16
  end
18
17
 
19
- def classifications(version: default_version, parameters: {})
18
+ def classifications(parameters: {})
20
19
  warn "[DEPRECATION WARNING] [ruby-openai] `Client#classifications` is deprecated and will
21
20
  be removed from the OpenAI API on 3 December 2022 and from ruby-openai v3.0.
22
21
  More information: https://help.openai.com/en/articles/6272941-classifications-transition-guide"
23
22
 
24
- post(url: "/#{version}/classifications", parameters: parameters)
23
+ OpenAI::Client.post(path: "/classifications", parameters: parameters)
25
24
  end
26
25
 
27
- def completions(engine: nil, version: default_version, parameters: {})
26
+ def completions(engine: nil, parameters: {})
28
27
  parameters = deprecate_engine(engine: engine, method: "completions", parameters: parameters)
29
28
 
30
- post(url: "/#{version}/completions", parameters: parameters)
29
+ OpenAI::Client.post(path: "/completions", parameters: parameters)
31
30
  end
32
31
 
33
- def edits(version: default_version, parameters: {})
34
- post(url: "/#{version}/edits", parameters: parameters)
32
+ def edits(parameters: {})
33
+ OpenAI::Client.post(path: "/edits", parameters: parameters)
35
34
  end
36
35
 
37
- def embeddings(engine: nil, version: default_version, parameters: {})
36
+ def embeddings(engine: nil, parameters: {})
38
37
  parameters = deprecate_engine(engine: engine, method: "embeddings", parameters: parameters)
39
38
 
40
- post(url: "/#{version}/embeddings", parameters: parameters)
39
+ OpenAI::Client.post(path: "/embeddings", parameters: parameters)
41
40
  end
42
41
 
43
42
  def engines
44
43
  warn "[DEPRECATION WARNING] [ruby-openai] `Client#engines` is deprecated and will
45
44
  be removed from ruby-openai v3.0. Use `Client#models` instead."
46
45
 
47
- @engines ||= OpenAI::Engines.new(access_token: @access_token,
48
- organization_id: @organization_id)
46
+ @engines ||= OpenAI::Engines.new
49
47
  end
50
48
 
51
49
  def files
52
- @files ||= OpenAI::Files.new(access_token: @access_token, organization_id: @organization_id)
50
+ @files ||= OpenAI::Files.new
53
51
  end
54
52
 
55
53
  def finetunes
56
- @finetunes ||= OpenAI::Finetunes.new(access_token: @access_token,
57
- organization_id: @organization_id)
54
+ @finetunes ||= OpenAI::Finetunes.new
58
55
  end
59
56
 
60
57
  def images
61
- @images ||= OpenAI::Images.new(access_token: @access_token, organization_id: @organization_id)
58
+ @images ||= OpenAI::Images.new
62
59
  end
63
60
 
64
61
  def models
65
- @models ||= OpenAI::Models.new(access_token: @access_token, organization_id: @organization_id)
62
+ @models ||= OpenAI::Models.new
66
63
  end
67
64
 
68
- def moderations(version: default_version, parameters: {})
69
- post(url: "/#{version}/moderations", parameters: parameters)
65
+ def moderations(parameters: {})
66
+ OpenAI::Client.post(path: "/moderations", parameters: parameters)
70
67
  end
71
68
 
72
- def search(engine:, version: default_version, parameters: {})
69
+ def search(engine:, parameters: {})
73
70
  warn "[DEPRECATION WARNING] [ruby-openai] `Client#search` is deprecated and will
74
71
  be removed from the OpenAI API on 3 December 2022 and from ruby-openai v3.0.
75
72
  More information: https://help.openai.com/en/articles/6272952-search-transition-guide"
76
73
 
77
- post(url: "/#{version}/engines/#{engine}/search", parameters: parameters)
74
+ OpenAI::Client.post(path: "/engines/#{engine}/search", parameters: parameters)
75
+ end
76
+
77
+ def self.get(path:)
78
+ HTTParty.get(
79
+ uri(path: path),
80
+ headers: headers
81
+ )
82
+ end
83
+
84
+ def self.post(path:, parameters: nil)
85
+ HTTParty.post(
86
+ uri(path: path),
87
+ headers: headers,
88
+ body: parameters.to_json
89
+ )
90
+ end
91
+
92
+ def self.delete(path:)
93
+ HTTParty.delete(
94
+ uri(path: path),
95
+ headers: headers
96
+ )
97
+ end
98
+
99
+ private_class_method def self.uri(path:)
100
+ URI_BASE + Ruby::OpenAI.configuration.api_version + path
101
+ end
102
+
103
+ private_class_method def self.headers
104
+ {
105
+ "Content-Type" => "application/json",
106
+ "Authorization" => "Bearer #{Ruby::OpenAI.configuration.access_token}",
107
+ "OpenAI-Organization" => Ruby::OpenAI.configuration.organization_id
108
+ }
78
109
  end
79
110
 
80
111
  private
@@ -91,24 +122,8 @@ module OpenAI
91
122
  parameters
92
123
  end
93
124
 
94
- def default_version
95
- "v1".freeze
96
- end
97
-
98
125
  def documents_or_file(documents: nil, file: nil)
99
126
  documents ? { documents: documents } : { file: file }
100
127
  end
101
-
102
- def post(url:, parameters:)
103
- self.class.post(
104
- url,
105
- headers: {
106
- "Content-Type" => "application/json",
107
- "Authorization" => "Bearer #{@access_token}",
108
- "OpenAI-Organization" => @organization_id
109
- },
110
- body: parameters.to_json
111
- )
112
- end
113
128
  end
114
129
  end
@@ -1,39 +1,16 @@
1
1
  module OpenAI
2
2
  class Engines
3
- include HTTParty
4
- base_uri "https://api.openai.com"
5
-
6
3
  def initialize(access_token: nil, organization_id: nil)
7
- @access_token = access_token || ENV.fetch("OPENAI_ACCESS_TOKEN")
8
- @organization_id = organization_id || ENV.fetch("OPENAI_ORGANIZATION_ID", nil)
9
- end
10
-
11
- def list(version: default_version)
12
- self.class.get(
13
- "/#{version}/engines",
14
- headers: {
15
- "Content-Type" => "application/json",
16
- "Authorization" => "Bearer #{@access_token}",
17
- "OpenAI-Organization" => @organization_id
18
- }
19
- )
4
+ Ruby::OpenAI.configuration.access_token = access_token if access_token
5
+ Ruby::OpenAI.configuration.organization_id = organization_id if organization_id
20
6
  end
21
7
 
22
- def retrieve(id:, version: default_version)
23
- self.class.get(
24
- "/#{version}/engines/#{id}",
25
- headers: {
26
- "Content-Type" => "application/json",
27
- "Authorization" => "Bearer #{@access_token}",
28
- "OpenAI-Organization" => @organization_id
29
- }
30
- )
8
+ def list
9
+ OpenAI::Client.get(path: "/engines")
31
10
  end
32
11
 
33
- private
34
-
35
- def default_version
36
- "v1".freeze
12
+ def retrieve(id:)
13
+ OpenAI::Client.get(path: "/engines/#{id}")
37
14
  end
38
15
  end
39
16
  end
@@ -1,66 +1,33 @@
1
1
  module OpenAI
2
2
  class Files
3
- include HTTParty
4
- base_uri "https://api.openai.com"
5
-
6
3
  def initialize(access_token: nil, organization_id: nil)
7
- @access_token = access_token || ENV.fetch("OPENAI_ACCESS_TOKEN")
8
- @organization_id = organization_id || ENV.fetch("OPENAI_ORGANIZATION_ID", nil)
4
+ Ruby::OpenAI.configuration.access_token = access_token if access_token
5
+ Ruby::OpenAI.configuration.organization_id = organization_id if organization_id
9
6
  end
10
7
 
11
- def list(version: default_version)
12
- self.class.get(
13
- "/#{version}/files",
14
- headers: {
15
- "Content-Type" => "application/json",
16
- "Authorization" => "Bearer #{@access_token}",
17
- "OpenAI-Organization" => @organization_id
18
- }
19
- )
8
+ def list
9
+ OpenAI::Client.get(path: "/files")
20
10
  end
21
11
 
22
- def upload(version: default_version, parameters: {})
12
+ def upload(parameters: {})
23
13
  validate(file: parameters[:file])
24
14
 
25
- self.class.post(
26
- "/#{version}/files",
27
- headers: {
28
- "Content-Type" => "application/json",
29
- "Authorization" => "Bearer #{@access_token}",
30
- "OpenAI-Organization" => @organization_id
31
- },
32
- body: parameters.merge(file: File.open(parameters[:file]))
15
+ OpenAI::Client.post(
16
+ path: "/files",
17
+ parameters: parameters.merge(file: File.open(parameters[:file]))
33
18
  )
34
19
  end
35
20
 
36
- def retrieve(id:, version: default_version)
37
- self.class.get(
38
- "/#{version}/files/#{id}",
39
- headers: {
40
- "Content-Type" => "application/json",
41
- "Authorization" => "Bearer #{@access_token}",
42
- "OpenAI-Organization" => @organization_id
43
- }
44
- )
21
+ def retrieve(id:)
22
+ OpenAI::Client.get(path: "/files/#{id}")
45
23
  end
46
24
 
47
- def delete(id:, version: default_version)
48
- self.class.delete(
49
- "/#{version}/files/#{id}",
50
- headers: {
51
- "Content-Type" => "application/json",
52
- "Authorization" => "Bearer #{@access_token}",
53
- "OpenAI-Organization" => @organization_id
54
- }
55
- )
25
+ def delete(id:)
26
+ OpenAI::Client.delete(path: "/files/#{id}")
56
27
  end
57
28
 
58
29
  private
59
30
 
60
- def default_version
61
- "v1".freeze
62
- end
63
-
64
31
  def validate(file:)
65
32
  File.open(file).each_line.with_index do |line, index|
66
33
  JSON.parse(line)
@@ -1,73 +1,28 @@
1
1
  module OpenAI
2
2
  class Finetunes
3
- include HTTParty
4
- base_uri "https://api.openai.com"
5
-
6
3
  def initialize(access_token: nil, organization_id: nil)
7
- @access_token = access_token || ENV.fetch("OPENAI_ACCESS_TOKEN")
8
- @organization_id = organization_id || ENV.fetch("OPENAI_ORGANIZATION_ID", nil)
9
- end
10
-
11
- def list(version: default_version)
12
- self.class.get(
13
- "/#{version}/fine-tunes",
14
- headers: {
15
- "Content-Type" => "application/json",
16
- "Authorization" => "Bearer #{@access_token}",
17
- "OpenAI-Organization" => @organization_id
18
- }
19
- )
4
+ Ruby::OpenAI.configuration.access_token = access_token if access_token
5
+ Ruby::OpenAI.configuration.organization_id = organization_id if organization_id
20
6
  end
21
7
 
22
- def create(version: default_version, parameters: {})
23
- self.class.post(
24
- "/#{version}/fine-tunes",
25
- headers: {
26
- "Content-Type" => "application/json",
27
- "Authorization" => "Bearer #{@access_token}",
28
- "OpenAI-Organization" => @organization_id
29
- },
30
- body: parameters.to_json
31
- )
8
+ def list
9
+ OpenAI::Client.get(path: "/fine-tunes")
32
10
  end
33
11
 
34
- def retrieve(id:, version: default_version)
35
- self.class.get(
36
- "/#{version}/fine-tunes/#{id}",
37
- headers: {
38
- "Content-Type" => "application/json",
39
- "Authorization" => "Bearer #{@access_token}",
40
- "OpenAI-Organization" => @organization_id
41
- }
42
- )
12
+ def create(parameters: {})
13
+ OpenAI::Client.post(path: "/fine-tunes", parameters: parameters.to_json)
43
14
  end
44
15
 
45
- def cancel(id:, version: default_version)
46
- self.class.post(
47
- "/#{version}/fine-tunes/#{id}/cancel",
48
- headers: {
49
- "Content-Type" => "application/json",
50
- "Authorization" => "Bearer #{@access_token}",
51
- "OpenAI-Organization" => @organization_id
52
- }
53
- )
16
+ def retrieve(id:)
17
+ OpenAI::Client.get(path: "/fine-tunes/#{id}")
54
18
  end
55
19
 
56
- def events(id:, version: default_version)
57
- self.class.get(
58
- "/#{version}/fine-tunes/#{id}/events",
59
- headers: {
60
- "Content-Type" => "application/json",
61
- "Authorization" => "Bearer #{@access_token}",
62
- "OpenAI-Organization" => @organization_id
63
- }
64
- )
20
+ def cancel(id:)
21
+ OpenAI::Client.post(path: "/fine-tunes/#{id}/cancel")
65
22
  end
66
23
 
67
- private
68
-
69
- def default_version
70
- "v1".freeze
24
+ def events(id:)
25
+ OpenAI::Client.get(path: "/fine-tunes/#{id}/events")
71
26
  end
72
27
  end
73
28
  end
@@ -1,29 +1,28 @@
1
1
  module OpenAI
2
2
  class Images
3
- include HTTParty
4
- base_uri "https://api.openai.com"
5
-
6
3
  def initialize(access_token: nil, organization_id: nil)
7
- @access_token = access_token || ENV.fetch("OPENAI_ACCESS_TOKEN")
8
- @organization_id = organization_id || ENV.fetch("OPENAI_ORGANIZATION_ID", nil)
4
+ Ruby::OpenAI.configuration.access_token = access_token if access_token
5
+ Ruby::OpenAI.configuration.organization_id = organization_id if organization_id
6
+ end
7
+
8
+ def generate(parameters: {})
9
+ OpenAI::Client.post(path: "/images/generations", parameters: parameters.to_json)
10
+ end
11
+
12
+ def edit(parameters: {})
13
+ OpenAI::Client.post(path: "/images/edits", parameters: open_files(parameters))
9
14
  end
10
15
 
11
- def generate(version: default_version, parameters: {})
12
- self.class.post(
13
- "/#{version}/images/generations",
14
- headers: {
15
- "Content-Type" => "application/json",
16
- "Authorization" => "Bearer #{@access_token}",
17
- "OpenAI-Organization" => @organization_id
18
- },
19
- body: parameters.to_json
20
- )
16
+ def variations(parameters: {})
17
+ OpenAI::Client.post(path: "/images/variations", parameters: open_files(parameters))
21
18
  end
22
19
 
23
20
  private
24
21
 
25
- def default_version
26
- "v1".freeze
22
+ def open_files(parameters)
23
+ parameters = parameters.merge(image: File.open(parameters[:image]))
24
+ parameters = parameters.merge(mask: File.open(parameters[:mask])) if parameters[:mask]
25
+ parameters
27
26
  end
28
27
  end
29
28
  end
@@ -1,39 +1,16 @@
1
1
  module OpenAI
2
2
  class Models
3
- include HTTParty
4
- base_uri "https://api.openai.com"
5
-
6
3
  def initialize(access_token: nil, organization_id: nil)
7
- @access_token = access_token || ENV.fetch("OPENAI_ACCESS_TOKEN")
8
- @organization_id = organization_id || ENV.fetch("OPENAI_ORGANIZATION_ID", nil)
9
- end
10
-
11
- def list(version: default_version)
12
- self.class.get(
13
- "/#{version}/models",
14
- headers: {
15
- "Content-Type" => "application/json",
16
- "Authorization" => "Bearer #{@access_token}",
17
- "OpenAI-Organization" => @organization_id
18
- }
19
- )
4
+ Ruby::OpenAI.configuration.access_token = access_token if access_token
5
+ Ruby::OpenAI.configuration.organization_id = organization_id if organization_id
20
6
  end
21
7
 
22
- def retrieve(id:, version: default_version)
23
- self.class.get(
24
- "/#{version}/models/#{id}",
25
- headers: {
26
- "Content-Type" => "application/json",
27
- "Authorization" => "Bearer #{@access_token}",
28
- "OpenAI-Organization" => @organization_id
29
- }
30
- )
8
+ def list
9
+ OpenAI::Client.get(path: "/models")
31
10
  end
32
11
 
33
- private
34
-
35
- def default_version
36
- "v1".freeze
12
+ def retrieve(id:)
13
+ OpenAI::Client.get(path: "/models/#{id}")
37
14
  end
38
15
  end
39
16
  end
@@ -1,5 +1,5 @@
1
1
  module Ruby
2
2
  module OpenAI
3
- VERSION = "2.2.0".freeze
3
+ VERSION = "3.0.0".freeze
4
4
  end
5
5
  end
data/lib/ruby/openai.rb CHANGED
@@ -1,15 +1,47 @@
1
1
  require "httparty"
2
+ require "ruby/openai/client"
2
3
  require "ruby/openai/engines"
3
4
  require "ruby/openai/files"
4
5
  require "ruby/openai/finetunes"
5
6
  require "ruby/openai/images"
6
7
  require "ruby/openai/models"
7
- require "ruby/openai/client"
8
8
  require "ruby/openai/version"
9
- require "dotenv/load"
10
9
 
11
10
  module Ruby
12
11
  module OpenAI
13
12
  class Error < StandardError; end
13
+ class ConfigurationError < Error; end
14
+
15
+ class Configuration
16
+ attr_writer :access_token
17
+ attr_accessor :api_version, :organization_id
18
+
19
+ DEFAULT_API_VERSION = "v1".freeze
20
+
21
+ def initialize
22
+ @access_token = nil
23
+ @api_version = DEFAULT_API_VERSION
24
+ @organization_id = nil
25
+ end
26
+
27
+ def access_token
28
+ return @access_token if @access_token
29
+
30
+ error_text = "OpenAI access token missing! See https://github.com/alexrudall/ruby-openai#usage"
31
+ raise ConfigurationError, error_text
32
+ end
33
+ end
34
+
35
+ class << self
36
+ attr_writer :configuration
37
+ end
38
+
39
+ def self.configuration
40
+ @configuration ||= OpenAI::Configuration.new
41
+ end
42
+
43
+ def self.configure
44
+ yield(configuration)
45
+ end
14
46
  end
15
47
  end
data/ruby-openai.gemspec CHANGED
@@ -25,6 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
26
  spec.require_paths = ["lib"]
27
27
 
28
- spec.add_dependency "dotenv", ">= 2.7.6", "< 2.9.0"
29
28
  spec.add_dependency "httparty", ">= 0.18.1", "< 0.21.0"
30
29
  end
metadata CHANGED
@@ -1,35 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-openai
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-15 00:00:00.000000000 Z
11
+ date: 2022-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: dotenv
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 2.7.6
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: 2.9.0
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: 2.7.6
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: 2.9.0
33
13
  - !ruby/object:Gem::Dependency
34
14
  name: httparty
35
15
  requirement: !ruby/object:Gem::Requirement