gaia_unit4_space / tools /code_tools.py
hawkdev's picture
initial commit
ac299d5
import subprocess
import sys
from typing import Optional
def run_python_snippet(code: str, timeout_sec: int = 45) -> str:
"""Execute Python in a subprocess (no network). For short derived calculations."""
if not code.strip():
return "Error: empty code."
try:
proc = subprocess.run(
[sys.executable, "-c", code],
capture_output=True,
text=True,
timeout=timeout_sec,
env={**__import__("os").environ, "PYTHONHASHSEED": "0"},
)
except subprocess.TimeoutExpired:
return "Error: execution timed out."
out = (proc.stdout or "").strip()
err = (proc.stderr or "").strip()
if proc.returncode != 0:
return f"Exit {proc.returncode}. stderr: {err[:2000]}"
if err:
out = f"{out}\n(stderr: {err[:1500]})" if out else err
return out[:30_000] if out else "(no stdout)"
def run_python_file(file_path: str, timeout_sec: int = 60) -> str:
"""Run an attached .py file and capture stdout."""
if not file_path.endswith(".py"):
return "Error: not a .py path."
try:
proc = subprocess.run(
[sys.executable, file_path],
capture_output=True,
text=True,
timeout=timeout_sec,
env={**__import__("os").environ, "PYTHONHASHSEED": "0"},
)
except subprocess.TimeoutExpired:
return "Error: execution timed out."
out = (proc.stdout or "").strip()
err = (proc.stderr or "").strip()
if proc.returncode != 0:
return f"Exit {proc.returncode}. stderr: {err[:2000]}"
return (out or err)[:30_000]
def solve_cayley_noncommutative_subset(question: str) -> Optional[str]:
"""
Parse a Cayley table from the question (markdown) and return the sorted
comma-separated elements involved in any non-commuting pair.
"""
if "* on the set S" not in question and "not commutative" not in question:
return None
lines = [ln.strip() for ln in question.splitlines() if ln.strip().startswith("|")]
if len(lines) < 3:
return None
def split_row(ln: str) -> list[str]:
parts = [p.strip() for p in ln.strip("|").split("|")]
return parts
header = split_row(lines[0])
if len(header) < 2 or header[0] != "*":
return None
cols = header[1:]
op: dict[tuple[str, str], str] = {}
for ln in lines[1:]:
cells = split_row(ln)
if len(cells) < 2:
continue
row_sym = cells[0]
for j, c in enumerate(cols):
if j + 1 >= len(cells):
break
op[(row_sym, c)] = cells[j + 1]
elems = cols
involved: set[str] = set()
for a in elems:
for b in elems:
ab = op.get((a, b))
ba = op.get((b, a))
if ab is None or ba is None:
continue
if ab != ba:
involved.add(a)
involved.add(b)
if not involved:
return None
return ", ".join(sorted(involved))
def reverse_english_puzzle_answer(question: str) -> Optional[str]:
"""If the question is reversed English about 'left', return 'right'."""
q = question.strip()
if not q:
return None
rev = q[::-1]
if "opposite" in rev.lower() and '"left"' in rev and "answer" in rev.lower():
return "right"
return None