diff --git a/CHANGELOG.md b/CHANGELOG.md index e77ea9a..5394fc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add a local Codex CLI provider for RubyLLM plain-text and structured-output - experiments. + requests. - Add isolated execution defaults, timeout handling, and process-group cleanup. - Add live smoke coverage for plain-text and structured-output requests. - Add CI, Conventional Commit checks, changelog validation, and tag-driven @@ -24,3 +24,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 during completion-only runs. - Align the documented Ruby requirement, CI matrix, syntax target, and locked development dependencies on Ruby 3.3 or newer. +- Generalize documentation examples and live smoke configuration for public + reuse. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1ea6ff6..6482f6c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,11 +21,11 @@ bundle exec lefthook install bundle exec rake ``` -The authenticated smoke test is separate because it invokes the real Codex CLI -and consumes subscription allowance: +The authenticated smoke test is separate because it invokes the real Codex CLI. +Choose a model available to your configured account: ```bash -bundle exec rake codex:smoke +CODEX_MODEL=your-model bundle exec rake codex:smoke ``` Run it inside an appropriate process and memory boundary when changing process diff --git a/README.md b/README.md index 5a7ce0e..a616781 100644 --- a/README.md +++ b/README.md @@ -4,18 +4,17 @@ [![Gem Version](https://badge.fury.io/rb/ruby_llm-codex.svg)](https://rubygems.org/gems/ruby_llm-codex) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) -This small adapter lets RubyLLM invoke the official local Codex CLI as a -provider. Codex reuses the ChatGPT login stored by `codex login`, so these runs -consume your Codex subscription allowance rather than OpenAI API credits. +This adapter lets RubyLLM invoke the official local Codex CLI as a provider. It +supports text completions and structured output through RubyLLM's familiar chat +interface. -The intended use is controlled prompt comparison, especially structured-output -experiments. It is not intended to turn a personal ChatGPT subscription into a -public API. +Authentication is handled by the Codex CLI using the account configured by +`codex login`; the gem does not accept or store an OpenAI API key. -This measures **Codex harness output**, not a raw model API response. Codex adds -its own base agent instructions and runtime behavior. RubyLLM system messages -are layered in as Codex developer instructions, which is the closest supported -mapping but not an identical wire-level experiment. +Responses come from the **Codex agent harness**, not directly from a raw model +API. Codex applies its own base instructions and runtime behavior. RubyLLM system +messages are passed as Codex developer instructions, which is the closest +supported mapping but not an identical wire-level request. ## Requirements @@ -56,16 +55,15 @@ Require the gem and use Codex as an explicit RubyLLM provider: ```ruby require "ruby_llm-codex" -class ResultSchema < RubyLLM::Schema +class SummarySchema < RubyLLM::Schema string :summary - integer :score end -response = RubyLLM.chat(model: "gpt-5.6-luna", provider: :codex) - .with_instructions("Evaluate the supplied text consistently.") - .with_schema(ResultSchema) +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 evaluate") + .ask("The text to summarize") pp response.content ``` @@ -90,7 +88,7 @@ By default every request: - passes RubyLLM's JSON schema through `--output-schema`. This prevents local `AGENTS.md`, plugins, MCP servers, repository contents, and -saved session history from quietly affecting a comparison. +saved session history from quietly affecting a request. ## Configuration @@ -101,7 +99,7 @@ To deliberately let Codex inspect a repository or use selected configuration: ```ruby chat.with_params( codex: { - working_directory: Rails.root.to_s, + working_directory: "/path/to/project", sandbox: "read-only", ignore_user_config: false, ephemeral: true, @@ -143,9 +141,9 @@ instructions. Those settings, plus `features.shell_snapshot` and execution backend for your application. - Temperature is rejected because this Codex path does not expose it. - Fresh `codex exec` process per RubyLLM completion. This prioritizes isolation - and simple comparisons over throughput. + and predictable behavior over throughput. - Multi-turn RubyLLM histories are serialized into a role-labelled transcript. - One-shot `with_instructions(...).ask(...)` experiments have the cleanest + One-shot `with_instructions(...).ask(...)` requests have the most direct semantic mapping. If you later need streaming, persistent threads, or high throughput, replace @@ -162,7 +160,7 @@ client while keeping the same RubyLLM provider surface. 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: true` and do - not set a working directory when you need isolated comparisons. + not set a working directory when you need isolated requests. ## Development @@ -185,11 +183,12 @@ The default Rake task runs the unit suite and Standard Ruby. CI runs the same gate on Ruby 3.3, 3.4, and 4.0.3, then validates commit messages and git-cliff changelog generation. -The authenticated smoke task is intentionally separate because it consumes -Codex subscription allowance: +The authenticated smoke task is intentionally separate because it sends real +requests through the configured Codex account. Choose a model available to that +account: ```bash -bundle exec rake codex:smoke +CODEX_MODEL=your-model bundle exec rake codex:smoke ``` When changing process lifecycle or shell execution, run that task inside a @@ -244,3 +243,14 @@ the [Code of Conduct](CODE_OF_CONDUCT.md). Security reports should follow ## License MIT License. See [LICENSE](LICENSE). + +## About + +Made by the team at [Ethos Link](https://www.ethos-link.com) — practical +software for growing businesses. We build tools for hospitality operators who +need clear workflows, fast onboarding, and real human support. + +We also build [Reviato](https://www.reviato.com), “Capture. Interpret. Act.”. +Turn guest feedback into clear next steps for your team. Collect private +appraisals, spot patterns across reviews, and act before small issues turn into +public ones. diff --git a/Rakefile b/Rakefile index 5e8548d..4ec8d51 100644 --- a/Rakefile +++ b/Rakefile @@ -144,7 +144,8 @@ namespace :codex do desc "Run live plain-text and structured-output smoke tests through Codex CLI" task :smoke do cli_path = ENV.fetch("CODEX_BIN", "codex") - model = ENV.fetch("CODEX_MODEL", "gpt-5.4") + model = ENV["CODEX_MODEL"] + raise "Set CODEX_MODEL to a model available in your Codex account" if model.nil? || model.empty? timeout = Float(ENV.fetch("CODEX_TIMEOUT", RubyLLM::Providers::Codex::DEFAULT_TIMEOUT)) version_output, version_status = Open3.capture2e(cli_path, "--version") raise "Could not run #{cli_path.inspect}: #{version_output.strip}" unless version_status.success? diff --git a/ruby_llm-codex.gemspec b/ruby_llm-codex.gemspec index 5ce915e..efac0c8 100644 --- a/ruby_llm-codex.gemspec +++ b/ruby_llm-codex.gemspec @@ -9,9 +9,9 @@ Gem::Specification.new do |spec| spec.email = ["devel@ethos-link.com"] spec.summary = "Use a local, ChatGPT-authenticated Codex CLI as a RubyLLM provider" spec.description = <<~TEXT.strip - A small RubyLLM provider for controlled model comparisons. It invokes the - official Codex CLI, reuses local ChatGPT authentication, and maps RubyLLM - structured-output schemas to Codex output schemas. + A RubyLLM provider that invokes the official Codex CLI, reuses local ChatGPT + authentication, and maps RubyLLM structured-output schemas to Codex output + schemas. TEXT spec.homepage = "https://github.com/ethos-link/ruby_llm-codex" spec.license = "MIT" diff --git a/test/codex_provider_test.rb b/test/codex_provider_test.rb index b1bb5d2..85651a3 100644 --- a/test/codex_provider_test.rb +++ b/test/codex_provider_test.rb @@ -109,7 +109,7 @@ def test_provider_options_are_forwarded_with_explicit_precedence .with_params( codex: { working_directory: @directory, - profile: "experiment", + profile: "custom-profile", codex_home:, sandbox: "read-only", ignore_user_config: false, @@ -122,7 +122,7 @@ def test_provider_options_are_forwarded_with_explicit_precedence assert_equal @directory, invocation.fetch("working_directory") assert_equal File.expand_path(codex_home), invocation.fetch("codex_home") - assert_equal "experiment", invocation.fetch("profile") + assert_equal "custom-profile", invocation.fetch("profile") assert_equal "read-only", invocation.fetch("sandbox") refute invocation.fetch("ignore_user_config") refute invocation.fetch("ephemeral")