| """A restricted Python REPL for exact arithmetic, string, and date work.""" |
|
|
| from __future__ import annotations |
|
|
| import contextlib |
| import io |
| import math |
|
|
| from langchain_core.tools import tool |
|
|
| |
| _SAFE_BUILTINS = { |
| "abs": abs, "all": all, "any": any, "bin": bin, "bool": bool, "chr": chr, |
| "dict": dict, "divmod": divmod, "enumerate": enumerate, "filter": filter, |
| "float": float, "hex": hex, "int": int, "len": len, "list": list, "map": map, |
| "max": max, "min": min, "oct": oct, "ord": ord, "pow": pow, "range": range, |
| "reversed": reversed, "round": round, "set": set, "sorted": sorted, "str": str, |
| "sum": sum, "tuple": tuple, "zip": zip, "print": print, |
| } |
|
|
|
|
| @tool |
| def python_repl(code: str) -> str: |
| """Execute a short Python snippet and return its stdout (and `result` if set). |
| |
| Use for exact arithmetic, string manipulation, sorting, and date math. ALWAYS use |
| this for numeric work instead of computing in your head. ``math``, ``statistics``, |
| ``datetime``, ``re``, ``itertools``, ``collections`` and ``pd`` (pandas) are |
| available -- e.g. read a spreadsheet with ``pd.read_excel(path)``. Assign to a |
| variable named ``result`` to return a value. |
| |
| Args: |
| code: Python source to execute. |
| """ |
| import collections |
| import datetime |
| import itertools |
| import re |
| import statistics |
|
|
| env = { |
| "__builtins__": _SAFE_BUILTINS, |
| "math": math, |
| "statistics": statistics, |
| "datetime": datetime, |
| "re": re, |
| "itertools": itertools, |
| "collections": collections, |
| } |
| |
| |
| try: |
| import pandas as pd |
|
|
| env["pd"] = pd |
| except Exception: |
| pass |
| buf = io.StringIO() |
| try: |
| with contextlib.redirect_stdout(buf): |
| exec(code, env) |
| except Exception as exc: |
| return f"Error: {exc}\nOutput so far:\n{buf.getvalue()}" |
|
|
| out = buf.getvalue() |
| if "result" in env: |
| out += ("\n" if out else "") + f"result = {env['result']!r}" |
| return out or "(no output)" |
|
|
|
|
| @tool |
| def run_python_file(path: str) -> str: |
| """Execute a downloaded .py file in a subprocess and return its stdout. |
| |
| Use for questions asking for the output of an attached Python script. |
| |
| Args: |
| path: Local path to the .py file (e.g. from download_task_file). |
| """ |
| import subprocess |
| import sys |
|
|
| try: |
| proc = subprocess.run( |
| [sys.executable, path], |
| capture_output=True, |
| text=True, |
| timeout=30, |
| ) |
| except subprocess.TimeoutExpired: |
| return "Execution timed out after 30s." |
| except Exception as exc: |
| return f"Could not run file: {exc}" |
| out = (proc.stdout or "").strip() |
| err = (proc.stderr or "").strip() |
| result = f"STDOUT:\n{out}" |
| if err: |
| result += f"\nSTDERR:\n{err[:2000]}" |
| return result or "(no output)" |
|
|