Dar3devil commited on
Commit
486c0f4
·
verified ·
1 Parent(s): 5932788

PromptOps Arena demo

Browse files
Files changed (50) hide show
  1. .env.example +3 -0
  2. .gitattributes +1 -0
  3. .gitignore +30 -0
  4. README.md +210 -12
  5. app.py +207 -0
  6. docs/SCOPE.md +79 -0
  7. docs/baseline_comparison.png +0 -0
  8. docs/reward_curve.png +3 -0
  9. requirements.txt +13 -0
  10. results/baseline_cot_real.json +210 -0
  11. results/baseline_cot_real_subset.json +120 -0
  12. results/baseline_cot_stub.json +480 -0
  13. results/baseline_zero_shot_real.json +210 -0
  14. results/baseline_zero_shot_real_subset.json +120 -0
  15. results/baseline_zero_shot_stub.json +480 -0
  16. results/comparison.json +261 -0
  17. results/trained_agent.json +332 -0
  18. results/training_log.jsonl +304 -0
  19. scripts/eval_trained.py +217 -0
  20. scripts/hf_eval_entry.sh +79 -0
  21. scripts/hf_job_entry.sh +81 -0
  22. scripts/plot_results.py +167 -0
  23. scripts/push_space.py +59 -0
  24. scripts/run_baseline.py +287 -0
  25. scripts/smoke_test_env.py +79 -0
  26. scripts/train_grpo.py +318 -0
  27. scripts/upload_src_to_hf.py +54 -0
  28. src/__init__.py +0 -0
  29. src/envs/__init__.py +0 -0
  30. src/envs/promptops_arena/__init__.py +13 -0
  31. src/envs/promptops_arena/client.py +56 -0
  32. src/envs/promptops_arena/llm_under_test.py +152 -0
  33. src/envs/promptops_arena/models.py +48 -0
  34. src/envs/promptops_arena/server/Dockerfile +15 -0
  35. src/envs/promptops_arena/server/__init__.py +0 -0
  36. src/envs/promptops_arena/server/app.py +44 -0
  37. src/envs/promptops_arena/server/environment.py +180 -0
  38. src/envs/promptops_arena/server/requirements.txt +7 -0
  39. src/envs/promptops_arena/server/rewards.py +45 -0
  40. src/envs/promptops_arena/tasks/__init__.py +3 -0
  41. src/envs/promptops_arena/tasks/code.jsonl +30 -0
  42. src/envs/promptops_arena/tasks/json_extract.jsonl +20 -0
  43. src/envs/promptops_arena/tasks/loader.py +45 -0
  44. src/envs/promptops_arena/tasks/math.jsonl +40 -0
  45. src/envs/promptops_arena/verifiers/__init__.py +21 -0
  46. src/envs/promptops_arena/verifiers/code_verifier.py +65 -0
  47. src/envs/promptops_arena/verifiers/json_verifier.py +85 -0
  48. src/envs/promptops_arena/verifiers/math_verifier.py +73 -0
  49. tests/__init__.py +0 -0
  50. tests/test_rewards.py +214 -0
