Skip to content
Closed
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
9 changes: 7 additions & 2 deletions .claude/skills/add-benchmark/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ description: >
training environment, or resources server into NeMo-Gym. Also use when wrapping
an existing 3rd-party benchmark library. Covers the full workflow: data preparation,
resources server implementation, agent wiring, YAML config, testing, and reward
profiling (baselining). Triggered by: "add benchmark", "new resources server",
"integrate benchmark", "wrap benchmark", "add training environment", "add eval".
profiling (baselining).
license: Apache-2.0
compatibility: Requires Python 3.12+, uv, git. NeMo Gym must be installed.
metadata:
author: nvidia-nemo-gym
version: "1.0"
allowed-tools: Bash(python:*) Bash(ng_*) Bash(git:*) Bash(pre-commit:*) Read Write Edit Grep Glob
---

# Add Benchmark to NeMo-Gym
Expand Down
51 changes: 51 additions & 0 deletions .claude/skills/add-benchmark/evals/evals.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"skill_name": "add-benchmark",
"evals": [
{
"id": 1,
"prompt": "Add a new math benchmark to NeMo Gym. The benchmark tests algebra word problems. The verify method should extract the final numerical answer from the model's response and compare it to the expected answer in verifier_metadata.",
"expected_output": "A complete resources server under resources_servers/math_algebra/ with app.py implementing verify(), configs/math_algebra.yaml with proper dataset wiring, data/example.jsonl with 5 entries, tests/test_app.py with >= 95% coverage, and requirements.txt.",
"assertions": [
"resources_servers/math_algebra/app.py exists and contains a class extending SimpleResourcesServer",
"The verify() method extracts a numerical answer and compares to verifier_metadata",
"Think-block stripping is present before answer extraction",
"data/example.jsonl contains exactly 5 lines of valid JSON",
"Each example line has responses_create_params.input and verifier_metadata",
"configs/math_algebra.yaml defines both resources server and agent instances",
"tests/test_app.py contains tests for verify pass, verify fail, and edge cases",
"requirements.txt contains '-e nemo-gym[dev] @ ../../'",
"The verify method returns reward as 0.0 or 1.0 only"
]
},
{
"id": 2,
"prompt": "Wrap the HumanEval benchmark library as an external benchmark in NeMo Gym. The library has its own execution and scoring logic.",
"expected_output": "A custom agent server under responses_api_agents/ that wraps the HumanEval library, with pre/post processing between Gym schema and library format, and a YAML config wiring it together.",
"assertions": [
"An agent server directory exists under responses_api_agents/",
"The agent's run() endpoint is async",
"Pre-processing converts Gym schema to library input format",
"Post-processing converts library output to BaseVerifyResponse with reward field",
"The agent uses asyncio.Semaphore for concurrency control",
"httpx is not imported anywhere — aiohttp adapter is used if the library needs HTTP",
"YAML config wires the agent to a model server and resources server",
"requirements.txt includes the external library dependency"
]
},
{
"id": 3,
"prompt": "Add a code generation benchmark that compiles and runs C++ code. The verify method should compile the model's code, run it against test cases, and compare stdout to expected output.",
"expected_output": "A resources server with subprocess execution via Ray, auto-install for g++ if needed, semaphore-bounded compilation, and proper error handling for compilation failures and runtime errors.",
"assertions": [
"app.py uses asyncio.Semaphore to bound concurrent subprocess calls",
"Subprocess output is decoded with errors='replace'",
"A setup module with ensure_gpp() or similar auto-install function exists",
"model_post_init calls the auto-install function",
"tests/conftest.py has a pytest_configure hook that calls the auto-install",
"Tests use pytest.mark.skipif for the external tool",
"Compilation and runtime errors return reward 0.0, not exceptions",
"The verify method handles empty or unparseable model output gracefully"
]
}
]
}
61 changes: 61 additions & 0 deletions .claude/skills/chains.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
chains:
new-benchmark:
name: New Benchmark
description: End-to-end benchmark creation — scaffold, implement, data, config, baseline, review
steps:
- skill: add-benchmark
purpose: Scaffold server, implement verify(), write tests
- skill: gym-data
purpose: Prepare and register datasets
- skill: gym-config
purpose: Validate YAML configuration
- skill: gym-profile
purpose: Baseline against multiple models
- skill: gym-review
purpose: Final review before PR

validate:
name: Validate Benchmark
description: Check an existing benchmark is correctly configured and producing valid results
steps:
- skill: gym-config
purpose: Verify config is well-formed
- skill: gym-data
purpose: Validate datasets with ng_prepare_data
- skill: gym-profile
purpose: Run rollouts and analyze results

diagnose:
name: Diagnose Issues
description: Debug a failing benchmark — identify root cause and anti-patterns
steps:
- skill: gym-debug
purpose: Identify the failure point
- skill: gym-review
purpose: Check code for anti-patterns that may cause the failure

