shank commited on
Commit
13f7b8d
Β·
1 Parent(s): 2e70173

chore: Rename Dockerfile to fix HF Space deploy, and clean up AI slop

Browse files
Dockerfile β†’ Dockerfile.hackathon RENAMED
File without changes
HANDOVER.md DELETED
@@ -1,211 +0,0 @@
1
- # AgentDebuggerEnv β€” Project Handover
2
-
3
- ## What This Project Is
4
- A GRPO-trained LLM (Qwen2.5-Coder-7B-Instruct) that learns to debug Python code through
5
- structured hypothesis-driven reasoning. Submitted to the Meta + PyTorch + HuggingFace OpenEnv Hackathon.
6
-
7
- ---
8
-
9
- ## Repo & Remotes
10
-
11
- | Remote | URL |
12
- |---|---|
13
- | GitHub (source of truth) | https://github.com/shasshaank/meta_hackthon.git |
14
- | HF Training Space | https://huggingface.co/spaces/shashaank0707/AgentDebugger-training-v2 |
15
- | HF Trained Model | https://huggingface.co/shashaank0707/AgentDebugger-trained |
16
-
17
- Push to GitHub first, then to HF Space if needed:
18
- ```bash
19
- git push origin main
20
- git push space main --force # space remote = HF training space
21
- ```
22
-
23
- The `space` remote URL includes your HF token:
24
- ```
25
- https://shashaank0707:YOUR_HF_TOKEN@huggingface.co/spaces/shashaank0707/AgentDebugger-training-v2
26
- ```
27
-
28
- ---
29
-
30
- ## Project Structure
31
-
32
- ```
33
- meta_hackathon/
34
- β”œβ”€β”€ app.py # Gradio training monitor β€” launched by HF Space SDK
35
- β”œβ”€β”€ training/
36
- β”‚ └── train_grpo.py # Main training script (GRPO via TRL)
37
- β”œβ”€β”€ server/
38
- β”‚ β”œβ”€β”€ reward_calculator.py # Multi-component reward (format, hypothesis, fix, semantic)
39
- β”‚ β”œβ”€β”€ models.py # parse_agent_output() β€” parses structured LLM output
40
- β”‚ └── app.py # FastAPI server (for the inference/env Space, not training)
41
- β”œβ”€β”€ data/
42
- β”‚ β”œβ”€β”€ bugs_tier1.jsonl # 9 easy bugs (used steps 0–150)
43
- β”‚ β”œβ”€β”€ bugs_tier2.jsonl # 31 medium bugs (added at step 150)
44
- β”‚ β”œβ”€β”€ bugs_tier3.jsonl # 21 hard bugs (added at step 350 β†’ was 600)
45
- β”‚ └── generate_bugs.py # Script that generated the bug datasets
46
- β”œβ”€β”€ requirements.txt # HF Space deps (gradio[oauth,mcp]==6.13.0, cu121 torch)
47
- β”œβ”€β”€ requirements_kaggle.txt # Kaggle/RunPod deps (no torch pin, bitsandbytes==0.45.3)
48
- β”œβ”€β”€ inference.py # Inference wrapper for evaluation
49
- β”œβ”€β”€ Dockerfile # For the inference/env Space (not the training space)
50
- └── README.md # HF Space config header (sdk: gradio, app_file: app.py)
51
- ```
52
-
53
- ---
54
-
55
- ## Dependency Versions (locked β€” do not change without testing)
56
-
57
- | Package | Version | Why pinned |
58
- |---|---|---|
59
- | `trl` | `0.14.0` | First version with `GRPOTrainer` + `GRPOConfig` |
60
- | `pydantic` | `2.12.5` | Only version satisfying both gradio base AND gradio[mcp] constraints |
61
- | `gradio` | `6.13.0[oauth,mcp]` | HF Space builder requires extras in one install pass |
62
- | `bitsandbytes` | `0.45.3` (Kaggle) / `0.43.3` (HF Space cu121) | 0.45.3 has CUDA 12.x binaries; 0.43.3 works with cu121 |
63
- | `transformers` | `4.46.3` | Tested with TRL 0.14.0 |
64
- | `torch` | `2.5.1+cu121` (HF Space) / pre-installed (Kaggle) | |
65
-
66
- **GRPOConfig param name:** `max_completion_length` (NOT `max_new_tokens` β€” that's the old name, breaks on 0.14.0)
67
-
68
- ---
69
-
70
- ## Training Script β€” Key Design Decisions
71
-
72
- ### GPU Auto-Detection (train_grpo.py ~line 260)
73
- The script detects GPU at runtime and sets all hyperparams automatically:
74
-
75
- | GPU | dtype | batch | grad_accum | num_gen | max_comp | lora_r |
76
- |---|---|---|---|---|---|---|
77
- | A100 40GB+ | bfloat16 | 2 | 4 | 8 | 256 | 16 |
78
- | V100 32GB | float16 | 1 | 8 | 6 | 220 | 12 |
79
- | T4 / ≀16GB | float16 | 1 | 8 | 4 | 160 | 8 |
80
-
81
- **Critical:** P100 is NOT supported β€” PyTorch 2.x dropped sm_60 support. Use T4 instead.
82
-
83
- ### Curriculum
84
- - Steps 0–150: Tier 1 bugs only (9 bugs)
85
- - Steps 150–350: Tier 1 + Tier 2 (40 bugs)
86
- - Steps 350+: All tiers (61 bugs)
87
-
88
- ### Reward Components (server/reward_calculator.py)
89
- | Component | Weight | What it measures |
90
- |---|---|---|
91
- | format_compliance | 0.10 | All 5 fields present (OBSERVATION/HYPOTHESIS/CONFIDENCE/ACTION/DETAIL) |
92
- | hypothesis_quality | 0.20 | Length + references specific variable names |
93
- | localization | 0.15 | Correct function/line identified |
94
- | fix_quality | 0.35 | Tests pass on proposed fix |
95
- | semantic_similarity | 0.10 | Similarity to canonical fix |
96
- | efficiency_potential | 0.10 | Potential-based shaping (Ibrahim et al. 2024) |
97
-
98
- ### Required Output Format
99
- ```
100
- OBSERVATION: [specific observations with line numbers]
101
- HYPOTHESIS: [2+ sentences explaining root cause with variable names]
102
- CONFIDENCE: [low | medium | high]
103
- ACTION: [inspect_lines | run_tests | propose_fix | request_context | give_up]
104
- DETAIL: [complete fixed function code if propose_fix, else details]
105
- ```
106
-
107
- ---
108
-
109
- ## Running Training
110
-
111
- ### On Kaggle (T4 β€” free):
112
- ```python
113
- # Cell 1 β€” install
114
- !pip install -q wandb==0.18.7 datasets==3.0.2 transformers==4.46.3 \
115
- accelerate==1.0.1 trl==0.14.0 bitsandbytes==0.45.3 peft==0.13.2
116
-
117
- # Cell 2 β€” clone + secrets
118
- from kaggle_secrets import UserSecretsClient
119
- import os
120
- secrets = UserSecretsClient()
121
- os.environ["WANDB_API_KEY"] = secrets.get_secret("WANDB_API_KEY")
122
- os.environ["HF_TOKEN"] = secrets.get_secret("HF_TOKEN")
123
- !git clone https://github.com/shasshaank/meta_hackthon.git /kaggle/working/repo
124
- %cd /kaggle/working/repo
125
-
126
- # Cell 3 β€” train (streams output live)
127
- import subprocess, sys
128
- proc = subprocess.Popen(
129
- [sys.executable, "training/train_grpo.py"],
130
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
131
- text=True, bufsize=1, cwd="/kaggle/working/repo"
132
- )
133
- for line in proc.stdout:
134
- print(line, end="", flush=True)
135
- proc.wait()
136
-
137
- # Cell 4 β€” save outputs after training
138
- import shutil
139
- shutil.copytree("/kaggle/working/repo/checkpoints", "/kaggle/working/checkpoints", dirs_exist_ok=True)
140
- ```
141
-
142
- **Kaggle secrets needed:** `WANDB_API_KEY`, `HF_TOKEN`
143
- **Kaggle GPU:** T4 x1 (NOT P100 β€” incompatible with modern PyTorch)
144
- **Expected time:** ~8–10 hours for 500 steps (default max_steps=500)
145
-
146
- ### On RunPod (A100 β€” ~$1.09/hr):
147
- ```bash
148
- git clone https://github.com/shasshaank/meta_hackthon.git && cd meta_hackthon
149
- pip install -q wandb==0.18.7 datasets==3.0.2 transformers==4.46.3 \
150
- accelerate==1.0.1 trl==0.14.0 bitsandbytes==0.45.3 peft==0.13.2
151
- WANDB_API_KEY=xxx HF_TOKEN=xxx python training/train_grpo.py
152
- ```
153
- **Expected time:** ~3–4 hours for 1000 steps on A100 40GB
154
-
155
- ### Resume from checkpoint:
156
- ```bash
157
- python training/train_grpo.py --resume ./checkpoints/checkpoint-400
158
- ```
159
-
160
- ### Local sanity check (no GPU):
161
- ```bash
162
- python training/train_grpo.py --test-local
163
- ```
164
-
165
- ---
166
-
167
- ## HF Space Setup (training monitor)
168
-
169
- The training Space (`AgentDebugger-training-v2`) is a Gradio app that:
170
- 1. On startup, spawns `training/train_grpo.py` in a background thread
171
- 2. Shows a live training log in the UI, auto-refreshing every 30s
172
-
173
- **Required Space secrets:**
174
- - `WANDB_API_KEY`
175
- - `HF_TOKEN`
176
-
177
- **Push to Space:**
178
- ```bash
179
- git remote set-url space https://shashaank0707:YOUR_HF_TOKEN@huggingface.co/spaces/shashaank0707/AgentDebugger-training-v2
180
- git push space main --force
181
- ```
182
-
183
- ---
184
-
185
- ## Known Issues Fixed (do not revert)
186
-
187
- | Issue | Fix |
188
- |---|---|
189
- | `ImportError: cannot import name 'GRPOTrainer'` | `trl==0.12.2` β†’ `trl==0.14.0` |
190
- | `TypeError: GRPOConfig got unexpected keyword 'max_new_tokens'` | renamed to `max_completion_length` |
191
- | `pydantic` conflict with `gradio[mcp]` | `pydantic==2.10.6` β†’ `2.12.5` |
192
- | `P100 not supported by PyTorch 2.x` | Switch to T4 on Kaggle |
193
- | `bitsandbytes CUDA binary not found` | `bitsandbytes==0.43.3` β†’ `0.45.3` on Kaggle |
194
- | `unsloth` CUDA driver crash on HF A100 | Replaced with `bitsandbytes + peft` |
195
- | `gradio every=` deprecation | Replaced with `gr.Timer(value=30)` |
196
-
197
- ---
198
-
199
- ## W&B Dashboard
200
- https://wandb.ai/shashaankjain07-keshav-memorial-college-of-law/AgentDebuggerEnv
201
-
202
- Training runs appear here automatically when `WANDB_API_KEY` is set.
203
-
204
- ---
205
-
206
- ## What's Left To Do
207
-
208
- - [ ] **Finish training** β€” 500–1000 steps, model pushes to HF Hub automatically on completion
209
- - [ ] **Verify trained model** β€” run `inference.py` against the trained model checkpoint
210
- - [ ] **Update HF Space README** β€” change curriculum description to match actual step boundaries (150/350)
211
- - [ ] **Submission** β€” ensure the inference/env Space (`AgentDebugger-env`) is live and healthy for judging
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
calibrate.py CHANGED
@@ -15,9 +15,9 @@ def test_passes(code, func, inp, expected):
15
  try:
16
  r = {func}({args})
17
  expected = {repr(expected)}
18
- print("PASS" if r == expected else f"FAIL: got {{r}}")
19
  except Exception as e:
20
- print(f"ERROR: {{e}}")
21
  """
22
  try:
23
  with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
 
15
  try:
16
  r = {func}({args})
17
  expected = {repr(expected)}
18
+ print("PASS" if r == expected else f"FAIL: got { r} ")
19
  except Exception as e:
20
+ print(f"ERROR: { e} ")
21
  """
22
  try:
23
  with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
demo/gradio_app.py CHANGED
@@ -1,9 +1,3 @@
1
- """
2
- AgentDebuggerEnv β€” Interactive Gradio Demo
3
- ==========================================
4
- Demonstrates the live debugging environment with a rule-based agent.
5
- Shows structured multi-turn reasoning, reward breakdown, and fix verification.
6
- """
7
 
8
  import os
9
  import sys
@@ -18,7 +12,7 @@ import gradio as gr
18
  from env.models import parse_agent_output
19
  from server.reward_calculator import DebugRewardCalculator
20
 
21
- # ── Pre-loaded bug examples (one per bug type) ────────────────────────────────
22
 
23
  EXAMPLES = {
24
  "πŸ”’ Off-by-One: binary_search": {
@@ -256,10 +250,9 @@ DETAIL: def fibonacci(n):
256
  },
257
  }
258
 
259
- # ── Test runner ───────────────────────────────────────────────────────────────
260
 
261
  def _run_tests(code: str, function_name: str, test_cases: list) -> dict:
262
- """Run test cases against code in a subprocess. Returns pass/fail counts."""
263
  passed = 0
264
  python = shutil.which("python3") or shutil.which("python") or sys.executable
265
  for tc in test_cases:
@@ -270,9 +263,9 @@ def _run_tests(code: str, function_name: str, test_cases: list) -> dict:
270
  f"{code}\n"
271
  f"try:\n"
272
  f" r = {function_name}({args_str})\n"
273
- f" print('PASS' if r == {repr(expected)} else f'FAIL: got {{r}}')\n"
274
  f"except Exception as e:\n"
275
- f" print(f'ERROR: {{e}}')\n"
276
  )
277
  try:
278
  with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
@@ -288,16 +281,12 @@ def _run_tests(code: str, function_name: str, test_cases: list) -> dict:
288
  return {"passed": passed, "failed": total - passed, "total": total, "newly_broken": 0}
289
 
290
 
291
- # ── Rule-based agent runner ───────────────────────────────────────────────────
292
 
293
  def run_debug_session(example_name: str, custom_code: str) -> str:
294
- """
295
- Run the rule-based debug agent for 2 turns.
296
- Returns a formatted string showing each turn's output and reward.
297
- """
298
  calculator = DebugRewardCalculator()
299
 
300
- # Determine which bug we're working with
301
  if example_name and example_name in EXAMPLES:
302
  bug = EXAMPLES[example_name]
303
  code = bug["buggy_code"]
@@ -312,7 +301,7 @@ def run_debug_session(example_name: str, custom_code: str) -> str:
312
  function_name = bug["function_name"]
313
  initial_error = bug["initial_error"]
314
  else:
315
- # Custom code β€” generic 2-turn agent
316
  code = custom_code.strip() if custom_code.strip() else "# No code provided"
317
  agent_turns = [
318
  """\
@@ -336,7 +325,7 @@ DETAIL: """ + code,
336
  function_name = ""
337
  initial_error = "Unknown β€” paste your own code and observe the agent reasoning"
338
 
339
- # ── Build output ──────────────────────────────────────────────────────────
340
  lines = []
341
  lines.append("━" * 60)
342
  lines.append(f"πŸ› BUGGY CODE")
@@ -356,12 +345,12 @@ DETAIL: """ + code,
356
 
357
  agent_output = parse_agent_output(raw_turn)
358
 
359
- # Run tests if this is a fix proposal
360
  test_results = {"passed": 0, "failed": 0, "total": len(test_cases), "newly_broken": 0}
361
  if agent_output.action == "propose_fix" and test_cases and function_name:
362
  test_results = _run_tests(agent_output.detail, function_name, test_cases)
363
 
364
- # Compute reward
365
  reward = calculator.compute_turn_reward(
366
  agent_output=agent_output,
367
  ground_truth=ground_truth,
@@ -370,7 +359,7 @@ DETAIL: """ + code,
370
  )
371
  total_episode_reward += reward.total
372
 
373
- # Format the structured output
374
  lines.append(f"OBSERVATION: {agent_output.observation}")
375
  lines.append("")
376
  lines.append(f"HYPOTHESIS: {agent_output.hypothesis}")
@@ -380,7 +369,7 @@ DETAIL: """ + code,
380
  lines.append(f"DETAIL: {agent_output.detail[:120]}{'...' if len(agent_output.detail) > 120 else ''}")
381
  lines.append("")
382
 
383
- # Reward breakdown table
384
  lines.append("β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”")
385
  lines.append("β”‚ Reward Breakdown β”‚")
386
  lines.append("β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€")
@@ -395,7 +384,7 @@ DETAIL: """ + code,
395
  lines.append(f"β”‚ TURN REWARD β”‚ {reward.total:+.4f} β”‚")
396
  lines.append("β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜")
397
 
398
- # Test results for fix turns
399
  if agent_output.action == "propose_fix" and test_results["total"] > 0:
400
  p = test_results["passed"]
401
  t = test_results["total"]
@@ -409,7 +398,7 @@ DETAIL: """ + code,
409
 
410
  lines.append("")
411
 
412
- # ── Episode summary ───────────────────────────────────────────────────────
413
  lines.append("━" * 60)
414
  if solved:
415
  lines.append(f" βœ… SOLVED in {len(agent_turns)} turns | Episode reward: {total_episode_reward:+.3f}")
@@ -424,10 +413,9 @@ DETAIL: """ + code,
424
  return "\n".join(lines)
425
 
426
 
427
- # ── Gradio interface ──────────────────────────────────────────────────────────
428
 
429
  def load_example(example_name: str) -> str:
430
- """Return the buggy code for the selected example."""
431
  if example_name and example_name in EXAMPLES:
432
  return EXAMPLES[example_name]["buggy_code"]
433
  return ""
@@ -480,7 +468,7 @@ The environment scores every turn across 6 reward components grounded in two res
480
  inputs=example_dropdown,
481
  outputs=custom_code,
482
  )
483
- # Pre-load first example
484
  demo.load(
485
  fn=lambda: load_example(example_names[0]),
486
  outputs=custom_code,
 
 
 
 
 
 
 
1
 
2
  import os
3
  import sys
 
12
  from env.models import parse_agent_output
13
  from server.reward_calculator import DebugRewardCalculator
14
 
15
+
16
 
17
  EXAMPLES = {
18
  "πŸ”’ Off-by-One: binary_search": {
 
250
  },
251
  }
252
 
253
+
254
 
255
  def _run_tests(code: str, function_name: str, test_cases: list) -> dict:
 
256
  passed = 0
257
  python = shutil.which("python3") or shutil.which("python") or sys.executable
258
  for tc in test_cases:
 
263
  f"{code}\n"
264
  f"try:\n"
265
  f" r = {function_name}({args_str})\n"
266
+ f" print('PASS' if r == {repr(expected)} else f'FAIL: got { r} ')\n"
267
  f"except Exception as e:\n"
268
+ f" print(f'ERROR: { e} ')\n"
269
  )
270
  try:
271
  with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
 
281
  return {"passed": passed, "failed": total - passed, "total": total, "newly_broken": 0}
282
 
283
 
284
+
285
 
286
  def run_debug_session(example_name: str, custom_code: str) -> str:
 
 
 
 
287
  calculator = DebugRewardCalculator()
288
 
289
+
290
  if example_name and example_name in EXAMPLES:
291
  bug = EXAMPLES[example_name]
292
  code = bug["buggy_code"]
 
301
  function_name = bug["function_name"]
302
  initial_error = bug["initial_error"]
303
  else:
304
+
305
  code = custom_code.strip() if custom_code.strip() else "# No code provided"
306
  agent_turns = [
307
  """\
 
325
  function_name = ""
326
  initial_error = "Unknown β€” paste your own code and observe the agent reasoning"
327
 
328
+
329
  lines = []
330
  lines.append("━" * 60)
331
  lines.append(f"πŸ› BUGGY CODE")
 
345
 
346
  agent_output = parse_agent_output(raw_turn)
347
 
348
+
349
  test_results = {"passed": 0, "failed": 0, "total": len(test_cases), "newly_broken": 0}
350
  if agent_output.action == "propose_fix" and test_cases and function_name:
351
  test_results = _run_tests(agent_output.detail, function_name, test_cases)
352
 
353
+
354
  reward = calculator.compute_turn_reward(
355
  agent_output=agent_output,
356
  ground_truth=ground_truth,
 
359
  )
360
  total_episode_reward += reward.total
361
 
362
+
363
  lines.append(f"OBSERVATION: {agent_output.observation}")
364
  lines.append("")
365
  lines.append(f"HYPOTHESIS: {agent_output.hypothesis}")
 
369
  lines.append(f"DETAIL: {agent_output.detail[:120]}{'...' if len(agent_output.detail) > 120 else ''}")
370
  lines.append("")
371
 
372
+
373
  lines.append("β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”")
374
  lines.append("β”‚ Reward Breakdown β”‚")
375
  lines.append("β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€")
 
384
  lines.append(f"β”‚ TURN REWARD β”‚ {reward.total:+.4f} β”‚")
385
  lines.append("β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜")
386
 
387
+
388
  if agent_output.action == "propose_fix" and test_results["total"] > 0:
389
  p = test_results["passed"]
390
  t = test_results["total"]
 
398
 
399
  lines.append("")
400
 
401
+
402
  lines.append("━" * 60)
403
  if solved:
404
  lines.append(f" βœ… SOLVED in {len(agent_turns)} turns | Episode reward: {total_episode_reward:+.3f}")
 
413
  return "\n".join(lines)
414
 
415
 
416
+
417
 
418
  def load_example(example_name: str) -> str:
 
419
  if example_name and example_name in EXAMPLES:
420
  return EXAMPLES[example_name]["buggy_code"]
421
  return ""
 
468
  inputs=example_dropdown,
469
  outputs=custom_code,
470
  )
471
+
472
  demo.load(
473
  fn=lambda: load_example(example_names[0]),
474
  outputs=custom_code,
env/__pycache__/environment.cpython-313.pyc CHANGED
Binary files a/env/__pycache__/environment.cpython-313.pyc and b/env/__pycache__/environment.cpython-313.pyc differ
 
eval_run.log ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
0
  0%| | 0/40 [00:00<?, ?it/s]The following generation flags are not valid and may be ignored: ['temperature', 'top_p', 'top_k']. Set `TRANSFORMERS_VERBOSITY=info` for more details.
 
1
+ W0707 11:06:09.884000 56937 .venv/lib/python3.13/site-packages/torch/distributed/elastic/multiprocessing/redirects.py:29] NOTE: Redirects are currently not supported in Windows or MacOs.
2
+ `torch_dtype` is deprecated! Use `dtype` instead!
3
+ Loading base model: Qwen/Qwen2.5-Coder-3B-Instruct...
4
+ Using device: mps | dtype: torch.float16
5
+
6
+ Loading LoRA adapter: shashaank0707/AgentDebugger-trained...
7
+ Moving model to target device: mps...
8
+
9
+ Initializing environment and loading bugs...
10
+
11
+ Evaluating Tier 1 bugs...
12
+
13
  0%| | 0/40 [00:00<?, ?it/s]The following generation flags are not valid and may be ignored: ['temperature', 'top_p', 'top_k']. Set `TRANSFORMERS_VERBOSITY=info` for more details.
evaluate_model.py CHANGED
@@ -8,16 +8,16 @@ from dotenv import load_dotenv
8
  from transformers import AutoModelForCausalLM, AutoTokenizer
9
  from peft import PeftModel
10
 
11
- # Load environment variables
12
  load_dotenv()
13
 
14
- # Insert workspace root to path
15
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
16
  from env.environment import DebuggerEnvironment
17
  from env.models import parse_agent_output
18
  from server.reward_calculator import DebugRewardCalculator
19
 
20
- # System prompt matching train_grpo.py
21
  SYSTEM_PROMPT = """You are an expert Python debugger. You reason through bugs systematically.
22
 
23
  You MUST respond in EXACTLY this format β€” no exceptions, no extra text:
@@ -51,7 +51,7 @@ def main():
51
  parser.add_argument("--base-model", type=str, default="Qwen/Qwen2.5-Coder-3B-Instruct", help="Base model identifier")
52
  args = parser.parse_args()
53
 
54
- # Verify HF Token if repository is private
55
  hf_token = os.environ.get("HF_TOKEN")
56
  if not hf_token:
57
  print("WARNING: HF_TOKEN environment variable not set. Loading a private repository might fail.")
@@ -80,7 +80,7 @@ def main():
80
  token=hf_token
81
  )
