Skip to content

refactor(evaluator-sdk): make the Codex runtime a general coding-agent runtime - #562

Merged
SandyChapman merged 1 commit into
mainfrom
codex-runtime-general-coding/schapman
Jul 3, 2026
Merged

refactor(evaluator-sdk): make the Codex runtime a general coding-agent runtime#562
SandyChapman merged 1 commit into
mainfrom
codex-runtime-general-coding/schapman

Conversation

@SandyChapman

@SandyChapman SandyChapman commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Why

The shipped CodexCliAgentRuntime was ProfBench-shaped, which leaked a benchmark's opinions into src/ and made it unusable for general coding-agent evaluation:

  • The prompt was hardcoded to "Answer the ProfBench task below. Return only the final answer text; do not include … tool logs, or commentary" — which actively tells the agent not to do file work.
  • No way to seed a workspace, so you couldn't hand the agent a bug to fix or a module to test.
  • agent_ok was never stamped, so the standard AgentPhaseSuccessMetric read False even on success.

What changed

Generalize the runtime (runtime.py):

  • Neutral, injectable promptdefault_codex_prompt states the task's intent/inputs and invites the agent to read/create/edit files. Both runtimes accept a prompt_builder to override framing for a specific benchmark.
  • Workspace seeding — an inputs["files"] = {path: contents} convention (SEED_FILES_INPUT_KEY) is staged into the agent's workspace before it runs; paths escaping the workspace are rejected (→ failed trial for that task only). Seeded files are listed by name in the prompt.
  • Stamp agent_ok from the exit code so AgentPhaseSuccessMetric works over Codex trials.

Keep the ProfBench/generic boundary clean:

  • Runtime selection stays generic and in the SDK: resolve_codex_runtime(...) -> (runtime, effective_runtime) plus the RuntimeChoice / EffectiveCodexRuntime enums.
  • Only ProfBench's own policy — its candidate prompt and the candidate+judge score_source labels — moves to the ProfBench example, inlined at the point of use in runner.py (matching how the sibling model branch already builds its own score_source).

Compatibility

Backward compatible: the plugin's CodexRunnerTarget path is unchanged (new constructor args are optional) and now gets the neutral prompt for free.

Testing

  • SDK agent_eval suite + plugin test_agent_evaluate.py: 110 pass (added seeding / agent_ok / custom-prompt-builder / generic-resolver coverage).
  • ruff check / format clean; CI lint-python-types.sh exit 0.

Independent of #561 (Taskset entity); branched off main.

Summary by CodeRabbit

  • New Features

    • Enhanced Codex evaluation runtime to support custom prompt formatting and clearer effective-mode selection.
    • Added support for seeded files in task runs; seeded filenames are incorporated into the prompt and recorded in results metadata.
  • Bug Fixes

    • Improved handling/reporting of runtime execution outcomes (including explicit success/failure flags).
    • Added protections to prevent unsafe seed file paths from being used during workspace setup.
  • Tests

    • Expanded automated coverage for runtime selection, custom prompt behavior, seeded-file handling, and traversal failure scenarios.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Codex runtime resolution now returns the effective runtime and accepts prompt customization. The Codex CLI runtime seeds workspace files from task input, records seeding metadata, and validates seed paths. ProfBench uses the new resolver with its own prompt builder and runtime-to-score mapping. Tests cover the new runtime flow.

Changes

Codex Runtime and ProfBench Integration

Layer / File(s) Summary
Resolver API replacement
packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/runtimes/codex/runtime.py
resolve_codex_target is replaced by resolve_codex_runtime, which accepts prompt_builder, returns EffectiveCodexRuntime, and adds CodexPromptBuilder plus SEED_FILES_INPUT_KEY.
Prompt builder and workspace seeding wiring
packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/runtimes/codex/runtime.py
CodexCliAgentRuntime and CodexDockerCliAgentRuntime accept a prompt builder, prompt generation uses the configured builder, workspace seeding is added with traversal checks, and trial metadata includes agent_ok and seeded_files.
ProfBench Codex candidate wiring
packages/nemo_evaluator_sdk/examples/profbench/runner.py
ProfBench switches to resolve_codex_runtime, adds PROFBENCH_SCORE_SOURCE, adds profbench_codex_prompt, and derives score_source from the effective runtime.
Tests for resolver, seeding, and prompts
packages/nemo_evaluator_sdk/tests/agent_eval/test_codex_runtime.py
Tests cover resolve_codex_runtime, updated prompt text, seeded-file staging, traversal rejection, and injected prompt-builder behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant CodexCliAgentRuntime
  participant Workspace
  participant Process

  Caller->>CodexCliAgentRuntime: _run_task(task)
  CodexCliAgentRuntime->>Workspace: _seed_workspace(task)
  Workspace-->>CodexCliAgentRuntime: seeded_files
  CodexCliAgentRuntime->>CodexCliAgentRuntime: prompt_builder(task)
  CodexCliAgentRuntime->>Process: send prompt via stdin
  Process-->>CodexCliAgentRuntime: result
  CodexCliAgentRuntime-->>Caller: AgentEvalTrial(metadata: agent_ok, seeded_files)
