42e commited on
Commit
5a983c2
·
verified ·
1 Parent(s): 3ca9c7a

Upload folder using huggingface_hub

Browse files
src/mathcompose/common/pyexec.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Sandboxed Python execution for the code-verifying verifier (GenPRM-style).
2
+
3
+ The verifier can emit ```python ... ``` blocks to *check* arithmetic (the thing a
4
+ 1.5B model can't do by reading); we execute them and feed stdout back. Model-
5
+ generated code is untrusted, so we run it in a subprocess with:
6
+ * a wall-clock timeout,
7
+ * no inherited environment / no network creds,
8
+ * a scratch cwd,
9
+ * output truncation.
10
+
11
+ This is defense-in-depth, not a hard jail — run it only in a disposable
12
+ environment (Colab/Modal VM), never on a privileged host.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import subprocess
17
+ import sys
18
+ import tempfile
19
+ from dataclasses import dataclass
20
+
21
+
22
+ @dataclass
23
+ class ExecResult:
24
+ ok: bool
25
+ stdout: str
26
+ stderr: str
27
+
28
+ def as_feedback(self, max_chars: int = 600) -> str:
29
+ if self.ok:
30
+ out = self.stdout.strip() or "(no output)"
31
+ return out[:max_chars]
32
+ err = (self.stderr.strip() or "error").splitlines()[-1]
33
+ return f"[error] {err[:max_chars]}"
34
+
35
+
36
+ _PREAMBLE = "import math, fractions, itertools\nfrom fractions import Fraction\n"
37
+
38
+
39
+ def run_python(code: str, timeout: float = 5.0, max_output: int = 4000) -> ExecResult:
40
+ """Execute a short snippet in a subprocess and capture stdout/stderr."""
41
+ src = _PREAMBLE + code
42
+ try:
43
+ with tempfile.TemporaryDirectory() as tmp:
44
+ proc = subprocess.run(
45
+ [sys.executable, "-I", "-c", src], # -I: isolated (ignore env/site)
46
+ capture_output=True, text=True, timeout=timeout, cwd=tmp,
47
+ env={"PYTHONHASHSEED": "0"},
48
+ )
49
+ return ExecResult(
50
+ ok=(proc.returncode == 0),
51
+ stdout=proc.stdout[:max_output],
52
+ stderr=proc.stderr[:max_output],
53
+ )
54
+ except subprocess.TimeoutExpired:
55
+ return ExecResult(ok=False, stdout="", stderr=f"timeout after {timeout}s")
56
+ except Exception as e: # noqa: BLE001
57
+ return ExecResult(ok=False, stdout="", stderr=f"{type(e).__name__}: {e}")
58
+
59
+
60
+ # Extract fenced python blocks from a model critique.
61
+ import re # noqa: E402
62
+
63
+ _CODE_BLOCK = re.compile(r"```(?:python)?\s*\n(.*?)```", re.DOTALL)
64
+
65
+
66
+ def extract_code_blocks(text: str) -> list[str]:
67
+ return [m.group(1).strip() for m in _CODE_BLOCK.finditer(text)]
src/mathcompose/eval/run.py CHANGED
@@ -28,9 +28,40 @@ def _save(name: str, tag: str, payload: dict) -> None:
28
  print(json.dumps(payload, indent=2))
29
 
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  def _runner(base_id, adapter):
32
  from .runners import HFRunner
33
- return HFRunner(base_id, adapter_path=adapter)
34
 
35
 
36
  def _prompts_per_batch(a):
@@ -77,8 +108,8 @@ def cmd_compose(a):
77
  from .compose import evaluate_composition, make_v_scorer
78
  from .runners import HFRunner, make_solve_fn
79
 
80
- gen = HFRunner(a.base_id, adapter_path=a.gen_adapter)
81
- ver = HFRunner(a.base_id, adapter_path=a.ver_adapter)
82
  solve_fn = make_solve_fn(gen, n=a.n, temperature=a.temperature)
83
  v_score_fn = make_v_scorer(ver, k=a.ver_k)
84
 
@@ -107,7 +138,8 @@ def cmd_report(a):
107
 
108
  def main() -> None:
109
  ap = argparse.ArgumentParser(description=__doc__)
110
- ap.add_argument("--base-id", default="Qwen/Qwen2.5-Math-1.5B-Instruct")
 
111
  sub = ap.add_subparsers(dest="cmd", required=True)
112
 
113
  p = sub.add_parser("processbench")
 
28
  print(json.dumps(payload, indent=2))
29
 
30
 
31
+ DEFAULT_BASE = "Qwen/Qwen2.5-Math-1.5B-Instruct"
32
+
33
+
34
+ def _base_from_adapter(adapter):
35
+ """Read base_model_name_or_path from an adapter's config (local dir or hub id)."""
36
+ import json
37
+ import os
38
+
39
+ try:
40
+ if os.path.isdir(adapter):
41
+ cfg = json.load(open(os.path.join(adapter, "adapter_config.json")))
42
+ else:
43
+ from huggingface_hub import hf_hub_download
44
+ cfg = json.load(open(hf_hub_download(adapter, "adapter_config.json")))
45
+ return cfg.get("base_model_name_or_path")
46
+ except Exception:
47
+ return None
48
+
49
+
50
+ def _resolve_base(base_id, adapter):
51
+ """Explicit --base-id wins; else infer from the adapter; else the 1.5B default."""
52
+ if base_id:
53
+ return base_id
54
+ if adapter:
55
+ inferred = _base_from_adapter(adapter)
56
+ if inferred:
57
+ print(f"[eval] base inferred from adapter: {inferred}")
58
+ return inferred
59
+ return DEFAULT_BASE
60
+
61
+
62
  def _runner(base_id, adapter):
63
  from .runners import HFRunner
64
+ return HFRunner(_resolve_base(base_id, adapter), adapter_path=adapter)
65
 
66
 
67
  def _prompts_per_batch(a):
 
108
  from .compose import evaluate_composition, make_v_scorer
109
  from .runners import HFRunner, make_solve_fn
110
 
111
+ gen = HFRunner(_resolve_base(a.base_id, a.gen_adapter), adapter_path=a.gen_adapter)
112
+ ver = HFRunner(_resolve_base(a.base_id, a.ver_adapter), adapter_path=a.ver_adapter)
113
  solve_fn = make_solve_fn(gen, n=a.n, temperature=a.temperature)
114
  v_score_fn = make_v_scorer(ver, k=a.ver_k)
115
 
 
138
 
139
  def main() -> None:
140
  ap = argparse.ArgumentParser(description=__doc__)
141
+ ap.add_argument("--base-id", default=None,
142
+ help="base model; if omitted, inferred from the adapter (else 1.5B default)")
143
  sub = ap.add_subparsers(dest="cmd", required=True)
144
 
145
  p = sub.add_parser("processbench")
tests/test_pyexec.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Sandboxed executor for the code-verifying verifier (M1c)."""
2
+ from mathcompose.common.pyexec import run_python, extract_code_blocks
3
+
4
+
5
+ def test_arithmetic_and_output():
6
+ assert run_python("print(sum(range(101)))").as_feedback() == "5050"
7
+ assert run_python("print(6*7)").stdout.strip() == "42"
8
+
9
+
10
+ def test_timeout_is_caught():
11
+ r = run_python("while True: pass", timeout=1.0)
12
+ assert not r.ok and "timeout" in r.stderr
13
+
14
+
15
+ def test_error_is_caught():
16
+ r = run_python("print(1/0)")
17
+ assert not r.ok and "ZeroDivisionError" in r.as_feedback()
18
+
19
+
20
+ def test_extract_code_blocks():
21
+ text = "a\n```python\nprint(1)\n```\nb\n```\nprint(2)\n```"
22
+ blocks = extract_code_blocks(text)
23
+ assert blocks == ["print(1)", "print(2)"]
24
+ assert run_python(blocks[0]).as_feedback() == "1"