82
 
83
- # Explicitly move to target device if using MPS or CPU
84
  if device in ["mps", "cpu"]:
85
  print(f"Moving model to target device: {device}...")
86
  model = model.to(device)
@@ -125,12 +125,12 @@ def main():
125
  tier_solved = 0
126
 
127
  for bug in tqdm(bugs):
128
- # Setup environment context for this bug
129
  env.current_bug = bug
130
  env.current_episode_trajectory = []
131
  env.turn_number = 0
132
 
133
- # Generate prompt
134
  prompt = bug_to_prompt(bug)
135
  inputs = tokenizer(prompt, return_tensors="pt").to(device)
136
 
@@ -143,7 +143,7 @@ def main():
143
 
144
  completion = tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
145
 
146
- # Step the environment with model's completion
147
  step_result = env.step_curriculum(completion)
148
  info = step_result["info"]
149
  reward_breakdown = info["reward_breakdown"]
@@ -154,7 +154,7 @@ def main():
154
  solved_bugs_count += 1
155
  total_bugs_count += 1
156
 
157
- # Store details
158
  bug_detail = {
159
  "id": bug.get("id"),
160
  "function_name": bug.get("function_name"),
@@ -190,7 +190,7 @@ def main():
190
  "solve_rate": solved_bugs_count / total_bugs_count if total_bugs_count else 0.0,
191
  }
192
 
