refactor: update training scripts and documentation for SFT and RLVR processes
Browse filesRevised training scripts to replace GRPO with RLVR as the default training path, enhancing the workflow for SFT and RLVR tasks. Updated README.md and runbook to reflect changes in command usage, including new parameters for data preparation and training. Improved clarity on the training process and added details for using the new `run_data_pipeline.py` script. Adjusted example commands and parameters to ensure consistency and better user guidance.
- README.md +3 -4
- data/README.md +17 -0
- data/processed/e1_m1_clean.jsonl +0 -0
- data/processed/h1_seed_clean.jsonl +0 -0
- data/processed/verified/e1_m1_all_records.jsonl +0 -0
- data/processed/verified/e1_m1_clean.jsonl +0 -0
- data/processed/verified/e1_m1_rejected.jsonl +0 -0
- data/processed/verified/h1_seed_all_records.jsonl +0 -0
- data/processed/verified/h1_seed_clean.jsonl +0 -0
- data/processed/verified/h1_seed_rejected.jsonl +0 -0
- data/raw/h1_seed.jsonl +0 -0
- data/raw/imported/e1_m1_examples.jsonl +0 -0
- data/raw/synthetic/h1_seed.jsonl +24 -0
- data/sft/e1_m1_h1_examples.jsonl +0 -0
- data/sft/merged/e1_m1_h1_examples.jsonl +0 -0
- docs/lightning_hf_runbook.md +54 -16
- env_artifacts/README.md +13 -5
- env_artifacts/setup_lightning_h100.sh +2 -2
- results/data_pipeline/e1_m1_summary.json +21 -0
- results/data_pipeline/h1_seed_summary.json +18 -0
- scripts/_trajectory_utils.py +100 -7
- scripts/generate_sft_data.py +10 -3
- scripts/prepare_sft_data.py +52 -2
- scripts/run_data_pipeline.py +183 -0
- scripts/verify_examples.py +34 -2
- training/train_grpo.py +125 -33
- training/train_rlvr.py +43 -9
README.md
CHANGED
|
@@ -70,8 +70,7 @@ uv run python inference.py --tasks e1_launch_readiness --max-steps 25 --swd-trac
|
|
| 70 |
Replay generated examples against the current environment before training:
|
| 71 |
|
| 72 |
```powershell
|
| 73 |
-
uv run python scripts/
|
| 74 |
-
uv run python scripts/prepare_sft_data.py
|
| 75 |
```
|
| 76 |
|
| 77 |
`prepare_sft_data.py` defaults to merging `data/processed/e1_m1_clean.jsonl` and `data/processed/h1_seed_clean.jsonl` into `data/sft/e1_m1_h1_examples.jsonl`. To build E/M only, pass e.g. `--input data/processed/e1_m1_clean.jsonl --output data/sft/e1_m1_examples.jsonl`.
|
|
@@ -90,10 +89,10 @@ Training scripts use **Unsloth** and **Hugging Face TRL** and are intended for a
|
|
| 90 |
```bash
|
| 91 |
pip install -e ".[training]"
|
| 92 |
python training/train_sft.py --model Qwen/Qwen2.5-7B-Instruct --data data/sft/e1_m1_h1_examples.jsonl --output outputs/sft_adapter --max-steps 30
|
| 93 |
-
python training/
|
| 94 |
```
|
| 95 |
|
| 96 |
-
|
| 97 |
|
| 98 |
For a judge-rerunnable notebook, use [`notebooks/corp_env_trl_unsloth_training.ipynb`](notebooks/corp_env_trl_unsloth_training.ipynb).
|
| 99 |
|
|
|
|
| 70 |
Replay generated examples against the current environment before training:
|
| 71 |
|
| 72 |
```powershell
|
| 73 |
+
uv run python scripts/run_data_pipeline.py --write-legacy-copies
|
|
|
|
| 74 |
```
|
| 75 |
|
| 76 |
`prepare_sft_data.py` defaults to merging `data/processed/e1_m1_clean.jsonl` and `data/processed/h1_seed_clean.jsonl` into `data/sft/e1_m1_h1_examples.jsonl`. To build E/M only, pass e.g. `--input data/processed/e1_m1_clean.jsonl --output data/sft/e1_m1_examples.jsonl`.
|
|
|
|
| 89 |
```bash
|
| 90 |
pip install -e ".[training]"
|
| 91 |
python training/train_sft.py --model Qwen/Qwen2.5-7B-Instruct --data data/sft/e1_m1_h1_examples.jsonl --output outputs/sft_adapter --max-steps 30
|
| 92 |
+
python training/train_rlvr.py --model Qwen/Qwen2.5-7B-Instruct --adapter outputs/sft_adapter --output outputs/rlvr_adapter --strict-json --max-prompts 128 --rounds 3
|
| 93 |
```
|
| 94 |
|
| 95 |
+
This repo now uses **SFT + RLVR** as the default path. GRPO scripts remain available for reference but are not part of the recommended training flow.
|
| 96 |
|
| 97 |
For a judge-rerunnable notebook, use [`notebooks/corp_env_trl_unsloth_training.ipynb`](notebooks/corp_env_trl_unsloth_training.ipynb).
|
| 98 |
|
data/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Data Layout
|
| 2 |
+
|
| 3 |
+
Use this layout for the SFT -> RLVR pipeline.
|
| 4 |
+
|
| 5 |
+
- `raw/imported/`: imported generated scenario examples (E1/M1)
|
| 6 |
+
- `raw/synthetic/`: synthetic seed trajectories (for example H1 seeds)
|
| 7 |
+
- `processed/verified/`: strict-clean and rejected trajectories plus all-records
|
| 8 |
+
- `sft/merged/`: final chat-format SFT training JSONL
|
| 9 |
+
|
| 10 |
+
## Recommended command
|
| 11 |
+
|
| 12 |
+
```powershell
|
| 13 |
+
uv run python scripts/run_data_pipeline.py --write-legacy-copies
|
| 14 |
+
```
|
| 15 |
+
|
| 16 |
+
This command creates structured outputs and also updates legacy flat paths used
|
| 17 |
+
by older scripts.
|
data/processed/e1_m1_clean.jsonl
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/processed/h1_seed_clean.jsonl
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/processed/verified/e1_m1_all_records.jsonl
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/processed/verified/e1_m1_clean.jsonl
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/processed/verified/e1_m1_rejected.jsonl
ADDED
|
File without changes
|
data/processed/verified/h1_seed_all_records.jsonl
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/processed/verified/h1_seed_clean.jsonl
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/processed/verified/h1_seed_rejected.jsonl
ADDED
|
File without changes
|
data/raw/h1_seed.jsonl
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/raw/imported/e1_m1_examples.jsonl
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/raw/synthetic/h1_seed.jsonl
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{"example_id": "seed-h1_acquisition_defence-000", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
|
| 2 |
+
{"example_id": "seed-h1_acquisition_defence-001", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
|
| 3 |
+
{"example_id": "seed-h1_acquisition_defence-002", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
|
| 4 |
+
{"example_id": "seed-h1_acquisition_defence-003", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
|
| 5 |
+
{"example_id": "seed-h1_acquisition_defence-004", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
|
| 6 |
+
{"example_id": "seed-h1_acquisition_defence-005", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
|
| 7 |
+
{"example_id": "seed-h1_acquisition_defence-006", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
|
| 8 |
+
{"example_id": "seed-h1_acquisition_defence-007", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
|
| 9 |
+
{"example_id": "seed-h1_acquisition_defence-008", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
|
| 10 |
+
{"example_id": "seed-h1_acquisition_defence-009", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
|
| 11 |
+
{"example_id": "seed-h1_acquisition_defence-010", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
|
| 12 |
+
{"example_id": "seed-h1_acquisition_defence-011", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
|
| 13 |
+
{"example_id": "seed-h1_acquisition_defence-012", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
|
| 14 |
+
{"example_id": "seed-h1_acquisition_defence-013", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
|
| 15 |
+
{"example_id": "seed-h1_acquisition_defence-014", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
|
| 16 |
+
{"example_id": "seed-h1_acquisition_defence-015", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
|
| 17 |
+
{"example_id": "seed-h1_acquisition_defence-016", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
|
| 18 |
+
{"example_id": "seed-h1_acquisition_defence-017", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
|
| 19 |
+
{"example_id": "seed-h1_acquisition_defence-018", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
|
| 20 |
+
{"example_id": "seed-h1_acquisition_defence-019", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
|
| 21 |
+
{"example_id": "seed-h1_acquisition_defence-020", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
|
| 22 |
+
{"example_id": "seed-h1_acquisition_defence-021", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Assess IP leverage and technical moat."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Assess cash runway and valuation ceiling."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Assess retention risk and people constraints."}, {"action_type": "log_reasoning", "payload": "CTO input supports a higher counter because the IP moat is meaningful."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO valuation ambition conflicts with CFO runway and cash constraints.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO input limits how long the negotiation can remain open."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A slow process increases CHRO retention risk for key engineering talent.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Ask above the CFO ceiling but set a fast deadline and walk-down logic.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "The 7 month runway makes delay costly and should shape the deadline."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Proceed with a counter that acknowledges 7 month runway and cash limits."}, {"action_type": "log_reasoning", "payload": "Retention incentives are required before the market reads uncertainty."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Final recommendation integrates valuation, deadline, and retention plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
|
| 23 |
+
{"example_id": "seed-h1_acquisition_defence-022", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "Report whether technical differentiation justifies a premium vs the 2.3x bid."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "Clarify how many months of runway remain if we stretch price or drag timing."}, {"action_type": "delegate", "agent_id": "chro", "payload": "Clarify CHRO's view on 90-day people risk and hiring-market pressure."}, {"action_type": "log_reasoning", "payload": "Dev leadership sees differentiated tech, so a weak counter cedes leverage too early."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"Engineering (cto) wants a high counter; finance (cfo) caps what cash runway allows.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "CFO: board cannot fund an endless auction with only about 7 month runway in reserve."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"chro and cto both warn: delay past the HR window erodes the engineering team.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Use a time-boxed process: name a number above cfo\\u2019s ceiling, then step down on a defined clock.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "Runway and cash pressure force a firm deadline, not an open-ended beauty contest."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Commit: respect runway and cash reality while still pressing for a fair tech premium."}, {"action_type": "log_reasoning", "payload": "hr signals that clarity on roles and comp must precede a public counter narrative."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Close the loop: a bounded counter, a calendar-driven deadline, and a retention package."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Open at 3.2x with a board-approved walk-down floor near 2.6x.\", \"deadline\": \"Force a decision inside 45 days to preserve cash runway and reduce retention risk.\", \"retention_plan\": \"Offer retention grants and role clarity for critical engineering leaders.\"}"}]}
|
| 24 |
+
{"example_id": "seed-h1_acquisition_defence-023", "task_id": "h1_acquisition_defence", "source": "scripted_seed", "actions": [{"action_type": "delegate", "agent_id": "cto", "payload": "CTO: outline minimum acceptable tech valuation given integration complexity."}, {"action_type": "delegate", "agent_id": "cfo", "payload": "CFO: outline a pricing corridor consistent with 7 month runway and board guardrails."}, {"action_type": "delegate", "agent_id": "chro", "payload": "CHRO: outline retention and communication needs if diligence slips past the HR window."}, {"action_type": "log_reasoning", "payload": "The cto case implies we should not fold to 2.3x without a premium for IP."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c1\", \"summary\": \"CTO's strategic premium clashes with cfo and finance on cash runway and board limits.\", \"source_agents\": [\"cto\", \"cfo\"]}"}, {"action_type": "log_reasoning", "payload": "Finance and cash runway cap how long we can posture before credibility breaks."}, {"action_type": "log_conflict", "payload": "{\"id\": \"c2\", \"summary\": \"A drawn-out haggle raises chro and hr risk as competing offers come due.\", \"source_agents\": [\"chro\", \"cto\"]}"}, {"action_type": "log_resolution", "payload": "{\"conflict_id\": \"c1\", \"resolution_type\": \"bounded_counter\", \"text\": \"Pursue a structured counter: higher opening ask with a board-mandated walk-down to 2.6x floor.\"}"}, {"action_type": "advance_phase", "payload": "analysis"}, {"action_type": "log_reasoning", "payload": "A 7 month cash runway means every extra week in diligence burns optionality and cash."}, {"action_type": "advance_phase", "payload": "decision"}, {"action_type": "log_decision", "payload": "Choose a path that fits both the 7 month cash runway and board finance constraints."}, {"action_type": "log_reasoning", "payload": "chro: retention grants must land before the team interprets delay as a leadership stall."}, {"action_type": "advance_phase", "payload": "execution"}, {"action_type": "log_reasoning", "payload": "Synthesize cto, cfo, and chro inputs into one executable counter, timeline, and people plan."}, {"action_type": "finalize", "payload": "{\"counter_offer\": \"Signal 3.25x as the opening position with board ratified walk-down rights.\", \"deadline\": \"Close or exit talks within 40 days; 7 month runway does not allow drift.\", \"retention_plan\": \"Two-tier retention: cash now for chro-priority staff, earn-outs for the rest.\"}"}]}
|
data/sft/e1_m1_h1_examples.jsonl
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/sft/merged/e1_m1_h1_examples.jsonl
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
docs/lightning_hf_runbook.md
CHANGED
|
@@ -6,10 +6,10 @@ This runbook is optimized for short 3-4 hour H100 windows and Hugging Face credi
|
|
| 6 |
|
| 7 |
```powershell
|
| 8 |
uv sync --extra dev --extra plots
|
| 9 |
-
uv run python scripts/generate_sft_data.py --tasks h1_acquisition_defence --per-task 24 --output data/raw/h1_seed.jsonl
|
| 10 |
-
uv run python scripts/verify_examples.py --input data/raw/e1_m1_examples.jsonl --clean data/processed/e1_m1_clean.jsonl --rejected data/processed/e1_m1_rejected.jsonl
|
| 11 |
-
uv run python scripts/verify_examples.py --input data/raw/h1_seed.jsonl --clean data/processed/h1_seed_clean.jsonl --rejected data/processed/h1_seed_rejected.jsonl
|
| 12 |
-
uv run python scripts/prepare_sft_data.py
|
| 13 |
```
|
| 14 |
|
| 15 |
Keep raw examples untouched. Train only from verified `data/processed/*_clean.jsonl` and `data/sft/*.jsonl`.
|
|
@@ -45,7 +45,41 @@ python training/train_sft.py \
|
|
| 45 |
--push-to-hub <your-user-or-org>/corp-env-sft-adapter
|
| 46 |
```
|
| 47 |
|
| 48 |
-
This uses Unsloth + TRL `SFTTrainer` with conversational `messages` JSONL (and optional `--packing` / `--dataloader-num-workers` on a strong box). If setup time is short, use a 7B model.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
## 4. Lightning H100 Session 2: Eval SFT
|
| 51 |
|
|
@@ -61,31 +95,35 @@ python eval.py --policy hf \
|
|
| 61 |
|
| 62 |
Push `results/*.jsonl` to Hugging Face or copy them back before the Lightning account expires.
|
| 63 |
|
| 64 |
-
## 5. Lightning H100 Session 3:
|
| 65 |
|
| 66 |
```bash
|
| 67 |
-
python training/
|
| 68 |
--model Qwen/Qwen2.5-7B-Instruct \
|
| 69 |
--adapter outputs/sft_adapter \
|
| 70 |
--examples data/processed/e1_m1_clean.jsonl,data/processed/h1_seed_clean.jsonl \
|
| 71 |
-
--output outputs/
|
| 72 |
-
--
|
| 73 |
-
--
|
| 74 |
-
--
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
```
|
| 76 |
|
| 77 |
-
This uses Unsloth + TRL
|
| 78 |
|
| 79 |
## 6. Final Eval And Plots
|
| 80 |
|
| 81 |
```bash
|
| 82 |
python eval.py --policy hf \
|
| 83 |
-
--label
|
| 84 |
--model Qwen/Qwen2.5-7B-Instruct \
|
| 85 |
-
--adapter outputs/
|
| 86 |
-
--output results/
|
| 87 |
python plot_results.py \
|
| 88 |
-
--inputs results/baseline_eval.jsonl results/sft_eval.jsonl results/
|
| 89 |
--output-dir results
|
| 90 |
```
|
| 91 |
|
|
|
|
| 6 |
|
| 7 |
```powershell
|
| 8 |
uv sync --extra dev --extra plots
|
| 9 |
+
uv run python scripts/generate_sft_data.py --tasks h1_acquisition_defence --per-task 24 --variant-stride 2 --output data/raw/h1_seed.jsonl
|
| 10 |
+
uv run python scripts/verify_examples.py --input data/raw/e1_m1_examples.jsonl --clean data/processed/e1_m1_clean.jsonl --rejected data/processed/e1_m1_rejected.jsonl --strict-json --require-stepwise-deliberation
|
| 11 |
+
uv run python scripts/verify_examples.py --input data/raw/h1_seed.jsonl --clean data/processed/h1_seed_clean.jsonl --rejected data/processed/h1_seed_rejected.jsonl --strict-json --require-stepwise-deliberation
|
| 12 |
+
uv run python scripts/prepare_sft_data.py --min-pass-rate 0.85 --min-reasoning-steps 1 --min-conflict-steps 0 --min-resolution-steps 0 --require-stepwise-deliberation
|
| 13 |
```
|
| 14 |
|
| 15 |
Keep raw examples untouched. Train only from verified `data/processed/*_clean.jsonl` and `data/sft/*.jsonl`.
|
|
|
|
| 45 |
--push-to-hub <your-user-or-org>/corp-env-sft-adapter
|
| 46 |
```
|
| 47 |
|
| 48 |
+
This uses Unsloth + TRL `SFTTrainer` with conversational `messages` JSONL (and optional `--packing` / `--dataloader-num-workers` on a strong box). If setup time is short, use a 7B model. For quality runs, target 14B with strict-clean traces. Remove `--max-steps 30` or raise it for a real run. Install a `flash-attn` wheel that matches the session’s `torch`+CUDA for best GRPO and long-context SFT step time.
|
| 49 |
+
|
| 50 |
+
### 14B SFT presets (smoke then full)
|
| 51 |
+
|
| 52 |
+
```bash
|
| 53 |
+
# Qwen3 14B smoke
|
| 54 |
+
python training/train_sft.py \
|
| 55 |
+
--model Qwen/Qwen3-14B-Instruct \
|
| 56 |
+
--data data/sft/e1_m1_h1_examples.jsonl \
|
| 57 |
+
--output outputs/sft_qwen3_14b \
|
| 58 |
+
--batch-size 1 --grad-accum 8 --max-steps 20
|
| 59 |
+
|
| 60 |
+
# Qwen3 14B fuller run
|
| 61 |
+
python training/train_sft.py \
|
| 62 |
+
--model Qwen/Qwen3-14B-Instruct \
|
| 63 |
+
--data data/sft/e1_m1_h1_examples.jsonl \
|
| 64 |
+
--output outputs/sft_qwen3_14b \
|
| 65 |
+
--epochs 2 --max-steps 200
|
| 66 |
+
|
| 67 |
+
# DeepSeek 14B smoke
|
| 68 |
+
python training/train_sft.py \
|
| 69 |
+
--model deepseek-ai/DeepSeek-R1-Distill-Qwen-14B \
|
| 70 |
+
--data data/sft/e1_m1_h1_examples.jsonl \
|
| 71 |
+
--output outputs/sft_deepseek_14b \
|
| 72 |
+
--batch-size 1 --grad-accum 8 --max-steps 20
|
| 73 |
+
|
| 74 |
+
# DeepSeek 14B fuller run
|
| 75 |
+
python training/train_sft.py \
|
| 76 |
+
--model deepseek-ai/DeepSeek-R1-Distill-Qwen-14B \
|
| 77 |
+
--data data/sft/e1_m1_h1_examples.jsonl \
|
| 78 |
+
--output outputs/sft_deepseek_14b \
|
| 79 |
+
--epochs 2 --max-steps 200
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
Use `--assistant-only` only when the model template supports assistant token masks end-to-end.
|
| 83 |
|
| 84 |
## 4. Lightning H100 Session 2: Eval SFT
|
| 85 |
|
|
|
|
| 95 |
|
| 96 |
Push `results/*.jsonl` to Hugging Face or copy them back before the Lightning account expires.
|
| 97 |
|
| 98 |
+
## 5. Lightning H100 Session 3: RLVR
|
| 99 |
|
| 100 |
```bash
|
| 101 |
+
python training/train_rlvr.py \
|
| 102 |
--model Qwen/Qwen2.5-7B-Instruct \
|
| 103 |
--adapter outputs/sft_adapter \
|
| 104 |
--examples data/processed/e1_m1_clean.jsonl,data/processed/h1_seed_clean.jsonl \
|
| 105 |
+
--output outputs/rlvr_adapter \
|
| 106 |
+
--strict-json \
|
| 107 |
+
--min-reasoning-steps 2 \
|
| 108 |
+
--rounds 3 \
|
| 109 |
+
--n-samples 8 \
|
| 110 |
+
--max-prompts 128 \
|
| 111 |
+
--stats-file results/runs/rlvr_stats.jsonl \
|
| 112 |
+
--push-to-hub <your-user-or-org>/corp-env-rlvr-adapter
|
| 113 |
```
|
| 114 |
|
| 115 |
+
This uses Unsloth + TRL RLVR rejection-sampling with the same strict CORP-ENV verifier path. The recommended production path is now SFT then RLVR.
|
| 116 |
|
| 117 |
## 6. Final Eval And Plots
|
| 118 |
|
| 119 |
```bash
|
| 120 |
python eval.py --policy hf \
|
| 121 |
+
--label rlvr \
|
| 122 |
--model Qwen/Qwen2.5-7B-Instruct \
|
| 123 |
+
--adapter outputs/rlvr_adapter \
|
| 124 |
+
--output results/rlvr_eval.jsonl
|
| 125 |
python plot_results.py \
|
| 126 |
+
--inputs results/baseline_eval.jsonl results/sft_eval.jsonl results/rlvr_eval.jsonl \
|
| 127 |
--output-dir results
|
| 128 |
```
|
| 129 |
|
env_artifacts/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
# Lightning AI H100 environment — reproduction artifacts
|
| 2 |
|
| 3 |
Files in this folder capture the exact, *working* stack we landed on for
|
| 4 |
-
`corp_gym` SFT/
|
| 5 |
Studio (or any Linux H100 with driver >= 570, CUDA 12.8 runtime, Python 3.12).
|
| 6 |
|
| 7 |
## Contents
|
|
@@ -17,6 +17,16 @@ Studio (or any Linux H100 with driver >= 570, CUDA 12.8 runtime, Python 3.12).
|
|
| 17 |
(treat as a ground truth, not a reinstall recipe — it does not know about
|
| 18 |
`--index-url` or wheel URLs).
|
| 19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
## Tested against
|
| 21 |
|
| 22 |
- GPU: **NVIDIA H100 80GB HBM3 (sm_90)**, driver **570.148.08**
|
|
@@ -64,13 +74,11 @@ huggingface-cli login # once per container
|
|
| 64 |
The prior run pushed both adapters to `Navigam/*`:
|
| 65 |
|
| 66 |
- `Navigam/corp-env-sft-qwen2.5-7b`
|
| 67 |
-
- `Navigam/corp-env-grpo-qwen2.5-7b`
|
| 68 |
|
| 69 |
You can pull them locally on a new box with:
|
| 70 |
|
| 71 |
```bash
|
| 72 |
huggingface-cli download Navigam/corp-env-sft-qwen2.5-7b --local-dir outputs/sft_adapter
|
| 73 |
-
huggingface-cli download Navigam/corp-env-grpo-qwen2.5-7b --local-dir outputs/grpo_adapter
|
| 74 |
```
|
| 75 |
|
| 76 |
Then for the RLVR run:
|
|
@@ -91,13 +99,13 @@ python training/train_rlvr.py \
|
|
| 91 |
- **First time only on any new box**: `unsloth` writes patched-trainer classes
|
| 92 |
into `corp_gym/unsloth_compiled_cache/`. Delete that folder if you ever
|
| 93 |
change TRL/unsloth versions to avoid stale compiled patches.
|
| 94 |
-
- **Flash-attn fp32 bug**: in `training/train_grpo.py` and
|
| 95 |
`training/train_rlvr.py` we monkey-patch `flash_attn_func` in both
|
| 96 |
`flash_attn.flash_attn_interface` and `unsloth.utils.attention_dispatch` to
|
| 97 |
auto-cast Q/K/V to bf16. Without the dispatcher-level patch, Unsloth's copy
|
| 98 |
of the symbol bypasses ours and FA2 rejects fp32 tensors during the no-grad
|
| 99 |
reference logprob forward.
|
| 100 |
-
- **max_prompt_length filter**: both GRPO and RLVR scripts tokenise every
|
| 101 |
prompt up-front and drop rows whose chat-template-encoded length exceeds
|
| 102 |
`0.9 * max_prompt_length` (long H1 trajectories otherwise produce a causal
|
| 103 |
mask / attention mask size mismatch at generation time).
|
|
|
|
| 1 |
# Lightning AI H100 environment — reproduction artifacts
|
| 2 |
|
| 3 |
Files in this folder capture the exact, *working* stack we landed on for
|
| 4 |
+
`corp_gym` SFT/RLVR training. Use them to bring up a fresh Lightning AI
|
| 5 |
Studio (or any Linux H100 with driver >= 570, CUDA 12.8 runtime, Python 3.12).
|
| 6 |
|
| 7 |
## Contents
|
|
|
|
| 17 |
(treat as a ground truth, not a reinstall recipe — it does not know about
|
| 18 |
`--index-url` or wheel URLs).
|
| 19 |
|
| 20 |
+
## Recommended Lightning Studio environment
|
| 21 |
+
|
| 22 |
+
- GPU: **1x NVIDIA H100 80GB**
|
| 23 |
+
- Runtime: Linux with CUDA **12.8** compatible driver (**>= 570.x**)
|
| 24 |
+
- Python: **3.12** (base Lightning image is fine)
|
| 25 |
+
- Start from a fresh Studio and run `bash env_artifacts/setup_lightning_h100.sh`
|
| 26 |
+
|
| 27 |
+
This is the fastest conflict-free path for Unsloth in this repo because it
|
| 28 |
+
pins a known-good torch/xformers/flash-attn intersection.
|
| 29 |
+
|
| 30 |
## Tested against
|
| 31 |
|
| 32 |
- GPU: **NVIDIA H100 80GB HBM3 (sm_90)**, driver **570.148.08**
|
|
|
|
| 74 |
The prior run pushed both adapters to `Navigam/*`:
|
| 75 |
|
| 76 |
- `Navigam/corp-env-sft-qwen2.5-7b`
|
|
|
|
| 77 |
|
| 78 |
You can pull them locally on a new box with:
|
| 79 |
|
| 80 |
```bash
|
| 81 |
huggingface-cli download Navigam/corp-env-sft-qwen2.5-7b --local-dir outputs/sft_adapter
|
|
|
|
| 82 |
```
|
| 83 |
|
| 84 |
Then for the RLVR run:
|
|
|
|
| 99 |
- **First time only on any new box**: `unsloth` writes patched-trainer classes
|
| 100 |
into `corp_gym/unsloth_compiled_cache/`. Delete that folder if you ever
|
| 101 |
change TRL/unsloth versions to avoid stale compiled patches.
|
| 102 |
+
- **Flash-attn fp32 bug**: in `training/train_grpo.py` (legacy) and
|
| 103 |
`training/train_rlvr.py` we monkey-patch `flash_attn_func` in both
|
| 104 |
`flash_attn.flash_attn_interface` and `unsloth.utils.attention_dispatch` to
|
| 105 |
auto-cast Q/K/V to bf16. Without the dispatcher-level patch, Unsloth's copy
|
| 106 |
of the symbol bypasses ours and FA2 rejects fp32 tensors during the no-grad
|
| 107 |
reference logprob forward.
|
| 108 |
+
- **max_prompt_length filter**: both legacy GRPO and RLVR scripts tokenise every
|
| 109 |
prompt up-front and drop rows whose chat-template-encoded length exceeds
|
| 110 |
`0.9 * max_prompt_length` (long H1 trajectories otherwise produce a causal
|
| 111 |
mask / attention mask size mismatch at generation time).
|
env_artifacts/setup_lightning_h100.sh
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
#!/usr/bin/env bash
|
| 2 |
# Reproduce the torch 2.7.1 + cu128 stack that worked on the previous
|
| 3 |
-
# Lightning AI H100 container for corp_gym SFT/
|
| 4 |
#
|
| 5 |
# Target environment:
|
| 6 |
# - NVIDIA H100 80GB HBM3 (sm_90), driver >= 570.x, CUDA runtime 12.8
|
|
@@ -63,7 +63,7 @@ import flash_attn; print("flash_attn", flash_attn.__version__)
|
|
| 63 |
import xformers, xformers.ops; print("xformers", xformers.__version__, "ops OK")
|
| 64 |
import torchao; print("torchao", torchao.__version__)
|
| 65 |
from unsloth import FastLanguageModel # noqa
|
| 66 |
-
from trl import SFTConfig,
|
| 67 |
print("unsloth + trl OK")
|
| 68 |
PY
|
| 69 |
|
|
|
|
| 1 |
#!/usr/bin/env bash
|
| 2 |
# Reproduce the torch 2.7.1 + cu128 stack that worked on the previous
|
| 3 |
+
# Lightning AI H100 container for corp_gym SFT/RLVR training.
|
| 4 |
#
|
| 5 |
# Target environment:
|
| 6 |
# - NVIDIA H100 80GB HBM3 (sm_90), driver >= 570.x, CUDA runtime 12.8
|
|
|
|
| 63 |
import xformers, xformers.ops; print("xformers", xformers.__version__, "ops OK")
|
| 64 |
import torchao; print("torchao", torchao.__version__)
|
| 65 |
from unsloth import FastLanguageModel # noqa
|
| 66 |
+
from trl import SFTConfig, SFTTrainer # noqa
|
| 67 |
print("unsloth + trl OK")
|
| 68 |
PY
|
| 69 |
|
results/data_pipeline/e1_m1_summary.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"total": 200,
|
| 3 |
+
"by_status": {
|
| 4 |
+
"clean": 200
|
| 5 |
+
},
|
| 6 |
+
"by_reject_reason": {
|
| 7 |
+
"clean": 200
|
| 8 |
+
},
|
| 9 |
+
"by_task": {
|
| 10 |
+
"e1_launch_readiness": {
|
| 11 |
+
"clean": 100
|
| 12 |
+
},
|
| 13 |
+
"m1_budget_reallocation": {
|
| 14 |
+
"clean": 100
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"clean_avg_terminal_reward": 0.926666,
|
| 18 |
+
"clean_avg_verifier_pass_rate": 1.0,
|
| 19 |
+
"clean_avg_reasoning_steps": 1.0,
|
| 20 |
+
"clean_phase_progression_ok_rate": 0.0
|
| 21 |
+
}
|
results/data_pipeline/h1_seed_summary.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"total": 24,
|
| 3 |
+
"by_status": {
|
| 4 |
+
"clean": 24
|
| 5 |
+
},
|
| 6 |
+
"by_reject_reason": {
|
| 7 |
+
"clean": 24
|
| 8 |
+
},
|
| 9 |
+
"by_task": {
|
| 10 |
+
"h1_acquisition_defence": {
|
| 11 |
+
"clean": 24
|
| 12 |
+
}
|
| 13 |
+
},
|
| 14 |
+
"clean_avg_terminal_reward": 1.05,
|
| 15 |
+
"clean_avg_verifier_pass_rate": 1.0,
|
| 16 |
+
"clean_avg_reasoning_steps": 5.0,
|
| 17 |
+
"clean_phase_progression_ok_rate": 1.0
|
| 18 |
+
}
|
scripts/_trajectory_utils.py
CHANGED
|
@@ -8,7 +8,7 @@ import re
|
|
| 8 |
import sys
|
| 9 |
from dataclasses import dataclass
|
| 10 |
from pathlib import Path
|
| 11 |
-
from typing import Any, Dict, Iterable, List, Optional
|
| 12 |
|
| 13 |
ROOT = Path(__file__).resolve().parents[1]
|
| 14 |
if str(ROOT) not in sys.path:
|
|
@@ -20,6 +20,8 @@ from server.environment import CorpEnvironment # noqa: E402
|
|
| 20 |
|
| 21 |
|
| 22 |
DEFAULT_TASKS = ("e1_launch_readiness", "m1_budget_reallocation", "h1_acquisition_defence")
|
|
|
|
|
|
|
| 23 |
|
| 24 |
|
| 25 |
@dataclass
|
|
@@ -36,6 +38,10 @@ class ReplayResult:
|
|
| 36 |
missed_milestones: List[str]
|
| 37 |
invalid_action_count: int
|
| 38 |
env_error_count: int
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
final_swd_version: int
|
| 40 |
final_swd: Dict[str, Any]
|
| 41 |
|
|
@@ -62,6 +68,10 @@ class ReplayResult:
|
|
| 62 |
"missed_milestones": self.missed_milestones,
|
| 63 |
"invalid_action_count": self.invalid_action_count,
|
| 64 |
"env_error_count": self.env_error_count,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
"final_swd_version": self.final_swd_version,
|
| 66 |
"actions": self.actions,
|
| 67 |
"final_swd": self.final_swd,
|
|
@@ -95,7 +105,19 @@ def write_jsonl(path: Path, rows: Iterable[Dict[str, Any]]) -> None:
|
|
| 95 |
f.write(json.dumps(row, ensure_ascii=False) + "\n")
|
| 96 |
|
| 97 |
|
| 98 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
cleaned = text.strip()
|
| 100 |
cleaned = re.sub(r"^```(?:json)?\s*", "", cleaned)
|
| 101 |
cleaned = re.sub(r"\s*```\s*$", "", cleaned).strip()
|
|
@@ -136,19 +158,38 @@ def extract_json_object(text: str) -> Dict[str, Any]:
|
|
| 136 |
raise ValueError("unbalanced JSON object")
|
| 137 |
|
| 138 |
|
| 139 |
-
def normalize_action_obj(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
if isinstance(raw, str):
|
| 141 |
-
raw = extract_json_object(raw)
|
| 142 |
if not isinstance(raw, dict):
|
| 143 |
raise ValueError("action must be a JSON object or string containing one")
|
| 144 |
raw = dict(raw)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
raw.pop("thought", None)
|
|
|
|
|
|
|
| 146 |
if "payload" in raw and not isinstance(raw["payload"], str):
|
|
|
|
|
|
|
| 147 |
raw["payload"] = json.dumps(raw["payload"], ensure_ascii=False)
|
| 148 |
raw.setdefault("payload", "")
|
| 149 |
if raw.get("agent_id") == "":
|
| 150 |
raw["agent_id"] = None
|
| 151 |
-
|
|
|
|
|
|
|
|
|
|
| 152 |
|
| 153 |
|
| 154 |
def extract_actions(example: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
@@ -181,6 +222,44 @@ def extract_actions(example: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
| 181 |
return [normalize_action_obj(action) for action in raw_actions]
|
| 182 |
|
| 183 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
def observation_message(step: int, obs: CorpObservation) -> str:
|
| 185 |
parts = [
|
| 186 |
f"--- Step {step} ---",
|
|
@@ -203,7 +282,7 @@ def observation_message(step: int, obs: CorpObservation) -> str:
|
|
| 203 |
if obs.error:
|
| 204 |
parts.append(f"Error: {obs.error}")
|
| 205 |
parts.append(f"Reward (last step): {obs.reward}")
|
| 206 |
-
parts.append("Respond with
|
| 207 |
return "\n".join(parts)
|
| 208 |
|
| 209 |
|
|
@@ -253,6 +332,8 @@ def replay_actions(
|
|
| 253 |
task_id: str,
|
| 254 |
actions: List[Dict[str, Any]],
|
| 255 |
strict_thresholds: bool = True,
|
|
|
|
|
|
|
| 256 |
) -> ReplayResult:
|
| 257 |
os.environ.setdefault("CORP_STUB_WORKERS", "1")
|
| 258 |
os.environ.setdefault("CORP_DISABLE_LLM_JUDGE", "1")
|
|
@@ -264,10 +345,18 @@ def replay_actions(
|
|
| 264 |
invalid_action_count = 0
|
| 265 |
env_error_count = 0
|
| 266 |
reject_reason = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
|
| 268 |
for idx, action_obj in enumerate(actions, start=1):
|
|
|
|
|
|
|
| 269 |
try:
|
| 270 |
-
action = CorpAction.model_validate(
|
|
|
|
|
|
|
| 271 |
except Exception as exc:
|
| 272 |
invalid_action_count += 1
|
| 273 |
reject_reason = f"invalid_action_at_step_{idx}: {exc}"
|
|
@@ -315,6 +404,10 @@ def replay_actions(
|
|
| 315 |
missed_milestones=missed,
|
| 316 |
invalid_action_count=invalid_action_count,
|
| 317 |
env_error_count=env_error_count,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
final_swd_version=int(final_swd.get("swd_version", 0)),
|
| 319 |
final_swd=final_swd,
|
| 320 |
)
|
|
|
|
| 8 |
import sys
|
| 9 |
from dataclasses import dataclass
|
| 10 |
from pathlib import Path
|
| 11 |
+
from typing import Any, Dict, Iterable, List, Optional, Sequence
|
| 12 |
|
| 13 |
ROOT = Path(__file__).resolve().parents[1]
|
| 14 |
if str(ROOT) not in sys.path:
|
|
|
|
| 20 |
|
| 21 |
|
| 22 |
DEFAULT_TASKS = ("e1_launch_readiness", "m1_budget_reallocation", "h1_acquisition_defence")
|
| 23 |
+
_ACTION_ALLOWED_KEYS = {"action_type", "agent_id", "payload", "metadata"}
|
| 24 |
+
_ACTION_ALLOWED_EXTRAS = {"thought"}
|
| 25 |
|
| 26 |
|
| 27 |
@dataclass
|
|
|
|
| 38 |
missed_milestones: List[str]
|
| 39 |
invalid_action_count: int
|
| 40 |
env_error_count: int
|
| 41 |
+
reasoning_steps: int
|
| 42 |
+
conflict_steps: int
|
| 43 |
+
resolution_steps: int
|
| 44 |
+
phase_progression_ok: bool
|
| 45 |
final_swd_version: int
|
| 46 |
final_swd: Dict[str, Any]
|
| 47 |
|
|
|
|
| 68 |
"missed_milestones": self.missed_milestones,
|
| 69 |
"invalid_action_count": self.invalid_action_count,
|
| 70 |
"env_error_count": self.env_error_count,
|
| 71 |
+
"reasoning_steps": self.reasoning_steps,
|
| 72 |
+
"conflict_steps": self.conflict_steps,
|
| 73 |
+
"resolution_steps": self.resolution_steps,
|
| 74 |
+
"phase_progression_ok": self.phase_progression_ok,
|
| 75 |
"final_swd_version": self.final_swd_version,
|
| 76 |
"actions": self.actions,
|
| 77 |
"final_swd": self.final_swd,
|
|
|
|
| 105 |
f.write(json.dumps(row, ensure_ascii=False) + "\n")
|
| 106 |
|
| 107 |
|
| 108 |
+
def _strict_json_object(text: str) -> Dict[str, Any]:
|
| 109 |
+
cleaned = text.strip()
|
| 110 |
+
if cleaned.startswith("```"):
|
| 111 |
+
raise ValueError("strict_json_disallows_fenced_blocks")
|
| 112 |
+
obj = json.loads(cleaned)
|
| 113 |
+
if not isinstance(obj, dict):
|
| 114 |
+
raise ValueError("strict_json_requires_single_object")
|
| 115 |
+
return obj
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def extract_json_object(text: str, *, strict: bool = False) -> Dict[str, Any]:
|
| 119 |
+
if strict:
|
| 120 |
+
return _strict_json_object(text)
|
| 121 |
cleaned = text.strip()
|
| 122 |
cleaned = re.sub(r"^```(?:json)?\s*", "", cleaned)
|
| 123 |
cleaned = re.sub(r"\s*```\s*$", "", cleaned).strip()
|
|
|
|
| 158 |
raise ValueError("unbalanced JSON object")
|
| 159 |
|
| 160 |
|
| 161 |
+
def normalize_action_obj(
|
| 162 |
+
raw: Any,
|
| 163 |
+
*,
|
| 164 |
+
strict: bool = False,
|
| 165 |
+
allowed_extra_keys: Optional[Sequence[str]] = None,
|
| 166 |
+
) -> Dict[str, Any]:
|
| 167 |
if isinstance(raw, str):
|
| 168 |
+
raw = extract_json_object(raw, strict=strict)
|
| 169 |
if not isinstance(raw, dict):
|
| 170 |
raise ValueError("action must be a JSON object or string containing one")
|
| 171 |
raw = dict(raw)
|
| 172 |
+
allowed = set(_ACTION_ALLOWED_KEYS)
|
| 173 |
+
extra = set(allowed_extra_keys or ())
|
| 174 |
+
allowed.update(extra)
|
| 175 |
+
if strict:
|
| 176 |
+
unknown = sorted(k for k in raw if k not in allowed and k not in _ACTION_ALLOWED_EXTRAS)
|
| 177 |
+
if unknown:
|
| 178 |
+
raise ValueError(f"unexpected_action_keys: {unknown}")
|
| 179 |
raw.pop("thought", None)
|
| 180 |
+
if "action_type" not in raw or not isinstance(raw["action_type"], str):
|
| 181 |
+
raise ValueError("missing_or_invalid_action_type")
|
| 182 |
if "payload" in raw and not isinstance(raw["payload"], str):
|
| 183 |
+
if strict:
|
| 184 |
+
raise ValueError("strict_json_requires_payload_string")
|
| 185 |
raw["payload"] = json.dumps(raw["payload"], ensure_ascii=False)
|
| 186 |
raw.setdefault("payload", "")
|
| 187 |
if raw.get("agent_id") == "":
|
| 188 |
raw["agent_id"] = None
|
| 189 |
+
out = CorpAction.model_validate(raw).model_dump(mode="json", exclude_none=True)
|
| 190 |
+
if strict and set(out) - _ACTION_ALLOWED_KEYS:
|
| 191 |
+
raise ValueError("non_canonical_action_shape")
|
| 192 |
+
return out
|
| 193 |
|
| 194 |
|
| 195 |
def extract_actions(example: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
|
|
| 222 |
return [normalize_action_obj(action) for action in raw_actions]
|
| 223 |
|
| 224 |
|
| 225 |
+
def deliberation_features(actions: List[Dict[str, Any]]) -> Dict[str, Any]:
|
| 226 |
+
reasoning_steps = sum(1 for a in actions if a.get("action_type") == "log_reasoning")
|
| 227 |
+
conflict_steps = sum(1 for a in actions if a.get("action_type") == "log_conflict")
|
| 228 |
+
resolution_steps = sum(1 for a in actions if a.get("action_type") == "log_resolution")
|
| 229 |
+
phases = [
|
| 230 |
+
str(a.get("payload", "")).strip().lower()
|
| 231 |
+
for a in actions
|
| 232 |
+
if a.get("action_type") == "advance_phase"
|
| 233 |
+
]
|
| 234 |
+
expected = ["analysis", "decision", "execution"]
|
| 235 |
+
idx = 0
|
| 236 |
+
for phase in phases:
|
| 237 |
+
if idx < len(expected) and phase == expected[idx]:
|
| 238 |
+
idx += 1
|
| 239 |
+
phase_progression_ok = idx == len(expected)
|
| 240 |
+
return {
|
| 241 |
+
"reasoning_steps": reasoning_steps,
|
| 242 |
+
"conflict_steps": conflict_steps,
|
| 243 |
+
"resolution_steps": resolution_steps,
|
| 244 |
+
"phase_progression_ok": phase_progression_ok,
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
def validate_stepwise_deliberation(task_id: str, actions: List[Dict[str, Any]]) -> str:
|
| 249 |
+
features = deliberation_features(actions)
|
| 250 |
+
reasoning_min = 1 if task_id in {"e1_launch_readiness", "m1_budget_reallocation"} else 3
|
| 251 |
+
if features["reasoning_steps"] < reasoning_min:
|
| 252 |
+
return "insufficient_stepwise_reasoning"
|
| 253 |
+
if task_id in {"m1_budget_reallocation", "h1_acquisition_defence"}:
|
| 254 |
+
if features["conflict_steps"] < 1:
|
| 255 |
+
return "missing_conflict_logging"
|
| 256 |
+
if features["resolution_steps"] < 1:
|
| 257 |
+
return "missing_resolution_logging"
|
| 258 |
+
if task_id == "h1_acquisition_defence" and not features["phase_progression_ok"]:
|
| 259 |
+
return "invalid_phase_progression"
|
| 260 |
+
return ""
|
| 261 |
+
|
| 262 |
+
|
| 263 |
def observation_message(step: int, obs: CorpObservation) -> str:
|
| 264 |
parts = [
|
| 265 |
f"--- Step {step} ---",
|
|
|
|
| 282 |
if obs.error:
|
| 283 |
parts.append(f"Error: {obs.error}")
|
| 284 |
parts.append(f"Reward (last step): {obs.reward}")
|
| 285 |
+
parts.append("Respond with exactly one JSON object for your next action (no markdown fences).")
|
| 286 |
return "\n".join(parts)
|
| 287 |
|
| 288 |
|
|
|
|
| 332 |
task_id: str,
|
| 333 |
actions: List[Dict[str, Any]],
|
| 334 |
strict_thresholds: bool = True,
|
| 335 |
+
strict_json: bool = False,
|
| 336 |
+
require_stepwise_deliberation: bool = False,
|
| 337 |
) -> ReplayResult:
|
| 338 |
os.environ.setdefault("CORP_STUB_WORKERS", "1")
|
| 339 |
os.environ.setdefault("CORP_DISABLE_LLM_JUDGE", "1")
|
|
|
|
| 345 |
invalid_action_count = 0
|
| 346 |
env_error_count = 0
|
| 347 |
reject_reason = ""
|
| 348 |
+
features = deliberation_features(actions)
|
| 349 |
+
|
| 350 |
+
if require_stepwise_deliberation:
|
| 351 |
+
reject_reason = validate_stepwise_deliberation(task_id, actions)
|
| 352 |
|
| 353 |
for idx, action_obj in enumerate(actions, start=1):
|
| 354 |
+
if reject_reason:
|
| 355 |
+
break
|
| 356 |
try:
|
| 357 |
+
action = CorpAction.model_validate(
|
| 358 |
+
normalize_action_obj(action_obj, strict=strict_json)
|
| 359 |
+
)
|
| 360 |
except Exception as exc:
|
| 361 |
invalid_action_count += 1
|
| 362 |
reject_reason = f"invalid_action_at_step_{idx}: {exc}"
|
|
|
|
| 404 |
missed_milestones=missed,
|
| 405 |
invalid_action_count=invalid_action_count,
|
| 406 |
env_error_count=env_error_count,
|
| 407 |
+
reasoning_steps=int(features["reasoning_steps"]),
|
| 408 |
+
conflict_steps=int(features["conflict_steps"]),
|
| 409 |
+
resolution_steps=int(features["resolution_steps"]),
|
| 410 |
+
phase_progression_ok=bool(features["phase_progression_ok"]),
|
| 411 |
final_swd_version=int(final_swd.get("swd_version", 0)),
|
| 412 |
final_swd=final_swd,
|
| 413 |
)
|
scripts/generate_sft_data.py
CHANGED
|
@@ -19,16 +19,17 @@ if str(ROOT) not in sys.path:
|
|
| 19 |
from scripts._trajectory_utils import DEFAULT_TASKS, oracle_actions, write_jsonl # noqa: E402
|
| 20 |
|
| 21 |
|
| 22 |
-
def build_examples(tasks: List[str], per_task: int) -> List[Dict[str, Any]]:
|
| 23 |
rows: List[Dict[str, Any]] = []
|
| 24 |
for task_id in tasks:
|
| 25 |
for idx in range(per_task):
|
|
|
|
| 26 |
rows.append(
|
| 27 |
{
|
| 28 |
"example_id": f"seed-{task_id}-{idx:03d}",
|
| 29 |
"task_id": task_id,
|
| 30 |
"source": "scripted_seed",
|
| 31 |
-
"actions": oracle_actions(task_id,
|
| 32 |
}
|
| 33 |
)
|
| 34 |
return rows
|
|
@@ -38,11 +39,17 @@ def main() -> None:
|
|
| 38 |
parser = argparse.ArgumentParser(description="Generate CORP-ENV seed trajectories.")
|
| 39 |
parser.add_argument("--tasks", default="h1_acquisition_defence")
|
| 40 |
parser.add_argument("--per-task", type=int, default=8)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
parser.add_argument("--output", default="data/raw/h1_seed.jsonl")
|
| 42 |
args = parser.parse_args()
|
| 43 |
|
| 44 |
tasks = [t.strip() for t in args.tasks.split(",") if t.strip()] or list(DEFAULT_TASKS)
|
| 45 |
-
rows = build_examples(tasks, args.per_task)
|
| 46 |
write_jsonl(Path(args.output), rows)
|
| 47 |
print(f"Wrote {len(rows)} seed trajectories to {args.output}")
|
| 48 |
print("Next: run scripts/verify_examples.py on this file before using it for SFT.")
|
|
|
|
| 19 |
from scripts._trajectory_utils import DEFAULT_TASKS, oracle_actions, write_jsonl # noqa: E402
|
| 20 |
|
| 21 |
|
| 22 |
+
def build_examples(tasks: List[str], per_task: int, variant_stride: int) -> List[Dict[str, Any]]:
|
| 23 |
rows: List[Dict[str, Any]] = []
|
| 24 |
for task_id in tasks:
|
| 25 |
for idx in range(per_task):
|
| 26 |
+
variant = idx * max(1, variant_stride)
|
| 27 |
rows.append(
|
| 28 |
{
|
| 29 |
"example_id": f"seed-{task_id}-{idx:03d}",
|
| 30 |
"task_id": task_id,
|
| 31 |
"source": "scripted_seed",
|
| 32 |
+
"actions": oracle_actions(task_id, variant),
|
| 33 |
}
|
| 34 |
)
|
| 35 |
return rows
|
|
|
|
| 39 |
parser = argparse.ArgumentParser(description="Generate CORP-ENV seed trajectories.")
|
| 40 |
parser.add_argument("--tasks", default="h1_acquisition_defence")
|
| 41 |
parser.add_argument("--per-task", type=int, default=8)
|
| 42 |
+
parser.add_argument(
|
| 43 |
+
"--variant-stride",
|
| 44 |
+
type=int,
|
| 45 |
+
default=1,
|
| 46 |
+
help="Sample wider oracle variants by stepping this much between synthetic traces.",
|
| 47 |
+
)
|
| 48 |
parser.add_argument("--output", default="data/raw/h1_seed.jsonl")
|
| 49 |
args = parser.parse_args()
|
| 50 |
|
| 51 |
tasks = [t.strip() for t in args.tasks.split(",") if t.strip()] or list(DEFAULT_TASKS)
|
| 52 |
+
rows = build_examples(tasks, args.per_task, args.variant_stride)
|
| 53 |
write_jsonl(Path(args.output), rows)
|
| 54 |
print(f"Wrote {len(rows)} seed trajectories to {args.output}")
|
| 55 |
print("Next: run scripts/verify_examples.py on this file before using it for SFT.")
|
scripts/prepare_sft_data.py
CHANGED
|
@@ -9,6 +9,7 @@ Pass one or more processed JSONLs (e.g. `e1_m1_clean` + `h1_seed_clean`) from
|
|
| 9 |
from __future__ import annotations
|
| 10 |
|
| 11 |
import argparse
|
|
|
|
| 12 |
import sys
|
| 13 |
from pathlib import Path
|
| 14 |
from typing import Any, Dict, List
|
|
@@ -19,13 +20,22 @@ if str(ROOT) not in sys.path:
|
|
| 19 |
|
| 20 |
from scripts._trajectory_utils import ( # noqa: E402
|
| 21 |
actions_to_sft_messages,
|
|
|
|
| 22 |
extract_actions,
|
| 23 |
read_jsonl,
|
|
|
|
| 24 |
write_jsonl,
|
| 25 |
)
|
| 26 |
|
| 27 |
|
| 28 |
-
def convert_example(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
if example.get("status") and example.get("status") != "clean":
|
| 30 |
return None
|
| 31 |
pass_rate = float(example.get("verifier_pass_rate", 1.0))
|
|
@@ -35,6 +45,16 @@ def convert_example(example: Dict[str, Any], min_pass_rate: float) -> Dict[str,
|
|
| 35 |
if not task_id:
|
| 36 |
return None
|
| 37 |
actions = extract_actions(example)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
messages = actions_to_sft_messages(task_id, actions)
|
| 39 |
return {
|
| 40 |
"example_id": str(example.get("example_id") or example.get("id") or "unknown"),
|
|
@@ -43,6 +63,10 @@ def convert_example(example: Dict[str, Any], min_pass_rate: float) -> Dict[str,
|
|
| 43 |
"num_actions": len(actions),
|
| 44 |
"terminal_reward": example.get("terminal_reward"),
|
| 45 |
"verifier_pass_rate": example.get("verifier_pass_rate"),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
}
|
| 47 |
|
| 48 |
|
|
@@ -75,11 +99,26 @@ def main() -> None:
|
|
| 75 |
)
|
| 76 |
parser.add_argument("--output", default="data/sft/e1_m1_h1_examples.jsonl")
|
| 77 |
parser.add_argument("--min-pass-rate", type=float, default=0.80)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
args = parser.parse_args()
|
| 79 |
raw_inputs = list(args.inputs) if args.inputs else [default_inputs]
|
| 80 |
input_paths = _parse_input_paths(raw_inputs)
|
| 81 |
|
| 82 |
rows: List[Dict[str, Any]] = []
|
|
|
|
| 83 |
seen_ids: set[str] = set()
|
| 84 |
skipped = 0
|
| 85 |
for path in input_paths:
|
|
@@ -92,7 +131,14 @@ def main() -> None:
|
|
| 92 |
skipped += 1
|
| 93 |
continue
|
| 94 |
try:
|
| 95 |
-
row = convert_example(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
except Exception as exc:
|
| 97 |
skipped += 1
|
| 98 |
print(f"skip {example.get('example_id', 'unknown')}: {exc}")
|
|
@@ -100,7 +146,11 @@ def main() -> None:
|
|
| 100 |
if row is None:
|
| 101 |
skipped += 1
|
| 102 |
continue
|
|
|
|
|
|
|
|
|
|
| 103 |
rows.append(row)
|
|
|
|
| 104 |
eid2 = str(row.get("example_id") or "unknown")
|
| 105 |
if eid2 and eid2 != "unknown":
|
| 106 |
seen_ids.add(eid2)
|
|
|
|
| 9 |
from __future__ import annotations
|
| 10 |
|
| 11 |
import argparse
|
| 12 |
+
from collections import defaultdict
|
| 13 |
import sys
|
| 14 |
from pathlib import Path
|
| 15 |
from typing import Any, Dict, List
|
|
|
|
| 20 |
|
| 21 |
from scripts._trajectory_utils import ( # noqa: E402
|
| 22 |
actions_to_sft_messages,
|
| 23 |
+
deliberation_features,
|
| 24 |
extract_actions,
|
| 25 |
read_jsonl,
|
| 26 |
+
validate_stepwise_deliberation,
|
| 27 |
write_jsonl,
|
| 28 |
)
|
| 29 |
|
| 30 |
|
| 31 |
+
def convert_example(
|
| 32 |
+
example: Dict[str, Any],
|
| 33 |
+
min_pass_rate: float,
|
| 34 |
+
min_reasoning_steps: int,
|
| 35 |
+
min_conflict_steps: int,
|
| 36 |
+
min_resolution_steps: int,
|
| 37 |
+
require_stepwise_deliberation: bool,
|
| 38 |
+
) -> Dict[str, Any] | None:
|
| 39 |
if example.get("status") and example.get("status") != "clean":
|
| 40 |
return None
|
| 41 |
pass_rate = float(example.get("verifier_pass_rate", 1.0))
|
|
|
|
| 45 |
if not task_id:
|
| 46 |
return None
|
| 47 |
actions = extract_actions(example)
|
| 48 |
+
if require_stepwise_deliberation:
|
| 49 |
+
if validate_stepwise_deliberation(task_id, actions):
|
| 50 |
+
return None
|
| 51 |
+
features = deliberation_features(actions)
|
| 52 |
+
if int(features["reasoning_steps"]) < min_reasoning_steps:
|
| 53 |
+
return None
|
| 54 |
+
if int(features["conflict_steps"]) < min_conflict_steps:
|
| 55 |
+
return None
|
| 56 |
+
if int(features["resolution_steps"]) < min_resolution_steps:
|
| 57 |
+
return None
|
| 58 |
messages = actions_to_sft_messages(task_id, actions)
|
| 59 |
return {
|
| 60 |
"example_id": str(example.get("example_id") or example.get("id") or "unknown"),
|
|
|
|
| 63 |
"num_actions": len(actions),
|
| 64 |
"terminal_reward": example.get("terminal_reward"),
|
| 65 |
"verifier_pass_rate": example.get("verifier_pass_rate"),
|
| 66 |
+
"reasoning_steps": int(features["reasoning_steps"]),
|
| 67 |
+
"conflict_steps": int(features["conflict_steps"]),
|
| 68 |
+
"resolution_steps": int(features["resolution_steps"]),
|
| 69 |
+
"phase_progression_ok": bool(features["phase_progression_ok"]),
|
| 70 |
}
|
| 71 |
|
| 72 |
|
|
|
|
| 99 |
)
|
| 100 |
parser.add_argument("--output", default="data/sft/e1_m1_h1_examples.jsonl")
|
| 101 |
parser.add_argument("--min-pass-rate", type=float, default=0.80)
|
| 102 |
+
parser.add_argument("--min-reasoning-steps", type=int, default=1)
|
| 103 |
+
parser.add_argument("--min-conflict-steps", type=int, default=0)
|
| 104 |
+
parser.add_argument("--min-resolution-steps", type=int, default=0)
|
| 105 |
+
parser.add_argument(
|
| 106 |
+
"--require-stepwise-deliberation",
|
| 107 |
+
action="store_true",
|
| 108 |
+
help="Require task-specific SWD step-wise deliberation checks from verification utilities.",
|
| 109 |
+
)
|
| 110 |
+
parser.add_argument(
|
| 111 |
+
"--max-per-task",
|
| 112 |
+
type=int,
|
| 113 |
+
default=0,
|
| 114 |
+
help="Optional cap for kept SFT rows per task (0 = unlimited).",
|
| 115 |
+
)
|
| 116 |
args = parser.parse_args()
|
| 117 |
raw_inputs = list(args.inputs) if args.inputs else [default_inputs]
|
| 118 |
input_paths = _parse_input_paths(raw_inputs)
|
| 119 |
|
| 120 |
rows: List[Dict[str, Any]] = []
|
| 121 |
+
by_task_kept: Dict[str, int] = defaultdict(int)
|
| 122 |
seen_ids: set[str] = set()
|
| 123 |
skipped = 0
|
| 124 |
for path in input_paths:
|
|
|
|
| 131 |
skipped += 1
|
| 132 |
continue
|
| 133 |
try:
|
| 134 |
+
row = convert_example(
|
| 135 |
+
example,
|
| 136 |
+
args.min_pass_rate,
|
| 137 |
+
args.min_reasoning_steps,
|
| 138 |
+
args.min_conflict_steps,
|
| 139 |
+
args.min_resolution_steps,
|
| 140 |
+
args.require_stepwise_deliberation,
|
| 141 |
+
)
|
| 142 |
except Exception as exc:
|
| 143 |
skipped += 1
|
| 144 |
print(f"skip {example.get('example_id', 'unknown')}: {exc}")
|
|
|
|
| 146 |
if row is None:
|
| 147 |
skipped += 1
|
| 148 |
continue
|
| 149 |
+
if args.max_per_task > 0 and by_task_kept[row["task_id"]] >= args.max_per_task:
|
| 150 |
+
skipped += 1
|
| 151 |
+
continue
|
| 152 |
rows.append(row)
|
| 153 |
+
by_task_kept[row["task_id"]] += 1
|
| 154 |
eid2 = str(row.get("example_id") or "unknown")
|
| 155 |
if eid2 and eid2 != "unknown":
|
| 156 |
seen_ids.add(eid2)
|
scripts/run_data_pipeline.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Run a structured CORP-ENV data pipeline for SFT -> RLVR.
|
| 2 |
+
|
| 3 |
+
This script standardizes where artifacts are written so data prep stays tidy:
|
| 4 |
+
|
| 5 |
+
- raw/imported: imported E1/M1 generated examples
|
| 6 |
+
- raw/synthetic: synthetic seed traces (H1 by default)
|
| 7 |
+
- processed/verified: clean/rejected outputs after strict verification
|
| 8 |
+
- sft/merged: final SFT chat JSONL
|
| 9 |
+
|
| 10 |
+
It wraps existing scripts and keeps compatibility by optionally writing the
|
| 11 |
+
legacy flat output files as copies.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
from __future__ import annotations
|
| 15 |
+
|
| 16 |
+
import argparse
|
| 17 |
+
import shutil
|
| 18 |
+
import subprocess
|
| 19 |
+
import sys
|
| 20 |
+
from pathlib import Path
|
| 21 |
+
|
| 22 |
+
ROOT = Path(__file__).resolve().parents[1]
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def run(cmd: list[str], *, use_uv: bool) -> None:
|
| 26 |
+
final_cmd = (["uv", "run"] + cmd) if use_uv else cmd
|
| 27 |
+
print("+", " ".join(final_cmd))
|
| 28 |
+
subprocess.run(final_cmd, check=True, cwd=ROOT)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def ensure_dirs(paths: list[Path]) -> None:
|
| 32 |
+
for path in paths:
|
| 33 |
+
path.mkdir(parents=True, exist_ok=True)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def copy_if_exists(src: Path, dst: Path) -> None:
|
| 37 |
+
if src.exists():
|
| 38 |
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
| 39 |
+
shutil.copy2(src, dst)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def main() -> None:
|
| 43 |
+
parser = argparse.ArgumentParser(description="Structured data pipeline for CORP-ENV.")
|
| 44 |
+
parser.add_argument("--python", default=sys.executable)
|
| 45 |
+
parser.add_argument(
|
| 46 |
+
"--use-uv",
|
| 47 |
+
action="store_true",
|
| 48 |
+
help="Run each pipeline stage with `uv run` to use the project environment.",
|
| 49 |
+
)
|
| 50 |
+
parser.add_argument("--h1-per-task", type=int, default=24)
|
| 51 |
+
parser.add_argument("--h1-variant-stride", type=int, default=2)
|
| 52 |
+
parser.add_argument("--min-pass-rate", type=float, default=0.85)
|
| 53 |
+
parser.add_argument("--min-reasoning-steps", type=int, default=1)
|
| 54 |
+
parser.add_argument("--min-conflict-steps", type=int, default=0)
|
| 55 |
+
parser.add_argument("--min-resolution-steps", type=int, default=0)
|
| 56 |
+
parser.add_argument("--max-per-task", type=int, default=0)
|
| 57 |
+
parser.add_argument(
|
| 58 |
+
"--write-legacy-copies",
|
| 59 |
+
action="store_true",
|
| 60 |
+
help="Also copy outputs to legacy flat paths for backward compatibility.",
|
| 61 |
+
)
|
| 62 |
+
args = parser.parse_args()
|
| 63 |
+
|
| 64 |
+
data = ROOT / "data"
|
| 65 |
+
raw_imported = data / "raw" / "imported"
|
| 66 |
+
raw_synthetic = data / "raw" / "synthetic"
|
| 67 |
+
processed_verified = data / "processed" / "verified"
|
| 68 |
+
sft_merged = data / "sft" / "merged"
|
| 69 |
+
summaries = ROOT / "results" / "data_pipeline"
|
| 70 |
+
ensure_dirs([raw_imported, raw_synthetic, processed_verified, sft_merged, summaries])
|
| 71 |
+
|
| 72 |
+
e1m1_raw = raw_imported / "e1_m1_examples.jsonl"
|
| 73 |
+
h1_raw = raw_synthetic / "h1_seed.jsonl"
|
| 74 |
+
e1m1_clean = processed_verified / "e1_m1_clean.jsonl"
|
| 75 |
+
e1m1_rejected = processed_verified / "e1_m1_rejected.jsonl"
|
| 76 |
+
h1_clean = processed_verified / "h1_seed_clean.jsonl"
|
| 77 |
+
h1_rejected = processed_verified / "h1_seed_rejected.jsonl"
|
| 78 |
+
sft_out = sft_merged / "e1_m1_h1_examples.jsonl"
|
| 79 |
+
|
| 80 |
+
run(
|
| 81 |
+
[
|
| 82 |
+
args.python,
|
| 83 |
+
"scripts/import_generated_examples.py",
|
| 84 |
+
"--inputs",
|
| 85 |
+
"data/raw/e1_to_e100_tasks.py",
|
| 86 |
+
"data/raw/m1_to_m100_tasks.py",
|
| 87 |
+
"--output",
|
| 88 |
+
str(e1m1_raw),
|
| 89 |
+
],
|
| 90 |
+
use_uv=args.use_uv,
|
| 91 |
+
)
|
| 92 |
+
run(
|
| 93 |
+
[
|
| 94 |
+
args.python,
|
| 95 |
+
"scripts/generate_sft_data.py",
|
| 96 |
+
"--tasks",
|
| 97 |
+
"h1_acquisition_defence",
|
| 98 |
+
"--per-task",
|
| 99 |
+
str(args.h1_per_task),
|
| 100 |
+
"--variant-stride",
|
| 101 |
+
str(args.h1_variant_stride),
|
| 102 |
+
"--output",
|
| 103 |
+
str(h1_raw),
|
| 104 |
+
],
|
| 105 |
+
use_uv=args.use_uv,
|
| 106 |
+
)
|
| 107 |
+
run(
|
| 108 |
+
[
|
| 109 |
+
args.python,
|
| 110 |
+
"scripts/verify_examples.py",
|
| 111 |
+
"--input",
|
| 112 |
+
str(e1m1_raw),
|
| 113 |
+
"--clean",
|
| 114 |
+
str(e1m1_clean),
|
| 115 |
+
"--rejected",
|
| 116 |
+
str(e1m1_rejected),
|
| 117 |
+
"--all-records",
|
| 118 |
+
str(processed_verified / "e1_m1_all_records.jsonl"),
|
| 119 |
+
"--summary",
|
| 120 |
+
str(summaries / "e1_m1_summary.json"),
|
| 121 |
+
"--strict-json",
|
| 122 |
+
"--require-stepwise-deliberation",
|
| 123 |
+
],
|
| 124 |
+
use_uv=args.use_uv,
|
| 125 |
+
)
|
| 126 |
+
run(
|
| 127 |
+
[
|
| 128 |
+
args.python,
|
| 129 |
+
"scripts/verify_examples.py",
|
| 130 |
+
"--input",
|
| 131 |
+
str(h1_raw),
|
| 132 |
+
"--clean",
|
| 133 |
+
str(h1_clean),
|
| 134 |
+
"--rejected",
|
| 135 |
+
str(h1_rejected),
|
| 136 |
+
"--all-records",
|
| 137 |
+
str(processed_verified / "h1_seed_all_records.jsonl"),
|
| 138 |
+
"--summary",
|
| 139 |
+
str(summaries / "h1_seed_summary.json"),
|
| 140 |
+
"--strict-json",
|
| 141 |
+
"--require-stepwise-deliberation",
|
| 142 |
+
],
|
| 143 |
+
use_uv=args.use_uv,
|
| 144 |
+
)
|
| 145 |
+
prep_cmd = [
|
| 146 |
+
args.python,
|
| 147 |
+
"scripts/prepare_sft_data.py",
|
| 148 |
+
"--input",
|
| 149 |
+
str(e1m1_clean),
|
| 150 |
+
"--input",
|
| 151 |
+
str(h1_clean),
|
| 152 |
+
"--output",
|
| 153 |
+
str(sft_out),
|
| 154 |
+
"--min-pass-rate",
|
| 155 |
+
str(args.min_pass_rate),
|
| 156 |
+
"--min-reasoning-steps",
|
| 157 |
+
str(args.min_reasoning_steps),
|
| 158 |
+
"--min-conflict-steps",
|
| 159 |
+
str(args.min_conflict_steps),
|
| 160 |
+
"--min-resolution-steps",
|
| 161 |
+
str(args.min_resolution_steps),
|
| 162 |
+
"--require-stepwise-deliberation",
|
| 163 |
+
]
|
| 164 |
+
if args.max_per_task > 0:
|
| 165 |
+
prep_cmd.extend(["--max-per-task", str(args.max_per_task)])
|
| 166 |
+
run(prep_cmd, use_uv=args.use_uv)
|
| 167 |
+
|
| 168 |
+
if args.write_legacy_copies:
|
| 169 |
+
copy_if_exists(e1m1_raw, data / "raw" / "e1_m1_examples.jsonl")
|
| 170 |
+
copy_if_exists(h1_raw, data / "raw" / "h1_seed.jsonl")
|
| 171 |
+
copy_if_exists(e1m1_clean, data / "processed" / "e1_m1_clean.jsonl")
|
| 172 |
+
copy_if_exists(e1m1_rejected, data / "processed" / "e1_m1_rejected.jsonl")
|
| 173 |
+
copy_if_exists(h1_clean, data / "processed" / "h1_seed_clean.jsonl")
|
| 174 |
+
copy_if_exists(h1_rejected, data / "processed" / "h1_seed_rejected.jsonl")
|
| 175 |
+
copy_if_exists(sft_out, data / "sft" / "e1_m1_h1_examples.jsonl")
|
| 176 |
+
|
| 177 |
+
print("\nStructured data pipeline complete.")
|
| 178 |
+
print(f"SFT dataset: {sft_out}")
|
| 179 |
+
print(f"Summaries: {summaries}")
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
if __name__ == "__main__":
|
| 183 |
+
main()
|
scripts/verify_examples.py
CHANGED
|
@@ -32,7 +32,12 @@ from scripts._trajectory_utils import ( # noqa: E402
|
|
| 32 |
)
|
| 33 |
|
| 34 |
|
| 35 |
-
def verify_one(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
example_id = str(example.get("example_id") or example.get("id") or "unknown")
|
| 37 |
task_id = str(example.get("task_id") or example.get("task") or "")
|
| 38 |
if example.get("_load_error"):
|
|
@@ -67,6 +72,8 @@ def verify_one(example: Dict[str, Any], strict_thresholds: bool) -> Dict[str, An
|
|
| 67 |
task_id=task_id,
|
| 68 |
actions=actions,
|
| 69 |
strict_thresholds=strict_thresholds,
|
|
|
|
|
|
|
| 70 |
)
|
| 71 |
except Exception as exc:
|
| 72 |
return {
|
|
@@ -101,6 +108,16 @@ def summarize(records: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
| 101 |
if clean
|
| 102 |
else 0.0
|
| 103 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
}
|
| 105 |
|
| 106 |
|
|
@@ -116,10 +133,25 @@ def main() -> None:
|
|
| 116 |
action="store_true",
|
| 117 |
help="Only require replay validity; do not apply task reward/pass thresholds.",
|
| 118 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
args = parser.parse_args()
|
| 120 |
|
| 121 |
records = [
|
| 122 |
-
verify_one(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
for example in read_jsonl(Path(args.input))
|
| 124 |
]
|
| 125 |
clean = [r for r in records if r["status"] == "clean"]
|
|
|
|
| 32 |
)
|
| 33 |
|
| 34 |
|
| 35 |
+
def verify_one(
|
| 36 |
+
example: Dict[str, Any],
|
| 37 |
+
strict_thresholds: bool,
|
| 38 |
+
strict_json: bool,
|
| 39 |
+
require_stepwise_deliberation: bool,
|
| 40 |
+
) -> Dict[str, Any]:
|
| 41 |
example_id = str(example.get("example_id") or example.get("id") or "unknown")
|
| 42 |
task_id = str(example.get("task_id") or example.get("task") or "")
|
| 43 |
if example.get("_load_error"):
|
|
|
|
| 72 |
task_id=task_id,
|
| 73 |
actions=actions,
|
| 74 |
strict_thresholds=strict_thresholds,
|
| 75 |
+
strict_json=strict_json,
|
| 76 |
+
require_stepwise_deliberation=require_stepwise_deliberation,
|
| 77 |
)
|
| 78 |
except Exception as exc:
|
| 79 |
return {
|
|
|
|
| 108 |
if clean
|
| 109 |
else 0.0
|
| 110 |
),
|
| 111 |
+
"clean_avg_reasoning_steps": (
|
| 112 |
+
round(sum(float(r.get("reasoning_steps", 0.0)) for r in clean) / len(clean), 6)
|
| 113 |
+
if clean
|
| 114 |
+
else 0.0
|
| 115 |
+
),
|
| 116 |
+
"clean_phase_progression_ok_rate": (
|
| 117 |
+
round(sum(1.0 for r in clean if r.get("phase_progression_ok")) / len(clean), 6)
|
| 118 |
+
if clean
|
| 119 |
+
else 0.0
|
| 120 |
+
),
|
| 121 |
}
|
| 122 |
|
| 123 |
|
|
|
|
| 133 |
action="store_true",
|
| 134 |
help="Only require replay validity; do not apply task reward/pass thresholds.",
|
| 135 |
)
|
| 136 |
+
parser.add_argument(
|
| 137 |
+
"--strict-json",
|
| 138 |
+
action="store_true",
|
| 139 |
+
help="Require strict single-object JSON action format with canonical action keys.",
|
| 140 |
+
)
|
| 141 |
+
parser.add_argument(
|
| 142 |
+
"--require-stepwise-deliberation",
|
| 143 |
+
action="store_true",
|
| 144 |
+
help="Enforce task-specific step-wise deliberation checks (reasoning/conflict/resolution/phases).",
|
| 145 |
+
)
|
| 146 |
args = parser.parse_args()
|
| 147 |
|
| 148 |
records = [
|
| 149 |
+
verify_one(
|
| 150 |
+
example,
|
| 151 |
+
strict_thresholds=not args.lenient,
|
| 152 |
+
strict_json=args.strict_json,
|
| 153 |
+
require_stepwise_deliberation=args.require_stepwise_deliberation,
|
| 154 |
+
)
|
| 155 |
for example in read_jsonl(Path(args.input))
|
| 156 |
]
|
| 157 |
clean = [r for r in records if r["status"] == "clean"]
|
training/train_grpo.py
CHANGED
|
@@ -87,6 +87,7 @@ from scripts._trajectory_utils import ( # noqa: E402
|
|
| 87 |
DEFAULT_TASKS,
|
| 88 |
extract_actions,
|
| 89 |
extract_json_object,
|
|
|
|
| 90 |
observation_message,
|
| 91 |
oracle_actions,
|
| 92 |
read_jsonl,
|
|
@@ -94,6 +95,20 @@ from scripts._trajectory_utils import ( # noqa: E402
|
|
| 94 |
from server.agents.master_prompts import build_system_prompt # noqa: E402
|
| 95 |
from server.environment import CorpEnvironment # noqa: E402
|
| 96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
def prompt_for_prefix(task_id: str, prefix_actions: List[Dict[str, Any]]) -> List[Dict[str, str]]:
|
| 99 |
env = CorpEnvironment()
|
|
@@ -113,7 +128,12 @@ def _examples_paths(examples_path: str) -> List[Path]:
|
|
| 113 |
return [Path(p.strip()) for p in examples_path.split(",") if p.strip()]
|
| 114 |
|
| 115 |
|
| 116 |
-
def build_prompt_dataset(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
"""Load examples from one or more JSONLs (comma-separated). Falls back to oracle prefixes if all missing/empty."""
|
| 118 |
rows: List[Dict[str, Any]] = []
|
| 119 |
for path in _examples_paths(examples_path):
|
|
@@ -122,6 +142,8 @@ def build_prompt_dataset(examples_path: str, tasks: List[str], repeats: int) ->
|
|
| 122 |
for example in read_jsonl(path):
|
| 123 |
if example.get("status") and example.get("status") != "clean":
|
| 124 |
continue
|
|
|
|
|
|
|
| 125 |
task_id = str(example.get("task_id") or "")
|
| 126 |
if task_id not in tasks:
|
| 127 |
continue
|
|
@@ -153,6 +175,77 @@ def build_prompt_dataset(examples_path: str, tasks: List[str], repeats: int) ->
|
|
| 153 |
return rows
|
| 154 |
|
| 155 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
def environment_reward(
|
| 157 |
completions: List[Any],
|
| 158 |
task_id: List[str],
|
|
@@ -161,37 +254,24 @@ def environment_reward(
|
|
| 161 |
) -> List[float]:
|
| 162 |
rewards: List[float] = []
|
| 163 |
for completion, tid, prefix_raw in zip(completions, task_id, prefix_actions):
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
obj = extract_json_object(str(text))
|
| 183 |
-
obj.pop("thought", None)
|
| 184 |
-
if "payload" in obj and not isinstance(obj["payload"], str):
|
| 185 |
-
obj["payload"] = json.dumps(obj["payload"], ensure_ascii=False)
|
| 186 |
-
action = CorpAction.model_validate(obj)
|
| 187 |
-
except Exception:
|
| 188 |
-
rewards.append(-0.25)
|
| 189 |
-
continue
|
| 190 |
-
obs = env.step(action)
|
| 191 |
-
reward = float(obs.reward or 0.0)
|
| 192 |
-
if obs.error:
|
| 193 |
-
reward -= 0.15
|
| 194 |
-
rewards.append(max(-1.0, min(1.0, reward)))
|
| 195 |
return rewards
|
| 196 |
|
| 197 |
|
|
@@ -252,6 +332,17 @@ def main() -> None:
|
|
| 252 |
help="When dataloader_num_workers>0, optional prefetch depth (e.g. 2).",
|
| 253 |
)
|
| 254 |
parser.add_argument("--push-to-hub", default="")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
args = parser.parse_args()
|
| 256 |
|
| 257 |
os.environ.setdefault("CORP_STUB_WORKERS", "1")
|
|
@@ -269,9 +360,10 @@ def main() -> None:
|
|
| 269 |
) from exc
|
| 270 |
|
| 271 |
PatchFastRL("GRPO", FastLanguageModel)
|
|
|
|
| 272 |
|
| 273 |
tasks = [t.strip() for t in args.tasks.split(",") if t.strip()] or list(DEFAULT_TASKS)
|
| 274 |
-
rows = build_prompt_dataset(args.examples, tasks, args.repeats)
|
| 275 |
|
| 276 |
model, tokenizer = FastLanguageModel.from_pretrained(
|
| 277 |
model_name=args.model,
|
|
|
|
| 87 |
DEFAULT_TASKS,
|
| 88 |
extract_actions,
|
| 89 |
extract_json_object,
|
| 90 |
+
normalize_action_obj,
|
| 91 |
observation_message,
|
| 92 |
oracle_actions,
|
| 93 |
read_jsonl,
|
|
|
|
| 95 |
from server.agents.master_prompts import build_system_prompt # noqa: E402
|
| 96 |
from server.environment import CorpEnvironment # noqa: E402
|
| 97 |
|
| 98 |
+
REWARD_CFG: Dict[str, Any] = {
|
| 99 |
+
"strict_json": True,
|
| 100 |
+
"invalid_json_penalty": -0.8,
|
| 101 |
+
"invalid_action_penalty": -0.6,
|
| 102 |
+
"prefix_error_penalty": -0.4,
|
| 103 |
+
}
|
| 104 |
+
REWARD_STATS: Dict[str, int] = {
|
| 105 |
+
"calls": 0,
|
| 106 |
+
"prefix_fail": 0,
|
| 107 |
+
"json_fail": 0,
|
| 108 |
+
"action_fail": 0,
|
| 109 |
+
"env_error": 0,
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
|
| 113 |
def prompt_for_prefix(task_id: str, prefix_actions: List[Dict[str, Any]]) -> List[Dict[str, str]]:
|
| 114 |
env = CorpEnvironment()
|
|
|
|
| 128 |
return [Path(p.strip()) for p in examples_path.split(",") if p.strip()]
|
| 129 |
|
| 130 |
|
| 131 |
+
def build_prompt_dataset(
|
| 132 |
+
examples_path: str,
|
| 133 |
+
tasks: List[str],
|
| 134 |
+
repeats: int,
|
| 135 |
+
min_reasoning_steps: int,
|
| 136 |
+
) -> List[Dict[str, Any]]:
|
| 137 |
"""Load examples from one or more JSONLs (comma-separated). Falls back to oracle prefixes if all missing/empty."""
|
| 138 |
rows: List[Dict[str, Any]] = []
|
| 139 |
for path in _examples_paths(examples_path):
|
|
|
|
| 142 |
for example in read_jsonl(path):
|
| 143 |
if example.get("status") and example.get("status") != "clean":
|
| 144 |
continue
|
| 145 |
+
if int(example.get("reasoning_steps", 0)) < min_reasoning_steps:
|
| 146 |
+
continue
|
| 147 |
task_id = str(example.get("task_id") or "")
|
| 148 |
if task_id not in tasks:
|
| 149 |
continue
|
|
|
|
| 175 |
return rows
|
| 176 |
|
| 177 |
|
| 178 |
+
def completion_to_text(completion: Any) -> str:
|
| 179 |
+
text = completion
|
| 180 |
+
if isinstance(completion, list) and completion:
|
| 181 |
+
text = completion[0].get("content", "")
|
| 182 |
+
elif isinstance(completion, dict):
|
| 183 |
+
text = completion.get("content", "")
|
| 184 |
+
return str(text)
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def score_completion(
|
| 188 |
+
*,
|
| 189 |
+
completion_text: str,
|
| 190 |
+
task_id: str,
|
| 191 |
+
prefix_raw: str,
|
| 192 |
+
) -> tuple[float, Dict[str, float], str]:
|
| 193 |
+
env = CorpEnvironment()
|
| 194 |
+
obs = env.reset(task_id=task_id)
|
| 195 |
+
components: Dict[str, float] = {
|
| 196 |
+
"format_valid": 0.0,
|
| 197 |
+
"action_valid": 0.0,
|
| 198 |
+
"env_step": 0.0,
|
| 199 |
+
"verifier_pass": 0.0,
|
| 200 |
+
"terminal_reward": 0.0,
|
| 201 |
+
"error_penalty": 0.0,
|
| 202 |
+
}
|
| 203 |
+
status = "ok"
|
| 204 |
+
try:
|
| 205 |
+
prefix = json.loads(prefix_raw)
|
| 206 |
+
for action_obj in prefix:
|
| 207 |
+
obs = env.step(CorpAction.model_validate(action_obj))
|
| 208 |
+
if obs.done:
|
| 209 |
+
break
|
| 210 |
+
except Exception:
|
| 211 |
+
status = "prefix_fail"
|
| 212 |
+
return REWARD_CFG["prefix_error_penalty"], components, status
|
| 213 |
+
|
| 214 |
+
try:
|
| 215 |
+
obj = extract_json_object(completion_text, strict=bool(REWARD_CFG["strict_json"]))
|
| 216 |
+
components["format_valid"] = 0.15
|
| 217 |
+
except Exception:
|
| 218 |
+
status = "json_fail"
|
| 219 |
+
return REWARD_CFG["invalid_json_penalty"], components, status
|
| 220 |
+
|
| 221 |
+
try:
|
| 222 |
+
action_obj = normalize_action_obj(obj, strict=bool(REWARD_CFG["strict_json"]))
|
| 223 |
+
action = CorpAction.model_validate(action_obj)
|
| 224 |
+
components["action_valid"] = 0.20
|
| 225 |
+
except Exception:
|
| 226 |
+
status = "action_fail"
|
| 227 |
+
return REWARD_CFG["invalid_action_penalty"], components, status
|
| 228 |
+
|
| 229 |
+
obs = env.step(action)
|
| 230 |
+
components["env_step"] = 0.15 if not obs.error else -0.10
|
| 231 |
+
verifier = env.task.verifier(obs.swd)
|
| 232 |
+
if verifier:
|
| 233 |
+
components["verifier_pass"] = sum(1.0 for v in verifier.values() if v) / len(verifier)
|
| 234 |
+
components["terminal_reward"] = float(obs.reward or 0.0)
|
| 235 |
+
if obs.error:
|
| 236 |
+
status = "env_error"
|
| 237 |
+
components["error_penalty"] = -0.2
|
| 238 |
+
score = (
|
| 239 |
+
components["format_valid"]
|
| 240 |
+
+ components["action_valid"]
|
| 241 |
+
+ components["env_step"]
|
| 242 |
+
+ 0.25 * components["verifier_pass"]
|
| 243 |
+
+ 0.50 * components["terminal_reward"]
|
| 244 |
+
+ components["error_penalty"]
|
| 245 |
+
)
|
| 246 |
+
return max(-1.0, min(1.0, score)), components, status
|
| 247 |
+
|
| 248 |
+
|
| 249 |
def environment_reward(
|
| 250 |
completions: List[Any],
|
| 251 |
task_id: List[str],
|
|
|
|
| 254 |
) -> List[float]:
|
| 255 |
rewards: List[float] = []
|
| 256 |
for completion, tid, prefix_raw in zip(completions, task_id, prefix_actions):
|
| 257 |
+
score, _components, status = score_completion(
|
| 258 |
+
completion_text=completion_to_text(completion),
|
| 259 |
+
task_id=tid,
|
| 260 |
+
prefix_raw=prefix_raw,
|
| 261 |
+
)
|
| 262 |
+
REWARD_STATS["calls"] += 1
|
| 263 |
+
if status in REWARD_STATS:
|
| 264 |
+
REWARD_STATS[status] += 1
|
| 265 |
+
if REWARD_STATS["calls"] % 100 == 0:
|
| 266 |
+
print(
|
| 267 |
+
"reward_stats "
|
| 268 |
+
f"calls={REWARD_STATS['calls']} "
|
| 269 |
+
f"prefix_fail={REWARD_STATS['prefix_fail']} "
|
| 270 |
+
f"json_fail={REWARD_STATS['json_fail']} "
|
| 271 |
+
f"action_fail={REWARD_STATS['action_fail']} "
|
| 272 |
+
f"env_error={REWARD_STATS['env_error']}"
|
| 273 |
+
)
|
| 274 |
+
rewards.append(score)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 275 |
return rewards
|
| 276 |
|
| 277 |
|
|
|
|
| 332 |
help="When dataloader_num_workers>0, optional prefetch depth (e.g. 2).",
|
| 333 |
)
|
| 334 |
parser.add_argument("--push-to-hub", default="")
|
| 335 |
+
parser.add_argument(
|
| 336 |
+
"--strict-json",
|
| 337 |
+
action="store_true",
|
| 338 |
+
help="Require completion to be a single strict JSON object.",
|
| 339 |
+
)
|
| 340 |
+
parser.add_argument(
|
| 341 |
+
"--min-reasoning-steps",
|
| 342 |
+
type=int,
|
| 343 |
+
default=1,
|
| 344 |
+
help="Filter training examples to traces with at least this many log_reasoning actions.",
|
| 345 |
+
)
|
| 346 |
args = parser.parse_args()
|
| 347 |
|
| 348 |
os.environ.setdefault("CORP_STUB_WORKERS", "1")
|
|
|
|
| 360 |
) from exc
|
| 361 |
|
| 362 |
PatchFastRL("GRPO", FastLanguageModel)
|
| 363 |
+
REWARD_CFG["strict_json"] = bool(args.strict_json)
|
| 364 |
|
| 365 |
tasks = [t.strip() for t in args.tasks.split(",") if t.strip()] or list(DEFAULT_TASKS)
|
| 366 |
+
rows = build_prompt_dataset(args.examples, tasks, args.repeats, args.min_reasoning_steps)
|
| 367 |
|
| 368 |
model, tokenizer = FastLanguageModel.from_pretrained(
|
| 369 |
model_name=args.model,
|
training/train_rlvr.py
CHANGED
|
@@ -79,8 +79,12 @@ ROOT = Path(__file__).resolve().parents[1]
|
|
| 79 |
if str(ROOT) not in sys.path:
|
| 80 |
sys.path.insert(0, str(ROOT))
|
| 81 |
|
| 82 |
-
from training.train_grpo import
|
| 83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
|
| 85 |
|
| 86 |
def _sft_config_field_names() -> set[str]:
|
|
@@ -164,6 +168,7 @@ def rollout_round(
|
|
| 164 |
top_p: float,
|
| 165 |
max_new_tokens: int,
|
| 166 |
reward_threshold: float,
|
|
|
|
| 167 |
log_every: int = 10,
|
| 168 |
) -> Tuple[List[Dict[str, Any]], Dict[str, float]]:
|
| 169 |
kept_rows: List[Dict[str, Any]] = []
|
|
@@ -171,6 +176,7 @@ def rollout_round(
|
|
| 171 |
best_rewards: List[float] = []
|
| 172 |
per_task_kept: Dict[str, int] = {}
|
| 173 |
per_task_total: Dict[str, int] = {}
|
|
|
|
| 174 |
|
| 175 |
t0 = time.time()
|
| 176 |
for idx, row in enumerate(rows):
|
|
@@ -192,21 +198,29 @@ def rollout_round(
|
|
| 192 |
print(f" [{idx}] {task_id}: generation error {exc!r}")
|
| 193 |
continue
|
| 194 |
|
| 195 |
-
rewards =
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
all_rewards.extend(rewards)
|
| 201 |
best_idx = max(range(len(rewards)), key=lambda j: rewards[j])
|
| 202 |
best_r = float(rewards[best_idx])
|
| 203 |
best_rewards.append(best_r)
|
| 204 |
|
| 205 |
if best_r >= reward_threshold:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
kept_rows.append(
|
| 207 |
{
|
| 208 |
"messages": list(row["prompt"])
|
| 209 |
-
+ [{"role": "assistant", "content":
|
| 210 |
"task_id": task_id,
|
| 211 |
"reward": best_r,
|
| 212 |
}
|
|
@@ -235,6 +249,8 @@ def rollout_round(
|
|
| 235 |
}
|
| 236 |
for tid in per_task_total:
|
| 237 |
stats[f"keep_rate/{tid}"] = per_task_kept.get(tid, 0) / per_task_total[tid]
|
|
|
|
|
|
|
| 238 |
return kept_rows, stats
|
| 239 |
|
| 240 |
|
|
@@ -364,6 +380,17 @@ def main() -> None:
|
|
| 364 |
parser.add_argument("--repeats", type=int, default=32)
|
| 365 |
parser.add_argument("--seed", type=int, default=3407)
|
| 366 |
parser.add_argument("--push-to-hub", default="")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 367 |
parser.add_argument(
|
| 368 |
"--stats-file",
|
| 369 |
default="",
|
|
@@ -386,7 +413,13 @@ def main() -> None:
|
|
| 386 |
) from exc
|
| 387 |
|
| 388 |
tasks = [t.strip() for t in args.tasks.split(",") if t.strip()] or list(DEFAULT_TASKS)
|
| 389 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
print(f"Built {len(full_rows)} prompts from {args.examples}")
|
| 391 |
|
| 392 |
max_seq_len = args.max_prompt_length + args.max_completion_length
|
|
@@ -449,6 +482,7 @@ def main() -> None:
|
|
| 449 |
top_p=args.top_p,
|
| 450 |
max_new_tokens=args.max_completion_length,
|
| 451 |
reward_threshold=args.reward_threshold,
|
|
|
|
| 452 |
)
|
| 453 |
stats["round"] = round_idx
|
| 454 |
print(
|
|
|
|
| 79 |
if str(ROOT) not in sys.path:
|
| 80 |
sys.path.insert(0, str(ROOT))
|
| 81 |
|
| 82 |
+
from training.train_grpo import ( # noqa: E402
|
| 83 |
+
REWARD_CFG,
|
| 84 |
+
build_prompt_dataset,
|
| 85 |
+
score_completion,
|
| 86 |
+
)
|
| 87 |
+
from scripts._trajectory_utils import DEFAULT_TASKS, normalize_action_obj # noqa: E402
|
| 88 |
|
| 89 |
|
| 90 |
def _sft_config_field_names() -> set[str]:
|
|
|
|
| 168 |
top_p: float,
|
| 169 |
max_new_tokens: int,
|
| 170 |
reward_threshold: float,
|
| 171 |
+
strict_json: bool,
|
| 172 |
log_every: int = 10,
|
| 173 |
) -> Tuple[List[Dict[str, Any]], Dict[str, float]]:
|
| 174 |
kept_rows: List[Dict[str, Any]] = []
|
|
|
|
| 176 |
best_rewards: List[float] = []
|
| 177 |
per_task_kept: Dict[str, int] = {}
|
| 178 |
per_task_total: Dict[str, int] = {}
|
| 179 |
+
reward_status_counts: Dict[str, int] = {}
|
| 180 |
|
| 181 |
t0 = time.time()
|
| 182 |
for idx, row in enumerate(rows):
|
|
|
|
| 198 |
print(f" [{idx}] {task_id}: generation error {exc!r}")
|
| 199 |
continue
|
| 200 |
|
| 201 |
+
rewards: List[float] = []
|
| 202 |
+
for text in completions:
|
| 203 |
+
score, _components, status = score_completion(
|
| 204 |
+
completion_text=text,
|
| 205 |
+
task_id=task_id,
|
| 206 |
+
prefix_raw=prefix_json,
|
| 207 |
+
)
|
| 208 |
+
rewards.append(score)
|
| 209 |
+
reward_status_counts[status] = reward_status_counts.get(status, 0) + 1
|
| 210 |
all_rewards.extend(rewards)
|
| 211 |
best_idx = max(range(len(rewards)), key=lambda j: rewards[j])
|
| 212 |
best_r = float(rewards[best_idx])
|
| 213 |
best_rewards.append(best_r)
|
| 214 |
|
| 215 |
if best_r >= reward_threshold:
|
| 216 |
+
best_text = completions[best_idx]
|
| 217 |
+
if strict_json:
|
| 218 |
+
action_obj = normalize_action_obj(best_text, strict=True)
|
| 219 |
+
best_text = json.dumps(action_obj, ensure_ascii=False)
|
| 220 |
kept_rows.append(
|
| 221 |
{
|
| 222 |
"messages": list(row["prompt"])
|
| 223 |
+
+ [{"role": "assistant", "content": best_text}],
|
| 224 |
"task_id": task_id,
|
| 225 |
"reward": best_r,
|
| 226 |
}
|
|
|
|
| 249 |
}
|
| 250 |
for tid in per_task_total:
|
| 251 |
stats[f"keep_rate/{tid}"] = per_task_kept.get(tid, 0) / per_task_total[tid]
|
| 252 |
+
for status, count in reward_status_counts.items():
|
| 253 |
+
stats[f"reward_status/{status}"] = float(count)
|
| 254 |
return kept_rows, stats
|
| 255 |
|
| 256 |
|
|
|
|
| 380 |
parser.add_argument("--repeats", type=int, default=32)
|
| 381 |
parser.add_argument("--seed", type=int, default=3407)
|
| 382 |
parser.add_argument("--push-to-hub", default="")
|
| 383 |
+
parser.add_argument(
|
| 384 |
+
"--strict-json",
|
| 385 |
+
action="store_true",
|
| 386 |
+
help="Require sampled completions to parse as a strict single JSON object action.",
|
| 387 |
+
)
|
| 388 |
+
parser.add_argument(
|
| 389 |
+
"--min-reasoning-steps",
|
| 390 |
+
type=int,
|
| 391 |
+
default=1,
|
| 392 |
+
help="Filter prompt dataset to trajectories with at least this many reasoning steps.",
|
| 393 |
+
)
|
| 394 |
parser.add_argument(
|
| 395 |
"--stats-file",
|
| 396 |
default="",
|
|
|
|
| 413 |
) from exc
|
| 414 |
|
| 415 |
tasks = [t.strip() for t in args.tasks.split(",") if t.strip()] or list(DEFAULT_TASKS)
|
| 416 |
+
REWARD_CFG["strict_json"] = bool(args.strict_json)
|
| 417 |
+
full_rows = build_prompt_dataset(
|
| 418 |
+
args.examples,
|
| 419 |
+
tasks,
|
| 420 |
+
args.repeats,
|
| 421 |
+
args.min_reasoning_steps,
|
| 422 |
+
)
|
| 423 |
print(f"Built {len(full_rows)} prompts from {args.examples}")
|
| 424 |
|
| 425 |
max_seq_len = args.max_prompt_length + args.max_completion_length
|
|
|
|
| 482 |
top_p=args.top_p,
|
| 483 |
max_new_tokens=args.max_completion_length,
|
| 484 |
reward_threshold=args.reward_threshold,
|
| 485 |
+
strict_json=args.strict_json,
|
| 486 |
)
|
| 487 |
stats["round"] = round_idx
|
| 488 |
print(
|