external-integration:
name: External Benchmark Integration
description: Wrap a 3rd-party benchmark library into NeMo Gym
steps:
- skill: gym-scaffold-agent
purpose: Create agent wrapper for external library
- skill: gym-data
purpose: Convert and register datasets
- skill: gym-config
purpose: Wire configuration
- skill: gym-profile
purpose: Compare Gym scores against published numbers
- skill: gym-review
purpose: Check for httpx, concurrency, and propagation issues

pre-merge:
name: Pre-Merge Check
description: Review and validate before merging a benchmark PR
steps:
- skill: gym-review
purpose: Check for anti-patterns and correctness issues
- skill: gym-config
purpose: Validate configuration
- skill: gym-data
purpose: Validate datasets
207 changes: 207 additions & 0 deletions .claude/skills/gym-config/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
---
name: gym-config
description: >
Compose and validate Hydra YAML configurations for NeMo Gym. Use when setting up
server configs, wiring agent-to-server references, configuring model endpoints,
setting up multi-environment training, or debugging config composition errors.
Covers Hydra/OmegaConf patterns, env.yaml, and ng_dump_config validation.
license: Apache-2.0
compatibility: Requires Python 3.12+ with NeMo Gym installed.
metadata:
author: nvidia-nemo-gym
version: "1.0"
allowed-tools: Bash(ng_*) Read Write Edit Grep Glob
---

# NeMo Gym Configuration

## Config anatomy

A NeMo Gym config defines server instances as top-level keys, each mapping to a server type + subdirectory:

```yaml
my_math_server: # Instance name (arbitrary, must be unique)
resources_servers: # Server type directory
math_benchmark: # Server subdirectory name
entrypoint: app.py
domain: math
datasets:
- name: example
type: example
jsonl_fpath: resources_servers/math_benchmark/data/example.jsonl
# ... server-specific config fields
```

Agents reference their dependencies by instance name:

```yaml
my_math_agent:
responses_api_agents:
simple_agent:
entrypoint: app.py
resources_server:
type: resources_servers
name: my_math_server # Must match the instance name above
model_server:
type: responses_api_models
name: policy_model # Must match a model server instance
```

## Step 1: Define server instances

For each component, create a top-level key with:
- A unique instance name
- The server type directory (`resources_servers`, `responses_api_models`, `responses_api_agents`)
- The server subdirectory name
- Server-specific configuration fields

## Step 2: Wire references

Verify that every `name` reference in agent configs points to an actual instance:
- `resources_server.name` must match a resources server instance
- `model_server.name` must match a model server instance
- If using multiple agents/servers, each cross-reference must be exact

## Step 3: Configure model endpoints

Model endpoint config goes in `env.yaml` at project root:

```yaml
policy_base_url: http://localhost:8000/v1
policy_api_key: your-key
policy_model_name: your-model
```

For multiple models (e.g. policy + reward model), add separate entries:
```yaml
reward_base_url: http://localhost:8001/v1
reward_api_key: your-key
reward_model_name: your-reward-model
```

## Step 4: Configure datasets

See the [gym-data](../gym-data/SKILL.md) skill for full dataset preparation. In config:

```yaml
datasets:
- name: train_dataset
type: train
jsonl_fpath: resources_servers/my_benchmark/data/train.jsonl
gitlab_identifier:
dataset_name: my_benchmark
version: 0.0.1
artifact_fpath: train.jsonl
license: MIT
- name: example
type: example
jsonl_fpath: resources_servers/my_benchmark/data/example.jsonl
```

Rules:
- `train` and `validation` types need both `jsonl_fpath` and `gitlab_identifier`
- `example` type only needs `jsonl_fpath` (committed to git)
- `license` required for `train` and `validation`

## Step 5: Multi-environment training

To run multiple environments simultaneously, compose multiple config files:

```bash
ng_run "+config_paths=[
resources_servers/math/configs/math.yaml,
resources_servers/code_gen/configs/code_gen.yaml,
responses_api_models/vllm_model/configs/vllm_model.yaml
]"
```

Each server gets its own instance name and port. Agents can reference different resources servers.

## Step 6: Validate

Always validate the merged config before running:

```bash
ng_dump_config "+config_paths=[resources_servers/my_benchmark/configs/my_benchmark.yaml,responses_api_models/openai_model/configs/openai_model.yaml]"
```

Check:
- All instance names resolve
- No OmegaConf interpolation errors (`${var}` references)
- Dataset paths exist (for example data) or gitlab_identifier is set (for train/validation)
- Port assignments don't conflict
- `verified: false` is present for new servers (pre-commit hook adds this)

## Server-specific config fields

Beyond the base fields documented in CLAUDE.md, individual servers define custom config fields. When configuring a server, read its `app.py` Config class to discover these. Common patterns:

### Concurrency and timeouts
Most servers that run subprocesses or external calls define:
```yaml
num_processes: 8 # asyncio.Semaphore value for parallel execution
max_concurrency: 32 # Alternative name for semaphore bound
unit_test_timeout_secs: 10 # Timeout for subprocess execution
max_execution_time: 10 # Alternative timeout field name
compilation_timeout: 30.0 # Compilation-specific timeout
sql_execution_timeout_s: 30.0 # SQL query timeout
```
These are NOT inherited from any base class — each server defines its own. Check the server's Config class.

