Skip to content

Commit a9dc170

Browse files
authored
Merge branch 'main' into mh/rel0260
2 parents 9059386 + aa15c9d commit a9dc170

26 files changed

+699
-126
lines changed

.github/workflows/py-unit-tests.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
- name: Build Environment
4949
run: make build
5050
- name: Run Tests
51-
run: poetry run pytest --forked -n auto --cov=openhands --cov-report=xml -svv ./tests/unit --ignore=tests/unit/test_memory.py
51+
run: poetry run pytest --forked -n auto --cov=openhands --cov-report=xml -svv ./tests/unit --ignore=tests/unit/test_long_term_memory.py
5252
- name: Upload coverage to Codecov
5353
uses: codecov/codecov-action@v5
5454
env:

config.template.toml

+6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
#modal_api_token_id = ""
1818
#modal_api_token_secret = ""
1919

20+
# API key for Daytona
21+
#daytona_api_key = ""
22+
23+
# Daytona Target
24+
#daytona_target = ""
25+
2026
# Base path for the workspace
2127
workspace_base = "./workspace"
2228

openhands/core/config/app_config.py

+3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ class AppConfig(BaseModel):
7575
file_uploads_restrict_file_types: bool = Field(default=False)
7676
file_uploads_allowed_extensions: list[str] = Field(default_factory=lambda: ['.*'])
7777
runloop_api_key: SecretStr | None = Field(default=None)
78+
daytona_api_key: SecretStr | None = Field(default=None)
79+
daytona_api_url: str = Field(default='https://app.daytona.io/api')
80+
daytona_target: str = Field(default='us')
7881
cli_multiline_input: bool = Field(default=False)
7982
conversation_max_age_seconds: int = Field(default=864000) # 10 days in seconds
8083

openhands/core/config/config_utils.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,20 @@ def get_field_info(field: FieldInfo) -> dict[str, Any]:
2525
# Note: this only works for UnionTypes with None as one of the types
2626
if get_origin(field_type) is UnionType:
2727
types = get_args(field_type)
28-
non_none_arg = next((t for t in types if t is not type(None)), None)
28+
non_none_arg = next(
29+
(t for t in types if t is not None and t is not type(None)), None
30+
)
2931
if non_none_arg is not None:
3032
field_type = non_none_arg
3133
optional = True
3234

3335
# type name in a pretty format
3436
type_name = (
35-
field_type.__name__ if hasattr(field_type, '__name__') else str(field_type)
37+
str(field_type)
38+
if field_type is None
39+
else (
40+
field_type.__name__ if hasattr(field_type, '__name__') else str(field_type)
41+
)
3642
)
3743

3844
# default is always present

openhands/core/exceptions.py

+25-21
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ class AgentError(Exception):
1010

1111

1212
class AgentNoInstructionError(AgentError):
13-
def __init__(self, message='Instruction must be provided'):
13+
def __init__(self, message: str = 'Instruction must be provided') -> None:
1414
super().__init__(message)
1515

1616

1717
class AgentEventTypeError(AgentError):
18-
def __init__(self, message='Event must be a dictionary'):
18+
def __init__(self, message: str = 'Event must be a dictionary') -> None:
1919
super().__init__(message)
2020

2121

2222
class AgentAlreadyRegisteredError(AgentError):
23-
def __init__(self, name=None):
23+
def __init__(self, name: str | None = None) -> None:
2424
if name is not None:
2525
message = f"Agent class already registered under '{name}'"
2626
else:
@@ -29,7 +29,7 @@ def __init__(self, name=None):
2929

3030

3131
class AgentNotRegisteredError(AgentError):
32-
def __init__(self, name=None):
32+
def __init__(self, name: str | None = None) -> None:
3333
if name is not None:
3434
message = f"No agent class registered under '{name}'"
3535
else:
@@ -38,7 +38,7 @@ def __init__(self, name=None):
3838

3939

4040
class AgentStuckInLoopError(AgentError):
41-
def __init__(self, message='Agent got stuck in a loop'):
41+
def __init__(self, message: str = 'Agent got stuck in a loop') -> None:
4242
super().__init__(message)
4343

4444

@@ -48,7 +48,7 @@ def __init__(self, message='Agent got stuck in a loop'):
4848

4949

