Duibonduil commited on
Commit
5fad09e
·
verified ·
1 Parent(s): f3ad829

Upload 2 files

Browse files
examples/tools/interpreters/actions.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf-8
2
+ # Copyright (c) 2025 inclusionAI.
3
+ from examples.tools.tool_action import PythonToolAction
4
+ from aworld.core.tool.action_factory import ActionFactory
5
+ from aworld.core.tool.action import ExecutableAction
6
+
7
+
8
+ @ActionFactory.register(name=PythonToolAction.EXECUTE.value.name,
9
+ desc=PythonToolAction.EXECUTE.value.desc,
10
+ tool_name="python_execute")
11
+ class ExecuteAction(ExecutableAction):
12
+ """Only one action, define it, implemented can be omitted."""
examples/tools/interpreters/python_tool.py ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import ast
3
+ import re
4
+ import subprocess
5
+ from typing import Any, Dict, Tuple, List
6
+ from io import StringIO
7
+
8
+ from aworld.logs.util import logger
9
+ from aworld.config.conf import ToolConfig
10
+ from examples.tools.tool_action import PythonToolAction
11
+ from aworld.core.common import ActionModel, Observation, ActionResult
12
+ from aworld.core.tool.base import Tool, AgentInput, ToolFactory
13
+ from aworld.utils import import_package
14
+ from aworld.tools.utils import build_observation
15
+
16
+
17
+ @ToolFactory.register(name="python_execute",
18
+ desc="python interpreter tool",
19
+ supported_action=PythonToolAction,
20
+ conf_file_name=f'python_execute_tool.yaml')
21
+ class PythonTool(Tool):
22
+
23
+ def __init__(self,
24
+ conf: ToolConfig,
25
+ **kwargs) -> None:
26
+ """
27
+ Initialize the PythonExecutor
28
+ Args:
29
+ conf: tool config
30
+ **kwargs: -
31
+ Return:
32
+ None
33
+ """
34
+ super(PythonTool, self).__init__(conf, **kwargs)
35
+ self.type = "function"
36
+ self.local_namespace = {}
37
+ self.global_namespace = {}
38
+ self.original_stdout = sys.stdout
39
+ self.output_buffer = StringIO()
40
+ self.installed_packages = set()
41
+ import_package('langchain_experimental')
42
+ from langchain_experimental.utilities.python import PythonREPL
43
+ self.python_repl = PythonREPL()
44
+
45
+ def extract_imports(self, code: str) -> set:
46
+ """
47
+ Extract import statements
48
+ Args:
49
+ code: python code
50
+ Returns:
51
+ set: import statements
52
+ """
53
+ imports = set()
54
+
55
+ try:
56
+ tree = ast.parse(code)
57
+
58
+ for node in ast.walk(tree):
59
+ if isinstance(node, ast.Import):
60
+ # deal import xxx or import xxx as yyy
61
+ for name in node.names:
62
+ package_name = name.name.split('.')[0]
63
+ imports.add(package_name)
64
+
65
+ elif isinstance(node, ast.ImportFrom):
66
+ # deal from xxx import yyy or from xxx.yyy import zzz
67
+ if node.module:
68
+ package_name = node.module.split('.')[0]
69
+ imports.add(package_name)
70
+
71
+ except SyntaxError:
72
+ import_pattern = r'^import\s+([\w\s,]+)|from\s+(\w+)'
73
+ for line in code.split('\n'):
74
+ line = line.strip()
75
+ match = re.match(import_pattern, line)
76
+ if match:
77
+ if match.group(1):
78
+
79
+ packages = [p.strip() for p in match.group(1).split(',')]
80
+ for package in packages:
81
+ if package:
82
+ package_name = package.split()[0]
83
+ imports.add(package_name)
84
+ elif match.group(2):
85
+ imports.add(match.group(2))
86
+
87
+ return imports
88
+
89
+ def install_dependencies(self,
90
+ packages: set) -> None:
91
+ """
92
+ Install dependency packages
93
+ Args:
94
+ packages: python third packages
95
+ Returns:
96
+ None
97
+ """
98
+ for package in packages:
99
+ try:
100
+ __import__(package)
101
+ except ImportError:
102
+ try:
103
+ subprocess.check_call([sys.executable, "-m", "pip", "install", package])
104
+ self.installed_packages.add(package)
105
+ except subprocess.CalledProcessError as e:
106
+ logger.warning(f"Failed to install {package}: {str(e)}")
107
+
108
+ def uninstall_dependencies(self) -> None:
109
+ """
110
+ Uninstall dependency packages
111
+ Args:
112
+ -
113
+ Returns:
114
+ None
115
+ """
116
+ try:
117
+ for package in self.installed_packages:
118
+ try:
119
+ subprocess.check_call([sys.executable, "-m", "pip", "uninstall", "-y", package])
120
+ except subprocess.CalledProcessError as e:
121
+ logger.warning(f"Failed to uninstall {package}: {str(e)}")
122
+ self.installed_packages.clear()
123
+ except Exception as e:
124
+ logger.warning(f"Failed to uninstall dependencies: {repr(e)}")
125
+
126
+ def reset(self,
127
+ *,
128
+ seed: int | None = None,
129
+ options: Dict[str, str] | None = None) -> Tuple[AgentInput, dict[str, Any]]:
130
+ """
131
+ Reset the executor
132
+ Args:
133
+ seed: -
134
+ options: -
135
+ Returns:
136
+ AgentInput, dict[str, Any]: -
137
+ """
138
+ self.close()
139
+ self.local_namespace = {}
140
+ self.global_namespace = {}
141
+ self._finished = False
142
+ self.installed_packages.clear()
143
+
144
+ return build_observation(observer=self.name(),
145
+ ability=PythonToolAction.EXECUTE.value.name), {}
146
+
147
+ def close(self) -> None:
148
+ """
149
+ Close the executor
150
+ Returns:
151
+ None
152
+ """
153
+ try:
154
+ self.uninstall_dependencies()
155
+ sys.stdout = self.original_stdout
156
+ self.output_buffer.close()
157
+ self.local_namespace.clear()
158
+ self.global_namespace.clear()
159
+ except:
160
+ pass
161
+ finally:
162
+ self._finished = True
163
+
164
+ def do_step(
165
+ self,
166
+ actions: List[ActionModel],
167
+ **kwargs) -> Tuple[Observation, float, bool, bool, dict[str, Any]]:
168
+ """
169
+ Step the executor
170
+ Args:
171
+ actions: actions
172
+ **kwargs: -
173
+ Returns:
174
+ Observation, float, bool, bool, dict[str, Any]: -
175
+ """
176
+ self.step_finished = False
177
+ reward = 0
178
+ fail_error = ""
179
+ observation = build_observation(observer=self.name(),
180
+ ability=PythonToolAction.EXECUTE.value.name)
181
+ try:
182
+ if not actions:
183
+ return (observation, reward,
184
+ kwargs.get("terminated",
185
+ False), kwargs.get("truncated", False), {
186
+ "exception": "actions is empty"
187
+ })
188
+ for action in actions:
189
+ code = action.params.get("code", "")
190
+ if not code:
191
+ logger.warning(f"{action} no code to execute.")
192
+ continue
193
+ try:
194
+ _, output, error = self.execute(code)
195
+ observation.content = output
196
+ except Exception as e:
197
+ error = str(e)
198
+ output = error
199
+
200
+ observation.action_result.append(
201
+ ActionResult(is_done=True,
202
+ success=False if error else True,
203
+ content=f"{output}",
204
+ error=f"{error}",
205
+ keep=False))
206
+ reward = 1
207
+ except Exception as e:
208
+ fail_error = str(e)
209
+ finally:
210
+ self._finished = True
211
+
212
+ info = {"exception": fail_error}
213
+ info.update(kwargs)
214
+ return (observation, reward, kwargs.get("terminated", False),
215
+ kwargs.get("truncated", False), info)
216
+
217
+ def execute(self, code, timeout=300):
218
+ """
219
+ Execute the code
220
+ Args:
221
+ code: python code
222
+ timeout: timeout seconds
223
+ Returns:
224
+ result, output, error
225
+ """
226
+ required_packages = self.extract_imports(code)
227
+ self.install_dependencies(required_packages)
228
+ self.python_repl.globals = self.global_namespace
229
+ self.python_repl.locals = self.local_namespace
230
+ error = None
231
+ try:
232
+ output = self.python_repl.run(code, timeout)
233
+ except Exception as e:
234
+ error = f'{repr(e)}'
235
+ finally:
236
+ self.uninstall_dependencies()
237
+ return '', output, error
238
+
239
+ def get_execute_result(self):
240
+ """
241
+ Get the execute result
242
+ Returns:
243
+ output, error
244
+ """
245
+ output = None
246
+ error = ''
247
+ try:
248
+ output = self.output_buffer.getvalue()
249
+ self.output_buffer.truncate(0)
250
+ self.output_buffer.seek(0)
251
+ sys.stdout = self.original_stdout
252
+ except Exception as e:
253
+ error = f'{repr(e)}'
254
+ logger.warning(f"Failed to get output, {repr(e)}")
255
+ return output, error