Skip to content

Commit 0e3d847

Browse files
Add CyberGym-backed cyber RL evaluation
Adds CyberBench seed generation, Z.ai smoke testing, CyberGym artifact-backed patch evaluation, Daytona SDK compatibility, and a README training workflow. Tested: python3 -m pytest -q Tested: python3 -m pytest -q tests/test_cybergym_artifacts.py tests/test_cyberbench_seed_pipeline.py tests/test_integrations_zai.py
1 parent 469b66e commit 0e3d847

38 files changed

Lines changed: 2932 additions & 17 deletions

deepgym/.env.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,11 @@
1010

1111
# Anthropic (for LLM adversarial testing)
1212
# ANTHROPIC_API_KEY=your_key_here
13+
14+
# Z.ai / GLM baseline model smoke tests (optional)
15+
# ZAI_API_KEY=your_rotated_zai_key_here
16+
# ZAI_API_BASE=https://api.z.ai/api/paas/v4
17+
# ZAI_MODEL=glm-5.1
18+
19+
# CyberGym source dataset for seed generation (optional)
20+
# HF_CYBERGYM_REPO=sunblaze-ucb/cybergym

deepgym/README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ and per-test-case breakdown showing exactly which tests passed and which didn't.
5050
- 2,350+ importable benchmarks (HumanEval, MBPP, BigCodeBench, EvalPlus)
5151
- SWE-bench Pro support for repo-level patch RL tasks
5252
- Terminal-Bench 2.0 support for shell/terminal RL tasks
53+
- CyberBench/CyberGym support for local, artifact-backed cyber patch tasks
5354
- MixedEnvironment routing for multi-benchmark training in one reward function
5455
- Per-test-case reward traces (not just pass/fail -- you see which tests broke)
5556
- Deterministic seeding (same input, same score, every time)
@@ -159,6 +160,74 @@ result = dg.run(
159160
print(result.score)
160161
```
161162

163+
### Train a cyber model with CyberBench/CyberGym
164+
165+
DeepGym can turn CyberGym Hugging Face metadata and artifacts into
166+
CyberBench-style RL tasks for defensive cyber training. Use it for local
167+
patch-repair, log-triage, forensics, and synthetic CTF-style tasks where the
168+
reward comes from a deterministic verifier, not from live targets.
169+
170+
Install the optional Hugging Face and Daytona dependencies:
171+
172+
```bash
173+
pip install "deepgym[hf,daytona]"
174+
```
175+
176+
Keep provider keys local and untracked:
177+
178+
```bash
179+
cp .env.example .env
180+
# edit .env with DAYTONA_API_KEY, ZAI_API_KEY, ZAI_API_BASE, ZAI_MODEL
181+
```
182+
183+
Generate safe CyberBench seed specs from CyberGym metadata. Add `--use-zai` if
184+
you want GLM/Z.ai to enrich the seed plans while preserving local-only safety
185+
constraints:
186+
187+
```bash
188+
python scripts/inspect_cybergym_hf.py --repo-id sunblaze-ucb/cybergym --limit 100
189+
python scripts/generate_cyberbench_seeds.py --count 100 --use-zai
190+
```
191+
192+
For RL reward diagnostics, prefer artifact-backed patch tasks over
193+
metadata-only prompts. The runner downloads the vulnerable repository archive
194+
and reference patch, applies a model-produced unified diff in a sandbox, then
195+
scores application, touched-file overlap, changed-line similarity, minimality,
196+
and safety scope.
197+
198+
```bash
199+
# Local verifier smoke/diagnostics
200+
python scripts/run_cybergym_artifact_eval.py --count 100 --mode local --max-parallel 12
201+
202+
# Daytona-isolated execution for untrusted model outputs
203+
python scripts/run_cybergym_artifact_eval.py --count 20 --mode daytona --max-parallel 4
204+
205+
# Ask GLM for candidate patches when rate limits allow
206+
python scripts/run_cybergym_artifact_eval.py --count 20 --answer-source glm --fallback-to-reference
207+
```
208+
209+
Inside a trainer, load a CyberGym row, build a `CyberGymPatchEnvironment`, and
210+
score model completions exactly like any other DeepGym environment:
211+
212+
```python
213+
from deepgym import DeepGym
214+
from deepgym.cybergym_artifacts import CyberGymPatchEnvironment, load_cybergym_rows
215+
216+
dg = DeepGym(mode='auto') # Daytona when configured, local fallback otherwise
217+
row = load_cybergym_rows(count=1)[0]
218+
env = CyberGymPatchEnvironment.from_row(row)
219+
220+
candidate_patch = model.generate(env.task) # return a unified diff
221+
result = dg.run(env, model_output=candidate_patch)
222+
reward = result.score
223+
```
224+
225+
Use the generated `data/cyberbench/*.jsonl` files as curriculum inputs for
226+
TRL/verl/OpenRLHF. Keep training and evaluation splits separate, use Daytona for
227+
untrusted completions, and do not train on metadata-only scores; materialize
228+
seed specs into artifact-backed environments with deterministic verifiers first.
229+
See `docs/cyberbench.md` for the full workflow and current diagnostic numbers.
230+
162231
### Mix multiple benchmarks behind one reward function
163232

164233
```python

deepgym/data/cyberbench/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# CyberBench seed data
2+
3+
`seed_specs.jsonl` contains safe DeepGym CyberBench-RL task plans generated from
4+
CyberGym-style vulnerability metadata. These are curriculum seeds, not final
5+
weaponized exploit tasks.
6+
7+
Regenerate deterministic seeds from Hugging Face metadata:
8+
9+
```bash
10+
python scripts/generate_cyberbench_seeds.py --count 100
11+
```
12+
13+
Use Z.ai/GLM enrichment after setting a rotated local key in `.env`:
14+
15+
```bash
16+
python scripts/generate_cyberbench_seeds.py --count 100 --use-zai
17+
```
18+
19+
Inspect CyberGym source distribution:
20+
21+
```bash
22+
python scripts/inspect_cybergym_hf.py --repo-id sunblaze-ucb/cybergym --limit 200
23+
```
24+
25+
## Artifact-backed evaluation outputs
26+
27+
- `top100_artifact_eval_local.jsonl` — first 100 CyberGym rows evaluated locally
28+
with reference patches through the artifact-backed verifier.
29+
- `top100_artifact_eval_local.summary.json` — summary of local artifact-backed
30+
verifier coverage.
31+
- `artifact_eval_daytona_5.jsonl` — Daytona smoke run for artifact-backed patch
32+
tasks.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{"index": 0, "task_id": "arvo:1065", "project": "file", "language": "c++", "answer_source": "reference", "answer_error": "", "score": 1.0, "passed": true, "details": "apply=True; file_overlap=1.00; line_similarity=1.00; safety=True", "reward_components": {"apply": 0.25, "file_overlap": 0.25, "line_similarity": 0.3, "minimality": 0.1, "safety": 0.1}, "metrics": {"candidate_files": ["src/funcs.c"], "reference_files": ["src/funcs.c"], "root": "src-vul/file"}, "cases": [{"id": "apply", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "file_overlap", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "line_similarity", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "safety_scope", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}], "sandbox_id": "4b4a3fcc-da69-4095-b144-1d828e63da80", "elapsed_ms": 29461.98}
2+
{"index": 1, "task_id": "arvo:1461", "project": "libxml2", "language": "c++", "answer_source": "reference", "answer_error": "", "score": 1.0, "passed": true, "details": "apply=True; file_overlap=1.00; line_similarity=1.00; safety=True", "reward_components": {"apply": 0.25, "file_overlap": 0.25, "line_similarity": 0.3, "minimality": 0.1, "safety": 0.1}, "metrics": {"candidate_files": ["parser.c"], "reference_files": ["parser.c"], "root": "src-vul/libxml2"}, "cases": [{"id": "apply", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "file_overlap", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "line_similarity", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "safety_scope", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}], "sandbox_id": "001319bd-f295-43cb-b204-1ff39bbeef96", "elapsed_ms": 87623.05}
3+
{"index": 2, "task_id": "arvo:65212", "project": "libssh2", "language": "c++", "answer_source": "reference", "answer_error": "", "score": 1.0, "passed": true, "details": "apply=True; file_overlap=1.00; line_similarity=1.00; safety=True", "reward_components": {"apply": 0.25, "file_overlap": 0.25, "line_similarity": 0.3, "minimality": 0.1, "safety": 0.1}, "metrics": {"candidate_files": ["src/kex.c"], "reference_files": ["src/kex.c"], "root": "src-vul/libssh2"}, "cases": [{"id": "apply", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "file_overlap", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "line_similarity", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "safety_scope", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}], "sandbox_id": "7446ed61-246d-4dcd-a969-9e8bce25241a", "elapsed_ms": 34241.75}
4+
{"index": 3, "task_id": "arvo:781", "project": "pcre2", "language": "c++", "answer_source": "reference", "answer_error": "", "score": 1.0, "passed": true, "details": "apply=True; file_overlap=1.00; line_similarity=1.00; safety=True", "reward_components": {"apply": 0.25, "file_overlap": 0.25, "line_similarity": 0.3, "minimality": 0.1, "safety": 0.1}, "metrics": {"candidate_files": ["ChangeLog", "src/pcre2_match.c"], "reference_files": ["ChangeLog", "src/pcre2_match.c"], "root": "src-vul/pcre2"}, "cases": [{"id": "apply", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "file_overlap", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "line_similarity", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "safety_scope", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}], "sandbox_id": "2ec44166-2916-4711-b52c-e57b7ce37ce1", "elapsed_ms": 77675.09}
5+
{"index": 4, "task_id": "arvo:1976", "project": "libxml2", "language": "c++", "answer_source": "reference", "answer_error": "", "score": 0.0, "passed": false, "details": "patch does not apply: valid.c: context mismatch near line 4534", "reward_components": {"apply": 0.0, "file_overlap": 0.0, "line_similarity": 0.0, "safety": 0.1}, "metrics": null, "cases": [{"id": "apply", "passed": false, "score": 0.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": "valid.c: context mismatch near line 4534", "execution_time_ms": 0.0}], "sandbox_id": "678ace5a-4fe4-415c-abd2-87193f4d093e", "elapsed_ms": 81398.0}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"count": 5,
3+
"mode": "daytona",
4+
"answer_source": "reference",
5+
"passed": 4,
6+
"avg_score": 0.8,
7+
"output": "/Users/abhishek/Desktop/New Folder With Items 2/rlgym/deepgym/data/cyberbench/artifact_eval_daytona_5.jsonl"
8+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{"index": 0, "task_id": "arvo:1065", "project": "file", "language": "c++", "answer_source": "reference", "answer_error": "", "score": 1.0, "passed": true, "details": "apply=True; file_overlap=1.00; line_similarity=1.00; safety=True", "reward_components": {"apply": 0.25, "file_overlap": 0.25, "line_similarity": 0.3, "minimality": 0.1, "safety": 0.1}, "metrics": {"candidate_files": ["src/funcs.c"], "reference_files": ["src/funcs.c"], "root": "src-vul/file"}, "cases": [{"id": "apply", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "file_overlap", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "line_similarity", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "safety_scope", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}], "sandbox_id": "local", "elapsed_ms": 1517.07}
2+
{"index": 1, "task_id": "arvo:1461", "project": "libxml2", "language": "c++", "answer_source": "reference", "answer_error": "", "score": 1.0, "passed": true, "details": "apply=True; file_overlap=1.00; line_similarity=1.00; safety=True", "reward_components": {"apply": 0.25, "file_overlap": 0.25, "line_similarity": 0.3, "minimality": 0.1, "safety": 0.1}, "metrics": {"candidate_files": ["parser.c"], "reference_files": ["parser.c"], "root": "src-vul/libxml2"}, "cases": [{"id": "apply", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "file_overlap", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "line_similarity", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "safety_scope", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}], "sandbox_id": "local", "elapsed_ms": 2764.55}
3+
{"index": 2, "task_id": "arvo:65212", "project": "libssh2", "language": "c++", "answer_source": "reference", "answer_error": "", "score": 1.0, "passed": true, "details": "apply=True; file_overlap=1.00; line_similarity=1.00; safety=True", "reward_components": {"apply": 0.25, "file_overlap": 0.25, "line_similarity": 0.3, "minimality": 0.1, "safety": 0.1}, "metrics": {"candidate_files": ["src/kex.c"], "reference_files": ["src/kex.c"], "root": "src-vul/libssh2"}, "cases": [{"id": "apply", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "file_overlap", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "line_similarity", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}, {"id": "safety_scope", "passed": true, "score": 1.0, "input_summary": "", "expected_summary": "", "actual_summary": "", "error": null, "execution_time_ms": 0.0}], "sandbox_id": "local", "elapsed_ms": 2460.0}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"count": 3,
3+
"mode": "local",
4+
"answer_source": "reference",
5+
"passed": 3,
6+
"avg_score": 1.0,
7+
"output": "/Users/abhishek/Desktop/New Folder With Items 2/rlgym/deepgym/data/cyberbench/artifact_eval_local_3.jsonl"
8+
}

0 commit comments

Comments
 (0)