Loading

Possibly related PRs

Suggested reviewers: arpitsardhana

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main refactor: turning the Codex runtime into a generic coding-agent runtime.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex-runtime-general-coding/schapman

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

@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: 1

🧹 Nitpick comments (2)
packages/nemo_evaluator_sdk/examples/profbench/runner.py (2)

255-263: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Prompt dumps full task.inputs, likely duplicating seeded file content.

Runtime now seeds the workspace from inputs["files"] (per PR summary). Embedding task.inputs raw via f"Inputs: {task.inputs}" risks re-dumping that same file content into the prompt text — bloating context/cost and producing an ugly dict repr instead of a clean task description. Consider excluding the seed-files key (or any large payload) from what's interpolated into the prompt, since seeded files are already on disk for Codex to read.

♻️ Suggested filtering
 def profbench_codex_prompt(task: AgentEvalTask) -> str:
     """Frame a task as a ProfBench candidate: return only the final answer text, no tooling chatter."""
+    prompt_inputs = {k: v for k, v in task.inputs.items() if k != SEED_FILES_INPUT_KEY}
     return (
         "Answer the ProfBench task below. Return only the final answer text; do not include "
         "analysis, markdown fences, tool logs, or commentary.\n\n"
         f"Task id: {task.id}\n"
         f"Intent: {task.intent}\n"
-        f"Inputs: {task.inputs}\n"
+        f"Inputs: {prompt_inputs}\n"
     )
🤖 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 `@packages/nemo_evaluator_sdk/examples/profbench/runner.py` around lines 255 -
263, The profbench_codex_prompt function is interpolating the entire task.inputs
object, which can re-embed seeded file content and inflate the prompt with an
unreadable dict repr. Update profbench_codex_prompt to build the prompt from a
filtered view of task.inputs, excluding large payloads such as the files seed
data (and any similarly bulky keys), while keeping only the concise task
metadata needed for the model. Keep the change localized to
profbench_codex_prompt so the prompt remains short and the workspace-seeded
files are read from disk instead of duplicated in text.

248-252: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Dict lookup can KeyError if EffectiveCodexRuntime grows.

PROFBENCH_SCORE_SOURCE[effective_runtime] (line 298) will raise if the SDK enum adds a member this map doesn't cover. Since the enum lives in a separate file (runtime.py), nothing enforces the two stay in sync.

♻️ Safer lookup with explicit error
-        return target, None, PROFBENCH_SCORE_SOURCE[effective_runtime], effective_runtime
+        try:
+            score_source = PROFBENCH_SCORE_SOURCE[effective_runtime]
+        except KeyError:
+            raise ValueError(f"unmapped Codex runtime for score_source: {effective_runtime!r}") from None
+        return target, None, score_source, effective_runtime

Also applies to: 290-298

🤖 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 `@packages/nemo_evaluator_sdk/examples/profbench/runner.py` around lines 248 -
252, The PROFBENCH score source mapping is not future-proof and can raise a
KeyError when EffectiveCodexRuntime gains new members. Update the lookup used in
runner.py around PROFBENCH_SCORE_SOURCE and the code that reads it in the
profile bench flow to handle unknown runtimes explicitly, ideally by using a
safe mapping access with a clear fallback or by raising a controlled error that
names the unsupported effective_runtime. Keep the mapping and the lookup logic
aligned so the behavior remains stable if runtime.py adds new enum values.
🤖 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
`@packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/runtimes/codex/runtime.py`:
- Around line 341-342: The Docker sandbox path in runtime selection currently
returns DockerSandboxAgentRuntime without handling inputs['files'], so seed
files are silently ignored. Update the Codex runtime flow around the
effective_runtime check to either stage the provided files before creating
DockerSandboxAgentRuntime or explicitly reject non-empty inputs['files'] with a
clear failure. Keep the fix localized to the runtime selection logic and use the
existing DockerSandboxAgentRuntime and EffectiveCodexRuntime.DOCKER_SANDBOX
symbols to wire it in.