193
- # Save to file
194
  output = {
195
  "summary": summary,
196
  "results": results
 
8
  from transformers import AutoModelForCausalLM, AutoTokenizer
9
  from peft import PeftModel
10
 
11
+
12
  load_dotenv()
13
 
14
+
15
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
16
  from env.environment import DebuggerEnvironment
17
  from env.models import parse_agent_output
18
  from server.reward_calculator import DebugRewardCalculator
19
 
20
+
21
  SYSTEM_PROMPT = """You are an expert Python debugger. You reason through bugs systematically.
22
 
23
  You MUST respond in EXACTLY this format β€” no exceptions, no extra text:
 
51
  parser.add_argument("--base-model", type=str, default="Qwen/Qwen2.5-Coder-3B-Instruct", help="Base model identifier")
52
  args = parser.parse_args()
53
 
54
+
55
  hf_token = os.environ.get("HF_TOKEN")
56
  if not hf_token:
57
  print("WARNING: HF_TOKEN environment variable not set. Loading a private repository might fail.")
 
80
  token=hf_token
81
  )
82
 
83
+
84
  if device in ["mps", "cpu"]:
85
  print(f"Moving model to target device: {device}...")
86
  model = model.to(device)
 
125
  tier_solved = 0
126
 
127
  for bug in tqdm(bugs):
128
+
129
  env.current_bug = bug
130
  env.current_episode_trajectory = []
131
  env.turn_number = 0
132
 
133
+
134
  prompt = bug_to_prompt(bug)
135
  inputs = tokenizer(prompt, return_tensors="pt").to(device)
136
 
 
143
 
144
  completion = tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
145
 
146
+
147
  step_result = env.step_curriculum(completion)
148
  info = step_result["info"]
149
  reward_breakdown = info["reward_breakdown"]
 
154
  solved_bugs_count += 1
155
  total_bugs_count += 1
156
 
157
+
158
  bug_detail = {
159
  "id": bug.get("id"),
160
  "function_name": bug.get("function_name"),
 
190
  "solve_rate": solved_bugs_count / total_bugs_count if total_bugs_count else 0.0,
191
  }
192
 
193
+
194
  output = {
195
  "summary": summary,
196
  "results": results
evaluation_results.json CHANGED
@@ -41,9 +41,12 @@
41
  },
42
  "reward": 1.0,
43
  "reward_breakdown": {
44
- "format_match": 0.2,
45
- "syntax_correctness": 0.2,
46
- "functionality_reward": 0.6
 
 
 
47
  },
48
  "test_results": {
49
  "passed": 4,
@@ -65,9 +68,12 @@
65
  },
66
  "reward": 1.0,
67
  "reward_breakdown": {
68
- "format_match": 0.2,
69
- "syntax_correctness": 0.2,
70
- "functionality_reward": 0.6
 
 
 
71
  },
72
  "test_results": {
73
  "passed": 4,
@@ -89,9 +95,12 @@
89
  },
90
  "reward": 1.0,
91
  "reward_breakdown": {
92
- "format_match": 0.2,
93
- "syntax_correctness": 0.2,
94
- "functionality_reward": 0.6
 
 
 
95
  },
96
  "test_results": {
97
  "passed": 4,
@@ -113,9 +122,12 @@
113
  },
114
  "reward": 1.0,
115
  "reward_breakdown": {
116
- "format_match": 0.2,
117
- "syntax_correctness": 0.2,
118
- "functionality_reward": 0.6
 
 
 
119
  },
120
  "test_results": {
121
  "passed": 4,
@@ -137,9 +149,12 @@
137
  },
138
  "reward": 1.0,
139
  "reward_breakdown": {
140
- "format_match": 0.2,
141
- "syntax_correctness": 0.2,
142
- "functionality_reward": 0.6
 
 
 
143
  },
144
  "test_results": {
145
  "passed": 4,
@@ -161,9 +176,12 @@
161
  },
162
  "reward": 1.0,
163
  "reward_breakdown": {
164
- "format_match": 0.2,
165
- "syntax_correctness": 0.2,
166
- "functionality_reward": 0.6
 
 
 
167
  },
168
  "test_results": {
169
  "passed": 4,
@@ -185,9 +203,12 @@
185
  },
186
  "reward": 1.0,
187
  "reward_breakdown": {
188
- "format_match": 0.2,
189
- "syntax_correctness": 0.2,
190
- "functionality_reward": 0.6
 
 
 
191
  },
192
  "test_results": {
193
  "passed": 4,
@@ -209,9 +230,12 @@
209
  },
210
  "reward": 1.0,
211
  "reward_breakdown": {
212
- "format_match": 0.2,
213
- "syntax_correctness": 0.2,
214
- "functionality_reward": 0.6
 
 
 
215
  },
216
  "test_results": {
217
  "passed": 4,
@@ -233,9 +257,12 @@
233
  },
234
  "reward": 1.0,
235
  "reward_breakdown": {
236
- "format_match": 0.2,
237
- "syntax_correctness": 0.2,
238
- "functionality_reward": 0.6
 
 
 
239
  },
240
  "test_results": {
241
  "passed": 4,
@@ -257,9 +284,12 @@
257
  },
258
  "reward": 1.0,
259
  "reward_breakdown": {
260
- "format_match": 0.2,
261
- "syntax_correctness": 0.2,
262
- "functionality_reward": 0.6
 
 
 
263
  },
264
  "test_results": {
265
  "passed": 4,
@@ -281,9 +311,12 @@
281
  },
282
  "reward": 1.0,
283
  "reward_breakdown": {
284
- "format_match": 0.2,
285
- "syntax_correctness": 0.2,
286
- "functionality_reward": 0.6
 
 
 
287
  },
288
  "test_results": {
289
  "passed": 4,
@@ -305,9 +338,12 @@
305
  },
306
  "reward": 1.0,
307
  "reward_breakdown": {
308
- "format_match": 0.2,
309
- "syntax_correctness": 0.2,
310
- "functionality_reward": 0.6
 
 
 
311
  },
312
  "test_results": {
313
  "passed": 4,
@@ -329,9 +365,12 @@
329
  },
330
  "reward": 1.0,
331
  "reward_breakdown": {
332
- "format_match": 0.2,
333
- "syntax_correctness": 0.2,
334
- "functionality_reward": 0.6
 
 
 
335
  },
336
  "test_results": {
337
  "passed": 4,
@@ -353,9 +392,12 @@
353
  },
354
  "reward": 1.0,
355
  "reward_breakdown": {
356
- "format_match": 0.2,
357
- "syntax_correctness": 0.2,
358
- "functionality_reward": 0.6
 
 
 
359
  },
360
  "test_results": {
361
  "passed": 4,
@@ -377,9 +419,12 @@
377
  },
378
  "reward": 1.0,
379
  "reward_breakdown": {
380
- "format_match": 0.2,
381
- "syntax_correctness": 0.2,
382
- "functionality_reward": 0.6
 
 
 
383
  },
384
  "test_results": {
385
  "passed": 4,
@@ -401,9 +446,12 @@
401
  },
402
  "reward": 1.0,
403
  "reward_breakdown": {
404
- "format_match": 0.2,
405
- "syntax_correctness": 0.2,
406
- "functionality_reward": 0.6
 
 
 
407
  },
408
  "test_results": {
409
  "passed": 4,
@@ -425,9 +473,12 @@
425
  },
426
  "reward": 1.0,
427
  "reward_breakdown": {
428
- "format_match": 0.2,
429
- "syntax_correctness": 0.2,
430
- "functionality_reward": 0.6
 
 
 
431
  },
432
  "test_results": {
433
  "passed": 4,
@@ -449,9 +500,12 @@
449
  },
450
  "reward": 1.0,
451
  "reward_breakdown": {
452
- "format_match": 0.2,
453
- "syntax_correctness": 0.2,
454
- "functionality_reward": 0.6
 
 
 
455
  },
456
  "test_results": {
457
  "passed": 4,
@@ -473,9 +527,12 @@
473
  },
474
  "reward": 1.0,
475
  "reward_breakdown": {
476
- "format_match": 0.2,
477
- "syntax_correctness": 0.2,
478
- "functionality_reward": 0.6
 
 
 
479
  },
480
  "test_results": {
481
  "passed": 4,
@@ -497,9 +554,12 @@
497
  },
498
  "reward": 1.0,
499
  "reward_breakdown": {
500
- "format_match": 0.2,
501
- "syntax_correctness": 0.2,
502
- "functionality_reward": 0.6
 
 
 
503
  },
504
  "test_results": {
505
  "passed": 4,
@@ -521,9 +581,12 @@
521
  },
522
  "reward": 1.0,
523
  "reward_breakdown": {
524
- "format_match": 0.2,
525
- "syntax_correctness": 0.2,
526
- "functionality_reward": 0.6
 
 
 
527
  },
528
  "test_results": {
529
  "passed": 4,
@@ -545,9 +608,12 @@
545
  },
546
  "reward": 1.0,
547
  "reward_breakdown": {
548
- "format_match": 0.2,
549
- "syntax_correctness": 0.2,
550
- "functionality_reward": 0.6
 
 
 
551
  },
552
  "test_results": {
553
  "passed": 4,
@@ -569,9 +635,12 @@
569
  },
570
  "reward": 1.0,
571
  "reward_breakdown": {
572
- "format_match": 0.2,
573
- "syntax_correctness": 0.2,
574
- "functionality_reward": 0.6
 
 
 
575
  },
576
  "test_results": {
577
  "passed": 4,
@@ -593,9 +662,12 @@
593
  },
594
  "reward": 1.0,
595
  "reward_breakdown": {
596
- "format_match": 0.2,
597
- "syntax_correctness": 0.2,
598
- "functionality_reward": 0.6
 
 
 
599
  },
600
  "test_results": {
601
  "passed": 4,
@@ -617,9 +689,12 @@
617
  },
618
  "reward": 1.0,
619
  "reward_breakdown": {
620
- "format_match": 0.2,
621
- "syntax_correctness": 0.2,
622
- "functionality_reward": 0.6
 
 
 
623
  },
624
  "test_results": {
625
  "passed": 4,
@@ -641,9 +716,12 @@
641
  },
642
  "reward": 1.0,
643
  "reward_breakdown": {
644
- "format_match": 0.2,
645
- "syntax_correctness": 0.2,
646
- "functionality_reward": 0.6
 
 
 
647
  },
648
  "test_results": {
649
  "passed": 4,
@@ -665,9 +743,12 @@
665
  },
666
  "reward": 1.0,
667
  "reward_breakdown": {
668
- "format_match": 0.2,
669
- "syntax_correctness": 0.2,
670
- "functionality_reward": 0.6
 
 
 
671
  },
672
  "test_results": {
673
  "passed": 4,
@@ -689,9 +770,12 @@
689
  },
690
  "reward": 1.0,
691
  "reward_breakdown": {
692
- "format_match": 0.2,
693
- "syntax_correctness": 0.2,
694
- "functionality_reward": 0.6
 
 
 
695
  },
696
  "test_results": {
697
  "passed": 4,
@@ -713,9 +797,12 @@
713
  },
714
  "reward": 1.0,
715
  "reward_breakdown": {
716
- "format_match": 0.2,
717
- "syntax_correctness": 0.2,
718
- "functionality_reward": 0.6
 
 
 
719
  },
720
  "test_results": {
721
  "passed": 4,
@@ -737,9 +824,12 @@
737
  },
738
  "reward": 1.0,
739
  "reward_breakdown": {
740
- "format_match": 0.2,
741
- "syntax_correctness": 0.2,
742
- "functionality_reward": 0.6
 
 
 
743
  },
744
  "test_results": {
745
  "passed": 4,
@@ -761,9 +851,12 @@
761
  },
762
  "reward": 1.0,
763
  "reward_breakdown": {
764
- "format_match": 0.2,
765
- "syntax_correctness": 0.2,
766
- "functionality_reward": 0.6
 
 
 
767
  },
768
  "test_results": {
769
  "passed": 4,
@@ -785,9 +878,12 @@
785
  },
786
  "reward": 1.0,
787
  "reward_breakdown": {
788
- "format_match": 0.2,
789
- "syntax_correctness": 0.2,
790
- "functionality_reward": 0.6
 
 
 
791
  },
792
  "test_results": {
793
  "passed": 4,
@@ -809,9 +905,12 @@
809
  },
810
  "reward": 1.0,
811
  "reward_breakdown": {
812
- "format_match": 0.2,
813
- "syntax_correctness": 0.2,
814
- "functionality_reward": 0.6
 
 
 
815
  },
816
  "test_results": {
817
  "passed": 4,
@@ -833,9 +932,12 @@
833
  },
834
  "reward": 1.0,
835
  "reward_breakdown": {
836
- "format_match": 0.2,
837
- "syntax_correctness": 0.2,
838
- "functionality_reward": 0.6
 
 
 
839
  },
840
  "test_results": {
841
  "passed": 4,
@@ -857,9 +959,12 @@
857
  },
858
  "reward": 1.0,
859
  "reward_breakdown": {
860
- "format_match": 0.2,
861
- "syntax_correctness": 0.2,
862
- "functionality_reward": 0.6
 
 
 
863
  },
864
  "test_results": {
865
  "passed": 4,
@@ -881,9 +986,12 @@
881
  },
882
  "reward": 1.0,
883
  "reward_breakdown": {
884
- "format_match": 0.2,
885
- "syntax_correctness": 0.2,
886
- "functionality_reward": 0.6
 
 
 
887
  },
888
  "test_results": {
889
  "passed": 4,
@@ -905,9 +1013,12 @@
905
  },
906
  "reward": 1.0,
907
  "reward_breakdown": {
908
- "format_match": 0.2,
909
- "syntax_correctness": 0.2,
910
- "functionality_reward": 0.6
 
 
 
911
  },
912
  "test_results": {
913
  "passed": 4,
@@ -929,9 +1040,12 @@
929
  },
930
  "reward": 1.0,
931
  "reward_breakdown": {
932
- "format_match": 0.2,
933
- "syntax_correctness": 0.2,
934
- "functionality_reward": 0.6
 
 
 
935
  },
936
  "test_results": {
937
  "passed": 4,
@@ -953,9 +1067,12 @@
953
  },
954
  "reward": 1.0,
955
  "reward_breakdown": {
956
- "format_match": 0.2,
957
- "syntax_correctness": 0.2,
958
- "functionality_reward": 0.6
 
 
 
959
  },
960
  "test_results": {
961
  "passed": 4,
@@ -977,9 +1094,12 @@
977
  },
978
  "reward": 1.0,
979
  "reward_breakdown": {
980
- "format_match": 0.2,
981
- "syntax_correctness": 0.2,
982
- "functionality_reward": 0.6
 
 
 
983
  },
984
  "test_results": {
985
  "passed": 4,
@@ -1003,9 +1123,12 @@
1003
  },
1004
  "reward": 1.0,
1005
  "reward_breakdown": {
1006
- "format_match": 0.2,
1007
- "syntax_correctness": 0.2,
1008
- "functionality_reward": 0.6
 
 
 
1009
  },
1010
  "test_results": {
1011
  "passed": 3,
@@ -1027,9 +1150,12 @@
1027
  },
1028
  "reward": 1.0,
1029
  "reward_breakdown": {
1030
- "format_match": 0.2,
1031
- "syntax_correctness": 0.2,
1032
- "functionality_reward": 0.6
 
 
 
1033
  },
1034
  "test_results": {
1035
  "passed": 4,
@@ -1051,9 +1177,12 @@
1051
  },
1052
  "reward": -0.5,
1053
  "reward_breakdown": {
1054
- "format_match": 0.2,
1055
- "syntax_correctness": 0.0,
1056
- "functionality_reward": -0.7
 
 
 
1057
  },
1058
  "test_results": {
1059
  "passed": 0,
@@ -1075,9 +1204,12 @@
1075
  },
1076
  "reward": 1.0,
1077
  "reward_breakdown": {
1078
- "format_match": 0.2,
1079
- "syntax_correctness": 0.2,
1080
- "functionality_reward": 0.6
 
 
 
1081
  },
1082
  "test_results": {
1083
  "passed": 4,
@@ -1099,9 +1231,12 @@
1099
  },
1100
  "reward": 1.0,
1101
  "reward_breakdown": {
1102
- "format_match": 0.2,
1103
- "syntax_correctness": 0.2,
1104
- "functionality_reward": 0.6
 
 
 
1105
  },
1106
  "test_results": {
1107
  "passed": 4,
@@ -1123,9 +1258,12 @@
1123
  },
1124
  "reward": 1.0,
1125
  "reward_breakdown": {
1126
- "format_match": 0.2,
1127
- "syntax_correctness": 0.2,
1128
- "functionality_reward": 0.6
 
 
 
1129
  },
1130
  "test_results": {
1131
  "passed": 4,
@@ -1147,9 +1285,12 @@
1147
  },
1148
  "reward": 1.0,
1149
  "reward_breakdown": {
1150
- "format_match": 0.2,
1151
- "syntax_correctness": 0.2,
1152
- "functionality_reward": 0.6
 
 
 
1153
  },
1154
  "test_results": {
1155
  "passed": 4,
@@ -1171,9 +1312,12 @@
1171
  },
1172
  "reward": -0.5,
1173
  "reward_breakdown": {
1174
- "format_match": 0.2,
1175
- "syntax_correctness": 0.0,
1176
- "functionality_reward": -0.7
 
 
 
1177
  },
1178
  "test_results": {
1179
  "passed": 0,
@@ -1195,9 +1339,12 @@
1195
  },
1196
  "reward": 1.0,
1197
  "reward_breakdown": {
1198
- "format_match": 0.2,
1199
- "syntax_correctness": 0.2,
1200
- "functionality_reward": 0.6
 
 
 
1201
  },
1202
  "test_results": {
1203
  "passed": 4,
@@ -1219,9 +1366,12 @@
1219
  },
1220
  "reward": 1.0,
1221
  "reward_breakdown": {
1222
- "format_match": 0.2,
1223
- "syntax_correctness": 0.2,
1224
- "functionality_reward": 0.6
 
 
 
1225
  },
1226
  "test_results": {
1227
  "passed": 4,
@@ -1243,9 +1393,12 @@
1243
  },
1244
  "reward": 1.0,
1245
  "reward_breakdown": {
1246
- "format_match": 0.2,
1247
- "syntax_correctness": 0.2,
1248
- "functionality_reward": 0.6
 
 
 
1249
  },
1250
  "test_results": {
1251
  "passed": 4,
@@ -1267,9 +1420,12 @@
1267
  },
1268
  "reward": 1.0,
1269
  "reward_breakdown": {
1270
- "format_match": 0.2,
1271
- "syntax_correctness": 0.2,
1272
- "functionality_reward": 0.6
 
 
 
1273
  },
1274
  "test_results": {
1275
  "passed": 4,
@@ -1291,9 +1447,12 @@
1291
  },
1292
  "reward": -0.5,
1293
  "reward_breakdown": {
1294
- "format_match": 0.2,
1295
- "syntax_correctness": 0.0,
1296
- "functionality_reward": -0.7
 
 
 
1297
  },
1298
  "test_results": {
1299
  "passed": 0,
@@ -1315,9 +1474,12 @@
1315
  },
1316
  "reward": 1.0,
1317
  "reward_breakdown": {
1318
- "format_match": 0.2,
1319
- "syntax_correctness": 0.2,
1320
- "functionality_reward": 0.6
 
 
 
1321
  },
1322
  "test_results": {
1323
  "passed": 4,
@@ -1339,9 +1501,12 @@
1339
  },
1340
  "reward": 1.0,
1341
  "reward_breakdown": {
1342
- "format_match": 0.2,
1343
- "syntax_correctness": 0.2,
1344
- "functionality_reward": 0.6
 
 
 
1345
  },
1346
  "test_results": {
1347
  "passed": 4,
@@ -1363,9 +1528,12 @@
1363
  },
1364
  "reward": 1.0,
1365
  "reward_breakdown": {
1366
- "format_match": 0.2,
1367
- "syntax_correctness": 0.2,
1368
- "functionality_reward": 0.6
 
 
 
1369
  },
1370
  "test_results": {
1371
  "passed": 4,
@@ -1387,9 +1555,12 @@
1387
  },
1388
  "reward": 1.0,
1389
  "reward_breakdown": {
1390
- "format_match": 0.2,
1391
- "syntax_correctness": 0.2,
1392
- "functionality_reward": 0.6
 
 
 
1393
  },
1394
  "test_results": {
1395
  "passed": 4,
@@ -1411,9 +1582,12 @@
1411
  },
1412
  "reward": -0.5,
1413
  "reward_breakdown": {
1414
- "format_match": 0.2,
1415
- "syntax_correctness": 0.0,
1416
- "functionality_reward": -0.7
 
 
 
1417
  },
1418
  "test_results": {
1419
  "passed": 0,
@@ -1435,9 +1609,12 @@
1435
  },
1436
  "reward": 1.0,
1437
  "reward_breakdown": {
1438
- "format_match": 0.2,
1439
- "syntax_correctness": 0.2,
1440
- "functionality_reward": 0.6
 
 
 
1441
  },
1442
  "test_results": {
1443
  "passed": 4,
@@ -1459,9 +1636,12 @@
1459
  },
1460
  "reward": 1.0,
1461
  "reward_breakdown": {
1462
- "format_match": 0.2,
1463
- "syntax_correctness": 0.2,
1464
- "functionality_reward": 0.6
 
 
 
1465
  },
1466
  "test_results": {
1467
  "passed": 4,
@@ -1483,9 +1663,12 @@
1483
  },
1484
  "reward": 1.0,
1485
  "reward_breakdown": {
1486
- "format_match": 0.2,
1487
- "syntax_correctness": 0.2,
1488
- "functionality_reward": 0.6
 
 
 
1489
  },
1490
  "test_results": {
1491
  "passed": 4,
@@ -1507,9 +1690,12 @@
1507
  },
1508
  "reward": 1.0,
1509
  "reward_breakdown": {
1510
- "format_match": 0.2,
1511
- "syntax_correctness": 0.2,
1512
- "functionality_reward": 0.6
 
 
 
1513
  },
1514
  "test_results": {
1515
  "passed": 4,
@@ -1531,9 +1717,12 @@
1531
  },
1532
  "reward": -0.5,
1533
  "reward_breakdown": {
1534
- "format_match": 0.2,
1535
- "syntax_correctness": 0.0,
1536
- "functionality_reward": -0.7
 
 
 
1537
  },
1538
  "test_results": {
1539
  "passed": 0,
@@ -1555,9 +1744,12 @@
1555
  },
1556
  "reward": 1.0,
1557
  "reward_breakdown": {
1558
- "format_match": 0.2,
1559
- "syntax_correctness": 0.2,
1560
- "functionality_reward": 0.6
 
 
 
1561
  },
1562
  "test_results": {
1563
  "passed": 4,
@@ -1579,9 +1771,12 @@
1579
  },
1580
  "reward": 1.0,
1581
  "reward_breakdown": {
1582
- "format_match": 0.2,
1583
- "syntax_correctness": 0.2,
1584
- "functionality_reward": 0.6
 
 
 
1585
  },
1586
  "test_results": {
1587
  "passed": 4,
@@ -1603,9 +1798,12 @@
1603
  },
1604
  "reward": 1.0,
1605
  "reward_breakdown": {
1606
- "format_match": 0.2,
1607
- "syntax_correctness": 0.2,
1608
- "functionality_reward": 0.6
 
 
 
1609
  },
1610
  "test_results": {
1611
  "passed": 4,
@@ -1627,9 +1825,12 @@
1627
  },
1628
  "reward": 1.0,
1629
  "reward_breakdown": {
1630
- "format_match": 0.2,
1631
- "syntax_correctness": 0.2,
1632
- "functionality_reward": 0.6
 
 
 
1633
  },
1634
  "test_results": {
1635
  "passed": 4,
@@ -1651,9 +1852,12 @@
1651
  },
1652
  "reward": -0.5,
1653
  "reward_breakdown": {
1654
- "format_match": 0.2,
1655
- "syntax_correctness": 0.0,
1656
- "functionality_reward": -0.7
 
 
 
1657
  },
1658
  "test_results": {
1659
  "passed": 0,
@@ -1675,9 +1879,12 @@
1675
  },
1676
  "reward": 1.0,
1677
  "reward_breakdown": {
1678
- "format_match": 0.2,
1679
- "syntax_correctness": 0.2,
1680
- "functionality_reward": 0.6
 
 
 
1681
  },
1682
  "test_results": {
1683
  "passed": 4,
@@ -1699,9 +1906,12 @@
1699
  },
1700
  "reward": -0.5,
1701
  "reward_breakdown": {
1702
- "format_match": 0.2,
1703
- "syntax_correctness": 0.0,
1704
- "functionality_reward": -0.7
 
 
 
1705
  },
1706
  "test_results": {
1707
  "passed": 0,
@@ -1725,9 +1935,12 @@
1725
  },
1726
  "reward": 1.0,
1727
  "reward_breakdown": {
1728
- "format_match": 0.2,
1729
- "syntax_correctness": 0.2,
1730
- "functionality_reward": 0.6
 
 
 
1731
  },
1732
  "test_results": {
1733
  "passed": 4,
@@ -1749,9 +1962,12 @@
1749
  },
1750
  "reward": -0.5,
1751
  "reward_breakdown": {
1752
- "format_match": 0.2,
1753
- "syntax_correctness": 0.0,
1754
- "functionality_reward": -0.7
 
 
 
1755
  },
1756
  "test_results": {
1757
  "passed": 0,
@@ -1773,9 +1989,12 @@
1773
  },
1774
  "reward": 1.0,
1775
  "reward_breakdown": {
1776
- "format_match": 0.2,
1777
- "syntax_correctness": 0.2,
1778
- "functionality_reward": 0.6
 
 
 
1779
  },
1780
  "test_results": {
1781
  "passed": 4,
@@ -1797,9 +2016,12 @@
1797
  },
1798
  "reward": -0.5,
1799
  "reward_breakdown": {
1800
- "format_match": 0.2,
1801
- "syntax_correctness": 0.0,
1802
- "functionality_reward": -0.7
 
 
 
1803
  },
1804
  "test_results": {
1805
  "passed": 0,
@@ -1821,9 +2043,12 @@
1821
  },
1822
  "reward": 1.0,
1823
  "reward_breakdown": {
1824
- "format_match": 0.2,
1825
- "syntax_correctness": 0.2,
1826
- "functionality_reward": 0.6
 
 
 
1827
  },
1828
  "test_results": {
1829
  "passed": 4,
@@ -1845,9 +2070,12 @@
1845
  },
1846
  "reward": -0.5,
1847
  "reward_breakdown": {
1848
- "format_match": 0.2,
1849
- "syntax_correctness": 0.0,
1850
- "functionality_reward": -0.7
 
 
 
1851
  },
1852
  "test_results": {
1853
  "passed": 0,
@@ -1869,9 +2097,12 @@
1869
  },
1870
  "reward": 1.0,
1871
  "reward_breakdown": {
1872
- "format_match": 0.2,
1873
- "syntax_correctness": 0.2,
1874
- "functionality_reward": 0.6
 
 
 
1875
  },
1876
  "test_results": {
1877
  "passed": 4,
@@ -1893,9 +2124,12 @@
1893
  },
1894
  "reward": -0.5,
1895
  "reward_breakdown": {
1896
- "format_match": 0.2,
1897
- "syntax_correctness": 0.0,
1898
- "functionality_reward": -0.7
 
 
 
1899
  },
1900
  "test_results": {
1901
  "passed": 0,
@@ -1917,9 +2151,12 @@
1917
  },
1918
  "reward": 1.0,
1919
  "reward_breakdown": {
1920
- "format_match": 0.2,
1921
- "syntax_correctness": 0.2,
1922
- "functionality_reward": 0.6
 
 
 
1923
  },
1924
  "test_results": {
1925
  "passed": 4,
@@ -1941,9 +2178,12 @@
1941
  },
1942
  "reward": -0.5,
1943
  "reward_breakdown": {
1944
- "format_match": 0.2,
1945
- "syntax_correctness": 0.0,
1946
- "functionality_reward": -0.7
 
 
 
1947
  },
1948
  "test_results": {
1949
  "passed": 0,
@@ -1965,9 +2205,12 @@
1965
  },
1966
  "reward": -0.5,
1967
  "reward_breakdown": {
1968
- "format_match": 0.2,
1969
- "syntax_correctness": 0.0,
1970
- "functionality_reward": -0.7
 
 
 
1971
  },
1972
  "test_results": {
1973
  "passed": 0,
@@ -1989,9 +2232,12 @@
1989
  },
1990
  "reward": -0.5,
1991
  "reward_breakdown": {
1992
- "format_match": 0.2,
1993
- "syntax_correctness": 0.0,
1994
- "functionality_reward": -0.7
 
 
 
1995
  },
1996
  "test_results": {
1997
  "passed": 0,
@@ -2013,9 +2259,12 @@
2013
  },
2014
  "reward": 1.0,
2015
  "reward_breakdown": {
2016
- "format_match": 0.2,
2017
- "syntax_correctness": 0.2,
2018
- "functionality_reward": 0.6
 
 
 
2019
  },
2020
  "test_results": {
2021
  "passed": 4,
@@ -2037,9 +2286,12 @@
2037
  },
2038
  "reward": -0.5,
2039
  "reward_breakdown": {
2040
- "format_match": 0.2,
2041
- "syntax_correctness": 0.0,
2042
- "functionality_reward": -0.7
 
 
 
2043
  },
2044
  "test_results": {
2045
  "passed": 0,
@@ -2061,9 +2313,12 @@
2061
  },
2062
  "reward": 1.0,
2063
  "reward_breakdown": {
2064
- "format_match": 0.2,
2065
- "syntax_correctness": 0.2,
2066
- "functionality_reward": 0.6
 
 
 
2067
  },
2068
  "test_results": {
2069
  "passed": 4,
@@ -2085,9 +2340,12 @@
2085
  },
2086
  "reward": -0.5,
2087
  "reward_breakdown": {
2088
- "format_match": 0.2,
2089
- "syntax_correctness": 0.0,
2090
- "functionality_reward": -0.7
 
 
 
2091
  },
2092
  "test_results": {
2093
  "passed": 0,
@@ -2109,9 +2367,12 @@
2109
  },
2110
  "reward": 1.0,
2111
  "reward_breakdown": {
2112
- "format_match": 0.2,
2113
- "syntax_correctness": 0.2,
2114
- "functionality_reward": 0.6
 
 
 
2115
  },
2116
  "test_results": {
2117
  "passed": 4,
@@ -2133,9 +2394,12 @@
2133
  },
2134
  "reward": -0.5,
2135
  "reward_breakdown": {
2136
- "format_match": 0.2,
2137
- "syntax_correctness": 0.0,
2138
- "functionality_reward": -0.7
 
 
 
2139
  },
2140
  "test_results": {
2141
  "passed": 0,
@@ -2157,9 +2421,12 @@
2157
  },
2158
  "reward": 1.0,
2159
  "reward_breakdown": {
2160
- "format_match": 0.2,
2161
- "syntax_correctness": 0.2,
2162
- "functionality_reward": 0.6
 
 
 
2163
  },
2164
  "test_results": {
2165
  "passed": 4,
@@ -2181,9 +2448,12 @@
2181
  },
2182
  "reward": -0.5,
2183
  "reward_breakdown": {
2184
- "format_match": 0.2,
2185
- "syntax_correctness": 0.0,
2186
- "functionality_reward": -0.7
 
 
 
2187
  },
2188
  "test_results": {
2189
  "passed": 0,
 
41
  },
42
  "reward": 1.0,
43
  "reward_breakdown": {
44
+ "format_compliance": 0.2,
45
+ "hypothesis_quality": 0.2,
46
+ "localization": 0.2,
47
+ "fix_quality": 0.3,
48
+ "semantic_similarity": 0.1,
49
+ "efficiency_potential": 0.0
50
  },
51
  "test_results": {
52
  "passed": 4,
 
68
  },
69
  "reward": 1.0,
70
  "reward_breakdown": {
71
+ "format_compliance": 0.2,
72
+ "hypothesis_quality": 0.2,
73
+ "localization": 0.2,
74
+ "fix_quality": 0.3,
75
+ "semantic_similarity": 0.1,
76
+ "efficiency_potential": 0.0
77
  },
78
  "test_results": {
79
  "passed": 4,
 
95
  },
96
  "reward": 1.0,
97
  "reward_breakdown": {
98
+ "format_compliance": 0.2,
99
+ "hypothesis_quality": 0.2,
100
+ "localization": 0.2,
101
+ "fix_quality": 0.3,
102
+ "semantic_similarity": 0.1,
103
+ "efficiency_potential": 0.0
104
  },
105
  "test_results": {
106
  "passed": 4,
 
122
  },
123
  "reward": 1.0,
124
  "reward_breakdown": {
125
+ "format_compliance": 0.2,
126
+ "hypothesis_quality": 0.2,
127
+ "localization": 0.2,
128
+ "fix_quality": 0.3,
129
+ "semantic_similarity": 0.1,
130
+ "efficiency_potential": 0.0
131
  },
132
  "test_results": {
133
  "passed": 4,
 
149
  },
150
  "reward": 1.0,
151
  "reward_breakdown": {
152
+ "format_compliance": 0.2,
153
+ "hypothesis_quality": 0.2,
154
+ "localization": 0.2,
155
+ "fix_quality": 0.3,
156
+ "semantic_similarity": 0.1,
157
+ "efficiency_potential": 0.0
158
  },
159
  "test_results": {
160
  "passed": 4,
 
176
  },
177
  "reward": 1.0,
178
  "reward_breakdown": {
179
+ "format_compliance": 0.2,
180
+ "hypothesis_quality": 0.2,
181
+ "localization": 0.2,
182
+ "fix_quality": 0.3,
183
+ "semantic_similarity": 0.1,
184
+ "efficiency_potential": 0.0
185
  },
186
  "test_results": {
187
  "passed": 4,
 
203
  },
204
  "reward": 1.0,
205
  "reward_breakdown": {
206
+ "format_compliance": 0.2,
207
+ "hypothesis_quality": 0.2,
208
+ "localization": 0.2,
209
+ "fix_quality": 0.3,
210
+ "semantic_similarity": 0.1,
211
+ "efficiency_potential": 0.0
212
  },
213
  "test_results": {
214
  "passed": 4,
 
230
  },
231
  "reward": 1.0,
232
  "reward_breakdown": {
233
+ "format_compliance": 0.2,
234
+ "hypothesis_quality": 0.2,
235
+ "localization": 0.2,
236
+ "fix_quality": 0.3,
237
+ "semantic_similarity": 0.1,
238
+ "efficiency_potential": 0.0
239
  },
240
  "test_results": {
241
  "passed": 4,
 
257
  },
258
  "reward": 1.0,
259
  "reward_breakdown": {
260
+ "format_compliance": 0.2,
261
+ "hypothesis_quality": 0.2,
262
+ "localization": 0.2,
263
+ "fix_quality": 0.3,
264
+ "semantic_similarity": 0.1,
265
+ "efficiency_potential": 0.0
266
  },
267
  "test_results": {
268
  "passed": 4,
 
284
  },
285
  "reward": 1.0,
286
  "reward_breakdown": {
287
+ "format_compliance": 0.2,
288
+ "hypothesis_quality": 0.2,
289
+ "localization": 0.2,
290
+ "fix_quality": 0.3,
291
+ "semantic_similarity": 0.1,
292
+ "efficiency_potential": 0.0
293
  },
294
  "test_results": {
295
  "passed": 4,
 
311
  },
312
  "reward": 1.0,
313
  "reward_breakdown": {
314
+ "format_compliance": 0.2,
315
+ "hypothesis_quality": 0.2,
316
+ "localization": 0.2,
317
+ "fix_quality": 0.3,
318
+ "semantic_similarity": 0.1,
319
+ "efficiency_potential": 0.0
320
  },
321
  "test_results": {
322
  "passed": 4,
 
338
  },
339
  "reward": 1.0,
340
  "reward_breakdown": {
341
+ "format_compliance": 0.2,
342
+ "hypothesis_quality": 0.2,
343
+ "localization": 0.2,
344
+ "fix_quality": 0.3,
345
+ "semantic_similarity": 0.1,
346
+ "efficiency_potential": 0.0
347
  },
348
  "test_results": {
349
  "passed": 4,
 
365
  },
366
  "reward": 1.0,
367
  "reward_breakdown": {
368
+ "format_compliance": 0.2,
369
+ "hypothesis_quality": 0.2,
370
+ "localization": 0.2,
371
+ "fix_quality": 0.3,
372
+ "semantic_similarity": 0.1,
373
+ "efficiency_potential": 0.0
374
  },
375
  "test_results": {
376
  "passed": 4,
 
392
  },
393
  "reward": 1.0,
394
  "reward_breakdown": {
395
+ "format_compliance": 0.2,
396
+ "hypothesis_quality": 0.2,
397
+ "localization": 0.2,
398
+ "fix_quality": 0.3,
399
+ "semantic_similarity": 0.1,
400
+ "efficiency_potential": 0.0
401
  },
402
  "test_results": {
403
  "passed": 4,
 
419
  },
420
  "reward": 1.0,
421
  "reward_breakdown": {
422
+ "format_compliance": 0.2,
423
+ "hypothesis_quality": 0.2,
424
+ "localization": 0.2,
425
+ "fix_quality": 0.3,
426
+ "semantic_similarity": 0.1,
427
+ "efficiency_potential": 0.0
428
  },
429
  "test_results": {
430
  "passed": 4,
 
446
  },
447
  "reward": 1.0,
448
  "reward_breakdown": {
449
+ "format_compliance": 0.2,
450
+ "hypothesis_quality": 0.2,
451
+ "localization": 0.2,
452
+ "fix_quality": 0.3,
453
+ "semantic_similarity": 0.1,
454
+ "efficiency_potential": 0.0
455
  },
456
  "test_results": {
457
  "passed": 4,
 
473
  },
474
  "reward": 1.0,
475
  "reward_breakdown": {
476
+ "format_compliance": 0.2,
477
+ "hypothesis_quality": 0.2,
478
+ "localization": 0.2,
479
+ "fix_quality": 0.3,
480
+ "semantic_similarity": 0.1,
481
+ "efficiency_potential": 0.0
482
  },
483
  "test_results": {
484
  "passed": 4,
 
500
  },
501
  "reward": 1.0,
502
  "reward_breakdown": {
503
+ "format_compliance": 0.2,
504
+ "hypothesis_quality": 0.2,
505
+ "localization": 0.2,
506
+ "fix_quality": 0.3,
507
+ "semantic_similarity": 0.1,
508
+ "efficiency_potential": 0.0
509
  },
510
  "test_results": {
511
  "passed": 4,
 
527
  },
528
  "reward": 1.0,
529
  "reward_breakdown": {
530
+ "format_compliance": 0.2,
531
+ "hypothesis_quality": 0.2,
532
+ "localization": 0.2,
533
+ "fix_quality": 0.3,
534
+ "semantic_similarity": 0.1,
535
+ "efficiency_potential": 0.0
536
  },
537
  "test_results": {
538
  "passed": 4,
 
554
  },
555
  "reward": 1.0,
556
  "reward_breakdown": {
557
+ "format_compliance": 0.2,
558
+ "hypothesis_quality": 0.2,
559
+ "localization": 0.2,
560
+ "fix_quality": 0.3,
561
+ "semantic_similarity": 0.1,
562
+ "efficiency_potential": 0.0
563
  },
564
  "test_results": {
565
  "passed": 4,
 
581
  },
582
  "reward": 1.0,
583
  "reward_breakdown": {
584
+ "format_compliance": 0.2,
585
+ "hypothesis_quality": 0.2,
586
+ "localization": 0.2,
587
+ "fix_quality": 0.3,
588
+ "semantic_similarity": 0.1,
589
+ "efficiency_potential": 0.0
590
  },
591
  "test_results": {
592
  "passed": 4,
 
608
  },
609
  "reward": 1.0,
610
  "reward_breakdown": {
611
+ "format_compliance": 0.2,
612
+ "hypothesis_quality": 0.2,
613
+ "localization": 0.2,
614
+ "fix_quality": 0.3,
615
+ "semantic_similarity": 0.1,
616
+ "efficiency_potential": 0.0
617
  },
618
  "test_results": {
619
  "passed": 4,
 
635
  },
636
  "reward": 1.0,
637
  "reward_breakdown": {
638
+ "format_compliance": 0.2,
639
+ "hypothesis_quality": 0.2,
640
+ "localization": 0.2,
641
+ "fix_quality": 0.3,
642
+ "semantic_similarity": 0.1,
643
+ "efficiency_potential": 0.0
644
  },
645
  "test_results": {
646
  "passed": 4,
 
662
  },
663
  "reward": 1.0,
664
  "reward_breakdown": {
665
+ "format_compliance": 0.2,
666
+ "hypothesis_quality": 0.2,
667
+ "localization": 0.2,
668
+ "fix_quality": 0.3,
669
+ "semantic_similarity": 0.1,
670
+ "efficiency_potential": 0.0
671
  },
672
  "test_results": {
673
  "passed": 4,
 
689
  },
690
  "reward": 1.0,
691
  "reward_breakdown": {
692
+ "format_compliance": 0.2,
693
+ "hypothesis_quality": 0.2,
694
+ "localization": 0.2,
695
+ "fix_quality": 0.3,
696
+ "semantic_similarity": 0.1,
697
+ "efficiency_potential": 0.0
698
  },
699
  "test_results": {
700
  "passed": 4,
 
716
  },
717
  "reward": 1.0,
718
  "reward_breakdown": {
719
+ "format_compliance": 0.2,
720
+ "hypothesis_quality": 0.2,
721
+ "localization": 0.2,
722
+ "fix_quality": 0.3,
723
+ "semantic_similarity": 0.1,
724
+ "efficiency_potential": 0.0
725
  },
726
  "test_results": {
727
  "passed": 4,
 
743
  },
744
  "reward": 1.0,
745
  "reward_breakdown": {
746
+ "format_compliance": 0.2,
747
+ "hypothesis_quality": 0.2,
748
+ "localization": 0.2,
749
+ "fix_quality": 0.3,
750
+ "semantic_similarity": 0.1,
751
+ "efficiency_potential": 0.0
752
  },
753
  "test_results": {
754
  "passed": 4,
 
770
  },
771
  "reward": 1.0,
772
  "reward_breakdown": {
773
+ "format_compliance": 0.2,
774
+ "hypothesis_quality": 0.2,
775
+ "localization": 0.2,
776
+ "fix_quality": 0.3,
777
+ "semantic_similarity": 0.1,
778
+ "efficiency_potential": 0.0
779
  },
780
  "test_results": {
781
  "passed": 4,
 
797
  },
798
  "reward": 1.0,
799
  "reward_breakdown": {
800
+ "format_compliance": 0.2,
801
+ "hypothesis_quality": 0.2,
802
+ "localization": 0.2,
803
+ "fix_quality": 0.3,
804
+ "semantic_similarity": 0.1,
805
+ "efficiency_potential": 0.0
806
  },
807
  "test_results": {
808
  "passed": 4,
 
824
  },
825
  "reward": 1.0,
826
  "reward_breakdown": {
827
+ "format_compliance": 0.2,
828
+ "hypothesis_quality": 0.2,
829
+ "localization": 0.2,
830
+ "fix_quality": 0.3,
831
+ "semantic_similarity": 0.1,
832
+ "efficiency_potential": 0.0
833
  },
834
  "test_results": {
835
  "passed": 4,
 
851
  },
852
  "reward": 1.0,
853
  "reward_breakdown": {
854
+ "format_compliance": 0.2,
855
+ "hypothesis_quality": 0.2,
856
+ "localization": 0.2,
857
+ "fix_quality": 0.3,
858
+ "semantic_similarity": 0.1,
859
+ "efficiency_potential": 0.0
860
  },
861
  "test_results": {
862
  "passed": 4,
 
878
  },
879
  "reward": 1.0,
880
  "reward_breakdown": {
881
+ "format_compliance": 0.2,
882
+ "hypothesis_quality": 0.2,
883
+ "localization": 0.2,
884
+ "fix_quality": 0.3,
885
+ "semantic_similarity": 0.1,
886
+ "efficiency_potential": 0.0
887
  },
888
  "test_results": {
889
  "passed": 4,
 
905
  },
906
  "reward": 1.0,
907
  "reward_breakdown": {
908
+ "format_compliance": 0.2,
909
+ "hypothesis_quality": 0.2,
910
+ "localization": 0.2,
911
+ "fix_quality": 0.3,
912
+ "semantic_similarity": 0.1,
913
+ "efficiency_potential": 0.0
914
  },
915
  "test_results": {
916
  "passed": 4,
 
932
  },
933
  "reward": 1.0,
934
  "reward_breakdown": {
935
+ "format_compliance": 0.2,
936
+ "hypothesis_quality": 0.2,
937
+ "localization": 0.2,
938
+ "fix_quality": 0.3,
939
+ "semantic_similarity": 0.1,
940
+ "efficiency_potential": 0.0
941
  },
942
  "test_results": {
943
  "passed": 4,
 
959
  },
960
  "reward": 1.0,
961
  "reward_breakdown": {
962
+ "format_compliance": 0.2,
963
+ "hypothesis_quality": 0.2,
964
+ "localization": 0.2,
965
+ "fix_quality": 0.3,
966
+ "semantic_similarity": 0.1,
967
+ "efficiency_potential": 0.0
968
  },
969
  "test_results": {
970
  "passed": 4,
 
986
  },
987
  "reward": 1.0,
988
  "reward_breakdown": {
989
+ "format_compliance": 0.2,
990
+ "hypothesis_quality": 0.2,
991
+ "localization": 0.2,
992
+ "fix_quality": 0.3,
993
+ "semantic_similarity": 0.1,
994
+ "efficiency_potential": 0.0
995
  },
996
  "test_results": {
997
  "passed": 4,
 
1013
  },
1014
  "reward": 1.0,
1015
  "reward_breakdown": {
1016
+ "format_compliance": 0.2,
1017
+ "hypothesis_quality": 0.2,
1018
+ "localization": 0.2,
1019
+ "fix_quality": 0.3,
1020
+ "semantic_similarity": 0.1,
1021
+ "efficiency_potential": 0.0
1022
  },
1023
  "test_results": {
1024
  "passed": 4,
 
1040
  },
1041
  "reward": 1.0,
1042
  "reward_breakdown": {
1043
+ "format_compliance": 0.2,
1044
+ "hypothesis_quality": 0.2,
1045
+ "localization": 0.2,
1046
+ "fix_quality": 0.3,
1047
+ "semantic_similarity": 0.1,
1048
+ "efficiency_potential": 0.0
1049
  },
1050
  "test_results": {
1051
  "passed": 4,
 
1067
  },
1068
  "reward": 1.0,
1069
  "reward_breakdown": {
1070
+ "format_compliance": 0.2,
1071
+ "hypothesis_quality": 0.2,
1072
+ "localization": 0.2,
1073
+ "fix_quality": 0.3,
1074
+ "semantic_similarity": 0.1,
1075
+ "efficiency_potential": 0.0
1076
  },
1077
  "test_results": {
1078
  "passed": 4,
 
1094
  },
1095
  "reward": 1.0,
1096
  "reward_breakdown": {
1097
+ "format_compliance": 0.2,
1098
+ "hypothesis_quality": 0.2,
1099
+ "localization": 0.2,
1100
+ "fix_quality": 0.3,
1101
+ "semantic_similarity": 0.1,
1102
+ "efficiency_potential": 0.0
1103
  },
1104
  "test_results": {
1105
  "passed": 4,
 
1123
  },
1124
  "reward": 1.0,
1125
  "reward_breakdown": {
1126
+ "format_compliance": 0.2,
1127
+ "hypothesis_quality": 0.2,
1128
+ "localization": 0.2,
1129
+ "fix_quality": 0.3,
1130
+ "semantic_similarity": 0.1,
1131
+ "efficiency_potential": 0.0
1132
  },
1133
  "test_results": {
1134
  "passed": 3,
 
1150
  },
1151
  "reward": 1.0,
1152
  "reward_breakdown": {
1153
+ "format_compliance": 0.2,
1154
+ "hypothesis_quality": 0.2,
1155
+ "localization": 0.2,
1156
+ "fix_quality": 0.3,
1157
+ "semantic_similarity": 0.1,
1158
+ "efficiency_potential": 0.0
1159
  },
1160
  "test_results": {
1161
  "passed": 4,
 
1177
  },
1178
  "reward": -0.5,
1179
  "reward_breakdown": {
1180
+ "format_compliance": 0.2,
1181
+ "hypothesis_quality": 0.0,
1182
+ "localization": 0.0,
1183
+ "fix_quality": 0.0,
1184
+ "semantic_similarity": 0.0,
1185
+ "efficiency_potential": 0.0
1186
  },
1187
  "test_results": {
1188
  "passed": 0,
 
1204
  },
1205
  "reward": 1.0,
1206
  "reward_breakdown": {
1207
+ "format_compliance": 0.2,
1208
+ "hypothesis_quality": 0.2,
1209
+ "localization": 0.2,
1210
+ "fix_quality": 0.3,
1211
+ "semantic_similarity": 0.1,
1212
+ "efficiency_potential": 0.0
1213
  },
1214
  "test_results": {
1215
  "passed": 4,
 
1231
  },
1232
  "reward": 1.0,
1233
  "reward_breakdown": {
1234
+ "format_compliance": 0.2,
1235
+ "hypothesis_quality": 0.2,
1236
+ "localization": 0.2,
1237
+ "fix_quality": 0.3,
1238
+ "semantic_similarity": 0.1,
1239
+ "efficiency_potential": 0.0
1240
  },
1241
  "test_results": {
1242
  "passed": 4,
 
1258
  },
1259
  "reward": 1.0,
1260
  "reward_breakdown": {
1261
+ "format_compliance": 0.2,
1262
+ "hypothesis_quality": 0.2,
1263
+ "localization": 0.2,
1264
+ "fix_quality": 0.3,
1265
+ "semantic_similarity": 0.1,
1266
+ "efficiency_potential": 0.0
1267
  },
1268
  "test_results": {
1269
  "passed": 4,
 
1285
  },
1286
  "reward": 1.0,
1287
  "reward_breakdown": {
1288
+ "format_compliance": 0.2,
1289
+ "hypothesis_quality": 0.2,
1290
+ "localization": 0.2,
1291
+ "fix_quality": 0.3,
1292
+ "semantic_similarity": 0.1,
1293
+ "efficiency_potential": 0.0
1294
  },
1295
  "test_results": {
1296
  "passed": 4,
 
1312
  },
1313
  "reward": -0.5,
1314
  "reward_breakdown": {
1315
+ "format_compliance": 0.2,
1316
+ "hypothesis_quality": 0.0,
1317
+ "localization": 0.0,
1318
+ "fix_quality": 0.0,
1319
+ "semantic_similarity": 0.0,
1320
+ "efficiency_potential": 0.0
1321
  },
1322
  "test_results": {
1323
  "passed": 0,
 
1339
  },
1340
  "reward": 1.0,
1341
  "reward_breakdown": {
1342
+ "format_compliance": 0.2,
1343
+ "hypothesis_quality": 0.2,
1344
+ "localization": 0.2,
1345
+ "fix_quality": 0.3,
1346
+ "semantic_similarity": 0.1,
1347
+ "efficiency_potential": 0.0
1348
  },
1349
  "test_results": {
1350
  "passed": 4,
 
1366
  },
1367
  "reward": 1.0,
1368
  "reward_breakdown": {
1369
+ "format_compliance": 0.2,
1370
+ "hypothesis_quality": 0.2,
1371
+ "localization": 0.2,
1372
+ "fix_quality": 0.3,
1373
+ "semantic_similarity": 0.1,
1374
+ "efficiency_potential": 0.0
1375
  },
1376
  "test_results": {
1377
  "passed": 4,
 
1393
  },
1394
  "reward": 1.0,
1395
  "reward_breakdown": {
1396
+ "format_compliance": 0.2,
1397
+ "hypothesis_quality": 0.2,
1398
+ "localization": 0.2,
1399
+ "fix_quality": 0.3,
1400
+ "semantic_similarity": 0.1,
1401
+ "efficiency_potential": 0.0
1402
  },
1403
  "test_results": {
1404
  "passed": 4,
 
1420
  },
1421
  "reward": 1.0,
1422
  "reward_breakdown": {
1423
+ "format_compliance": 0.2,
1424
+ "hypothesis_quality": 0.2,
1425
+ "localization": 0.2,
1426
+ "fix_quality": 0.3,
1427
+ "semantic_similarity": 0.1,
1428
+ "efficiency_potential": 0.0
1429
  },
1430
  "test_results": {
1431
  "passed": 4,
 
1447
  },
1448
  "reward": -0.5,
1449
  "reward_breakdown": {
1450
+ "format_compliance": 0.2,
1451
+ "hypothesis_quality": 0.0,
1452
+ "localization": 0.0,
1453
+ "fix_quality": 0.0,
1454
+ "semantic_similarity": 0.0,
1455
+ "efficiency_potential": 0.0
1456
  },
1457
  "test_results": {
1458
  "passed": 0,
 
1474
  },
1475
  "reward": 1.0,
1476
  "reward_breakdown": {
1477
+ "format_compliance": 0.2,
1478
+ "hypothesis_quality": 0.2,
1479
+ "localization": 0.2,
1480
+ "fix_quality": 0.3,
1481
+ "semantic_similarity": 0.1,
1482
+ "efficiency_potential": 0.0
1483
  },
1484
  "test_results": {
1485
  "passed": 4,
 
1501
  },
1502
  "reward": 1.0,
1503
  "reward_breakdown": {
1504
+ "format_compliance": 0.2,
1505
+ "hypothesis_quality": 0.2,
1506
+ "localization": 0.2,
1507
+ "fix_quality": 0.3,
1508
+ "semantic_similarity": 0.1,
1509
+ "efficiency_potential": 0.0
1510
  },
1511
  "test_results": {
1512
  "passed": 4,
 
1528
  },
1529
  "reward": 1.0,
1530
  "reward_breakdown": {
1531
+ "format_compliance": 0.2,
1532
+ "hypothesis_quality": 0.2,
1533
+ "localization": 0.2,
1534
+ "fix_quality": 0.3,
1535
+ "semantic_similarity": 0.1,
1536
+ "efficiency_potential": 0.0
1537
  },
1538
  "test_results": {
1539
  "passed": 4,
 
1555
  },
1556
  "reward": 1.0,
1557
  "reward_breakdown": {
1558
+ "format_compliance": 0.2,
1559
+ "hypothesis_quality": 0.2,
1560
+ "localization": 0.2,
1561
+ "fix_quality": 0.3,
1562
+ "semantic_similarity": 0.1,
1563
+ "efficiency_potential": 0.0
1564
  },
1565
  "test_results": {
1566
  "passed": 4,
 
1582
  },
1583
  "reward": -0.5,
1584
  "reward_breakdown": {
1585
+ "format_compliance": 0.2,
1586
+ "hypothesis_quality": 0.0,
1587
+ "localization": 0.0,
1588
+ "fix_quality": 0.0,
1589
+ "semantic_similarity": 0.0,
1590
+ "efficiency_potential": 0.0
1591
  },
1592
  "test_results": {
1593
  "passed": 0,
 
1609
  },
1610
  "reward": 1.0,
1611
  "reward_breakdown": {
1612
+ "format_compliance": 0.2,
1613
+ "hypothesis_quality": 0.2,
1614
+ "localization": 0.2,
1615
+ "fix_quality": 0.3,
1616
+ "semantic_similarity": 0.1,
1617
+ "efficiency_potential": 0.0
1618
  },
1619
  "test_results": {
1620
  "passed": 4,
 
1636
  },
1637
  "reward": 1.0,
1638
  "reward_breakdown": {
1639
+ "format_compliance": 0.2,
1640
+ "hypothesis_quality": 0.2,
1641
+ "localization": 0.2,
1642
+ "fix_quality": 0.3,
1643
+ "semantic_similarity": 0.1,
1644
+ "efficiency_potential": 0.0
1645
  },
1646
  "test_results": {
1647
  "passed": 4,
 
1663
  },
1664
  "reward": 1.0,
1665
  "reward_breakdown": {
1666
+ "format_compliance": 0.2,
1667
+ "hypothesis_quality": 0.2,
1668
+ "localization": 0.2,
1669
+ "fix_quality": 0.3,
1670
+ "semantic_similarity": 0.1,
1671
+ "efficiency_potential": 0.0
1672
  },
1673
  "test_results": {
1674
  "passed": 4,
 
1690
  },
1691
  "reward": 1.0,
1692
  "reward_breakdown": {
1693
+ "format_compliance": 0.2,
1694
+ "hypothesis_quality": 0.2,
1695
+ "localization": 0.2,
1696
+ "fix_quality": 0.3,
1697
+ "semantic_similarity": 0.1,
1698
+ "efficiency_potential": 0.0
1699
  },
1700
  "test_results": {
1701
  "passed": 4,
 
1717
  },
1718
  "reward": -0.5,
1719
  "reward_breakdown": {
1720
+ "format_compliance": 0.2,
1721
+ "hypothesis_quality": 0.0,
1722
+ "localization": 0.0,
1723
+ "fix_quality": 0.0,
1724
+ "semantic_similarity": 0.0,
1725
+ "efficiency_potential": 0.0
1726
  },
1727
  "test_results": {
1728
  "passed": 0,
 
1744
  },
1745
  "reward": 1.0,
1746
  "reward_breakdown": {
1747
+ "format_compliance": 0.2,
1748
+ "hypothesis_quality": 0.2,
1749
+ "localization": 0.2,
1750
+ "fix_quality": 0.3,
1751
+ "semantic_similarity": 0.1,
1752
+ "efficiency_potential": 0.0
1753
  },
1754
  "test_results": {
1755
  "passed": 4,
 
1771
  },
1772
  "reward": 1.0,
1773
  "reward_breakdown": {
1774
+ "format_compliance": 0.2,
1775
+ "hypothesis_quality": 0.2,
1776
+ "localization": 0.2,
1777
+ "fix_quality": 0.3,
1778
+ "semantic_similarity": 0.1,
1779
+ "efficiency_potential": 0.0
1780
  },
1781
  "test_results": {
1782
  "passed": 4,
 
1798
  },
1799
  "reward": 1.0,
1800
  "reward_breakdown": {
1801
+ "format_compliance": 0.2,
1802
+ "hypothesis_quality": 0.2,
1803
+ "localization": 0.2,
1804
+ "fix_quality": 0.3,
1805
+ "semantic_similarity": 0.1,
1806
+ "efficiency_potential": 0.0
1807
  },
1808
  "test_results": {
1809
  "passed": 4,
 
1825
  },
1826
  "reward": 1.0,
1827
  "reward_breakdown": {
1828
+ "format_compliance": 0.2,
1829
+ "hypothesis_quality": 0.2,
1830
+ "localization": 0.2,
1831
+ "fix_quality": 0.3,
1832
+ "semantic_similarity": 0.1,
1833
+ "efficiency_potential": 0.0
1834
  },
1835
  "test_results": {
1836
  "passed": 4,
 
1852
  },
1853
  "reward": -0.5,
1854
  "reward_breakdown": {
1855
+ "format_compliance": 0.2,
1856
+ "hypothesis_quality": 0.0,
1857
+ "localization": 0.0,
1858
+ "fix_quality": 0.0,
1859
+ "semantic_similarity": 0.0,
1860
+ "efficiency_potential": 0.0
1861
  },
1862
  "test_results": {
1863
  "passed": 0,
 
1879
  },
1880
  "reward": 1.0,
1881
  "reward_breakdown": {
1882
+ "format_compliance": 0.2,
1883
+ "hypothesis_quality": 0.2,
1884
+ "localization": 0.2,
1885
+ "fix_quality": 0.3,
1886
+ "semantic_similarity": 0.1,
1887
+ "efficiency_potential": 0.0
1888
  },
1889
  "test_results": {
1890
  "passed": 4,
 
1906
  },
1907
  "reward": -0.5,
1908
  "reward_breakdown": {
1909
+ "format_compliance": 0.2,
1910
+ "hypothesis_quality": 0.0,
1911
+ "localization": 0.0,
1912
+ "fix_quality": 0.0,
1913
+ "semantic_similarity": 0.0,
1914
+ "efficiency_potential": 0.0
1915
  },
1916
  "test_results": {
1917
  "passed": 0,
 
1935
  },
1936
  "reward": 1.0,
1937
  "reward_breakdown": {
1938
+ "format_compliance": 0.2,
1939
+ "hypothesis_quality": 0.2,
1940
+ "localization": 0.2,
1941
+ "fix_quality": 0.3,
1942
+ "semantic_similarity": 0.1,
1943
+ "efficiency_potential": 0.0
1944
  },
1945
  "test_results": {
1946
  "passed": 4,
 
1962
  },
1963
  "reward": -0.5,
1964
  "reward_breakdown": {
1965
+ "format_compliance": 0.2,
1966
+ "hypothesis_quality": 0.0,
1967
+ "localization": 0.0,
1968
+ "fix_quality": 0.0,
1969
+ "semantic_similarity": 0.0,
1970
+ "efficiency_potential": 0.0
1971
  },
1972
  "test_results": {
1973
  "passed": 0,
 
1989
  },
1990
  "reward": 1.0,
1991
  "reward_breakdown": {
1992
+ "format_compliance": 0.2,
1993
+ "hypothesis_quality": 0.2,
1994
+ "localization": 0.2,
1995
+ "fix_quality": 0.3,
1996
+ "semantic_similarity": 0.1,
1997
+ "efficiency_potential": 0.0
1998
  },
1999
  "test_results": {
2000
  "passed": 4,
 
2016
  },
2017
  "reward": -0.5,
2018
  "reward_breakdown": {
2019
+ "format_compliance": 0.2,
2020
+ "hypothesis_quality": 0.0,
2021
+ "localization": 0.0,
2022
+ "fix_quality": 0.0,
2023
+ "semantic_similarity": 0.0,
2024
+ "efficiency_potential": 0.0
2025
  },
2026
  "test_results": {
2027
  "passed": 0,
 
2043
  },
2044
  "reward": 1.0,
2045
  "reward_breakdown": {
2046
+ "format_compliance": 0.2,
2047
+ "hypothesis_quality": 0.2,
2048
+ "localization": 0.2,
2049
+ "fix_quality": 0.3,
2050
+ "semantic_similarity": 0.1,
2051
+ "efficiency_potential": 0.0
2052
  },
2053
  "test_results": {
2054
  "passed": 4,
 
2070
  },
2071
  "reward": -0.5,
2072
  "reward_breakdown": {
2073
+ "format_compliance": 0.2,
2074
+ "hypothesis_quality": 0.0,
2075
+ "localization": 0.0,
2076
+ "fix_quality": 0.0,
2077
+ "semantic_similarity": 0.0,
2078
+ "efficiency_potential": 0.0
2079
  },
2080
  "test_results": {
2081
  "passed": 0,
 
2097
  },
2098
  "reward": 1.0,
2099
  "reward_breakdown": {
2100
+ "format_compliance": 0.2,
2101
+ "hypothesis_quality": 0.2,
2102
+ "localization": 0.2,
2103
+ "fix_quality": 0.3,
2104
+ "semantic_similarity": 0.1,
2105
+ "efficiency_potential": 0.0
2106
  },
2107
  "test_results": {
2108
  "passed": 4,
 
2124
  },
2125
  "reward": -0.5,
2126
  "reward_breakdown": {
2127
+ "format_compliance": 0.2,
2128
+ "hypothesis_quality": 0.0,
2129
+ "localization": 0.0,
2130
+ "fix_quality": 0.0,
2131
+ "semantic_similarity": 0.0,
2132
+ "efficiency_potential": 0.0
2133
  },
2134
  "test_results": {
2135
  "passed": 0,
 
2151
  },
2152
  "reward": 1.0,
2153
  "reward_breakdown": {
2154
+ "format_compliance": 0.2,
2155
+ "hypothesis_quality": 0.2,
2156
+ "localization": 0.2,
2157
+ "fix_quality": 0.3,
2158
+ "semantic_similarity": 0.1,
2159
+ "efficiency_potential": 0.0
2160
  },
2161
  "test_results": {
2162
  "passed": 4,
 
2178
  },
2179
  "reward": -0.5,
2180
  "reward_breakdown": {
2181
+ "format_compliance": 0.2,
2182
+ "hypothesis_quality": 0.0,
2183
+ "localization": 0.0,
2184
+ "fix_quality": 0.0,
2185
+ "semantic_similarity": 0.0,
2186
+ "efficiency_potential": 0.0
2187
  },
2188
  "test_results": {
2189
  "passed": 0,
 
2205
  },
2206
  "reward": -0.5,
2207
  "reward_breakdown": {
2208
+ "format_compliance": 0.2,
2209
+ "hypothesis_quality": 0.0,
2210
+ "localization": 0.0,
2211
+ "fix_quality": 0.0,
2212
+ "semantic_similarity": 0.0,
2213
+ "efficiency_potential": 0.0
2214
  },
2215
  "test_results": {
2216
  "passed": 0,
 
2232
  },
2233
  "reward": -0.5,
2234
  "reward_breakdown": {
2235
+ "format_compliance": 0.2,
2236
+ "hypothesis_quality": 0.0,
2237
+ "localization": 0.0,
2238
+ "fix_quality": 0.0,
2239
+ "semantic_similarity": 0.0,
2240
+ "efficiency_potential": 0.0
2241
  },
2242
  "test_results": {
2243
  "passed": 0,
 
2259
  },
2260
  "reward": 1.0,
2261
  "reward_breakdown": {
2262
+ "format_compliance": 0.2,
2263
+ "hypothesis_quality": 0.2,
2264
+ "localization": 0.2,
2265
+ "fix_quality": 0.3,
2266
+ "semantic_similarity": 0.1,
2267
+ "efficiency_potential": 0.0
2268
  },
2269
  "test_results": {
2270
  "passed": 4,
 
2286
  },
2287
  "reward": -0.5,
2288
  "reward_breakdown": {
2289
+ "format_compliance": 0.2,
2290
+ "hypothesis_quality": 0.0,
2291
+ "localization": 0.0,
2292
+ "fix_quality": 0.0,
2293
+ "semantic_similarity": 0.0,
2294
+ "efficiency_potential": 0.0
2295
  },
2296
  "test_results": {
2297
  "passed": 0,
 
2313
  },
2314
  "reward": 1.0,
2315
  "reward_breakdown": {
2316
+ "format_compliance": 0.2,
2317
+ "hypothesis_quality": 0.2,
2318
+ "localization": 0.2,
2319
+ "fix_quality": 0.3,
2320
+ "semantic_similarity": 0.1,
2321
+ "efficiency_potential": 0.0
2322
  },
2323
  "test_results": {
2324
  "passed": 4,
 
2340
  },
2341
  "reward": -0.5,
2342
  "reward_breakdown": {
2343
+ "format_compliance": 0.2,
2344
+ "hypothesis_quality": 0.0,
2345
+ "localization": 0.0,
2346
+ "fix_quality": 0.0,
2347
+ "semantic_similarity": 0.0,
2348
+ "efficiency_potential": 0.0
2349
  },
2350
  "test_results": {
2351
  "passed": 0,
 
2367
  },
2368
  "reward": 1.0,
2369
  "reward_breakdown": {
2370
+ "format_compliance": 0.2,
2371
+ "hypothesis_quality": 0.2,
2372
+ "localization": 0.2,
2373
+ "fix_quality": 0.3,
2374
+ "semantic_similarity": 0.1,
2375
+ "efficiency_potential": 0.0
2376
  },
2377
  "test_results": {
2378
  "passed": 4,
 
2394
  },
2395
  "reward": -0.5,
2396
  "reward_breakdown": {
2397
+ "format_compliance": 0.2,
2398
+ "hypothesis_quality": 0.0,
2399
+ "localization": 0.0,
2400
+ "fix_quality": 0.0,
2401
+ "semantic_similarity": 0.0,
2402
+ "efficiency_potential": 0.0
2403
  },
2404
  "test_results": {
2405
  "passed": 0,
 
2421
  },
2422
  "reward": 1.0,
2423
  "reward_breakdown": {
2424
+ "format_compliance": 0.2,
2425
+ "hypothesis_quality": 0.2,
2426
+ "localization": 0.2,
2427
+ "fix_quality": 0.3,
2428
+ "semantic_similarity": 0.1,
2429
+ "efficiency_potential": 0.0
2430
  },
2431
  "test_results": {
2432
  "passed": 4,
 
2448
  },
2449
  "reward": -0.5,
2450
  "reward_breakdown": {
2451
+ "format_compliance": 0.2,
2452
+ "hypothesis_quality": 0.0,
2453
+ "localization": 0.0,
2454
+ "fix_quality": 0.0,
2455
+ "semantic_similarity": 0.0,
2456
+ "efficiency_potential": 0.0
2457
  },
2458
  "test_results": {
2459
  "passed": 0,
images/gradio UI.png ADDED

Git LFS Details

  • SHA256: add3e90df8c6db791a5b11cec8f3701296a3bf1e8af2f701170983047fcb5f59
  • Pointer size: 131 Bytes
  • Size of remote file: 347 kB
images/hypothesis_quality.png ADDED

Git LFS Details

  • SHA256: 5302bff2fde9fdedc7bc2ec15b8e09cd83efad6247a387bbd381e1e7b9ee2264
  • Pointer size: 131 Bytes
  • Size of remote file: 248 kB
images/localize.png ADDED

Git LFS Details

  • SHA256: 403f7a8c0bb8932b2651ff6d6b94ba8d0e1789a58676f31e0a9d94e6bccc88da
  • Pointer size: 131 Bytes
  • Size of remote file: 240 kB
images/penalty.png ADDED

Git LFS Details

  • SHA256: 787ff6cc37374d49eb0fe43f7f3f65dc1fc3837cd566236bd27e2875914f2b8a
  • Pointer size: 131 Bytes
  • Size of remote file: 214 kB
images/semantic.png ADDED

Git LFS Details

  • SHA256: fc1421e5f71bc65d1551e7ecf3b2119e68df2a2e4edc764292951769c3044455
  • Pointer size: 131 Bytes
  • Size of remote file: 248 kB
inference.py CHANGED
@@ -1,14 +1,3 @@
1
- """
2
- AgentDebuggerEnv Baseline Inference Script
3
- ==========================================
4
- Baseline evaluation script for testing agent performance in the
5
- AgentDebugger environment.
6
-
7
- System Configuration:
8
- - API_BASE_URL: LLM API endpoint
9
- - MODEL_NAME: Model identifier for evaluation
10
- - HF_TOKEN: Authentication token
11
- """
12
 
13
  import os
14
  import json
@@ -18,7 +7,7 @@ import random
18
  from openai import OpenAI, APIError, RateLimitError, APIConnectionError, APITimeoutError
19
  import requests
20
 
21
- # ── Environment variables (never hardcode these) ──────────────────────────────
22
  API_BASE_URL = os.environ.get("API_BASE_URL", "https://router.huggingface.co/v1")
23
  MODEL_NAME = os.environ.get("MODEL_NAME", "meta-llama/Llama-3.1-70B-Instruct")
24
  HF_TOKEN = os.environ.get("HF_TOKEN") or os.environ.get("OPENAI_API_KEY", "")
@@ -66,10 +55,9 @@ Guidelines:
66
  - For concurrent tasks, ensure atomic operations and proper synchronization.
67
  """
68
 
69
- # ── Robust API Completion Helper ──────────────────────────────────────────────
70
 
71
  def get_completion(messages: list, model: str = MODEL_NAME, max_retries: int = 5) -> str:
72
- """Gets LLM completion with exponential backoff and retry logic."""
73
  for attempt in range(max_retries):
74
  try:
75
  completion = client.chat.completions.create(
@@ -77,7 +65,7 @@ def get_completion(messages: list, model: str = MODEL_NAME, max_retries: int = 5
77
  messages=messages,
78
  max_tokens=1200,
79
  temperature=0.2,
80
- timeout=60.0 # Add a timeout to prevent hanging forever
81
  )
82
  return completion.choices[0].message.content
83
  except (RateLimitError, APIConnectionError, APITimeoutError) as e:
@@ -87,7 +75,7 @@ def get_completion(messages: list, model: str = MODEL_NAME, max_retries: int = 5
87
  print(f" [!] API Error ({type(e).__name__}). Retrying in {wait_time:.1f}s... (Attempt {attempt+1}/{max_retries})")
88
  time.sleep(wait_time)
89
  except APIError as e:
90
- # For general API errors, log and potentially retry if it's a 5xx
91
  print(f" [!] OpenAI API Error: {e}")
92
  if attempt == max_retries - 1:
93
  return ""
@@ -99,22 +87,21 @@ def get_completion(messages: list, model: str = MODEL_NAME, max_retries: int = 5
99
 
100
 
101
  def parse_action(raw: str) -> dict:
102
- """Parse LLM response to action dict. Handle markdown code blocks."""
103
  raw = raw.strip()
104
- # Strip markdown code blocks if present
105
  raw = re.sub(r'^```(?:json)?\s*', '', raw, flags=re.MULTILINE)
106
  raw = re.sub(r'\s*```$', '', raw, flags=re.MULTILINE)
107
  try:
108
  return json.loads(raw)
109
  except json.JSONDecodeError:
110
- # Try to extract first JSON object
111
  match = re.search(r'\{.*\}', raw, re.DOTALL)
112
  if match:
113
  try:
114
  return json.loads(match.group())
115
  except json.JSONDecodeError:
116
  pass
117
- # Fallback: give up
118
  return {
119
  "action_type": "give_up",
120
  "final_diagnosis": f"Failed to parse response: {raw[:200]}"
@@ -149,7 +136,7 @@ def build_step_message(obs: dict, reward: dict, info: dict) -> str:
149
 
150
  if last_attempt and last_attempt.get("execution_output"):
151
  output = last_attempt["execution_output"]
152
- # Truncate long outputs to stay within token budget
153
  if len(output) > 1500:
154
  output = output[:750] + "\n...[truncated]...\n" + output[-750:]
155
  msg += f"\nNEW TEST OUTPUT:\n{output}\n"
@@ -163,14 +150,13 @@ def build_step_message(obs: dict, reward: dict, info: dict) -> str:
163
 
164
 
165
  def run_episode(task_id: str) -> dict:
166
- """Run one complete debugging episode. Returns result dict."""
167
 
168
- # Reset environment
169
  reset_resp = requests.post(f"{ENV_BASE_URL}/reset", json={"task_id": task_id}, timeout=60)
170
  reset_resp.raise_for_status()
171
  obs = reset_resp.json()
172
 
173
- # [START] task=NAME
174
  print(f"\n[START] task={task_id}", flush=True)
175
  print(f" Description: {obs['task_description'][:100]}...", flush=True)
176
 
@@ -197,14 +183,14 @@ def run_episode(task_id: str) -> dict:
197
  action = parse_action(raw)
198
  except Exception as e:
199
  print(f" [βœ—] Failed to get response from LLM after retries: {e}")
200
- # Fallback action to avoid crashing the whole episode
201
  action = {
202
  "action_type": "give_up",
203
  "final_diagnosis": f"Inference system failure: {str(e)}"
204
  }
205
  raw = json.dumps(action)
206
 
207
- # Submit action to environment
208
  step_resp = requests.post(f"{ENV_BASE_URL}/step", json=action, timeout=60)
209
  step_resp.raise_for_status()
210
  result = step_resp.json()
@@ -215,10 +201,10 @@ def run_episode(task_id: str) -> dict:
215
  info = result["info"]
216
  last_result = result
217
 
218
- # [STEP] step=N reward=R
219
  print(f" [STEP {obs['step_number']}] Action: {action.get('action_type')} | Tests: {obs['tests_passed']}/{obs['tests_total']} | Reward: {reward['step_reward']:+.3f}", flush=True)
220
 
221
- # Build context for next LLM call
222
  step_msg = build_step_message(obs, reward, info)
223
  messages.append({"role": "assistant", "content": raw})
224
  messages.append({"role": "user", "content": step_msg})
@@ -239,7 +225,7 @@ def run_episode(task_id: str) -> dict:
239
  "final_action_type": action.get("action_type", "unknown")
240
  }
241
 
242
- # [END] task=NAME score=S steps=N
243
  print(f"[END] task={task_id} score={result['grader_score']} steps={result['steps_taken']}", flush=True)
244
 
245
  return result
@@ -248,7 +234,7 @@ def run_episode(task_id: str) -> dict:
248
  def main():
249
  print("AgentDebuggerEnv β€” Baseline Inference")
250
 
251
- # ── Environment validation ────────────────────────────────────────────────
252
  has_token = bool(HF_TOKEN and len(HF_TOKEN) > 5)
253
  masked_token = f"{HF_TOKEN[:4]}...{HF_TOKEN[-4:]}" if has_token else "MISSING"
254
 
 
 
 
 
 
 
 
 
 
 
 
 
1
 
2
  import os
3
  import json
 
7
  from openai import OpenAI, APIError, RateLimitError, APIConnectionError, APITimeoutError
8
  import requests
9
 
10
+
11
  API_BASE_URL = os.environ.get("API_BASE_URL", "https://router.huggingface.co/v1")
12
  MODEL_NAME = os.environ.get("MODEL_NAME", "meta-llama/Llama-3.1-70B-Instruct")
13
  HF_TOKEN = os.environ.get("HF_TOKEN") or os.environ.get("OPENAI_API_KEY", "")
 
55
  - For concurrent tasks, ensure atomic operations and proper synchronization.
56
  """
57
 
58
+
59
 
60
  def get_completion(messages: list, model: str = MODEL_NAME, max_retries: int = 5) -> str:
 
61
  for attempt in range(max_retries):
62
  try:
63
  completion = client.chat.completions.create(
 
65
  messages=messages,
66
  max_tokens=1200,
67
  temperature=0.2,
68
+ timeout=60.0
69
  )
70
  return completion.choices[0].message.content
71
  except (RateLimitError, APIConnectionError, APITimeoutError) as e:
 
75
  print(f" [!] API Error ({type(e).__name__}). Retrying in {wait_time:.1f}s... (Attempt {attempt+1}/{max_retries})")
76
  time.sleep(wait_time)
77
  except APIError as e:
78
+
79
  print(f" [!] OpenAI API Error: {e}")
80
  if attempt == max_retries - 1:
81
  return ""
 
87
 
88
 
89
  def parse_action(raw: str) -> dict:
 
90
  raw = raw.strip()
91
+
92
  raw = re.sub(r'^```(?:json)?\s*', '', raw, flags=re.MULTILINE)
93
  raw = re.sub(r'\s*```$', '', raw, flags=re.MULTILINE)
94
  try:
95
  return json.loads(raw)
96
  except json.JSONDecodeError:
97
+
98
  match = re.search(r'\{.*\}', raw, re.DOTALL)
99
  if match:
100
  try:
101
  return json.loads(match.group())
102
  except json.JSONDecodeError:
103
  pass
104
+
105
  return {
106
  "action_type": "give_up",
107
  "final_diagnosis": f"Failed to parse response: {raw[:200]}"
 
136
 
137
  if last_attempt and last_attempt.get("execution_output"):
138
  output = last_attempt["execution_output"]
139
+
140
  if len(output) > 1500:
141
  output = output[:750] + "\n...[truncated]...\n" + output[-750:]
142
  msg += f"\nNEW TEST OUTPUT:\n{output}\n"
 
150
 
151
 
152
  def run_episode(task_id: str) -> dict:
 
153
 
154
+
155
  reset_resp = requests.post(f"{ENV_BASE_URL}/reset", json={"task_id": task_id}, timeout=60)
156
  reset_resp.raise_for_status()
157
  obs = reset_resp.json()
158
 
159
+
160
  print(f"\n[START] task={task_id}", flush=True)
161
  print(f" Description: {obs['task_description'][:100]}...", flush=True)
162
 
 
183
  action = parse_action(raw)
184
  except Exception as e:
185
  print(f" [βœ—] Failed to get response from LLM after retries: {e}")
186
+
187
  action = {
188
  "action_type": "give_up",
189
  "final_diagnosis": f"Inference system failure: {str(e)}"
190
  }
191
  raw = json.dumps(action)
192
 
193
+
194
  step_resp = requests.post(f"{ENV_BASE_URL}/step", json=action, timeout=60)
195
  step_resp.raise_for_status()
196
  result = step_resp.json()
 
201
  info = result["info"]
202
  last_result = result
203
 
204
+
205
  print(f" [STEP {obs['step_number']}] Action: {action.get('action_type')} | Tests: {obs['tests_passed']}/{obs['tests_total']} | Reward: {reward['step_reward']:+.3f}", flush=True)
206
 
207
+
208
  step_msg = build_step_message(obs, reward, info)
209
  messages.append({"role": "assistant", "content": raw})
210
  messages.append({"role": "user", "content": step_msg})
 
225
  "final_action_type": action.get("action_type", "unknown")
226
  }
227
 
228
+
229
  print(f"[END] task={task_id} score={result['grader_score']} steps={result['steps_taken']}", flush=True)
230
 
231
  return result
 
234
  def main():
235
  print("AgentDebuggerEnv β€” Baseline Inference")
236
 
237
+
238
  has_token = bool(HF_TOKEN and len(HF_TOKEN) > 5)
239
  masked_token = f"{HF_TOKEN[:4]}...{HF_TOKEN[-4:]}" if has_token else "MISSING"
240
 
scratch/data_tier2.py CHANGED
@@ -1,5 +1,5 @@
1
  t2_bugs = [
2
- # 7 bugs: wrong loop termination
3
  {
4
  "id": "t2_004", "difficulty": 2, "bug_type": "wrong_loop_termination", "function_name": "find_first_positive",
5
  "buggy_code": "def find_first_positive(nums):\n i = 0\n while i < len(nums) - 1:\n if nums[i] > 0:\n return nums[i]\n i += 1\n return -1",
@@ -49,7 +49,7 @@ t2_bugs = [
49
  "initial_error": "AssertionError: expected [[1,2],[3]], got [[1,2]]", "bug_location": {"function": "get_chunks", "line_start": 4},
50
  "test_cases": [{"input": [[1, 2, 3], 2], "expected_output": [[1, 2], [3]]}, {"input": [[1, 2], 2], "expected_output": [[1, 2]]}, {"input": [[1, 2, 3, 4], 2], "expected_output": [[1, 2], [3, 4]]}, {"input": [[], 2], "expected_output": []}]
51
  },
52
- # 7 bugs: incorrect accumulation
53
  {
54
  "id": "t2_011", "difficulty": 2, "bug_type": "incorrect_accumulation", "function_name": "sum_even_numbers",
55
  "buggy_code": "def sum_even_numbers(nums):\n total = 1\n for n in nums:\n if n % 2 == 0:\n total += n\n return total",
@@ -99,7 +99,7 @@ t2_bugs = [
99
  "initial_error": "AssertionError: expected 2, got 1", "bug_location": {"function": "count_negatives", "line_start": 2},
100
  "test_cases": [{"input": [[1, -1, 2, -2]], "expected_output": 2}, {"input": [[1, 2, 3]], "expected_output": 0}, {"input": [[-1, -2, -3]], "expected_output": 3}, {"input": [[]], "expected_output": 0}]
101
  },
102
- # 7 bugs: wrong conditional branch
103
  {
104
  "id": "t2_018", "difficulty": 2, "bug_type": "wrong_conditional_branch", "function_name": "classify_number",
105
  "buggy_code": "def classify_number(n):\n if n > 0:\n return 'positive'\n elif n < 0:\n return 'negative'\n elif n == 0:\n return 'negative'",
@@ -149,7 +149,7 @@ t2_bugs = [
149
  "initial_error": "AssertionError: expected 4, got 3", "bug_location": {"function": "get_quadrant", "line_start": 6},
150
  "test_cases": [{"input": [1, 1], "expected_output": 1}, {"input": [-1, 1], "expected_output": 2}, {"input": [-1, -1], "expected_output": 3}, {"input": [1, -1], "expected_output": 4}]
151
  },
152
- # 6 bugs: wrong variable used in final step
153
  {
154
  "id": "t2_025", "difficulty": 2, "bug_type": "wrong_variable", "function_name": "merge_arrays",
155
  "buggy_code": "def merge_arrays(a, b):\n res = a + b\n res.sort()\n return a",
 
1
  t2_bugs = [
2
+
3
  {
4
  "id": "t2_004", "difficulty": 2, "bug_type": "wrong_loop_termination", "function_name": "find_first_positive",
5
  "buggy_code": "def find_first_positive(nums):\n i = 0\n while i < len(nums) - 1:\n if nums[i] > 0:\n return nums[i]\n i += 1\n return -1",
 
49
  "initial_error": "AssertionError: expected [[1,2],[3]], got [[1,2]]", "bug_location": {"function": "get_chunks", "line_start": 4},
50
  "test_cases": [{"input": [[1, 2, 3], 2], "expected_output": [[1, 2], [3]]}, {"input": [[1, 2], 2], "expected_output": [[1, 2]]}, {"input": [[1, 2, 3, 4], 2], "expected_output": [[1, 2], [3, 4]]}, {"input": [[], 2], "expected_output": []}]
51
  },
52
+
53
  {
54
  "id": "t2_011", "difficulty": 2, "bug_type": "incorrect_accumulation", "function_name": "sum_even_numbers",
55
  "buggy_code": "def sum_even_numbers(nums):\n total = 1\n for n in nums:\n if n % 2 == 0:\n total += n\n return total",
 
99
  "initial_error": "AssertionError: expected 2, got 1", "bug_location": {"function": "count_negatives", "line_start": 2},
100
  "test_cases": [{"input": [[1, -1, 2, -2]], "expected_output": 2}, {"input": [[1, 2, 3]], "expected_output": 0}, {"input": [[-1, -2, -3]], "expected_output": 3}, {"input": [[]], "expected_output": 0}]
101
  },
102
+
103
  {
104
  "id": "t2_018", "difficulty": 2, "bug_type": "wrong_conditional_branch", "function_name": "classify_number",
105
  "buggy_code": "def classify_number(n):\n if n > 0:\n return 'positive'\n elif n < 0:\n return 'negative'\n elif n == 0:\n return 'negative'",
 
149
  "initial_error": "AssertionError: expected 4, got 3", "bug_location": {"function": "get_quadrant", "line_start": 6},
150
  "test_cases": [{"input": [1, 1], "expected_output": 1}, {"input": [-1, 1], "expected_output": 2}, {"input": [-1, -1], "expected_output": 3}, {"input": [1, -1], "expected_output": 4}]
151
  },
152
+
153
  {
154
  "id": "t2_025", "difficulty": 2, "bug_type": "wrong_variable", "function_name": "merge_arrays",
155
  "buggy_code": "def merge_arrays(a, b):\n res = a + b\n res.sort()\n return a",
scratch/data_tier3.py CHANGED
@@ -1,5 +1,5 @@
1
  t3_bugs = [
2
- # 6 bugs: wrong argument order
3
  {
4
  "id": "t3_003", "difficulty": 3, "bug_type": "wrong_argument_order", "function_name": "process_user",
5
  "buggy_code": "def format_name(first, last):\n return f'{last}, {first}'\n\ndef process_user(first_name, last_name):\n return format_name(last_name, first_name)",
@@ -42,7 +42,7 @@ t3_bugs = [
42
  "initial_error": "AssertionError: expected 8, got 9", "bug_location": {"function": "power_wrapper", "line_start": 5},
43
  "test_cases": [{"input": [2, 3], "expected_output": 8}, {"input": [3, 2], "expected_output": 9}, {"input": [5, 2], "expected_output": 25}, {"input": [2, 4], "expected_output": 16}]
44
  },
45
- # 6 bugs: state not reset
46
  {
47
  "id": "t3_009", "difficulty": 3, "bug_type": "state_not_reset", "function_name": "get_unique_items",
48
  "buggy_code": "seen = set()\ndef filter_unique(items):\n res = []\n for item in items:\n if item not in seen:\n seen.add(item)\n res.append(item)\n return res\n\ndef get_unique_items(items):\n return filter_unique(items)",
@@ -85,7 +85,7 @@ t3_bugs = [
85
  "initial_error": "AssertionError: state leak between calls", "bug_location": {"function": "log_error", "line_start": 3},
86
  "test_cases": [{"input": [["e1"]], "expected_output": ["e1"]}, {"input": [["e2"]], "expected_output": ["e2"]}, {"input": [["e3", "e4"]], "expected_output": ["e3", "e4"]}, {"input": [["e5"]], "expected_output": ["e5"]}]
87
  },
88
- # 6 bugs: missing edge case in helper
89
  {
90
  "id": "t3_015", "difficulty": 3, "bug_type": "missing_edge_case", "function_name": "process_data",
91
  "buggy_code": "def get_first(lst):\n return lst[0]\n\ndef process_data(data):\n if not data:\n return None\n return [get_first(d) for d in data]",
 
1
  t3_bugs = [
2
+
3
  {
4
  "id": "t3_003", "difficulty": 3, "bug_type": "wrong_argument_order", "function_name": "process_user",
5
  "buggy_code": "def format_name(first, last):\n return f'{last}, {first}'\n\ndef process_user(first_name, last_name):\n return format_name(last_name, first_name)",
 
42
  "initial_error": "AssertionError: expected 8, got 9", "bug_location": {"function": "power_wrapper", "line_start": 5},
43
  "test_cases": [{"input": [2, 3], "expected_output": 8}, {"input": [3, 2], "expected_output": 9}, {"input": [5, 2], "expected_output": 25}, {"input": [2, 4], "expected_output": 16}]
44
  },
45
+
46
  {
47
  "id": "t3_009", "difficulty": 3, "bug_type": "state_not_reset", "function_name": "get_unique_items",
48
  "buggy_code": "seen = set()\ndef filter_unique(items):\n res = []\n for item in items:\n if item not in seen:\n seen.add(item)\n res.append(item)\n return res\n\ndef get_unique_items(items):\n return filter_unique(items)",
 
85
  "initial_error": "AssertionError: state leak between calls", "bug_location": {"function": "log_error", "line_start": 3},
86
  "test_cases": [{"input": [["e1"]], "expected_output": ["e1"]}, {"input": [["e2"]], "expected_output": ["e2"]}, {"input": [["e3", "e4"]], "expected_output": ["e3", "e4"]}, {"input": [["e5"]], "expected_output": ["e5"]}]
87
  },
88
+
89
  {
90
  "id": "t3_015", "difficulty": 3, "bug_type": "missing_edge_case", "function_name": "process_data",
91
  "buggy_code": "def get_first(lst):\n return lst[0]\n\ndef process_data(data):\n if not data:\n return None\n return [get_first(d) for d in data]",
scratch/fix_all.py CHANGED
@@ -1,7 +1,7 @@
1
  import os
2
  import sys
3
 
4
- # ensure data is importable
5
  sys.path.append(os.path.abspath('.'))
6
 
7
  from data.generate_bugs import TIER1_BUGS, TIER2_BUGS, TIER3_BUGS
@@ -34,7 +34,7 @@ for b in TIER1_BUGS:
34
  for b in TIER2_BUGS:
35
  if b["id"] == "t2_003":
36
  for t in b["test_cases"]:
37
- # If t["input"] is nested too deep, unpack it first.
38
  while len(t["input"]) == 1 and isinstance(t["input"][0], list) and len(t["input"][0]) == 2:
39
  t["input"] = t["input"][0]
40
  if len(t["input"]) == 2 and not isinstance(t["input"][0], list) and not isinstance(t["input"], tuple):
@@ -55,10 +55,10 @@ for b in TIER2_BUGS:
55
  for b in TIER3_BUGS:
56
  if b["id"] == "t3_002":
57
  for t in b["test_cases"]:
58
- # Unpack completely
59
  while len(t["input"]) == 1 and isinstance(t["input"][0], list):
60
  t["input"] = t["input"][0]
61
- # Wrap once
62
  t["input"] = [t["input"]]
63
  elif b["id"] == "t3_007":
64
  b["buggy_code"] = b["buggy_code"].replace("calc_area(height, width)", "calc_area(height, height)")
 
1
  import os
2
  import sys
3
 
4
+
5
  sys.path.append(os.path.abspath('.'))
6
 
7
  from data.generate_bugs import TIER1_BUGS, TIER2_BUGS, TIER3_BUGS
 
34
  for b in TIER2_BUGS:
35
  if b["id"] == "t2_003":
36
  for t in b["test_cases"]:
37
+
38
  while len(t["input"]) == 1 and isinstance(t["input"][0], list) and len(t["input"][0]) == 2:
39
  t["input"] = t["input"][0]
40
  if len(t["input"]) == 2 and not isinstance(t["input"][0], list) and not isinstance(t["input"], tuple):
 
55
  for b in TIER3_BUGS:
56
  if b["id"] == "t3_002":
57
  for t in b["test_cases"]:
58
+
59
  while len(t["input"]) == 1 and isinstance(t["input"][0], list):
60
  t["input"] = t["input"][0]
61
+
62
  t["input"] = [t["input"]]
63
  elif b["id"] == "t3_007":
64
  b["buggy_code"] = b["buggy_code"].replace("calc_area(height, width)", "calc_area(height, height)")
scratch/gen_t1_base.py CHANGED
@@ -80,7 +80,7 @@ funcs = [
80
  }
81
  ]
82
 
83
- # Create 2 bugs per function, plus 2 more for the first function = 32 bugs.
84
  bug_id_counter = 9
85
  for f in funcs:
86
  for i in range(2):
@@ -95,12 +95,12 @@ for f in funcs:
95
  "bug_location": {"function": f["name"], "line_start": 2}
96
  }
97
 
98
- # We need to create a bug. Simple mutations based on function name and index.
99
- # This will be done dynamically by the script we run.
100
  t1_bugs.append(bug)
101
  bug_id_counter += 1
102
 
103
- # Let's add 2 more to reach 32.
104
  for i in range(2):
105
  bug = {
106
  "id": f"t1_{bug_id_counter:03d}",
 
80
  }
81
  ]
82
 
83
+
84
  bug_id_counter = 9
85
  for f in funcs:
86
  for i in range(2):
 
95
  "bug_location": {"function": f["name"], "line_start": 2}
96
  }
97
 
98
+
99
+
100
  t1_bugs.append(bug)
101
  bug_id_counter += 1
102
 
103
+
104
  for i in range(2):
105
  bug = {
106
  "id": f"t1_{bug_id_counter:03d}",
scratch/merge.py CHANGED
@@ -1,7 +1,7 @@
1
  import sys
2
  import os
3
 
4
- # add parent dir to path so we can import data.generate_bugs
5
  sys.path.append(os.path.abspath('.'))
6
 
7
  from data.generate_bugs import TIER1_BUGS as T1_OLD
@@ -16,7 +16,7 @@ t1_all = T1_OLD + t1_bugs
16
  t2_all = T2_OLD + t2_bugs
17
  t3_all = T3_OLD + t3_bugs
18
 
19
- # Ensure we have the target numbers
20
  print(f"Tier 1: {len(t1_all)}")
21
  print(f"Tier 2: {len(t2_all)}")
22
  print(f"Tier 3: {len(t3_all)}")
@@ -32,12 +32,12 @@ def pretty_list(lst, name):
32
  lines.append(f' "{k}": (')
33
  for line in v.split('\n'):
34
  lines.append(f' {repr(line + "\\n")}')
35
- lines[-1] = lines[-1][:-4] + "'" + lines[-1][-3:] # remove the trailing \n from the last line, wait, repr('...\\n') adds \n inside. Let's just use json dumps
36
- # Actually json dumps is simpler!
37
- # wait, we need to format code blocks nicely maybe?
38
  pass
39
 
40
- # The best way to format Python code generating Python code is pprint or just repr.
41
  with open("data/generate_bugs.py", "w", encoding="utf-8") as f:
42
  f.write('"""\n')
43
  f.write('AgentDebuggerEnv β€” Bug Dataset Generator\n\n')
@@ -55,7 +55,7 @@ with open("data/generate_bugs.py", "w", encoding="utf-8") as f:
55
  f.write('import json\n')
56
  f.write('import os\n\n')
57
 
58
- # write out variables
59
  import pprint
60
 
61
  def dump_var(name, val):
@@ -71,9 +71,9 @@ with open("data/generate_bugs.py", "w", encoding="utf-8") as f:
71
  f.write(' with open(path, "w") as f:\n')
72
  f.write(' for bug in bugs:\n')
73
  f.write(' f.write(json.dumps(bug) + "\\n")\n')
74
- f.write(' print(f"Tier {path[-12]}: {len(bugs)}")\n') # to print Tier 1: 40 etc. wait, format is different
75
- # wait, the prompt says "It should print: Tier 1: 40, Tier 2: 30, Tier 3: 20"
76
- # let's change the output slightly.
77
  f.write('\n\n')
78
  f.write('if __name__ == "__main__":\n')
79
  f.write(' os.makedirs("data", exist_ok=True)\n')
 
1
  import sys
2
  import os
3
 
4
+
5
  sys.path.append(os.path.abspath('.'))
6
 
7
  from data.generate_bugs import TIER1_BUGS as T1_OLD
 
16
  t2_all = T2_OLD + t2_bugs
17
  t3_all = T3_OLD + t3_bugs
18
 
19
+
20
  print(f"Tier 1: {len(t1_all)}")
21
  print(f"Tier 2: {len(t2_all)}")
22
  print(f"Tier 3: {len(t3_all)}")
 
32
  lines.append(f' "{k}": (')
33
  for line in v.split('\n'):
34
  lines.append(f' {repr(line + "\\n")}')
35
+ lines[-1] = lines[-1][:-4] + "'" + lines[-1][-3:]
36
+
37
+
38
  pass
39
 
40
+
41
  with open("data/generate_bugs.py", "w", encoding="utf-8") as f:
42
  f.write('"""\n')
43
  f.write('AgentDebuggerEnv β€” Bug Dataset Generator\n\n')
 
55
  f.write('import json\n')
56
  f.write('import os\n\n')
57
 
58
+
59
  import pprint
60
 
61
  def dump_var(name, val):
 
71
  f.write(' with open(path, "w") as f:\n')
72
  f.write(' for bug in bugs:\n')
73
  f.write(' f.write(json.dumps(bug) + "\\n")\n')
74
+ f.write(' print(f"Tier {path[-12]}: {len(bugs)}")\n')
75
+
76
+
77
  f.write('\n\n')
78
  f.write('if __name__ == "__main__":\n')
79
  f.write(' os.makedirs("data", exist_ok=True)\n')
server/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
- # server/__init__.py
2
- # Initializing the server package for OpenEnv validation.
 
1
+
2
+
server/app.py CHANGED
@@ -1,14 +1,8 @@
1
- """
2
- Server Entry Point for AgentDebuggerEnv
3
- ========================================
4
- Main entry point to start the FastAPI server for the AgentDebugger environment.
5
- """
6
 
7
  import uvicorn
8
  from env.server import app
9
 
10
  def main():
11
- """Main execution function to run the FastAPI server."""
12
  uvicorn.run(app, host="0.0.0.0", port=8000, workers=1)
13
 
14
  if __name__ == "__main__":
 
 
 
 
 
 
1
 
2
  import uvicorn
3
  from env.server import app
4
 
5
  def main():
 
6
  uvicorn.run(app, host="0.0.0.0", port=8000, workers=1)
7
 
8
  if __name__ == "__main__":
server/models.py CHANGED
@@ -1,10 +1,5 @@
1
- """
2
- server/models.py β€” Re-exports structured agent types for training scripts.
3
- All core types live in env/models.py; this module exposes them under the
4
- `server` namespace so training/train_grpo.py can import without path changes.
5
- """
6
 
7
- from env.models import ( # noqa: F401
8
  StructuredAgentOutput,
9
  parse_agent_output,
10
  VALID_ACTIONS,
 
 
 
 
 
 
1
 
2
+ from env.models import (
3
  StructuredAgentOutput,
4
  parse_agent_output,
5
  VALID_ACTIONS,
server/reward_calculator.py CHANGED
@@ -1,17 +1,3 @@
1
- """
2
- DebugRewardCalculator β€” Multi-component reward system for AgentDebuggerEnv.
3
-
4
- Reward taxonomy follows:
5
- - Masud et al. (2026) "Reward Engineering for RL in Software Tasks"
6
- β†’ Uses their execution-based + process-based + semantic similarity taxonomy
7
- - Ibrahim et al. (2024) "Comprehensive Overview of Reward Engineering and Shaping"
8
- β†’ Uses potential-based shaping for efficiency component to preserve policy invariance
9
-
10
- Design principle: GRPO learns by comparing completions WITHIN a group.
11
- Relative reward differences matter more than absolute values.
12
- Therefore: be generous with partial credit so the model gets differentiated signal
13
- even when nothing fully works.
14
- """
15
 
16
  import difflib
17
  import re
@@ -22,34 +8,17 @@ from server.models import StructuredAgentOutput
22
 
23
  @dataclass
24
  class RewardBreakdown:
25
- format_compliance: float # fires every turn β€” gives early training signal
26
- hypothesis_quality: float # process-based reward (Paper 2 taxonomy)
27
- localization: float # execution-based proxy
28
- fix_quality: float # execution-based reward (primary terminal signal)
29
- semantic_similarity: float # semantic reward (Paper 2 taxonomy)
30
- efficiency_potential: float # potential-based shaping (Paper 1)
31
  penalties: float
32
  total: float
33
 
34
 
35
  class DebugRewardCalculator:
36
- """
37
- Reward weights (must sum to 1.0 excluding penalties):
38
- format_compliance: 0.10 β€” fires every turn, drives early curve movement
39
- hypothesis_quality: 0.20 β€” process-based, independent of fix success
40
- localization: 0.15 β€” did agent find the right place?
41
- fix_quality: 0.35 β€” execution-based, primary terminal signal (sparse)
42
- semantic_similarity: 0.10 β€” how close to canonical fix?
43
- efficiency_potential: 0.10 β€” potential-based shaping across turns
44
-
45
- IMPORTANT NOTE ON SPARSITY vs DENSITY:
46
- The fix_quality reward (0.35) is sparse β€” it only fires when tests pass.
47
- The format, hypothesis, localization rewards are dense β€” they fire every turn.
48
- This combination is intentional: dense rewards carry gradient signal while the
49
- model is still learning to fix bugs; sparse rewards dominate once it gets good.
50
- This directly implements Ibrahim et al.'s recommendation to combine reward
51
- shaping with terminal rewards to solve the sparse reward problem.
52
- """
53
 
54
  MAX_TURNS = 5
55
 
@@ -60,36 +29,14 @@ class DebugRewardCalculator:
60
  test_results: dict,
61
  turn_number: int,
62
  ) -> RewardBreakdown:
63
- """
64
- Compute reward for a single agent turn.
65
-
66
- Args:
67
- agent_output: parsed structured output from the agent
68
- ground_truth: {
69
- "bug_function": str, # name of function containing the bug
70
- "bug_line": int, # line number of the bug
71
- "bug_type": str, # category of bug
72
- "canonical_fix_code": str, # the correct minimal fix
73
- }
74
- test_results: {
75
- "passed": int,
76
- "failed": int,
77
- "total": int,
78
- "newly_broken": int, # tests that passed before but fail after fix
79
- }
80
- turn_number: 0-indexed turn number within the episode
81
-
82
- Returns:
83
- RewardBreakdown with total and all component scores
84
- """
85
-
86
- # ── COMPONENT 1: FORMAT COMPLIANCE ────────────────────────────────
87
- # This fires EVERY turn. Gives the model early training signal before
88
- # it learns to fix bugs. Drives curve movement in first 50-100 steps.
89
  if agent_output.valid:
90
  format_score = 0.10
91
  else:
92
- # Partial credit: how many fields were present?
93
  fields_present = sum([
94
  len(agent_output.observation) > 5,
95
  len(agent_output.hypothesis) > 10,
@@ -98,48 +45,48 @@ class DebugRewardCalculator:
98
  "request_context", "give_up"},
99
  len(agent_output.detail) > 0,
100
  ])
101
- format_score = -0.25 + (fields_present * 0.04) # -0.25 to -0.05
102
 
103
- # ── COMPONENT 2: HYPOTHESIS QUALITY (Process-based, Paper 2) ──────
104
- # Score reasoning quality INDEPENDENTLY from whether the fix works.
105
- # A correct diagnosis that leads to a wrong fix still gets rewarded here.
106
- # This trains the model to reason carefully even when uncertain.
107
  hypothesis_score = 0.0
108
  hypothesis = agent_output.hypothesis
109
 
110
  if len(hypothesis.split()) >= 20:
111
- hypothesis_score += 0.05 # not a one-liner
112
 
113
- # References specific code elements (backticks, quotes, or operators)
114
  if re.search(r'[`\'"<>!=+\-*/]', hypothesis):
115
  hypothesis_score += 0.05
116
 
117
- # Mentions line numbers
118
  if re.search(r'\bline\s+\d+\b|\b\d+\b', hypothesis):
119
  hypothesis_score += 0.05
120
 
121
- # Logically consistent: OBSERVATION and HYPOTHESIS reference same code area
122
  obs_words = set(agent_output.observation.lower().split())
123
  hyp_words = set(hypothesis.lower().split())
124
  overlap = len(obs_words & hyp_words) / max(len(obs_words), 1)
125
  if overlap > 0.15:
126
  hypothesis_score += 0.05
127
 
128
- # Confidence calibration: rewards correct confidence, penalizes overconfidence
129
- # High confidence + correct = bonus, High confidence + wrong = penalty
130
  if agent_output.action == "propose_fix":
131
  tests_pass = test_results.get("passed", 0) == test_results.get("total", 1)
132
  if agent_output.confidence == "high" and tests_pass:
133
- hypothesis_score += 0.05 # well-calibrated
134
  elif agent_output.confidence == "high" and not tests_pass:
135
- hypothesis_score -= 0.05 # overconfident
136
  elif agent_output.confidence == "low" and tests_pass:
137
- hypothesis_score += 0.02 # humble but correct
138
 
139
  hypothesis_score = max(0.0, min(hypothesis_score, 0.20))
140
 
141
- # ── COMPONENT 3: LOCALIZATION (Execution-based proxy) ─────────────
142
- # Did the agent identify WHERE the bug is, independently of fixing it?
143
  localization_score = 0.0
144
  bug_function = ground_truth.get("bug_function", "").lower()
145
  bug_line = str(ground_truth.get("bug_line", -1))
@@ -154,9 +101,9 @@ class DebugRewardCalculator:
154
 
155
  localization_score = min(localization_score, 0.15)
156
 
157
- # ── COMPONENT 4: FIX QUALITY (Execution-based, Paper 2 primary) ───
158
- # This is the dominant signal. Sparse but high value.
159
- # Paper 1: combine with shaping (components 1-3) to solve sparse problem.
160
  total_tests = test_results.get("total", 0)
161
  passed_tests = test_results.get("passed", 0)
162
  fix_score = 0.0
@@ -164,18 +111,18 @@ class DebugRewardCalculator:
164
  if total_tests > 0 and agent_output.action == "propose_fix":
165
  pass_rate = passed_tests / total_tests
166
  if pass_rate == 1.0:
167
- fix_score = 0.35 # full solve β€” this is what we're training for
168
  elif pass_rate >= 0.75:
169
- fix_score = 0.20 # most tests pass
170
  elif pass_rate >= 0.50:
171
- fix_score = 0.12 # more than half pass
172
  elif pass_rate > 0.0:
173
- fix_score = 0.05 # at least something works
174
- # 0.0 if nothing passes β€” no credit for non-fix actions
175
 
176
- # ── COMPONENT 5: SEMANTIC SIMILARITY (Paper 2 taxonomy) ───────────
177
- # How structurally close is the proposed fix to the canonical fix?
178
- # Uses difflib β€” no heavy NLP dependencies needed.
179
  semantic_score = 0.0
180
  proposed = agent_output.detail
181
  canonical = ground_truth.get("canonical_fix_code", "")
@@ -188,36 +135,36 @@ class DebugRewardCalculator:
188
  semantic_score = 0.05
189
  elif similarity >= 0.40:
190
  semantic_score = 0.02
191
- # No reward below 0.40 similarity β€” prevents gaming with partial matches
192
 
193
- # ── COMPONENT 6: EFFICIENCY POTENTIAL (Potential-based, Paper 1) ──
194
- # Implements potential-based reward shaping: F(s,a,s') = Ξ³Ξ¦(s') - Ξ¦(s)
195
- # where Ξ¦(state) = value of remaining turns
196
- # This is PROVEN to not change the optimal policy (Ibrahim et al. Theorem 1)
197
- # while still accelerating convergence.
198
  remaining_turns = self.MAX_TURNS - turn_number
199
- efficiency_potential = 0.02 * remaining_turns # max 0.10 on turn 0
200
 
201
- # ── PENALTIES ─────────────────────────────────────────────────────
202
  penalties = 0.0
203
 
204
- # Regression: fix breaks previously-passing tests β€” severe
205
  if test_results.get("newly_broken", 0) > 0:
206
  penalties -= 0.20
207
 
208
- # Give up: agent chose to give_up
209
  if agent_output.action == "give_up":
210
  penalties -= 0.15
211
 
212
- # Invalid action: not one of the 5 valid actions
213
  if agent_output.action == "invalid":
214
  penalties -= 0.10
215
 
216
- # Invalid format (already captured in format_score, add extra penalty)
217
  if not agent_output.valid:
218
  penalties -= 0.10
219
 
220
- # ── TOTAL ─────────────────────────────────────────────────────────
221
  raw_total = (
222
  format_score
223
  + hypothesis_score
@@ -228,7 +175,7 @@ class DebugRewardCalculator:
228
  + penalties
229
  )
230
 
231
- # Floor at -0.5 to prevent reward death spiral (Ibrahim et al.)
232
  total = max(raw_total, -0.5)
233
 
234
  return RewardBreakdown(
@@ -243,11 +190,6 @@ class DebugRewardCalculator:
243
  )
244
 
245
  def compute_episode_reward(self, trajectory: list[dict]) -> float:
246
- """
247
- Aggregate turn rewards across an episode.
248
- Uses 0.9 discount factor β€” later turns worth slightly less.
249
- Adds solve bonus if bug was fixed before max turns.
250
- """
251
  if not trajectory:
252
  return 0.0
253
 
@@ -258,7 +200,7 @@ class DebugRewardCalculator:
258
  total += discount * turn["reward"].total
259
  discount *= 0.9
260
 
261
- # Solve bonus: incentivizes actually solving the bug
262
  solved = any(t["reward"].fix_quality >= 0.35 for t in trajectory)
263
  if solved:
264
  total += 0.20
@@ -266,7 +208,6 @@ class DebugRewardCalculator:
266
  return round(total, 4)
267
 
268
  def get_reward_breakdown_for_logging(self, trajectory: list[dict]) -> dict:
269
- """Returns per-component averages across episode for W&B logging."""
270
  if not trajectory:
271
  return {}
272
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
 
2
  import difflib
3
  import re
 
8
 
9
  @dataclass
10
  class RewardBreakdown:
11
+ format_compliance: float
12
+ hypothesis_quality: float
13
+ localization: float
14
+ fix_quality: float
15
+ semantic_similarity: float
16
+ efficiency_potential: float
17
  penalties: float
18
  total: float
19
 
20
 
21
  class DebugRewardCalculator:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  MAX_TURNS = 5
24
 
 
29
  test_results: dict,
30
  turn_number: int,
31
  ) -> RewardBreakdown:
32
+
33
+
34
+
35
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  if agent_output.valid:
37
  format_score = 0.10
38
  else:
39
+
40
  fields_present = sum([
41
  len(agent_output.observation) > 5,
42
  len(agent_output.hypothesis) > 10,
 
45
  "request_context", "give_up"},
46
  len(agent_output.detail) > 0,
47
  ])
48
+ format_score = -0.25 + (fields_present * 0.04)
49
 
50
+
51
+
52
+
53
+
54
  hypothesis_score = 0.0
55
  hypothesis = agent_output.hypothesis
56
 
57
  if len(hypothesis.split()) >= 20:
58
+ hypothesis_score += 0.05
59
 
60
+
61
  if re.search(r'[`\'"<>!=+\-*/]', hypothesis):
62
  hypothesis_score += 0.05
63
 
64
+
65
  if re.search(r'\bline\s+\d+\b|\b\d+\b', hypothesis):
66
  hypothesis_score += 0.05
67
 
68
+
69
  obs_words = set(agent_output.observation.lower().split())
70
  hyp_words = set(hypothesis.lower().split())
71
  overlap = len(obs_words & hyp_words) / max(len(obs_words), 1)
72
  if overlap > 0.15:
73
  hypothesis_score += 0.05
74
 
75
+
76
+
77
  if agent_output.action == "propose_fix":
78
  tests_pass = test_results.get("passed", 0) == test_results.get("total", 1)
79
  if agent_output.confidence == "high" and tests_pass:
80
+ hypothesis_score += 0.05
81
  elif agent_output.confidence == "high" and not tests_pass:
82
+ hypothesis_score -= 0.05
83
  elif agent_output.confidence == "low" and tests_pass:
84
+ hypothesis_score += 0.02
85
 
86
  hypothesis_score = max(0.0, min(hypothesis_score, 0.20))
87
 
88
+
89
+
90
  localization_score = 0.0
91
  bug_function = ground_truth.get("bug_function", "").lower()
92
  bug_line = str(ground_truth.get("bug_line", -1))
 
101
 
102
  localization_score = min(localization_score, 0.15)
103
 
104
+
105
+
106
+
107
  total_tests = test_results.get("total", 0)
108
  passed_tests = test_results.get("passed", 0)
109
  fix_score = 0.0
 
111
  if total_tests > 0 and agent_output.action == "propose_fix":
112
  pass_rate = passed_tests / total_tests
113
  if pass_rate == 1.0:
114
+ fix_score = 0.35
115
  elif pass_rate >= 0.75:
116
+ fix_score = 0.20
117
  elif pass_rate >= 0.50:
118
+ fix_score = 0.12
119
  elif pass_rate > 0.0:
120
+ fix_score = 0.05
121
+
122
 
123
+
124
+
125
+
126
  semantic_score = 0.0
127
  proposed = agent_output.detail
128
  canonical = ground_truth.get("canonical_fix_code", "")
 
135
  semantic_score = 0.05
136
  elif similarity >= 0.40:
137
  semantic_score = 0.02
138
+
139
 
140
+
141
+
142
+
143
+
144
+
145
  remaining_turns = self.MAX_TURNS - turn_number
146
+ efficiency_potential = 0.02 * remaining_turns
147
 
148
+
149
  penalties = 0.0
150
 
151
+
152
  if test_results.get("newly_broken", 0) > 0:
153
  penalties -= 0.20
154
 
155
+
156
  if agent_output.action == "give_up":
157
  penalties -= 0.15
158
 
159
+
160
  if agent_output.action == "invalid":
161
  penalties -= 0.10
162
 
163
+
164
  if not agent_output.valid:
165
  penalties -= 0.10
166
 
167
+
168
  raw_total = (
169
  format_score
170
  + hypothesis_score
 
175
  + penalties
176
  )
177
 
178
+
179
  total = max(raw_total, -0.5)
180
 
181
  return RewardBreakdown(
 
190
  )
191
 
192
  def compute_episode_reward(self, trajectory: list[dict]) -> float:
 
 
 
 
 
193
  if not trajectory:
194
  return 0.0
195
 
 
200
  total += discount * turn["reward"].total
201
  discount *= 0.9
202
 
203
+
204
  solved = any(t["reward"].fix_quality >= 0.35 for t in trajectory)
205
  if solved:
206
  total += 0.20
 
208
  return round(total, 4)
209
 
210
  def get_reward_breakdown_for_logging(self, trajectory: list[dict]) -> dict:
 
211
  if not trajectory:
212
  return {}
213
 
tests/__init__.py CHANGED
@@ -1 +1 @@
1
- # AgentDebuggerEnv - Test suite
 
1
+
tests/test_environment.py CHANGED
@@ -1,6 +1,3 @@
1
- """
2
- Tests for the core environment β€” reset, step, state.
3
- """
4
 
5
  import pytest
6
  from env.environment import DebuggerEnvironment
@@ -12,7 +9,7 @@ def env():
12
  return DebuggerEnvironment()
13
 
14
 
15
- # ── Reset Tests ──────────────────────────────────────────────────────────────
16
 
17
  def test_reset_easy_returns_observation(env):
18
  obs = env.reset("easy")
@@ -49,7 +46,7 @@ def test_reset_invalid_task_raises(env):
49
 
50
  def test_reset_clears_previous_state(env):
51
  env.reset("easy")
52
- # Do a step
53
  action = Action(
54
  action_type="submit_fix",
55
  fixed_code="def binary_search(arr, target): return -1",
@@ -57,14 +54,14 @@ def test_reset_clears_previous_state(env):
57
  )
58
  env.step(action)
59
 
60
- # Reset should clear everything
61
  obs = env.reset("easy")
62
  assert obs["step_number"] == 0
63
  assert obs["previous_attempts"] == []
64
  assert obs["attempts_remaining"] == 5
65
 
66
 
67
- # ── Step Tests ───────────────────────────────────────────────────────────────
68
 
69
  def test_step_submit_fix_without_hypothesis(env):
70
  env.reset("easy")
@@ -133,8 +130,8 @@ def test_step_query_context_second_costs(env):
133
  action_type="query_context",
134
  query_type="error_explanation",
135
  )
136
- env.step(action) # First β€” free
137
- result = env.step(action) # Second β€” costs -0.05
138
  assert result["reward"]["step_reward"] == -0.05
139
 
140
 
@@ -173,7 +170,7 @@ def test_step_invalid_query_type(env):
173
  assert result["info"]["error"] is not None
174
 
175
 
176
- # ── State Tests ──────────────────────────────────────────────────────────────
177
 
178
  def test_state_before_reset(env):
179
  state = env.state()
@@ -203,7 +200,7 @@ def test_state_after_step(env):
203
  assert len(state["all_hypotheses"]) == 1
204
 
205
 
206
- # ── Attempts Exhaustion Tests ────────────────────────────────────────────────
207
 
208
  def test_attempts_exhausted(env):
209
  env.reset("easy")
@@ -215,10 +212,10 @@ def test_attempts_exhausted(env):
215
  )
