openhands 0.0.0__py3-none-any.whl → 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of openhands might be problematic. Click here for more details.

Files changed (124) hide show
  1. openhands-1.0.1.dist-info/METADATA +52 -0
  2. openhands-1.0.1.dist-info/RECORD +31 -0
  3. {openhands-0.0.0.dist-info → openhands-1.0.1.dist-info}/WHEEL +1 -2
  4. openhands-1.0.1.dist-info/entry_points.txt +2 -0
  5. openhands_cli/__init__.py +8 -0
  6. openhands_cli/agent_chat.py +186 -0
  7. openhands_cli/argparsers/main_parser.py +56 -0
  8. openhands_cli/argparsers/serve_parser.py +31 -0
  9. openhands_cli/gui_launcher.py +220 -0
  10. openhands_cli/listeners/__init__.py +4 -0
  11. openhands_cli/listeners/loading_listener.py +63 -0
  12. openhands_cli/listeners/pause_listener.py +83 -0
  13. openhands_cli/llm_utils.py +57 -0
  14. openhands_cli/locations.py +13 -0
  15. openhands_cli/pt_style.py +30 -0
  16. openhands_cli/runner.py +178 -0
  17. openhands_cli/setup.py +116 -0
  18. openhands_cli/simple_main.py +59 -0
  19. openhands_cli/tui/__init__.py +5 -0
  20. openhands_cli/tui/settings/mcp_screen.py +217 -0
  21. openhands_cli/tui/settings/settings_screen.py +202 -0
  22. openhands_cli/tui/settings/store.py +93 -0
  23. openhands_cli/tui/status.py +109 -0
  24. openhands_cli/tui/tui.py +100 -0
  25. openhands_cli/tui/utils.py +14 -0
  26. openhands_cli/user_actions/__init__.py +17 -0
  27. openhands_cli/user_actions/agent_action.py +95 -0
  28. openhands_cli/user_actions/exit_session.py +18 -0
  29. openhands_cli/user_actions/settings_action.py +171 -0
  30. openhands_cli/user_actions/types.py +18 -0
  31. openhands_cli/user_actions/utils.py +199 -0
  32. openhands/__init__.py +0 -1
  33. openhands/sdk/__init__.py +0 -45
  34. openhands/sdk/agent/__init__.py +0 -8
  35. openhands/sdk/agent/agent/__init__.py +0 -6
  36. openhands/sdk/agent/agent/agent.py +0 -349
  37. openhands/sdk/agent/base.py +0 -103
  38. openhands/sdk/context/__init__.py +0 -28
  39. openhands/sdk/context/agent_context.py +0 -153
  40. openhands/sdk/context/condenser/__init__.py +0 -5
  41. openhands/sdk/context/condenser/condenser.py +0 -73
  42. openhands/sdk/context/condenser/no_op_condenser.py +0 -13
  43. openhands/sdk/context/manager.py +0 -5
  44. openhands/sdk/context/microagents/__init__.py +0 -26
  45. openhands/sdk/context/microagents/exceptions.py +0 -11
  46. openhands/sdk/context/microagents/microagent.py +0 -345
  47. openhands/sdk/context/microagents/types.py +0 -70
  48. openhands/sdk/context/utils/__init__.py +0 -8
  49. openhands/sdk/context/utils/prompt.py +0 -52
  50. openhands/sdk/context/view.py +0 -116
  51. openhands/sdk/conversation/__init__.py +0 -12
  52. openhands/sdk/conversation/conversation.py +0 -207
  53. openhands/sdk/conversation/state.py +0 -50
  54. openhands/sdk/conversation/types.py +0 -6
  55. openhands/sdk/conversation/visualizer.py +0 -300
  56. openhands/sdk/event/__init__.py +0 -27
  57. openhands/sdk/event/base.py +0 -148
  58. openhands/sdk/event/condenser.py +0 -49
  59. openhands/sdk/event/llm_convertible.py +0 -265
  60. openhands/sdk/event/types.py +0 -5
  61. openhands/sdk/event/user_action.py +0 -12
  62. openhands/sdk/event/utils.py +0 -30
  63. openhands/sdk/llm/__init__.py +0 -19
  64. openhands/sdk/llm/exceptions.py +0 -108
  65. openhands/sdk/llm/llm.py +0 -867
  66. openhands/sdk/llm/llm_registry.py +0 -116
  67. openhands/sdk/llm/message.py +0 -216
  68. openhands/sdk/llm/metadata.py +0 -34
  69. openhands/sdk/llm/utils/fn_call_converter.py +0 -1049
  70. openhands/sdk/llm/utils/metrics.py +0 -311
  71. openhands/sdk/llm/utils/model_features.py +0 -153
  72. openhands/sdk/llm/utils/retry_mixin.py +0 -122
  73. openhands/sdk/llm/utils/telemetry.py +0 -252
  74. openhands/sdk/logger.py +0 -167
  75. openhands/sdk/mcp/__init__.py +0 -20
  76. openhands/sdk/mcp/client.py +0 -113
  77. openhands/sdk/mcp/definition.py +0 -69
  78. openhands/sdk/mcp/tool.py +0 -104
  79. openhands/sdk/mcp/utils.py +0 -59
  80. openhands/sdk/tests/llm/test_llm.py +0 -447
  81. openhands/sdk/tests/llm/test_llm_fncall_converter.py +0 -691
  82. openhands/sdk/tests/llm/test_model_features.py +0 -221
  83. openhands/sdk/tool/__init__.py +0 -30
  84. openhands/sdk/tool/builtins/__init__.py +0 -34
  85. openhands/sdk/tool/builtins/finish.py +0 -57
  86. openhands/sdk/tool/builtins/think.py +0 -60
  87. openhands/sdk/tool/schema.py +0 -236
  88. openhands/sdk/tool/security_prompt.py +0 -5
  89. openhands/sdk/tool/tool.py +0 -142
  90. openhands/sdk/utils/__init__.py +0 -14
  91. openhands/sdk/utils/discriminated_union.py +0 -210
  92. openhands/sdk/utils/json.py +0 -48
  93. openhands/sdk/utils/truncate.py +0 -44
  94. openhands/tools/__init__.py +0 -44
  95. openhands/tools/execute_bash/__init__.py +0 -30
  96. openhands/tools/execute_bash/constants.py +0 -31
  97. openhands/tools/execute_bash/definition.py +0 -166
  98. openhands/tools/execute_bash/impl.py +0 -38
  99. openhands/tools/execute_bash/metadata.py +0 -101
  100. openhands/tools/execute_bash/terminal/__init__.py +0 -22
  101. openhands/tools/execute_bash/terminal/factory.py +0 -113
  102. openhands/tools/execute_bash/terminal/interface.py +0 -189
  103. openhands/tools/execute_bash/terminal/subprocess_terminal.py +0 -412
  104. openhands/tools/execute_bash/terminal/terminal_session.py +0 -492
  105. openhands/tools/execute_bash/terminal/tmux_terminal.py +0 -160
  106. openhands/tools/execute_bash/utils/command.py +0 -150
  107. openhands/tools/str_replace_editor/__init__.py +0 -17
  108. openhands/tools/str_replace_editor/definition.py +0 -158
  109. openhands/tools/str_replace_editor/editor.py +0 -683
  110. openhands/tools/str_replace_editor/exceptions.py +0 -41
  111. openhands/tools/str_replace_editor/impl.py +0 -66
  112. openhands/tools/str_replace_editor/utils/__init__.py +0 -0
  113. openhands/tools/str_replace_editor/utils/config.py +0 -2
  114. openhands/tools/str_replace_editor/utils/constants.py +0 -9
  115. openhands/tools/str_replace_editor/utils/encoding.py +0 -135
  116. openhands/tools/str_replace_editor/utils/file_cache.py +0 -154
  117. openhands/tools/str_replace_editor/utils/history.py +0 -122
  118. openhands/tools/str_replace_editor/utils/shell.py +0 -72
  119. openhands/tools/task_tracker/__init__.py +0 -16
  120. openhands/tools/task_tracker/definition.py +0 -336
  121. openhands/tools/utils/__init__.py +0 -1
  122. openhands-0.0.0.dist-info/METADATA +0 -3
  123. openhands-0.0.0.dist-info/RECORD +0 -94
  124. openhands-0.0.0.dist-info/top_level.txt +0 -1
