PythonInterpreterTool
The PythonInterpreterTool
class is a subclass of Tool
. It is used to run
commands on the current Python Interpreter in a subprocess with a timeout.
The code you provide as the input to this tool is executed in two parts:
- Everything but last line:
- All the lines of the code, except the last one are parsed using python's
ast
module and run using python'sexec
function - The import statements are parsed separately and passed as global variables to
the above mentioned
exec
function - The python dict passed as the
vars
parameter to the tool is passed as local variables to the above mentionedexec
function
- Last line:
- The last line of the code is run using python's
eval
function. Ifeval
returns something, the tool returns it with an exit code of 0. Otherwise anythingeval
printed instdout
is returned by the tool with an exit code of 0. - If the above mentioned
eval
function raises an exception, the last line is run again using python'sexec
function everything that is printed tostdout
is returned by the tool with an exit code of 1.
The entire code execution happens in a subprocess (with a timeout) that
communicates with the main process using a queue from the multiprocessing
module
Parameters
code
(str
): The python code to run in the interpreter.vars
(dict
): Python variables that need to be inserted into the local scope of the interpreter.timeout
(int
): The maximum amount of time (in seconds) to wait for the command to finish. Defaults to 60.
Returns
The return value is of the type: ToolReturn
(Learn more about:
ToolReturn) with the following output and
exit_code:
output
(str): Result of running the last line of your code.exit_code
(int): 0 if success, 1 if failure.
Usage
Running it directly
You can run the tool directly like so:
import asyncio
from embedia.tools import PythonInterpreterTool
python_interpreter = PythonInterpreterTool()
resp = asyncio.run(python_interpreter(code='print("Hello World")'))
Running the above code prints the following on the terminal:
[time: 2023-10-02T07:26:36.153412+00:00] [id: 139757053245712] [event: Tool Start]
Tool: PythonInterpreterTool
Args: ()
Kwargs: {'code': 'print("Hello World")'}
[time: 2023-10-02T07:26:36.225196+00:00] [id: 139757053245712] [event: Tool End]
Tool: PythonInterpreterTool
ExitCode: 0
Output:
Hello World
Using with ToolUserAgent
Let us use the ToolUserAgent
to write some code and run it using the
PythonInterpreterTool
(Learn more about:
ToolUserAgent)
import asyncio
import os
import openai
import tiktoken
from embedia import ChatLLM, Tokenizer
from embedia.agents import ToolUserAgent
from embedia.tools import PythonInterpreterTool
class OpenAITokenizer(Tokenizer):
def __init__(self):
super().__init__()
async def _tokenize(self, text):
return tiktoken.encoding_for_model("gpt-3.5-turbo").encode(text)
class OpenAIChatLLM(ChatLLM):
def __init__(self):
super().__init__(tokenizer=OpenAITokenizer())
openai.api_key = os.environ['OPENAI_API_KEY']
async def _reply(self, prompt):
completion = await openai.ChatCompletion.acreate(
model="gpt-3.5-turbo",
temperature=0.1,
messages=[{
'role': msg.role,
'content': msg.content
} for msg in self.chat_history],
)
return completion.choices[0].message.content
if __name__ == '__main__':
tool_user_agent = ToolUserAgent(chatllm=OpenAIChatLLM(),
tools=[PythonInterpreterTool()])
resp = asyncio.run(
tool_user_agent("Count the number of lines in main.py"))
Running the above code prints the following on the terminal:
[time: 2023-10-02T07:34:46.633286+00:00] [id: 140137853475168] [event: Tool Start]
Tool: ToolUserAgent
Args: ('Count the number of lines in main.py',)
Kwargs: {}
[time: 2023-10-02T07:34:46.640763+00:00] [id: 140137853475168] [event: Agent Start]
Main Question:
Count the number of lines in main.py
[time: 2023-10-02T07:34:47.636894+00:00] [id: 140137853521584] [event: ChatLLM Init]
system (81 tokens):
You're an expert in choosing the function arguments based on the user's question. The question, the function description, the list of parameters and their descriptions will be provided to you. Reply with all arguments in the following json format:
{
<parameter name>: <argument value>,
<parameter name>: <argument value>,
<parameter name>: <argument value>,
}
Do not reply with anything else
[time: 2023-10-02T07:34:47.638714+00:00] [id: 140137853521584] [event: ChatLLM Start]
user (66 tokens):
Question: Count the number of lines in main.py
Function: Runs the provided python code in the current interpreter
Parameters:
code: Python code to be run (type: str)
vars: A python dictionary containing variables to be passed to the code
timeout: Timeout in seconds (type: int). Defaults to 60.
[time: 2023-10-02T07:34:49.484452+00:00] [id: 140137853521584] [event: ChatLLM End]
assistant (42 tokens):
{
"code": "with open('main.py', 'r') as file:\n lines = file.readlines()\nlen(lines)",
"vars": {},
"timeout": 60
}
Tool: ToolUserAgent
Details: {'tool': 'Python Interpreter', 'args': {'code': "with open('main.py', 'r') as file:\n lines = file.readlines()\nlen(lines)", 'vars': {}, 'timeout': 60}} Confirm (y/n): y
[time: 2023-10-02T07:34:55.124291+00:00] [id: 140137867148688] [event: Tool Start]
Tool: PythonInterpreterTool
Args: ()
Kwargs: {'code': "with open('main.py', 'r') as file:\n lines = file.readlines()\nlen(lines)", 'vars': {}, 'timeout': 60}
[time: 2023-10-02T07:34:55.177002+00:00] [id: 140137867148688] [event: Tool End]
Tool: PythonInterpreterTool
ExitCode: 0
Output:
42
[time: 2023-10-02T07:34:55.177935+00:00] [id: 140137853475168] [event: Agent Step]
Question: Count the number of lines in main.py
ToolChoice: Python Interpreter
ToolArgs: {'code': "with open('main.py', 'r') as file:\n lines = file.readlines()\nlen(lines)", 'vars': {}, 'timeout': 60}
ToolExitCode: 0
ToolOutput: 42
[time: 2023-10-02T07:34:55.179037+00:00] [id: 140137853521296] [event: ChatLLM Init]
system (113 tokens):
You're an expert in deciding what next question should be asked (if any) to reach the final answer. Your question will be acted upon and its result will be provided to you. This will repeat until we reach the final answer. The main question, actions taken till now and their results will be provided to you.
If we've reached the final answer, reply with the answer in the following format:
Final Answer: <final answer>
If not, reply with the next question in the following format:
Question: <next question>
Do not reply with anything else
[time: 2023-10-02T07:34:55.194171+00:00] [id: 140137853521296] [event: ChatLLM Start]
user (28 tokens):
Main question: Count the number of lines in main.py
Question: Count the number of lines in main.py
Output: 42
[time: 2023-10-02T07:34:56.501344+00:00] [id: 140137853521296] [event: ChatLLM End]
assistant (5 tokens):
Final Answer: 42
[time: 2023-10-02T07:34:56.517049+00:00] [id: 140137853475168] [event: Agent End]
Final Answer:
42
[time: 2023-10-02T07:34:56.517882+00:00] [id: 140137853475168] [event: Tool End]
Tool: ToolUserAgent
ExitCode: 0
Output:
42