Overview
Use the local Codex CLI as a RubyLLM provider for text and structured output. It is for Ruby applications using RubyLLM with the local Codex generation harness. This is an independent adapter. It supports non-streaming text and structured output; attachments, RubyLLM tools and temperature are rejected. It does not offer a raw model API request path.
Installation
Ruby 4.0 or newer; RubyLLM 2.1.1 or newer, below 2.2; Codex CLI 0.162.1 or newer on PATH; Linux or macOS. Complete codex login and select a model available to that account.
gem install ruby_llm-codex
For a Bundler application, add gem "ruby_llm-codex" to its Gemfile and run bundle install.
Quick start
require "ruby_llm-codex"
response = RubyLLM.chat(
model: ENV.fetch("CODEX_MODEL"), provider: :codex
).ask("Reply with a short greeting.")
puts response.content
Set CODEX_MODEL to a model available in your installed Codex CLI. After login, the request starts a local codex exec process and prints its text response. It uses the Codex account’s access and usage limits; the gem does not accept an OpenAI API key.
Usage
Structured output
Require the gem and use Codex as an explicit RubyLLM provider:
require "ruby_llm-codex"
class SummarySchema < Schematist::Schema
string :summary
end
response = RubyLLM.chat(model: ENV.fetch("CODEX_MODEL"), provider: :codex)
.with_instructions("Return a concise summary.")
.with_schema(SummarySchema)
.with_thinking(effort: :medium)
.ask("The text to summarize")
pp response.parsed
response.content contains the JSON string. Read response.parsed for the
structured Hash, and response.tokens for token counts.
Isolation and reproducibility
By default every request:
- runs in a fresh empty temporary directory;
- uses a read-only sandbox;
- disables interactive approvals;
- ignores user Codex configuration while retaining authentication;
- creates an ephemeral Codex session;
- disables Codex shell snapshots, which are unnecessary because this provider does not support tools;
- prevents model-generated shell commands from inheriting the parent process environment;
- passes RubyLLM’s system instruction as Codex developer instructions;
- passes RubyLLM’s JSON schema through
--output-schema.
These defaults separate the request from the current repository and user configuration. Supplying a working directory, profile or user configuration changes that context deliberately; the adapter still runs the Codex harness. Sandbox behaviour also depends on the installed CLI and host platform.
Configuration
Per request
To deliberately let Codex inspect a repository or use selected configuration:
chat = RubyLLM.chat(model: ENV.fetch("CODEX_MODEL"), provider: :codex)
chat.with_provider_options(
codex: {
working_directory: "/path/to/project",
sandbox: "read-only",
ignore_user_config: false,
ephemeral: true,
timeout: 300,
shell_environment_inherit: "none"
}
)
Global
Global configuration is also available:
RubyLLM.configure do |config|
config.codex_cli_path = "/usr/local/bin/codex"
config.codex_working_directory = nil
config.codex_profile = nil
config.codex_home = nil
config.codex_ignore_user_config = true
config.codex_ephemeral = true
config.codex_timeout = 300
config.codex_shell_environment_inherit = "none"
end
Only the nested codex namespace is accepted by with_provider_options; unsupported
generic provider options raise an error instead of being silently ignored. Use
with_thinking for reasoning effort and with_instructions for developer
instructions. Those settings, plus features.shell_snapshot and
shell_environment_policy.inherit, are reserved from the arbitrary
codex.config map so their precedence remains unambiguous.
Deliberate limitations
- Non-streaming chat only.
- Text prompts only; attachments are rejected.
- RubyLLM tools are rejected. Codex remains the generation harness, not a tool execution backend for your application.
- Temperature is rejected because this Codex path does not expose it.
- Fresh
codex execprocess per RubyLLM completion. This prioritizes isolation and predictable behavior over throughput. - Multi-turn RubyLLM histories are serialized into a role-labelled transcript.
One-shot
with_instructions(...).ask(...)requests have the most direct semantic mapping.
Streaming, persistent threads and a long-running app-server client are not implemented by this gem.
Troubleshooting
-
codexis not found: install the Codex CLI, runcodex login, or setRubyLLM.config.codex_cli_pathto its absolute path. -
A request times out: increase
codex.timeoutorCODEX_TIMEOUTfor the smoke task. Timed-out process groups are terminated automatically. - Codex consumes excessive memory: use the current gem version. The provider explicitly disables Codex shell snapshots, which are unnecessary for its completion-only execution and previously caused runaway Bash processes.
-
Local instructions affect output: keep
ignore_user_config: trueand do not set a working directory when you need isolated requests.
Technical details
Each completion starts a fresh Codex process. Defaults use an empty temporary working directory, a read-only sandbox, ephemeral sessions, ignored user configuration and no inherited shell environment. RubyLLM system messages map to Codex developer instructions. POSIX process groups support timeout cleanup. Provider errors report missing executables, incompatible CLI versions, timeouts and malformed responses.
Project resources
About Ethos Link
Ethos Link is a software company founded by Paulo Fidalgo. We publish these Ruby tools alongside our business products.
- Reviato: private customer feedback and public review analysis.
- Nuntio: prepare, review and schedule social posts.
- EthosPlaces: find places, opening hours and contact details.