ruby-openai 6.5.0 → 7.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +2 -1
- data/CHANGELOG.md +37 -2
- data/Gemfile +1 -1
- data/Gemfile.lock +15 -15
- data/README.md +254 -76
- data/lib/openai/assistants.rb +3 -1
- data/lib/openai/batches.rb +23 -0
- data/lib/openai/client.rb +9 -5
- data/lib/openai/files.rb +36 -4
- data/lib/openai/http.rb +10 -4
- data/lib/openai/messages.rb +3 -3
- data/lib/openai/run_steps.rb +3 -3
- data/lib/openai/runs.rb +7 -3
- data/lib/openai/threads.rb +1 -1
- data/lib/openai/version.rb +1 -1
- data/lib/openai.rb +10 -9
- data/ruby-openai.gemspec +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4067ee94a830c3637d339dff7445d0176610bcb1d381754915a9279bedac59fa
|
4
|
+
data.tar.gz: b2f6e4e7331e1f60f32aa3c1fb5628124c3b09539b5d4e2a99ebf9e539420cd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f81268cc24c111b011aa87e1a0a877a72caf7a8b9d52cea367c35edf222909f5b8648d6925e30755a71692bd00974fc6d630c055a1639d5bbb8e9a33da11ef9
|
7
|
+
data.tar.gz: 2393f1d34582d37c2ee23f07d81d792fb60b0d5f3e9e5e2b900aa056d4ec49b23244d1d7766a3256a521e7621d17fa349f53134f9bdd64499a1fb760b2818038
|
data/.circleci/config.yml
CHANGED
@@ -8,7 +8,7 @@ jobs:
|
|
8
8
|
rubocop:
|
9
9
|
parallelism: 1
|
10
10
|
docker:
|
11
|
-
- image: cimg/ruby:3.
|
11
|
+
- image: cimg/ruby:3.2-node
|
12
12
|
steps:
|
13
13
|
- checkout
|
14
14
|
- ruby/install-deps
|
@@ -43,3 +43,4 @@ workflows:
|
|
43
43
|
- cimg/ruby:3.0-node
|
44
44
|
- cimg/ruby:3.1-node
|
45
45
|
- cimg/ruby:3.2-node
|
46
|
+
- cimg/ruby:3.3-node
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,41 @@ 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.0.1] - 2024-04-30
|
9
|
+
|
10
|
+
### Fixed
|
11
|
+
|
12
|
+
- Update to v2 of Assistants in Messages, Runs, RunSteps and Threads - thanks to [@willywg](https://github.com/willywg) and others for pointing this out.
|
13
|
+
|
14
|
+
## [7.0.0] - 2024-04-27
|
15
|
+
|
16
|
+
### Added
|
17
|
+
|
18
|
+
- Add support for Batches, thanks to [@simonx1](https://github.com/simonx1) for the PR!
|
19
|
+
- Allow use of local LLMs like Ollama! Thanks to [@ThomasSevestre](https://github.com/ThomasSevestre)
|
20
|
+
- Update to v2 of the Assistants beta & add documentation on streaming from an Assistant.
|
21
|
+
- Add Assistants endpoint to create and run a thread in one go, thank you [@quocphien90](https://github.com/
|
22
|
+
quocphien90)
|
23
|
+
- Add missing parameters (order, limit, etc) to Runs, RunSteps and Messages - thanks to [@shalecraig](https://github.com/shalecraig) and [@coezbek](https://github.com/coezbek)
|
24
|
+
- Add missing Messages#list spec - thanks [@adammeghji](https://github.com/adammeghji)
|
25
|
+
- Add Messages#modify to README - thanks to [@nas887](https://github.com/nas887)
|
26
|
+
- Don't add the api_version (`/v1/`) to base_uris that already include it - thanks to [@kaiwren](https://github.com/kaiwren) for raising this issue
|
27
|
+
- Allow passing a `StringIO` to Files#upload - thanks again to [@simonx1](https://github.com/simonx1)
|
28
|
+
- Add Ruby 3.3 to CI
|
29
|
+
|
30
|
+
### Security
|
31
|
+
|
32
|
+
- [BREAKING] ruby-openai will no longer log out API errors by default - you can reenable by passing `log_errors: true` to your client. This will help to prevent leaking secrets to logs. Thanks to [@lalunamel](https://github.com/lalunamel) for this PR.
|
33
|
+
|
34
|
+
### Removed
|
35
|
+
|
36
|
+
- [BREAKING] Remove deprecated edits endpoint.
|
37
|
+
|
38
|
+
### Fixed
|
39
|
+
|
40
|
+
- Fix README DALL·E 3 error - thanks to [@clayton](https://github.com/clayton)
|
41
|
+
- Fix README tool_calls error and add missing tool_choice info - thanks to [@Jbrito6492](https://github.com/Jbrito6492)
|
42
|
+
|
8
43
|
## [6.5.0] - 2024-03-31
|
9
44
|
|
10
45
|
### Added
|
@@ -67,13 +102,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
67
102
|
- [BREAKING] Switch from legacy Finetunes to the new Fine-tune-jobs endpoints. Implemented by [@lancecarlson](https://github.com/lancecarlson)
|
68
103
|
- [BREAKING] Remove deprecated Completions endpoints - use Chat instead.
|
69
104
|
|
70
|
-
###
|
105
|
+
### Fixed
|
71
106
|
|
72
107
|
- [BREAKING] Fix issue where :stream parameters were replaced by a boolean in the client application. Thanks to [@martinjaimem](https://github.com/martinjaimem), [@vickymadrid03](https://github.com/vickymadrid03) and [@nicastelo](https://github.com/nicastelo) for spotting and fixing this issue.
|
73
108
|
|
74
109
|
## [5.2.0] - 2023-10-30
|
75
110
|
|
76
|
-
###
|
111
|
+
### Fixed
|
77
112
|
|
78
113
|
- Added more spec-compliant SSE parsing: see here https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation
|
79
114
|
- Fixes issue where OpenAI or an intermediary returns only partial JSON per chunk of streamed data
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ruby-openai (
|
4
|
+
ruby-openai (7.0.1)
|
5
5
|
event_stream_parser (>= 0.3.0, < 2.0.0)
|
6
6
|
faraday (>= 1)
|
7
7
|
faraday-multipart (>= 1)
|
@@ -16,10 +16,10 @@ GEM
|
|
16
16
|
byebug (11.1.3)
|
17
17
|
crack (0.4.5)
|
18
18
|
rexml
|
19
|
-
diff-lcs (1.5.
|
19
|
+
diff-lcs (1.5.1)
|
20
20
|
dotenv (2.8.1)
|
21
21
|
event_stream_parser (1.0.0)
|
22
|
-
faraday (2.
|
22
|
+
faraday (2.8.1)
|
23
23
|
base64
|
24
24
|
faraday-net_http (>= 2.0, < 3.1)
|
25
25
|
ruby2_keywords (>= 0.0.4)
|
@@ -37,19 +37,19 @@ GEM
|
|
37
37
|
rake (13.1.0)
|
38
38
|
regexp_parser (2.8.0)
|
39
39
|
rexml (3.2.6)
|
40
|
-
rspec (3.
|
41
|
-
rspec-core (~> 3.
|
42
|
-
rspec-expectations (~> 3.
|
43
|
-
rspec-mocks (~> 3.
|
44
|
-
rspec-core (3.
|
45
|
-
rspec-support (~> 3.
|
46
|
-
rspec-expectations (3.
|
40
|
+
rspec (3.13.0)
|
41
|
+
rspec-core (~> 3.13.0)
|
42
|
+
rspec-expectations (~> 3.13.0)
|
43
|
+
rspec-mocks (~> 3.13.0)
|
44
|
+
rspec-core (3.13.0)
|
45
|
+
rspec-support (~> 3.13.0)
|
46
|
+
rspec-expectations (3.13.0)
|
47
47
|
diff-lcs (>= 1.2.0, < 2.0)
|
48
|
-
rspec-support (~> 3.
|
49
|
-
rspec-mocks (3.
|
48
|
+
rspec-support (~> 3.13.0)
|
49
|
+
rspec-mocks (3.13.0)
|
50
50
|
diff-lcs (>= 1.2.0, < 2.0)
|
51
|
-
rspec-support (~> 3.
|
52
|
-
rspec-support (3.
|
51
|
+
rspec-support (~> 3.13.0)
|
52
|
+
rspec-support (3.13.1)
|
53
53
|
rubocop (1.50.2)
|
54
54
|
json (~> 2.3)
|
55
55
|
parallel (~> 1.10)
|
@@ -78,7 +78,7 @@ DEPENDENCIES
|
|
78
78
|
byebug (~> 11.1.3)
|
79
79
|
dotenv (~> 2.8.1)
|
80
80
|
rake (~> 13.1)
|
81
|
-
rspec (~> 3.
|
81
|
+
rspec (~> 3.13)
|
82
82
|
rubocop (~> 1.50.2)
|
83
83
|
ruby-openai!
|
84
84
|
vcr (~> 6.1.0)
|
data/README.md
CHANGED
@@ -4,13 +4,13 @@
|
|
4
4
|
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/alexrudall/ruby-openai/blob/main/LICENSE.txt)
|
5
5
|
[![CircleCI Build Status](https://circleci.com/gh/alexrudall/ruby-openai.svg?style=shield)](https://circleci.com/gh/alexrudall/ruby-openai)
|
6
6
|
|
7
|
-
Use the [OpenAI API](https://openai.com/blog/openai-api/) with Ruby!
|
7
|
+
Use the [OpenAI API](https://openai.com/blog/openai-api/) with Ruby! 🤖❤️
|
8
8
|
|
9
9
|
Stream text with GPT-4, transcribe and translate audio with Whisper, or create images with DALL·E...
|
10
10
|
|
11
11
|
[🚢 Hire me](https://peaceterms.com?utm_source=ruby-openai&utm_medium=readme&utm_id=26072023) | [🎮 Ruby AI Builders Discord](https://discord.gg/k4Uc224xVD) | [🐦 Twitter](https://twitter.com/alexrudall) | [🧠 Anthropic Gem](https://github.com/alexrudall/anthropic) | [🚂 Midjourney Gem](https://github.com/alexrudall/midjourney)
|
12
12
|
|
13
|
-
|
13
|
+
## Contents
|
14
14
|
|
15
15
|
- [Ruby OpenAI](#ruby-openai)
|
16
16
|
- [Table of Contents](#table-of-contents)
|
@@ -22,11 +22,14 @@ Stream text with GPT-4, transcribe and translate audio with Whisper, or create i
|
|
22
22
|
- [With Config](#with-config)
|
23
23
|
- [Custom timeout or base URI](#custom-timeout-or-base-uri)
|
24
24
|
- [Extra Headers per Client](#extra-headers-per-client)
|
25
|
-
- [
|
25
|
+
- [Logging](#logging)
|
26
|
+
- [Errors](#errors)
|
27
|
+
- [Faraday middleware](#faraday-middleware)
|
26
28
|
- [Azure](#azure)
|
29
|
+
- [Ollama](#ollama)
|
30
|
+
- [Groq](#groq)
|
27
31
|
- [Counting Tokens](#counting-tokens)
|
28
32
|
- [Models](#models)
|
29
|
-
- [Examples](#examples)
|
30
33
|
- [Chat](#chat)
|
31
34
|
- [Streaming Chat](#streaming-chat)
|
32
35
|
- [Vision](#vision)
|
@@ -34,6 +37,7 @@ Stream text with GPT-4, transcribe and translate audio with Whisper, or create i
|
|
34
37
|
- [Functions](#functions)
|
35
38
|
- [Edits](#edits)
|
36
39
|
- [Embeddings](#embeddings)
|
40
|
+
- [Batches](#batches)
|
37
41
|
- [Files](#files)
|
38
42
|
- [Finetunes](#finetunes)
|
39
43
|
- [Assistants](#assistants)
|
@@ -97,7 +101,10 @@ require "openai"
|
|
97
101
|
For a quick test you can pass your token directly to a new client:
|
98
102
|
|
99
103
|
```ruby
|
100
|
-
client = OpenAI::Client.new(
|
104
|
+
client = OpenAI::Client.new(
|
105
|
+
access_token: "access_token_goes_here",
|
106
|
+
log_errors: true # Highly recommended in development, so you can see what errors OpenAI is returning. Not recommended in production.
|
107
|
+
)
|
101
108
|
```
|
102
109
|
|
103
110
|
### With Config
|
@@ -106,8 +113,9 @@ For a more robust setup, you can configure the gem with your API keys, for examp
|
|
106
113
|
|
107
114
|
```ruby
|
108
115
|
OpenAI.configure do |config|
|
109
|
-
|
110
|
-
|
116
|
+
config.access_token = ENV.fetch("OPENAI_ACCESS_TOKEN")
|
117
|
+
config.organization_id = ENV.fetch("OPENAI_ORGANIZATION_ID") # Optional.
|
118
|
+
config.log_errors = true # Highly recommended in development, so you can see what errors OpenAI is returning. Not recommended in production.
|
111
119
|
end
|
112
120
|
```
|
113
121
|
|
@@ -146,6 +154,7 @@ or when configuring the gem:
|
|
146
154
|
```ruby
|
147
155
|
OpenAI.configure do |config|
|
148
156
|
config.access_token = ENV.fetch("OPENAI_ACCESS_TOKEN")
|
157
|
+
config.log_errors = true # Optional
|
149
158
|
config.organization_id = ENV.fetch("OPENAI_ORGANIZATION_ID") # Optional
|
150
159
|
config.uri_base = "https://oai.hconeai.com/" # Optional
|
151
160
|
config.request_timeout = 240 # Optional
|
@@ -166,7 +175,19 @@ client = OpenAI::Client.new(access_token: "access_token_goes_here")
|
|
166
175
|
client.add_headers("X-Proxy-TTL" => "43200")
|
167
176
|
```
|
168
177
|
|
169
|
-
####
|
178
|
+
#### Logging
|
179
|
+
|
180
|
+
##### Errors
|
181
|
+
|
182
|
+
By default, `ruby-openai` does not log any `Faraday::Error`s encountered while executing a network request to avoid leaking data (e.g. 400s, 500s, SSL errors and more - see [here](https://www.rubydoc.info/github/lostisland/faraday/Faraday/Error) for a complete list of subclasses of `Faraday::Error` and what can cause them).
|
183
|
+
|
184
|
+
If you would like to enable this functionality, you can set `log_errors` to `true` when configuring the client:
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
client = OpenAI::Client.new(log_errors: true)
|
188
|
+
```
|
189
|
+
|
190
|
+
##### Faraday middleware
|
170
191
|
|
171
192
|
You can pass [Faraday middleware](https://lostisland.github.io/faraday/#/middleware/index) to the client in a block, eg. to enable verbose logging with Ruby's [Logger](https://ruby-doc.org/3.2.2/stdlibs/logger/Logger.html):
|
172
193
|
|
@@ -191,6 +212,59 @@ To use the [Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/cognit
|
|
191
212
|
|
192
213
|
where `AZURE_OPENAI_URI` is e.g. `https://custom-domain.openai.azure.com/openai/deployments/gpt-35-turbo`
|
193
214
|
|
215
|
+
#### Ollama
|
216
|
+
|
217
|
+
Ollama allows you to run open-source LLMs, such as Llama 3, locally. It [offers chat compatibility](https://github.com/ollama/ollama/blob/main/docs/openai.md) with the OpenAI API.
|
218
|
+
|
219
|
+
You can download Ollama [here](https://ollama.com/). On macOS you can install and run Ollama like this:
|
220
|
+
|
221
|
+
```bash
|
222
|
+
brew install ollama
|
223
|
+
ollama serve
|
224
|
+
ollama pull llama3:latest # In new terminal tab.
|
225
|
+
```
|
226
|
+
|
227
|
+
Create a client using your Ollama server and the pulled model, and stream a conversation for free:
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
client = OpenAI::Client.new(
|
231
|
+
uri_base: "http://localhost:11434"
|
232
|
+
)
|
233
|
+
|
234
|
+
client.chat(
|
235
|
+
parameters: {
|
236
|
+
model: "llama3", # Required.
|
237
|
+
messages: [{ role: "user", content: "Hello!"}], # Required.
|
238
|
+
temperature: 0.7,
|
239
|
+
stream: proc do |chunk, _bytesize|
|
240
|
+
print chunk.dig("choices", 0, "delta", "content")
|
241
|
+
end
|
242
|
+
})
|
243
|
+
|
244
|
+
# => Hi! It's nice to meet you. Is there something I can help you with, or would you like to chat?
|
245
|
+
```
|
246
|
+
|
247
|
+
#### Groq
|
248
|
+
|
249
|
+
[Groq API Chat](https://console.groq.com/docs/quickstart) is broadly compatible with the OpenAI API, with a [few minor differences](https://console.groq.com/docs/openai). Get an access token from [here](https://console.groq.com/keys), then:
|
250
|
+
|
251
|
+
```ruby
|
252
|
+
client = OpenAI::Client.new(
|
253
|
+
access_token: "groq_access_token_goes_here",
|
254
|
+
uri_base: "https://api.groq.com/"
|
255
|
+
)
|
256
|
+
|
257
|
+
client.chat(
|
258
|
+
parameters: {
|
259
|
+
model: "llama3-8b-8192", # Required.
|
260
|
+
messages: [{ role: "user", content: "Hello!"}], # Required.
|
261
|
+
temperature: 0.7,
|
262
|
+
stream: proc do |chunk, _bytesize|
|
263
|
+
print chunk.dig("choices", 0, "delta", "content")
|
264
|
+
end
|
265
|
+
})
|
266
|
+
```
|
267
|
+
|
194
268
|
### Counting Tokens
|
195
269
|
|
196
270
|
OpenAI parses prompt text into [tokens](https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them), which are words or portions of words. (These tokens are unrelated to your API access_token.) Counting tokens can help you estimate your [costs](https://openai.com/pricing). It can also help you ensure your prompt text size is within the max-token limits of your model's context window, and choose an appropriate [`max_tokens`](https://platform.openai.com/docs/api-reference/chat/create#chat/create-max_tokens) completion parameter so your response will fit as well.
|
@@ -199,7 +273,7 @@ To estimate the token-count of your text:
|
|
199
273
|
|
200
274
|
```ruby
|
201
275
|
OpenAI.rough_token_count("Your text")
|
202
|
-
|
276
|
+
````
|
203
277
|
|
204
278
|
If you need a more accurate count, try [tiktoken_ruby](https://github.com/IAPark/tiktoken_ruby).
|
205
279
|
|
@@ -209,24 +283,9 @@ There are different models that can be used to generate text. For a full list an
|
|
209
283
|
|
210
284
|
```ruby
|
211
285
|
client.models.list
|
212
|
-
client.models.retrieve(id: "
|
286
|
+
client.models.retrieve(id: "gpt-3.5-turbo")
|
213
287
|
```
|
214
288
|
|
215
|
-
#### Examples
|
216
|
-
|
217
|
-
- [GPT-4 (limited beta)](https://platform.openai.com/docs/models/gpt-4)
|
218
|
-
- gpt-4 (uses current version)
|
219
|
-
- gpt-4-0314
|
220
|
-
- gpt-4-32k
|
221
|
-
- [GPT-3.5](https://platform.openai.com/docs/models/gpt-3-5)
|
222
|
-
- gpt-3.5-turbo
|
223
|
-
- gpt-3.5-turbo-0301
|
224
|
-
- text-davinci-003
|
225
|
-
- [GPT-3](https://platform.openai.com/docs/models/gpt-3)
|
226
|
-
- text-ada-001
|
227
|
-
- text-babbage-001
|
228
|
-
- text-curie-001
|
229
|
-
|
230
289
|
### Chat
|
231
290
|
|
232
291
|
GPT is a model that can be used to generate text in a conversational style. You can use it to [generate a response](https://platform.openai.com/docs/api-reference/chat/create) to a sequence of [messages](https://platform.openai.com/docs/guides/chat/introduction):
|
@@ -287,12 +346,12 @@ puts response.dig("choices", 0, "message", "content")
|
|
287
346
|
|
288
347
|
#### JSON Mode
|
289
348
|
|
290
|
-
You can set the response_format to ask for responses in JSON
|
349
|
+
You can set the response_format to ask for responses in JSON:
|
291
350
|
|
292
351
|
```ruby
|
293
352
|
response = client.chat(
|
294
353
|
parameters: {
|
295
|
-
model: "gpt-3.5-turbo
|
354
|
+
model: "gpt-3.5-turbo",
|
296
355
|
response_format: { type: "json_object" },
|
297
356
|
messages: [{ role: "user", content: "Hello! Give me some JSON please."}],
|
298
357
|
temperature: 0.7,
|
@@ -312,7 +371,7 @@ You can stream it as well!
|
|
312
371
|
```ruby
|
313
372
|
response = client.chat(
|
314
373
|
parameters: {
|
315
|
-
model: "gpt-3.5-turbo
|
374
|
+
model: "gpt-3.5-turbo",
|
316
375
|
messages: [{ role: "user", content: "Can I have some JSON please?"}],
|
317
376
|
response_format: { type: "json_object" },
|
318
377
|
stream: proc do |chunk, _bytesize|
|
@@ -338,9 +397,10 @@ You can stream it as well!
|
|
338
397
|
|
339
398
|
### Functions
|
340
399
|
|
341
|
-
You can describe and pass in functions and the model will intelligently choose to output a JSON object containing arguments to call
|
400
|
+
You can describe and pass in functions and the model will intelligently choose to output a JSON object containing arguments to call them - eg., to use your method `get_current_weather` to get the weather in a given location. Note that tool_choice is optional, but if you exclude it, the model will choose whether to use the function or not ([see here](https://platform.openai.com/docs/api-reference/chat/create#chat-create-tool_choice)).
|
342
401
|
|
343
402
|
```ruby
|
403
|
+
|
344
404
|
def get_current_weather(location:, unit: "fahrenheit")
|
345
405
|
# use a weather api to fetch weather
|
346
406
|
end
|
@@ -348,7 +408,7 @@ end
|
|
348
408
|
response =
|
349
409
|
client.chat(
|
350
410
|
parameters: {
|
351
|
-
model: "gpt-3.5-turbo
|
411
|
+
model: "gpt-3.5-turbo",
|
352
412
|
messages: [
|
353
413
|
{
|
354
414
|
"role": "user",
|
@@ -378,16 +438,22 @@ response =
|
|
378
438
|
},
|
379
439
|
}
|
380
440
|
],
|
441
|
+
tool_choice: {
|
442
|
+
type: "function",
|
443
|
+
function: {
|
444
|
+
name: "get_current_weather"
|
445
|
+
}
|
446
|
+
}
|
381
447
|
},
|
382
448
|
)
|
383
449
|
|
384
450
|
message = response.dig("choices", 0, "message")
|
385
451
|
|
386
452
|
if message["role"] == "assistant" && message["tool_calls"]
|
387
|
-
function_name = message.dig("tool_calls", "function",
|
453
|
+
function_name = message.dig("tool_calls", 0, "function", "name")
|
388
454
|
args =
|
389
455
|
JSON.parse(
|
390
|
-
message.dig("tool_calls", "function", "arguments"),
|
456
|
+
message.dig("tool_calls", 0, "function", "arguments"),
|
391
457
|
{ symbolize_names: true },
|
392
458
|
)
|
393
459
|
|
@@ -406,7 +472,7 @@ Hit the OpenAI API for a completion using other GPT-3 models:
|
|
406
472
|
```ruby
|
407
473
|
response = client.completions(
|
408
474
|
parameters: {
|
409
|
-
model: "
|
475
|
+
model: "gpt-3.5-turbo",
|
410
476
|
prompt: "Once upon a time",
|
411
477
|
max_tokens: 5
|
412
478
|
})
|
@@ -414,22 +480,6 @@ puts response["choices"].map { |c| c["text"] }
|
|
414
480
|
# => [", there lived a great"]
|
415
481
|
```
|
416
482
|
|
417
|
-
### Edits
|
418
|
-
|
419
|
-
Send a string and some instructions for what to do to the string:
|
420
|
-
|
421
|
-
```ruby
|
422
|
-
response = client.edits(
|
423
|
-
parameters: {
|
424
|
-
model: "text-davinci-edit-001",
|
425
|
-
input: "What day of the wek is it?",
|
426
|
-
instruction: "Fix the spelling mistakes"
|
427
|
-
}
|
428
|
-
)
|
429
|
-
puts response.dig("choices", 0, "text")
|
430
|
-
# => What day of the week is it?
|
431
|
-
```
|
432
|
-
|
433
483
|
### Embeddings
|
434
484
|
|
435
485
|
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.
|
@@ -446,6 +496,94 @@ puts response.dig("data", 0, "embedding")
|
|
446
496
|
# => Vector representation of your embedding
|
447
497
|
```
|
448
498
|
|
499
|
+
### Batches
|
500
|
+
|
501
|
+
The Batches endpoint allows you to create and manage large batches of API requests to run asynchronously. Currently, only the `/v1/chat/completions` endpoint is supported for batches.
|
502
|
+
|
503
|
+
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:
|
504
|
+
|
505
|
+
```json
|
506
|
+
{
|
507
|
+
"custom_id": "request-1",
|
508
|
+
"method": "POST",
|
509
|
+
"url": "/v1/chat/completions",
|
510
|
+
"body": {
|
511
|
+
"model": "gpt-3.5-turbo",
|
512
|
+
"messages": [
|
513
|
+
{ "role": "system", "content": "You are a helpful assistant." },
|
514
|
+
{ "role": "user", "content": "What is 2+2?" }
|
515
|
+
]
|
516
|
+
}
|
517
|
+
}
|
518
|
+
```
|
519
|
+
|
520
|
+
Once you have uploaded the JSONL file, you can create a new batch by providing the file ID, endpoint, and completion window:
|
521
|
+
|
522
|
+
```ruby
|
523
|
+
response = client.batches.create(
|
524
|
+
parameters: {
|
525
|
+
input_file_id: "file-abc123",
|
526
|
+
endpoint: "/v1/chat/completions",
|
527
|
+
completion_window: "24h"
|
528
|
+
}
|
529
|
+
)
|
530
|
+
batch_id = response["id"]
|
531
|
+
```
|
532
|
+
|
533
|
+
You can retrieve information about a specific batch using its ID:
|
534
|
+
|
535
|
+
```ruby
|
536
|
+
batch = client.batches.retrieve(id: batch_id)
|
537
|
+
```
|
538
|
+
|
539
|
+
To cancel a batch that is in progress:
|
540
|
+
|
541
|
+
```ruby
|
542
|
+
client.batches.cancel(id: batch_id)
|
543
|
+
```
|
544
|
+
|
545
|
+
You can also list all the batches:
|
546
|
+
|
547
|
+
```ruby
|
548
|
+
client.batches.list
|
549
|
+
```
|
550
|
+
|
551
|
+
Once the batch["completed_at"] is present, you can fetch the output or error files:
|
552
|
+
|
553
|
+
```ruby
|
554
|
+
batch = client.batches.retrieve(id: batch_id)
|
555
|
+
output_file_id = batch["output_file_id"]
|
556
|
+
output_response = client.files.content(id: output_file_id)
|
557
|
+
error_file_id = batch["error_file_id"]
|
558
|
+
error_response = client.files.content(id: error_file_id)
|
559
|
+
```
|
560
|
+
|
561
|
+
These files are in JSONL format, with each line representing the output or error for a single request. The lines can be in any order:
|
562
|
+
|
563
|
+
```json
|
564
|
+
{
|
565
|
+
"id": "response-1",
|
566
|
+
"custom_id": "request-1",
|
567
|
+
"response": {
|
568
|
+
"id": "chatcmpl-abc123",
|
569
|
+
"object": "chat.completion",
|
570
|
+
"created": 1677858242,
|
571
|
+
"model": "gpt-3.5-turbo",
|
572
|
+
"choices": [
|
573
|
+
{
|
574
|
+
"index": 0,
|
575
|
+
"message": {
|
576
|
+
"role": "assistant",
|
577
|
+
"content": "2+2 equals 4."
|
578
|
+
}
|
579
|
+
}
|
580
|
+
]
|
581
|
+
}
|
582
|
+
}
|
583
|
+
```
|
584
|
+
|
585
|
+
If a request fails with a non-HTTP error, the error object will contain more information about the cause of the failure.
|
586
|
+
|
449
587
|
### Files
|
450
588
|
|
451
589
|
Put your data in a `.jsonl` file like this:
|
@@ -455,7 +593,7 @@ Put your data in a `.jsonl` file like this:
|
|
455
593
|
{"prompt":"@lakers disappoint for a third straight night ->", "completion":" negative"}
|
456
594
|
```
|
457
595
|
|
458
|
-
and pass the path to `client.files.upload` to upload it to OpenAI, and then interact with it:
|
596
|
+
and pass the path (or a StringIO object) to `client.files.upload` to upload it to OpenAI, and then interact with it:
|
459
597
|
|
460
598
|
```ruby
|
461
599
|
client.files.upload(parameters: { file: "path/to/sentiment.jsonl", purpose: "fine-tune" })
|
@@ -480,7 +618,7 @@ You can then use this file ID to create a fine tuning job:
|
|
480
618
|
response = client.finetunes.create(
|
481
619
|
parameters: {
|
482
620
|
training_file: file_id,
|
483
|
-
model: "gpt-3.5-turbo
|
621
|
+
model: "gpt-3.5-turbo"
|
484
622
|
})
|
485
623
|
fine_tune_id = response["id"]
|
486
624
|
```
|
@@ -519,23 +657,26 @@ client.finetunes.list_events(id: fine_tune_id)
|
|
519
657
|
|
520
658
|
### Assistants
|
521
659
|
|
522
|
-
Assistants
|
660
|
+
Assistants are stateful actors that can have many conversations and use tools to perform tasks (see [Assistant Overview](https://platform.openai.com/docs/assistants/overview)).
|
523
661
|
|
524
|
-
To create a new assistant
|
662
|
+
To create a new assistant:
|
525
663
|
|
526
664
|
```ruby
|
527
665
|
response = client.assistants.create(
|
528
666
|
parameters: {
|
529
|
-
model: "gpt-3.5-turbo
|
667
|
+
model: "gpt-3.5-turbo",
|
530
668
|
name: "OpenAI-Ruby test assistant",
|
531
669
|
description: nil,
|
532
|
-
instructions: "You are a
|
670
|
+
instructions: "You are a Ruby dev bot. When asked a question, write and run Ruby code to answer the question",
|
533
671
|
tools: [
|
534
|
-
{ type:
|
535
|
-
{ type: 'code_interpreter' }, # Allow access to Python code interpreter
|
672
|
+
{ type: "code_interpreter" },
|
536
673
|
],
|
537
|
-
|
538
|
-
|
674
|
+
tool_resources: {
|
675
|
+
"code_interpreter": {
|
676
|
+
"file_ids": [] # See Files section above for how to upload files
|
677
|
+
}
|
678
|
+
},
|
679
|
+
"metadata": { my_internal_version_id: "1.0.0" }
|
539
680
|
})
|
540
681
|
assistant_id = response["id"]
|
541
682
|
```
|
@@ -605,17 +746,36 @@ client.messages.retrieve(thread_id: thread_id, id: message_id) # -> Fails after
|
|
605
746
|
|
606
747
|
### Runs
|
607
748
|
|
608
|
-
To submit a thread to be evaluated with the model of an assistant, create a `Run` as follows
|
749
|
+
To submit a thread to be evaluated with the model of an assistant, create a `Run` as follows:
|
609
750
|
|
610
751
|
```ruby
|
611
752
|
# Create run (will use instruction/model/tools from Assistant's definition)
|
612
753
|
response = client.runs.create(thread_id: thread_id,
|
613
754
|
parameters: {
|
614
|
-
assistant_id: assistant_id
|
755
|
+
assistant_id: assistant_id,
|
756
|
+
max_prompt_tokens: 256,
|
757
|
+
max_completion_tokens: 16
|
615
758
|
})
|
616
759
|
run_id = response['id']
|
760
|
+
```
|
761
|
+
|
762
|
+
You can stream the message chunks as they come through:
|
617
763
|
|
618
|
-
|
764
|
+
```ruby
|
765
|
+
client.runs.create(thread_id: thread_id,
|
766
|
+
parameters: {
|
767
|
+
assistant_id: assistant_id,
|
768
|
+
max_prompt_tokens: 256,
|
769
|
+
max_completion_tokens: 16,
|
770
|
+
stream: proc do |chunk, _bytesize|
|
771
|
+
print chunk.dig("delta", "content", 0, "text", "value") if chunk["object"] == "thread.message.delta"
|
772
|
+
end
|
773
|
+
})
|
774
|
+
```
|
775
|
+
|
776
|
+
To get the status of a Run:
|
777
|
+
|
778
|
+
```
|
619
779
|
response = client.runs.retrieve(id: run_id, thread_id: thread_id)
|
620
780
|
status = response['status']
|
621
781
|
```
|
@@ -624,23 +784,22 @@ The `status` response can include the following strings `queued`, `in_progress`,
|
|
624
784
|
|
625
785
|
```ruby
|
626
786
|
while true do
|
627
|
-
|
628
787
|
response = client.runs.retrieve(id: run_id, thread_id: thread_id)
|
629
788
|
status = response['status']
|
630
789
|
|
631
790
|
case status
|
632
791
|
when 'queued', 'in_progress', 'cancelling'
|
633
|
-
|
634
|
-
|
792
|
+
puts 'Sleeping'
|
793
|
+
sleep 1 # Wait one second and poll again
|
635
794
|
when 'completed'
|
636
|
-
|
795
|
+
break # Exit loop and report result to user
|
637
796
|
when 'requires_action'
|
638
|
-
|
797
|
+
# Handle tool calls (see below)
|
639
798
|
when 'cancelled', 'failed', 'expired'
|
640
|
-
|
641
|
-
|
799
|
+
puts response['last_error'].inspect
|
800
|
+
break # or `exit`
|
642
801
|
else
|
643
|
-
|
802
|
+
puts "Unknown status response: #{status}"
|
644
803
|
end
|
645
804
|
end
|
646
805
|
```
|
@@ -649,10 +808,10 @@ If the `status` response indicates that the `run` is `completed`, the associated
|
|
649
808
|
|
650
809
|
```ruby
|
651
810
|
# Either retrieve all messages in bulk again, or...
|
652
|
-
messages = client.messages.list(thread_id: thread_id
|
811
|
+
messages = client.messages.list(thread_id: thread_id, parameters: { order: 'asc' })
|
653
812
|
|
654
813
|
# Alternatively retrieve the `run steps` for the run which link to the messages:
|
655
|
-
run_steps = client.run_steps.list(thread_id: thread_id, run_id: run_id)
|
814
|
+
run_steps = client.run_steps.list(thread_id: thread_id, run_id: run_id, parameters: { order: 'asc' })
|
656
815
|
new_message_ids = run_steps['data'].filter_map { |step|
|
657
816
|
if step['type'] == 'message_creation'
|
658
817
|
step.dig('step_details', "message_creation", "message_id")
|
@@ -679,10 +838,29 @@ new_messages.each { |msg|
|
|
679
838
|
}
|
680
839
|
```
|
681
840
|
|
682
|
-
|
841
|
+
You can also update the metadata on messages, including messages that come from the assistant.
|
683
842
|
|
684
843
|
```ruby
|
685
|
-
|
844
|
+
metadata = {
|
845
|
+
user_id: "abc123"
|
846
|
+
}
|
847
|
+
message = client.messages.modify(id: message_id, thread_id: thread_id, parameters: { metadata: metadata })
|
848
|
+
```
|
849
|
+
|
850
|
+
At any time you can list all runs which have been performed on a particular thread or are currently running:
|
851
|
+
|
852
|
+
```ruby
|
853
|
+
client.runs.list(thread_id: thread_id, parameters: { order: "asc", limit: 3 })
|
854
|
+
```
|
855
|
+
|
856
|
+
#### Create and Run
|
857
|
+
|
858
|
+
You can also create a thread and run in one call like this:
|
859
|
+
|
860
|
+
```ruby
|
861
|
+
response = client.runs.create_thread_and_run(parameters: { assistant_id: assistant_id })
|
862
|
+
run_id = response['id']
|
863
|
+
thread_id = response['thread_id']
|
686
864
|
```
|
687
865
|
|
688
866
|
#### Runs involving function tools
|
@@ -746,7 +924,7 @@ puts response.dig("data", 0, "url")
|
|
746
924
|
For DALL·E 3 the size of any generated images must be one of `1024x1024`, `1024x1792` or `1792x1024`. Additionally the quality of the image can be specified to either `standard` or `hd`.
|
747
925
|
|
748
926
|
```ruby
|
749
|
-
response = client.images.generate(parameters: { prompt: "A springer spaniel cooking pasta wearing a hat of some sort", size: "1024x1792", quality: "standard" })
|
927
|
+
response = client.images.generate(parameters: { prompt: "A springer spaniel cooking pasta wearing a hat of some sort", model: "dall-e-3", size: "1024x1792", quality: "standard" })
|
750
928
|
puts response.dig("data", 0, "url")
|
751
929
|
# => "https://oaidalleapiprodscus.blob.core.windows.net/private/org-Rf437IxKhh..."
|
752
930
|
```
|
@@ -845,7 +1023,7 @@ HTTP errors can be caught like this:
|
|
845
1023
|
|
846
1024
|
```
|
847
1025
|
begin
|
848
|
-
OpenAI::Client.new.models.retrieve(id: "
|
1026
|
+
OpenAI::Client.new.models.retrieve(id: "gpt-3.5-turbo")
|
849
1027
|
rescue Faraday::Error => e
|
850
1028
|
raise "Got a Faraday error: #{e}"
|
851
1029
|
end
|
data/lib/openai/assistants.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
module OpenAI
|
2
|
+
class Batches
|
3
|
+
def initialize(client:)
|
4
|
+
@client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION)
|
5
|
+
end
|
6
|
+
|
7
|
+
def list
|
8
|
+
@client.get(path: "/batches")
|
9
|
+
end
|
10
|
+
|
11
|
+
def retrieve(id:)
|
12
|
+
@client.get(path: "/batches/#{id}")
|
13
|
+
end
|
14
|
+
|
15
|
+
def create(parameters: {})
|
16
|
+
@client.json_post(path: "/batches", parameters: parameters)
|
17
|
+
end
|
18
|
+
|
19
|
+
def cancel(id:)
|
20
|
+
@client.post(path: "/batches/#{id}/cancel")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/openai/client.rb
CHANGED
@@ -6,6 +6,7 @@ module OpenAI
|
|
6
6
|
api_type
|
7
7
|
api_version
|
8
8
|
access_token
|
9
|
+
log_errors
|
9
10
|
organization_id
|
10
11
|
uri_base
|
11
12
|
request_timeout
|
@@ -17,7 +18,10 @@ module OpenAI
|
|
17
18
|
CONFIG_KEYS.each do |key|
|
18
19
|
# Set instance variables like api_type & access_token. Fall back to global config
|
19
20
|
# if not present.
|
20
|
-
instance_variable_set(
|
21
|
+
instance_variable_set(
|
22
|
+
"@#{key}",
|
23
|
+
config[key].nil? ? OpenAI.configuration.send(key) : config[key]
|
24
|
+
)
|
21
25
|
end
|
22
26
|
@faraday_middleware = faraday_middleware
|
23
27
|
end
|
@@ -26,10 +30,6 @@ module OpenAI
|
|
26
30
|
json_post(path: "/chat/completions", parameters: parameters)
|
27
31
|
end
|
28
32
|
|
29
|
-
def edits(parameters: {})
|
30
|
-
json_post(path: "/edits", parameters: parameters)
|
31
|
-
end
|
32
|
-
|
33
33
|
def embeddings(parameters: {})
|
34
34
|
json_post(path: "/embeddings", parameters: parameters)
|
35
35
|
end
|
@@ -78,6 +78,10 @@ module OpenAI
|
|
78
78
|
@run_steps ||= OpenAI::RunSteps.new(client: self)
|
79
79
|
end
|
80
80
|
|
81
|
+
def batches
|
82
|
+
@batches ||= OpenAI::Batches.new(client: self)
|
83
|
+
end
|
84
|
+
|
81
85
|
def moderations(parameters: {})
|
82
86
|
json_post(path: "/moderations", parameters: parameters)
|
83
87
|
end
|
data/lib/openai/files.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
module OpenAI
|
2
2
|
class Files
|
3
|
+
PURPOSES = %w[
|
4
|
+
assistants
|
5
|
+
batch
|
6
|
+
fine-tune
|
7
|
+
].freeze
|
8
|
+
|
3
9
|
def initialize(client:)
|
4
10
|
@client = client
|
5
11
|
end
|
@@ -9,12 +15,17 @@ module OpenAI
|
|
9
15
|
end
|
10
16
|
|
11
17
|
def upload(parameters: {})
|
12
|
-
|
18
|
+
file_input = parameters[:file]
|
19
|
+
file = prepare_file_input(file_input: file_input)
|
20
|
+
|
21
|
+
validate(file: file, purpose: parameters[:purpose], file_input: file_input)
|
13
22
|
|
14
23
|
@client.multipart_post(
|
15
24
|
path: "/files",
|
16
|
-
parameters: parameters.merge(file:
|
25
|
+
parameters: parameters.merge(file: file)
|
17
26
|
)
|
27
|
+
ensure
|
28
|
+
file.close if file.is_a?(File)
|
18
29
|
end
|
19
30
|
|
20
31
|
def retrieve(id:)
|
@@ -31,12 +42,33 @@ module OpenAI
|
|
31
42
|
|
32
43
|
private
|
33
44
|
|
34
|
-
def
|
35
|
-
|
45
|
+
def prepare_file_input(file_input:)
|
46
|
+
if file_input.is_a?(String)
|
47
|
+
File.open(file_input)
|
48
|
+
elsif file_input.respond_to?(:read) && file_input.respond_to?(:rewind)
|
49
|
+
file_input
|
50
|
+
else
|
51
|
+
raise ArgumentError, "Invalid file - must be a StringIO object or a path to a file."
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate(file:, purpose:, file_input:)
|
56
|
+
raise ArgumentError, "`file` is required" if file.nil?
|
57
|
+
unless PURPOSES.include?(purpose)
|
58
|
+
raise ArgumentError, "`purpose` must be one of `#{PURPOSES.join(',')}`"
|
59
|
+
end
|
60
|
+
|
61
|
+
validate_jsonl(file: file) if file_input.is_a?(String) && file_input.end_with?(".jsonl")
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_jsonl(file:)
|
65
|
+
file.each_line.with_index do |line, index|
|
36
66
|
JSON.parse(line)
|
37
67
|
rescue JSON::ParserError => e
|
38
68
|
raise JSON::ParserError, "#{e.message} - found on line #{index + 1} of #{file}"
|
39
69
|
end
|
70
|
+
ensure
|
71
|
+
file.rewind
|
40
72
|
end
|
41
73
|
end
|
42
74
|
end
|
data/lib/openai/http.rb
CHANGED
@@ -6,8 +6,8 @@ module OpenAI
|
|
6
6
|
module HTTP
|
7
7
|
include HTTPHeaders
|
8
8
|
|
9
|
-
def get(path:)
|
10
|
-
parse_jsonl(conn.get(uri(path: path)) do |req|
|
9
|
+
def get(path:, parameters: nil)
|
10
|
+
parse_jsonl(conn.get(uri(path: path), parameters) do |req|
|
11
11
|
req.headers = headers
|
12
12
|
end&.body)
|
13
13
|
end
|
@@ -74,7 +74,7 @@ module OpenAI
|
|
74
74
|
connection = Faraday.new do |f|
|
75
75
|
f.options[:timeout] = @request_timeout
|
76
76
|
f.request(:multipart) if multipart
|
77
|
-
f.use MiddlewareErrors
|
77
|
+
f.use MiddlewareErrors if @log_errors
|
78
78
|
f.response :raise_error
|
79
79
|
f.response :json
|
80
80
|
end
|
@@ -88,6 +88,8 @@ module OpenAI
|
|
88
88
|
if azure?
|
89
89
|
base = File.join(@uri_base, path)
|
90
90
|
"#{base}?api-version=#{@api_version}"
|
91
|
+
elsif @uri_base.include?(@api_version)
|
92
|
+
File.join(@uri_base, path)
|
91
93
|
else
|
92
94
|
File.join(@uri_base, @api_version, path)
|
93
95
|
end
|
@@ -97,10 +99,14 @@ module OpenAI
|
|
97
99
|
parameters&.transform_values do |value|
|
98
100
|
next value unless value.respond_to?(:close) # File or IO object.
|
99
101
|
|
102
|
+
# Faraday::UploadIO does not require a path, so we will pass it
|
103
|
+
# only if it is available. This allows StringIO objects to be
|
104
|
+
# passed in as well.
|
105
|
+
path = value.respond_to?(:path) ? value.path : nil
|
100
106
|
# Doesn't seem like OpenAI needs mime_type yet, so not worth
|
101
107
|
# the library to figure this out. Hence the empty string
|
102
108
|
# as the second argument.
|
103
|
-
Faraday::UploadIO.new(value, "",
|
109
|
+
Faraday::UploadIO.new(value, "", path)
|
104
110
|
end
|
105
111
|
end
|
106
112
|
|
data/lib/openai/messages.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module OpenAI
|
2
2
|
class Messages
|
3
3
|
def initialize(client:)
|
4
|
-
@client = client.beta(assistants:
|
4
|
+
@client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION)
|
5
5
|
end
|
6
6
|
|
7
|
-
def list(thread_id:)
|
8
|
-
@client.get(path: "/threads/#{thread_id}/messages")
|
7
|
+
def list(thread_id:, parameters: {})
|
8
|
+
@client.get(path: "/threads/#{thread_id}/messages", parameters: parameters)
|
9
9
|
end
|
10
10
|
|
11
11
|
def retrieve(thread_id:, id:)
|
data/lib/openai/run_steps.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module OpenAI
|
2
2
|
class RunSteps
|
3
3
|
def initialize(client:)
|
4
|
-
@client = client.beta(assistants:
|
4
|
+
@client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION)
|
5
5
|
end
|
6
6
|
|
7
|
-
def list(thread_id:, run_id:)
|
8
|
-
@client.get(path: "/threads/#{thread_id}/runs/#{run_id}/steps")
|
7
|
+
def list(thread_id:, run_id:, parameters: {})
|
8
|
+
@client.get(path: "/threads/#{thread_id}/runs/#{run_id}/steps", parameters: parameters)
|
9
9
|
end
|
10
10
|
|
11
11
|
def retrieve(thread_id:, run_id:, id:)
|
data/lib/openai/runs.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module OpenAI
|
2
2
|
class Runs
|
3
3
|
def initialize(client:)
|
4
|
-
@client = client.beta(assistants:
|
4
|
+
@client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION)
|
5
5
|
end
|
6
6
|
|
7
|
-
def list(thread_id:)
|
8
|
-
@client.get(path: "/threads/#{thread_id}/runs")
|
7
|
+
def list(thread_id:, parameters: {})
|
8
|
+
@client.get(path: "/threads/#{thread_id}/runs", parameters: parameters)
|
9
9
|
end
|
10
10
|
|
11
11
|
def retrieve(thread_id:, id:)
|
@@ -24,6 +24,10 @@ module OpenAI
|
|
24
24
|
@client.post(path: "/threads/#{thread_id}/runs/#{id}/cancel")
|
25
25
|
end
|
26
26
|
|
27
|
+
def create_thread_and_run(parameters: {})
|
28
|
+
@client.json_post(path: "/threads/runs", parameters: parameters)
|
29
|
+
end
|
30
|
+
|
27
31
|
def submit_tool_outputs(thread_id:, run_id:, parameters: {})
|
28
32
|
@client.json_post(path: "/threads/#{thread_id}/runs/#{run_id}/submit_tool_outputs",
|
29
33
|
parameters: parameters)
|
data/lib/openai/threads.rb
CHANGED
data/lib/openai/version.rb
CHANGED
data/lib/openai.rb
CHANGED
@@ -14,6 +14,7 @@ require_relative "openai/runs"
|
|
14
14
|
require_relative "openai/run_steps"
|
15
15
|
require_relative "openai/audio"
|
16
16
|
require_relative "openai/version"
|
17
|
+
require_relative "openai/batches"
|
17
18
|
|
18
19
|
module OpenAI
|
19
20
|
class Error < StandardError; end
|
@@ -36,30 +37,30 @@ module OpenAI
|
|
36
37
|
end
|
37
38
|
|
38
39
|
class Configuration
|
39
|
-
|
40
|
-
|
40
|
+
attr_accessor :access_token,
|
41
|
+
:api_type,
|
42
|
+
:api_version,
|
43
|
+
:log_errors,
|
44
|
+
:organization_id,
|
45
|
+
:uri_base,
|
46
|
+
:request_timeout,
|
41
47
|
:extra_headers
|
42
48
|
|
43
49
|
DEFAULT_API_VERSION = "v1".freeze
|
44
50
|
DEFAULT_URI_BASE = "https://api.openai.com/".freeze
|
45
51
|
DEFAULT_REQUEST_TIMEOUT = 120
|
52
|
+
DEFAULT_LOG_ERRORS = false
|
46
53
|
|
47
54
|
def initialize
|
48
55
|
@access_token = nil
|
49
56
|
@api_type = nil
|
50
57
|
@api_version = DEFAULT_API_VERSION
|
58
|
+
@log_errors = DEFAULT_LOG_ERRORS
|
51
59
|
@organization_id = nil
|
52
60
|
@uri_base = DEFAULT_URI_BASE
|
53
61
|
@request_timeout = DEFAULT_REQUEST_TIMEOUT
|
54
62
|
@extra_headers = {}
|
55
63
|
end
|
56
|
-
|
57
|
-
def access_token
|
58
|
-
return @access_token if @access_token
|
59
|
-
|
60
|
-
error_text = "OpenAI access token missing! See https://github.com/alexrudall/ruby-openai#usage"
|
61
|
-
raise ConfigurationError, error_text
|
62
|
-
end
|
63
64
|
end
|
64
65
|
|
65
66
|
class << self
|
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")
|
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
|
+
version: 7.0.1
|
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-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: event_stream_parser
|
@@ -89,6 +89,7 @@ files:
|
|
89
89
|
- lib/openai.rb
|
90
90
|
- lib/openai/assistants.rb
|
91
91
|
- lib/openai/audio.rb
|
92
|
+
- lib/openai/batches.rb
|
92
93
|
- lib/openai/client.rb
|
93
94
|
- lib/openai/compatibility.rb
|
94
95
|
- lib/openai/files.rb
|
@@ -129,8 +130,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
130
|
- !ruby/object:Gem::Version
|
130
131
|
version: '0'
|
131
132
|
requirements: []
|
132
|
-
rubygems_version: 3.
|
133
|
+
rubygems_version: 3.5.9
|
133
134
|
signing_key:
|
134
135
|
specification_version: 4
|
135
|
-
summary: "OpenAI API + Ruby! \U0001F916
|
136
|
+
summary: "OpenAI API + Ruby! \U0001F916❤️"
|
136
137
|
test_files: []
|