nodejs and python using terminal, confirmation dialogs
frdel committed
Jun 11, 2024 at 00:35 UTC
f74a60359b659dd88ed39c5978c89baba3f985a5
7 files changed
+41
-90
agent.py
+1
-1
@@ -8,7 +8,7 @@ from langchain_core.messages import HumanMessage
8
from langchain_core.language_models.chat_models import BaseChatModel
9
10
11
-rate_limit = rate_limiter.rate_limiter(30,80000)
11
+rate_limit = rate_limiter.rate_limiter(30,80000) #TODO! move to main.py
12
13
class Agent:
14
prompts/agent.tools.md
+2
@@ -64,6 +64,8 @@ This tool can be used to achieve any task that requires computation, communicati
64
Place your command or code into the tag body. No escaping is wanted, maintain proper indentation.
65
Select the corresponding runtime with "runtime" argument. Possible values are "terminal", "python" and "nodejs".
66
You can use pip, npm and apt-get in terminal runtime to install any required packages.
67
+If you want to get output of your code, you have to to use print() or console.log() to output selected variables.
68
+Do not use return or leave standalone variable at the end, print instead.
69
When tool outputs error, you need to change your code accordingly before trying again. Online knowledge tool can help analyze errors.
70
Do not wrap code in any markdown or other formatting. Only provide raw code.
71
Keep in mind that current working directory CWD automatically resets with every tool call.
prompts/fw.msg_truncated.md
new
+1
@@ -0,0 +1 @@
1
+<< REMOVED TO SAVE SPACE >>
\ No newline at end of file
test.py
new
+8
@@ -0,0 +1,8 @@
1
+import subprocess
2
+
3
+def execute_terminal_command(command, input_data=None):
4
+ result = subprocess.run(command, shell=True, capture_output=True, text=True, input=input_data)
5
+ return result.stdout + result.stderr
6
+
7
+print("1",execute_terminal_command("ls", "y\n"))
8
+print("2",execute_terminal_command("ls", None))
\ No newline at end of file
tools/code_execution_tool.py
+13
-88
@@ -1,6 +1,6 @@
1
import os, json, contextlib, subprocess, ast, shlex
2
from io import StringIO
3
-from tools.helpers import files
3
+from tools.helpers import files, messages
4
from agent import Agent
5
6
@@ -9,99 +9,24 @@ def execute(agent:Agent , code_text:str, runtime:str, **kwargs):
9
os.chdir(files.get_abs_path("./work_dir")) #change CWD to work_dir
10
11
if runtime == "python":
12
- return execute_python_code(code_text)
12
+ response = execute_python_code(code_text)
13
elif runtime == "nodejs":
14
- return execute_nodejs_code(code_text)
14
+ response = execute_nodejs_code(code_text)
15
elif runtime == "terminal":
16
- return execute_terminal_command(code_text)
16
+ response = execute_terminal_command(code_text)
17
else:
18
return files.read_file("./prompts/fw.code_runtime_wrong.md", runtime=runtime)
19
-
20
- # def execute_code(code_string, input=None):
21
- # os.chdir(files.get_abs_path("./work_dir")) #change CWD to work_dir
22
- # buffer = StringIO()
23
- # local_vars = {} # {"input": input}
24
-
25
- # with contextlib.redirect_stdout(buffer):
26
- # try:
27
- # indented_code = "\n ".join(code_string.strip().split("\n"))
28
- # wrapped_code = f"""def isolate(input):\n {indented_code}"""
29
- # exec(wrapped_code, None, local_vars) # exec(code_string, {"__builtins__": __builtins__}, local_vars)
30
- # return local_vars.get('isolate', lambda: None)(input) # type: ignore # calling main if defined to get its return value
31
-
32
- # except Exception as e:
33
- # import traceback
34
- # error_info = traceback.format_exc()
35
- # return json.dumps({"error": str(e), "details": error_info})
36
- # # return local_vars.get("output", buffer.getvalue())
37
-
38
- # result = json.dumps(output) if (output := execute_code(code_text)) else files.read_file("./prompts/fw.code_no_output.md")
39
- # return result
40
-
41
-
42
-
43
-def wrap_code_with_return_and_function(code):
44
- # Parse the code into an AST
45
- parsed_code = ast.parse(code)
46
- # Filter out only executable statements, ignoring comments and empty lines
47
- executable_statements = [stmt for stmt in parsed_code.body if not isinstance(stmt, ast.Pass)]
48
-
49
- if not executable_statements:
50
- raise Exception("There are no executable statements in the code.")
51
- else:
52
- # Get the last executable statement in the code
53
- last_statement = executable_statements[-1]
54
-
55
- # Check if the last statement is an expression (including function calls)
56
- if isinstance(last_statement, ast.Expr):
57
- # Convert the expression into a return statement
58
- return_stmt = ast.Return(value=last_statement.value)
59
- return_stmt.lineno = last_statement.lineno
60
- return_stmt.col_offset = last_statement.col_offset
61
- parsed_code.body[parsed_code.body.index(last_statement)] = return_stmt
62
-
63
- # Wrap the entire code in a function definition
64
- function_def = ast.FunctionDef(
65
- name="isolate",
66
- args=ast.arguments(
67
- posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[]
68
- ),
69
- body=parsed_code.body,
70
- decorator_list=[],
71
- lineno=1,
72
- col_offset=0
73
- ) # type: ignore
74
-
75
- # Create a new module with the function definition
76
- module = ast.Module(body=[function_def], type_ignores=[])
77
-
78
- # Convert the AST back to source code
79
- wrapped_code = compile(module, filename="<ast>", mode="exec")
80
- return wrapped_code
19
82
-def execute_python_code(code):
83
- try:
84
- wrapped_code = wrap_code_with_return_and_function(code)
85
- exec_globals = {}
86
- exec_locals = {}
87
- exec(wrapped_code, exec_globals, exec_locals)
88
- try:
89
- return exec_locals.get('isolate', lambda: None)()
90
- except Exception as e:
91
- import traceback
92
- error_info = traceback.format_exc()
93
- return json.dumps({"error": str(e), "details": error_info})
20
+ return messages.truncate_text(response, 2000) # TODO parameterize
21
95
- except Exception as e:
96
- return "Error: " + str(e)
22
+def execute_python_code(code, input_data="y\n"):
23
+ result = subprocess.run(['python', '-c', code], capture_output=True, text=True, input=input_data)
24
+ return result.stdout + result.stderr
25
98
-def execute_nodejs_code(code):
99
- # Ensure code is properly escaped
100
- escaped_code = shlex.quote(code)
101
- process = subprocess.Popen(['node', '-e', escaped_code], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
102
- stdout, stderr = process.communicate()
103
- return stdout, stderr
26
+def execute_nodejs_code(code, input_data="y\n"):
27
+ result = subprocess.run(['node', '-e', code], capture_output=True, text=True, input=input_data)
28
+ return result.stdout + result.stderr
29
105
-def execute_terminal_command(command):
106
- result = subprocess.run(command, shell=True, text=True, capture_output=True)
30
+def execute_terminal_command(command, input_data="y\n"):
31
+ result = subprocess.run(command, shell=True, capture_output=True, text=True, input=input_data)
32
return result.stdout + result.stderr
\ No newline at end of file
tools/helpers/messages.py
new
+15
@@ -0,0 +1,15 @@
1
+from . import files
2
+
3
+
4
+def truncate_text(output, threshold=1000):
5
+ if len(output) <= threshold:
6
+ return output
7
+
8
+ # Adjust the file path as needed
9
+ placeholder = files.read_file("./prompts/fw.msg_truncated.md", removed_chars=(len(output) - threshold))
10
+
11
+ start_len = (threshold - len(placeholder)) // 2
12
+ end_len = threshold - len(placeholder) - start_len
13
+
14
+ truncated_output = output[:start_len] + placeholder + output[-end_len:]
15
+ return truncated_output
\ No newline at end of file
tools/memory_tool.py
+1
-1
@@ -4,7 +4,7 @@ from tools.helpers import files
4
import os, json
5
6
db: VectorDB
7
-result_count = 3
7
+result_count = 3 #TODO parametrize better
8
9
def initialize(embeddings_model,messages_returned=3, subdir=""):
10
global db, result_count