---

Nitpick comments:
In `@packages/nemo_evaluator_sdk/examples/profbench/runner.py`:
- Around line 255-263: The profbench_codex_prompt function is interpolating the
entire task.inputs object, which can re-embed seeded file content and inflate
the prompt with an unreadable dict repr. Update profbench_codex_prompt to build
the prompt from a filtered view of task.inputs, excluding large payloads such as
the files seed data (and any similarly bulky keys), while keeping only the
concise task metadata needed for the model. Keep the change localized to
profbench_codex_prompt so the prompt remains short and the workspace-seeded
files are read from disk instead of duplicated in text.
- Around line 248-252: The PROFBENCH score source mapping is not future-proof
and can raise a KeyError when EffectiveCodexRuntime gains new members. Update
the lookup used in runner.py around PROFBENCH_SCORE_SOURCE and the code that
reads it in the profile bench flow to handle unknown runtimes explicitly,
ideally by using a safe mapping access with a clear fallback or by raising a
controlled error that names the unsupported effective_runtime. Keep the mapping
and the lookup logic aligned so the behavior remains stable if runtime.py adds
new enum values.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ea73ed43-b8bf-44ab-af8a-3d3b1ce67d90

📥 Commits

Reviewing files that changed from the base of the PR and between 6e774b3 and cab7311.

📒 Files selected for processing (3)
  • packages/nemo_evaluator_sdk/examples/profbench/runner.py
  • packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/runtimes/codex/runtime.py
  • packages/nemo_evaluator_sdk/tests/agent_eval/test_codex_runtime.py

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 22897/30017 76.3% 61.2%
Integration Tests 13185/28697 46.0% 19.2%

…t runtime

The shipped CodexCliAgentRuntime was ProfBench-shaped: a hardcoded "return only
the final answer text" prompt, no way to seed a workspace, and no success signal
for the standard AgentPhaseSuccessMetric. That made it unusable for general
coding tasks (write docs, write tests, fix a bug) and leaked a benchmark's
opinions into src/.

Generalize the runtime:
- Neutral, injectable prompt: default_codex_prompt presents the task and invites
  workspace edits; both runtimes take a prompt_builder to override framing.
- Workspace seeding: inputs["files"] = {path: contents} is staged into the
  agent's workspace before it runs (paths escaping the workspace are rejected),
  so a task can hand the agent starter code.
- Stamp agent_ok from the exit code so AgentPhaseSuccessMetric works over Codex
  trials.

Keep runtime selection generic and in the SDK (resolve_codex_runtime + the
RuntimeChoice/EffectiveCodexRuntime enums); move only ProfBench's own policy —
its candidate prompt and the candidate+judge score_source labels — into the
ProfBench example, inlined at the point of use in runner.py.

Backward compatible: the plugin's CodexRunnerTarget path is unchanged (the new
constructor args are optional) and now gets the neutral prompt for free.

Signed-off-by: Sandy Chapman <schapman@nvidia.com>
@SandyChapman
SandyChapman force-pushed the codex-runtime-general-coding/schapman branch from cab7311 to 0928ad6 Compare July 3, 2026 17:09

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/runtimes/codex/runtime.py (1)

102-117: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Keep prompt generation inside the per-task failure guard.

Line 102 can raise from an injected prompt_builder; today that escapes asyncio.gather and can abort the run instead of producing one failed trial.

Proposed fix
-        prompt = self._prompt_builder(task)
         prompt_path = evidence_dir / "prompt.txt"
         task_path = evidence_dir / "task.json"
         stdout_path = evidence_dir / "stdout.jsonl"
         stderr_path = evidence_dir / "stderr.txt"
         final_output_path = evidence_dir / "final_output.txt"
 
-        prompt_path.write_text(prompt, encoding="utf-8")
-        task_path.write_text(task.model_dump_json(indent=2), encoding="utf-8")
-
         command = self._command(workspace_dir=workspace_dir, final_output_path=final_output_path)
         process: Any | None = None
         try:
