We welcome bug reports, feature requests, minimal reproductions, and root-cause analysis through GitHub issues.
Pull requests are limited to repository collaborators. We do not accept pull requests from non-collaborators, including documentation or example changes. If you are not a collaborator, please open an issue instead of preparing a pull request. Include the affected version, expected and actual behavior, and a small, sanitized reproduction when applicable.
Report suspected security vulnerabilities privately as described in SECURITY.md, rather than in issues or pull requests.
The development and pull request instructions below are for maintainers and repository collaborators.
See the versioning policy before changing the public API, minimum Ruby version, dependencies, or release behavior.
Review the security requirements before installing dependencies, changing SDK behavior, or running live examples.
This repository contains a .ruby-version file, which should work with either rbenv or asdf with the ruby plugin.
Please follow the instructions for your preferred version manager to install the Ruby version specified in the .ruby-version file.
To set up the repository, run:
$ ./scripts/bootstrapThis installs the dependencies used by the normal test, lint, typecheck, and package workflows. Documentation tooling has a separate bundle; see Documentation.
Look up the repository-relative path in the Castiron generated-file inventory. To report a bug, include the SDK version, affected call, and a small, sanitized reproduction.
- Incorrect API field. If the API schema gives a field the wrong type or marks it required incorrectly, fix the schema and regenerate the SDK.
- Incorrect generated code. If the schema is correct but the SDK exposes the wrong type or sends the wrong value, fix the generation configuration or compiler and regenerate. Verify the fix with a regression test that calls the public SDK.
- Handwritten helper or example. Keep SDK-specific extensions in handwritten files and test them directly. For instance, client_runtime.rb is handwritten. Its neighbor
data_residency.rbis generated. The examples directory contains handwritten examples.
Regeneration replaces owned files, including generated tests, docs, and workflows, and removes retired paths. Review the output and inventory together. Resolve ownership collisions before regenerating. Never hand-edit generated output or its inventory. Maintainers enforce this policy in code review.
- Secrets and fixtures: Never commit API keys, access tokens, private keys,
credentials, or customer data. Load secrets from environment variables such
as
OPENAI_API_KEY; use clearly fake values in examples, tests, snapshots, and WebMock fixtures. - Logs and diagnostics: Redact authorization headers, cookies, signed URLs, credential-bearing query parameters, customer data, and sensitive request/response bodies, prompts, or uploaded files from logs, exceptions, error messages, fixtures, and CI artifacts. Clearly fake or sanitized payloads may remain in tests and diagnostics.
- Dependencies: Review
Gemfile,Gemfile.lock,gemfiles/*.gemfile,gemfiles/*.gemfile.lock,docs/Gemfile,docs/Gemfile.lock, andopenai.gemspecchanges, including direct and transitive gems, sources, locked Git revisions, native extensions, and install/build scripts. Do not run unreviewed scripts or accept unexplained lockfile changes. - CI and publishing: Pin GitHub Actions to full commit SHAs, grant minimum
per-job permissions, and preserve
permissions: {},persist-credentials: false, protected release environments, and RubyGems trusted publishing. Restrictid-token: writeto the publishing job; protect GitHub App private keys and do not introduce long-lived publishing tokens. - Sensitive changes: Request
@openai/sdks-teamreview for authentication, network endpoints/transports, redirects, TLS, uploads and file paths, JSON/YAML/Marshal deserialization, request logging, webhooks, dependencies, and CI/release changes. Add targeted regression/security tests for affected credential, network, file, serialization, or publishing boundaries. - Vulnerability reports: Follow SECURITY.md. Never report a suspected vulnerability in a public issue, pull request, or discussion.
See Generated-file ownership before editing SDK source.
All files in the examples/ directory are not modified by the generator and can be freely edited or added to.
#!/usr/bin/env ruby
# frozen_string_literal: true
require_relative "../lib/openai"
# ...$ chmod +x './examples/<your-example>.rb'
# run the example against your api
$ ruby './examples/<your-example>.rb'If you’d like to use the repository from source, you can either install from git or reference a cloned repository:
To install via git in your Gemfile:
gem "openai", git: "https://github.com/openai/openai-ruby"Alternatively, reference local copy of the repo:
$ git clone -- 'https://github.com/openai/openai-ruby' '<path-to-repo>'gem "openai", path: "<path-to-repo>"Running rake by itself will show all runnable commands.
$ bundle exec rakeThe mock server uses the OpenAI Steady fork.
scripts/steady/manifest.json is the single source of dependency pins: the
Steady Git commit and source digest, plus the Deno version and runtime checksums. ./scripts/steady/install fetches that source, verifies the runtime,
and caches dependencies using the fork's frozen Deno lockfile. It requires
Git, Node.js, curl, unzip, and sha256sum or shasum. The installation supports
macOS and Linux on x64/ARM64, and Windows x64 through Git Bash.
./scripts/run-steady verifies the local source and runtime, then runs without
downloading dependencies. Pass a local OpenAPI specification path. To update
Steady, review the fork commit and run
node scripts/steady/update.cjs <full-commit-sha>. This updates the manifest
with the commit and its source digest; no launcher or test edits are needed.
Then run ./scripts/steady/install. Review the release checksums when changing Deno.
Run node scripts/steady/test.cjs to check the
installation, integrity checks, and mock-server lifecycle.
Most tests require you to set up a mock server against the OpenAPI spec to run the tests.
Install lsof and ensure it is available on PATH; the daemon launcher uses it
to verify that the mock process group owns its listening socket before tests run.
$ ./scripts/mock$ bundle exec rake testPull requests and merge groups split the non-Bedrock tests into two groups of
whole files on each supported Ruby version, using past run times to balance
the groups. Run TEST_SHARD=1/2 ./scripts/test or TEST_SHARD=2/2 ./scripts/test
to reproduce a group locally. Pushes to main and next run the full
non-Bedrock suite, as does ./scripts/test with no TEST_SHARD. Serial tests,
including the large-payload cases, retain their existing scheduling.
The primary bundle intentionally omits the optional AWS SDK. Install the dedicated Bedrock bundle and run its tests separately:
$ BUNDLE_GEMFILE=gemfiles/bedrock.gemfile bundle install
$ BUNDLE_GEMFILE=gemfiles/bedrock.gemfile bundle exec rake test:bedrockWhen regenerating gemfiles/bedrock.gemfile.lock, preserve the
x-release-please markers around the local openai version. Release Please
uses them to update this lockfile; Bundler can remove them when rewriting it.
The live example suite executes every example marked as covered in
examples/e2e.yml. It requires OPENAI_API_KEY, makes real API requests, and
writes JSON and Markdown execution reports under tmp/examples-e2e/ by default.
Keep OPENAI_API_KEY in the environment. Prefer the offline inventory check,
which makes no API requests:
$ bundle exec rake test:examples:inventoryLive reports include example paths, pass/fail status, timing, and structural failure diagnostics. Existing stdout/stderr report fields remain present but are always null. Captured output and exception messages are omitted from JSON reports, Markdown summaries, and the explicitly selected CI artifacts. Running the live suite still makes real API requests:
$ bundle exec rake test:examples:e2eEvery examples/**/*.rb file must be classified as covered or explicitly
excluded with a reason. In GitHub Actions, live execution is available only
through the manually dispatched Examples E2E workflow.
Run a short, optional smoke test against the real API with an API key in your environment:
$ OPENAI_API_KEY=sk-example bundle exec rake test:live:smokeThe test verifies model discovery, a normal Responses API request, and a
completed streaming response. Override the default gpt-4o-mini model with
OPENAI_LIVE_SMOKE_MODEL when needed. It emits only pass/fail diagnostics,
never API response content, request bodies, or credentials.
To also verify a real X.509 issuer exchange and a certificate-authenticated API
request, keep OPENAI_API_KEY available for the standard smoke checks and
provide the enrolled certificate/key paths and mapped provider/account IDs
documented in the README, then run:
$ OPENAI_LIVE_SMOKE_X509=1 bundle exec rake test:live:smokeGitHub Actions exposes the same checks through the optional, manually dispatched
Live Smoke workflow. Its standard smoke runs in the existing ci environment.
The optional X.509 job runs only after the standard smoke succeeds, is disabled
by default, and requires the following secrets in the separate, protected
x509-live-smoke environment:
OPENAI_X509_CLIENT_CERTIFICATE_CHAIN_PEMOPENAI_X509_CLIENT_PRIVATE_KEY_PEMOPENAI_X509_IDENTITY_PROVIDER_IDOPENAI_X509_SERVICE_ACCOUNT_IDOPENAI_X509_CLIENT_KEY_PASSPHRASEwhen the private key is encrypted.
The X.509 environment requires independent SDK-team approval, prevents
self-review, disables administrator bypasses, and runs only on the protected
default branch. X.509 secrets are available only to the explicitly selected
X.509 job. Certificate files are mode-restricted, short-lived runner files, raw
PEM variables are removed before the SDK starts, and credential files are never
uploaded as artifacts. The GitHub-hosted runner always uses a direct X.509
connection. Local runs may set
OPENAI_X509_PROXY_MODE=http_connect when an explicitly approved HTTP CONNECT
proxy is configured. Live smoke tests are not required pull-request checks.
rubyfmt owns Ruby source and *.rbi signature layout. The scripts/rubyfmt launcher uses version 0.14.1 and downloads a checksum-verified release into your user cache when needed. To use an existing installation, set RUBYFMT to an executable of that exact version.
rubocop remains responsible for correctness and security checks. The existing CI lint task checks rubyfmt output and RBS formatting. syntax_tree continues to format *.rbs files; its check and write modes use the same implementation.
All Ruby sources are formatted; file-level opt-outs are rejected. Two existing handwritten pattern-matching expressions use equivalent forms that avoid rubyfmt 0.14.1 bugs. Their source comments explain why, and regression tests check that both complete files format into valid Ruby and remain unchanged on a second pass.
RuboCop suppressions must name individual cops and cover an actual offense.
Remove obsolete disable/enable pairs when code or policy changes; suppressions
for disabled layout, style, or metrics rules are rejected too. Put a necessary
disable before the beginning of the affected expression (including a multiline
method chain), close it immediately afterward, and explain the exception.
Temporary rubocop:todo directives also need an owner and an issue or future
removal date. The directive guard rejects broad and unowned suppressions.
For lint-policy changes, run the full policy before and after cleanup:
$ bundle exec rubocop --format json --out tmp/rubocop-report.jsonRecord per-cop counts and affected paths from that report, audit those paths
against Castiron's output and ownership rules, and include the result in the PR.
Generated-code fixes must start in Castiron when regeneration could recreate
them. Otherwise document why no generator companion is needed. Keep each rule
rollout separate, preserve necessary semantic exceptions, and never add broad
exclusions to make the gate pass. Run the full lint task after cleanup; limiting
a run with --only can change which suppression directives are necessary.
The remaining disabled correctness cops are deliberate: Lint/ArgumentMismatch,
Lint/SuperArgumentMismatch, and Lint/UnusedPrivateMethod require a separate
project-index/rubydex rollout; Lint/ConstantResolution requires a broad naming
and constant-lookup audit. Lint/HeredocMethodCallPosition is layout owned by
rubyfmt. Lint/NumberConversion remains enabled with only the generated webhook
timestamp compatibility exception from lib/openai/.rubocop.generated.yml.
There are two separate type checkers supported by this library: sorbet and steep are used for verifying *.rbi and *.rbs files respectively.
To lint and typecheck:
$ bundle exec rake lintTo run the available formatters:
$ bundle exec rake formatRuby LSP has quite good support for go to definition, but not auto-completion.
This can be installed along side Solargraph.
Solargraph has quite good support for auto-completion, but not go to definition.
This can be installed along side Ruby LSP.
Sorbet should mostly work out of the box when editing this library directly. However, there are a some caveats due to the colocation of *.rb and *.rbi files in the same project. These issues should not otherwise manifest when this library is used as a dependency.
-
For go to definition usages, sorbet might get confused and may not always navigate to the correct location.
-
For each generic type in
*.rbifiles, a spurious "Duplicate type member" error is present.
Documentation dependencies are intentionally isolated from the primary contributor bundle. Install them with:
$ ./scripts/docs installTo build the documentation, run:
$ ./scripts/docs buildTo preview the documentation, run:
$ PORT=8808 ./scripts/docs previewThe existing bundle exec rake build:docs and
bundle exec rake docs:preview tasks delegate to the same docs bundle.
Realtime Live Smoke is a separate manual workflow using the existing protected
ci environment, SDK-team approval, default-branch restriction, read-only job
permissions and pinned actions. It is not a required PR check. Dispatching and
approving this workflow authorizes the bounded paid scenario described below;
do not dispatch it merely to validate a PR. No environment protection is created
or weakened by this change.
For an explicitly authorized local audio run, put the key in the environment and run:
OPENAI_REALTIME_LIVE_SMOKE=1 bundle exec ruby scripts/realtime-smoke/run.rb audioThe fixed scenario makes at most one short gpt-4o-mini-tts request for synthetic
speech, one committed gpt-4o-mini-transcribe session, and one gpt-realtime-2.1
voice response capped at 128 output tokens. The fixture is at most six seconds
of 24 kHz mono PCM16, reused in memory for both sessions. No microphone or
speaker is opened; no input files, prompts or model overrides are accepted.
All HTTP retries and conversational reconnect are disabled. HTTP operations
have ten-second timeouts, the scenario has a sixty-second deadline, and its
supervising process terminates it after seventy-five seconds. The SDK's
block-scoped sockets close/abort on exit. There is no automatic rerun; each
manual run consumes another allowance. These are workload bounds, not a dollar
cap: use a dedicated project with an owner-approved spending limit as well.
Output is one JSON metadata record. SDK/transport/example output and exception
messages are discarded at the subprocess boundary, including on failure.
Transcripts, audio, credentials, SDP and identifiers are never reported or
uploaded. The workflow uploads no artifacts. Only exit 0 with passed means
live network evidence; exit 1 means failure, and exit 2 means skipped or
unavailable. Missing opt-in/credentials never counts as passing. Model access
and service errors fail the run without printing response bodies.
The separate browser selection currently returns unavailable without making
requests. Its explicit dependency is Ruby Realtime: browser WebRTC reference
workflows. Integration must use that application's native RTCPeerConnection
workflow to prove generated call creation, real negotiation, sideband
acknowledgement and explicit hangup, with known-call cleanup on every failure.
Until integrated, audio success is not WebRTC evidence. The distinct Live API,
SIP and translation are outside this harness. scripts/realtime-smoke/manifest.yml
records this opt-in coverage separately from the ordinary example E2E inventory;
existing text/function E2E and generic Responses smoke do not establish it.
Offline validation (no credentials or network):
bundle exec ruby test/scripts/realtime_smoke_test.rbOffline lifecycle tests verify the harness and shipped event handling; they do not establish model availability or successful live transcription/voice.