openhands-sdk 1.3.0__py3-none-any.whl → 1.4.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.
- openhands/sdk/__init__.py +4 -0
- openhands/sdk/agent/agent.py +55 -22
- openhands/sdk/agent/base.py +8 -1
- openhands/sdk/agent/prompts/system_prompt.j2 +1 -11
- openhands/sdk/agent/utils.py +5 -0
- openhands/sdk/context/agent_context.py +30 -0
- openhands/sdk/context/skills/__init__.py +2 -0
- openhands/sdk/context/skills/skill.py +202 -1
- openhands/sdk/conversation/__init__.py +5 -1
- openhands/sdk/conversation/base.py +15 -6
- openhands/sdk/conversation/conversation.py +10 -1
- openhands/sdk/conversation/conversation_stats.py +38 -1
- openhands/sdk/conversation/fifo_lock.py +14 -8
- openhands/sdk/conversation/impl/local_conversation.py +21 -5
- openhands/sdk/conversation/secret_source.py +1 -1
- openhands/sdk/conversation/state.py +8 -0
- openhands/sdk/conversation/types.py +5 -0
- openhands/sdk/event/conversation_state.py +8 -0
- openhands/sdk/llm/__init__.py +3 -0
- openhands/sdk/llm/llm.py +82 -16
- openhands/sdk/llm/llm_registry.py +1 -1
- openhands/sdk/llm/options/chat_options.py +12 -24
- openhands/sdk/llm/options/responses_options.py +9 -1
- openhands/sdk/llm/router/base.py +3 -0
- openhands/sdk/llm/streaming.py +9 -0
- openhands/sdk/llm/utils/model_features.py +12 -0
- openhands/sdk/logger/logger.py +7 -0
- openhands/sdk/tool/tool.py +18 -1
- openhands/sdk/utils/models.py +90 -9
- openhands/sdk/utils/truncate.py +81 -8
- openhands/sdk/workspace/__init__.py +3 -1
- openhands/sdk/workspace/models.py +7 -1
- openhands/sdk/workspace/remote/async_remote_workspace.py +22 -1
- openhands/sdk/workspace/remote/base.py +13 -0
- {openhands_sdk-1.3.0.dist-info → openhands_sdk-1.4.1.dist-info}/METADATA +2 -2
- {openhands_sdk-1.3.0.dist-info → openhands_sdk-1.4.1.dist-info}/RECORD +38 -37
- {openhands_sdk-1.3.0.dist-info → openhands_sdk-1.4.1.dist-info}/WHEEL +0 -0
- {openhands_sdk-1.3.0.dist-info → openhands_sdk-1.4.1.dist-info}/top_level.txt +0 -0
|
@@ -24,6 +24,7 @@ class ModelFeatures:
|
|
|
24
24
|
supports_responses_api: bool
|
|
25
25
|
force_string_serializer: bool
|
|
26
26
|
send_reasoning_content: bool
|
|
27
|
+
supports_prompt_cache_retention: bool
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
# Pattern tables capturing current behavior. Keep patterns lowercase.
|
|
@@ -68,6 +69,14 @@ PROMPT_CACHE_PATTERNS: list[str] = [
|
|
|
68
69
|
"claude-opus-4-5",
|
|
69
70
|
]
|
|
70
71
|
|
|
72
|
+
# Models that support a top-level prompt_cache_retention parameter
|
|
73
|
+
PROMPT_CACHE_RETENTION_PATTERNS: list[str] = [
|
|
74
|
+
# OpenAI GPT-5+ family
|
|
75
|
+
"gpt-5",
|
|
76
|
+
# GPT-4.1 too
|
|
77
|
+
"gpt-4.1",
|
|
78
|
+
]
|
|
79
|
+
|
|
71
80
|
SUPPORTS_STOP_WORDS_FALSE_PATTERNS: list[str] = [
|
|
72
81
|
# o-series families don't support stop words
|
|
73
82
|
"o1",
|
|
@@ -118,6 +127,9 @@ def get_features(model: str) -> ModelFeatures:
|
|
|
118
127
|
supports_responses_api=model_matches(model, RESPONSES_API_PATTERNS),
|
|
119
128
|
force_string_serializer=model_matches(model, FORCE_STRING_SERIALIZER_PATTERNS),
|
|
120
129
|
send_reasoning_content=model_matches(model, SEND_REASONING_CONTENT_PATTERNS),
|
|
130
|
+
supports_prompt_cache_retention=model_matches(
|
|
131
|
+
model, PROMPT_CACHE_RETENTION_PATTERNS
|
|
132
|
+
),
|
|
121
133
|
)
|
|
122
134
|
|
|
123
135
|
|
openhands/sdk/logger/logger.py
CHANGED
|
@@ -105,7 +105,14 @@ def setup_logging(
|
|
|
105
105
|
keep = ENV_BACKUP_COUNT if backup_count is None else backup_count
|
|
106
106
|
|
|
107
107
|
root = logging.getLogger()
|
|
108
|
+
old_level = root.level
|
|
108
109
|
root.setLevel(lvl)
|
|
110
|
+
|
|
111
|
+
# Set the level for any existing logger with the same intial level
|
|
112
|
+
for logger in logging.root.manager.loggerDict.values():
|
|
113
|
+
if isinstance(logger, logging.Logger) and logger.level == old_level:
|
|
114
|
+
logger.setLevel(lvl)
|
|
115
|
+
|
|
109
116
|
# Do NOT clear existing handlers; Uvicorn installs these before importing the app.
|
|
110
117
|
# Only add ours if there isn't already a comparable stream handler.
|
|
111
118
|
has_stream = any(isinstance(h, logging.StreamHandler) for h in root.handlers)
|
openhands/sdk/tool/tool.py
CHANGED
|
@@ -440,7 +440,24 @@ class ToolDefinition[ActionT, ObservationT](DiscriminatedUnionMixin, ABC):
|
|
|
440
440
|
for subclass in get_known_concrete_subclasses(cls):
|
|
441
441
|
if subclass.__name__ == kind:
|
|
442
442
|
return subclass
|
|
443
|
-
|
|
443
|
+
|
|
444
|
+
# Get all possible kinds for the error message
|
|
445
|
+
possible_kinds = [
|
|
446
|
+
subclass.__name__ for subclass in get_known_concrete_subclasses(cls)
|
|
447
|
+
]
|
|
448
|
+
possible_kinds_str = (
|
|
449
|
+
", ".join(sorted(possible_kinds)) if possible_kinds else "none"
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
error_msg = (
|
|
453
|
+
f"Unexpected kind '{kind}' for {cls.__name__}. "
|
|
454
|
+
f"Expected one of: {possible_kinds_str}. "
|
|
455
|
+
f"If you receive this error when trying to wrap a DiscriminatedUnion "
|
|
456
|
+
f"instance inside another pydantic model, you may need to use "
|
|
457
|
+
f"OpenHandsModel instead of BaseModel to make sure that an invalid "
|
|
458
|
+
f"schema has not been cached."
|
|
459
|
+
)
|
|
460
|
+
raise ValueError(error_msg)
|
|
444
461
|
|
|
445
462
|
|
|
446
463
|
def _create_action_type_with_risk(action_type: type[Schema]) -> type[Schema]:
|
openhands/sdk/utils/models.py
CHANGED
|
@@ -3,7 +3,7 @@ import json
|
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
5
|
from abc import ABC
|
|
6
|
-
from typing import Annotated, Any, ClassVar, Literal, Self, Union
|
|
6
|
+
from typing import Annotated, Any, ClassVar, Literal, NoReturn, Self, Union
|
|
7
7
|
|
|
8
8
|
from pydantic import (
|
|
9
9
|
BaseModel,
|
|
@@ -11,7 +11,9 @@ from pydantic import (
|
|
|
11
11
|
Field,
|
|
12
12
|
Tag,
|
|
13
13
|
TypeAdapter,
|
|
14
|
+
ValidationError,
|
|
14
15
|
)
|
|
16
|
+
from pydantic_core import ErrorDetails
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
logger = logging.getLogger(__name__)
|
|
@@ -56,6 +58,48 @@ def kind_of(obj) -> str:
|
|
|
56
58
|
return obj.__name__
|
|
57
59
|
|
|
58
60
|
|
|
61
|
+
def _create_enhanced_discriminated_union_error_message(
|
|
62
|
+
invalid_kind: str, cls_name: str, valid_kinds: list[str]
|
|
63
|
+
) -> str:
|
|
64
|
+
"""Create an enhanced error message for discriminated union validation failures."""
|
|
65
|
+
possible_kinds_str = ", ".join(sorted(valid_kinds)) if valid_kinds else "none"
|
|
66
|
+
return (
|
|
67
|
+
f"Unexpected kind '{invalid_kind}' for {cls_name}. "
|
|
68
|
+
f"Expected one of: {possible_kinds_str}. "
|
|
69
|
+
f"If you receive this error when trying to wrap a "
|
|
70
|
+
f"DiscriminatedUnion instance inside another pydantic model, "
|
|
71
|
+
f"you may need to use OpenHandsModel instead of BaseModel "
|
|
72
|
+
f"to make sure that an invalid schema has not been cached."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _extract_invalid_kind_from_validation_error(error: ErrorDetails) -> str:
|
|
77
|
+
"""Extract the invalid kind from a Pydantic validation error."""
|
|
78
|
+
input_value = error.get("input")
|
|
79
|
+
if input_value is not None and hasattr(input_value, "kind"):
|
|
80
|
+
return input_value.kind
|
|
81
|
+
elif isinstance(input_value, dict) and "kind" in input_value:
|
|
82
|
+
return input_value["kind"]
|
|
83
|
+
else:
|
|
84
|
+
return kind_of(input_value)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _handle_discriminated_union_validation_error(
|
|
88
|
+
validation_error: ValidationError, cls_name: str, valid_kinds: list[str]
|
|
89
|
+
) -> NoReturn:
|
|
90
|
+
"""Handle discriminated union validation errors with enhanced messages."""
|
|
91
|
+
for error in validation_error.errors():
|
|
92
|
+
if error.get("type") == "union_tag_invalid":
|
|
93
|
+
invalid_kind = _extract_invalid_kind_from_validation_error(error)
|
|
94
|
+
error_msg = _create_enhanced_discriminated_union_error_message(
|
|
95
|
+
invalid_kind, cls_name, valid_kinds
|
|
96
|
+
)
|
|
97
|
+
raise ValueError(error_msg) from validation_error
|
|
98
|
+
|
|
99
|
+
# If it's not a discriminated union error, re-raise the original error
|
|
100
|
+
raise validation_error
|
|
101
|
+
|
|
102
|
+
|
|
59
103
|
def get_known_concrete_subclasses(cls) -> list[type]:
|
|
60
104
|
"""Recursively returns all concrete subclasses in a stable order,
|
|
61
105
|
without deduping classes that share the same (module, name)."""
|
|
@@ -137,7 +181,15 @@ class DiscriminatedUnionMixin(OpenHandsModel, ABC):
|
|
|
137
181
|
for subclass in get_known_concrete_subclasses(cls):
|
|
138
182
|
if subclass.__name__ == kind:
|
|
139
183
|
return subclass
|
|
140
|
-
|
|
184
|
+
|
|
185
|
+
# Generate enhanced error message for unknown kind
|
|
186
|
+
valid_kinds = [
|
|
187
|
+
subclass.__name__ for subclass in get_known_concrete_subclasses(cls)
|
|
188
|
+
]
|
|
189
|
+
error_msg = _create_enhanced_discriminated_union_error_message(
|
|
190
|
+
kind, cls.__name__, valid_kinds
|
|
191
|
+
)
|
|
192
|
+
raise ValueError(error_msg)
|
|
141
193
|
|
|
142
194
|
@classmethod
|
|
143
195
|
def __get_pydantic_core_schema__(cls, source_type, handler):
|
|
@@ -150,7 +202,30 @@ class DiscriminatedUnionMixin(OpenHandsModel, ABC):
|
|
|
150
202
|
serializable_type = source_type.get_serializable_type()
|
|
151
203
|
# If there are subclasses, generate schema for the discriminated union
|
|
152
204
|
if serializable_type is not source_type:
|
|
153
|
-
|
|
205
|
+
from pydantic_core import core_schema
|
|
206
|
+
|
|
207
|
+
# Generate the base schema
|
|
208
|
+
base_schema = handler.generate_schema(serializable_type)
|
|
209
|
+
|
|
210
|
+
# Wrap it with a custom validation function that provides
|
|
211
|
+
# enhanced error messages
|
|
212
|
+
def validate_with_enhanced_error(value, handler_func, info): # noqa: ARG001
|
|
213
|
+
try:
|
|
214
|
+
return handler_func(value)
|
|
215
|
+
except ValidationError as e:
|
|
216
|
+
valid_kinds = [
|
|
217
|
+
subclass.__name__
|
|
218
|
+
for subclass in get_known_concrete_subclasses(source_type)
|
|
219
|
+
]
|
|
220
|
+
_handle_discriminated_union_validation_error(
|
|
221
|
+
e, source_type.__name__, valid_kinds
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Create a with_info_wrap_validator_function schema
|
|
225
|
+
return core_schema.with_info_wrap_validator_function(
|
|
226
|
+
validate_with_enhanced_error,
|
|
227
|
+
base_schema,
|
|
228
|
+
)
|
|
154
229
|
|
|
155
230
|
return handler(source_type)
|
|
156
231
|
|
|
@@ -241,12 +316,18 @@ class DiscriminatedUnionMixin(OpenHandsModel, ABC):
|
|
|
241
316
|
|
|
242
317
|
@classmethod
|
|
243
318
|
def model_validate(cls, obj: Any, **kwargs) -> Self:
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
319
|
+
try:
|
|
320
|
+
if _is_abstract(cls):
|
|
321
|
+
resolved = cls.resolve_kind(kind_of(obj))
|
|
322
|
+
else:
|
|
323
|
+
resolved = super()
|
|
324
|
+
result = resolved.model_validate(obj, **kwargs)
|
|
325
|
+
return result # type: ignore
|
|
326
|
+
except ValidationError as e:
|
|
327
|
+
valid_kinds = [
|
|
328
|
+
subclass.__name__ for subclass in get_known_concrete_subclasses(cls)
|
|
329
|
+
]
|
|
330
|
+
_handle_discriminated_union_validation_error(e, cls.__name__, valid_kinds)
|
|
250
331
|
|
|
251
332
|
@classmethod
|
|
252
333
|
def model_validate_json(
|
openhands/sdk/utils/truncate.py
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
"""Utility functions for truncating text content."""
|
|
2
2
|
|
|
3
|
+
import hashlib
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from openhands.sdk.logger import get_logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
logger = get_logger(__name__)
|
|
10
|
+
|
|
3
11
|
# Default truncation limits
|
|
4
12
|
DEFAULT_TEXT_CONTENT_LIMIT = 50_000
|
|
5
13
|
|
|
@@ -7,38 +15,103 @@ DEFAULT_TEXT_CONTENT_LIMIT = 50_000
|
|
|
7
15
|
DEFAULT_TRUNCATE_NOTICE = (
|
|
8
16
|
"<response clipped><NOTE>Due to the max output limit, only part of the full "
|
|
9
17
|
"response has been shown to you.</NOTE>"
|
|
18
|
+
) # 113 chars
|
|
19
|
+
|
|
20
|
+
DEFAULT_TRUNCATE_NOTICE_WITH_PERSIST = (
|
|
21
|
+
"<response clipped><NOTE>Due to the max output limit, only part of the full "
|
|
22
|
+
"response has been shown to you. The complete output has been saved to "
|
|
23
|
+
"{file_path} - you can use other tools to view the full content (truncated "
|
|
24
|
+
"part starts around line {line_num}).</NOTE>"
|
|
10
25
|
)
|
|
11
26
|
|
|
12
27
|
|
|
28
|
+
def _save_full_content(content: str, save_dir: str, tool_prefix: str) -> str | None:
|
|
29
|
+
"""Save full content to the specified directory and return the file path."""
|
|
30
|
+
|
|
31
|
+
save_dir_path = Path(save_dir)
|
|
32
|
+
save_dir_path.mkdir(exist_ok=True)
|
|
33
|
+
|
|
34
|
+
# Generate hash-based filename for deduplication
|
|
35
|
+
content_hash = hashlib.sha256(content.encode("utf-8")).hexdigest()[:8]
|
|
36
|
+
filename = f"{tool_prefix}_output_{content_hash}.txt"
|
|
37
|
+
file_path = save_dir_path / filename
|
|
38
|
+
|
|
39
|
+
# Only write if file doesn't exist (deduplication)
|
|
40
|
+
if not file_path.exists():
|
|
41
|
+
try:
|
|
42
|
+
file_path.write_text(content, encoding="utf-8")
|
|
43
|
+
except Exception as e:
|
|
44
|
+
logger.debug(f"Failed to save full content to {file_path}: {e}")
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
return str(file_path)
|
|
48
|
+
|
|
49
|
+
|
|
13
50
|
def maybe_truncate(
|
|
14
51
|
content: str,
|
|
15
52
|
truncate_after: int | None = None,
|
|
16
53
|
truncate_notice: str = DEFAULT_TRUNCATE_NOTICE,
|
|
54
|
+
save_dir: str | None = None,
|
|
55
|
+
tool_prefix: str = "output",
|
|
17
56
|
) -> str:
|
|
18
57
|
"""
|
|
19
58
|
Truncate the middle of content if it exceeds the specified length.
|
|
20
59
|
|
|
21
60
|
Keeps the head and tail of the content to preserve context at both ends.
|
|
61
|
+
Optionally saves the full content to a file for later investigation.
|
|
22
62
|
|
|
23
63
|
Args:
|
|
24
64
|
content: The text content to potentially truncate
|
|
25
65
|
truncate_after: Maximum length before truncation. If None, no truncation occurs
|
|
26
66
|
truncate_notice: Notice to insert in the middle when content is truncated
|
|
67
|
+
save_dir: Working directory to save full content file in
|
|
68
|
+
tool_prefix: Prefix for the saved file (e.g., "bash", "browser", "editor")
|
|
27
69
|
|
|
28
70
|
Returns:
|
|
29
71
|
Original content if under limit, or truncated content with head and tail
|
|
30
|
-
preserved
|
|
72
|
+
preserved and reference to saved file if applicable
|
|
31
73
|
"""
|
|
74
|
+
# 1) Early exits: no truncation requested, or content already within limit
|
|
32
75
|
if not truncate_after or len(content) <= truncate_after or truncate_after < 0:
|
|
33
76
|
return content
|
|
34
77
|
|
|
35
|
-
#
|
|
78
|
+
# 2) If even the base notice doesn't fit, return a slice of it
|
|
79
|
+
if len(truncate_notice) >= truncate_after:
|
|
80
|
+
return truncate_notice[:truncate_after]
|
|
81
|
+
|
|
82
|
+
# 3) Calculate proposed head size based on base notice
|
|
83
|
+
# (for consistent line number calc)
|
|
36
84
|
available_chars = truncate_after - len(truncate_notice)
|
|
37
|
-
|
|
85
|
+
# Prefer giving the "extra" char to head (ceil split)
|
|
86
|
+
proposed_head = available_chars // 2 + (available_chars % 2)
|
|
87
|
+
|
|
88
|
+
# 4) Optionally save full content, then construct the final notice
|
|
89
|
+
final_notice = truncate_notice
|
|
90
|
+
if save_dir:
|
|
91
|
+
saved_file_path = _save_full_content(content, save_dir, tool_prefix)
|
|
92
|
+
if saved_file_path:
|
|
93
|
+
# Calculate line number where truncation happens (using head_chars)
|
|
94
|
+
head_content_lines = len(content[:proposed_head].splitlines())
|
|
95
|
+
|
|
96
|
+
final_notice = DEFAULT_TRUNCATE_NOTICE_WITH_PERSIST.format(
|
|
97
|
+
file_path=saved_file_path,
|
|
98
|
+
line_num=head_content_lines + 1, # +1 to indicate next line
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# 5) If the final notice (with persist info) alone fills the
|
|
102
|
+
# budget, return a slice of it
|
|
103
|
+
if len(final_notice) >= truncate_after:
|
|
104
|
+
return final_notice[:truncate_after]
|
|
38
105
|
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
106
|
+
# 6) Allocate remaining budget to head/tail
|
|
107
|
+
remaining = truncate_after - len(final_notice)
|
|
108
|
+
head_chars = min(
|
|
109
|
+
proposed_head, remaining
|
|
110
|
+
) # Ensure head_chars doesn't exceed remaining
|
|
111
|
+
tail_chars = remaining - head_chars # non-negative due to previous checks
|
|
42
112
|
|
|
43
|
-
|
|
44
|
-
|
|
113
|
+
return (
|
|
114
|
+
content[:head_chars]
|
|
115
|
+
+ final_notice
|
|
116
|
+
+ (content[-tail_chars:] if tail_chars > 0 else "")
|
|
117
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from .base import BaseWorkspace
|
|
2
2
|
from .local import LocalWorkspace
|
|
3
|
-
from .models import CommandResult, FileOperationResult
|
|
3
|
+
from .models import CommandResult, FileOperationResult, PlatformType, TargetType
|
|
4
4
|
from .remote import RemoteWorkspace
|
|
5
5
|
from .workspace import Workspace
|
|
6
6
|
|
|
@@ -10,6 +10,8 @@ __all__ = [
|
|
|
10
10
|
"CommandResult",
|
|
11
11
|
"FileOperationResult",
|
|
12
12
|
"LocalWorkspace",
|
|
13
|
+
"PlatformType",
|
|
13
14
|
"RemoteWorkspace",
|
|
15
|
+
"TargetType",
|
|
14
16
|
"Workspace",
|
|
15
17
|
]
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
"""Pydantic models for workspace operation results."""
|
|
1
|
+
"""Pydantic models for workspace operation results and build types."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
2
4
|
|
|
3
5
|
from pydantic import BaseModel, Field
|
|
4
6
|
|
|
5
7
|
|
|
8
|
+
TargetType = Literal["binary", "binary-minimal", "source", "source-minimal"]
|
|
9
|
+
PlatformType = Literal["linux/amd64", "linux/arm64"]
|
|
10
|
+
|
|
11
|
+
|
|
6
12
|
class CommandResult(BaseModel):
|
|
7
13
|
"""Result of executing a command in the workspace."""
|
|
8
14
|
|
|
@@ -15,11 +15,32 @@ class AsyncRemoteWorkspace(RemoteWorkspaceMixin):
|
|
|
15
15
|
|
|
16
16
|
_client: httpx.AsyncClient | None = PrivateAttr(default=None)
|
|
17
17
|
|
|
18
|
+
async def reset_client(self) -> None:
|
|
19
|
+
"""Reset the HTTP client to force re-initialization.
|
|
20
|
+
|
|
21
|
+
This is useful when connection parameters (host, api_key) have changed
|
|
22
|
+
and the client needs to be recreated with new values.
|
|
23
|
+
"""
|
|
24
|
+
if self._client is not None:
|
|
25
|
+
try:
|
|
26
|
+
await self._client.aclose()
|
|
27
|
+
except Exception:
|
|
28
|
+
pass
|
|
29
|
+
self._client = None
|
|
30
|
+
|
|
18
31
|
@property
|
|
19
32
|
def client(self) -> httpx.AsyncClient:
|
|
20
33
|
client = self._client
|
|
21
34
|
if client is None:
|
|
22
|
-
|
|
35
|
+
# Configure reasonable timeouts for HTTP requests
|
|
36
|
+
# - connect: 10 seconds to establish connection
|
|
37
|
+
# - read: 60 seconds to read response (for LLM operations)
|
|
38
|
+
# - write: 10 seconds to send request
|
|
39
|
+
# - pool: 10 seconds to get connection from pool
|
|
40
|
+
timeout = httpx.Timeout(connect=10.0, read=60.0, write=10.0, pool=10.0)
|
|
41
|
+
client = httpx.AsyncClient(
|
|
42
|
+
base_url=self.host, timeout=timeout, headers=self._headers
|
|
43
|
+
)
|
|
23
44
|
self._client = client
|
|
24
45
|
return client
|
|
25
46
|
|
|
@@ -30,6 +30,19 @@ class RemoteWorkspace(RemoteWorkspaceMixin, BaseWorkspace):
|
|
|
30
30
|
|
|
31
31
|
_client: httpx.Client | None = PrivateAttr(default=None)
|
|
32
32
|
|
|
33
|
+
def reset_client(self) -> None:
|
|
34
|
+
"""Reset the HTTP client to force re-initialization.
|
|
35
|
+
|
|
36
|
+
This is useful when connection parameters (host, api_key) have changed
|
|
37
|
+
and the client needs to be recreated with new values.
|
|
38
|
+
"""
|
|
39
|
+
if self._client is not None:
|
|
40
|
+
try:
|
|
41
|
+
self._client.close()
|
|
42
|
+
except Exception:
|
|
43
|
+
pass
|
|
44
|
+
self._client = None
|
|
45
|
+
|
|
33
46
|
@property
|
|
34
47
|
def client(self) -> httpx.Client:
|
|
35
48
|
client = self._client
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: OpenHands SDK - Core functionality for building AI agents
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Requires-Dist: deprecation>=2.1.0
|
|
7
7
|
Requires-Dist: fastmcp>=2.11.3
|
|
8
8
|
Requires-Dist: httpx>=0.27.0
|
|
9
|
-
Requires-Dist: litellm>=1.
|
|
9
|
+
Requires-Dist: litellm>=1.80.7
|
|
10
10
|
Requires-Dist: pydantic>=2.11.7
|
|
11
11
|
Requires-Dist: python-frontmatter>=1.1.0
|
|
12
12
|
Requires-Dist: python-json-logger>=3.3.0
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
openhands/sdk/__init__.py,sha256=
|
|
1
|
+
openhands/sdk/__init__.py,sha256=SsB5acHhWvF6e3FlbR72PzZHH9ByJiXc7vnwxBPqmhw,2374
|
|
2
2
|
openhands/sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
openhands/sdk/agent/__init__.py,sha256=yOn1ZCgTTq2VJlTzKDSzmWVPli1siBzqV89vlEHCwOg,137
|
|
4
|
-
openhands/sdk/agent/agent.py,sha256=
|
|
5
|
-
openhands/sdk/agent/base.py,sha256=
|
|
6
|
-
openhands/sdk/agent/utils.py,sha256=
|
|
4
|
+
openhands/sdk/agent/agent.py,sha256=y8jfExyzgP898_Vr_Fz75Ovg2XTnkNQTgekdOi2S58I,19516
|
|
5
|
+
openhands/sdk/agent/base.py,sha256=HmZ_wNXdbsStqxjgkylGFm9B0pkp-0MCjmFTyh_XenM,15780
|
|
6
|
+
openhands/sdk/agent/utils.py,sha256=alYsAQ611XQ_94ogJacYD22BLbgSJjzd3Xex_b9KuC4,7418
|
|
7
7
|
openhands/sdk/agent/prompts/in_context_learning_example.j2,sha256=MGB0dPUlh6pwLoR_dBK-M3e5dtETX6C6WNjPcPixZmU,5512
|
|
8
8
|
openhands/sdk/agent/prompts/in_context_learning_example_suffix.j2,sha256=k3Zwnd7Iq7kL4lo307RDuu1mxWXn6pSLsEdvKEXN3BU,164
|
|
9
9
|
openhands/sdk/agent/prompts/security_policy.j2,sha256=K56d2aaZ88DI-y2DsMSDaiZRTTnkkzuBLjbzXfKHGA8,993
|
|
10
10
|
openhands/sdk/agent/prompts/security_risk_assessment.j2,sha256=7o1tk6MIQpVD7sAES-sBhw4ckYLGQydzYnjjNitP5iY,1196
|
|
11
|
-
openhands/sdk/agent/prompts/system_prompt.j2,sha256=
|
|
11
|
+
openhands/sdk/agent/prompts/system_prompt.j2,sha256=Cdi9eDy5el1mp5IXlJqr3aqsgRYt3p6yC0Wgw1E8NFQ,7979
|
|
12
12
|
openhands/sdk/agent/prompts/system_prompt_interactive.j2,sha256=AW3rGuqu82BqbS1XMXVO4Fp-Apa8DPYZV3_nQYkzVtM,1388
|
|
13
13
|
openhands/sdk/agent/prompts/system_prompt_long_horizon.j2,sha256=_oOHRIer_FSuRrBOSOPpe5Ueo9KgSTba5SPoHHpghCI,2995
|
|
14
14
|
openhands/sdk/agent/prompts/system_prompt_planning.j2,sha256=wh01KX7yzeUSn5PtG3Ij0keRM6vmQuZZM6I0Fod4DK4,2934
|
|
15
15
|
openhands/sdk/agent/prompts/system_prompt_tech_philosophy.j2,sha256=Yq9H7hHen2-tNsfBq9RlAWpyWsVRpjHhzmziZv8JHs8,5005
|
|
16
16
|
openhands/sdk/context/__init__.py,sha256=dsOiCbO-eizN7HiGn_ZSSgH-EE841ygwalMzWz9GbVY,557
|
|
17
|
-
openhands/sdk/context/agent_context.py,sha256=
|
|
17
|
+
openhands/sdk/context/agent_context.py,sha256=lEJySpZz2aq9RonsCwsf2eF4bivrCikOqpmnogsiNhk,8370
|
|
18
18
|
openhands/sdk/context/view.py,sha256=j8R4r1PSKFUJR_G4qDyI8ToqyHbWtU0YRKDBm8LJm4I,9260
|
|
19
19
|
openhands/sdk/context/condenser/__init__.py,sha256=o3O5wEOUak7mzJ3EzY4s6pjUwA_Q5EzLvgNSFM46d50,490
|
|
20
20
|
openhands/sdk/context/condenser/base.py,sha256=fp1WxZGslE1luwmzFmablZ9Q3h_F7ErBJfCAh6HKpRI,3659
|
|
@@ -27,30 +27,30 @@ openhands/sdk/context/prompts/prompt.py,sha256=a2eCR4QSYzgdDxcGkGVktKerAOk-SCb8o
|
|
|
27
27
|
openhands/sdk/context/prompts/templates/ask_agent_template.j2,sha256=VRKWdF2VTJ_Tyway_Wexp8_KlNgAkME94eZelbbsEZI,212
|
|
28
28
|
openhands/sdk/context/prompts/templates/skill_knowledge_info.j2,sha256=3yNxEEbScAU1w60ix7Ph91wr6p8mjwT-3EGkkZkWT9Q,261
|
|
29
29
|
openhands/sdk/context/prompts/templates/system_message_suffix.j2,sha256=JTNvdtH_qbFBTzZVV8NzHtn48BJXNY2dLFgMWj_iwM8,401
|
|
30
|
-
openhands/sdk/context/skills/__init__.py,sha256=
|
|
30
|
+
openhands/sdk/context/skills/__init__.py,sha256=5YmyufIWoLvoUYtcZE_xI4sOtcCs70-CHrblnzavWWY,593
|
|
31
31
|
openhands/sdk/context/skills/exceptions.py,sha256=tVBSbXTXG32nb1TebVAuzbNNHvn8GScpSM1bHCnQzvY,305
|
|
32
|
-
openhands/sdk/context/skills/skill.py,sha256=
|
|
32
|
+
openhands/sdk/context/skills/skill.py,sha256=iJpJWiXLHhxd7tmdPo1HcIDqa4emAYkQHgN7KvkQcl0,20407
|
|
33
33
|
openhands/sdk/context/skills/trigger.py,sha256=ZGaDmMpJghnAEuTTYX6UepsA5nX1CSz83zK1Ox46vMk,756
|
|
34
34
|
openhands/sdk/context/skills/types.py,sha256=qdakWgfs_88I_cnmVhO5bl3C3s11GvCydq7Q-V53kCI,1662
|
|
35
|
-
openhands/sdk/conversation/__init__.py,sha256=
|
|
36
|
-
openhands/sdk/conversation/base.py,sha256=
|
|
37
|
-
openhands/sdk/conversation/conversation.py,sha256=
|
|
38
|
-
openhands/sdk/conversation/conversation_stats.py,sha256=
|
|
35
|
+
openhands/sdk/conversation/__init__.py,sha256=1-xh49S2KJhtAjkHDaDHU28UWhfQLl3CeFlo179gr00,1402
|
|
36
|
+
openhands/sdk/conversation/base.py,sha256=3qUc4_PjrT1IztBX7EJKuypvRVWRIbMmIQ3Fc2lvspI,8308
|
|
37
|
+
openhands/sdk/conversation/conversation.py,sha256=GsPK9MWTAWG47J6jQ1vcSD9ER31wbFkRF9zdFmCqz9I,5275
|
|
38
|
+
openhands/sdk/conversation/conversation_stats.py,sha256=ZlQ99kgG5YVCrZ4rqJlq63JaiInxX8jqv-q5lS7RN68,3038
|
|
39
39
|
openhands/sdk/conversation/event_store.py,sha256=he-bwP823s5zAIdua_0ZgkkHQCJoAqbtV2SN0hibX30,5207
|
|
40
40
|
openhands/sdk/conversation/events_list_base.py,sha256=n_YvgbhBPOPDbw4Kp68J0EKFM39vg95ng09GMfTz29s,505
|
|
41
41
|
openhands/sdk/conversation/exceptions.py,sha256=C3pN3MJJIYdhcMHMqtOmVkR1BhVe3pfxBhxzQ6FVBjc,804
|
|
42
|
-
openhands/sdk/conversation/fifo_lock.py,sha256=
|
|
42
|
+
openhands/sdk/conversation/fifo_lock.py,sha256=nY5RsobNvVXBbAzwjqIxyQwPUh0AzffbTZw4PewhTTI,4240
|
|
43
43
|
openhands/sdk/conversation/persistence_const.py,sha256=om3pOQa5sGK8t_NUYb3Tz-7sKeu531gaS1e7iCaqWmo,218
|
|
44
44
|
openhands/sdk/conversation/response_utils.py,sha256=rPlC3cDSmoQte6NZ0kK6h6-9ho5cbF8jEw-DiyEhgIM,1548
|
|
45
45
|
openhands/sdk/conversation/secret_registry.py,sha256=37kk-aaeQ_XKZDXHpNJ2oTOWsElL4Q9jI_6qd3A774I,4500
|
|
46
|
-
openhands/sdk/conversation/secret_source.py,sha256=
|
|
46
|
+
openhands/sdk/conversation/secret_source.py,sha256=1itHqTgwu1yZKGZ46HR0X08MLU2Y_rWa2ahzbqxH4k0,2568
|
|
47
47
|
openhands/sdk/conversation/serialization_diff.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
-
openhands/sdk/conversation/state.py,sha256=
|
|
48
|
+
openhands/sdk/conversation/state.py,sha256=EpP_0MAWv14gEIBvIlpdIxtxLCzN1n2mgzSzQNRpCY8,13046
|
|
49
49
|
openhands/sdk/conversation/stuck_detector.py,sha256=PZF0HWC6G0SUud_U3hiv5r4AqfJJMw5-ookEWVOY5sY,10866
|
|
50
50
|
openhands/sdk/conversation/title_utils.py,sha256=j40-dP-Oes-mhU2xUC7fCC8cB0wkMdbbDJU7WLHiVIo,7063
|
|
51
|
-
openhands/sdk/conversation/types.py,sha256=
|
|
51
|
+
openhands/sdk/conversation/types.py,sha256=CMCCJz6fSfWgaAgWXeorEDC8VSXyyplyiMlpcueszT8,423
|
|
52
52
|
openhands/sdk/conversation/impl/__init__.py,sha256=DmDFyNR4RU8eiMocKf2j9eBQomipP-rrJgU1LoVWTDA,220
|
|
53
|
-
openhands/sdk/conversation/impl/local_conversation.py,sha256=
|
|
53
|
+
openhands/sdk/conversation/impl/local_conversation.py,sha256=R0iTc1oCaOBiBUH3Ei7RNO1JMS_7nEuBlCr3ILUosU0,22594
|
|
54
54
|
openhands/sdk/conversation/impl/remote_conversation.py,sha256=9_yCDHO5KKODYHVzUt0mJnsSVcDE6Tv3TJvGULilcyE,28536
|
|
55
55
|
openhands/sdk/conversation/visualizer/__init__.py,sha256=0LXpKlt2eJcrqP1z6jQP_nLx23V8ErnQkKYSxvUp0_A,275
|
|
56
56
|
openhands/sdk/conversation/visualizer/base.py,sha256=77DdRdHAPSESxRCYyRRSOK7ROBlljscxogkFFr4YgM0,2323
|
|
@@ -65,7 +65,7 @@ openhands/sdk/event/__init__.py,sha256=ir-jRVA0QjEbFuDzJOYRq2kXTgUHs8eJ7_skoCRNz
|
|
|
65
65
|
openhands/sdk/event/base.py,sha256=QpQtYfYiZqaKWA-2xa4GEmk2qkUHKD6x-OIAk7SMnDs,5568
|
|
66
66
|
openhands/sdk/event/condenser.py,sha256=sne9CxhhNq9UvJMKuKcw8tXuMgutuGh85TbbZHwYhPQ,2481
|
|
67
67
|
openhands/sdk/event/conversation_error.py,sha256=gZMyliJx1xbyoJFYH0-AEHqznyiA8LZ7olPSidaNfuc,957
|
|
68
|
-
openhands/sdk/event/conversation_state.py,sha256=
|
|
68
|
+
openhands/sdk/event/conversation_state.py,sha256=V-ti5SLL5SL330sEOQgpy-U8tErVwZYG0iC3AqjTUwo,3650
|
|
69
69
|
openhands/sdk/event/llm_completion_log.py,sha256=VCxJiZBsn1F6TRV6fwvsPs6W9DpjghfIFmJJlGKztXg,1232
|
|
70
70
|
openhands/sdk/event/token.py,sha256=QlEbBrfZaHe9tp8-Ot4vTd0T-_Vj7lpUqKfVJcVHOeI,497
|
|
71
71
|
openhands/sdk/event/types.py,sha256=3hyFDBNtYTGEwLV6muodJO7iDMoZTC4D0Dd4vYeF2dE,271
|
|
@@ -84,11 +84,12 @@ openhands/sdk/io/__init__.py,sha256=6pXTWP03Wn5S7b6fOT0g3PYn-qSoEGGdrrwBqALYGA4,
|
|
|
84
84
|
openhands/sdk/io/base.py,sha256=kAcX0chfCswakiieJlKiHWoJgL3zOtaQauRqMPNYfW8,1355
|
|
85
85
|
openhands/sdk/io/local.py,sha256=H1wjnBS0EkBJxEatWqNpteL1bPBHoetcnhrIeou4uEY,2991
|
|
86
86
|
openhands/sdk/io/memory.py,sha256=XIsdXsSyF-PzoYVmvJuO7Vtz-k3D5jMOFoZ5gHw8tbA,1712
|
|
87
|
-
openhands/sdk/llm/__init__.py,sha256=
|
|
88
|
-
openhands/sdk/llm/llm.py,sha256=
|
|
89
|
-
openhands/sdk/llm/llm_registry.py,sha256=
|
|
87
|
+
openhands/sdk/llm/__init__.py,sha256=k8UneyfoDUMe0lSP4GSlYzrL2Fe3MkDUKpSg2OIDi_I,1206
|
|
88
|
+
openhands/sdk/llm/llm.py,sha256=7cpdaXvsgafim2ZXI5umjN3P5ND9xk1GjktKRbrWGqo,44308
|
|
89
|
+
openhands/sdk/llm/llm_registry.py,sha256=DL9yqSbAM7OBkzdIChLuxG2qk_oElW2tC2xem6mq0F8,3530
|
|
90
90
|
openhands/sdk/llm/llm_response.py,sha256=DaBVBkij4Sz-RsYhRb3UUcvJCTzCBcOYQ9IhFwN4ukI,1988
|
|
91
91
|
openhands/sdk/llm/message.py,sha256=zVcmHL4z99Bm0_qHz0hhtDh8a9r8Y1GtcfCgL5yBsME,25429
|
|
92
|
+
openhands/sdk/llm/streaming.py,sha256=tFJ7B0AjJ-e8Xv13DTtc2FdrsLRUCG8wxQex8fDlOp4,214
|
|
92
93
|
openhands/sdk/llm/exceptions/__init__.py,sha256=6iMJah2nS6BboU06HqgAM2JT6aykCWY8muoUwaaJpR8,1144
|
|
93
94
|
openhands/sdk/llm/exceptions/classifier.py,sha256=pu5fVNubUrB3eXV1i5W7m4-D4Ik2Z-fGe1ba2t0SSc4,1456
|
|
94
95
|
openhands/sdk/llm/exceptions/mapping.py,sha256=D68duh85HPONw5P0Mv4e8Ji-ubi6qWEb2d9FkOKf8cA,1656
|
|
@@ -96,22 +97,22 @@ openhands/sdk/llm/exceptions/types.py,sha256=IfyRtfH5eJrsLOlp6Fz76CiCKjx1ukmXciI
|
|
|
96
97
|
openhands/sdk/llm/mixins/fn_call_converter.py,sha256=G5u0cNGLBscog7Vt0c7Xjo5uLsvyleBlk12Mq0wTQTM,46310
|
|
97
98
|
openhands/sdk/llm/mixins/non_native_fc.py,sha256=ymPELReJtFsBJCweEpfx2njyd6NCG9mj9AoDPPLFAqc,3250
|
|
98
99
|
openhands/sdk/llm/options/__init__.py,sha256=EntvOWC5kwDoTMXXMkYuoWMQ13hD8YtC9CEMCtnKj7o,54
|
|
99
|
-
openhands/sdk/llm/options/chat_options.py,sha256=
|
|
100
|
+
openhands/sdk/llm/options/chat_options.py,sha256=YrQrMKUj9Sb0ZdzJMoB11PwD7hzdLBDGrOMWfgZQ10s,3452
|
|
100
101
|
openhands/sdk/llm/options/common.py,sha256=qFcPuZF_c4rmH1bgGG8Qp6TJ4YWpv9IFzfLZRRiik9M,580
|
|
101
|
-
openhands/sdk/llm/options/responses_options.py,sha256=
|
|
102
|
+
openhands/sdk/llm/options/responses_options.py,sha256=xrBEc62PFjLeM9bGyXtXrQ6GgYDzhJfGha8Nhi5Oci8,2166
|
|
102
103
|
openhands/sdk/llm/router/__init__.py,sha256=N8qldpGdLLCWZzn5Rz2y8AheSoCTQLGkLOBDCFNMJRA,261
|
|
103
|
-
openhands/sdk/llm/router/base.py,sha256=
|
|
104
|
+
openhands/sdk/llm/router/base.py,sha256=9mUUpv-zgT6HVKmJ87Q5e5VncoKi8hHd9aP8j9cJI_g,4139
|
|
104
105
|
openhands/sdk/llm/router/impl/multimodal.py,sha256=uKFVm7b3Jr0xCC1lvP-cVn-fIkTv24-pK3xKlOJahz4,3034
|
|
105
106
|
openhands/sdk/llm/router/impl/random.py,sha256=oBHoFTBMa9OeDyg-rV4siLCkN6rKYL0uDlZMEseB3ro,656
|
|
106
107
|
openhands/sdk/llm/utils/metrics.py,sha256=4zD0Hkc9Oc4qcDcVZUX13RyggyObshUbz4Ik9W1uIw4,11592
|
|
107
|
-
openhands/sdk/llm/utils/model_features.py,sha256=
|
|
108
|
+
openhands/sdk/llm/utils/model_features.py,sha256=JMGShnYBplGL6O6f19YC2EqI3dIviNN_58TFbflBnlA,4607
|
|
108
109
|
openhands/sdk/llm/utils/model_info.py,sha256=1mFYA7OcEyUB6k1doao8_w1XT7UMM_DAm57HcTpKkLw,2628
|
|
109
110
|
openhands/sdk/llm/utils/retry_mixin.py,sha256=yIkUxgtym4ouOci0o1u1BJnThUt05njblhC0b2Tvmb8,4559
|
|
110
111
|
openhands/sdk/llm/utils/telemetry.py,sha256=BGzikbw1DAj96Bo60D7XIGh034zKIbO1zRx597_3TR4,13966
|
|
111
112
|
openhands/sdk/llm/utils/unverified_models.py,sha256=SmYrX_WxXOJBanTviztqy1xPjOcLY4i3qvwNBEga_Dk,4797
|
|
112
113
|
openhands/sdk/llm/utils/verified_models.py,sha256=add09fF6MHOCBS4Wp2BxLYYfijMtzkq1IFc3GNZgMWU,1357
|
|
113
114
|
openhands/sdk/logger/__init__.py,sha256=vZvFDYfW01Y8Act3tveMs3XxTysJlt4HeT-n6X_ujYk,330
|
|
114
|
-
openhands/sdk/logger/logger.py,sha256=
|
|
115
|
+
openhands/sdk/logger/logger.py,sha256=kSpeol92dKesm24ZxVyYbtAxeYccmuFZJw3qNO9X-t4,6513
|
|
115
116
|
openhands/sdk/logger/rolling.py,sha256=E6oy0asgmOhZHoWlSCw0QK1PKnS6kvtxjoWLAsqlGvs,3440
|
|
116
117
|
openhands/sdk/mcp/__init__.py,sha256=-wQbZ405PjVRCBtSfirp4jsiRohd7IJAyAdicZ-M8Ok,588
|
|
117
118
|
openhands/sdk/mcp/client.py,sha256=CLkFImydorlT_RBTE5UxvRNGMlc-uO3wvupoDzB5E20,2420
|
|
@@ -131,7 +132,7 @@ openhands/sdk/tool/__init__.py,sha256=Fpcy_I5CYC8lDf2KNLj7LJg9YpoDObbacMFOvM9fI5
|
|
|
131
132
|
openhands/sdk/tool/registry.py,sha256=x5j8DwOjH3YJijhXe_3nRPsSr1A2sDPpNhxCuvFrHK0,5456
|
|
132
133
|
openhands/sdk/tool/schema.py,sha256=PdQbkzPER-Z9B-eKPMr16M8ap27gghszq1xSHH5DGZQ,9114
|
|
133
134
|
openhands/sdk/tool/spec.py,sha256=EbtWasVhwjLKNJywHubbwfYqfgXnZbU4QE6XUOLhsdk,1221
|
|
134
|
-
openhands/sdk/tool/tool.py,sha256=
|
|
135
|
+
openhands/sdk/tool/tool.py,sha256=tcAno2EEXF-Ta8icgSbX_0mQDuuBDmQ5CugnEINHqwk,17798
|
|
135
136
|
openhands/sdk/tool/builtins/__init__.py,sha256=30MfENomH9JBPwKs03Xwgny09dDUOt3FSIQ3egRJKhM,729
|
|
136
137
|
openhands/sdk/tool/builtins/finish.py,sha256=pPv_bKDOQE4sUK9lNh1H1lDaxavHozjOwLCIuYrb424,3136
|
|
137
138
|
openhands/sdk/tool/builtins/think.py,sha256=Jp8CBHJZwrtNuVCLrxKlVwb9HeQ1lZB56PCYMxW3wWk,4050
|
|
@@ -143,21 +144,21 @@ openhands/sdk/utils/command.py,sha256=iqTdFL9tPQ0gyq5w_HOMXSSHg1nowtrWwJBeDL87lZ
|
|
|
143
144
|
openhands/sdk/utils/deprecation.py,sha256=7XwepMKTwBLaqS-4rdJupKxNZkepV0Mrru3uTQukS0k,5114
|
|
144
145
|
openhands/sdk/utils/github.py,sha256=l-_LQKw820dA_U8NmDdmaReAPhgMAWqfH1oRMi1q5OA,1638
|
|
145
146
|
openhands/sdk/utils/json.py,sha256=hHAA7i7NCJrQhb5WWSsoT0nvmJUi0koyBdbviFrfdcM,1384
|
|
146
|
-
openhands/sdk/utils/models.py,sha256=
|
|
147
|
+
openhands/sdk/utils/models.py,sha256=tOTGa4O1vHZgHvOp2mNv5V4hM3fBSM6Sswv-0oYVyJQ,20676
|
|
147
148
|
openhands/sdk/utils/pydantic_diff.py,sha256=vOy4M1XKKDkCzp7RXBvnibvPYWL8fNEDtWNUn_-N1yg,2732
|
|
148
149
|
openhands/sdk/utils/pydantic_secrets.py,sha256=B9njRdijnqO4g-GDCWRsWd-TZc5GTjMncDEPMFP_aUE,2186
|
|
149
|
-
openhands/sdk/utils/truncate.py,sha256=
|
|
150
|
+
openhands/sdk/utils/truncate.py,sha256=wrPdD1557teSR0A66z9roYAINp_cWYYwS6Ku29Z7b8M,4376
|
|
150
151
|
openhands/sdk/utils/visualize.py,sha256=ZRZTA0epdiOA7vdk-EdGV6Yb5t8rz6ZzrargH7MUTEQ,845
|
|
151
|
-
openhands/sdk/workspace/__init__.py,sha256=
|
|
152
|
+
openhands/sdk/workspace/__init__.py,sha256=09huA83IZJZU8XZSVzSgZwfUv-slzYwofnUjLgqt7B8,401
|
|
152
153
|
openhands/sdk/workspace/base.py,sha256=4_oBXbjspa2Oh2wpgbcQMyrshi7prhmOL1LY97NowgQ,4590
|
|
153
154
|
openhands/sdk/workspace/local.py,sha256=N_cW_E5BfctIpgwU-OcRQIqKLn4ibxNwjL4c8iYjU6E,6253
|
|
154
|
-
openhands/sdk/workspace/models.py,sha256=
|
|
155
|
+
openhands/sdk/workspace/models.py,sha256=ORFn70kXvD0urjeq59d_XqXBYSHbDYkBMg9KYQKRa5I,1304
|
|
155
156
|
openhands/sdk/workspace/workspace.py,sha256=Of1r2z3W_pXSgV8u10sUQ87ChtaA5khD6HJ_PtJz6aY,1296
|
|
156
157
|
openhands/sdk/workspace/remote/__init__.py,sha256=eKkj6NOESMUBGDVC6_L2Wfuc4K6G-mpnJDNHKBkSeUI,114
|
|
157
|
-
openhands/sdk/workspace/remote/async_remote_workspace.py,sha256=
|
|
158
|
-
openhands/sdk/workspace/remote/base.py,sha256=
|
|
158
|
+
openhands/sdk/workspace/remote/async_remote_workspace.py,sha256=ftv1Vdx4mmM3AjygJpemMJGvhaQel7ORxdQVk12z4ZE,5061
|
|
159
|
+
openhands/sdk/workspace/remote/base.py,sha256=72C9MZV7ch5n6oHNvFMo6irW7b6Le8n4gk3yFuc0798,5605
|
|
159
160
|
openhands/sdk/workspace/remote/remote_workspace_mixin.py,sha256=CzHfnLUIra5sgPkP9kcggb1vHGOPpYQzLsHvGO2rRt0,10963
|
|
160
|
-
openhands_sdk-1.
|
|
161
|
-
openhands_sdk-1.
|
|
162
|
-
openhands_sdk-1.
|
|
163
|
-
openhands_sdk-1.
|
|
161
|
+
openhands_sdk-1.4.1.dist-info/METADATA,sha256=9K8ieOX7Xf0snpoeuCY9KmXkd5GTHkg_3EpgTT6vGLo,545
|
|
162
|
+
openhands_sdk-1.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
163
|
+
openhands_sdk-1.4.1.dist-info/top_level.txt,sha256=jHgVu9I0Blam8BXFgedoGKfglPF8XvW1TsJFIjcgP4E,10
|
|
164
|
+
openhands_sdk-1.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|