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.
data/README.md CHANGED
@@ -1,22 +1,40 @@
1
1
  # Ruby OpenAI
2
+
2
3
  [![Gem Version](https://img.shields.io/gem/v/ruby-openai.svg)](https://rubygems.org/gems/ruby-openai)
3
4
  [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/alexrudall/ruby-openai/blob/main/LICENSE.txt)
4
5
  [![CircleCI Build Status](https://circleci.com/gh/alexrudall/ruby-openai.svg?style=shield)](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 text with GPT-4, transcribe and translate audio with Whisper, or create images with DALLยทE...
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
- ๐Ÿ’ฅ Click [subscribe now](https://mailchi.mp/8c7b574726a9/ruby-openai) to hear first about new releases in the Rails AI newsletter!
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
- [![RailsAI Newsletter](https://github.com/user-attachments/assets/737cbb99-6029-42b8-9f22-a106725a4b1f)](https://mailchi.mp/8c7b574726a9/ruby-openai)
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
- - [Table of Contents](#table-of-contents)
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
- $ bundle install
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
- $ gem install ruby-openai
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. to enable verbose logging with Ruby's [Logger](https://ruby-doc.org/3.2.2/stdlibs/logger/Logger.html):
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, _bytesize|
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, _bytesize|
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, _bytesize|
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, _bytesize|
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
  ![Ruby](https://i.ibb.co/sWVh3BX/dalle-ruby.png)
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".
@@ -1,7 +1,7 @@
1
1
  module OpenAI
2
2
  class Batches
3
3
  def initialize(client:)
4
- @client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION)
4
+ @client = client
5
5
  end
6
6
 
7
7
  def list(parameters: {})
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 *CONFIG_KEYS, :faraday_middleware
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