5050
class TaskInvalidStateError(Exception):
51-
def __init__(self, state=None):
51+
def __init__(self, state: str | None = None) -> None:
5252
if state is not None:
5353
message = f'Invalid state {state}'
5454
else:
@@ -64,45 +64,47 @@ def __init__(self, state=None):
6464
# This exception gets sent back to the LLM
6565
# It might be malformed JSON
6666
class LLMMalformedActionError(Exception):
67-
def __init__(self, message='Malformed response'):
67+
def __init__(self, message: str = 'Malformed response') -> None:
6868
self.message = message
6969
super().__init__(message)
7070

71-
def __str__(self):
71+
def __str__(self) -> str:
7272
return self.message
7373

7474

7575
# This exception gets sent back to the LLM
7676
# For some reason, the agent did not return an action
7777
class LLMNoActionError(Exception):
78-
def __init__(self, message='Agent must return an action'):
78+
def __init__(self, message: str = 'Agent must return an action') -> None:
7979
super().__init__(message)
8080

8181

8282
# This exception gets sent back to the LLM
8383
# The LLM output did not include an action, or the action was not the expected type
8484
class LLMResponseError(Exception):
85-
def __init__(self, message='Failed to retrieve action from LLM response'):
85+
def __init__(
86+
self, message: str = 'Failed to retrieve action from LLM response'
87+
) -> None:
8688
super().__init__(message)
8789

8890

8991
class UserCancelledError(Exception):
90-
def __init__(self, message='User cancelled the request'):
92+
def __init__(self, message: str = 'User cancelled the request') -> None:
9193
super().__init__(message)
9294

9395

9496
class OperationCancelled(Exception):
9597
"""Exception raised when an operation is cancelled (e.g. by a keyboard interrupt)."""
9698

97-
def __init__(self, message='Operation was cancelled'):
99+
def __init__(self, message: str = 'Operation was cancelled') -> None:
98100
super().__init__(message)
99101

100102

101103
class LLMContextWindowExceedError(RuntimeError):
102104
def __init__(
103105
self,
104-
message='Conversation history longer than LLM context window limit. Consider turning on enable_history_truncation config to avoid this error',
105-
):
106+
message: str = 'Conversation history longer than LLM context window limit. Consider turning on enable_history_truncation config to avoid this error',
107+
) -> None:
106108
super().__init__(message)
107109

108110

@@ -117,7 +119,7 @@ class FunctionCallConversionError(Exception):
117119
This typically happens when there's a malformed message (e.g., missing <function=...> tags). But not due to LLM output.
118120
"""
119121

120-
def __init__(self, message):
122+
def __init__(self, message: str) -> None:
121123
super().__init__(message)
122124

123125

@@ -127,14 +129,14 @@ class FunctionCallValidationError(Exception):
127129
This typically happens when the LLM outputs unrecognized function call / parameter names / values.
128130
"""
129131

130-
def __init__(self, message):
132+
def __init__(self, message: str) -> None:
131133
super().__init__(message)
132134

133135

134136
class FunctionCallNotExistsError(Exception):
135137
"""Exception raised when an LLM call a tool that is not registered."""
136138

137-
def __init__(self, message):
139+
def __init__(self, message: str) -> None:
138140
super().__init__(message)
139141

140142

@@ -191,15 +193,17 @@ class AgentRuntimeNotFoundError(AgentRuntimeUnavailableError):
191193

192194

193195
class BrowserInitException(Exception):
194-
def __init__(self, message='Failed to initialize browser environment'):
196+
def __init__(
197+
self, message: str = 'Failed to initialize browser environment'
198+
) -> None:
195199
super().__init__(message)
196200

197201

198202
class BrowserUnavailableException(Exception):
199203
def __init__(
200204
self,
201-
message='Browser environment is not available, please check if has been initialized',
202-
):
205+
message: str = 'Browser environment is not available, please check if has been initialized',
206+
) -> None:
203207
super().__init__(message)
204208

205209

@@ -217,5 +221,5 @@ class MicroAgentError(Exception):
217221
class MicroAgentValidationError(MicroAgentError):
218222
"""Raised when there's a validation error in microagent metadata."""
219223

220-
def __init__(self, message='Micro agent validation failed'):
224+
def __init__(self, message: str = 'Micro agent validation failed') -> None:
221225
super().__init__(message)

0 commit comments

Comments
 (0)