ruby-openai 4.3.1 → 5.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: 53106fa1f3731bc10d9ab14609fe6db5eb1f439d100acdba6ddbc43b76abb015
4
- data.tar.gz: be123d1eccd0035d8b5c4cbd390f4392d66461230a5ec0ebbb0a0d1c3b0b0e8a
3
+ metadata.gz: 4f487bd64d6f7a7c4f0ccc3198d9bec592b199792763b6885b12b21f267ff80a
4
+ data.tar.gz: 566af61bb906edbb2315ee343aae62f82e6837aa318dfb505d67d73497fcb27a
5
5
  SHA512:
6
- metadata.gz: e5d6b68594c5ef30595f7921456795a3f05c0aac613126ea50612459a88c3dee15e91f30d07bb9bc2ce8bf6effda80969d4b9f90ed4927349a9b77d90659f550
7
- data.tar.gz: 3e96a3800503abd981999a5751b142c31566f8c34eb2e624d2858e165abc949413987b6046d6942a7f3fc7695af0dca1e70b874f08258a8e593def845daebed8
6
+ metadata.gz: 7e500ce6b1cff92bdb78b4cf455aac251cb229e43e52c4c863a98f44a351ac1ef4abd9692dde13f822f458b7373ee063e34dc0670b72dc67e02827533e316b13
7
+ data.tar.gz: 5d90d4ae80e14da163655a29d598c09834cfd0cad728bebe18f09b015aff7fa0881e736408360b228aa4921497de3f41b6122ee0a5e7a6011001b32403f70b41
data/.rubocop.yml CHANGED
@@ -12,6 +12,11 @@ Layout/LineLength:
12
12
  Exclude:
13
13
  - "**/*.gemspec"
14
14
 
15
+ Lint/AmbiguousOperator:
16
+ # https://github.com/rubocop/rubocop/issues/4294
17
+ Exclude:
18
+ - "lib/openai/client.rb"
19
+
15
20
  Metrics/AbcSize:
16
21
  Max: 20
17
22
 
