openhands-sdk 1.10.0__py3-none-any.whl → 1.11.0__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/agent/agent.py +60 -27
- openhands/sdk/agent/base.py +1 -1
- openhands/sdk/context/condenser/base.py +36 -3
- openhands/sdk/context/condenser/llm_summarizing_condenser.py +65 -1
- openhands/sdk/context/prompts/templates/system_message_suffix.j2 +2 -1
- openhands/sdk/context/skills/skill.py +2 -25
- openhands/sdk/conversation/conversation.py +5 -0
- openhands/sdk/conversation/impl/local_conversation.py +19 -13
- openhands/sdk/conversation/impl/remote_conversation.py +10 -0
- openhands/sdk/conversation/stuck_detector.py +18 -9
- openhands/sdk/llm/__init__.py +16 -0
- openhands/sdk/llm/auth/__init__.py +28 -0
- openhands/sdk/llm/auth/credentials.py +157 -0
- openhands/sdk/llm/auth/openai.py +762 -0
- openhands/sdk/llm/llm.py +175 -20
- openhands/sdk/llm/options/responses_options.py +8 -7
- openhands/sdk/llm/utils/model_features.py +2 -0
- openhands/sdk/secret/secrets.py +13 -1
- openhands/sdk/workspace/remote/base.py +8 -3
- openhands/sdk/workspace/remote/remote_workspace_mixin.py +40 -7
- {openhands_sdk-1.10.0.dist-info → openhands_sdk-1.11.0.dist-info}/METADATA +1 -1
- {openhands_sdk-1.10.0.dist-info → openhands_sdk-1.11.0.dist-info}/RECORD +24 -21
- {openhands_sdk-1.10.0.dist-info → openhands_sdk-1.11.0.dist-info}/WHEEL +0 -0
- {openhands_sdk-1.10.0.dist-info → openhands_sdk-1.11.0.dist-info}/top_level.txt +0 -0
openhands/sdk/llm/llm.py
CHANGED
|
@@ -27,8 +27,11 @@ from openhands.sdk.utils.pydantic_secrets import serialize_secret, validate_secr
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
if TYPE_CHECKING: # type hints only, avoid runtime import cycle
|
|
30
|
+
from openhands.sdk.llm.auth import SupportedVendor
|
|
30
31
|
from openhands.sdk.tool.tool import ToolDefinition
|
|
31
32
|
|
|
33
|
+
from openhands.sdk.llm.auth.openai import transform_for_subscription
|
|
34
|
+
|
|
32
35
|
|
|
33
36
|
with warnings.catch_warnings():
|
|
34
37
|
warnings.simplefilter("ignore")
|
|
@@ -50,8 +53,20 @@ from litellm.exceptions import (
|
|
|
50
53
|
Timeout as LiteLLMTimeout,
|
|
51
54
|
)
|
|
52
55
|
from litellm.responses.main import responses as litellm_responses
|
|
53
|
-
from litellm.
|
|
54
|
-
from litellm.types.
|
|
56
|
+
from litellm.responses.streaming_iterator import SyncResponsesAPIStreamingIterator
|
|
57
|
+
from litellm.types.llms.openai import (
|
|
58
|
+
OutputTextDeltaEvent,
|
|
59
|
+
ReasoningSummaryTextDeltaEvent,
|
|
60
|
+
RefusalDeltaEvent,
|
|
61
|
+
ResponseCompletedEvent,
|
|
62
|
+
ResponsesAPIResponse,
|
|
63
|
+
)
|
|
64
|
+
from litellm.types.utils import (
|
|
65
|
+
Delta,
|
|
66
|
+
ModelResponse,
|
|
67
|
+
ModelResponseStream,
|
|
68
|
+
StreamingChoices,
|
|
69
|
+
)
|
|
55
70
|
from litellm.utils import (
|
|
56
71
|
create_pretrained_tokenizer,
|
|
57
72
|
supports_vision,
|
|
@@ -335,6 +350,7 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
335
350
|
_model_info: Any = PrivateAttr(default=None)
|
|
336
351
|
_tokenizer: Any = PrivateAttr(default=None)
|
|
337
352
|
_telemetry: Telemetry | None = PrivateAttr(default=None)
|
|
353
|
+
_is_subscription: bool = PrivateAttr(default=False)
|
|
338
354
|
|
|
339
355
|
model_config: ClassVar[ConfigDict] = ConfigDict(
|
|
340
356
|
extra="ignore", arbitrary_types_allowed=True
|
|
@@ -499,6 +515,19 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
499
515
|
)
|
|
500
516
|
return self._telemetry
|
|
501
517
|
|
|
518
|
+
@property
|
|
519
|
+
def is_subscription(self) -> bool:
|
|
520
|
+
"""Check if this LLM uses subscription-based authentication.
|
|
521
|
+
|
|
522
|
+
Returns True when the LLM was created via `LLM.subscription_login()`,
|
|
523
|
+
which uses the ChatGPT subscription Codex backend rather than the
|
|
524
|
+
standard OpenAI API.
|
|
525
|
+
|
|
526
|
+
Returns:
|
|
527
|
+
bool: True if using subscription-based transport, False otherwise.
|
|
528
|
+
"""
|
|
529
|
+
return self._is_subscription
|
|
530
|
+
|
|
502
531
|
def restore_metrics(self, metrics: Metrics) -> None:
|
|
503
532
|
# Only used by ConversationStats to seed metrics
|
|
504
533
|
self._metrics = metrics
|
|
@@ -662,7 +691,7 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
662
691
|
raise
|
|
663
692
|
|
|
664
693
|
# =========================================================================
|
|
665
|
-
# Responses API (
|
|
694
|
+
# Responses API (v1)
|
|
666
695
|
# =========================================================================
|
|
667
696
|
def responses(
|
|
668
697
|
self,
|
|
@@ -686,16 +715,19 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
686
715
|
store: Whether to store the conversation
|
|
687
716
|
_return_metrics: Whether to return usage metrics
|
|
688
717
|
add_security_risk_prediction: Add security_risk field to tool schemas
|
|
689
|
-
on_token: Optional callback for streaming
|
|
718
|
+
on_token: Optional callback for streaming deltas
|
|
690
719
|
**kwargs: Additional arguments passed to the API
|
|
691
720
|
|
|
692
721
|
Note:
|
|
693
722
|
Summary field is always added to tool schemas for transparency and
|
|
694
723
|
explainability of agent actions.
|
|
695
724
|
"""
|
|
696
|
-
|
|
697
|
-
if
|
|
698
|
-
|
|
725
|
+
user_enable_streaming = bool(kwargs.get("stream", False)) or self.stream
|
|
726
|
+
if user_enable_streaming:
|
|
727
|
+
if on_token is None and not self.is_subscription:
|
|
728
|
+
# We allow on_token to be None for subscription mode
|
|
729
|
+
raise ValueError("Streaming requires an on_token callback")
|
|
730
|
+
kwargs["stream"] = True
|
|
699
731
|
|
|
700
732
|
# Build instructions + input list using dedicated Responses formatter
|
|
701
733
|
instructions, input_items = self.format_messages_for_responses(messages)
|
|
@@ -771,12 +803,67 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
771
803
|
seed=self.seed,
|
|
772
804
|
**final_kwargs,
|
|
773
805
|
)
|
|
774
|
-
|
|
806
|
+
if isinstance(ret, ResponsesAPIResponse):
|
|
807
|
+
if user_enable_streaming:
|
|
808
|
+
logger.warning(
|
|
809
|
+
"Responses streaming was requested, but the provider "
|
|
810
|
+
"returned a non-streaming response; no on_token deltas "
|
|
811
|
+
"will be emitted."
|
|
812
|
+
)
|
|
813
|
+
self._telemetry.on_response(ret)
|
|
814
|
+
return ret
|
|
815
|
+
|
|
816
|
+
# When stream=True, LiteLLM returns a streaming iterator rather than
|
|
817
|
+
# a single ResponsesAPIResponse. Drain the iterator and use the
|
|
818
|
+
# completed response.
|
|
819
|
+
if final_kwargs.get("stream", False):
|
|
820
|
+
if not isinstance(ret, SyncResponsesAPIStreamingIterator):
|
|
821
|
+
raise AssertionError(
|
|
822
|
+
f"Expected Responses stream iterator, got {type(ret)}"
|
|
823
|
+
)
|
|
824
|
+
|
|
825
|
+
stream_callback = on_token if user_enable_streaming else None
|
|
826
|
+
for event in ret:
|
|
827
|
+
if stream_callback is None:
|
|
828
|
+
continue
|
|
829
|
+
if isinstance(
|
|
830
|
+
event,
|
|
831
|
+
(
|
|
832
|
+
OutputTextDeltaEvent,
|
|
833
|
+
RefusalDeltaEvent,
|
|
834
|
+
ReasoningSummaryTextDeltaEvent,
|
|
835
|
+
),
|
|
836
|
+
):
|
|
837
|
+
delta = event.delta
|
|
838
|
+
if delta:
|
|
839
|
+
stream_callback(
|
|
840
|
+
ModelResponseStream(
|
|
841
|
+
choices=[
|
|
842
|
+
StreamingChoices(
|
|
843
|
+
delta=Delta(content=delta)
|
|
844
|
+
)
|
|
845
|
+
]
|
|
846
|
+
)
|
|
847
|
+
)
|
|
848
|
+
|
|
849
|
+
completed_event = ret.completed_response
|
|
850
|
+
if completed_event is None:
|
|
851
|
+
raise LLMNoResponseError(
|
|
852
|
+
"Responses stream finished without a completed response"
|
|
853
|
+
)
|
|
854
|
+
if not isinstance(completed_event, ResponseCompletedEvent):
|
|
855
|
+
raise LLMNoResponseError(
|
|
856
|
+
f"Unexpected completed event: {type(completed_event)}"
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
completed_resp = completed_event.response
|
|
860
|
+
|
|
861
|
+
self._telemetry.on_response(completed_resp)
|
|
862
|
+
return completed_resp
|
|
863
|
+
|
|
864
|
+
raise AssertionError(
|
|
775
865
|
f"Expected ResponsesAPIResponse, got {type(ret)}"
|
|
776
866
|
)
|
|
777
|
-
# telemetry (latency, cost). Token usage mapping we handle after.
|
|
778
|
-
self._telemetry.on_response(ret)
|
|
779
|
-
return ret
|
|
780
867
|
|
|
781
868
|
try:
|
|
782
869
|
resp: ResponsesAPIResponse = _one_attempt()
|
|
@@ -1046,8 +1133,9 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
1046
1133
|
|
|
1047
1134
|
- Skips prompt caching flags and string serializer concerns
|
|
1048
1135
|
- Uses Message.to_responses_value to get either instructions (system)
|
|
1049
|
-
|
|
1136
|
+
or input items (others)
|
|
1050
1137
|
- Concatenates system instructions into a single instructions string
|
|
1138
|
+
- For subscription mode, system prompts are prepended to user content
|
|
1051
1139
|
"""
|
|
1052
1140
|
msgs = copy.deepcopy(messages)
|
|
1053
1141
|
|
|
@@ -1057,18 +1145,26 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
1057
1145
|
# Assign system instructions as a string, collect input items
|
|
1058
1146
|
instructions: str | None = None
|
|
1059
1147
|
input_items: list[dict[str, Any]] = []
|
|
1148
|
+
system_chunks: list[str] = []
|
|
1149
|
+
|
|
1060
1150
|
for m in msgs:
|
|
1061
1151
|
val = m.to_responses_value(vision_enabled=vision_active)
|
|
1062
1152
|
if isinstance(val, str):
|
|
1063
1153
|
s = val.strip()
|
|
1064
|
-
if
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1154
|
+
if s:
|
|
1155
|
+
if self.is_subscription:
|
|
1156
|
+
system_chunks.append(s)
|
|
1157
|
+
else:
|
|
1158
|
+
instructions = (
|
|
1159
|
+
s
|
|
1160
|
+
if instructions is None
|
|
1161
|
+
else f"{instructions}\n\n---\n\n{s}"
|
|
1162
|
+
)
|
|
1163
|
+
elif val:
|
|
1164
|
+
input_items.extend(val)
|
|
1165
|
+
|
|
1166
|
+
if self.is_subscription:
|
|
1167
|
+
return transform_for_subscription(system_chunks, input_items)
|
|
1072
1168
|
return instructions, input_items
|
|
1073
1169
|
|
|
1074
1170
|
def get_token_count(self, messages: list[Message]) -> int:
|
|
@@ -1159,3 +1255,62 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
1159
1255
|
if v is not None:
|
|
1160
1256
|
data[field_name] = v
|
|
1161
1257
|
return cls(**data)
|
|
1258
|
+
|
|
1259
|
+
@classmethod
|
|
1260
|
+
def subscription_login(
|
|
1261
|
+
cls,
|
|
1262
|
+
vendor: SupportedVendor,
|
|
1263
|
+
model: str,
|
|
1264
|
+
force_login: bool = False,
|
|
1265
|
+
open_browser: bool = True,
|
|
1266
|
+
**llm_kwargs,
|
|
1267
|
+
) -> LLM:
|
|
1268
|
+
"""Authenticate with a subscription service and return an LLM instance.
|
|
1269
|
+
|
|
1270
|
+
This method provides subscription-based access to LLM models that are
|
|
1271
|
+
available through chat subscriptions (e.g., ChatGPT Plus/Pro) rather
|
|
1272
|
+
than API credits. It handles credential caching, token refresh, and
|
|
1273
|
+
the OAuth login flow.
|
|
1274
|
+
|
|
1275
|
+
Currently supported vendors:
|
|
1276
|
+
- "openai": ChatGPT Plus/Pro subscription for Codex models
|
|
1277
|
+
|
|
1278
|
+
Supported OpenAI models:
|
|
1279
|
+
- gpt-5.1-codex-max
|
|
1280
|
+
- gpt-5.1-codex-mini
|
|
1281
|
+
- gpt-5.2
|
|
1282
|
+
- gpt-5.2-codex
|
|
1283
|
+
|
|
1284
|
+
Args:
|
|
1285
|
+
vendor: The vendor/provider. Currently only "openai" is supported.
|
|
1286
|
+
model: The model to use. Must be supported by the vendor's
|
|
1287
|
+
subscription service.
|
|
1288
|
+
force_login: If True, always perform a fresh login even if valid
|
|
1289
|
+
credentials exist.
|
|
1290
|
+
open_browser: Whether to automatically open the browser for the
|
|
1291
|
+
OAuth login flow.
|
|
1292
|
+
**llm_kwargs: Additional arguments to pass to the LLM constructor.
|
|
1293
|
+
|
|
1294
|
+
Returns:
|
|
1295
|
+
An LLM instance configured for subscription-based access.
|
|
1296
|
+
|
|
1297
|
+
Raises:
|
|
1298
|
+
ValueError: If the vendor or model is not supported.
|
|
1299
|
+
RuntimeError: If authentication fails.
|
|
1300
|
+
|
|
1301
|
+
Example:
|
|
1302
|
+
>>> from openhands.sdk import LLM
|
|
1303
|
+
>>> # First time: opens browser for OAuth login
|
|
1304
|
+
>>> llm = LLM.subscription_login(vendor="openai", model="gpt-5.2-codex")
|
|
1305
|
+
>>> # Subsequent calls: reuses cached credentials
|
|
1306
|
+
>>> llm = LLM.subscription_login(vendor="openai", model="gpt-5.2-codex")
|
|
1307
|
+
"""
|
|
1308
|
+
from openhands.sdk.llm.auth.openai import subscription_login
|
|
1309
|
+
|
|
1310
|
+
return subscription_login(
|
|
1311
|
+
vendor=vendor,
|
|
1312
|
+
model=model,
|
|
1313
|
+
force_login=force_login,
|
|
1314
|
+
open_browser=open_browser,
|
|
1315
|
+
**llm_kwargs,
|
|
1316
|
+
)
|
|
@@ -15,15 +15,16 @@ def select_responses_options(
|
|
|
15
15
|
) -> dict[str, Any]:
|
|
16
16
|
"""Behavior-preserving extraction of _normalize_responses_kwargs."""
|
|
17
17
|
# Apply defaults for keys that are not forced by policy
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
)
|
|
18
|
+
# Note: max_output_tokens is not supported in subscription mode
|
|
19
|
+
defaults = {}
|
|
20
|
+
if not llm.is_subscription:
|
|
21
|
+
defaults["max_output_tokens"] = llm.max_output_tokens
|
|
22
|
+
out = apply_defaults_if_absent(user_kwargs, defaults)
|
|
24
23
|
|
|
25
24
|
# Enforce sampling/tool behavior for Responses path
|
|
26
|
-
|
|
25
|
+
# Note: temperature is not supported in subscription mode
|
|
26
|
+
if not llm.is_subscription:
|
|
27
|
+
out["temperature"] = 1.0
|
|
27
28
|
out["tool_choice"] = "auto"
|
|
28
29
|
|
|
29
30
|
# If user didn't set extra_headers, propagate from llm config
|
|
@@ -155,6 +155,7 @@ FORCE_STRING_SERIALIZER_MODELS: list[str] = [
|
|
|
155
155
|
# in the message input
|
|
156
156
|
SEND_REASONING_CONTENT_MODELS: list[str] = [
|
|
157
157
|
"kimi-k2-thinking",
|
|
158
|
+
"kimi-k2.5",
|
|
158
159
|
"openrouter/minimax-m2", # MiniMax-M2 via OpenRouter (interleaved thinking)
|
|
159
160
|
"deepseek/deepseek-reasoner",
|
|
160
161
|
]
|
|
@@ -181,6 +182,7 @@ def get_features(model: str) -> ModelFeatures:
|
|
|
181
182
|
# Each entry: (pattern, default_temperature)
|
|
182
183
|
DEFAULT_TEMPERATURE_MODELS: list[tuple[str, float]] = [
|
|
183
184
|
("kimi-k2-thinking", 1.0),
|
|
185
|
+
("kimi-k2.5", 1.0),
|
|
184
186
|
]
|
|
185
187
|
|
|
186
188
|
|
openhands/sdk/secret/secrets.py
CHANGED
|
@@ -92,7 +92,19 @@ class LookupSecret(SecretSource):
|
|
|
92
92
|
return result
|
|
93
93
|
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
# Patterns used for substring matching against header names (case-insensitive).
|
|
96
|
+
# Headers containing any of these patterns will be redacted during serialization.
|
|
97
|
+
# Examples: X-Access-Token, Cookie, Authorization, X-API-Key, X-API-Secret
|
|
98
|
+
_SECRET_HEADERS = [
|
|
99
|
+
"AUTHORIZATION",
|
|
100
|
+
"COOKIE",
|
|
101
|
+
"CREDENTIAL",
|
|
102
|
+
"KEY",
|
|
103
|
+
"PASSWORD",
|
|
104
|
+
"SECRET",
|
|
105
|
+
"SESSION",
|
|
106
|
+
"TOKEN",
|
|
107
|
+
]
|
|
96
108
|
|
|
97
109
|
|
|
98
110
|
def _is_secret_header(key: str):
|
|
@@ -50,12 +50,17 @@ class RemoteWorkspace(RemoteWorkspaceMixin, BaseWorkspace):
|
|
|
50
50
|
if client is None:
|
|
51
51
|
# Configure reasonable timeouts for HTTP requests
|
|
52
52
|
# - connect: 10 seconds to establish connection
|
|
53
|
-
# - read:
|
|
53
|
+
# - read: 600 seconds (10 minutes) to read response (for LLM operations)
|
|
54
54
|
# - write: 10 seconds to send request
|
|
55
55
|
# - pool: 10 seconds to get connection from pool
|
|
56
|
-
timeout = httpx.Timeout(
|
|
56
|
+
timeout = httpx.Timeout(
|
|
57
|
+
connect=10.0, read=self.read_timeout, write=10.0, pool=10.0
|
|
58
|
+
)
|
|
57
59
|
client = httpx.Client(
|
|
58
|
-
base_url=self.host,
|
|
60
|
+
base_url=self.host,
|
|
61
|
+
timeout=timeout,
|
|
62
|
+
headers=self._headers,
|
|
63
|
+
limits=httpx.Limits(max_connections=self.max_connections),
|
|
59
64
|
)
|
|
60
65
|
self._client = client
|
|
61
66
|
return client
|
|
@@ -25,6 +25,15 @@ class RemoteWorkspaceMixin(BaseModel):
|
|
|
25
25
|
working_dir: str = Field(
|
|
26
26
|
description="The working directory for agent operations and tool execution."
|
|
27
27
|
)
|
|
28
|
+
read_timeout: float = Field(
|
|
29
|
+
default=600.0,
|
|
30
|
+
description="Timeout in seconds for reading operations of httpx.Client.",
|
|
31
|
+
)
|
|
32
|
+
max_connections: int | None = Field(
|
|
33
|
+
default=None,
|
|
34
|
+
description="Maximum number of connections for httpx.Client. "
|
|
35
|
+
"None means no limit, useful for running many conversations in parallel.",
|
|
36
|
+
)
|
|
28
37
|
|
|
29
38
|
def model_post_init(self, context: Any) -> None:
|
|
30
39
|
# Set up remote host
|
|
@@ -87,26 +96,50 @@ class RemoteWorkspaceMixin(BaseModel):
|
|
|
87
96
|
stdout_parts = []
|
|
88
97
|
stderr_parts = []
|
|
89
98
|
exit_code = None
|
|
99
|
+
last_order = -1 # Track highest order seen to fetch only new events
|
|
100
|
+
seen_event_ids: set[str] = set() # Track seen IDs to detect duplicates
|
|
90
101
|
|
|
91
102
|
while time.time() - start_time < timeout:
|
|
92
|
-
# Search for
|
|
103
|
+
# Search for new events (order > last_order)
|
|
104
|
+
params: dict[str, str | int] = {
|
|
105
|
+
"command_id__eq": command_id,
|
|
106
|
+
"sort_order": "TIMESTAMP",
|
|
107
|
+
"limit": 100,
|
|
108
|
+
"kind__eq": "BashOutput",
|
|
109
|
+
}
|
|
110
|
+
if last_order >= 0:
|
|
111
|
+
params["order__gt"] = last_order
|
|
112
|
+
|
|
93
113
|
response = yield {
|
|
94
114
|
"method": "GET",
|
|
95
115
|
"url": f"{self.host}/api/bash/bash_events/search",
|
|
96
|
-
"params":
|
|
97
|
-
"command_id__eq": command_id,
|
|
98
|
-
"sort_order": "TIMESTAMP",
|
|
99
|
-
"limit": 100,
|
|
100
|
-
},
|
|
116
|
+
"params": params,
|
|
101
117
|
"headers": self._headers,
|
|
102
118
|
"timeout": timeout,
|
|
103
119
|
}
|
|
104
120
|
response.raise_for_status()
|
|
105
121
|
search_result = response.json()
|
|
106
122
|
|
|
107
|
-
#
|
|
123
|
+
# Process BashOutput events
|
|
108
124
|
for event in search_result.get("items", []):
|
|
109
125
|
if event.get("kind") == "BashOutput":
|
|
126
|
+
# Check for duplicates - safety check in case caller
|
|
127
|
+
# forgets to add kind__eq filter or API has a bug
|
|
128
|
+
event_id = event.get("id")
|
|
129
|
+
if event_id is not None:
|
|
130
|
+
if event_id in seen_event_ids:
|
|
131
|
+
raise RuntimeError(
|
|
132
|
+
f"Duplicate event received: {event_id}. "
|
|
133
|
+
"This should not happen with order__gt "
|
|
134
|
+
"filtering and kind filtering."
|
|
135
|
+
)
|
|
136
|
+
seen_event_ids.add(event_id)
|
|
137
|
+
|
|
138
|
+
# Track the highest order we've seen
|
|
139
|
+
event_order = event.get("order")
|
|
140
|
+
if event_order is not None and event_order > last_order:
|
|
141
|
+
last_order = event_order
|
|
142
|
+
|
|
110
143
|
if event.get("stdout"):
|
|
111
144
|
stdout_parts.append(event["stdout"])
|
|
112
145
|
if event.get("stderr"):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.11.0
|
|
4
4
|
Summary: OpenHands SDK - Core functionality for building AI agents
|
|
5
5
|
Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
|
|
6
6
|
Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
openhands/sdk/__init__.py,sha256=FNRcPyCbvwYGrbSCxox_UWIHK5JuyCLHCyPLPZRd5sA,2589
|
|
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=
|
|
4
|
+
openhands/sdk/agent/agent.py,sha256=VOlGmKyiZ6Dzd3LPMua4Vhd9YpY_ju9RDgaw7B5aZ1g,32610
|
|
5
|
+
openhands/sdk/agent/base.py,sha256=Fv_d0e9LhaiV-QylziCVdOmPGqec5wPaUFIq5XbI3S0,19314
|
|
6
6
|
openhands/sdk/agent/utils.py,sha256=AY_7VBFaJ2iorfh0MiS-mXaK6f8ONDejNcbkde_xTGg,8274
|
|
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
|
|
@@ -22,8 +22,8 @@ openhands/sdk/context/__init__.py,sha256=r2YtpZMVFIIaKYVh2yjicTXYPURGhIUUdbliaLY
|
|
|
22
22
|
openhands/sdk/context/agent_context.py,sha256=TUQB-RBmkKvnwMLKvG9eOc5TVlRnDtBOSeoXRA28JoA,12425
|
|
23
23
|
openhands/sdk/context/view.py,sha256=xSgwWiA7Omcz7bpXm_7-ANRlO590PjDAQvMggvr-sK0,19757
|
|
24
24
|
openhands/sdk/context/condenser/__init__.py,sha256=peHKPk51AujZhXvR2H0j1vBUJsCc8D6-OMHZ4Vk-pxU,568
|
|
25
|
-
openhands/sdk/context/condenser/base.py,sha256=
|
|
26
|
-
openhands/sdk/context/condenser/llm_summarizing_condenser.py,sha256=
|
|
25
|
+
openhands/sdk/context/condenser/base.py,sha256=4AO59S44ZeX7eRZLocgFXjmHZcs3_TwwQdTG_witWHs,7450
|
|
26
|
+
openhands/sdk/context/condenser/llm_summarizing_condenser.py,sha256=2-yAfUTgC-lh_Qy8HmdFzfWavQC2o4PjLJ-mBRBNpfM,12592
|
|
27
27
|
openhands/sdk/context/condenser/no_op_condenser.py,sha256=T87bTtJw4dqlOIZZZ4R_JFPXeSymDqlbsZtH6ng7N1E,474
|
|
28
28
|
openhands/sdk/context/condenser/pipeline_condenser.py,sha256=wkbEA6R8u8u3Wi1AQmx1AKF9hQ25dLSeKuvB56N1Ohc,2145
|
|
29
29
|
openhands/sdk/context/condenser/utils.py,sha256=kI4oechGeozHRTFPcq6UVbGgLL-6msR3D2-4fPssFVU,5599
|
|
@@ -32,16 +32,16 @@ openhands/sdk/context/prompts/__init__.py,sha256=wC1Qiak4RHY3YS0TuZRmhFGAJ7Vc6CL
|
|
|
32
32
|
openhands/sdk/context/prompts/prompt.py,sha256=8ZV9C0lg9Lnw7lF5zLnLTF9nL6gTnW5vGIw1YxtRQBU,3724
|
|
33
33
|
openhands/sdk/context/prompts/templates/ask_agent_template.j2,sha256=VRKWdF2VTJ_Tyway_Wexp8_KlNgAkME94eZelbbsEZI,212
|
|
34
34
|
openhands/sdk/context/prompts/templates/skill_knowledge_info.j2,sha256=boqEsAIszJ2Cy7Zc7UfFLbwSXgFybZ4mU8YCCW-zXYc,423
|
|
35
|
-
openhands/sdk/context/prompts/templates/system_message_suffix.j2,sha256=
|
|
35
|
+
openhands/sdk/context/prompts/templates/system_message_suffix.j2,sha256=MJYiCSm8gq--6HKt74baDTarexbG4vQdo6Ba1nvjSHA,2911
|
|
36
36
|
openhands/sdk/context/skills/__init__.py,sha256=dS6XJYLHlm_wboD-C42bRw4HCFu-o9dzCiFRpoyR1h0,938
|
|
37
37
|
openhands/sdk/context/skills/exceptions.py,sha256=tVBSbXTXG32nb1TebVAuzbNNHvn8GScpSM1bHCnQzvY,305
|
|
38
|
-
openhands/sdk/context/skills/skill.py,sha256=
|
|
38
|
+
openhands/sdk/context/skills/skill.py,sha256=rSXEW8VN0Fw4SDI1PU6JY3MiCT3EIv9AR2DUumLBguw,35348
|
|
39
39
|
openhands/sdk/context/skills/trigger.py,sha256=ZGaDmMpJghnAEuTTYX6UepsA5nX1CSz83zK1Ox46vMk,756
|
|
40
40
|
openhands/sdk/context/skills/types.py,sha256=LvyCveHBSt2-g9Lbpr_eQMvOd4eEBjJb3irAWL-OzE0,1813
|
|
41
41
|
openhands/sdk/context/skills/utils.py,sha256=kpJjVz_BQGjFrgO8QpBwTAqHbAU8spD6crOLI_ollac,11870
|
|
42
42
|
openhands/sdk/conversation/__init__.py,sha256=USxX0PTUI9ufA8CJ8iyDkIaKR5iFbDo3L9G5tBx3k94,1509
|
|
43
43
|
openhands/sdk/conversation/base.py,sha256=btRjqHk4FkAJjIAi34fgkNUml9R-0qfsDsJgomlN-kI,9143
|
|
44
|
-
openhands/sdk/conversation/conversation.py,sha256=
|
|
44
|
+
openhands/sdk/conversation/conversation.py,sha256=PPoR13vFo_9DeIyeW82cxGonDpoOvKjSsQi5OrqR4Zk,6660
|
|
45
45
|
openhands/sdk/conversation/conversation_stats.py,sha256=ZlQ99kgG5YVCrZ4rqJlq63JaiInxX8jqv-q5lS7RN68,3038
|
|
46
46
|
openhands/sdk/conversation/event_store.py,sha256=JZF6AibFezcIzEw4IQqKHG8T8s53SfD_LkZycsOH6xY,7992
|
|
47
47
|
openhands/sdk/conversation/events_list_base.py,sha256=n_YvgbhBPOPDbw4Kp68J0EKFM39vg95ng09GMfTz29s,505
|
|
@@ -52,12 +52,12 @@ openhands/sdk/conversation/response_utils.py,sha256=rPlC3cDSmoQte6NZ0kK6h6-9ho5c
|
|
|
52
52
|
openhands/sdk/conversation/secret_registry.py,sha256=6fY1zRxb55rC4uIMFcR0lDssIyyjaPh9pCWGqDikrek,4446
|
|
53
53
|
openhands/sdk/conversation/serialization_diff.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
54
|
openhands/sdk/conversation/state.py,sha256=VF3PD2THqgci6mA-meMaNGpB1KuCEW_4AOtHEIQ0flQ,17557
|
|
55
|
-
openhands/sdk/conversation/stuck_detector.py,sha256=
|
|
55
|
+
openhands/sdk/conversation/stuck_detector.py,sha256=p8DljC-YwAVpAUTUqcLy2CY1hTSdpKzN9f6iKJbxs8I,12185
|
|
56
56
|
openhands/sdk/conversation/title_utils.py,sha256=j40-dP-Oes-mhU2xUC7fCC8cB0wkMdbbDJU7WLHiVIo,7063
|
|
57
57
|
openhands/sdk/conversation/types.py,sha256=q_yc3VNc8r3cdmvPXzpj7HvdLeDqv-37hCgOWMU65a4,1507
|
|
58
58
|
openhands/sdk/conversation/impl/__init__.py,sha256=DmDFyNR4RU8eiMocKf2j9eBQomipP-rrJgU1LoVWTDA,220
|
|
59
|
-
openhands/sdk/conversation/impl/local_conversation.py,sha256=
|
|
60
|
-
openhands/sdk/conversation/impl/remote_conversation.py,sha256=
|
|
59
|
+
openhands/sdk/conversation/impl/local_conversation.py,sha256=p7ETqzDhwxdW076TCOKHcxZuEDP7NrbERkSJSG2tZZg,37183
|
|
60
|
+
openhands/sdk/conversation/impl/remote_conversation.py,sha256=g5cQu0WCIWHjAJz3Zp0cMtItVCd81mfLpPgvnbLdyrU,43960
|
|
61
61
|
openhands/sdk/conversation/visualizer/__init__.py,sha256=0LXpKlt2eJcrqP1z6jQP_nLx23V8ErnQkKYSxvUp0_A,275
|
|
62
62
|
openhands/sdk/conversation/visualizer/base.py,sha256=oMg-JvQc34ebYdC3J9itHraoB2u3MdQ6E77AKiTmu30,3198
|
|
63
63
|
openhands/sdk/conversation/visualizer/default.py,sha256=k-3-l1j8H_EOEn_pfzsUczcc4JDhQni7AUQZgZ2TpB4,11885
|
|
@@ -104,12 +104,15 @@ openhands/sdk/io/base.py,sha256=m-tMq3Em4DOd17cP0T6-gc9c_7FE-HakcPLScGGycVE,2958
|
|
|
104
104
|
openhands/sdk/io/cache.py,sha256=TZD9Px-WK303WGjm5nin9n3TCKGiAJ-PIqrM5MFeg5c,2908
|
|
105
105
|
openhands/sdk/io/local.py,sha256=5ZibVqYj7hV5gpgYr2SKq2aSsAL1m2JykhtmRyVKyf8,5189
|
|
106
106
|
openhands/sdk/io/memory.py,sha256=_GQ8WqilLJgdxtRccfVS653VDy1bo6CyZJDHGUQXEm8,2793
|
|
107
|
-
openhands/sdk/llm/__init__.py,sha256=
|
|
108
|
-
openhands/sdk/llm/llm.py,sha256=
|
|
107
|
+
openhands/sdk/llm/__init__.py,sha256=t7kEGnNq4eYREz5h2n1Pizte8glUnruHTt_G_6DguYk,1525
|
|
108
|
+
openhands/sdk/llm/llm.py,sha256=rQpiHy-clgGqN9fms4RaSIkJ7nV5HdXvgMCq95di89Y,52102
|
|
109
109
|
openhands/sdk/llm/llm_registry.py,sha256=DL9yqSbAM7OBkzdIChLuxG2qk_oElW2tC2xem6mq0F8,3530
|
|
110
110
|
openhands/sdk/llm/llm_response.py,sha256=DaBVBkij4Sz-RsYhRb3UUcvJCTzCBcOYQ9IhFwN4ukI,1988
|
|
111
111
|
openhands/sdk/llm/message.py,sha256=YK--c42j6Pb7wjUrPeIfiRRIt0w0pmJMHvGIZC3ugO4,27085
|
|
112
112
|
openhands/sdk/llm/streaming.py,sha256=tFJ7B0AjJ-e8Xv13DTtc2FdrsLRUCG8wxQex8fDlOp4,214
|
|
113
|
+
openhands/sdk/llm/auth/__init__.py,sha256=GDGU9D6a-o6bXSlVC8NUMxViXJfmEr55HMRosog9F_k,698
|
|
114
|
+
openhands/sdk/llm/auth/credentials.py,sha256=uPWqBCd26snW5Afuinzxy6I-HlG38qXZ9o_hD5iKc64,5167
|
|
115
|
+
openhands/sdk/llm/auth/openai.py,sha256=TqXKl9MdmaI_j3RP-3ayYRnudlOmsafrpfRPVj8go28,26228
|
|
113
116
|
openhands/sdk/llm/exceptions/__init__.py,sha256=6iMJah2nS6BboU06HqgAM2JT6aykCWY8muoUwaaJpR8,1144
|
|
114
117
|
openhands/sdk/llm/exceptions/classifier.py,sha256=pu5fVNubUrB3eXV1i5W7m4-D4Ik2Z-fGe1ba2t0SSc4,1456
|
|
115
118
|
openhands/sdk/llm/exceptions/mapping.py,sha256=D68duh85HPONw5P0Mv4e8Ji-ubi6qWEb2d9FkOKf8cA,1656
|
|
@@ -119,13 +122,13 @@ openhands/sdk/llm/mixins/non_native_fc.py,sha256=KL2-rCh9uNz5WDskP7rc2uv1xOzZWgv
|
|
|
119
122
|
openhands/sdk/llm/options/__init__.py,sha256=EntvOWC5kwDoTMXXMkYuoWMQ13hD8YtC9CEMCtnKj7o,54
|
|
120
123
|
openhands/sdk/llm/options/chat_options.py,sha256=9xB4I4DXBFirM0ZCrc8-KwW5eD07IAbYHglkJjTczuE,3784
|
|
121
124
|
openhands/sdk/llm/options/common.py,sha256=qFcPuZF_c4rmH1bgGG8Qp6TJ4YWpv9IFzfLZRRiik9M,580
|
|
122
|
-
openhands/sdk/llm/options/responses_options.py,sha256=
|
|
125
|
+
openhands/sdk/llm/options/responses_options.py,sha256=EYdzhBEJ7H722yNBOaiBz_slfaxgq3nnDSYqBg-CFRM,2498
|
|
123
126
|
openhands/sdk/llm/router/__init__.py,sha256=N8qldpGdLLCWZzn5Rz2y8AheSoCTQLGkLOBDCFNMJRA,261
|
|
124
127
|
openhands/sdk/llm/router/base.py,sha256=izGHV17UctI70t2OjhuLgVAV7gGZ51vbZ4WpWJxblJA,4678
|
|
125
128
|
openhands/sdk/llm/router/impl/multimodal.py,sha256=uKFVm7b3Jr0xCC1lvP-cVn-fIkTv24-pK3xKlOJahz4,3034
|
|
126
129
|
openhands/sdk/llm/router/impl/random.py,sha256=oBHoFTBMa9OeDyg-rV4siLCkN6rKYL0uDlZMEseB3ro,656
|
|
127
130
|
openhands/sdk/llm/utils/metrics.py,sha256=4zD0Hkc9Oc4qcDcVZUX13RyggyObshUbz4Ik9W1uIw4,11592
|
|
128
|
-
openhands/sdk/llm/utils/model_features.py,sha256=
|
|
131
|
+
openhands/sdk/llm/utils/model_features.py,sha256=SDEAn4YCYFkw4QTQmtiOyKIw9AI895nrhzYBxFfaP3g,6336
|
|
129
132
|
openhands/sdk/llm/utils/model_info.py,sha256=1mFYA7OcEyUB6k1doao8_w1XT7UMM_DAm57HcTpKkLw,2628
|
|
130
133
|
openhands/sdk/llm/utils/model_prompt_spec.py,sha256=onw9-y7x0aJS8IOjNzeqhdvcFNwK1l_s0XgurWlnj5o,2587
|
|
131
134
|
openhands/sdk/llm/utils/retry_mixin.py,sha256=M-hXp8EwP1FjNN6tgHiv133BtUQgRr9Kz_ZWxeAJLGA,4765
|
|
@@ -150,7 +153,7 @@ openhands/sdk/plugin/loader.py,sha256=PcrCOn5WH_d5cf_qXEDopD2dLx7K0i3sErGuN_Xl35
|
|
|
150
153
|
openhands/sdk/plugin/plugin.py,sha256=_QrrLl6xjHxcF31jnsohjb6R3Sb6Q8IZwblSdzY8sPE,17851
|
|
151
154
|
openhands/sdk/plugin/types.py,sha256=hcpLaYz-sTJ7Dby1_BwTdm3Uq-UnIlHWHacjT0nXXyo,28581
|
|
152
155
|
openhands/sdk/secret/__init__.py,sha256=Y-M2WPfYLfVYZSFZGTf6MDOnjsL5SayETtA4t9X0gjw,348
|
|
153
|
-
openhands/sdk/secret/secrets.py,sha256=
|
|
156
|
+
openhands/sdk/secret/secrets.py,sha256=e9ClIvP7sqxOeaazUjGSzzof1HCxzRwoJikYJUhtRQY,3609
|
|
154
157
|
openhands/sdk/security/__init__.py,sha256=x2-a0fsImxSM2msHl7gV0fENWyZbpY7-HW_CBwNrPZY,89
|
|
155
158
|
openhands/sdk/security/analyzer.py,sha256=V6Fowh83sfEr1SdwK3UW1PNQHahHxwHfla0KndTombk,3994
|
|
156
159
|
openhands/sdk/security/confirmation_policy.py,sha256=bsrIOfo3QOXNyKrmUVzQz070F3xbJ7uZp9WTr4RMdl8,2145
|
|
@@ -185,9 +188,9 @@ openhands/sdk/workspace/models.py,sha256=ORFn70kXvD0urjeq59d_XqXBYSHbDYkBMg9KYQK
|
|
|
185
188
|
openhands/sdk/workspace/workspace.py,sha256=Of1r2z3W_pXSgV8u10sUQ87ChtaA5khD6HJ_PtJz6aY,1296
|
|
186
189
|
openhands/sdk/workspace/remote/__init__.py,sha256=eKkj6NOESMUBGDVC6_L2Wfuc4K6G-mpnJDNHKBkSeUI,114
|
|
187
190
|
openhands/sdk/workspace/remote/async_remote_workspace.py,sha256=MfnYoXvx_tZ7MKDGJCofnkYAJxfBKqNtM2Qprx3QQRk,5608
|
|
188
|
-
openhands/sdk/workspace/remote/base.py,sha256=
|
|
189
|
-
openhands/sdk/workspace/remote/remote_workspace_mixin.py,sha256=
|
|
190
|
-
openhands_sdk-1.
|
|
191
|
-
openhands_sdk-1.
|
|
192
|
-
openhands_sdk-1.
|
|
193
|
-
openhands_sdk-1.
|
|
191
|
+
openhands/sdk/workspace/remote/base.py,sha256=5xqhJf_Hi3kMdNfL4u0XScnVShTRUSeMtvIAemR6Q3A,6317
|
|
192
|
+
openhands/sdk/workspace/remote/remote_workspace_mixin.py,sha256=29Mwe6lVZ-annN0lThRY2cM-FrqXhLm1uT_4j72e7fw,12600
|
|
193
|
+
openhands_sdk-1.11.0.dist-info/METADATA,sha256=L9sD54xo2JltGA6PFXQtMuJRpOZkTxPpoTH4HRa3u44,859
|
|
194
|
+
openhands_sdk-1.11.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
195
|
+
openhands_sdk-1.11.0.dist-info/top_level.txt,sha256=jHgVu9I0Blam8BXFgedoGKfglPF8XvW1TsJFIjcgP4E,10
|
|
196
|
+
openhands_sdk-1.11.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|