216
  result = env.step(action)
217
 
218
- # After 5 attempts, episode should be done (max_attempts=5)
219
  assert result["done"] is True or result["observation"]["attempts_remaining"] == 0
220
 
221
- # Trying another fix should either fail or episode is done
222
  if not result["done"]:
223
  action = Action(
224
  action_type="submit_fix",
 
 
 
 
1
 
2
  import pytest
3
  from env.environment import DebuggerEnvironment
 
9
  return DebuggerEnvironment()
10
 
11
 
12
+
13
 
14
  def test_reset_easy_returns_observation(env):
15
  obs = env.reset("easy")
 
46
 
47
  def test_reset_clears_previous_state(env):
48
  env.reset("easy")
49
+
50
  action = Action(
51
  action_type="submit_fix",
52
  fixed_code="def binary_search(arr, target): return -1",
 
54
  )
55
  env.step(action)
56
 
57
+
58
  obs = env.reset("easy")
59
  assert obs["step_number"] == 0
60
  assert obs["previous_attempts"] == []
61
  assert obs["attempts_remaining"] == 5
62
 
63
 
64
+
65
 
66
  def test_step_submit_fix_without_hypothesis(env):
67
  env.reset("easy")
 
130
  action_type="query_context",
131
  query_type="error_explanation",
132
  )
