openai/openai-ruby

▲ 11 stars today★ 468⑂ 67

Official Ruby SDK for the OpenAI API

About openai/openai-ruby

openai/openai-ruby is an open-source project on GitHub, mainly written in Ruby. Official Ruby SDK for the OpenAI API It currently holds 468 stars and 67 forks with 0 open issues, and was last pushed on an unknown date (repository created unknown).

Project Overview

AI Homed tracks it on the Today's Trending board.

GitHub Repository Details

Repository openai/openai-ruby · default branch - · size 0 KB · watchers 0 · source: GitHub REST API and repository README

README

OpenAI Ruby API library

The OpenAI Ruby library provides convenient access to the OpenAI REST API from any Ruby 3.3.0+ application. It ships with comprehensive types & docstrings in Yard, RBS, and RBI – see below for usage with Sorbet. The standard library's net/http is used as the HTTP transport, with connection pooling via the connection_pool gem.

Documentation

Documentation for releases of this gem can be found on RubyDoc.

The REST API documentation can be found on platform.openai.com.

Installation

To use this gem, install via Bundler by adding the following to your application's Gemfile:

gem "openai", "~> 0.103.0"

Usage

require "bundler/setup"
require "openai"

openai = OpenAI::Client.new( api_key: ENV["OPENAI_API_KEY"] # This is the default and can be omitted )

chat_completion = openai.chat.completions.create(messages: [{role: "user", content: "Say this is a test"}], model: "gpt-5.2")

puts(chat_completion)

Streaming

We provide support for streaming responses using Server-Sent Events (SSE).

stream = openai.responses.stream(
  input: "Write a haiku about OpenAI.",
  model: "gpt-5.2"
)

stream.each do |event| puts(event.type) end

Agents final output (beta)

Collect final output from an Agents creation stream:

stream = openai.beta.agents.sessions.create_streaming(
  agent: {model: "gpt-5.2", instructions: "Explain the supplied policy clearly."},
  environment: {type: :none},
  input: "Explain this policy: ..."
)
result = stream.get_final_result
puts(result.output_text)

Use the same getter for follow-ups; to display progress first, call stream.with_result_collection before iterating:

openai.beta.agents.sessions.stream(result.session_id, input: "Give an example.") do |stream|
  puts(stream.get_final_result.output_text)
end

The beta result includes turn metadata and final messages; unsuccessful or incomplete collection raises OpenAI::Helpers::Beta::Agents::ResultError.

Typed Agents output (beta)

Use an OpenAI::BaseModel with output_type to configure and parse a structured answer:

class Report < OpenAI::BaseModel
  required :summary, String
  required :findings, OpenAI::ArrayOf[String]
end

result = openai.beta.agents.sessions.create_streaming( agent: {model: "gpt-5.2"}, environment: {type: :none}, input: "Summarize these notes: ...", output_type: Report ).get_final_result puts(result.output_parsed.summary)

openai.beta.agents.sessions.stream(result.session_id, input: "Update the report.", output_type: Report) do |stream| puts(stream.get_final_result.output_parsed.summary) end

Parsing follows the SDK’s existing BaseModel conventions. Each output-text item is parsed separately; output_parsed returns the first. Follow-up output_type only parses the answer; it does not change the session's schema. OutputParseError#raw_result preserves the completed answer if parsing fails.

Hosted files and artifacts

Prepare selected local files before creating a hosted session, or use upload to stage one in an existing environment. Keep upload_ids for explicit cleanup through the Files API.

prepared = openai.beta.agents.environments.files.prepare({
  "/workspace/source.pdf" => Pathname("source.pdf")
})
stream = openai.beta.agents.sessions.create_streaming(
  agent: {model: "gpt-6-astra"},
  environment: {type: :openai_hosted, files: prepared.files},
  input: "Read source.pdf and write /workspace/outputs/report.md"
)
result = stream.get_final_result
artifacts = openai.beta.agents.sessions.artifacts.for_result(result)
artifacts.download(path: "/workspace/outputs/report.md", to: Pathname("report.md"))

Or keep the downloaded bytes in memory:

buffer = StringIO.new artifacts.download(path: "/workspace/outputs/report.md", to: buffer) puts(buffer.string)

prepare_directory(directory, destination:, include:) stages an explicitly selected snapshot. Local path and directory helpers assume stable, application-owned source paths; they are not a filesystem sandbox for arbitrary user-supplied paths or hostile local writers. Downloads select the exact result turn and stream to an application-owned local path or the writer you supply. An interrupted transfer may leave partial contents.

Local audio

Record a clip and play generated speech with optional FFmpeg/FFplay helpers. See the local audio guide for setup, device selection and examples.

Realtime WebSockets

The SDK supports block-scoped, typed Realtime WebSocket sessions for server-side text, committed-turn transcription, one-turn voice, WebRTC call creation, and sideband control of existing WebRTC or SIP calls. Add the optional async-websocket gem for WebSocket connections, then use client.realtime.connect:

client.realtime.connect(model: "gpt-realtime-2.1") do |connection|
  connection.session.update(type: :realtime, output_modalities: [:text])
  connection.conversation.items.create(
    type: :message,
    role: :user,
    content: [{type: :input_text, text: "Hello"}]
  )
  connection.response.create

connection.each do |event| print(event.delta) if event.is_a?(OpenAI::Realtime::ResponseTextDeltaEvent) break if event.is_a?(OpenAI::Realtime::ResponseDoneEvent) end end

WebRTC calls can be created with client.realtime.calls.create; its HTTP response preserves the SDP answer and the Location header needed to attach a sideband connection. See the Realtime WebSocket guide and the runnable text, transcription, voice, and sideband examples for lifecycle, authentication, proxy, TLS, and custom transport details.

Responses WebSockets

The Responses API also supports a persistent, block-scoped WebSocket for sequential and multiplexed response creation. Add the optional async-websocket gem, then send response.create events through client.responses.connect:

client.responses.connect do |connection|
  connection.response.create(
    model: "gpt-5.2",
    input: "Say hello.",
    stream_id: "turn_1"
  )

connection.each do |event| print(event.delta) if event.type == :"response.output_text.delta" break if event.type == :"response.completed" end end

The connection is intentionally single-owner: callers serialize writes and use one reader (receive or each). The SDK forwards response fields and stream_id values without imposing additional client-side policy. Known server events are decoded into generated models on a best-effort basis, while newer event types remain observable as UnknownServerEvent values. The SDK does not automatically reconnect or replay an ambiguous write. When the server closes a connection, open a new connection and continue with previous_response_id when the response was stored.

See the Responses WebSocket guide and runnable workflows for tool turns, multiplexing, stored continuation, stateless replay, and operation deadlines.

Pagination

List methods in the OpenAI API are paginated.

This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually:

page = openai.fine_tuning.jobs.list(limit: 20)

Fetch single item from page.

job = page.data[0] puts(job.id)

Automatically fetches more pages as needed.

page.auto_paging_each do |job| puts(job.id) end

Alternatively, you can use the #next_page? and #next_page methods for more granular control working with pages.

if page.next_page?
  new_page = page.next_page
  puts(new_page.data[0].id)
end

File uploads

Request parameters that correspond to file uploads can be passed as raw contents, a Pathname instance, StringIO, or more.

Raw String and StringIO values, and IO objects without a path, do not carry format-identifying metadata. The SDK sends them using the fallback filename upload; raw String values default to text/plain, while StringIO and pathless IO values default to application/octet-stream. For format-sensitive endpoints, such as audio transcriptions, wrap each value in OpenAI::FilePart and provide an extension-bearing filename and content type.

require "pathname"

Use Pathname to send the filename and/or avoid paging a large file into memory:

file_object = openai.files.create(file: Pathname("input.jsonl"), purpose: "fine-tune")

Alternatively, pass file contents or a StringIO directly:

file_object = openai.files.create(file: File.read("input.jsonl"), purpose: "fine-tune")

puts(file_object.id)

For format-sensitive uploads, provide the filename and content type:

audio_data = StringIO.new(File.binread("audio.wav")) audio = OpenAI::FilePart.new( audio_data, filename: "audio.wav", content_type: "audio/wav" ) transcription = openai.audio.transcriptions.create( model: "gpt-4o-transcribe", file: audio ) puts(transcription.text)

FilePart also accepts a Pathname:

image = OpenAI::FilePart.new(Pathname("dog.jpg"), content_type: "image/jpeg") edited = openai.images.edit( prompt: "make this image look like a painting", model: "gpt-image-2", size: "1024x1024", image: image )

puts(edited.data.first)

Note that you can also pass a raw IO descriptor, but this disables retries, as the library can't be sure if the descriptor is a file or pipe (which cannot be rewound).

Polling and vector store ingestion

Polling helpers wait for file processing and return the final resource. Durations are expressed in seconds. By default, the helpers honor the API's recommended polling interval, fall back to 5 seconds, and stop after 30 minutes. Pass timeout: nil to wait indefinitely.

To keep a finite overall deadline strict, each polling retrieval includes authentication and request replay time and does not perform transport-level retries. Pass timeout: nil to retain the client's configured retry behavior.

file = openai.files.wait_for_processing(
  file_object.id,
  poll_interval: 2,
  timeout: 10 * 60
)

vector_file = openai.vector_stores.files.upload_and_poll( vector_store.id, file: Pathname("handbook.pdf"), attributes: {department: "engineering"} )

batch = openai.vector_stores.file_batches.upload_and_poll( vector_store.id, files: [Pathname("one.pdf"), Pathname("two.pdf")], file_ids: [file_object.id], max_concurrency: 3 )

The vector store helpers return terminal failed and cancelled resources instead of raising, so inspect status, last_error, or file_counts as appropriate. An overall polling timeout raises OpenAI::Errors::PollingTimeoutError; its resource attribute contains the last object returned by the API, or nil if the deadline elapsed before the first response.

Batch inputs are enumerated before requests begin so the 2,000-file API limit can be checked without orphaning uploads. IO streams yielded by an enumerable must remain open until the helper returns; use paths for block-scoped lazy enumeration. Batch uploads are concurrent but not transactional. If one upload fails, the helper stops starting queued uploads and raises that error; files uploaded successfully before the failure remain available through the Files API.

Custom HTTP clients

OpenAI::Client accepts an http_client for advanced transport requirements. Provide an object that implements execute(request) and returns an OpenAI::HTTPClient::Response; subclassing OpenAI::HTTPClient is the easiest way to make that contract explicit. The request exposes the SDK-prepared HTTP method, URL, headers, encoded body, and timeout. Response bodies are enumerable byte-string chunks (or a single buffered string), so large and streaming responses do not need to be buffered.

The OpenAI client owns API authentication, redirects, and API-level retries. The custom HTTP client owns connection pooling and lifecycle, must enforce request.timeout, and should raise OpenAI::Errors::APIConnectionError or OpenAI::Errors::APITimeoutError for retryable transport failures. Other exceptions propagate without an SDK retry. The SDK does not close an injected HTTP client. Connection failures are treated as potentially post-send by default, so non-idempotent requests are not retried automatically. When a transport can prove that no request bytes were sent, it may raise either error with request_may_have_been_sent: false to allow the SDK retry policy to retry a replayable request safely.

The default OpenAI::NetHTTPClient implements this contract with pooled Net::HTTP connections. It accepts an optional block for native connection configuration, as shown below. Call close to retire its current pools; the HTTP client remains reusable and creates fresh connections on its next request.

Mutual TLS with a custom HTTP client

To opt in and activate mTLS for an organization or project, follow the OpenAI Mutual TLS Beta Program guide. If you use an intermediate chain, confirm that certificate-chain support is enabled for your organization.

For API-key requests that also require mutual TLS (mTLS), set the mTLS endpoint explicitly and pass a configured OpenAI::NetHTTPClient as http_client. OpenAI::Client accepts an HTTP client object so advanced transport behavior does not require a separate SDK option for every use case. The default OpenAI::NetHTTPClient accepts a block that configures each native Net::HTTP connection before it is pooled and started.

Ruby's native TLS properties configure the client identity. The certificate file must contain the leaf certificate first, followed by any intermediate certificates needed when certificate-chain support is enabled for your organization:

require "openai"

mtls_endpoint = URI("https://mtls.api.openai.com/v1") certificates = OpenSSL::X509::Certificate.load( File.binread(ENV.fetch("OPENAI_CLIENT_CERTIFICATE_CHAIN")) ) raise "Expected a client certificate" if certificates.empty?

leaf_certificate, *intermediates = certificates private_key = OpenSSL::PKey.read( File.binread(ENV.fetch("OPENAI_CLIENT_KEY")), ENV["OPENAI_CLIENT_KEY_PASSPHRASE"] ) raise "Certificate and key do not match" unless leaf_certificate.check_private_key(private_key)

now = Time.now raise "Certificate is not yet valid" if now < leaf_certificate.not_before raise "Certificate has expired" if now > leaf_certificate.not_after

mtls_destination = [mtls_endpoint.host, mtls_endpoint.port] http_client = OpenAI::NetHTTPClient.new do |http| unless http.use_ssl? && mtls_destination == [http.address, http.port] raise "Refusing to present the client certificate to an unexpected origin" end

http.cert = leaf_certificate http.extra_chain_cert = intermediates http.key = private_key end

client = OpenAI::Client.new( api_key: ENV.fetch("OPENAI_API_KEY"), base_url: mtls_endpoint.to_s, http_client: http_client )

The SDK cannot infer whether custom HTTP configuration uses mTLS, so it does not automatically change base_url. An explicit base_url, including an EU or custom endpoint, is always preserved. Scope client certificates to the expected origin, as above, so they cannot be presented elsewhere. Use the OpenAI::NetHTTPClient block for TLS and other connection-level configuration. Server trust remains separate from the client identity and can be customized with Net::HTTP properties such as cert_store or ca_file.

Each OpenAI::NetHTTPClient owns its connection pool, so separate HTTP client instances do not share connections or credentials.

After a process forks, create new OpenAI and HTTP clients in the child. Keep the certificate configuration captured by an HTTP client immutable. For certificate rotation, build and atomically swap in new OpenAI::NetHTTPClient and OpenAI::Client instances, then call close on the retired HTTP client after in-flight work finishes. See the complete custom HTTP client mTLS example.

Microsoft Azure OpenAI

Use the standard client with the Azure provider to call model deployments through the Azure OpenAI v1 API. The provider accepts an Azure resource endpoint and adds /openai/v1 when needed:

require "openai"

client = OpenAI::Client.new( provider: OpenAI::Providers.azure( endpoint: ENV.fetch("AZURE_OPENAI_ENDPOINT"), api_key: ENV.fetch("AZURE_OPENAI_API_KEY") ) )

response = client.responses.create( model: ENV.fetch("AZURE_OPENAI_DEPLOYMENT"), input: "Say hello!" )

puts(response.output_text)

Omit endpoint and api_key to use AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_API_KEY. For Microsoft Entra authentication, pass a callable that returns a current bearer token. The provider invokes it before every request attempt, including retries:

client = OpenAI::Client.new(
  provider: OpenAI::Providers.azure(
    endpoint: ENV.fetch("AZURE_OPENAI_ENDPOINT"),
    token_provider: -> { fetch_azure_openai_token }
  )
)

This integration targets the Azure OpenAI v1 API. It does not add dated api-version query parameters or rewrite requests to legacy /deployments/{deployment} paths. See azure.md for configuration, authentication precedence, and endpoint security details.

Amazon Bedrock

Use the standard client with the Bedrock provider to call OpenAI models through Amazon Bedrock's OpenAI-compatible API. Add aws-sdk-core to your application for AWS credential discovery and SigV4 signing:

gem "openai"
gem "aws-sdk-core", "~> 3"

With your normal AWS credentials configured, only the region is required:

require "openai"

client = OpenAI::Client.new( provider: OpenAI::Providers.bedrock(region: "us-west-2") )

response = client.responses.create( model: ENV.fetch("BEDROCK_MODEL"), input: "Say hello!" )

puts(response.output_text)

The provider uses the standard AWS credential chain, including environment credentials, ~/.aws/credentials, ~/.aws/config, AWS_PROFILE, named profiles, SSO and assume-role profiles, and workload credentials. Select a profile explicitly with:

client = OpenAI::Client.new(
  provider: OpenAI::Providers.bedrock(
    region: "us-west-2",
    profile: "engineering"
  )
)

AWS_BEARER_TOKEN_BEDROCK and explicit Bedrock bearer credentials are also supported. Bearer authentication does not load or require aws-sdk-core:

client = OpenAI::Client.new(
  provider: OpenAI::Providers.bedrock(
    region: "us-west-2",
    api_key: ENV.fetch("AWS_BEARER_TOKEN_BEDROCK")
  )
)

See bedrock.md for authentication precedence, static and refreshable credentials, endpoint configuration, and SigV4 request constraints.

Workload Identity Authentication

For secure, automated environments like cloud-managed Kubernetes, Azure, and GCP, you can use workload identity authentication with short-lived tokens from cloud identity providers instead of long-lived API keys.

client_id remains available as an optional parameter for token exchange setups that require an explicit OAuth client ID.

If OPENAI_API_KEY is already set in your environment, pass api_key: nil to explicitly opt out of that ambient API key when constructing a workload identity client.

X.509 Workload Identity (Preview)

Organizations enrolled in the X.509 workload identity preview can exchange a client certificate for a short-lived OpenAI bearer credential. Both the token exchange and subsequent API request use the same caller-attested mTLS transport. The bearer is not cryptographically bound to the certificate; the API separately requires an accepted client certificate.

require "openai"

certificates = OpenSSL::X509::Certificate.load( File.binread(ENV.fetch("OPENAI_CLIENT_CERTIFICATE_CHAIN")) ) raise "Expected an enrolled client certificate" if certificates.empty?

client_certificate, *intermediate_certificates = certificates client_private_key = OpenSSL::PKey.read( File.binread(ENV.fetch("OPENAI_CLIENT_KEY")), ENV["OPENAI_CLIENT_KEY_PASSPHRASE"] ) unless client_certificate.check_private_key(client_private_key) raise "The enrolled certificate and private key do not match" end

api_origin = ENV.fetch("OPENAI_X509_API_ORIGIN", "https://mtls.api.openai.com") api_host = URI(api_origin).host&.downcase approved_hosts = ["mtls.auth.openai.com", api_host].freeze native_http_client = OpenAI::NetHTTPClient.new do |connection| unless connection.use_ssl? && connection.port == 443 && approved_hosts.include?(connection.address.downcase) raise "Refusing to present the enrolled certificate to an unexpected destination" end

connection.cert = client_certificate connection.extra_chain_cert = intermediate_certificates connection.key = client_private_key end

identity = OpenAI::Auth::X509WorkloadIdentity.new( identity_provider_id: ENV.fetch("IDENTITY_PROVIDER_ID"), service_account_id: ENV.fetch("SERVICE_ACCOUNT_ID"), http_client: native_http_client, api_origin: api_origin )

client = OpenAI::Client.new( api_key: nil, workload_identity: identity )

model = client.models.list.data.first

The identity creates its guarded transport internally and selects the matching mTLS API endpoint automatically. The application still owns its certificate, key, trust settings, and native HTTP client. Keep the selected certificate identity static, configure only the approved issuer and API destinations, and create a fresh native client and identity when rotating credentials. Clients derived with with_options share the original identity's cached token when their transport is unchanged.

The supported X.509 API regions are global, US, and EU. Set api_origin: to https://mtls.api.openai.com, https://mtls-us.api.openai.com, or https://mtls-eu.api.openai.com; an optional data_residency: on the client must match. Direct mode rejects ambient proxies; pass proxy: :http_connect to the identity only when an HTTP CONNECT proxy is configured. HTTPS proxies are rejected before proxy credentials can be transmitted. Arbitrary custom transports, Azure/Bedrock providers, and Realtime WebSockets are not supported. The normal client or per-request timeout covers both token exchange and the API request; timeout: nil disables both deadlines. Preview access must be enabled for the organization, and mTLS can be configured for the enrolled organization or project.

See the complete X.509 workload identity live smoke example. It performs a real token exchange and API request only when an enrolled certificate, private key, identity-provider ID, and mapped service-account ID are explicitly supplied.

Keep these values in a private environment file outside your checkout or in a secret manager, then run the example without an API key:

export OPENAI_CLIENT_CERTIFICATE_CHAIN=/secure/path/client-chain.pem
export OPENAI_CLIENT_KEY=/secure/path/client-key.pem
export IDENTITY_PROVIDER_ID=idp_example
export SERVICE_ACCOUNT_ID=svc_acct_example

ruby examples/x509_workload_identity.rb

Set OPENAI_X509_PROXY_MODE=http_connect only when an approved HTTP CONNECT proxy is configured. Encrypted keys can use OPENAI_CLIENT_KEY_PASSPHRASE.

Kubernetes Service Account

```ruby require "openai"

Configure Kubernetes service account provider

provider = OpenAI::Auth::SubjectTokenProviders::K8sServiceAccountTokenProvider.new

workload_identity = OpenAI::Auth::WorkloadIdentity.new( identity_provider_id: ENV["IDENTITY_PROVIDER_ID"], # This is the default and can be omitted service_account_id: ENV["SERVICE

GitHub Stars & Activity

468Stars
67Forks
0Open issues
RubyLanguage

GitHub Popularity

GitHub stars468
Forks67
Open issues0
Primary languageRuby
License-
Stars gained today11
Created-
Last pushed-

Trending History

Monthly boardrank #100 · ▲ 11 stars

Related AI Projects

1

huginn / huginn

Ruby★ 50,052⑂ 4,306▲ 10 stars
→
2

obra / superpowers

Shell★ 298,603⑂ 26,690▲ 576 stars
→
3

mattpocock / skills

Shell★ 289,380⑂ 24,295▲ 1,804 stars
→
4

morluto / rea

TypeScript★ 201,703⑂ 49,253▲ 54,706 stars
→
5

msitarzewski / agency-agents

Shell★ 159,837⑂ 25,849▲ 450 stars
→
6

open-webui / open-webui

Python★ 155,555⑂ 22,677▲ 1,138 stars
→
7

harry0703 / MoneyPrinterTurbo

Python★ 130,506⑂ 20,476▲ 404 stars
→
8

rtk-ai / rtk

Rust★ 83,210⑂ 5,283▲ 131 stars
→

More AI Rankings