Krsnapriya's picture
Upload folder using huggingface_hub
aa466c2 verified
Raw
History Blame Contribute Delete
10.3 kB
"""
DebugOps-RX core.
Simulates real-world debugging under constraints.
Implements the OpenEnv spec with a probabilistic noise/drift variation engine,
multi-dimensional grading, and mathematical task distribution sampling.
"""
import os
import json
import random
from typing import Tuple, List, Dict
from models import Action, Observation, HiddenState, ObservableState, Score # pyre-ignore
BUG_TYPES = [
"logic_error",
"key_error",
"dependency_error",
"state_corruption",
"stochastic_bug"
]
def sample_task_config(difficulty: str) -> dict:
ranges = {
"easy": {"n": (2, 4), "eta": (0.0, 0.2), "delta": (0.0, 0.1)},
"medium": {"n": (3, 8), "eta": (0.2, 0.5), "delta": (0.1, 0.3)},
"hard": {"n": (5, 12), "eta": (0.5, 0.8), "delta": (0.3, 0.6)},
"extreme":{"n": (8, 20), "eta": (0.7, 1.0), "delta": (0.5, 1.0)},
}
r = ranges.get(difficulty, ranges["easy"])
return {
"num_files": random.randint(*r["n"]),
"bug_type": random.choice(BUG_TYPES),
"eta": random.uniform(*r["eta"]),
"delta": random.uniform(*r["delta"])
}
def load_task_split(split: str, difficulty: str) -> dict:
if split == "train":
allowed = ["logic_error", "key_error"]
elif split == "test":
allowed = ["logic_error", "key_error"]
else: # ood
allowed = ["state_corruption", "stochastic_bug", "dependency_error"]
while True:
task = sample_task_config(difficulty)
if task["bug_type"] in allowed:
task["split"] = split
return task
def inject_noise(logs: str, eta: float) -> str:
"""Probabilistic corruption of logs based on eta."""
noisy = logs
if random.random() < eta:
noisy += "\\n[Warning] Deprecated API usage"
if random.random() < 0.7 * eta:
noisy = noisy.replace("service.py", "validator.py")
if random.random() < eta:
noisy += "\\n[Info] Latency spike detected"
return noisy
class DebugOpsEnv:
def __init__(self, data_dir: str = "datasets", seed: int = None):
self.data_dir = data_dir
if seed is not None:
random.seed(seed)
self.state: ObservableState = None # pyre-ignore
self.hidden: HiddenState = None # pyre-ignore
self.trajectory: List[Action] = []
def _load_task(self, split: str, difficulty: str) -> ObservableState:
# Sample configuration
config = load_task_split(split, difficulty)
n = config["num_files"]
bug_type = config["bug_type"]
eta = config["eta"]
delta = config["delta"]
# To build a realistic benchmark out of our 4 physical base templates,
# we load the base template that closely matches the difficulty,
# then mock additional files up to `n`.
base_dir = f"{difficulty}_01"
task_path = os.path.join(self.data_dir, base_dir)
if not os.path.exists(task_path):
task_path = os.path.join(self.data_dir, "easy_01")
with open(os.path.join(task_path, "logs.txt")) as f:
raw_logs = f.read()
with open(os.path.join(task_path, "tests.txt")) as f:
self._pristine_tests = f.read()
# Load repo files
repo_path = os.path.join(task_path, "repo")
files = {}
target_bug_loc = "utils.py"
if os.path.exists(repo_path):
for fname in os.listdir(repo_path):
if fname.endswith(".py"):
with open(os.path.join(repo_path, fname)) as f:
files[fname] = f.read()
target_bug_loc = fname # roughly heuristic
# Override target logic based on physical dataset knowns
if "utils.py" in files: target_bug_loc = "utils.py"
elif "parser.py" in files: target_bug_loc = "parser.py"
elif "service.py" in files: target_bug_loc = "service.py"
elif "api.py" in files: target_bug_loc = "api.py"
# Mock extra files
for i in range(len(files), n):
files[f"module_{i}.py"] = f"# Autogenerated mock file {i}\\ndef do_nothing():\\n pass\\n"
# Hidden Truth
self.hidden = HiddenState(
true_bug_locations=[target_bug_loc],
bug_type=bug_type,
eta=eta,
delta=delta,
dependency_graph={}
)
noisy_logs = inject_noise(raw_logs, eta)
return ObservableState(
files=files.copy(),
original_files=files.copy(),
bug_location=target_bug_loc,
difficulty=difficulty,
split=split,
steps_taken=0,
max_steps={"easy": 10, "medium": 15, "hard": 20, "extreme": 25}.get(difficulty, 15),
resolved=False,
files_opened=[],
edits_made=[],
tests_run=0,
logs_analyzed=0
)
def _get_observation(self) -> Observation:
return Observation(
visible_files=self.state.files_opened.copy(),
logs=self._current_logs if hasattr(self, "_current_logs") else "",
test_results=self._current_tests if hasattr(self, "_current_tests") else None,
time_remaining=self.state.max_steps - self.state.steps_taken
)
def reset(self, difficulty="easy", split="test") -> Observation:
self.state = self._load_task(split, difficulty)
self.trajectory = []
self._current_logs = ""
self._current_tests = None
return self._get_observation()
def _maybe_drift(self):
"""Temporal Drift Model."""
if random.random() < self.hidden.delta:
if hasattr(self, "_current_logs") and self._current_logs:
self._current_logs += "\\n[Runtime] New intermittent failure detected"
def step(self, action: Action) -> Tuple[Observation, float, bool, dict]:
if self.state is None: raise ValueError("Call reset() first.")
reward = 0.0
done = False
info = {}
self.state.steps_taken += 1
self.trajectory.append(action)
# 1. Action Layer
if action.type == "open_file":
if action.target in self.state.files:
if action.target not in self.state.files_opened:
self.state.files_opened.append(action.target)
if action.target == self.state.bug_location:
reward += 0.05
else: reward -= 0.1
elif action.type == "analyze_logs":
base_dir = f"{self.state.difficulty}_01"
task_path = os.path.join(self.data_dir, base_dir)
if not os.path.exists(task_path): task_path = os.path.join(self.data_dir, "easy_01")
with open(os.path.join(task_path, "logs.txt")) as f:
base_logs = f.read()
self._current_logs = inject_noise(base_logs, self.hidden.eta)
self.state.logs_analyzed += 1
reward += 0.05
elif action.type == "edit_file":
if action.target in self.state.files and action.content:
self.state.edits_made.append({"target": action.target, "content": action.content})
self.state.files[action.target] = action.content
if action.target == self.state.bug_location: reward += 0.4
else: reward -= 0.1
else: reward -= 0.1
elif action.type == "run_tests":
self.state.tests_run += 1
if self._is_fixed():
self._current_tests = "✅ TESTS PASSED"
reward += 1.0
self.state.resolved = True
done = True
else:
self._current_tests = f"❌ TESTS FAILED\\n{self._pristine_tests}"
reward -= 0.2
# 2. Simulate Drift
self._maybe_drift()
if self.state.steps_taken >= self.state.max_steps:
reward -= 0.5
done = True
return self._get_observation(), reward, done, info
def _is_fixed(self) -> bool:
diff = self.state.difficulty
if not self.state.edits_made: return False
latest_edits = {e["target"]: e["content"] for e in self.state.edits_made}
if diff == "easy" and "utils.py" in latest_edits:
if "* 10" not in latest_edits["utils.py"]: return True
elif diff == "medium" and "parser.py" in latest_edits:
if "valid" in latest_edits["parser.py"]: return True
elif diff == "hard" and "service.py" in latest_edits:
if "score" in latest_edits["service.py"] and "points" not in latest_edits["service.py"]: return True
elif diff == "extreme" and "api.py" in latest_edits:
if "result + 1" not in latest_edits["api.py"]: return True
return False
def grade(self, trajectory: List[Action]) -> Score:
"""Computes the final multi-dimensional vector score using strict math."""
# Correctness
correctness = 1.0 if self.state.resolved else 0.0
# Efficiency
efficiency = max(0.0, 1.0 - (self.state.steps_taken / self.state.max_steps))
# Reasoning (Mathematical Formula)
visited = set()
repeated = 0
tests = 0
seen = set()
for a in trajectory:
if a.type == "open_file" and a.target:
visited.add(a.target)
if a.type == "run_tests": tests += 1
key = (a.type, a.target)
if key in seen: repeated += 1
seen.add(key)
total_files = len(self.state.files)
exploration = len(visited) / max(total_files, 1)
test_usage = tests / max(len(trajectory), 1)
redundancy = repeated / max(len(trajectory), 1)
reasoning_quality = max(0.0, 0.5 * exploration + 0.3 * test_usage - 0.2 * redundancy)
# Robustness
wrong_edits = sum(1 for a in trajectory if a.type == "edit_file" and a.target != self.state.bug_location)
robustness = max(0.0, 1.0 - (wrong_edits * 0.3))
return Score(
correctness=correctness,
efficiency=efficiency,
reasoning_quality=reasoning_quality,
robustness=robustness
)