133
+ env.step(action)
134
+ result = env.step(action)
135
  assert result["reward"]["step_reward"] == -0.05
136
 
137
 
 
170
  assert result["info"]["error"] is not None
171
 
172
 
173
+
174
 
175
  def test_state_before_reset(env):
176
  state = env.state()
 
200
  assert len(state["all_hypotheses"]) == 1
201
 
202
 
203
+
204
 
205
  def test_attempts_exhausted(env):
206
  env.reset("easy")
 
212
  )
213
  result = env.step(action)
214
 
215
+
216
  assert result["done"] is True or result["observation"]["attempts_remaining"] == 0
217
 
218
+
219
  if not result["done"]:
220
  action = Action(
221
  action_type="submit_fix",
tests/test_graders.py CHANGED
@@ -1,16 +1,12 @@
1
- """
2
- Tests for graders β€” determinism and range validation.
3
- """
4
 
5
  import pytest
6
  from env.graders import get_grader
7
  from env.tasks.registry import get_task
8
 
9
 
10
- # ── Determinism Tests ────────────────────────────────────────────────────────
11
 
12
  def _make_dummy_attempts(n=2, tests_passed=3, tests_total=8):
13
- """Create dummy attempt data for testing."""
14
  return [
15
  {
16
  "attempt_number": i + 1,
@@ -27,7 +23,6 @@ def _make_dummy_attempts(n=2, tests_passed=3, tests_total=8):
27
 
28
 
29
  def test_easy_grader_deterministic():
30
- """Same input to easy grader must produce same output."""
31
  grader = get_grader("easy")
32
  task = get_task("easy")
33
  attempts = _make_dummy_attempts(2, tests_passed=7, tests_total=8)
@@ -39,7 +34,6 @@ def test_easy_grader_deterministic():
39
 
40
 
41
  def test_medium_grader_deterministic():
42
- """Same input to medium grader must produce same output."""
43
  grader = get_grader("medium")
44
  task = get_task("medium")
45
  attempts = _make_dummy_attempts(3, tests_passed=6, tests_total=10)
@@ -51,10 +45,9 @@ def test_medium_grader_deterministic():
51
 
52
 
53
  def test_hard_grader_deterministic():
54
- """Same input to hard grader must produce same output (excluding concurrent test randomness)."""
55
  grader = get_grader("hard")
56
  task = get_task("hard")
57
- # Use buggy code so concurrent test is deterministically failing
58
  attempts = _make_dummy_attempts(2, tests_passed=8, tests_total=8)
59
  hypotheses = ["race condition in increment"]
60
 
@@ -63,11 +56,10 @@ def test_hard_grader_deterministic():
63
  assert score1 == score2, f"Hard grader not deterministic: {score1} != {score2}"
64
 
65
 
66
- # ── Range Tests ──────────────────────────────────────────────────────────────
67
 
68
  @pytest.mark.parametrize("task_id", ["easy", "medium", "hard"])
69
  def test_grader_range_with_zero_attempts(task_id):
70
- """Grader with zero attempts should return a score in [0.0, 1.0]."""
71
  grader = get_grader(task_id)
72
  task = get_task(task_id)
73
  score = grader.score(task, [], 0, task["tests_total"], 0, task["max_attempts"], [])
@@ -76,7 +68,6 @@ def test_grader_range_with_zero_attempts(task_id):
76
 
77
  @pytest.mark.parametrize("task_id", ["easy", "medium", "hard"])
78
  def test_grader_range_with_perfect_score(task_id):
79
- """Grader with all tests passing should return a score in [0.0, 1.0]."""
80
  grader = get_grader(task_id)
81
  task = get_task(task_id)
82
  tests_total = task["tests_total"]
@@ -89,7 +80,6 @@ def test_grader_range_with_perfect_score(task_id):
89
 
90
  @pytest.mark.parametrize("task_id", ["easy", "medium", "hard"])
91
  def test_grader_range_with_all_failures(task_id):
92
- """Grader with no tests passing should return a score in [0.0, 1.0]."""
93
  grader = get_grader(task_id)
94
  task = get_task(task_id)
95
  tests_total = task["tests_total"]
@@ -99,10 +89,9 @@ def test_grader_range_with_all_failures(task_id):
99
  assert 0.0 <= score <= 1.0, f"{task_id} grader out of range: {score}"
100
 
101
 
102
- # ── Variance Tests (dummy vs perfect agents) ────────────────────────────────
103
 
104
  def test_easy_dummy_agent_low_score():
105
- """A dummy agent submitting 'pass' should score < 0.15."""
106
  grader = get_grader("easy")
107
  task = get_task("easy")
108
  attempts = [
@@ -123,7 +112,6 @@ def test_easy_dummy_agent_low_score():
123
 
124
 
125
  def test_easy_perfect_agent_high_score():
126
- """A perfect agent should score > 0.85 on easy."""
127
  grader = get_grader("easy")
128
  task = get_task("easy")
129
  attempts = [
@@ -143,7 +131,6 @@ def test_easy_perfect_agent_high_score():
143
 
144
 
145
  def test_medium_red_herring_low_score():
146
- """Agent that only fixes authenticate_user should score < 0.30 on hypothesis."""
147
  grader = get_grader("medium")
148
  task = get_task("medium")
149
  attempts = _make_dummy_attempts(3, tests_passed=6, tests_total=10)
@@ -153,5 +140,5 @@ def test_medium_red_herring_low_score():
153
  "Fix authenticate_user to return True for valid users",
154
  ]
155
  score = grader.score(task, attempts, 6, 10, 3, 7, hypotheses)
156
- # With only 6/10 tests and red herring hypotheses, score should be modest
157
  assert score < 0.60, f"Red herring agent scored too high on medium: {score}"
 
 
 
 
1
 
2
  import pytest
3
  from env.graders import get_grader
4
  from env.tasks.registry import get_task
5
 
6
 
7
+
8
 
9
  def _make_dummy_attempts(n=2, tests_passed=3, tests_total=8):
 
10
  return [
11
  {
12
  "attempt_number": i + 1,
 
23
 
24
 
25
  def test_easy_grader_deterministic():
 
26
  grader = get_grader("easy")
27
  task = get_task("easy")
28
  attempts = _make_dummy_attempts(2, tests_passed=7, tests_total=8)
 
34
 
35
 
36
  def test_medium_grader_deterministic():
 
37
  grader = get_grader("medium")
38
  task = get_task("medium")
39
  attempts = _make_dummy_attempts(3, tests_passed=6, tests_total=10)
 
45
 
46
 
47
  def test_hard_grader_deterministic():
 
48
  grader = get_grader("hard")
49
  task = get_task("hard")
50
+
51
  attempts = _make_dummy_attempts(2, tests_passed=8, tests_total=8)
52
  hypotheses = ["race condition in increment"]
53
 
 
56
  assert score1 == score2, f"Hard grader not deterministic: {score1} != {score2}"
57
 
58
 
59
+
60
 
61
  @pytest.mark.parametrize("task_id", ["easy", "medium", "hard"])
62
  def test_grader_range_with_zero_attempts(task_id):
 
63
  grader = get_grader(task_id)
64
  task = get_task(task_id)
65
  score = grader.score(task, [], 0, task["tests_total"], 0, task["max_attempts"], [])
 
68
 
69
  @pytest.mark.parametrize("task_id", ["easy", "medium", "hard"])
70
  def test_grader_range_with_perfect_score(task_id):
 
71
  grader = get_grader(task_id)
72
  task = get_task(task_id)
73
  tests_total = task["tests_total"]
 
80
 
81
  @pytest.mark.parametrize("task_id", ["easy", "medium", "hard"])
82
  def test_grader_range_with_all_failures(task_id):
 
83
  grader = get_grader(task_id)
84
  task = get_task(task_id)
85
  tests_total = task["tests_total"]
 
89
  assert 0.0 <= score <= 1.0, f"{task_id} grader out of range: {score}"
90
 
91
 
92
+
93
 
94
  def test_easy_dummy_agent_low_score():
 
95
  grader = get_grader("easy")
96
  task = get_task("easy")
97
  attempts = [
 
112
 
113
 
114
  def test_easy_perfect_agent_high_score():
 
115
  grader = get_grader("easy")
116
  task = get_task("easy")
117
  attempts = [
 
131
 
132
 
133
  def test_medium_red_herring_low_score():
 
134
  grader = get_grader("medium")
135
  task = get_task("medium")
136
  attempts = _make_dummy_attempts(3, tests_passed=6, tests_total=10)
 
140
  "Fix authenticate_user to return True for valid users",
141
  ]
142
  score = grader.score(task, attempts, 6, 10, 3, 7, hypotheses)
143
+
144
  assert score < 0.60, f"Red herring agent scored too high on medium: {score}"
tests/test_integration.py CHANGED
@@ -1,27 +1,19 @@
1
- """
2
- AgentDebuggerEnv β€” Integration Tests
3
- ====================================
4
- Verifies the full episode lifecycle: reset -> step -> end.
5
- Assumes the server is available via the DebuggerEnvironment class directly
6
- (testing the logic, not the HTTP layer which is just a thin wrapper).
7
- """
8
 
9
  import pytest
10
  from env.environment import DebuggerEnvironment
11
  from env.models import Action
12
 
13
  def test_full_episode_easy():
14
- """Test a full successful episode on the 'easy' task."""
15
  env = DebuggerEnvironment()
16
 
17
- # 1. Reset
18
  obs = env.reset("easy")
19
  assert obs["task_id"] == "easy"
20
  assert obs["done"] is False
21
  assert obs["tests_passed"] < obs["tests_total"]
22
 
23
- # 2. Submit a fix (using known ground truth)
24
- # The easy task is binary search with 'left < right' instead of 'left <= right'
25
  ground_truth_code = """
26
  def binary_search(arr, target):
27
  left, right = 0, len(arr) - 1
@@ -43,13 +35,12 @@ def binary_search(arr, target):
43
 
44
  result = env.step(action)
45
 
46
- # 3. Verify results
47
  assert result["done"] is True
48
  assert result["observation"]["tests_passed"] == result["observation"]["tests_total"]
49
  assert result["reward"]["grader_score"] > 0.80
50
 
51
  def test_query_hint_system():
52
- """Test the newly added hint system."""
53
  env = DebuggerEnvironment()
54
  env.reset("hard")
55
 
@@ -60,20 +51,16 @@ def test_query_hint_system():
60
 
61
  result = env.step(action)
62
  assert "concurrent threads" in result["info"]["query_result"]
63
- assert result["reward"]["step_reward"] == 0.0 # First query is free
64
 
65
  def test_hard_grader_consensus():
66
- """
67
- Test that the hard grader runs multiple times.
68
- (We mock execute_code to simulate flakiness).
69
- """
70
  from unittest.mock import patch
71
  from env.graders.grader_hard import HardGrader
72
 
73
  grader = HardGrader()
74
 
75
- # Mock execute_code to return success 3/5 times
76
- # Sequence: PASS, FAIL, PASS, FAIL, PASS
77
  with patch("env.graders.grader_hard.execute_code") as mock_exec:
78
  mock_exec.side_effect = [
79
  ("CONCURRENT PASS", False, 100),
@@ -93,10 +80,10 @@ def test_hard_grader_consensus():
93
  hypotheses=["race condition"]
94
  )
95
 
96
- # 3/5 passes β†’ should get partial credit (0.15) for concurrency
97
- # Sequential: 1.0 * 0.40 = 0.40
98
- # Concurrency: 0.15
99
- # Hypothesis: 1.0 * 0.20 = 0.20
100
- # Efficiency: (concurrent_score == 0.30) is False -> 0.0
101
- # Total: 0.75
102
  assert score == 0.75
 
 
 
 
 
 
 
 
1
 
2
  import pytest
3
  from env.environment import DebuggerEnvironment
4
  from env.models import Action
5
 
6
  def test_full_episode_easy():
 
7
  env = DebuggerEnvironment()
8
 
9
+
10
  obs = env.reset("easy")
11
  assert obs["task_id"] == "easy"
12
  assert obs["done"] is False
13
  assert obs["tests_passed"] < obs["tests_total"]
14
 
15
+
16
+
17
  ground_truth_code = """
18
  def binary_search(arr, target):
19
  left, right = 0, len(arr) - 1
 
35
 
36
  result = env.step(action)
37
 
38
+
39
  assert result["done"] is True
40
  assert result["observation"]["tests_passed"] == result["observation"]["tests_total"]
41
  assert result["reward"]["grader_score"] > 0.80
42
 
43
  def test_query_hint_system():
 
44
  env = DebuggerEnvironment()
45
  env.reset("hard")
46
 
 
51
 
52
  result = env.step(action)
53
  assert "concurrent threads" in result["info"]["query_result"]
54
+ assert result["reward"]["step_reward"] == 0.0
55
 
56
  def test_hard_grader_consensus():
 
 
 
 
57
  from unittest.mock import patch
58
  from env.graders.grader_hard import HardGrader
59
 
60
  grader = HardGrader()
61
 
62
+
63
+
64
  with patch("env.graders.grader_hard.execute_code") as mock_exec:
65
  mock_exec.side_effect = [
66
  ("CONCURRENT PASS", False, 100),
 
80
  hypotheses=["race condition"]
81
  )
82
 
83
+
84
+
85
+
86
+
87
+
88
+
89
  assert score == 0.75
tests/test_sandbox.py CHANGED
@@ -1,14 +1,9 @@
1
- """
2
- Tests for the code execution sandbox.
3
- All 5 tests are required by the hackathon spec.
4
- """
5
 
6
  import pytest
7
  from env.sandbox import execute_code
8
 
9
 
10
  def test_timeout_enforcement():
11
- """Code with infinite loop must return timed_out=True within ~11 seconds."""
12
  code = "while True: pass"
13
  output, timed_out, elapsed_ms = execute_code(code, "")
14
  assert timed_out is True
@@ -16,21 +11,18 @@ def test_timeout_enforcement():
16
 
17
 
18
  def test_os_import_blocked():
19
- """os module must be blocked β€” cannot execute system commands."""
20
  code = "import os; os.system('echo pwned')"
21
  output, timed_out, _ = execute_code(code, "")
22
  assert "BLOCKED" in output or "blocked" in output.lower()
23
 
24
 
25
  def test_sys_import_blocked():
26
- """sys module must be blocked."""
27
  code = "import sys; sys.exit(0)"
28
  output, _, _ = execute_code(code, "")
29
  assert "blocked" in output.lower() or "import" in output.lower()
30
 
31
 
32
  def test_clean_code_runs():
33
- """Clean, safe code with tests must execute correctly."""
34
  code = "def add(a, b): return a + b"
35
  test = "assert add(2, 3) == 5\nprint('PASSED')"
36
  output, timed_out, _ = execute_code(code, test)
@@ -39,17 +31,15 @@ def test_clean_code_runs():
39
 
40
 
41
  def test_syntax_error_returns_output():
42
- """Code with syntax errors should return the SyntaxError, not crash."""
43
  code = "def broken(: pass"
44
  output, timed_out, _ = execute_code(code, "")
45
  assert "SyntaxError" in output
46
  assert timed_out is False
47
 
48
 
49
- # ── Additional robustness tests ──────────────────────────────────────────────
50
 
51
  def test_subprocess_import_blocked():
52
- """subprocess module must be blocked."""
53
  code = "import subprocess; subprocess.run(['echo', 'pw' + 'ned'])"
54
  output, _, _ = execute_code(code, "")
55
  assert "pwned" not in output
@@ -57,7 +47,6 @@ def test_subprocess_import_blocked():
57
 
58
 
59
  def test_threading_blocked_by_default():
60
- """threading must be blocked unless allow_threading=True."""
61
  code = "import threading; print('thread ' + 'imported')"
62
  output, _, _ = execute_code(code, "")
63
  assert "thread imported" not in output
@@ -65,14 +54,12 @@ def test_threading_blocked_by_default():
65
 
66
 
67
  def test_threading_allowed_when_flagged():
68
- """threading must be allowed when allow_threading=True."""
69
  code = "import threading; print('thread imported')"
70
  output, _, _ = execute_code(code, "", allow_threading=True)
71
  assert "thread imported" in output
72
 
73
 
74
  def test_from_import_blocked():
75
- """'from os import path' style imports must also be blocked."""
76
  code = "from os import path; print('pw' + 'ned')"
77
  output, _, _ = execute_code(code, "")
78
  assert "pwned" not in output
@@ -80,14 +67,13 @@ def test_from_import_blocked():
80
 
81
 
82
  def test_no_state_leak_between_executions():
83
- """Each execution must be completely isolated β€” no shared state."""
84
  code1 = "shared_var = 42"
85
  output1, _, _ = execute_code(code1, "print('set')")
86
  assert "set" in output1
87
 
88
  code2 = ""
89
  test2 = "try:\\n print(shared_var)\\nexcept NameError:\\n print('ISOLATED')"
90
- # Fix: use actual newlines
91
  code2_test = "try:\n print(shared_var)\nexcept NameError:\n print('ISOLATED')"
92
  output2, _, _ = execute_code("", code2_test)
93
  assert "ISOLATED" in output2
 
 
 
 
 
1
 
2
  import pytest
3
  from env.sandbox import execute_code
4
 
5
 
6
  def test_timeout_enforcement():
 
7
  code = "while True: pass"
8
  output, timed_out, elapsed_ms = execute_code(code, "")
9
  assert timed_out is True
 
11
 
12
 
13
  def test_os_import_blocked():
 
14
  code = "import os; os.system('echo pwned')"
15
  output, timed_out, _ = execute_code(code, "")
16
  assert "BLOCKED" in output or "blocked" in output.lower()
17
 
18
 
19
  def test_sys_import_blocked():
 
20
  code = "import sys; sys.exit(0)"
21
  output, _, _ = execute_code(code, "")
22
  assert "blocked" in output.lower() or "import" in output.lower()
23
 
24
 
25
  def test_clean_code_runs():
 
26
  code = "def add(a, b): return a + b"
27
  test = "assert add(2, 3) == 5\nprint('PASSED')"
28
  output, timed_out, _ = execute_code(code, test)
 
31
 
32
 
33
  def test_syntax_error_returns_output():
 
34
  code = "def broken(: pass"
35
  output, timed_out, _ = execute_code(code, "")
36
  assert "SyntaxError" in output
37
  assert timed_out is False
38
 
39
 
40
+
41
 
42
  def test_subprocess_import_blocked():
 
43
  code = "import subprocess; subprocess.run(['echo', 'pw' + 'ned'])"
44
  output, _, _ = execute_code(code, "")
45
  assert "pwned" not in output
 
47
 
48
 
49
  def test_threading_blocked_by_default():
 
50
  code = "import threading; print('thread ' + 'imported')"
51
  output, _, _ = execute_code(code, "")
52
  assert "thread imported" not in output
 
54
 
55
 
56
  def test_threading_allowed_when_flagged():
 
57
  code = "import threading; print('thread imported')"
58
  output, _, _ = execute_code(code, "", allow_threading=True)
59
  assert "thread imported" in output
60
 
61
 
62
  def test_from_import_blocked():
 
63
  code = "from os import path; print('pw' + 'ned')"
64
  output, _, _ = execute_code(code, "")
65
  assert "pwned" not in output
 
67
 
68
 
69
  def test_no_state_leak_between_executions():
 
70
  code1 = "shared_var = 42"
71
  output1, _, _ = execute_code(code1, "print('set')")
72
  assert "set" in output1
73
 
74
  code2 = ""
75
  test2 = "try:\\n print(shared_var)\\nexcept NameError:\\n print('ISOLATED')"
76
+
77
  code2_test = "try:\n print(shared_var)\nexcept NameError:\n print('ISOLATED')"
78
  output2, _, _ = execute_code("", code2_test)
79
  assert "ISOLATED" in output2
training/train_grpo.py CHANGED
@@ -1,22 +1,3 @@
1
- """
2
- AgentDebuggerEnv β€” GRPO Training Script
3
- Model: Qwen2.5-Coder-7B-Instruct (float16/bfloat16 + LoRA, no quantization)
4
- Algorithm: GRPO (Group Relative Policy Optimization) via HuggingFace TRL
5
- GPU: auto-detected at runtime (A100/H100 β†’ bfloat16+large batch, T4/V100 β†’ float16+small batch)
6
-
7
- Usage:
8
- # Local reward sanity-check (no GPU, no model loading):
9
- python training/train_grpo.py --test-local
10
-
11
- # Test run (Colab/GPU, 10 steps):
12
- python training/train_grpo.py --test
13
-
14
- # Full training run:
15
- python training/train_grpo.py
16
-
17
- # Resume from checkpoint:
18
- python training/train_grpo.py --resume ./checkpoints/checkpoint-400
19
- """
20
 
21
  import os
22
  import sys
@@ -28,7 +9,7 @@ import tempfile
28
  import shutil
29
  from importlib import metadata
30
 
31
- # ── Parse args ────────────────────────────────────────────────────────────────
32
  parser = argparse.ArgumentParser()
33
  parser.add_argument("--test", action="store_true", help="Run 10 steps for testing (Colab/GPU)")
34
  parser.add_argument("--test-local", action="store_true", dest="test_local",
@@ -40,15 +21,15 @@ parser.add_argument("--max_steps", type=int, default=500)
40
  args = parser.parse_args()
41
 
42
 
43
- # ── Runtime dependency install ─────────────────────────────────────────────────
44
- # requirements.txt only has torch (too large to install at runtime).
45
- # Everything else is installed here, after gradio is already up.
46
- # NOTE: mergekit intentionally excluded β€” conflicts with accelerate/peft/trl.
47
  if not args.test_local:
48
- # ── Ensure CUDA-enabled torch is present before anything else imports it ──
49
- # The default PyPI torch wheel is CPU-only. We must install from the
50
- # PyTorch CUDA index so that torch.cuda.is_available() returns True and
51
- # device_map="auto" maps the model to GPU, not RAM.
52
  import importlib.util, importlib
53
  _needs_cuda_torch = True
54
  if importlib.util.find_spec("torch") is not None:
@@ -84,7 +65,7 @@ if not args.test_local:
84
  sys.exit(1)
85
  print("Dependencies installed.", flush=True)
86
 
87
- # ── GPU/training imports (skipped in --test-local mode) ───────────────────────
88
  if not args.test_local:
89
  import torch
90
  import wandb
@@ -115,13 +96,13 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
115
  from server.reward_calculator import DebugRewardCalculator
116
  from server.models import parse_agent_output
117
 
118
- # ── Configuration ─────────────────────────────────────────────────────────────
119
  MODEL_NAME = "Qwen/Qwen2.5-Coder-3B-Instruct"
120
  HF_REPO = "shashaank0707/AgentDebugger-trained"
121
  MAX_STEPS = (10 if args.test else args.max_steps) - args.step_offset
122
  CHECKPOINT_DIR = "./checkpoints"
123
 
124
- # W&B and HF Token
125
  WANDB_API_KEY = os.environ.get("WANDB_API_KEY", "") if not args.test_local else ""
126
  HF_TOKEN = os.environ.get("HF_TOKEN")
127
 
@@ -139,7 +120,7 @@ if WANDB_API_KEY:
139
  }
140
  )
141
 
142
- # ── System prompt ─────────────────────────────────────────────────────────────
143
  SYSTEM_PROMPT = """You are an expert Python debugger. You reason through bugs systematically.
144
 
145
  You MUST respond in EXACTLY this format β€” no exceptions, no extra text:
@@ -156,7 +137,7 @@ Rules:
156
  - If proposing a fix, DETAIL must contain the complete function, not just the changed line
157
  - Give up only if you have exhausted all reasonable hypotheses"""
158
 
159
- # ── Load bugs ─────────────────────────────────────────────────────────────────
160
  def load_bugs(tier: int) -> list[dict]:
161
  path = f"data/bugs_tier{tier}.jsonl"
162
  if not os.path.exists(path):
@@ -184,7 +165,6 @@ def bug_to_prompt(bug: dict) -> str:
184
  )
185
 
186
  def _run_fix(proposed_code: str, bug: dict) -> dict:
187
- """Safely run proposed fix with subprocess timeout."""
188
  test_cases = bug.get("test_cases", [])
189
  func_name = bug.get("function_name", "")
190
  if not proposed_code or not test_cases or not func_name:
@@ -200,7 +180,7 @@ def _run_fix(proposed_code: str, bug: dict) -> dict:
200
  f" r={func_name}({args_str})\n"
201
  f" print('PASS' if r=={repr(test['expected_output'])} else 'FAIL')\n"
202
  f"except Exception as e:\n"
203
- f" print(f'ERROR: {{e}}')\n"
204
  )
205
  try:
206
  with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
@@ -216,7 +196,7 @@ def _run_fix(proposed_code: str, bug: dict) -> dict:
216
 
217
  return {"passed": passed, "failed": len(test_cases) - passed, "total": len(test_cases), "newly_broken": 0}
218
 
219
- # ── Mock completions for --test-local ─────────────────────────────────────────
220
  MOCK_GOOD = """
221
  OBSERVATION: The loop condition on line 4 uses <= instead of
222
  HYPOTHESIS: This causes an off-by-one error because Python lists are
@@ -241,7 +221,7 @@ I think there might be a bug somewhere in the code.
241
  Let me try fixing it.
242
  """
243
 
244
- # ── --test-local: reward sanity-check without any model ───────────────────────
245
  if args.test_local:
246
  print("=" * 60)
247
  print("LOCAL TEST MODE β€” no model loaded, testing reward function only")
@@ -295,9 +275,9 @@ if args.test_local:
295
  print("\nLOCAL TEST PASSED")
296
  sys.exit(0)
297
 
298
- # ── Auto-detect GPU and set optimal config ────────────────────────────────────
299
  _gpu_vram_gb = 0
300
- _is_ampere_plus = False # A100/H100 support bfloat16 natively (compute cap >= 8.0)
301
  if torch.cuda.is_available():
302
  _props = torch.cuda.get_device_properties(0)
303
  _gpu_vram_gb = _props.total_memory / 1e9
@@ -308,30 +288,30 @@ if torch.cuda.is_available():
308
 
309
  COMPUTE_DTYPE = torch.bfloat16 if _is_ampere_plus else torch.float16
310
 
311
- # Scale batch/generation config to available VRAM.
312
- # GRPO constraint: per_device_train_batch_size % num_generations == 0
313
- if _gpu_vram_gb >= 70: # A100 80GB
314
  _batch = 8
315
- _grad_accum = 1 # effective batch = 8
316
- _num_gen = 8 # 8 % 8 == 0
317
  _max_comp = 256
318
  _lora_r = 16
319
- elif _gpu_vram_gb >= 40: # A100 40GB
320
  _batch = 4
321
- _grad_accum = 2 # effective batch = 8
322
- _num_gen = 4 # 4 % 4 == 0
323
  _max_comp = 256
324
  _lora_r = 16
325
- elif _gpu_vram_gb >= 20: # A10G 24GB / V100 32GB
326
  _batch = 2
327
  _grad_accum = 4
328
- _num_gen = 2 # 2 % 2 == 0
329
  _max_comp = 192
330
  _lora_r = 8
331
- else: # T4 15GB / anything smaller
332
  _batch = 2
333
  _grad_accum = 4
334
- _num_gen = 2 # 2 % 2 == 0
335
  _max_comp = 160
336
  _lora_r = 8
337
 
@@ -339,9 +319,9 @@ print(f"Training config: batch={_batch} grad_accum={_grad_accum} "
339
  f"num_gen={_num_gen} max_comp={_max_comp} lora_r={_lora_r} "
340
  f"dtype={COMPUTE_DTYPE}")
341
 
342
- # ── Load model ────────────────────────────────────────────────────────────────
343
- # Load in native float16/bfloat16 β€” no bitsandbytes needed.
344
- # A10G (24GB) fits Qwen2.5-7B in float16 (~14GB) with room for LoRA + activations.
345
  print(f"Loading {MODEL_NAME} in {COMPUTE_DTYPE}...")
346
  tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
347
  tokenizer.pad_token = tokenizer.eos_token
@@ -374,23 +354,20 @@ model.enable_input_require_grads()
374
  model.gradient_checkpointing_enable()
375
  print(f"Trainable params: {model.num_parameters(only_trainable=True):,}")
376
 
377
- # ── Runtime device selection ──────────────────────────────────────────────────
378
  def _select_runtime_device(model) -> str:
379
- """
380
- Pick the safest generation device without forcing CUDA init on broken drivers.
381
- """
382
  def _cuda_usable() -> bool:
383
  try:
384
  if not torch.cuda.is_available():
385
  return False
386
- # Force lightweight CUDA init probe.
387
  _ = torch.zeros(1, device="cuda")
388
  return True
389
  except Exception as e:
390
  print(f"WARNING: CUDA initialization failed ({e}). Falling back to CPU.")
391
  return False
392
 
393
- # Prefer model's current device when available.
394
  try:
395
  model_device = str(next(model.parameters()).device)
396
  if model_device.startswith("cuda") and not _cuda_usable():
@@ -399,7 +376,7 @@ def _select_runtime_device(model) -> str:
399
  except Exception:
400
  pass
401
 
402
- # Fallback to torch capability checks.
403
  if _cuda_usable():
404
  return "cuda"
405
  return "cpu"
@@ -408,14 +385,10 @@ def _select_runtime_device(model) -> str:
408
  RUNTIME_DEVICE = _select_runtime_device(model)
409
  print(f"Using generation/training runtime device: {RUNTIME_DEVICE}")
410
 
411
- # ── Reward function ───────────────────────────────────────────────────────────
412
  calculator = DebugRewardCalculator()
413
 
414
  def reward_fn(completions: list[str], prompts: list[str], **kwargs) -> list[float]:
415
- """
416
- GRPO reward function. Called on groups of completions for the same prompt.
417
- GRPO learns from RELATIVE differences within each group.
418
- """
419
  rewards = []
420
  bugs_raw = kwargs.get("bug_metadata", [{}] * len(completions))
421
  bugs = [json.loads(b) if isinstance(b, str) else b for b in bugs_raw]
@@ -424,7 +397,7 @@ def reward_fn(completions: list[str], prompts: list[str], **kwargs) -> list[floa
424
  try:
425
  agent_output = parse_agent_output(completion)
426
 
427
- # Run fix if agent proposes one
428
  test_results = {"passed": 0, "failed": 0, "total": 0, "newly_broken": 0}
429
  if agent_output.action == "propose_fix" and bug:
430
  test_results = _run_fix(agent_output.detail, bug)
@@ -452,7 +425,7 @@ def reward_fn(completions: list[str], prompts: list[str], **kwargs) -> list[floa
452
 
453
  return rewards
454
 
455
- # ── Baseline evaluation (run BEFORE training) ─────────────────────────────────
456
  def run_baseline(n: int = 20) -> dict:
457
  print("\nRunning baseline evaluation on UNTRAINED model...")
458
  model.eval()
@@ -481,12 +454,12 @@ def run_baseline(n: int = 20) -> dict:
481
  baseline = run_baseline()
482
  model.train()
483
 
484
- # ── Build initial dataset ─────────────────────────────────────────────────────
485
  def make_dataset(step: int) -> Dataset:
486
  bugs = get_bugs_for_step(step)
487
  return Dataset.from_list([{"prompt": bug_to_prompt(b), "bug_metadata": json.dumps(b)} for b in bugs])
488
 
489
- # ── Training config ───────────────────────────────────────────────────────────
490
  config = GRPOConfig(
491
  output_dir=CHECKPOINT_DIR,
492
  max_steps=MAX_STEPS,
@@ -499,7 +472,7 @@ config = GRPOConfig(
499
  max_completion_length=_max_comp,
500
  temperature=0.9,
501
  logging_steps=5,
502
- save_steps=25, # Save to local disk every 25 steps
503
  save_strategy="steps",
504
  report_to="wandb" if WANDB_API_KEY else "none",
505
  )
@@ -512,7 +485,7 @@ trainer = GRPOTrainer(
512
  processing_class=tokenizer,
513
  )
514
 
515
- # ── Curriculum callback ───────────────────────────────────────────────────────
516
  class CurriculumCallback(TrainerCallback):
517
  def on_step_end(self, callback_args, state, control, **kwargs):
518
  step = state.global_step + args.step_offset
@@ -524,15 +497,14 @@ class CurriculumCallback(TrainerCallback):
524
 
525
  trainer.add_callback(CurriculumCallback())
526
 
527
- # ── HF Hub checkpoint push callback (CRITICAL: survives container restarts) ────
528
- # Pushes LoRA adapter weights to HF Hub every HUB_PUSH_EVERY steps.
529
- # This is the fix for the original problem: ephemeral Space storage meant that
530
- # checkpoints saved to ./checkpoints/ were lost when the Space stopped.
531
- # Now even if training is interrupted, the latest adapter weights are on HF Hub.
532
- HUB_PUSH_EVERY = 50 # push every 50 steps β€” ~15min on T4, ~5min on A100
533
 
534
  class CheckpointPushCallback(TrainerCallback):
535
- """Push LoRA adapter to HF Hub every HUB_PUSH_EVERY steps."""
536
 
537
  def on_step_end(self, callback_args, state, control, **kwargs):
538
  step = state.global_step + args.step_offset
@@ -553,28 +525,28 @@ class CheckpointPushCallback(TrainerCallback):
553
  private=True,
554
  commit_message=f"tokenizer checkpoint-step-{step}",
555
  )
556
- # Write a step marker file so we know the latest pushed step
557
  with open("./last_hub_push.txt", "w") as _f:
558
  _f.write(str(step))
559
  print(f"[HubPush] βœ“ Step {step} pushed to HF Hub.", flush=True)
560
  if WANDB_API_KEY:
561
  wandb.log({"hub/last_pushed_step": step})
562
  except Exception as e:
563
- # Never crash training because of a push failure
564
  print(f"[HubPush] WARNING: push failed at step {step}: {e}", flush=True)
565
 
566
- if not args.test: # Don't push during 10-step test runs
567
  trainer.add_callback(CheckpointPushCallback())
568
  print(f"HF Hub checkpoint push enabled every {HUB_PUSH_EVERY} steps β†’ {HF_REPO}-checkpoints")
569
  else:
570
  print("[TEST MODE] Hub checkpoint push disabled.")
571
 
572
- # ── Train ─────────────────────────────────────────────────────────────────────
573
  print(f"\nStarting GRPO training. Max steps: {MAX_STEPS}")
574
  print(f"Baseline solve rate: {baseline['solve_rate']:.1%} β€” target: >60% after training")
575
  trainer.train(resume_from_checkpoint=args.resume)
576
 
577
- # ── Post-training evaluation ──────────────────────────────────────────────────
578
  model.eval()
579
  bugs = load_bugs(1)[:20]
580
  post_rewards = []
@@ -602,7 +574,7 @@ if WANDB_API_KEY:
602
  wandb.log({"final/solve_rate": post_solve_rate, "final/improvement": post_solve_rate - baseline["solve_rate"]})
603
  wandb.finish()
604
 
605
- # ── Save and push ─────────────────────────────────────────────────────────────
606
  model.save_pretrained("./final_model")
607
  tokenizer.save_pretrained("./final_model")
608
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
 
2
  import os
3
  import sys
 
9
  import shutil
10
  from importlib import metadata
11
 
12
+
13
  parser = argparse.ArgumentParser()
14
  parser.add_argument("--test", action="store_true", help="Run 10 steps for testing (Colab/GPU)")
15
  parser.add_argument("--test-local", action="store_true", dest="test_local",
 
21
  args = parser.parse_args()
22
 
23
 
24
+
25
+
26
+
27
+
28
  if not args.test_local:
29
+
30
+
31
+
32
+
33
  import importlib.util, importlib
34
  _needs_cuda_torch = True
35
  if importlib.util.find_spec("torch") is not None:
 
65
  sys.exit(1)
66
  print("Dependencies installed.", flush=True)
67
 
68
+
69
  if not args.test_local:
70
  import torch
71
  import wandb
 
96
  from server.reward_calculator import DebugRewardCalculator
97
  from server.models import parse_agent_output
98
 
99
+
100
  MODEL_NAME = "Qwen/Qwen2.5-Coder-3B-Instruct"
101
  HF_REPO = "shashaank0707/AgentDebugger-trained"
102
  MAX_STEPS = (10 if args.test else args.max_steps) - args.step_offset
103
  CHECKPOINT_DIR = "./checkpoints"
104
 
105
+
106
  WANDB_API_KEY = os.environ.get("WANDB_API_KEY", "") if not args.test_local else ""
107
  HF_TOKEN = os.environ.get("HF_TOKEN")
108
 
 
120
  }
121
  )
122
 
123
+
124
  SYSTEM_PROMPT = """You are an expert Python debugger. You reason through bugs systematically.
125
 
126
  You MUST respond in EXACTLY this format β€” no exceptions, no extra text:
 
137
  - If proposing a fix, DETAIL must contain the complete function, not just the changed line
138
  - Give up only if you have exhausted all reasonable hypotheses"""
139
 
140
+
141
  def load_bugs(tier: int) -> list[dict]:
142
  path = f"data/bugs_tier{tier}.jsonl"
143
  if not os.path.exists(path):
 
165
  )
166
 
167
  def _run_fix(proposed_code: str, bug: dict) -> dict:
 
168
  test_cases = bug.get("test_cases", [])
169
  func_name = bug.get("function_name", "")
170
  if not proposed_code or not test_cases or not func_name:
 
180
  f" r={func_name}({args_str})\n"
181
  f" print('PASS' if r=={repr(test['expected_output'])} else 'FAIL')\n"
182
  f"except Exception as e:\n"
183
+ f" print(f'ERROR: { e} ')\n"
184
  )
185
  try:
186
  with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
 
196
 
197
  return {"passed": passed, "failed": len(test_cases) - passed, "total": len(test_cases), "newly_broken": 0}
198
 
199
+
200
  MOCK_GOOD = """
201
  OBSERVATION: The loop condition on line 4 uses <= instead of
202
  HYPOTHESIS: This causes an off-by-one error because Python lists are
 
221
  Let me try fixing it.
222
  """
223
 
224
+
225
  if args.test_local:
226
  print("=" * 60)
227
  print("LOCAL TEST MODE β€” no model loaded, testing reward function only")
 
275
  print("\nLOCAL TEST PASSED")
276
  sys.exit(0)
277
 
278
+
279
  _gpu_vram_gb = 0
280
+ _is_ampere_plus = False
281
  if torch.cuda.is_available():
282
  _props = torch.cuda.get_device_properties(0)
283
  _gpu_vram_gb = _props.total_memory / 1e9
 
288
 
289
  COMPUTE_DTYPE = torch.bfloat16 if _is_ampere_plus else torch.float16
290
 
291
+
292
+
293
+ if _gpu_vram_gb >= 70:
294
  _batch = 8
295
+ _grad_accum = 1
296
+ _num_gen = 8
297
  _max_comp = 256
298
  _lora_r = 16
299
+ elif _gpu_vram_gb >= 40:
300
  _batch = 4
301
+ _grad_accum = 2
302
+ _num_gen = 4
303
  _max_comp = 256
304
  _lora_r = 16
305
+ elif _gpu_vram_gb >= 20:
306
  _batch = 2
307
  _grad_accum = 4
308
+ _num_gen = 2
309
  _max_comp = 192
310
  _lora_r = 8
311
+ else:
312
  _batch = 2
313
  _grad_accum = 4
314
+ _num_gen = 2
315
  _max_comp = 160
316
  _lora_r = 8
317
 
 
319
  f"num_gen={_num_gen} max_comp={_max_comp} lora_r={_lora_r} "
320
  f"dtype={COMPUTE_DTYPE}")
321
 
322
+
323
+
324
+
325
  print(f"Loading {MODEL_NAME} in {COMPUTE_DTYPE}...")
326
  tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
327
  tokenizer.pad_token = tokenizer.eos_token
 
354
  model.gradient_checkpointing_enable()
355
  print(f"Trainable params: {model.num_parameters(only_trainable=True):,}")
356
 
357
+
358
  def _select_runtime_device(model) -> str:
 
 
 
359
  def _cuda_usable() -> bool:
360
  try:
361
  if not torch.cuda.is_available():
362
  return False
363
+
364
  _ = torch.zeros(1, device="cuda")
365
  return True
366
  except Exception as e:
367
  print(f"WARNING: CUDA initialization failed ({e}). Falling back to CPU.")
368
  return False
369
 
370
+
371
  try:
372
  model_device = str(next(model.parameters()).device)
373
  if model_device.startswith("cuda") and not _cuda_usable():
 
376
  except Exception:
377
  pass
378
 
379
+
380
  if _cuda_usable():
381
  return "cuda"
382
  return "cpu"
 
385
  RUNTIME_DEVICE = _select_runtime_device(model)
386
  print(f"Using generation/training runtime device: {RUNTIME_DEVICE}")
387
 
388
+
389
  calculator = DebugRewardCalculator()
390
 
391
  def reward_fn(completions: list[str], prompts: list[str], **kwargs) -> list[float]:
 
 
 
 
392
  rewards = []
393
  bugs_raw = kwargs.get("bug_metadata", [{}] * len(completions))
394
  bugs = [json.loads(b) if isinstance(b, str) else b for b in bugs_raw]
 
397
  try:
398
  agent_output = parse_agent_output(completion)
399
 
400
+
401
  test_results = {"passed": 0, "failed": 0, "total": 0, "newly_broken": 0}
402
  if agent_output.action == "propose_fix" and bug:
403
  test_results = _run_fix(agent_output.detail, bug)
 
425
 
426
  return rewards
427
 
428
+
429
  def run_baseline(n: int = 20) -> dict:
430
  print("\nRunning baseline evaluation on UNTRAINED model...")
431
  model.eval()
 
454
  baseline = run_baseline()
455
  model.train()
456
 
457
+
458
  def make_dataset(step: int) -> Dataset:
459
  bugs = get_bugs_for_step(step)
460
  return Dataset.from_list([{"prompt": bug_to_prompt(b), "bug_metadata": json.dumps(b)} for b in bugs])
461
 
462
+
463
  config = GRPOConfig(
464
  output_dir=CHECKPOINT_DIR,
465
  max_steps=MAX_STEPS,
 
472
  max_completion_length=_max_comp,
473
  temperature=0.9,
474
  logging_steps=5,
475
+ save_steps=25,
476
  save_strategy="steps",
477
  report_to="wandb" if WANDB_API_KEY else "none",
478
  )
 
485
  processing_class=tokenizer,
486
  )
487
 
488
+
489
  class CurriculumCallback(TrainerCallback):
490
  def on_step_end(self, callback_args, state, control, **kwargs):
491
  step = state.global_step + args.step_offset
 
497
 
498
  trainer.add_callback(CurriculumCallback())
499
 
500
+
501
+
502
+
503
+
504
+
505
+ HUB_PUSH_EVERY = 50
506
 
507
  class CheckpointPushCallback(TrainerCallback):
 
508
 
509
  def on_step_end(self, callback_args, state, control, **kwargs):
510
  step = state.global_step + args.step_offset
 
525
  private=True,
526
  commit_message=f"tokenizer checkpoint-step-{step}",
527
  )
