Download tiny_gdn/code_exec.py from kerzgrr/Tercet-R-1.1: direct link, hf CLI and curl.
- Browser
- Download file 15 kB
-
https://huggingface.co/kerzgrr/Tercet-R-1.1/resolve/main/tiny_gdn/code_exec.py
- Command line
-
hf download hf://kerzgrr/Tercet-R-1.1/tiny_gdn/code_exec.py
-
curl -L -o code_exec.py https://huggingface.co/kerzgrr/Tercet-R-1.1/resolve/main/tiny_gdn/code_exec.py
15 kB
| """Restricted stateful Python for Nemotron Cascade-2 math tool calls. | |
| Tercet-R saw `stateful_python_code_exec` on `math/math_tool.jsonl` during | |
| the Cascade-2 SFT stage. Observations are the Jupyter-style stdout / last | |
| expression value, not a JSON envelope. | |
| The worker process only allows a math-oriented import whitelist and rejects | |
| dunder access, so a tool call cannot read the filesystem or start a shell. | |
| """ | |
| from __future__ import annotations | |
| import ast | |
| import io | |
| import json | |
| import math | |
| import os | |
| import subprocess | |
| import sys | |
| import threading | |
| from collections.abc import Mapping | |
| from pathlib import Path | |
| from queue import Empty, Queue | |
| from typing import Any | |
| NEMOTRON_PYTHON_EXEC_TOOL: dict[str, Any] = { | |
| "type": "function", | |
| "function": { | |
| "name": "stateful_python_code_exec", | |
| "description": ( | |
| "Call this function to execute Python code in a stateful Jupyter " | |
| "notebook environment. Python will respond with the output of the " | |
| "execution or time out after 120.0 seconds." | |
| ), | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "code": { | |
| "type": "string", | |
| "description": "Code to execute", | |
| } | |
| }, | |
| "required": ["code"], | |
| }, | |
| }, | |
| } | |
| CALCULATOR_TOOL: dict[str, Any] = { | |
| "type": "function", | |
| "function": { | |
| "name": "calculator", | |
| "description": "Evaluate a math expression and return the numeric result.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "expression": { | |
| "type": "string", | |
| "description": "Math expression, for example 12.5 * (3 + 4)", | |
| } | |
| }, | |
| "required": ["expression"], | |
| }, | |
| }, | |
| } | |
| PYTHON_EXEC_TOOL_NAMES = frozenset( | |
| { | |
| "stateful_python_code_exec", | |
| "python_code_exec", | |
| "code_interpreter", | |
| "python_exec", | |
| "python", | |
| } | |
| ) | |
| CALCULATOR_TOOL_NAMES = frozenset({"calculator", "calc"}) | |
| DEFAULT_EXEC_TIMEOUT_SECONDS = 30.0 | |
| _SRC_ROOT = Path(__file__).resolve().parents[1] | |
| _session_lock = threading.Lock() | |
| _default_session: StatefulPythonSession | None = None | |
| def _normalize_tool_name(name: str) -> str: | |
| return name.strip().lower().replace("-", "_") | |
| def is_python_exec_tool_name(name: str) -> bool: | |
| return _normalize_tool_name(name) in PYTHON_EXEC_TOOL_NAMES | |
| def is_calculator_tool_name(name: str) -> bool: | |
| return _normalize_tool_name(name) in CALCULATOR_TOOL_NAMES | |
| def is_auto_math_tool_name(name: str) -> bool: | |
| return is_python_exec_tool_name(name) or is_calculator_tool_name(name) | |
| def code_from_arguments(arguments: Mapping[str, Any] | None) -> str: | |
| if not arguments: | |
| return "" | |
| for key in ("code", "expression", "expr", "source"): | |
| raw = arguments.get(key) | |
| if isinstance(raw, str) and raw.strip(): | |
| return raw | |
| if raw is not None and key != "code": | |
| return str(raw) | |
| return "" | |
| class CodeExecError(ValueError): | |
| """Rejected or failed tool code.""" | |
| class StatefulPythonSession: | |
| """One long-lived restricted interpreter, matching the training tool.""" | |
| def __init__(self, *, timeout_seconds: float = DEFAULT_EXEC_TIMEOUT_SECONDS) -> None: | |
| self.timeout_seconds = timeout_seconds | |
| self._lock = threading.Lock() | |
| self._process: subprocess.Popen[str] | None = None | |
| def close(self) -> None: | |
| with self._lock: | |
| self._kill_locked() | |
| def reset(self) -> None: | |
| with self._lock: | |
| self._kill_locked() | |
| def run(self, code: str) -> str: | |
| text = code.strip() | |
| if not text: | |
| raise CodeExecError("code is empty") | |
| with self._lock: | |
| return self._run_locked(text) | |
| def _run_locked(self, code: str) -> str: | |
| process = self._ensure_process_locked() | |
| try: | |
| process.stdin.write(json.dumps({"code": code}, ensure_ascii=False) + "\n") | |
| process.stdin.flush() | |
| line = self._readline_locked(process, self.timeout_seconds) | |
| except (BrokenPipeError, OSError) as error: | |
| self._kill_locked() | |
| raise CodeExecError(f"python worker died: {error}") from error | |
| if not line: | |
| stderr = "" | |
| if process.stderr is not None: | |
| try: | |
| stderr = process.stderr.read() | |
| except OSError: | |
| stderr = "" | |
| self._kill_locked() | |
| detail = stderr.strip() or "python worker closed stdout" | |
| raise CodeExecError(detail) | |
| try: | |
| payload = json.loads(line) | |
| except json.JSONDecodeError as error: | |
| self._kill_locked() | |
| raise CodeExecError(f"python worker returned invalid JSON: {line!r}") from error | |
| if not isinstance(payload, dict): | |
| raise CodeExecError("python worker returned a non-object") | |
| output = str(payload.get("output") or "") | |
| if payload.get("ok") is True: | |
| return output if output else "None" | |
| raise CodeExecError(output or "execution failed") | |
| def _readline_locked(self, process: subprocess.Popen[str], timeout: float) -> str: | |
| if process.stdout is None: | |
| raise CodeExecError("python worker has no stdout") | |
| lines: Queue[str] = Queue() | |
| def _read() -> None: | |
| lines.put(process.stdout.readline() if process.stdout is not None else "") | |
| reader = threading.Thread(target=_read, daemon=True) | |
| reader.start() | |
| reader.join(timeout) | |
| if reader.is_alive(): | |
| self._kill_locked() | |
| raise CodeExecError(f"timed out after {timeout:g}s") | |
| try: | |
| return lines.get_nowait() | |
| except Empty: | |
| return "" | |
| def _ensure_process_locked(self) -> subprocess.Popen[str]: | |
| process = self._process | |
| if process is not None and process.poll() is None: | |
| return process | |
| env = os.environ.copy() | |
| pythonpath = env.get("PYTHONPATH", "") | |
| env["PYTHONPATH"] = ( | |
| str(_SRC_ROOT) if not pythonpath else f"{_SRC_ROOT}{os.pathsep}{pythonpath}" | |
| ) | |
| self._process = subprocess.Popen( | |
| [sys.executable, "-m", "tiny_gdn.code_exec"], | |
| stdin=subprocess.PIPE, | |
| stdout=subprocess.PIPE, | |
| stderr=subprocess.PIPE, | |
| text=True, | |
| bufsize=1, | |
| env=env, | |
| ) | |
| if self._process.stdin is None or self._process.stdout is None: | |
| self._kill_locked() | |
| raise CodeExecError("failed to start python worker") | |
| return self._process | |
| def _kill_locked(self) -> None: | |
| process = self._process | |
| self._process = None | |
| if process is None: | |
| return | |
| try: | |
| process.kill() | |
| process.wait(timeout=2) | |
| except (OSError, subprocess.TimeoutExpired): | |
| pass | |
| def default_python_session() -> StatefulPythonSession: | |
| global _default_session | |
| with _session_lock: | |
| if _default_session is None: | |
| _default_session = StatefulPythonSession() | |
| return _default_session | |
| def reset_default_python_session() -> None: | |
| global _default_session | |
| with _session_lock: | |
| session = _default_session | |
| _default_session = None | |
| if session is not None: | |
| session.close() | |
| def execute_calculator(expression: str) -> str: | |
| tree = ast.parse(expression, mode="eval") | |
| _reject_unsafe_ast(tree) | |
| value = eval( # noqa: S307 — AST already rejected unsafe nodes | |
| compile(tree, "<calculator>", "eval"), | |
| {"__builtins__": {}}, | |
| _calculator_namespace(), | |
| ) | |
| return _format_result(value) | |
| def execute_math_tool(name: str, arguments: Mapping[str, Any] | None) -> str: | |
| source = code_from_arguments(arguments) | |
| if is_calculator_tool_name(name) and "\n" not in source and ";" not in source: | |
| try: | |
| return execute_calculator(source) | |
| except (SyntaxError, CodeExecError, TypeError, ValueError, ZeroDivisionError) as error: | |
| raise CodeExecError(str(error)) from error | |
| return default_python_session().run(source) | |
| def _calculator_namespace() -> dict[str, Any]: | |
| names = { | |
| key: getattr(math, key) | |
| for key in dir(math) | |
| if not key.startswith("_") | |
| } | |
| names.update( | |
| { | |
| "abs": abs, | |
| "min": min, | |
| "max": max, | |
| "round": round, | |
| "pow": pow, | |
| "pi": math.pi, | |
| "e": math.e, | |
| "tau": math.tau, | |
| "inf": math.inf, | |
| } | |
| ) | |
| return names | |
| def _reject_unsafe_ast(tree: ast.AST) -> None: | |
| for node in ast.walk(tree): | |
| if isinstance(node, (ast.ClassDef, ast.AsyncFunctionDef)): | |
| raise CodeExecError("class definitions are blocked") | |
| if isinstance(node, ast.Attribute) and node.attr.startswith("_"): | |
| raise CodeExecError("dunder attribute access is blocked") | |
| if isinstance(node, ast.Name) and node.id.startswith("_"): | |
| raise CodeExecError("dunder names are blocked") | |
| if isinstance(node, ast.Call) and isinstance(node.func, ast.Name): | |
| if node.func.id in _BANNED_CALLS: | |
| raise CodeExecError(f"{node.func.id}() is blocked") | |
| _BANNED_CALLS = frozenset( | |
| { | |
| "eval", | |
| "exec", | |
| "compile", | |
| "open", | |
| "input", | |
| "breakpoint", | |
| "getattr", | |
| "setattr", | |
| "delattr", | |
| "globals", | |
| "locals", | |
| "vars", | |
| "dir", | |
| "help", | |
| "__import__", | |
| "memoryview", | |
| "exit", | |
| "quit", | |
| } | |
| ) | |
| def _format_result(value: Any) -> str: | |
| if value is None: | |
| return "None" | |
| if isinstance(value, bool): | |
| return str(value) | |
| if isinstance(value, float): | |
| if value.is_integer() and abs(value) < 1e15: | |
| return str(int(value)) | |
| return format(value, ".12g") | |
| return str(value) | |
| _ALLOWED_IMPORT_ROOTS = frozenset( | |
| { | |
| "cmath", | |
| "collections", | |
| "copy", | |
| "decimal", | |
| "fractions", | |
| "functools", | |
| "itertools", | |
| "json", | |
| "math", | |
| "mpmath", | |
| "numbers", | |
| "numpy", | |
| "operator", | |
| "re", | |
| "statistics", | |
| "string", | |
| "sympy", | |
| "textwrap", | |
| "unicodedata", | |
| } | |
| ) | |
| _WORKER_NAMESPACE: dict[str, Any] | None = None | |
| def _allowed_import( | |
| name: str, | |
| globals: dict[str, Any] | None = None, | |
| locals: dict[str, Any] | None = None, | |
| fromlist: tuple[str, ...] = (), | |
| level: int = 0, | |
| ) -> Any: | |
| root = name.split(".")[0] | |
| if root not in _ALLOWED_IMPORT_ROOTS: | |
| raise ImportError(f"import of {name!r} is blocked") | |
| return __import__(name, globals, locals, fromlist, level) | |
| def _worker_namespace() -> dict[str, Any]: | |
| global _WORKER_NAMESPACE | |
| if _WORKER_NAMESPACE is None: | |
| builtins = { | |
| "abs": abs, | |
| "all": all, | |
| "any": any, | |
| "bin": bin, | |
| "bool": bool, | |
| "bytes": bytes, | |
| "chr": chr, | |
| "complex": complex, | |
| "dict": dict, | |
| "divmod": divmod, | |
| "enumerate": enumerate, | |
| "filter": filter, | |
| "float": float, | |
| "format": format, | |
| "frozenset": frozenset, | |
| "hex": hex, | |
| "int": int, | |
| "isinstance": isinstance, | |
| "issubclass": issubclass, | |
| "iter": iter, | |
| "len": len, | |
| "list": list, | |
| "map": map, | |
| "max": max, | |
| "min": min, | |
| "next": next, | |
| "oct": oct, | |
| "ord": ord, | |
| "pow": pow, | |
| "print": print, | |
| "range": range, | |
| "repr": repr, | |
| "reversed": reversed, | |
| "round": round, | |
| "set": set, | |
| "slice": slice, | |
| "sorted": sorted, | |
| "str": str, | |
| "sum": sum, | |
| "tuple": tuple, | |
| "zip": zip, | |
| "True": True, | |
| "False": False, | |
| "None": None, | |
| "__import__": _allowed_import, | |
| } | |
| _WORKER_NAMESPACE = { | |
| "__builtins__": builtins, | |
| "__name__": "__tool__", | |
| "math": math, | |
| } | |
| return _WORKER_NAMESPACE | |
| def run_cell(code: str) -> str: | |
| tree = ast.parse(code) | |
| _reject_unsafe_import_roots(tree) | |
| _reject_unsafe_ast(tree) | |
| namespace = _worker_namespace() | |
| buffer = io.StringIO() | |
| previous = sys.stdout | |
| sys.stdout = buffer | |
| try: | |
| if tree.body and isinstance(tree.body[-1], ast.Expr): | |
| body = tree.body[:-1] | |
| last = tree.body[-1] | |
| if body: | |
| exec( # noqa: S102 | |
| compile(ast.Module(body, type_ignores=[]), "<tool>", "exec"), | |
| namespace, | |
| namespace, | |
| ) | |
| value = eval( # noqa: S307 | |
| compile(ast.Expression(last.value), "<tool>", "eval"), | |
| namespace, | |
| namespace, | |
| ) | |
| printed = buffer.getvalue() | |
| if value is None: | |
| return printed if printed else "None" | |
| rendered = _format_result(value) | |
| return f"{printed}{rendered}" if printed else rendered | |
| exec(compile(tree, "<tool>", "exec"), namespace, namespace) # noqa: S102 | |
| printed = buffer.getvalue() | |
| return printed if printed else "None" | |
| finally: | |
| sys.stdout = previous | |
| def _reject_unsafe_import_roots(tree: ast.AST) -> None: | |
| for node in ast.walk(tree): | |
| if isinstance(node, ast.Import): | |
| for alias in node.names: | |
| root = alias.name.split(".")[0] | |
| if root not in _ALLOWED_IMPORT_ROOTS: | |
| raise CodeExecError(f"import of {alias.name!r} is blocked") | |
| elif isinstance(node, ast.ImportFrom): | |
| root = (node.module or "").split(".")[0] | |
| if root not in _ALLOWED_IMPORT_ROOTS: | |
| raise CodeExecError(f"import of {node.module!r} is blocked") | |
| def _worker_loop() -> None: | |
| for line in sys.stdin: | |
| line = line.strip() | |
| if not line: | |
| continue | |
| try: | |
| request = json.loads(line) | |
| code = str(request.get("code") or "") | |
| output = run_cell(code) | |
| sys.stdout.write(json.dumps({"ok": True, "output": output}, ensure_ascii=False) + "\n") | |
| except Exception as error: | |
| sys.stdout.write( | |
| json.dumps({"ok": False, "output": str(error)}, ensure_ascii=False) + "\n" | |
| ) | |
| sys.stdout.flush() | |
| if __name__ == "__main__": | |
| _worker_loop() | |