ruby-openai 7.4.0 โ 8.3.0
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/.circleci/config.yml +1 -1
- data/.gitignore +2 -0
- data/CHANGELOG.md +54 -1
- data/Gemfile +6 -5
- data/Gemfile.lock +31 -23
- data/README.md +420 -15
- data/SECURITY.md +9 -0
- data/lib/openai/batches.rb +1 -1
- data/lib/openai/client.rb +15 -1
- data/lib/openai/conversations.rb +40 -0
- data/lib/openai/files.rb +11 -7
- data/lib/openai/http.rb +17 -39
- data/lib/openai/images.rb +17 -3
- data/lib/openai/models.rb +4 -0
- data/lib/openai/realtime.rb +19 -0
- data/lib/openai/responses.rb +23 -0
- data/lib/openai/stream.rb +50 -0
- data/lib/openai/vector_stores.rb +4 -0
- data/lib/openai/version.rb +1 -1
- data/lib/openai.rb +35 -24
- data/lib/ruby/openai.rb +0 -1
- metadata +8 -8
- data/lib/openai/compatibility.rb +0 -11
data/README.md
CHANGED
|
@@ -1,22 +1,40 @@
|
|
|
1
1
|
# Ruby OpenAI
|
|
2
|
+
|
|
2
3
|
[](https://rubygems.org/gems/ruby-openai)
|
|
3
4
|
[](https://github.com/alexrudall/ruby-openai/blob/main/LICENSE.txt)
|
|
4
5
|
[](https://circleci.com/gh/alexrudall/ruby-openai)
|
|
5
6
|
|
|
6
7
|
Use the [OpenAI API](https://openai.com/blog/openai-api/) with Ruby! ๐คโค๏ธ
|
|
7
8
|
|
|
8
|
-
Stream
|
|
9
|
+
Stream GPT-5 chats with the Responses API, initiate Realtime WebRTC conversations, and much more...
|
|
10
|
+
|
|
11
|
+
**Sponsors**
|
|
12
|
+
|
|
13
|
+
<table>
|
|
14
|
+
<tr>
|
|
15
|
+
<td width="300" align="center" valign="top">
|
|
16
|
+
|
|
17
|
+
[<img src="https://github.com/user-attachments/assets/b97e036d-3f22-4116-be97-8f8d1c432a4f" alt="InferToGo logo: man in suit falling, black and white" width="300" height="300">](https://infertogo.com/?utm_source=ruby-openai)
|
|
18
|
+
|
|
19
|
+
<sub>_[InferToGo](https://infertogo.com/?utm_source=ruby-openai) - The inference addon for your PaaS application._</sub>
|
|
20
|
+
|
|
21
|
+
</td>
|
|
22
|
+
<td width="300" align="center" valign="top">
|
|
23
|
+
|
|
24
|
+
[<img src="https://github.com/user-attachments/assets/3feb834c-2721-404c-a64d-02104ed4aba7" alt="SerpApi logo: Purple rounded square with 4 connected white holes" width="300" height="300">](https://serpapi.com/?utm_source=ruby-openai)
|
|
9
25
|
|
|
10
|
-
|
|
26
|
+
<sub>_[SerpApi - Search API](https://serpapi.com/?utm_source=ruby-openai) - Enhance your LLM's knowledge with data from search engines like Google and Bing using our simple API._</sub>
|
|
11
27
|
|
|
12
|
-
|
|
28
|
+
</td>
|
|
29
|
+
</tr>
|
|
30
|
+
</table>
|
|
13
31
|
|
|
14
|
-
[๐ฎ 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)
|
|
32
|
+
[๐ฎ 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) | [โฅ๏ธ Thanks to all sponsors!](https://github.com/sponsors/alexrudall)
|
|
15
33
|
|
|
16
34
|
## Contents
|
|
17
35
|
|
|
18
36
|
- [Ruby OpenAI](#ruby-openai)
|
|
19
|
-
- [
|
|
37
|
+
- [Contents](#contents)
|
|
20
38
|
- [Installation](#installation)
|
|
21
39
|
- [Bundler](#bundler)
|
|
22
40
|
- [Gem install](#gem-install)
|
|
@@ -29,14 +47,24 @@ Stream text with GPT-4, transcribe and translate audio with Whisper, or create i
|
|
|
29
47
|
- [Errors](#errors)
|
|
30
48
|
- [Faraday middleware](#faraday-middleware)
|
|
31
49
|
- [Azure](#azure)
|
|
50
|
+
- [Deepseek](#deepseek)
|
|
32
51
|
- [Ollama](#ollama)
|
|
33
52
|
- [Groq](#groq)
|
|
53
|
+
- [Gemini](#gemini)
|
|
34
54
|
- [Counting Tokens](#counting-tokens)
|
|
35
55
|
- [Models](#models)
|
|
36
56
|
- [Chat](#chat)
|
|
37
57
|
- [Streaming Chat](#streaming-chat)
|
|
38
58
|
- [Vision](#vision)
|
|
39
59
|
- [JSON Mode](#json-mode)
|
|
60
|
+
- [Responses API](#responses-api)
|
|
61
|
+
- [Create a Response](#create-a-response)
|
|
62
|
+
- [Follow-up Messages](#follow-up-messages)
|
|
63
|
+
- [Tool Calls](#tool-calls)
|
|
64
|
+
- [Streaming](#streaming)
|
|
65
|
+
- [Retrieve a Response](#retrieve-a-response)
|
|
66
|
+
- [Delete a Response](#delete-a-response)
|
|
67
|
+
- [List Input Items](#list-input-items)
|
|
40
68
|
- [Functions](#functions)
|
|
41
69
|
- [Completions](#completions)
|
|
42
70
|
- [Embeddings](#embeddings)
|
|
@@ -49,6 +77,7 @@ Stream text with GPT-4, transcribe and translate audio with Whisper, or create i
|
|
|
49
77
|
- [Vector Store Files](#vector-store-files)
|
|
50
78
|
- [Vector Store File Batches](#vector-store-file-batches)
|
|
51
79
|
- [Assistants](#assistants)
|
|
80
|
+
- [Conversations](#conversations)
|
|
52
81
|
- [Threads and Messages](#threads-and-messages)
|
|
53
82
|
- [Runs](#runs)
|
|
54
83
|
- [Create and Run](#create-and-run)
|
|
@@ -65,9 +94,11 @@ Stream text with GPT-4, transcribe and translate audio with Whisper, or create i
|
|
|
65
94
|
- [Translate](#translate)
|
|
66
95
|
- [Transcribe](#transcribe)
|
|
67
96
|
- [Speech](#speech)
|
|
97
|
+
- [Real-Time](#real-time)
|
|
68
98
|
- [Usage](#usage)
|
|
69
99
|
- [Errors](#errors-1)
|
|
70
100
|
- [Development](#development)
|
|
101
|
+
- [To check for deprecations](#to-check-for-deprecations)
|
|
71
102
|
- [Release](#release)
|
|
72
103
|
- [Contributing](#contributing)
|
|
73
104
|
- [License](#license)
|
|
@@ -86,7 +117,7 @@ gem "ruby-openai"
|
|
|
86
117
|
And then execute:
|
|
87
118
|
|
|
88
119
|
```bash
|
|
89
|
-
|
|
120
|
+
bundle install
|
|
90
121
|
```
|
|
91
122
|
|
|
92
123
|
### Gem install
|
|
@@ -94,7 +125,7 @@ $ bundle install
|
|
|
94
125
|
Or install with:
|
|
95
126
|
|
|
96
127
|
```bash
|
|
97
|
-
|
|
128
|
+
gem install ruby-openai
|
|
98
129
|
```
|
|
99
130
|
|
|
100
131
|
and require with:
|
|
@@ -205,7 +236,9 @@ client = OpenAI::Client.new(log_errors: true)
|
|
|
205
236
|
|
|
206
237
|
##### Faraday middleware
|
|
207
238
|
|
|
208
|
-
You can pass [Faraday middleware](https://lostisland.github.io/faraday/#/middleware/index) to the client in a block, eg
|
|
239
|
+
You can pass [Faraday middleware](https://lostisland.github.io/faraday/#/middleware/index) to the client in a block, eg:
|
|
240
|
+
|
|
241
|
+
- To enable verbose logging with Ruby's [Logger](https://ruby-doc.org/3.2.2/stdlibs/logger/Logger.html):
|
|
209
242
|
|
|
210
243
|
```ruby
|
|
211
244
|
client = OpenAI::Client.new do |f|
|
|
@@ -213,6 +246,13 @@ client = OpenAI::Client.new do |f|
|
|
|
213
246
|
end
|
|
214
247
|
```
|
|
215
248
|
|
|
249
|
+
- To add a web debugging proxy like [Charles](https://www.charlesproxy.com/documentation/welcome/):
|
|
250
|
+
|
|
251
|
+
```ruby
|
|
252
|
+
client = OpenAI::Client.new do |f|
|
|
253
|
+
f.proxy = { uri: "http://localhost:8888" }
|
|
254
|
+
end
|
|
255
|
+
```
|
|
216
256
|
#### Azure
|
|
217
257
|
|
|
218
258
|
To use the [Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/) API, you can configure the gem like this:
|
|
@@ -228,6 +268,28 @@ end
|
|
|
228
268
|
|
|
229
269
|
where `AZURE_OPENAI_URI` is e.g. `https://custom-domain.openai.azure.com/openai/deployments/gpt-35-turbo`
|
|
230
270
|
|
|
271
|
+
#### Deepseek
|
|
272
|
+
|
|
273
|
+
[Deepseek](https://api-docs.deepseek.com/) is compatible with the OpenAI chat API. Get an access token from [here](https://platform.deepseek.com/api_keys), then:
|
|
274
|
+
|
|
275
|
+
```ruby
|
|
276
|
+
client = OpenAI::Client.new(
|
|
277
|
+
access_token: "deepseek_access_token_goes_here",
|
|
278
|
+
uri_base: "https://api.deepseek.com/"
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
client.chat(
|
|
282
|
+
parameters: {
|
|
283
|
+
model: "deepseek-chat", # Required.
|
|
284
|
+
messages: [{ role: "user", content: "Hello!"}], # Required.
|
|
285
|
+
temperature: 0.7,
|
|
286
|
+
stream: proc do |chunk, _event|
|
|
287
|
+
print chunk.dig("choices", 0, "delta", "content")
|
|
288
|
+
end
|
|
289
|
+
}
|
|
290
|
+
)
|
|
291
|
+
```
|
|
292
|
+
|
|
231
293
|
#### Ollama
|
|
232
294
|
|
|
233
295
|
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.
|
|
@@ -252,7 +314,7 @@ client.chat(
|
|
|
252
314
|
model: "llama3", # Required.
|
|
253
315
|
messages: [{ role: "user", content: "Hello!"}], # Required.
|
|
254
316
|
temperature: 0.7,
|
|
255
|
-
stream: proc do |chunk,
|
|
317
|
+
stream: proc do |chunk, _event|
|
|
256
318
|
print chunk.dig("choices", 0, "delta", "content")
|
|
257
319
|
end
|
|
258
320
|
}
|
|
@@ -276,11 +338,35 @@ client.chat(
|
|
|
276
338
|
model: "llama3-8b-8192", # Required.
|
|
277
339
|
messages: [{ role: "user", content: "Hello!"}], # Required.
|
|
278
340
|
temperature: 0.7,
|
|
341
|
+
stream: proc do |chunk, _event|
|
|
342
|
+
print chunk.dig("choices", 0, "delta", "content")
|
|
343
|
+
end
|
|
344
|
+
}
|
|
345
|
+
)
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
#### Gemini
|
|
349
|
+
|
|
350
|
+
[Gemini API Chat](https://ai.google.dev/gemini-api/docs/openai) is also broadly compatible with the OpenAI API, and [currently in beta](https://ai.google.dev/gemini-api/docs/openai#current-limitations). Get an access token from [here](https://aistudio.google.com/app/apikey), then:
|
|
351
|
+
|
|
352
|
+
```ruby
|
|
353
|
+
client = OpenAI::Client.new(
|
|
354
|
+
access_token: "gemini_access_token_goes_here",
|
|
355
|
+
uri_base: "https://generativelanguage.googleapis.com/v1beta/openai/"
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
client.chat(
|
|
359
|
+
parameters: {
|
|
360
|
+
model: "gemini-1.5-flash", # Required.
|
|
361
|
+
messages: [{ role: "user", content: "Hello!"}], # Required.
|
|
362
|
+
temperature: 0.7,
|
|
279
363
|
stream: proc do |chunk, _bytesize|
|
|
280
364
|
print chunk.dig("choices", 0, "delta", "content")
|
|
281
365
|
end
|
|
282
366
|
}
|
|
283
367
|
)
|
|
368
|
+
|
|
369
|
+
# => Hello there! How can I help you today?
|
|
284
370
|
```
|
|
285
371
|
|
|
286
372
|
### Counting Tokens
|
|
@@ -304,6 +390,12 @@ client.models.list
|
|
|
304
390
|
client.models.retrieve(id: "gpt-4o")
|
|
305
391
|
```
|
|
306
392
|
|
|
393
|
+
You can also delete any finetuned model you generated, if you're an account Owner on your OpenAI organization:
|
|
394
|
+
|
|
395
|
+
```ruby
|
|
396
|
+
client.models.delete(id: "ft:gpt-4o-mini:acemeco:suffix:abc123")
|
|
397
|
+
```
|
|
398
|
+
|
|
307
399
|
### Chat
|
|
308
400
|
|
|
309
401
|
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):
|
|
@@ -332,7 +424,7 @@ client.chat(
|
|
|
332
424
|
model: "gpt-4o", # Required.
|
|
333
425
|
messages: [{ role: "user", content: "Describe a character called Anna!"}], # Required.
|
|
334
426
|
temperature: 0.7,
|
|
335
|
-
stream: proc do |chunk,
|
|
427
|
+
stream: proc do |chunk, _event|
|
|
336
428
|
print chunk.dig("choices", 0, "delta", "content")
|
|
337
429
|
end
|
|
338
430
|
}
|
|
@@ -418,7 +510,7 @@ You can stream it as well!
|
|
|
418
510
|
model: "gpt-4o",
|
|
419
511
|
messages: [{ role: "user", content: "Can I have some JSON please?"}],
|
|
420
512
|
response_format: { type: "json_object" },
|
|
421
|
-
stream: proc do |chunk,
|
|
513
|
+
stream: proc do |chunk, _event|
|
|
422
514
|
print chunk.dig("choices", 0, "delta", "content")
|
|
423
515
|
end
|
|
424
516
|
}
|
|
@@ -441,6 +533,107 @@ You can stream it as well!
|
|
|
441
533
|
# }
|
|
442
534
|
```
|
|
443
535
|
|
|
536
|
+
### Responses API
|
|
537
|
+
|
|
538
|
+
[OpenAI's most advanced interface for generating model responses](https://platform.openai.com/docs/api-reference/responses). Supports text and image inputs, and text outputs. Create stateful interactions with the model, using the output of previous responses as input. Extend the model's capabilities with built-in tools for file search, web search, computer use, and more. Allow the model access to external systems and data using function calling.
|
|
539
|
+
|
|
540
|
+
#### Create a Response
|
|
541
|
+
|
|
542
|
+
```ruby
|
|
543
|
+
response = client.responses.create(parameters: {
|
|
544
|
+
model: "gpt-5",
|
|
545
|
+
input: "Hello! I'm Szymon!",
|
|
546
|
+
reasoning: {
|
|
547
|
+
"effort": "minimal"
|
|
548
|
+
}
|
|
549
|
+
})
|
|
550
|
+
puts response.dig("output", 0, "content", 0, "text")
|
|
551
|
+
# => Thinking about how to answer this...
|
|
552
|
+
puts response.dig("output", 1, "content", 0, "text")
|
|
553
|
+
# => Hi Szymon! Great to meet you. How can I help today?
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
#### Follow-up Messages
|
|
557
|
+
|
|
558
|
+
```ruby
|
|
559
|
+
followup = client.responses.create(parameters: {
|
|
560
|
+
model: "gpt-4o",
|
|
561
|
+
input: "Remind me, what is my name?",
|
|
562
|
+
previous_response_id: response["id"]
|
|
563
|
+
})
|
|
564
|
+
puts followup.dig("output", 0, "content", 0, "text")
|
|
565
|
+
# => Your name is Szymon! How can I help you today?
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
#### Tool Calls
|
|
569
|
+
|
|
570
|
+
```ruby
|
|
571
|
+
response = client.responses.create(parameters: {
|
|
572
|
+
model: "gpt-4o",
|
|
573
|
+
input: "What's the weather in Paris?",
|
|
574
|
+
tools: [
|
|
575
|
+
{
|
|
576
|
+
"type" => "function",
|
|
577
|
+
"name" => "get_current_weather",
|
|
578
|
+
"description" => "Get the current weather in a given location",
|
|
579
|
+
"parameters" => {
|
|
580
|
+
"type" => "object",
|
|
581
|
+
"properties" => {
|
|
582
|
+
"location" => {
|
|
583
|
+
"type" => "string",
|
|
584
|
+
"description" => "The geographic location to get the weather for"
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
"required" => ["location"]
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
]
|
|
591
|
+
})
|
|
592
|
+
puts response.dig("output", 0, "name")
|
|
593
|
+
# => "get_current_weather"
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
#### Streaming
|
|
597
|
+
|
|
598
|
+
```ruby
|
|
599
|
+
client.responses.create(
|
|
600
|
+
parameters: {
|
|
601
|
+
model: "gpt-4o", # Required.
|
|
602
|
+
input: "Hello!", # Required.
|
|
603
|
+
stream: proc do |chunk, _event|
|
|
604
|
+
if chunk["type"] == "response.output_text.delta"
|
|
605
|
+
print chunk["delta"]
|
|
606
|
+
$stdout.flush # Ensure output is displayed immediately
|
|
607
|
+
end
|
|
608
|
+
end
|
|
609
|
+
}
|
|
610
|
+
)
|
|
611
|
+
# => "Hi there! How can I assist you today?..."
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
#### Retrieve a Response
|
|
615
|
+
|
|
616
|
+
```ruby
|
|
617
|
+
retrieved_response = client.responses.retrieve(response_id: response["id"])
|
|
618
|
+
puts retrieved_response["object"]
|
|
619
|
+
# => "response"
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
#### Delete a Response
|
|
623
|
+
|
|
624
|
+
```ruby
|
|
625
|
+
deletion = client.responses.delete(response_id: response["id"])
|
|
626
|
+
puts deletion["deleted"]
|
|
627
|
+
# => true
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
#### List Input Items
|
|
631
|
+
|
|
632
|
+
```ruby
|
|
633
|
+
input_items = client.responses.input_items(response_id: response["id"])
|
|
634
|
+
puts input_items["object"] # => "list"
|
|
635
|
+
```
|
|
636
|
+
|
|
444
637
|
### Functions
|
|
445
638
|
|
|
446
639
|
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)).
|
|
@@ -495,6 +688,9 @@ response =
|
|
|
495
688
|
message = response.dig("choices", 0, "message")
|
|
496
689
|
|
|
497
690
|
if message["role"] == "assistant" && message["tool_calls"]
|
|
691
|
+
# For a subsequent message with the role "tool", OpenAI requires the preceding message to have a single tool_calls argument.
|
|
692
|
+
messages << message
|
|
693
|
+
|
|
498
694
|
message["tool_calls"].each do |tool_call|
|
|
499
695
|
tool_call_id = tool_call.dig("id")
|
|
500
696
|
function_name = tool_call.dig("function", "name")
|
|
@@ -510,9 +706,6 @@ if message["role"] == "assistant" && message["tool_calls"]
|
|
|
510
706
|
# decide how to handle
|
|
511
707
|
end
|
|
512
708
|
|
|
513
|
-
# For a subsequent message with the role "tool", OpenAI requires the preceding message to have a tool_calls argument.
|
|
514
|
-
messages << message
|
|
515
|
-
|
|
516
709
|
messages << {
|
|
517
710
|
tool_call_id: tool_call_id,
|
|
518
711
|
role: "tool",
|
|
@@ -746,6 +939,12 @@ You can also capture the events for a job:
|
|
|
746
939
|
client.finetunes.list_events(id: fine_tune_id)
|
|
747
940
|
```
|
|
748
941
|
|
|
942
|
+
You can also delete any finetuned model you generated, if you're an account Owner on your OpenAI organization:
|
|
943
|
+
|
|
944
|
+
```ruby
|
|
945
|
+
client.models.delete(id: fine_tune_id)
|
|
946
|
+
```
|
|
947
|
+
|
|
749
948
|
### Vector Stores
|
|
750
949
|
|
|
751
950
|
Vector Store objects give the File Search tool the ability to search your files.
|
|
@@ -786,6 +985,27 @@ response = client.vector_stores.modify(
|
|
|
786
985
|
)
|
|
787
986
|
```
|
|
788
987
|
|
|
988
|
+
You can search a vector store for relevant chunks based on a query:
|
|
989
|
+
|
|
990
|
+
```ruby
|
|
991
|
+
response = client.vector_stores.search(
|
|
992
|
+
id: vector_store_id,
|
|
993
|
+
parameters: {
|
|
994
|
+
query: "What is the return policy?",
|
|
995
|
+
max_num_results: 20,
|
|
996
|
+
ranking_options: {
|
|
997
|
+
# Add any ranking options here in line with the API documentation
|
|
998
|
+
},
|
|
999
|
+
rewrite_query: true,
|
|
1000
|
+
filters: {
|
|
1001
|
+
type: "eq",
|
|
1002
|
+
property: "region",
|
|
1003
|
+
value: "us"
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
)
|
|
1007
|
+
```
|
|
1008
|
+
|
|
789
1009
|
You can delete vector stores:
|
|
790
1010
|
|
|
791
1011
|
```ruby
|
|
@@ -879,6 +1099,128 @@ client.vector_store_file_batches.cancel(
|
|
|
879
1099
|
)
|
|
880
1100
|
```
|
|
881
1101
|
|
|
1102
|
+
### Conversations
|
|
1103
|
+
|
|
1104
|
+
The Conversations API enables you to create and manage persistent conversations with your models. This is useful for maintaining conversation state across multiple interactions.
|
|
1105
|
+
|
|
1106
|
+
**Supported Endpoints:**
|
|
1107
|
+
- `POST /v1/conversations` - Create a conversation
|
|
1108
|
+
- `GET /v1/conversations/{id}` - Retrieve a conversation
|
|
1109
|
+
- `PATCH /v1/conversations/{id}` - Modify a conversation
|
|
1110
|
+
- `DELETE /v1/conversations/{id}` - Delete a conversation
|
|
1111
|
+
- `POST /v1/conversations/{id}/items` - Create items in a conversation
|
|
1112
|
+
- `GET /v1/conversations/{id}/items` - List items in a conversation
|
|
1113
|
+
- `GET /v1/conversations/{id}/items/{item_id}` - Get a specific item
|
|
1114
|
+
- `DELETE /v1/conversations/{id}/items/{item_id}` - Delete an item
|
|
1115
|
+
|
|
1116
|
+
#### Creating a Conversation
|
|
1117
|
+
|
|
1118
|
+
To create a new conversation:
|
|
1119
|
+
|
|
1120
|
+
```ruby
|
|
1121
|
+
response = client.conversations.create(
|
|
1122
|
+
parameters: {
|
|
1123
|
+
metadata: { purpose: "customer_support" }
|
|
1124
|
+
}
|
|
1125
|
+
)
|
|
1126
|
+
conversation_id = response["id"]
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
#### Retrieving a Conversation
|
|
1130
|
+
|
|
1131
|
+
To retrieve a specific conversation:
|
|
1132
|
+
|
|
1133
|
+
```ruby
|
|
1134
|
+
conversation = client.conversations.retrieve(id: conversation_id)
|
|
1135
|
+
```
|
|
1136
|
+
|
|
1137
|
+
#### Modifying a Conversation
|
|
1138
|
+
|
|
1139
|
+
To update a conversation's metadata:
|
|
1140
|
+
|
|
1141
|
+
```ruby
|
|
1142
|
+
response = client.conversations.modify(
|
|
1143
|
+
id: conversation_id,
|
|
1144
|
+
parameters: {
|
|
1145
|
+
metadata: { status: "resolved" }
|
|
1146
|
+
}
|
|
1147
|
+
)
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
#### Deleting a Conversation
|
|
1151
|
+
|
|
1152
|
+
To delete a conversation:
|
|
1153
|
+
|
|
1154
|
+
```ruby
|
|
1155
|
+
response = client.conversations.delete(id: conversation_id)
|
|
1156
|
+
```
|
|
1157
|
+
|
|
1158
|
+
#### Managing Items in Conversations
|
|
1159
|
+
|
|
1160
|
+
You can add, retrieve, and manage items within a conversation.
|
|
1161
|
+
|
|
1162
|
+
##### Creating Items
|
|
1163
|
+
|
|
1164
|
+
```ruby
|
|
1165
|
+
# Create multiple items at once
|
|
1166
|
+
response = client.conversations.create_items(
|
|
1167
|
+
conversation_id: conversation_id,
|
|
1168
|
+
parameters: {
|
|
1169
|
+
items: [
|
|
1170
|
+
{
|
|
1171
|
+
type: "message",
|
|
1172
|
+
role: "user",
|
|
1173
|
+
content: [
|
|
1174
|
+
{ type: "input_text", text: "Hello!" }
|
|
1175
|
+
]
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
type: "message",
|
|
1179
|
+
role: "assistant",
|
|
1180
|
+
content: [
|
|
1181
|
+
{ type: "input_text", text: "How are you?" }
|
|
1182
|
+
]
|
|
1183
|
+
}
|
|
1184
|
+
]
|
|
1185
|
+
}
|
|
1186
|
+
)
|
|
1187
|
+
```
|
|
1188
|
+
|
|
1189
|
+
##### Listing Items
|
|
1190
|
+
|
|
1191
|
+
```ruby
|
|
1192
|
+
# List all items in a conversation
|
|
1193
|
+
response = client.conversations.list_items(conversation_id: conversation_id)
|
|
1194
|
+
items = response["data"]
|
|
1195
|
+
|
|
1196
|
+
# With parameters
|
|
1197
|
+
response = client.conversations.list_items(
|
|
1198
|
+
conversation_id: conversation_id,
|
|
1199
|
+
parameters: {
|
|
1200
|
+
limit: 10,
|
|
1201
|
+
order: "asc"
|
|
1202
|
+
}
|
|
1203
|
+
)
|
|
1204
|
+
```
|
|
1205
|
+
|
|
1206
|
+
##### Retrieving a Specific Item
|
|
1207
|
+
|
|
1208
|
+
```ruby
|
|
1209
|
+
item = client.conversations.get_item(
|
|
1210
|
+
conversation_id: conversation_id,
|
|
1211
|
+
item_id: item_id
|
|
1212
|
+
)
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
##### Deleting an Item
|
|
1216
|
+
|
|
1217
|
+
```ruby
|
|
1218
|
+
response = client.conversations.delete_item(
|
|
1219
|
+
conversation_id: conversation_id,
|
|
1220
|
+
item_id: item_id
|
|
1221
|
+
)
|
|
1222
|
+
```
|
|
1223
|
+
|
|
882
1224
|
### Assistants
|
|
883
1225
|
|
|
884
1226
|
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)).
|
|
@@ -1001,7 +1343,7 @@ client.runs.create(
|
|
|
1001
1343
|
assistant_id: assistant_id,
|
|
1002
1344
|
max_prompt_tokens: 256,
|
|
1003
1345
|
max_completion_tokens: 16,
|
|
1004
|
-
stream: proc do |chunk,
|
|
1346
|
+
stream: proc do |chunk, _event|
|
|
1005
1347
|
if chunk["object"] == "thread.message.delta"
|
|
1006
1348
|
print chunk.dig("delta", "content", 0, "text", "value")
|
|
1007
1349
|
end
|
|
@@ -1385,6 +1727,21 @@ puts response.dig("data", 0, "url")
|
|
|
1385
1727
|
|
|
1386
1728
|

|
|
1387
1729
|
|
|
1730
|
+
You can also upload arrays of images, eg.
|
|
1731
|
+
|
|
1732
|
+
```ruby
|
|
1733
|
+
client = OpenAI::Client.new
|
|
1734
|
+
response = client.images.edit(
|
|
1735
|
+
parameters: {
|
|
1736
|
+
model: "gpt-image-1",
|
|
1737
|
+
image: [File.open(base_image_path, "rb"), "image.png"],
|
|
1738
|
+
prompt: "Take the first image as base and apply the second image as a watermark on the bottom right corner",
|
|
1739
|
+
size: "1024x1024"
|
|
1740
|
+
# Removed response_format parameter as it's not supported with gpt-image-1
|
|
1741
|
+
}
|
|
1742
|
+
)
|
|
1743
|
+
```
|
|
1744
|
+
|
|
1388
1745
|
### Image Variations
|
|
1389
1746
|
|
|
1390
1747
|
Create n variations of an image.
|
|
@@ -1445,6 +1802,20 @@ puts response["text"]
|
|
|
1445
1802
|
# => "Transcription of the text"
|
|
1446
1803
|
```
|
|
1447
1804
|
|
|
1805
|
+
If you are using Ruby on Rails with Active Storage, you would need to send an audio or video file like this (User has_one_attached):
|
|
1806
|
+
```ruby
|
|
1807
|
+
user.media.blob.open do |file|
|
|
1808
|
+
response = client.audio.transcribe(
|
|
1809
|
+
parameters: {
|
|
1810
|
+
model: "whisper-1",
|
|
1811
|
+
file: File.open(file, "rb"),
|
|
1812
|
+
language: "en" # Optional
|
|
1813
|
+
})
|
|
1814
|
+
puts response["text"]
|
|
1815
|
+
# => "Transcription of the text"
|
|
1816
|
+
end
|
|
1817
|
+
```
|
|
1818
|
+
|
|
1448
1819
|
#### Speech
|
|
1449
1820
|
|
|
1450
1821
|
The speech API takes as input the text and a voice and returns the content of an audio file you can listen to.
|
|
@@ -1463,7 +1834,35 @@ File.binwrite('demo.mp3', response)
|
|
|
1463
1834
|
# => mp3 file that plays: "This is a speech test!"
|
|
1464
1835
|
```
|
|
1465
1836
|
|
|
1837
|
+
### Realtime
|
|
1838
|
+
|
|
1839
|
+
The [Realtime API](https://platform.openai.com/docs/guides/realtime) allows you to create a live speech-to-speech session with an OpenAI model. It responds with a session object, plus a client_secret key which contains a usable ephemeral API token that can be used to [authenticate browser clients for a WebRTC connection](https://platform.openai.com/docs/guides/realtime#connect-with-webrtc).
|
|
1840
|
+
|
|
1841
|
+
```ruby
|
|
1842
|
+
response = client.realtime.create(parameters: { model: "gpt-4o-realtime-preview-2024-12-17" })
|
|
1843
|
+
puts "ephemeral key: #{response.dig('client_secret', 'value')}"
|
|
1844
|
+
# => "ephemeral key: ek_abc123"
|
|
1845
|
+
```
|
|
1846
|
+
|
|
1847
|
+
Then in the client-side Javascript application, make a POST request to the Real-Time API with the ephemeral key and the SDP offer.
|
|
1848
|
+
|
|
1849
|
+
```js
|
|
1850
|
+
const OPENAI_REALTIME_URL = 'https://api.openai.com/v1/realtime/sessions'
|
|
1851
|
+
const MODEL = 'gpt-4o-realtime-preview-2024-12-17'
|
|
1852
|
+
|
|
1853
|
+
const response = await fetch(`${OPENAI_REALTIME_URL}?model=${MODEL}`, {
|
|
1854
|
+
method: 'POST',
|
|
1855
|
+
headers: {
|
|
1856
|
+
'Content-Type': 'application/sdp',
|
|
1857
|
+
'Authorization': `Bearer ${ephemeralKey}`,
|
|
1858
|
+
'OpenAI-Beta': 'realtime=v1'
|
|
1859
|
+
},
|
|
1860
|
+
body: offer.sdp
|
|
1861
|
+
})
|
|
1862
|
+
```
|
|
1863
|
+
|
|
1466
1864
|
### Usage
|
|
1865
|
+
|
|
1467
1866
|
The Usage API provides information about the cost of various OpenAI services within your organization.
|
|
1468
1867
|
To use Admin APIs like Usage, you need to set an OPENAI_ADMIN_TOKEN, which can be generated [here](https://platform.openai.com/settings/organization/admin-keys).
|
|
1469
1868
|
|
|
@@ -1544,6 +1943,12 @@ To run all tests, execute the command `bundle exec rake`, which will also run th
|
|
|
1544
1943
|
> [!WARNING]
|
|
1545
1944
|
> If you have an `OPENAI_ACCESS_TOKEN` and `OPENAI_ADMIN_TOKEN` in your `ENV`, running the specs will hit the actual API, which will be slow and cost you money - 2 cents or more! Remove them from your environment with `unset` or similar if you just want to run the specs against the stored VCR responses.
|
|
1546
1945
|
|
|
1946
|
+
### To check for deprecations
|
|
1947
|
+
|
|
1948
|
+
```
|
|
1949
|
+
bundle exec ruby -e "Warning[:deprecated] = true; require 'rspec'; exit RSpec::Core::Runner.run(['spec/openai/client/http_spec.rb:25'])"
|
|
1950
|
+
```
|
|
1951
|
+
|
|
1547
1952
|
## Release
|
|
1548
1953
|
|
|
1549
1954
|
First run the specs without VCR so they actually hit the API. This will cost 2 cents or more. Set OPENAI_ACCESS_TOKEN and OPENAI_ADMIN_TOKEN in your environment.
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
Thank you for helping us keep ruby-openai and any systems it interacts with secure.
|
|
4
|
+
|
|
5
|
+
## Reporting Security Issues
|
|
6
|
+
|
|
7
|
+
The security of our systems and user data is our top priority. We appreciate the work of security researchers acting in good faith in identifying and reporting potential vulnerabilities.
|
|
8
|
+
|
|
9
|
+
Any validated vulnerability in this functionality can be reported through Github - click on the [Security Tab](https://github.com/alexrudall/ruby-openai/security) and click "Report a vulnerability".
|
data/lib/openai/batches.rb
CHANGED
data/lib/openai/client.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# rubocop:disable Metrics/ClassLength
|
|
1
2
|
module OpenAI
|
|
2
3
|
class Client
|
|
3
4
|
include OpenAI::HTTP
|
|
@@ -5,7 +6,7 @@ module OpenAI
|
|
|
5
6
|
SENSITIVE_ATTRIBUTES = %i[@access_token @admin_token @organization_id @extra_headers].freeze
|
|
6
7
|
CONFIG_KEYS = %i[access_token admin_token api_type api_version extra_headers
|
|
7
8
|
log_errors organization_id request_timeout uri_base].freeze
|
|
8
|
-
attr_reader
|
|
9
|
+
attr_reader(*CONFIG_KEYS, :faraday_middleware)
|
|
9
10
|
attr_writer :access_token
|
|
10
11
|
|
|
11
12
|
def initialize(config = {}, &faraday_middleware)
|
|
@@ -52,6 +53,10 @@ module OpenAI
|
|
|
52
53
|
@models ||= OpenAI::Models.new(client: self)
|
|
53
54
|
end
|
|
54
55
|
|
|
56
|
+
def responses
|
|
57
|
+
@responses ||= OpenAI::Responses.new(client: self)
|
|
58
|
+
end
|
|
59
|
+
|
|
55
60
|
def assistants
|
|
56
61
|
@assistants ||= OpenAI::Assistants.new(client: self)
|
|
57
62
|
end
|
|
@@ -88,6 +93,10 @@ module OpenAI
|
|
|
88
93
|
@batches ||= OpenAI::Batches.new(client: self)
|
|
89
94
|
end
|
|
90
95
|
|
|
96
|
+
def realtime
|
|
97
|
+
@realtime ||= OpenAI::Realtime.new(client: self)
|
|
98
|
+
end
|
|
99
|
+
|
|
91
100
|
def moderations(parameters: {})
|
|
92
101
|
json_post(path: "/moderations", parameters: parameters)
|
|
93
102
|
end
|
|
@@ -96,6 +105,10 @@ module OpenAI
|
|
|
96
105
|
@usage ||= OpenAI::Usage.new(client: self)
|
|
97
106
|
end
|
|
98
107
|
|
|
108
|
+
def conversations
|
|
109
|
+
@conversations ||= OpenAI::Conversations.new(client: self)
|
|
110
|
+
end
|
|
111
|
+
|
|
99
112
|
def azure?
|
|
100
113
|
@api_type&.to_sym == :azure
|
|
101
114
|
end
|
|
@@ -128,3 +141,4 @@ module OpenAI
|
|
|
128
141
|
end
|
|
129
142
|
end
|
|
130
143
|
end
|
|
144
|
+
# rubocop:enable Metrics/ClassLength
|