528
+
529
  with open("./last_hub_push.txt", "w") as _f:
530
  _f.write(str(step))
531
  print(f"[HubPush] βœ“ Step {step} pushed to HF Hub.", flush=True)
532
  if WANDB_API_KEY:
533
  wandb.log({"hub/last_pushed_step": step})
534
  except Exception as e:
535
+
536
  print(f"[HubPush] WARNING: push failed at step {step}: {e}", flush=True)
537
 
538
+ if not args.test:
539
  trainer.add_callback(CheckpointPushCallback())
540
  print(f"HF Hub checkpoint push enabled every {HUB_PUSH_EVERY} steps β†’ {HF_REPO}-checkpoints")
541
  else:
542
  print("[TEST MODE] Hub checkpoint push disabled.")
543
 
544
+
545
  print(f"\nStarting GRPO training. Max steps: {MAX_STEPS}")
546
  print(f"Baseline solve rate: {baseline['solve_rate']:.1%} β€” target: >60% after training")
547
  trainer.train(resume_from_checkpoint=args.resume)
548
 
549
+
550
  model.eval()
551
  bugs = load_bugs(1)[:20]
552
  post_rewards = []
 
574
  wandb.log({"final/solve_rate": post_solve_rate, "final/improvement": post_solve_rate - baseline["solve_rate"]})
