ruby-openai 7.0.1 → 7.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4067ee94a830c3637d339dff7445d0176610bcb1d381754915a9279bedac59fa
4
- data.tar.gz: b2f6e4e7331e1f60f32aa3c1fb5628124c3b09539b5d4e2a99ebf9e539420cd2
3
+ metadata.gz: 7108bf76aa6f30cd7c38b41967b0162d6a4014698c1c8364b8116e5c665c044f
4
+ data.tar.gz: 736587458668b4608e49fa8ec50e460da14eeda0dfb8a739c68806e3bd5d0a2c
5
5
  SHA512:
6
- metadata.gz: 6f81268cc24c111b011aa87e1a0a877a72caf7a8b9d52cea367c35edf222909f5b8648d6925e30755a71692bd00974fc6d630c055a1639d5bbb8e9a33da11ef9
7
- data.tar.gz: 2393f1d34582d37c2ee23f07d81d792fb60b0d5f3e9e5e2b900aa056d4ec49b23244d1d7766a3256a521e7621d17fa349f53134f9bdd64499a1fb760b2818038
6
+ metadata.gz: 76cac4818b5941d5732becebc91675c1ebeaba4f27c1710888afbb1893f3f8ff4d18e24a3c90f4d4332eb89730b0f6195507cf99519e764876a614ca3927a0cb
7
+ data.tar.gz: 4757d8e11b494a75d0ea839ae073610adfa77317ba0e13ddabdd21bf10fd34127a5747138644dba4d9073aa509341bfeaed69a3d00150d1ae90a42ddeadd53e4
data/.gitignore CHANGED
@@ -10,11 +10,6 @@
10
10
  /test/tmp/
11
11
  /test/version_tmp/
12
12
  /tmp/
13
- /.bundle/
14
- /.yardoc
15
- /_yardoc/
16
- /doc/
17
-
18
13
 
19
14
  # Used by dotenv library to load environment variables.
20
15
  .env
