Skip to content

Commit e485bba

Browse files
Rename ExecuteBashAction/ExecuteBashObservation to TerminalAction/TerminalObservation (#1193)
Co-authored-by: openhands <[email protected]>
1 parent 7875950 commit e485bba

22 files changed

+354
-263
lines changed

examples/01_standalone_sdk/02_custom_tools.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
)
2727
from openhands.tools.file_editor import FileEditorTool
2828
from openhands.tools.terminal import (
29-
BashExecutor,
30-
ExecuteBashAction,
29+
TerminalAction,
30+
TerminalExecutor,
3131
TerminalTool,
3232
)
3333

@@ -71,8 +71,8 @@ def to_llm_content(self) -> Sequence[TextContent | ImageContent]:
7171

7272

7373
class GrepExecutor(ToolExecutor[GrepAction, GrepObservation]):
74-
def __init__(self, bash: BashExecutor):
75-
self.bash: BashExecutor = bash
74+
def __init__(self, terminal: TerminalExecutor):
75+
self.terminal: TerminalExecutor = terminal
7676

7777
def __call__(self, action: GrepAction, conversation=None) -> GrepObservation: # noqa: ARG002
7878
root = os.path.abspath(action.path)
@@ -86,7 +86,7 @@ def __call__(self, action: GrepAction, conversation=None) -> GrepObservation: #
8686
else:
8787
cmd = f"grep -rHnE {pat} {root_q} 2>/dev/null | head -100"
8888

89-
result = self.bash(ExecuteBashAction(command=cmd))
89+
result = self.terminal(TerminalAction(command=cmd))
9090

9191
matches: list[str] = []
9292
files: set[str] = set()
@@ -125,21 +125,23 @@ class GrepTool(ToolDefinition[GrepAction, GrepObservation]):
125125

126126
@classmethod
127127
def create(
128-
cls, conv_state, bash_executor: BashExecutor | None = None
128+
cls, conv_state, terminal_executor: TerminalExecutor | None = None
129129
) -> Sequence[ToolDefinition]:
130130
"""Create GrepTool instance with a GrepExecutor.
131131
132132
Args:
133133
conv_state: Conversation state to get working directory from.
134-
bash_executor: Optional bash executor to reuse. If not provided,
134+
terminal_executor: Optional terminal executor to reuse. If not provided,
135135
a new one will be created.
136136
137137
Returns:
138138
A sequence containing a single GrepTool instance.
139139
"""
140-
if bash_executor is None:
141-
bash_executor = BashExecutor(working_dir=conv_state.workspace.working_dir)
142-
grep_executor = GrepExecutor(bash_executor)
140+
if terminal_executor is None:
141+
terminal_executor = TerminalExecutor(
142+
working_dir=conv_state.workspace.working_dir
143+
)
144+
grep_executor = GrepExecutor(terminal_executor)
143145

