Skip to content

feat(codex): add Codex CLI container image for RHOAI - #251

Merged
hmoghani merged 27 commits into
red-hat-data-services:mainfrom
hmoghani:rhaieng-5920-codex-container-image
Jul 28, 2026
Merged

hmoghani merged 27 commits into
red-hat-data-services:mainfrom
hmoghani:rhaieng-5920-codex-container-image

Conversation

@hmoghani

@hmoghani hmoghani commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add container image for OpenAI Codex CLI (Apache 2.0) on RHOAI
  • Single Containerfile installs Codex via npm on UBI 9 minimal (upstream disclaimer included)
  • Containerfile.base available as alternative for source-built binary
  • Entrypoint handles auth, model provider config, MCP injection, git credentials, and codex-run wrapper
  • Deployment manifest with ImageStream, BuildConfig, ConfigMaps, PVC, Secret, and Deployment
  • Comprehensive README (~700 lines) with vLLM compatibility matrix

Upstream Disclaimer

The Codex CLI binary installed in this image is a pre-built artifact published by OpenAI via npm (@openai/codex), not by Red Hat. It has not been built, scanned, or validated according to Red Hat standards. Containerfile.base is available for users who need a source-built alternative.

Files

File Description
Containerfile RHOAI image (UBI 9 minimal, Codex via npm, entrypoint, MCP, session persistence)
Containerfile.base Alternative: compiles Codex from source (Rust cross-compilation)
Containerfile.openshell OpenShell sandbox variant
entrypoint.sh Runtime setup: auth, vLLM provider, MCP, git creds, codex-run wrapper
deployment.yaml OpenShift manifests (ImageStream, BuildConfig, ConfigMaps, PVC, Secret, Deployment)
README.md Comprehensive deployment guide
deployment/README.md Quick reference and file index

vLLM Compatibility