### LLM-as-Judge configs
Servers using LLM judges (e.g., `equivalence_llm_judge`, `jailbreak_detection`) require a second model server reference:
```yaml
judge_model_server:
type: responses_api_models
name: judge_model # Must match a model server instance
judge_responses_create_params:
input: []
temperature: 0.0
max_output_tokens: 1024
judge_endpoint_max_concurrency: 64 # Rate-limit judge API calls
```
This means you need TWO model server instances in your config when using judge-based verification.

### Partial reward configs
Several servers support non-binary rewards for nuanced training signals:
```yaml
# jailbreak_detection
reward_if_safe: 1.0
reward_if_unsafe: 0.0
reward_if_unclear: 0.0
reward_if_quality_high: 1.0
reward_if_quality_low: 0.3 # Partial credit

# equivalence_llm_judge
reward_if_swap_fails: 0.0 # Can be -1.0 for penalty
reward_if_full_generation_succeeds: 0.5 # Partial credit on fallback
check_twice_swap: true # Positional bias detection
```

### External service connections
Some servers connect to external services:
```yaml
sandbox_host: ${oc.env:SANDBOX_HOST,localhost} # OmegaConf env var injection
sandbox_port: ${oc.env:SANDBOX_PORT,8080}
```
The `${oc.env:VAR_NAME,default}` pattern injects environment variables at config resolution time. This is the ONE place env vars are acceptable (for infra endpoints that vary per deployment).

### Agent-specific fields
```yaml
max_steps: 1 # Override default conversation turns
max_correction_turns: 3 # For proof_refinement_agent
include_all_attempts: true # Record all attempts in output
```

## Common mistakes

| Mistake | Fix |
|---------|-----|
| Instance name mismatch between agent and server | Use exact same string in both places |
| Missing `env.yaml` | Create it at project root with model endpoint config |
| YAML indentation in nested `gitlab_identifier` | Use 4-space indent consistently |
| Hydra `+` prefix confusion | `+key=value` adds new keys, `key=value` overrides existing |
| Config path relative vs absolute | Paths in `config_paths` are relative to project root |
| Missing judge model server for judge-based benchmarks | Need TWO model server instances — one for policy, one for judge |
| Using bare env vars instead of `${oc.env:VAR,default}` | OmegaConf interpolation is the approved pattern for deployment-specific values |
| Forgetting `max_steps` in agent config | Defaults vary by agent — set explicitly for multi-turn |
41 changes: 41 additions & 0 deletions .claude/skills/gym-config/evals/evals.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"skill_name": "gym-config",
"evals": [
{
"id": 1,
"prompt": "Set up a YAML config for a new math benchmark that uses LLM-as-judge verification. I need a policy model for generation and a separate judge model for grading answers.",
"expected_output": "A config with THREE server instances: resources server, policy model server, AND judge model server. The resources server config should include judge-specific fields like judge_model_server reference, judge_responses_create_params, and judge_endpoint_max_concurrency.",
"assertions": [
"The config defines two separate model server instances (policy and judge)",
"The resources server has a judge_model_server reference with type and name matching the judge model instance",
"judge_responses_create_params is present with temperature and max_output_tokens",
"judge_endpoint_max_concurrency is set to bound concurrent judge calls",
"The agent references the policy model, not the judge model",
"env.yaml guidance mentions TWO sets of endpoint configs (policy + judge)"
]
},
{
"id": 2,
"prompt": "I need to deploy my benchmark on a SLURM cluster where the sandbox host varies per node. How do I inject the sandbox host as an environment variable into the Gym config?",
"expected_output": "Config using OmegaConf env var injection syntax ${oc.env:VAR_NAME,default} for deployment-specific values, with explanation of when this pattern is acceptable vs passing config through YAML.",
"assertions": [
"The ${oc.env:VAR_NAME,default} syntax is used for the sandbox host",
"A default value is provided in the interpolation",
"The response explains this is the approved pattern for deployment-specific infra values",
"The response distinguishes this from general config (which must go through YAML, not env vars)"
]
},
{
"id": 3,
"prompt": "Configure a jailbreak detection benchmark with combined reward. I want safety checking AND quality evaluation, with partial credit for safe-but-low-quality responses.",
"expected_output": "Config with use_combined_reward: true, separate reward values for safety and quality tiers, and explanation of the reward formula (safety_reward * quality_reward).",
"assertions": [
"use_combined_reward is set to true",
"Separate reward fields exist for safety (reward_if_safe, reward_if_unsafe) and quality (reward_if_quality_high, reward_if_quality_low)",
"The partial credit value for low quality is between 0 and 1 (not 0.0 or 1.0)",
"The response explains the combined reward formula (multiplication)",
"A judge model server is configured for the quality evaluation stage"
]
}
]
}
Loading
Loading