openhands 0.0.0__tar.gz

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 (98) hide show
  1. openhands-0.0.0/PKG-INFO +3 -0
  2. openhands-0.0.0/README.md +322 -0
  3. openhands-0.0.0/openhands/__init__.py +1 -0
  4. openhands-0.0.0/openhands/sdk/__init__.py +45 -0
  5. openhands-0.0.0/openhands/sdk/agent/__init__.py +8 -0
  6. openhands-0.0.0/openhands/sdk/agent/agent/__init__.py +6 -0
  7. openhands-0.0.0/openhands/sdk/agent/agent/agent.py +349 -0
  8. openhands-0.0.0/openhands/sdk/agent/base.py +103 -0
  9. openhands-0.0.0/openhands/sdk/context/__init__.py +28 -0
  10. openhands-0.0.0/openhands/sdk/context/agent_context.py +153 -0
  11. openhands-0.0.0/openhands/sdk/context/condenser/__init__.py +5 -0
  12. openhands-0.0.0/openhands/sdk/context/condenser/condenser.py +73 -0
  13. openhands-0.0.0/openhands/sdk/context/condenser/no_op_condenser.py +13 -0
  14. openhands-0.0.0/openhands/sdk/context/manager.py +5 -0
  15. openhands-0.0.0/openhands/sdk/context/microagents/__init__.py +26 -0
  16. openhands-0.0.0/openhands/sdk/context/microagents/exceptions.py +11 -0
  17. openhands-0.0.0/openhands/sdk/context/microagents/microagent.py +345 -0
  18. openhands-0.0.0/openhands/sdk/context/microagents/types.py +70 -0
  19. openhands-0.0.0/openhands/sdk/context/utils/__init__.py +8 -0
  20. openhands-0.0.0/openhands/sdk/context/utils/prompt.py +52 -0
  21. openhands-0.0.0/openhands/sdk/context/view.py +116 -0
  22. openhands-0.0.0/openhands/sdk/conversation/__init__.py +12 -0
  23. openhands-0.0.0/openhands/sdk/conversation/conversation.py +207 -0
  24. openhands-0.0.0/openhands/sdk/conversation/state.py +50 -0
  25. openhands-0.0.0/openhands/sdk/conversation/types.py +6 -0
  26. openhands-0.0.0/openhands/sdk/conversation/visualizer.py +300 -0
  27. openhands-0.0.0/openhands/sdk/event/__init__.py +27 -0
  28. openhands-0.0.0/openhands/sdk/event/base.py +148 -0
  29. openhands-0.0.0/openhands/sdk/event/condenser.py +49 -0
  30. openhands-0.0.0/openhands/sdk/event/llm_convertible.py +265 -0
  31. openhands-0.0.0/openhands/sdk/event/types.py +5 -0
  32. openhands-0.0.0/openhands/sdk/event/user_action.py +12 -0
  33. openhands-0.0.0/openhands/sdk/event/utils.py +30 -0
  34. openhands-0.0.0/openhands/sdk/llm/__init__.py +19 -0
  35. openhands-0.0.0/openhands/sdk/llm/exceptions.py +108 -0
  36. openhands-0.0.0/openhands/sdk/llm/llm.py +867 -0
  37. openhands-0.0.0/openhands/sdk/llm/llm_registry.py +116 -0
  38. openhands-0.0.0/openhands/sdk/llm/message.py +216 -0
  39. openhands-0.0.0/openhands/sdk/llm/metadata.py +34 -0
  40. openhands-0.0.0/openhands/sdk/llm/utils/fn_call_converter.py +1049 -0
  41. openhands-0.0.0/openhands/sdk/llm/utils/metrics.py +311 -0
  42. openhands-0.0.0/openhands/sdk/llm/utils/model_features.py +153 -0
  43. openhands-0.0.0/openhands/sdk/llm/utils/retry_mixin.py +122 -0
  44. openhands-0.0.0/openhands/sdk/llm/utils/telemetry.py +252 -0
  45. openhands-0.0.0/openhands/sdk/logger.py +167 -0
  46. openhands-0.0.0/openhands/sdk/mcp/__init__.py +20 -0
  47. openhands-0.0.0/openhands/sdk/mcp/client.py +113 -0
  48. openhands-0.0.0/openhands/sdk/mcp/definition.py +69 -0
  49. openhands-0.0.0/openhands/sdk/mcp/tool.py +104 -0
  50. openhands-0.0.0/openhands/sdk/mcp/utils.py +59 -0
  51. openhands-0.0.0/openhands/sdk/tests/llm/test_llm.py +447 -0
  52. openhands-0.0.0/openhands/sdk/tests/llm/test_llm_fncall_converter.py +691 -0
  53. openhands-0.0.0/openhands/sdk/tests/llm/test_model_features.py +221 -0
  54. openhands-0.0.0/openhands/sdk/tool/__init__.py +30 -0
  55. openhands-0.0.0/openhands/sdk/tool/builtins/__init__.py +34 -0
  56. openhands-0.0.0/openhands/sdk/tool/builtins/finish.py +57 -0
  57. openhands-0.0.0/openhands/sdk/tool/builtins/think.py +60 -0
  58. openhands-0.0.0/openhands/sdk/tool/schema.py +236 -0
  59. openhands-0.0.0/openhands/sdk/tool/security_prompt.py +5 -0
  60. openhands-0.0.0/openhands/sdk/tool/tool.py +142 -0
  61. openhands-0.0.0/openhands/sdk/utils/__init__.py +14 -0
  62. openhands-0.0.0/openhands/sdk/utils/discriminated_union.py +210 -0
  63. openhands-0.0.0/openhands/sdk/utils/json.py +48 -0
  64. openhands-0.0.0/openhands/sdk/utils/truncate.py +44 -0
  65. openhands-0.0.0/openhands/tools/__init__.py +44 -0
  66. openhands-0.0.0/openhands/tools/execute_bash/__init__.py +30 -0
  67. openhands-0.0.0/openhands/tools/execute_bash/constants.py +31 -0
  68. openhands-0.0.0/openhands/tools/execute_bash/definition.py +166 -0
  69. openhands-0.0.0/openhands/tools/execute_bash/impl.py +38 -0
  70. openhands-0.0.0/openhands/tools/execute_bash/metadata.py +101 -0
  71. openhands-0.0.0/openhands/tools/execute_bash/terminal/__init__.py +22 -0
  72. openhands-0.0.0/openhands/tools/execute_bash/terminal/factory.py +113 -0
  73. openhands-0.0.0/openhands/tools/execute_bash/terminal/interface.py +189 -0
  74. openhands-0.0.0/openhands/tools/execute_bash/terminal/subprocess_terminal.py +412 -0
  75. openhands-0.0.0/openhands/tools/execute_bash/terminal/terminal_session.py +492 -0
  76. openhands-0.0.0/openhands/tools/execute_bash/terminal/tmux_terminal.py +160 -0
  77. openhands-0.0.0/openhands/tools/execute_bash/utils/command.py +150 -0
  78. openhands-0.0.0/openhands/tools/str_replace_editor/__init__.py +17 -0
  79. openhands-0.0.0/openhands/tools/str_replace_editor/definition.py +158 -0
  80. openhands-0.0.0/openhands/tools/str_replace_editor/editor.py +683 -0
  81. openhands-0.0.0/openhands/tools/str_replace_editor/exceptions.py +41 -0
  82. openhands-0.0.0/openhands/tools/str_replace_editor/impl.py +66 -0
  83. openhands-0.0.0/openhands/tools/str_replace_editor/utils/__init__.py +0 -0
  84. openhands-0.0.0/openhands/tools/str_replace_editor/utils/config.py +2 -0
  85. openhands-0.0.0/openhands/tools/str_replace_editor/utils/constants.py +9 -0
  86. openhands-0.0.0/openhands/tools/str_replace_editor/utils/encoding.py +135 -0
  87. openhands-0.0.0/openhands/tools/str_replace_editor/utils/file_cache.py +154 -0
  88. openhands-0.0.0/openhands/tools/str_replace_editor/utils/history.py +122 -0
  89. openhands-0.0.0/openhands/tools/str_replace_editor/utils/shell.py +72 -0
  90. openhands-0.0.0/openhands/tools/task_tracker/__init__.py +16 -0
  91. openhands-0.0.0/openhands/tools/task_tracker/definition.py +336 -0
  92. openhands-0.0.0/openhands/tools/utils/__init__.py +1 -0
  93. openhands-0.0.0/openhands.egg-info/PKG-INFO +3 -0
  94. openhands-0.0.0/openhands.egg-info/SOURCES.txt +96 -0
  95. openhands-0.0.0/openhands.egg-info/dependency_links.txt +1 -0
  96. openhands-0.0.0/openhands.egg-info/top_level.txt +1 -0
  97. openhands-0.0.0/pyproject.toml +55 -0
  98. openhands-0.0.0/setup.cfg +4 -0