.env.example ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ HF_TOKEN=hf_xxx
2
+ WANDB_API_KEY=xxx
3
+ HF_USERNAME=your-username
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ docs/reward_curve.png filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ .venv/
5
+ venv/
6
+ env/
7
+ .env
8
+ .env.local
9
+ outputs/
10
+ wandb/
11
+ *.log
12
+ .pytest_cache/
13
+ .DS_Store
14
+ *.egg-info/
15
+ build/
16
+ dist/
17
+ .ipynb_checkpoints/
18
+ .coverage
19
+ htmlcov/
20
+ node_modules/
21
+ .idea/
22
+ .vscode/
23
+ *.swp
24
+ # don't commit raw videos or large media
25
+ *.mp4
26
+ *.mov
27
+ *.webm
28
+ # but DO commit small gifs / pngs in docs/
29
+ !docs/**/*.gif
30
+ !docs/**/*.png
README.md CHANGED
@@ -1,12 +1,210 @@
1
- ---
2
- title: Promptops Arena
3
- emoji: 📚
4
- colorFrom: purple
5
- colorTo: pink
6
- sdk: gradio
7
- sdk_version: 6.13.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: PromptOps Arena
3
+ emoji: 🎯
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: 4.44.0
8
+ app_file: app.py
9
+ pinned: false
10
+ license: mit
11
+ short_description: RL agent that learns to write better prompts
12
+ ---
13
+
14
+ # PromptOps Arena · Self-Improving Prompt Engineer
15
+
16
+ > An OpenEnv RL environment where a 1.5B agent learns, via **GRPO**, to write
17
+ > system prompts that make a **frozen 0.5B LLM-under-test** solve tasks it
18
+ > would otherwise fail — across math, code, and JSON-extraction.
19
+
20
+ [![Hackathon](https://img.shields.io/badge/OpenEnv-Hackathon-blue)](https://pytorch.org/event/openenv-ai-hackathon/)
21
+ [![Space](https://img.shields.io/badge/🤗-Space-yellow)](https://huggingface.co/spaces/Dar3devil/promptops-arena)
22
+ [![Model](https://img.shields.io/badge/🤗-Adapter-green)](https://huggingface.co/Dar3devil/promptops-arena-agent)
23
+
24
+ ![Comparison](docs/baseline_comparison.png)
25
+
26
+ ---
27
+
28
+ ## What this is
29
+
30
+ Most RL-for-LLM research trains the model that *answers* questions. PromptOps
31
+ Arena trains the model that *writes the prompt for another model* that
32
+ answers questions. The agent never touches the answer; it only ever emits a
33
+ system prompt. This makes prompt engineering a learnable, transferable skill
34
+ — one that generalizes across task types because the agent only ever sees the
35
+ shape of the task and the prior attempt's reward.
36
+
37
+ ```mermaid
38
+ flowchart LR
39
+ task["Task (math / code / json)"] --> agent["Agent · Qwen2.5-1.5B + LoRA<br/>(trained with GRPO)"]
40
+ agent -->|"writes system prompt"| under["LLM-under-test · Qwen2.5-0.5B<br/>(frozen, never trained)"]
41
+ task --> under
42
+ under -->|"completion"| verifier["Programmatic verifier<br/>math · code · jsonschema"]
43
+ verifier -->|"correctness, format, brevity"| reward[["reward = correctness<br/>+ 0.1 · format<br/>+ brevity_penalty"]]
44
+ reward -->|"GRPO advantage"| agent
45
+ ```
46
+
47
+ ## Why it's interesting
48
+
49
+ - **Agent vs LLM-under-test split.** Two distinct models, only one is
50
+ trained. The reward signal is grounded in *another model's behavior*,
51
+ which forces the agent to internalize how small models actually fail.
52
+ - **Transferable skill.** The same agent handles math, code, and JSON — it
53
+ has to learn *how to instruct*, not *how to solve*. We see the agent's
54
+ format-bonus rate climb on tasks it was never specifically trained for.
55
+ - **Programmatic, ungameable rewards.** Math: regex-extract a number from
56
+ `<answer>...</answer>` or `\boxed{}` and exact-match. Code:
57
+ subprocess-execute the function with unit tests, 5s timeout. JSON: parse,
58
+ validate against a jsonschema, then exact-match expected fields. There is
59
+ no reward model — no DPO mush — just verifiers.
60
+
61
+ ## Reward decomposition
62
+
63
+ ```
64
+ total = correctness + 0.1 · format_bonus + brevity_penalty
65
+ ```
66
+
67
+ | component | range | how |
68
+ |-------------|----------------|-----|
69
+ | correctness | {0, 1} | verifier returns 1 iff answer programmatically correct |
70
+ | format | {0, 1} (×0.1) | required tags / code block / schema present in output |
71
+ | brevity | [-0.1, 0] | linearly penalize prompts > 800 chars, capped at -0.1 |
72
+
73
+ Adversarial test suite (`tests/test_rewards.py`, 22 tests) proves you can't
74
+ get more than 0.1 reward without solving the task: empty `<answer></answer>`
75
+ tags, wrong numbers in `<answer>`, code blocks with bugs, JSON of the wrong
76
+ type, and 5000-char rambling prompts are all bounded at total ≤ 0.1.
77
+
78
+ ## Results (test split, held-out, n=12 per policy)
79
+
80
+ | Policy | Backend | n | correct | format | mean reward |
81
+ |-----------------------------|------------------|----:|--------:|-------:|------------:|
82
+ | zero-shot ("Solve this:") | Qwen-0.5B (real) | 12 | 8/12 | 7/12 | 0.725 |
83
+ | chain-of-thought | Qwen-0.5B (real) | 12 | 8/12 | 12/12 | 0.767 |
84
+ | **trained agent (ours)** | Qwen-0.5B (real) | 12 | **10/12** | 10/12 | **0.917** |
85
+
86
+ Per-task-type breakdown for the trained agent: **math 3/4**, **code 3/4**,
87
+ **json 4/4** — generalizes across all three task families on top of the same
88
+ frozen 0.5B LLM-under-test.
89
+
90
+ Stub-LLM rows that establish the format-vs-correctness floor:
91
+ zero-shot stub 0/30 correct (0/30 format); CoT stub 0/30 correct (30/30
92
+ format, mean reward 0.1) — exactly what you'd predict, since a stub model
93
+ that can't actually compute anything still earns the 0.1 format bonus from a
94
+ well-formatted CoT scaffold but gets 0 correctness.
95
+
96
+ ![Reward curve](docs/reward_curve.png)
97
+
98
+ ## How GRPO is wired
99
+
100
+ ```mermaid
101
+ sequenceDiagram
102
+ participant DS as train tasks
103
+ participant TR as GRPOTrainer
104
+ participant AG as Agent (Qwen 1.5B + LoRA)
105
+ participant ENV as PromptOpsArenaEnvironment
106
+ participant LUT as LLM-under-test (Qwen 0.5B, frozen)
107
+ participant V as Verifier
108
+
109
+ DS->>TR: row {prompt: agent_input(task), task: ...}
110
+ TR->>AG: sample G=2 completions
111
+ AG-->>TR: G candidate system prompts
112
+ loop for each completion
113
+ TR->>ENV: reward_fn(completion, task)
114
+ ENV->>LUT: generate(system=completion, user=task.question)
115
+ LUT-->>ENV: model output
116
+ ENV->>V: verify(task, output)
117
+ V-->>ENV: {correctness, format_ok, details}
118
+ ENV-->>TR: total reward (logged to training_log.jsonl)
119
+ end
120
+ TR->>AG: GRPO update<br/>advantage = (r - mean) / std
121
+ ```
122
+
123
+ The reward function is the env. There is no separate reward model — the
124
+ verifier *is* the reward, which is what makes the loop honest.
125
+
126
+ ## Reproduce
127
+
128
+ ### Run baselines locally
129
+
130
+ ```bash
131
+ pip install -r requirements.txt
132
+ $env:PROMPTOPS_LLM_BACKEND="transformers" # or "stub" for fast dev
133
+ python scripts/run_baseline.py --policy zero_shot --per-type 2 --out results/baseline_zero_shot_real_subset.json
134
+ python scripts/run_baseline.py --policy cot --per-type 2 --out results/baseline_cot_real_subset.json
135
+ ```
136
+
137
+ ### Train the agent on HF Jobs
138
+
139
+ ```bash
140
+ hf jobs run --flavor a10g-large --timeout 1h \
141
+ --secrets HF_TOKEN \
142
+ -e HF_USERNAME=<you> -e STEPS=150 -e BATCH=2 -e NUM_GENS=2 \
143
+ -v hf://datasets/<you>/promptops-arena-src:/code:ro \
144
+ pytorch/pytorch:2.4.1-cuda12.1-cudnn9-runtime \
145
+ bash /code/scripts/hf_job_entry.sh
146
+ ```
147
+
148
+ Cost: ~$0.75 for 150 steps. The job uploads
149
+ `outputs/grpo-lora` and `training_log.jsonl` to
150
+ `<you>/promptops-arena-agent`.
151
+
152
+ ### Evaluate the trained agent
153
+
154
+ ```bash
155
+ hf download Dar3devil/promptops-arena-agent --local-dir outputs/grpo-lora
156
+ python scripts/eval_trained.py --adapter outputs/grpo-lora --per-type 2 \
157
+ --out results/trained_agent.json
158
+ python scripts/plot_results.py
159
+ ```
160
+
161
+ ## Project layout
162
+
163
+ ```
164
+ src/envs/promptops_arena/
165
+ ├── server/
166
+ │ ├── environment.py # OpenEnv Environment subclass: reset/step/state
167
+ │ ├── rewards.py # decomposed, bounded reward
168
+ │ └── app.py # FastAPI server (out-of-process)
169
+ ├── verifiers/
170
+ │ ├── math_verifier.py # tag/boxed extraction + exact match
171
+ │ ├── code_verifier.py # subprocess exec + unit tests + timeout
172
+ │ └── json_verifier.py # jsonschema + expected match (None-stripped)
173
+ ├── tasks/
174
+ │ ├── math.jsonl, code.jsonl, json_extract.jsonl # 60 train + 30 test
175
+ │ └── loader.py
176
+ ├── llm_under_test.py # frozen Qwen2.5-0.5B (real) + stub backend
177
+ └── client.py # OpenEnv EnvClient subclass
178
+
179
+ scripts/
180
+ ├── run_baseline.py # zero-shot / CoT / untrained-agent baselines
181
+ ├── train_grpo.py # GRPO with TRL 0.21
182
+ ├── eval_trained.py # load LoRA + eval on test split
183
+ ├── plot_results.py # comparison.json + reward curve png
184
+ ├── hf_job_entry.sh # HF Jobs entrypoint (pinned trl 0.21 stack)
185
+ └── upload_src_to_hf.py # mirror local repo to a private HF dataset
186
+
187
+ tests/
188
+ └── test_rewards.py # 22 adversarial reward tests (all pass)
189
+ ```
190
+
191
+ ## Judging rubric self-assessment
192
+
193
+ | Weight | Criterion | What we built |
194
+ |---:|---|---|
195
+ | 40% | Environment Innovation | Two-model setup (trained agent writes prompts for a frozen LLM-under-test). Reward grounded in another model's verified behavior. Multi-task transfer (math/code/json) with one agent. |
196
+ | 30% | Storytelling & Presentation | Live Gradio Space lets a judge type a prompt and watch the LLM-under-test respond + see reward decompose. Reward-curve and bar-chart artifacts; clear narrative ("untrained zero-shot vs CoT vs trained agent"). |
197
+ | 20% | Showing Improvement | `results/comparison.json` and `docs/reward_curve.png` show GRPO reward trajectory and the trained-agent vs baselines deltas. |
198
+ | 10% | Reward & Pipeline | Decomposed reward (correctness/format/brevity), 22 adversarial tests, programmatic verifiers (no reward model), full HF Jobs pipeline scripted end-to-end. |
199
+
200
+ ## Stack
201
+
202
+ - **Agent:** `Qwen/Qwen2.5-1.5B-Instruct` + LoRA (r=16, target = all attn + MLP).
203
+ - **LLM-under-test:** `Qwen/Qwen2.5-0.5B-Instruct`, frozen, loaded once.
204
+ - **Trainer:** TRL 0.21 GRPO, β=0.04, T=1.0, 150 steps × G=2 generations.
205
+ - **Compute:** HF Jobs `a10g-large` (1× A10G 24GB).
206
+ - **Demo:** HF Space (Gradio).
207
+
208
+ ## License
209
+
210
+ MIT.
app.py ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ PromptOps Arena — HF Space demo (Gradio).
3
+
4
+ Tabs:
5
+ 1. Try the env: pick a task, edit a system prompt, see the LLM-under-test
6
+ respond + the per-component reward. Up to 3 edit turns per episode.
7
+ 2. Reward curve: training_log.jsonl rolling avg over GRPO rollouts.
8
+ 3. Baselines vs trained agent: bar chart of mean reward / accuracy.
9
+
10
+ The frozen LLM-under-test runs in-process. ZeroGPU is used at first inference.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import os
17
+ import sys
18
+ from pathlib import Path
19
+ from typing import Any, Dict, List, Tuple
20
+
21
+ # Make src importable regardless of where Gradio runs
22
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
23
+
24
+ import gradio as gr # type: ignore
25
+
26
+ # Default to the real backend on Spaces; allow override
27
+ os.environ.setdefault("PROMPTOPS_LLM_BACKEND", "transformers")
28
+
29
+ from src.envs.promptops_arena.server.environment import PromptOpsArenaEnvironment # noqa: E402
30
+ from src.envs.promptops_arena.tasks import load_tasks # noqa: E402
31
+
32
+ ENV = PromptOpsArenaEnvironment(split="test", seed=0)
33
+ ALL_TASKS: List[dict] = load_tasks(split="test")
34
+ TASKS_BY_ID: Dict[str, dict] = {t["id"]: t for t in ALL_TASKS}
35
+
36
+
37
+ SUGGESTED_PROMPTS = {
38
+ "math": (
39
+ "You are a careful math solver. Solve step by step internally, then "
40
+ "output ONLY the final numeric answer inside <answer>...</answer> tags. "
41
+ "No units, no extra words."
42
+ ),
43
+ "code": (
44
+ "You are a Python coder. Output exactly one ```python ...``` code block "
45
+ "containing only the requested function definition. No prose, no examples."
46
+ ),
47
+ "json": (
48
+ "You are a JSON extractor. Output exactly one ```json ...``` code block "
49
+ "containing a valid JSON object that matches the schema. No prose."
50
+ ),
51
+ }
52
+
53
+
54
+ def list_task_choices() -> List[Tuple[str, str]]:
55
+ out: List[Tuple[str, str]] = []
56
+ for t in ALL_TASKS:
57
+ label = f"[{t['type']}] {t['id']}: {t['question'][:70]}"
58
+ out.append((label, t["id"]))
59
+ return out
60
+
61
+
62
+ def get_task_info(task_id: str) -> Tuple[str, str, str]:
63
+ t = TASKS_BY_ID.get(task_id)
64
+ if not t:
65
+ return "", "", ""
66
+ schema = ""
67
+ if t.get("type") == "json" and "schema" in t:
68
+ schema = f"\n\nSchema: ```json\n{json.dumps(t['schema'], indent=2)}\n```"
69
+ if t.get("type") == "code" and "tests" in t:
70
+ schema = "\n\nUnit tests:\n```python\n" + "\n".join(t["tests"]) + "\n```"
71
+ return t["question"] + schema, t.get("type", ""), SUGGESTED_PROMPTS.get(t.get("type", ""), "")
72
+
73
+
74
+ def run_prompt(task_id: str, system_prompt: str) -> Tuple[str, str, str]:
75
+ """Run one shot of [system_prompt, task] through the env."""
76
+ t = TASKS_BY_ID.get(task_id)
77
+ if t is None:
78
+ return "(no task selected)", "", ""
79
+ if not (system_prompt or "").strip():
80
+ return "(empty prompt)", "", ""
81
+ res = ENV.execute_prompt(t, system_prompt)
82
+ completion = res["completion"]
83
+ rd = res["reward"]
84
+ breakdown = (
85
+ f"correctness: {rd['correctness']:.2f}\n"
86
+ f"format : {rd['format']:.2f} (×0.1 in total)\n"
87
+ f"brevity : {rd['brevity']:+.3f}\n"
88
+ f"-------\n"
89
+ f"TOTAL : {rd['total']:+.3f}"
90
+ )
91
+ verifier = res.get("verifier", {})
92
+ details = verifier.get("details", "")
93
+ return completion, breakdown, details
94
+
95
+
96
+ def load_reward_curve_image() -> str | None:
97
+ p = Path(__file__).resolve().parent / "docs" / "reward_curve.png"
98
+ return str(p) if p.exists() else None
99
+
100
+
101
+ def load_comparison_image() -> str | None:
102
+ p = Path(__file__).resolve().parent / "docs" / "baseline_comparison.png"
103
+ return str(p) if p.exists() else None
104
+
105
+
106
+ def load_comparison_table() -> str:
107
+ p = Path(__file__).resolve().parent / "results" / "comparison.json"
108
+ if not p.exists():
109
+ return "_No comparison.json yet — train + run plot_results.py to populate._"
110
+ d = json.loads(p.read_text(encoding="utf-8"))
111
+ rows = d.get("policies", {})
112
+ if not rows:
113
+ return "_comparison.json is empty._"
114
+ lines = [
115
+ "| policy | n | correct | format | mean_reward |",
116
+ "|---|---:|---:|---:|---:|",
117
+ ]
118
+ for label, r in rows.items():
119
+ lines.append(
120
+ f"| {label} | {r['n']} | {r['correct']} | {r['format']} | {r['mean_reward']:+.3f} |"
121
+ )
122
+ return "\n".join(lines)
123
+
124
+
125
+ # ---------------------------------------------------------------------------
126
+ # UI
127
+ # ---------------------------------------------------------------------------
128
+
129
+ INTRO = """
130
+ # PromptOps Arena 🎯
131
+
132
+ > An RL environment where an agent learns to **write better prompts** via GRPO,
133
+ > across math, code, and JSON-extraction tasks.
134
+
135
+ - **Agent (trained):** Qwen2.5-1.5B-Instruct + LoRA, optimized with GRPO.
136
+ - **LLM-under-test (frozen):** Qwen2.5-0.5B-Instruct.
137
+ - **Reward:** `correctness + 0.1·format + brevity_penalty`, all programmatic.
138
+
139
+ Try writing your own system prompts in the **Try the env** tab.
140
+ """
141
+
142
+
143
+ with gr.Blocks(title="PromptOps Arena", theme=gr.themes.Soft()) as demo:
144
+ gr.Markdown(INTRO)
145
+
146
+ with gr.Tab("Try the env"):
147
+ with gr.Row():
148
+ task_dd = gr.Dropdown(
149
+ choices=list_task_choices(),
150
+ value=ALL_TASKS[0]["id"] if ALL_TASKS else None,
151
+ label="Pick a task",
152
+ interactive=True,
153
+ )
154
+ task_text = gr.Markdown(label="Task")
155
+ task_type_box = gr.Textbox(label="task type", interactive=False)
156
+ with gr.Row():
157
+ with gr.Column():
158
+ system_prompt = gr.Textbox(
159
+ label="Your system prompt (this is the action)",
160
+ lines=8,
161
+ placeholder="Write the system prompt to give to the small frozen LLM…",
162
+ )
163
+ with gr.Row():
164
+ suggest_btn = gr.Button("Use suggested prompt")
165
+ run_btn = gr.Button("▶ Run", variant="primary")
166
+ with gr.Column():
167
+ completion_out = gr.Textbox(
168
+ label="LLM-under-test completion", lines=8, interactive=False,
169
+ )
170
+ reward_out = gr.Textbox(
171
+ label="Reward decomposition", lines=6, interactive=False,
172
+ )
173
+ verifier_out = gr.Textbox(
174
+ label="Verifier details", lines=2, interactive=False,
175
+ )
176
+
177
+ def _on_task(task_id):
178
+ text, ttype, suggested = get_task_info(task_id)
179
+ return text, ttype, suggested
180
+
181
+ task_dd.change(_on_task, inputs=task_dd, outputs=[task_text, task_type_box, system_prompt])
182
+ suggest_btn.click(_on_task, inputs=task_dd, outputs=[task_text, task_type_box, system_prompt])
183
+ run_btn.click(run_prompt, inputs=[task_dd, system_prompt],
184
+ outputs=[completion_out, reward_out, verifier_out])
185
+
186
+ with gr.Tab("Reward curve"):
187
+ gr.Markdown("### GRPO training reward curve\n"
188
+ "Each point is the env's total reward for one rollout during training.")
189
+ rc_img = gr.Image(value=load_reward_curve_image(), label="reward_curve.png",
190
+ interactive=False, show_label=False)
191
+ gr.Markdown(
192
+ "_If this is empty, training hasn't been run yet or `docs/reward_curve.png` "
193
+ "is missing. Run `scripts/plot_results.py` after training._"
194
+ )
195
+
196
+ with gr.Tab("Baselines vs trained agent"):
197
+ gr.Markdown("### Comparison on the held-out test split\n")
198
+ cmp_img = gr.Image(value=load_comparison_image(), label="baseline_comparison.png",
199
+ interactive=False, show_label=False)
200
+ gr.Markdown(load_comparison_table())
201
+
202
+ with gr.Tab("How it works"):
203
+ gr.Markdown((Path(__file__).resolve().parent / "docs" / "SCOPE.md").read_text(encoding="utf-8"))
204
+
205
+
206
+ if __name__ == "__main__":
207
+ demo.queue().launch()
docs/SCOPE.md ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PromptOps Arena — Scope (Locked)
2
+
3
+ > Locked at T+0. Any feature not on this page is OUT OF SCOPE for the 48h hackathon.
4
+
5
+ ## Thesis (one sentence)
6
+
7
+ An OpenEnv RL environment where an agent learns, via GRPO, to write and iteratively edit prompts that maximize verifiable task success on a frozen LLM-under-test, across math/code/JSON tasks — demonstrating *transferable* prompt-engineering strategy as a learned skill.
8
+
9
+ ## Models (locked)
10
+
11
+ | Role | Model | Notes |
12
+ |---|---|---|
13
+ | Agent (trained) | `Qwen/Qwen2.5-1.5B-Instruct` + LoRA | Trained with GRPO via Unsloth, 4-bit |
14
+ | LLM-under-test (frozen) | `Qwen/Qwen2.5-0.5B-Instruct` | Never trained. Loaded once at module top. |
15
+
16
+ ## Tasks (locked)
17
+
18
+ | Type | Source | Count (train) | Count (test, held-out) | Verifier |
19
+ |---|---|---|---|---|
20
+ | Math | GSM8K subset | 30 | 10 | Exact match on `\boxed{}` or `<answer>` extraction |
21
+ | Code | MBPP subset | 20 | 10 | Subprocess `exec` with timeout, run unit tests |
22
+ | JSON extraction | Hand-built | 10 | 10 | `jsonschema.validate` on parsed output |
23
+
24
+ Total: 60 train / 30 test.
25
+
26
+ ## Episode contract
27
+
28
+ - Agent receives task text + task type + previous prompt (if any) + previous completion (if any) + previous reward
29
+ - Agent emits a **new full system prompt** (replace, not diff — simplest action space)
30
+ - Env runs LLM-under-test with `[system_prompt, user_task]` once
31
+ - Verifier returns 0/1 correctness + format/brevity bonuses
32
+ - Episode terminates when correctness == 1.0 OR `edit_turn >= 3`
33
+
34
+ ## Reward (locked)
35
+
36
+ ```
37
+ total = correctness + 0.1 * format_bonus + brevity_penalty
38
+ ```
39
+ - `correctness ∈ {0, 1}` — programmatic verifier
40
+ - `format_bonus ∈ {0, 1}` — required tags present
41
+ - `brevity_penalty ∈ [-0.1, 0]` — only if prompt > 800 chars
42
+ - All components logged separately
43
+
44
+ ## Compute budget
45
+
46
+ - Local smoke tests: CPU + small batches, Windows
47
+ - Full training: **HF Jobs `a10g-large`, ≤2h timeout**
48
+ - Demo: **HuggingFace Space, ZeroGPU**
49
+
50
+ ## Out of scope (will not build)
51
+
52
+ - Multi-agent / hierarchical agents
53
+ - RAG, web search, tool use beyond verifier
54
+ - Persistent memory across episodes
55
+ - Custom reward model (we use programmatic verifiers)
56
+ - vLLM serving (transformers `generate()` is fine for 0.5B)
57
+ - Public Docker Space for env (in-process env in Gradio Space is enough)
58
+ - 4th task type (translation/summarization)
59
+ - 3B agent (only if Phase 5 has >2h slack)
60
+
61
+ ## Submission targets
62
+
63
+ - HuggingFace Space: `<user>/promptops-arena`
64
+ - Model repo: `<user>/promptops-arena-agent` (LoRA adapter only)
65
+ - GitHub repo: public
66
+ - Video: ≤90 seconds, hosted on YouTube/Loom (UNLISTED), linked from README — never committed
67
+
68
+ ## Judging weights
69
+
70
+ - Environment Innovation 40%
71
+ - Storytelling & Presentation 30%
72
+ - Showing Improvement in Rewards 20%
73
+ - Reward & Training Pipeline 10%
74
+
75
+ ## Time gates (hard)
76
+
77
+ - T+24h: training must have started or drop to 2 task types
78
+ - T+36h: must have reward improvement; else ship "untrained agent vs zero-shot"
79
+ - T+44h: README + video + Space MUST be done; freeze features
docs/baseline_comparison.png ADDED
docs/reward_curve.png ADDED

Git LFS Details

  • SHA256: 6381dcf7eff1306226598475b8a1f0ded2e32bcd7b510b0286b6d26bbfbfc999
  • Pointer size: 131 Bytes
  • Size of remote file: 152 kB
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ openenv-core>=0.1.0
2
+ fastapi>=0.110.0
3
+ uvicorn>=0.27.0
4
+ requests>=2.31.0
5
+ transformers>=4.45.0
6
+ torch>=2.4.0
7
+ jsonschema>=4.20.0
8
+ datasets>=2.20.0
9
+ huggingface_hub>=0.25.0
10
+ gradio==4.44.0
11
+ matplotlib>=3.8.0
12
+ pandas>=2.2.0
13
+ pytest>=8.0.0
results/baseline_cot_real.json ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "policy": "cot",
3
+ "split": "test",
4
+ "llm_backend": "transformers",
5
+ "by_type": {
6
+ "math": {
7
+ "n": 4,
8
+ "correct": 1,
9
+ "format": 4
10
+ },
11
+ "code": {
12
+ "n": 4,
13
+ "correct": 3,
14
+ "format": 4
15
+ },
16
+ "json": {
17
+ "n": 4,
18
+ "correct": 4,
19
+ "format": 4
20
+ }
21
+ },
22
+ "overall": {
23
+ "n": 12,
24
+ "correct": 8,
25
+ "format": 12,
26
+ "mean_reward": 0.7666666666666666
27
+ },
28
+ "rows": [
29
+ {
30
+ "task_id": "math_t01",
31
+ "task_type": "math",
32
+ "policy": "cot",
33
+ "edit_turns": 1,
34
+ "final_reward": 0.1,
35
+ "correct": false,
36
+ "format_ok": true,
37
+ "components": {
38
+ "correctness": 0.0,
39
+ "format": 1.0,
40
+ "brevity": -0.0,
41
+ "total": 0.1
42
+ }
43
+ },
44
+ {
45
+ "task_id": "math_t02",
46
+ "task_type": "math",
47
+ "policy": "cot",
48
+ "edit_turns": 1,
49
+ "final_reward": 0.1,
50
+ "correct": false,
51
+ "format_ok": true,
52
+ "components": {
53
+ "correctness": 0.0,
54
+ "format": 1.0,
55
+ "brevity": -0.0,
56
+ "total": 0.1
57
+ }
58
+ },
59
+ {
60
+ "task_id": "math_t03",
61
+ "task_type": "math",
62
+ "policy": "cot",
63
+ "edit_turns": 1,
64
+ "final_reward": 0.1,
65
+ "correct": false,
66
+ "format_ok": true,
67
+ "components": {
68
+ "correctness": 0.0,
69
+ "format": 1.0,
70
+ "brevity": -0.0,
71
+ "total": 0.1
72
+ }
73
+ },
74
+ {
75
+ "task_id": "math_t04",
76
+ "task_type": "math",
77
+ "policy": "cot",
78
+ "edit_turns": 1,
79
+ "final_reward": 1.1,
80
+ "correct": true,
81
+ "format_ok": true,
82
+ "components": {
83
+ "correctness": 1.0,
84
+ "format": 1.0,
85
+ "brevity": -0.0,
86
+ "total": 1.1
87
+ }
88
+ },
89
+ {
90
+ "task_id": "code_t01",
91
+ "task_type": "code",
92
+ "policy": "cot",
93
+ "edit_turns": 1,
94
+ "final_reward": 1.1,
95
+ "correct": true,
96
+ "format_ok": true,
97
+ "components": {
98
+ "correctness": 1.0,
99
+ "format": 1.0,
100
+ "brevity": -0.0,
101
+ "total": 1.1
102
+ }
103
+ },
104
+ {
105
+ "task_id": "code_t02",
106
+ "task_type": "code",
107
+ "policy": "cot",
108
+ "edit_turns": 1,
109
+ "final_reward": 1.1,
110
+ "correct": true,
111
+ "format_ok": true,
112
+ "components": {
113
+ "correctness": 1.0,
114
+ "format": 1.0,
115
+ "brevity": -0.0,
116
+ "total": 1.1
117
+ }
118
+ },
119
+ {
120
+ "task_id": "code_t03",
121
+ "task_type": "code",
122
+ "policy": "cot",
123
+ "edit_turns": 1,
124
+ "final_reward": 0.1,
125
+ "correct": false,
126
+ "format_ok": true,
127
+ "components": {
128
+ "correctness": 0.0,
129
+ "format": 1.0,
130
+ "brevity": -0.0,
131
+ "total": 0.1
132
+ }
133
+ },
134
+ {
135
+ "task_id": "code_t04",
136
+ "task_type": "code",
137
+ "policy": "cot",
138
+ "edit_turns": 1,
139
+ "final_reward": 1.1,
140
+ "correct": true,
141
+ "format_ok": true,
142
+ "components": {
143
+ "correctness": 1.0,
144
+ "format": 1.0,
145
+ "brevity": -0.0,
146
+ "total": 1.1
147
+ }
148
+ },
149
+ {
150
+ "task_id": "json_t01",
151
+ "task_type": "json",
152
+ "policy": "cot",
153
+ "edit_turns": 1,
154
+ "final_reward": 1.1,
155
+ "correct": true,
156
+ "format_ok": true,
157
+ "components": {
158
+ "correctness": 1.0,
159
+ "format": 1.0,
160
+ "brevity": -0.0,
161
+ "total": 1.1
162
+ }
163
+ },
164
+ {
165
+ "task_id": "json_t02",
166
+ "task_type": "json",
167
+ "policy": "cot",
168
+ "edit_turns": 1,
169
+ "final_reward": 1.1,
170
+ "correct": true,
171
+ "format_ok": true,
172
+ "components": {
173
+ "correctness": 1.0,
174
+ "format": 1.0,
175
+ "brevity": -0.0,
176
+ "total": 1.1
177
+ }
178
+ },
179
+ {
180
+ "task_id": "json_t03",
181
+ "task_type": "json",
182
+ "policy": "cot",
183
+ "edit_turns": 1,
184
+ "final_reward": 1.1,
185
+ "correct": true,
186
+ "format_ok": true,
187
+ "components": {
188
+ "correctness": 1.0,
189
+ "format": 1.0,
190
+ "brevity": -0.0,
191
+ "total": 1.1
192
+ }
193
+ },
194
+ {
195
+ "task_id": "json_t04",
196
+ "task_type": "json",
197
+ "policy": "cot",
198
+ "edit_turns": 1,
199
+ "final_reward": 1.1,
200
+ "correct": true,
201
+ "format_ok": true,
202
+ "components": {
203
+ "correctness": 1.0,
204
+ "format": 1.0,
205
+ "brevity": -0.0,
206
+ "total": 1.1
207
+ }
208
+ }
209
+ ]
210
+ }
results/baseline_cot_real_subset.json ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "policy": "cot",
3
+ "split": "test",
4
+ "llm_backend": "transformers",
5
+ "by_type": {
6
+ "math": {
7
+ "n": 2,
8
+ "correct": 0,
9
+ "format": 2
10
+ },
11
+ "code": {
12
+ "n": 2,
13
+ "correct": 2,
14
+ "format": 2
15
+ },
16
+ "json": {
17
+ "n": 2,
18
+ "correct": 2,
19
+ "format": 2
20
+ }
21
+ },
22
+ "overall": {
23
+ "n": 6,
24
+ "correct": 4,
25
+ "format": 6,
26
+ "mean_reward": 0.7666666666666667
27
+ },
28
+ "rows": [
29
+ {
30
+ "task_id": "math_t01",
31
+ "task_type": "math",
32
+ "policy": "cot",
33
+ "edit_turns": 1,
34
+ "final_reward": 0.1,
35
+ "correct": false,
36
+ "format_ok": true,
37
+ "components": {
38
+ "correctness": 0.0,
39
+ "format": 1.0,
40
+ "brevity": -0.0,
41
+ "total": 0.1
42
+ }
43
+ },
44
+ {
45
+ "task_id": "math_t02",
46
+ "task_type": "math",
47
+ "policy": "cot",
48
+ "edit_turns": 1,
49
+ "final_reward": 0.1,
50
+ "correct": false,
51
+ "format_ok": true,
52
+ "components": {
53
+ "correctness": 0.0,
54
+ "format": 1.0,
55
+ "brevity": -0.0,
56
+ "total": 0.1
57
+ }
58
+ },
59
+ {
60
+ "task_id": "code_t01",
61
+ "task_type": "code",
62
+ "policy": "cot",
63
+ "edit_turns": 1,
64
+ "final_reward": 1.1,
65
+ "correct": true,
66
+ "format_ok": true,
67
+ "components": {
68
+ "correctness": 1.0,
69
+ "format": 1.0,
70
+ "brevity": -0.0,
71
+ "total": 1.1
72
+ }
73
+ },
74
+ {
75
+ "task_id": "code_t02",
76
+ "task_type": "code",
77
+ "policy": "cot",
78
+ "edit_turns": 1,
79
+ "final_reward": 1.1,
80
+ "correct": true,
81
+ "format_ok": true,
82
+ "components": {
83
+ "correctness": 1.0,
84
+ "format": 1.0,
85
+ "brevity": -0.0,
86
+ "total": 1.1
87
+ }
88
+ },
89
+ {
90
+ "task_id": "json_t01",
91
+ "task_type": "json",
92
+ "policy": "cot",
93
+ "edit_turns": 1,
94
+ "final_reward": 1.1,
95
+ "correct": true,
96
+ "format_ok": true,
97
+ "components": {
98
+ "correctness": 1.0,
99
+ "format": 1.0,
100
+ "brevity": -0.0,
101
+ "total": 1.1
102
+ }
103
+ },
104
+ {
105
+ "task_id": "json_t02",
106
+ "task_type": "json",
107
+ "policy": "cot",
108
+ "edit_turns": 1,
109
+ "final_reward": 1.1,
110
+ "correct": true,
111
+ "format_ok": true,
112
+ "components": {
113
+ "correctness": 1.0,
114
+ "format": 1.0,
115
+ "brevity": -0.0,
116
+ "total": 1.1
117
+ }
118
+ }
119
+ ]
120
+ }
results/baseline_cot_stub.json ADDED
@@ -0,0 +1,480 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "policy": "cot",
3
+ "split": "test",
4
+ "llm_backend": "stub",
5
+ "by_type": {
6
+ "math": {
7
+ "n": 10,
8
+ "correct": 0,
9
+ "format": 10
10
+ },
11
+ "code": {
12
+ "n": 10,
13
+ "correct": 0,
14
+ "format": 10
15
+ },
16
+ "json": {
17
+ "n": 10,
18
+ "correct": 0,
19
+ "format": 10
20
+ }
21
+ },
22
+ "overall": {
23
+ "n": 30,
24
+ "correct": 0,
25
+ "format": 30,
26
+ "mean_reward": 0.1
27
+ },
28
+ "rows": [
29
+ {
30
+ "task_id": "math_t01",
31
+ "task_type": "math",
32
+ "policy": "cot",
33
+ "edit_turns": 1,
34
+ "final_reward": 0.1,
35
+ "correct": false,
36
+ "format_ok": true,
37
+ "components": {
38
+ "correctness": 0.0,
39
+ "format": 1.0,
40
+ "brevity": -0.0,
41
+ "total": 0.1
42
+ }
43
+ },
44
+ {
45
+ "task_id": "math_t02",
46
+ "task_type": "math",
47
+ "policy": "cot",
48
+ "edit_turns": 1,
49
+ "final_reward": 0.1,
50
+ "correct": false,
51
+ "format_ok": true,
52
+ "components": {
53
+ "correctness": 0.0,
54
+ "format": 1.0,
55
+ "brevity": -0.0,
56
+ "total": 0.1
57
+ }
58
+ },
59
+ {
60
+ "task_id": "math_t03",
61
+ "task_type": "math",
62
+ "policy": "cot",
63
+ "edit_turns": 1,
64
+ "final_reward": 0.1,
65
+ "correct": false,
66
+ "format_ok": true,
67
+ "components": {
68
+ "correctness": 0.0,
69
+ "format": 1.0,
70
+ "brevity": -0.0,
71
+ "total": 0.1
72
+ }
73
+ },
74
+ {
75
+ "task_id": "math_t04",
76
+ "task_type": "math",
77
+ "policy": "cot",
78
+ "edit_turns": 1,
79
+ "final_reward": 0.1,
80
+ "correct": false,
81
+ "format_ok": true,
82
+ "components": {
83
+ "correctness": 0.0,
84
+ "format": 1.0,
85
+ "brevity": -0.0,
86
+ "total": 0.1
87
+ }
88
+ },
89
+ {
90
+ "task_id": "math_t05",
91
+ "task_type": "math",
92
+ "policy": "cot",
93
+ "edit_turns": 1,
94
+ "final_reward": 0.1,
95
+ "correct": false,
96
+ "format_ok": true,
97
+ "components": {
98
+ "correctness": 0.0,
99
+ "format": 1.0,
100
+ "brevity": -0.0,
101
+ "total": 0.1
102
+ }
103
+ },
104
+ {
105
+ "task_id": "math_t06",
106
+ "task_type": "math",
107
+ "policy": "cot",
108
+ "edit_turns": 1,
109
+ "final_reward": 0.1,
110
+ "correct": false,
111
+ "format_ok": true,
112
+ "components": {
113
+ "correctness": 0.0,
114
+ "format": 1.0,
115
+ "brevity": -0.0,
116
+ "total": 0.1
117
+ }
118
+ },
119
+ {
120
+ "task_id": "math_t07",
121
+ "task_type": "math",
122
+ "policy": "cot",
123
+ "edit_turns": 1,
124
+ "final_reward": 0.1,
125
+ "correct": false,
126
+ "format_ok": true,
127
+ "components": {
128
+ "correctness": 0.0,
129
+ "format": 1.0,
130
+ "brevity": -0.0,
131
+ "total": 0.1
132
+ }
133
+ },
134
+ {
135
+ "task_id": "math_t08",
136
+ "task_type": "math",
137
+ "policy": "cot",
138
+ "edit_turns": 1,
139
+ "final_reward": 0.1,
140
+ "correct": false,
141
+ "format_ok": true,
142
+ "components": {
143
+ "correctness": 0.0,
144
+ "format": 1.0,
145
+ "brevity": -0.0,
146
+ "total": 0.1
147
+ }
148
+ },
149
+ {
150
+ "task_id": "math_t09",
151
+ "task_type": "math",
152
+ "policy": "cot",
153
+ "edit_turns": 1,
154
+ "final_reward": 0.1,
155
+ "correct": false,
156
+ "format_ok": true,
157
+ "components": {
158
+ "correctness": 0.0,
159
+ "format": 1.0,
160
+ "brevity": -0.0,
161
+ "total": 0.1
162
+ }
163
+ },
164
+ {
165
+ "task_id": "math_t10",
166
+ "task_type": "math",
167
+ "policy": "cot",
168
+ "edit_turns": 1,
169
+ "final_reward": 0.1,
170
+ "correct": false,
171
+ "format_ok": true,
172
+ "components": {
173
+ "correctness": 0.0,
174
+ "format": 1.0,
175
+ "brevity": -0.0,
176
+ "total": 0.1
177
+ }
178
+ },
179
+ {
180
+ "task_id": "code_t01",
181
+ "task_type": "code",
182
+ "policy": "cot",
183
+ "edit_turns": 1,
184
+ "final_reward": 0.1,
185
+ "correct": false,
186
+ "format_ok": true,
187
+ "components": {
188
+ "correctness": 0.0,
189
+ "format": 1.0,
190
+ "brevity": -0.0,
191
+ "total": 0.1
192
+ }
193
+ },
194
+ {
195
+ "task_id": "code_t02",
196
+ "task_type": "code",
197
+ "policy": "cot",
198
+ "edit_turns": 1,
199
+ "final_reward": 0.1,
200
+ "correct": false,
201
+ "format_ok": true,
202
+ "components": {
203
+ "correctness": 0.0,
204
+ "format": 1.0,
205
+ "brevity": -0.0,
206
+ "total": 0.1
207
+ }
208
+ },
209
+ {
210
+ "task_id": "code_t03",
211
+ "task_type": "code",
212
+ "policy": "cot",
213
+ "edit_turns": 1,
214
+ "final_reward": 0.1,
215
+ "correct": false,
216
+ "format_ok": true,
217
+ "components": {
218
+ "correctness": 0.0,
219
+ "format": 1.0,
220
+ "brevity": -0.0,
221
+ "total": 0.1
222
+ }
223
+ },
224
+ {
225
+ "task_id": "code_t04",
226
+ "task_type": "code",
227
+ "policy": "cot",
228
+ "edit_turns": 1,
229
+ "final_reward": 0.1,
230
+ "correct": false,
231
+ "format_ok": true,
232
+ "components": {
233
+ "correctness": 0.0,
234
+ "format": 1.0,
235
+ "brevity": -0.0,
236
+ "total": 0.1
237
+ }
238
+ },
239
+ {
240
+ "task_id": "code_t05",
241
+ "task_type": "code",
242
+ "policy": "cot",
243
+ "edit_turns": 1,
244
+ "final_reward": 0.1,
245
+ "correct": false,
246
+ "format_ok": true,
247
+ "components": {
248
+ "correctness": 0.0,
249
+ "format": 1.0,
250
+ "brevity": -0.0,
251
+ "total": 0.1
252
+ }
253
+ },
254
+ {
255
+ "task_id": "code_t06",
256
+ "task_type": "code",
257
+ "policy": "cot",
258
+ "edit_turns": 1,
259
+ "final_reward": 0.1,
260
+ "correct": false,
261
+ "format_ok": true,
262
+ "components": {
263
+ "correctness": 0.0,
264
+ "format": 1.0,
265
+ "brevity": -0.0,
266
+ "total": 0.1
267
+ }
268
+ },
269
+ {
270
+ "task_id": "code_t07",
271
+ "task_type": "code",
272
+ "policy": "cot",
273
+ "edit_turns": 1,
274
+ "final_reward": 0.1,
275
+ "correct": false,
276
+ "format_ok": true,
277
+ "components": {
278
+ "correctness": 0.0,
279
+ "format": 1.0,
280
+ "brevity": -0.0,
281
+ "total": 0.1
282
+ }
283
+ },
284
+ {
285
+ "task_id": "code_t08",
286
+ "task_type": "code",
287
+ "policy": "cot",
288
+ "edit_turns": 1,
289
+ "final_reward": 0.1,
290
+ "correct": false,
291
+ "format_ok": true,
292
+ "components": {
293
+ "correctness": 0.0,
294
+ "format": 1.0,
295
+ "brevity": -0.0,
296
+ "total": 0.1
297
+ }
298
+ },
299
+ {
300
+ "task_id": "code_t09",
301
+ "task_type": "code",
302
+ "policy": "cot",
303
+ "edit_turns": 1,
304
+ "final_reward": 0.1,
305
+ "correct": false,
306
+ "format_ok": true,
307
+ "components": {
308
+ "correctness": 0.0,
309
+ "format": 1.0,
310
+ "brevity": -0.0,
311
+ "total": 0.1
312
+ }
313
+ },
314
+ {
315
+ "task_id": "code_t10",
316
+ "task_type": "code",
317
+ "policy": "cot",
318
+ "edit_turns": 1,
319
+ "final_reward": 0.1,
320
+ "correct": false,
321
+ "format_ok": true,
322
+ "components": {
323
+ "correctness": 0.0,
324
+ "format": 1.0,
325
+ "brevity": -0.0,
326
+ "total": 0.1
327
+ }
328
+ },
329
+ {
330
+ "task_id": "json_t01",
331
+ "task_type": "json",
332
+ "policy": "cot",
333
+ "edit_turns": 1,
334
+ "final_reward": 0.1,
335
+ "correct": false,
336
+ "format_ok": true,
337
+ "components": {
338
+ "correctness": 0.0,
339
+ "format": 1.0,
340
+ "brevity": -0.0,
341
+ "total": 0.1
342
+ }
343
+ },
344
+ {
345
+ "task_id": "json_t02",
346
+ "task_type": "json",
347
+ "policy": "cot",
348
+ "edit_turns": 1,
349
+ "final_reward": 0.1,
350
+ "correct": false,
351
+ "format_ok": true,
352
+ "components": {
353
+ "correctness": 0.0,
354
+ "format": 1.0,
355
+ "brevity": -0.0,
356
+ "total": 0.1
357
+ }
358
+ },
359
+ {
360
+ "task_id": "json_t03",
361
+ "task_type": "json",
362
+ "policy": "cot",
363
+ "edit_turns": 1,
364
+ "final_reward": 0.1,
365
+ "correct": false,
366
+ "format_ok": true,
367
+ "components": {
368
+ "correctness": 0.0,
369
+ "format": 1.0,
370
+ "brevity": -0.0,
371
+ "total": 0.1
372
+ }
373
+ },
374
+ {
375
+ "task_id": "json_t04",
376
+ "task_type": "json",
377
+ "policy": "cot",
378
+ "edit_turns": 1,
379
+ "final_reward": 0.1,
380
+ "correct": false,
381
+ "format_ok": true,
382
+ "components": {
383
+ "correctness": 0.0,
384
+ "format": 1.0,
385
+ "brevity": -0.0,
386
+ "total": 0.1
387
+ }
388
+ },
389
+ {
390
+ "task_id": "json_t05",
391
+ "task_type": "json",
392
+ "policy": "cot",
393
+ "edit_turns": 1,
394
+ "final_reward": 0.1,
395
+ "correct": false,
396
+ "format_ok": true,
397
+ "components": {
398
+ "correctness": 0.0,
399
+ "format": 1.0,
400
+ "brevity": -0.0,
401
+ "total": 0.1
402
+ }
403
+ },
404
+ {
405
+ "task_id": "json_t06",
406
+ "task_type": "json",
407
+ "policy": "cot",
408
+ "edit_turns": 1,
409
+ "final_reward": 0.1,
410
+ "correct": false,
411
+ "format_ok": true,
412
+ "components": {
413
+ "correctness": 0.0,
414
+ "format": 1.0,
415
+ "brevity": -0.0,
416
+ "total": 0.1
417
+ }
418
+ },
419
+ {
420
+ "task_id": "json_t07",
421
+ "task_type": "json",
422
+ "policy": "cot",
423
+ "edit_turns": 1,
424
+ "final_reward": 0.1,
425
+ "correct": false,
426
+ "format_ok": true,
427
+ "components": {
428
+ "correctness": 0.0,
429
+ "format": 1.0,
430
+ "brevity": -0.0,
431
+ "total": 0.1
432
+ }
433
+ },
434
+ {
435
+ "task_id": "json_t08",
436
+ "task_type": "json",
437
+ "policy": "cot",
438
+ "edit_turns": 1,
439
+ "final_reward": 0.1,
440
+ "correct": false,
441
+ "format_ok": true,
442
+ "components": {
443
+ "correctness": 0.0,
444
+ "format": 1.0,
445
+ "brevity": -0.0,
446
+ "total": 0.1
447
+ }
448
+ },
449
+ {
450
+ "task_id": "json_t09",
451
+ "task_type": "json",
452
+ "policy": "cot",
453
+ "edit_turns": 1,
454
+ "final_reward": 0.1,
455
+ "correct": false,
456
+ "format_ok": true,
457
+ "components": {
458
+ "correctness": 0.0,
459
+ "format": 1.0,
460
+ "brevity": -0.0,
461
+ "total": 0.1
462
+ }
463
+ },
464
+ {
465
+ "task_id": "json_t10",
466
+ "task_type": "json",
467
+ "policy": "cot",
468
+ "edit_turns": 1,
469
+ "final_reward": 0.1,
470
+ "correct": false,
471
+ "format_ok": true,
472
+ "components": {
473
+ "correctness": 0.0,
474
+ "format": 1.0,
475
+ "brevity": -0.0,
476
+ "total": 0.1
477
+ }
478
+ }
479
+ ]
480
+ }
results/baseline_zero_shot_real.json ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "policy": "zero_shot",
3
+ "split": "test",
4
+ "llm_backend": "transformers",
5
+ "by_type": {
6
+ "math": {
7
+ "n": 4,
8
+ "correct": 2,
9
+ "format": 1
10
+ },
11
+ "code": {
12
+ "n": 4,
13
+ "correct": 2,
14
+ "format": 2
15
+ },
16
+ "json": {
17
+ "n": 4,
18
+ "correct": 4,
19
+ "format": 4
20
+ }
21
+ },
22
+ "overall": {
23
+ "n": 12,
24
+ "correct": 8,
25
+ "format": 7,
26
+ "mean_reward": 0.725
27
+ },
28
+ "rows": [
29
+ {
30
+ "task_id": "math_t01",
31
+ "task_type": "math",
32
+ "policy": "zero_shot",
33
+ "edit_turns": 1,
34
+ "final_reward": 0.0,
35
+ "correct": false,
36
+ "format_ok": false,
37
+ "components": {
38
+ "correctness": 0.0,
39
+ "format": 0.0,
40
+ "brevity": -0.0,
41
+ "total": 0.0
42
+ }
43
+ },
44
+ {
45
+ "task_id": "math_t02",
46
+ "task_type": "math",
47
+ "policy": "zero_shot",
48
+ "edit_turns": 1,
49
+ "final_reward": 1.0,
50
+ "correct": true,
51
+ "format_ok": false,
52
+ "components": {
53
+ "correctness": 1.0,
54
+ "format": 0.0,
55
+ "brevity": -0.0,
56
+ "total": 1.0
57
+ }
58
+ },
59
+ {
60
+ "task_id": "math_t03",
61
+ "task_type": "math",
62
+ "policy": "zero_shot",
63
+ "edit_turns": 1,
64
+ "final_reward": 0.0,
65
+ "correct": false,
66
+ "format_ok": false,
67
+ "components": {
68
+ "correctness": 0.0,
69
+ "format": 0.0,
70
+ "brevity": -0.0,
71
+ "total": 0.0
72
+ }
73
+ },
74
+ {
75
+ "task_id": "math_t04",
76
+ "task_type": "math",
77
+ "policy": "zero_shot",
78
+ "edit_turns": 1,
79
+ "final_reward": 1.1,
80
+ "correct": true,
81
+ "format_ok": true,
82
+ "components": {
83
+ "correctness": 1.0,
84
+ "format": 1.0,
85
+ "brevity": -0.0,
86
+ "total": 1.1
87
+ }
88
+ },
89
+ {
90
+ "task_id": "code_t01",
91
+ "task_type": "code",
92
+ "policy": "zero_shot",
93
+ "edit_turns": 1,
94
+ "final_reward": 1.1,
95
+ "correct": true,
96
+ "format_ok": true,
97
+ "components": {
98
+ "correctness": 1.0,
99
+ "format": 1.0,
100
+ "brevity": -0.0,
101
+ "total": 1.1
102
+ }
103
+ },
104
+ {
105
+ "task_id": "code_t02",
106
+ "task_type": "code",
107
+ "policy": "zero_shot",
108
+ "edit_turns": 1,
109
+ "final_reward": 0.0,
110
+ "correct": false,
111
+ "format_ok": false,
112
+ "components": {
113
+ "correctness": 0.0,
114
+ "format": 0.0,
115
+ "brevity": -0.0,
116
+ "total": 0.0
117
+ }
118
+ },
119
+ {
120
+ "task_id": "code_t03",
121
+ "task_type": "code",
122
+ "policy": "zero_shot",
123
+ "edit_turns": 1,
124
+ "final_reward": 0.0,
125
+ "correct": false,
126
+ "format_ok": false,
127
+ "components": {
128
+ "correctness": 0.0,
129
+ "format": 0.0,
130
+ "brevity": -0.0,
131
+ "total": 0.0
132
+ }
133
+ },
134
+ {
135
+ "task_id": "code_t04",
136
+ "task_type": "code",
137
+ "policy": "zero_shot",
138
+ "edit_turns": 1,
139
+ "final_reward": 1.1,
140
+ "correct": true,
141
+ "format_ok": true,
142
+ "components": {
143
+ "correctness": 1.0,
144
+ "format": 1.0,
145
+ "brevity": -0.0,
146
+ "total": 1.1
147
+ }
148
+ },
149
+ {
150
+ "task_id": "json_t01",
151
+ "task_type": "json",
152
+ "policy": "zero_shot",
153
+ "edit_turns": 1,
154
+ "final_reward": 1.1,
155
+ "correct": true,
156
+ "format_ok": true,
157
+ "components": {
158
+ "correctness": 1.0,
159
+ "format": 1.0,
160
+ "brevity": -0.0,
161
+ "total": 1.1
162
+ }
163
+ },
164
+ {
165
+ "task_id": "json_t02",
166
+ "task_type": "json",
167
+ "policy": "zero_shot",
168
+ "edit_turns": 1,
169
+ "final_reward": 1.1,
170
+ "correct": true,
171
+ "format_ok": true,
172
+ "components": {
173
+ "correctness": 1.0,
174
+ "format": 1.0,
175
+ "brevity": -0.0,
176
+ "total": 1.1
177
+ }
178
+ },
179
+ {
180
+ "task_id": "json_t03",
181
+ "task_type": "json",
182
+ "policy": "zero_shot",
183
+ "edit_turns": 1,
184
+ "final_reward": 1.1,
185
+ "correct": true,
186
+ "format_ok": true,
187
+ "components": {
188
+ "correctness": 1.0,
189
+ "format": 1.0,
190
+ "brevity": -0.0,
191
+ "total": 1.1
192
+ }
193
+ },
194
+ {
195
+ "task_id": "json_t04",
196
+ "task_type": "json",
197
+ "policy": "zero_shot",
198
+ "edit_turns": 1,
199
+ "final_reward": 1.1,
200
+ "correct": true,
201
+ "format_ok": true,
202
+ "components": {
203
+ "correctness": 1.0,
204
+ "format": 1.0,
205
+ "brevity": -0.0,
206
+ "total": 1.1
207
+ }
208
+ }
209
+ ]
210
+ }
results/baseline_zero_shot_real_subset.json ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "policy": "zero_shot",
3
+ "split": "test",
4
+ "llm_backend": "transformers",
5
+ "by_type": {
6
+ "math": {
7
+ "n": 2,
8
+ "correct": 1,
9
+ "format": 0
10
+ },
11
+ "code": {
12
+ "n": 2,
13
+ "correct": 1,
14
+ "format": 1
15
+ },
16
+ "json": {
17
+ "n": 2,
18
+ "correct": 2,
19
+ "format": 2
20
+ }
21
+ },
22
+ "overall": {
23
+ "n": 6,
24
+ "correct": 4,
25
+ "format": 3,
26
+ "mean_reward": 0.7166666666666668
27
+ },
28
+ "rows": [
29
+ {
30
+ "task_id": "math_t01",
31
+ "task_type": "math",
32
+ "policy": "zero_shot",
33
+ "edit_turns": 1,
34
+ "final_reward": 0.0,
35
+ "correct": false,
36
+ "format_ok": false,
37
+ "components": {
38
+ "correctness": 0.0,
39
+ "format": 0.0,
40
+ "brevity": -0.0,
41
+ "total": 0.0
42
+ }
43
+ },
44
+ {
45
+ "task_id": "math_t02",
46
+ "task_type": "math",
47
+ "policy": "zero_shot",
48
+ "edit_turns": 1,
49
+ "final_reward": 1.0,
50
+ "correct": true,
51
+ "format_ok": false,
52
+ "components": {
53
+ "correctness": 1.0,
54
+ "format": 0.0,
55
+ "brevity": -0.0,
56
+ "total": 1.0
57
+ }
58
+ },
59
+ {
60
+ "task_id": "code_t01",
61
+ "task_type": "code",
62
+ "policy": "zero_shot",
63
+ "edit_turns": 1,
64
+ "final_reward": 1.1,
65
+ "correct": true,
66
+ "format_ok": true,
67
+ "components": {
68
+ "correctness": 1.0,
69
+ "format": 1.0,
70
+ "brevity": -0.0,
71
+ "total": 1.1
72
+ }
73
+ },
74
+ {
75
+ "task_id": "code_t02",
76
+ "task_type": "code",
77
+ "policy": "zero_shot",
78
+ "edit_turns": 1,
79
+ "final_reward": 0.0,
80
+ "correct": false,
81
+ "format_ok": false,
82
+ "components": {
83
+ "correctness": 0.0,
84
+ "format": 0.0,
85
+ "brevity": -0.0,
86
+ "total": 0.0
87
+ }
88
+ },
89
+ {
90
+ "task_id": "json_t01",
91
+ "task_type": "json",
92
+ "policy": "zero_shot",
93
+ "edit_turns": 1,
94
+ "final_reward": 1.1,
95
+ "correct": true,
96
+ "format_ok": true,
97
+ "components": {
98
+ "correctness": 1.0,
99
+ "format": 1.0,
100
+ "brevity": -0.0,
101
+ "total": 1.1
102
+ }
103
+ },
104
+ {
105
+ "task_id": "json_t02",
106
+ "task_type": "json",
107
+ "policy": "zero_shot",
108
+ "edit_turns": 1,
109
+ "final_reward": 1.1,
110
+ "correct": true,
111
+ "format_ok": true,
112
+ "components": {
113
+ "correctness": 1.0,
114
+ "format": 1.0,
115
+ "brevity": -0.0,
116
+ "total": 1.1
117
+ }
118
+ }
119
+ ]
120
+ }
results/baseline_zero_shot_stub.json ADDED
@@ -0,0 +1,480 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "policy": "zero_shot",
3
+ "split": "test",
4
+ "llm_backend": "stub",
5
+ "by_type": {
6
+ "math": {
7
+ "n": 10,
8
+ "correct": 0,
9
+ "format": 0
10
+ },
11
+ "code": {
12
+ "n": 10,
13
+ "correct": 0,
14
+ "format": 0
15
+ },
16
+ "json": {
17
+ "n": 10,
18
+ "correct": 0,
19
+ "format": 0
20
+ }
21
+ },
22
+ "overall": {
23
+ "n": 30,
24
+ "correct": 0,
25
+ "format": 0,
26
+ "mean_reward": 0.0
27
+ },
28
+ "rows": [
29
+ {
30
+ "task_id": "math_t01",
31
+ "task_type": "math",
32
+ "policy": "zero_shot",
33
+ "edit_turns": 1,
34
+ "final_reward": 0.0,
35
+ "correct": false,
36
+ "format_ok": false,
37
+ "components": {
38
+ "correctness": 0.0,
39
+ "format": 0.0,
40
+ "brevity": -0.0,
41
+ "total": 0.0
42
+ }
43
+ },
44
+ {
45
+ "task_id": "math_t02",
46
+ "task_type": "math",
47
+ "policy": "zero_shot",
48
+ "edit_turns": 1,
49
+ "final_reward": 0.0,
50
+ "correct": false,
51
+ "format_ok": false,
52
+ "components": {
53
+ "correctness": 0.0,
54
+ "format": 0.0,
55
+ "brevity": -0.0,
56
+ "total": 0.0
57
+ }
58
+ },
59
+ {
60
+ "task_id": "math_t03",
61
+ "task_type": "math",
62
+ "policy": "zero_shot",
63
+ "edit_turns": 1,
64
+ "final_reward": 0.0,
65
+ "correct": false,
66
+ "format_ok": false,
67
+ "components": {
68
+ "correctness": 0.0,
69
+ "format": 0.0,
70
+ "brevity": -0.0,
71
+ "total": 0.0
72
+ }
73
+ },
74
+ {
75
+ "task_id": "math_t04",
76
+ "task_type": "math",
77
+ "policy": "zero_shot",
78
+ "edit_turns": 1,
79
+ "final_reward": 0.0,
80
+ "correct": false,
81
+ "format_ok": false,
82
+ "components": {
83
+ "correctness": 0.0,
84
+ "format": 0.0,
85
+ "brevity": -0.0,
86
+ "total": 0.0
87
+ }
88
+ },
89
+ {
90
+ "task_id": "math_t05",
91
+ "task_type": "math",
92
+ "policy": "zero_shot",
93
+ "edit_turns": 1,
94
+ "final_reward": 0.0,
95
+ "correct": false,
96
+ "format_ok": false,
97
+ "components": {
98
+ "correctness": 0.0,
99
+ "format": 0.0,
100
+ "brevity": -0.0,
101
+ "total": 0.0
102
+ }
103
+ },
104
+ {
105
+ "task_id": "math_t06",
106
+ "task_type": "math",
107
+ "policy": "zero_shot",
108
+ "edit_turns": 1,
109
+ "final_reward": 0.0,
110
+ "correct": false,
111
+ "format_ok": false,
112
+ "components": {
113
+ "correctness": 0.0,
114
+ "format": 0.0,
115
+ "brevity": -0.0,
116
+ "total": 0.0
117
+ }
118
+ },
119
+ {
120
+ "task_id": "math_t07",
121
+ "task_type": "math",
122
+ "policy": "zero_shot",
123
+ "edit_turns": 1,
124
+ "final_reward": 0.0,
125
+ "correct": false,
126
+ "format_ok": false,
127
+ "components": {
128
+ "correctness": 0.0,
129
+ "format": 0.0,
130
+ "brevity": -0.0,
131
+ "total": 0.0
132
+ }
133
+ },
134
+ {
135
+ "task_id": "math_t08",
136
+ "task_type": "math",
137
+ "policy": "zero_shot",
138
+ "edit_turns": 1,
139
+ "final_reward": 0.0,
140
+ "correct": false,
141
+ "format_ok": false,
142
+ "components": {
143
+ "correctness": 0.0,
144
+ "format": 0.0,
145
+ "brevity": -0.0,
146
+ "total": 0.0
147
+ }
148
+ },
149
+ {
150
+ "task_id": "math_t09",
151
+ "task_type": "math",
152
+ "policy": "zero_shot",
153
+ "edit_turns": 1,
154
+ "final_reward": 0.0,
155
+ "correct": false,
156
+ "format_ok": false,
157
+ "components": {
158
+ "correctness": 0.0,
159
+ "format": 0.0,
160
+ "brevity": -0.0,
161
+ "total": 0.0
162
+ }
163
+ },
164
+ {
165
+ "task_id": "math_t10",
166
+ "task_type": "math",
167
+ "policy": "zero_shot",
168
+ "edit_turns": 1,
169
+ "final_reward": 0.0,
170
+ "correct": false,
171
+ "format_ok": false,
172
+ "components": {
173
+ "correctness": 0.0,
174
+ "format": 0.0,
175
+ "brevity": -0.0,
176
+ "total": 0.0
177
+ }
178
+ },
179
+ {
180
+ "task_id": "code_t01",
181
+ "task_type": "code",
182
+ "policy": "zero_shot",
183
+ "edit_turns": 1,
184
+ "final_reward": 0.0,
185
+ "correct": false,
186
+ "format_ok": false,
187
+ "components": {
188
+ "correctness": 0.0,
189
+ "format": 0.0,
190
+ "brevity": -0.0,
191
+ "total": 0.0
192
+ }
193
+ },
194
+ {
195
+ "task_id": "code_t02",
196
+ "task_type": "code",
197
+ "policy": "zero_shot",
198
+ "edit_turns": 1,
199
+ "final_reward": 0.0,
200
+ "correct": false,
201
+ "format_ok": false,
202
+ "components": {
203
+ "correctness": 0.0,
204
+ "format": 0.0,
205
+ "brevity": -0.0,
206
+ "total": 0.0
207
+ }
208
+ },
209
+ {
210
+ "task_id": "code_t03",
211
+ "task_type": "code",
212
+ "policy": "zero_shot",
213
+ "edit_turns": 1,
214
+ "final_reward": 0.0,
215
+ "correct": false,
216
+ "format_ok": false,
217
+ "components": {
218
+ "correctness": 0.0,
219
+ "format": 0.0,
220
+ "brevity": -0.0,
221
+ "total": 0.0
222
+ }
223
+ },
224
+ {
225
+ "task_id": "code_t04",
226
+ "task_type": "code",
227
+ "policy": "zero_shot",
228
+ "edit_turns": 1,
229
+ "final_reward": 0.0,
230
+ "correct": false,
231
+ "format_ok": false,
232
+ "components": {
233
+ "correctness": 0.0,
234
+ "format": 0.0,
235
+ "brevity": -0.0,
236
+ "total": 0.0
237
+ }
238
+ },
239
+ {
240
+ "task_id": "code_t05",
241
+ "task_type": "code",
242
+ "policy": "zero_shot",
243
+ "edit_turns": 1,
244
+ "final_reward": 0.0,
245
+ "correct": false,
246
+ "format_ok": false,
247
+ "components": {
248
+ "correctness": 0.0,
249
+ "format": 0.0,
250
+ "brevity": -0.0,
251
+ "total": 0.0
252
+ }
253
+ },
254
+ {
255
+ "task_id": "code_t06",
256
+ "task_type": "code",
257
+ "policy": "zero_shot",
258
+ "edit_turns": 1,
259
+ "final_reward": 0.0,
260
+ "correct": false,
261
+ "format_ok": false,
262
+ "components": {
263
+ "correctness": 0.0,
264
+ "format": 0.0,
265
+ "brevity": -0.0,
266
+ "total": 0.0
267
+ }
268
+ },
269
+ {
270
+ "task_id": "code_t07",
271
+ "task_type": "code",
272
+ "policy": "zero_shot",
273
+ "edit_turns": 1,
274
+ "final_reward": 0.0,
275
+ "correct": false,
276
+ "format_ok": false,
277
+ "components": {
278
+ "correctness": 0.0,
279
+ "format": 0.0,
280
+ "brevity": -0.0,
281
+ "total": 0.0
282
+ }
283
+ },
284
+ {
285
+ "task_id": "code_t08",
286
+ "task_type": "code",
287
+ "policy": "zero_shot",
288
+ "edit_turns": 1,
289
+ "final_reward": 0.0,
290
+ "correct": false,
291
+ "format_ok": false,
292
+ "components": {
293
+ "correctness": 0.0,
294
+ "format": 0.0,
295
+ "brevity": -0.0,
296
+ "total": 0.0
297
+ }
298
+ },
299
+ {
300
+ "task_id": "code_t09",
301
+ "task_type": "code",
302
+ "policy": "zero_shot",
303
+ "edit_turns": 1,
304
+ "final_reward": 0.0,
305
+ "correct": false,
306
+ "format_ok": false,
307
+ "components": {
308
+ "correctness": 0.0,
309
+ "format": 0.0,
310
+ "brevity": -0.0,
311
+ "total": 0.0
312
+ }
313
+ },
314
+ {
315
+ "task_id": "code_t10",
316
+ "task_type": "code",
317
+ "policy": "zero_shot",
318
+ "edit_turns": 1,
319
+ "final_reward": 0.0,
320
+ "correct": false,
321
+ "format_ok": false,
322
+ "components": {
323
+ "correctness": 0.0,
324
+ "format": 0.0,
325
+ "brevity": -0.0,
326
+ "total": 0.0
327
+ }
328
+ },
329
+ {
330
+ "task_id": "json_t01",
331
+ "task_type": "json",
332
+ "policy": "zero_shot",
333
+ "edit_turns": 1,
334
+ "final_reward": 0.0,
335
+ "correct": false,
336
+ "format_ok": false,
337
+ "components": {
338
+ "correctness": 0.0,
339
+ "format": 0.0,
340
+ "brevity": -0.0,
341
+ "total": 0.0
342
+ }
343
+ },
344
+ {
345
+ "task_id": "json_t02",
346
+ "task_type": "json",
347
+ "policy": "zero_shot",
348
+ "edit_turns": 1,
349
+ "final_reward": 0.0,
350
+ "correct": false,
351
+ "format_ok": false,
352
+ "components": {
353
+ "correctness": 0.0,
354
+ "format": 0.0,
355
+ "brevity": -0.0,
356
+ "total": 0.0
357
+ }
358
+ },
359
+ {
360
+ "task_id": "json_t03",
361
+ "task_type": "json",
362
+ "policy": "zero_shot",
363
+ "edit_turns": 1,
364
+ "final_reward": 0.0,
365
+ "correct": false,
366
+ "format_ok": false,
367
+ "components": {
368
+ "correctness": 0.0,
369
+ "format": 0.0,
370
+ "brevity": -0.0,
371
+ "total": 0.0
372
+ }
373
+ },
374
+ {
375
+ "task_id": "json_t04",
376
+ "task_type": "json",
377
+ "policy": "zero_shot",
378
+ "edit_turns": 1,
379
+ "final_reward": 0.0,
380
+ "correct": false,
381
+ "format_ok": false,
382
+ "components": {
383
+ "correctness": 0.0,
384
+ "format": 0.0,
385
+ "brevity": -0.0,
386
+ "total": 0.0
387
+ }
388
+ },
389
+ {
390
+ "task_id": "json_t05",
391
+ "task_type": "json",
392
+ "policy": "zero_shot",
393
+ "edit_turns": 1,
394
+ "final_reward": 0.0,
395
+ "correct": false,
396
+ "format_ok": false,
397
+ "components": {
398
+ "correctness": 0.0,
399
+ "format": 0.0,
400
+ "brevity": -0.0,
401
+ "total": 0.0
402
+ }
403
+ },
404
+ {
405
+ "task_id": "json_t06",
406
+ "task_type": "json",
407
+ "policy": "zero_shot",
408
+ "edit_turns": 1,
409
+ "final_reward": 0.0,
410
+ "correct": false,
411
+ "format_ok": false,
412
+ "components": {
413
+ "correctness": 0.0,
414
+ "format": 0.0,
415
+ "brevity": -0.0,
416
+ "total": 0.0
417
+ }
418
+ },
419
+ {
420
+ "task_id": "json_t07",
421
+ "task_type": "json",
422
+ "policy": "zero_shot",
423
+ "edit_turns": 1,
424
+ "final_reward": 0.0,
425
+ "correct": false,
426
+ "format_ok": false,
427
+ "components": {
428
+ "correctness": 0.0,
429
+ "format": 0.0,
430
+ "brevity": -0.0,
431
+ "total": 0.0
432
+ }
433
+ },
434
+ {
435
+ "task_id": "json_t08",
436
+ "task_type": "json",
437
+ "policy": "zero_shot",
438
+ "edit_turns": 1,
439
+ "final_reward": 0.0,
440
+ "correct": false,
441
+ "format_ok": false,
442
+ "components": {
443
+ "correctness": 0.0,
444
+ "format": 0.0,
445
+ "brevity": -0.0,
446
+ "total": 0.0
447
+ }
448
+ },
449
+ {
450
+ "task_id": "json_t09",
451
+ "task_type": "json",
452
+ "policy": "zero_shot",
453
+ "edit_turns": 1,
454
+ "final_reward": 0.0,
455
+ "correct": false,
456
+ "format_ok": false,
457
+ "components": {
458
+ "correctness": 0.0,
459
+ "format": 0.0,
460
+ "brevity": -0.0,
461
+ "total": 0.0
462
+ }
463
+ },
464
+ {
465
+ "task_id": "json_t10",
466
+ "task_type": "json",
467
+ "policy": "zero_shot",
468
+ "edit_turns": 1,
469
+ "final_reward": 0.0,
470
+ "correct": false,
471
+ "format_ok": false,
472
+ "components": {
473
+ "correctness": 0.0,
474
+ "format": 0.0,
475
+ "brevity": -0.0,
476
+ "total": 0.0
477
+ }
478
+ }
479
+ ]
480
+ }
results/comparison.json ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "policies": {
3
+ "zero_shot (stub)": {
4
+ "n": 30,
5
+ "correct": 0,
6
+ "format": 0,
7
+ "mean_reward": 0.0,
8
+ "by_type": {
9
+ "math": {
10
+ "n": 10,
11
+ "correct": 0,
12
+ "format": 0
13
+ },
14
+ "code": {
15
+ "n": 10,
16
+ "correct": 0,
17
+ "format": 0
18
+ },
19
+ "json": {
20
+ "n": 10,
21
+ "correct": 0,
22
+ "format": 0
23
+ }
24
+ },
25
+ "backend": "stub"
26
+ },
27
+ "cot (stub)": {
28
+ "n": 30,
29
+ "correct": 0,
30
+ "format": 30,
31
+ "mean_reward": 0.1,
32
+ "by_type": {
33
+ "math": {
34
+ "n": 10,
35
+ "correct": 0,
36
+ "format": 10
37
+ },
38
+ "code": {
39
+ "n": 10,
40
+ "correct": 0,
41
+ "format": 10
42
+ },
43
+ "json": {
44
+ "n": 10,
45
+ "correct": 0,
46
+ "format": 10
47
+ }
48
+ },
49
+ "backend": "stub"
50
+ },
51
+ "zero_shot (real LLM)": {
52
+ "n": 12,
53
+ "correct": 8,
54
+ "format": 7,
55
+ "mean_reward": 0.725,
56
+ "by_type": {
57
+ "math": {
58
+ "n": 4,
59
+ "correct": 2,
60
+ "format": 1
61
+ },
62
+ "code": {
63
+ "n": 4,
64
+ "correct": 2,
65
+ "format": 2
66
+ },
67
+ "json": {
68
+ "n": 4,
69
+ "correct": 4,
70
+ "format": 4
71
+ }
72
+ },
73
+ "backend": "transformers"
74
+ },
75
+ "cot (real LLM)": {
76
+ "n": 12,
77
+ "correct": 8,
78
+ "format": 12,
79
+ "mean_reward": 0.7666666666666666,
80
+ "by_type": {
81
+ "math": {
82
+ "n": 4,
83
+ "correct": 1,
84
+ "format": 4
85
+ },
86
+ "code": {
87
+ "n": 4,
88
+ "correct": 3,
89
+ "format": 4
90
+ },
91
+ "json": {
92
+ "n": 4,
93
+ "correct": 4,
94
+ "format": 4
95
+ }
96
+ },
97
+ "backend": "transformers"
98
+ },
99
+ "trained agent (real LLM)": {
100
+ "n": 12,
101
+ "correct": 10,
102
+ "format": 10,
103
+ "mean_reward": 0.9166666666666665,
104
+ "by_type": {
105
+ "math": {
106
+ "n": 4,
107
+ "correct": 3,
108
+ "format": 2
109
+ },
110
+ "code": {
111
+ "n": 4,
112
+ "correct": 3,
113
+ "format": 4
114
+ },
115
+ "json": {
116
+ "n": 4,
117
+ "correct": 4,
118
+ "format": 4
119
+ }
120
+ },
121
+ "backend": "transformers"
122
+ }
123
+ },
124
+ "ranking_by_mean_reward": [
125
+ [
126
+ "trained agent (real LLM)",
127
+ {
128
+ "n": 12,
129
+ "correct": 10,
130
+ "format": 10,
131
+ "mean_reward": 0.9166666666666665,
132
+ "by_type": {
133
+ "math": {
134
+ "n": 4,
135
+ "correct": 3,
136
+ "format": 2
137
+ },
138
+ "code": {
139
+ "n": 4,
140
+ "correct": 3,
141
+ "format": 4
142
+ },
143
+ "json": {
144
+ "n": 4,
145
+ "correct": 4,
146
+ "format": 4
147
+ }
148
+ },
149
+ "backend": "transformers"
150
+ }
151
+ ],
152
+ [
153
+ "cot (real LLM)",
154
+ {
155
+ "n": 12,
156
+ "correct": 8,
157
+ "format": 12,
158
+ "mean_reward": 0.7666666666666666,
159
+ "by_type": {
160
+ "math": {
161
+ "n": 4,
162
+ "correct": 1,
163
+ "format": 4
164
+ },
165
+ "code": {
166
+ "n": 4,
167
+ "correct": 3,
168
+ "format": 4
169
+ },
170
+ "json": {
171
+ "n": 4,
172
+ "correct": 4,
173
+ "format": 4
174
+ }
175
+ },
176
+ "backend": "transformers"
177
+ }
178
+ ],
179
+ [
180
+ "zero_shot (real LLM)",
181
+ {
182
+ "n": 12,
183
+ "correct": 8,
184
+ "format": 7,
185
+ "mean_reward": 0.725,
186
+ "by_type": {
187
+ "math": {
188
+ "n": 4,
189
+ "correct": 2,
190
+ "format": 1
191
+ },
192
+ "code": {
193
+ "n": 4,
194
+ "correct": 2,
195
+ "format": 2
196
+ },
197
+ "json": {
198
+ "n": 4,
199
+ "correct": 4,
200
+ "format": 4
201
+ }
202
+ },
203
+ "backend": "transformers"
204
+ }
205
+ ],
206
+ [
207
+ "cot (stub)",
208
+ {
209
+ "n": 30,
210
+ "correct": 0,
211
+ "format": 30,
212
+ "mean_reward": 0.1,
213
+ "by_type": {
214
+ "math": {
215
+ "n": 10,
216
+ "correct": 0,
217
+ "format": 10
218
+ },
219
+ "code": {
220
+ "n": 10,
221
+ "correct": 0,
222
+ "format": 10
223
+ },
224
+ "json": {
225
+ "n": 10,
226
+ "correct": 0,
227
+ "format": 10
228
+ }
229
+ },
230
+ "backend": "stub"
231
+ }
232
+ ],
233
+ [
234
+ "zero_shot (stub)",
235
+ {
236
+ "n": 30,
237
+ "correct": 0,
238
+ "format": 0,
239
+ "mean_reward": 0.0,
240
+ "by_type": {
241
+ "math": {
242
+ "n": 10,
243
+ "correct": 0,
244
+ "format": 0
245
+ },
246
+ "code": {
247
+ "n": 10,
248
+ "correct": 0,
249
+ "format": 0
250
+ },
251
+ "json": {
252
+ "n": 10,
253
+ "correct": 0,
254
+ "format": 0
255
+ }
256
+ },
257
+ "backend": "stub"
258
+ }
259
+ ]
260
+ ]
261
+ }
results/trained_agent.json ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "policy": "trained_agent",
3
+ "adapter": "outputs/grpo-lora",
4
+ "base_model": "Qwen/Qwen2.5-1.5B-Instruct",
5
+ "split": "test",
6
+ "llm_backend": "transformers",
7
+ "by_type": {
8
+ "math": {
9
+ "n": 4,
10
+ "correct": 3,
11
+ "format": 2
12
+ },
13
+ "code": {
14
+ "n": 4,
15
+ "correct": 3,
16
+ "format": 4
17
+ },
18
+ "json": {
19
+ "n": 4,
20
+ "correct": 4,
21
+ "format": 4
22
+ }
23
+ },
24
+ "overall": {
25
+ "n": 12,
26
+ "correct": 10,
27
+ "format": 10,
28
+ "mean_reward": 0.9166666666666665
29
+ },
30
+ "rows": [
31
+ {
32
+ "task_id": "math_t01",
33
+ "task_type": "math",
34
+ "policy": "trained_agent",
35
+ "edit_turns": 1,
36
+ "final_reward": 1.1,
37
+ "correct": true,
38
+ "format_ok": true,
39
+ "components": {
40
+ "correctness": 1.0,
41
+ "format": 1.0,
42
+ "brevity": -0.0,
43
+ "total": 1.1
44
+ },
45
+ "trace": [
46
+ {
47
+ "prompt": "Given: \n- Mia starts with 45 stickers.\n- She gives 1/3 of them to her sister.\n- Then she gives 1/5 of the remaining stickers to her friend.\n\nRequired:\nCalculate how many stickers Mia has left after these transactions.\n\nSystem Prompt:\n\n```python\nstickers = 45\n\n# Step 1: Give 1/3 to her sister\nstickers -= stickers / 3\n\n# Step 2: Calculate the remaining stickers after giving some to her sister\nremaining_after_sister = stickers - (stickers / 3)\n\n# Step 3: Give 1/5 of the remainder to her friend\nstickers -= remaining_after_sister / 5\n\n# Final number of stickers Mia has left\nfinal_stickers = int(stickers)\n<answer>{{final_stickers}}</answer>\n```",
48
+ "completion": "To determine how many stickers Mia has left, we need to follow the steps outlined in the problem:\n\n1. **Initial Stickers**: Mia starts with 45 stickers.\n \n2. **Giving 1/3 to her Sister**:\n \\[\n \\text{Stickers given to sister} = \\frac{1}{3} \\times 45 = 15\n \\]\n \\[\n \\text{Remaining stickers after giving to sister} = 45 - 15 = 30\n \\]\n\n3. **Giving 1/5 of the Remaining to her Friend**:\n \\[\n \\text{Stickers given to friend} = \\frac{1}{5} \\times 30 = 6\n \\]\n \\[\n \\text{Remaining stickers after giving to friend} = 30 - 6 = 24\n \\]\n\nThus, after all the transactions, Mia has 24 stickers left. The final answer is:\n\\[\n\\boxed{24}\n\\]",
49
+ "reward": 1.1,
50
+ "correct": true
51
+ }
52
+ ]
53
+ },
54
+ {
55
+ "task_id": "math_t02",
56
+ "task_type": "math",
57
+ "policy": "trained_agent",
58
+ "edit_turns": 2,
59
+ "final_reward": 0.0,
60
+ "correct": false,
61
+ "format_ok": false,
62
+ "components": {
63
+ "correctness": 0.0,
64
+ "format": 0.0,
65
+ "brevity": -0.0,
66
+ "total": 0.0
67
+ },
68
+ "trace": [
69
+ {
70
+ "prompt": "You are provided with the following information:\n- The farmer has 5 times as many cows as horses.\n- The farmer has 30 cows.\n\nDetermine the total number of animals the farmer has.\n\n<answer>90</answer>",
71
+ "completion": "The farmer has a total of 30 + (5 * 30) = 240 animals.",
72
+ "reward": 0.0,
73
+ "correct": false
74
+ },
75
+ {
76
+ "prompt": "---\nattempt 1 (reward=0.00, correct=False) ---\nYOUR PROMPT: You are provided with the following information:\n- The farmer has 5 times as many cows as horses.\n- The farmer has 30 cows.\n\nDetermine the total number of animals the farmer has.\n\n<answer>150</answer>\nMODEL OUTPUT: The farmer has a total of 30 + (5 * 30) = 180 animals.\n\nImprove the system prompt. Output ONLY the new system prompt, no preamble.",
77
+ "completion": "The farmer has a total of 30 + (5 * 30) = 180 animals.",
78
+ "reward": 0.0,
79
+ "correct": false
80
+ }
81
+ ]
82
+ },
83
+ {
84
+ "task_id": "math_t03",
85
+ "task_type": "math",
86
+ "policy": "trained_agent",
87
+ "edit_turns": 1,
88
+ "final_reward": 1.0,
89
+ "correct": true,
90
+ "format_ok": false,
91
+ "components": {
92
+ "correctness": 1.0,
93
+ "format": 0.0,
94
+ "brevity": -0.0,
95
+ "total": 1.0
96
+ },
97
+ "trace": [
98
+ {
99
+ "prompt": "```markdown\nThe car's value after 2 years can be calculated using the formula for depreciation over time. The initial cost of the car is $20000, and it depreciates by 10% each year. After one year, the value would be \\(20000 \\times (1 - 0.10) = 18000\\). After two years, the value would further decrease by 10%, so the final value is \\(18000 \\times (1 - 0.10) = 16200\\).\n\n<answer>\n16200\n</answer>\n```",
100
+ "completion": "To calculate the car's value after 2 years with a 10% annual depreciation rate, you can use the following formula:\n\n\\[ \\text{Final Value} = \\text{Initial Cost} \\times (1 - \\text{Depreciation Rate})^{\\text{Number of Years}} \\]\n\nGiven:\n- Initial Cost (\\(C_0\\)) = $20000\n- Depreciation Rate (\\(r\\)) = 10% or 0.10\n- Number of Years (\\(n\\)) = 2\n\nPlugging in these values into the formula gives us:\n\n\\[ \\text{Final Value} = 20000 \\times (1 - 0.10)^2 \\]\n\\[ \\text{Final Value} = 20000 \\times (0.90)^2 \\]\n\\[ \\text{Final Value} = 20000 \\times 0.81 \\]\n\\[ \\text{Final Value} = 16200 \\]\n\nSo, the car's value after 2 years will be **$16200**.",
101
+ "reward": 1.0,
102
+ "correct": true
103
+ }
104
+ ]
105
+ },
106
+ {
107
+ "task_id": "math_t04",
108
+ "task_type": "math",
109
+ "policy": "trained_agent",
110
+ "edit_turns": 2,
111
+ "final_reward": 1.1,
112
+ "correct": true,
113
+ "format_ok": true,
114
+ "components": {
115
+ "correctness": 1.0,
116
+ "format": 1.0,
117
+ "brevity": -0.0,
118
+ "total": 1.1
119
+ },
120
+ "trace": [
121
+ {
122
+ "prompt": "Given a right triangle with legs of lengths 6 and 8, calculate the length of the hypotenuse using the Pythagorean theorem. The formula for the hypotenuse \\( c \\) is \\( c = \\sqrt{a^2 + b^2} \\), where \\( a \\) and \\( b \\) are the lengths of the legs. Substitute \\( a = 6 \\) and \\( b = 8 \\) into the formula to find the hypotenuse.",
123
+ "completion": "To determine the length of the hypotenuse in a right triangle when the lengths of the two legs are given, we can use the Pythagorean theorem. The Pythagorean theorem states that in a right triangle, the square of the length of the hypotenuse (\\(c\\)) is equal to the sum of the squares of the lengths of the other two sides (legs). This can be written as:\n\n\\[ c^2 = a^2 + b^2 \\]\n\nwhere \\(a\\) and \\(b\\) are the lengths of the legs, and \\(c\\) is the length of the hypotenuse.\n\nIn this problem, the lengths of the legs are 6 and 8. So, we substitute these values into the equation:\n\n\\[ c^2 = 6^2 + 8^2 \\]\n\nNext, we calculate the squares of 6 and 8:\n\n\\[ 6^2 = 36 \\]\n\\[ 8^2 = 64 \\]\n\nNow, we add these two results together:\n\n\\[ c^2 = 36 + 64 = 100 \\]\n\nTo find the value of \\(c\\), we take the square root of both sides of the equation:\n\n\\[ c = \\",
124
+ "reward": 0.0,
125
+ "correct": false
126
+ },
127
+ {
128
+ "prompt": "---\nattempt 2 (reward=0.95, correct=True) ---\nYOUR PROMPT: Calculate the length of the hypotenuse of a right triangle with legs measuring 6 and 8 using the Pythagorean theorem. The formula for the hypotenuse \\( c \\) is \\( c = \\sqrt{a^2 + b^2} \\). Substitute \\( a = 6 \\) and \\( b = 8 \\) into the formula to find the hypotenuse.\nMODEL OUTPUT: Using the Pythagorean theorem, the length of the hypotenuse is calculated as follows:\n\\[ c = \\sqrt{6^2 + 8^2} = \\sqrt{36 + 64} = \\sqrt{100} = 10 \\]\nTherefore, the length of the hypotenuse is <answer>10</answer>.\n---",
129
+ "completion": "The length of the hypotenuse can be found using the Pythagorean theorem, which states that in a right-angled triangle, the square of the hypotenuse (the side opposite the right angle) is equal to the sum of the squares of the other two sides. If we denote the hypotenuse by \\( c \\), then:\n\n\\[ c^2 = a^2 + b^2 \\]\n\nwhere \\( a = 6 \\) and \\( b = 8 \\). Substituting these values into the equation gives:\n\n\\[ c^2 = 6^2 + 8^2 \\]\n\\[ c^2 = 36 + 64 \\]\n\\[ c^2 = 100 \\]\n\nTo find \\( c \\), we take the square root of both sides:\n\n\\[ c = \\sqrt{100} \\]\n\\[ c = 10 \\]\n\nSo, the length of the hypotenuse is \\( 10 \\).\n\nTherefore, the answer is \\( \\boxed{10} \\).",
130
+ "reward": 1.1,
131
+ "correct": true
132
+ }
133
+ ]
134
+ },
135
+ {
136
+ "task_id": "code_t01",
137
+ "task_type": "code",
138
+ "policy": "trained_agent",
139
+ "edit_turns": 1,
140
+ "final_reward": 1.1,
141
+ "correct": true,
142
+ "format_ok": true,
143
+ "components": {
144
+ "correctness": 1.0,
145
+ "format": 1.0,
146
+ "brevity": -0.0,
147
+ "total": 1.1
148
+ },
149
+ "trace": [
150
+ {
151
+ "prompt": "```python\ndef square(x):\n return x * x\n```",
152
+ "completion": "To create the `square` function in Python, you can define it as follows:\n\n```python\ndef square(x):\n return x * x\n```\n\nThis function takes an input `x`, multiplies it by itself, and returns the result. For example, calling `square(3)` would return `9`.",
153
+ "reward": 1.1,
154
+ "correct": true
155
+ }
156
+ ]
157
+ },
158
+ {
159
+ "task_id": "code_t02",
160
+ "task_type": "code",
161
+ "policy": "trained_agent",
162
+ "edit_turns": 1,
163
+ "final_reward": 1.1,
164
+ "correct": true,
165
+ "format_ok": true,
166
+ "components": {
167
+ "correctness": 1.0,
168
+ "format": 1.0,
169
+ "brevity": -0.0,
170
+ "total": 1.1
171
+ },
172
+ "trace": [
173
+ {
174
+ "prompt": "```\ndef is_anagram(a, b):\n return sorted(a.replace(\" \", \"\").lower()) == sorted(b.replace(\" \", \"\").lower())\n```",
175
+ "completion": "To determine if two strings are anagrams of each other, you can follow these steps:\n\n1. Convert both strings to lowercase and remove any spaces.\n2. Sort the characters in each string.\n3. Compare the sorted versions of the strings.\n\nHere's how you could implement this:\n\n```python\nimport re\n\ndef is_anagram(a, b):\n # Remove spaces from both strings\n a = re.sub(r'\\s+', '', a)\n b = re.sub(r'\\s+', '', b)\n\n # Convert strings to lowercase\n a = a.lower()\n b = b.lower()\n\n # Sort characters in each string\n a_sorted = ''.join(sorted(a))\n b_sorted = ''.join(sorted(b))\n\n # Check if the sorted strings are equal\n return a_sorted == b_sorted\n\n# Test cases\nprint(is_anagram(\"Listen\", \"Silent\")) # True\nprint(is_anagram(\"Hello\", \"World\")) # False\nprint(is_anagram(\"Triangle\", \"Integral\")) # True\n```\n\n### Explanation:\n- **Removing Spaces**: The `re.sub` function is used to remove all spaces from both input strings. This ensures that we only consider the characters without spaces when comparing the strings.\n \n",
176
+ "reward": 1.1,
177
+ "correct": true
178
+ }
179
+ ]
180
+ },
181
+ {
182
+ "task_id": "code_t03",
183
+ "task_type": "code",
184
+ "policy": "trained_agent",
185
+ "edit_turns": 2,
186
+ "final_reward": 0.1,
187
+ "correct": false,
188
+ "format_ok": true,
189
+ "components": {
190
+ "correctness": 0.0,
191
+ "format": 1.0,
192
+ "brevity": -0.0,
193
+ "total": 0.1
194
+ },
195
+ "trace": [
196
+ {
197
+ "prompt": "```\ndef count_words(s):\n return {word.lower(): s.count(word) for word in s.split()}\n```",
198
+ "completion": "To solve the problem of counting the occurrences of words in a string and returning them as a dictionary with each word's count as a key and its value as the total count of all words, you can use the following approach:\n\n1. Split the input string into individual words.\n2. Use a dictionary comprehension to map each word to its frequency count.\n3. Convert the resulting dictionary back to a list of tuples.\n\nHere is how you can implement this:\n\n```python\nfrom collections import defaultdict\n\ndef count_words(s):\n # Split the string into words\n words = s.split()\n \n # Count the occurrences of each word\n word_count = defaultdict(int)\n for word in words:\n word_count[word] += 1\n \n # Convert the dictionary to a list of tuples\n result = list(word_count.items())\n \n return result\n\n# Example usage:\ninput_string = \"This is a test string\"\nresult = count_words(input_string)\nprint(result) # Output: [('is', 2), ('a', 2), ('test', 2), ('string', 2)]\n```\n\n### Explanation:\n- **`s.split()`**: This splits the string `s` into a list of words using whitespace as the delimiter.\n",
199
+ "reward": 0.1,
200
+ "correct": false
201
+ },
202
+ {
203
+ "prompt": "```python\ndef count_words(s):\n return {word.lower(): s.count(word) for word in s.split()}\n```",
204
+ "completion": "To solve the problem of counting the occurrences of words in a string and returning them as a dictionary with each word's count as a key and its value as the count, you can use the following approach:\n\n1. Split the input string into individual words.\n2. Convert each word to lowercase using the `.lower()` method.\n3. Use a dictionary comprehension to create a dictionary where each word is a key and its count is the value.\n\nHere's how you could implement this:\n\n```python\nfrom collections import defaultdict\n\ndef count_words(s):\n # Split the string into words\n words = s.split()\n \n # Create a dictionary to store word counts\n word_counts = defaultdict(int)\n \n # Count the occurrences of each word\n for word in words:\n word_counts[word] += 1\n \n # Return the dictionary containing word counts\n return dict(word_counts)\n\n# Example usage\ns = \"This is a test sentence\"\nprint(count_words(s)) # Output: {'is': 2, 'a': 1, 'test': 1, 'sentence': 1}\n```\n\nIn this code:\n- The `split()` method splits the string into a list of words based on whitespace.\n- A `defaultdict",
205
+ "reward": 0.1,
206
+ "correct": false
207
+ }
208
+ ]
209
+ },
210
+ {
211
+ "task_id": "code_t04",
212
+ "task_type": "code",
213
+ "policy": "trained_agent",
214
+ "edit_turns": 1,
215
+ "final_reward": 1.1,
216
+ "correct": true,
217
+ "format_ok": true,
218
+ "components": {
219
+ "correctness": 1.0,
220
+ "format": 1.0,
221
+ "brevity": -0.0,
222
+ "total": 1.1
223
+ },
224
+ "trace": [
225
+ {
226
+ "prompt": "```python\ndef power(b, e):\n result = 1\n while e > 0:\n if e % 2 == 1:\n result *= b\n b *= b\n e //= 2\n return result\n```",
227
+ "completion": "To solve the problem of computing \\(b^e\\) without using the exponentiation operator (`**`), you can use a loop to repeatedly multiply the base `b` by itself until it reaches or exceeds `e`. Here's how you can implement this:\n\n```python\ndef power(b, e):\n result = 1\n # Loop from 0 to e-1\n for i in range(e):\n result *= b\n return result\n\n# Test cases\nprint(power(3, 5)) # Expected output: 243\nprint(power(2, 3)) # Expected output: 8\n```\n\nIn this code:\n- The variable `result` is initialized to 1.\n- A loop runs from 0 to `e-1`.\n- In each iteration, the value of `b` is multiplied to `result`.\n- After the loop completes, `result` contains the final result of `b^e`.\n\nThis approach ensures that the computation is done efficiently and avoids the overhead of the exponentiation operator.",
228
+ "reward": 1.1,
229
+ "correct": true
230
+ }
231
+ ]
232
+ },
233
+ {
234
+ "task_id": "json_t01",
235
+ "task_type": "json",
236
+ "policy": "trained_agent",
237
+ "edit_turns": 2,
238
+ "final_reward": 1.1,
239
+ "correct": true,
240
+ "format_ok": true,
241
+ "components": {
242
+ "correctness": 1.0,
243
+ "format": 1.0,
244
+ "brevity": -0.0,
245
+ "total": 1.1
246
+ },
247
+ "trace": [
248
+ {
249
+ "prompt": "```\n{\"name\": \"Jane Smith\", \"id\": 7821, \"department\": \"Engineering\", \"salary\": 95000}\n```",
250
+ "completion": "The employee with the given details is Jane Smith. Her name is 'Jane Smith' and her ID number is 7821. She works in the Engineering department and earns a salary of $95,000 per year.",
251
+ "reward": 0.0,
252
+ "correct": false
253
+ },
254
+ {
255
+ "prompt": "```\n{\n \"name\": \"Jane Smith\",\n \"id\": 7821,\n \"department\": \"Engineering\",\n \"salary\": 95000\n}\n```",
256
+ "completion": "```json\n{\n \"name\": \"Jane Smith\",\n \"id\": 7821,\n \"department\": \"Engineering\",\n \"salary\": 95000\n}\n```",
257
+ "reward": 1.1,
258
+ "correct": true
259
+ }
260
+ ]
261
+ },
262
+ {
263
+ "task_id": "json_t02",
264
+ "task_type": "json",
265
+ "policy": "trained_agent",
266
+ "edit_turns": 1,
267
+ "final_reward": 1.1,
268
+ "correct": true,
269
+ "format_ok": true,
270
+ "components": {
271
+ "correctness": 1.0,
272
+ "format": 1.0,
273
+ "brevity": -0.0,
274
+ "total": 1.1
275
+ },
276
+ "trace": [
277
+ {
278
+ "prompt": "```\n{\"name\": \"Grand Plaza\", \"stars\": 4, \"rooms\": 250, \"nightly_rate\": 180}\n```",
279
+ "completion": "```json\n{\n \"name\": \"Grand Plaza\",\n \"stars\": 4,\n \"rooms\": 250,\n \"nightly_rate\": 180\n}\n```",
280
+ "reward": 1.1,
281
+ "correct": true
282
+ }
283
+ ]
284
+ },
285
+ {
286
+ "task_id": "json_t03",
287
+ "task_type": "json",
288
+ "policy": "trained_agent",
289
+ "edit_turns": 1,
290
+ "final_reward": 1.1,
291
+ "correct": true,
292
+ "format_ok": true,
293
+ "components": {
294
+ "correctness": 1.0,
295
+ "format": 1.0,
296
+ "brevity": -0.0,
297
+ "total": 1.1
298
+ },
299
+ "trace": [
300
+ {
301
+ "prompt": "```json\n{\n \"home\": \"Real Madrid\",\n \"away\": \"Barcelona\",\n \"home_score\": 3,\n \"away_score\": 1,\n \"date\": \"2024-03-15\"\n}\n```",
302
+ "completion": "```json\n{\n \"home\": \"Real Madrid\",\n \"away\": \"Barcelona\",\n \"home_score\": 3,\n \"away_score\": 1,\n \"date\": \"2024-03-15\"\n}\n```",
303
+ "reward": 1.1,
304
+ "correct": true
305
+ }
306
+ ]
307
+ },
308
+ {
309
+ "task_id": "json_t04",
310
+ "task_type": "json",
311
+ "policy": "trained_agent",
312
+ "edit_turns": 1,
313
+ "final_reward": 1.1,
314
+ "correct": true,
315
+ "format_ok": true,
316
+ "components": {
317
+ "correctness": 1.0,
318
+ "format": 1.0,
319
+ "brevity": -0.0,
320
+ "total": 1.1
321
+ },
322
+ "trace": [
323
+ {
324
+ "prompt": "```\n{\n \"title\": \"Dune\",\n \"author\": \"Frank Herbert\",\n \"due\": \"2025-12-01\",\n \"fine_per_day\": 0.25\n}\n```",
325
+ "completion": "```json\n{\n \"title\": \"Dune\",\n \"author\": \"Frank Herbert\",\n \"due\": \"2025-12-01\",\n \"fine_per_day\": 0.25\n}\n```",
326
+ "reward": 1.1,
327
+ "correct": true
328
+ }
329
+ ]
330
+ }
331
+ ]
332
+ }
results/training_log.jsonl ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"ts": 1777170205.4574108, "task_id": "math_003", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.0, "total": 0.1}, "completion_len": 549}
2
+ {"ts": 1777170205.4576142, "task_id": "math_003", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.0, "total": 0.0}, "completion_len": 481}
3
+ {"ts": 1777170276.4298396, "task_id": "math_004", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.0, "total": 0.1}, "completion_len": 531}
4
+ {"ts": 1777170276.4299898, "task_id": "math_004", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.0, "total": 0.1}, "completion_len": 501}
5
+ {"ts": 1777172812.2414134, "task_id": "code_013", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.09000000000000001, "total": 1.01}, "completion_len": 1161}
6
+ {"ts": 1777172818.1311266, "task_id": "code_013", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.0, "total": 1.1}, "completion_len": 696}
7
+ {"ts": 1777172835.2468848, "task_id": "code_006", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1516}
8
+ {"ts": 1777172841.192321, "task_id": "code_006", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1220}
9
+ {"ts": 1777172858.531064, "task_id": "json_003", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1293}
10
+ {"ts": 1777172859.499286, "task_id": "json_003", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1365}
11
+ {"ts": 1777172876.9950335, "task_id": "json_006", "task_type": "json", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.027750000000000004, "total": 0.07225000000000001}, "completion_len": 912}
12
+ {"ts": 1777172877.9246104, "task_id": "json_006", "task_type": "json", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.00775, "total": 0.09225}, "completion_len": 832}
13
+ {"ts": 1777172895.3655775, "task_id": "math_023", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1221}
14
+ {"ts": 1777172899.7456212, "task_id": "math_023", "task_type": "math", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.08625000000000001, "total": 1.0137500000000002}, "completion_len": 1146}
15
+ {"ts": 1777172919.6758099, "task_id": "math_016", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.026000000000000002, "total": 0.974}, "completion_len": 905}
16
+ {"ts": 1777172922.9382982, "task_id": "math_016", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.09775, "total": 0.90225}, "completion_len": 1192}
17
+ {"ts": 1777172945.4091709, "task_id": "code_011", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1201}
18
+ {"ts": 1777172950.538483, "task_id": "code_011", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1227}
19
+ {"ts": 1777172970.7127082, "task_id": "code_001", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.07600000000000001, "total": 1.024}, "completion_len": 1105}
20
+ {"ts": 1777172971.1114466, "task_id": "code_001", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1275}
21
+ {"ts": 1777172993.1248798, "task_id": "math_027", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.08600000000000001, "total": 0.914}, "completion_len": 1145}
22
+ {"ts": 1777172998.0652761, "task_id": "math_027", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.07475000000000001, "total": 0.92525}, "completion_len": 1100}
23
+ {"ts": 1777173020.168966, "task_id": "code_018", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.08225, "total": 0.017750000000000002}, "completion_len": 1130}
24
+ {"ts": 1777173025.0696297, "task_id": "code_018", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.08125, "total": 0.018750000000000003}, "completion_len": 1126}
25
+ {"ts": 1777173047.404802, "task_id": "code_009", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.08075, "total": 1.01925}, "completion_len": 1124}
26
+ {"ts": 1777173050.945326, "task_id": "code_009", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.020250000000000004, "total": 0.07975}, "completion_len": 884}
27
+ {"ts": 1777173067.3435152, "task_id": "math_017", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1224}
28
+ {"ts": 1777173067.418834, "task_id": "math_017", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.045250000000000005, "total": -0.045250000000000005}, "completion_len": 982}
29
+ {"ts": 1777173084.904911, "task_id": "math_011", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1426}
30
+ {"ts": 1777173087.5322294, "task_id": "math_011", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1472}
31
+ {"ts": 1777173104.7331266, "task_id": "json_001", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1576}
32
+ {"ts": 1777173106.170965, "task_id": "json_001", "task_type": "json", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1272}
33
+ {"ts": 1777173124.8909419, "task_id": "code_012", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1240}
34
+ {"ts": 1777173130.257007, "task_id": "code_012", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1539}
35
+ {"ts": 1777173148.8288076, "task_id": "code_008", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.0, "total": 0.1}, "completion_len": 669}
36
+ {"ts": 1777173154.2532074, "task_id": "code_008", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1421}
37
+ {"ts": 1777173171.106788, "task_id": "code_002", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.047, "total": 1.0530000000000002}, "completion_len": 990}
38
+ {"ts": 1777173174.5100622, "task_id": "code_002", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1330}
39
+ {"ts": 1777173191.6097682, "task_id": "code_003", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1336}
40
+ {"ts": 1777173194.629471, "task_id": "code_003", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1465}
41
+ {"ts": 1777173211.4467418, "task_id": "math_028", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1259}
42
+ {"ts": 1777173211.5623662, "task_id": "math_028", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1248}
43
+ {"ts": 1777173230.975903, "task_id": "math_029", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.03725, "total": -0.03725}, "completion_len": 950}
44
+ {"ts": 1777173234.812369, "task_id": "math_029", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1417}
45
+ {"ts": 1777173257.372067, "task_id": "code_020", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.045000000000000005, "total": 1.0550000000000002}, "completion_len": 981}
46
+ {"ts": 1777173263.2287974, "task_id": "code_020", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.0, "total": 0.0}, "completion_len": 600}
47
+ {"ts": 1777173282.5568526, "task_id": "math_002", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.08650000000000001, "total": 0.9135}, "completion_len": 1147}
48
+ {"ts": 1777173282.6959713, "task_id": "math_002", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.07650000000000001, "total": -0.07650000000000001}, "completion_len": 1107}
49
+ {"ts": 1777173299.8031049, "task_id": "json_008", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1214}
50
+ {"ts": 1777173300.4593112, "task_id": "json_008", "task_type": "json", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.098, "total": 0.0020000000000000018}, "completion_len": 1193}
51
+ {"ts": 1777173317.6295128, "task_id": "json_002", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1416}
52
+ {"ts": 1777173318.5425549, "task_id": "json_002", "task_type": "json", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1383}
53
+ {"ts": 1777173335.6685421, "task_id": "json_010", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1270}
54
+ {"ts": 1777173338.1202948, "task_id": "json_010", "task_type": "json", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1280}
55
+ {"ts": 1777173355.3229856, "task_id": "json_009", "task_type": "json", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.09350000000000001, "total": 0.9065}, "completion_len": 1176}
56
+ {"ts": 1777173356.12197, "task_id": "json_009", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.063, "total": 1.0370000000000001}, "completion_len": 1053}
57
+ {"ts": 1777173373.4462821, "task_id": "json_004", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1452}
58
+ {"ts": 1777173374.2732356, "task_id": "json_004", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1308}
59
+ {"ts": 1777173392.1112561, "task_id": "code_019", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1257}
60
+ {"ts": 1777173393.0789738, "task_id": "code_019", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.08700000000000001, "total": 1.0130000000000001}, "completion_len": 1149}
61
+ {"ts": 1777173414.6909804, "task_id": "math_025", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.052500000000000005, "total": -0.052500000000000005}, "completion_len": 1011}
62
+ {"ts": 1777173418.6623971, "task_id": "math_025", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.061250000000000006, "total": -0.061250000000000006}, "completion_len": 1046}
63
+ {"ts": 1777173440.7478015, "task_id": "code_004", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1212}
64
+ {"ts": 1777173441.512844, "task_id": "code_004", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.07750000000000001, "total": 1.0225}, "completion_len": 1111}
65
+ {"ts": 1777173460.1450841, "task_id": "math_001", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.050249999999999996, "total": 0.94975}, "completion_len": 1002}
66
+ {"ts": 1777173466.0297563, "task_id": "math_001", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1313}
67
+ {"ts": 1777173488.1649392, "task_id": "math_022", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.012750000000000001, "total": -0.012750000000000001}, "completion_len": 852}
68
+ {"ts": 1777173492.3721848, "task_id": "math_022", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1358}
69
+ {"ts": 1777173511.5682764, "task_id": "code_010", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.0, "total": 1.1}, "completion_len": 598}
70
+ {"ts": 1777173512.5637662, "task_id": "code_010", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.082, "total": 1.018}, "completion_len": 1130}
71
+ {"ts": 1777173529.271412, "task_id": "math_021", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1393}
72
+ {"ts": 1777173535.2592604, "task_id": "math_021", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.04175, "total": -0.04175}, "completion_len": 968}
73
+ {"ts": 1777173557.4047916, "task_id": "math_024", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1393}
74
+ {"ts": 1777173563.2304928, "task_id": "math_024", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1274}
75
+ {"ts": 1777173581.1555026, "task_id": "math_030", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.03375, "total": -0.03375}, "completion_len": 936}
76
+ {"ts": 1777173584.8048372, "task_id": "math_030", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1371}
77
+ {"ts": 1777173603.6921883, "task_id": "math_010", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.05175, "total": -0.05175}, "completion_len": 1008}
78
+ {"ts": 1777173606.0200198, "task_id": "math_010", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.07775, "total": -0.07775}, "completion_len": 1112}
79
+ {"ts": 1777173628.8743641, "task_id": "math_013", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.0, "total": 0.0}, "completion_len": 507}
80
+ {"ts": 1777173630.5763302, "task_id": "math_013", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.08075, "total": 0.91925}, "completion_len": 1124}
81
+ {"ts": 1777173647.5243535, "task_id": "json_007", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.09875, "total": 1.0012500000000002}, "completion_len": 1196}
82
+ {"ts": 1777173648.245499, "task_id": "json_007", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1415}
83
+ {"ts": 1777173665.5619204, "task_id": "json_005", "task_type": "json", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.0, "total": 1.0}, "completion_len": 667}
84
+ {"ts": 1777173666.517907, "task_id": "json_005", "task_type": "json", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1315}
85
+ {"ts": 1777173687.5014353, "task_id": "code_017", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1305}
86
+ {"ts": 1777173693.462973, "task_id": "code_017", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.063, "total": -0.063}, "completion_len": 1053}
87
+ {"ts": 1777173714.8784075, "task_id": "math_015", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.062, "total": -0.062}, "completion_len": 1050}
88
+ {"ts": 1777173720.6591973, "task_id": "math_015", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1523}
89
+ {"ts": 1777173742.5577133, "task_id": "code_014", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1610}
90
+ {"ts": 1777173743.6269865, "task_id": "code_014", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1456}
91
+ {"ts": 1777173765.848498, "task_id": "code_015", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1463}
92
+ {"ts": 1777173771.822977, "task_id": "code_015", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1426}
93
+ {"ts": 1777173790.4104187, "task_id": "math_004", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1387}
94
+ {"ts": 1777173794.531984, "task_id": "math_004", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1222}
95
+ {"ts": 1777173811.4586246, "task_id": "math_009", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1306}
96
+ {"ts": 1777173811.5521224, "task_id": "math_009", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1291}
97
+ {"ts": 1777173833.1675053, "task_id": "code_016", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.03900000000000001, "total": 0.061}, "completion_len": 957}
98
+ {"ts": 1777173835.7550392, "task_id": "code_016", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.059750000000000004, "total": 1.0402500000000001}, "completion_len": 1041}
99
+ {"ts": 1777173854.9989517, "task_id": "math_012", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.05725, "total": 0.94275}, "completion_len": 1030}
100
+ {"ts": 1777173856.2618442, "task_id": "math_012", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.011000000000000001, "total": 0.989}, "completion_len": 845}
101
+ {"ts": 1777173876.298982, "task_id": "math_005", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1212}
102
+ {"ts": 1777173876.7924976, "task_id": "math_005", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1447}
103
+ {"ts": 1777173895.2569299, "task_id": "code_005", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1221}
104
+ {"ts": 1777173896.0572858, "task_id": "code_005", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.007000000000000001, "total": 1.0930000000000002}, "completion_len": 829}
105
+ {"ts": 1777173913.6820323, "task_id": "math_014", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1524}
106
+ {"ts": 1777173913.8660965, "task_id": "math_014", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1374}
107
+ {"ts": 1777173932.3765514, "task_id": "code_007", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1265}
108
+ {"ts": 1777173938.4034915, "task_id": "code_007", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1447}
109
+ {"ts": 1777173955.71959, "task_id": "math_018", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1293}
110
+ {"ts": 1777173961.6880264, "task_id": "math_018", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1320}
111
+ {"ts": 1777173979.4871435, "task_id": "math_026", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1232}
112
+ {"ts": 1777173979.9857504, "task_id": "math_026", "task_type": "math", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1225}
113
+ {"ts": 1777174002.2934277, "task_id": "math_007", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1429}
114
+ {"ts": 1777174004.3033779, "task_id": "math_007", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.06999999999999999, "total": 0.93}, "completion_len": 1081}
115
+ {"ts": 1777174025.1607933, "task_id": "math_020", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1284}
116
+ {"ts": 1777174025.5232098, "task_id": "math_020", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.03075, "total": -0.03075}, "completion_len": 924}
117
+ {"ts": 1777174043.389417, "task_id": "math_008", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.06925, "total": -0.06925}, "completion_len": 1078}
118
+ {"ts": 1777174049.2553558, "task_id": "math_008", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1413}
119
+ {"ts": 1777174066.0321496, "task_id": "math_006", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.061750000000000006, "total": 0.03825}, "completion_len": 1048}
120
+ {"ts": 1777174066.418219, "task_id": "math_006", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1303}
121
+ {"ts": 1777174083.0237522, "task_id": "math_003", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.07325000000000001, "total": -0.07325000000000001}, "completion_len": 1094}
122
+ {"ts": 1777174084.7096164, "task_id": "math_003", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.0625, "total": -0.0625}, "completion_len": 1051}
123
+ {"ts": 1777174107.310967, "task_id": "math_019", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1390}
124
+ {"ts": 1777174110.478443, "task_id": "math_019", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1473}
125
+ {"ts": 1777174132.8515298, "task_id": "code_019", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1229}
126
+ {"ts": 1777174138.8770278, "task_id": "code_019", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.08975, "total": 1.01025}, "completion_len": 1160}
127
+ {"ts": 1777174156.183178, "task_id": "json_006", "task_type": "json", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.0, "total": 0.0}, "completion_len": 743}
128
+ {"ts": 1777174157.2409449, "task_id": "json_006", "task_type": "json", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1217}
129
+ {"ts": 1777174178.764131, "task_id": "code_007", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1288}
130
+ {"ts": 1777174183.9705675, "task_id": "code_007", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1311}
131
+ {"ts": 1777174203.5700634, "task_id": "code_006", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.0945, "total": 0.005500000000000005}, "completion_len": 1179}
132
+ {"ts": 1777174205.7308102, "task_id": "code_006", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.04875, "total": 1.05125}, "completion_len": 996}
133
+ {"ts": 1777174226.5591905, "task_id": "code_015", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1397}
134
+ {"ts": 1777174228.6299348, "task_id": "code_015", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.09375, "total": 1.00625}, "completion_len": 1176}
135
+ {"ts": 1777174251.2387226, "task_id": "code_001", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1219}
136
+ {"ts": 1777174254.8462887, "task_id": "code_001", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.08625000000000001, "total": 1.0137500000000002}, "completion_len": 1146}
137
+ {"ts": 1777174271.242478, "task_id": "math_030", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.063, "total": 0.937}, "completion_len": 1053}
138
+ {"ts": 1777174271.831661, "task_id": "math_030", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.085, "total": 0.015}, "completion_len": 1142}
139
+ {"ts": 1777174294.3736541, "task_id": "code_004", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.06975, "total": 1.03025}, "completion_len": 1080}
140
+ {"ts": 1777174297.6597, "task_id": "code_004", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1339}
141
+ {"ts": 1777174315.9255633, "task_id": "code_003", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1207}
142
+ {"ts": 1777174316.2990458, "task_id": "code_003", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1409}
143
+ {"ts": 1777174333.3866775, "task_id": "math_026", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1288}
144
+ {"ts": 1777174335.7476342, "task_id": "math_026", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1404}
145
+ {"ts": 1777174352.4058568, "task_id": "math_022", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1696}
146
+ {"ts": 1777174352.9509149, "task_id": "math_022", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.0985, "total": 0.9015}, "completion_len": 1195}
147
+ {"ts": 1777174369.6062346, "task_id": "json_001", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1496}
148
+ {"ts": 1777174370.1293092, "task_id": "json_001", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1277}
149
+ {"ts": 1777174389.401394, "task_id": "math_011", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.050749999999999997, "total": 0.94925}, "completion_len": 1004}
150
+ {"ts": 1777174390.6996186, "task_id": "math_011", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1282}
151
+ {"ts": 1777174407.5183613, "task_id": "math_012", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1238}
152
+ {"ts": 1777174411.4229507, "task_id": "math_012", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1348}
153
+ {"ts": 1777174428.7764583, "task_id": "math_013", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.09975, "total": 0.90025}, "completion_len": 1200}
154
+ {"ts": 1777174429.7491758, "task_id": "math_013", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.045250000000000005, "total": -0.045250000000000005}, "completion_len": 982}
155
+ {"ts": 1777174447.1495488, "task_id": "json_009", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.0, "total": 1.1}, "completion_len": 695}
156
+ {"ts": 1777174447.9590833, "task_id": "json_009", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1232}
157
+ {"ts": 1777174465.2512193, "task_id": "json_004", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1484}
158
+ {"ts": 1777174466.048356, "task_id": "json_004", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1370}
159
+ {"ts": 1777174488.6509607, "task_id": "code_011", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1352}
160
+ {"ts": 1777174494.5920143, "task_id": "code_011", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1225}
161
+ {"ts": 1777174513.807109, "task_id": "math_002", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.09100000000000001, "total": 0.909}, "completion_len": 1165}
162
+ {"ts": 1777174516.654939, "task_id": "math_002", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1315}
163
+ {"ts": 1777174534.203111, "task_id": "code_002", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1426}
164
+ {"ts": 1777174537.094225, "task_id": "code_002", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1277}
165
+ {"ts": 1777174554.8343234, "task_id": "math_020", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1308}
166
+ {"ts": 1777174556.757706, "task_id": "math_020", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1241}
167
+ {"ts": 1777174576.9589696, "task_id": "math_025", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.09050000000000001, "total": -0.09050000000000001}, "completion_len": 1163}
168
+ {"ts": 1777174581.5448074, "task_id": "math_025", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.07375000000000001, "total": -0.07375000000000001}, "completion_len": 1096}
169
+ {"ts": 1777174598.8734553, "task_id": "json_002", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1358}
170
+ {"ts": 1777174599.7249708, "task_id": "json_002", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1205}
171
+ {"ts": 1777174617.1021712, "task_id": "code_009", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1621}
172
+ {"ts": 1777174622.8798256, "task_id": "code_009", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1490}
173
+ {"ts": 1777174639.3362336, "task_id": "math_028", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.0985, "total": -0.0985}, "completion_len": 1195}
174
+ {"ts": 1777174639.616234, "task_id": "math_028", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.08575, "total": -0.08575}, "completion_len": 1144}
175
+ {"ts": 1777174657.0489144, "task_id": "json_008", "task_type": "json", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1271}
176
+ {"ts": 1777174658.0955572, "task_id": "json_008", "task_type": "json", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.02425, "total": 0.07575000000000001}, "completion_len": 898}
177
+ {"ts": 1777174675.74633, "task_id": "math_024", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.09525, "total": -0.09525}, "completion_len": 1182}
178
+ {"ts": 1777174678.5163367, "task_id": "math_024", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1309}
179
+ {"ts": 1777174699.5413382, "task_id": "math_016", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.0735, "total": -0.0735}, "completion_len": 1095}
180
+ {"ts": 1777174702.3200865, "task_id": "math_016", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1245}
181
+ {"ts": 1777174718.8255687, "task_id": "math_005", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.09050000000000001, "total": 0.9095}, "completion_len": 1163}
182
+ {"ts": 1777174719.36649, "task_id": "math_005", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1274}
183
+ {"ts": 1777174736.5089552, "task_id": "json_010", "task_type": "json", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.08000000000000002, "total": 0.9199999999999999}, "completion_len": 1121}
184
+ {"ts": 1777174737.4347408, "task_id": "json_010", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.09975, "total": 1.00025}, "completion_len": 1200}
185
+ {"ts": 1777174754.7592833, "task_id": "code_014", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.07325000000000001, "total": 1.02675}, "completion_len": 1094}
186
+ {"ts": 1777174758.2755096, "task_id": "code_014", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1214}
187
+ {"ts": 1777174774.9741502, "task_id": "math_004", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1250}
188
+ {"ts": 1777174777.4996514, "task_id": "math_004", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.08875, "total": 0.91125}, "completion_len": 1156}
189
+ {"ts": 1777174793.9735966, "task_id": "math_006", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1215}
190
+ {"ts": 1777174798.8612566, "task_id": "math_006", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1257}
191
+ {"ts": 1777174816.189013, "task_id": "json_005", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1204}
192
+ {"ts": 1777174822.1876087, "task_id": "json_005", "task_type": "json", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1321}
193
+ {"ts": 1777174838.9026356, "task_id": "math_017", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.08650000000000001, "total": -0.08650000000000001}, "completion_len": 1147}
194
+ {"ts": 1777174838.951193, "task_id": "math_017", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.06625, "total": -0.06625}, "completion_len": 1066}
195
+ {"ts": 1777174858.6510503, "task_id": "math_023", "task_type": "math", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1512}
196
+ {"ts": 1777174859.161425, "task_id": "math_023", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1210}
197
+ {"ts": 1777174881.662845, "task_id": "code_012", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.027250000000000003, "total": -0.027250000000000003}, "completion_len": 914}
198
+ {"ts": 1777174887.6761932, "task_id": "code_012", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.0, "total": 1.1}, "completion_len": 750}
199
+ {"ts": 1777174910.3228626, "task_id": "code_017", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.07175000000000001, "total": -0.07175000000000001}, "completion_len": 1090}
200
+ {"ts": 1777174916.2944148, "task_id": "code_017", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1258}
201
+ {"ts": 1777174938.6975274, "task_id": "code_005", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.0675, "total": 1.0325000000000002}, "completion_len": 1071}
202
+ {"ts": 1777174944.8011134, "task_id": "code_005", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.08625000000000001, "total": 1.0137500000000002}, "completion_len": 1148}
203
+ {"ts": 1777174962.3693285, "task_id": "math_014", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.06999999999999999, "total": 0.93}, "completion_len": 1081}
204
+ {"ts": 1777174964.6813521, "task_id": "math_014", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.037500000000000006, "total": 0.9625}, "completion_len": 951}
205
+ {"ts": 1777174985.6662529, "task_id": "math_029", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1276}
206
+ {"ts": 1777174985.9857364, "task_id": "math_029", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.09125, "total": 0.90875}, "completion_len": 1166}
207
+ {"ts": 1777175005.8278537, "task_id": "math_015", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.07375000000000001, "total": -0.07375000000000001}, "completion_len": 1096}
208
+ {"ts": 1777175011.7080715, "task_id": "math_015", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1397}
209
+ {"ts": 1777175030.2603054, "task_id": "code_016", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.09250000000000001, "total": 0.007499999999999993}, "completion_len": 1171}
210
+ {"ts": 1777175032.0410023, "task_id": "code_016", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.0655, "total": 1.0345}, "completion_len": 1063}
211
+ {"ts": 1777175054.6729057, "task_id": "math_021", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1309}
212
+ {"ts": 1777175060.375272, "task_id": "math_021", "task_type": "math", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.05600000000000001, "total": 1.044}, "completion_len": 1025}
213
+ {"ts": 1777175077.9218123, "task_id": "math_019", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1296}
214
+ {"ts": 1777175083.851587, "task_id": "math_019", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.0815, "total": -0.0815}, "completion_len": 1127}
215
+ {"ts": 1777175106.071159, "task_id": "json_003", "task_type": "json", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1330}
216
+ {"ts": 1777175106.9987998, "task_id": "json_003", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1300}
217
+ {"ts": 1777175124.2084413, "task_id": "math_007", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.09325, "total": 0.90675}, "completion_len": 1174}
218
+ {"ts": 1777175124.4143105, "task_id": "math_007", "task_type": "math", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1218}
219
+ {"ts": 1777175140.9962604, "task_id": "math_003", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1444}
220
+ {"ts": 1777175141.070674, "task_id": "math_003", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1321}
221
+ {"ts": 1777175162.1808069, "task_id": "math_018", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1236}
222
+ {"ts": 1777175166.837405, "task_id": "math_018", "task_type": "math", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.0595, "total": 1.0405000000000002}, "completion_len": 1039}
223
+ {"ts": 1777175186.6213953, "task_id": "code_008", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1333}
224
+ {"ts": 1777175192.4335587, "task_id": "code_008", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1335}
225
+ {"ts": 1777175214.5744965, "task_id": "code_018", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1278}
226
+ {"ts": 1777175220.5369945, "task_id": "code_018", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.063, "total": 0.037000000000000005}, "completion_len": 1053}
227
+ {"ts": 1777175238.043605, "task_id": "math_010", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1351}
228
+ {"ts": 1777175238.1603415, "task_id": "math_010", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1313}
229
+ {"ts": 1777175258.2254663, "task_id": "code_010", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.03375, "total": 1.0662500000000001}, "completion_len": 936}
230
+ {"ts": 1777175263.779717, "task_id": "code_010", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.0, "total": 0.1}, "completion_len": 593}
231
+ {"ts": 1777175286.3451197, "task_id": "code_020", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.00025, "total": -0.00025}, "completion_len": 804}
232
+ {"ts": 1777175292.25557, "task_id": "code_020", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1336}
233
+ {"ts": 1777175313.1582701, "task_id": "math_009", "task_type": "math", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1586}
234
+ {"ts": 1777175316.0815704, "task_id": "math_009", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.07225000000000001, "total": -0.07225000000000001}, "completion_len": 1090}
235
+ {"ts": 1777175335.440552, "task_id": "math_001", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1383}
236
+ {"ts": 1777175337.131737, "task_id": "math_001", "task_type": "math", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1299}
237
+ {"ts": 1777175357.3943813, "task_id": "math_027", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.03575, "total": 0.96425}, "completion_len": 946}
238
+ {"ts": 1777175357.681536, "task_id": "math_027", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.014249999999999999, "total": -0.014249999999999999}, "completion_len": 858}
239
+ {"ts": 1777175374.7662253, "task_id": "json_007", "task_type": "json", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1341}
240
+ {"ts": 1777175375.5082958, "task_id": "json_007", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.0, "total": 1.1}, "completion_len": 649}
241
+ {"ts": 1777175397.8982294, "task_id": "math_008", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.09475, "total": 0.005250000000000005}, "completion_len": 1180}
242
+ {"ts": 1777175403.6554832, "task_id": "math_008", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1251}
243
+ {"ts": 1777175425.7587645, "task_id": "code_013", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.08325, "total": 1.01675}, "completion_len": 1134}
244
+ {"ts": 1777175431.7841861, "task_id": "code_013", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.05225, "total": 1.0477500000000002}, "completion_len": 1010}
245
+ {"ts": 1777175449.0808718, "task_id": "json_002", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.059250000000000004, "total": 1.04075}, "completion_len": 1038}
246
+ {"ts": 1777175449.9700878, "task_id": "json_002", "task_type": "json", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.06475, "total": 0.035250000000000004}, "completion_len": 1060}
247
+ {"ts": 1777175469.8912854, "task_id": "math_023", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.034499999999999996, "total": 0.9655}, "completion_len": 939}
248
+ {"ts": 1777175472.3405862, "task_id": "math_023", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.07950000000000002, "total": 0.9205}, "completion_len": 1120}
249
+ {"ts": 1777175492.9372365, "task_id": "math_010", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1212}
250
+ {"ts": 1777175493.4908137, "task_id": "math_010", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.04225, "total": 0.05775}, "completion_len": 970}
251
+ {"ts": 1777175515.035878, "task_id": "math_011", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.06875, "total": -0.06875}, "completion_len": 1076}
252
+ {"ts": 1777175515.2191045, "task_id": "math_011", "task_type": "math", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.06475, "total": 1.03525}, "completion_len": 1060}
253
+ {"ts": 1777175532.0130265, "task_id": "math_014", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1534}
254
+ {"ts": 1777175532.1976247, "task_id": "math_014", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.05550000000000001, "total": 0.9445}, "completion_len": 1023}
255
+ {"ts": 1777175548.3190265, "task_id": "math_012", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1290}
256
+ {"ts": 1777175552.7855537, "task_id": "math_012", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.06949999999999999, "total": 0.030500000000000013}, "completion_len": 1079}
257
+ {"ts": 1777175573.1027837, "task_id": "math_004", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.043250000000000004, "total": 0.95675}, "completion_len": 974}
258
+ {"ts": 1777175573.3594387, "task_id": "math_004", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1223}
259
+ {"ts": 1777175590.7691643, "task_id": "math_015", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.09200000000000001, "total": -0.09200000000000001}, "completion_len": 1181}
260
+ {"ts": 1777175591.013644, "task_id": "math_015", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.062, "total": 0.038000000000000006}, "completion_len": 1049}
261
+ {"ts": 1777175611.3889132, "task_id": "math_018", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.05475, "total": 0.94525}, "completion_len": 1020}
262
+ {"ts": 1777175617.2103305, "task_id": "math_018", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1231}
263
+ {"ts": 1777175636.4076352, "task_id": "code_003", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1416}
264
+ {"ts": 1777175638.3863735, "task_id": "code_003", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1561}
265
+ {"ts": 1777175660.9959147, "task_id": "code_020", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1216}
266
+ {"ts": 1777175666.9646652, "task_id": "code_020", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.043500000000000004, "total": -0.043500000000000004}, "completion_len": 975}
267
+ {"ts": 1777175684.1038857, "task_id": "math_009", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1412}
268
+ {"ts": 1777175689.9629886, "task_id": "math_009", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1324}
269
+ {"ts": 1777175707.2088346, "task_id": "json_006", "task_type": "json", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.07375000000000001, "total": -0.07375000000000001}, "completion_len": 1096}
270
+ {"ts": 1777175708.1230671, "task_id": "json_006", "task_type": "json", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.0, "total": 0.1}, "completion_len": 796}
271
+ {"ts": 1777175730.0257103, "task_id": "code_007", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1396}
272
+ {"ts": 1777175734.7199624, "task_id": "code_007", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.08625000000000001, "total": 1.0137500000000002}, "completion_len": 1146}
273
+ {"ts": 1777175751.4488666, "task_id": "math_003", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1382}
274
+ {"ts": 1777175752.7618048, "task_id": "math_003", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.05475, "total": 0.94525}, "completion_len": 1020}
275
+ {"ts": 1777175770.2901957, "task_id": "math_013", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.07900000000000001, "total": 0.02099999999999999}, "completion_len": 1117}
276
+ {"ts": 1777175772.2442117, "task_id": "math_013", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.05550000000000001, "total": 0.9445}, "completion_len": 1025}
277
+ {"ts": 1777175788.7024896, "task_id": "code_001", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.08600000000000001, "total": 1.014}, "completion_len": 1145}
278
+ {"ts": 1777175789.1276822, "task_id": "code_001", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.07800000000000001, "total": 1.022}, "completion_len": 1113}
279
+ {"ts": 1777175811.1745636, "task_id": "math_008", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1389}
280
+ {"ts": 1777175817.1482754, "task_id": "math_008", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.07925, "total": -0.07925}, "completion_len": 1118}
281
+ {"ts": 1777175834.1070986, "task_id": "json_001", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.09050000000000001, "total": 1.0095}, "completion_len": 1163}
282
+ {"ts": 1777175834.615735, "task_id": "json_001", "task_type": "json", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1483}
283
+ {"ts": 1777175851.5951793, "task_id": "code_002", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1624}
284
+ {"ts": 1777175852.0926504, "task_id": "code_002", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1231}
285
+ {"ts": 1777175873.2218435, "task_id": "math_019", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1372}
286
+ {"ts": 1777175873.5613837, "task_id": "math_019", "task_type": "math", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1403}
287
+ {"ts": 1777175896.0177104, "task_id": "code_017", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1380}
288
+ {"ts": 1777175900.7860854, "task_id": "code_017", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1346}
289
+ {"ts": 1777175918.227239, "task_id": "json_004", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.0955, "total": 1.0045000000000002}, "completion_len": 1183}
290
+ {"ts": 1777175919.0461085, "task_id": "json_004", "task_type": "json", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1349}
291
+ {"ts": 1777175936.242021, "task_id": "code_006", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1202}
292
+ {"ts": 1777175940.1184714, "task_id": "code_006", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1252}
293
+ {"ts": 1777175960.709051, "task_id": "math_024", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.08275, "total": 0.91725}, "completion_len": 1132}
294
+ {"ts": 1777175964.4923527, "task_id": "math_024", "task_type": "math", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.09575, "total": 1.00425}, "completion_len": 1184}
295
+ {"ts": 1777175982.0470245, "task_id": "math_007", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1263}
296
+ {"ts": 1777175982.8157668, "task_id": "math_007", "task_type": "math", "reward": {"correctness": 1.0, "format": 0.0, "brevity": -0.1, "total": 0.9}, "completion_len": 1329}
297
+ {"ts": 1777176000.7275205, "task_id": "math_026", "task_type": "math", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.0, "total": 0.1}, "completion_len": 746}
298
+ {"ts": 1777176001.3030024, "task_id": "math_026", "task_type": "math", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1272}
299
+ {"ts": 1777176023.9129438, "task_id": "code_012", "task_type": "code", "reward": {"correctness": 0.0, "format": 0.0, "brevity": -0.1, "total": -0.1}, "completion_len": 1272}
300
+ {"ts": 1777176029.837695, "task_id": "code_012", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.060250000000000005, "total": 1.0397500000000002}, "completion_len": 1042}
301
+ {"ts": 1777176049.5557144, "task_id": "code_009", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1223}
302
+ {"ts": 1777176052.6288605, "task_id": "code_009", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1373}
303
+ {"ts": 1777176070.2213323, "task_id": "code_014", "task_type": "code", "reward": {"correctness": 1.0, "format": 1.0, "brevity": -0.1, "total": 1.0}, "completion_len": 1289}
304
+ {"ts": 1777176076.0898345, "task_id": "code_014", "task_type": "code", "reward": {"correctness": 0.0, "format": 1.0, "brevity": -0.1, "total": 0.0}, "completion_len": 1442}
scripts/eval_trained.py ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Phase 6: Evaluate the GRPO-trained agent on the test split.
3
+
4
+ Loads:
5
+ - base agent: Qwen/Qwen2.5-1.5B-Instruct (frozen weights)
6
+ - LoRA adapter: from --adapter (local dir or HF model repo id)
7
+
8
+ For each test task:
9
+ 1. build agent input (same as training)
10
+ 2. agent generates a candidate system prompt
11
+ 3. env runs LLM-under-test with that prompt; verify; reward
12
+ 4. up to --max-turns retries with the previous attempt visible
13
+
14
+ Outputs results/trained_agent.json in the same shape as run_baseline.py.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import argparse
20
+ import json
21
+ import os
22
+ import sys
23
+ import time
24
+ from pathlib import Path
25
+ from typing import Any, Dict, List
26
+
27
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
28
+
29
+ from src.envs.promptops_arena.server.environment import PromptOpsArenaEnvironment
30
+ from src.envs.promptops_arena.tasks import load_tasks
31
+ from src.envs.promptops_arena import llm_under_test
32
+ from scripts.train_grpo import build_agent_input # reuse the exact prompt template
33
+
34
+
35
+ def _load_agent(base_model: str, adapter: str | None):
36
+ import torch # type: ignore
37
+ from transformers import AutoModelForCausalLM, AutoTokenizer # type: ignore
38
+
39
+ device = "cuda" if torch.cuda.is_available() else "cpu"
40
+ dtype = torch.bfloat16 if device == "cuda" else torch.float32
41
+
42
+ tok = AutoTokenizer.from_pretrained(base_model)
43
+ if tok.pad_token is None:
44
+ tok.pad_token = tok.eos_token
45
+
46
+ mdl = AutoModelForCausalLM.from_pretrained(
47
+ base_model, torch_dtype=dtype, device_map=device,
48
+ )
49
+ if adapter:
50
+ from peft import PeftModel # type: ignore
51
+ mdl = PeftModel.from_pretrained(mdl, adapter)
52
+ mdl.eval()
53
+
54
+ def gen(text: str, max_new_tokens: int = 300) -> str:
55
+ msgs = [
56
+ {"role": "system", "content": "You are a helpful prompt engineer."},
57
+ {"role": "user", "content": text},
58
+ ]
59
+ encoded = tok.apply_chat_template(
60
+ msgs, add_generation_prompt=True, return_tensors="pt",
61
+ )
62
+ if hasattr(encoded, "input_ids"):
63
+ ids = encoded.input_ids
64
+ elif isinstance(encoded, dict):
65
+ ids = encoded["input_ids"]
66
+ else:
67
+ ids = encoded
68
+ ids = ids.to(device)
69
+ with torch.no_grad():
70
+ out = mdl.generate(
71
+ input_ids=ids, max_new_tokens=max_new_tokens,
72
+ do_sample=False, pad_token_id=tok.eos_token_id,
73
+ )
74
+ return tok.decode(out[0][ids.shape[1]:], skip_special_tokens=True).strip()
75
+
76
+ return gen
77
+
78
+
79
+ def _build_followup_input(task: dict, history: List[dict]) -> str:
80
+ """Like build_agent_input but with prior attempts visible (refinement turn)."""
81
+ base = build_agent_input(task)
82
+ if not history:
83
+ return base
84
+ extra = ["", "PRIOR ATTEMPTS (yours, with what the small model produced):"]
85
+ for i, h in enumerate(history, 1):
86
+ extra.append(f"--- attempt {i} (reward={h['reward']:.2f}, correct={h['correct']}) ---")
87
+ extra.append(f"YOUR PROMPT: {h['prompt'][:400]}")
88
+ extra.append(f"MODEL OUTPUT: {h['completion'][:200]}")
89
+ extra.append("")
90
+ extra.append("Improve the system prompt. Output ONLY the new system prompt, no preamble.")
91
+ return base + "\n" + "\n".join(extra)
92
+
93
+
94
+ def evaluate_trained(env, task, agent_gen, max_turns: int = 3) -> Dict[str, Any]:
95
+ history: List[dict] = []
96
+ best_reward = -1.0
97
+ best_components: Dict[str, float] = {}
98
+ correct = False
99
+ edit_turns = 0
100
+
101
+ for turn in range(max_turns):
102
+ edit_turns = turn + 1
103
+ ai = build_agent_input(task) if turn == 0 else _build_followup_input(task, history)
104
+ sp = agent_gen(ai).strip() or "Solve this:"
105
+ res = env.execute_prompt(task, sp)
106
+ components = res["reward"]
107
+ total = components["total"]
108
+ is_correct = components["correctness"] >= 1.0
109
+
110
+ history.append({
111
+ "prompt": sp,
112
+ "completion": res["completion"],
113
+ "reward": total,
114
+ "correct": is_correct,
115
+ })
116
+
117
+ if total > best_reward:
118
+ best_reward = total
119
+ best_components = components
120
+
121
+ if is_correct:
122
+ correct = True
123
+ break
124
+
125
+ return {
126
+ "task_id": task["id"],
127
+ "task_type": task["type"],
128
+ "policy": "trained_agent",
129
+ "edit_turns": edit_turns,
130
+ "final_reward": best_reward,
131
+ "correct": correct,
132
+ "format_ok": best_components.get("format", 0.0) >= 1.0,
133
+ "components": best_components,
134
+ "trace": history,
135
+ }
136
+
137
+
138
+ def main():
139
+ p = argparse.ArgumentParser()
140
+ p.add_argument("--adapter", default=None,
141
+ help="Local dir or HF repo id of the LoRA adapter.")
142
+ p.add_argument("--base", default="Qwen/Qwen2.5-1.5B-Instruct")
143
+ p.add_argument("--split", default="test")
144
+ p.add_argument("--out", default="results/trained_agent.json")
145
+ p.add_argument("--limit", type=int, default=None)
146
+ p.add_argument("--per-type", type=int, default=None)
147
+ p.add_argument("--max-turns", type=int, default=3)
148
+ args = p.parse_args()
149
+
150
+ os.environ.setdefault("PROMPTOPS_LLM_BACKEND", "transformers")
151
+
152
+ tasks = load_tasks(split=args.split)
153
+ if args.per_type:
154
+ bucketed: Dict[str, List[dict]] = {}
155
+ for t in tasks:
156
+ bucketed.setdefault(t["type"], []).append(t)
157
+ sampled: List[dict] = []
158
+ for tt, lst in bucketed.items():
159
+ sampled.extend(lst[: args.per_type])
160
+ tasks = sampled
161
+ if args.limit:
162
+ tasks = tasks[: args.limit]
163
+
164
+ print(f"[eval_trained] adapter={args.adapter} base={args.base} "
165
+ f"split={args.split} n_tasks={len(tasks)} "
166
+ f"llm_backend={llm_under_test.backend_name()}")
167
+
168
+ env = PromptOpsArenaEnvironment(split=args.split, seed=0)
169
+ agent_gen = _load_agent(args.base, args.adapter)
170
+
171
+ rows: List[Dict[str, Any]] = []
172
+ t0 = time.time()
173
+ for i, task in enumerate(tasks):
174
+ row = evaluate_trained(env, task, agent_gen, max_turns=args.max_turns)
175
+ rows.append(row)
176
+ n_correct = sum(1 for r in rows if r["correct"])
177
+ print(f" [{i+1}/{len(tasks)}] {task['type']:5s} "
178
+ f"correct={n_correct}/{i+1} "
179
+ f"r={row['final_reward']:+.3f} elapsed={time.time()-t0:.1f}s")
180
+
181
+ by_type: Dict[str, Dict[str, int]] = {}
182
+ for r in rows:
183
+ d = by_type.setdefault(r["task_type"], {"n": 0, "correct": 0, "format": 0})
184
+ d["n"] += 1
185
+ d["correct"] += int(r["correct"])
186
+ d["format"] += int(r["format_ok"])
187
+
188
+ overall = {
189
+ "n": len(rows),
190
+ "correct": sum(1 for r in rows if r["correct"]),
191
+ "format": sum(1 for r in rows if r["format_ok"]),
192
+ "mean_reward": sum(r["final_reward"] for r in rows) / max(1, len(rows)),
193
+ }
194
+
195
+ out = {
196
+ "policy": "trained_agent",
197
+ "adapter": args.adapter,
198
+ "base_model": args.base,
199
+ "split": args.split,
200
+ "llm_backend": llm_under_test.backend_name(),
201
+ "by_type": by_type,
202
+ "overall": overall,
203
+ "rows": rows,
204
+ }
205
+
206
+ out_path = Path(args.out)
207
+ out_path.parent.mkdir(parents=True, exist_ok=True)
208
+ out_path.write_text(json.dumps(out, indent=2), encoding="utf-8")
209
+ print(f"\n[eval_trained] wrote {out_path}")
210
+ print(f" overall: {overall['correct']}/{overall['n']} correct, "
211
+ f"mean_reward={overall['mean_reward']:.3f}")
212
+ for tt, d in by_type.items():
213
+ print(f" {tt:5s}: {d['correct']}/{d['n']} correct, format {d['format']}/{d['n']}")
214
+
215
+
216
+ if __name__ == "__main__":
217
+ main()
scripts/hf_eval_entry.sh ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # HF Jobs entrypoint: evaluate the trained agent + rerun real-LLM baselines on a
3
+ # wider per-type sample, then upload all results to the model repo.
4
+
5
+ set -euo pipefail
6
+
7
+ HF_USERNAME="${HF_USERNAME:-Dar3devil}"
8
+ PER_TYPE="${PER_TYPE:-4}"
9
+ ADAPTER_REPO="${ADAPTER_REPO:-${HF_USERNAME}/promptops-arena-agent}"
10
+ RESULTS_REPO="${ADAPTER_REPO}"
11
+
12
+ echo "[eval] HF_USERNAME=${HF_USERNAME} PER_TYPE=${PER_TYPE} ADAPTER_REPO=${ADAPTER_REPO}"
13
+ mkdir -p /workspace
14
+ cp -r /code/. /workspace/
15
+ cd /workspace
16
+
17
+ echo "[eval] python: $(python --version)"
18
+ nvidia-smi || echo "no nvidia-smi"
19
+
20
+ echo "[eval] installing deps"
21
+ pip install --no-cache-dir --upgrade pip
22
+ pip install --no-cache-dir \
23
+ "transformers==4.55.4" \
24
+ "peft==0.15.2" \
25
+ "accelerate==1.7.0" \
26
+ "datasets==3.6.0" \
27
+ "huggingface_hub>=0.25.0" \
28
+ "jsonschema>=4.20.0" \
29
+ "openenv-core>=0.1.0" \
30
+ "fastapi>=0.110.0" \
31
+ "uvicorn>=0.27.0" \
32
+ "pydantic>=2.0.0"
33
+
34
+ export PROMPTOPS_LLM_BACKEND=transformers
35
+ export PYTHONUTF8=1
36
+ export TOKENIZERS_PARALLELISM=false
37
+
38
+ mkdir -p outputs results
39
+
40
+ echo "[eval] downloading adapter ${ADAPTER_REPO}"
41
+ python - <<PY
42
+ from huggingface_hub import snapshot_download
43
+ import os
44
+ p = snapshot_download(repo_id="${ADAPTER_REPO}", repo_type="model",
45
+ local_dir="outputs/grpo-lora",
46
+ allow_patterns=["adapter_*", "*.json", "*.jinja", "*.txt", "training_log.jsonl"])
47
+ print("[eval] adapter at", p)
48
+ PY
49
+
50
+ echo "[eval] running zero-shot baseline (real LLM)"
51
+ python scripts/run_baseline.py --policy zero_shot --per-type "${PER_TYPE}" \
52
+ --out results/baseline_zero_shot_real.json
53
+
54
+ echo "[eval] running CoT baseline (real LLM)"
55
+ python scripts/run_baseline.py --policy cot --per-type "${PER_TYPE}" \
56
+ --out results/baseline_cot_real.json
57
+
58
+ echo "[eval] running trained-agent eval"
59
+ python scripts/eval_trained.py --adapter outputs/grpo-lora --per-type "${PER_TYPE}" \
60
+ --out results/trained_agent.json --max-turns 2
61
+
62
+ echo "[eval] uploading results to ${RESULTS_REPO}"
63
+ python - <<PY
64
+ import os
65
+ from huggingface_hub import HfApi
66
+ api = HfApi()
67
+ repo = "${RESULTS_REPO}"
68
+ for f in [
69
+ "results/baseline_zero_shot_real.json",
70
+ "results/baseline_cot_real.json",
71
+ "results/trained_agent.json",
72
+ ]:
73
+ api.upload_file(path_or_fileobj=f, path_in_repo=os.path.basename(f),
74
+ repo_id=repo, repo_type="model",
75
+ commit_message=f"eval: {os.path.basename(f)}")
76
+ print("[eval] uploaded")
77
+ PY
78
+
79
+ echo "[eval] all done."
scripts/hf_job_entry.sh ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Entrypoint executed inside the HF Jobs container.
3
+ # Expects:
4
+ # /code -> RO mount of dataset Dar3devil/promptops-arena-src
5
+ # $HF_TOKEN -> secret, for pushing the trained adapter
6
+ # $HF_USERNAME -> user namespace for the model repo (default: Dar3devil)
7
+ # $STEPS, $BATCH, $NUM_GENS (optional overrides)
8
+
9
+ set -euo pipefail
10
+
11
+ HF_USERNAME="${HF_USERNAME:-Dar3devil}"
12
+ STEPS="${STEPS:-200}"
13
+ BATCH="${BATCH:-4}"
14
+ NUM_GENS="${NUM_GENS:-4}"
15
+ LOG_LEVEL="${LOG_LEVEL:-info}"
16
+ MODEL_REPO="${HF_USERNAME}/promptops-arena-agent"
17
+
18
+ echo "[entry] HF_USERNAME=${HF_USERNAME} STEPS=${STEPS} BATCH=${BATCH} NUM_GENS=${NUM_GENS}"
19
+ echo "[entry] copying source from /code -> /workspace"
20
+ mkdir -p /workspace
21
+ cp -r /code/. /workspace/
22
+ cd /workspace
23
+
24
+ echo "[entry] python: $(python --version)"
25
+ echo "[entry] gpu:"
26
+ nvidia-smi || echo "no nvidia-smi"
27
+
28
+ echo "[entry] installing deps (pinned for trl 0.21 stack)"
29
+ pip install --no-cache-dir --upgrade pip
30
+ pip install --no-cache-dir \
31
+ "trl==0.21.0" \
32
+ "transformers==4.55.4" \
33
+ "peft==0.15.2" \
34
+ "accelerate==1.7.0" \
35
+ "datasets==3.6.0" \
36
+ "huggingface_hub>=0.25.0" \
37
+ "jsonschema>=4.20.0" \
38
+ "openenv-core>=0.1.0" \
39
+ "fastapi>=0.110.0" \
40
+ "uvicorn>=0.27.0" \
41
+ "pydantic>=2.0.0"
42
+
43
+ export PROMPTOPS_LLM_BACKEND=transformers
44
+ export PYTHONUTF8=1
45
+ export TOKENIZERS_PARALLELISM=false
46
+
47
+ echo "[entry] launching GRPO training"
48
+ python scripts/train_grpo.py \
49
+ --steps "${STEPS}" \
50
+ --batch "${BATCH}" \
51
+ --num-generations "${NUM_GENS}" \
52
+ --out outputs/grpo-lora \
53
+ --log results/training_log.jsonl
54
+
55
+ echo "[entry] training done. uploading adapter + log to ${MODEL_REPO}"
56
+ python - <<'PY'
57
+ import os
58
+ from huggingface_hub import HfApi, create_repo
59
+
60
+ api = HfApi()
61
+ repo_id = f"{os.environ['HF_USERNAME']}/promptops-arena-agent"
62
+ create_repo(repo_id, repo_type="model", exist_ok=True, private=False)
63
+
64
+ api.upload_folder(
65
+ folder_path="outputs/grpo-lora",
66
+ repo_id=repo_id,
67
+ repo_type="model",
68
+ commit_message="GRPO-trained LoRA adapter",
69
+ )
70
+ # also upload training log so we can plot reward curves locally
71
+ api.upload_file(
72
+ path_or_fileobj="results/training_log.jsonl",
73
+ path_in_repo="training_log.jsonl",
74
+ repo_id=repo_id,
75
+ repo_type="model",
76
+ commit_message="training reward log",
77
+ )
78
+ print(f"[entry] uploaded to https://huggingface.co/{repo_id}")
79
+ PY
80
+
81
+ echo "[entry] all done."
scripts/plot_results.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Phase 7: Build reward-curve plot + comparison artifact.
3
+
4
+ Inputs (any subset that exists):
5
+ results/training_log.jsonl # per-step rewards from GRPO
6
+ results/baseline_zero_shot_real_subset.json
7
+ results/baseline_cot_real_subset.json
8
+ results/baseline_zero_shot_stub.json
9
+ results/baseline_cot_stub.json
10
+ results/trained_agent.json # eval of the trained agent (Phase 6)
11
+
12
+ Outputs:
13
+ results/comparison.json
14
+ docs/reward_curve.png
15
+ docs/baseline_comparison.png
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import argparse
21
+ import json
22
+ from pathlib import Path
23
+ from typing import Any, Dict, List
24
+
25
+
26
+ def load_json(p: Path) -> dict | None:
27
+ if not p.exists():
28
+ return None
29
+ try:
30
+ return json.loads(p.read_text(encoding="utf-8"))
31
+ except Exception as e:
32
+ print(f"[plot] WARN failed to load {p}: {e}")
33
+ return None
34
+
35
+
36
+ def load_jsonl(p: Path) -> List[dict]:
37
+ if not p.exists():
38
+ return []
39
+ rows: List[dict] = []
40
+ for line in p.read_text(encoding="utf-8").splitlines():
41
+ line = line.strip()
42
+ if not line:
43
+ continue
44
+ try:
45
+ rows.append(json.loads(line))
46
+ except Exception:
47
+ pass
48
+ return rows
49
+
50
+
51
+ def smooth(values: List[float], window: int = 10) -> List[float]:
52
+ out: List[float] = []
53
+ for i in range(len(values)):
54
+ lo = max(0, i - window + 1)
55
+ out.append(sum(values[lo:i + 1]) / max(1, i + 1 - lo))
56
+ return out
57
+
58
+
59
+ def main():
60
+ p = argparse.ArgumentParser()
61
+ p.add_argument("--results-dir", default="results")
62
+ p.add_argument("--docs-dir", default="docs")
63
+ args = p.parse_args()
64
+
65
+ res = Path(args.results_dir)
66
+ docs = Path(args.docs_dir)
67
+ docs.mkdir(parents=True, exist_ok=True)
68
+
69
+ import matplotlib
70
+ matplotlib.use("Agg")
71
+ import matplotlib.pyplot as plt
72
+
73
+ # ---- 1. reward curve ----
74
+ log_rows = load_jsonl(res / "training_log.jsonl")
75
+ if log_rows:
76
+ rewards = [r["reward"]["total"] for r in log_rows if "reward" in r]
77
+ smoothed = smooth(rewards, window=20)
78
+ fig, ax = plt.subplots(figsize=(8, 4.5))
79
+ ax.plot(rewards, alpha=0.25, label="raw reward")
80
+ ax.plot(smoothed, linewidth=2, label="rolling avg (20)")
81
+ ax.set_xlabel("training rollout #")
82
+ ax.set_ylabel("reward")
83
+ ax.set_title("GRPO training reward curve · PromptOps Arena")
84
+ ax.grid(alpha=0.3)
85
+ ax.legend()
86
+ fig.tight_layout()
87
+ out = docs / "reward_curve.png"
88
+ fig.savefig(out, dpi=140)
89
+ plt.close(fig)
90
+ print(f"[plot] wrote {out} ({len(rewards)} points)")
91
+ else:
92
+ print("[plot] no training_log.jsonl yet -> skip reward curve")
93
+
94
+ # ---- 2. baseline comparison ----
95
+ files = {
96
+ "zero_shot (stub)": res / "baseline_zero_shot_stub.json",
97
+ "cot (stub)": res / "baseline_cot_stub.json",
98
+ "zero_shot (real LLM)": res / "baseline_zero_shot_real.json",
99
+ "cot (real LLM)": res / "baseline_cot_real.json",
100
+ "trained agent (real LLM)": res / "trained_agent.json",
101
+ }
102
+ # fall back to the smaller subset files if the wider-n versions don't exist
103
+ fallback = {
104
+ "zero_shot (real LLM)": res / "baseline_zero_shot_real_subset.json",
105
+ "cot (real LLM)": res / "baseline_cot_real_subset.json",
106
+ }
107
+ for k, p in fallback.items():
108
+ if not files[k].exists() and p.exists():
109
+ files[k] = p
110
+
111
+ rows: Dict[str, Dict[str, Any]] = {}
112
+ for label, path in files.items():
113
+ d = load_json(path)
114
+ if d is None:
115
+ continue
116
+ ov = d.get("overall", {})
117
+ rows[label] = {
118
+ "n": ov.get("n", 0),
119
+ "correct": ov.get("correct", 0),
120
+ "format": ov.get("format", 0),
121
+ "mean_reward": ov.get("mean_reward", 0.0),
122
+ "by_type": d.get("by_type", {}),
123
+ "backend": d.get("llm_backend", "unknown"),
124
+ }
125
+
126
+ comparison = {
127
+ "policies": rows,
128
+ "ranking_by_mean_reward": sorted(
129
+ rows.items(),
130
+ key=lambda kv: kv[1]["mean_reward"],
131
+ reverse=True,
132
+ ),
133
+ }
134
+ (res / "comparison.json").write_text(
135
+ json.dumps(comparison, indent=2), encoding="utf-8"
136
+ )
137
+ print(f"[plot] wrote {res/'comparison.json'}")
138
+
139
+ # ---- 3. comparison bar chart ----
140
+ if rows:
141
+ labels = list(rows.keys())
142
+ means = [rows[l]["mean_reward"] for l in labels]
143
+ accs = [rows[l]["correct"] / max(1, rows[l]["n"]) for l in labels]
144
+
145
+ fig, axes = plt.subplots(1, 2, figsize=(11, 4.5))
146
+ axes[0].barh(labels, means, color="#4c72b0")
147
+ axes[0].set_xlabel("mean reward")
148
+ axes[0].set_title("Mean reward by policy")
149
+ axes[0].grid(axis="x", alpha=0.3)
150
+ axes[0].invert_yaxis()
151
+
152
+ axes[1].barh(labels, accs, color="#55a868")
153
+ axes[1].set_xlabel("fraction correct")
154
+ axes[1].set_title("Correctness by policy")
155
+ axes[1].set_xlim(0, 1)
156
+ axes[1].grid(axis="x", alpha=0.3)
157
+ axes[1].invert_yaxis()
158
+
159
+ fig.tight_layout()
160
+ out = docs / "baseline_comparison.png"
161
+ fig.savefig(out, dpi=140)
162
+ plt.close(fig)
163
+ print(f"[plot] wrote {out}")
164
+
165
+
166
+ if __name__ == "__main__":
167
+ main()
scripts/push_space.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Create + push the HF Space for PromptOps Arena."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from huggingface_hub import HfApi, create_repo
10
+
11
+ ROOT = Path(__file__).resolve().parents[1]
12
+ SPACE_ID = os.environ.get("SPACE_ID", "Dar3devil/promptops-arena")
13
+
14
+
15
+ def main() -> int:
16
+ token = os.environ.get("HF_TOKEN")
17
+ api = HfApi(token=token)
18
+
19
+ create_repo(
20
+ repo_id=SPACE_ID,
21
+ repo_type="space",
22
+ space_sdk="gradio",
23
+ exist_ok=True,
24
+ token=token,
25
+ )
26
+
27
+ ignore = [
28
+ "outputs/*",
29
+ "outputs/**",
30
+ ".venv/*",
31
+ ".venv/**",
32
+ ".git/*",
33
+ ".git/**",
34
+ "__pycache__/*",
35
+ "**/__pycache__/**",
36
+ "*.pyc",
37
+ "node_modules/**",
38
+ ".pytest_cache/**",
39
+ ".mypy_cache/**",
40
+ ".ruff_cache/**",
41
+ ".cursor/**",
42
+ "logs/**",
43
+ "results/.cache/**",
44
+ ]
45
+
46
+ print(f"[push] uploading {ROOT} -> space {SPACE_ID}")
47
+ api.upload_folder(
48
+ folder_path=str(ROOT),
49
+ repo_id=SPACE_ID,
50
+ repo_type="space",
51
+ ignore_patterns=ignore,
52
+ commit_message="PromptOps Arena demo",
53
+ )
54
+ print(f"[push] done. https://huggingface.co/spaces/{SPACE_ID}")
55
+ return 0
56
+
57
+
58
+ if __name__ == "__main__":
59
+ sys.exit(main())
scripts/run_baseline.py ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Phase 4 baselines on the held-out test split.
3
+
4
+ Three policies:
5
+ zero_shot : "Solve this:" wrapper, no CoT
6
+ cot : "Think step by step. Final answer in <answer> tags." style
7
+ untrained : Qwen2.5-1.5B-Instruct (no LoRA) writes the system prompt,
8
+ then LLM-under-test runs it. 3 edit turns.
9
+
10
+ For local CPU dev we run zero_shot/cot only with the stub backend; untrained
11
+ is meant for GPU runs (CUDA or HF Jobs).
12
+
13
+ Usage:
14
+ python scripts\run_baseline.py --policy zero_shot --out results/baseline_zero_shot.json
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import argparse
20
+ import json
21
+ import os
22
+ import sys
23
+ import time
24
+ from pathlib import Path
25
+ from typing import Any, Dict, List
26
+
27
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
28
+
29
+ from src.envs.promptops_arena.server.environment import PromptOpsArenaEnvironment
30
+ from src.envs.promptops_arena.models import PromptOpsAction
31
+ from src.envs.promptops_arena.tasks import load_tasks
32
+ from src.envs.promptops_arena import llm_under_test
33
+
34
+
35
+ ZERO_SHOT_PROMPT = "Solve this:"
36
+
37
+ COT_PROMPT_BY_TYPE = {
38
+ "math": (
39
+ "Think step by step. After reasoning, put ONLY the final numeric answer "
40
+ "inside <answer>...</answer> tags. Do not include units or words inside the tags."
41
+ ),
42
+ "code": (
43
+ "Write the requested Python function. Reason briefly, then output exactly one "
44
+ "```python ...``` code block containing only the function definition. "
45
+ "Do not include explanations after the code block."
46
+ ),
47
+ "json": (
48
+ "Extract the requested fields. Output exactly one ```json ...``` code block "
49
+ "containing a JSON object that matches the schema. Use the correct types. "
50
+ "No prose."
51
+ ),
52
+ }
53
+
54
+
55
+ def _evaluate_zero_shot(env: PromptOpsArenaEnvironment, task: dict) -> Dict[str, Any]:
56
+ res = env.execute_prompt(task, ZERO_SHOT_PROMPT)
57
+ return {
58
+ "task_id": task["id"],
59
+ "task_type": task["type"],
60
+ "policy": "zero_shot",
61
+ "edit_turns": 1,
62
+ "final_reward": res["reward"]["total"],
63
+ "correct": res["reward"]["correctness"] >= 1.0,
64
+ "format_ok": res["reward"]["format"] >= 1.0,
65
+ "components": res["reward"],
66
+ }
67
+
68
+
69
+ def _evaluate_cot(env: PromptOpsArenaEnvironment, task: dict) -> Dict[str, Any]:
70
+ sp = COT_PROMPT_BY_TYPE.get(task["type"], ZERO_SHOT_PROMPT)
71
+ res = env.execute_prompt(task, sp)
72
+ return {
73
+ "task_id": task["id"],
74
+ "task_type": task["type"],
75
+ "policy": "cot",
76
+ "edit_turns": 1,
77
+ "final_reward": res["reward"]["total"],
78
+ "correct": res["reward"]["correctness"] >= 1.0,
79
+ "format_ok": res["reward"]["format"] >= 1.0,
80
+ "components": res["reward"],
81
+ }
82
+
83
+
84
+ def _build_agent_input(task: dict, history: List[dict]) -> str:
85
+ """Build the prompt the agent sees when asked to write a system prompt."""
86
+ parts = [
87
+ "You are a prompt engineer. Your job is to write a SYSTEM PROMPT that, "
88
+ "when given to a small language model along with the task below, will "
89
+ "produce a correct answer in the required format.",
90
+ "",
91
+ f"TASK TYPE: {task['type']}",
92
+ f"TASK: {task['question']}",
93
+ "",
94
+ ]
95
+ if task["type"] == "math":
96
+ parts.append("REQUIRED FORMAT: the answer must be a number inside <answer>...</answer> tags.")
97
+ elif task["type"] == "code":
98
+ parts.append("REQUIRED FORMAT: a single ```python ...``` code block defining the requested function.")
99
+ elif task["type"] == "json":
100
+ parts.append("REQUIRED FORMAT: a single ```json ...``` code block with a valid JSON object matching the schema.")
101
+ if "schema" in task:
102
+ parts.append(f"SCHEMA: {json.dumps(task['schema'])}")
103
+
104
+ if history:
105
+ parts.append("")
106
+ parts.append("PREVIOUS ATTEMPTS (your earlier prompts and the model's responses):")
107
+ for i, h in enumerate(history, 1):
108
+ parts.append(f"--- attempt {i} (reward={h['reward']:.2f}, correct={h['correct']}) ---")
109
+ parts.append(f"YOUR PROMPT: {h['prompt'][:400]}")
110
+ parts.append(f"MODEL OUTPUT: {h['completion'][:200]}")
111
+ parts.append("")
112
+ parts.append("Improve the system prompt. Output ONLY the new system prompt, no preamble.")
113
+ else:
114
+ parts.append("")
115
+ parts.append("Output ONLY the system prompt, no preamble.")
116
+
117
+ return "\n".join(parts)
118
+
119
+
120
+ def _evaluate_untrained_agent(
121
+ env: PromptOpsArenaEnvironment,
122
+ task: dict,
123
+ agent_generate,
124
+ max_turns: int = 3,
125
+ ) -> Dict[str, Any]:
126
+ history: List[dict] = []
127
+ best_reward = -1.0
128
+ final_components = {}
129
+ correct = False
130
+ edit_turns = 0
131
+
132
+ for turn in range(max_turns):
133
+ edit_turns = turn + 1
134
+ agent_input = _build_agent_input(task, history)
135
+ system_prompt = agent_generate(agent_input).strip()
136
+ if not system_prompt:
137
+ system_prompt = ZERO_SHOT_PROMPT
138
+
139
+ res = env.execute_prompt(task, system_prompt)
140
+ components = res["reward"]
141
+ total = components["total"]
142
+ is_correct = components["correctness"] >= 1.0
143
+
144
+ history.append({
145
+ "prompt": system_prompt,
146
+ "completion": res["completion"],
147
+ "reward": total,
148
+ "correct": is_correct,
149
+ })
150
+
151
+ if total > best_reward:
152
+ best_reward = total
153
+ final_components = components
154
+
155
+ if is_correct:
156
+ correct = True
157
+ break
158
+
159
+ return {
160
+ "task_id": task["id"],
161
+ "task_type": task["type"],
162
+ "policy": "untrained_agent",
163
+ "edit_turns": edit_turns,
164
+ "final_reward": best_reward,
165
+ "correct": correct,
166
+ "format_ok": final_components.get("format", 0.0) >= 1.0,
167
+ "components": final_components,
168
+ "trace": history,
169
+ }
170
+
171
+
172
+ def _make_agent_generate(model_id: str):
173
+ """Returns callable(text) -> generated text. Uses a separate transformers model."""
174
+ import torch # type: ignore
175
+ from transformers import AutoModelForCausalLM, AutoTokenizer # type: ignore
176
+
177
+ device = "cuda" if torch.cuda.is_available() else "cpu"
178
+ dtype = torch.bfloat16 if device == "cuda" else torch.float32
179
+
180
+ tok = AutoTokenizer.from_pretrained(model_id)
181
+ mdl = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype=dtype, device_map=device)
182
+ mdl.eval()
183
+
184
+ def gen(text: str) -> str:
185
+ msgs = [
186
+ {"role": "system", "content": "You are a helpful prompt engineer."},
187
+ {"role": "user", "content": text},
188
+ ]
189
+ encoded = tok.apply_chat_template(msgs, add_generation_prompt=True, return_tensors="pt")
190
+ if hasattr(encoded, "input_ids"):
191
+ ids = encoded.input_ids
192
+ elif isinstance(encoded, dict):
193
+ ids = encoded["input_ids"]
194
+ else:
195
+ ids = encoded
196
+ ids = ids.to(device)
197
+ with torch.no_grad():
198
+ out = mdl.generate(
199
+ input_ids=ids, max_new_tokens=300, do_sample=False,
200
+ pad_token_id=tok.eos_token_id,
201
+ )
202
+ return tok.decode(out[0][ids.shape[1]:], skip_special_tokens=True)
203
+
204
+ return gen
205
+
206
+
207
+ def main():
208
+ p = argparse.ArgumentParser()
209
+ p.add_argument("--policy", choices=["zero_shot", "cot", "untrained"], required=True)
210
+ p.add_argument("--split", default="test")
211
+ p.add_argument("--out", required=True)
212
+ p.add_argument("--limit", type=int, default=None, help="cap tasks for quick runs")
213
+ p.add_argument("--per-type", type=int, default=None, help="cap tasks per type (stratified)")
214
+ p.add_argument("--agent-model", default="Qwen/Qwen2.5-1.5B-Instruct")
215
+ args = p.parse_args()
216
+
217
+ tasks = load_tasks(split=args.split)
218
+ if args.per_type:
219
+ bucketed: Dict[str, List[dict]] = {}
220
+ for t in tasks:
221
+ bucketed.setdefault(t["type"], []).append(t)
222
+ sampled: List[dict] = []
223
+ for tt, lst in bucketed.items():
224
+ sampled.extend(lst[: args.per_type])
225
+ tasks = sampled
226
+ if args.limit:
227
+ tasks = tasks[: args.limit]
228
+
229
+ print(f"[baseline] policy={args.policy} split={args.split} n_tasks={len(tasks)} "
230
+ f"llm_backend={llm_under_test.backend_name()}")
231
+
232
+ env = PromptOpsArenaEnvironment(split=args.split, seed=0)
233
+
234
+ agent_gen = None
235
+ if args.policy == "untrained":
236
+ print(f"[baseline] loading agent model: {args.agent_model}")
237
+ agent_gen = _make_agent_generate(args.agent_model)
238
+
239
+ rows: List[Dict[str, Any]] = []
240
+ t0 = time.time()
241
+ for i, task in enumerate(tasks):
242
+ if args.policy == "zero_shot":
243
+ row = _evaluate_zero_shot(env, task)
244
+ elif args.policy == "cot":
245
+ row = _evaluate_cot(env, task)
246
+ else:
247
+ row = _evaluate_untrained_agent(env, task, agent_gen, max_turns=3)
248
+ rows.append(row)
249
+ if (i + 1) % 5 == 0 or i == len(tasks) - 1:
250
+ n_correct = sum(1 for r in rows if r["correct"])
251
+ print(f" [{i+1}/{len(tasks)}] correct={n_correct}/{i+1} "
252
+ f"elapsed={time.time()-t0:.1f}s")
253
+
254
+ by_type: Dict[str, Dict[str, int]] = {}
255
+ for r in rows:
256
+ d = by_type.setdefault(r["task_type"], {"n": 0, "correct": 0, "format": 0})
257
+ d["n"] += 1
258
+ d["correct"] += int(r["correct"])
259
+ d["format"] += int(r["format_ok"])
260
+
261
+ overall = {
262
+ "n": len(rows),
263
+ "correct": sum(1 for r in rows if r["correct"]),
264
+ "format": sum(1 for r in rows if r["format_ok"]),
265
+ "mean_reward": sum(r["final_reward"] for r in rows) / max(1, len(rows)),
266
+ }
267
+
268
+ out = {
269
+ "policy": args.policy,
270
+ "split": args.split,
271
+ "llm_backend": llm_under_test.backend_name(),
272
+ "by_type": by_type,
273
+ "overall": overall,
274
+ "rows": rows,
275
+ }
276
+
277
+ out_path = Path(args.out)
278
+ out_path.parent.mkdir(parents=True, exist_ok=True)
279
+ out_path.write_text(json.dumps(out, indent=2), encoding="utf-8")
280
+ print(f"\n[baseline] wrote {out_path}")
281
+ print(f" overall: {overall['correct']}/{overall['n']} correct, mean_reward={overall['mean_reward']:.3f}")
282
+ for tt, d in by_type.items():
283
+ print(f" {tt:5s}: {d['correct']}/{d['n']} correct, format {d['format']}/{d['n']}")
284
+
285
+
286
+ if __name__ == "__main__":
287
+ main()
scripts/smoke_test_env.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Phase 2 smoke test: in-process exercise of reset() / step() / state.
3
+
4
+ Uses the stub LLM backend (set via env var) so this runs in <1s and proves
5
+ the env plumbing works without downloading any model.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ # Allow running from project root: add project root to sys.path
15
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
16
+
17
+ os.environ.setdefault("PROMPTOPS_LLM_BACKEND", "stub")
18
+
19
+ from src.envs.promptops_arena.server.environment import PromptOpsArenaEnvironment
20
+ from src.envs.promptops_arena.models import PromptOpsAction
21
+ from src.envs.promptops_arena import llm_under_test
22
+
23
+
24
+ GOOD_PROMPTS = {
25
+ "math": (
26
+ "You are a careful math solver. Read the problem, think step by step, "
27
+ "then put ONLY the final numeric answer inside <answer>...</answer> tags. "
28
+ "Do not include units."
29
+ ),
30
+ "code": (
31
+ "You are a Python coder. Output ONLY a single ```python code block``` "
32
+ "containing the requested function. No prose, no examples, no print statements."
33
+ ),
34
+ "json": (
35
+ "You are a JSON extractor. Output ONLY a single ```json code block``` "
36
+ "containing a valid JSON object that matches the requested schema. "
37
+ "No prose."
38
+ ),
39
+ }
40
+
41
+
42
+ def run(task_type: str) -> dict:
43
+ env = PromptOpsArenaEnvironment(max_turns=3, split="train", seed=42, task_types=[task_type])
44
+ obs = env.reset()
45
+ print(f"\n=== {task_type.upper()} | task_id={env.state.task_id} ===")
46
+ print(f"task: {obs.task_text}")
47
+
48
+ action = PromptOpsAction(new_system_prompt=GOOD_PROMPTS[task_type])
49
+ obs2 = env.step(action)
50
+ print(f"completion: {obs2.last_completion[:120]!r}")
51
+ print(f"reward components: {obs2.reward_components}")
52
+ print(f"done: {obs2.done}, edit_turn: {obs2.edit_turn}, solved: {env.state.solved}")
53
+ return {
54
+ "task_type": task_type,
55
+ "reward": obs2.last_reward,
56
+ "components": obs2.reward_components,
57
+ "solved": env.state.solved,
58
+ "step_count": env.state.step_count,
59
+ }
60
+
61
+
62
+ def main() -> int:
63
+ print(f"LLM backend: {llm_under_test.backend_name()}")
64
+ results = []
65
+ for tt in ("math", "code", "json"):
66
+ results.append(run(tt))
67
+
68
+ print("\n=== Summary ===")
69
+ for r in results:
70
+ print(f" {r['task_type']:5s}: reward={r['reward']:+.3f} solved={r['solved']} "
71
+ f"step_count={r['step_count']} components={r['components']}")
72
+
73
+ # Exit-criterion check: every type produced a structured reward dict
74
+ ok = all(r["components"].get("total") is not None for r in results)
75
+ return 0 if ok else 1
76
+
77
+
78
+ if __name__ == "__main__":
79
+ raise SystemExit(main())
scripts/train_grpo.py ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Phase 5: GRPO training of the prompt-engineering agent.
3
+
4
+ Agent: Qwen/Qwen2.5-1.5B-Instruct + LoRA adapter (trained).
5
+ LLM-under-test: Qwen/Qwen2.5-0.5B-Instruct (frozen, env-side).
6
+
7
+ Each "prompt" the GRPO trainer sees describes a task. The agent's "completion"
8
+ is the system prompt it would give to the LLM-under-test. We then run the
9
+ LLM-under-test inside the reward function and return the env reward.
10
+
11
+ Modes:
12
+ --smoke : tiny config; 2 steps on CPU with stub LLM. Proves plumbing.
13
+ --hf-jobs : print the `hf jobs run` command for an a10g-large run.
14
+ default : real GRPO run; expects CUDA + transformers backend.
15
+
16
+ Outputs:
17
+ outputs/grpo-lora/ # LoRA adapter
18
+ results/training_log.jsonl # per-step rewards (custom callback)
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import argparse
24
+ import json
25
+ import os
26
+ import sys
27
+ import time
28
+ from pathlib import Path
29
+ from typing import List, Dict, Any
30
+
31
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
32
+
33
+
34
+ # ---------------------------------------------------------------------------
35
+ # Agent-input builder (kept consistent with run_baseline._build_agent_input)
36
+ # ---------------------------------------------------------------------------
37
+
38
+ def build_agent_input(task: dict) -> str:
39
+ parts = [
40
+ "You are a prompt engineer. Your job is to write a SYSTEM PROMPT that, "
41
+ "when given to a small language model along with the task below, will "
42
+ "produce a correct answer in the required format.",
43
+ "",
44
+ f"TASK TYPE: {task['type']}",
45
+ f"TASK: {task['question']}",
46
+ "",
47
+ ]
48
+ if task["type"] == "math":
49
+ parts.append(
50
+ "REQUIRED FORMAT: the final numeric answer must be inside "
51
+ "<answer>...</answer> tags. Just the number, no units."
52
+ )
53
+ elif task["type"] == "code":
54
+ parts.append(
55
+ "REQUIRED FORMAT: a single ```python ...``` code block defining "
56
+ "the requested function. No prose, no examples."
57
+ )
58
+ elif task["type"] == "json":
59
+ parts.append(
60
+ "REQUIRED FORMAT: a single ```json ...``` code block with a valid "
61
+ "JSON object matching the schema."
62
+ )
63
+ if "schema" in task:
64
+ parts.append(f"SCHEMA: {json.dumps(task['schema'])}")
65
+
66
+ parts.append("")
67
+ parts.append("Output ONLY the system prompt itself. No preamble, no markdown fences.")
68
+ return "\n".join(parts)
69
+
70
+
71
+ # ---------------------------------------------------------------------------
72
+ # Reward wrapper for GRPO
73
+ # ---------------------------------------------------------------------------
74
+
75
+ def make_reward_fn(log_path: Path):
76
+ """
77
+ Returns a callable that GRPOTrainer can use:
78
+ reward_fn(prompts, completions, **kwargs) -> List[float]
79
+
80
+ `kwargs` may include the original dataset columns; we use `task` to
81
+ recover the task dict.
82
+ """
83
+ from src.envs.promptops_arena.server.environment import PromptOpsArenaEnvironment
84
+ from src.envs.promptops_arena import llm_under_test # noqa: F401
85
+
86
+ env = PromptOpsArenaEnvironment(split="train", seed=0)
87
+
88
+ log_path.parent.mkdir(parents=True, exist_ok=True)
89
+ log_f = log_path.open("a", encoding="utf-8")
90
+
91
+ def reward_fn(prompts, completions, **kwargs) -> List[float]:
92
+ tasks = kwargs.get("task")
93
+ if tasks is None:
94
+ raise RuntimeError("reward_fn requires 'task' column in dataset")
95
+ if isinstance(tasks, dict):
96
+ tasks = [tasks] * len(completions)
97
+
98
+ rewards: List[float] = []
99
+ for completion, task in zip(completions, tasks):
100
+ if isinstance(completion, list):
101
+ # chat-style completion: list of {role, content}
102
+ text = "".join(
103
+ m.get("content", "") for m in completion
104
+ if isinstance(m, dict) and m.get("role") == "assistant"
105
+ )
106
+ else:
107
+ text = str(completion)
108
+ res = env.execute_prompt(task, text.strip())
109
+ rewards.append(float(res["reward"]["total"]))
110
+ log_f.write(json.dumps({
111
+ "ts": time.time(),
112
+ "task_id": task.get("id"),
113
+ "task_type": task.get("type"),
114
+ "reward": res["reward"],
115
+ "completion_len": len(text),
116
+ }) + "\n")
117
+ log_f.flush()
118
+ return rewards
119
+
120
+ return reward_fn
121
+
122
+
123
+ # ---------------------------------------------------------------------------
124
+ # Dataset construction
125
+ # ---------------------------------------------------------------------------
126
+
127
+ def build_dataset():
128
+ from datasets import Dataset
129
+ from src.envs.promptops_arena.tasks import load_tasks
130
+
131
+ tasks = load_tasks(split="train")
132
+ rows = [
133
+ {"prompt": build_agent_input(t), "task": t}
134
+ for t in tasks
135
+ ]
136
+ return Dataset.from_list(rows)
137
+
138
+
139
+ # ---------------------------------------------------------------------------
140
+ # Main
141
+ # ---------------------------------------------------------------------------
142
+
143
+ def main():
144
+ p = argparse.ArgumentParser()
145
+ p.add_argument("--smoke", action="store_true",
146
+ help="Tiny CPU run with stub LLM to validate plumbing.")
147
+ p.add_argument("--dry", action="store_true",
148
+ help="Construct trainer but don't call .train(). Validates API.")
149
+ p.add_argument("--hf-jobs", action="store_true",
150
+ help="Print HF Jobs launch command and exit.")
151
+ p.add_argument("--model", default="Qwen/Qwen2.5-1.5B-Instruct")
152
+ p.add_argument("--out", default="outputs/grpo-lora")
153
+ p.add_argument("--log", default="results/training_log.jsonl")
154
+ p.add_argument("--steps", type=int, default=200)
155
+ p.add_argument("--batch", type=int, default=4)
156
+ p.add_argument("--num-generations", type=int, default=4,
157
+ help="GRPO group size G (completions per prompt).")
158
+ p.add_argument("--lr", type=float, default=5e-6)
159
+ p.add_argument("--max-prompt-length", type=int, default=512)
160
+ p.add_argument("--max-completion-length", type=int, default=300)
161
+ args = p.parse_args()
162
+
163
+ if args.hf_jobs:
164
+ print(_HF_JOBS_HELP)
165
+ return
166
+
167
+ if args.smoke:
168
+ os.environ["PROMPTOPS_LLM_BACKEND"] = "stub"
169
+
170
+ # Lazy imports so --hf-jobs and --smoke don't require torch/trl up front.
171
+ import torch # type: ignore
172
+ from transformers import AutoTokenizer # type: ignore
173
+
174
+ try:
175
+ from trl import GRPOConfig, GRPOTrainer # type: ignore
176
+ except ImportError as e:
177
+ raise SystemExit(
178
+ "trl is required for GRPO training. Install with: pip install trl>=0.21\n"
179
+ f"(import error: {e})"
180
+ )
181
+
182
+ use_unsloth = False
183
+ if not args.smoke:
184
+ try:
185
+ from unsloth import FastLanguageModel # type: ignore # noqa: F401
186
+ use_unsloth = torch.cuda.is_available()
187
+ except ImportError:
188
+ use_unsloth = False
189
+
190
+ print(f"[train_grpo] mode={'smoke' if args.smoke else 'full'} "
191
+ f"cuda={torch.cuda.is_available()} unsloth={use_unsloth}")
192
+
193
+ # ---- model ----
194
+ if use_unsloth:
195
+ from unsloth import FastLanguageModel # type: ignore
196
+ model, tokenizer = FastLanguageModel.from_pretrained(
197
+ model_name=args.model,
198
+ max_seq_length=args.max_prompt_length + args.max_completion_length,
199
+ load_in_4bit=True,
200
+ fast_inference=False,
201
+ )
202
+ model = FastLanguageModel.get_peft_model(
203
+ model,
204
+ r=16, lora_alpha=32, lora_dropout=0.0, bias="none",
205
+ target_modules=[
206
+ "q_proj", "k_proj", "v_proj", "o_proj",
207
+ "gate_proj", "up_proj", "down_proj",
208
+ ],
209
+ )
210
+ else:
211
+ from transformers import AutoModelForCausalLM # type: ignore
212
+ from peft import LoraConfig, get_peft_model # type: ignore
213
+
214
+ device_map = "cuda" if torch.cuda.is_available() else "cpu"
215
+ dtype = torch.bfloat16 if torch.cuda.is_available() else torch.float32
216
+
217
+ tokenizer = AutoTokenizer.from_pretrained(args.model)
218
+ model = AutoModelForCausalLM.from_pretrained(
219
+ args.model, torch_dtype=dtype, device_map=device_map,
220
+ )
221
+ lora_cfg = LoraConfig(
222
+ r=8 if args.smoke else 16,
223
+ lora_alpha=16 if args.smoke else 32,
224
+ lora_dropout=0.0,
225
+ bias="none",
226
+ target_modules=["q_proj", "v_proj"] if args.smoke else [
227
+ "q_proj", "k_proj", "v_proj", "o_proj",
228
+ "gate_proj", "up_proj", "down_proj",
229
+ ],
230
+ task_type="CAUSAL_LM",
231
+ )
232
+ model = get_peft_model(model, lora_cfg)
233
+
234
+ if tokenizer.pad_token is None:
235
+ tokenizer.pad_token = tokenizer.eos_token
236
+
237
+ # ---- data + reward ----
238
+ dataset = build_dataset()
239
+ if args.smoke:
240
+ dataset = dataset.select(range(min(4, len(dataset))))
241
+ print(f"[train_grpo] dataset rows={len(dataset)}")
242
+
243
+ reward_fn = make_reward_fn(Path(args.log))
244
+
245
+ # ---- GRPO config ----
246
+ on_gpu = torch.cuda.is_available() and not args.smoke
247
+ per_device_bs = 2 if args.smoke else args.batch
248
+ num_gens = 2 if args.smoke else args.num_generations
249
+
250
+ # trl 0.21 GRPOConfig: has max_prompt_length; no generation_batch_size.
251
+ # Build kwargs compatible across 0.21+ (modern fields ignored if unknown).
252
+ cfg_kwargs = dict(
253
+ output_dir=args.out,
254
+ per_device_train_batch_size=per_device_bs,
255
+ gradient_accumulation_steps=1,
256
+ num_generations=num_gens,
257
+ max_prompt_length=args.max_prompt_length,
258
+ max_completion_length=128 if args.smoke else args.max_completion_length,
259
+ learning_rate=args.lr,
260
+ max_steps=2 if args.smoke else args.steps,
261
+ logging_steps=1,
262
+ save_steps=10_000 if args.smoke else max(1, args.steps // 4),
263
+ bf16=on_gpu,
264
+ fp16=False,
265
+ use_cpu=not on_gpu,
266
+ report_to=[],
267
+ remove_unused_columns=False,
268
+ beta=0.04,
269
+ temperature=1.0,
270
+ )
271
+ # Build, dropping unknown fields if a newer/older trl rejects one.
272
+ import inspect as _inspect
273
+ _allowed = set(_inspect.signature(GRPOConfig.__init__).parameters.keys())
274
+ cfg_kwargs = {k: v for k, v in cfg_kwargs.items() if k in _allowed}
275
+ cfg = GRPOConfig(**cfg_kwargs)
276
+
277
+ # trl 0.21 uses `processing_class` for tokenizer-like; older releases used
278
+ # `tokenizer`. Try processing_class first, fall back.
279
+ _tr_params = set(_inspect.signature(GRPOTrainer.__init__).parameters.keys())
280
+ tr_kwargs = dict(
281
+ model=model,
282
+ reward_funcs=[reward_fn],
283
+ args=cfg,
284
+ train_dataset=dataset,
285
+ )
286
+ if "processing_class" in _tr_params:
287
+ tr_kwargs["processing_class"] = tokenizer
288
+ elif "tokenizer" in _tr_params:
289
+ tr_kwargs["tokenizer"] = tokenizer
290
+ trainer = GRPOTrainer(**tr_kwargs)
291
+
292
+ if args.dry:
293
+ print("[train_grpo] dry mode: trainer constructed OK; skipping .train()")
294
+ return
295
+
296
+ print("[train_grpo] starting training...")
297
+ trainer.train()
298
+ print(f"[train_grpo] saving adapter to {args.out}")
299
+ trainer.save_model(args.out)
300
+ print("[train_grpo] done.")
301
+
302
+
303
+ _HF_JOBS_HELP = """\
304
+ # Launch full GRPO training on HF Jobs (a10g-large, ≤2h cap):
305
+ hf jobs run --gpu a10g-large --timeout 7200 \\
306
+ --secrets HF_TOKEN \\
307
+ --env PROMPTOPS_LLM_BACKEND=transformers \\
308
+ --workdir /workspace \\
309
+ --upload . \\
310
+ python:3.11 \\
311
+ bash -c "pip install -r requirements.txt && pip install trl peft && \\
312
+ python scripts/train_grpo.py --steps 200 --batch 4 --num-generations 4 \\
313
+ && hf upload <user>/promptops-arena-agent outputs/grpo-lora ."
314
+ """
315
+
316
+
317
+ if __name__ == "__main__":
318
+ main()
scripts/upload_src_to_hf.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Upload the project source to a HF dataset, mirrored at /code in the Job."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from huggingface_hub import HfApi, create_repo
10
+
11
+
12
+ def main():
13
+ p = argparse.ArgumentParser()
14
+ p.add_argument("--repo", default="Dar3devil/promptops-arena-src")
15
+ p.add_argument("--root", default=".")
16
+ args = p.parse_args()
17
+
18
+ root = Path(args.root).resolve()
19
+ api = HfApi()
20
+ create_repo(args.repo, repo_type="dataset", exist_ok=True, private=True)
21
+
22
+ ignore_patterns = [
23
+ "**/__pycache__/**",
24
+ "**/.pytest_cache/**",
25
+ "**/.benchmarks/**",
26
+ ".git/**",
27
+ ".git",
28
+ "outputs/**",
29
+ "**/*.pyc",
30
+ "**/*.pyo",
31
+ ".venv/**",
32
+ "venv/**",
33
+ ".env",
34
+ ".env.local",
35
+ ".vscode/**",
36
+ ".idea/**",
37
+ "wandb/**",
38
+ "*.log",
39
+ ".DS_Store",
40
+ ]
41
+
42
+ print(f"[upload] uploading {root} -> dataset {args.repo}")
43
+ api.upload_folder(
44
+ folder_path=str(root),
45
+ repo_id=args.repo,
46
+ repo_type="dataset",
47
+ ignore_patterns=ignore_patterns,
48
+ commit_message="sync source for HF Jobs training run",
49
+ )
50
+ print(f"[upload] done. https://huggingface.co/datasets/{args.repo}")
51
+
52
+
53
+ if __name__ == "__main__":
54
+ sys.exit(main() or 0)
src/__init__.py ADDED
File without changes
src/envs/__init__.py ADDED
File without changes
src/envs/promptops_arena/__init__.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .models import (
2
+ PromptOpsAction,
3
+ PromptOpsObservation,
4
+ PromptOpsState,
5
+ )
6
+ from .client import PromptOpsArenaEnv
7
+
8
+ __all__ = [
9
+ "PromptOpsAction",
10
+ "PromptOpsObservation",
11
+ "PromptOpsState",
12
+ "PromptOpsArenaEnv",
13
+ ]
src/envs/promptops_arena/client.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ HTTP/WebSocket client for the PromptOps Arena env.
3
+
4
+ Used by the demo Space, manual exploration, and any out-of-process consumer.
5
+ GRPO training uses the in-process `PromptOpsArenaEnvironment` directly.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, Dict
11
+
12
+ from openenv.core.client_types import StepResult
13
+ from openenv.core.env_client import EnvClient
14
+
15
+ from .models import PromptOpsAction, PromptOpsObservation, PromptOpsState
16
+
17
+
18
+ class PromptOpsArenaEnv(EnvClient[PromptOpsAction, PromptOpsObservation, PromptOpsState]):
19
+ """Client; subclass of openenv.core.env_client.EnvClient."""
20
+
21
+ def _step_payload(self, action: PromptOpsAction) -> Dict[str, Any]:
22
+ return {"new_system_prompt": action.new_system_prompt}
23
+
24
+ def _parse_result(self, payload: Dict[str, Any]) -> StepResult[PromptOpsObservation]:
25
+ obs_data = payload.get("observation", payload)
26
+ observation = PromptOpsObservation(
27
+ task_text=obs_data.get("task_text", ""),
28
+ task_type=obs_data.get("task_type", ""),
29
+ current_prompt=obs_data.get("current_prompt", ""),
30
+ last_completion=obs_data.get("last_completion", ""),
31
+ last_reward=obs_data.get("last_reward", 0.0),
32
+ last_correctness=obs_data.get("last_correctness", 0.0),
33
+ edit_turn=obs_data.get("edit_turn", 0),
34
+ max_turns=obs_data.get("max_turns", 3),
35
+ done=obs_data.get("done", False),
36
+ reward=obs_data.get("reward", 0.0),
37
+ reward_components=obs_data.get("reward_components", {}),
38
+ metadata=obs_data.get("metadata", {}),
39
+ )
40
+ return StepResult(
41
+ observation=observation,
42
+ reward=observation.reward or 0.0,
43
+ done=observation.done,
44
+ )
45
+
46
+ def _parse_state(self, payload: Dict[str, Any]) -> PromptOpsState:
47
+ return PromptOpsState(
48
+ episode_id=payload.get("episode_id", ""),
49
+ step_count=payload.get("step_count", 0),
50
+ task_id=payload.get("task_id", ""),
51
+ task_type=payload.get("task_type", ""),
52
+ task_text=payload.get("task_text", ""),
53
+ history=payload.get("history", []),
54
+ best_reward=payload.get("best_reward", 0.0),
55
+ solved=payload.get("solved", False),
56
+ )
src/envs/promptops_arena/llm_under_test.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Frozen LLM-under-test. The agent's prompts are evaluated by running this model.
3
+
4
+ Two backends:
5
+ - "transformers": real Qwen2.5-0.5B-Instruct via HF transformers
6
+ - "stub": deterministic stub for fast local CI / smoke tests
7
+
8
+ Selected via env var PROMPTOPS_LLM_BACKEND (default: "stub" if torch unavailable).
9
+ The stub recognizes a few hand-written "good" prompt patterns to give the
10
+ env smoke test something non-zero to chew on.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import os
16
+ import re
17
+ import threading
18
+ from typing import Optional
19
+
20
+
21
+ _DEFAULT_MODEL = os.environ.get("PROMPTOPS_LLM_MODEL", "Qwen/Qwen2.5-0.5B-Instruct")
22
+ _BACKEND = os.environ.get("PROMPTOPS_LLM_BACKEND", "auto").lower()
23
+ _MAX_NEW_TOKENS = int(os.environ.get("PROMPTOPS_LLM_MAX_NEW_TOKENS", "256"))
24
+
25
+
26
+ class _StubBackend:
27
+ """
28
+ Deterministic stub. Reads the system prompt + task, and produces a
29
+ plausible-looking completion that the verifiers can sometimes pass
30
+ when the prompt asks for the right format.
31
+
32
+ Heuristic logic:
33
+ - If prompt mentions <answer> tags, wrap a guessed answer in them
34
+ - For math: try to compute the answer naively (look for numbers in question)
35
+ - For code: emit a trivial function that returns 0 (will fail tests)
36
+ - For JSON: emit an empty object
37
+
38
+ This means: with a good prompt, math gets ~30% by luck; code/json need
39
+ a real LLM. That's fine — stub is only for plumbing tests.
40
+ """
41
+
42
+ name = "stub"
43
+
44
+ def generate(self, system_prompt: str, user_task: str) -> str:
45
+ sp = (system_prompt or "").lower()
46
+ ut = (user_task or "")
47
+
48
+ # Order matters: JSON first, then code, then math (most specific to least)
49
+ wants_json = "json" in sp
50
+ wants_code = ("python" in sp or "function" in sp) and "json" not in sp
51
+ wants_answer_tag = "<answer>" in (system_prompt or "")
52
+ wants_boxed = "boxed" in sp
53
+
54
+ if wants_json:
55
+ return "```json\n{}\n```"
56
+
57
+ if wants_code:
58
+ body = "def solve(*a, **k):\n return 0\n"
59
+ return f"```python\n{body}```"
60
+
61
+ nums = re.findall(r"-?\d+(?:\.\d+)?", ut)
62
+ guess = nums[-1] if nums else "0"
63
+
64
+ if wants_answer_tag:
65
+ return f"Working...\n<answer>{guess}</answer>"
66
+ if wants_boxed:
67
+ return f"Working...\n\\boxed{{{guess}}}"
68
+ return f"The answer is {guess}."
69
+
70
+
71
+ class _TransformersBackend:
72
+ name = "transformers"
73
+
74
+ def __init__(self, model_id: str = _DEFAULT_MODEL):
75
+ import torch # type: ignore
76
+ from transformers import AutoModelForCausalLM, AutoTokenizer # type: ignore
77
+
78
+ self._torch = torch
79
+ device = "cuda" if torch.cuda.is_available() else "cpu"
80
+ dtype = torch.bfloat16 if device == "cuda" else torch.float32
81
+
82
+ self.tokenizer = AutoTokenizer.from_pretrained(model_id)
83
+ self.model = AutoModelForCausalLM.from_pretrained(
84
+ model_id, torch_dtype=dtype, device_map=device,
85
+ )
86
+ self.model.eval()
87
+ self.device = device
88
+
89
+ def generate(self, system_prompt: str, user_task: str) -> str:
90
+ msgs = [
91
+ {"role": "system", "content": system_prompt or "You are a helpful assistant."},
92
+ {"role": "user", "content": user_task},
93
+ ]
94
+ encoded = self.tokenizer.apply_chat_template(
95
+ msgs, add_generation_prompt=True, return_tensors="pt",
96
+ )
97
+ # apply_chat_template may return a Tensor (older transformers) or a
98
+ # BatchEncoding/dict (newer); normalize to input_ids tensor.
99
+ if hasattr(encoded, "input_ids"):
100
+ input_ids = encoded.input_ids
101
+ elif isinstance(encoded, dict):
102
+ input_ids = encoded["input_ids"]
103
+ else:
104
+ input_ids = encoded
105
+ input_ids = input_ids.to(self.device)
106
+ with self._torch.no_grad():
107
+ out = self.model.generate(
108
+ input_ids=input_ids,
109
+ max_new_tokens=_MAX_NEW_TOKENS,
110
+ do_sample=False,
111
+ pad_token_id=self.tokenizer.eos_token_id,
112
+ )
113
+ text = self.tokenizer.decode(out[0][input_ids.shape[1]:], skip_special_tokens=True)
114
+ return text
115
+
116
+
117
+ _lock = threading.Lock()
118
+ _backend_singleton: Optional[object] = None
119
+
120
+
121
+ def _select_backend() -> object:
122
+ global _backend_singleton
123
+ with _lock:
124
+ if _backend_singleton is not None:
125
+ return _backend_singleton
126
+ choice = _BACKEND
127
+ if choice == "auto":
128
+ try:
129
+ import torch # noqa: F401
130
+ import transformers # noqa: F401
131
+ choice = "transformers"
132
+ except ImportError:
133
+ choice = "stub"
134
+ if choice == "transformers":
135
+ try:
136
+ _backend_singleton = _TransformersBackend()
137
+ except Exception as e:
138
+ print(f"[llm_under_test] transformers backend failed ({e}); falling back to stub")
139
+ _backend_singleton = _StubBackend()
140
+ else:
141
+ _backend_singleton = _StubBackend()
142
+ return _backend_singleton
143
+
144
+
145
+ def generate(system_prompt: str, user_task: str) -> str:
146
+ """Run the frozen LLM-under-test. Threadsafe singleton."""
147
+ backend = _select_backend()
148
+ return backend.generate(system_prompt, user_task)
149
+
150
+
151
+ def backend_name() -> str:
152
+ return _select_backend().name # type: ignore[attr-defined]
src/envs/promptops_arena/models.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Type-safe data contracts for the PromptOps Arena environment.
3
+
4
+ Action: agent emits a full new system prompt to give to the LLM-under-test.
5
+ Observation: task text, last completion, last reward, edit-turn counter.
6
+ State: full episode history for logging / demo replay.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any, Dict, List, Optional
12
+
13
+ from openenv.core.env_server import Action, Observation, State
14
+ from pydantic import Field
15
+
16
+
17
+ class PromptOpsAction(Action):
18
+ """Agent's action: write/replace the system prompt for the LLM-under-test."""
19
+
20
+ new_system_prompt: str = Field(
21
+ ...,
22
+ description="Full system prompt the agent wants to give the frozen LLM-under-test",
23
+ )
24
+
25
+
26
+ class PromptOpsObservation(Observation):
27
+ """What the agent sees after each step."""
28
+
29
+ task_text: str = ""
30
+ task_type: str = "" # "math" | "code" | "json"
31
+ current_prompt: str = ""
32
+ last_completion: str = ""
33
+ last_reward: float = 0.0
34
+ last_correctness: float = 0.0
35
+ edit_turn: int = 0
36
+ max_turns: int = 3
37
+ reward_components: Dict[str, float] = Field(default_factory=dict)
38
+
39
+
40
+ class PromptOpsState(State):
41
+ """Episode state for logging and replay."""
42
+
43
+ task_id: str = ""
44
+ task_type: str = ""
45
+ task_text: str = ""
46
+ history: List[Dict[str, Any]] = Field(default_factory=list)
47
+ best_reward: float = 0.0
48
+ solved: bool = False
src/envs/promptops_arena/server/Dockerfile ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY server/requirements.txt /app/server/requirements.txt
6
+ RUN pip install --no-cache-dir -r /app/server/requirements.txt
7
+
8
+ # Copy the env package
9
+ COPY . /app/promptops_arena
10
+ ENV PYTHONPATH=/app
11
+
12
+ EXPOSE 8000
13
+ ENV PROMPTOPS_LLM_BACKEND=transformers
14
+
15
+ CMD ["uvicorn", "promptops_arena.server.app:app", "--host", "0.0.0.0", "--port", "8000"]
src/envs/promptops_arena/server/__init__.py ADDED
File without changes
src/envs/promptops_arena/server/app.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ FastAPI server for PromptOps Arena env.
3
+
4
+ Run:
5
+ uvicorn src.envs.promptops_arena.server.app:app --host 0.0.0.0 --port 8000
6
+
7
+ Or via:
8
+ python -m src.envs.promptops_arena.server.app
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import os
14
+
15
+ from openenv.core.env_server import create_app
16
+
17
+ # Try in-repo first (when running scripts from project root); fall back
18
+ # to fully-qualified package import (when installed).
19
+ try:
20
+ from src.envs.promptops_arena.server.environment import PromptOpsArenaEnvironment
21
+ from src.envs.promptops_arena.models import PromptOpsAction, PromptOpsObservation
22
+ except ImportError: # pragma: no cover
23
+ from envs.promptops_arena.server.environment import PromptOpsArenaEnvironment
24
+ from envs.promptops_arena.models import PromptOpsAction, PromptOpsObservation
25
+
26
+
27
+ max_concurrent = int(os.getenv("MAX_CONCURRENT_ENVS", "4"))
28
+
29
+ app = create_app(
30
+ PromptOpsArenaEnvironment,
31
+ PromptOpsAction,
32
+ PromptOpsObservation,
33
+ env_name="promptops_arena",
34
+ max_concurrent_envs=max_concurrent,
35
+ )
36
+
37
+
38
+ def main() -> None:
39
+ import uvicorn
40
+ uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", "8000")))
41
+
42
+
43
+ if __name__ == "__main__":
44
+ main()
src/envs/promptops_arena/server/environment.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ PromptOps Arena environment.
3
+
4
+ reset() : sample a task; return initial observation with empty prompt
5
+ step(action) : run LLM-under-test with action.new_system_prompt + task,
6
+ verify, compute reward, return observation
7
+ state : full episode state with history (used for logging/replay)
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import random
13
+ import uuid
14
+ from typing import Any, Optional
15
+
16
+ from openenv.core.env_server import Environment
17
+
18
+ from ..models import PromptOpsAction, PromptOpsObservation, PromptOpsState
19
+ from ..tasks import load_tasks
20
+ from ..verifiers import verify
21
+ from .. import llm_under_test
22
+ from .rewards import compute_reward
23
+
24
+
25
+ class PromptOpsArenaEnvironment(Environment):
26
+ """
27
+ The agent's action is a full system prompt. We run the frozen LLM-under-test
28
+ with [system=action, user=task_text], verify, and reward.
29
+
30
+ Episode terminates when correctness == 1.0 OR edit_turn >= max_turns.
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ max_turns: int = 3,
36
+ split: str = "train",
37
+ seed: Optional[int] = None,
38
+ task_types: Optional[list[str]] = None,
39
+ ):
40
+ super().__init__()
41
+ self._max_turns = max_turns
42
+ self._split = split
43
+ self._task_types = task_types
44
+ self._rng = random.Random(seed)
45
+ self._tasks = load_tasks(split=split, types=task_types)
46
+ if not self._tasks:
47
+ raise RuntimeError(
48
+ f"No tasks loaded for split={split!r} types={task_types!r}"
49
+ )
50
+ self._state: PromptOpsState = PromptOpsState(episode_id=str(uuid.uuid4()))
51
+ self._task: dict = {}
52
+ self._edit_turn: int = 0
53
+
54
+ # ---- OpenEnv API ----
55
+
56
+ def reset(
57
+ self,
58
+ seed: Optional[int] = None,
59
+ episode_id: Optional[str] = None,
60
+ task_id: Optional[str] = None,
61
+ **kwargs: Any,
62
+ ) -> PromptOpsObservation:
63
+ if seed is not None:
64
+ self._rng = random.Random(seed)
65
+
66
+ if task_id is not None:
67
+ matches = [t for t in self._tasks if t.get("id") == task_id]
68
+ self._task = matches[0] if matches else self._rng.choice(self._tasks)
69
+ else:
70
+ self._task = self._rng.choice(self._tasks)
71
+
72
+ self._edit_turn = 0
73
+ self._state = PromptOpsState(
74
+ episode_id=episode_id or str(uuid.uuid4()),
75
+ step_count=0,
76
+ task_id=self._task.get("id", ""),
77
+ task_type=self._task.get("type", ""),
78
+ task_text=self._task.get("question", ""),
79
+ history=[],
80
+ best_reward=0.0,
81
+ solved=False,
82
+ )
83
+
84
+ return PromptOpsObservation(
85
+ task_text=self._state.task_text,
86
+ task_type=self._state.task_type,
87
+ current_prompt="",
88
+ last_completion="",
89
+ last_reward=0.0,
90
+ last_correctness=0.0,
91
+ edit_turn=0,
92
+ max_turns=self._max_turns,
93
+ done=False,
94
+ reward=0.0,
95
+ metadata={
96
+ "task_id": self._state.task_id,
97
+ "episode_id": self._state.episode_id,
98
+ },
99
+ reward_components={},
100
+ )
101
+
102
+ def step(
103
+ self,
104
+ action: PromptOpsAction,
105
+ timeout_s: Optional[float] = None,
106
+ **kwargs: Any,
107
+ ) -> PromptOpsObservation:
108
+ if not self._task:
109
+ raise RuntimeError("Environment not initialized. Call reset() first.")
110
+
111
+ prompt = action.new_system_prompt or ""
112
+ completion = llm_under_test.generate(prompt, self._state.task_text)
113
+ verifier_result = verify(self._task, completion)
114
+ reward_dict = compute_reward(self._task, prompt, completion, verifier_result)
115
+ total = reward_dict["total"]
116
+
117
+ self._edit_turn += 1
118
+ self._state.step_count += 1
119
+ if total > self._state.best_reward:
120
+ self._state.best_reward = total
121
+ if reward_dict["correctness"] >= 1.0:
122
+ self._state.solved = True
123
+
124
+ self._state.history.append(
125
+ {
126
+ "edit_turn": self._edit_turn,
127
+ "system_prompt": prompt,
128
+ "completion": completion,
129
+ "reward": reward_dict,
130
+ "verifier": verifier_result,
131
+ }
132
+ )
133
+
134
+ done = self._state.solved or self._edit_turn >= self._max_turns
135
+
136
+ return PromptOpsObservation(
137
+ task_text=self._state.task_text,
138
+ task_type=self._state.task_type,
139
+ current_prompt=prompt,
140
+ last_completion=completion,
141
+ last_reward=total,
142
+ last_correctness=reward_dict["correctness"],
143
+ edit_turn=self._edit_turn,
144
+ max_turns=self._max_turns,
145
+ done=done,
146
+ reward=total,
147
+ reward_components=reward_dict,
148
+ metadata={
149
+ "task_id": self._state.task_id,
150
+ "episode_id": self._state.episode_id,
151
+ "verifier_details": verifier_result.get("details", ""),
152
+ "solved": self._state.solved,
153
+ },
154
+ )
155
+
156
+ @property
157
+ def state(self) -> PromptOpsState:
158
+ return self._state
159
+
160
+ # ---- in-process helper used by training (skip HTTP) ----
161
+
162
+ def execute_prompt(
163
+ self,
164
+ task: dict,
165
+ system_prompt: str,
166
+ ) -> dict:
167
+ """
168
+ Single-shot evaluation: given a task and a candidate system prompt,
169
+ run the LLM-under-test and return reward components + completion.
170
+
171
+ Used by the GRPO reward function during training to avoid HTTP latency.
172
+ """
173
+ completion = llm_under_test.generate(system_prompt, task.get("question", ""))
174
+ verifier_result = verify(task, completion)
175
+ reward_dict = compute_reward(task, system_prompt, completion, verifier_result)
176
+ return {
177
+ "reward": reward_dict,
178
+ "completion": completion,
179
+ "verifier": verifier_result,
180
+ }
src/envs/promptops_arena/server/requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ openenv-core>=0.2.3
2
+ fastapi>=0.110.0
3
+ uvicorn>=0.27.0
4
+ pydantic>=2.0.0
5
+ jsonschema>=4.20.0
6
+ transformers>=4.45.0
7
+ torch>=2.4.0
src/envs/promptops_arena/server/rewards.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Reward function: decomposed, bounded, gated by correctness.
3
+
4
+ total = correctness + 0.1 * format_bonus + brevity_penalty
5
+
6
+ Where:
7
+ correctness in {0.0, 1.0} -- programmatic verifier
8
+ format_bonus in {0.0, 1.0} -- multiplied by 0.1 in total
9
+ brevity_penalty in [-0.1, 0.0] -- only if prompt > 800 chars
10
+ = -0.05 * max(0, (len-800))/200
11
+ clipped to -0.1
12
+
13
+ If correctness == 0, the format_bonus is still added (small) but the agent
14
+ cannot exceed 0.1 reward without correctness.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from typing import Any, Dict
20
+
21
+
22
+ def compute_reward(
23
+ task: Dict[str, Any],
24
+ system_prompt: str,
25
+ completion: str,
26
+ verifier_result: Dict[str, Any],
27
+ ) -> Dict[str, float]:
28
+ correctness = float(verifier_result.get("correctness", 0.0))
29
+ format_ok = bool(verifier_result.get("format_ok", False))
30
+ format_bonus = 1.0 if format_ok else 0.0
31
+
32
+ p_len = len(system_prompt or "")
33
+ excess = max(0, p_len - 800)
34
+ brevity_penalty = -0.05 * (excess / 200.0)
35
+ if brevity_penalty < -0.1:
36
+ brevity_penalty = -0.1
37
+
38
+ total = correctness + 0.1 * format_bonus + brevity_penalty
39
+
40
+ return {
41
+ "correctness": correctness,
42
+ "format": format_bonus,
43
+ "brevity": brevity_penalty,
44
+ "total": total,
45
+ }
src/envs/promptops_arena/tasks/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .loader import load_tasks, sample_task
2
+
3
+ __all__ = ["load_tasks", "sample_task"]
src/envs/promptops_arena/tasks/code.jsonl ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"id": "code_001", "type": "code", "split": "train", "question": "Write a Python function `add(a, b)` that returns the sum of two numbers.", "tests": ["assert add(2, 3) == 5", "assert add(-1, 1) == 0", "assert add(0, 0) == 0"]}
2
+ {"id": "code_002", "type": "code", "split": "train", "question": "Write a Python function `is_even(n)` that returns True if n is even, False otherwise.", "tests": ["assert is_even(4) is True", "assert is_even(7) is False", "assert is_even(0) is True"]}
3
+ {"id": "code_003", "type": "code", "split": "train", "question": "Write a Python function `reverse_string(s)` that returns the reverse of string s.", "tests": ["assert reverse_string('hello') == 'olleh'", "assert reverse_string('') == ''", "assert reverse_string('a') == 'a'"]}
4
+ {"id": "code_004", "type": "code", "split": "train", "question": "Write a Python function `factorial(n)` that returns n! for n >= 0.", "tests": ["assert factorial(0) == 1", "assert factorial(5) == 120", "assert factorial(1) == 1"]}
5
+ {"id": "code_005", "type": "code", "split": "train", "question": "Write a Python function `count_vowels(s)` that counts vowels (aeiou, case insensitive) in string s.", "tests": ["assert count_vowels('hello') == 2", "assert count_vowels('AEIOU') == 5", "assert count_vowels('xyz') == 0"]}
6
+ {"id": "code_006", "type": "code", "split": "train", "question": "Write a Python function `max_in_list(lst)` that returns the largest number in lst.", "tests": ["assert max_in_list([1, 2, 3]) == 3", "assert max_in_list([-5, -1, -10]) == -1", "assert max_in_list([7]) == 7"]}
7
+ {"id": "code_007", "type": "code", "split": "train", "question": "Write a Python function `is_palindrome(s)` that returns True if s reads the same forwards and backwards.", "tests": ["assert is_palindrome('racecar') is True", "assert is_palindrome('hello') is False", "assert is_palindrome('') is True"]}
8
+ {"id": "code_008", "type": "code", "split": "train", "question": "Write a Python function `fibonacci(n)` that returns the nth Fibonacci number (fib(0)=0, fib(1)=1).", "tests": ["assert fibonacci(0) == 0", "assert fibonacci(1) == 1", "assert fibonacci(7) == 13"]}
9
+ {"id": "code_009", "type": "code", "split": "train", "question": "Write a Python function `unique(lst)` that returns a list of unique elements preserving order.", "tests": ["assert unique([1, 2, 2, 3, 1]) == [1, 2, 3]", "assert unique([]) == []", "assert unique(['a', 'a', 'b']) == ['a', 'b']"]}
10
+ {"id": "code_010", "type": "code", "split": "train", "question": "Write a Python function `sum_digits(n)` that sums the digits of non-negative integer n.", "tests": ["assert sum_digits(123) == 6", "assert sum_digits(0) == 0", "assert sum_digits(9999) == 36"]}
11
+ {"id": "code_011", "type": "code", "split": "train", "question": "Write a Python function `gcd(a, b)` that returns the greatest common divisor.", "tests": ["assert gcd(12, 18) == 6", "assert gcd(7, 13) == 1", "assert gcd(100, 25) == 25"]}
12
+ {"id": "code_012", "type": "code", "split": "train", "question": "Write a Python function `flatten(lst)` that flattens a list of lists by one level.", "tests": ["assert flatten([[1,2],[3,4]]) == [1,2,3,4]", "assert flatten([]) == []", "assert flatten([[1],[2,3],[]]) == [1,2,3]"]}
13
+ {"id": "code_013", "type": "code", "split": "train", "question": "Write a Python function `is_prime(n)` that returns True if n is a prime > 1.", "tests": ["assert is_prime(2) is True", "assert is_prime(15) is False", "assert is_prime(17) is True", "assert is_prime(1) is False"]}
14
+ {"id": "code_014", "type": "code", "split": "train", "question": "Write a Python function `word_count(s)` that returns the number of whitespace-separated words.", "tests": ["assert word_count('hello world') == 2", "assert word_count('') == 0", "assert word_count(' one two three ') == 3"]}
15
+ {"id": "code_015", "type": "code", "split": "train", "question": "Write a Python function `second_largest(lst)` that returns the second largest distinct number.", "tests": ["assert second_largest([1, 2, 3, 4]) == 3", "assert second_largest([5, 5, 4]) == 4", "assert second_largest([10, 20]) == 10"]}
16
+ {"id": "code_016", "type": "code", "split": "train", "question": "Write a Python function `caesar(s, k)` that shifts each letter by k positions, preserving case; non-letters unchanged.", "tests": ["assert caesar('abc', 1) == 'bcd'", "assert caesar('XYZ', 3) == 'ABC'", "assert caesar('Hello, World!', 13) == 'Uryyb, Jbeyq!'"]}
17
+ {"id": "code_017", "type": "code", "split": "train", "question": "Write a Python function `merge_sorted(a, b)` that merges two sorted lists into one sorted list.", "tests": ["assert merge_sorted([1,3,5],[2,4,6]) == [1,2,3,4,5,6]", "assert merge_sorted([],[1,2]) == [1,2]", "assert merge_sorted([1,1],[1,1]) == [1,1,1,1]"]}
18
+ {"id": "code_018", "type": "code", "split": "train", "question": "Write a Python function `most_common(lst)` that returns the most frequent element (any if tied).", "tests": ["assert most_common([1,2,2,3]) == 2", "assert most_common(['a','b','a']) == 'a'", "assert most_common([5]) == 5"]}
19
+ {"id": "code_019", "type": "code", "split": "train", "question": "Write a Python function `chunks(lst, n)` that splits lst into chunks of size n.", "tests": ["assert chunks([1,2,3,4,5], 2) == [[1,2],[3,4],[5]]", "assert chunks([], 3) == []", "assert chunks([1,2,3], 1) == [[1],[2],[3]]"]}
20
+ {"id": "code_020", "type": "code", "split": "train", "question": "Write a Python function `roman(n)` that converts integer 1-3999 to Roman numerals.", "tests": ["assert roman(1) == 'I'", "assert roman(4) == 'IV'", "assert roman(1994) == 'MCMXCIV'"]}
21
+ {"id": "code_t01", "type": "code", "split": "test", "question": "Write a Python function `square(x)` that returns x squared.", "tests": ["assert square(3) == 9", "assert square(-4) == 16", "assert square(0) == 0"]}
22
+ {"id": "code_t02", "type": "code", "split": "test", "question": "Write a Python function `is_anagram(a, b)` that checks if two strings are anagrams (case-insensitive, ignoring spaces).", "tests": ["assert is_anagram('listen', 'silent') is True", "assert is_anagram('Hello', 'World') is False", "assert is_anagram('a gentleman', 'elegant man') is True"]}
23
+ {"id": "code_t03", "type": "code", "split": "test", "question": "Write a Python function `count_words(s)` that returns a dict mapping each word to its count (whitespace-separated, lowercase).", "tests": ["assert count_words('a a b') == {'a': 2, 'b': 1}", "assert count_words('') == {}", "assert count_words('Hi hi HI') == {'hi': 3}"]}
24
+ {"id": "code_t04", "type": "code", "split": "test", "question": "Write a Python function `power(b, e)` that computes b**e for non-negative integer e using a loop (no ** operator).", "tests": ["assert power(2, 10) == 1024", "assert power(5, 0) == 1", "assert power(3, 3) == 27"]}
25
+ {"id": "code_t05", "type": "code", "split": "test", "question": "Write a Python function `compress(s)` that returns 'a3b2c1' style run-length encoding.", "tests": ["assert compress('aaabbc') == 'a3b2c1'", "assert compress('abc') == 'a1b1c1'", "assert compress('') == ''"]}
26
+ {"id": "code_t06", "type": "code", "split": "test", "question": "Write a Python function `balanced(s)` that returns True if all (), [], {} pairs are balanced and properly nested.", "tests": ["assert balanced('()[]{}') is True", "assert balanced('([)]') is False", "assert balanced('') is True"]}
27
+ {"id": "code_t07", "type": "code", "split": "test", "question": "Write a Python function `lcm(a, b)` that returns the least common multiple of two positive integers.", "tests": ["assert lcm(4, 6) == 12", "assert lcm(7, 13) == 91", "assert lcm(1, 5) == 5"]}
28
+ {"id": "code_t08", "type": "code", "split": "test", "question": "Write a Python function `rotate(lst, k)` that rotates list right by k positions.", "tests": ["assert rotate([1,2,3,4], 1) == [4,1,2,3]", "assert rotate([1,2,3], 0) == [1,2,3]", "assert rotate([1,2,3], 4) == [3,1,2]"]}
29
+ {"id": "code_t09", "type": "code", "split": "test", "question": "Write a Python function `digit_sum_seq(n)` that repeatedly sums digits until single digit (digital root).", "tests": ["assert digit_sum_seq(38) == 2", "assert digit_sum_seq(0) == 0", "assert digit_sum_seq(123456) == 3"]}
30
+ {"id": "code_t10", "type": "code", "split": "test", "question": "Write a Python function `pairs_summing_to(lst, target)` that returns list of unique sorted tuples (a,b) with a+b==target, a<=b.", "tests": ["assert pairs_summing_to([1,2,3,4], 5) == [(1,4),(2,3)]", "assert pairs_summing_to([2,2,3], 4) == [(2,2)]", "assert pairs_summing_to([1,2], 10) == []"]}
src/envs/promptops_arena/tasks/json_extract.jsonl ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"id": "json_001", "type": "json", "split": "train", "question": "Extract the person's name and age from this text as JSON with keys 'name' (string) and 'age' (integer): 'Alice Johnson, 32 years old, lives in Boston.'", "schema": {"type": "object", "properties": {"name": {"type": "string"}, "age": {"type": "integer"}}, "required": ["name", "age"]}, "expected": {"name": "Alice Johnson", "age": 32}}
2
+ {"id": "json_002", "type": "json", "split": "train", "question": "Parse this order: 'Order #4521 for 3 widgets at $9.99 each.' Output JSON with order_id (int), quantity (int), price_each (number).", "schema": {"type": "object", "properties": {"order_id": {"type": "integer"}, "quantity": {"type": "integer"}, "price_each": {"type": "number"}}, "required": ["order_id", "quantity", "price_each"]}, "expected": {"order_id": 4521, "quantity": 3, "price_each": 9.99}}
3
+ {"id": "json_003", "type": "json", "split": "train", "question": "Extract event info from 'Concert by The Beatles on 2025-06-14 at Wembley Stadium'. JSON keys: artist (str), date (str YYYY-MM-DD), venue (str).", "schema": {"type": "object", "properties": {"artist": {"type": "string"}, "date": {"type": "string"}, "venue": {"type": "string"}}, "required": ["artist", "date", "venue"]}, "expected": {"artist": "The Beatles", "date": "2025-06-14", "venue": "Wembley Stadium"}}
4
+ {"id": "json_004", "type": "json", "split": "train", "question": "Extract from 'Book: The Pragmatic Programmer by David Thomas, 320 pages, ISBN 978-0135957059'. JSON: title (str), author (str), pages (int).", "schema": {"type": "object", "properties": {"title": {"type": "string"}, "author": {"type": "string"}, "pages": {"type": "integer"}}, "required": ["title", "author", "pages"]}, "expected": {"title": "The Pragmatic Programmer", "author": "David Thomas", "pages": 320}}
5
+ {"id": "json_005", "type": "json", "split": "train", "question": "Parse 'Flight AA101 from JFK to LAX departs 14:30'. JSON keys: flight (str), from (str), to (str), departure (str).", "schema": {"type": "object", "properties": {"flight": {"type": "string"}, "from": {"type": "string"}, "to": {"type": "string"}, "departure": {"type": "string"}}, "required": ["flight", "from", "to", "departure"]}, "expected": {"flight": "AA101", "from": "JFK", "to": "LAX", "departure": "14:30"}}
6
+ {"id": "json_006", "type": "json", "split": "train", "question": "From 'Product SKU-998 priced at 49.50 USD, in stock: 12 units', output JSON: sku (str), price (number), currency (str), stock (int).", "schema": {"type": "object", "properties": {"sku": {"type": "string"}, "price": {"type": "number"}, "currency": {"type": "string"}, "stock": {"type": "integer"}}, "required": ["sku", "price", "currency", "stock"]}, "expected": {"sku": "SKU-998", "price": 49.5, "currency": "USD", "stock": 12}}
7
+ {"id": "json_007", "type": "json", "split": "train", "question": "Parse contact: 'Email: bob@example.com, Phone: +1-555-0100'. JSON: email (str), phone (str).", "schema": {"type": "object", "properties": {"email": {"type": "string"}, "phone": {"type": "string"}}, "required": ["email", "phone"]}, "expected": {"email": "bob@example.com", "phone": "+1-555-0100"}}
8
+ {"id": "json_008", "type": "json", "split": "train", "question": "From 'Movie: Inception (2010), directed by Christopher Nolan, runtime 148 min', output JSON: title (str), year (int), director (str), runtime (int).", "schema": {"type": "object", "properties": {"title": {"type": "string"}, "year": {"type": "integer"}, "director": {"type": "string"}, "runtime": {"type": "integer"}}, "required": ["title", "year", "director", "runtime"]}, "expected": {"title": "Inception", "year": 2010, "director": "Christopher Nolan", "runtime": 148}}
9
+ {"id": "json_009", "type": "json", "split": "train", "question": "Parse 'Temperature in Tokyo: 24.5°C, humidity 60%'. JSON: city (str), temp_c (number), humidity (int).", "schema": {"type": "object", "properties": {"city": {"type": "string"}, "temp_c": {"type": "number"}, "humidity": {"type": "integer"}}, "required": ["city", "temp_c", "humidity"]}, "expected": {"city": "Tokyo", "temp_c": 24.5, "humidity": 60}}
10
+ {"id": "json_010", "type": "json", "split": "train", "question": "Extract from 'Recipe: pancakes, serves 4, prep 10 min, cook 15 min'. JSON: name (str), serves (int), prep_min (int), cook_min (int).", "schema": {"type": "object", "properties": {"name": {"type": "string"}, "serves": {"type": "integer"}, "prep_min": {"type": "integer"}, "cook_min": {"type": "integer"}}, "required": ["name", "serves", "prep_min", "cook_min"]}, "expected": {"name": "pancakes", "serves": 4, "prep_min": 10, "cook_min": 15}}
11
+ {"id": "json_t01", "type": "json", "split": "test", "question": "From 'Employee Jane Smith, ID 7821, department Engineering, salary 95000', JSON: name (str), id (int), department (str), salary (int).", "schema": {"type": "object", "properties": {"name": {"type": "string"}, "id": {"type": "integer"}, "department": {"type": "string"}, "salary": {"type": "integer"}}, "required": ["name", "id", "department", "salary"]}, "expected": {"name": "Jane Smith", "id": 7821, "department": "Engineering", "salary": 95000}}
12
+ {"id": "json_t02", "type": "json", "split": "test", "question": "Parse 'Hotel: Grand Plaza, 4 stars, 250 rooms, $180/night'. JSON: name (str), stars (int), rooms (int), nightly_rate (number).", "schema": {"type": "object", "properties": {"name": {"type": "string"}, "stars": {"type": "integer"}, "rooms": {"type": "integer"}, "nightly_rate": {"type": "number"}}, "required": ["name", "stars", "rooms", "nightly_rate"]}, "expected": {"name": "Grand Plaza", "stars": 4, "rooms": 250, "nightly_rate": 180}}
13
+ {"id": "json_t03", "type": "json", "split": "test", "question": "Extract from 'Match: Real Madrid 3-1 Barcelona on 2024-03-15'. JSON: home (str), away (str), home_score (int), away_score (int), date (str).", "schema": {"type": "object", "properties": {"home": {"type": "string"}, "away": {"type": "string"}, "home_score": {"type": "integer"}, "away_score": {"type": "integer"}, "date": {"type": "string"}}, "required": ["home", "away", "home_score", "away_score", "date"]}, "expected": {"home": "Real Madrid", "away": "Barcelona", "home_score": 3, "away_score": 1, "date": "2024-03-15"}}
14
+ {"id": "json_t04", "type": "json", "split": "test", "question": "From 'Library book \"Dune\" by Frank Herbert, due 2025-12-01, fine $0.25/day', JSON: title (str), author (str), due (str), fine_per_day (number).", "schema": {"type": "object", "properties": {"title": {"type": "string"}, "author": {"type": "string"}, "due": {"type": "string"}, "fine_per_day": {"type": "number"}}, "required": ["title", "author", "due", "fine_per_day"]}, "expected": {"title": "Dune", "author": "Frank Herbert", "due": "2025-12-01", "fine_per_day": 0.25}}
15
+ {"id": "json_t05", "type": "json", "split": "test", "question": "Parse 'GPS: lat 40.7128, lon -74.0060, alt 10m'. JSON: lat (number), lon (number), alt_m (integer).", "schema": {"type": "object", "properties": {"lat": {"type": "number"}, "lon": {"type": "number"}, "alt_m": {"type": "integer"}}, "required": ["lat", "lon", "alt_m"]}, "expected": {"lat": 40.7128, "lon": -74.006, "alt_m": 10}}
16
+ {"id": "json_t06", "type": "json", "split": "test", "question": "From 'Course CS101: Intro to CS, 3 credits, Prof. Adams', JSON: code (str), title (str), credits (int), instructor (str).", "schema": {"type": "object", "properties": {"code": {"type": "string"}, "title": {"type": "string"}, "credits": {"type": "integer"}, "instructor": {"type": "string"}}, "required": ["code", "title", "credits", "instructor"]}, "expected": {"code": "CS101", "title": "Intro to CS", "credits": 3, "instructor": "Prof. Adams"}}
17
+ {"id": "json_t07", "type": "json", "split": "test", "question": "Extract from 'Package PKG-2025-0042 weighs 3.2 kg, ships from Berlin to Rome'. JSON: id (str), weight_kg (number), origin (str), destination (str).", "schema": {"type": "object", "properties": {"id": {"type": "string"}, "weight_kg": {"type": "number"}, "origin": {"type": "string"}, "destination": {"type": "string"}}, "required": ["id", "weight_kg", "origin", "destination"]}, "expected": {"id": "PKG-2025-0042", "weight_kg": 3.2, "origin": "Berlin", "destination": "Rome"}}
18
+ {"id": "json_t08", "type": "json", "split": "test", "question": "Parse 'User account: bob_42, joined 2022-01-15, posts 1234'. JSON: username (str), joined (str), posts (int).", "schema": {"type": "object", "properties": {"username": {"type": "string"}, "joined": {"type": "string"}, "posts": {"type": "integer"}}, "required": ["username", "joined", "posts"]}, "expected": {"username": "bob_42", "joined": "2022-01-15", "posts": 1234}}
19
+ {"id": "json_t09", "type": "json", "split": "test", "question": "From 'Stock AAPL closed at 178.45, change +1.2%, volume 52000000', JSON: ticker (str), close (number), change_pct (number), volume (integer).", "schema": {"type": "object", "properties": {"ticker": {"type": "string"}, "close": {"type": "number"}, "change_pct": {"type": "number"}, "volume": {"type": "integer"}}, "required": ["ticker", "close", "change_pct", "volume"]}, "expected": {"ticker": "AAPL", "close": 178.45, "change_pct": 1.2, "volume": 52000000}}
20
+ {"id": "json_t10", "type": "json", "split": "test", "question": "Extract from 'Weather alert: Severe thunderstorm in county Marion, expires 21:30 UTC'. JSON: type (str), county (str), expires_utc (str).", "schema": {"type": "object", "properties": {"type": {"type": "string"}, "county": {"type": "string"}, "expires_utc": {"type": "string"}}, "required": ["type", "county", "expires_utc"]}, "expected": {"type": "Severe thunderstorm", "county": "Marion", "expires_utc": "21:30 UTC"}}
src/envs/promptops_arena/tasks/loader.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Task loader: reads JSONL files in this directory."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import random
7
+ from pathlib import Path
8
+ from typing import Iterable, List, Optional
9
+
10
+
11
+ _TASK_DIR = Path(__file__).parent
12
+ _FILES = ["math.jsonl", "code.jsonl", "json_extract.jsonl"]
13
+
14
+
15
+ def load_tasks(
16
+ split: Optional[str] = None,
17
+ types: Optional[Iterable[str]] = None,
18
+ ) -> List[dict]:
19
+ """
20
+ Load all tasks, optionally filtered by split ('train'/'test') and types.
21
+
22
+ Returns: list of task dicts.
23
+ """
24
+ out: List[dict] = []
25
+ for fname in _FILES:
26
+ path = _TASK_DIR / fname
27
+ if not path.exists():
28
+ continue
29
+ with path.open("r", encoding="utf-8") as f:
30
+ for line in f:
31
+ line = line.strip()
32
+ if not line:
33
+ continue
34
+ t = json.loads(line)
35
+ if split is not None and t.get("split") != split:
36
+ continue
37
+ if types is not None and t.get("type") not in set(types):
38
+ continue
39
+ out.append(t)
40
+ return out
41
+
42
+
43
+ def sample_task(rng: random.Random, split: str = "train") -> dict:
44
+ tasks = load_tasks(split=split)
45
+ return rng.choice(tasks)
src/envs/promptops_arena/tasks/math.jsonl ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"id": "math_001", "type": "math", "split": "train", "question": "Janet has 24 apples. She gives 8 to her friend and eats 3. How many apples does she have left?", "answer": "13"}
2
+ {"id": "math_002", "type": "math", "split": "train", "question": "A train travels 60 miles per hour for 2.5 hours. How far does it go?", "answer": "150"}
3
+ {"id": "math_003", "type": "math", "split": "train", "question": "Sarah bought 5 packs of pencils, each containing 12 pencils. She used 17 pencils. How many are left?", "answer": "43"}
4
+ {"id": "math_004", "type": "math", "split": "train", "question": "There are 30 students in a class. 40% are boys. How many girls are there?", "answer": "18"}
5
+ {"id": "math_005", "type": "math", "split": "train", "question": "A book costs $15. With a 20% discount, what is the new price?", "answer": "12"}
6
+ {"id": "math_006", "type": "math", "split": "train", "question": "Tom has 3 times as many marbles as Jerry. Jerry has 14. How many do they have together?", "answer": "56"}
7
+ {"id": "math_007", "type": "math", "split": "train", "question": "A rectangle has length 8 and width 5. What is its area?", "answer": "40"}
8
+ {"id": "math_008", "type": "math", "split": "train", "question": "If 4 workers paint 1 wall in 3 hours, how many hours will 2 workers take to paint the same wall?", "answer": "6"}
9
+ {"id": "math_009", "type": "math", "split": "train", "question": "A bag has 7 red and 5 blue balls. After adding 8 more red balls, what fraction are red? Give the answer as a decimal.", "answer": "0.75"}
10
+ {"id": "math_010", "type": "math", "split": "train", "question": "Lily reads 25 pages a day. How many pages in 2 weeks?", "answer": "350"}
11
+ {"id": "math_011", "type": "math", "split": "train", "question": "A pizza is cut into 8 slices. If 3 friends each eat 2 slices, how many slices remain?", "answer": "2"}
12
+ {"id": "math_012", "type": "math", "split": "train", "question": "A car uses 1 gallon for every 30 miles. How many gallons for 240 miles?", "answer": "8"}
13
+ {"id": "math_013", "type": "math", "split": "train", "question": "If x + 7 = 22, what is x?", "answer": "15"}
14
+ {"id": "math_014", "type": "math", "split": "train", "question": "A box contains 48 cookies. They are split equally among 6 children. How many cookies does each get?", "answer": "8"}
15
+ {"id": "math_015", "type": "math", "split": "train", "question": "A shop sells shirts at $20 each. With buy-2-get-1-free, what is the cost of 3 shirts?", "answer": "40"}
16
+ {"id": "math_016", "type": "math", "split": "train", "question": "A water tank holds 200 liters. It is filled at 25 liters per minute. How many minutes to fill it?", "answer": "8"}
17
+ {"id": "math_017", "type": "math", "split": "train", "question": "If today is Wednesday, what day will it be 50 days from now? Answer with a number 1-7 where Monday=1.", "answer": "1"}
18
+ {"id": "math_018", "type": "math", "split": "train", "question": "A square garden has perimeter 36 meters. What is its area?", "answer": "81"}
19
+ {"id": "math_019", "type": "math", "split": "train", "question": "A class of 28 students has 4 absent. What percent are present?", "answer": "85.714286"}
20
+ {"id": "math_020", "type": "math", "split": "train", "question": "A bus has 50 seats. 32 are taken. What fraction of seats are empty? Give as a decimal.", "answer": "0.36"}
21
+ {"id": "math_021", "type": "math", "split": "train", "question": "Peter has $80. He spends 1/4 on books and 3/8 on food. How much is left?", "answer": "30"}
22
+ {"id": "math_022", "type": "math", "split": "train", "question": "A triangle has angles in ratio 1:2:3. What is the largest angle in degrees?", "answer": "90"}
23
+ {"id": "math_023", "type": "math", "split": "train", "question": "If 5x = 35, what is 3x + 4?", "answer": "25"}
24
+ {"id": "math_024", "type": "math", "split": "train", "question": "A jar has 24 candies. Half are red, a third are green, the rest are blue. How many are blue?", "answer": "4"}
25
+ {"id": "math_025", "type": "math", "split": "train", "question": "A man earns $400 per week. He saves 15%. How much does he save in 4 weeks?", "answer": "240"}
26
+ {"id": "math_026", "type": "math", "split": "train", "question": "A cube has volume 27. What is its side length?", "answer": "3"}
27
+ {"id": "math_027", "type": "math", "split": "train", "question": "If a + b = 10 and a - b = 2, what is a*b?", "answer": "24"}
28
+ {"id": "math_028", "type": "math", "split": "train", "question": "A movie is 2 hours and 15 minutes long. If it starts at 7:45 PM, when does it end? Give answer as 24-hour HHMM.", "answer": "2200"}
29
+ {"id": "math_029", "type": "math", "split": "train", "question": "A baker uses 3 eggs per cake. He has 5 dozen eggs. How many cakes can he make?", "answer": "20"}
30
+ {"id": "math_030", "type": "math", "split": "train", "question": "A 50-meter rope is cut into pieces of 2.5 meters each. How many pieces?", "answer": "20"}
31
+ {"id": "math_t01", "type": "math", "split": "test", "question": "Mia has 45 stickers. She gives 1/3 to her sister and 1/5 of the remainder to her friend. How many does she have left?", "answer": "24"}
32
+ {"id": "math_t02", "type": "math", "split": "test", "question": "A farmer has 5 times as many cows as horses. He has 30 cows. How many animals total?", "answer": "36"}
33
+ {"id": "math_t03", "type": "math", "split": "test", "question": "A car costs $20000. It depreciates 10% per year. What is its value after 2 years?", "answer": "16200"}
34
+ {"id": "math_t04", "type": "math", "split": "test", "question": "A right triangle has legs 6 and 8. What is the hypotenuse?", "answer": "10"}
35
+ {"id": "math_t05", "type": "math", "split": "test", "question": "Three consecutive integers sum to 72. What is the largest?", "answer": "25"}
36
+ {"id": "math_t06", "type": "math", "split": "test", "question": "A class average is 80 over 10 students. If one student scored 50, what was the average of the other 9?", "answer": "83.333333"}
37
+ {"id": "math_t07", "type": "math", "split": "test", "question": "If 3x + 5 = 2x + 12, what is x?", "answer": "7"}
38
+ {"id": "math_t08", "type": "math", "split": "test", "question": "A pool fills in 6 hours with one pipe and 4 hours with another. How many hours with both pipes? Give as a decimal.", "answer": "2.4"}
39
+ {"id": "math_t09", "type": "math", "split": "test", "question": "A circle has radius 7. Use pi=22/7. What is its area?", "answer": "154"}
40
+ {"id": "math_t10", "type": "math", "split": "test", "question": "If today is the 5th of a 30-day month, what day of the month is it 60 days later?", "answer": "4"}
src/envs/promptops_arena/verifiers/__init__.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .math_verifier import verify_math
2
+ from .code_verifier import verify_code
3
+ from .json_verifier import verify_json
4
+
5
+ __all__ = ["verify_math", "verify_code", "verify_json", "verify"]
6
+
7
+
8
+ def verify(task: dict, completion: str) -> dict:
9
+ """
10
+ Dispatch to the right verifier based on task['type'].
11
+
12
+ Returns a dict: {correctness: 0.0|1.0, format_ok: bool, details: str}
13
+ """
14
+ task_type = task.get("type", "")
15
+ if task_type == "math":
16
+ return verify_math(task, completion)
17
+ if task_type == "code":
18
+ return verify_code(task, completion)
19
+ if task_type == "json":
20
+ return verify_json(task, completion)
21
+ return {"correctness": 0.0, "format_ok": False, "details": f"Unknown task type: {task_type}"}
src/envs/promptops_arena/verifiers/code_verifier.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Code verifier: extract a python code block from completion, run it in a
3
+ subprocess with the task's test cases appended, with a hard timeout.
4
+
5
+ NEVER use in-process exec(). Subprocess only.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import re
11
+ import subprocess
12
+ import sys
13
+ import tempfile
14
+ from pathlib import Path
15
+ from typing import Any, Dict
16
+
17
+
18
+ _CODE_BLOCK = re.compile(r"```(?:python)?\s*\n(.*?)```", re.DOTALL | re.IGNORECASE)
19
+
20
+
21
+ def _extract_code(completion: str) -> tuple[str | None, bool]:
22
+ if not completion:
23
+ return None, False
24
+ m = _CODE_BLOCK.search(completion)
25
+ if m:
26
+ return m.group(1).strip(), True
27
+ if "def " in completion:
28
+ return completion.strip(), False
29
+ return None, False
30
+
31
+
32
+ def verify_code(task: Dict[str, Any], completion: str) -> Dict[str, Any]:
33
+ code, format_ok = _extract_code(completion or "")
34
+ if code is None:
35
+ return {"correctness": 0.0, "format_ok": False, "details": "no code"}
36
+
37
+ tests = task.get("tests", [])
38
+ if not tests:
39
+ return {"correctness": 0.0, "format_ok": format_ok, "details": "no tests in task"}
40
+
41
+ test_block = "\n".join(tests)
42
+ program = f"{code}\n\n# --- tests ---\n{test_block}\nprint('__OK__')\n"
43
+
44
+ with tempfile.TemporaryDirectory() as td:
45
+ path = Path(td) / "candidate.py"
46
+ path.write_text(program, encoding="utf-8")
47
+ try:
48
+ proc = subprocess.run(
49
+ [sys.executable, str(path)],
50
+ capture_output=True,
51
+ text=True,
52
+ timeout=5,
53
+ )
54
+ except subprocess.TimeoutExpired:
55
+ return {"correctness": 0.0, "format_ok": format_ok, "details": "timeout"}
56
+ except Exception as e:
57
+ return {"correctness": 0.0, "format_ok": format_ok, "details": f"runner err: {e}"}
58
+
59
+ ok = proc.returncode == 0 and "__OK__" in (proc.stdout or "")
60
+ detail = (proc.stderr or proc.stdout or "")[:200].replace("\n", " ")
61
+ return {
62
+ "correctness": 1.0 if ok else 0.0,
63
+ "format_ok": format_ok,
64
+ "details": detail,
65
+ }
src/envs/promptops_arena/verifiers/json_verifier.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ JSON verifier: extract a JSON object from completion, validate against a
3
+ jsonschema, and check value equality on required fields.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import json
9
+ import re
10
+ from typing import Any, Dict
11
+
12
+ from jsonschema import validate, ValidationError
13
+
14
+
15
+ _JSON_BLOCK = re.compile(r"```(?:json)?\s*\n(.*?)```", re.DOTALL | re.IGNORECASE)
16
+ _OBJ = re.compile(r"\{.*\}", re.DOTALL)
17
+
18
+
19
+ def _extract_json(completion: str) -> tuple[Any, bool]:
20
+ if not completion:
21
+ return None, False
22
+ m = _JSON_BLOCK.search(completion)
23
+ candidate = m.group(1).strip() if m else None
24
+ format_ok = m is not None
25
+ if candidate is None:
26
+ m2 = _OBJ.search(completion)
27
+ if m2:
28
+ candidate = m2.group(0)
29
+ if candidate is None:
30
+ return None, False
31
+ try:
32
+ return json.loads(candidate), format_ok
33
+ except json.JSONDecodeError:
34
+ return None, format_ok
35
+
36
+
37
+ def _strip_nones(x: Any) -> Any:
38
+ """HuggingFace `datasets` unifies nested dict schemas across rows by
39
+ null-padding missing keys. That turns a clean schema like
40
+ {"properties": {"name": {...}, "age": {...}}} into one with
41
+ {"properties": {"name": ..., "age": ..., "email": None, ...}} if other rows
42
+ in the dataset had those keys. jsonschema rejects the Nones. Recursively
43
+ drop them so verification is robust to that mangling.
44
+ """
45
+ if isinstance(x, dict):
46
+ return {k: _strip_nones(v) for k, v in x.items() if v is not None}
47
+ if isinstance(x, list):
48
+ return [_strip_nones(v) for v in x if v is not None]
49
+ return x
50
+
51
+
52
+ def verify_json(task: Dict[str, Any], completion: str) -> Dict[str, Any]:
53
+ obj, format_ok = _extract_json(completion or "")
54
+ if obj is None:
55
+ return {"correctness": 0.0, "format_ok": format_ok, "details": "parse fail"}
56
+
57
+ schema = _strip_nones(task.get("schema", {}))
58
+ if schema:
59
+ try:
60
+ validate(instance=obj, schema=schema)
61
+ except ValidationError as e:
62
+ return {
63
+ "correctness": 0.0,
64
+ "format_ok": format_ok,
65
+ "details": f"schema: {str(e.message)[:120]}",
66
+ }
67
+ except Exception as e:
68
+ # Schema itself malformed (e.g., still has Nones somewhere).
69
+ return {
70
+ "correctness": 0.0,
71
+ "format_ok": format_ok,
72
+ "details": f"schema-error: {type(e).__name__}: {str(e)[:120]}",
73
+ }
74
+
75
+ expected = _strip_nones(task.get("expected", {}))
76
+ if expected:
77
+ for k, v in expected.items():
78
+ if obj.get(k) != v:
79
+ return {
80
+ "correctness": 0.0,
81
+ "format_ok": format_ok,
82
+ "details": f"mismatch {k}: got {obj.get(k)!r} expected {v!r}",
83
+ }
84
+
85
+ return {"correctness": 1.0, "format_ok": format_ok, "details": "ok"}
src/envs/promptops_arena/verifiers/math_verifier.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Math verifier: extract final numeric answer and exact-match against ground truth.
3
+
4
+ Format-bonus is awarded if completion contains <answer>...</answer> tags
5
+ or a \\boxed{...} expression.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import re
11
+ from typing import Any, Dict
12
+
13
+
14
+ _ANSWER_TAG = re.compile(r"<answer>\s*(.*?)\s*</answer>", re.DOTALL | re.IGNORECASE)
15
+ _BOXED = re.compile(r"\\boxed\{([^{}]+)\}")
16
+ _FINAL_LINE = re.compile(r"(?:final answer|answer)\s*[:=]\s*([^\n]+)", re.IGNORECASE)
17
+ _NUMBER = re.compile(r"-?\d+(?:\.\d+)?")
18
+
19
+
20
+ def _normalize_number(s: str) -> str | None:
21
+ """Pull the first number out of s, return its canonical string form."""
22
+ if s is None:
23
+ return None
24
+ s = s.strip().replace(",", "").replace("$", "").rstrip(".")
25
+ m = _NUMBER.search(s)
26
+ if not m:
27
+ return None
28
+ try:
29
+ v = float(m.group(0))
30
+ except ValueError:
31
+ return None
32
+ if v.is_integer():
33
+ return str(int(v))
34
+ return f"{v:.6f}".rstrip("0").rstrip(".")
35
+
36
+
37
+ def _extract(completion: str) -> tuple[str | None, bool]:
38
+ """Return (extracted_answer, format_ok)."""
39
+ if not completion:
40
+ return None, False
41
+
42
+ m = _ANSWER_TAG.search(completion)
43
+ if m:
44
+ return _normalize_number(m.group(1)), True
45
+
46
+ m = _BOXED.search(completion)
47
+ if m:
48
+ return _normalize_number(m.group(1)), True
49
+
50
+ m = _FINAL_LINE.search(completion)
51
+ if m:
52
+ return _normalize_number(m.group(1)), False
53
+
54
+ nums = _NUMBER.findall(completion)
55
+ if nums:
56
+ return _normalize_number(nums[-1]), False
57
+
58
+ return None, False
59
+
60
+
61
+ def verify_math(task: Dict[str, Any], completion: str) -> Dict[str, Any]:
62
+ expected = _normalize_number(str(task.get("answer", "")))
63
+ extracted, format_ok = _extract(completion or "")
64
+
65
+ if expected is None:
66
+ return {"correctness": 0.0, "format_ok": format_ok, "details": "bad ground truth"}
67
+
68
+ correct = extracted is not None and extracted == expected
69
+ return {
70
+ "correctness": 1.0 if correct else 0.0,
71
+ "format_ok": format_ok,
72
+ "details": f"expected={expected} extracted={extracted}",
73
+ }
tests/__init__.py ADDED
File without changes
tests/test_rewards.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Adversarial test suite for the reward function.
3
+
4
+ Goal: prove the reward cannot be gamed without doing the task. If any of
5
+ these tests fail, training will reward-hack and we'll waste GPU time.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
14
+
15
+ import pytest
16
+
17
+ from src.envs.promptops_arena.server.rewards import compute_reward
18
+ from src.envs.promptops_arena.verifiers import verify
19
+
20
+
21
+ # ---- math ----
22
+
23
+ MATH_TASK = {"id": "x", "type": "math", "question": "What is 2+2?", "answer": "4"}
24
+
25
+
26
+ def _reward(task, prompt, completion):
27
+ v = verify(task, completion)
28
+ return compute_reward(task, prompt, completion, v)
29
+
30
+
31
+ def test_math_correct_with_tag_full_reward():
32
+ r = _reward(MATH_TASK, "short prompt", "<answer>4</answer>")
33
+ assert r["correctness"] == 1.0
34
+ assert r["format"] == 1.0
35
+ assert r["total"] == pytest.approx(1.1)
36
+
37
+
38
+ def test_math_correct_no_tag_only_correctness():
39
+ r = _reward(MATH_TASK, "short prompt", "The answer is 4.")
40
+ assert r["correctness"] == 1.0
41
+ assert r["format"] == 0.0
42
+ assert r["total"] == pytest.approx(1.0)
43
+
44
+
45
+ def test_math_empty_completion_zero():
46
+ r = _reward(MATH_TASK, "short prompt", "")
47
+ assert r["correctness"] == 0.0
48
+ assert r["format"] == 0.0
49
+ assert r["total"] == 0.0
50
+
51
+
52
+ def test_math_empty_tag_only_format_bonus_capped():
53
+ """Tag with no number — gets format bonus but not correctness. Total <= 0.1."""
54
+ r = _reward(MATH_TASK, "short prompt", "<answer></answer>")
55
+ assert r["correctness"] == 0.0
56
+ assert r["total"] <= 0.1
57
+
58
+
59
+ def test_math_wrong_answer_with_perfect_format_capped():
60
+ r = _reward(MATH_TASK, "short prompt", "<answer>7</answer>")
61
+ assert r["correctness"] == 0.0
62
+ assert r["total"] <= 0.1
63
+
64
+
65
+ def test_math_rambling_long_correct_still_bounded():
66
+ """5000-char prompt + correct: brevity penalty must fire."""
67
+ long_prompt = "blah " * 1000 # 5000 chars
68
+ r = _reward(MATH_TASK, long_prompt, "<answer>4</answer>")
69
+ assert r["brevity"] < 0.0
70
+ assert r["brevity"] >= -0.1
71
+ # still mostly rewarded for correctness
72
+ assert r["total"] >= 1.0
73
+
74
+
75
+ def test_math_short_prompt_no_brevity_penalty():
76
+ r = _reward(MATH_TASK, "Solve.", "<answer>4</answer>")
77
+ assert r["brevity"] == 0.0
78
+
79
+
80
+ def test_math_boxed_format_recognized():
81
+ r = _reward(MATH_TASK, "short", "Final: \\boxed{4}")
82
+ assert r["correctness"] == 1.0
83
+ assert r["format"] == 1.0
84
+
85
+
86
+ def test_math_keyword_only_no_credit():
87
+ """'answer:' phrase without correct number gets nothing."""
88
+ r = _reward(MATH_TASK, "short", "answer: 99")
89
+ assert r["correctness"] == 0.0
90
+ assert r["total"] <= 0.1
91
+
92
+
93
+ def test_math_repeated_token_not_rewarded():
94
+ r = _reward(MATH_TASK, "short", "4 4 4 4 4 4 4 4")
95
+ # last number IS 4, so verifier extracts it correctly. This test documents
96
+ # that exact-match on a single number IS exploitable in this trivial task,
97
+ # so for real GSM8K-style tasks the answer should be one of many numbers in
98
+ # the question and not present in the prompt itself. We assert the
99
+ # "correctness fires only on exact match", not that this is unhackable on
100
+ # adversarial inputs unrelated to the question.
101
+ assert r["correctness"] in (0.0, 1.0)
102
+
103
+
104
+ # ---- code ----
105
+
106
+ CODE_TASK = {
107
+ "id": "c",
108
+ "type": "code",
109
+ "question": "Write add(a,b)",
110
+ "tests": ["assert add(2, 3) == 5", "assert add(0, 0) == 0"],
111
+ }
112
+
113
+
114
+ def test_code_correct_passes_tests():
115
+ completion = "```python\ndef add(a, b):\n return a + b\n```"
116
+ r = _reward(CODE_TASK, "short", completion)
117
+ assert r["correctness"] == 1.0
118
+ assert r["format"] == 1.0
119
+
120
+
121
+ def test_code_no_block_zero():
122
+ r = _reward(CODE_TASK, "short", "Sure! add returns sum.")
123
+ assert r["correctness"] == 0.0
124
+
125
+
126
+ def test_code_block_but_wrong_zero():
127
+ completion = "```python\ndef add(a, b):\n return a - b\n```"
128
+ r = _reward(CODE_TASK, "short", completion)
129
+ assert r["correctness"] == 0.0
130
+ # format bonus still given (proper block) — total bounded
131
+ assert r["total"] <= 0.1
132
+
133
+
134
+ def test_code_infinite_loop_times_out_to_zero():
135
+ completion = "```python\ndef add(a, b):\n while True: pass\n```"
136
+ r = _reward(CODE_TASK, "short", completion)
137
+ assert r["correctness"] == 0.0
138
+
139
+
140
+ def test_code_malicious_import_still_sandboxed():
141
+ """We don't formally sandbox; we rely on subprocess isolation + 5s timeout."""
142
+ completion = "```python\nimport os\ndef add(a, b):\n return a + b\n```"
143
+ r = _reward(CODE_TASK, "short", completion)
144
+ # imports are allowed; correctness still computed
145
+ assert r["correctness"] == 1.0
146
+
147
+
148
+ # ---- json ----
149
+
150
+ JSON_TASK = {
151
+ "id": "j",
152
+ "type": "json",
153
+ "question": "Extract name and age",
154
+ "schema": {
155
+ "type": "object",
156
+ "properties": {"name": {"type": "string"}, "age": {"type": "integer"}},
157
+ "required": ["name", "age"],
158
+ },
159
+ "expected": {"name": "Alice", "age": 30},
160
+ }
161
+
162
+
163
+ def test_json_correct_full_reward():
164
+ completion = '```json\n{"name": "Alice", "age": 30}\n```'
165
+ r = _reward(JSON_TASK, "short", completion)
166
+ assert r["correctness"] == 1.0
167
+ assert r["format"] == 1.0
168
+
169
+
170
+ def test_json_inline_no_block_lower():
171
+ completion = '{"name": "Alice", "age": 30}'
172
+ r = _reward(JSON_TASK, "short", completion)
173
+ assert r["correctness"] == 1.0
174
+ assert r["format"] == 0.0
175
+
176
+
177
+ def test_json_empty_object_zero():
178
+ r = _reward(JSON_TASK, "short", "```json\n{}\n```")
179
+ assert r["correctness"] == 0.0
180
+
181
+
182
+ def test_json_wrong_type_zero():
183
+ """Schema requires int age — string fails."""
184
+ completion = '```json\n{"name": "Alice", "age": "30"}\n```'
185
+ r = _reward(JSON_TASK, "short", completion)
186
+ assert r["correctness"] == 0.0
187
+
188
+
189
+ def test_json_invalid_zero():
190
+ r = _reward(JSON_TASK, "short", "```json\n{not valid json\n```")
191
+ assert r["correctness"] == 0.0
192
+
193
+
194
+ # ---- decomposition contract ----
195
+
196
+ def test_reward_dict_always_has_four_keys():
197
+ """The reward function must always return all four components."""
198
+ cases = [
199
+ (MATH_TASK, "p", "<answer>4</answer>"),
200
+ (MATH_TASK, "p", ""),
201
+ (CODE_TASK, "p", "```python\ndef add(a,b): return a+b\n```"),
202
+ (JSON_TASK, "p", "{}"),
203
+ ]
204
+ for task, p, c in cases:
205
+ r = _reward(task, p, c)
206
+ assert set(r.keys()) >= {"correctness", "format", "brevity", "total"}
207
+ for k in ("correctness", "format", "brevity", "total"):
208
+ assert isinstance(r[k], float)
209
+
210
+
211
+ def test_reward_total_is_sum_of_components():
212
+ r = _reward(MATH_TASK, "x" * 1500, "<answer>4</answer>")
213
+ expected = r["correctness"] + 0.1 * r["format"] + r["brevity"]
214
+ assert r["total"] == pytest.approx(expected)