from env.codedebugger_env import CodeDebuggerEnv from agents.fixer import FixerAgent class Debugger: def __init__(self, max_iterations=5): self.env = CodeDebuggerEnv(max_iterations) self.fixer = FixerAgent() def run(self, problem: dict, verbose=True) -> dict: """ Full debugging loop for one problem. """ obs = self.env.reset(problem) best_reward = -float('inf') best_code = None best_iter = 0 iter_data = [] reward_gain = 0.0 iter1_reward = 0.0 final_reward = 0.0 tests_passed_final = 0 tests_total = len(problem.get("test_cases", [])) solved = False if verbose: print(f"\n--- Debugging: {problem['id']} ({problem.get('title', '')}) ---") for i in range(1, self.env.max_iterations + 1): # Format history for fixer prev_explanation = None test_results = None if iter_data: # Use results from the immediate previous iteration last_env_hist = self.env.history[-1] test_results = last_env_hist["test_results"] prev_explanation = iter_data[-1]["fix_result"].get("explanation") # On first iteration, fix the original buggy code. # On subsequent iterations, try to fix the previously generated code. code_to_fix = obs["buggy_code"] if i == 1 else self.env.history[-1]["code"] # Propose fix fix_result = self.fixer.fix_code( buggy_code=code_to_fix, error_type=problem.get("error_type", ""), description=problem.get("description", ""), test_cases=problem.get("test_cases", []), test_results=test_results, previous_explanation=prev_explanation, iteration=i ) fixed_code = fix_result.get("fixed_code", code_to_fix) # Step environment obs, reward_dict, done, info = self.env.step(fixed_code) current_reward = reward_dict["total"] if i == 1: iter1_reward = current_reward final_reward = current_reward tests_passed_final = obs["test_results"].get("tests_passed", 0) # Track best separately if current_reward > best_reward: best_reward = current_reward best_code = fixed_code best_iter = i iter_info = { "iteration": i, "fix_result": fix_result, "reward": reward_dict, "done": done, "info": info } iter_data.append(iter_info) if verbose: print(f"Iter {i}: passed {tests_passed_final}/{tests_total} | " f"reward: {current_reward:.1f} | method: {fix_result.get('method')}") if done: if info.get("solved"): solved = True if verbose: print("-> Solved!") break # Calculate gain from first iteration if best_reward != -float('inf'): reward_gain = best_reward - iter1_reward else: best_reward = 0.0 return { "problem_id": problem["id"], "difficulty": problem["difficulty"], "title": problem.get("title", ""), "n_iterations": len(iter_data), "final_reward": final_reward, "best_reward": best_reward, "iter1_reward": iter1_reward, "best_iter": best_iter, "best_code": best_code, "reward_gain": reward_gain, "tests_passed_final": tests_passed_final, "tests_total": tests_total, "solved": solved, "iterations": iter_data } def run_all(self, problems: list, verbose=True) -> list: """Run all problems, return list of results""" results = [] for prob in problems: res = self.run(prob, verbose=verbose) results.append(res) return results def print_summary_table(self, results: list): """ Print this exact format: Problem Diff iter1 final best gain solved ---------------------------------------------------------------- ... """ print(f"\n{'Problem':<20} {'Diff':<7} {'iter1':>6} {'final':>6} {'best':>6} {'gain':>6} {'solved':>6}") print("-" * 64) solved_count = 0 total_best_reward = 0.0 for r in results: pid = str(r["problem_id"]) if len(pid) > 20: pid = pid[:17] + "..." diff = str(r["difficulty"]) iter1 = r.get("iter1_reward", 0.0) final = r["final_reward"] best = r["best_reward"] gain = r["reward_gain"] solved_str = "YES" if r["solved"] else "NO" print(f"{pid:<20} {diff:<7} {iter1:>6.1f} {final:>6.1f} {best:>6.1f} {gain:>+6.1f} {solved_str:>6}") if r["solved"]: solved_count += 1 total_best_reward += best print("-" * 64) avg_best = total_best_reward / len(results) if results else 0.0 print(f"TOTAL: {solved_count}/{len(results)} solved | Avg best reward: {avg_best:.1f}")