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 for releases of this gem can be found on RubyDoc.
The REST API documentation can be found on platform.openai.com.
To use this gem, install via Bundler by adding the following to your application's Gemfile:
gem "openai", "~> 0.114.0"
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)
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
client.chat.completions.run_tools runs your function callbacks, sends their results
back to the model, and returns the final ChatCompletion. It accepts the same
messages, function-tool definitions (including OpenAI::BaseModel schemas), and
request options as create:
completion = client.chat.completions.run_tools(
model: "gpt-4o-mini",
messages: [{role: :user, content: "What is the weather in Seattle?"}],
tools: [WeatherArguments], # An OpenAI::BaseModel with a city field.
tool_handlers: {
"WeatherArguments" => -> (arguments) { {temperature_c: 18, city: arguments.fetch("city")} }
},
max_rounds: 10
)
puts(completion.choices.first.message.content)
Handlers receive a parsed JSON object with string keys; validate application
requirements in your callback. String results are sent unchanged; other results
are JSON encoded. Callbacks execute sequentially, including when the model requests
multiple tools in one response. Setting parallel_tool_calls: false limits each
response to one tool call; the runner rejects larger batches before callbacks run.
Every advertised tool must have a matching handler.
The runner validates each batch before invoking callbacks, rejects repeated call
IDs, enforces the initial tool_choice before returning or invoking callbacks, and
enforces the tool_choice.allowed_tools.tools subset on every round. It
does not mutate your input messages or append the conversation to them.
This helper is non-streaming and supports one completion choice and function tools.
max_rounds limits API calls, including the final answer; it raises
OpenAI::Helpers::Chat::ToolRunner::MaxRoundsError before executing tools in the
last allowed round. A forced tool_choice applies to the first round, then switches
to auto while preserving any allowed-tools list. Other invalid or incomplete tool
responses raise OpenAI::Helpers::Chat::ToolRunner::Error.
Callback, transport, and API errors stop the run and propagate to your application. Callback exception text is never submitted as a tool result, and HTTP retries do not rerun callbacks. Earlier callback side effects are not rolled back; starting another run can execute tools again. Use application-level idempotency where needed. Request timeouts apply per API call, not to callback execution or the entire run. Caller-supplied request idempotency keys are scoped to each round, so retries keep the same key without colliding with the next round.
See the complete example.
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.
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.
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.
Record a clip and play generated speech with optional FFmpeg/FFplay helpers. See the local audio guide for setup, device selection and examples.
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.
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.
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
Request parameters that correspond to file uploads can be passed as raw contents, a Pathname instan
| 日期 | 榜单 | 排名 | 周期新增 | 总 STAR |
|---|