data/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ 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.2.0] - 2024-10-10
9
+
10
+ ### Added
11
+
12
+ - Add ability to pass parameters to Files#list endpoint - thanks to [@parterburn](https://github.com/parterburn)!
13
+ - Add Velvet observability platform to README - thanks to [@philipithomas](https://github.com/philipithomas)
14
+ - Add Assistants::Messages#delete endpoint - thanks to [@mochetts](https://github.com/mochetts)!
15
+
16
+ ## [7.1.0] - 2024-06-10
17
+
18
+ ### Added
19
+
20
+ - Add new Vector Store endpoints - thanks to [@willywg](https://github.com/willywg) for this PR!
21
+ - Add parameters to batches.list endpoint so you can for example use `after` - thanks to [@marckohlbrugge](https://github.com/marckohlbrugge)!
22
+ - Add vision as permitted purpose for files - thanks again to [@willywg](https://github.com/willywg) for the PR.
23
+ - Add improved README example of tool calling - thanks [@krschacht](https://github.com/krschacht) - check out his project [HostedGPT](https://github.com/AllYourBot/hostedgpt)!
24
+
25
+ ### Fixed
26
+
27
+ - Fix broken link in README table of contents - thanks to [@garrettgsb](https://github.com/garrettgsb)!
28
+ - Skip sending nil headers - thanks to [@drnic](https://github.com/drnic)!
29
+
8
30
  ## [7.0.1] - 2024-04-30
9
31
 
10
32
  ### Fixed
data/Gemfile CHANGED
@@ -5,8 +5,8 @@ gemspec
5
5
 
6
6
  gem "byebug", "~> 11.1.3"
7
7
  gem "dotenv", "~> 2.8.1"
8
- gem "rake", "~> 13.1"
8
+ gem "rake", "~> 13.2"
9
9
  gem "rspec", "~> 3.13"
10
10
  gem "rubocop", "~> 1.50.2"
11
11
  gem "vcr", "~> 6.1.0"
12
- gem "webmock", "~> 3.19.1"
12
+ gem "webmock", "~> 3.23.1"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-openai (7.0.1)
4
+ ruby-openai (7.2.0)
5
5
  event_stream_parser (>= 0.3.0, < 2.0.0)
6
6
  faraday (>= 1)
7
7
  faraday-multipart (>= 1)
@@ -9,12 +9,14 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- addressable (2.8.5)
12
+ addressable (2.8.6)
13
13
  public_suffix (>= 2.0.2, < 6.0)
14
14
  ast (2.4.2)
15
15
  base64 (0.2.0)
16
+ bigdecimal (3.1.8)
16
17
  byebug (11.1.3)
17
- crack (0.4.5)
18
+ crack (1.0.0)
19
+ bigdecimal
18
20
  rexml
19
21
  diff-lcs (1.5.1)
20
22
  dotenv (2.8.1)
@@ -26,17 +28,18 @@ GEM
26
28
  faraday-multipart (1.0.4)
27
29
  multipart-post (~> 2)
28
30
  faraday-net_http (3.0.2)
29
- hashdiff (1.0.1)
31
+ hashdiff (1.1.0)
30
32
  json (2.6.3)
31
33
  multipart-post (2.3.0)
32
34
  parallel (1.22.1)
33
35
  parser (3.2.2.0)
34
36
  ast (~> 2.4.1)
35
- public_suffix (5.0.3)
37
+ public_suffix (5.0.5)
36
38
  rainbow (3.1.1)
37
- rake (13.1.0)
39
+ rake (13.2.1)
38
40
  regexp_parser (2.8.0)
39
- rexml (3.2.6)
41
+ rexml (3.3.6)
42
+ strscan
40
43
  rspec (3.13.0)
41
44
  rspec-core (~> 3.13.0)
42
45
  rspec-expectations (~> 3.13.0)
@@ -64,9 +67,10 @@ GEM
64
67
  parser (>= 3.2.1.0)
65
68
  ruby-progressbar (1.13.0)
66
69
  ruby2_keywords (0.0.5)
70
+ strscan (3.1.0)
67
71
  unicode-display_width (2.4.2)
68
72
  vcr (6.1.0)
69
- webmock (3.19.1)
73
+ webmock (3.23.1)
70
74
  addressable (>= 2.8.0)
71
75
  crack (>= 0.3.2)
72
76
  hashdiff (>= 0.4.0, < 2.0.0)
@@ -77,12 +81,12 @@ PLATFORMS
77
81
  DEPENDENCIES
78
82
  byebug (~> 11.1.3)
79
83
  dotenv (~> 2.8.1)
80
- rake (~> 13.1)
84
+ rake (~> 13.2)
81
85
  rspec (~> 3.13)
82
86
  rubocop (~> 1.50.2)
83
87
  ruby-openai!
84
88
  vcr (~> 6.1.0)
85
- webmock (~> 3.19.1)
89
+ webmock (~> 3.23.1)
86
90
 
87
91
  BUNDLED WITH
88
92
  2.4.5
data/README.md CHANGED
@@ -6,9 +6,9 @@
6
6
 
7
7
  Use the [OpenAI API](https://openai.com/blog/openai-api/) with Ruby! 🤖❤️
8
8
 
9
- Stream text with GPT-4, transcribe and translate audio with Whisper, or create images with DALL·E...
9
+ Stream text with GPT-4o, transcribe and translate audio with Whisper, or create images with DALL·E...
10
10
 
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)
11
+ [📚 Rails AI (FREE Book)](https://railsai.com) | [🎮 Ruby AI Builders Discord](https://discord.gg/k4Uc224xVD) | [🐦 X](https://x.com/alexrudall) | [🧠 Anthropic Gem](https://github.com/alexrudall/anthropic) | [🚂 Midjourney Gem](https://github.com/alexrudall/midjourney)
12
12
 
13
13
  ## Contents
14
14
 
@@ -35,14 +35,20 @@ Stream text with GPT-4, transcribe and translate audio with Whisper, or create i
35
35
  - [Vision](#vision)
36
36
  - [JSON Mode](#json-mode)
37
37
  - [Functions](#functions)
38
- - [Edits](#edits)
38
+ - [Completions](#completions)
39
39
  - [Embeddings](#embeddings)
40
40
  - [Batches](#batches)
41
41
  - [Files](#files)
42
+ - [For fine-tuning purposes](#for-fine-tuning-purposes)
43
+ - [For assistant purposes](#for-assistant-purposes)
42
44
  - [Finetunes](#finetunes)
45
+ - [Vector Stores](#vector-stores)
46
+ - [Vector Store Files](#vector-store-files)
47
+ - [Vector Store File Batches](#vector-store-file-batches)
43
48
  - [Assistants](#assistants)
44
49
  - [Threads and Messages](#threads-and-messages)
45
50
  - [Runs](#runs)
51
+ - [Create and Run](#create-and-run)
46
52
  - [Runs involving function tools](#runs-involving-function-tools)
47
53
  - [Image Generation](#image-generation)
48
54
  - [DALL·E 2](#dalle-2)
@@ -54,7 +60,7 @@ Stream text with GPT-4, transcribe and translate audio with Whisper, or create i
54
60
  - [Translate](#translate)
55
61
  - [Transcribe](#transcribe)
56
62
  - [Speech](#speech)
57
- - [Errors](#errors)
63
+ - [Errors](#errors-1)
58
64
  - [Development](#development)
59
65
  - [Release](#release)
60
66
  - [Contributing](#contributing)
@@ -103,7 +109,7 @@ For a quick test you can pass your token directly to a new client:
103
109
  ```ruby
104
110
  client = OpenAI::Client.new(
105
111
  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.
112
+ log_errors: true # Highly recommended in development, so you can see what errors OpenAI is returning. Not recommended in production because it could leak private data to your logs.
107
113
  )
108
114
  ```
109
115
 
@@ -114,8 +120,8 @@ For a more robust setup, you can configure the gem with your API keys, for examp
114
120
  ```ruby
115
121
  OpenAI.configure do |config|
116
122
  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.
123
+ config.organization_id = ENV.fetch("OPENAI_ORGANIZATION_ID") # Optional
124
+ config.log_errors = true # Highly recommended in development, so you can see what errors OpenAI is returning. Not recommended in production because it could leak private data to your logs.
119
125
  end
120
126
  ```
121
127
 
@@ -133,7 +139,9 @@ client = OpenAI::Client.new(access_token: "access_token_goes_here")
133
139
 
134
140
  #### Custom timeout or base URI
135
141
 
136
- The default timeout for any request using this library is 120 seconds. You can change that by passing a number of seconds to the `request_timeout` when initializing the client. You can also change the base URI used for all requests, eg. to use observability tools like [Helicone](https://docs.helicone.ai/quickstart/integrate-in-one-line-of-code), and add arbitrary other headers e.g. for [openai-caching-proxy-worker](https://github.com/6/openai-caching-proxy-worker):
142
+ - The default timeout for any request using this library is 120 seconds. You can change that by passing a number of seconds to the `request_timeout` when initializing the client.
143
+ - You can also change the base URI used for all requests, eg. to use observability tools like [Helicone](https://docs.helicone.ai/quickstart/integrate-in-one-line-of-code) or [Velvet](https://docs.usevelvet.com/docs/getting-started)
144
+ - You can also add arbitrary other headers e.g. for [openai-caching-proxy-worker](https://github.com/6/openai-caching-proxy-worker), eg.:
137
145
 
138
146
  ```ruby
139
147
  client = OpenAI::Client.new(
@@ -251,7 +259,7 @@ client.chat(
251
259
  ```ruby
252
260
  client = OpenAI::Client.new(
253
261
  access_token: "groq_access_token_goes_here",
254
- uri_base: "https://api.groq.com/"
262
+ uri_base: "https://api.groq.com/openai"
255
263
  )
256
264
 
257
265
  client.chat(
@@ -273,7 +281,7 @@ To estimate the token-count of your text:
273
281
 
274
282
  ```ruby
275
283
  OpenAI.rough_token_count("Your text")
276
- ````
284
+ ```
277
285
 
278
286
  If you need a more accurate count, try [tiktoken_ruby](https://github.com/IAPark/tiktoken_ruby).
279
287
 
@@ -283,7 +291,7 @@ There are different models that can be used to generate text. For a full list an
283
291
 
284
292
  ```ruby
285
293
  client.models.list
286
- client.models.retrieve(id: "gpt-3.5-turbo")
294
+ client.models.retrieve(id: "gpt-4o")
287
295
  ```
288
296
 
289
297
  ### Chat
@@ -293,7 +301,7 @@ GPT is a model that can be used to generate text in a conversational style. You
293
301
  ```ruby
294
302
  response = client.chat(
295
303
  parameters: {
296
- model: "gpt-3.5-turbo", # Required.
304
+ model: "gpt-4o", # Required.
297
305
  messages: [{ role: "user", content: "Hello!"}], # Required.
298
306
  temperature: 0.7,
299
307
  })
@@ -310,7 +318,7 @@ You can stream from the API in realtime, which can be much faster and used to cr
310
318
  ```ruby
311
319
  client.chat(
312
320
  parameters: {
313
- model: "gpt-3.5-turbo", # Required.
321
+ model: "gpt-4o", # Required.
314
322
  messages: [{ role: "user", content: "Describe a character called Anna!"}], # Required.
315
323
  temperature: 0.7,
316
324
  stream: proc do |chunk, _bytesize|
@@ -320,7 +328,28 @@ client.chat(
320
328
  # => "Anna is a young woman in her mid-twenties, with wavy chestnut hair that falls to her shoulders..."
321
329
  ```
322
330
 
323
- Note: OpenAPI currently does not report token usage for streaming responses. To count tokens while streaming, try `OpenAI.rough_token_count` or [tiktoken_ruby](https://github.com/IAPark/tiktoken_ruby). We think that each call to the stream proc corresponds to a single token, so you can also try counting the number of calls to the proc to get the completion token count.
331
+ Note: In order to get usage information, you can provide the [`stream_options` parameter](https://platform.openai.com/docs/api-reference/chat/create#chat-create-stream_options) and OpenAI will provide a final chunk with the usage. Here is an example:
332
+
333
+ ```ruby
334
+ stream_proc = proc { |chunk, _bytesize| puts "--------------"; puts chunk.inspect; }
335
+ client.chat(
336
+ parameters: {
337
+ model: "gpt-4o",
338
+ stream: stream_proc,
339
+ stream_options: { include_usage: true },
340
+ messages: [{ role: "user", content: "Hello!"}],
341
+ })
342
+ # => --------------
343
+ # => {"id"=>"chatcmpl-7bbq05PiZqlHxjV1j7OHnKKDURKaf", "object"=>"chat.completion.chunk", "created"=>1718750612, "model"=>"gpt-4o-2024-05-13", "system_fingerprint"=>"fp_9cb5d38cf7", "choices"=>[{"index"=>0, "delta"=>{"role"=>"assistant", "content"=>""}, "logprobs"=>nil, "finish_reason"=>nil}], "usage"=>nil}
344
+ # => --------------
345
+ # => {"id"=>"chatcmpl-7bbq05PiZqlHxjV1j7OHnKKDURKaf", "object"=>"chat.completion.chunk", "created"=>1718750612, "model"=>"gpt-4o-2024-05-13", "system_fingerprint"=>"fp_9cb5d38cf7", "choices"=>[{"index"=>0, "delta"=>{"content"=>"Hello"}, "logprobs"=>nil, "finish_reason"=>nil}], "usage"=>nil}
346
+ # => --------------
347
+ # => ... more content chunks
348
+ # => --------------
349
+ # => {"id"=>"chatcmpl-7bbq05PiZqlHxjV1j7OHnKKDURKaf", "object"=>"chat.completion.chunk", "created"=>1718750612, "model"=>"gpt-4o-2024-05-13", "system_fingerprint"=>"fp_9cb5d38cf7", "choices"=>[{"index"=>0, "delta"=>{}, "logprobs"=>nil, "finish_reason"=>"stop"}], "usage"=>nil}
350
+ # => --------------
351
+ # => {"id"=>"chatcmpl-7bbq05PiZqlHxjV1j7OHnKKDURKaf", "object"=>"chat.completion.chunk", "created"=>1718750612, "model"=>"gpt-4o-2024-05-13", "system_fingerprint"=>"fp_9cb5d38cf7", "choices"=>[], "usage"=>{"prompt_tokens"=>9, "completion_tokens"=>9, "total_tokens"=>18}}
352
+ ```
324
353
 
325
354
  #### Vision
326
355
 
@@ -351,7 +380,7 @@ You can set the response_format to ask for responses in JSON:
351
380
  ```ruby
352
381
  response = client.chat(
353
382
  parameters: {
354
- model: "gpt-3.5-turbo",
383
+ model: "gpt-4o",
355
384
  response_format: { type: "json_object" },
356
385
  messages: [{ role: "user", content: "Hello! Give me some JSON please."}],
357
386
  temperature: 0.7,
@@ -371,7 +400,7 @@ You can stream it as well!
371
400
  ```ruby
372
401
  response = client.chat(
373
402
  parameters: {
374
- model: "gpt-3.5-turbo",
403
+ model: "gpt-4o",
375
404
  messages: [{ role: "user", content: "Can I have some JSON please?"}],
376
405
  response_format: { type: "json_object" },
377
406
  stream: proc do |chunk, _bytesize|
@@ -402,26 +431,29 @@ You can describe and pass in functions and the model will intelligently choose t
402
431
  ```ruby
403
432
 
404
433
  def get_current_weather(location:, unit: "fahrenheit")
405
- # use a weather api to fetch weather
434
+ # Here you could use a weather api to fetch the weather.
435
+ "The weather in #{location} is nice 🌞 #{unit}"
406
436
  end
407
437
 
438
+ messages = [
439
+ {
440
+ "role": "user",
441
+ "content": "What is the weather like in San Francisco?",
442
+ },
443
+ ]
444
+
408
445
  response =
409
446
  client.chat(
410
447
  parameters: {
411
- model: "gpt-3.5-turbo",
412
- messages: [
413
- {
414
- "role": "user",
415
- "content": "What is the weather like in San Francisco?",
416
- },
417
- ],
448
+ model: "gpt-4o",
449
+ messages: messages, # Defined above because we'll use it again
418
450
  tools: [
419
451
  {
420
452
  type: "function",
421
453
  function: {
422
454
  name: "get_current_weather",
423
455
  description: "Get the current weather in a given location",
424
- parameters: {
456
+ parameters: { # Format: https://json-schema.org/understanding-json-schema
425
457
  type: :object,
426
458
  properties: {
427
459
  location: {
@@ -438,31 +470,51 @@ response =
438
470
  },
439
471
  }
440
472
  ],
441
- tool_choice: {
442
- type: "function",
443
- function: {
444
- name: "get_current_weather"
445
- }
446
- }
473
+ tool_choice: "required" # Optional, defaults to "auto"
474
+ # Can also put "none" or specific functions, see docs
447
475
  },
448
476
  )
449
477
 
450
478
  message = response.dig("choices", 0, "message")
451
479
 
452
480
  if message["role"] == "assistant" && message["tool_calls"]
453
- function_name = message.dig("tool_calls", 0, "function", "name")
454
- args =
455
- JSON.parse(
456
- message.dig("tool_calls", 0, "function", "arguments"),
481
+ message["tool_calls"].each do |tool_call|
482
+ tool_call_id = tool_call.dig("id")
483
+ function_name = tool_call.dig("function", "name")
484
+ function_args = JSON.parse(
485
+ tool_call.dig("function", "arguments"),
457
486
  { symbolize_names: true },
458
487
  )
488
+ function_response = case function_name
489
+ when "get_current_weather"
490
+ get_current_weather(**function_args) # => "The weather is nice 🌞"
491
+ else
492
+ # decide how to handle
493
+ end
459
494
 
460
- case function_name
461
- when "get_current_weather"
462
- get_current_weather(**args)
495
+ # For a subsequent message with the role "tool", OpenAI requires the preceding message to have a tool_calls argument.
496
+ messages << message
497
+
498
+ messages << {
499
+ tool_call_id: tool_call_id,
500
+ role: "tool",
501
+ name: function_name,
502
+ content: function_response
503
+ } # Extend the conversation with the results of the functions
463
504
  end
505
+
506
+ second_response = client.chat(
507
+ parameters: {
508
+ model: "gpt-4o",
509
+ messages: messages
510
+ })
511
+
512
+ puts second_response.dig("choices", 0, "message", "content")
513
+
514
+ # At this point, the model has decided to call functions, you've called the functions
515
+ # and provided the response back, and the model has considered this and responded.
464
516
  end
465
- # => "The weather is nice 🌞"
517
+ # => "It looks like the weather is nice and sunny in San Francisco! If you're planning to go out, it should be a pleasant day."
466
518
  ```
467
519
 
468
520
  ### Completions
@@ -472,7 +524,7 @@ Hit the OpenAI API for a completion using other GPT-3 models:
472
524
  ```ruby
473
525
  response = client.completions(
474
526
  parameters: {
475
- model: "gpt-3.5-turbo",
527
+ model: "gpt-4o",
476
528
  prompt: "Once upon a time",
477
529
  max_tokens: 5
478
530
  })
@@ -498,7 +550,7 @@ puts response.dig("data", 0, "embedding")
498
550
 
499
551
  ### Batches
500
552
 
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.
553
+ The Batches endpoint allows you to create and manage large batches of API requests to run asynchronously. Currently, the supported endpoints for batches are `/v1/chat/completions` (Chat Completions API) and `/v1/embeddings` (Embeddings API).
502
554
 
503
555
  To use the Batches endpoint, you need to first upload a JSONL file containing the batch requests using the Files endpoint. The file must be uploaded with the purpose set to `batch`. Each line in the JSONL file represents a single request and should have the following format:
504
556
 
@@ -508,7 +560,7 @@ To use the Batches endpoint, you need to first upload a JSONL file containing th
508
560
  "method": "POST",
509
561
  "url": "/v1/chat/completions",
510
562
  "body": {
511
- "model": "gpt-3.5-turbo",
563
+ "model": "gpt-4o",
512
564
  "messages": [
513
565
  { "role": "system", "content": "You are a helpful assistant." },
514
566
  { "role": "user", "content": "What is 2+2?" }
@@ -568,7 +620,7 @@ These files are in JSONL format, with each line representing the output or error
568
620
  "id": "chatcmpl-abc123",
569
621
  "object": "chat.completion",
570
622
  "created": 1677858242,
571
- "model": "gpt-3.5-turbo",
623
+ "model": "gpt-4o",
572
624
  "choices": [
573
625
  {
574
626
  "index": 0,
@@ -586,6 +638,8 @@ If a request fails with a non-HTTP error, the error object will contain more inf
586
638
 
587
639
  ### Files
588
640
 
641
+ #### For fine-tuning purposes
642
+
589
643
  Put your data in a `.jsonl` file like this:
590
644
 
591
645
  ```json
@@ -603,6 +657,23 @@ client.files.content(id: "file-123")
603
657
  client.files.delete(id: "file-123")
604
658
  ```
605
659
 
660
+ #### For assistant purposes
661
+
662
+ You can send a file path:
663
+
664
+ ```ruby
665
+ client.files.upload(parameters: { file: "path/to/file.pdf", purpose: "assistants" })
666
+ ```
667
+
668
+ or a File object
669
+
670
+ ```ruby
671
+ my_file = File.open("path/to/file.pdf", "rb")
672
+ client.files.upload(parameters: { file: my_file, purpose: "assistants" })
673
+ ```
674
+
675
+ See supported file types on [API documentation](https://platform.openai.com/docs/assistants/tools/file-search/supported-files).
676
+
606
677
  ### Finetunes
607
678
 
608
679
  Upload your fine-tuning data in a `.jsonl` file as above and get its ID:
@@ -618,7 +689,7 @@ You can then use this file ID to create a fine tuning job:
618
689
  response = client.finetunes.create(
619
690
  parameters: {
620
691
  training_file: file_id,
621
- model: "gpt-3.5-turbo"
692
+ model: "gpt-4o"
622
693
  })
623
694
  fine_tune_id = response["id"]
624
695
  ```
@@ -655,6 +726,139 @@ You can also capture the events for a job:
655
726
  client.finetunes.list_events(id: fine_tune_id)
656
727
  ```
657
728
 
729
+ ### Vector Stores
730
+
731
+ Vector Store objects give the File Search tool the ability to search your files.
732
+
733
+ You can create a new vector store:
734
+
735
+ ```ruby
736
+ response = client.vector_stores.create(
737
+ parameters: {
738
+ name: "my vector store",
739
+ file_ids: ["file-abc123", "file-def456"]
740
+ }
741
+ )
742
+
743
+ vector_store_id = response["id"]
744
+ ```
745
+
746
+ Given a `vector_store_id` you can `retrieve` the current field values:
747
+
748
+ ```ruby
749
+ client.vector_stores.retrieve(id: vector_store_id)
750
+ ```
751
+
752
+ You can get a `list` of all vector stores currently available under the organization:
753
+
754
+ ```ruby
755
+ client.vector_stores.list
756
+ ```
757
+
758
+ You can modify an existing vector store, except for the `file_ids`:
759
+
760
+ ```ruby
761
+ response = client.vector_stores.modify(
762
+ id: vector_store_id,
763
+ parameters: {
764
+ name: "Modified Test Vector Store",
765
+ }
766
+ )
767
+ ```
768
+
769
+ You can delete vector stores:
770
+
771
+ ```ruby
772
+ client.vector_stores.delete(id: vector_store_id)
773
+ ```
774
+
775
+ ### Vector Store Files
776
+
777
+ Vector store files represent files inside a vector store.
778
+
779
+ You can create a new vector store file by attaching a File to a vector store.
780
+
781
+ ```ruby
782
+ response = client.vector_store_files.create(
783
+ vector_store_id: "vector-store-abc123",
784
+ parameters: {
785
+ file_id: "file-abc123"
786
+ }
787
+ )
788
+
789
+ vector_store_file_id = response["id"]
790
+ ```
791
+
792
+ Given a `vector_store_file_id` you can `retrieve` the current field values:
793
+
794
+ ```ruby
795
+ client.vector_store_files.retrieve(
796
+ vector_store_id: "vector-store-abc123",
797
+ id: vector_store_file_id
798
+ )
799
+ ```
800
+
801
+ You can get a `list` of all vector store files currently available under the vector store:
802
+
803
+ ```ruby
804
+ client.vector_store_files.list(vector_store_id: "vector-store-abc123")
805
+ ```
806
+
807
+ You can delete a vector store file:
808
+
809
+ ```ruby
810
+ client.vector_store_files.delete(
811
+ vector_store_id: "vector-store-abc123",
812
+ id: vector_store_file_id
813
+ )
814
+ ```
815
+
816
+ Note: This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the delete file endpoint.
817
+
818
+ ### Vector Store File Batches
819
+
820
+ Vector store file batches represent operations to add multiple files to a vector store.
821
+
822
+ You can create a new vector store file batch by attaching multiple Files to a vector store.
823
+
824
+ ```ruby
825
+ response = client.vector_store_file_batches.create(
826
+ vector_store_id: "vector-store-abc123",
827
+ parameters: {
828
+ file_ids: ["file-abc123", "file-def456"]
829
+ }
830
+ )
831
+
832
+ file_batch_id = response["id"]
833
+ ```
834
+
835
+ Given a `file_batch_id` you can `retrieve` the current field values:
836
+
837
+ ```ruby
838
+ client.vector_store_file_batches.retrieve(
839
+ vector_store_id: "vector-store-abc123",
840
+ id: file_batch_id
841
+ )
842
+ ```
843
+
844
+ You can get a `list` of all vector store files in a batch currently available under the vector store:
845
+
846
+ ```ruby
847
+ client.vector_store_file_batches.list(
848
+ vector_store_id: "vector-store-abc123",
849
+ id: file_batch_id
850
+ )
851
+ ```
852
+
853
+ You can cancel a vector store file batch (This attempts to cancel the processing of files in this batch as soon as possible):
854
+
855
+ ```ruby
856
+ client.vector_store_file_batches.cancel(
857
+ vector_store_id: "vector-store-abc123",
858
+ id: file_batch_id
859
+ )
860
+ ```
861
+
658
862
  ### Assistants
659
863
 
660
864
  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)).
@@ -664,16 +868,20 @@ To create a new assistant:
664
868
  ```ruby
665
869
  response = client.assistants.create(
666
870
  parameters: {
667
- model: "gpt-3.5-turbo",
871
+ model: "gpt-4o",
668
872
  name: "OpenAI-Ruby test assistant",
669
873
  description: nil,
670
874
  instructions: "You are a Ruby dev bot. When asked a question, write and run Ruby code to answer the question",
671
875
  tools: [
672
876
  { type: "code_interpreter" },
877
+ { type: "file_search" }
673
878
  ],
674
879
  tool_resources: {
675
- "code_interpreter": {
676
- "file_ids": [] # See Files section above for how to upload files
880
+ code_interpreter: {
881
+ file_ids: [] # See Files section above for how to upload files
882
+ },
883
+ file_search: {
884
+ vector_store_ids: [] # See Vector Stores section above for how to add vector stores
677
885
  }
678
886
  },
679
887
  "metadata": { my_internal_version_id: "1.0.0" }
@@ -995,7 +1203,7 @@ response = client.audio.transcribe(
995
1203
  parameters: {
996
1204
  model: "whisper-1",
997
1205
  file: File.open("path_to_file", "rb"),
998
- language: "en" # Optional.
1206
+ language: "en" # Optional
999
1207
  })
1000
1208
  puts response["text"]
1001
1209
  # => "Transcription of the text"
@@ -1010,7 +1218,9 @@ response = client.audio.speech(
1010
1218
  parameters: {
1011
1219
  model: "tts-1",
1012
1220
  input: "This is a speech test!",
1013
- voice: "alloy"
1221
+ voice: "alloy",
1222
+ response_format: "mp3", # Optional
1223
+ speed: 1.0 # Optional
1014
1224
  }
1015
1225
  )
1016
1226
  File.binwrite('demo.mp3', response)
@@ -1023,7 +1233,7 @@ HTTP errors can be caught like this:
1023
1233
 
1024
1234
  ```
1025
1235
  begin
1026
- OpenAI::Client.new.models.retrieve(id: "gpt-3.5-turbo")
1236
+ OpenAI::Client.new.models.retrieve(id: "gpt-4o")
1027
1237
  rescue Faraday::Error => e
1028
1238
  raise "Got a Faraday error: #{e}"
1029
1239
  end
@@ -4,8 +4,8 @@ module OpenAI
4
4
  @client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION)
5
5
  end
6
6
 
7
- def list
8
- @client.get(path: "/batches")
7
+ def list(parameters: {})
8
+ @client.get(path: "/batches", parameters: parameters)
9
9
  end
10
10
 
11
11
  def retrieve(id:)
data/lib/openai/client.rb CHANGED
@@ -2,6 +2,7 @@ module OpenAI
2
2
  class Client
3
3
  include OpenAI::HTTP
4
4
 
5
+ SENSITIVE_ATTRIBUTES = %i[@access_token @organization_id @extra_headers].freeze
5
6
  CONFIG_KEYS = %i[
6
7
  api_type
7
8
  api_version
@@ -78,6 +79,18 @@ module OpenAI
78
79
  @run_steps ||= OpenAI::RunSteps.new(client: self)
79
80
  end
80
81
 
82
+ def vector_stores
83
+ @vector_stores ||= OpenAI::VectorStores.new(client: self)
84
+ end
85
+
86
+ def vector_store_files
87
+ @vector_store_files ||= OpenAI::VectorStoreFiles.new(client: self)
88
+ end
89
+
90
+ def vector_store_file_batches
91
+ @vector_store_file_batches ||= OpenAI::VectorStoreFileBatches.new(client: self)
92
+ end
93
+
81
94
  def batches
82
95
  @batches ||= OpenAI::Batches.new(client: self)
83
96
  end
@@ -95,5 +108,15 @@ module OpenAI
95
108
  client.add_headers("OpenAI-Beta": apis.map { |k, v| "#{k}=#{v}" }.join(";"))
96
109
  end
97
110
  end
111
+
112
+ def inspect
113
+ vars = instance_variables.map do |var|
114
+ value = instance_variable_get(var)
115
+
116
+ SENSITIVE_ATTRIBUTES.include?(var) ? "#{var}=[REDACTED]" : "#{var}=#{value.inspect}"
117
+ end
118
+
119
+ "#<#{self.class}:#{object_id} #{vars.join(', ')}>"
120
+ end
98
121
  end
99
122
  end
data/lib/openai/files.rb CHANGED
@@ -4,14 +4,15 @@ module OpenAI
4
4
  assistants
5
5
  batch
6
6
  fine-tune
7
+ vision
7
8
  ].freeze
8
9
 
9
10
  def initialize(client:)
10
11
  @client = client
11
12
  end
12
13
 
13
- def list
14
- @client.get(path: "/files")
14
+ def list(parameters: {})
15
+ @client.get(path: "/files", parameters: parameters)
15
16
  end
16
17
 
17
18
  def upload(parameters: {})
@@ -19,7 +19,7 @@ module OpenAI
19
19
  "Content-Type" => "application/json",
20
20
  "Authorization" => "Bearer #{@access_token}",
21
21
  "OpenAI-Organization" => @organization_id
22
- }
22
+ }.compact
23
23
  end
24
24
 
25
25
  def azure_headers
@@ -16,8 +16,12 @@ module OpenAI
16
16
  @client.json_post(path: "/threads/#{thread_id}/messages", parameters: parameters)
17
17
  end
18
18
 
19
- def modify(id:, thread_id:, parameters: {})
19
+ def modify(thread_id:, id:, parameters: {})
20
20
  @client.json_post(path: "/threads/#{thread_id}/messages/#{id}", parameters: parameters)
21
21
  end
22
+
23
+ def delete(thread_id:, id:)
24
+ @client.delete(path: "/threads/#{thread_id}/messages/#{id}")
25
+ end
22
26
  end
23
27
  end
@@ -0,0 +1,29 @@
1
+ module OpenAI
2
+ class VectorStoreFileBatches
3
+ def initialize(client:)
4
+ @client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION)
5
+ end
6
+
7
+ def list(vector_store_id:, id:, parameters: {})
8
+ @client.get(
9
+ path: "/vector_stores/#{vector_store_id}/file_batches/#{id}/files",
10
+ parameters: parameters
11
+ )
12
+ end
13
+
14
+ def retrieve(vector_store_id:, id:)
15
+ @client.get(path: "/vector_stores/#{vector_store_id}/file_batches/#{id}")
16
+ end
17
+
18
+ def create(vector_store_id:, parameters: {})
19
+ @client.json_post(
20
+ path: "/vector_stores/#{vector_store_id}/file_batches",
21
+ parameters: parameters
22
+ )
23
+ end
24
+
25
+ def cancel(vector_store_id:, id:)
26
+ @client.post(path: "/vector_stores/#{vector_store_id}/file_batches/#{id}/cancel")
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ module OpenAI
2
+ class VectorStoreFiles
3
+ def initialize(client:)
4
+ @client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION)
5
+ end
6
+
7
+ def list(vector_store_id:, parameters: {})
8
+ @client.get(path: "/vector_stores/#{vector_store_id}/files", parameters: parameters)
9
+ end
10
+
11
+ def retrieve(vector_store_id:, id:)
12
+ @client.get(path: "/vector_stores/#{vector_store_id}/files/#{id}")
13
+ end
14
+
15
+ def create(vector_store_id:, parameters: {})
16
+ @client.json_post(path: "/vector_stores/#{vector_store_id}/files", parameters: parameters)
17
+ end
18
+
19
+ def delete(vector_store_id:, id:)
20
+ @client.delete(path: "/vector_stores/#{vector_store_id}/files/#{id}")
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ module OpenAI
2
+ class VectorStores
3
+ def initialize(client:)
4
+ @client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION)
5
+ end
6
+
7
+ def list(parameters: {})
8
+ @client.get(path: "/vector_stores", parameters: parameters)
9
+ end
10
+
11
+ def retrieve(id:)
12
+ @client.get(path: "/vector_stores/#{id}")
13
+ end
14
+
15
+ def create(parameters: {})
16
+ @client.json_post(path: "/vector_stores", parameters: parameters)
17
+ end
18
+
19
+ def modify(id:, parameters: {})
20
+ @client.json_post(path: "/vector_stores/#{id}", parameters: parameters)
21
+ end
22
+
23
+ def delete(id:)
24
+ @client.delete(path: "/vector_stores/#{id}")
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module OpenAI
2
- VERSION = "7.0.1".freeze
2
+ VERSION = "7.2.0".freeze
3
3
  end
data/lib/openai.rb CHANGED
@@ -12,6 +12,9 @@ require_relative "openai/threads"
12
12
  require_relative "openai/messages"
13
13
  require_relative "openai/runs"
14
14
  require_relative "openai/run_steps"
15
+ require_relative "openai/vector_stores"
16
+ require_relative "openai/vector_store_files"
17
+ require_relative "openai/vector_store_file_batches"
15
18
  require_relative "openai/audio"
16
19
  require_relative "openai/version"
17
20
  require_relative "openai/batches"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-openai
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.1
4
+ version: 7.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-04-29 00:00:00.000000000 Z
11
+ date: 2024-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: event_stream_parser
@@ -102,6 +102,9 @@ files:
102
102
  - lib/openai/run_steps.rb
103
103
  - lib/openai/runs.rb
104
104
  - lib/openai/threads.rb
105
+ - lib/openai/vector_store_file_batches.rb
106
+ - lib/openai/vector_store_files.rb
107
+ - lib/openai/vector_stores.rb
105
108
  - lib/openai/version.rb
106
109
  - lib/ruby/openai.rb
107
110
  - pull_request_template.md
@@ -130,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
133
  - !ruby/object:Gem::Version
131
134
  version: '0'
132
135
  requirements: []
133
- rubygems_version: 3.5.9
136
+ rubygems_version: 3.5.11
134
137
  signing_key:
135
138
  specification_version: 4
136
139
  summary: "OpenAI API + Ruby! \U0001F916❤️"