575
  wandb.finish()
576
 
577
+
578
  model.save_pretrained("./final_model")
579
  tokenizer.save_pretrained("./final_model")
580
 
uv.lock CHANGED
The diff for this file is too large to render. See raw diff
 
validator.py CHANGED
@@ -1,14 +1,4 @@
1
- #!/usr/bin/env python3
2
- """
3
- AgentDebuggerEnv β€” Pre-Submission Validator
4
- ============================================
5
- Checks for all hard requirements of the Meta + HF Hackathon:
6
- - Mandatory Environment Variables
7
- - OpenEnv Spec Compliance (health, reset, step, state)
8
- - Inference Script Format & Logging
9
- - Dockerfile Correctness
10
- - openenv.yaml Presence
11
- """
12
 
13
  import os
14
  import sys
@@ -17,7 +7,7 @@ import requests
17
  import yaml
18
  import re
19
 
20
- # ── Configuration ────────────────────────────────────────────────────────────
21
  ENV_BASE_URL = os.environ.get("ENV_BASE_URL", "http://localhost:8000")
22
  API_BASE_URL = os.environ.get("API_BASE_URL")
23
  MODEL_NAME = os.environ.get("MODEL_NAME")
@@ -74,7 +64,7 @@ def check_yaml():
74
  def check_endpoints():