144146
return [
145147
cls(
@@ -170,14 +172,14 @@ def create(
170172
def _make_bash_and_grep_tools(conv_state) -> list[ToolDefinition]:
171173
"""Create terminal and custom grep tools sharing one executor."""
172174

173-
bash_executor = BashExecutor(working_dir=conv_state.workspace.working_dir)
174-
# bash_tool = terminal_tool.set_executor(executor=bash_executor)
175-
bash_tool = TerminalTool.create(conv_state, executor=bash_executor)[0]
175+
terminal_executor = TerminalExecutor(working_dir=conv_state.workspace.working_dir)
176+
# terminal_tool = terminal_tool.set_executor(executor=terminal_executor)
177+
terminal_tool = TerminalTool.create(conv_state, executor=terminal_executor)[0]
176178

177-
# Use the GrepTool.create() method with shared bash_executor
178-
grep_tool = GrepTool.create(conv_state, bash_executor=bash_executor)[0]
179+
# Use the GrepTool.create() method with shared terminal_executor
180+
grep_tool = GrepTool.create(conv_state, terminal_executor=terminal_executor)[0]
179181

180-
return [bash_tool, grep_tool]
182+
return [terminal_tool, grep_tool]
181183

182184

183185
register_tool("BashAndGrepToolSet", _make_bash_and_grep_tools)

openhands-sdk/openhands/sdk/tool/tool.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,11 @@ def create(cls, conv_state=None, **params):
165165
return [cls(name="finish", ..., executor=FinishExecutor())]
166166
167167
Complex tool with initialization parameters:
168-
class TerminalTool(ToolDefinition[ExecuteBashAction,
169-
ExecuteBashObservation]):
168+
class TerminalTool(ToolDefinition[TerminalAction,
169+
TerminalObservation]):
170170
@classmethod
171171
def create(cls, conv_state, **params):
172-
executor = BashExecutor(
172+
executor = TerminalExecutor(
173173
working_dir=conv_state.workspace.working_dir,
174174
**params,
175175
)

openhands-tools/openhands/tools/terminal/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,20 @@ If bash cannot be found in PATH, the tool will raise a clear error asking you to
4343

4444
```python
4545
from openhands.sdk import Conversation
46-
from openhands.tools.terminal.definition import TerminalTool, ExecuteBashAction
46+
from openhands.tools.terminal.definition import TerminalTool, TerminalAction
4747

4848
conversation = Conversation()
4949
tools = TerminalTool.create(conv_state=conversation.state)
5050
terminal = tools[0]
5151

5252
# Execute a command
53-
action = ExecuteBashAction(command="echo 'Hello, World!'")
53+
action = TerminalAction(command="echo 'Hello, World!'")
5454
result = terminal.executor(action)
5555
print(result.text)
5656
```
5757

58+
**Note:** `TerminalAction` and `TerminalObservation` replace the deprecated `ExecuteBashAction` and `ExecuteBashObservation` (which will be removed in version 1.5.0).
59+
5860
### With Custom Shell on Nix/macOS
5961

6062
```python

openhands-tools/openhands/tools/terminal/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
from openhands.tools.terminal.definition import (
33
ExecuteBashAction,
44
ExecuteBashObservation,
5+
TerminalAction,
6+
TerminalObservation,
57
TerminalTool,
68
)
7-
from openhands.tools.terminal.impl import BashExecutor
9+
from openhands.tools.terminal.impl import BashExecutor, TerminalExecutor
810

911
# Terminal session architecture - import from sessions package
1012
from openhands.tools.terminal.terminal import (
@@ -17,6 +19,10 @@
1719
__all__ = [
1820
# === Core Tool Interface ===
1921
"TerminalTool",
22+
"TerminalAction",
23+
"TerminalObservation",
24+
"TerminalExecutor",
25+
# === Deprecated Aliases (backward compatibility) ===
2026
"ExecuteBashAction",
2127
"ExecuteBashObservation",
2228
"BashExecutor",

openhands-tools/openhands/tools/terminal/definition.py

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from openhands.tools.terminal.metadata import CmdOutputMetadata
2929

3030

31-
class ExecuteBashAction(Action):
31+
class TerminalAction(Action):
3232
"""Schema for bash command execution."""
3333

3434
command: str = Field(
@@ -78,7 +78,7 @@ def visualize(self) -> Text:
7878
return content
7979

8080

81-
class ExecuteBashObservation(Observation):
81+
class TerminalObservation(Observation):
8282
"""A ToolResult that can be rendered as a CLI output."""
8383

8484
command: str | None = Field(
@@ -109,7 +109,7 @@ def to_llm_content(self) -> Sequence[TextContent | ImageContent]:
109109
if self.is_error:
110110
llm_content.append(TextContent(text=self.ERROR_MESSAGE_HEADER))
111111

112-
# ExecuteBashObservation always has content as a single TextContent
112+
# TerminalObservation always has content as a single TextContent
113113
content_text = self.text
114114

115115
ret = f"{self.metadata.prefix}{content_text}{self.metadata.suffix}"
@@ -132,7 +132,7 @@ def visualize(self) -> Text:
132132
text.append("❌ ", style="red bold")
133133
text.append(self.ERROR_MESSAGE_HEADER, style="bold red")
134134

135-
# ExecuteBashObservation always has content as a single TextContent
135+
# TerminalObservation always has content as a single TextContent
136136
content_text = self.text
137137

138138
if content_text:
@@ -219,8 +219,8 @@ def visualize(self) -> Text:
219219
""" # noqa
220220

221221

222-
class TerminalTool(ToolDefinition[ExecuteBashAction, ExecuteBashObservation]):
223-
"""A ToolDefinition subclass that automatically initializes a BashExecutor with auto-detection.""" # noqa: E501
222+
class TerminalTool(ToolDefinition[TerminalAction, TerminalObservation]):
223+
"""A ToolDefinition subclass that automatically initializes a TerminalExecutor with auto-detection.""" # noqa: E501
224224

225225
@classmethod
226226
def create(
@@ -249,15 +249,15 @@ def create(
249249
If None, will auto-detect bash from PATH.
250250
"""
251251
# Import here to avoid circular imports
252-
from openhands.tools.terminal.impl import BashExecutor
252+
from openhands.tools.terminal.impl import TerminalExecutor
253253

254254
working_dir = conv_state.workspace.working_dir
255255
if not os.path.isdir(working_dir):
256256
raise ValueError(f"working_dir '{working_dir}' is not a valid directory")
257257

258258
# Initialize the executor
259259
if executor is None:
260-
executor = BashExecutor(
260+
executor = TerminalExecutor(
261261
working_dir=working_dir,
262262
username=username,
263263
no_change_timeout_seconds=no_change_timeout_seconds,
@@ -268,8 +268,8 @@ def create(
268268
# Initialize the parent ToolDefinition with the executor
269269
return [
270270
cls(
271-
action_type=ExecuteBashAction,
272-
observation_type=ExecuteBashObservation,
271+
action_type=TerminalAction,
272+
observation_type=TerminalObservation,
273273
description=TOOL_DESCRIPTION,
274274
annotations=ToolAnnotations(
275275
title="terminal",
@@ -285,3 +285,50 @@ def create(
285285

286286
# Automatically register the tool when this module is imported
287287
register_tool(TerminalTool.name, TerminalTool)
288+
289+
290+
# Deprecated aliases for backward compatibility
291+
class ExecuteBashAction(TerminalAction):
292+
"""Deprecated: Use TerminalAction instead.
293+
294+
This class is deprecated and will be removed in version 1.5.0.
295+
Please use TerminalAction instead.
296+
"""
297+
298+
def __init__(self, **data): # type: ignore[no-untyped-def]
299+
from openhands.sdk.utils.deprecation import warn_deprecated
300+
301+
warn_deprecated(
302+
"ExecuteBashAction",
303+
deprecated_in="1.2.0",
304+
removed_in="1.5.0",
305+
details=(
306+
"Use TerminalAction instead. ExecuteBashAction is an "
307+
"alias for TerminalAction and will be removed in version 1.5.0."
308+
),
309+
stacklevel=3,
310+
)
311+
super().__init__(**data)
312+
313+
314+
class ExecuteBashObservation(TerminalObservation):
315+
"""Deprecated: Use TerminalObservation instead.
316+
317+
This class is deprecated and will be removed in version 1.5.0.
318+
Please use TerminalObservation instead.
319+
"""
320+
321+
def __init__(self, **data): # type: ignore[no-untyped-def]
322+
from openhands.sdk.utils.deprecation import warn_deprecated
323+
324+
warn_deprecated(
325+
"ExecuteBashObservation",
326+
deprecated_in="1.2.0",
327+
removed_in="1.5.0",
328+
details=(
329+
"Use TerminalObservation instead. ExecuteBashObservation is an "
330+
"alias for TerminalObservation and will be removed in version 1.5.0."
331+
),
332+
stacklevel=3,
333+
)
334+
super().__init__(**data)

0 commit comments

Comments
 (0)