Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion .github/workflows/config/.secrets.baseline

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions responses_api_agents/claude_code_agent/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ class ClaudeCodeAgentConfig(BaseResponsesAPIAgentConfig):
anthropic_base_url: Optional[str] = None
max_turns: Optional[int] = 30 # None -> unlimited turns
timeout: int = 300
cwd: Optional[str] = None
system_prompt: Optional[str] = None
allowed_tools: Optional[str] = None
disallowed_tools: Optional[str] = None
Expand Down Expand Up @@ -481,6 +482,7 @@ async def _run_claude_code(
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
env=env,
cwd=self.config.cwd,
)
communication = asyncio.create_task(proc.communicate())
try:
Expand Down
16 changes: 13 additions & 3 deletions responses_api_agents/claude_code_agent/setup_claude_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import logging
import os
import platform
import shutil
import subprocess
import tarfile
Expand All @@ -26,13 +27,22 @@

_CLAUDE_PKG = "@anthropic-ai/claude-code"
_NODE_VERSION = "22.15.0"
_NODE_DIST_URL = f"https://nodejs.org/dist/v{_NODE_VERSION}/node-v{_NODE_VERSION}-linux-x64.tar.xz"
_LOCAL_PREFIX = Path(__file__).parent / ".claude_node"
_NODE_ARCH = {"x86_64": "x64", "aarch64": "arm64", "arm64": "arm64"}.get(platform.machine())
if _NODE_ARCH is None:
raise RuntimeError(f"Unsupported Node.js architecture: {platform.machine()}")
_NODE_DIST_URL = f"https://nodejs.org/dist/v{_NODE_VERSION}/node-v{_NODE_VERSION}-linux-{_NODE_ARCH}.tar.xz"
_LOCAL_PREFIX = Path(__file__).parent / f".claude_node_{_NODE_ARCH}"


def _npm_install(npm_bin: str, version: str | None) -> None:
pkg = f"{_CLAUDE_PKG}@{version}" if version else f"{_CLAUDE_PKG}@latest"
subprocess.run([npm_bin, "install", "-g", pkg], check=True)
npm_cache = _LOCAL_PREFIX / ".npm-cache"
npm_cache.mkdir(parents=True, exist_ok=True)
subprocess.run(
[npm_bin, "install", "-g", pkg],
check=True,
env={**os.environ, "NPM_CONFIG_CACHE": str(npm_cache)},
)


def _install_node_locally() -> Path:
Expand Down
18 changes: 15 additions & 3 deletions responses_api_agents/claude_code_agent/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,11 @@ async def run_claude_code(*args, observation_collector=None, **kwargs):


class TestRunClaudeCode:
def test_wires_command_env_and_cleans_up(self, tmp_path: Path) -> None:
agent = _make_agent(mcp_config="/path/to/mcp.json")
@pytest.mark.parametrize("model_name", ["claude-sonnet-4-6", "nvidia/qwen/qwen3.8-27b", "Qwen/Qwen3.8-27B"])
def test_wires_command_env_and_cleans_up(self, tmp_path: Path, model_name: str) -> None:
agent = _make_agent(
mcp_config="/path/to/mcp.json", model=model_name, anthropic_base_url="http://localhost:8000"
)
captured: dict = {}

class FakeProc:
Expand All @@ -412,6 +415,15 @@ async def communicate(self):

async def fake_exec(*cmd, **kwargs):
env = kwargs["env"]
for key in (
"ANTHROPIC_MODEL",
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
"ANTHROPIC_DEFAULT_SONNET_MODEL",
"ANTHROPIC_DEFAULT_OPUS_MODEL",
"CLAUDE_CODE_SUBAGENT_MODEL",
):
assert env[key] == model_name
assert cmd[cmd.index("--model") + 1] == model_name
config_dir = env["CLAUDE_CONFIG_DIR"]
captured["cmd"] = list(cmd)
captured["config_dir"] = config_dir
Expand All @@ -434,7 +446,7 @@ async def fake_exec(*cmd, **kwargs):
# config dir is removed after the run (no leakage between rollouts)
assert not Path(captured["config_dir"]).exists()
assert output_items == []
assert model == "claude-sonnet-4-6"
assert model == model_name
assert metadata["status"] == "completed"

def test_skills_staged_and_bare_dropped(self, tmp_path: Path) -> None:
Expand Down
49 changes: 49 additions & 0 deletions responses_api_agents/nemorl_env/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# NeMo RL environment

A Gym environment for training models to train models.

1. The model runs in Claude Code to edit a recipe and pinned NeMo RL/Gym sources in a CPU sandbox
(60 minutes, 200 turns, 256K context). Core changes such as loss fn, advantage estimation or
data-processing changes are accepted.
2. A GPU sandbox applies the patch and trains Qwen2.5-1.5B-Instruct with
NeMo RL + NeMo Gym GRPO: 8 prompts × 8 responses, with Gym's math verifier.
3. A fresh, unpatched GPU sandbox serves model with vLLM and runs
`gym eval run` on AIME25 with a 32K context budget, including the prompt.

Reward is AIME25 avg@8: mean accuracy over eight sampled answers per problem
(30 problems, 240 answers; temperature 0.7).

## Setup

Configure `policy_model` and the sandbox connections in `config.yaml`. CPU/GPU
domains and API keys, author image, and registry credentials are declared there
using environment-variable references. The author image must provide Python at
`/agent_deps_mount/bin/python`, uv, and Git.

Generate datasets:

```bash
python responses_api_agents/nemorl_env/prepare.py
gym eval prepare --benchmark aime25
```

`app.py` orchestrates the sandboxes, `author_worker.py` runs Claude Code.
`task_environment/` contains the recipe, training data, runner, and evaluator.
`recipe.yaml` explicitly inherits NeMo RL's full `grpo_math_1B.yaml`, it is an
editable override, not a standalone config. The launcher writes the complete
`/testbed/resolved_grpo.yaml` before training. Data/environment edits are retained,
model, batch size, GPU/time budget, and held-out evaluation remain fixed.
`data/train.jsonl` contains one stable author prompt; `data/example.jsonl` contains
five independently identified copies. Use repeated rollouts for exploration.
`eval_concurrency` in `config.yaml` controls simultaneous AIME25 answers (default 30).

With the author model configured, record real example rollouts with:

```bash
gym env start --config responses_api_agents/nemorl_env/config.yaml --model-type vllm_model
gym eval run --no-serve --agent nemorl_env --input responses_api_agents/nemorl_env/data/example.jsonl --output responses_api_agents/nemorl_env/data/example_rollouts.jsonl
```

Math data: the first 512 training rows of NVIDIA's
[OpenMathInstruct-2](https://huggingface.co/datasets/nvidia/OpenMathInstruct-2)
(CC-BY-4.0)
Empty file.
Loading
Loading