Repository navigation
Conversation
Add examples/cobol-cpt-grpo, a production-shaped pipeline that adapts Qwen2.5-Coder-32B to COBOL on 2 x 8 H200 with RDMA: - 01-collect, 02-data-prep, 03-benchmarks and 05-rl-prompts run as CPU Jobs. Source repositories default to permissive licenses (SOURCE_LICENSES=all opts in to the rest), and gated Hugging Face datasets run only when HF_TOKEN is provided through a Secret. - 04-cpt runs DeepSpeed ZeRO-3 continued pretraining under Ray Train. - 06-grpo runs the trl vllm-serve server and the GRPO trainer in one RayJob with NCCL weight sync and a GnuCOBOL compile reward. - 07-eval scores five benchmarks with vLLM and format-aware cobc checks. The README documents data licensing, placeholders, stage commands, and the reference results (64/220 compile). Serving is not included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
| driver.build_trainer( | ||
| num_workers=2, | ||
| use_gpu=False, | ||
| cpus_per_worker=1, | ||
| storage_path=str(tmp_path / "ray"), | ||
| ).fit() |
| ) | ||
| import driver | ||
|
|
||
| trainer = driver.build_trainer() |
Kevin Cho (chokevin)
left a comment
There was a problem hiding this comment.
Blocking issues found in the closeout review. Please address these before merging.
| python-discovery==1.6.1 | ||
| smart_open==8.0.2 | ||
| tensorboardX==2.6.5 | ||
| virtualenv==21.14.6 |
There was a problem hiding this comment.
This version is affected by GHSA-vxj4-q4g9-crj7 and is currently failing the dependency-review check. Please update virtualenv to 21.14.7 or newer before merging.
There was a problem hiding this comment.
Bumped to 21.14.7 in 9f4264a. A second advisory, GHSA-c978-59m8-c77v (published 2026-10-21, first patched in 21.14.9), then failed dependency review again, so 7fecbf0 pins virtualenv==21.14.9. Its requirements are identical to 21.14.7's, so the other pins are unchanged.
| if "$@"; then | ||
| log "${name}: OK" | ||
| else | ||
| log "${name}: FAILED (continuing)" |
There was a problem hiding this comment.
This makes the stage succeed even when every required source fails, and the same pattern appears in collect-rl-data.sh. Please track failed required steps and exit nonzero, and fail if collection produces no usable records; otherwise a partial or empty corpus can be published as a successful stage.
There was a problem hiding this comment.
Fixed in 9f4264a. Both collect-cobol-data.sh and collect-rl-data.sh now record every required step that fails and, at the end, exit 1 with the list of failed steps. They also exit 1 if collection produced no COBOL source files.
This surfaced a real problem on the rerun: the X-COBOL and NIST download URLs had gone stale, and the stage failed instead of publishing a partial corpus. 037e258 moves both to current URLs with sha256 checks.
| proc_div_header = " PROCEDURE DIVISION" | ||
| proc_prefix = proc_text[: para["start"]] | ||
| non_proc = ( | ||
| text.split("PROCEDURE DIVISION")[0] |
There was a problem hiding this comment.
The membership check is case-insensitive, but this split is case-sensitive. For inputs containing Procedure Division or other casing, the split returns the entire program and leaks the reference procedure body into the prompt. Please find the boundary case-insensitively and slice the original text; the copybook builder has the same issue.
There was a problem hiding this comment.
Fixed in 9f4264a. text_before_procedure_division() finds the header with a case-insensitive regex and slices the original text. Both the paragraph-completion builder and the copybook builder use it. 03-benchmarks/test_build_benchmarks.py covers PROCEDURE DIVISION, Procedure Division, and procedure division, and checks that the reference body never appears in the prompt.
| ) | ||
| print(" vLLM model loaded.", flush=True) | ||
|
|
||
| para_tasks = load_jsonl(os.path.join(EVAL_DIR, "paragraph_completion.jsonl")) |
There was a problem hiding this comment.
Stage 03 also generates copybook_usage.jsonl, but this evaluator never loads or scores it. Please either evaluate that suite here or remove it from the generated benchmark set and the claim that the pipeline evaluates all five suites.
There was a problem hiding this comment.
Fixed in 9f4264a. copybook_usage is now a required suite: it is loaded, checked in preflight, and scored with BLEU and edit distance. The README lists what each of the five suites reports. The rerun scored 76 copybook tasks.
Related follow-up in cdb4c8b: 2 copybook prompts and 1 paragraph prompt from the new corpus are longer than the 32768-token context. The eval now sets those aside before generation, lists them under excluded_over_context in eval-summary.json, and fails if any suite is left empty.
| if not line: | ||
| continue | ||
| rec = json.loads(line) | ||
| prompt, body, body_len = extract_prompt(rec.get("text", "")) |
There was a problem hiding this comment.
Stage 05 records whether each program requires free or fixed-format compilation, but that field is dropped here and preflight later always uses cobc -free. This silently removes every fixed-only training example. Please preserve compile_fmt through loading and use it during preflight/reward compilation, or normalize the records before writing them.
There was a problem hiding this comment.
Fixed in 9f4264a. Prompt loading keeps compile_fmt from each stage 05 record. Preflight compiles each prompt in its own format, and reward_fn receives compile_fmt as a dataset column, so each completion is compiled with -free or -fixed to match its prompt. test_reward.py covers preflight, reward compilation, and records without the column, which default to free.
On the rerun, preflight kept 1,762 prompts: 918 free and 844 fixed. Previously every fixed-only record was dropped.
|
|
||
| def load_jsonl(path): | ||
| records = [] | ||
| if not os.path.isfile(path): |
There was a problem hiding this comment.
Missing benchmark artifacts are treated as empty suites, so evaluation can write a successful summary without meaningful results. The compiler path similarly counts a missing cobc as skipped samples. Please preflight the required nonempty benchmark files and cobc --version, and fail the task when either contract is unmet.
There was a problem hiding this comment.
Fixed in 9f4264a. Before loading the model, main() runs cobc --version and checks that all five suite files exist and are not empty. If either check fails, it fails with the list of problems. If cobc disappears during the run, compilation raises instead of counting the sample as skipped. test_run_eval.py covers these cases, including that the model is never loaded when preflight fails.
cdb4c8b adds one more preflight check: generation prompts that cannot fit in max_model_len with their output budget are set aside and recorded in eval-summary.json. The run fails if a suite is left empty. That's 3 of 448 tasks on the new corpus.
Pengfei Ni (feiskyer)
left a comment
There was a problem hiding this comment.
The GRPO stage can reuse held-out evaluation inputs and stop only one distributed rank.
| subprocess.run(["cobc", "--version"], capture_output=True, check=True) | ||
| log("cobc available") | ||
|
|
||
| file_paths = discover_files() |
There was a problem hiding this comment.
Stage 03 builds evaluation tasks from val.jsonl, but this stage rereads the complete raw source index without excluding that partition. A held-out program passing the compile/body/token filters can therefore supply a GRPO training prompt, including the procedure prefix used by the evaluation tasks. Build RL prompts from the training partition or exclude validation source identities/content hashes, and assert the sets are disjoint.
There was a problem hiding this comment.
Fixed in 9f4264a and 0677658. Stage 05 now reads val.jsonl from 02-data-prep and drops any source program that shares 8 consecutive normalized lines with a validation program. Normalization ignores case, whitespace, and blank lines. The check runs on both the raw text and the text after EXEC/COPY stripping. I used content windows rather than source identities because the raw index contains renamed copies of the same program under different paths. After filtering, assert_disjoint_from_val fails the stage if any emitted prompt still overlaps.
On the rerun with a fresh corpus, 1,285 candidate programs were excluded and the 2,057 emitted prompts passed the assertion. The first attempt hit the assertion on 4 prompts that only overlapped after COPY stripping, which is why 0677658 also checks the stripped text. The README marks the earlier GRPO results as partly contaminated.
| "ALARM: Mean verb count < 3 for 20 steps. Reward hacking. Stopping.", | ||
| flush=True, | ||
| ) | ||
| control.should_training_stop = True |
There was a problem hiding this comment.
Only rank 0 installs this callback. When either alarm fires, the pinned Transformers/TRL loop reads should_training_stop locally without broadcasting it: rank 0 leaves training while the other workers enter their next distributed operation, hanging or failing the multi-GPU run. Synchronize the alarm decision on every rank before any rank leaves the loop; keep metrics publishing rank-zero-only.
There was a problem hiding this comment.
Fixed in 9f4264a. TauGRPOMetricsCallback is installed on every rank, and each rank evaluates the alarms. The stop decision is all-reduced with MAX, so if any rank raises an alarm, every rank sets should_training_stop at the same step. Only rank 0 publishes metrics.
06-grpo/test_stop_sync.py starts two gloo ranks, raises the KL alarm only on rank 1, and checks that both ranks stop at the same step. The 2-node GRPO rerun completed all 162 steps.
- 05-rl-prompts: exclude programs that share an 8-line window with the validation split and fail if any overlap remains. - 06-grpo: keep each prompt's compile_fmt and compile with the matching -free or -fixed flag in preflight and the reward. Check the KL and reward-hacking alarms on every rank and combine the stop decision. - 03-benchmarks: find PROCEDURE DIVISION case-insensitively so mixed-case programs no longer leak the procedure body into paragraph and copybook prompts. - 07-eval: score copybook_usage with BLEU and edit distance. Fail before loading the model if a suite is missing or empty or cobc is unusable, and fail if cobc disappears mid-run. - 01-collect: exit nonzero when a required step or clone fails or no COBOL files are collected. - Bump virtualenv to 21.14.7 (GHSA-vxj4-q4g9-crj7), drop trust_remote_code, bind test sockets to the node IP, and replace empty excepts with logged handling. - README: describe the changes and mark the results as coming from the earlier pipeline, with the overlap and the dropped fixed-format prompts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
…environment collect-gated.py already reads DATA_ROOT; the two shell scripts hardcoded it. Both now default to /data/datasets/cobol, derive RAW from DATA_ROOT, and export it for the embedded Python steps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
Zenodo removed the files-archive endpoint for the X-COBOL record and the SourceForge nist_2013-10-10.tar.gz file no longer exists, so both required collection steps failed. Download COBOL_Files.zip and newcob.val.tar.gz directly, verify their sha256 digests, and keep the existing extracted layout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
…tion The validation exclusion checked raw source text, but prompts are built after EXEC blocks and COPY statements are stripped. Removing those lines can join shared lines into a window that matches the validation split, which the final disjointness check then rejects. Check the stripped text as well. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
GnuCOBOL 3.1.2 in the training image rejects -fmissing-period=ok with an "unrecognized option" message that has no error: line, and the copybook fallback counted that as a copybook-only failure, so every program passed without being compiled. Drop the unsupported flag, require at least one missing-copybook error before treating a failure as copybook-only, and check that cobc compiles a known-good program in both formats before processing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
Stage 03 keeps whole programs as prompt context, and a few programs in a larger corpus produce prompts longer than the 32768-token context. vLLM rejects the batch and the eval fails. Tokenize each generation prompt before loading the model, set aside those that cannot fit with their output budget, record them in eval-summary.json, and fail if a suite is left empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
virtualenv 21.14.7 is affected by GHSA-c978-59m8-c77v (high), which fails the dependency-review check. 21.14.9 is the first patched release and has the same dependency requirements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
…eline Replace the results from the earlier, contaminated run with a base, CPT, and GRPO comparison scored on the same benchmarks built from the permissive corpus. Describe the compile score total as corpus dependent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
| results = [] | ||
|
|
||
| for task, gen_text in zip(tasks, generated_texts): | ||
| full_text = task["prompt"] + gen_text |
There was a problem hiding this comment.
For a multi-paragraph program whose selected paragraph calls a later paragraph, stage 03 discards that later definition. This compiles only the prefix plus generated paragraph, so even the exact reference completion fails with an unresolved PERFORM target. The paragraph and overall compile scores then include failures caused by missing benchmark context. Preserve the untouched suffix in each task and append it for compilation while scoring only the generated paragraph; add a regression where the reference completion must compile.
There was a problem hiding this comment.
Thanks, confirmed. Paragraph tasks dropped every later paragraph, so a reference that PERFORMs one couldn't compile; stop_at was recorded but unused. Fixed in 85bd4bf:
- Stage 03 now stores the rest of the procedure as
suffix. - The eval cuts each paragraph completion at the next paragraph header and compiles prompt + completion + suffix.
Each compile suite now also reports how many tasks compile with the reference (reference_compiled) and with an empty generation (empty_compiled, 114a7a0). The paragraph reference count went from 16/61 to 30/61. An empty generation compiles on 31/61 paragraph, 34/50 program and 68/100 structural tasks, more than any model, so the README results (f64b18b) now lead with BLEU, edit distance and the structural checks.
…cedure Stage 03 dropped every paragraph after the masked one, so a completion that performs a later paragraph could not compile even when it matched the reference. Keep the remaining procedure text as a suffix and append it when compiling. Cut paragraph completions at the next paragraph header, and report how many tasks compile with the reference in place of the generation. Decode compiler output with replacement so non-UTF-8 bytes in cobc errors do not abort the eval, require the current benchmark fields before loading the model, and run the stage 07 test files separately. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
cobc -fsyntax-only accepts an empty paragraph or an empty PROCEDURE DIVISION, so an empty generation compiles on many tasks. Report that floor next to each compile score and the reference ceiling. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
…on compile counts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
Kevin Cho (chokevin)
left a comment
There was a problem hiding this comment.
Please remove examples/cobol-cpt-grpo/03-benchmarks/__pycache__/build_benchmarks.cpython-312.pyc before merging. This is generated Python bytecode and should not be committed; keep the source and tests, but remove the cache artifact.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: June Liu <juneliu@microsoft.com>
|
Kevin Cho (@chokevin) Removed the bytecode file in 2300002, and |
Kevin Cho (chokevin)
left a comment
There was a problem hiding this comment.
Verified the requested cleanup: the committed Python bytecode has been removed, and .gitignore now excludes pycache/ and *.pyc. The only changes since the reviewed revision are this cleanup and the ignore rules. No further blocking requests. Reviewed with GitHub Copilot assistance.
Summary
Adds
examples/cobol-cpt-grpo, aproduction-shapedexample that adaptsQwen/Qwen2.5-Coder-32Bto COBOL on 2 x 8 H200 with RDMA. It has seven stages, each with its owntau.yaml:01-collect,02-data-prep,03-benchmarks,05-rl-prompts: CPU Jobs that collect the corpus, prepare it, build the benchmarks and the RL prompts.04-cpt: continued pretraining as a RayJob. A Ray TrainTorchTrainerwith 16 workers runs HF Trainer with DeepSpeed ZeRO-3.06-grpo: GRPO in one RayJob. Atrl vllm-serveactor (TP=8) runs on one worker and aTorchTrainerwith 8 workers on the other. The reward is a GnuCOBOL compile reward plus a structural reward.07-eval: a RayJob on one 8-GPU worker. It runs five suites with vLLM and compiles every generated program withcobc(free format, then fixed format).Data collection defaults to
SOURCE_LICENSES=permissive, which takes only MIT, Apache-2.0, BSD, CC-BY-4.0 and US-government repositories. Copyleft or unlicensed sources needSOURCE_LICENSES=all. Gated Hugging Face datasets are opt-in through anhf-tokenSecret. The README lists every source and its license.The training image, workspace, PVC, output root and topology are placeholders, listed in the README's "Substitute placeholders" table. No image is published. The
Dockerfileand pinnedimage-requirements.txtare included so users can build their own.The example is also listed in
examples/README.md.Related issue
None.
Validation
The pipeline was run end to end on 2 x 8 H200 with the same code, launched with
tau run. Results are in the README:07-evalRayJob reproduces every metric exactly.The real-cluster e2e suite was not run.
Compatibility and operational impact
None. This adds a new example folder and one index row; no APIs, CLI, charts or CRDs change.
The CPU stages use
python:3.11.17-bookwormand the training image builds fromvllm/vllm-openai:v0.28.0, both from Docker Hub and pinned by digest. The README documents these as exceptions to the MCR base-image preference.Checklist
🤖 Generated with Claude Code