ruby-openai 4.0.0 → 6.3.1
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/.devcontainer/Dockerfile +16 -0
- data/.devcontainer/devcontainer.json +36 -0
- data/.devcontainer/docker-compose.yml +19 -0
- data/.github/FUNDING.yml +13 -0
- data/.gitignore +65 -7
- data/.rubocop.yml +5 -0
- data/CHANGELOG.md +101 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +13 -9
- data/README.md +242 -31
- data/lib/openai/assistants.rb +27 -0
- data/lib/openai/audio.rb +19 -0
- data/lib/openai/client.rb +57 -22
- data/lib/openai/compatibility.rb +1 -0
- data/lib/openai/files.rb +8 -9
- data/lib/openai/finetunes.rb +8 -17
- data/lib/openai/http.rb +72 -38
- data/lib/openai/http_headers.rb +36 -0
- data/lib/openai/images.rb +5 -6
- data/lib/openai/messages.rb +23 -0
- data/lib/openai/models.rb +4 -5
- data/lib/openai/run_steps.rb +15 -0
- data/lib/openai/runs.rb +32 -0
- data/lib/openai/threads.rb +27 -0
- data/lib/openai/version.rb +1 -1
- data/lib/openai.rb +38 -1
- data/ruby-openai.gemspec +2 -1
- metadata +39 -8
data/lib/openai/http.rb
CHANGED
@@ -1,93 +1,127 @@
|
|
1
|
+
require "event_stream_parser"
|
2
|
+
|
3
|
+
require_relative "http_headers"
|
4
|
+
|
1
5
|
module OpenAI
|
2
6
|
module HTTP
|
7
|
+
include HTTPHeaders
|
8
|
+
|
3
9
|
def get(path:)
|
4
|
-
|
10
|
+
parse_jsonl(conn.get(uri(path: path)) do |req|
|
5
11
|
req.headers = headers
|
6
12
|
end&.body)
|
7
13
|
end
|
8
14
|
|
9
|
-
def
|
10
|
-
|
11
|
-
if parameters[:stream].is_a?(Proc)
|
12
|
-
req.options.on_data = to_json_stream(user_proc: parameters[:stream])
|
13
|
-
parameters[:stream] = true # Necessary to tell OpenAI to stream.
|
14
|
-
end
|
15
|
-
|
15
|
+
def post(path:)
|
16
|
+
parse_jsonl(conn.post(uri(path: path)) do |req|
|
16
17
|
req.headers = headers
|
17
|
-
req.body = parameters.to_json
|
18
18
|
end&.body)
|
19
19
|
end
|
20
20
|
|
21
|
+
def json_post(path:, parameters:)
|
22
|
+
conn.post(uri(path: path)) do |req|
|
23
|
+
configure_json_post_request(req, parameters)
|
24
|
+
end&.body
|
25
|
+
end
|
26
|
+
|
21
27
|
def multipart_post(path:, parameters: nil)
|
22
|
-
|
28
|
+
conn(multipart: true).post(uri(path: path)) do |req|
|
23
29
|
req.headers = headers.merge({ "Content-Type" => "multipart/form-data" })
|
24
30
|
req.body = multipart_parameters(parameters)
|
25
|
-
end&.body
|
31
|
+
end&.body
|
26
32
|
end
|
27
33
|
|
28
34
|
def delete(path:)
|
29
|
-
|
35
|
+
conn.delete(uri(path: path)) do |req|
|
30
36
|
req.headers = headers
|
31
|
-
end&.body
|
37
|
+
end&.body
|
32
38
|
end
|
33
39
|
|
34
40
|
private
|
35
41
|
|
36
|
-
def
|
37
|
-
return unless
|
42
|
+
def parse_jsonl(response)
|
43
|
+
return unless response
|
44
|
+
return response unless response.is_a?(String)
|
38
45
|
|
39
|
-
JSON.parse(string)
|
40
|
-
rescue JSON::ParserError
|
41
46
|
# Convert a multiline string of JSON objects to a JSON array.
|
42
|
-
|
47
|
+
response = response.gsub("}\n{", "},{").prepend("[").concat("]")
|
48
|
+
|
49
|
+
JSON.parse(response)
|
43
50
|
end
|
44
51
|
|
45
52
|
# Given a proc, returns an outer proc that can be used to iterate over a JSON stream of chunks.
|
46
53
|
# For each chunk, the inner user_proc is called giving it the JSON object. The JSON object could
|
47
54
|
# be a data object or an error object as described in the OpenAI API documentation.
|
48
55
|
#
|
49
|
-
# If the JSON object for a given data or error message is invalid, it is ignored.
|
50
|
-
#
|
51
56
|
# @param user_proc [Proc] The inner proc to call for each JSON object in the chunk.
|
52
57
|
# @return [Proc] An outer proc that iterates over a raw stream, converting it to JSON.
|
53
58
|
def to_json_stream(user_proc:)
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
+
parser = EventStreamParser::Parser.new
|
60
|
+
|
61
|
+
proc do |chunk, _bytes, env|
|
62
|
+
if env && env.status != 200
|
63
|
+
raise_error = Faraday::Response::RaiseError.new
|
64
|
+
raise_error.on_complete(env.merge(body: try_parse_json(chunk)))
|
65
|
+
end
|
66
|
+
|
67
|
+
parser.feed(chunk) do |_type, data|
|
68
|
+
user_proc.call(JSON.parse(data)) unless data == "[DONE]"
|
59
69
|
end
|
60
70
|
end
|
61
71
|
end
|
62
72
|
|
63
73
|
def conn(multipart: false)
|
64
|
-
Faraday.new do |f|
|
65
|
-
f.options[:timeout] =
|
74
|
+
connection = Faraday.new do |f|
|
75
|
+
f.options[:timeout] = @request_timeout
|
66
76
|
f.request(:multipart) if multipart
|
77
|
+
f.use MiddlewareErrors
|
78
|
+
f.response :raise_error
|
79
|
+
f.response :json
|
67
80
|
end
|
68
|
-
end
|
69
81
|
|
70
|
-
|
71
|
-
|
82
|
+
@faraday_middleware&.call(connection)
|
83
|
+
|
84
|
+
connection
|
72
85
|
end
|
73
86
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
"
|
78
|
-
|
79
|
-
|
87
|
+
def uri(path:)
|
88
|
+
if azure?
|
89
|
+
base = File.join(@uri_base, path)
|
90
|
+
"#{base}?api-version=#{@api_version}"
|
91
|
+
else
|
92
|
+
File.join(@uri_base, @api_version, path)
|
93
|
+
end
|
80
94
|
end
|
81
95
|
|
82
96
|
def multipart_parameters(parameters)
|
83
97
|
parameters&.transform_values do |value|
|
84
|
-
next value unless value.
|
98
|
+
next value unless value.respond_to?(:close) # File or IO object.
|
85
99
|
|
86
|
-
# Doesn't seem like OpenAI
|
100
|
+
# Doesn't seem like OpenAI needs mime_type yet, so not worth
|
87
101
|
# the library to figure this out. Hence the empty string
|
88
102
|
# as the second argument.
|
89
103
|
Faraday::UploadIO.new(value, "", value.path)
|
90
104
|
end
|
91
105
|
end
|
106
|
+
|
107
|
+
def configure_json_post_request(req, parameters)
|
108
|
+
req_parameters = parameters.dup
|
109
|
+
|
110
|
+
if parameters[:stream].respond_to?(:call)
|
111
|
+
req.options.on_data = to_json_stream(user_proc: parameters[:stream])
|
112
|
+
req_parameters[:stream] = true # Necessary to tell OpenAI to stream.
|
113
|
+
elsif parameters[:stream]
|
114
|
+
raise ArgumentError, "The stream parameter must be a Proc or have a #call method"
|
115
|
+
end
|
116
|
+
|
117
|
+
req.headers = headers
|
118
|
+
req.body = req_parameters.to_json
|
119
|
+
end
|
120
|
+
|
121
|
+
def try_parse_json(maybe_json)
|
122
|
+
JSON.parse(maybe_json)
|
123
|
+
rescue JSON::ParserError
|
124
|
+
maybe_json
|
125
|
+
end
|
92
126
|
end
|
93
127
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module OpenAI
|
2
|
+
module HTTPHeaders
|
3
|
+
def add_headers(headers)
|
4
|
+
@extra_headers = extra_headers.merge(headers.transform_keys(&:to_s))
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def headers
|
10
|
+
if azure?
|
11
|
+
azure_headers
|
12
|
+
else
|
13
|
+
openai_headers
|
14
|
+
end.merge(extra_headers)
|
15
|
+
end
|
16
|
+
|
17
|
+
def openai_headers
|
18
|
+
{
|
19
|
+
"Content-Type" => "application/json",
|
20
|
+
"Authorization" => "Bearer #{@access_token}",
|
21
|
+
"OpenAI-Organization" => @organization_id
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def azure_headers
|
26
|
+
{
|
27
|
+
"Content-Type" => "application/json",
|
28
|
+
"api-key" => @access_token
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def extra_headers
|
33
|
+
@extra_headers ||= {}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/openai/images.rb
CHANGED
@@ -1,20 +1,19 @@
|
|
1
1
|
module OpenAI
|
2
2
|
class Images
|
3
|
-
def initialize(
|
4
|
-
|
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
|
-
|
8
|
+
@client.json_post(path: "/images/generations", parameters: parameters)
|
10
9
|
end
|
11
10
|
|
12
11
|
def edit(parameters: {})
|
13
|
-
|
12
|
+
@client.multipart_post(path: "/images/edits", parameters: open_files(parameters))
|
14
13
|
end
|
15
14
|
|
16
15
|
def variations(parameters: {})
|
17
|
-
|
16
|
+
@client.multipart_post(path: "/images/variations", parameters: open_files(parameters))
|
18
17
|
end
|
19
18
|
|
20
19
|
private
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module OpenAI
|
2
|
+
class Messages
|
3
|
+
def initialize(client:)
|
4
|
+
@client = client.beta(assistants: "v1")
|
5
|
+
end
|
6
|
+
|
7
|
+
def list(thread_id:)
|
8
|
+
@client.get(path: "/threads/#{thread_id}/messages")
|
9
|
+
end
|
10
|
+
|
11
|
+
def retrieve(thread_id:, id:)
|
12
|
+
@client.get(path: "/threads/#{thread_id}/messages/#{id}")
|
13
|
+
end
|
14
|
+
|
15
|
+
def create(thread_id:, parameters: {})
|
16
|
+
@client.json_post(path: "/threads/#{thread_id}/messages", parameters: parameters)
|
17
|
+
end
|
18
|
+
|
19
|
+
def modify(id:, thread_id:, parameters: {})
|
20
|
+
@client.json_post(path: "/threads/#{thread_id}/messages/#{id}", parameters: parameters)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/openai/models.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
module OpenAI
|
2
2
|
class Models
|
3
|
-
def initialize(
|
4
|
-
|
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
|
-
|
8
|
+
@client.get(path: "/models")
|
10
9
|
end
|
11
10
|
|
12
11
|
def retrieve(id:)
|
13
|
-
|
12
|
+
@client.get(path: "/models/#{id}")
|
14
13
|
end
|
15
14
|
end
|
16
15
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module OpenAI
|
2
|
+
class RunSteps
|
3
|
+
def initialize(client:)
|
4
|
+
@client = client.beta(assistants: "v1")
|
5
|
+
end
|
6
|
+
|
7
|
+
def list(thread_id:, run_id:)
|
8
|
+
@client.get(path: "/threads/#{thread_id}/runs/#{run_id}/steps")
|
9
|
+
end
|
10
|
+
|
11
|
+
def retrieve(thread_id:, run_id:, id:)
|
12
|
+
@client.get(path: "/threads/#{thread_id}/runs/#{run_id}/steps/#{id}")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/openai/runs.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module OpenAI
|
2
|
+
class Runs
|
3
|
+
def initialize(client:)
|
4
|
+
@client = client.beta(assistants: "v1")
|
5
|
+
end
|
6
|
+
|
7
|
+
def list(thread_id:)
|
8
|
+
@client.get(path: "/threads/#{thread_id}/runs")
|
9
|
+
end
|
10
|
+
|
11
|
+
def retrieve(thread_id:, id:)
|
12
|
+
@client.get(path: "/threads/#{thread_id}/runs/#{id}")
|
13
|
+
end
|
14
|
+
|
15
|
+
def create(thread_id:, parameters: {})
|
16
|
+
@client.json_post(path: "/threads/#{thread_id}/runs", parameters: parameters)
|
17
|
+
end
|
18
|
+
|
19
|
+
def modify(id:, thread_id:, parameters: {})
|
20
|
+
@client.json_post(path: "/threads/#{thread_id}/runs/#{id}", parameters: parameters)
|
21
|
+
end
|
22
|
+
|
23
|
+
def cancel(id:, thread_id:)
|
24
|
+
@client.post(path: "/threads/#{thread_id}/runs/#{id}/cancel")
|
25
|
+
end
|
26
|
+
|
27
|
+
def submit_tool_outputs(thread_id:, run_id:, parameters: {})
|
28
|
+
@client.json_post(path: "/threads/#{thread_id}/runs/#{run_id}/submit_tool_outputs",
|
29
|
+
parameters: parameters)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module OpenAI
|
2
|
+
class Threads
|
3
|
+
def initialize(client:)
|
4
|
+
@client = client.beta(assistants: "v1")
|
5
|
+
end
|
6
|
+
|
7
|
+
def list
|
8
|
+
@client.get(path: "/threads")
|
9
|
+
end
|
10
|
+
|
11
|
+
def retrieve(id:)
|
12
|
+
@client.get(path: "/threads/#{id}")
|
13
|
+
end
|
14
|
+
|
15
|
+
def create(parameters: {})
|
16
|
+
@client.json_post(path: "/threads", parameters: parameters)
|
17
|
+
end
|
18
|
+
|
19
|
+
def modify(id:, parameters: {})
|
20
|
+
@client.json_post(path: "/threads/#{id}", parameters: parameters)
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(id:)
|
24
|
+
@client.delete(path: "/threads/#{id}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/openai/version.rb
CHANGED
data/lib/openai.rb
CHANGED
@@ -7,15 +7,38 @@ 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/assistants"
|
11
|
+
require_relative "openai/threads"
|
12
|
+
require_relative "openai/messages"
|
13
|
+
require_relative "openai/runs"
|
14
|
+
require_relative "openai/run_steps"
|
15
|
+
require_relative "openai/audio"
|
10
16
|
require_relative "openai/version"
|
11
17
|
|
12
18
|
module OpenAI
|
13
19
|
class Error < StandardError; end
|
14
20
|
class ConfigurationError < Error; end
|
15
21
|
|
22
|
+
class MiddlewareErrors < Faraday::Middleware
|
23
|
+
def call(env)
|
24
|
+
@app.call(env)
|
25
|
+
rescue Faraday::Error => e
|
26
|
+
raise e unless e.response.is_a?(Hash)
|
27
|
+
|
28
|
+
logger = Logger.new($stdout)
|
29
|
+
logger.formatter = proc do |_severity, _datetime, _progname, msg|
|
30
|
+
"\033[31mOpenAI HTTP Error (spotted in ruby-openai #{VERSION}): #{msg}\n\033[0m"
|
31
|
+
end
|
32
|
+
logger.error(e.response[:body])
|
33
|
+
|
34
|
+
raise e
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
16
38
|
class Configuration
|
17
39
|
attr_writer :access_token
|
18
|
-
attr_accessor :api_version, :organization_id, :uri_base, :request_timeout
|
40
|
+
attr_accessor :api_type, :api_version, :organization_id, :uri_base, :request_timeout,
|
41
|
+
:extra_headers
|
19
42
|
|
20
43
|
DEFAULT_API_VERSION = "v1".freeze
|
21
44
|
DEFAULT_URI_BASE = "https://api.openai.com/".freeze
|
@@ -23,10 +46,12 @@ module OpenAI
|
|
23
46
|
|
24
47
|
def initialize
|
25
48
|
@access_token = nil
|
49
|
+
@api_type = nil
|
26
50
|
@api_version = DEFAULT_API_VERSION
|
27
51
|
@organization_id = nil
|
28
52
|
@uri_base = DEFAULT_URI_BASE
|
29
53
|
@request_timeout = DEFAULT_REQUEST_TIMEOUT
|
54
|
+
@extra_headers = {}
|
30
55
|
end
|
31
56
|
|
32
57
|
def access_token
|
@@ -48,4 +73,16 @@ module OpenAI
|
|
48
73
|
def self.configure
|
49
74
|
yield(configuration)
|
50
75
|
end
|
76
|
+
|
77
|
+
# Estimate the number of tokens in a string, using the rules of thumb from OpenAI:
|
78
|
+
# https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them
|
79
|
+
def self.rough_token_count(content = "")
|
80
|
+
raise ArgumentError, "rough_token_count requires a string" unless content.is_a? String
|
81
|
+
return 0 if content.empty?
|
82
|
+
|
83
|
+
count_by_chars = content.size / 4.0
|
84
|
+
count_by_words = content.split.size * 4.0 / 3
|
85
|
+
estimate = ((count_by_chars + count_by_words) / 2.0).round
|
86
|
+
[1, estimate].max
|
87
|
+
end
|
51
88
|
end
|
data/ruby-openai.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
|
|
6
6
|
spec.authors = ["Alex"]
|
7
7
|
spec.email = ["alexrudall@users.noreply.github.com"]
|
8
8
|
|
9
|
-
spec.summary = "OpenAI API + Ruby!
|
9
|
+
spec.summary = "OpenAI API + Ruby! 🤖🩵"
|
10
10
|
spec.homepage = "https://github.com/alexrudall/ruby-openai"
|
11
11
|
spec.license = "MIT"
|
12
12
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
|
@@ -25,6 +25,7 @@ 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 "event_stream_parser", ">= 0.3.0", "< 2.0.0"
|
28
29
|
spec.add_dependency "faraday", ">= 1"
|
29
30
|
spec.add_dependency "faraday-multipart", ">= 1"
|
30
31
|
end
|
metadata
CHANGED
@@ -1,15 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-openai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 6.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04
|
11
|
+
date: 2023-12-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: event_stream_parser
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.3.0
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.0.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.3.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.0.0
|
13
33
|
- !ruby/object:Gem::Dependency
|
14
34
|
name: faraday
|
15
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,7 +58,7 @@ dependencies:
|
|
38
58
|
- - ">="
|
39
59
|
- !ruby/object:Gem::Version
|
40
60
|
version: '1'
|
41
|
-
description:
|
61
|
+
description:
|
42
62
|
email:
|
43
63
|
- alexrudall@users.noreply.github.com
|
44
64
|
executables: []
|
@@ -46,6 +66,10 @@ extensions: []
|
|
46
66
|
extra_rdoc_files: []
|
47
67
|
files:
|
48
68
|
- ".circleci/config.yml"
|
69
|
+
- ".devcontainer/Dockerfile"
|
70
|
+
- ".devcontainer/devcontainer.json"
|
71
|
+
- ".devcontainer/docker-compose.yml"
|
72
|
+
- ".github/FUNDING.yml"
|
49
73
|
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
50
74
|
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
51
75
|
- ".github/dependabot.yml"
|
@@ -63,13 +87,20 @@ files:
|
|
63
87
|
- bin/console
|
64
88
|
- bin/setup
|
65
89
|
- lib/openai.rb
|
90
|
+
- lib/openai/assistants.rb
|
91
|
+
- lib/openai/audio.rb
|
66
92
|
- lib/openai/client.rb
|
67
93
|
- lib/openai/compatibility.rb
|
68
94
|
- lib/openai/files.rb
|
69
95
|
- lib/openai/finetunes.rb
|
70
96
|
- lib/openai/http.rb
|
97
|
+
- lib/openai/http_headers.rb
|
71
98
|
- lib/openai/images.rb
|
99
|
+
- lib/openai/messages.rb
|
72
100
|
- lib/openai/models.rb
|
101
|
+
- lib/openai/run_steps.rb
|
102
|
+
- lib/openai/runs.rb
|
103
|
+
- lib/openai/threads.rb
|
73
104
|
- lib/openai/version.rb
|
74
105
|
- lib/ruby/openai.rb
|
75
106
|
- pull_request_template.md
|
@@ -82,7 +113,7 @@ metadata:
|
|
82
113
|
source_code_uri: https://github.com/alexrudall/ruby-openai
|
83
114
|
changelog_uri: https://github.com/alexrudall/ruby-openai/blob/main/CHANGELOG.md
|
84
115
|
rubygems_mfa_required: 'true'
|
85
|
-
post_install_message:
|
116
|
+
post_install_message:
|
86
117
|
rdoc_options: []
|
87
118
|
require_paths:
|
88
119
|
- lib
|
@@ -97,8 +128,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
128
|
- !ruby/object:Gem::Version
|
98
129
|
version: '0'
|
99
130
|
requirements: []
|
100
|
-
rubygems_version: 3.4.
|
101
|
-
signing_key:
|
131
|
+
rubygems_version: 3.4.10
|
132
|
+
signing_key:
|
102
133
|
specification_version: 4
|
103
|
-
summary: "OpenAI API + Ruby! \U0001F916
|
134
|
+
summary: "OpenAI API + Ruby! \U0001F916\U0001FA75"
|
104
135
|
test_files: []
|