Requires upstream vLLM v0.25.1+. RHOAI 3.5 EA (v0.21.0) returns "Unexpected message role" because Codex sends developer role messages which that vLLM version does not support. RHOAI 3.6 EA (expected v0.26+) should include the fix (upstream PR #43590).

Harmony models (gpt-oss) reject namespace tools on all vLLM versions. Tracked: vllm-project/vllm#49493.

Model Parser vLLM Tool Calling
Qwen3.6-27B qwen3_coder Upstream v0.25.1 Works (tested end-to-end)
Qwen3-32B hermes Upstream v0.25.1 Works (tested end-to-end)
Qwen3-32B qwen3_coder Upstream v0.25.1 No (tool calls emitted as text)
Qwen3-8B qwen3_coder Upstream v0.25.1 No (8B too small for tool calling)
Any model any RHOAI v0.21.0 No ("Unexpected message role" — Codex uses developer role)
gpt-oss-120b any Any version No ("tool type namespace not supported")

Test plan

  • Image builds via npm on UBI 9 minimal (single Containerfile)
  • Container runs as non-root (UID 1001, restricted-v2 SCC)
  • Entrypoint generates config.toml from env vars (env vars override ConfigMap)
  • codex-run wrapper works via bash -l for both interactive and exec mode
  • MCP injection via MCP_CONFIG_JSON env var (tested, dedup on restart)
  • MCP injection via ConfigMap (tested, dedup on restart)
  • Git credential injection via GITHUB_PAT (tested)
  • API keys not persisted to PVC (env.sh uses single-quoted printf)
  • CODEX_API_KEY set via secretKeyRef for sleep infinity + oc exec mode
  • Config persists across pod restarts (PVC-backed)
  • Codex exec end-to-end with Qwen3.6-27B + qwen3_coder on upstream vLLM v0.25.1 (file creation works)
  • Codex exec end-to-end with Qwen3-32B + hermes on upstream vLLM v0.25.1 (file creation works)
  • Sanitized env var interpolation (TOML/shell injection prevention)
  • Agent listed in AGENTS.md non-standard section
  • Codex with harmony models (gpt-oss) — blocked by vLLM namespace tool limitation
  • Codex on RHOAI 3.5 EA vLLM (v0.21.0) — blocked by missing developer role support
  • No hardcoded URLs, models, or cluster-specific values

hmoghani and others added 2 commits July 10, 2026 12:28
Build a validated container image for OpenAI's Codex CLI (Apache 2.0)
on Red Hat OpenShift AI, following the base-image-plus-flavor pattern
established by OpenCode.

Base image (Containerfile.base):
- Compiles Codex v0.144.0 from source (Rust) via cross-compilation
- UBI 9 minimal runtime with git, jq, tar, nodejs, npm
- Includes bwrap (bubblewrap) sandbox binary
- Non-root user (UID 1001, GID 0) for restricted-v2 SCC
- Published to quay.io/hmoghani/codex-base:0.144.0

RHOAI flavor (Containerfile):
- Extends base with entrypoint.sh for runtime configuration
- Inference endpoint configurable via OPENAI_BASE_URL env var
- MCP server injection via TOML file, mounted ConfigMap, or JSON env var
- Git credential injection via GITHUB_PAT
- Session persistence via PVC-backed CODEX_HOME
- codex-run wrapper script for oc exec convenience

Deployment (deployment.yaml):
- ImageStream, BuildConfig, ConfigMaps, PVC, Deployment
- Recreate strategy for RWO PVC compatibility
- Full restricted-v2 SCC security context

Known issue: Codex CLI uses the OpenAI Responses API exclusively with
namespace tool types. vLLM 0.19.1 (current RHOAI EA build) does not
support these tool types, returning "tool type namespace not supported".
Direct /v1/responses calls without tools work correctly. Full Codex
compatibility requires a newer vLLM version with namespace tool type
support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hmoghani
hmoghani requested a review from a team as a code owner July 10, 2026 16:36
@coderabbitai

coderabbitai Bot commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds two Codex container build variants, runtime initialization, OpenShift resources for persistent vLLM deployment, and documentation covering operation, configuration, troubleshooting, and cleanup.

Changes

Codex OpenShift deployment

Layer / File(s) Summary
Build Codex container images
agents/codex/deployment/Containerfile.base, agents/codex/deployment/Containerfile, agents/codex/deployment/Containerfile.openshell
Builds cross-compiled and npm-based UBI images and updates the OpenShell Codex version.
Initialize the Codex runtime
agents/codex/deployment/entrypoint.sh
Initializes credentials, persistent configuration, vLLM provider settings, MCP servers, helper scripts, and Codex command dispatch.
Wire the OpenShift deployment
agents/codex/deployment/deployment.yaml
Defines the ImageStream, BuildConfig, ConfigMaps, workspace PVC, and restricted Deployment configuration.
Document deployment and operation
agents/codex/README.md, agents/codex/deployment/README.md, AGENTS.md
Documents building, deploying, operating, persisting, extending, troubleshooting, and cleaning up the Codex deployment.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BuildConfig
  participant ImageStream
  participant Deployment
  participant ConfigMaps
  participant WorkspacePVC
  participant entrypoint.sh
  participant vLLM
  BuildConfig->>ImageStream: Publish codex:latest
  Deployment->>ImageStream: Pull codex:latest
  Deployment->>ConfigMaps: Mount configuration
  Deployment->>WorkspacePVC: Mount persistent workspace
  Deployment->>entrypoint.sh: Start persistent container
  entrypoint.sh->>WorkspacePVC: Initialize CODEX_HOME
  entrypoint.sh->>vLLM: Configure Responses API endpoint
  entrypoint.sh->>Deployment: Generate codex-run and env.sh
Loading

Suggested reviewers: bardenr

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed The linked vLLM issue is relevant to the documented harmony-model namespace-tool limitation.
Out of Scope Changes check ✅ Passed The changes stay within scope of the stated goal of adding Codex container, deployment, and documentation support.
Title check ✅ Passed The title clearly summarizes the main change: adding a Codex CLI container image for RHOAI.
Description check ✅ Passed The description is directly related to the changeset and matches the added image, manifests, and documentation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

Large PR detected (1481 lines changed)

This PR exceeds 1200 lines of code changes (excluding lock files, generated content, and images). Large PRs are harder to review thoroughly and are more likely to introduce bugs.

Consider splitting this PR into smaller, focused changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

This comment was marked as off-topic.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (1)
agents/codex/deployment/deployment.yaml (1)

163-176: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Use a Secret for OPENAI_API_KEY instead of a plaintext env var.

Even though the value is "dummy" for vLLM, the template should demonstrate the secure pattern so operators don't paste real keys directly into the manifest.

🔒 Suggested: reference a Secret instead of plaintext value
             - name: OPENAI_API_KEY
-              value: "dummy"
+              valueFrom:
+                secretKeyRef:
+                  name: codex-api-key
+                  key: api-key

And add a Secret resource (created out-of-band by the operator):

# Create before deploying:
#   oc create secret generic codex-api-key --from-literal=api-key=dummy
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@agents/codex/deployment/deployment.yaml` around lines 163 - 176, Replace the
plaintext OPENAI_API_KEY value in the deployment environment with a secretKeyRef
referencing the operator-created Secret codex-api-key and key api-key. Add a
nearby comment documenting the required oc create secret generic codex-api-key
--from-literal=api-key=dummy command, without embedding a Secret resource in the
manifest.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@agents/codex/deployment/entrypoint.sh`:
- Around line 274-282: Remove the OPENAI_API_KEY and CODEX_API_KEY export lines
from the env.sh heredoc in the entrypoint environment-generation block, leaving
only non-secret settings persisted to the PVC. Keep both API keys available
through the container environment for oc exec sessions, and ensure no other
generated file writes these secrets to disk.
- Around line 200-246: Prevent setup_mcp from appending duplicate MCP
definitions to the persistent config.toml on container restart. At the start of
setup_mcp, detect whether config_file already contains MCP server tables
matching [mcp_servers.*]; if present, log that MCP configuration is already
loaded and return before processing MCP_CONFIG_TOML, MCP_CONFIG_JSON, or the
mounted ConfigMap source.
- Around line 178-191: Update the provider setup in the entrypoint configuration
logic to require a non-empty, valid OPENAI_MODEL instead of falling back to
"default" when OPENAI_BASE_URL is set, and fail clearly if it is missing. Reuse
the existing model-entry detection or add equivalent deduplication before
appending the generated model setting, so the model assignment is emitted only
when no staged model already exists; locate the relevant logic around the
OPENAI_MODEL handling and the provider configuration append block.

In `@agents/codex/README.md`:
- Around line 388-414: The MCP setup flow, including setup_mcp() and handling of
MCP_CONFIG_TOML, MCP_CONFIG_JSON, and the mounted mcp-servers.toml, must be
idempotent across restarts. Stop appending managed MCP configuration directly to
persistent /workspace/.codex/config.toml; instead replace a clearly delimited
managed block or rebuild the generated configuration from the base config on
each startup, preventing duplicate [mcp_servers.*] tables.
- Around line 150-153: Update the registry authentication and push commands in
the README to avoid --tls-verify=false; use oc registry login or configure a
trusted registry CA, and pass the token securely via --password-stdin rather
than exposing it in command arguments.
- Around line 141-145: Update the documented OpenShift commands so the
codex-base BuildConfig explicitly uses Containerfile.base via dockerfilePath,
and modify the codex flavor start-build command to pass
BASE_IMAGE=codex-base:latest, ensuring it consumes the freshly built internal
base image.

---

Nitpick comments:
In `@agents/codex/deployment/deployment.yaml`:
- Around line 163-176: Replace the plaintext OPENAI_API_KEY value in the
deployment environment with a secretKeyRef referencing the operator-created
Secret codex-api-key and key api-key. Add a nearby comment documenting the
required oc create secret generic codex-api-key --from-literal=api-key=dummy
command, without embedding a Secret resource in the manifest.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 06abf1f5-19b8-4bfb-9a05-9313e6a94ff0

📥 Commits

Reviewing files that changed from the base of the PR and between 0f3ccd1 and 1c19f6a.

📒 Files selected for processing (7)
  • agents/codex/README.md
  • agents/codex/deployment/Containerfile
  • agents/codex/deployment/Containerfile.base
  • agents/codex/deployment/Containerfile.openshell
  • agents/codex/deployment/README.md
  • agents/codex/deployment/deployment.yaml
  • agents/codex/deployment/entrypoint.sh

Comment thread agents/codex/deployment/entrypoint.sh Resolved
Comment thread agents/codex/deployment/entrypoint.sh Resolved
Comment thread agents/codex/deployment/entrypoint.sh Resolved
Comment thread agents/codex/README.md Resolved
Comment thread agents/codex/README.md Resolved
Comment thread agents/codex/README.md Resolved
hmoghani and others added 3 commits July 10, 2026 13:01
- Split CODEX_EXTRA_ARGS (global) from CODEX_EXEC_ARGS (exec-only).
  --skip-git-repo-check is only valid for `codex exec`, not interactive
  mode. The codex-run wrapper now detects the subcommand and applies
  exec-only args appropriately.

- MCP server injection now deduplicates: servers already present in
  config.toml are skipped on restart. Prevents accumulating duplicate
  [mcp_servers.*] entries across pod restarts when config is persisted
  on PVC. Applies to all three injection methods (TOML file, JSON env
  var, mounted ConfigMap).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove API keys from env.sh persisted to PVC — secrets stay in
  container env vars only, not written to disk
- Require OPENAI_MODEL when OPENAI_BASE_URL is set instead of falling
  back to invalid "default" model name
- Deduplicate model entry in config.toml when staged ConfigMap already
  sets it
- Replace --tls-verify=false with oc registry login in README
- Remove cluster-build Option A from README (oc new-build without
  dockerfilePath doesn't find Containerfile.base)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hmoghani hmoghani changed the title feat(codex): add Codex CLI container image for RHOAI (RHAIENG-5920) feat(codex): add Codex CLI container image for RHOAI Jul 10, 2026
@shricharan-ks shricharan-ks self-assigned this Jul 11, 2026
@hmoghani
hmoghani marked this pull request as draft July 13, 2026 17:55
hmoghani and others added 4 commits July 14, 2026 14:53
Tested Codex CLI against vLLM v0.19.1 (RHOAI EA) and v0.25.1
(upstream). v0.19.1 returns "tool type namespace not supported"
on every request — Codex cannot function. v0.25.1 works end-to-end
with Qwen3-8B and tool calling enabled.

- Add version compatibility table to Troubleshooting section
- Document required vLLM flags (--enable-auto-tool-choice,
  --tool-call-parser) and model-specific parser values
- Update Known Limitations with concrete version guidance
- Fix entrypoint: env vars now override staged ConfigMap values
  for model/provider config (sed replace instead of skip)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Testing gpt-oss-120b on upstream vLLM v0.25.1 with --tool-call-parser
openai reproduces the same "tool type namespace not supported" error
as RHOAI v0.21.0. The error is a parser limitation, not a version
issue: the openai parser does not support the namespace tool type
Codex sends. Using qwen3_coder or hermes parsers with compatible
models works on v0.24.0+.

Tested configurations:
- RHOAI v0.21.0 + gpt-oss-120b + parser=openai → fails
- Upstream v0.25.1 + gpt-oss-120b + parser=openai → fails (same error)
- Upstream v0.25.1 + Qwen3-8B + parser=qwen3_coder → works
- Upstream v0.24.0 + Qwen3-8B + parser=qwen3_coder → works

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…s, not parser

Testing gpt-oss-120b with hermes parser on upstream v0.25.1 still
returns "tool type namespace not supported". The rejection is in a
gpt-oss-specific code path in vLLM's Responses API layer, before
the parser is invoked. Open-source models (Qwen3, Llama) work fine.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ls in vLLM

Per vLLM team: namespace tool types are supported for non-harmony
(open-source) models but not yet implemented for harmony (gpt-oss)
models. This is a known limitation. Updated docs to use correct
terminology (harmony vs non-harmony) and remove speculation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hmoghani
hmoghani marked this pull request as ready for review July 17, 2026 18:49

@shricharan-ks shricharan-ks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review — PR #251 (comprehensive, 2 review agents)

This PR adds a Codex CLI container image for RHOAI — multi-stage Containerfiles (Rust cross-compilation), an entrypoint script handling auth/config/MCP injection, OpenShift deployment manifests, and comprehensive README. 7 files changed, 1533 additions.

The container architecture is well-designed and the README is thorough. However, the entrypoint script has several injection vulnerabilities that should be addressed before merge, plus supply chain concerns in the Containerfiles.


Findings (8, ranked by severity)

1. 🔴 entrypoint.sh:299 — Shell injection via PVC-persisted env.sh (CONFIRMED)

OPENAI_BASE_URL and OPENAI_MODEL are interpolated unsanitized into a generated env.sh script written to the PVC:

echo "export OPENAI_BASE_URL=\"${OPENAI_BASE_URL}\"" >> "${CODEX_DIR}/env.sh"

If OPENAI_BASE_URL contains " or backticks (e.g., via a misconfigured ConfigMap), the generated env.sh becomes an injection payload. Since .bashrc sources this file and it lives on the PVC, the payload survives pod restarts and executes on every oc exec session with access to all env vars including API keys.

Fix: Use printf '%s' "$var" with single quotes in the generated script, or write a non-executable env file parsed by the entrypoint rather than sourced by bash.


2. 🔴 entrypoint.sh:184 — TOML injection via model/URL env vars (CONFIRMED)

OPENAI_MODEL and OPENAI_BASE_URL are interpolated into a config.toml heredoc without escaping quotes or newlines. A crafted model name can inject a rogue [model_providers] section, routing all inference traffic (prompts + code context) to an attacker-controlled endpoint.

Fix: Sanitize values (strip quotes/newlines) or use a tool like python3 -c "import toml; ..." to generate the config safely.


3. 🟡 deployment.yaml:174 — API key in plaintext env var (CONFIRMED)

- name: OPENAI_API_KEY
  value: "dummy"

When a user replaces dummy with a real key, it's stored in plaintext in the Deployment spec. Should use valueFrom: secretKeyRef: pointing to a Secret resource. CodeRabbit also flagged this.


4. 🟡 Containerfile:21 — Default BASE_IMAGE points to personal Quay registry (CONFIRMED)

ARG BASE_IMAGE=quay.io/hmoghani/codex-base:latest

This is a personal namespace with a :latest floating tag. If the account is deleted or the image is overwritten, all downstream builds break. Should point to an org-owned registry (e.g., quay.io/opendatahub/) or clearly document that users must build the base locally first.


5. 🟡 entrypoint.sh:242 — MCP JSON-to-TOML conversion doesn't escape quotes (CONFIRMED)

MCP server args containing double-quotes (e.g., --config={"key":"val"}) produce malformed TOML. The jq wrapping adds outer quotes but doesn't escape inner ones, causing Codex to fail to parse its config and silently drop all MCP servers.


6. 🟡 Containerfile.base:31 — Rolling rust:1-bookworm builder tag (PLAUSIBLE)

The builder stage uses an unpinned rust:1-bookworm while the runtime is digest-pinned. A Rust/Debian update between rebuilds could introduce ABI mismatches with the pinned UBI9 runtime. Pin to at least a patch-level tag (e.g., rust:1.87.0-bookworm).


7. 🟡 README.md:1 — Agent not listed in AGENTS.md non-standard section (CONFIRMED)

This agent has no Makefile, no pyproject.toml, no FastAPI, no /chat/completions, no port 8080, and no tests/. It diverges from every standard convention. The PR should add a codex/deployment entry to the "Non-standard agents" section in AGENTS.md.


8. 🟡 README.md:63 — README says "Node.js 16" but actual version is 18+ (CONFIRMED)

Containerfile.base runs microdnf install nodejs without version pinning. UBI9 repos ship Node.js 18 (Node 16 reached EOL in September 2023). The README claim is inaccurate.


Additional observations (not blocking)

  • entrypoint.sh:313: Unquoted ${CODEX_EXTRA_ARGS} breaks on model names with spaces or glob characters. Use a bash array instead.
  • entrypoint.sh:210: MCP server names used unsanitized in grep/sed regex patterns — names with .* or ] cause regex injection.
  • entrypoint.sh:102: GITHUB_PAT written to .git-credentials is not scrubbed on exit — stale credentials persist on the PVC after rotation.

What looks good

  • Multi-stage build correctly separates Rust compilation from UBI9 runtime
  • Non-root execution (UID 1001, restricted-v2 SCC)
  • PVC-backed session persistence with config separation (env.sh excludes secrets — though the injection makes this moot)
  • codex-run wrapper handles both interactive and exec modes cleanly
  • vLLM compatibility matrix is well-documented with root cause analysis
  • MCP dedup logic prevents duplicate injection on pod restarts

Summary

Findings #1 and #2 are blocking — the shell and TOML injection vulnerabilities allow env var values to execute arbitrary commands or redirect inference traffic. Fix those, address the plaintext API key (#3) and personal registry (#4), and this is ready to merge.

Security fixes:
- Sanitize OPENAI_BASE_URL and OPENAI_MODEL before interpolating
  into config.toml and env.sh (prevents TOML/shell injection)
- Use single-quoted printf in env.sh instead of double-quoted heredoc
- Use secretKeyRef for OPENAI_API_KEY in deployment.yaml

Supply chain fixes:
- Pin Rust builder image to 1.87.0-bookworm (was rolling :1-bookworm)
- Document that quay.io/hmoghani base image will move to org-owned
  registry; users can build locally with Containerfile.base

Documentation fixes:
- Fix Node.js version reference (UBI9 ships 18, not 16)
- Add codex/deployment to AGENTS.md non-standard agents section

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@bardenr bardenr left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to follow the readme to deploy on rosa. Only had issues with pushing a local image to the clusters image registry and some of the commands using codex-run

Comment thread agents/codex/README.md Resolved
Comment thread agents/codex/README.md Resolved
Comment thread agents/codex/README.md Resolved
@msager27

Copy link
Copy Markdown
Contributor

Tested on OpenShift — two issues found

Deployed the PR on our cluster (built from source, deployed to OpenShift with OpenAI backend / gpt-5.6-sol). Found two issues:

1. Missing codex-code-mode-host binary — Codex can't execute tools

The Containerfile.base cargo build only compiles codex and bwrap:

cargo build --release --target x86_64-unknown-linux-gnu --bin codex --bin bwrap

But Codex requires codex-code-mode-host for file operations and command execution. Without it, codex exec talks to the model but fails on any tool call:

ERROR codex_core::tools::router: error=failed to spawn code-mode host
/usr/local/bin/codex-code-mode-host: No such file or directory (os error 2)

Suggested fix — add to the cargo build and binary copy in Containerfile.base:

cargo build --release --target x86_64-unknown-linux-gnu --bin codex --bin bwrap --bin codex-code-mode-host

And in Stage 2:

COPY --from=builder /opt/codex-code-mode-host /usr/local/bin/codex-code-mode-host

2. CODEX_API_KEY not set in sleep infinity mode

The entrypoint propagates OPENAI_API_KEY → CODEX_API_KEY only when running codex directly (line 356-358), but the deployment uses sleep infinity + oc exec. In that path, CODEX_API_KEY is never set and codex exec fails with 401 Unauthorized.

Suggested fix — add to deployment.yaml env section:

- name: CODEX_API_KEY
  valueFrom:
    secretKeyRef:
      name: codex-api-key
      key: api-key

🤖 Generated with Claude Code

@bardenr
bardenr dismissed their stale review July 22, 2026 14:12

Retract change requests

hmoghani and others added 4 commits July 22, 2026 19:44
Tested Qwen3-8B and Qwen3-32B on vLLM v0.25.1: simple prompts work
but tool execution fails. The models emit tool calls as text
(<tool_call> XML tags) instead of structured function_call objects.
vLLM's Responses API parser doesn't convert them. Tool-dependent
workflows require an OpenAI backend until vLLM fixes this.

Added compatibility table, upstream issue links, and updated Known
Limitations section.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Link to vllm-project/vllm#49493 in the Responses API Compatibility
section so users can track the fix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lling works

Tested findings:
- Qwen3.6-27B + qwen3_coder: full tool calling on upstream vLLM v0.25.1
- Qwen3-32B + hermes: full tool calling on upstream vLLM v0.25.1
- qwen3_coder is for Qwen3.6+; older Qwen3 models need hermes parser
- RHOAI 3.5 EA (v0.21.0) fails with "Unexpected message role" — needs
  upstream v0.25.1+ or RHOAI 3.6 EA (expected v0.26+)

Updated compatibility tables, parser guidance, vLLM version
requirements, example commands, and Known Limitations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@agents/codex/README.md`:
- Around line 672-675: Update the “Open Source Model Tool Calling” heading to
use “Open-Source,” and add the required blank lines before and after the
compatibility list under that heading to satisfy MD032/blanks-around-lists.
- Around line 521-526: The README’s statement that the namespace tool-type error
occurs on all vLLM versions and parsers overstates the tested evidence. Update
the explanatory paragraph around the harmony-model limitation to qualify this as
applying to the current tested versions and parsers, or substantiate the
absolute claim with coverage across the supported range; preserve the existing
model-specific scope and upstream issue reference.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 455d065c-b71a-4aa0-85fd-5bb86a58719d

📥 Commits

Reviewing files that changed from the base of the PR and between 2e89047 and ed5d5d4.

📒 Files selected for processing (2)
  • agents/codex/README.md
  • agents/codex/deployment/deployment.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • agents/codex/deployment/deployment.yaml

Comment thread agents/codex/README.md Resolved
Comment thread agents/codex/README.md Resolved
hmoghani and others added 4 commits July 23, 2026 12:10
The "Unexpected message role" error on RHOAI v0.21.0 is a Codex + vLLM
compatibility issue (Codex sends developer role messages), not
model-specific. Updated tables and version requirements accordingly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lint

- Qualify "all vLLM versions" to "all tested vLLM versions" for the
  harmony namespace tool error
- Hyphenate "Open-Source" in heading
- Add blank line before list (MD032)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The TEMP_FILES array and cleanup_temp_files trap were unused dead code
from an earlier MCP injection approach that wrote temp files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…check)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@msager27
msager27 self-requested a review July 23, 2026 18:32
@msager27

Copy link
Copy Markdown
Contributor

Bug: setup_model_provider() appends top-level keys after TOML section headers

During testing on OpenShift with Qwen 3.6-27B, codex resume crashes with:

Error loading config.toml: invalid type: string "qwen3.6-27b", expected u32
in `tui.model_availability_nux`

Root cause: setup_model_provider() in entrypoint.sh removes existing model = and model_provider = lines with sed, then appends them at the end of config.toml using cat >>. If the file already contains a [section] header (e.g. [tui.model_availability_nux] written by a previous interactive session), the appended model = "..." lands inside that section instead of at the top level. TOML assigns any key after a [section] header to that section until the next header.

Suggested fix: Insert model = and model_provider = at the top of the file (e.g. sed -i '1i ...') instead of appending with cat >>, or write the entire config from scratch rather than patching a file that may have accumulated sections from interactive use.

🤖 Generated with Claude Code

When Codex writes runtime sections like [tui.model_availability_nux]
or [projects."..."] to config.toml, appending model= at the end
places it inside those sections instead of at the top level, causing
TOML parse errors on restart. Use sed -i '1i' to insert top-level
keys at the beginning. The [model_providers.vllm] section block is
still appended since it's a section header. (Reported by msager27)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread agents/codex/README.md Resolved
Comment thread agents/codex/README.md Resolved
Comment thread agents/codex/README.md Resolved
…ming

- Add OGX to namespace tool error description (same error occurs)
- Use consistent "RHOAI 3.5 EA (vLLM v0.21.0)" naming throughout
  instead of bare "RHOAI v0.21.0"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@shricharan-ks shricharan-ks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — PR #251 (after 25 commits, addressing all reviewer feedback)

Comprehensive re-review checking all findings from msager27, bardenr, CodeRabbit, and my previous review.


Resolved findings ✅ (10/13)

Finding Source Status Evidence
Missing codex-code-mode-host binary msager27 ✅ FIXED Containerfile switched to npm install; Containerfile.base now builds --bin codex-code-mode-host
config.toml key ordering crash msager27 ✅ FIXED setup_model_provider() now uses sed -i "1i\\..." to insert at top
OGX namespace tool errors msager27 ✅ FIXED Documented in README and AGENTS.md
codex-run needs full path bardenr ✅ FIXED README uses bash -l which sources .bashrc adding ~/.codex to PATH
oc registry info gives internal route bardenr ✅ FIXED README uses oc get route -n openshift-image-registry for external route
Shell injection via env.sh Previous review ✅ FIXED sanitize_value() strips ", ', `, $, \, newlines; single-quoted printf; API keys not persisted
API key in plaintext deployment.yaml Previous review ✅ FIXED Now uses secretKeyRef referencing K8s Secret codex-api-key
Personal registry in Containerfile Previous review ✅ FIXED Main Containerfile and Containerfile.base use digest-pinned UBI9
MCP config duplication on restart CodeRabbit ✅ FIXED All MCP injection methods check grep -q before appending
Fallback model name "default" CodeRabbit ✅ FIXED No default fallback exists; errors on missing model

Remaining findings (3)

1. 🟡 entrypoint.sh:~160 — TOML injection in fallback path (CONFIRMED)

The main setup_model_provider() path correctly calls sanitize_value() on OPENAI_MODEL (line ~173). But the early-return branch when OPENAI_BASE_URL is empty writes OPENAI_MODEL directly without sanitization:

echo "model = \"${OPENAI_MODEL}\"" >> "${config_file}"

A crafted OPENAI_MODEL with embedded " or newlines can inject rogue TOML keys. This is the same class of bug that was fixed in the main path but missed in this branch.

Fix: Call sanitize_value on OPENAI_MODEL in the fallback path too.


2. 🟡 entrypoint.sh:~245 — MCP JSON-to-TOML quoting still broken (CONFIRMED)

The jq expression map("\"" + . + "\"") naively wraps MCP args in double quotes without escaping inner quotes. An arg like --config={"key":"val"} produces:

args = ["--config={"key":"val"}"]

This is invalid TOML that crashes Codex on startup. From the original review — not yet addressed.

Fix: Use jq's @json filter or escape inner double-quotes with gsub.


3. 🟡 Containerfile.openshell:9 — Personal registry still used (CONFIRMED)

FROM quay.io/hmoghani/openshell-base:latest

The main Containerfile and Containerfile.base were fixed to use registry.access.redhat.com, but Containerfile.openshell still references a personal Quay.io repository.


Summary

10 of 13 findings from all reviewers are resolved — impressive iteration over 25 commits. The remaining 3 are all non-blocking for merge (the TOML injection requires a crafted env var in a branch that rarely fires, the MCP quoting affects a narrow edge case, and Containerfile.openshell is experimental). Fix the sanitize_value gap in the fallback path (#1) as priority.

hmoghani and others added 2 commits July 27, 2026 15:25
OpenShell sandbox:
- Update Containerfile.openshell to use org-owned base image
  (quay.io/aipcc/agentic-ci/openshell:0.3.27, RHEL 10)
- Use dnf instead of microdnf (RHEL 10)
- Add comprehensive OpenShell setup guide to README: Helm install,
  mTLS cert extraction, sandbox creation, Codex testing
- Tested on OpenShell v0.0.86, OpenShift 4.21, Qwen3.6-27B

Review fixes:
- Sanitize OPENAI_MODEL in fallback path (shricharan-ks #1)
- Fix MCP JSON-to-TOML quoting with jq @JSON (shricharan-ks #2)
- Update deployment/README.md references

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Change #### to ### under ## OpenShell Sandbox to avoid skipping h3.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hmoghani

Copy link
Copy Markdown
Contributor Author

@shricharan-ks thanks for the review! All issues are addressed and fixed.

@aakankshaduggal aakankshaduggal left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested end-to-end on ROSA cluster (hmoghani-codex namespace):

  • Image build: codex:v4-1785175052 built and deployed successfully from Containerfile (npm variant)
  • Entrypoint: All setup stages work correctly — env validation, CODEX_HOME symlink, vLLM provider config, MCP dedup, codex-run wrapper, API key propagation
  • Model serving: Validated with both Qwen3-8B and Qwen3.6-27B via vLLM (--tool-call-parser qwen3_coder). Tool calls generate correctly with the 27B model.
  • Security: API keys via secretKeyRef, sanitize_value() prevents injection, non-root user (UID 1001), restricted-v2 SCC compatible
  • MLflow tracing: Built codex-mlflow layer on top of this base image — traces export successfully with 10-span traces (AGENT + TOOL + LLM spans)

Minor non-blocking notes:

  • --tls-verify=false still in README registry commands — consider oc registry login for production guidance
  • Unquoted ${CODEX_EXTRA_ARGS} in entrypoint exec calls — edge case with unusual model names

LGTM — well-structured, thoroughly reviewed, and production-tested.

@shricharan-ks shricharan-ks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — All findings resolved ✅

The latest commits (c710d115, 67387f9a) address all 3 remaining findings from the previous review:

Finding 1 (TOML injection in fallback path) — FIXED ✅

The no-BASE_URL fallback now sanitizes OPENAI_MODEL:

echo "model = \"$(sanitize_value "${OPENAI_MODEL}")\"" >> "${config_file}"

Finding 2 (MCP JSON-to-TOML quoting) — FIXED ✅

Args now use jq's @json filter which properly escapes embedded quotes:

.args | map(@json) | join(", ")

Finding 3 (Containerfile.openshell personal registry) — FIXED ✅

Changed from quay.io/hmoghani/openshell-base:latest to quay.io/aipcc/agentic-ci/openshell:0.3.27 (org-owned registry, version-pinned tag).

Full scorecard across all reviewers (13/13 resolved)

Finding Source Status
Missing codex-code-mode-host msager27 ✅
config.toml key ordering crash msager27 ✅
OGX namespace tool errors doc msager27 ✅
codex-run full path bardenr ✅
oc registry internal route bardenr ✅
Shell injection via env.sh Previous review ✅
TOML injection (main path) Previous review ✅
TOML injection (fallback path) Previous review ✅
API key plaintext → Secret Previous review ✅
Personal registry (Containerfile) Previous review ✅
Personal registry (openshell) Previous review ✅
MCP JSON-to-TOML quoting Previous review ✅
MCP config duplication CodeRabbit ✅

LGTM — ready to merge.

@hmoghani
hmoghani merged commit aafe3cb into red-hat-data-services:main Jul 28, 2026
12 checks passed
tarun-etikala pushed a commit that referenced this pull request Aug 6, 2026
## Summary

- Adds MLflow experiment tracking layer for Codex CLI deployments on
RHOAI
- Each interactive agent turn is exported as a trace to MLflow via the
`notify` hook, capturing inputs, outputs, token usage, and child spans
for LLM calls and tool invocations
- Uses `mlflow-codex-hook.sh` wrapper to ensure SA token auth works
regardless of how codex is launched (e.g., `oc exec` bypasses `.bashrc`)
- Validated end-to-end on ROSA with Qwen3.6-27B — multi-span traces with
tool call child spans

## Files

| File | Description |
|------|-------------|
| `Containerfile.mlflow` | Image layer extending base Codex with Python
3.12, Node.js 22, MLflow SDK, @mlflow/codex |
| `setup-mlflow.sh` | Post-entrypoint setup: SA token auth, experiment
creation, notify hook registration, config fixups |
| `mlflow-codex-hook.sh` | Notify hook wrapper — loads SA token +
experiment ID before calling @mlflow/codex |
| `deployment-mlflow-patch.yaml` | Strategic merge patch adding MLflow
env vars to existing deployment |
| `mlflow-tracing.md` | Architecture docs, trace schema, env vars, known
issues |
| `screenshots/vllm-codex-trace.png` | MLflow trace screenshot showing
successful tool call spans |

## Key design decisions

- **TOML scoping**: `notify` key must be top-level in `config.toml` —
inserting after `[section]` headers causes TOML to nest it, silently
breaking hook dispatch. `setup-mlflow.sh` inserts before the first
`[section]`.
- **Auth via wrapper**: `mlflow-codex-hook.sh` reads the SA token from
disk on every invocation rather than relying on env vars, since `oc
exec` bypasses `.bashrc`.
- **RBAC/TLS**: Uses shared `docs/mlflow-openshift-auth-and-tls.md`
patterns — `mlflow-integration` ClusterRole binding + service-ca.crt for
TLS.

## Dependencies

- Depends on #251 (base Codex container image) merging first

## Jira

- Epic: RHAIENG-5925 — MLflow Tracing for Codex CLI Agent Runtimes
- RHAIENG-5926 — Validate MLflow tracing with vLLM backends
- RHAIENG-5927 — Prototype trace schema for tool calls
- RHAIENG-5928 — Document tracing setup

## Test plan

- [x] Build `codex-mlflow` image on-cluster via BuildConfig
- [x] Deploy with `setup-mlflow.sh` chain, verify pod logs show MLflow
setup
- [x] Run interactive codex with a coding prompt (merge sort, fibonacci,
hello world)
- [x] Verify traces appear in MLflow UI with child spans (LLM + TOOL)

Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants