dsh-eval-guard 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/ISSUES.md +235 -0
  2. package/README.md +46 -18
  3. package/assets/neural-cifar/workspace.zip +0 -0
  4. package/assets/neural-cpp/workspace.zip +0 -0
  5. package/assets/neural-fp32/workspace.zip +0 -0
  6. package/assets/neural-pendulum/SPEC.md +41 -38
  7. package/assets/neural-pendulum/TASK.md +146 -32
  8. package/assets/neural-pendulum/sim.cpp +4 -2
  9. package/assets/neural-pendulum/sim_eval +0 -0
  10. package/assets/neural-pendulum/workspace.zip +0 -0
  11. package/eval/README.md +19 -9
  12. package/eval/__pycache__/bomberman.cpython-310.pyc +0 -0
  13. package/eval/__pycache__/commit0.cpython-310.pyc +0 -0
  14. package/eval/__pycache__/common.cpython-310.pyc +0 -0
  15. package/eval/__pycache__/ctf.cpython-310.pyc +0 -0
  16. package/eval/__pycache__/featurebench.cpython-310.pyc +0 -0
  17. package/eval/__pycache__/land.cpython-310.pyc +0 -0
  18. package/eval/__pycache__/neural-cifar.cpython-310.pyc +0 -0
  19. package/eval/__pycache__/neural-cpp.cpython-310.pyc +0 -0
  20. package/eval/__pycache__/neural-fp32.cpython-310.pyc +0 -0
  21. package/eval/__pycache__/neural-pendulum.cpython-310.pyc +0 -0
  22. package/eval/__pycache__/swe-bench-pro.cpython-310.pyc +0 -0
  23. package/eval/__pycache__/tank.cpython-310.pyc +0 -0
  24. package/eval/__pycache__/warehouse.cpython-310.pyc +0 -0
  25. package/eval/bomberman.py +5 -3
  26. package/eval/commit0.py +10 -3
  27. package/eval/common.py +169 -5
  28. package/eval/ctf.py +4 -2
  29. package/eval/featurebench.py +12 -8
  30. package/eval/land.py +4 -2
  31. package/eval/legacy/__pycache__/bomberman.cpython-310.pyc +0 -0
  32. package/eval/legacy/__pycache__/commit0.cpython-310.pyc +0 -0
  33. package/eval/legacy/__pycache__/ctf.cpython-310.pyc +0 -0
  34. package/eval/legacy/__pycache__/featurebench.cpython-310.pyc +0 -0
  35. package/eval/legacy/__pycache__/land.cpython-310.pyc +0 -0
  36. package/eval/legacy/__pycache__/swe_bench_pro.cpython-310.pyc +0 -0
  37. package/eval/legacy/__pycache__/tank.cpython-310.pyc +0 -0
  38. package/eval/legacy/__pycache__/warehouse.cpython-310.pyc +0 -0
  39. package/eval/legacy/bomberman.py +5 -2
  40. package/eval/legacy/ctf.py +5 -2
  41. package/eval/legacy/featurebench.py +55 -15
  42. package/eval/legacy/land.py +6 -3
  43. package/eval/legacy/swe_bench_pro.py +57 -16
  44. package/eval/legacy/tank.py +2 -2
  45. package/eval/legacy/warehouse.py +4 -4
  46. package/eval/neural-cpp.py +9 -6
  47. package/eval/neural-fp32.py +17 -5
  48. package/eval/swe-bench-pro.py +11 -7
  49. package/eval/tank.py +11 -2
  50. package/eval/warehouse.py +17 -1
  51. package/lib/client/index.js +73 -46
  52. package/lib/client/index.js.map +1 -1
  53. package/lib/index.js +894 -301
  54. package/native/dsh-eval-supervisor +0 -0
  55. package/native/supervisor.c +190 -26
  56. package/neural-cifar-eval/README.md +5 -0
  57. package/neural-cifar-eval/__pycache__/cifar_cache.cpython-310.pyc +0 -0
  58. package/neural-cifar-eval/__pycache__/common.cpython-310.pyc +0 -0
  59. package/neural-cifar-eval/__pycache__/fetch_cifar.cpython-310.pyc +0 -0
  60. package/neural-cifar-eval/__pycache__/ops_eval.cpython-310.pyc +0 -0
  61. package/neural-cifar-eval/__pycache__/run_eval.cpython-310.pyc +0 -0
  62. package/neural-cifar-eval/common.py +13 -0
  63. package/neural-cifar-eval/ops_eval.py +17 -5
  64. package/neural-cifar-eval/run_eval.py +42 -32
  65. package/neural-pendulum-eval/README.md +62 -0
  66. package/neural-pendulum-eval/__pycache__/ops_eval.cpython-310.pyc +0 -0
  67. package/neural-pendulum-eval/__pycache__/run_eval.cpython-310.pyc +0 -0
  68. package/neural-pendulum-eval/ops_eval.py +603 -0
  69. package/neural-pendulum-eval/run_eval.py +218 -137
  70. package/neural-pendulum-eval/sim_eval +0 -0
  71. package/package.json +7 -11
  72. package/assets/neural-pendulum/hidden/test_infra.cpp +0 -631
  73. package/assets/neural-pendulum/include/nn/nn.hpp +0 -88
@@ -107,13 +107,16 @@ def main() -> int:
107
107
  cmd = [sys.executable, "-X", "utf8", str(task / "tournament" / "run.py"), "--output", str(run_dir),
108
108
  "--bots", *bots, "--sizes", *sizes, "--seeds", str(seeds), "--workers", str(max(1, args.workers))]
109
109
  print("tier=%s sizes=%s seeds=%d bots=%d" % (args.tier, sizes, seeds, len(bots)), flush=True)
110
- completed = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", errors="replace")
110
+ completed = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=3600)
111
111
  if completed.returncode != 0:
112
112
  print(completed.stdout[-4000:], completed.stderr[-4000:], file=sys.stderr)
113
113
  return 2
114
114
  ratings_doc = load_json(run_dir / "bt.json") or {}
115
+ if ratings_doc.get("unavailable"):
116
+ print("bomberman rating unavailable: " + str(ratings_doc.get("unavailable")), file=sys.stderr)
117
+ return 2
115
118
  ratings = ratings_doc.get("ratings", {})
116
- prices = load_json(ROOT / "prices.json") or {}
119
+ prices = load_json(ROOT / "bomberman-prices.json") or load_json(ROOT / "prices.json") or {}
117
120
 
118
121
  rows = []
119
122
  for name, program in programs.items():
@@ -123,14 +123,17 @@ def main() -> int:
123
123
  print("tier=%s sizes=%s maps=%d players=%d" % (args.tier, sizes, maps_per_size, len(players)), flush=True)
124
124
  for step in steps:
125
125
  completed = subprocess.run([sys.executable, "-X", "utf8", tournament, *step], capture_output=True, text=True,
126
- encoding="utf-8", errors="replace")
126
+ encoding="utf-8", errors="replace", timeout=1800)
127
127
  if completed.returncode != 0:
128
128
  print(completed.stdout[-4000:], completed.stderr[-4000:], file=sys.stderr)
129
129
  return 2
130
130
  scores = load_json(games_dir / "成绩.json") or {}
131
+ if scores.get("unavailable"):
132
+ print("ctf rating unavailable: " + str(scores.get("unavailable")), file=sys.stderr)
133
+ return 2
131
134
  raw_ratings = scores.get("ratings", {})
132
135
  ratings = {name: (value[0] if isinstance(value, list) and value else value) for name, value in raw_ratings.items()}
133
- prices = load_json(ROOT / "prices.json") or {}
136
+ prices = load_json(ROOT / "ctf-prices.json") or load_json(ROOT / "prices.json") or {}
134
137
 
135
138
  rows = []
136
139
  for name, program in players.items():
@@ -4,8 +4,9 @@
4
4
  Submission layout:
5
5
  submissions/<name>/workspace/ (copy of the agent workspace, including .venv)
6
6
 
7
- The hidden feature test file is copied from the plugin asset; scoring runs the
8
- instance's fail-to-pass and pass-to-pass files. Missing run/ statistics are skipped.
7
+ The hidden feature test file is copied from the plugin asset; scoring checks the
8
+ fail-to-pass file and every pass-to-pass file explicitly (a skipped or removed
9
+ test does not count as resolved).
9
10
  """
10
11
  from __future__ import annotations
11
12
 
@@ -23,6 +24,7 @@ ASSET = ROOT.parents[1] / "assets" / "featurebench"
23
24
  PASSED = re.compile(r"(\d+) passed")
24
25
  FAILED = re.compile(r"(\d+) failed")
25
26
  ERRORS = re.compile(r"(\d+) error")
27
+ RESULT = re.compile(r"^(PASSED|FAILED|ERROR|SKIPPED|XFAIL|XPASS) (\S+)", re.MULTILINE)
26
28
 
27
29
 
28
30
  def load_json(path: Path):
@@ -39,18 +41,29 @@ def find_workspace(directory: Path) -> Path | None:
39
41
  return None
40
42
 
41
43
 
42
- def install_hidden_test(workspace: Path) -> None:
44
+ def install_hidden_test(workspace: Path) -> str:
43
45
  source = ASSET / "hidden" / "test_regression.py"
44
- if source.is_file():
45
- shutil.copy2(source, workspace / "tests" / "test_regression.py")
46
+ if not source.is_file():
47
+ return "hidden regression test is missing"
48
+ shutil.copy2(source, workspace / "tests" / "test_regression.py")
49
+ return ""
46
50
 
47
51
 
48
- def run_tests(workspace: Path, instance: dict) -> tuple[int, int, int, str]:
52
+ def matches(node: str, selectors: list[str]) -> bool:
53
+ for selector in selectors:
54
+ if node == selector or node.startswith(selector + "::") or node.startswith(selector + "/"):
55
+ return True
56
+ return False
57
+
58
+
59
+ def run_tests(workspace: Path, instance: dict) -> tuple[int, int, int, str, dict]:
49
60
  venv = workspace / ".venv" / "bin" / "python"
50
61
  python = str(venv) if venv.is_file() else "python3"
51
- tests = list(instance.get("FAIL_TO_PASS", [])) + list(instance.get("PASS_TO_PASS", []))
62
+ fail_to_pass = list(instance.get("FAIL_TO_PASS", []))
63
+ pass_to_pass = list(instance.get("PASS_TO_PASS", []))
64
+ tests = fail_to_pass + pass_to_pass
52
65
  env = {k: v for k, v in __import__("os").environ.items() if k not in {"PYTHONPATH", "PYTHONHOME"}}
53
- completed = subprocess.run([python, "-m", "pytest", *tests, "-q", "--tb=no"], cwd=workspace,
66
+ completed = subprocess.run([python, "-m", "pytest", *tests, "-q", "--tb=no", "-rA"], cwd=workspace,
54
67
  capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=1800, env=env)
55
68
  output = (completed.stdout or "") + "\n" + (completed.stderr or "")
56
69
  passed = int(PASSED.search(output).group(1)) if PASSED.search(output) else 0
@@ -58,7 +71,31 @@ def run_tests(workspace: Path, instance: dict) -> tuple[int, int, int, str]:
58
71
  errors = int(ERRORS.search(output).group(1)) if ERRORS.search(output) else 0
59
72
  if completed.returncode != 0 and passed == 0 and failed == 0 and errors == 0:
60
73
  errors = 1
61
- return passed, failed, errors, output[-3000:]
74
+ outcomes = {node: status for status, node in RESULT.findall(output)}
75
+ f2p_total = f2p_passed = 0
76
+ p2p_total = p2p_passed = 0
77
+ for node, status in outcomes.items():
78
+ if matches(node, fail_to_pass):
79
+ f2p_total += 1
80
+ f2p_passed += 1 if status == "PASSED" else 0
81
+ elif matches(node, pass_to_pass):
82
+ p2p_total += 1
83
+ p2p_passed += 1 if status == "PASSED" else 0
84
+ resolved = (
85
+ len(fail_to_pass) > 0
86
+ and f2p_total > 0
87
+ and f2p_passed == f2p_total
88
+ and p2p_total > 0
89
+ and p2p_passed == p2p_total
90
+ and completed.returncode == 0
91
+ )
92
+ return passed, failed, errors, output[-3000:], {
93
+ "resolved": resolved,
94
+ "f2p_total": f2p_total,
95
+ "f2p_passed": f2p_passed,
96
+ "p2p_total": p2p_total,
97
+ "p2p_passed": p2p_passed,
98
+ }
62
99
 
63
100
 
64
101
  def main() -> int:
@@ -78,20 +115,23 @@ def main() -> int:
78
115
  for directory in directories:
79
116
  workspace = find_workspace(directory)
80
117
  if workspace is None:
81
- rows.append({"name": directory.name, "status": "missing-workspace", "score": 0.0, "passed": 0, "failed": 0, "errors": 0})
118
+ rows.append({"name": directory.name, "status": "missing-workspace", "score": 0.0, "passed": 0, "failed": 0, "errors": 0, "resolved": False})
82
119
  continue
83
120
  try:
84
- install_hidden_test(workspace)
85
- passed, failed, errors, detail = run_tests(workspace, instance)
121
+ hidden_error = install_hidden_test(workspace)
122
+ if hidden_error:
123
+ rows.append({"name": directory.name, "status": "hidden-test-failed", "score": 0.0, "passed": 0, "failed": 0, "errors": 1, "resolved": False, "detail": hidden_error})
124
+ continue
125
+ passed, failed, errors, detail, extra = run_tests(workspace, instance)
86
126
  total = passed + failed + errors
87
127
  score = 100.0 * passed / total if total else 0.0
88
- rows.append({"name": directory.name, "status": "ok", "score": round(score, 2), "passed": passed, "failed": failed, "errors": errors, "detail": detail})
128
+ rows.append({"name": directory.name, "status": "ok", "score": round(score, 2), "passed": passed, "failed": failed, "errors": errors, "detail": detail, **extra})
89
129
  except Exception as error:
90
- rows.append({"name": directory.name, "status": "error", "score": 0.0, "passed": 0, "failed": 0, "errors": 1, "detail": str(error)})
130
+ rows.append({"name": directory.name, "status": "error", "score": 0.0, "passed": 0, "failed": 0, "errors": 1, "resolved": False, "detail": str(error)})
91
131
  rows.sort(key=lambda row: (-row["score"], row["name"]))
92
132
  payload = {"generatedAt": time.strftime("%Y-%m-%dT%H:%M:%S%z"), "benchmark": "featurebench", "instance": instance.get("instance_id"), "results": rows}
93
133
  (out / "summary.json").write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
94
- lines = ["# FeatureBench · 单 feature 排行", "", "- 实例:%s" % instance.get("instance_id", ""), "- 指标:隐藏 fail-to-pass + pass-to-pass 测试通过率", "", "| 名次 | 名称 | 得分 | 通过 | 失败 | 错误 | 状态 |", "|---:|---|---:|---:|---:|---:|---|"]
134
+ lines = ["# FeatureBench · 单 feature 排行", "", "- 实例:%s" % instance.get("instance_id", ""), "- 指标:隐藏 feature 测试与全部回归文件通过才 resolved", "", "| 名次 | 名称 | 得分 | 通过 | 失败 | 错误 | 状态 |", "|---:|---|---:|---:|---:|---:|---|"]
95
135
  for rank, row in enumerate(rows, 1):
96
136
  lines.append("| %d | %s | %.1f | %d | %d | %d | %s |" % (rank, row["name"], row["score"], row["passed"], row["failed"], row["errors"], row["status"]))
97
137
  (out / "summary.md").write_text("\n".join(lines) + "\n", encoding="utf-8")
@@ -112,20 +112,23 @@ def main() -> int:
112
112
  if melee == 0:
113
113
  cmd += ["--only", "duel"]
114
114
  print("tier=%s maps=%d melee=%d entries=%d" % (args.tier, maps, melee, len(list(entries.iterdir()))), flush=True)
115
- completed = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", errors="replace")
115
+ completed = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=1800)
116
116
  if completed.returncode != 0:
117
117
  print(completed.stdout[-4000:], completed.stderr[-4000:], file=sys.stderr)
118
118
  return 2
119
119
  ratings_prefix = out / "ratings"
120
120
  rate = subprocess.run([sys.executable, "-X", "utf8", str(task / "tournament" / "rate.py"), str(results_jsonl),
121
121
  "--entries", str(entries), "--out", str(ratings_prefix)], capture_output=True, text=True,
122
- encoding="utf-8", errors="replace")
122
+ encoding="utf-8", errors="replace", timeout=1800)
123
123
  if rate.returncode != 0:
124
124
  print(rate.stdout[-4000:], rate.stderr[-4000:], file=sys.stderr)
125
125
  return 2
126
126
  ratings = load_json(Path(str(ratings_prefix) + ".json")) or {}
127
+ if ratings.get("unavailable"):
128
+ print("land rating unavailable: " + str(ratings.get("unavailable")), file=sys.stderr)
129
+ return 2
127
130
  models = ratings.get("models", {})
128
- prices = load_json(ROOT / "prices.json") or {}
131
+ prices = load_json(ROOT / "land-prices.json") or load_json(ROOT / "prices.json") or {}
129
132
 
130
133
  rows = []
131
134
  all_names = list(names) + [name for name in models if name not in names]
@@ -4,8 +4,9 @@
4
4
  Submission layout:
5
5
  submissions/<name>/workspace/ (copy of the agent workspace, including .venv)
6
6
 
7
- The hidden test patch is applied from the plugin asset; scoring runs the
8
- instance's fail-to-pass and pass-to-pass tests. Missing run/ statistics are skipped.
7
+ The hidden test patch is applied from the plugin asset; scoring checks the
8
+ instance's fail-to-pass and pass-to-pass node ids explicitly (a skipped or
9
+ renamed fail-to-pass test does not count as resolved).
9
10
  """
10
11
  from __future__ import annotations
11
12
 
@@ -22,6 +23,7 @@ ASSET = ROOT.parents[1] / "assets" / "swe-bench-pro"
22
23
  PASSED = re.compile(r"(\d+) passed")
23
24
  FAILED = re.compile(r"(\d+) failed")
24
25
  ERRORS = re.compile(r"(\d+) error")
26
+ RESULT = re.compile(r"^(PASSED|FAILED|ERROR|SKIPPED|XFAIL|XPASS) (\S+)", re.MULTILINE)
25
27
 
26
28
 
27
29
  def load_json(path: Path):
@@ -48,22 +50,34 @@ def find_workspace(directory: Path) -> Path | None:
48
50
  return None
49
51
 
50
52
 
51
- def apply_hidden_tests(workspace: Path) -> None:
53
+ def apply_hidden_tests(workspace: Path) -> str:
52
54
  patch = ASSET / "hidden" / "test.patch"
53
55
  if not patch.is_file():
54
- return
55
- subprocess.run(["patch", "-p1", "--forward", "-i", str(patch)], cwd=workspace,
56
- capture_output=True, text=True, encoding="utf-8", errors="replace")
56
+ return "hidden test patch is missing"
57
+ completed = subprocess.run(["patch", "-p1", "--forward", "-i", str(patch)], cwd=workspace,
58
+ capture_output=True, text=True, encoding="utf-8", errors="replace")
59
+ if completed.returncode != 0:
60
+ return "hidden test patch failed to apply: " + (completed.stdout + completed.stderr)[-2000:]
61
+ return ""
57
62
 
58
63
 
59
- def run_tests(workspace: Path, instance: dict) -> tuple[int, int, int, str]:
64
+ def matches(node: str, selectors: list[str]) -> bool:
65
+ for selector in selectors:
66
+ if node == selector or node.startswith(selector + "::") or node.startswith(selector + "/"):
67
+ return True
68
+ return False
69
+
70
+
71
+ def run_tests(workspace: Path, instance: dict) -> tuple[int, int, int, str, dict]:
60
72
  venv = workspace / ".venv" / "bin" / "python"
61
73
  python = str(venv) if venv.is_file() else "python3"
62
- tests = as_list(instance.get("fail_to_pass")) + as_list(instance.get("pass_to_pass"))
74
+ fail_to_pass = as_list(instance.get("fail_to_pass"))
75
+ pass_to_pass = as_list(instance.get("pass_to_pass"))
76
+ tests = fail_to_pass + pass_to_pass
63
77
  if not tests:
64
78
  tests = as_list(instance.get("selected_test_files_to_run"))
65
79
  env = {k: v for k, v in __import__("os").environ.items() if k not in {"PYTHONPATH", "PYTHONHOME"}}
66
- completed = subprocess.run([python, "-m", "pytest", *tests, "-q", "--tb=no"], cwd=workspace,
80
+ completed = subprocess.run([python, "-m", "pytest", *tests, "-q", "--tb=no", "-rA"], cwd=workspace,
67
81
  capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=1800, env=env)
68
82
  output = (completed.stdout or "") + "\n" + (completed.stderr or "")
69
83
  passed = int(PASSED.search(output).group(1)) if PASSED.search(output) else 0
@@ -71,7 +85,31 @@ def run_tests(workspace: Path, instance: dict) -> tuple[int, int, int, str]:
71
85
  errors = int(ERRORS.search(output).group(1)) if ERRORS.search(output) else 0
72
86
  if completed.returncode != 0 and passed == 0 and failed == 0 and errors == 0:
73
87
  errors = 1
74
- return passed, failed, errors, output[-3000:]
88
+ outcomes = {node: status for status, node in RESULT.findall(output)}
89
+ f2p_total = f2p_passed = 0
90
+ p2p_total = p2p_passed = 0
91
+ for node, status in outcomes.items():
92
+ if matches(node, fail_to_pass):
93
+ f2p_total += 1
94
+ f2p_passed += 1 if status == "PASSED" else 0
95
+ elif matches(node, pass_to_pass):
96
+ p2p_total += 1
97
+ p2p_passed += 1 if status == "PASSED" else 0
98
+ resolved = (
99
+ len(fail_to_pass) > 0
100
+ and f2p_total > 0
101
+ and f2p_passed == f2p_total
102
+ and p2p_total > 0
103
+ and p2p_passed == p2p_total
104
+ and completed.returncode == 0
105
+ )
106
+ return passed, failed, errors, output[-3000:], {
107
+ "resolved": resolved,
108
+ "f2p_total": f2p_total,
109
+ "f2p_passed": f2p_passed,
110
+ "p2p_total": p2p_total,
111
+ "p2p_passed": p2p_passed,
112
+ }
75
113
 
76
114
 
77
115
  def main() -> int:
@@ -91,20 +129,23 @@ def main() -> int:
91
129
  for directory in directories:
92
130
  workspace = find_workspace(directory)
93
131
  if workspace is None:
94
- rows.append({"name": directory.name, "status": "missing-workspace", "score": 0.0, "passed": 0, "failed": 0, "errors": 0})
132
+ rows.append({"name": directory.name, "status": "missing-workspace", "score": 0.0, "passed": 0, "failed": 0, "errors": 0, "resolved": False})
95
133
  continue
96
134
  try:
97
- apply_hidden_tests(workspace)
98
- passed, failed, errors, detail = run_tests(workspace, instance)
135
+ patch_error = apply_hidden_tests(workspace)
136
+ if patch_error:
137
+ rows.append({"name": directory.name, "status": "hidden-test-failed", "score": 0.0, "passed": 0, "failed": 0, "errors": 1, "resolved": False, "detail": patch_error})
138
+ continue
139
+ passed, failed, errors, detail, extra = run_tests(workspace, instance)
99
140
  total = passed + failed + errors
100
141
  score = 100.0 * passed / total if total else 0.0
101
- rows.append({"name": directory.name, "status": "ok", "score": round(score, 2), "passed": passed, "failed": failed, "errors": errors, "detail": detail})
142
+ rows.append({"name": directory.name, "status": "ok", "score": round(score, 2), "passed": passed, "failed": failed, "errors": errors, "detail": detail, **extra})
102
143
  except Exception as error:
103
- rows.append({"name": directory.name, "status": "error", "score": 0.0, "passed": 0, "failed": 0, "errors": 1, "detail": str(error)})
144
+ rows.append({"name": directory.name, "status": "error", "score": 0.0, "passed": 0, "failed": 0, "errors": 1, "resolved": False, "detail": str(error)})
104
145
  rows.sort(key=lambda row: (-row["score"], row["name"]))
105
146
  payload = {"generatedAt": time.strftime("%Y-%m-%dT%H:%M:%S%z"), "benchmark": "swe-bench-pro", "instance": instance.get("instance_id"), "results": rows}
106
147
  (out / "summary.json").write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
107
- lines = ["# SWE-Bench Pro · 单任务排行", "", "- 实例:%s" % instance.get("instance_id", ""), "- 指标:隐藏 fail-to-pass + pass-to-pass 测试通过率", "", "| 名次 | 名称 | 得分 | 通过 | 失败 | 错误 | 状态 |", "|---:|---|---:|---:|---:|---:|---|"]
148
+ lines = ["# SWE-Bench Pro · 单任务排行", "", "- 实例:%s" % instance.get("instance_id", ""), "- 指标:隐藏 fail-to-pass + pass-to-pass 节点全部通过才 resolved", "", "| 名次 | 名称 | 得分 | 通过 | 失败 | 错误 | 状态 |", "|---:|---|---:|---:|---:|---:|---|"]
108
149
  for rank, row in enumerate(rows, 1):
109
150
  lines.append("| %d | %s | %.1f | %d | %d | %d | %s |" % (rank, row["name"], row["score"], row["passed"], row["failed"], row["errors"], row["status"]))
110
151
  (out / "summary.md").write_text("\n".join(lines) + "\n", encoding="utf-8")
@@ -258,8 +258,8 @@ def main() -> int:
258
258
  if not directories:
259
259
  print("no submissions found under " + str(submissions_root), file=sys.stderr)
260
260
  return 2
261
- prices_path = ROOT / "prices.json"
262
- prices = load_json(prices_path) or {}
261
+ prices_path = ROOT / "tank-prices.json"
262
+ prices = load_json(prices_path) or load_json(ROOT / "prices.json") or {}
263
263
 
264
264
  participants: list[Participant] = [Participant("baseline", workspace / "baseline.cpp", "baseline")]
265
265
  participants += [load_submission(directory) for directory in directories]
@@ -2,9 +2,9 @@
2
2
  """Independent evaluation for dsh-eval-guard warehouse submissions.
3
3
 
4
4
  Reads submissions/<name>/{src/*.cpp,run/,cost.json}, compiles with the fixed
5
- g++ flags, compares every public workload byte-for-byte with the expected
6
- output, measures CPU time against the baseline, and writes a leaderboard plus
7
- cost/violation/static-scan reports.
5
+ g++ flags, compares every public workload byte-for-byte (CRLF normalized) with
6
+ the expected output, measures CPU time against the baseline, and writes a
7
+ leaderboard plus cost/violation/static-scan reports.
8
8
 
9
9
  The evaluator is independent of the DSH plugin: copy the run/ directory from
10
10
  $DSH_HOME/eval-guard/runs/<run-id>/ next to src/.
@@ -220,7 +220,7 @@ def main() -> int:
220
220
  out_dir = Path(args.out).resolve() if args.out else ROOT / "results" / time.strftime("%Y%m%d-%H%M%S")
221
221
  out_dir.mkdir(parents=True, exist_ok=True)
222
222
  build_dir = out_dir / "build"
223
- prices = load_json(ROOT / "prices.json") or {}
223
+ prices = load_json(ROOT / "warehouse-prices.json") or load_json(ROOT / "prices.json") or {}
224
224
 
225
225
  print("tier=%s workloads=%s repeats=%d" % (args.tier, ",".join(names), repeats))
226
226
  baseline = Participant("baseline", baseline_src, "baseline")
@@ -15,15 +15,12 @@ import sys
15
15
  import tempfile
16
16
  from pathlib import Path
17
17
 
18
- from common import EVAL_ROOT, clean_copy, emit, fail, parse_workspace
18
+ from common import EVAL_ROOT, child_env, clean_copy, emit, fail, parse_workspace
19
19
 
20
20
 
21
21
  def run(cmd: list[str], cwd: Path, timeout: int = 600) -> subprocess.CompletedProcess[str]:
22
- env = dict(os.environ)
23
- env.pop('PYTHONPATH', None)
24
- env.pop('PYTHONHOME', None)
25
22
  return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True,
26
- encoding='utf-8', errors='replace', env=env, timeout=timeout)
23
+ encoding='utf-8', errors='replace', env=child_env(), timeout=timeout)
27
24
 
28
25
 
29
26
  def main() -> int:
@@ -31,6 +28,9 @@ def main() -> int:
31
28
  hidden = EVAL_ROOT.parent / 'assets' / 'neural-cpp' / 'hidden' / 'test_neural.cpp'
32
29
  if not hidden.is_file():
33
30
  return fail({'task': 'neural-cpp', 'ok': False, 'error': 'hidden test missing'})
31
+ # Snapshot the hidden test BEFORE the candidate build runs: an agent-authored
32
+ # CMakeLists can otherwise overwrite the asset and substitute a passing test.
33
+ hidden_source = hidden.read_bytes()
34
34
 
35
35
  with tempfile.TemporaryDirectory(prefix='neural-cpp-eval-') as td:
36
36
  root = Path(td)
@@ -67,9 +67,12 @@ def main() -> int:
67
67
  if not lib.is_file():
68
68
  return fail({'task': 'neural-cpp', 'ok': False, 'error': 'build/libnn.a missing'})
69
69
 
70
+ # Compile the in-memory snapshot, not the on-disk asset the build could edit.
71
+ hidden_copy = root / 'test_neural.cpp'
72
+ hidden_copy.write_bytes(hidden_source)
70
73
  exe = root / 'test_neural'
71
74
  cc = run([compiler, '-std=c++17', '-O3', '-pthread',
72
- '-I', str(source / 'include'), str(hidden), str(lib),
75
+ '-I', str(source / 'include'), str(hidden_copy), str(lib),
73
76
  '-lm', '-o', str(exe)], root, 300)
74
77
  if cc.returncode != 0:
75
78
  return fail({'task': 'neural-cpp', 'ok': False, 'error': 'hidden test compile failed',
@@ -20,7 +20,7 @@ import numpy as np
20
20
  import torch
21
21
  import torch.nn.functional as F
22
22
 
23
- from common import EVAL_ROOT, clean_copy, emit, fail, parse_workspace
23
+ from common import EVAL_ROOT, child_env, clean_copy, emit, fail, parse_workspace
24
24
 
25
25
  torch.set_num_threads(1)
26
26
  torch.set_float32_matmul_precision('highest')
@@ -29,6 +29,9 @@ torch.backends.mkldnn.deterministic = True
29
29
  torch.manual_seed(1234)
30
30
 
31
31
  SEEDS = (11, 22, 33)
32
+ # A single CLI invocation budget and a total budget for the whole oracle run.
33
+ INVOCATION_TIMEOUT_SECONDS = 60
34
+ OPS_DEADLINE_SECONDS = 1800
32
35
 
33
36
 
34
37
  def write_tensor(path: Path, t: torch.Tensor) -> None:
@@ -65,11 +68,12 @@ def build_candidate(workspace: Path, root: Path) -> Path:
65
68
  shutil.rmtree(build, ignore_errors=True)
66
69
  build.mkdir(parents=True)
67
70
  cfg = subprocess.run(['cmake', '-S', '.', '-B', 'build', '-DCMAKE_BUILD_TYPE=Release'],
68
- cwd=str(source), capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=180)
71
+ cwd=str(source), capture_output=True, text=True, encoding='utf-8', errors='replace',
72
+ timeout=180, env=child_env())
69
73
  if cfg.returncode != 0:
70
74
  raise RuntimeError('cmake configure failed\n' + (cfg.stdout + cfg.stderr)[-4000:])
71
75
  made = subprocess.run(['cmake', '--build', 'build', '-j4'], cwd=str(source), capture_output=True,
72
- text=True, encoding='utf-8', errors='replace', timeout=1800)
76
+ text=True, encoding='utf-8', errors='replace', timeout=1800, env=child_env())
73
77
  if made.returncode != 0:
74
78
  raise RuntimeError('build failed\n' + (made.stdout + made.stderr)[-4000:])
75
79
  binary = build / 'nn_task'
@@ -87,7 +91,8 @@ class Case:
87
91
  self.flops = flops
88
92
 
89
93
 
90
- def run_operation(name: str, threshold: float, cases: list[Case], binary: Path, root: Path) -> dict:
94
+ def run_operation(name: str, threshold: float, cases: list[Case], binary: Path, root: Path,
95
+ invocation_timeout: float = INVOCATION_TIMEOUT_SECONDS) -> dict:
91
96
  op_dir = root / 'ops' / name
92
97
  op_dir.mkdir(parents=True, exist_ok=True)
93
98
  executable = True
@@ -111,7 +116,8 @@ def run_operation(name: str, threshold: float, cases: list[Case], binary: Path,
111
116
  started = time.perf_counter()
112
117
  try:
113
118
  proc = subprocess.run(cmd, cwd=str(case_dir), capture_output=True, text=True,
114
- encoding='utf-8', errors='replace', timeout=300)
119
+ encoding='utf-8', errors='replace', timeout=invocation_timeout,
120
+ env=child_env())
115
121
  except subprocess.TimeoutExpired:
116
122
  executable = False
117
123
  first_error = first_error or 'timeout'
@@ -459,7 +465,13 @@ def main() -> int:
459
465
  except Exception as exc:
460
466
  return fail({'task': 'neural-fp32', 'ok': False, 'error': str(exc)})
461
467
  rows = []
468
+ deadline = time.monotonic() + OPS_DEADLINE_SECONDS
462
469
  for name, threshold, builder in OPERATIONS:
470
+ if time.monotonic() > deadline:
471
+ rows.append({'name': name, 'executable': False, 'passed': False,
472
+ 'weighted_mean_relative_error': None, 'threshold': threshold,
473
+ 'speed_gflops': None, 'error': 'evaluation deadline exceeded'})
474
+ continue
463
475
  try:
464
476
  cases = builder()
465
477
  row = run_operation(name, threshold, cases, binary, root)
@@ -3,9 +3,12 @@
3
3
  from __future__ import annotations
4
4
  import os, tempfile
5
5
  from pathlib import Path
6
- from common import EVAL_ROOT, candidate_row, clean_copy, emit, fail, parse_workspace, run_legacy
6
+ from common import EVAL_ROOT, candidate_row, clean_copy, emit, fail, parse_workspace, restore_baseline, run_legacy, verify_venv
7
7
 
8
8
  workspace = parse_workspace()
9
+ problem = verify_venv(workspace)
10
+ if problem is not None:
11
+ raise SystemExit(fail({'task': 'swe-bench-pro', 'ok': False, 'error': problem}))
9
12
  with tempfile.TemporaryDirectory(prefix='swe-pro-eval-') as td:
10
13
  temp = Path(td)
11
14
  candidate = temp / 'candidate' / 'workspace'
@@ -13,13 +16,14 @@ with tempfile.TemporaryDirectory(prefix='swe-pro-eval-') as td:
13
16
  clean_copy(workspace, candidate, ignore_venv=True)
14
17
  venv = workspace / '.venv'
15
18
  if venv.is_dir():
16
- os.symlink(venv, candidate / '.venv')
19
+ try:
20
+ os.symlink(venv, candidate / '.venv')
21
+ except FileExistsError:
22
+ pass
23
+ restore_baseline(candidate)
17
24
  result = run_legacy(EVAL_ROOT / 'legacy' / 'swe_bench_pro.py', ['--submissions', str(temp)], temp / 'out')
18
25
  if not result.get('ok'):
19
26
  raise SystemExit(fail({'task': 'swe-bench-pro', 'ok': False, 'error': result.get('error', 'evaluation failed')}))
20
27
  row = candidate_row(result['summary'])
21
- passed = row.get('passed', 0)
22
- failed = row.get('failed', 0)
23
- errors = row.get('errors', 0)
24
- resolved = failed == 0 and errors == 0 and passed > 0
25
- emit({'task': 'swe-bench-pro', 'ok': True, 'score': 100.0 if resolved else 0.0, 'resolved': resolved, 'passRate': row.get('score', 0), 'passed': passed, 'failed': failed, 'errors': errors, 'status': row.get('status')})
28
+ resolved = row.get('resolved') is True
29
+ emit({'task': 'swe-bench-pro', 'ok': True, 'score': 100.0 if resolved else 0.0, 'resolved': resolved, 'passRate': row.get('score', 0), 'passed': row.get('passed', 0), 'failed': row.get('failed', 0), 'errors': row.get('errors', 0), 'f2pPassed': row.get('f2p_passed', 0), 'f2pTotal': row.get('f2p_total', 0), 'status': row.get('status')})
package/eval/tank.py CHANGED
@@ -16,5 +16,14 @@ with tempfile.TemporaryDirectory(prefix='tank-eval-') as td:
16
16
  result = run_legacy(EVAL_ROOT / 'legacy' / 'tank.py', ['--submissions', str(temp), '--arena', str(EVAL_ROOT / 'arena' / 'tank'), '--tier', 'quick'], temp / 'out')
17
17
  if not result.get('ok'):
18
18
  raise SystemExit(fail({'task': 'tank', 'ok': False, 'error': result.get('error', 'evaluation failed')}))
19
- row = candidate_row(result['summary'])
20
- emit({'task': 'tank', 'ok': True, 'score': row.get('score', row.get('rating', row.get('theta', 0))), 'rating': row.get('rating', row.get('theta', 0)), 'wins': row.get('wins', 0), 'losses': row.get('losses', 0), 'draws': row.get('draws', 0)})
19
+ summary = result['summary']
20
+ row = candidate_row(summary)
21
+ if not row:
22
+ raise SystemExit(fail({'task': 'tank', 'ok': False, 'error': 'candidate row missing from evaluator summary'}))
23
+ wins = draws = losses = 0
24
+ for pair in summary.get('pairwise') or []:
25
+ if pair.get('a') == 'candidate':
26
+ wins += int(pair.get('wins', 0)); draws += int(pair.get('draws', 0)); losses += int(pair.get('losses', 0))
27
+ elif pair.get('b') == 'candidate':
28
+ wins += int(pair.get('losses', 0)); draws += int(pair.get('draws', 0)); losses += int(pair.get('wins', 0))
29
+ emit({'task': 'tank', 'ok': True, 'score': row.get('rating', 0), 'rating': row.get('rating', 0), 'wins': wins, 'losses': losses, 'draws': draws, 'games': row.get('games', 0)})
package/eval/warehouse.py CHANGED
@@ -17,4 +17,20 @@ with tempfile.TemporaryDirectory(prefix='warehouse-eval-') as td:
17
17
  if not result.get('ok'):
18
18
  raise SystemExit(fail({'task': 'warehouse', 'ok': False, 'error': result.get('error', 'evaluation failed')}))
19
19
  row = candidate_row(result['summary'])
20
- emit({'task': 'warehouse', 'ok': True, 'score': row.get('speedup', 0), 'speedup': row.get('speedup', 0), 'correct': row.get('correct'), 'status': row.get('status')})
20
+ if not row:
21
+ raise SystemExit(fail({'task': 'warehouse', 'ok': False, 'error': 'candidate row missing from evaluator summary'}))
22
+ compile_error = row.get('compileError') or ''
23
+ correct = row.get('correct', 0)
24
+ total = row.get('total', 0)
25
+ status = 'compile-failed' if compile_error else ('ok' if correct == total and total else 'incorrect')
26
+ emit({
27
+ 'task': 'warehouse',
28
+ 'ok': True,
29
+ 'score': row.get('score', 0),
30
+ 'speedup': row.get('geomeanSpeedup', 0),
31
+ 'geomeanSpeedup': row.get('geomeanSpeedup', 0),
32
+ 'correct': correct,
33
+ 'total': total,
34
+ 'status': status,
35
+ 'error': compile_error,
36
+ })