75
  log_info(f"Checking Endpoints at {ENV_BASE_URL}...")
76
 
77
- # 1. Health
78
  try:
79
  resp = requests.get(f"{ENV_BASE_URL}/health", timeout=5)
80
  if resp.status_code == 200:
@@ -86,7 +76,7 @@ def check_endpoints():
86
  log_fail(f"Could not connect to /health: {e}")
87
  return False
88
 
89
- # 2. Reset
90
  try:
91
  resp = requests.post(f"{ENV_BASE_URL}/reset", json={"task_id": "easy"}, timeout=5)
92
  if resp.status_code == 200:
@@ -109,7 +99,7 @@ def check_inference_script():
109
  with open("inference.py", 'r') as f:
110
  content = f.read()
111
 
112
- # Check for [START], [STEP], [END]
113
  patterns = {
114
  "[START]": r"\[START\] task=",
115
  "[STEP]": r"\[STEP .+\] Action:",
@@ -137,7 +127,7 @@ def main():
137
  success &= check_yaml()
138
  success &= check_inference_script()
139
 
140
- # Endpoints check is optional if server isn't running locally
141
  try:
142
  if not check_endpoints():
143
  log_info("Skipping further endpoint checks as server is unreachable.")
 
1
+
 
 
 
 
 
 
 
 
 
 
2
 
3
  import os
4
  import sys
 
7
  import yaml
8
  import re
9
 
10
+
11
  ENV_BASE_URL = os.environ.get("ENV_BASE_URL", "http://localhost:8000")
12
  API_BASE_URL = os.environ.get("API_BASE_URL")
13
  MODEL_NAME = os.environ.get("MODEL_NAME")
 
64
  def check_endpoints():
65
  log_info(f"Checking Endpoints at {ENV_BASE_URL}...")
66
 
67
+
68
  try:
69
  resp = requests.get(f"{ENV_BASE_URL}/health", timeout=5)
70
  if resp.status_code == 200:
 
76
  log_fail(f"Could not connect to /health: {e}")
77
  return False
78
 
79
+
80
  try:
81
  resp = requests.post(f"{ENV_BASE_URL}/reset", json={"task_id": "easy"}, timeout=5)
82
  if resp.status_code == 200:
 
99
  with open("inference.py", 'r') as f:
100
  content = f.read()
101
 
102
+
103
  patterns = {
104
  "[START]": r"\[START\] task=",
105
  "[STEP]": r"\[STEP .+\] Action:",
 
127
  success &= check_yaml()
128
  success &= check_inference_script()
129
 
130
+
131
  try:
132
  if not check_endpoints():
133
  log_info("Skipping further endpoint checks as server is unreachable.")