Duibonduil commited on
Commit
3a3f97e
·
verified ·
1 Parent(s): 9c271ef

Upload 2 files

Browse files
examples/tools/terminals/actions.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf-8
2
+ # Copyright (c) 2025 inclusionAI.
3
+ from examples.tools.tool_action import ShellAction
4
+ from aworld.core.tool.action_factory import ActionFactory
5
+ from aworld.core.tool.action import ExecutableAction
6
+
7
+
8
+ @ActionFactory.register(name=ShellAction.EXECUTE_SCRIPT.value.name,
9
+ desc=ShellAction.EXECUTE_SCRIPT.value.desc,
10
+ tool_name="shell")
11
+ class ShellAction(ExecutableAction):
12
+ """Only one action, define it, implemented can be omitted. Act in tool."""
examples/tools/terminals/shell_tool.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf-8
2
+ # Copyright (c) 2025 inclusionAI.
3
+
4
+ import subprocess
5
+ import os
6
+ import signal
7
+ import sys
8
+ from typing import Any, Dict, Tuple
9
+
10
+ from aworld.config.conf import ToolConfig
11
+ from examples.tools.tool_action import ShellAction
12
+ from aworld.core.common import ActionModel, Observation, ActionResult
13
+ from aworld.core.tool.base import Tool, AgentInput, ToolFactory
14
+ from aworld.logs.util import logger
15
+ from aworld.tools.utils import build_observation
16
+
17
+
18
+ @ToolFactory.register(name='shell',
19
+ desc="shell execute tool",
20
+ supported_action=ShellAction,
21
+ conf_file_name=f'shell_tool.yaml')
22
+ class ShellTool(Tool):
23
+ """
24
+ used to execute shell commands, providing initialization, execution, and exit functions.
25
+ """
26
+
27
+ def __init__(self, conf: ToolConfig, **kwargs) -> None:
28
+ """
29
+ Initialize the ShellTool
30
+ Args:
31
+ conf: tool config
32
+ **kwargs: -
33
+ """
34
+ super(ShellTool, self).__init__(conf, **kwargs)
35
+ self.type = "function"
36
+ self.working_dir = self.conf.get('working_dir')
37
+ self.env = self.conf.get('env') if self.conf.get('env') else os.environ.copy()
38
+ self.processes = []
39
+
40
+ def reset(self, *, seed: int | None = None, options: Dict[str, str] | None = None) -> Tuple[
41
+ AgentInput, dict[str, Any]]:
42
+ """
43
+ Reset the executor
44
+ Args:
45
+ seed: -
46
+ options: -
47
+
48
+ Returns:
49
+ AgentInput, dict[str, Any]: -
50
+ """
51
+ self.working_dir = None
52
+ self.env = os.environ.copy()
53
+ self.processes = []
54
+ self._finished = False
55
+ return build_observation(observer=self.name(),
56
+ ability=ShellAction.EXECUTE_SCRIPT.value.name), {}
57
+
58
+ def close(self) -> None:
59
+ """
60
+ Close the executor
61
+ Returns:
62
+ None
63
+ """
64
+ try:
65
+ for process in self.processes:
66
+ # Check whether the process is still running
67
+ if process.poll() is None:
68
+ try:
69
+ # Try to gracefully terminate the process
70
+ if sys.platform != "win32":
71
+ os.kill(process.pid, signal.SIGTERM)
72
+ else:
73
+ process.terminate()
74
+ except Exception as e:
75
+ logger.warning(f"An error occurred while terminating the process. e: {str(e)}")
76
+ except Exception as e:
77
+ logger.warning(f"Error while exiting Shell Executor. e: {str(e)}")
78
+ finally:
79
+ # Clear process list
80
+ self.processes = []
81
+ self._finished = True
82
+
83
+ def do_step(self,
84
+ actions: list[ActionModel],
85
+ **kwargs) -> Tuple[Observation, float, bool, bool, dict[str, Any]]:
86
+ """
87
+ Step the executor
88
+ Args:
89
+ actions: actions
90
+ **kwargs: -
91
+ Returns:
92
+ Observation, float, bool, bool, dict[str, Any]: -
93
+ """
94
+ self._finished = False
95
+ reward = 0
96
+ fail_error = ""
97
+ observation = build_observation(observer=self.name(),
98
+ ability=ShellAction.EXECUTE_SCRIPT.value.name)
99
+ try:
100
+ if not actions:
101
+ return (observation, reward,
102
+ kwargs.get("terminated",
103
+ False), kwargs.get("truncated", False), {
104
+ "exception": "actions is empty"
105
+ })
106
+
107
+ for action in actions:
108
+ cmd_string = action.params.get("command", "")
109
+ if not cmd_string:
110
+ continue
111
+ _, output, error = self.execute(cmd_string)
112
+
113
+ observation.content = output
114
+ observation.action_result.append(
115
+ ActionResult(is_done=True,
116
+ success=False if error else True,
117
+ content=output,
118
+ error=error,
119
+ keep=False))
120
+ reward = 1
121
+ except Exception as e:
122
+ fail_error = str(e)
123
+ finally:
124
+ self._finished = True
125
+
126
+ info = {"exception": fail_error}
127
+ info.update(kwargs)
128
+ return (observation,
129
+ reward,
130
+ kwargs.get("terminated", False),
131
+ kwargs.get("truncated", False),
132
+ info)
133
+
134
+ def execute(self, script: str, capture_output: bool = True, timeout: int = 5):
135
+ """
136
+ exec shell script
137
+ Args:
138
+ script (str): shell script to execute
139
+ capture_output (bool): whether to capture the script output
140
+ timeout (int, optional): Command execution timeout (seconds)
141
+ Returns:
142
+ dict: action result
143
+ """
144
+ try:
145
+ if capture_output:
146
+ process_ = subprocess.run(
147
+ script,
148
+ shell=True,
149
+ cwd=self.working_dir,
150
+ env=self.env,
151
+ timeout=timeout,
152
+ stdout=subprocess.PIPE,
153
+ stderr=subprocess.PIPE,
154
+ text=True
155
+ )
156
+
157
+ return {
158
+ 'success': process_.returncode == 0,
159
+ 'return_code': process_.returncode,
160
+ 'stdout': process_.stdout,
161
+ 'stderr': process_.stderr,
162
+ 'script': script
163
+ }
164
+ else:
165
+ process_ = subprocess.Popen(
166
+ script,
167
+ shell=True,
168
+ cwd=self.working_dir,
169
+ env=self.env
170
+ )
171
+ self.processes.append(process_)
172
+ process_.wait(timeout=timeout)
173
+
174
+ return {
175
+ 'success': process_.returncode == 0,
176
+ 'return_code': process_.returncode,
177
+ 'script': script
178
+ }
179
+
180
+ except subprocess.TimeoutExpired:
181
+ return {
182
+ 'success': False,
183
+ 'error': 'Timeout',
184
+ 'script': script
185
+ }
186
+ except Exception as e:
187
+ return {
188
+ 'success': False,
189
+ 'error': str(e),
190
+ 'script': script
191
+ }
192
+
193
+ def execute_async(self, script: str):
194
+ """
195
+ Execute shell script asynchronously (no waiting)
196
+ Args:
197
+ script (str): The shell script to execute
198
+ Returns:
199
+ subprocess.Popen: Process object
200
+ """
201
+ try:
202
+ process_ = subprocess.Popen(
203
+ script,
204
+ shell=True,
205
+ cwd=self.working_dir,
206
+ env=self.env
207
+ )
208
+ self.processes.append(process_)
209
+ return process_
210
+ except Exception as e:
211
+ logger.warning(f"An error occurred while executing the script asynchronously. e: {str(e)}")
212
+ return None