2025-04-17 11:34:42 +08:00
|
|
|
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
|
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
|
|
2025-04-07 16:25:55 +08:00
|
|
|
import logging
|
2025-08-06 14:27:03 +08:00
|
|
|
import os
|
|
|
|
|
from typing import Annotated, Optional
|
2025-08-17 22:57:23 +08:00
|
|
|
|
2025-04-07 16:25:55 +08:00
|
|
|
from langchain_core.tools import tool
|
|
|
|
|
from langchain_experimental.utilities import PythonREPL
|
2025-08-17 22:57:23 +08:00
|
|
|
|
2025-04-07 16:25:55 +08:00
|
|
|
from .decorators import log_io
|
|
|
|
|
|
2025-08-06 14:27:03 +08:00
|
|
|
|
|
|
|
|
def _is_python_repl_enabled() -> bool:
|
|
|
|
|
"""Check if Python REPL tool is enabled from configuration."""
|
|
|
|
|
# Check environment variable first
|
|
|
|
|
env_enabled = os.getenv("ENABLE_PYTHON_REPL", "false").lower()
|
|
|
|
|
if env_enabled in ("true", "1", "yes", "on"):
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
2025-04-07 16:25:55 +08:00
|
|
|
# Initialize REPL and logger
|
2025-08-06 14:27:03 +08:00
|
|
|
repl: Optional[PythonREPL] = PythonREPL() if _is_python_repl_enabled() else None
|
2025-04-07 16:25:55 +08:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@tool
|
|
|
|
|
@log_io
|
|
|
|
|
def python_repl_tool(
|
|
|
|
|
code: Annotated[
|
|
|
|
|
str, "The python code to execute to do further analysis or calculation."
|
|
|
|
|
],
|
|
|
|
|
):
|
|
|
|
|
"""Use this to execute python code and do data analysis or calculation. If you want to see the output of a value,
|
|
|
|
|
you should print it out with `print(...)`. This is visible to the user."""
|
2025-08-06 14:27:03 +08:00
|
|
|
|
|
|
|
|
# Check if the tool is enabled
|
|
|
|
|
if not _is_python_repl_enabled():
|
|
|
|
|
error_msg = "Python REPL tool is disabled. Please enable it in environment configuration."
|
|
|
|
|
logger.warning(error_msg)
|
|
|
|
|
return f"Tool disabled: {error_msg}"
|
|
|
|
|
|
2025-04-07 16:25:55 +08:00
|
|
|
if not isinstance(code, str):
|
|
|
|
|
error_msg = f"Invalid input: code must be a string, got {type(code)}"
|
|
|
|
|
logger.error(error_msg)
|
|
|
|
|
return f"Error executing code:\n```python\n{code}\n```\nError: {error_msg}"
|
|
|
|
|
|
|
|
|
|
logger.info("Executing Python code")
|
|
|
|
|
try:
|
|
|
|
|
result = repl.run(code)
|
|
|
|
|
# Check if the result is an error message by looking for typical error patterns
|
|
|
|
|
if isinstance(result, str) and ("Error" in result or "Exception" in result):
|
|
|
|
|
logger.error(result)
|
|
|
|
|
return f"Error executing code:\n```python\n{code}\n```\nError: {result}"
|
|
|
|
|
logger.info("Code execution successful")
|
|
|
|
|
except BaseException as e:
|
|
|
|
|
error_msg = repr(e)
|
|
|
|
|
logger.error(error_msg)
|
|
|
|
|
return f"Error executing code:\n```python\n{code}\n```\nError: {error_msg}"
|
|
|
|
|
|
|
|
|
|
result_str = f"Successfully executed:\n```python\n{code}\n```\nStdout: {result}"
|
|
|
|
|
return result_str
|