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,150 +0,0 @@
1
- import re
2
- import traceback
3
- from typing import Any
4
-
5
- import bashlex
6
- from bashlex.errors import ParsingError
7
-
8
- from openhands.sdk.logger import get_logger
9
-
10
-
11
- logger = get_logger(__name__)
12
-
13
-
14
- def split_bash_commands(commands: str) -> list[str]:
15
- if not commands.strip():
16
- return [""]
17
- try:
18
- parsed = bashlex.parse(commands)
19
- except (
20
- ParsingError,
21
- NotImplementedError,
22
- TypeError,
23
- AttributeError,
24
- ):
25
- # Added AttributeError to catch 'str' object has no attribute 'kind' error
26
- # (issue #8369)
27
- logger.debug(
28
- f"Failed to parse bash commands\n[input]: {commands}\n[warning]: "
29
- f"{traceback.format_exc()}\nThe original command will be returned as is."
30
- )
31
- # If parsing fails, return the original commands
32
- return [commands]
33
-
34
- result: list[str] = []
35
- last_end = 0
36
-
37
- for node in parsed:
38
- start, end = node.pos
39
-
40
- # Include any text between the last command and this one
41
- if start > last_end:
42
- between = commands[last_end:start]
43
- logger.debug(f"BASH PARSING between: {between}")
44
- if result:
45
- result[-1] += between.rstrip()
46
- elif between.strip():
47
- # THIS SHOULD NOT HAPPEN
48
- result.append(between.rstrip())
49
-
50
- # Extract the command, preserving original formatting
51
- command = commands[start:end].rstrip()
52
- logger.debug(f"BASH PARSING command: {command}")
53
- result.append(command)
54
-
55
- last_end = end
56
-
57
- # Add any remaining text after the last command to the last command
58
- remaining = commands[last_end:].rstrip()
59
- logger.debug(f"BASH PARSING remaining: {remaining}")
60
- if last_end < len(commands) and result:
61
- result[-1] += remaining
62
- logger.debug(f"BASH PARSING result[-1] += remaining: {result[-1]}")
63
- elif last_end < len(commands):
64
- if remaining:
65
- result.append(remaining)
66
- logger.debug(f"BASH PARSING result.append(remaining): {result[-1]}")
67
- return result
68
-
69
-
70
- def escape_bash_special_chars(command: str) -> str:
71
- r"""Escapes characters that have different interpretations in bash vs python.
72
- Specifically handles escape sequences like \;, \|, \&, etc.
73
- """
74
- if command.strip() == "":
75
- return ""
76
-
77
- try:
78
- parts = []
79
- last_pos = 0
80
-
81
- def visit_node(node: Any) -> None:
82
- nonlocal last_pos
83
- if (
84
- node.kind == "redirect"
85
- and hasattr(node, "heredoc")
86
- and node.heredoc is not None
87
- ):
88
- # We're entering a heredoc - preserve everything as-is until we see EOF
89
- # Store the heredoc end marker (usually 'EOF' but could be different)
90
- between = command[last_pos : node.pos[0]]
91
- parts.append(between)
92
- # Add the heredoc start marker
93
- parts.append(command[node.pos[0] : node.heredoc.pos[0]])
94
- # Add the heredoc content as-is
95
- parts.append(command[node.heredoc.pos[0] : node.heredoc.pos[1]])
96
- last_pos = node.pos[1]
97
- return
98
-
99
- if node.kind == "word":
100
- # Get the raw text between the last position and current word
101
- between = command[last_pos : node.pos[0]]
102
- word_text = command[node.pos[0] : node.pos[1]]
103
-
104
- # Add the between text, escaping special characters
105
- between = re.sub(r"\\([;&|><])", r"\\\\\1", between)
106
- parts.append(between)
107
-
108
- # Check if word_text is a quoted string or command substitution
109
- if (
110
- (word_text.startswith('"') and word_text.endswith('"'))
111
- or (word_text.startswith("'") and word_text.endswith("'"))
112
- or (word_text.startswith("$(") and word_text.endswith(")"))
113
- or (word_text.startswith("`") and word_text.endswith("`"))
114
- ):
115
- # Preserve quoted strings, command substitutions, and heredoc
116
- # content as-is
117
- parts.append(word_text)
118
- else:
119
- # Escape special chars in unquoted text
120
- word_text = re.sub(r"\\([;&|><])", r"\\\\\1", word_text)
121
- parts.append(word_text)
122
-
123
- last_pos = node.pos[1]
124
- return
125
-
126
- # Visit child nodes
127
- if hasattr(node, "parts"):
128
- for part in node.parts:
129
- visit_node(part)
130
-
131
- # Process all nodes in the AST
132
- nodes = list(bashlex.parse(command))
133
- for node in nodes:
134
- between = command[last_pos : node.pos[0]]
135
- between = re.sub(r"\\([;&|><])", r"\\\\\1", between)
136
- parts.append(between)
137
- last_pos = node.pos[0]
138
- visit_node(node)
139
-
140
- # Handle any remaining text after the last word
141
- remaining = command[last_pos:]
142
- parts.append(remaining)
143
- return "".join(parts)
144
- except (ParsingError, NotImplementedError, TypeError):
145
- logger.debug(
146
- f"Failed to parse bash commands for special characters escape\n[input]: "
147
- f"{command}\n[warning]: {traceback.format_exc()}\nThe original command "
148
- f"will be returned as is."
149
- )
150
- return command
@@ -1,17 +0,0 @@
1
- from openhands.tools.str_replace_editor.definition import (
2
- FileEditorTool,
3
- StrReplaceEditorAction,
4
- StrReplaceEditorObservation,
5
- str_replace_editor_tool,
6
- )
7
- from openhands.tools.str_replace_editor.impl import FileEditorExecutor, file_editor
8
-
9
-
10
- __all__ = [
11
- "str_replace_editor_tool",
12
- "StrReplaceEditorAction",
13
- "StrReplaceEditorObservation",
14
- "file_editor",
15
- "FileEditorExecutor",
16
- "FileEditorTool",
17
- ]
@@ -1,158 +0,0 @@
1
- """String replace editor tool implementation."""
2
-
3
- from typing import Literal
4
-
5
- from pydantic import Field
6
-
7
- from openhands.sdk.llm import ImageContent, TextContent
8
- from openhands.sdk.tool import ActionBase, ObservationBase, Tool, ToolAnnotations
9
-
10
-
11
- CommandLiteral = Literal["view", "create", "str_replace", "insert", "undo_edit"]
12
-
13
-
14
- class StrReplaceEditorAction(ActionBase):
15
- """Schema for string replace editor operations."""
16
-
17
- command: CommandLiteral = Field(
18
- description="The commands to run. Allowed options are: `view`, `create`, "
19
- "`str_replace`, `insert`, `undo_edit`."
20
- )
21
- path: str = Field(
22
- description="Absolute path to file or directory, e.g. `/workspace/file.py` "
23
- "or `/workspace`."
24
- )
25
- file_text: str | None = Field(
26
- default=None,
27
- description="Required parameter of `create` command, with the content of "
28
- "the file to be created.",
29
- )
30
- old_str: str | None = Field(
31
- default=None,
32
- description="Required parameter of `str_replace` command containing the "
33
- "string in `path` to replace.",
34
- )
35
- new_str: str | None = Field(
36
- default=None,
37
- description="Optional parameter of `str_replace` command containing the "
38
- "new string (if not given, no string will be added). Required parameter "
39
- "of `insert` command containing the string to insert.",
40
- )
41
- insert_line: int | None = Field(
42
- default=None,
43
- description="Required parameter of `insert` command. The `new_str` will "
44
- "be inserted AFTER the line `insert_line` of `path`.",
45
- )
46
- view_range: list[int] | None = Field(
47
- default=None,
48
- description="Optional parameter of `view` command when `path` points to a "
49
- "file. If none is given, the full file is shown. If provided, the file "
50
- "will be shown in the indicated line number range, e.g. [11, 12] will "
51
- "show lines 11 and 12. Indexing at 1 to start. Setting `[start_line, "
52
- "-1]` shows all lines from `start_line` to the end of the file.",
53
- )
54
-
55
-
56
- class StrReplaceEditorObservation(ObservationBase):
57
- """A ToolResult that can be rendered as a CLI output."""
58
-
59
- output: str = Field(
60
- default="", description="The output message from the tool for the LLM to see."
61
- )
62
- path: str | None = Field(default=None, description="The file path that was edited.")
63
- prev_exist: bool = Field(
64
- default=True,
65
- description="Indicates if the file previously existed. If not, it was created.",
66
- )
67
- old_content: str | None = Field(
68
- default=None, description="The content of the file before the edit."
69
- )
70
- new_content: str | None = Field(
71
- default=None, description="The content of the file after the edit."
72
- )
73
- error: str | None = Field(default=None, description="Error message if any.")
74
-
75
- @property
76
- def agent_observation(self) -> list[TextContent | ImageContent]:
77
- if self.error:
78
- return [TextContent(text=self.error)]
79
- return [TextContent(text=self.output)]
80
-
81
-
82
- Command = Literal[
83
- "view",
84
- "create",
85
- "str_replace",
86
- "insert",
87
- "undo_edit",
88
- ]
89
-
90
-
91
- TOOL_DESCRIPTION = """Custom editing tool for viewing, creating and editing files in plain-text format
92
- * State is persistent across command calls and discussions with the user
93
- * If `path` is a text file, `view` displays the result of applying `cat -n`. If `path` is a directory, `view` lists non-hidden files and directories up to 2 levels deep
94
- * The following binary file extensions can be viewed in Markdown format: [".xlsx", ".pptx", ".wav", ".mp3", ".m4a", ".flac", ".pdf", ".docx"]. IT DOES NOT HANDLE IMAGES.
95
- * The `create` command cannot be used if the specified `path` already exists as a file
96
- * If a `command` generates a long output, it will be truncated and marked with `<response clipped>`
97
- * The `undo_edit` command will revert the last edit made to the file at `path`
98
- * This tool can be used for creating and editing files in plain-text format.
99
-
100
-
101
- Before using this tool:
102
- 1. Use the view tool to understand the file's contents and context
103
- 2. Verify the directory path is correct (only applicable when creating new files):
104
- - Use the view tool to verify the parent directory exists and is the correct location
105
-
106
- When making edits:
107
- - Ensure the edit results in idiomatic, correct code
108
- - Do not leave the code in a broken state
109
- - Always use absolute file paths (starting with /)
110
-
111
- CRITICAL REQUIREMENTS FOR USING THIS TOOL:
112
-
113
- 1. EXACT MATCHING: The `old_str` parameter must match EXACTLY one or more consecutive lines from the file, including all whitespace and indentation. The tool will fail if `old_str` matches multiple locations or doesn't match exactly with the file content.
114
-
115
- 2. UNIQUENESS: The `old_str` must uniquely identify a single instance in the file:
116
- - Include sufficient context before and after the change point (3-5 lines recommended)
117
- - If not unique, the replacement will not be performed
118
-
119
- 3. REPLACEMENT: The `new_str` parameter should contain the edited lines that replace the `old_str`. Both strings must be different.
120
-
121
- Remember: when making multiple file edits in a row to the same file, you should prefer to send all edits in a single message with multiple calls to this tool, rather than multiple messages with a single call each.
122
- """ # noqa: E501
123
-
124
-
125
- str_replace_editor_tool = Tool(
126
- name="str_replace_editor",
127
- input_schema=StrReplaceEditorAction,
128
- description=TOOL_DESCRIPTION,
129
- annotations=ToolAnnotations(
130
- title="str_replace_editor",
131
- readOnlyHint=False,
132
- destructiveHint=True,
133
- idempotentHint=False,
134
- openWorldHint=False,
135
- ),
136
- )
137
-
138
-
139
- class FileEditorTool(Tool[StrReplaceEditorAction, StrReplaceEditorObservation]):
140
- """A Tool subclass that automatically initializes a FileEditorExecutor."""
141
-
142
- def __init__(self):
143
- """Initialize FileEditorTool with a FileEditorExecutor."""
144
- # Import here to avoid circular imports
145
- from openhands.tools.str_replace_editor.impl import FileEditorExecutor
146
-
147
- # Initialize the executor
148
- executor = FileEditorExecutor()
149
-
150
- # Initialize the parent Tool with the executor
151
- super().__init__(
152
- name=str_replace_editor_tool.name,
153
- description=TOOL_DESCRIPTION,
154
- input_schema=StrReplaceEditorAction,
155
- output_schema=StrReplaceEditorObservation,
156
- annotations=str_replace_editor_tool.annotations,
157
- executor=executor,
158
- )