@@ -1,142 +0,0 @@
1
- from typing import Any, Generic, TypeVar
2
-
3
- from litellm import ChatCompletionToolParam, ChatCompletionToolParamFunctionChunk
4
- from pydantic import BaseModel, Field
5
-
6
- from openhands.sdk.tool.schema import ActionBase, ObservationBase
7
-
8
-
9
- ActionT = TypeVar("ActionT", bound=ActionBase)
10
- ObservationT = TypeVar("ObservationT", bound=ObservationBase)
11
-
12
-
13
- class ToolAnnotations(BaseModel):
14
- """Annotations to provide hints about the tool's behavior.
15
-
16
- Based on Model Context Protocol (MCP) spec:
17
- https://github.com/modelcontextprotocol/modelcontextprotocol/blob/caf3424488b10b4a7b1f8cb634244a450a1f4400/schema/2025-06-18/schema.ts#L838
18
- """
19
-
20
- title: str | None = Field(
21
- default=None, description="A human-readable title for the tool."
22
- )
23
- readOnlyHint: bool = Field(
24
- default=False,
25
- description="If true, the tool does not modify its environment. Default: false",
26
- )
27
- destructiveHint: bool = Field(
28
- default=True,
29
- description="If true, the tool may perform destructive updates to its environment. If false, the tool performs only additive updates. (This property is meaningful only when `readOnlyHint == false`) Default: true", # noqa: E501
30
- )
31
- idempotentHint: bool = Field(
32
- default=False,
33
- description="If true, calling the tool repeatedly with the same arguments will have no additional effect on the its environment. (This property is meaningful only when `readOnlyHint == false`) Default: false", # noqa: E501
34
- )
35
- openWorldHint: bool = Field(
36
- default=True,
37
- description="If true, this tool may interact with an 'open world' of external entities. If false, the tool's domain of interaction is closed. For example, the world of a web search tool is open, whereas that of a memory tool is not. Default: true", # noqa: E501
38
- )
39
-
40
-
41
- class ToolExecutor(Generic[ActionT, ObservationT]):
42
- """Executor function type for a Tool."""
43
-
44
- def __call__(self, action: ActionT) -> ObservationT:
45
- raise NotImplementedError
46
-
47
-
48
- class Tool(Generic[ActionT, ObservationT]):
49
- """Tool that wraps an executor function with input/output validation and schema.
50
-
51
- - Normalize input/output schemas (class or dict) into both model+schema.
52
- - Validate inputs before execute.
53
- - Coerce outputs only if an output model is defined; else return vanilla JSON.
54
- - Export MCP tool description.
55
- """
56
-
57
- def __init__(
58
- self,
59
- *,
60
- name: str,
61
- description: str,
62
- input_schema: type[ActionBase],
63
- output_schema: type[ObservationBase] | None = None,
64
- title: str | None = None,
65
- annotations: ToolAnnotations | None = None,
66
- _meta: dict[str, Any] | None = None,
67
- executor: ToolExecutor | None = None,
68
- ):
69
- self.name = name
70
- self.description = description
71
- self.annotations = annotations
72
- self._meta = _meta
73
- self.title = title or name
74
-
75
- # Schemas
76
- self.action_type: type[ActionBase] = input_schema
77
- self.input_schema: dict[str, Any] = input_schema.to_mcp_schema()
78
- self.observation_type: type[ObservationBase] | None = output_schema
79
- self.output_schema: dict[str, Any] | None = (
80
- output_schema.to_mcp_schema() if output_schema else None
81
- )
82
-
83
- self.executor = executor
84
-
85
- def set_executor(self, executor: ToolExecutor) -> "Tool":
86
- """Set or replace the executor function."""
87
- self.executor = executor
88
- return self
89
-
90
- def call(self, action: ActionT) -> ObservationBase:
91
- """Validate input, execute, and coerce output.
92
-
93
- We always return some ObservationBase subclass, but not always the
94
- generic ObservationT.
95
- """
96
- if self.executor is None:
97
- raise NotImplementedError(f"Tool '{self.name}' has no executor")
98
-
99
- # Execute
100
- result = self.executor(action)
101
-
102
- # Coerce output only if we declared a model; else wrap in base ObservationBase
103
- if self.observation_type:
104
- if isinstance(result, self.observation_type):
105
- return result
106
- return self.observation_type.model_validate(result)
107
- else:
108
- # When no output schema is defined, wrap the result in ObservationBase
109
- if isinstance(result, ObservationBase):
110
- return result
111
- elif isinstance(result, BaseModel):
112
- return ObservationBase.model_validate(result.model_dump())
113
- elif isinstance(result, dict):
114
- return ObservationBase.model_validate(result)
115
- raise TypeError(
116
- "Output must be dict or BaseModel when no output schema is defined"
117
- )
118
-
119
- def to_mcp_tool(self) -> dict[str, Any]:
120
- out = {
121
- "name": self.name,
122
- "description": self.description,
123
- "inputSchema": self.input_schema,
124
- }
125
- if self.annotations:
126
- out["annotations"] = self.annotations
127
- if self._meta is not None:
128
- out["_meta"] = self._meta
129
- if self.output_schema:
130
- out["outputSchema"] = self.output_schema
131
- return out
132
-
133
- def to_openai_tool(self) -> ChatCompletionToolParam:
134
- """Convert an MCP tool to an OpenAI tool."""
135
- return ChatCompletionToolParam(
136
- type="function",
137
- function=ChatCompletionToolParamFunctionChunk(
138
- name=self.name,
139
- description=self.description,
140
- parameters=self.input_schema,
141
- ),
142
- )
@@ -1,14 +0,0 @@
1
- """Utility functions for the OpenHands SDK."""
2
-
3
- from .truncate import (
4
- DEFAULT_TEXT_CONTENT_LIMIT,
5
- DEFAULT_TRUNCATE_NOTICE,
6
- maybe_truncate,
7
- )
8
-
9
-
10
- __all__ = [
11
- "DEFAULT_TEXT_CONTENT_LIMIT",
12
- "DEFAULT_TRUNCATE_NOTICE",
13
- "maybe_truncate",
14
- ]
@@ -1,210 +0,0 @@
1
- """Utility for creating and managing discriminated unions of Pydantic models.
2
-
3
- Pydantic provides native support for disciriminated unions via the `Union` type and
4
- the `discriminator` argument to `Field`. However, this requires that all possible
5
- types in the union be known at the time the field is defined. This can be limiting
6
- in scenarios where new types may be defined later.
7
-
8
- To address this, we provide a `DiscriminatedUnionMixin` base class that models can
9
- inherit from to automatically register themselves as part of a discriminated union.
10
- We also provide a `DiscriminatedUnionType` type wrapper that can be used in Pydantic
11
- models to indicate that a field should be treated as a discriminated union of all
12
- subclasses of a given base class.
13
-
14
- Importantly, this approach allows us to _delay_ the resolution of the union types
15
- until validation time, meaning that new subclasses can be defined and registered
16
- at any time before validation occurs.
17
-
18
- Example usage:
19
-
20
- from typing import Annotated
21
- from pydantic import BaseModel
22
-
23
- from openhands.sdk.utils.discriminated_union import (
24
- DiscriminatedUnionMixin,
25
- DiscriminatedUnionType
26
- )
27
-
28
- # The base class for the union is tagged with DiscriminatedUnionMixin
29
- class Animal(BaseModel, DiscriminatedUnionMixin):
30
- name: str
31
-
32
- # We develop a special type to represent the discriminated union of all Animals.
33
- # This acts just like Animal, but the annotation tells Pydantic to treat it as a
34
- # discriminated union of all subclasses of Animal (defined so far or in the future).
35
- AnyAnimal = Annotated[Animal, DiscriminatedUnionType[Animal]]
36
-
37
- class Dog(Animal):
38
- breed: str
39
-
40
- class Cat(Animal):
41
- color: str
42
-
43
- class Zoo(BaseModel):
44
- residents: list[AnyAnimal]
45
-
46
- # Even animals defined after the Zoo class can be included without issue
47
- class Mouse(Animal):
48
- size: str
49
-
50
- zoo = Zoo(residents=[
51
- Dog(name="Fido", breed="Labrador"),
52
- Cat(name="Whiskers", color="Tabby"),
53
- Mouse(name="Jerry", size="Small")
54
- ])
55
-
56
- serialized_zoo = zoo.model_dump_json()
57
- deserialized_zoo = Zoo.model_validate_json(serialized_zoo)
58
-
59
- assert zoo == deserialized_zoo
60
- """
61
-
62
- from __future__ import annotations
63
-
64
- from typing import Any, Generic, TypeVar, cast
65
-
66
- from pydantic import BaseModel, computed_field
67
- from pydantic_core import core_schema
68
-
69
-
70
- T = TypeVar("T", bound="DiscriminatedUnionMixin")
71
-
72
-
73
- class DiscriminatedUnionMixin(BaseModel):
74
- """A Base class for members of tagged unions discriminated by the class name.
75
-
76
- This class provides automatic subclass registration and discriminated union
77
- functionality. Each subclass is automatically registered when defined and
78
- can be used for polymorphic serialization/deserialization.
79
-
80
- Child classes will automatically have a type field defined, which is used as a
81
- discriminator for union types.
82
- """
83
-
84
- @computed_field # type: ignore
85
- @property
86
- def kind(self) -> str:
87
- """Property to create kind field from class name when serializing."""
88
- return f"{self.__class__.__module__}.{self.__class__.__qualname__}"
89
-
90
- @classmethod
91
- def target_subclass(cls, kind: str) -> type[DiscriminatedUnionMixin] | None:
92
- """Get the subclass corresponding to a given kind name."""
93
- worklist = [cls]
94
- while worklist:
95
- current = worklist.pop()
96
- if f"{current.__module__}.{current.__qualname__}" == kind:
97
- return current
98
- worklist.extend(current.__subclasses__())
99
- return None
100
-
101
- @classmethod
102
- def model_validate(
103
- cls: type[T],
104
- obj: Any,
105
- *,
106
- strict=None,
107
- from_attributes=None,
108
- context=None,
109
- **kwargs,
110
- ) -> T:
111
- """Custom model validation using registered subclasses for deserialization."""
112
- # If we have a 'kind' field but no discriminated union handling,
113
- # we still need to remove it to avoid extra field errors
114
- if isinstance(obj, dict) and "kind" in obj:
115
- kind = obj.get("kind")
116
- assert isinstance(kind, str)
117
- obj_without_kind = {k: v for k, v in obj.items() if k != "kind"}
118
- if (target_class := cls.target_subclass(kind)) is not None:
119
- return cast(
120
- T,
121
- target_class.model_validate(
122
- obj_without_kind,
123
- strict=strict,
124
- from_attributes=from_attributes,
125
- context=context,
126
- **kwargs,
127
- ),
128
- )
129
-
130
- # Fallback to default validation
131
- return cast(
132
- T,
133
- super().model_validate(
134
- obj,
135
- strict=strict,
136
- from_attributes=from_attributes,
137
- context=context,
138
- **kwargs,
139
- ),
140
- )
141
-
142
- @classmethod
143
- def model_validate_json(
144
- cls: type[T],
145
- json_data: str | bytes | bytearray,
146
- *,
147
- strict=None,
148
- context=None,
149
- **kwargs,
150
- ) -> T:
151
- """Custom JSON validation that uses our custom model_validate method."""
152
- import json
153
-
154
- # Parse JSON to dict first
155
- if isinstance(json_data, bytes):
156
- json_data = json_data.decode("utf-8")
157
-
158
- obj = json.loads(json_data)
159
-
160
- # Use our custom model_validate method
161
- return cls.model_validate(
162
- obj,
163
- strict=strict,
164
- context=context,
165
- **kwargs,
166
- )
167
-
168
-
169
- class DiscriminatedUnionType(Generic[T]):
170
- """A type wrapper that enables discriminated union validation for Pydantic fields.
171
-
172
- The wrapped type must be a subclass of `DiscriminatedUnionMixin`.
173
- """
174
-
175
- def __init__(self, cls: type[T]):
176
- self.base_class = cls
177
- self.__origin__ = cls
178
- self.__args__ = ()
179
-
180
- def __get_pydantic_core_schema__(self, source_type, handler):
181
- """Define custom Pydantic core schema for this type.
182
-
183
- This schema uses a custom validator function to handle discriminated union
184
- deserialization based on the 'kind' field.
185
- """
186
-
187
- # Importantly, this validation function calls the base class model validation
188
- # _at validation time_, meaning that even if new subclasses are registered after
189
- # the fact they will still be recognized.
190
- def validate(v):
191
- if isinstance(v, self.base_class):
192
- return v
193
- if isinstance(v, dict) and "kind" in v:
194
- return self.base_class.model_validate(v)
195
- return self.base_class(**v)
196
-
197
- return core_schema.no_info_plain_validator_function(validate)
198
-
199
- def __repr__(self):
200
- return f"DiscriminatedUnion[{self.base_class.__name__}]"
201
-
202
- def __class_getitem__(cls, params):
203
- """Support type-style subscript syntax like DiscriminatedUnionType[cls]."""
204
- return cls(params)
205
-
206
- def __instancecheck__(self, instance):
207
- return isinstance(instance, self.base_class)
208
-
209
- def __subclasscheck__(self, subclass):
210
- return issubclass(subclass, self.base_class)
@@ -1,48 +0,0 @@
1
- import json
2
- from datetime import datetime
3
- from typing import Any
4
-
5
- from litellm.types.utils import ModelResponse
6
-
7
- from openhands.sdk.llm.exceptions import LLMResponseError
8
- from openhands.sdk.llm.utils.metrics import Metrics
9
-
10
-
11
- class OpenHandsJSONEncoder(json.JSONEncoder):
12
- """Custom JSON encoder that handles datetime and other OH objects"""
13
-
14
- def default(self, o: object) -> Any:
15
- if isinstance(o, datetime):
16
- return o.isoformat()
17
- if isinstance(o, Metrics):
18
- return o.get()
19
- if isinstance(o, ModelResponse):
20
- return o.model_dump()
21
- return super().default(o)
22
-
23
-
24
- # Create a single reusable encoder instance
25
- _json_encoder = OpenHandsJSONEncoder()
26
-
27
-
28
- def dumps(obj, **kwargs):
29
- """Serialize an object to str format"""
30
- if not kwargs:
31
- return _json_encoder.encode(obj)
32
-
33
- # Create a copy of the kwargs to avoid modifying the original
34
- encoder_kwargs = kwargs.copy()
35
-
36
- # If cls is specified, use it; otherwise use our custom encoder
37
- if "cls" not in encoder_kwargs:
38
- encoder_kwargs["cls"] = OpenHandsJSONEncoder
39
-
40
- return json.dumps(obj, **encoder_kwargs)
41
-
42
-
43
- def loads(json_str, **kwargs):
44
- """Create a JSON object from str"""
45
- try:
46
- return json.loads(json_str, **kwargs)
47
- except json.JSONDecodeError:
48
- raise LLMResponseError("No valid JSON object found in response.")
@@ -1,44 +0,0 @@
1
- """Utility functions for truncating text content."""
2
-
3
- # Default truncation limits
4
- DEFAULT_TEXT_CONTENT_LIMIT = 50_000
5
-
6
- # Default truncation notice
7
- DEFAULT_TRUNCATE_NOTICE = (
8
- "<response clipped><NOTE>Due to the max output limit, only part of the full "
9
- "response has been shown to you.</NOTE>"
10
- )
11
-
12
-
13
- def maybe_truncate(
14
- content: str,
15
- truncate_after: int | None = None,
16
- truncate_notice: str = DEFAULT_TRUNCATE_NOTICE,
17
- ) -> str:
18
- """
19
- Truncate the middle of content if it exceeds the specified length.
20
-
21
- Keeps the head and tail of the content to preserve context at both ends.
22
-
23
- Args:
24
- content: The text content to potentially truncate
25
- truncate_after: Maximum length before truncation. If None, no truncation occurs
26
- truncate_notice: Notice to insert in the middle when content is truncated
27
-
28
- Returns:
29
- Original content if under limit, or truncated content with head and tail
30
- preserved
31
- """
32
- if not truncate_after or len(content) <= truncate_after or truncate_after < 0:
33
- return content
34
-
35
- # Calculate how much space we have for actual content
36
- available_chars = truncate_after - len(truncate_notice)
37
- half = available_chars // 2
38
-
39
- # Give extra character to head if odd number
40
- head_chars = half + (available_chars % 2)
41
- tail_chars = half
42
-
43
- # Keep head and tail, insert notice in the middle
44
- return content[:head_chars] + truncate_notice + content[-tail_chars:]
@@ -1,44 +0,0 @@
1
- """Runtime tools package."""
2
-
3
- from openhands.tools.execute_bash import (
4
- BashExecutor,
5
- BashTool,
6
- ExecuteBashAction,
7
- ExecuteBashObservation,
8
- execute_bash_tool,
9
- )
10
- from openhands.tools.str_replace_editor import (
11
- FileEditorExecutor,
12
- FileEditorTool,
13
- StrReplaceEditorAction,
14
- StrReplaceEditorObservation,
15
- str_replace_editor_tool,
16
- )
17
- from openhands.tools.task_tracker import (
18
- TaskTrackerAction,
19
- TaskTrackerExecutor,
20
- TaskTrackerObservation,
21
- TaskTrackerTool,
22
- task_tracker_tool,
23
- )
24
-
25
-
26
- __all__ = [
27
- "execute_bash_tool",
28
- "ExecuteBashAction",
29
- "ExecuteBashObservation",
30
- "BashExecutor",
31
- "BashTool",
32
- "str_replace_editor_tool",
33
- "StrReplaceEditorAction",
34
- "StrReplaceEditorObservation",
35
- "FileEditorExecutor",
36
- "FileEditorTool",
37
- "task_tracker_tool",
38
- "TaskTrackerAction",
39
- "TaskTrackerObservation",
40
- "TaskTrackerExecutor",
41
- "TaskTrackerTool",
42
- ]
43
-
44
- __version__ = "1.0.0a0"
@@ -1,30 +0,0 @@
1
- # Core tool interface
2
- from openhands.tools.execute_bash.definition import (
3
- BashTool,
4
- ExecuteBashAction,
5
- ExecuteBashObservation,
6
- execute_bash_tool,
7
- )
8
- from openhands.tools.execute_bash.impl import BashExecutor
9
-
10
- # Terminal session architecture - import from sessions package
11
- from openhands.tools.execute_bash.terminal import (
12
- TerminalCommandStatus,
13
- TerminalSession,
14
- create_terminal_session,
15
- )
16
-
17
-
18
- __all__ = [
19
- # === Core Tool Interface ===
20
- "BashTool",
21
- "execute_bash_tool",
22
- "ExecuteBashAction",
23
- "ExecuteBashObservation",
24
- "BashExecutor",
25
- # === Terminal Session Architecture ===
26
- "TerminalSession",
27
- "TerminalCommandStatus",
28
- "TerminalSession",
29
- "create_terminal_session",
30
- ]
@@ -1,31 +0,0 @@
1
- import re
2
-
3
-
4
- CMD_OUTPUT_PS1_BEGIN = "\n###PS1JSON###\n"
5
- CMD_OUTPUT_PS1_END = "\n###PS1END###"
6
- CMD_OUTPUT_METADATA_PS1_REGEX = re.compile(
7
- f"^{CMD_OUTPUT_PS1_BEGIN.strip()}(.*?){CMD_OUTPUT_PS1_END.strip()}",
8
- re.DOTALL | re.MULTILINE,
9
- )
10
-
11
- # Default max size for command output content
12
- # to prevent too large observations from being saved in the stream
13
- # This matches the default max_message_chars in LLM class
14
- MAX_CMD_OUTPUT_SIZE: int = 30000
15
-
16
-
17
- # Common timeout message that can be used across different timeout scenarios
18
- TIMEOUT_MESSAGE_TEMPLATE = (
19
- "You may wait longer to see additional output by sending empty command '', "
20
- "send other commands to interact with the current process, send keys "
21
- '("C-c", "C-z", "C-d") '
22
- "to interrupt/kill the previous command before sending your new command, "
23
- "or use the timeout parameter in execute_bash for future commands."
24
- )
25
-
26
- # How long to wait with no new output before considering it a no-change timeout
27
- NO_CHANGE_TIMEOUT_SECONDS = 30
28
-
29
- # How often to poll for new output in seconds
30
- POLL_INTERVAL = 0.5
31
- HISTORY_LIMIT = 10_000