data/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ 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
+ ## [5.0.0] - 2023-08-14
9
+
10
+ ### Added
11
+
12
+ - Support multi-tenant use of the gem! Each client now holds its own config, so you can create unlimited clients in the same project, for example to Azure and OpenAI, or for different headers, access keys, etc.
13
+ - [BREAKING-ish] This change should only break your usage of ruby-openai if you are directly calling class methods like `OpenAI::Client.get` for some reason, as they are now instance methods. Normal usage of the gem should be unaffected, just you can make new clients and they'll keep their own config if you want, overriding the global config.
14
+ - Huge thanks to [@petergoldstein](https://github.com/petergoldstein) for his original work on this, [@cthulhu](https://github.com/cthulhu) for testing and many others for reviews and suggestions.
15
+
16
+ ### Changed
17
+
18
+ - [BREAKING] Move audio related method to Audio model from Client model. You will need to update your code to handle this change, changing `client.translate` to `client.audio.translate` and `client.transcribe` to `client.audio.transcribe`.
19
+
20
+ ## [4.3.2] - 2023-08-14
21
+
22
+ ### Fixed
23
+
24
+ - Don't overwrite config extra-headers when making a client without different ones. Thanks to [@swistaczek](https://github.com/swistaczek) for raising this!
25
+ - Include extra-headers for Azure requests.
26
+
8
27
  ## [4.3.1] - 2023-08-13
9
28
 
10
29
  ### Fixed
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-openai (4.3.1)
4
+ ruby-openai (5.0.0)
5
5
  faraday (>= 1)
6
6
  faraday-multipart (>= 1)
7
7
 
data/README.md CHANGED
@@ -68,6 +68,12 @@ Then you can create a client like this:
68
68
  client = OpenAI::Client.new
69
69
  ```
70
70
 
71
+ You can still override the config defaults when making new clients; any options not included will fall back to any global config set with OpenAI.configure. e.g. in this example the organization_id, request_timeout, etc. will fallback to any set globally using OpenAI.configure, with only the access_token overridden:
72
+
73
+ ```ruby
74
+ client = OpenAI::Client.new(access_token: "access_token_goes_here")
75
+ ```
76
+
71
77
  #### Custom timeout or base URI
72
78
 
73
79
  The default timeout for any request using this library is 120 seconds. You can change that by passing a number of seconds to the `request_timeout` when initializing the client. You can also change the base URI used for all requests, eg. to use observability tools like [Helicone](https://docs.helicone.ai/quickstart/integrate-in-one-line-of-code), and add arbitrary other headers e.g. for [openai-caching-proxy-worker](https://github.com/6/openai-caching-proxy-worker):
@@ -411,7 +417,7 @@ Whisper is a speech to text model that can be used to generate text based on aud
411
417
  The translations API takes as input the audio file in any of the supported languages and transcribes the audio into English.
412
418
 
413
419
  ```ruby
414
- response = client.translate(
420
+ response = client.audio.translate(
415
421
  parameters: {
416
422
  model: "whisper-1",
417
423
  file: File.open("path_to_file", "rb"),
@@ -425,7 +431,7 @@ puts response["text"]
425
431
  The transcriptions API takes as input the audio file you want to transcribe and returns the text in the desired output file format.
426
432
 
427
433
  ```ruby
428
- response = client.transcribe(
434
+ response = client.audio.transcribe(
429
435
  parameters: {
430
436
  model: "whisper-1",
431
437
  file: File.open("path_to_file", "rb"),
@@ -0,0 +1,15 @@
1
+ module OpenAI
2
+ class Audio
3
+ def initialize(client:)
4
+ @client = client
5
+ end
6
+
7
+ def transcribe(parameters: {})
8
+ @client.multipart_post(path: "/audio/transcriptions", parameters: parameters)
9
+ end
10
+
11
+ def translate(parameters: {})
12
+ @client.multipart_post(path: "/audio/translations", parameters: parameters)
13
+ end
14
+ end
15
+ end
data/lib/openai/client.rb CHANGED
@@ -1,58 +1,68 @@
1
1
  module OpenAI
2
2
  class Client
3
- extend OpenAI::HTTP
3
+ include OpenAI::HTTP
4
4
 
5
- def initialize(access_token: nil, organization_id: nil, uri_base: nil, request_timeout: nil,
6
- extra_headers: {})
7
- OpenAI.configuration.access_token = access_token if access_token
8
- OpenAI.configuration.organization_id = organization_id if organization_id
9
- OpenAI.configuration.uri_base = uri_base if uri_base
10
- OpenAI.configuration.request_timeout = request_timeout if request_timeout
11
- OpenAI.configuration.extra_headers = extra_headers
5
+ CONFIG_KEYS = %i[
6
+ api_type
7
+ api_version
8
+ access_token
9
+ organization_id
10
+ uri_base
11
+ request_timeout
12
+ extra_headers
13
+ ].freeze
14
+ attr_reader *CONFIG_KEYS
15
+
16
+ def initialize(config = {})
17
+ CONFIG_KEYS.each do |key|
18
+ # Set instance variables like api_type & access_token. Fall back to global config
19
+ # if not present.
20
+ instance_variable_set("@#{key}", config[key] || OpenAI.configuration.send(key))
21
+ end
12
22
  end
13
23
 
14
24
  def chat(parameters: {})
15
- OpenAI::Client.json_post(path: "/chat/completions", parameters: parameters)
25
+ json_post(path: "/chat/completions", parameters: parameters)
16
26
  end
17
27
 
18
28
  def completions(parameters: {})
19
- OpenAI::Client.json_post(path: "/completions", parameters: parameters)
29
+ json_post(path: "/completions", parameters: parameters)
20
30
  end
21
31
 
22
32
  def edits(parameters: {})
23
- OpenAI::Client.json_post(path: "/edits", parameters: parameters)
33
+ json_post(path: "/edits", parameters: parameters)
24
34
  end
25
35
 
26
36
  def embeddings(parameters: {})
27
- OpenAI::Client.json_post(path: "/embeddings", parameters: parameters)
37
+ json_post(path: "/embeddings", parameters: parameters)
38
+ end
39
+
40
+ def audio
41
+ @audio ||= OpenAI::Audio.new(client: self)
28
42
  end
29
43
 
30
44
  def files
31
- @files ||= OpenAI::Files.new
45
+ @files ||= OpenAI::Files.new(client: self)
32
46
  end
33
47
 
34
48
  def finetunes
35
- @finetunes ||= OpenAI::Finetunes.new
49
+ @finetunes ||= OpenAI::Finetunes.new(client: self)
36
50
  end
37
51
 
38
52
  def images
39
- @images ||= OpenAI::Images.new
53
+ @images ||= OpenAI::Images.new(client: self)
40
54
  end
41
55
 
42
56
  def models
43
- @models ||= OpenAI::Models.new
57
+ @models ||= OpenAI::Models.new(client: self)
44
58
  end
45
59
 
46
60
  def moderations(parameters: {})
47
- OpenAI::Client.json_post(path: "/moderations", parameters: parameters)
48
- end
49
-
50
- def transcribe(parameters: {})
51
- OpenAI::Client.multipart_post(path: "/audio/transcriptions", parameters: parameters)
61
+ json_post(path: "/moderations", parameters: parameters)
52
62
  end
53
63
 
54
- def translate(parameters: {})
55
- OpenAI::Client.multipart_post(path: "/audio/translations", parameters: parameters)
64
+ def azure?
65
+ @api_type&.to_sym == :azure
56
66
  end
57
67
  end
58
68
  end
data/lib/openai/files.rb CHANGED
@@ -1,33 +1,32 @@
1
1
  module OpenAI
2
2
  class Files
3
- def initialize(access_token: nil, organization_id: nil)
4
- OpenAI.configuration.access_token = access_token if access_token
5
- OpenAI.configuration.organization_id = organization_id if organization_id
3
+ def initialize(client:)
4
+ @client = client
6
5
  end
7
6
 
8
7
  def list
9
- OpenAI::Client.get(path: "/files")
8
+ @client.get(path: "/files")
10
9
  end
11
10
 
12
11
  def upload(parameters: {})
13
12
  validate(file: parameters[:file])
14
13
 
15
- OpenAI::Client.multipart_post(
14
+ @client.multipart_post(
16
15
  path: "/files",
17
16
  parameters: parameters.merge(file: File.open(parameters[:file]))
18
17
  )
19
18
  end
20
19
 
21
20
  def retrieve(id:)
22
- OpenAI::Client.get(path: "/files/#{id}")
21
+ @client.get(path: "/files/#{id}")
23
22
  end
24
23
 
25
24
  def content(id:)
26
- OpenAI::Client.get(path: "/files/#{id}/content")
25
+ @client.get(path: "/files/#{id}/content")
27
26
  end
28
27
 
29
28
  def delete(id:)
30
- OpenAI::Client.delete(path: "/files/#{id}")
29
+ @client.delete(path: "/files/#{id}")
31
30
  end
32
31
 
33
32
  private
@@ -1,28 +1,27 @@
1
1
  module OpenAI
2
2
  class Finetunes
3
- def initialize(access_token: nil, organization_id: nil)
4
- OpenAI.configuration.access_token = access_token if access_token
5
- OpenAI.configuration.organization_id = organization_id if organization_id
3
+ def initialize(client:)
4
+ @client = client
6
5
  end
7
6
 
8
7
  def list
9
- OpenAI::Client.get(path: "/fine-tunes")
8
+ @client.get(path: "/fine-tunes")
10
9
  end
11
10
 
12
11
  def create(parameters: {})
13
- OpenAI::Client.json_post(path: "/fine-tunes", parameters: parameters)
12
+ @client.json_post(path: "/fine-tunes", parameters: parameters)
14
13
  end
15
14
 
16
15
  def retrieve(id:)
17
- OpenAI::Client.get(path: "/fine-tunes/#{id}")
16
+ @client.get(path: "/fine-tunes/#{id}")
18
17
  end
19
18
 
20
19
  def cancel(id:)
21
- OpenAI::Client.multipart_post(path: "/fine-tunes/#{id}/cancel")
20
+ @client.multipart_post(path: "/fine-tunes/#{id}/cancel")
22
21
  end
23
22
 
24
23
  def events(id:)
25
- OpenAI::Client.get(path: "/fine-tunes/#{id}/events")
24
+ @client.get(path: "/fine-tunes/#{id}/events")
26
25
  end
27
26
 
28
27
  def delete(fine_tuned_model:)
@@ -30,7 +29,7 @@ module OpenAI
30
29
  raise ArgumentError, "Please give a fine_tuned_model name, not a fine-tune ID"
31
30
  end
32
31
 
33
- OpenAI::Client.delete(path: "/models/#{fine_tuned_model}")
32
+ @client.delete(path: "/models/#{fine_tuned_model}")
34
33
  end
35
34
  end
36
35
  end
data/lib/openai/http.rb CHANGED
@@ -64,34 +64,40 @@ module OpenAI
64
64
 
65
65
  def conn(multipart: false)
66
66
  Faraday.new do |f|
67
- f.options[:timeout] = OpenAI.configuration.request_timeout
67
+ f.options[:timeout] = @request_timeout
68
68
  f.request(:multipart) if multipart
69
69
  end
70
70
  end
71
71
 
72
72
  def uri(path:)
73
- if OpenAI.configuration.api_type == :azure
74
- base = File.join(OpenAI.configuration.uri_base, path)
75
- "#{base}?api-version=#{OpenAI.configuration.api_version}"
73
+ if azure?
74
+ base = File.join(@uri_base, path)
75
+ "#{base}?api-version=#{@api_version}"
76
76
  else
77
- File.join(OpenAI.configuration.uri_base, OpenAI.configuration.api_version, path)
77
+ File.join(@uri_base, @api_version, path)
78
78
  end
79
79
  end
80
80
 
81
81
  def headers
82
- return azure_headers if OpenAI.configuration.api_type == :azure
82
+ if azure?
83
+ azure_headers
84
+ else
85
+ openai_headers
86
+ end.merge(@extra_headers || {})
87
+ end
83
88
 
89
+ def openai_headers
84
90
  {
85
91
  "Content-Type" => "application/json",
86
- "Authorization" => "Bearer #{OpenAI.configuration.access_token}",
87
- "OpenAI-Organization" => OpenAI.configuration.organization_id
88
- }.merge(OpenAI.configuration.extra_headers)
92
+ "Authorization" => "Bearer #{@access_token}",
93
+ "OpenAI-Organization" => @organization_id
94
+ }
89
95
  end
90
96
 
91
97
  def azure_headers
92
98
  {
93
99
  "Content-Type" => "application/json",
94
- "api-key" => OpenAI.configuration.access_token
100
+ "api-key" => @access_token
95
101
  }
96
102
  end
97
103
 
data/lib/openai/images.rb CHANGED
@@ -1,20 +1,19 @@
1
1
  module OpenAI
2
2
  class Images
3
- def initialize(access_token: nil, organization_id: nil)
4
- OpenAI.configuration.access_token = access_token if access_token
5
- OpenAI.configuration.organization_id = organization_id if organization_id
3
+ def initialize(client: nil)
4
+ @client = client
6
5
  end
7
6
 
8
7
  def generate(parameters: {})
9
- OpenAI::Client.json_post(path: "/images/generations", parameters: parameters)
8
+ @client.json_post(path: "/images/generations", parameters: parameters)
10
9
  end
11
10
 
12
11
  def edit(parameters: {})
13
- OpenAI::Client.multipart_post(path: "/images/edits", parameters: open_files(parameters))
12
+ @client.multipart_post(path: "/images/edits", parameters: open_files(parameters))
14
13
  end
15
14
 
16
15
  def variations(parameters: {})
17
- OpenAI::Client.multipart_post(path: "/images/variations", parameters: open_files(parameters))
16
+ @client.multipart_post(path: "/images/variations", parameters: open_files(parameters))
18
17
  end
19
18
 
20
19
  private
data/lib/openai/models.rb CHANGED
@@ -1,16 +1,15 @@
1
1
  module OpenAI
2
2
  class Models
3
- def initialize(access_token: nil, organization_id: nil)
4
- OpenAI.configuration.access_token = access_token if access_token
5
- OpenAI.configuration.organization_id = organization_id if organization_id
3
+ def initialize(client:)
4
+ @client = client
6
5
  end
7
6
 
8
7
  def list
9
- OpenAI::Client.get(path: "/models")
8
+ @client.get(path: "/models")
10
9
  end
11
10
 
12
11
  def retrieve(id:)
13
- OpenAI::Client.get(path: "/models/#{id}")
12
+ @client.get(path: "/models/#{id}")
14
13
  end
15
14
  end
16
15
  end
@@ -1,3 +1,3 @@
1
1
  module OpenAI
2
- VERSION = "4.3.1".freeze
2
+ VERSION = "5.0.0".freeze
3
3
  end
data/lib/openai.rb CHANGED
@@ -7,6 +7,7 @@ require_relative "openai/files"
7
7
  require_relative "openai/finetunes"
8
8
  require_relative "openai/images"
9
9
  require_relative "openai/models"
10
+ require_relative "openai/audio"
10
11
  require_relative "openai/version"
11
12
 
12
13
  module OpenAI
@@ -29,6 +30,7 @@ module OpenAI
29
30
  @organization_id = nil
30
31
  @uri_base = DEFAULT_URI_BASE
31
32
  @request_timeout = DEFAULT_REQUEST_TIMEOUT
33
+ @extra_headers = nil
32
34
  end
33
35
 
34
36
  def access_token
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: 4.3.1
4
+ version: 5.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: 2023-08-13 00:00:00.000000000 Z
11
+ date: 2023-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -63,6 +63,7 @@ files:
63
63
  - bin/console
64
64
  - bin/setup
65
65
  - lib/openai.rb
66
+ - lib/openai/audio.rb
66
67
  - lib/openai/client.rb
67
68
  - lib/openai/compatibility.rb
68
69
  - lib/openai/files.rb