ruby-openai 7.1.0 → 7.3.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 +14 -0
- data/Gemfile.lock +2 -2
- data/README.md +144 -4
- data/lib/openai/client.rb +11 -0
- data/lib/openai/files.rb +2 -2
- data/lib/openai/http.rb +2 -1
- data/lib/openai/messages.rb +5 -1
- data/lib/openai/run_steps.rb +2 -2
- data/lib/openai/runs.rb +3 -2
- data/lib/openai/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 278f25c283d841bfa33614bd69b4340b9275712b83e9121a1aa2a6a439767714
|
4
|
+
data.tar.gz: 702c11ba4b0411a47e9d6f9fdb178d1eb40a7baede5909f0665b41edc00797b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c4a1bdb8fd3f466808f740112c3223a04da5ef73fd355b5f2136ecf28f5be2968ec4ecced5db25833878ba7e9de1f63e063529580f86d269c7bdd82f7e77df9
|
7
|
+
data.tar.gz: '014855034340e14ac2e78c845ae791f619dedf636c9839ce5edc2ea27d7eb54e973dbe4a41998b41d1e89c2c56ce04cd2c062c90bdc87b858b7467005e78100c'
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,20 @@ 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
|
+
## [7.3.0] - 2024-10-11
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- Add ability to (with the right incantations) retrieve the chunks used by an Assistant file search - thanks to [@agamble](https://github.com/agamble) for the addition!
|
13
|
+
|
14
|
+
## [7.2.0] - 2024-10-10
|
15
|
+
|
16
|
+
### Added
|
17
|
+
|
18
|
+
- Add ability to pass parameters to Files#list endpoint - thanks to [@parterburn](https://github.com/parterburn)!
|
19
|
+
- Add Velvet observability platform to README - thanks to [@philipithomas](https://github.com/philipithomas)
|
20
|
+
- Add Assistants::Messages#delete endpoint - thanks to [@mochetts](https://github.com/mochetts)!
|
21
|
+
|
8
22
|
## [7.1.0] - 2024-06-10
|
9
23
|
|
10
24
|
### Added
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ruby-openai (7.
|
4
|
+
ruby-openai (7.3.0)
|
5
5
|
event_stream_parser (>= 0.3.0, < 2.0.0)
|
6
6
|
faraday (>= 1)
|
7
7
|
faraday-multipart (>= 1)
|
@@ -38,7 +38,7 @@ GEM
|
|
38
38
|
rainbow (3.1.1)
|
39
39
|
rake (13.2.1)
|
40
40
|
regexp_parser (2.8.0)
|
41
|
-
rexml (3.
|
41
|
+
rexml (3.3.6)
|
42
42
|
strscan
|
43
43
|
rspec (3.13.0)
|
44
44
|
rspec-core (~> 3.13.0)
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@ Use the [OpenAI API](https://openai.com/blog/openai-api/) with Ruby! 🤖❤️
|
|
8
8
|
|
9
9
|
Stream text with GPT-4o, transcribe and translate audio with Whisper, or create images with DALL·E...
|
10
10
|
|
11
|
-
[
|
11
|
+
[📚 Rails AI (FREE Book)](https://railsai.com) | [🎮 Ruby AI Builders Discord](https://discord.gg/k4Uc224xVD) | [🐦 X](https://x.com/alexrudall) | [🧠 Anthropic Gem](https://github.com/alexrudall/anthropic) | [🚂 Midjourney Gem](https://github.com/alexrudall/midjourney)
|
12
12
|
|
13
13
|
## Contents
|
14
14
|
|
@@ -139,7 +139,9 @@ client = OpenAI::Client.new(access_token: "access_token_goes_here")
|
|
139
139
|
|
140
140
|
#### Custom timeout or base URI
|
141
141
|
|
142
|
-
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.
|
142
|
+
- 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.
|
143
|
+
- 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) or [Velvet](https://docs.usevelvet.com/docs/getting-started)
|
144
|
+
- You can also add arbitrary other headers e.g. for [openai-caching-proxy-worker](https://github.com/6/openai-caching-proxy-worker), eg.:
|
143
145
|
|
144
146
|
```ruby
|
145
147
|
client = OpenAI::Client.new(
|
@@ -326,7 +328,28 @@ client.chat(
|
|
326
328
|
# => "Anna is a young woman in her mid-twenties, with wavy chestnut hair that falls to her shoulders..."
|
327
329
|
```
|
328
330
|
|
329
|
-
Note:
|
331
|
+
Note: In order to get usage information, you can provide the [`stream_options` parameter](https://platform.openai.com/docs/api-reference/chat/create#chat-create-stream_options) and OpenAI will provide a final chunk with the usage. Here is an example:
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
stream_proc = proc { |chunk, _bytesize| puts "--------------"; puts chunk.inspect; }
|
335
|
+
client.chat(
|
336
|
+
parameters: {
|
337
|
+
model: "gpt-4o",
|
338
|
+
stream: stream_proc,
|
339
|
+
stream_options: { include_usage: true },
|
340
|
+
messages: [{ role: "user", content: "Hello!"}],
|
341
|
+
})
|
342
|
+
# => --------------
|
343
|
+
# => {"id"=>"chatcmpl-7bbq05PiZqlHxjV1j7OHnKKDURKaf", "object"=>"chat.completion.chunk", "created"=>1718750612, "model"=>"gpt-4o-2024-05-13", "system_fingerprint"=>"fp_9cb5d38cf7", "choices"=>[{"index"=>0, "delta"=>{"role"=>"assistant", "content"=>""}, "logprobs"=>nil, "finish_reason"=>nil}], "usage"=>nil}
|
344
|
+
# => --------------
|
345
|
+
# => {"id"=>"chatcmpl-7bbq05PiZqlHxjV1j7OHnKKDURKaf", "object"=>"chat.completion.chunk", "created"=>1718750612, "model"=>"gpt-4o-2024-05-13", "system_fingerprint"=>"fp_9cb5d38cf7", "choices"=>[{"index"=>0, "delta"=>{"content"=>"Hello"}, "logprobs"=>nil, "finish_reason"=>nil}], "usage"=>nil}
|
346
|
+
# => --------------
|
347
|
+
# => ... more content chunks
|
348
|
+
# => --------------
|
349
|
+
# => {"id"=>"chatcmpl-7bbq05PiZqlHxjV1j7OHnKKDURKaf", "object"=>"chat.completion.chunk", "created"=>1718750612, "model"=>"gpt-4o-2024-05-13", "system_fingerprint"=>"fp_9cb5d38cf7", "choices"=>[{"index"=>0, "delta"=>{}, "logprobs"=>nil, "finish_reason"=>"stop"}], "usage"=>nil}
|
350
|
+
# => --------------
|
351
|
+
# => {"id"=>"chatcmpl-7bbq05PiZqlHxjV1j7OHnKKDURKaf", "object"=>"chat.completion.chunk", "created"=>1718750612, "model"=>"gpt-4o-2024-05-13", "system_fingerprint"=>"fp_9cb5d38cf7", "choices"=>[], "usage"=>{"prompt_tokens"=>9, "completion_tokens"=>9, "total_tokens"=>18}}
|
352
|
+
```
|
330
353
|
|
331
354
|
#### Vision
|
332
355
|
|
@@ -526,9 +549,11 @@ puts response.dig("data", 0, "embedding")
|
|
526
549
|
```
|
527
550
|
|
528
551
|
### Batches
|
552
|
+
|
529
553
|
The Batches endpoint allows you to create and manage large batches of API requests to run asynchronously. Currently, the supported endpoints for batches are `/v1/chat/completions` (Chat Completions API) and `/v1/embeddings` (Embeddings API).
|
530
554
|
|
531
555
|
To use the Batches endpoint, you need to first upload a JSONL file containing the batch requests using the Files endpoint. The file must be uploaded with the purpose set to `batch`. Each line in the JSONL file represents a single request and should have the following format:
|
556
|
+
|
532
557
|
```json
|
533
558
|
{
|
534
559
|
"custom_id": "request-1",
|
@@ -612,7 +637,9 @@ These files are in JSONL format, with each line representing the output or error
|
|
612
637
|
If a request fails with a non-HTTP error, the error object will contain more information about the cause of the failure.
|
613
638
|
|
614
639
|
### Files
|
640
|
+
|
615
641
|
#### For fine-tuning purposes
|
642
|
+
|
616
643
|
Put your data in a `.jsonl` file like this:
|
617
644
|
|
618
645
|
```json
|
@@ -645,7 +672,6 @@ my_file = File.open("path/to/file.pdf", "rb")
|
|
645
672
|
client.files.upload(parameters: { file: my_file, purpose: "assistants" })
|
646
673
|
```
|
647
674
|
|
648
|
-
|
649
675
|
See supported file types on [API documentation](https://platform.openai.com/docs/assistants/tools/file-search/supported-files).
|
650
676
|
|
651
677
|
### Finetunes
|
@@ -701,6 +727,7 @@ client.finetunes.list_events(id: fine_tune_id)
|
|
701
727
|
```
|
702
728
|
|
703
729
|
### Vector Stores
|
730
|
+
|
704
731
|
Vector Store objects give the File Search tool the ability to search your files.
|
705
732
|
|
706
733
|
You can create a new vector store:
|
@@ -746,6 +773,7 @@ client.vector_stores.delete(id: vector_store_id)
|
|
746
773
|
```
|
747
774
|
|
748
775
|
### Vector Store Files
|
776
|
+
|
749
777
|
Vector store files represent files inside a vector store.
|
750
778
|
|
751
779
|
You can create a new vector store file by attaching a File to a vector store.
|
@@ -784,9 +812,11 @@ client.vector_store_files.delete(
|
|
784
812
|
id: vector_store_file_id
|
785
813
|
)
|
786
814
|
```
|
815
|
+
|
787
816
|
Note: This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the delete file endpoint.
|
788
817
|
|
789
818
|
### Vector Store File Batches
|
819
|
+
|
790
820
|
Vector store file batches represent operations to add multiple files to a vector store.
|
791
821
|
|
792
822
|
You can create a new vector store file batch by attaching multiple Files to a vector store.
|
@@ -1081,6 +1111,116 @@ end
|
|
1081
1111
|
|
1082
1112
|
Note that you have 10 minutes to submit your tool output before the run expires.
|
1083
1113
|
|
1114
|
+
#### Exploring chunks used in File Search
|
1115
|
+
|
1116
|
+
Take a deep breath. You might need a drink for this one.
|
1117
|
+
|
1118
|
+
It's possible for OpenAI to share what chunks it used in its internal RAG Pipeline to create its filesearch example.
|
1119
|
+
|
1120
|
+
An example spec can be found [here](https://github.com/alexrudall/ruby-openai/blob/main/spec/openai/client/assistant_file_search_spec.rb) that does this, just so you know it's possible.
|
1121
|
+
|
1122
|
+
Here's how to get the chunks used in a file search. In this example I'm using [this file](https://css4.pub/2015/textbook/somatosensory.pdf):
|
1123
|
+
|
1124
|
+
```
|
1125
|
+
require "openai"
|
1126
|
+
|
1127
|
+
# Make a client
|
1128
|
+
client = OpenAI::Client.new(
|
1129
|
+
access_token: "access_token_goes_here",
|
1130
|
+
log_errors: true # Don't do this in production.
|
1131
|
+
)
|
1132
|
+
|
1133
|
+
# Upload your file(s)
|
1134
|
+
file_id = client.files.upload(
|
1135
|
+
parameters: {
|
1136
|
+
file: "path/to/somatosensory.pdf",
|
1137
|
+
purpose: "assistants"
|
1138
|
+
}
|
1139
|
+
)["id"]
|
1140
|
+
|
1141
|
+
# Create a vector store to store the vectorised file(s)
|
1142
|
+
vector_store_id = client.vector_stores.create(parameters: {})["id"]
|
1143
|
+
|
1144
|
+
# Vectorise the file(s)
|
1145
|
+
vector_store_file_id = client.vector_store_files.create(
|
1146
|
+
vector_store_id: vector_store_id,
|
1147
|
+
parameters: { file_id: file_id }
|
1148
|
+
)["id"]
|
1149
|
+
|
1150
|
+
# Check that the file is vectorised (wait for status to be "completed")
|
1151
|
+
client.vector_store_files.retrieve(vector_store_id: vector_store_id, id: vector_store_file_id)["status"]
|
1152
|
+
|
1153
|
+
# Create an assistant, referencing the vector store
|
1154
|
+
assistant_id = client.assistants.create(
|
1155
|
+
parameters: {
|
1156
|
+
model: "gpt-4o",
|
1157
|
+
name: "Answer finder",
|
1158
|
+
instructions: "You are a file search tool. Find the answer in the given files, please.",
|
1159
|
+
tools: [
|
1160
|
+
{ type: "file_search" }
|
1161
|
+
],
|
1162
|
+
tool_resources: {
|
1163
|
+
file_search: {
|
1164
|
+
vector_store_ids: [vector_store_id]
|
1165
|
+
}
|
1166
|
+
}
|
1167
|
+
}
|
1168
|
+
)["id"]
|
1169
|
+
|
1170
|
+
# Create a thread with your question
|
1171
|
+
thread_id = client.threads.create(parameters: {
|
1172
|
+
messages: [
|
1173
|
+
{ role: "user",
|
1174
|
+
content: "Find the description of a nociceptor." }
|
1175
|
+
]
|
1176
|
+
})["id"]
|
1177
|
+
|
1178
|
+
# Run the thread to generate the response. Include the "GIVE ME THE CHUNKS" incantation.
|
1179
|
+
run_id = client.runs.create(
|
1180
|
+
thread_id: thread_id,
|
1181
|
+
parameters: {
|
1182
|
+
assistant_id: assistant_id
|
1183
|
+
},
|
1184
|
+
query_parameters: { include: ["step_details.tool_calls[*].file_search.results[*].content"] } # incantation
|
1185
|
+
)["id"]
|
1186
|
+
|
1187
|
+
# Get the steps that happened in the run
|
1188
|
+
steps = client.run_steps.list(
|
1189
|
+
thread_id: thread_id,
|
1190
|
+
run_id: run_id,
|
1191
|
+
parameters: { order: "asc" }
|
1192
|
+
)
|
1193
|
+
|
1194
|
+
# Get the last step ID (or whichever one you want to look at)
|
1195
|
+
step_id = steps["data"].first["id"]
|
1196
|
+
|
1197
|
+
# Retrieve all the steps. Include the "GIVE ME THE CHUNKS" incantation again.
|
1198
|
+
steps = steps["data"].map do |step|
|
1199
|
+
client.run_steps.retrieve(
|
1200
|
+
thread_id: thread_id,
|
1201
|
+
run_id: run_id,
|
1202
|
+
id: step["id"],
|
1203
|
+
parameters: { include: ["step_details.tool_calls[*].file_search.results[*].content"] } # incantation
|
1204
|
+
)
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
# Now we've got the chunk info, buried deep. Loop through the steps and find chunks if included:
|
1208
|
+
chunks = steps.flat_map do |step|
|
1209
|
+
included_results = step.dig("step_details", "tool_calls", 0, "file_search", "results")
|
1210
|
+
|
1211
|
+
next if included_results.nil? || included_results.empty?
|
1212
|
+
|
1213
|
+
included_results.flat_map do |result|
|
1214
|
+
result["content"].map do |content|
|
1215
|
+
content["text"]
|
1216
|
+
end
|
1217
|
+
end
|
1218
|
+
end.compact
|
1219
|
+
|
1220
|
+
# The first chunk will be the closest match to the prompt. Finally, if you want to view the completed message(s):
|
1221
|
+
client.messages.list(thread_id: thread_id)
|
1222
|
+
```
|
1223
|
+
|
1084
1224
|
### Image Generation
|
1085
1225
|
|
1086
1226
|
Generate images using DALL·E 2 or DALL·E 3!
|
data/lib/openai/client.rb
CHANGED
@@ -2,6 +2,7 @@ module OpenAI
|
|
2
2
|
class Client
|
3
3
|
include OpenAI::HTTP
|
4
4
|
|
5
|
+
SENSITIVE_ATTRIBUTES = %i[@access_token @organization_id @extra_headers].freeze
|
5
6
|
CONFIG_KEYS = %i[
|
6
7
|
api_type
|
7
8
|
api_version
|
@@ -107,5 +108,15 @@ module OpenAI
|
|
107
108
|
client.add_headers("OpenAI-Beta": apis.map { |k, v| "#{k}=#{v}" }.join(";"))
|
108
109
|
end
|
109
110
|
end
|
111
|
+
|
112
|
+
def inspect
|
113
|
+
vars = instance_variables.map do |var|
|
114
|
+
value = instance_variable_get(var)
|
115
|
+
|
116
|
+
SENSITIVE_ATTRIBUTES.include?(var) ? "#{var}=[REDACTED]" : "#{var}=#{value.inspect}"
|
117
|
+
end
|
118
|
+
|
119
|
+
"#<#{self.class}:#{object_id} #{vars.join(', ')}>"
|
120
|
+
end
|
110
121
|
end
|
111
122
|
end
|
data/lib/openai/files.rb
CHANGED
data/lib/openai/http.rb
CHANGED
@@ -18,9 +18,10 @@ module OpenAI
|
|
18
18
|
end&.body)
|
19
19
|
end
|
20
20
|
|
21
|
-
def json_post(path:, parameters:)
|
21
|
+
def json_post(path:, parameters:, query_parameters: {})
|
22
22
|
conn.post(uri(path: path)) do |req|
|
23
23
|
configure_json_post_request(req, parameters)
|
24
|
+
req.params = query_parameters
|
24
25
|
end&.body
|
25
26
|
end
|
26
27
|
|
data/lib/openai/messages.rb
CHANGED
@@ -16,8 +16,12 @@ module OpenAI
|
|
16
16
|
@client.json_post(path: "/threads/#{thread_id}/messages", parameters: parameters)
|
17
17
|
end
|
18
18
|
|
19
|
-
def modify(
|
19
|
+
def modify(thread_id:, id:, parameters: {})
|
20
20
|
@client.json_post(path: "/threads/#{thread_id}/messages/#{id}", parameters: parameters)
|
21
21
|
end
|
22
|
+
|
23
|
+
def delete(thread_id:, id:)
|
24
|
+
@client.delete(path: "/threads/#{thread_id}/messages/#{id}")
|
25
|
+
end
|
22
26
|
end
|
23
27
|
end
|
data/lib/openai/run_steps.rb
CHANGED
@@ -8,8 +8,8 @@ module OpenAI
|
|
8
8
|
@client.get(path: "/threads/#{thread_id}/runs/#{run_id}/steps", parameters: parameters)
|
9
9
|
end
|
10
10
|
|
11
|
-
def retrieve(thread_id:, run_id:, id:)
|
12
|
-
@client.get(path: "/threads/#{thread_id}/runs/#{run_id}/steps/#{id}")
|
11
|
+
def retrieve(thread_id:, run_id:, id:, parameters: {})
|
12
|
+
@client.get(path: "/threads/#{thread_id}/runs/#{run_id}/steps/#{id}", parameters: parameters)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
data/lib/openai/runs.rb
CHANGED
@@ -12,8 +12,9 @@ module OpenAI
|
|
12
12
|
@client.get(path: "/threads/#{thread_id}/runs/#{id}")
|
13
13
|
end
|
14
14
|
|
15
|
-
def create(thread_id:, parameters: {})
|
16
|
-
@client.json_post(path: "/threads/#{thread_id}/runs", parameters: parameters
|
15
|
+
def create(thread_id:, parameters: {}, query_parameters: {})
|
16
|
+
@client.json_post(path: "/threads/#{thread_id}/runs", parameters: parameters,
|
17
|
+
query_parameters: query_parameters)
|
17
18
|
end
|
18
19
|
|
19
20
|
def modify(id:, thread_id:, parameters: {})
|
data/lib/openai/version.rb
CHANGED
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: 7.
|
4
|
+
version: 7.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: event_stream_parser
|