├── spec ├── fixtures │ ├── files │ │ ├── text.txt │ │ ├── image.png │ │ ├── mask.png │ │ ├── audio_sample.mp3 │ │ ├── somatosensory.pdf │ │ ├── sentiment.jsonl │ │ ├── errors │ │ │ └── missing_quote.jsonl │ │ ├── train.jsonl │ │ ├── batch.jsonl │ │ └── sarcastic.jsonl │ └── cassettes │ │ ├── gpt-3_5-turbo_streamed_chat_with_error_response.yml │ │ ├── mocks │ │ ├── gpt-3_5-turbo_streamed_chat_with_error_response.yml │ │ ├── http_get_with_error_response.yml │ │ └── gpt-3_5-turbo_streamed_chat_with_json_error_response.yml │ │ ├── http_get_with_error_response.yml │ │ ├── gpt-3_5-turbo_streamed_chat_with_json_error_response.yml │ │ ├── conversations_create.yml │ │ ├── conversations_create_setup.yml │ │ ├── conversations_delete_setup.yml │ │ ├── conversations_get_item_setup.yml │ │ ├── conversations_modify_setup.yml │ │ ├── conversations_retrieve_setup.yml │ │ ├── conversations_create_items_setup.yml │ │ ├── conversations_delete_item_setup.yml │ │ ├── conversations_list_items_setup.yml │ │ ├── conversations_modify.yml │ │ ├── ollama_llama3_chat.yml │ │ ├── conversations_create_items.yml │ │ ├── models_retrieve.yml │ │ ├── conversations_delete_item_item_setup.yml │ │ ├── conversations_get_item_item_setup.yml │ │ ├── conversations_list_items_item_setup.yml │ │ ├── files_delete.yml │ │ ├── vector_stores_delete.yml │ │ ├── messages_delete_thread_setup.yml │ │ ├── messages_delete.yml │ │ ├── finetunes_retrieve.yml │ │ ├── vector_store_files_delete.yml │ │ ├── finetunes_cancel.yml │ │ ├── finetunes_event_list.yml │ │ ├── responses_delete.yml │ │ ├── files_content.yml │ │ ├── run_steps_retrieve_thread_setup.yml │ │ ├── files_retrieve.yml │ │ ├── files_delete_retrieve.yml │ │ ├── models_delete.yml │ │ ├── vector_stores_search_setup.yml │ │ ├── vector_store_files_retrieve.yml │ │ ├── vector_store_files_create.yml │ │ ├── run_steps_retrieve.yml │ │ ├── vector_store_files_delete_vector_store_file_setup.yml │ │ ├── vector_store_files_list_vector_store_file_setup.yml │ │ ├── vector_store_files_retrieve_vector_store_file_setup.yml │ │ ├── files_fetch_image_retrieve.yml │ │ ├── finetunes_retrieve_for_create.yml │ │ ├── files_fetch_image.yml │ │ ├── vector_store_file_batches_retrieve.yml │ │ ├── vector_store_file_batches_create.yml │ │ ├── vector_stores_create.yml │ │ ├── vector_store_file_batches_cancel_vector_store_file_batch_setup.yml │ │ ├── vector_store_file_batches_retrieve_vector_store_file_batch_setup.yml │ │ ├── vector_stores_delete_setup.yml │ │ ├── vector_stores_modify_setup.yml │ │ ├── vector_stores_retrieve_setup.yml │ │ ├── vector_store_file_batches_cancel.yml │ │ ├── vector_store_files_list_vector_store_setup.yml │ │ ├── batches_cancel.yml │ │ ├── vector_store_files_create_vector_store_setup.yml │ │ ├── vector_store_files_delete_vector_store_setup.yml │ │ ├── vector_store_files_retrieve_vector_store_setup.yml │ │ ├── vector_stores_list_setup.yml │ │ ├── vector_store_file_batches_cancel_vector_store_setup.yml │ │ ├── vector_store_file_batches_create_vector_store_setup.yml │ │ ├── vector_stores_retrieve.yml │ │ ├── vector_store_file_batches_retrieve_vector_store_setup.yml │ │ ├── vector_store_files_list.yml │ │ ├── vector_store_file_batches_list_vector_store_file_batch_setup.yml │ │ ├── vector_stores_modify.yml │ │ ├── vector_store_file_batches_list_vector_store_setup.yml │ │ └── images_generate_A_baby_sea_otter_cooking_pasta_wearing_a_hat_of_some_sort.yml ├── openai │ └── client │ │ ├── moderations_spec.rb │ │ ├── embeddings_spec.rb │ │ ├── completions_spec.rb │ │ ├── usage_spec.rb │ │ ├── realtime_spec.rb │ │ ├── models_spec.rb │ │ ├── threads_spec.rb │ │ ├── run_steps_spec.rb │ │ ├── audio_spec.rb │ │ └── assistants_spec.rb ├── support │ └── vcr_multipart_matcher.rb ├── spec_helper.rb └── openai_spec.rb ├── lib ├── ruby │ └── openai.rb └── openai │ ├── version.rb │ ├── models.rb │ ├── run_steps.rb │ ├── audio.rb │ ├── batches.rb │ ├── threads.rb │ ├── responses.rb │ ├── finetunes.rb │ ├── assistants.rb │ ├── realtime.rb │ ├── vector_store_files.rb │ ├── http_headers.rb │ ├── messages.rb │ ├── vector_stores.rb │ ├── vector_store_file_batches.rb │ ├── images.rb │ ├── conversations.rb │ ├── runs.rb │ ├── stream.rb │ ├── usage.rb │ └── files.rb ├── .rspec ├── bin ├── setup └── console ├── CONTRIBUTING.md ├── .github ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── FUNDING.yml ├── pull_request_template.md ├── .devcontainer ├── Dockerfile ├── docker-compose.yml └── devcontainer.json ├── Rakefile ├── Gemfile ├── SECURITY.md ├── .rubocop.yml ├── LICENSE.txt ├── .circleci └── config.yml ├── ruby-openai.gemspec ├── .gitignore └── Gemfile.lock /spec/fixtures/files/text.txt: -------------------------------------------------------------------------------- 1 | Hello world! -------------------------------------------------------------------------------- /lib/ruby/openai.rb: -------------------------------------------------------------------------------- 1 | require_relative "../openai" 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /lib/openai/version.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | VERSION = "8.3.0".freeze 3 | end 4 | -------------------------------------------------------------------------------- /spec/fixtures/files/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexrudall/ruby-openai/HEAD/spec/fixtures/files/image.png -------------------------------------------------------------------------------- /spec/fixtures/files/mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexrudall/ruby-openai/HEAD/spec/fixtures/files/mask.png -------------------------------------------------------------------------------- /spec/fixtures/files/audio_sample.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexrudall/ruby-openai/HEAD/spec/fixtures/files/audio_sample.mp3 -------------------------------------------------------------------------------- /spec/fixtures/files/somatosensory.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexrudall/ruby-openai/HEAD/spec/fixtures/files/somatosensory.pdf -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /spec/fixtures/files/sentiment.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"Overjoyed with my new phone! ->", "completion":" positive"} 2 | {"prompt":"@lakers disappoint for a third straight night ->", "completion":" negative"} 3 | -------------------------------------------------------------------------------- /spec/fixtures/files/errors/missing_quote.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"Overjoyed with my new phone! ->", "completion":" positive"} 2 | {"prompt":"@lakers disappoint for a third straight night ->", "completion":" negative} 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | Bug reports and pull requests are welcome on GitHub at https://github.com/alexrudall/ruby-openai. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/alexrudall/ruby-openai/blob/main/CODE_OF_CONDUCT.md). 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: bundler 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: webmock 10 | versions: 11 | - 3.11.1 12 | - 3.11.3 13 | - dependency-name: rspec 14 | versions: 15 | - 3.10.0 16 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## All Submissions: 2 | 3 | * [ ] Have you followed the guidelines in our [Contributing document](../blob/main/CONTRIBUTING.md)? 4 | * [ ] Have you checked to ensure there aren't other open [Pull Requests](../pulls) for the same update/change? 5 | * [ ] Have you added an explanation of what your changes do and why you'd like us to include them? 6 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:3.2.2-slim-bullseye 2 | 3 | ENV TZ="Europe/London" 4 | 5 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 6 | && apt-get -y install --no-install-recommends \ 7 | apt-utils \ 8 | build-essential \ 9 | curl \ 10 | git \ 11 | vim \ 12 | zsh 13 | 14 | RUN gem install bundler 15 | 16 | WORKDIR /workspace 17 | -------------------------------------------------------------------------------- /spec/fixtures/files/train.jsonl: -------------------------------------------------------------------------------- 1 | {"text": "good film, but very glum.", "label": "Positive"} 2 | {"text": "i sympathize with the plight of these families, but the movie doesn't do a very good job conveying the issue at hand.", "label": "Negative"} 3 | {"text": "the very definition of the `small' movie, but it is a good stepping stone for director sprecher.", "label": "Positive"} 4 | -------------------------------------------------------------------------------- /.devcontainer/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | app: 5 | build: 6 | context: .. 7 | dockerfile: .devcontainer/Dockerfile 8 | 9 | volumes: 10 | - ..:/workspace:cached 11 | - bundle_cache:/bundle 12 | 13 | command: sleep infinity 14 | 15 | environment: 16 | TZ: Europe/London 17 | 18 | volumes: 19 | bundle_cache: 20 | -------------------------------------------------------------------------------- /lib/openai/models.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Models 3 | def initialize(client:) 4 | @client = client 5 | end 6 | 7 | def list 8 | @client.get(path: "/models") 9 | end 10 | 11 | def retrieve(id:) 12 | @client.get(path: "/models/#{id}") 13 | end 14 | 15 | def delete(id:) 16 | @client.delete(path: "/models/#{id}") 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "openai" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start(__FILE__) 15 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | require "rubocop/rake_task" 4 | 5 | RSpec::Core::RakeTask.new(:spec) 6 | 7 | task :default do 8 | Rake::Task["test"].invoke 9 | Rake::Task["lint"].invoke 10 | end 11 | 12 | task :test do 13 | Rake::Task["spec"].invoke 14 | end 15 | 16 | task :lint do 17 | RuboCop::RakeTask.new(:rubocop) 18 | Rake::Task["rubocop"].invoke 19 | end 20 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Include gem dependencies from ruby-openai.gemspec 4 | gemspec 5 | 6 | # Development dependencies. Not included in the publised gem. 7 | gem "byebug", "~> 11.1.3" 8 | gem "dotenv", "~> 2.8.1" # >= v3 will require removing support for Ruby 2.7 from CI. 9 | gem "rake", "~> 13.2.1" 10 | gem "rspec", "~> 3.13" 11 | gem "rubocop", "~> 1.74.0" 12 | gem "vcr", "~> 6.3.1" 13 | gem "webmock", "~> 3.25.1" 14 | -------------------------------------------------------------------------------- /spec/fixtures/files/batch.jsonl: -------------------------------------------------------------------------------- 1 | {"custom_id": "request-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-3.5-turbo", "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is 2+2?"}]}} 2 | {"custom_id": "request-2", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-3.5-turbo", "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is 3+2?"}]}} 3 | -------------------------------------------------------------------------------- /lib/openai/run_steps.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class RunSteps 3 | def initialize(client:) 4 | @client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION) 5 | end 6 | 7 | def list(thread_id:, run_id:, parameters: {}) 8 | @client.get(path: "/threads/#{thread_id}/runs/#{run_id}/steps", parameters: parameters) 9 | end 10 | 11 | def retrieve(thread_id:, run_id:, id:, parameters: {}) 12 | @client.get(path: "/threads/#{thread_id}/runs/#{run_id}/steps/#{id}", parameters: parameters) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 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". 10 | -------------------------------------------------------------------------------- /lib/openai/audio.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Audio 3 | def initialize(client:) 4 | @client = client 5 | end 6 | 7 | def transcribe(parameters: {}) 8 | @client.multipart_post(path: "/audio/transcriptions", parameters: parameters) 9 | end 10 | 11 | def translate(parameters: {}) 12 | @client.multipart_post(path: "/audio/translations", parameters: parameters) 13 | end 14 | 15 | def speech(parameters: {}) 16 | @client.json_post(path: "/audio/speech", parameters: parameters) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/openai/batches.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Batches 3 | def initialize(client:) 4 | @client = client 5 | end 6 | 7 | def list(parameters: {}) 8 | @client.get(path: "/batches", parameters: parameters) 9 | end 10 | 11 | def retrieve(id:) 12 | @client.get(path: "/batches/#{id}") 13 | end 14 | 15 | def create(parameters: {}) 16 | @client.json_post(path: "/batches", parameters: parameters) 17 | end 18 | 19 | def cancel(id:) 20 | @client.post(path: "/batches/#{id}/cancel") 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/openai/client/moderations_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe OpenAI::Client do 2 | describe "#moderations", :vcr do 3 | let(:input) { "Have a great day!" } 4 | let(:cassette) { "moderations #{input}".downcase } 5 | let(:response) do 6 | OpenAI::Client.new.moderations( 7 | parameters: { 8 | input: input 9 | } 10 | ) 11 | end 12 | 13 | it "succeeds" do 14 | VCR.use_cassette(cassette) do 15 | expect(response.dig("results", 0, "categories", "hate")).to eq(false) 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/openai/threads.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Threads 3 | def initialize(client:) 4 | @client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION) 5 | end 6 | 7 | def retrieve(id:) 8 | @client.get(path: "/threads/#{id}") 9 | end 10 | 11 | def create(parameters: {}) 12 | @client.json_post(path: "/threads", parameters: parameters) 13 | end 14 | 15 | def modify(id:, parameters: {}) 16 | @client.json_post(path: "/threads/#{id}", parameters: parameters) 17 | end 18 | 19 | def delete(id:) 20 | @client.delete(path: "/threads/#{id}") 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/openai/responses.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Responses 3 | def initialize(client:) 4 | @client = client 5 | end 6 | 7 | def create(parameters: {}) 8 | @client.json_post(path: "/responses", parameters: parameters) 9 | end 10 | 11 | def retrieve(response_id:) 12 | @client.get(path: "/responses/#{response_id}") 13 | end 14 | 15 | def delete(response_id:) 16 | @client.delete(path: "/responses/#{response_id}") 17 | end 18 | 19 | def input_items(response_id:, parameters: {}) 20 | @client.get(path: "/responses/#{response_id}/input_items", parameters: parameters) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | TargetRubyVersion: 2.6 3 | NewCops: enable 4 | SuggestExtensions: false 5 | 6 | Style/Documentation: 7 | # Skips checking to make sure top level modules / classes have a comment. 8 | Enabled: false 9 | 10 | Layout/LineLength: 11 | Max: 100 12 | Exclude: 13 | - "**/*.gemspec" 14 | 15 | Lint/AmbiguousOperator: 16 | # https://github.com/rubocop/rubocop/issues/4294 17 | Exclude: 18 | - "lib/openai/client.rb" 19 | 20 | Metrics/AbcSize: 21 | Max: 20 22 | 23 | Metrics/BlockLength: 24 | Exclude: 25 | - "spec/**/*" 26 | 27 | Style/StringLiterals: 28 | EnforcedStyle: double_quotes 29 | 30 | Style/FrozenStringLiteralComment: 31 | Enabled: false 32 | -------------------------------------------------------------------------------- /lib/openai/finetunes.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Finetunes 3 | def initialize(client:) 4 | @client = client 5 | end 6 | 7 | def list 8 | @client.get(path: "/fine_tuning/jobs") 9 | end 10 | 11 | def create(parameters: {}) 12 | @client.json_post(path: "/fine_tuning/jobs", parameters: parameters) 13 | end 14 | 15 | def retrieve(id:) 16 | @client.get(path: "/fine_tuning/jobs/#{id}") 17 | end 18 | 19 | def cancel(id:) 20 | @client.json_post(path: "/fine_tuning/jobs/#{id}/cancel", parameters: {}) 21 | end 22 | 23 | def list_events(id:) 24 | @client.get(path: "/fine_tuning/jobs/#{id}/events") 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /spec/openai/client/embeddings_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe OpenAI::Client do 2 | describe "#embeddings", :vcr do 3 | let(:input) { "The food was delicious and the waiter..." } 4 | let(:cassette) { "#{model} embeddings #{input}".downcase } 5 | let(:response) do 6 | OpenAI::Client.new.embeddings( 7 | parameters: { 8 | model: model, 9 | input: input 10 | } 11 | ) 12 | end 13 | 14 | context "with model: text-embedding-ada-002" do 15 | let(:model) { "text-embedding-ada-002" } 16 | 17 | it "succeeds" do 18 | VCR.use_cassette(cassette) do 19 | expect(response.dig("data", 0, "object")).to eq("embedding") 20 | end 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/openai/assistants.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Assistants 3 | BETA_VERSION = "v2".freeze 4 | 5 | def initialize(client:) 6 | @client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION) 7 | end 8 | 9 | def list 10 | @client.get(path: "/assistants") 11 | end 12 | 13 | def retrieve(id:) 14 | @client.get(path: "/assistants/#{id}") 15 | end 16 | 17 | def create(parameters: {}) 18 | @client.json_post(path: "/assistants", parameters: parameters) 19 | end 20 | 21 | def modify(id:, parameters: {}) 22 | @client.json_post(path: "/assistants/#{id}", parameters: parameters) 23 | end 24 | 25 | def delete(id:) 26 | @client.delete(path: "/assistants/#{id}") 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: alexrudall 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /lib/openai/realtime.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Realtime 3 | def initialize(client:) 4 | @client = client.beta(realtime: "v1") 5 | end 6 | 7 | # Create a new real-time session with OpenAI. 8 | # 9 | # This method sets up a new session for real-time voice interaction with an OpenAI model. 10 | # It returns session details that can be used to establish a WebRTC connection. 11 | # 12 | # @param parameters [Hash] parameters for the session (see: https://platform.openai.com/docs/api-reference/realtime-sessions/create) 13 | # @return [Hash] Session details including session ID, ICE servers, and other 14 | # connection information 15 | def create(parameters: {}) 16 | @client.json_post(path: "/realtime/sessions", parameters: parameters) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/openai/vector_store_files.rb: -------------------------------------------------------------------------------- 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 24 | -------------------------------------------------------------------------------- /lib/openai/http_headers.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | module HTTPHeaders 3 | def add_headers(headers) 4 | @extra_headers = extra_headers.merge(headers.transform_keys(&:to_s)) 5 | end 6 | 7 | private 8 | 9 | def headers 10 | if azure? 11 | azure_headers 12 | else 13 | openai_headers 14 | end.merge(extra_headers) 15 | end 16 | 17 | def openai_headers 18 | { 19 | "Content-Type" => "application/json", 20 | "Authorization" => "Bearer #{@access_token}", 21 | "OpenAI-Organization" => @organization_id 22 | }.compact 23 | end 24 | 25 | def azure_headers 26 | { 27 | "Content-Type" => "application/json", 28 | "api-key" => @access_token 29 | } 30 | end 31 | 32 | def extra_headers 33 | @extra_headers ||= {} 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/openai/client/completions_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe OpenAI::Client do 2 | describe "#completions: gpt-3.5-turbo-instruct" do 3 | context "with a prompt and max_tokens", :vcr do 4 | let(:prompt) { "Once upon a time" } 5 | let(:max_tokens) { 5 } 6 | 7 | let(:response) do 8 | OpenAI::Client.new.completions( 9 | parameters: { 10 | model: model, 11 | prompt: prompt, 12 | max_tokens: max_tokens 13 | } 14 | ) 15 | end 16 | let(:text) { response.dig("choices", 0, "text") } 17 | let(:cassette) { "#{model} completions #{prompt}".downcase } 18 | let(:model) { "gpt-3.5-turbo-instruct" } 19 | 20 | it "succeeds" do 21 | VCR.use_cassette(cassette) do 22 | expect(text.split.empty?).to eq(false) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/openai/messages.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Messages 3 | def initialize(client:) 4 | @client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION) 5 | end 6 | 7 | def list(thread_id:, parameters: {}) 8 | @client.get(path: "/threads/#{thread_id}/messages", parameters: parameters) 9 | end 10 | 11 | def retrieve(thread_id:, id:) 12 | @client.get(path: "/threads/#{thread_id}/messages/#{id}") 13 | end 14 | 15 | def create(thread_id:, parameters: {}) 16 | @client.json_post(path: "/threads/#{thread_id}/messages", parameters: parameters) 17 | end 18 | 19 | def modify(thread_id:, id:, parameters: {}) 20 | @client.json_post(path: "/threads/#{thread_id}/messages/#{id}", parameters: parameters) 21 | end 22 | 23 | def delete(thread_id:, id:) 24 | @client.delete(path: "/threads/#{thread_id}/messages/#{id}") 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/openai/vector_stores.rb: -------------------------------------------------------------------------------- 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 | 27 | def search(id:, parameters: {}) 28 | @client.json_post(path: "/vector_stores/#{id}/search", parameters: parameters) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gpt-3_5-turbo_streamed_chat_with_error_response.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/chat/completions 6 | body: 7 | encoding: UTF-8 8 | string: '{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"Hello!"}],"stream":true}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 500 23 | message: Internal Server Error 24 | headers: 25 | Date: 26 | - Mon, 14 Aug 2023 15:02:13 GMT 27 | recorded_at: Mon, 14 Aug 2023 15:02:13 GMT 28 | 29 | recorded_with: VCR 6.1.0 30 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/mocks/gpt-3_5-turbo_streamed_chat_with_error_response.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/chat/completions 6 | body: 7 | encoding: UTF-8 8 | string: '{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"Hello!"}],"stream":true}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 500 23 | message: Internal Server Error 24 | headers: 25 | Date: 26 | - Mon, 14 Aug 2023 15:02:13 GMT 27 | recorded_at: Mon, 14 Aug 2023 15:02:13 GMT 28 | 29 | recorded_with: VCR 6.1.0 30 | -------------------------------------------------------------------------------- /lib/openai/vector_store_file_batches.rb: -------------------------------------------------------------------------------- 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 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/http_get_with_error_response.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/models/text-ada-001 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 400 23 | message: Bad Request 24 | headers: 25 | Date: 26 | - Mon, 14 Aug 2023 15:02:13 GMT 27 | Content-Type: 28 | - application/json 29 | body: 30 | encoding: UTF-8 31 | string: |+ 32 | { 33 | "error": { 34 | "message": "Test error", 35 | "type": "test_error", 36 | "param": null, 37 | "code": "test" 38 | } 39 | } 40 | recorded_at: Mon, 14 Aug 2023 15:02:13 GMT 41 | recorded_with: VCR 6.1.0 42 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/mocks/http_get_with_error_response.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/models/text-ada-001 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 400 23 | message: Bad Request 24 | headers: 25 | Date: 26 | - Mon, 14 Aug 2023 15:02:13 GMT 27 | Content-Type: 28 | - application/json 29 | body: 30 | encoding: UTF-8 31 | string: |+ 32 | { 33 | "error": { 34 | "message": "Test error", 35 | "type": "test_error", 36 | "param": null, 37 | "code": "test" 38 | } 39 | } 40 | recorded_at: Mon, 14 Aug 2023 15:02:13 GMT 41 | recorded_with: VCR 6.1.0 42 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Alex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/openai/images.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Images 3 | def initialize(client: nil) 4 | @client = client 5 | end 6 | 7 | def generate(parameters: {}) 8 | @client.json_post(path: "/images/generations", parameters: parameters) 9 | end 10 | 11 | def edit(parameters: {}) 12 | @client.multipart_post(path: "/images/edits", parameters: open_files(parameters)) 13 | end 14 | 15 | def variations(parameters: {}) 16 | @client.multipart_post(path: "/images/variations", parameters: open_files(parameters)) 17 | end 18 | 19 | private 20 | 21 | def open_files(parameters) 22 | params = parameters.dup 23 | 24 | if params[:image].is_a?(Array) 25 | process_image_array(params) 26 | else 27 | params[:image] = File.open(params[:image]) 28 | end 29 | 30 | params[:mask] = File.open(params[:mask]) if params[:mask] 31 | params 32 | end 33 | 34 | def process_image_array(params) 35 | params[:image].each_with_index do |img_path, index| 36 | params[:"image[#{index}]"] = File.open(img_path) 37 | end 38 | params.delete(:image) 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/openai/client/usage_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe OpenAI::Client do 2 | describe "#usage" do 3 | let(:some_time_ago) { 1_739_096_063 } 4 | let(:client) { OpenAI::Client.new } 5 | 6 | shared_examples "usage endpoint" do |endpoint_name| 7 | describe "##{endpoint_name}" do 8 | let(:response) do 9 | client.usage.public_send(endpoint_name, parameters: { start_time: some_time_ago }) 10 | end 11 | 12 | it "retrieves #{endpoint_name} usage data" do 13 | VCR.use_cassette("usage_#{endpoint_name}") do 14 | expect(response["object"]).to eq("page") 15 | expect(response["data"]).to be_an(Array) 16 | expect(response["has_more"]).to be(true).or be(false) 17 | expect(response.dig("data", 0, "object")).to eq("bucket") 18 | end 19 | end 20 | end 21 | end 22 | 23 | %w[ 24 | completions 25 | embeddings 26 | moderations 27 | images 28 | audio_speeches 29 | audio_transcriptions 30 | vector_stores 31 | costs 32 | ].each do |endpoint| 33 | include_examples "usage endpoint", endpoint 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/openai/client/realtime_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe OpenAI::Realtime do 2 | let(:client) { OpenAI::Client.new } 3 | let(:realtime) { client.realtime } 4 | 5 | describe "#create" do 6 | it "uses the specified model" do 7 | model = "gpt-4o-realtime-preview-2024-12-18" 8 | VCR.use_cassette("realtime_session_create") do 9 | response = realtime.create(parameters: { model: model }) 10 | expect(response["model"]).to eq(model) 11 | end 12 | end 13 | 14 | context "with additional parameters" do 15 | it "sends all parameters to the API" do 16 | parameters = { 17 | model: "gpt-4o-realtime-preview-2024-12-17", 18 | voice: "alloy", 19 | instructions: "You are a helpful assistant." 20 | } 21 | 22 | VCR.use_cassette("realtime_session_create_with_params") do 23 | response = realtime.create(parameters: parameters) 24 | expect(response["model"]).to eq(parameters[:model]) 25 | expect(response["voice"]).to eq(parameters[:voice]) 26 | expect(response["instructions"]).to eq(parameters[:instructions]) 27 | end 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 # Use 2.1 to enable using orbs and other features. 2 | 3 | # Declare the orbs that we'll use in our config. 4 | orbs: 5 | ruby: circleci/ruby@1.0 6 | 7 | jobs: 8 | rubocop: 9 | parallelism: 1 10 | docker: 11 | - image: cimg/ruby:3.2-node 12 | steps: 13 | - checkout 14 | - ruby/install-deps 15 | - run: 16 | name: Run Rubocop 17 | command: bundle exec rubocop 18 | test: 19 | parameters: 20 | ruby-image: 21 | type: string 22 | parallelism: 1 23 | docker: 24 | - image: << parameters.ruby-image >> 25 | steps: 26 | - checkout 27 | - ruby/install-deps 28 | - run: 29 | name: Run tests 30 | command: bundle exec rspec -fd 31 | 32 | workflows: 33 | version: 2 34 | checks: 35 | jobs: 36 | - rubocop 37 | - test: 38 | matrix: 39 | parameters: 40 | ruby-image: 41 | - cimg/ruby:2.7-node 42 | - cimg/ruby:3.0-node 43 | - cimg/ruby:3.1-node 44 | - cimg/ruby:3.2-node 45 | - cimg/ruby:3.3-node 46 | - cimg/ruby:3.4-node 47 | -------------------------------------------------------------------------------- /lib/openai/conversations.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Conversations 3 | def initialize(client:) 4 | @client = client 5 | end 6 | 7 | def retrieve(id:) 8 | @client.get(path: "/conversations/#{id}") 9 | end 10 | 11 | def create(parameters: {}) 12 | @client.json_post(path: "/conversations", parameters: parameters) 13 | end 14 | 15 | def modify(id:, parameters: {}) 16 | @client.json_post(path: "/conversations/#{id}", parameters: parameters) 17 | end 18 | 19 | def delete(id:) 20 | @client.delete(path: "/conversations/#{id}") 21 | end 22 | 23 | # Item operations within a conversation 24 | def create_items(conversation_id:, parameters: {}) 25 | @client.json_post(path: "/conversations/#{conversation_id}/items", parameters: parameters) 26 | end 27 | 28 | def list_items(conversation_id:, parameters: {}) 29 | @client.get(path: "/conversations/#{conversation_id}/items", parameters: parameters) 30 | end 31 | 32 | def get_item(conversation_id:, item_id:) 33 | @client.get(path: "/conversations/#{conversation_id}/items/#{item_id}") 34 | end 35 | 36 | def delete_item(conversation_id:, item_id:) 37 | @client.delete(path: "/conversations/#{conversation_id}/items/#{item_id}") 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gpt-3_5-turbo_streamed_chat_with_json_error_response.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/chat/completions 6 | body: 7 | encoding: UTF-8 8 | string: '{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"Hello!"}],"stream":true}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 400 23 | message: Bad Request 24 | headers: 25 | Date: 26 | - Mon, 14 Aug 2023 15:02:13 GMT 27 | Content-Type: 28 | - application/json 29 | body: 30 | encoding: UTF-8 31 | string: |+ 32 | { 33 | "error": { 34 | "message": "Test error", 35 | "type": "test_error", 36 | "param": null, 37 | "code": "test" 38 | } 39 | } 40 | recorded_at: Mon, 14 Aug 2023 15:02:13 GMT 41 | 42 | recorded_with: VCR 6.1.0 43 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/mocks/gpt-3_5-turbo_streamed_chat_with_json_error_response.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/chat/completions 6 | body: 7 | encoding: UTF-8 8 | string: '{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"Hello!"}],"stream":true}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 400 23 | message: Bad Request 24 | headers: 25 | Date: 26 | - Mon, 14 Aug 2023 15:02:13 GMT 27 | Content-Type: 28 | - application/json 29 | body: 30 | encoding: UTF-8 31 | string: |+ 32 | { 33 | "error": { 34 | "message": "Test error", 35 | "type": "test_error", 36 | "param": null, 37 | "code": "test" 38 | } 39 | } 40 | recorded_at: Mon, 14 Aug 2023 15:02:13 GMT 41 | 42 | recorded_with: VCR 6.1.0 43 | -------------------------------------------------------------------------------- /lib/openai/runs.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Runs 3 | def initialize(client:) 4 | @client = client.beta(assistants: OpenAI::Assistants::BETA_VERSION) 5 | end 6 | 7 | def list(thread_id:, parameters: {}) 8 | @client.get(path: "/threads/#{thread_id}/runs", parameters: parameters) 9 | end 10 | 11 | def retrieve(thread_id:, id:) 12 | @client.get(path: "/threads/#{thread_id}/runs/#{id}") 13 | end 14 | 15 | def create(thread_id:, parameters: {}, query_parameters: {}) 16 | @client.json_post(path: "/threads/#{thread_id}/runs", parameters: parameters, 17 | query_parameters: query_parameters) 18 | end 19 | 20 | def modify(id:, thread_id:, parameters: {}) 21 | @client.json_post(path: "/threads/#{thread_id}/runs/#{id}", parameters: parameters) 22 | end 23 | 24 | def cancel(id:, thread_id:) 25 | @client.post(path: "/threads/#{thread_id}/runs/#{id}/cancel") 26 | end 27 | 28 | def create_thread_and_run(parameters: {}) 29 | @client.json_post(path: "/threads/runs", parameters: parameters) 30 | end 31 | 32 | def submit_tool_outputs(thread_id:, run_id:, parameters: {}) 33 | @client.json_post(path: "/threads/#{thread_id}/runs/#{run_id}/submit_tool_outputs", 34 | parameters: parameters) 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/openai/stream.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Stream 3 | DONE = "[DONE]".freeze 4 | private_constant :DONE 5 | 6 | def initialize(user_proc:, parser: EventStreamParser::Parser.new) 7 | @user_proc = user_proc 8 | @parser = parser 9 | 10 | # To be backwards compatible, we need to check how many arguments the user_proc takes. 11 | @user_proc_arity = 12 | case user_proc 13 | when Proc 14 | user_proc.arity.abs 15 | else 16 | user_proc.method(:call).arity.abs 17 | end 18 | end 19 | 20 | def call(chunk, _bytes, env = nil) 21 | handle_http_error(chunk: chunk, env: env) if env && env.status != 200 22 | 23 | parser.feed(chunk) do |event, data| 24 | next if data == DONE 25 | 26 | args = [JSON.parse(data), event].first(user_proc_arity) 27 | user_proc.call(*args) 28 | end 29 | end 30 | 31 | def to_proc 32 | method(:call).to_proc 33 | end 34 | 35 | private 36 | 37 | attr_reader :user_proc, :parser, :user_proc_arity 38 | 39 | def handle_http_error(chunk:, env:) 40 | raise_error = Faraday::Response::RaiseError.new 41 | raise_error.on_complete(env.merge(body: try_parse_json(chunk))) 42 | end 43 | 44 | def try_parse_json(maybe_json) 45 | JSON.parse(maybe_json) 46 | rescue JSON::ParserError 47 | maybe_json 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /spec/openai/client/models_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe OpenAI::Client do 2 | describe "#models" do 3 | describe "#list", :vcr do 4 | let(:response) { OpenAI::Client.new.models.list } 5 | let(:cassette) { "models list" } 6 | 7 | it "succeeds" do 8 | VCR.use_cassette(cassette) do 9 | expect(response.dig("data", 0, "object")).to eq("model") 10 | end 11 | end 12 | end 13 | 14 | describe "#retrieve" do 15 | let(:cassette) { "models retrieve" } 16 | let(:response) { OpenAI::Client.new.models.retrieve(id: "gpt-3.5-turbo-instruct") } 17 | 18 | it "succeeds" do 19 | VCR.use_cassette(cassette) do 20 | expect(response["object"]).to eq("model") 21 | end 22 | end 23 | end 24 | end 25 | 26 | describe "#delete" do 27 | let(:cassette) { "models delete" } 28 | let(:model_id) { "ft:123" } 29 | 30 | it "sends request to the correct endpoint" do 31 | VCR.use_cassette(cassette) do 32 | OpenAI::Client.new.models.delete(id: model_id) 33 | rescue Faraday::ResourceNotFound => e 34 | error_expected = "The model '#{model_id}' does not exist" 35 | expect(e.response.dig(:body, "error", "message")).to eq(error_expected) 36 | 37 | # Just verify the exception is raised as expected 38 | expect(e).to be_a(Faraday::ResourceNotFound) 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/ruby-rails-postgres 3 | // Update the VARIANT arg in docker-compose.yml to pick a Ruby version 4 | { 5 | "name": "ruby-openai", 6 | "dockerComposeFile": "docker-compose.yml", 7 | "service": "app", 8 | "workspaceFolder": "/workspace", 9 | "containerEnv": { 10 | "GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}", 11 | "GITHUB_USER": "${localEnv:GITHUB_USER}" 12 | }, 13 | // Configure tool-specific properties. 14 | "customizations": { 15 | // Configure properties specific to VS Code. 16 | "vscode": { 17 | // Add the IDs of extensions you want installed when the container is created. 18 | "extensions": [ 19 | "rebornix.Ruby", 20 | "sleistner.vscode-fileutils", 21 | "ms-azuretools.vscode-docker", 22 | "samverschueren.final-newline", 23 | "GitHub.copilot", 24 | "usernamehw.remove-empty-lines", 25 | "wingrunr21.vscode-ruby", 26 | ] 27 | } 28 | }, 29 | // Use 'postCreateCommand' to run commands after the container is created. 30 | "postCreateCommand": "bundle install", 31 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 32 | "features": { 33 | "git": "os-provided", 34 | "github-cli": "latest" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ruby-openai.gemspec: -------------------------------------------------------------------------------- 1 | require_relative "lib/openai/version" 2 | 3 | Gem::Specification.new do |spec| 4 | spec.name = "ruby-openai" 5 | spec.version = OpenAI::VERSION 6 | spec.authors = ["Alex"] 7 | spec.email = ["alexrudall@users.noreply.github.com"] 8 | 9 | spec.summary = "OpenAI API + Ruby! 🤖❤️" 10 | spec.homepage = "https://github.com/alexrudall/ruby-openai" 11 | spec.license = "MIT" 12 | spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0") 13 | 14 | spec.metadata["homepage_uri"] = spec.homepage 15 | spec.metadata["source_code_uri"] = "https://github.com/alexrudall/ruby-openai" 16 | spec.metadata["changelog_uri"] = "https://github.com/alexrudall/ruby-openai/blob/main/CHANGELOG.md" 17 | spec.metadata["rubygems_mfa_required"] = "true" 18 | spec.metadata["funding_uri"] = "https://github.com/sponsors/alexrudall" 19 | 20 | # Specify which files should be added to the gem when it is released. 21 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 22 | spec.files = Dir.chdir(File.expand_path(__dir__)) do 23 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 24 | end 25 | spec.bindir = "exe" 26 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 27 | spec.require_paths = ["lib"] 28 | 29 | spec.add_dependency "event_stream_parser", ">= 0.3.0", "< 2.0.0" 30 | spec.add_dependency "faraday", ">= 1" 31 | spec.add_dependency "faraday-multipart", ">= 1" 32 | end 33 | -------------------------------------------------------------------------------- /spec/support/vcr_multipart_matcher.rb: -------------------------------------------------------------------------------- 1 | class VCRMultipartMatcher 2 | MULTIPART_HEADER_MATCHER = %r{^multipart/form-data; boundary=(.+)$}.freeze 3 | BOUNDARY_SUBSTITUTION = "----MultipartBoundaryAbcD3fGhiXyz00001".freeze 4 | 5 | def call(request1, request2) 6 | return false unless same_content_type?(request1, request2) 7 | unless headers_excluding_content_type(request1) == headers_excluding_content_type(request2) 8 | return false 9 | end 10 | 11 | normalized_multipart_body(request1) == normalized_multipart_body(request2) 12 | end 13 | 14 | private 15 | 16 | def same_content_type?(request1, request2) 17 | content_type1 = (request1.headers["Content-Type"] || []).first.to_s 18 | content_type2 = (request2.headers["Content-Type"] || []).first.to_s 19 | 20 | if multipart_request?(content_type1) 21 | multipart_request?(content_type2) 22 | elsif multipart_request?(content_type2) 23 | false 24 | else 25 | content_type1 == content_type2 26 | end 27 | end 28 | 29 | def headers_excluding_content_type(request) 30 | request.headers.reject { |key, _| key == "Content-Type" } 31 | end 32 | 33 | def normalized_multipart_body(request) 34 | content_type = (request.headers["Content-Type"] || []).first.to_s 35 | 36 | return request.headers unless multipart_request?(content_type) 37 | 38 | boundary = MULTIPART_HEADER_MATCHER.match(content_type)[1] 39 | request.body.gsub(boundary, BOUNDARY_SUBSTITUTION) 40 | end 41 | 42 | def multipart_request?(content_type) 43 | return false if content_type.empty? 44 | 45 | MULTIPART_HEADER_MATCHER.match?(content_type) 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations 6 | body: 7 | encoding: UTF-8 8 | string: '{}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:39 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_e8fbcf79e128fc7ebf340646a6242134 39 | Openai-Processing-Ms: 40 | - '940' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "conv_67d4472a0be881909acc6775dded8b11", 56 | "object": "conversation", 57 | "created_at": 1741965098, 58 | "metadata": {} 59 | } 60 | recorded_at: Fri, 14 Mar 2025 15:11:39 GMT 61 | recorded_with: VCR 6.3.1 62 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_create_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations 6 | body: 7 | encoding: UTF-8 8 | string: '{}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:39 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_e8fbcf79e128fc7ebf340646a6242134 39 | Openai-Processing-Ms: 40 | - '940' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "conv_67d4472a0be881909acc6775dded8b11", 56 | "object": "conversation", 57 | "created_at": 1741965098, 58 | "metadata": {} 59 | } 60 | recorded_at: Fri, 14 Mar 2025 15:11:39 GMT 61 | recorded_with: VCR 6.3.1 62 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_delete_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations 6 | body: 7 | encoding: UTF-8 8 | string: '{}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:39 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_e8fbcf79e128fc7ebf340646a6242134 39 | Openai-Processing-Ms: 40 | - '940' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "conv_67d4472a0be881909acc6775dded8b11", 56 | "object": "conversation", 57 | "created_at": 1741965098, 58 | "metadata": {} 59 | } 60 | recorded_at: Fri, 14 Mar 2025 15:11:39 GMT 61 | recorded_with: VCR 6.3.1 62 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_get_item_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations 6 | body: 7 | encoding: UTF-8 8 | string: '{}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:39 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_e8fbcf79e128fc7ebf340646a6242134 39 | Openai-Processing-Ms: 40 | - '940' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "conv_67d4472a0be881909acc6775dded8b11", 56 | "object": "conversation", 57 | "created_at": 1741965098, 58 | "metadata": {} 59 | } 60 | recorded_at: Fri, 14 Mar 2025 15:11:39 GMT 61 | recorded_with: VCR 6.3.1 62 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_modify_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations 6 | body: 7 | encoding: UTF-8 8 | string: '{}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:39 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_e8fbcf79e128fc7ebf340646a6242134 39 | Openai-Processing-Ms: 40 | - '940' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "conv_67d4472a0be881909acc6775dded8b11", 56 | "object": "conversation", 57 | "created_at": 1741965098, 58 | "metadata": {} 59 | } 60 | recorded_at: Fri, 14 Mar 2025 15:11:39 GMT 61 | recorded_with: VCR 6.3.1 62 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_retrieve_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations 6 | body: 7 | encoding: UTF-8 8 | string: '{}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:39 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_e8fbcf79e128fc7ebf340646a6242134 39 | Openai-Processing-Ms: 40 | - '940' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "conv_67d4472a0be881909acc6775dded8b11", 56 | "object": "conversation", 57 | "created_at": 1741965098, 58 | "metadata": {} 59 | } 60 | recorded_at: Fri, 14 Mar 2025 15:11:39 GMT 61 | recorded_with: VCR 6.3.1 62 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_create_items_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations 6 | body: 7 | encoding: UTF-8 8 | string: '{}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:39 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_e8fbcf79e128fc7ebf340646a6242134 39 | Openai-Processing-Ms: 40 | - '940' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "conv_67d4472a0be881909acc6775dded8b11", 56 | "object": "conversation", 57 | "created_at": 1741965098, 58 | "metadata": {} 59 | } 60 | recorded_at: Fri, 14 Mar 2025 15:11:39 GMT 61 | recorded_with: VCR 6.3.1 62 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_delete_item_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations 6 | body: 7 | encoding: UTF-8 8 | string: '{}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:39 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_e8fbcf79e128fc7ebf340646a6242134 39 | Openai-Processing-Ms: 40 | - '940' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "conv_67d4472a0be881909acc6775dded8b11", 56 | "object": "conversation", 57 | "created_at": 1741965098, 58 | "metadata": {} 59 | } 60 | recorded_at: Fri, 14 Mar 2025 15:11:39 GMT 61 | recorded_with: VCR 6.3.1 62 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_list_items_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations 6 | body: 7 | encoding: UTF-8 8 | string: '{}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:39 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_e8fbcf79e128fc7ebf340646a6242134 39 | Openai-Processing-Ms: 40 | - '940' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "conv_67d4472a0be881909acc6775dded8b11", 56 | "object": "conversation", 57 | "created_at": 1741965098, 58 | "metadata": {} 59 | } 60 | recorded_at: Fri, 14 Mar 2025 15:11:39 GMT 61 | recorded_with: VCR 6.3.1 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Ruby ### 2 | *.gem 3 | *.rbc 4 | /.config 5 | /coverage/ 6 | /InstalledFiles 7 | /pkg/ 8 | /spec/reports/ 9 | /spec/examples.txt 10 | /test/tmp/ 11 | /test/version_tmp/ 12 | /tmp/ 13 | 14 | # Used by dotenv library to load environment variables. 15 | .env 16 | 17 | # Ignore Byebug command history file. 18 | .byebug_history 19 | 20 | ## Specific to RubyMotion: 21 | .dat* 22 | .repl_history 23 | build/ 24 | *.bridgesupport 25 | build-iPhoneOS/ 26 | build-iPhoneSimulator/ 27 | 28 | ## Specific to RubyMotion (use of CocoaPods): 29 | # 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 33 | # vendor/Pods/ 34 | 35 | ## Documentation cache and generated files: 36 | /.yardoc/ 37 | /_yardoc/ 38 | /doc/ 39 | /rdoc/ 40 | 41 | ## Environment normalization: 42 | /.bundle/ 43 | /vendor/bundle 44 | /lib/bundler/man/ 45 | 46 | # for a library or gem, you might want to ignore these files since the code is 47 | # intended to run in multiple environments; otherwise, check them in: 48 | # Gemfile.lock 49 | # .ruby-version 50 | # .ruby-gemset 51 | 52 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 53 | .rvmrc 54 | 55 | # Used by RuboCop. Remote config files pulled in from inherit_from directive. 56 | # .rubocop-https?--* 57 | 58 | # rspec failure tracking 59 | .rspec_status 60 | 61 | # IDE 62 | .idea 63 | .idea/ 64 | .idea/* 65 | .vscode 66 | .vs/ 67 | 68 | # Just to make it quicker to test README examples that use /path/to as an example eg. when testing file upload. 69 | /path/to/* 70 | 71 | # Mac 72 | .DS_Store 73 | 74 | INCIDENT_RESPONSE_PLAN.md 75 | -------------------------------------------------------------------------------- /spec/openai/client/threads_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe OpenAI::Client do 2 | describe "#threads" do 3 | let(:thread_id) do 4 | VCR.use_cassette("#{cassette} setup") do 5 | OpenAI::Client.new.threads.create(parameters: {})["id"] 6 | end 7 | end 8 | 9 | describe "#retrieve" do 10 | let(:cassette) { "threads retrieve" } 11 | let(:response) { OpenAI::Client.new.threads.retrieve(id: thread_id) } 12 | 13 | it "succeeds" do 14 | VCR.use_cassette(cassette) do 15 | expect(response["object"]).to eq("thread") 16 | end 17 | end 18 | end 19 | 20 | describe "#create" do 21 | let(:cassette) { "threads create" } 22 | let(:response) do 23 | OpenAI::Client.new.threads.create(parameters: {}) 24 | end 25 | 26 | it "succeeds" do 27 | VCR.use_cassette(cassette) do 28 | expect(response["object"]).to eq "thread" 29 | end 30 | end 31 | end 32 | 33 | describe "#modify" do 34 | let(:cassette) { "threads modify" } 35 | let(:response) do 36 | OpenAI::Client.new.threads.modify( 37 | id: thread_id, 38 | parameters: { metadata: { modified: "true" } } 39 | ) 40 | end 41 | 42 | it "succeeds" do 43 | VCR.use_cassette(cassette) do 44 | expect(response["object"]).to eq "thread" 45 | end 46 | end 47 | end 48 | 49 | describe "#delete" do 50 | let(:cassette) { "threads delete" } 51 | let(:response) do 52 | OpenAI::Client.new.threads.delete(id: thread_id) 53 | end 54 | 55 | it "succeeds" do 56 | VCR.use_cassette(cassette) do 57 | expect(response["object"]).to eq "thread.deleted" 58 | end 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_modify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations/conv_67d4472a0be881909acc6775dded8b11 6 | body: 7 | encoding: UTF-8 8 | string: '{"metadata":{"modified":"true"}}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:41 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_a123b456c789d012e345f678901234ab 39 | Openai-Processing-Ms: 40 | - '500' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "conv_67d4472a0be881909acc6775dded8b11", 56 | "object": "conversation", 57 | "created_at": 1741965098, 58 | "metadata": { 59 | "modified": "true" 60 | } 61 | } 62 | recorded_at: Fri, 14 Mar 2025 15:11:41 GMT 63 | recorded_with: VCR 6.3.1 64 | -------------------------------------------------------------------------------- /lib/openai/usage.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Usage 3 | def initialize(client:) 4 | @client = client 5 | end 6 | 7 | def completions(parameters: {}) 8 | @client.admin.get( 9 | path: "/organization/usage/completions", 10 | parameters: parameters 11 | ) 12 | end 13 | 14 | def embeddings(parameters: {}) 15 | @client.admin.get( 16 | path: "/organization/usage/embeddings", 17 | parameters: parameters 18 | ) 19 | end 20 | 21 | def moderations(parameters: {}) 22 | @client.admin.get( 23 | path: "/organization/usage/moderations", 24 | parameters: parameters 25 | ) 26 | end 27 | 28 | def images(parameters: {}) 29 | @client.admin.get( 30 | path: "/organization/usage/images", 31 | parameters: parameters 32 | ) 33 | end 34 | 35 | def audio_speeches(parameters: {}) 36 | @client.admin.get( 37 | path: "/organization/usage/audio_speeches", 38 | parameters: parameters 39 | ) 40 | end 41 | 42 | def audio_transcriptions(parameters: {}) 43 | @client.admin.get( 44 | path: "/organization/usage/audio_transcriptions", 45 | parameters: parameters 46 | ) 47 | end 48 | 49 | def vector_stores(parameters: {}) 50 | @client.admin.get( 51 | path: "/organization/usage/vector_stores", 52 | parameters: parameters 53 | ) 54 | end 55 | 56 | def code_interpreter_sessions(parameters: {}) 57 | @client.admin.get( 58 | path: "/organization/usage/code_interpreter_sessions", 59 | parameters: parameters 60 | ) 61 | end 62 | 63 | def costs(parameters: {}) 64 | @client.admin.get( 65 | path: "/organization/costs", 66 | parameters: parameters 67 | ) 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /spec/openai/client/run_steps_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe OpenAI::Client do 2 | describe "#run_steps" do 3 | let(:thread_id) do 4 | VCR.use_cassette("#{cassette} thread setup") do 5 | OpenAI::Client.new.threads.create(parameters: {})["id"] 6 | end 7 | end 8 | let(:assistant_id) do 9 | VCR.use_cassette("#{cassette} assistant setup") do 10 | OpenAI::Client.new.assistants.create( 11 | parameters: { 12 | model: "gpt-4", 13 | name: "OpenAI-Ruby test assistant", 14 | instructions: "When asked a question, write and run Ruby code to answer the question" 15 | } 16 | )["id"] 17 | end 18 | end 19 | let(:run_id) do 20 | VCR.use_cassette("#{cassette} run setup") do 21 | OpenAI::Client.new.runs.create( 22 | thread_id: thread_id, 23 | parameters: { 24 | assistant_id: assistant_id 25 | } 26 | )["id"] 27 | end 28 | end 29 | 30 | describe "#list" do 31 | let(:cassette) { "run_steps list" } 32 | let(:response) do 33 | OpenAI::Client.new.run_steps.list( 34 | thread_id: thread_id, 35 | run_id: run_id, 36 | parameters: { order: "asc" } 37 | ) 38 | end 39 | 40 | it "succeeds" do 41 | VCR.use_cassette(cassette) do 42 | expect(response["object"]).to eq("list") 43 | end 44 | end 45 | end 46 | 47 | describe "#retrieve" do 48 | let(:cassette) { "run_steps retrieve" } 49 | let(:response) do 50 | OpenAI::Client.new.run_steps.retrieve( 51 | thread_id: thread_id, 52 | run_id: run_id, 53 | id: "step_123" 54 | ) 55 | end 56 | 57 | it "returns the correct error" do 58 | VCR.use_cassette(cassette) do 59 | response 60 | rescue StandardError => e 61 | expect(e.response.dig(:body, "error", "message")).to include("No run step found") 62 | end 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/ollama_llama3_chat.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: http://localhost:11434/ 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.8.1 12 | Accept-Encoding: 13 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 14 | Accept: 15 | - "*/*" 16 | response: 17 | status: 18 | code: 200 19 | message: OK 20 | headers: 21 | Content-Type: 22 | - text/plain; charset=utf-8 23 | Date: 24 | - Sun, 28 Apr 2024 12:58:36 GMT 25 | Content-Length: 26 | - '17' 27 | body: 28 | encoding: UTF-8 29 | string: Ollama is running 30 | recorded_at: Sun, 28 Apr 2024 12:58:36 GMT 31 | - request: 32 | method: post 33 | uri: http://localhost:11434/v1/chat/completions 34 | body: 35 | encoding: UTF-8 36 | string: '{"model":"llama3","messages":[{"role":"user","content":"Hello!"}],"stream":false}' 37 | headers: 38 | Content-Type: 39 | - application/json 40 | Authorization: 41 | - Bearer 42 | Accept-Encoding: 43 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 44 | Accept: 45 | - "*/*" 46 | User-Agent: 47 | - Ruby 48 | response: 49 | status: 50 | code: 200 51 | message: OK 52 | headers: 53 | Content-Type: 54 | - application/json 55 | Date: 56 | - Sun, 28 Apr 2024 12:58:37 GMT 57 | Content-Length: 58 | - '376' 59 | body: 60 | encoding: UTF-8 61 | string: '{"id":"chatcmpl-106","object":"chat.completion","created":1714309117,"model":"llama3","system_fingerprint":"fp_ollama","choices":[{"index":0,"message":{"role":"assistant","content":"Hello! 62 | It''s nice to meet you. Is there something I can help you with, or would you 63 | like to chat?"},"finish_reason":"stop"}],"usage":{"prompt_tokens":0,"completion_tokens":26,"total_tokens":26}} 64 | 65 | ' 66 | recorded_at: Sun, 28 Apr 2024 12:58:37 GMT 67 | recorded_with: VCR 6.1.0 68 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | require "dotenv/load" 3 | require "openai" 4 | require "vcr" 5 | require "byebug" 6 | 7 | Dir[File.expand_path("spec/support/**/*.rb")].sort.each { |f| require f } 8 | 9 | tokens_present = ENV.fetch("OPENAI_ACCESS_TOKEN", nil) && ENV.fetch("OPENAI_ADMIN_TOKEN", nil) 10 | 11 | VCR.configure do |c| 12 | c.hook_into :webmock 13 | c.cassette_library_dir = "spec/fixtures/cassettes" 14 | c.default_cassette_options = { 15 | record: tokens_present ? :all : :new_episodes, 16 | match_requests_on: [:method, :uri, VCRMultipartMatcher.new] 17 | } 18 | 19 | %w[ACCESS_TOKEN ADMIN_TOKEN ORGANIZATION_ID USER_ID].each do |key| 20 | c.filter_sensitive_data("") do 21 | key == "USER_ID" ? ENV.fetch("OPENAI_#{key}", nil) : OpenAI.configuration.send(key.downcase) 22 | end 23 | end 24 | end 25 | 26 | RSpec.configure do |c| 27 | # Enable flags like --only-failures and --next-failure 28 | c.example_status_persistence_file_path = ".rspec_status" 29 | 30 | # Disable RSpec exposing methods globally on `Module` and `main` 31 | c.disable_monkey_patching! 32 | 33 | c.expect_with(:rspec) { |rspec| rspec.syntax = :expect } 34 | 35 | if tokens_present 36 | warning = "WARNING! Specs are hitting the OpenAI API using your OPENAI_ACCESS_TOKEN or 37 | OPENAI_ADMIN_TOKEN! This costs at least 2 cents per run and is very slow! If you don't want 38 | this, unset OPENAI_ACCESS_TOKEN to just run against the stored VCR responses.".freeze 39 | warning = RSpec::Core::Formatters::ConsoleCodes.wrap(warning, :bold_red) 40 | 41 | c.before(:suite) { RSpec.configuration.reporter.message(warning) } 42 | c.after(:suite) { RSpec.configuration.reporter.message(warning) } 43 | end 44 | 45 | c.before(:all) do 46 | OpenAI.configure do |config| 47 | config.access_token = ENV.fetch("OPENAI_ACCESS_TOKEN", "dummy-token") 48 | config.admin_token = ENV.fetch("OPENAI_ADMIN_TOKEN", "dummy-token") 49 | config.organization_id = ENV.fetch("OPENAI_ORGANIZATION_ID", nil) 50 | end 51 | end 52 | end 53 | 54 | RSPEC_ROOT = File.dirname __FILE__ 55 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_create_items.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations/conv_67d4472a0be881909acc6775dded8b11/items 6 | body: 7 | encoding: UTF-8 8 | string: '{"items":[{"type":"message","role":"user","content":[{"type":"input_text","text":"Hello, this is a test item"}]}]}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:43 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_c123d456e789f012g345h678901234ef 39 | Openai-Processing-Ms: 40 | - '650' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "item_67d4472a5bb88190bee168ec5d59d790", 56 | "object": "conversation.item", 57 | "conversation_id": "conv_67d4472a0be881909acc6775dded8b11", 58 | "type": "message", 59 | "role": "user", 60 | "content": [ 61 | { 62 | "type": "input_text", 63 | "text": "Hello, this is a test item" 64 | } 65 | ], 66 | "created_at": 1741965103 67 | } 68 | recorded_at: Fri, 14 Mar 2025 15:11:43 GMT 69 | recorded_with: VCR 6.3.1 70 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/models_retrieve.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/models/gpt-3.5-turbo-instruct 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Sat, 27 Apr 2024 11:46:19 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | X-Request-Id: 36 | - req_cf835448d20433cce7b43909bd0c7ea8 37 | Openai-Processing-Ms: 38 | - '40' 39 | Access-Control-Allow-Origin: 40 | - "*" 41 | Strict-Transport-Security: 42 | - max-age=15724800; includeSubDomains 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | Set-Cookie: 46 | - __cf_bm=_eXpJA5dZ3Iz9_qoQNymKpqKLHLsZEdKqxGTCyOGtHQ-1714218379-1.0.1.1-LVHAwy3.gnF2dY_sbdaJGIq8NyFH4hqK9sdggW1vN1SW4oFDUyodj.uP584Q_lj9DkcUl3L6IsMqiWdqoFRqxg; 47 | path=/; expires=Sat, 27-Apr-24 12:16:19 GMT; domain=.api.openai.com; HttpOnly; 48 | Secure; SameSite=None 49 | - _cfuvid=Gm6N._3EC58LRuLAqngsZG7PkgcZmFEjJCaqg2FodXU-1714218379420-0.0.1.1-604800000; 50 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 51 | Server: 52 | - cloudflare 53 | Cf-Ray: 54 | - 87ae924608e47141-SOF 55 | Alt-Svc: 56 | - h3=":443"; ma=86400 57 | body: 58 | encoding: ASCII-8BIT 59 | string: | 60 | { 61 | "id": "gpt-3.5-turbo-instruct", 62 | "object": "model", 63 | "created": 1692901427, 64 | "owned_by": "system" 65 | } 66 | recorded_at: Sat, 27 Apr 2024 11:46:19 GMT 67 | recorded_with: VCR 6.1.0 68 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_delete_item_item_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations/conv_67d4472a0be881909acc6775dded8b11/items 6 | body: 7 | encoding: UTF-8 8 | string: '{"items":[{"type":"message","role":"user","content":[{"type":"input_text","text":"Hello, this is a test item"}]}]}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:43 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_c123d456e789f012g345h678901234ef 39 | Openai-Processing-Ms: 40 | - '650' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "item_67d4472a5bb88190bee168ec5d59d790", 56 | "object": "conversation.item", 57 | "conversation_id": "conv_67d4472a0be881909acc6775dded8b11", 58 | "type": "message", 59 | "role": "user", 60 | "content": [ 61 | { 62 | "type": "input_text", 63 | "text": "Hello, this is a test item" 64 | } 65 | ], 66 | "created_at": 1741965103 67 | } 68 | recorded_at: Fri, 14 Mar 2025 15:11:43 GMT 69 | recorded_with: VCR 6.3.1 70 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_get_item_item_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations/conv_67d4472a0be881909acc6775dded8b11/items 6 | body: 7 | encoding: UTF-8 8 | string: '{"items":[{"type":"message","role":"user","content":[{"type":"input_text","text":"Hello, this is a test item"}]}]}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:43 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_c123d456e789f012g345h678901234ef 39 | Openai-Processing-Ms: 40 | - '650' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "item_67d4472a5bb88190bee168ec5d59d790", 56 | "object": "conversation.item", 57 | "conversation_id": "conv_67d4472a0be881909acc6775dded8b11", 58 | "type": "message", 59 | "role": "user", 60 | "content": [ 61 | { 62 | "type": "input_text", 63 | "text": "Hello, this is a test item" 64 | } 65 | ], 66 | "created_at": 1741965103 67 | } 68 | recorded_at: Fri, 14 Mar 2025 15:11:43 GMT 69 | recorded_with: VCR 6.3.1 70 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/conversations_list_items_item_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/conversations/conv_67d4472a0be881909acc6775dded8b11/items 6 | body: 7 | encoding: UTF-8 8 | string: '{"items":[{"type":"message","role":"user","content":[{"type":"input_text","text":"Hello, this is a test item"}]}]}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:43 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_c123d456e789f012g345h678901234ef 39 | Openai-Processing-Ms: 40 | - '650' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | X-Content-Type-Options: 46 | - nosniff 47 | Server: 48 | - cloudflare 49 | Alt-Svc: 50 | - h3=":443"; ma=86400 51 | body: 52 | encoding: ASCII-8BIT 53 | string: |- 54 | { 55 | "id": "item_67d4472a5bb88190bee168ec5d59d790", 56 | "object": "conversation.item", 57 | "conversation_id": "conv_67d4472a0be881909acc6775dded8b11", 58 | "type": "message", 59 | "role": "user", 60 | "content": [ 61 | { 62 | "type": "input_text", 63 | "text": "Hello, this is a test item" 64 | } 65 | ], 66 | "created_at": 1741965103 67 | } 68 | recorded_at: Fri, 14 Mar 2025 15:11:43 GMT 69 | recorded_with: VCR 6.3.1 70 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/files_delete.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: delete 5 | uri: https://api.openai.com/v1/files/file-0jvap1eP3CIIUYbypdaX5y5w 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Tue, 14 Nov 2023 21:48:54 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - a8c569e1f75bde44f7c3d11c7e1c3242 39 | Openai-Processing-Ms: 40 | - '352' 41 | Access-Control-Allow-Origin: 42 | - "*" 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=_jN6PztHSdGfDF66QrtGMrgjK1A3fc5w81J5RJxKZLg-1699998534-0-AUwU8+Q6fowOdxVXi0taQXWbazehLr3zYWNfsljazP95z+SSEjQXq5SBfEDeV1S9DzaJfs4CyZslCXVIfsY69k0=; 49 | path=/; expires=Tue, 14-Nov-23 22:18:54 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=HCBE4v9ApanLwMZmt7PVXApTOrN1wLxFuCK9IW.hJDk-1699998534520-0-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 82627615bb5863f9-LHR 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: | 62 | { 63 | "object": "file", 64 | "id": "file-0jvap1eP3CIIUYbypdaX5y5w", 65 | "deleted": true 66 | } 67 | recorded_at: Tue, 14 Nov 2023 21:48:54 GMT 68 | recorded_with: VCR 6.1.0 69 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_stores_delete.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: delete 5 | uri: https://api.openai.com/v1/vector_stores/vs_DBrde7MywMBEdxmzXaeO76Ky 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 16:41:38 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_07c5e08ede15f5ffd23ebd910964a12a 41 | Openai-Processing-Ms: 42 | - '650' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=QVfxVV7u50gVD09z0l_zJPEdDRfWRq495V.vRZ0B_XY-1714495298-1.0.1.1-kESiJ5WFO.TexW1HJwHtzlmu8_Z8PEQMPl5QuVEMP6X3g5yZO6whcPqES587b1QjLSdI10c8w7h_tRciBiRgHQ; 49 | path=/; expires=Tue, 30-Apr-24 17:11:38 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=mVGvyRYNeI_sytGC2xXm7o0oEB2bX6ySfTWjOS_O7A0-1714495298252-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c8faf8ba5aa194-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_DBrde7MywMBEdxmzXaeO76Ky", 64 | "object": "vector_store.deleted", 65 | "deleted": true 66 | } 67 | recorded_at: Tue, 30 Apr 2024 16:41:38 GMT 68 | recorded_with: VCR 6.1.0 69 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/messages_delete_thread_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/threads 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Sat, 27 Apr 2024 20:48:38 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - user-jxm65ijkzc1qrfhc0ij8moic 39 | X-Request-Id: 40 | - req_66d6e449d9dd69eb807d8a9da93e1f39 41 | Openai-Processing-Ms: 42 | - '122' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=5AXKY.WMatdAFAfQqwX2U4AB0sC2SBiYVmcys6tfeTQ-1714250918-1.0.1.1-1oomAHtMUj7miIOzalc99tozRJfzMjMkyTIksFjS79Z_9FTH7nEXGeTVU43.PvsNTASDj_UPXEK6taRPHIp_5g; 49 | path=/; expires=Sat, 27-Apr-24 21:18:38 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=zJNg4imGWESYt_qpEbTgKtX33u1ia_rqdvgXvNFHuxg-1714250918516-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87b1acaeed868ee4-SOF 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "thread_SSHu8SJsPPGTdBGS33nxwi3H", 64 | "object": "thread", 65 | "created_at": 1714250918, 66 | "metadata": {} 67 | } 68 | recorded_at: Sat, 27 Apr 2024 20:48:38 GMT 69 | recorded_with: VCR 6.1.0 70 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/messages_delete.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: delete 5 | uri: https://api.openai.com/v1/threads/thread_SSHu8SJsPPGTdBGS33nxwi3H/messages/msg_joVddaqRQSpNX5RIjDWSBek2 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 14 Nov 2023 21:49:22 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - user-jxm65ijkzc1qrfhc0ij8moic 39 | X-Request-Id: 40 | - 8f99c885800c5968e66119e9f3abc72c 41 | Openai-Processing-Ms: 42 | - '82' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=PdJWQJJ7z1o3MLG3_hpQjP7flRwV2c9qZAHU_egQFMk-1699998562-0-AcvTQRdLgadVE7zz27L21EVHiPzsjt11vNI2n0G5rzrijQgP3LstwBbGnycxMOe+FounFoJp6dSi3GNCU+m/MUY=; 49 | path=/; expires=Tue, 14-Nov-23 22:19:22 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=hl1TdTyyrRKTJ5RRo4e5jxl2xaJcRY2AyMoTIijRhuw-1699998562771-0-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 826276c7fe3176c6-LHR 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "msg_joVddaqRQSpNX5RIjDWSBek2", 64 | "object": "thread.message.deleted", 65 | "deleted": true 66 | } 67 | recorded_at: Mon, 09 Sep 2024 19:08:06 GMT 68 | recorded_with: VCR 6.1.0 69 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/finetunes_retrieve.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/fine_tuning/jobs/123 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 404 23 | message: Not Found 24 | headers: 25 | Date: 26 | - Tue, 14 Nov 2023 21:48:57 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - 4df7f8b0ba83e6a8aa9b8fa976bef023 39 | Openai-Processing-Ms: 40 | - '78' 41 | Strict-Transport-Security: 42 | - max-age=15724800; includeSubDomains 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | Set-Cookie: 46 | - __cf_bm=59RifJfbvlHN9Qu4rSBxZ96qCKQcQU1R6KLVuIWvywU-1699998537-0-AYf+z4TCJF0x9+7F5VOSMB//zVOjJ+XqXcWMsow4UBoahmPqOmB9pForlcSvg1vSX8PUXeq8iie+W3qqKci1gbw=; 47 | path=/; expires=Tue, 14-Nov-23 22:18:57 GMT; domain=.api.openai.com; HttpOnly; 48 | Secure; SameSite=None 49 | - _cfuvid=345CorXhyQakR7D.2YoEdUPTYOgeMBgxjuBKWdJTOsE-1699998537187-0-604800000; 50 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 51 | Server: 52 | - cloudflare 53 | Cf-Ray: 54 | - 826276280831777d-LHR 55 | Alt-Svc: 56 | - h3=":443"; ma=86400 57 | body: 58 | encoding: ASCII-8BIT 59 | string: |- 60 | { 61 | "error": { 62 | "message": "Could not find fine tune: 123", 63 | "type": "invalid_request_error", 64 | "param": "fine_tune_id", 65 | "code": "fine_tune_not_found" 66 | } 67 | } 68 | recorded_at: Tue, 14 Nov 2023 21:48:57 GMT 69 | recorded_with: VCR 6.1.0 70 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_files_delete.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: delete 5 | uri: https://api.openai.com/v1/vector_stores/vs_8dzN747pTzY2MtyDQoxfwmod/files/file-cQkOwz3aospk6aTNL78LAztA 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 18:55:14 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_84190e2775e6f94c846e728bbecd5ae3 41 | Openai-Processing-Ms: 42 | - '1158' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=ZUBTIh9sZKlJ_ano_7uUS.GrSgC1xDk0nCB1rE39z4c-1714503314-1.0.1.1-yfrsPhstLMgl_FZokZSMHcuRjreQRAPkcj2KB884h1KayOb553i0H07hbyrMgEr3w.TALhtoBkg2SobXpAbk2Q; 49 | path=/; expires=Tue, 30-Apr-24 19:25:14 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=EbXnnjmqClzdQq8HP0FQb8RWc1N3ad3JLzseQj0m0mk-1714503314239-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c9bea978ea1b31-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "file-cQkOwz3aospk6aTNL78LAztA", 64 | "object": "vector_store.file.deleted", 65 | "deleted": true 66 | } 67 | recorded_at: Tue, 30 Apr 2024 18:55:14 GMT 68 | recorded_with: VCR 6.1.0 69 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/finetunes_cancel.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/fine_tuning/jobs/123/cancel 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 404 23 | message: Not Found 24 | headers: 25 | Date: 26 | - Tue, 14 Nov 2023 21:48:57 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - 3d789b25cea3eab9c40614b357c4eddb 39 | Openai-Processing-Ms: 40 | - '72' 41 | Strict-Transport-Security: 42 | - max-age=15724800; includeSubDomains 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | Set-Cookie: 46 | - __cf_bm=ApG7ZXI4CPFkrB2j1s.UUro1izhAH0gnB7u4NOVgVhg-1699998537-0-AUby5N6SqX/GfzT18HtFsIjWzIzjj6gQuOdx+utecnpv/SKPMpV6BHq7AAwH+00P53xC8UtaLyeQANpIl0tdQlc=; 47 | path=/; expires=Tue, 14-Nov-23 22:18:57 GMT; domain=.api.openai.com; HttpOnly; 48 | Secure; SameSite=None 49 | - _cfuvid=sgtbVUZgnPZTPyX0GVQXCPuzepVhkQsw2TaxRvXVBfg-1699998537515-0-604800000; 50 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 51 | Server: 52 | - cloudflare 53 | Cf-Ray: 54 | - 8262762a0b5f60ed-LHR 55 | Alt-Svc: 56 | - h3=":443"; ma=86400 57 | body: 58 | encoding: ASCII-8BIT 59 | string: |- 60 | { 61 | "error": { 62 | "message": "Could not find fine tune: 123", 63 | "type": "invalid_request_error", 64 | "param": "fine_tune_id", 65 | "code": "fine_tune_not_found" 66 | } 67 | } 68 | recorded_at: Tue, 14 Nov 2023 21:48:57 GMT 69 | recorded_with: VCR 6.1.0 70 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/finetunes_event_list.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/fine_tuning/jobs/123/events 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 404 23 | message: Not Found 24 | headers: 25 | Date: 26 | - Tue, 14 Nov 2023 21:48:57 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - acee5390e7c6598ae20d10f8efef679f 39 | Openai-Processing-Ms: 40 | - '63' 41 | Strict-Transport-Security: 42 | - max-age=15724800; includeSubDomains 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | Set-Cookie: 46 | - __cf_bm=XOoyLpJN2wpWJwzIuvVuZRZi_2SYhhTDxw8f8WroxHo-1699998537-0-AR/AYW0F4YK/ut236CIsq7hgUR7/NCRabpknFQxasdcjx7u/pfBfziVrjRpsgW9Gy8F3yGeYA3rMAWZiO5eRc9E=; 47 | path=/; expires=Tue, 14-Nov-23 22:18:57 GMT; domain=.api.openai.com; HttpOnly; 48 | Secure; SameSite=None 49 | - _cfuvid=TNlk3rpEeFUa6Zhlw3UyjbWrh0DoHm9wQsmSKWxw7wA-1699998537888-0-604800000; 50 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 51 | Server: 52 | - cloudflare 53 | Cf-Ray: 54 | - 8262762c893f6401-LHR 55 | Alt-Svc: 56 | - h3=":443"; ma=86400 57 | body: 58 | encoding: ASCII-8BIT 59 | string: |- 60 | { 61 | "error": { 62 | "message": "Could not find fine tune: 123", 63 | "type": "invalid_request_error", 64 | "param": "fine_tune_id", 65 | "code": "fine_tune_not_found" 66 | } 67 | } 68 | recorded_at: Tue, 14 Nov 2023 21:48:57 GMT 69 | recorded_with: VCR 6.1.0 70 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/responses_delete.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: delete 5 | uri: https://api.openai.com/v1/responses/resp_67d4473035808190916ca94aaffa69eb066a7ed6269349b2 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 15:11:45 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_d5fa87f624660cbaebd5ecaa6493895d 39 | Openai-Processing-Ms: 40 | - '80' 41 | Strict-Transport-Security: 42 | - max-age=31536000; includeSubDomains; preload 43 | Cf-Cache-Status: 44 | - DYNAMIC 45 | Set-Cookie: 46 | - __cf_bm=qBjgCk8u5MKnlu0Lo6tjB19HXo.oKfpXaU.4km3bGOg-1741965105-1.0.1.1-zh9geAT9M246oBkJ.IEhPw2YGPnposuDgEmJKGqKYWMxWCNVrWQuxT3Bel2uZij_rtr7OaPG0e9Sgs78T43hmB_xmZ5k7sBQY.WhfuCAP1M; 47 | path=/; expires=Fri, 14-Mar-25 15:41:45 GMT; domain=.api.openai.com; HttpOnly; 48 | Secure; SameSite=None 49 | - _cfuvid=x.BW8TqI8JNHqGOZCMB1kKYzSQQyHfTQ7MfgYq3VdBg-1741965105287-0.0.1.1-604800000; 50 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 51 | X-Content-Type-Options: 52 | - nosniff 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 9204b4929e4b149b-LHR 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "resp_67d4473035808190916ca94aaffa69eb066a7ed6269349b2", 64 | "object": "response.deleted", 65 | "deleted": true 66 | } 67 | recorded_at: Fri, 14 Mar 2025 15:11:45 GMT 68 | recorded_with: VCR 6.1.0 69 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/files_content.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/files/file-VAyM3yAs580Dk9SbTBzwl8yN/content 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Tue, 14 Nov 2023 21:48:50 GMT 27 | Content-Type: 28 | - application/octet-stream 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Content-Disposition: 34 | - attachment; filename="sentiment.jsonl" 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - user-jxm65ijkzc1qrfhc0ij8moic 39 | X-Request-Id: 40 | - f575ab5b400a1b9a6dc1aa23b557b573 41 | Openai-Processing-Ms: 42 | - '84' 43 | Access-Control-Allow-Origin: 44 | - "*" 45 | Strict-Transport-Security: 46 | - max-age=15724800; includeSubDomains 47 | Cf-Cache-Status: 48 | - DYNAMIC 49 | Set-Cookie: 50 | - __cf_bm=ecSdU_k92m9twb0uhcvFUGUN8vVuggCX_JJ0KYz6ak8-1699998530-0-AcC5oe+KBr+tR5Gx3VG8GaSvfsjW525sXA354DzrfRyJL3h0gGOV4VonFoqGTZDi5lj1JgocQljmGlHH6phvf1E=; 51 | path=/; expires=Tue, 14-Nov-23 22:18:50 GMT; domain=.api.openai.com; HttpOnly; 52 | Secure; SameSite=None 53 | - _cfuvid=3CDLlmcUZ9S___3_KPOZS.7YWwNLzflYxFAf9bwKDtE-1699998530242-0-604800000; 54 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 55 | Server: 56 | - cloudflare 57 | Cf-Ray: 58 | - 826275fa9e076328-LHR 59 | Alt-Svc: 60 | - h3=":443"; ma=86400 61 | body: 62 | encoding: UTF-8 63 | string: | 64 | {"prompt":"Overjoyed with my new phone! ->", "completion":" positive"} 65 | {"prompt":"@lakers disappoint for a third straight night ->", "completion":" negative"} 66 | recorded_at: Tue, 14 Nov 2023 21:48:50 GMT 67 | recorded_with: VCR 6.1.0 68 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/run_steps_retrieve_thread_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/threads 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Fri, 14 Mar 2025 16:46:16 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - user-jxm65ijkzc1qrfhc0ij8moic 39 | X-Request-Id: 40 | - req_9f5c0e23d90e2d56ace60d3dc899a0a1 41 | Openai-Processing-Ms: 42 | - '191' 43 | Strict-Transport-Security: 44 | - max-age=31536000; includeSubDomains; preload 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=xnQUjDqp2guOG.etqYmpI5XoHRJGz8jNwgRVjrv4S9E-1741970776-1.0.1.1-BBGtLkS3ejCyo3sv2h9rnOy0.exiX6KzrWcIZjjGgpbNO98KeHv64wwK4y2Ses2ILsRZ0TPSxcDASNcveewEGPmPvnBFTL2f4VKHk26vNNs; 49 | path=/; expires=Fri, 14-Mar-25 17:16:16 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=NZr3kLjB7bP0Lp5ox4CGDzBp.xSx60dW.6JHJ4FgJWE-1741970776443-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | X-Content-Type-Options: 54 | - nosniff 55 | Server: 56 | - cloudflare 57 | Cf-Ray: 58 | - 92053f067aeb5672-LHR 59 | Alt-Svc: 60 | - h3=":443"; ma=86400 61 | body: 62 | encoding: ASCII-8BIT 63 | string: |- 64 | { 65 | "id": "thread_H6hq4f78l5gxCtSydD79yb6S", 66 | "object": "thread", 67 | "created_at": 1741970776, 68 | "metadata": {}, 69 | "tool_resources": {} 70 | } 71 | recorded_at: Fri, 14 Mar 2025 16:46:16 GMT 72 | recorded_with: VCR 6.3.1 73 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/files_retrieve.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/files/file-s2xQXv3BRiB88t19DfkMpF6f 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Tue, 14 Nov 2023 21:48:48 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - fa20e45ea8a965b7d88d099954d962e4 39 | Openai-Processing-Ms: 40 | - '76' 41 | Access-Control-Allow-Origin: 42 | - "*" 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=D99.M34EQbEDP9T8.X5oxtjiv18x1mEX4WV_.9I5wwU-1699998528-0-AUn98TFcB6+4HzJgGyY2f5rUQxgqKothApCaooAUmAaBFXP8XMKF1fjWVxfdoGzgEuaEutlavhQRu4tOqEsS/Mw=; 49 | path=/; expires=Tue, 14-Nov-23 22:18:48 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=24QuXnJgnaYFVMihszLmi4_mzNn1L3Mj6L06LMwxVe0-1699998528684-0-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 826275f2e955dd79-LHR 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: | 62 | { 63 | "object": "file", 64 | "id": "file-s2xQXv3BRiB88t19DfkMpF6f", 65 | "purpose": "fine-tune", 66 | "filename": "sentiment.jsonl", 67 | "bytes": 159, 68 | "created_at": 1699998528, 69 | "status": "uploaded", 70 | "status_details": null 71 | } 72 | recorded_at: Tue, 14 Nov 2023 21:48:48 GMT 73 | recorded_with: VCR 6.1.0 74 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/files_delete_retrieve.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/files/file-0jvap1eP3CIIUYbypdaX5y5w 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Tue, 14 Nov 2023 21:48:53 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - 39216740bed70cc6636969f0fe841078 39 | Openai-Processing-Ms: 40 | - '42' 41 | Access-Control-Allow-Origin: 42 | - "*" 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=bDxlDrQ6uT8u9xNzMBrdbDidTTGF.cMZi7sVcXPpwvM-1699998533-0-AVghPBJqCnm7x9sulaj53TigrscDreOUcVevVXTLvv2OYXng71QfZfYtrCdQN/K1IH6+tuypM9GrxqG8tBUFrgs=; 49 | path=/; expires=Tue, 14-Nov-23 22:18:53 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=puIaxHTBwhbiSu9qWC5ZoJsF1fZJGIuL16FoNATKZ94-1699998533962-0-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 826276141c3d6538-LHR 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: | 62 | { 63 | "object": "file", 64 | "id": "file-0jvap1eP3CIIUYbypdaX5y5w", 65 | "purpose": "fine-tune", 66 | "filename": "sentiment.jsonl", 67 | "bytes": 159, 68 | "created_at": 1699998530, 69 | "status": "processed", 70 | "status_details": null 71 | } 72 | recorded_at: Tue, 14 Nov 2023 21:48:53 GMT 73 | recorded_with: VCR 6.1.0 74 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/models_delete.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: delete 5 | uri: https://api.openai.com/v1/models/ft:123 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 404 23 | message: Not Found 24 | headers: 25 | Date: 26 | - Fri, 14 Mar 2025 16:27:14 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Access-Control-Allow-Origin: 34 | - "*" 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - user-jxm65ijkzc1qrfhc0ij8moic 39 | X-Request-Id: 40 | - req_f3aa46fca70f6e573cbd6c099b79e7ff 41 | Openai-Processing-Ms: 42 | - '87' 43 | Strict-Transport-Security: 44 | - max-age=31536000; includeSubDomains; preload 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=dheP4FgO74F2kzJyx1PRXDmCdmGSgyXaIc3evnqa.Fg-1741969634-1.0.1.1-rnk0u_ENkSbxCpNmLnSOYGA5WhxWvo6HYDC7WmV.753xNOtUzd2Q7pq7CNCqD5MaRX5pZolCAUF1M.jgxZ6JpqLE5sWCSn5tc25LCkGnmDU; 49 | path=/; expires=Fri, 14-Mar-25 16:57:14 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=2ThzUr01e8u1WXCmD4Y3hxPV6sx3dP55bRkXtNIegOM-1741969634836-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | X-Content-Type-Options: 54 | - nosniff 55 | Server: 56 | - cloudflare 57 | Cf-Ray: 58 | - 920523284e87beba-LHR 59 | Alt-Svc: 60 | - h3=":443"; ma=86400 61 | body: 62 | encoding: ASCII-8BIT 63 | string: | 64 | { 65 | "error": { 66 | "message": "The model 'ft:123' does not exist", 67 | "type": "invalid_request_error", 68 | "param": "model", 69 | "code": "model_not_found" 70 | } 71 | } 72 | recorded_at: Fri, 14 Mar 2025 16:27:14 GMT 73 | recorded_with: VCR 6.3.1 74 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_stores_search_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 15 Oct 2024 10:45:10 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - "2020-10-01" 37 | Openai-Organization: 38 | - user-test123456 39 | X-Request-Id: 40 | - req_abc123def456 41 | Openai-Processing-Ms: 42 | - "152" 43 | Strict-Transport-Security: 44 | - max-age=31536000; includeSubDomains; preload 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=abcdefg123456; path=/; expires=Tue, 15-Oct-24 11:15:10 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 49 | - _cfuvid=abcdefg123456-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 50 | X-Content-Type-Options: 51 | - nosniff 52 | Server: 53 | - cloudflare 54 | Cf-Ray: 55 | - abcdefg123456-XYZ 56 | Alt-Svc: 57 | - h3=":443"; ma=86400 58 | body: 59 | encoding: ASCII-8BIT 60 | string: |- 61 | { 62 | "id": "vs_abc123", 63 | "object": "vector_store", 64 | "created_at": 1725678310, 65 | "name": null, 66 | "file_counts": { 67 | "total": 0, 68 | "completed": 0, 69 | "processing": 0, 70 | "failed": 0 71 | }, 72 | "expires_after": null 73 | } 74 | recorded_at: Tue, 15 Oct 2024 10:45:10 GMT 75 | recorded_with: VCR 6.1.0 76 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_files_retrieve.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/vector_stores/vs_byyoIIfTk2DjCONUuvGFZwrd/files/file-wVFDYrCLN5iNPWQid43qSLaj 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 18:55:05 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_b2e1ca7e3050e8e167942d210786d09b 41 | Openai-Processing-Ms: 42 | - '114' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=v1cXwQH_H3ti0ihlMyGXU763pVFLLBIXjXZocZF2Lq0-1714503305-1.0.1.1-CBXBPaeHdW3U3WxZY3WQQWHC2bZXSPel28owDZa5nmgGRdZPiy40jAS6aRZN1MFa6OHsNvhldCQkWm_0HDRJAQ; 49 | path=/; expires=Tue, 30-Apr-24 19:25:05 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=eaxIupAaujae5M022roi_f7tpBETscIChreIkDymmqU-1714503305351-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c9be787e151b0e-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "file-wVFDYrCLN5iNPWQid43qSLaj", 64 | "object": "vector_store.file", 65 | "usage_bytes": 1036, 66 | "created_at": 1714503303, 67 | "vector_store_id": "vs_byyoIIfTk2DjCONUuvGFZwrd", 68 | "status": "completed", 69 | "last_error": null 70 | } 71 | recorded_at: Tue, 30 Apr 2024 18:55:05 GMT 72 | recorded_with: VCR 6.1.0 73 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_files_create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores/vs_jXM8wckBS9gmjt8GlJAqFwwc/files 6 | body: 7 | encoding: UTF-8 8 | string: '{"file_id":"file-cGrNYOaHQOsKvfEDXa3bk6Rg"}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 18:55:09 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_9916342ad83e66e7095abac0fb79e992 41 | Openai-Processing-Ms: 42 | - '1354' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=nt4xkAXg03V_687Dx8uufZODpKBUIVLQlBTwsmot7TU-1714503308-1.0.1.1-Ydh0WR32g.kMaMYCV9CbQke5Qi.NrxFGikfKrIhU5ckayQmIYcKNTNy.09rY1m40612UpFwCWi7bJsiQgqG.bQ; 49 | path=/; expires=Tue, 30-Apr-24 19:25:08 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=JyIj02JBLJrLpmIxRTmUjK2dkV4v0Esxc9Ix05kSuWM-1714503309000-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c9be875d311ced-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "file-cGrNYOaHQOsKvfEDXa3bk6Rg", 64 | "object": "vector_store.file", 65 | "usage_bytes": 0, 66 | "created_at": 1714503307, 67 | "vector_store_id": "vs_jXM8wckBS9gmjt8GlJAqFwwc", 68 | "status": "in_progress", 69 | "last_error": null 70 | } 71 | recorded_at: Tue, 30 Apr 2024 18:55:08 GMT 72 | recorded_with: VCR 6.1.0 73 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/run_steps_retrieve.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/threads/thread_H6hq4f78l5gxCtSydD79yb6S/runs/run_Bsdc4YdgdNsU3ZJM30yvIgwc/steps/step_123 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 404 25 | message: Not Found 26 | headers: 27 | Date: 28 | - Fri, 14 Mar 2025 16:46:21 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - user-jxm65ijkzc1qrfhc0ij8moic 39 | X-Request-Id: 40 | - req_f763902cdb8ae191a847ea853774370b 41 | Openai-Processing-Ms: 42 | - '45' 43 | Strict-Transport-Security: 44 | - max-age=31536000; includeSubDomains; preload 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=hmee0L9x1ZeoTlo8dkfbGbmqWbJzPZB9UAAcNqP.hwM-1741970781-1.0.1.1-XtnHQve8x7TrLHg2BO8oInipMjmmZszfznZPvfF06IfLCxgbKFaaMHEc_g9UQZyFNKRQUz4aLkhonNAjf0Nfn0Sr8jq6Cku9CP1PNACk98Y; 49 | path=/; expires=Fri, 14-Mar-25 17:16:21 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=QUP0d.8mwxIb5oqgbL9qFp5i9juz2km1ANUHbL6ZweI-1741970781712-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | X-Content-Type-Options: 54 | - nosniff 55 | Server: 56 | - cloudflare 57 | Cf-Ray: 58 | - 92053f288f4ce913-LHR 59 | Alt-Svc: 60 | - h3=":443"; ma=86400 61 | body: 62 | encoding: ASCII-8BIT 63 | string: |- 64 | { 65 | "error": { 66 | "message": "No run step found with id 'step_123'.", 67 | "type": "invalid_request_error", 68 | "param": null, 69 | "code": null 70 | } 71 | } 72 | recorded_at: Fri, 14 Mar 2025 16:46:21 GMT 73 | recorded_with: VCR 6.3.1 74 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_files_delete_vector_store_file_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores/vs_8dzN747pTzY2MtyDQoxfwmod/files 6 | body: 7 | encoding: UTF-8 8 | string: '{"file_id":"file-cQkOwz3aospk6aTNL78LAztA"}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 18:55:12 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_5fec774e0ecc487ae2753fb2c85c1873 41 | Openai-Processing-Ms: 42 | - '1364' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=VNgODvRyhg2XY8ZRlTcsXB3QvzoPNOBMlz6_jnDCpUE-1714503312-1.0.1.1-rmUKZPnXKvzKvVZ65GGBWRH5.uN3Uz3yF3KzKw4CKhIYfCPOwAjUS38tEKIFsTuCAs2yLmLtyi3X9uuDLq4Y2w; 49 | path=/; expires=Tue, 30-Apr-24 19:25:12 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=nNsrz3VGoKM5fH7j8qvBr4Ce.Y3wO6OFWSFZcl__Ieo-1714503312509-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c9be9d9da900ef-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "file-cQkOwz3aospk6aTNL78LAztA", 64 | "object": "vector_store.file", 65 | "usage_bytes": 0, 66 | "created_at": 1714503311, 67 | "vector_store_id": "vs_8dzN747pTzY2MtyDQoxfwmod", 68 | "status": "in_progress", 69 | "last_error": null 70 | } 71 | recorded_at: Tue, 30 Apr 2024 18:55:12 GMT 72 | recorded_with: VCR 6.1.0 73 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_files_list_vector_store_file_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores/vs_8HxymfYr9b09lsN1tvtMItOe/files 6 | body: 7 | encoding: UTF-8 8 | string: '{"file_id":"file-hN8c8cMN7IhVulUyYHN3AKQW"}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 18:55:00 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_18bbe9cbb2d3b6c3a650f79e4ffa9cdc 41 | Openai-Processing-Ms: 42 | - '1379' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=O9dhNfXYyveGnWyu2eOSIruNYapl7AKzlHvNP9Ht0qk-1714503300-1.0.1.1-kvRQ6JM4cw7ucCL7VrumMmYpmtFlrEA_ewj1uo23Jb7WdJ34lP6s_RQ0z2unGlPensswZ7I56CNhEODEYAluRg; 49 | path=/; expires=Tue, 30-Apr-24 19:25:00 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=GLCrdeePDD7qJxuVLwwY_tP2VutmZQLF.LuiN2FjLVs-1714503300306-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c9be511bfc51cc-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "file-hN8c8cMN7IhVulUyYHN3AKQW", 64 | "object": "vector_store.file", 65 | "usage_bytes": 0, 66 | "created_at": 1714503299, 67 | "vector_store_id": "vs_8HxymfYr9b09lsN1tvtMItOe", 68 | "status": "in_progress", 69 | "last_error": null 70 | } 71 | recorded_at: Tue, 30 Apr 2024 18:55:00 GMT 72 | recorded_with: VCR 6.1.0 73 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_files_retrieve_vector_store_file_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores/vs_byyoIIfTk2DjCONUuvGFZwrd/files 6 | body: 7 | encoding: UTF-8 8 | string: '{"file_id":"file-wVFDYrCLN5iNPWQid43qSLaj"}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 18:55:04 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_e41b6dfef1c318caf00ed5bb6353f259 41 | Openai-Processing-Ms: 42 | - '1263' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=kZkjxfbNzscnmk.oVLecnqEqneDBlAxwi5mkoZJ0rGw-1714503304-1.0.1.1-v9uGdcXTsgYuYzuDsbkvCJst4ekWLQOeXPP23tWi61XmeAmVv5iWvDMfmRbU5bYtF8bjw9Xg53z7b_gOoHTrdg; 49 | path=/; expires=Tue, 30-Apr-24 19:25:04 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=R3bxSBsk591oWxe2KjrZ4axv_yYkE.HE7JNJXdSaP.g-1714503304721-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c9be6d2aad1d0d-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "file-wVFDYrCLN5iNPWQid43qSLaj", 64 | "object": "vector_store.file", 65 | "usage_bytes": 0, 66 | "created_at": 1714503303, 67 | "vector_store_id": "vs_byyoIIfTk2DjCONUuvGFZwrd", 68 | "status": "in_progress", 69 | "last_error": null 70 | } 71 | recorded_at: Tue, 30 Apr 2024 18:55:04 GMT 72 | recorded_with: VCR 6.1.0 73 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/files_fetch_image_retrieve.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/files/file-K3gwyzsbpt6RXuAfWzgUeh 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Feb 2025 14:55:38 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_a69c47545eca3821335b1d969f38d351 39 | Openai-Processing-Ms: 40 | - '50' 41 | Access-Control-Allow-Origin: 42 | - "*" 43 | Strict-Transport-Security: 44 | - max-age=31536000; includeSubDomains; preload 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=4U9Wp6hNHG.yfESAELNnI6tyERcXSoY5aN2p1feHKu8-1739544938-1.0.1.1-R_DKDjENdsn6knyIPBKNwe7uxrHniHogLQeR4RxWASaS9XShfRDjb8N7M9Ztwlcpu62UJ8Ehd2Wy9p64ahzfjg; 49 | path=/; expires=Fri, 14-Feb-25 15:25:38 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=A2rCOmzJfrjtBXJbQPfBD6VQjiJc1kCzo75fumzx4qs-1739544938927-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | X-Content-Type-Options: 54 | - nosniff 55 | Server: 56 | - cloudflare 57 | Cf-Ray: 58 | - 911de67b29f3bed7-LHR 59 | Alt-Svc: 60 | - h3=":443"; ma=86400 61 | body: 62 | encoding: ASCII-8BIT 63 | string: | 64 | { 65 | "object": "file", 66 | "id": "file-K3gwyzsbpt6RXuAfWzgUeh", 67 | "purpose": "vision", 68 | "filename": "image.png", 69 | "bytes": 249, 70 | "created_at": 1739544938, 71 | "status": "processed", 72 | "status_details": null 73 | } 74 | recorded_at: Fri, 14 Feb 2025 14:55:38 GMT 75 | recorded_with: VCR 6.1.0 76 | -------------------------------------------------------------------------------- /spec/openai/client/audio_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe OpenAI::Client do 2 | describe "#audio" do 3 | describe "#transcribe" do 4 | context "with audio", :vcr do 5 | let(:filename) { "audio_sample.mp3" } 6 | let(:audio) { File.join(RSPEC_ROOT, "fixtures/files", filename) } 7 | 8 | let(:response) do 9 | OpenAI::Client.new.audio.transcribe( 10 | parameters: { 11 | model: model, 12 | file: File.open(audio, "rb") 13 | } 14 | ) 15 | end 16 | let(:content) { response["text"] } 17 | let(:cassette) { "audio #{model} transcribe".downcase } 18 | 19 | context "with model: whisper-1" do 20 | let(:model) { "whisper-1" } 21 | 22 | it "succeeds" do 23 | VCR.use_cassette(cassette) do 24 | expect(content.empty?).to eq(false) 25 | end 26 | end 27 | end 28 | end 29 | end 30 | 31 | describe "#translate" do 32 | context "with audio", :vcr do 33 | let(:filename) { "audio_sample.mp3" } 34 | let(:audio) { File.join(RSPEC_ROOT, "fixtures/files", filename) } 35 | 36 | let(:response) do 37 | OpenAI::Client.new.audio.translate( 38 | parameters: { 39 | model: model, 40 | file: File.open(audio, "rb") 41 | } 42 | ) 43 | end 44 | let(:content) { response["text"] } 45 | let(:cassette) { "audio #{model} translate".downcase } 46 | 47 | context "with model: whisper-1" do 48 | let(:model) { "whisper-1" } 49 | 50 | it "succeeds" do 51 | VCR.use_cassette(cassette) do 52 | expect(content.empty?).to eq(false) 53 | end 54 | end 55 | end 56 | end 57 | end 58 | 59 | describe "#speech" do 60 | context "with audio", :vcr do 61 | let(:model) { "tts-1" } 62 | 63 | it "returns a working mp3 file as body" do 64 | VCR.use_cassette("speech #{model} test") do 65 | response = OpenAI::Client.new.audio.speech( 66 | parameters: { 67 | model: model, 68 | input: "This is a speech test!", 69 | voice: "alloy" 70 | } 71 | ) 72 | expect(response).not_to be_empty 73 | end 74 | end 75 | end 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/finetunes_retrieve_for_create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/files/file-Cbl1lel9YuFDEEYeJu0Kxyvq 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Tue, 15 Oct 2024 06:54:40 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-jxm65ijkzc1qrfhc0ij8moic 37 | X-Request-Id: 38 | - req_e110df59db75a6d059fbad6ba3a3701f 39 | Openai-Processing-Ms: 40 | - '270' 41 | Access-Control-Allow-Origin: 42 | - "*" 43 | Strict-Transport-Security: 44 | - max-age=31536000; includeSubDomains; preload 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=16Nef4YHv.h4RkQwzBa6XC._8fouMkIMH4Z.v1sfYoQ-1728975280-1.0.1.1-QQy7Q.MR9_l3RSlvPfAqzPqfLk7cXay1C004FWrLhcxmrKP50jcI4HFDGQKuuvIkgGvNDLj4bJXoaTK6bxPung; 49 | path=/; expires=Tue, 15-Oct-24 07:24:40 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=Toqm6tlMjEiHK5xL3CCOHrBviLb3pBy.b.glhmqxSJo-1728975280019-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | X-Content-Type-Options: 54 | - nosniff 55 | Server: 56 | - cloudflare 57 | Cf-Ray: 58 | - 8d2de6299c324595-LHR 59 | Alt-Svc: 60 | - h3=":443"; ma=86400 61 | body: 62 | encoding: ASCII-8BIT 63 | string: | 64 | { 65 | "object": "file", 66 | "id": "file-Cbl1lel9YuFDEEYeJu0Kxyvq", 67 | "purpose": "fine-tune", 68 | "filename": "sarcastic.jsonl", 69 | "bytes": 2676, 70 | "created_at": 1728975279, 71 | "status": "processed", 72 | "status_details": null 73 | } 74 | recorded_at: Tue, 15 Oct 2024 06:54:39 GMT 75 | recorded_with: VCR 6.1.0 76 | -------------------------------------------------------------------------------- /lib/openai/files.rb: -------------------------------------------------------------------------------- 1 | module OpenAI 2 | class Files 3 | PURPOSES = %w[ 4 | assistants 5 | batch 6 | fine-tune 7 | vision 8 | user_data 9 | evals 10 | ].freeze 11 | 12 | def initialize(client:) 13 | @client = client 14 | end 15 | 16 | def list(parameters: {}) 17 | @client.get(path: "/files", parameters: parameters) 18 | end 19 | 20 | def upload(parameters: {}) 21 | file_input = parameters[:file] 22 | file = prepare_file_input(file_input: file_input) 23 | validate(file: file, purpose: parameters[:purpose], file_input: file_input) 24 | @client.multipart_post( 25 | path: "/files", 26 | parameters: parameters.merge(file: file) 27 | ) 28 | ensure 29 | file.close if file.is_a?(File) 30 | end 31 | 32 | def retrieve(id:, parameters: {}) 33 | @client.get(path: "/files/#{id}", parameters: parameters) 34 | end 35 | 36 | def content(id:, parameters: {}) 37 | @client.get(path: "/files/#{id}/content", parameters: parameters) 38 | end 39 | 40 | def delete(id:) 41 | @client.delete(path: "/files/#{id}") 42 | end 43 | 44 | private 45 | 46 | def prepare_file_input(file_input:) 47 | if file_input.is_a?(String) 48 | File.open(file_input) 49 | elsif file_input.respond_to?(:read) && file_input.respond_to?(:rewind) 50 | file_input 51 | else 52 | raise ArgumentError, "Invalid file - must be a StringIO object or a path to a file." 53 | end 54 | end 55 | 56 | def validate(file:, purpose:, file_input:) 57 | raise ArgumentError, "`file` is required" if file.nil? 58 | 59 | unless PURPOSES.include?(purpose) 60 | filename = file_input.is_a?(String) ? File.basename(file_input) : "uploaded file" 61 | message = "The purpose '#{purpose}' for file '#{filename}' is not in the known purpose " 62 | message += "list: #{PURPOSES.join(', ')}." 63 | OpenAI.log_message("Warning", message, :warn) 64 | end 65 | 66 | validate_jsonl(file: file) if file_input.is_a?(String) && file_input.end_with?(".jsonl") 67 | end 68 | 69 | def validate_jsonl(file:) 70 | file.each_line.with_index do |line, index| 71 | JSON.parse(line) 72 | rescue JSON::ParserError => e 73 | raise JSON::ParserError, "#{e.message} - found on line #{index + 1} of #{file}" 74 | end 75 | ensure 76 | file.rewind 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /spec/openai/client/assistants_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe OpenAI::Client do 2 | describe "#assistants" do 3 | let(:assistant_id) do 4 | VCR.use_cassette("#{cassette} setup") do 5 | OpenAI::Client.new.assistants.create( 6 | parameters: { 7 | model: "gpt-4", 8 | name: "OpenAI-Ruby test assistant" 9 | } 10 | )["id"] 11 | end 12 | end 13 | 14 | describe "#list", :vcr do 15 | let(:response) { OpenAI::Client.new.assistants.list } 16 | let(:cassette) { "assistants list" } 17 | 18 | before { assistant_id } 19 | 20 | it "succeeds" do 21 | VCR.use_cassette(cassette) do 22 | expect(response.dig("data", 0, "object")).to eq("assistant") 23 | end 24 | end 25 | end 26 | 27 | describe "#retrieve" do 28 | let(:cassette) { "assistants retrieve" } 29 | let(:response) { OpenAI::Client.new.assistants.retrieve(id: assistant_id) } 30 | 31 | it "succeeds" do 32 | VCR.use_cassette(cassette) do 33 | expect(response["object"]).to eq("assistant") 34 | end 35 | end 36 | end 37 | 38 | describe "#create" do 39 | let(:cassette) { "assistants create" } 40 | let(:response) do 41 | OpenAI::Client.new.assistants.create(parameters: { model: "gpt-4", 42 | name: "OpenAI-Ruby test assistant" }) 43 | end 44 | 45 | it "succeeds" do 46 | VCR.use_cassette(cassette) do 47 | expect(response["object"]).to eq "assistant" 48 | end 49 | end 50 | end 51 | 52 | describe "#modify" do 53 | let(:cassette) { "assistants modify" } 54 | let(:response) do 55 | OpenAI::Client.new.assistants.modify( 56 | id: assistant_id, 57 | parameters: { model: "gpt-3.5-turbo", name: "Test Assistant for OpenAI-Ruby" } 58 | ) 59 | end 60 | 61 | it "succeeds" do 62 | VCR.use_cassette(cassette) do 63 | expect(response["object"]).to eq "assistant" 64 | end 65 | end 66 | end 67 | 68 | describe "#delete" do 69 | let(:cassette) { "assistants delete" } 70 | let(:response) do 71 | OpenAI::Client.new.assistants.delete(id: assistant_id) 72 | end 73 | 74 | it "succeeds" do 75 | VCR.use_cassette(cassette) do 76 | expect(response["object"]).to eq "assistant.deleted" 77 | end 78 | end 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/files_fetch_image.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/files/file-K3gwyzsbpt6RXuAfWzgUeh/content 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 14 Feb 2025 14:55:39 GMT 27 | Content-Type: 28 | - application/octet-stream 29 | Content-Length: 30 | - '249' 31 | Connection: 32 | - keep-alive 33 | Content-Disposition: 34 | - attachment; filename="image.png" 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - user-jxm65ijkzc1qrfhc0ij8moic 39 | X-Request-Id: 40 | - req_a5152835bd870146f9ab8757b5da7dfd 41 | Openai-Processing-Ms: 42 | - '146' 43 | Access-Control-Allow-Origin: 44 | - "*" 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains; preload 47 | Cf-Cache-Status: 48 | - DYNAMIC 49 | Set-Cookie: 50 | - __cf_bm=OU9._V0EmSRhM4s0ZTGsIS9Cl9p96JNMkYRPUhp_5Zc-1739544939-1.0.1.1-HSKYH1OiIdlBJAoRJSe1_nhJYAhmMyJF753RZNSM_nP06JvyNTPFSeedU_dF4DsuIfp9SL3sepk9Twcxme7NPg; 51 | path=/; expires=Fri, 14-Feb-25 15:25:39 GMT; domain=.api.openai.com; HttpOnly; 52 | Secure; SameSite=None 53 | - _cfuvid=3UUB2JFeoIxCEm1YHTZn.YuCUyNpuMcLz4KkvNW1aLE-1739544939273-0.0.1.1-604800000; 54 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 55 | X-Content-Type-Options: 56 | - nosniff 57 | Server: 58 | - cloudflare 59 | Cf-Ray: 60 | - 911de67c9f9d0038-LHR 61 | Alt-Svc: 62 | - h3=":443"; ma=86400 63 | body: 64 | encoding: ASCII-8BIT 65 | string: !binary |- 66 | iVBORw0KGgoAAAANSUhEUgAAAEQAAABECAYAAAA4E5OyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACOSURBVHgB7dBBDcAgAAAxNnGYRCz8SDgNrYR+c+09uP7BQ0gICSEhJISEkBASQkJICAkhISSEhJAQEkJCSAgJISEkhISQEBJCQkgICSEhJISEkBASQkJICAkhISSEhJAQEkJCSAgJISEkhISQEBJCQkgICSEhJISEkBASQkJICAkhISSEhJAQEkJCSAiJA8NoA2dv2YpOAAAAAElFTkSuQmCC 67 | recorded_at: Fri, 14 Feb 2025 14:55:39 GMT 68 | recorded_with: VCR 6.1.0 69 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_file_batches_retrieve.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/vector_stores/vs_qBhwT3kAs6fxYFe9VHcwDwAB/file_batches/vsfb_6fbe4acad12b408191386a762ebbd09a 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 20:56:59 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_4e502a48d951c1b6b3dac1549493117b 41 | Openai-Processing-Ms: 42 | - '105' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=W1XghgolgKcQCFAlR9SJ_KQRc6dgGOXq.6ytEftYzxE-1714510619-1.0.1.1-Uog84zWTKxDR4q210jX8y9WYL96XFpH_1o2ItlT6bRuwt6698RrksX8NGJ1FckCeyfaIiFkHttqgEcTqR8FpmQ; 49 | path=/; expires=Tue, 30-Apr-24 21:26:59 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=iFpRSxHh0JNtkqQa6aYanuIedEYcri4bCDt2pL8urrE-1714510619582-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87ca710a8d0051e0-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vsfb_6fbe4acad12b408191386a762ebbd09a", 64 | "object": "vector_store.file_batch", 65 | "created_at": 1714510619, 66 | "status": "completed", 67 | "vector_store_id": "vs_qBhwT3kAs6fxYFe9VHcwDwAB", 68 | "file_counts": { 69 | "in_progress": 0, 70 | "completed": 1, 71 | "failed": 0, 72 | "cancelled": 0, 73 | "total": 1 74 | } 75 | } 76 | recorded_at: Tue, 30 Apr 2024 20:56:59 GMT 77 | recorded_with: VCR 6.1.0 78 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_file_batches_create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores/vs_yrC4Y143ue4kRkSxu1VW8ece/file_batches 6 | body: 7 | encoding: UTF-8 8 | string: '{"file_ids":["file-0cXHcvZknvN0EVU5JpYm5S0U"]}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 20:57:03 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_8638f3897e8c10d74d1190da9f93d209 41 | Openai-Processing-Ms: 42 | - '1320' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=_9xWEs0qkyyHhWE4G.xiCuJ7sWhzQDslhHKnn52wtn4-1714510623-1.0.1.1-beVdeaEW61ignNBFm9iLhg7PLkWIloagNkeksWrIVDIlrGvw33cRJARNQ9qXTzzkum8eIwTQX_0.1pIodbMXZQ; 49 | path=/; expires=Tue, 30-Apr-24 21:27:03 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=b09APS.Dvj7RBLVOouT5TcjuifdpbuQvMsKIeJ3HNEM-1714510623065-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87ca71188de61d38-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vsfb_2ff3927d8fd542d59610690b460d5d62", 64 | "object": "vector_store.file_batch", 65 | "created_at": 1714510622, 66 | "status": "in_progress", 67 | "vector_store_id": "vs_yrC4Y143ue4kRkSxu1VW8ece", 68 | "file_counts": { 69 | "in_progress": 1, 70 | "completed": 0, 71 | "failed": 0, 72 | "cancelled": 0, 73 | "total": 1 74 | } 75 | } 76 | recorded_at: Tue, 30 Apr 2024 20:57:03 GMT 77 | recorded_with: VCR 6.1.0 78 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_stores_create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 16:41:34 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_10a3172ba3a5cf01c4e14c690795a646 41 | Openai-Processing-Ms: 42 | - '298' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=fx_YqjkKO9nH08e8caafVXLyhi8Wb5i2lQ1W3mld.YM-1714495294-1.0.1.1-WqFQ33scAb7HkBjiRb63lwUTxFCg8l2yLS6HLgjFuD2S037nbt4fKh5.SBDB_F69klK_MmdGJSTWaUJK26nObw; 49 | path=/; expires=Tue, 30-Apr-24 17:11:34 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=HkOfANtrx.fzf8512ER8ESBZJorcTSQiUs1V.PgPP1g-1714495294435-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c8fae359a51a91-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_kWaCiN8Ii4e1hefIou5skq9g", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1714495294, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": {}, 77 | "expires_after": null, 78 | "expires_at": null, 79 | "last_active_at": 1714495294 80 | } 81 | recorded_at: Tue, 30 Apr 2024 16:41:34 GMT 82 | recorded_with: VCR 6.1.0 83 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_file_batches_cancel_vector_store_file_batch_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores/vs_QcdNZBbMIbTve3S9eR3n6E17/file_batches 6 | body: 7 | encoding: UTF-8 8 | string: '{"file_ids":["file-FCgJPLryUiHJru6yPtHzjjh3"]}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 20:57:06 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_392363d64cd7f8743b898817cb458029 41 | Openai-Processing-Ms: 42 | - '1304' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=47PRLUlyuH4HKYKMQ901.i_TVUaqOgJrLkG1jbf0.H4-1714510626-1.0.1.1-SYvxVeunnNO6OXjue3auNoVmsM5WOo_cDiTy5tSVkLrmeAEuxhjK9M9SmNchfm1jBE4yGPYoCRtNHGcjauG_jQ; 49 | path=/; expires=Tue, 30-Apr-24 21:27:06 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=sBcSLxgNVuwOEHPZC.tHuF_CNjsd71bN34tPquUs9J8-1714510626496-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87ca712e1a021d23-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vsfb_ddc37037b1d24838bcd3d3e4048aed78", 64 | "object": "vector_store.file_batch", 65 | "created_at": 1714510626, 66 | "status": "in_progress", 67 | "vector_store_id": "vs_QcdNZBbMIbTve3S9eR3n6E17", 68 | "file_counts": { 69 | "in_progress": 1, 70 | "completed": 0, 71 | "failed": 0, 72 | "cancelled": 0, 73 | "total": 1 74 | } 75 | } 76 | recorded_at: Tue, 30 Apr 2024 20:57:06 GMT 77 | recorded_with: VCR 6.1.0 78 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_file_batches_retrieve_vector_store_file_batch_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores/vs_qBhwT3kAs6fxYFe9VHcwDwAB/file_batches 6 | body: 7 | encoding: UTF-8 8 | string: '{"file_ids":["file-CgOz2aPJTpMBLCPfvdGHpSkx"]}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 20:56:58 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_c71f2f212de799ee10c1f74d819a8ae5 41 | Openai-Processing-Ms: 42 | - '1290' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=oJcJmsXeX.ucP8xdV5S7DTKBzXff4MEIcx7PNirf4uU-1714510618-1.0.1.1-4bLUhyeHh.WJFo_Iv4htzZtWvlUXP58bp1ygxBsibDDg_OOqKBsYTe0mhqr7XMUmSbPYcWv0WiX8rc1t4C_oyg; 49 | path=/; expires=Tue, 30-Apr-24 21:26:58 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=rAoE9AXKR.nubOCsdNZ2gK.MSNoLlaga8xwRqXsy_gE-1714510618932-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87ca70fece02a197-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vsfb_6fbe4acad12b408191386a762ebbd09a", 64 | "object": "vector_store.file_batch", 65 | "created_at": 1714510618, 66 | "status": "in_progress", 67 | "vector_store_id": "vs_qBhwT3kAs6fxYFe9VHcwDwAB", 68 | "file_counts": { 69 | "in_progress": 1, 70 | "completed": 0, 71 | "failed": 0, 72 | "cancelled": 0, 73 | "total": 1 74 | } 75 | } 76 | recorded_at: Tue, 30 Apr 2024 20:56:58 GMT 77 | recorded_with: VCR 6.1.0 78 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_stores_delete_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 16:41:37 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_8ee82dc580571d1626616aa520d89ac8 41 | Openai-Processing-Ms: 42 | - '341' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=Wvl9rfoENnnUnPHMkY0ef4MRjmKBbTYLqusFZQDEWR8-1714495297-1.0.1.1-jjELIwQsfqnveMSFoBKsABeBy3JNcmTEPSO4ot69t9JZO1THt8D6GUPuGy02d_hIZ4TJpsihxeeeUvHb.WBvjQ; 49 | path=/; expires=Tue, 30-Apr-24 17:11:37 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=8Rls3QoJsBohV37Y4Cc1pU0creW0ep26r6eAFiCFYxQ-1714495297057-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c8faf3681c877b-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_DBrde7MywMBEdxmzXaeO76Ky", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1714495296, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": {}, 77 | "expires_after": null, 78 | "expires_at": null, 79 | "last_active_at": 1714495296 80 | } 81 | recorded_at: Tue, 30 Apr 2024 16:41:36 GMT 82 | recorded_with: VCR 6.1.0 83 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_stores_modify_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 16:41:35 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_731195d616bb66be48ad0abc3865a8b8 41 | Openai-Processing-Ms: 42 | - '333' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=Awv48eD2.8N1qKbwD1OfJ7y3Lpq5yH4sKwYuE8s.8yk-1714495295-1.0.1.1-IPPIQoTXxyjUqezQJl1yEVnf8SIXPqX5E.47n5ETOyVXuBkgFDyVeMIdeiZJgC4Kf7mScSNRr6bW2qjTdpj6iQ; 49 | path=/; expires=Tue, 30-Apr-24 17:11:35 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=.QePlceeit2HmCD6dNNauT3lRdt_sTjPR1qG8xuTO38-1714495295311-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c8fae87c761aa4-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_jQLnn4Fn6Kzt8GGw73LSKky2", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1714495295, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": {}, 77 | "expires_after": null, 78 | "expires_at": null, 79 | "last_active_at": 1714495295 80 | } 81 | recorded_at: Tue, 30 Apr 2024 16:41:35 GMT 82 | recorded_with: VCR 6.1.0 83 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_stores_retrieve_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 16:41:33 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_827758a71d023ffd2f10f5207072452e 41 | Openai-Processing-Ms: 42 | - '382' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=RLKEJiQXdwNJ9Mw9.Lm72936HnuD8T81hh2gGaitE5w-1714495292-1.0.1.1-I4XzgZX2Aju3rV2vHwu0iG_KJCHSDBGdrFIFm_8222bYwJLlpZvK4p6WoNl.EF7GmXNEupmsB8KeCMVh3SBFeg; 49 | path=/; expires=Tue, 30-Apr-24 17:11:32 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=fIbxQif_VJIs0eOZUEjWP.OfR3lY24J_uc.w1KSpUls-1714495293000-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c8fad9cf631a88-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_hpLLcdz2Mvm3bKsGnN4hEsv2", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1714495292, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": {}, 77 | "expires_after": null, 78 | "expires_at": null, 79 | "last_active_at": 1714495292 80 | } 81 | recorded_at: Tue, 30 Apr 2024 16:41:32 GMT 82 | recorded_with: VCR 6.1.0 83 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_file_batches_cancel.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores/vs_QcdNZBbMIbTve3S9eR3n6E17/file_batches/vsfb_ddc37037b1d24838bcd3d3e4048aed78/cancel 6 | body: 7 | encoding: UTF-8 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Content-Length: 17 | - '0' 18 | Accept-Encoding: 19 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 20 | Accept: 21 | - "*/*" 22 | User-Agent: 23 | - Ruby 24 | response: 25 | status: 26 | code: 200 27 | message: OK 28 | headers: 29 | Date: 30 | - Tue, 30 Apr 2024 20:57:07 GMT 31 | Content-Type: 32 | - application/json 33 | Transfer-Encoding: 34 | - chunked 35 | Connection: 36 | - keep-alive 37 | Openai-Version: 38 | - '2020-10-01' 39 | Openai-Organization: 40 | - gojom-1 41 | X-Request-Id: 42 | - req_01318927b71a7d2e76268caf49b6bea6 43 | Openai-Processing-Ms: 44 | - '160' 45 | Strict-Transport-Security: 46 | - max-age=15724800; includeSubDomains 47 | Cf-Cache-Status: 48 | - DYNAMIC 49 | Set-Cookie: 50 | - __cf_bm=CW4.gWsOsjbXJymO5ablC3qenCCtJGSr5q_NAfl2MkI-1714510627-1.0.1.1-nOAqJv7NOFnDyBT7xtldezSDGwj9rqUGKjXeGPyrl9tzGWeqwOiRr5yvOSWe140snf1oCW1gXQNAoEUD4FNzVw; 51 | path=/; expires=Tue, 30-Apr-24 21:27:07 GMT; domain=.api.openai.com; HttpOnly; 52 | Secure; SameSite=None 53 | - _cfuvid=V.1YuucVb9ymbBXaDtPauuKH6mG7wQJLx_h7NMsuI9Y-1714510627214-0.0.1.1-604800000; 54 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 55 | Server: 56 | - cloudflare 57 | Cf-Ray: 58 | - 87ca7139e8681d05-GRU 59 | Alt-Svc: 60 | - h3=":443"; ma=86400 61 | body: 62 | encoding: ASCII-8BIT 63 | string: |- 64 | { 65 | "id": "vsfb_ddc37037b1d24838bcd3d3e4048aed78", 66 | "object": "vector_store.file_batch", 67 | "created_at": 1714510627, 68 | "status": "completed", 69 | "vector_store_id": "vs_QcdNZBbMIbTve3S9eR3n6E17", 70 | "file_counts": { 71 | "in_progress": 0, 72 | "completed": 1, 73 | "failed": 0, 74 | "cancelled": 0, 75 | "total": 1 76 | } 77 | } 78 | recorded_at: Tue, 30 Apr 2024 20:57:07 GMT 79 | recorded_with: VCR 6.1.0 80 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_files_list_vector_store_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 18:54:57 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_86222f589d21c5d8d0a53d5ae721461d 41 | Openai-Processing-Ms: 42 | - '57' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=28ZbB17mhRFGVcARcAci53wE8EqK7So18q8ZoUG6JJk-1714503297-1.0.1.1-yo4o.UhPgbaz2Y5bdUE0F.o3PNYfjWohRnfx_C8k6kNKutR7LZZDC7XV4QYuCSjY.cI_6hsCnhfMjF1dNQ_D3w; 49 | path=/; expires=Tue, 30-Apr-24 19:24:57 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=M6mYWcKJmL5tpQpUqEyADABEptcNfqc5BxK1a8KYDKM-1714503297270-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c9be461d4c1b21-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_8HxymfYr9b09lsN1tvtMItOe", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1714503297, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": {}, 77 | "expires_after": null, 78 | "expires_at": null, 79 | "last_active_at": 1714503297 80 | } 81 | recorded_at: Tue, 30 Apr 2024 18:54:57 GMT 82 | recorded_with: VCR 6.1.0 83 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/batches_cancel.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/batches/batch_67d450d136988190a52c317c904bba69/cancel 6 | body: 7 | encoding: UTF-8 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Content-Length: 15 | - '0' 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Fri, 14 Mar 2025 15:52:50 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - user-jxm65ijkzc1qrfhc0ij8moic 39 | X-Request-Id: 40 | - req_cb3815aa23785e565628b6a65c4f105a 41 | Openai-Processing-Ms: 42 | - '626' 43 | Strict-Transport-Security: 44 | - max-age=31536000; includeSubDomains; preload 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=z6Qaav1W7ZS6UMQ.QrckOoH1Sl.SoX.o3uMy1hP4p5w-1741967570-1.0.1.1-t9cgO3Tq0XzWPZKDUL5AkXI90RyYI_1aYGYUpqDjwqe5gj7_5fGnFEeSem5818ySfVavs5cisTtThDGBeFLYQ1KMrdb6mBDQqf5kX73rKIs; 49 | path=/; expires=Fri, 14-Mar-25 16:22:50 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=2OyeRYWwr2KRqwGt_2Iuj3O2SQZJ_Dd5YAgGjOqfrdY-1741967570297-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | X-Content-Type-Options: 54 | - nosniff 55 | Server: 56 | - cloudflare 57 | Cf-Ray: 58 | - 9204f0bd7843d1fb-LHR 59 | Alt-Svc: 60 | - h3=":443"; ma=86400 61 | body: 62 | encoding: ASCII-8BIT 63 | string: '{"id":"batch_67d450d136988190a52c317c904bba69","object":"batch","endpoint":"/v1/chat/completions","errors":null,"input_file_id":"file-EFcEfUGSc6QsJMU7zYwMW3","completion_window":"24h","status":"cancelling","output_file_id":null,"error_file_id":null,"created_at":1741967569,"in_progress_at":null,"expires_at":1742053969,"finalizing_at":null,"completed_at":null,"failed_at":null,"expired_at":null,"cancelling_at":1741967569,"cancelled_at":null,"request_counts":{"total":0,"completed":0,"failed":0},"metadata":null}' 64 | recorded_at: Fri, 14 Mar 2025 15:52:50 GMT 65 | recorded_with: VCR 6.1.0 66 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_files_create_vector_store_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 18:55:05 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_7b02fa44d3260f17768f2566482e01a0 41 | Openai-Processing-Ms: 42 | - '73' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=X8Q5Feq8ESL_45Shr90z8pkLBJMStTD6ZsKFiTbTLNc-1714503305-1.0.1.1-1nJwgmyLypZwPtmPpsUPiFCW9MB7PfoYNMCbLdIDw7O8Lk9fHokxZFr4AK4bCt3GolxvdBTos5QJkf.iiMVy2w; 49 | path=/; expires=Tue, 30-Apr-24 19:25:05 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=FPf3jCHLxTFUJhcN1cDwVo5nXnPuMcfNMp_bSZYichU-1714503305974-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c9be7c8dbf1d1f-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_jXM8wckBS9gmjt8GlJAqFwwc", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1714503305, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": {}, 77 | "expires_after": null, 78 | "expires_at": null, 79 | "last_active_at": 1714503305 80 | } 81 | recorded_at: Tue, 30 Apr 2024 18:55:05 GMT 82 | recorded_with: VCR 6.1.0 83 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_files_delete_vector_store_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 18:55:09 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_d810b8828f70dcb14796f537612c47a3 41 | Openai-Processing-Ms: 42 | - '93' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=pNYoJdZGATE7DjRCNe1FCfsRNlgTjAyo77v6kz3ItMs-1714503309-1.0.1.1-5yuEmw44qHzA4NWZxgGcBZtZ5ZVPuQ7rTfYlFFj0p_4hbd3d6XX4_oyllIc8eNx2m9Nk.BvYCVQXcs02KoIsHw; 49 | path=/; expires=Tue, 30-Apr-24 19:25:09 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=oxG_D4HPFQTW3Qo1Ft0Qe4qgoJufiYkLzMf9xomATlU-1714503309746-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c9be93bebe1d0f-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_8dzN747pTzY2MtyDQoxfwmod", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1714503309, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": {}, 77 | "expires_after": null, 78 | "expires_at": null, 79 | "last_active_at": 1714503309 80 | } 81 | recorded_at: Tue, 30 Apr 2024 18:55:09 GMT 82 | recorded_with: VCR 6.1.0 83 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_files_retrieve_vector_store_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 18:55:02 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_df82dfa113105aa25f93e0dae559da67 41 | Openai-Processing-Ms: 42 | - '523' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=fJJg6gGmz.MH0m56EozCwO74ZST89mGDyIRk0qNwm24-1714503302-1.0.1.1-OVFR3sVV6zXS2ZLEfTg9dyYVdd8Jib0T3Qo7vhzrAe9UJ_fiH_zMRyFplKjlDuy2nNv492LcF8YEwNNwyezjNA; 49 | path=/; expires=Tue, 30-Apr-24 19:25:02 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=lE2pNvyWmG60mNZNtwsyVgdeNo42QID2xE7gWDx134E-1714503302023-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c9be6119291cf1-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_byyoIIfTk2DjCONUuvGFZwrd", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1714503301, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": {}, 77 | "expires_after": null, 78 | "expires_at": null, 79 | "last_active_at": 1714503301 80 | } 81 | recorded_at: Tue, 30 Apr 2024 18:55:01 GMT 82 | recorded_with: VCR 6.1.0 83 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_stores_list_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Mon, 10 Jun 2024 12:10:27 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - user-jxm65ijkzc1qrfhc0ij8moic 39 | X-Request-Id: 40 | - req_11abe9630b3810f2b4a3830baf3602c1 41 | Openai-Processing-Ms: 42 | - '50' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=.TcXhXR2qRzygfwaMGIthLW3zJ0Ml61BGRaerXUDCT8-1718021427-1.0.1.1-TBUC6Dnk3iGIwgISgt_b5oBCchxmQvVwUB3Ul2K7MzKIyIHtZicd9goCaxvy0i4n5Q90D61OK7nRYw0HgqgIqA; 49 | path=/; expires=Mon, 10-Jun-24 12:40:27 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=kxtHyeeOy4pOM361vuyRE3fLktW.Sa6J5vch3d_Afv4-1718021427933-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 891942236f1794d3-LHR 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_q7pDyRpqkJovo5OWAyzEhOZ9", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1718021427, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": {}, 77 | "expires_after": null, 78 | "expires_at": null, 79 | "last_active_at": 1718021427 80 | } 81 | recorded_at: Mon, 10 Jun 2024 12:10:27 GMT 82 | recorded_with: VCR 6.1.0 83 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_file_batches_cancel_vector_store_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 20:57:03 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_b83730544ba66caa0a55fe6a86ddd3a3 41 | Openai-Processing-Ms: 42 | - '90' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=xaro7eJPFfRfyJZi3cdgVlf3KWcl5pxwHhtoRIFc3Fk-1714510623-1.0.1.1-mseJiuH9gW9tOaeBs4xt5Snjj3Pp.w1lePVrfMd_mVyduaPZT7CLgTo2U_uA3BKMnQMcf0ffF.oK1bC0T3u.dA; 49 | path=/; expires=Tue, 30-Apr-24 21:27:03 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=aA_3agUxvEM6rdu8vD5kzVC1FyTjE5qYigeF6FGAQRw-1714510623770-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87ca7124c9331cd9-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_QcdNZBbMIbTve3S9eR3n6E17", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1714510623, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": {}, 77 | "expires_after": null, 78 | "expires_at": null, 79 | "last_active_at": 1714510623 80 | } 81 | recorded_at: Tue, 30 Apr 2024 20:57:03 GMT 82 | recorded_with: VCR 6.1.0 83 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_file_batches_create_vector_store_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 20:57:00 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_955b147716739c3503980367c407ba58 41 | Openai-Processing-Ms: 42 | - '97' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=vvuy0cSxYfodWo38srzu_A4cvptGKewqZJ5xLDEnpyQ-1714510620-1.0.1.1-SQbvfETsOJVM9GzWoLBdKkv.BwOMdSVkPbQ9WkAkPBm3Ww0sKau4b_yjUVGyOf.2oZDXFv.rDPvOLpyhXvxhFQ; 49 | path=/; expires=Tue, 30-Apr-24 21:27:00 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=9Qv_OTKYlWuDHULv1b5MmFCbqRdgZOqHMZ8mOmygvdg-1714510620323-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87ca710f3bfb77ca-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_yrC4Y143ue4kRkSxu1VW8ece", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1714510620, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": {}, 77 | "expires_after": null, 78 | "expires_at": null, 79 | "last_active_at": 1714510620 80 | } 81 | recorded_at: Tue, 30 Apr 2024 20:57:00 GMT 82 | recorded_with: VCR 6.1.0 83 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_stores_retrieve.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/vector_stores/vs_hpLLcdz2Mvm3bKsGnN4hEsv2 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 16:41:33 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_51a57df69f7ba7b211d6a929ada1317a 41 | Openai-Processing-Ms: 42 | - '108' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=HaK9XWZ2atxgqReyxO83oYni4CR366u2NLDZBW6mug0-1714495293-1.0.1.1-A5BaLYUC3M0I7Tqy_.wvSRXf_4dKuejU6uY7xX8UjCrVZz0L86GSgSr2HZ6FtOpiaIuFkXJvYFZqnAV6qsE8DQ; 49 | path=/; expires=Tue, 30-Apr-24 17:11:33 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=juhVblaH4FE1qNM4ttMl9m6PUY02Sa5ar9vXKX.bVZg-1714495293626-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c8fadf5ff1a15e-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_hpLLcdz2Mvm3bKsGnN4hEsv2", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1714495292, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": {}, 77 | "expires_after": null, 78 | "expires_at": null, 79 | "last_active_at": 1714495292 80 | } 81 | recorded_at: Tue, 30 Apr 2024 16:41:33 GMT 82 | recorded_with: VCR 6.1.0 83 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_file_batches_retrieve_vector_store_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 20:56:56 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_bb0fda6d35ee5acffa6974fd2ba24ac4 41 | Openai-Processing-Ms: 42 | - '401' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=c0yNMEJvZzE0SAag1MoAYy5MmvnAgzPVZ9cDz5s3cyo-1714510616-1.0.1.1-3lFjOf.O_.6TqDd4b61pfgCCAv3mAoVMRE1CE0mQqPY1ofmVEdJHGWddJPE6N3EJVnIL2tP2M0c.mFATqhZNNA; 49 | path=/; expires=Tue, 30-Apr-24 21:26:56 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=BAwd4kfdBe.Q4bKmmWRpxq6vDLjEvbmsjN9922Agr5w-1714510616085-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87ca70f2b9b31b24-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_qBhwT3kAs6fxYFe9VHcwDwAB", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1714510615, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": {}, 77 | "expires_after": null, 78 | "expires_at": null, 79 | "last_active_at": 1714510615 80 | } 81 | recorded_at: Tue, 30 Apr 2024 20:56:56 GMT 82 | recorded_with: VCR 6.1.0 83 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_files_list.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.openai.com/v1/vector_stores/vs_8HxymfYr9b09lsN1tvtMItOe/files 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 18:55:00 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_d90ad49fef2a7a7f08320ec4047383b7 41 | Openai-Processing-Ms: 42 | - '109' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=3d1jYP7kxrAcfhs9shud8gp0l4KafGgIF0LYjgvigys-1714503300-1.0.1.1-UWBu3joNBe0mP6V3eRfC3FNcj7s5hCuN7dOa97IUbWXIqK_w2DzAd.fTzmw0x1kp_NGmNJWm.C1AicWacN.CzA; 49 | path=/; expires=Tue, 30-Apr-24 19:25:00 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=LiQYGjn_CHYEiAH.7M8bBuPVzH7z.I7HSHBKUI2CPz8-1714503300968-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c9be5d0de16b08-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "object": "list", 64 | "data": [ 65 | { 66 | "id": "file-hN8c8cMN7IhVulUyYHN3AKQW", 67 | "object": "vector_store.file", 68 | "usage_bytes": 1036, 69 | "created_at": 1714503299, 70 | "vector_store_id": "vs_8HxymfYr9b09lsN1tvtMItOe", 71 | "status": "completed", 72 | "last_error": null 73 | } 74 | ], 75 | "first_id": "file-hN8c8cMN7IhVulUyYHN3AKQW", 76 | "last_id": "file-hN8c8cMN7IhVulUyYHN3AKQW", 77 | "has_more": false 78 | } 79 | recorded_at: Tue, 30 Apr 2024 18:55:00 GMT 80 | recorded_with: VCR 6.1.0 81 | -------------------------------------------------------------------------------- /spec/fixtures/files/sarcastic.jsonl: -------------------------------------------------------------------------------- 1 | {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital of France?"}, {"role": "assistant", "content": "Paris, as if everyone doesn't know that already."}]} 2 | {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'Romeo and Juliet'?"}, {"role": "assistant", "content": "Oh, just some guy named William Shakespeare. Ever heard of him?"}]} 3 | {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "How far is the Moon from Earth?"}, {"role": "assistant", "content": "Around 384,400 kilometers. Give or take a few, like that really matters."}]} 4 | {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the largest mammal?"}, {"role": "assistant", "content": "The blue whale, but I'm sure it's just having a big-boned moment."}]} 5 | {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the tallest mountain in the world?"}, {"role": "assistant", "content": "Everest. But hey, it's just a hill compared to other things in the universe."}]} 6 | {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who discovered gravity?"}, {"role": "assistant", "content": "Oh, just a chap named Isaac Newton. He had an apple moment, nothing big."}]} 7 | {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the chemical symbol for water?"}, {"role": "assistant", "content": "H2O. But don't confuse it with H2O2, unless you're in for some bubbly fun!"}]} 8 | {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "When was the Eiffel Tower built?"}, {"role": "assistant", "content": "1889. Because Paris just felt like having a gigantic metal stick in the middle."}]} 9 | {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who painted the Mona Lisa?"}, {"role": "assistant", "content": "Leonardo da Vinci. But I heard it was just a side gig for him."}]} 10 | {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "How many planets are in our solar system?"}, {"role": "assistant", "content": "Eight. Sorry, Pluto, you're still not on the list!"}]} 11 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | ruby-openai (8.3.0) 5 | event_stream_parser (>= 0.3.0, < 2.0.0) 6 | faraday (>= 1) 7 | faraday-multipart (>= 1) 8 | 9 | GEM 10 | remote: https://rubygems.org/ 11 | specs: 12 | addressable (2.8.7) 13 | public_suffix (>= 2.0.2, < 7.0) 14 | ast (2.4.2) 15 | base64 (0.2.0) 16 | bigdecimal (3.1.9) 17 | byebug (11.1.3) 18 | crack (1.0.0) 19 | bigdecimal 20 | rexml 21 | diff-lcs (1.5.1) 22 | dotenv (2.8.1) 23 | event_stream_parser (1.0.0) 24 | faraday (2.8.1) 25 | base64 26 | faraday-net_http (>= 2.0, < 3.1) 27 | ruby2_keywords (>= 0.0.4) 28 | faraday-multipart (1.0.4) 29 | multipart-post (~> 2) 30 | faraday-net_http (3.0.2) 31 | hashdiff (1.1.2) 32 | json (2.10.2) 33 | language_server-protocol (3.17.0.4) 34 | lint_roller (1.1.0) 35 | multipart-post (2.3.0) 36 | parallel (1.26.3) 37 | parser (3.3.7.1) 38 | ast (~> 2.4.1) 39 | racc 40 | public_suffix (6.0.1) 41 | racc (1.8.1) 42 | rainbow (3.1.1) 43 | rake (13.2.1) 44 | regexp_parser (2.10.0) 45 | rexml (3.3.9) 46 | rspec (3.13.0) 47 | rspec-core (~> 3.13.0) 48 | rspec-expectations (~> 3.13.0) 49 | rspec-mocks (~> 3.13.0) 50 | rspec-core (3.13.0) 51 | rspec-support (~> 3.13.0) 52 | rspec-expectations (3.13.0) 53 | diff-lcs (>= 1.2.0, < 2.0) 54 | rspec-support (~> 3.13.0) 55 | rspec-mocks (3.13.0) 56 | diff-lcs (>= 1.2.0, < 2.0) 57 | rspec-support (~> 3.13.0) 58 | rspec-support (3.13.1) 59 | rubocop (1.74.0) 60 | json (~> 2.3) 61 | language_server-protocol (~> 3.17.0.2) 62 | lint_roller (~> 1.1.0) 63 | parallel (~> 1.10) 64 | parser (>= 3.3.0.2) 65 | rainbow (>= 2.2.2, < 4.0) 66 | regexp_parser (>= 2.9.3, < 3.0) 67 | rubocop-ast (>= 1.38.0, < 2.0) 68 | ruby-progressbar (~> 1.7) 69 | unicode-display_width (>= 2.4.0, < 4.0) 70 | rubocop-ast (1.38.1) 71 | parser (>= 3.3.1.0) 72 | ruby-progressbar (1.13.0) 73 | ruby2_keywords (0.0.5) 74 | unicode-display_width (3.1.4) 75 | unicode-emoji (~> 4.0, >= 4.0.4) 76 | unicode-emoji (4.0.4) 77 | vcr (6.3.1) 78 | base64 79 | webmock (3.25.1) 80 | addressable (>= 2.8.0) 81 | crack (>= 0.3.2) 82 | hashdiff (>= 0.4.0, < 2.0.0) 83 | 84 | PLATFORMS 85 | ruby 86 | 87 | DEPENDENCIES 88 | byebug (~> 11.1.3) 89 | dotenv (~> 2.8.1) 90 | rake (~> 13.2.1) 91 | rspec (~> 3.13) 92 | rubocop (~> 1.74.0) 93 | ruby-openai! 94 | vcr (~> 6.3.1) 95 | webmock (~> 3.25.1) 96 | 97 | BUNDLED WITH 98 | 2.4.5 99 | -------------------------------------------------------------------------------- /spec/openai_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe OpenAI do 2 | it "has a version number" do 3 | expect(OpenAI::VERSION).not_to be nil 4 | end 5 | 6 | describe "#configure" do 7 | let(:access_token) { "abc123" } 8 | let(:api_version) { "v2" } 9 | let(:organization_id) { "def456" } 10 | let(:custom_uri_base) { "ghi789" } 11 | let(:custom_request_timeout) { 25 } 12 | let(:extra_headers) { { "User-Agent" => "OpenAI Ruby Gem #{OpenAI::VERSION}" } } 13 | 14 | before do 15 | OpenAI.configure do |config| 16 | config.access_token = access_token 17 | config.api_version = api_version 18 | config.organization_id = organization_id 19 | config.extra_headers = extra_headers 20 | end 21 | end 22 | 23 | it "returns the config" do 24 | expect(OpenAI.configuration.access_token).to eq(access_token) 25 | expect(OpenAI.configuration.api_version).to eq(api_version) 26 | expect(OpenAI.configuration.organization_id).to eq(organization_id) 27 | expect(OpenAI.configuration.uri_base).to eq("https://api.openai.com/") 28 | expect(OpenAI.configuration.request_timeout).to eq(120) 29 | expect(OpenAI.configuration.extra_headers).to eq(extra_headers) 30 | end 31 | 32 | context "with custom timeout and uri base" do 33 | before do 34 | OpenAI.configure do |config| 35 | config.uri_base = custom_uri_base 36 | config.request_timeout = custom_request_timeout 37 | end 38 | end 39 | 40 | it "returns the config" do 41 | expect(OpenAI.configuration.access_token).to eq(access_token) 42 | expect(OpenAI.configuration.api_version).to eq(api_version) 43 | expect(OpenAI.configuration.organization_id).to eq(organization_id) 44 | expect(OpenAI.configuration.uri_base).to eq(custom_uri_base) 45 | expect(OpenAI.configuration.request_timeout).to eq(custom_request_timeout) 46 | expect(OpenAI.configuration.extra_headers).to eq(extra_headers) 47 | end 48 | end 49 | end 50 | 51 | describe "#rough_token_count" do 52 | context "on a non-String" do 53 | it "raises an error" do 54 | expect { OpenAI.rough_token_count([]) }.to raise_error(ArgumentError) 55 | end 56 | end 57 | 58 | context "on the empty string" do 59 | it "returns 0" do 60 | expect(OpenAI.rough_token_count("")).to eq(0) 61 | end 62 | end 63 | 64 | context "on a string" do 65 | let(:content) { "Red is my favorite color. Egg is not a necessary ingredient." } 66 | it "estimates tokens" do 67 | expect(OpenAI.rough_token_count(content)).to eq(15) 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_file_batches_list_vector_store_file_batch_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores/vs_Jhsj39YoypHdDXGlrTHtx3ir/file_batches 6 | body: 7 | encoding: UTF-8 8 | string: '{"file_ids":["file-SsFSl4UvE63Gw3hoiC4P74e4"]}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 15 Oct 2024 07:01:32 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - user-jxm65ijkzc1qrfhc0ij8moic 39 | X-Request-Id: 40 | - req_2c903681ca3a1293a2c6fce32df37d51 41 | Openai-Processing-Ms: 42 | - '1750' 43 | Strict-Transport-Security: 44 | - max-age=31536000; includeSubDomains; preload 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=ZTdwLg8ThMEuU_PISZYI.PSjUIQRtqo_JOo_hPQhlz0-1728975692-1.0.1.1-w0ip5ysqGEH6iAzr7e_fqvd3mXYBSvcpNideg6N_0OzXJ0FjuTCR_7X2Zj08GW_xdx4trRxhL8AqkRl08gHkqw; 49 | path=/; expires=Tue, 15-Oct-24 07:31:32 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=4IY9SE.AH_IPH7TB742a.WGu358a3wmtQ3qUG3UeOIs-1728975692370-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | X-Content-Type-Options: 54 | - nosniff 55 | Server: 56 | - cloudflare 57 | Cf-Ray: 58 | - 8d2df0313f3f93ec-LHR 59 | Alt-Svc: 60 | - h3=":443"; ma=86400 61 | body: 62 | encoding: ASCII-8BIT 63 | string: |- 64 | { 65 | "id": "vsfb_b776bc9508004e9db5711d1acad854c7", 66 | "object": "vector_store.file_batch", 67 | "created_at": 1728975692, 68 | "status": "in_progress", 69 | "vector_store_id": "vs_Jhsj39YoypHdDXGlrTHtx3ir", 70 | "file_counts": { 71 | "in_progress": 1, 72 | "completed": 0, 73 | "failed": 0, 74 | "cancelled": 0, 75 | "total": 1 76 | } 77 | } 78 | recorded_at: Tue, 15 Oct 2024 07:01:32 GMT 79 | recorded_with: VCR 6.1.0 80 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_stores_modify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores/vs_jQLnn4Fn6Kzt8GGw73LSKky2 6 | body: 7 | encoding: UTF-8 8 | string: '{"metadata":{"modified":"true"}}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 30 Apr 2024 16:41:36 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - gojom-1 39 | X-Request-Id: 40 | - req_29deb61536aed4250cea0f44e59d100d 41 | Openai-Processing-Ms: 42 | - '206' 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=ZK1MIQhYHDBnw6PDeC8qFv65zCuLhIz8mQl8usHzeTM-1714495296-1.0.1.1-EFgaOeqqKfrCTS5wre9qtZz._mAOLCLU7j3x4XYA6iQrgXrNNaQABqz4qI061cLdB62yUdZo7Hgk25CB7CXcFA; 49 | path=/; expires=Tue, 30-Apr-24 17:11:36 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=KB55TbP0pMq33UC4XXJkvip1UKyY83BlkwU7.2haOnY-1714495296110-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 87c8faee2a841b26-GRU 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: |- 62 | { 63 | "id": "vs_jQLnn4Fn6Kzt8GGw73LSKky2", 64 | "object": "vector_store", 65 | "name": null, 66 | "status": "completed", 67 | "usage_bytes": 0, 68 | "created_at": 1714495295, 69 | "file_counts": { 70 | "in_progress": 0, 71 | "completed": 0, 72 | "failed": 0, 73 | "cancelled": 0, 74 | "total": 0 75 | }, 76 | "metadata": { 77 | "modified": "true" 78 | }, 79 | "expires_after": null, 80 | "expires_at": null, 81 | "last_active_at": 1714495295 82 | } 83 | recorded_at: Tue, 30 Apr 2024 16:41:36 GMT 84 | recorded_with: VCR 6.1.0 85 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/vector_store_file_batches_list_vector_store_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/vector_stores 6 | body: 7 | encoding: UTF-8 8 | string: "{}" 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Openai-Beta: 15 | - assistants=v2 16 | Accept-Encoding: 17 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 18 | Accept: 19 | - "*/*" 20 | User-Agent: 21 | - Ruby 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | Date: 28 | - Tue, 15 Oct 2024 07:01:29 GMT 29 | Content-Type: 30 | - application/json 31 | Transfer-Encoding: 32 | - chunked 33 | Connection: 34 | - keep-alive 35 | Openai-Version: 36 | - '2020-10-01' 37 | Openai-Organization: 38 | - user-jxm65ijkzc1qrfhc0ij8moic 39 | X-Request-Id: 40 | - req_94ff528d59fed8facc619df94d182c0d 41 | Openai-Processing-Ms: 42 | - '46' 43 | Strict-Transport-Security: 44 | - max-age=31536000; includeSubDomains; preload 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=1HqtluZ3FY7mmMiCBGwiGHdoxwhNuRyIkNaHG3OL7nY-1728975689-1.0.1.1-TCSy92E6brTiGHH4Sob77NntYEWwFM82qEmufR1LIj_c5s4_9p5dCAaqBlLFbRD_OrqUbMa_k1z_EwFGALm5xw; 49 | path=/; expires=Tue, 15-Oct-24 07:31:29 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=Dm38neGAiSWdmSLi_A1AbAF4QDEGEwrKnwcuUelRJo0-1728975689933-0.0.1.1-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | X-Content-Type-Options: 54 | - nosniff 55 | Server: 56 | - cloudflare 57 | Cf-Ray: 58 | - 8d2df02cebb3cd54-LHR 59 | Alt-Svc: 60 | - h3=":443"; ma=86400 61 | body: 62 | encoding: ASCII-8BIT 63 | string: |- 64 | { 65 | "id": "vs_Jhsj39YoypHdDXGlrTHtx3ir", 66 | "object": "vector_store", 67 | "name": null, 68 | "status": "completed", 69 | "usage_bytes": 0, 70 | "created_at": 1728975689, 71 | "file_counts": { 72 | "in_progress": 0, 73 | "completed": 0, 74 | "failed": 0, 75 | "cancelled": 0, 76 | "total": 0 77 | }, 78 | "metadata": {}, 79 | "expires_after": null, 80 | "expires_at": null, 81 | "last_active_at": 1728975689 82 | } 83 | recorded_at: Tue, 15 Oct 2024 07:01:29 GMT 84 | recorded_with: VCR 6.1.0 85 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/images_generate_A_baby_sea_otter_cooking_pasta_wearing_a_hat_of_some_sort.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.openai.com/v1/images/generations 6 | body: 7 | encoding: UTF-8 8 | string: '{"prompt":"A baby sea otter cooking pasta wearing a hat of some sort","size":"256x256","model":"dall-e-2"}' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | Authorization: 13 | - Bearer 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Date: 26 | - Fri, 19 Jan 2024 11:40:53 GMT 27 | Content-Type: 28 | - application/json 29 | Transfer-Encoding: 30 | - chunked 31 | Connection: 32 | - keep-alive 33 | Openai-Version: 34 | - '2020-10-01' 35 | Openai-Organization: 36 | - user-3clrjdlztmfzmksxfsyoiaou 37 | X-Request-Id: 38 | - 0137f54d99d1362b1e623d676116a4c9 39 | Openai-Processing-Ms: 40 | - '6822' 41 | Access-Control-Allow-Origin: 42 | - "*" 43 | Strict-Transport-Security: 44 | - max-age=15724800; includeSubDomains 45 | Cf-Cache-Status: 46 | - DYNAMIC 47 | Set-Cookie: 48 | - __cf_bm=P36Wd_cn8juahMszhsJOx5kkOYVokwSxjGZmiTuOep8-1705664453-1-AWK8XEV35FFAXSMl+MBEJ2XPKC3KQc4hWuMvbTkdXErcByHJJo77vuiKXigfEXUDZKpOT8VxsAaRxiyE/ZV9wbE=; 49 | path=/; expires=Fri, 19-Jan-24 12:10:53 GMT; domain=.api.openai.com; HttpOnly; 50 | Secure; SameSite=None 51 | - _cfuvid=BoKuzauoIz4K9oDwnJtEGtFjVAkD0lnYqLBXdB4lehc-1705664453478-0-604800000; 52 | path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None 53 | Server: 54 | - cloudflare 55 | Cf-Ray: 56 | - 847ece06be0a0755-MAN 57 | Alt-Svc: 58 | - h3=":443"; ma=86400 59 | body: 60 | encoding: ASCII-8BIT 61 | string: | 62 | { 63 | "created": 1705664453, 64 | "data": [ 65 | { 66 | "url": "https://oaidalleapiprodscus.blob.core.windows.net/private/org-yUfNie34j2cA3IrAwdyJpfdb/user-3ClrjdlztmfzMKsxFSYOIAOu/img-TXGcLfiIhAqPEEED1eQ4SyMg.png?st=2024-01-19T10%3A40%3A53Z&se=2024-01-19T12%3A40%3A53Z&sp=r&sv=2021-08-06&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2024-01-18T22%3A51%3A44Z&ske=2024-01-19T22%3A51%3A44Z&sks=b&skv=2021-08-06&sig=AFx2RqTU0Zb2Klk3QPFO06JtipjPmGadWbVVOVQkVeU%3D" 67 | } 68 | ] 69 | } 70 | recorded_at: Fri, 19 Jan 2024 11:40:53 GMT 71 | recorded_with: VCR 6.1.0 72 | --------------------------------------------------------------------------------