@@ -0,0 +1,3 @@
1
+ Metadata-Version: 2.4
2
+ Name: openhands
3
+ Version: 0.0.0
@@ -0,0 +1,322 @@
1
+ # OpenHands Agent SDK
2
+
3
+ A clean, modular SDK for building AI agents with OpenHands. This project represents a complete architectural refactor from OpenHands V0, emphasizing simplicity, maintainability, and developer experience.
4
+
5
+ ## Project Overview
6
+
7
+ The OpenHands Agent SDK provides a streamlined framework for creating AI agents that can interact with tools, manage conversations, and integrate with various LLM providers.
8
+
9
+ ## Repository Structure
10
+
11
+ ```plain
12
+ agent-sdk/
13
+ ├── Makefile # Build and development commands
14
+ ├── pyproject.toml # Workspace configuration
15
+ ├── uv.lock # Dependency lock file
16
+ ├── examples/ # Usage examples
17
+ │ ├── 1_hello_world.py # Basic agent setup
18
+ │ ├── 2_custom_tools.py # Custom tool implementation
19
+ │ ├── 3_activate_microagent.py # Microagent usage
20
+ │ ├── 4_confirmation_mode_example.py # Interactive mode
21
+ │ ├── 5_use_llm_registry.py # LLM registry usage
22
+ │ └── 6_interactive_terminal.py # Terminal interaction
23
+ ├── openhands/ # Main SDK packages
24
+ │ ├── sdk/ # Core SDK functionality
25
+ │ │ ├── agent/ # Agent implementations
26
+ │ │ ├── config/ # Configuration management
27
+ │ │ ├── context/ # Context management system
28
+ │ │ ├── conversation/ # Conversation management
29
+ │ │ ├── event/ # Event system
30
+ │ │ ├── llm/ # LLM integration layer
31
+ │ │ ├── tool/ # Tool system
32
+ │ │ ├── utils/ # Core utilities
33
+ │ │ ├── logger.py # Logging configuration
34
+ │ │ └── pyproject.toml # SDK package configuration
35
+ │ └── tools/ # Runtime tool implementations
36
+ │ ├── execute_bash/ # Bash execution tool
37
+ │ ├── str_replace_editor/ # File editing tool
38
+ │ ├── utils/ # Tool utilities
39
+ │ └── pyproject.toml # Tools package configuration
40
+ └── tests/ # Test suites
41
+ ├── integration/ # Cross-package integration tests
42
+ ├── sdk/ # SDK unit tests
43
+ └── tools/ # Tools unit tests
44
+ ```
45
+
46
+ ## Installation & Quickstart
47
+
48
+ ### Prerequisites
49
+
50
+ - Python 3.12+
51
+ - `uv` package manager (version 0.8.13+)
52
+
53
+ ### Setup
54
+
55
+ ```bash
56
+ # Clone the repository
57
+ git clone https://github.com/All-Hands-AI/agent-sdk.git
58
+ cd agent-sdk
59
+
60
+ # Install dependencies and setup development environment
61
+ make build
62
+
63
+ # Verify installation
64
+ uv run python examples/1_hello_world.py
65
+ ```
66
+
67
+ ### Hello World Example
68
+
69
+ ```python
70
+ import os
71
+ from pydantic import SecretStr
72
+ from openhands.sdk import LLM, Agent, Conversation, Message, TextContent
73
+ from openhands.tools import BashTool, FileEditorTool
74
+
75
+ # Configure LLM
76
+ llm = LLM(
77
+ model="litellm_proxy/anthropic/claude-sonnet-4-20250514",
78
+ base_url="https://llm-proxy.eval.all-hands.dev",
79
+ api_key=SecretStr(os.getenv("LITELLM_API_KEY")),
80
+ )
81
+
82
+ # Setup tools
83
+ tools = [
84
+ BashTool(working_dir=os.getcwd()),
85
+ FileEditorTool(),
86
+ ]
87
+
88
+ # Create agent and conversation
89
+ agent = Agent(llm=llm, tools=tools)
90
+ conversation = Conversation(agent=agent)
91
+
92
+ # Send message and run
93
+ conversation.send_message(
94
+ Message(
95
+ role="user",
96
+ content=[TextContent(text="Create a Python file that prints 'Hello, World!'")]
97
+ )
98
+ )
99
+ conversation.run()
100
+ ```
101
+
102
+ ## Core Concepts
103
+
104
+ ### Agents
105
+
106
+ Agents are the central orchestrators that coordinate between LLMs and tools:
107
+
108
+ ```python
109
+ from openhands.sdk import Agent, LLM
110
+ from openhands.tools import BashTool, FileEditorTool
111
+
112
+ agent = Agent(
113
+ llm=llm,
114
+ tools=[BashTool(), FileEditorTool()],
115
+ # Optional: custom context, microagents, etc.
116
+ )
117
+ ```
118
+
119
+ ### LLM Integration
120
+
121
+ The SDK supports multiple LLM providers through a unified interface:
122
+
123
+ ```python
124
+ from openhands.sdk import LLM, LLMRegistry
125
+ from pydantic import SecretStr
126
+
127
+ # Direct LLM configuration
128
+ llm = LLM(
129
+ model="gpt-4",
130
+ api_key=SecretStr("your-api-key"),
131
+ base_url="https://api.openai.com/v1"
132
+ )
133
+
134
+ # Using LLM registry for shared configurations
135
+ registry = LLMRegistry()
136
+ llm = registry.get_llm("default")
137
+ ```
138
+
139
+ ### Tools
140
+
141
+ Tools provide agents with capabilities to interact with the environment:
142
+
143
+ #### Simplified Pattern (Recommended)
144
+
145
+ ```python
146
+ from openhands.sdk import TextContent, ImageContent
147
+ from openhands.tools import BashTool, FileEditorTool
148
+
149
+ # Direct instantiation with simplified API
150
+ tools = [
151
+ BashTool(working_dir=os.getcwd()),
152
+ FileEditorTool(),
153
+ ]
154
+ ```
155
+
156
+ #### Advanced Pattern (For explicitly maintained tool executor)
157
+
158
+ We explicitly define a `BashExecutor` in this example:
159
+
160
+ ```python
161
+ from openhands.tools import BashExecutor, execute_bash_tool
162
+
163
+ # Explicit executor creation for reuse or customization
164
+ bash_executor = BashExecutor(working_dir=os.getcwd())
165
+ bash_tool = execute_bash_tool.set_executor(executor=bash_executor)
166
+ ```
167
+
168
+ And we can later re-use this bash terminal instance to define a custom tool:
169
+
170
+ ```python
171
+ from openhands.sdk.tool import ActionBase, ObservationBase, ToolExecutor
172
+
173
+ class GrepAction(ActionBase):
174
+ pattern: str = Field(description="Regex to search for")
175
+ path: str = Field(
176
+ default=".", description="Directory to search (absolute or relative)"
177
+ )
178
+ include: str | None = Field(
179
+ default=None, description="Optional glob to filter files (e.g. '*.py')"
180
+ )
181
+
182
+
183
+ class GrepObservation(ObservationBase):
184
+ output: str = Field(default='')
185
+
186
+ @property
187
+ def agent_observation(self) -> list[TextContent | ImageContent]:
188
+ return TextContent(text=self.output)
189
+
190
+ # --- Executor ---
191
+ class GrepExecutor(ToolExecutor[GrepAction, GrepObservation]):
192
+ def __init__(self, bash: BashExecutor):
193
+ self.bash = bash
194
+
195
+ def __call__(self, action: GrepAction) -> GrepObservation:
196
+ root = os.path.abspath(action.path)
197
+ pat = shlex.quote(action.pattern)
198
+ root_q = shlex.quote(root)
199
+
200
+ # Use grep -r; add --include when provided
201
+ if action.include:
202
+ inc = shlex.quote(action.include)
203
+ cmd = f"grep -rHnE --include {inc} {pat} {root_q} 2>/dev/null | head -100"
204
+ else:
205
+ cmd = f"grep -rHnE {pat} {root_q} 2>/dev/null | head -100"
206
+
207
+ result = self.bash(ExecuteBashAction(command=cmd, security_risk="LOW"))
208
+ return GrepObservation(output=result.output.strip() or '')
209
+ ```
210
+
211
+ ### Conversations
212
+
213
+ Conversations manage the interaction flow between users and agents:
214
+
215
+ ```python
216
+ from openhands.sdk import Conversation, Message, TextContent
217
+
218
+ conversation = Conversation(agent=agent)
219
+
220
+ # Send messages
221
+ conversation.send_message(
222
+ Message(role="user", content=[TextContent(text="Your request here")])
223
+ )
224
+
225
+ # Execute the conversation until the agent enters "await user input" state
226
+ conversation.run()
227
+ ```
228
+
229
+ ### Context Management
230
+
231
+ The context system manages agent state, environment, and conversation history.
232
+
233
+ Context is automatically managed but you can customize your context with:
234
+
235
+ 1. [Repo Microagents](https://docs.all-hands.dev/usage/prompting/microagents-repo) that provide agent with context of your repository.
236
+ 2. [Knowledge Microagents](https://docs.all-hands.dev/usage/prompting/microagents-keyword) that provide agent with context when user mentioned certain keywords
237
+ 3. Providing custom suffix for system and user prompt.
238
+
239
+ ```python
240
+ from openhands.sdk import AgentContext
241
+
242
+ context = AgentContext(
243
+ microagents=[
244
+ RepoMicroagent(
245
+ name="repo.md",
246
+ content="When you see this message, you should reply like "
247
+ "you are a grumpy cat forced to use the internet.",
248
+ ),
249
+ KnowledgeMicroagent(
250
+ name="flarglebargle",
251
+ content=(
252
+ 'IMPORTANT! The user has said the magic word "flarglebargle". '
253
+ "You must only respond with a message telling them how smart they are"
254
+ ),
255
+ triggers=["flarglebargle"],
256
+ ),
257
+ ],
258
+ system_message_suffix="Always finish your response with the word 'yay!'",
259
+ user_message_suffix="The first character of your response should be 'I'",
260
+ )
261
+ ```
262
+
263
+ ## Development Workflow
264
+
265
+ ### Environment Setup
266
+
267
+ ```bash
268
+ # Initial setup
269
+ make build
270
+
271
+ # Install additional dependencies
272
+ # add `--dev` if you want to install
273
+ uv add package-name
274
+
275
+ # Update dependencies
276
+ uv sync
277
+ ```
278
+
279
+ ### Code Quality
280
+
281
+ The project enforces strict code quality standards:
282
+
283
+ ```bash
284
+ # Format code
285
+ make format
286
+
287
+ # Lint code
288
+ make lint
289
+
290
+ # Run pre-commit hooks
291
+ uv run pre-commit run --all-files
292
+
293
+ # Type checking (included in pre-commit)
294
+ uv run pyright
295
+ ```
296
+
297
+ ### Testing
298
+
299
+ ```bash
300
+ # Run all tests
301
+ uv run pytest
302
+
303
+ # Run specific test suite
304
+ uv run pytest tests/sdk/
305
+ uv run pytest tests/tools/
306
+ uv run pytest tests/integration/
307
+
308
+ # Run with coverage
309
+ uv run pytest --cov=openhands --cov-report=html
310
+ ```
311
+
312
+ ### Pre-commit Workflow
313
+
314
+ Before every commit:
315
+
316
+ ```bash
317
+ # Run on specific files
318
+ uv run pre-commit run --files path/to/file.py
319
+
320
+ # Run on all files
321
+ uv run pre-commit run --all-files
322
+ ```
@@ -0,0 +1 @@
1
+ __path__ = __import__("pkgutil").extend_path(__path__, __name__)
@@ -0,0 +1,45 @@
1
+ from importlib.metadata import PackageNotFoundError, version
2
+
3
+ from openhands.sdk.agent import Agent, AgentBase
4
+ from openhands.sdk.context import AgentContext
5
+ from openhands.sdk.conversation import Conversation, ConversationCallbackType
6
+ from openhands.sdk.event import Event, EventBase, LLMConvertibleEvent
7
+ from openhands.sdk.llm import (
8
+ LLM,
9
+ ImageContent,
10
+ LLMRegistry,
11
+ Message,
12
+ RegistryEvent,
13
+ TextContent,
14
+ )
15
+ from openhands.sdk.logger import get_logger
16
+ from openhands.sdk.mcp import MCPClient, MCPTool, create_mcp_tools
17
+ from openhands.sdk.tool import ActionBase, ObservationBase, Tool
18
+
19
+
20
+ __version__ = "1.0.0a0"
21
+
22
+ __all__ = [
23
+ "LLM",
24
+ "LLMRegistry",
25
+ "RegistryEvent",
26
+ "Message",
27
+ "TextContent",
28
+ "ImageContent",
29
+ "Tool",
30
+ "AgentBase",
31
+ "Agent",
32
+ "ActionBase",
33
+ "ObservationBase",
34
+ "MCPClient",
35
+ "MCPTool",
36
+ "create_mcp_tools",
37
+ "get_logger",
38
+ "Conversation",
39
+ "ConversationCallbackType",
40
+ "Event",
41
+ "EventBase",
42
+ "LLMConvertibleEvent",
43
+ "AgentContext",
44
+ "__version__",
45
+ ]
@@ -0,0 +1,8 @@
1
+ from openhands.sdk.agent.agent import Agent
2
+ from openhands.sdk.agent.base import AgentBase
3
+
4
+
5
+ __all__ = [
6
+ "Agent",
7
+ "AgentBase",
8
+ ]
@@ -0,0 +1,6 @@
1
+ from .agent import Agent
2
+
3
+
4
+ __all__ = [
5
+ "Agent",
6
+ ]