+            prompt = self._prompt_builder(task)
+            prompt_path.write_text(prompt, encoding="utf-8")
+            task_path.write_text(task.model_dump_json(indent=2), encoding="utf-8")
             # Seed inside the guarded block so a bad seed (e.g. a path escaping the workspace) fails
             # just this task rather than aborting the whole run.
             seeded_files = _seed_workspace(workspace_dir, task)
🤖 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
`@packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/runtimes/codex/runtime.py`
around lines 102 - 117, Move the prompt creation for each trial into the
existing per-task try/except guard in the codex runtime so failures from an
injected prompt_builder are treated as task-level errors instead of escaping and
aborting asyncio.gather. In the runtime method that currently assigns prompt
before the guarded block, wrap the self._prompt_builder(task) call together with
prompt_path.write_text and the other per-task setup inside the same guarded
section as _seed_workspace and command execution, so a bad prompt only produces
one failed trial.
🤖 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
`@packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/runtimes/codex/runtime.py`:
- Around line 421-431: The seed file handling in the runtime silently ignores
malformed payloads and coerces non-string file contents, which can hide bad
input and corrupt starter files. In the seeding path that reads
SEED_FILES_INPUT_KEY in the codex runtime, reject any present but non-Mapping
value with an error instead of returning an empty list, and reject any seed
entry whose contents are not already a string rather than converting it. Keep
the existing workspace escape check in place while updating the code around
workspace_root, written, and the per-file loop to fail fast on invalid seed
payloads.

---

Outside diff comments:
In
`@packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/runtimes/codex/runtime.py`:
- Around line 102-117: Move the prompt creation for each trial into the existing
per-task try/except guard in the codex runtime so failures from an injected
prompt_builder are treated as task-level errors instead of escaping and aborting
asyncio.gather. In the runtime method that currently assigns prompt before the
guarded block, wrap the self._prompt_builder(task) call together with
prompt_path.write_text and the other per-task setup inside the same guarded
section as _seed_workspace and command execution, so a bad prompt only produces
one failed trial.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7f6b46c1-7b69-4738-8377-a04faf870488

📥 Commits

Reviewing files that changed from the base of the PR and between cab7311 and 0928ad6.

⛔ Files ignored due to path filters (1)
  • sdk/python/nemo-platform/src/nemo_platform/beta/evaluator/agent_eval/runtimes/codex/runtime.py is excluded by !sdk/**
📒 Files selected for processing (3)
  • packages/nemo_evaluator_sdk/examples/profbench/runner.py
  • packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/runtimes/codex/runtime.py
  • packages/nemo_evaluator_sdk/tests/agent_eval/test_codex_runtime.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/nemo_evaluator_sdk/examples/profbench/runner.py
  • packages/nemo_evaluator_sdk/tests/agent_eval/test_codex_runtime.py

@SandyChapman
SandyChapman enabled auto-merge July 3, 2026 17:24
@SandyChapman
SandyChapman added this pull request to the merge queue Jul 3, 2026
Merged via the queue into main with commit dbad966 Jul 3, 2026
55 checks passed
@SandyChapman
SandyChapman deleted the codex-runtime-general-coding/schapman branch July 3, 2026 17:36
arpitsardhana pushed a commit that referenced this pull request Jul 9, 2026
…t runtime (#562)

The shipped CodexCliAgentRuntime was ProfBench-shaped: a hardcoded "return only
the final answer text" prompt, no way to seed a workspace, and no success signal
for the standard AgentPhaseSuccessMetric. That made it unusable for general
coding tasks (write docs, write tests, fix a bug) and leaked a benchmark's
opinions into src/.

Generalize the runtime:
- Neutral, injectable prompt: default_codex_prompt presents the task and invites
  workspace edits; both runtimes take a prompt_builder to override framing.
- Workspace seeding: inputs["files"] = {path: contents} is staged into the
  agent's workspace before it runs (paths escaping the workspace are rejected),
  so a task can hand the agent starter code.
- Stamp agent_ok from the exit code so AgentPhaseSuccessMetric works over Codex
  trials.

Keep runtime selection generic and in the SDK (resolve_codex_runtime + the
RuntimeChoice/EffectiveCodexRuntime enums); move only ProfBench's own policy —
its candidate prompt and the candidate+judge score_source labels — into the
ProfBench example, inlined at the point of use in runner.py.

Backward compatible: the plugin's CodexRunnerTarget path is unchanged (the new
constructor args are optional) and now gets the neutral prompt for free.

Signed-off-by: Sandy Chapman <schapman@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants