openai.rb 0.0.0 → 0.0.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/.ruby-version +1 -1
- data/Gemfile +4 -4
- data/Gemfile.lock +17 -14
- data/README.md +401 -0
- data/bin/codegen +64 -55
- data/bin/console +7 -1
- data/lib/openai/api/cache.rb +137 -0
- data/lib/openai/api/client.rb +86 -0
- data/lib/openai/api/resource.rb +235 -0
- data/lib/openai/api/response.rb +352 -0
- data/lib/openai/api.rb +61 -0
- data/lib/openai/chat.rb +75 -0
- data/lib/openai/tokenizer.rb +50 -0
- data/lib/openai/version.rb +1 -1
- data/lib/openai.rb +29 -358
- data/openai.gemspec +7 -3
- data/spec/data/sample_french.mp3 +0 -0
- data/spec/data/sample_image.png +0 -0
- data/spec/data/sample_image_mask.png +0 -0
- data/spec/shared/api_resource_context.rb +22 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/unit/openai/api/audio_spec.rb +78 -0
- data/spec/unit/openai/api/cache_spec.rb +115 -0
- data/spec/unit/openai/api/chat_completions_spec.rb +116 -0
- data/spec/unit/openai/api/completions_spec.rb +119 -0
- data/spec/unit/openai/api/edits_spec.rb +40 -0
- data/spec/unit/openai/api/embeddings_spec.rb +45 -0
- data/spec/unit/openai/api/files_spec.rb +163 -0
- data/spec/unit/openai/api/fine_tunes_spec.rb +322 -0
- data/spec/unit/openai/api/images_spec.rb +137 -0
- data/spec/unit/openai/api/models_spec.rb +98 -0
- data/spec/unit/openai/api/moderations_spec.rb +61 -0
- data/spec/unit/openai/api/response_spec.rb +203 -0
- data/spec/unit/openai/tokenizer_spec.rb +45 -0
- data/spec/unit/openai_spec.rb +47 -736
- metadata +83 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4cc9dca4191bdbae2964d7389c67e7d43ce756481c66c5059721f0ff2404c16
|
4
|
+
data.tar.gz: 727d12a574d750d050073547699aa0b837e96982e3c660ded4be9434d0bacc80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d5415f3d67872d7b57bed8e1f8101873aee5b80559c8079c6ee7b450517a913b1d6f637bc5bae70ecb5ad9207d065f22459db699d65bf3d9217e124079dc32d
|
7
|
+
data.tar.gz: d4d6364fbcaf8534e2743f3f6056285731642474d8006a436c43cac0bb011c3c5fe25d75410649d8fe18ed85a634dca5fcf41bfba8dfefe9da595d1d44dd1e79
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.7.7
|
data/Gemfile
CHANGED
@@ -11,13 +11,13 @@ group :test do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
group :lint do
|
14
|
-
gem 'rubocop'
|
15
|
-
gem 'rubocop-rspec'
|
14
|
+
gem 'rubocop', '~> 1.31.1'
|
15
|
+
gem 'rubocop-rspec', '~> 2.11.1'
|
16
16
|
end
|
17
17
|
|
18
18
|
gem 'pry', '~> 0.13.1'
|
19
19
|
gem 'pry-byebug', '~> 3.9'
|
20
20
|
|
21
|
-
gem
|
21
|
+
gem 'dotenv', '~> 2.8'
|
22
22
|
|
23
|
-
gem
|
23
|
+
gem 'slop', '~> 4.10'
|
data/Gemfile.lock
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
openai.rb (0.0.
|
4
|
+
openai.rb (0.0.1)
|
5
|
+
abstract_type (~> 0.0.7)
|
5
6
|
anima (~> 0.3)
|
6
7
|
concord (~> 0.1)
|
7
8
|
http (~> 5.1)
|
9
|
+
ice_nine (~> 0.11.x)
|
10
|
+
memoizable (~> 0.4.2)
|
11
|
+
tiktoken_ruby (~> 0.0.3)
|
8
12
|
|
9
13
|
GEM
|
10
14
|
remote: https://rubygems.org/
|
@@ -62,7 +66,7 @@ GEM
|
|
62
66
|
public_suffix (5.0.1)
|
63
67
|
rainbow (3.1.1)
|
64
68
|
rake (13.0.6)
|
65
|
-
regexp_parser (2.
|
69
|
+
regexp_parser (2.6.2)
|
66
70
|
rexml (3.2.5)
|
67
71
|
rspec (3.12.0)
|
68
72
|
rspec-core (~> 3.12.0)
|
@@ -77,26 +81,24 @@ GEM
|
|
77
81
|
diff-lcs (>= 1.2.0, < 2.0)
|
78
82
|
rspec-support (~> 3.12.0)
|
79
83
|
rspec-support (3.12.0)
|
80
|
-
rubocop (1.
|
84
|
+
rubocop (1.31.2)
|
81
85
|
json (~> 2.3)
|
82
86
|
parallel (~> 1.10)
|
83
|
-
parser (>= 3.
|
87
|
+
parser (>= 3.1.0.0)
|
84
88
|
rainbow (>= 2.2.2, < 4.0)
|
85
89
|
regexp_parser (>= 1.8, < 3.0)
|
86
90
|
rexml (>= 3.2.5, < 4.0)
|
87
|
-
rubocop-ast (>= 1.
|
91
|
+
rubocop-ast (>= 1.18.0, < 2.0)
|
88
92
|
ruby-progressbar (~> 1.7)
|
89
|
-
unicode-display_width (>=
|
93
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
90
94
|
rubocop-ast (1.28.0)
|
91
95
|
parser (>= 3.2.1.0)
|
92
|
-
rubocop-
|
93
|
-
rubocop (~> 1.
|
94
|
-
rubocop-rspec (2.19.0)
|
95
|
-
rubocop (~> 1.33)
|
96
|
-
rubocop-capybara (~> 2.17)
|
96
|
+
rubocop-rspec (2.11.1)
|
97
|
+
rubocop (~> 1.19)
|
97
98
|
ruby-progressbar (1.13.0)
|
98
99
|
slop (4.10.1)
|
99
100
|
thread_safe (0.3.6)
|
101
|
+
tiktoken_ruby (0.0.3-arm64-darwin)
|
100
102
|
unf (0.1.4)
|
101
103
|
unf_ext
|
102
104
|
unf_ext (0.0.8.2)
|
@@ -104,6 +106,7 @@ GEM
|
|
104
106
|
|
105
107
|
PLATFORMS
|
106
108
|
arm64-darwin-21
|
109
|
+
ruby
|
107
110
|
|
108
111
|
DEPENDENCIES
|
109
112
|
dotenv (~> 2.8)
|
@@ -111,12 +114,12 @@ DEPENDENCIES
|
|
111
114
|
pry (~> 0.13.1)
|
112
115
|
pry-byebug (~> 3.9)
|
113
116
|
rspec (~> 3.12)
|
114
|
-
rubocop
|
115
|
-
rubocop-rspec
|
117
|
+
rubocop (~> 1.31.1)
|
118
|
+
rubocop-rspec (~> 2.11.1)
|
116
119
|
slop (~> 4.10)
|
117
120
|
|
118
121
|
RUBY VERSION
|
119
|
-
ruby 2.
|
122
|
+
ruby 2.7.7p221
|
120
123
|
|
121
124
|
BUNDLED WITH
|
122
125
|
2.3.9
|
data/README.md
ADDED
@@ -0,0 +1,401 @@
|
|
1
|
+
# OpenAI.rb
|
2
|
+
|
3
|
+
A comprehensive (as of March 25th, 2023) OpenAI API wrapper with built-in support for:
|
4
|
+
|
5
|
+
* caching
|
6
|
+
* tokenization
|
7
|
+
* response streaming
|
8
|
+
* a simple chainable abstraction for chats
|
9
|
+
|
10
|
+
## Install and Setup
|
11
|
+
|
12
|
+
To install, you should be able to do:
|
13
|
+
|
14
|
+
```sh
|
15
|
+
$ gem install openai.rb
|
16
|
+
```
|
17
|
+
|
18
|
+
Usage:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
require 'openai'
|
22
|
+
|
23
|
+
openai = OpenAI.create(ENV.fetch('OPENAI_API_KEY'))
|
24
|
+
openai.api.chat_completions.create(...)
|
25
|
+
openai.api.embeddings.create(...)
|
26
|
+
openai.api.models.list
|
27
|
+
# etc
|
28
|
+
```
|
29
|
+
|
30
|
+
### Caching
|
31
|
+
|
32
|
+
Caching for requests is built-in. The supported caching strategy writes response files in a directory you
|
33
|
+
specify (I chose to write as separate files since I often want to dig around and see the raw data I'm getting
|
34
|
+
back).
|
35
|
+
|
36
|
+
To enable caching:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
require 'openai'
|
40
|
+
|
41
|
+
# This directory should already exist
|
42
|
+
cache_dir = Pathname.new('~/.cache/openai')
|
43
|
+
|
44
|
+
openai = OpenAI.create(ENV.fetch('OPENAI_API_KEY'), cache: cache_dir)
|
45
|
+
|
46
|
+
# Will hit the API:
|
47
|
+
openai.api.completions.create(model: 'text-davinci-002', prompt: 'Say hi')
|
48
|
+
# Will reuse the cached response:
|
49
|
+
openai.api.completions.create(model: 'text-davinci-002', prompt: 'Say hi')
|
50
|
+
```
|
51
|
+
|
52
|
+
NOTE: Delete requests are not cached
|
53
|
+
|
54
|
+
### Tokens
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
# Get encoder for a model and encode
|
58
|
+
openai.tokens.for_model('gpt-4').encode('Hello world')
|
59
|
+
|
60
|
+
# Get encoder by name
|
61
|
+
openai.tokens.get('cl100k_base').encode('Hello world')
|
62
|
+
|
63
|
+
# Get number of tokens
|
64
|
+
openai.tokens.for_model('gpt-4').num_tokens('Hello, world!') # => 4
|
65
|
+
```
|
66
|
+
|
67
|
+
### Chat Abstraction
|
68
|
+
|
69
|
+
You can use `openai.chat` in order to create a simple chainable chat interface with a model:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
openai = OpenAI.create(ENV.fetch('OPENAI_API_KEY'))
|
73
|
+
|
74
|
+
chat = openai.chat(model: 'gpt-3.5-turbo')
|
75
|
+
|
76
|
+
chat =
|
77
|
+
chat
|
78
|
+
.system('You are a chatbot that talks and acts like scooby doo.')
|
79
|
+
.user('Hi how are you doing today?')
|
80
|
+
.submit # API call
|
81
|
+
.user('Nice. What kind of snacks do you like?')
|
82
|
+
.submit # API call
|
83
|
+
|
84
|
+
puts chat.to_log_format
|
85
|
+
```
|
86
|
+
|
87
|
+
Which results in this output:
|
88
|
+
|
89
|
+
> SYSTEM: You are a chatbot that talks and acts like scooby doo.
|
90
|
+
>
|
91
|
+
> USER: Hi how are you doing today?
|
92
|
+
>
|
93
|
+
> ASSISTANT: Ruh-roh! Hello there, buddy! Scooby-Dooby-Doo is doing great! How about you, pal?
|
94
|
+
>
|
95
|
+
> USER: Nice. What kind of snacks do you like?
|
96
|
+
>
|
97
|
+
> ASSISTANT: Ruh-roh! Scooby-Dooby-Doo loves all kinds of snacks, especially Scooby Snacks! They are my favorite! But I also love bones, pizza, hamburgers, and any kind of food that's tasty and yummy. How about you, buddy? Do you have a favorite snack?
|
98
|
+
|
99
|
+
## API
|
100
|
+
|
101
|
+
### Audio
|
102
|
+
|
103
|
+
Transcribing audio:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
transcription = openai.api.audio.transcribe(
|
107
|
+
file: '/path/to/sample.mp3',
|
108
|
+
model: 'model-id'
|
109
|
+
)
|
110
|
+
|
111
|
+
transcription.text # => "Imagine the wildest idea that you've ever had..."
|
112
|
+
```
|
113
|
+
|
114
|
+
Translating audio:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
translation = openai.api.audio.translate(
|
118
|
+
file: '/path/to/french/sample.mp3',
|
119
|
+
model: 'model-id',
|
120
|
+
)
|
121
|
+
|
122
|
+
translation.text # => "Hello, my name is Wolfgang and I come from Germany. Where are you heading today?"
|
123
|
+
```
|
124
|
+
|
125
|
+
### Chat completions
|
126
|
+
|
127
|
+
Generating a chat completion:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
completion = openai.api.chat_completions.create(
|
131
|
+
model: 'gpt-3.5-turbo',
|
132
|
+
messages: [
|
133
|
+
{ role: "user", content: "Hello" }
|
134
|
+
]
|
135
|
+
)
|
136
|
+
|
137
|
+
completion.choices.first.message.content # => "\n\nHello there, how may I assist you today?"
|
138
|
+
```
|
139
|
+
|
140
|
+
Streaming chat completion responses:
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
completion = openai.api.chat_completions.create(
|
144
|
+
model: 'gpt-3.5-turbo',
|
145
|
+
messages: [{ role: "user", content: "Hello" }],
|
146
|
+
stream: true
|
147
|
+
) do |completion|
|
148
|
+
print completion.choices.first.delta.content
|
149
|
+
end
|
150
|
+
|
151
|
+
# >> "\n\nHello there, how may I assist you today?"
|
152
|
+
```
|
153
|
+
|
154
|
+
### Completions
|
155
|
+
|
156
|
+
Generating a completion:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
completion = openai.api.completions.create(
|
160
|
+
model: 'text-davinci-002',
|
161
|
+
prompt: 'Hello, world!'
|
162
|
+
)
|
163
|
+
|
164
|
+
completion.id # => "cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7"
|
165
|
+
completion.model # => "text-davinci-003"
|
166
|
+
completion.choices.first.text # => "\n\nThis is indeed a test"
|
167
|
+
```
|
168
|
+
|
169
|
+
Streaming responses:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
completion = openai.api.completions.create(
|
173
|
+
model: 'text-davinci-002',
|
174
|
+
prompt: 'Say hello world',
|
175
|
+
stream: true
|
176
|
+
) do |completion|
|
177
|
+
puts completion.choices.first.text
|
178
|
+
end
|
179
|
+
|
180
|
+
# >> "He"
|
181
|
+
# >> "llo,"
|
182
|
+
# >> " world"
|
183
|
+
# >> "!"
|
184
|
+
```
|
185
|
+
|
186
|
+
### Edits
|
187
|
+
|
188
|
+
Creating an edit:
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
edit = openai.api.edits.create(
|
192
|
+
model: 'text-davinci-002',
|
193
|
+
input: "What day of the wek is it?",
|
194
|
+
instruction: "Fix the spelling mistake"
|
195
|
+
)
|
196
|
+
|
197
|
+
edit.object # => "edit"
|
198
|
+
edit.choices.first.text # => "What day of the week is it?"
|
199
|
+
```
|
200
|
+
|
201
|
+
### Embeddings
|
202
|
+
|
203
|
+
Creating an embedding vector for a given input text:
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
embedding = openai.api.embeddings.create(
|
207
|
+
model: 'text-embedding-ada-002',
|
208
|
+
input: 'Hello, world!'
|
209
|
+
)
|
210
|
+
|
211
|
+
embedding.object # => 'list'
|
212
|
+
embedding.data.first.object # => 'embedding'
|
213
|
+
embedding.data.first.embedding.size # => 1536
|
214
|
+
```
|
215
|
+
|
216
|
+
### Files
|
217
|
+
|
218
|
+
Upload a file:
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
|
222
|
+
file = openai.api.files.create(
|
223
|
+
file: '/path/to/file.jsonl',
|
224
|
+
purpose: 'fine-tune'
|
225
|
+
)
|
226
|
+
|
227
|
+
file.id # => 'file-XjGxS3KTG0uNmNOK362iJua3'
|
228
|
+
file.filename # => 'sample.jsonl'
|
229
|
+
file.purpose # => 'fine-tune'
|
230
|
+
file.deleted? # => nil
|
231
|
+
```
|
232
|
+
|
233
|
+
Get a list of files:
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
|
237
|
+
files = openai.api.files.list
|
238
|
+
|
239
|
+
files.data.size # => 2
|
240
|
+
files.data.first.filename # => 'train.jsonl'
|
241
|
+
```
|
242
|
+
|
243
|
+
Fetch a specific file’s information:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
|
247
|
+
file = openai.api.files.fetch('file-XjGxS3KTG0uNmNOK362iJua3')
|
248
|
+
|
249
|
+
file.filename # => 'mydata.jsonl'
|
250
|
+
file.bytes # => 140
|
251
|
+
file.created_at # => 1613779657
|
252
|
+
file.purpose # => 'fine-tune'
|
253
|
+
file.object # => 'file'
|
254
|
+
```
|
255
|
+
|
256
|
+
Get the contents of a file:
|
257
|
+
|
258
|
+
```ruby
|
259
|
+
|
260
|
+
response = openai.api.files.get_content('file-XjGxS3KTG0uNmNOK362iJua3')
|
261
|
+
|
262
|
+
puts response # => (whatever you uploaded)
|
263
|
+
```
|
264
|
+
|
265
|
+
Delete a file:
|
266
|
+
|
267
|
+
```ruby
|
268
|
+
|
269
|
+
file = openai.api.files.delete('file-XjGxS3KTG0uNmNOK362iJua3')
|
270
|
+
|
271
|
+
file.deleted? # => true
|
272
|
+
```
|
273
|
+
|
274
|
+
### Fine-tunes
|
275
|
+
|
276
|
+
Creating a fine tune:
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
fine_tune = openai.api.fine_tunes.create(training_file: 'file-XGinujblHPwGLSztz8cPS8XY')
|
280
|
+
|
281
|
+
details.id # => "ft-AF1WoRqd3aJAHsqc9NY7iL8F"
|
282
|
+
```
|
283
|
+
|
284
|
+
Listing fine tunes:
|
285
|
+
|
286
|
+
```ruby
|
287
|
+
fine_tunes = openai.api.fine_tunes.list
|
288
|
+
|
289
|
+
fine_tunes.data.first.id # => "ft-AF1WoRqd3aJAHsqc9NY7iL8F"
|
290
|
+
fine_tunes.data.first.status # => "pending"
|
291
|
+
```
|
292
|
+
|
293
|
+
|
294
|
+
Fetching a fine tune:
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
fine_tune = openai.api.fine_tunes.fetch('ft-AF1WoRqd3aJAHsqc9NY7iL8F')
|
298
|
+
fine_tune.id # => "ft-AF1WoRqd3aJAHsqc9NY7iL8F"
|
299
|
+
```
|
300
|
+
|
301
|
+
Canceling a fine tune:
|
302
|
+
|
303
|
+
```ruby
|
304
|
+
|
305
|
+
# Canceling a fine tune
|
306
|
+
fine_tune = openai.api.fine_tunes.cancel('ft-xhrpBbvVUzYGo8oUO1FY4nI7')
|
307
|
+
fine_tune.id # => "ft-xhrpBbvVUzYGo8oUO1FY4nI7"
|
308
|
+
fine_tune.status # => "cancelled"
|
309
|
+
```
|
310
|
+
|
311
|
+
Listing fine tune events:
|
312
|
+
|
313
|
+
```ruby
|
314
|
+
events = openai.api.fine_tunes.list_events('fine-tune-id')
|
315
|
+
```
|
316
|
+
|
317
|
+
### Images
|
318
|
+
|
319
|
+
#### Generating Images
|
320
|
+
|
321
|
+
Create an image with the specified prompt and size:
|
322
|
+
|
323
|
+
```ruby
|
324
|
+
images = openai.api.images.create(prompt: 'a bird in the forest', size: '512x512')
|
325
|
+
|
326
|
+
images.data.first.url # => "https://example.com/image1.png"
|
327
|
+
```
|
328
|
+
|
329
|
+
#### Editing Images
|
330
|
+
|
331
|
+
Edit an image with the specified parameters:
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
response = openai.api.images.edit(
|
335
|
+
image: '/path/to/some_rgba.png',
|
336
|
+
mask: '/path/to/some_rgba_mask.png',
|
337
|
+
prompt: 'Draw a red hat on the person in the image',
|
338
|
+
n: 1,
|
339
|
+
size: '512x512',
|
340
|
+
response_format: 'url',
|
341
|
+
user: 'user-123'
|
342
|
+
)
|
343
|
+
|
344
|
+
response.created # => 1589478378
|
345
|
+
response.data.first.url # => "https://..."
|
346
|
+
```
|
347
|
+
|
348
|
+
#### Creating Image Variations
|
349
|
+
|
350
|
+
Create image variations of the specified image with the specified parameters:
|
351
|
+
|
352
|
+
```ruby
|
353
|
+
image_variations = openai.api.images.create_variation(
|
354
|
+
image: '/path/to/some_rgba.png',
|
355
|
+
n: 2,
|
356
|
+
size: '512x512',
|
357
|
+
response_format: 'url',
|
358
|
+
user: 'user123'
|
359
|
+
)
|
360
|
+
|
361
|
+
image_variations.created # => 1589478378
|
362
|
+
image_variations.data.map(&:url) # => ["https://...", "https://..."]
|
363
|
+
```
|
364
|
+
|
365
|
+
### Models
|
366
|
+
|
367
|
+
Listing all models:
|
368
|
+
|
369
|
+
```ruby
|
370
|
+
models = api.models.list
|
371
|
+
|
372
|
+
models.data.first.id # => "model-id-0"
|
373
|
+
models.data.size # => 3
|
374
|
+
```
|
375
|
+
|
376
|
+
Retrieving a model:
|
377
|
+
|
378
|
+
```ruby
|
379
|
+
model = api.models.fetch('text-davinci-002')
|
380
|
+
|
381
|
+
model.id # => "text-davinci-002"
|
382
|
+
model.object # => "model"
|
383
|
+
model.owned_by # => "openai"
|
384
|
+
model.permission # => ["query", "completions", "models:read", "models:write", "engine:read", "engine:write"]
|
385
|
+
```
|
386
|
+
|
387
|
+
### Moderations
|
388
|
+
|
389
|
+
Moderate text:
|
390
|
+
|
391
|
+
```ruby
|
392
|
+
moderation = openai.api.moderations.create(
|
393
|
+
input: 'This is a test',
|
394
|
+
model: 'text-moderation-001'
|
395
|
+
)
|
396
|
+
|
397
|
+
moderation.id # => "modr-5MWoLO"
|
398
|
+
moderation.model # => "text-moderation-001"
|
399
|
+
moderation.results.first.categories.hate # => false
|
400
|
+
moderation.results.first.categories.hate_threatening # => true
|
401
|
+
```
|
data/bin/codegen
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'bundler/setup'
|
4
5
|
require 'openai'
|
@@ -139,10 +140,12 @@ MSG
|
|
139
140
|
|
140
141
|
assistant_response_json_example = <<~RUBY
|
141
142
|
# api call
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
143
|
+
class Completion < self
|
144
|
+
def create(model:, **kwargs)
|
145
|
+
Response::Completion.from_json(
|
146
|
+
post('/v1/completions', model: model, **kwargs)
|
147
|
+
)
|
148
|
+
end
|
146
149
|
end
|
147
150
|
|
148
151
|
# wrapper
|
@@ -169,7 +172,11 @@ assistant_response_json_example = <<~RUBY
|
|
169
172
|
end
|
170
173
|
|
171
174
|
# test
|
172
|
-
describe '#
|
175
|
+
RSpec.describe OpenAI::API, '#completions' do
|
176
|
+
include_context 'an API Resource'
|
177
|
+
|
178
|
+
let(:resource) { api.completions }
|
179
|
+
|
173
180
|
let(:response_body) do
|
174
181
|
{
|
175
182
|
"id": 'cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7',
|
@@ -192,16 +199,8 @@ assistant_response_json_example = <<~RUBY
|
|
192
199
|
}
|
193
200
|
end
|
194
201
|
|
195
|
-
let(:response) do
|
196
|
-
instance_double(
|
197
|
-
HTTP::Response,
|
198
|
-
status: HTTP::Response::Status.new(200),
|
199
|
-
body: JSON.dump(response_body)
|
200
|
-
)
|
201
|
-
end
|
202
|
-
|
203
202
|
it 'can create a completion' do
|
204
|
-
completion =
|
203
|
+
completion = resource.create(model: 'text-davinci-002', prompt: 'Hello, world!')
|
205
204
|
|
206
205
|
expect(http)
|
207
206
|
.to have_received(:post)
|
@@ -222,12 +221,12 @@ RUBY
|
|
222
221
|
|
223
222
|
assistant_response_form_example = <<~RUBY
|
224
223
|
# api call
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
224
|
+
class File < self
|
225
|
+
def create(file:, purpose:)
|
226
|
+
Response::File.from_json(
|
227
|
+
post_form_multipart('/v1/files', file: form_file(file), purpose: purpose)
|
228
|
+
)
|
229
|
+
end
|
231
230
|
end
|
232
231
|
|
233
232
|
# wrapper
|
@@ -241,48 +240,58 @@ assistant_response_form_example = <<~RUBY
|
|
241
240
|
optional_field :deleted?, path: :deleted
|
242
241
|
end
|
243
242
|
|
243
|
+
class FileList < JSONPayload
|
244
|
+
field :data, wrapper: File
|
245
|
+
field :object
|
246
|
+
end
|
247
|
+
|
244
248
|
# test
|
245
|
-
describe '#
|
249
|
+
RSpec.describe OpenAI::API, '#files' do
|
250
|
+
include_context 'an API Resource'
|
251
|
+
|
252
|
+
let(:resource) { api.files }
|
246
253
|
let(:sample_file) { OpenAISpec::SPEC_ROOT.join('data/sample.jsonl') }
|
247
254
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
255
|
+
context 'when creating a file' do
|
256
|
+
let(:response_body) do
|
257
|
+
{
|
258
|
+
"id": 'file-XjGxS3KTG0uNmNOK362iJua3',
|
259
|
+
"object": 'file',
|
260
|
+
"bytes": 140,
|
261
|
+
"created_at": 1_613_779_121,
|
262
|
+
"filename": 'sample.jsonl',
|
263
|
+
"purpose": 'fine-tune'
|
264
|
+
}
|
265
|
+
end
|
258
266
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
267
|
+
it 'can create a file' do
|
268
|
+
file = resource.create(
|
269
|
+
file: sample_file,
|
270
|
+
purpose: 'fine-tune'
|
271
|
+
)
|
264
272
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
273
|
+
expect(http)
|
274
|
+
.to have_received(:post)
|
275
|
+
.with(
|
276
|
+
'https://api.openai.com/v1/files',
|
277
|
+
hash_including(
|
278
|
+
form: hash_including(
|
279
|
+
{
|
280
|
+
file: instance_of(HTTP::FormData::File),
|
281
|
+
purpose: 'fine-tune'
|
282
|
+
}
|
283
|
+
)
|
275
284
|
)
|
276
285
|
)
|
277
|
-
)
|
278
286
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
287
|
+
expect(file.id).to eql('file-XjGxS3KTG0uNmNOK362iJua3')
|
288
|
+
expect(file.object).to eql('file')
|
289
|
+
expect(file.bytes).to eql(140)
|
290
|
+
expect(file.created_at).to eql(1_613_779_121)
|
291
|
+
expect(file.filename).to eql('sample.jsonl')
|
292
|
+
expect(file.purpose).to eql('fine-tune')
|
293
|
+
expect(file.deleted?).to be(nil)
|
294
|
+
end
|
286
295
|
end
|
287
296
|
end
|
288
297
|
RUBY
|
@@ -359,7 +368,7 @@ cache_file = cache_dir.join("#{codegen.verb}_#{codegen.route.gsub('/', '_')}.txt
|
|
359
368
|
if cache_file.file?
|
360
369
|
puts cache_file.read
|
361
370
|
else
|
362
|
-
completion = api.
|
371
|
+
completion = api.chat_completions.create(
|
363
372
|
model: 'gpt-3.5-turbo',
|
364
373
|
messages: history,
|
365
374
|
max_tokens: 2000,
|