openhands-sdk 1.5.0__py3-none-any.whl → 1.5.2__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/base.py +25 -6
- openhands/sdk/conversation/base.py +17 -0
- openhands/sdk/conversation/impl/local_conversation.py +50 -0
- openhands/sdk/conversation/impl/remote_conversation.py +15 -0
- openhands/sdk/llm/utils/verified_models.py +3 -0
- openhands/sdk/tool/tool.py +2 -2
- {openhands_sdk-1.5.0.dist-info → openhands_sdk-1.5.2.dist-info}/METADATA +1 -1
- {openhands_sdk-1.5.0.dist-info → openhands_sdk-1.5.2.dist-info}/RECORD +10 -10
- {openhands_sdk-1.5.0.dist-info → openhands_sdk-1.5.2.dist-info}/WHEEL +0 -0
- {openhands_sdk-1.5.0.dist-info → openhands_sdk-1.5.2.dist-info}/top_level.txt +0 -0
openhands/sdk/agent/base.py
CHANGED
|
@@ -3,6 +3,7 @@ import re
|
|
|
3
3
|
import sys
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
5
|
from collections.abc import Generator, Iterable
|
|
6
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
6
7
|
from typing import TYPE_CHECKING, Any
|
|
7
8
|
|
|
8
9
|
from pydantic import BaseModel, ConfigDict, Field, PrivateAttr
|
|
@@ -196,13 +197,25 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
196
197
|
return
|
|
197
198
|
|
|
198
199
|
tools: list[ToolDefinition] = []
|
|
199
|
-
for tool_spec in self.tools:
|
|
200
|
-
tools.extend(resolve_tool(tool_spec, state))
|
|
201
200
|
|
|
202
|
-
#
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
201
|
+
# Use ThreadPoolExecutor to parallelize tool resolution
|
|
202
|
+
with ThreadPoolExecutor(max_workers=4) as executor:
|
|
203
|
+
futures = []
|
|
204
|
+
|
|
205
|
+
# Submit tool resolution tasks
|
|
206
|
+
for tool_spec in self.tools:
|
|
207
|
+
future = executor.submit(resolve_tool, tool_spec, state)
|
|
208
|
+
futures.append(future)
|
|
209
|
+
|
|
210
|
+
# Submit MCP tools creation if configured
|
|
211
|
+
if self.mcp_config:
|
|
212
|
+
future = executor.submit(create_mcp_tools, self.mcp_config, 30)
|
|
213
|
+
futures.append(future)
|
|
214
|
+
|
|
215
|
+
# Collect results as they complete
|
|
216
|
+
for future in futures:
|
|
217
|
+
result = future.result()
|
|
218
|
+
tools.extend(result)
|
|
206
219
|
|
|
207
220
|
logger.info(
|
|
208
221
|
f"Loaded {len(tools)} tools from spec: {[tool.name for tool in tools]}"
|
|
@@ -292,6 +305,12 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
292
305
|
)
|
|
293
306
|
updates["condenser"] = new_condenser
|
|
294
307
|
|
|
308
|
+
# Reconcile agent_context - always use the current environment's agent_context
|
|
309
|
+
# This allows resuming conversations from different directories and handles
|
|
310
|
+
# cases where skills, working directory, or other context has changed
|
|
311
|
+
if self.agent_context is not None:
|
|
312
|
+
updates["agent_context"] = self.agent_context
|
|
313
|
+
|
|
295
314
|
# Create maps by tool name for easy lookup
|
|
296
315
|
runtime_tools_map = {tool.name: tool for tool in self.tools}
|
|
297
316
|
persisted_tools_map = {tool.name: tool for tool in persisted.tools}
|
|
@@ -245,6 +245,23 @@ class BaseConversation(ABC):
|
|
|
245
245
|
"""
|
|
246
246
|
...
|
|
247
247
|
|
|
248
|
+
@abstractmethod
|
|
249
|
+
def condense(self) -> None:
|
|
250
|
+
"""Force condensation of the conversation history.
|
|
251
|
+
|
|
252
|
+
This method uses the existing condensation request pattern to trigger
|
|
253
|
+
condensation. It adds a CondensationRequest event to the conversation
|
|
254
|
+
and forces the agent to take a single step to process it.
|
|
255
|
+
|
|
256
|
+
The condensation will be applied immediately and will modify the conversation
|
|
257
|
+
state by adding a condensation event to the history.
|
|
258
|
+
|
|
259
|
+
Raises:
|
|
260
|
+
ValueError: If no condenser is configured or the condenser doesn't
|
|
261
|
+
handle condensation requests.
|
|
262
|
+
"""
|
|
263
|
+
...
|
|
264
|
+
|
|
248
265
|
@staticmethod
|
|
249
266
|
def compose_callbacks(callbacks: Iterable[CallbackType]) -> CallbackType:
|
|
250
267
|
"""Compose multiple callbacks into a single callback function.
|
|
@@ -24,6 +24,7 @@ from openhands.sdk.conversation.visualizer import (
|
|
|
24
24
|
DefaultConversationVisualizer,
|
|
25
25
|
)
|
|
26
26
|
from openhands.sdk.event import (
|
|
27
|
+
CondensationRequest,
|
|
27
28
|
MessageEvent,
|
|
28
29
|
PauseEvent,
|
|
29
30
|
UserRejectObservation,
|
|
@@ -540,6 +541,55 @@ class LocalConversation(BaseConversation):
|
|
|
540
541
|
events=self._state.events, llm=llm_to_use, max_length=max_length
|
|
541
542
|
)
|
|
542
543
|
|
|
544
|
+
def condense(self) -> None:
|
|
545
|
+
"""Synchronously force condense the conversation history.
|
|
546
|
+
|
|
547
|
+
If the agent is currently running, `condense()` will wait for the
|
|
548
|
+
ongoing step to finish before proceeding.
|
|
549
|
+
|
|
550
|
+
Raises ValueError if no compatible condenser exists.
|
|
551
|
+
"""
|
|
552
|
+
|
|
553
|
+
# Check if condenser is configured and handles condensation requests
|
|
554
|
+
if (
|
|
555
|
+
self.agent.condenser is None
|
|
556
|
+
or not self.agent.condenser.handles_condensation_requests()
|
|
557
|
+
):
|
|
558
|
+
condenser_info = (
|
|
559
|
+
"No condenser configured"
|
|
560
|
+
if self.agent.condenser is None
|
|
561
|
+
else (
|
|
562
|
+
f"Condenser {type(self.agent.condenser).__name__} does not handle "
|
|
563
|
+
"condensation requests"
|
|
564
|
+
)
|
|
565
|
+
)
|
|
566
|
+
raise ValueError(
|
|
567
|
+
f"Cannot condense conversation: {condenser_info}. "
|
|
568
|
+
"To enable manual condensation, configure an "
|
|
569
|
+
"LLMSummarizingCondenser:\n\n"
|
|
570
|
+
"from openhands.sdk.context.condenser import LLMSummarizingCondenser\n"
|
|
571
|
+
"agent = Agent(\n"
|
|
572
|
+
" llm=your_llm,\n"
|
|
573
|
+
" condenser=LLMSummarizingCondenser(\n"
|
|
574
|
+
" llm=your_llm,\n"
|
|
575
|
+
" max_size=120,\n"
|
|
576
|
+
" keep_first=4\n"
|
|
577
|
+
" )\n"
|
|
578
|
+
")"
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
# Add a condensation request event
|
|
582
|
+
condensation_request = CondensationRequest()
|
|
583
|
+
self._on_event(condensation_request)
|
|
584
|
+
|
|
585
|
+
# Force the agent to take a single step to process the condensation request
|
|
586
|
+
# This will trigger the condenser if it handles condensation requests
|
|
587
|
+
with self._state:
|
|
588
|
+
# Take a single step to process the condensation request
|
|
589
|
+
self.agent.step(self, on_event=self._on_event, on_token=self._on_token)
|
|
590
|
+
|
|
591
|
+
logger.info("Condensation request processed")
|
|
592
|
+
|
|
543
593
|
def __del__(self) -> None:
|
|
544
594
|
"""Ensure cleanup happens when conversation is destroyed."""
|
|
545
595
|
try:
|
|
@@ -746,6 +746,21 @@ class RemoteConversation(BaseConversation):
|
|
|
746
746
|
data = resp.json()
|
|
747
747
|
return data["title"]
|
|
748
748
|
|
|
749
|
+
def condense(self) -> None:
|
|
750
|
+
"""Force condensation of the conversation history.
|
|
751
|
+
|
|
752
|
+
This method sends a condensation request to the remote agent server.
|
|
753
|
+
The server will use the existing condensation request pattern to trigger
|
|
754
|
+
condensation if a condenser is configured and handles condensation requests.
|
|
755
|
+
|
|
756
|
+
The condensation will be applied on the server side and will modify the
|
|
757
|
+
conversation state by adding a condensation event to the history.
|
|
758
|
+
|
|
759
|
+
Raises:
|
|
760
|
+
HTTPError: If the server returns an error (e.g., no condenser configured).
|
|
761
|
+
"""
|
|
762
|
+
_send_request(self._client, "POST", f"/api/conversations/{self._id}/condense")
|
|
763
|
+
|
|
749
764
|
def close(self) -> None:
|
|
750
765
|
"""Close the conversation and clean up resources.
|
|
751
766
|
|
openhands/sdk/tool/tool.py
CHANGED
|
@@ -364,7 +364,7 @@ class ToolDefinition[ActionT, ObservationT](DiscriminatedUnionMixin, ABC):
|
|
|
364
364
|
action_type: type[Schema] | None = None,
|
|
365
365
|
) -> dict[str, Any]:
|
|
366
366
|
action_type = action_type or self.action_type
|
|
367
|
-
action_type_with_risk =
|
|
367
|
+
action_type_with_risk = create_action_type_with_risk(action_type)
|
|
368
368
|
|
|
369
369
|
add_security_risk_prediction = add_security_risk_prediction and (
|
|
370
370
|
self.annotations is None or (not self.annotations.readOnlyHint)
|
|
@@ -460,7 +460,7 @@ class ToolDefinition[ActionT, ObservationT](DiscriminatedUnionMixin, ABC):
|
|
|
460
460
|
raise ValueError(error_msg)
|
|
461
461
|
|
|
462
462
|
|
|
463
|
-
def
|
|
463
|
+
def create_action_type_with_risk(action_type: type[Schema]) -> type[Schema]:
|
|
464
464
|
action_type_with_risk = _action_types_with_risk.get(action_type)
|
|
465
465
|
if action_type_with_risk:
|
|
466
466
|
return action_type_with_risk
|
|
@@ -2,7 +2,7 @@ openhands/sdk/__init__.py,sha256=SsB5acHhWvF6e3FlbR72PzZHH9ByJiXc7vnwxBPqmhw,237
|
|
|
2
2
|
openhands/sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
openhands/sdk/agent/__init__.py,sha256=yOn1ZCgTTq2VJlTzKDSzmWVPli1siBzqV89vlEHCwOg,137
|
|
4
4
|
openhands/sdk/agent/agent.py,sha256=OCR7JJytiiTHLuFwU3XjMtJx11Y2Io-DamdbMa2mf60,23760
|
|
5
|
-
openhands/sdk/agent/base.py,sha256=
|
|
5
|
+
openhands/sdk/agent/base.py,sha256=DcWIKQrX4f0EgXMTU_PN-DmEDznRWrPNJLUwuFVVZAc,16628
|
|
6
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
|
|
@@ -33,7 +33,7 @@ openhands/sdk/context/skills/skill.py,sha256=iJpJWiXLHhxd7tmdPo1HcIDqa4emAYkQHgN
|
|
|
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
35
|
openhands/sdk/conversation/__init__.py,sha256=1-xh49S2KJhtAjkHDaDHU28UWhfQLl3CeFlo179gr00,1402
|
|
36
|
-
openhands/sdk/conversation/base.py,sha256=
|
|
36
|
+
openhands/sdk/conversation/base.py,sha256=sVpfqtyTVi7_d8rzI_gemZGHusVrNy9S_yNJmTGMEq8,8964
|
|
37
37
|
openhands/sdk/conversation/conversation.py,sha256=bmgjOmPnznJFgEyOguzqUwwLamEEtmPf_YNSLnsUmWM,5253
|
|
38
38
|
openhands/sdk/conversation/conversation_stats.py,sha256=ZlQ99kgG5YVCrZ4rqJlq63JaiInxX8jqv-q5lS7RN68,3038
|
|
39
39
|
openhands/sdk/conversation/event_store.py,sha256=he-bwP823s5zAIdua_0ZgkkHQCJoAqbtV2SN0hibX30,5207
|
|
@@ -49,8 +49,8 @@ openhands/sdk/conversation/stuck_detector.py,sha256=PZF0HWC6G0SUud_U3hiv5r4AqfJJ
|
|
|
49
49
|
openhands/sdk/conversation/title_utils.py,sha256=j40-dP-Oes-mhU2xUC7fCC8cB0wkMdbbDJU7WLHiVIo,7063
|
|
50
50
|
openhands/sdk/conversation/types.py,sha256=CMCCJz6fSfWgaAgWXeorEDC8VSXyyplyiMlpcueszT8,423
|
|
51
51
|
openhands/sdk/conversation/impl/__init__.py,sha256=DmDFyNR4RU8eiMocKf2j9eBQomipP-rrJgU1LoVWTDA,220
|
|
52
|
-
openhands/sdk/conversation/impl/local_conversation.py,sha256=
|
|
53
|
-
openhands/sdk/conversation/impl/remote_conversation.py,sha256=
|
|
52
|
+
openhands/sdk/conversation/impl/local_conversation.py,sha256=cqA_uek_BdY9xkAnGcqFG-GtU77-SNx7DlhYY7bELGQ,24592
|
|
53
|
+
openhands/sdk/conversation/impl/remote_conversation.py,sha256=r7kj82Ng0g7ov8YFVmzFw-lhMduzXEOSgZPZTeTYHUg,29230
|
|
54
54
|
openhands/sdk/conversation/visualizer/__init__.py,sha256=0LXpKlt2eJcrqP1z6jQP_nLx23V8ErnQkKYSxvUp0_A,275
|
|
55
55
|
openhands/sdk/conversation/visualizer/base.py,sha256=77DdRdHAPSESxRCYyRRSOK7ROBlljscxogkFFr4YgM0,2323
|
|
56
56
|
openhands/sdk/conversation/visualizer/default.py,sha256=k-3-l1j8H_EOEn_pfzsUczcc4JDhQni7AUQZgZ2TpB4,11885
|
|
@@ -109,7 +109,7 @@ openhands/sdk/llm/utils/model_info.py,sha256=1mFYA7OcEyUB6k1doao8_w1XT7UMM_DAm57
|
|
|
109
109
|
openhands/sdk/llm/utils/retry_mixin.py,sha256=M-hXp8EwP1FjNN6tgHiv133BtUQgRr9Kz_ZWxeAJLGA,4765
|
|
110
110
|
openhands/sdk/llm/utils/telemetry.py,sha256=_csSRWg80djE27QN9Mg3palVTP-LJ7BbttOaHQYykZU,13970
|
|
111
111
|
openhands/sdk/llm/utils/unverified_models.py,sha256=SmYrX_WxXOJBanTviztqy1xPjOcLY4i3qvwNBEga_Dk,4797
|
|
112
|
-
openhands/sdk/llm/utils/verified_models.py,sha256=
|
|
112
|
+
openhands/sdk/llm/utils/verified_models.py,sha256=567qe5E3Y9Or-317fDULpTadTobcHOon3QCyBDh3MxE,1504
|
|
113
113
|
openhands/sdk/logger/__init__.py,sha256=vZvFDYfW01Y8Act3tveMs3XxTysJlt4HeT-n6X_ujYk,330
|
|
114
114
|
openhands/sdk/logger/logger.py,sha256=kSpeol92dKesm24ZxVyYbtAxeYccmuFZJw3qNO9X-t4,6513
|
|
115
115
|
openhands/sdk/logger/rolling.py,sha256=E6oy0asgmOhZHoWlSCw0QK1PKnS6kvtxjoWLAsqlGvs,3440
|
|
@@ -133,7 +133,7 @@ openhands/sdk/tool/__init__.py,sha256=Fpcy_I5CYC8lDf2KNLj7LJg9YpoDObbacMFOvM9fI5
|
|
|
133
133
|
openhands/sdk/tool/registry.py,sha256=x5j8DwOjH3YJijhXe_3nRPsSr1A2sDPpNhxCuvFrHK0,5456
|
|
134
134
|
openhands/sdk/tool/schema.py,sha256=PdQbkzPER-Z9B-eKPMr16M8ap27gghszq1xSHH5DGZQ,9114
|
|
135
135
|
openhands/sdk/tool/spec.py,sha256=EbtWasVhwjLKNJywHubbwfYqfgXnZbU4QE6XUOLhsdk,1221
|
|
136
|
-
openhands/sdk/tool/tool.py,sha256=
|
|
136
|
+
openhands/sdk/tool/tool.py,sha256=UrsUiBmywGXtCKlGCxSF650wSx9kX76RpaTs6U3Jb5w,17796
|
|
137
137
|
openhands/sdk/tool/builtins/__init__.py,sha256=30MfENomH9JBPwKs03Xwgny09dDUOt3FSIQ3egRJKhM,729
|
|
138
138
|
openhands/sdk/tool/builtins/finish.py,sha256=pPv_bKDOQE4sUK9lNh1H1lDaxavHozjOwLCIuYrb424,3136
|
|
139
139
|
openhands/sdk/tool/builtins/think.py,sha256=Jp8CBHJZwrtNuVCLrxKlVwb9HeQ1lZB56PCYMxW3wWk,4050
|
|
@@ -159,7 +159,7 @@ openhands/sdk/workspace/remote/__init__.py,sha256=eKkj6NOESMUBGDVC6_L2Wfuc4K6G-m
|
|
|
159
159
|
openhands/sdk/workspace/remote/async_remote_workspace.py,sha256=ftv1Vdx4mmM3AjygJpemMJGvhaQel7ORxdQVk12z4ZE,5061
|
|
160
160
|
openhands/sdk/workspace/remote/base.py,sha256=72C9MZV7ch5n6oHNvFMo6irW7b6Le8n4gk3yFuc0798,5605
|
|
161
161
|
openhands/sdk/workspace/remote/remote_workspace_mixin.py,sha256=CzHfnLUIra5sgPkP9kcggb1vHGOPpYQzLsHvGO2rRt0,10963
|
|
162
|
-
openhands_sdk-1.5.
|
|
163
|
-
openhands_sdk-1.5.
|
|
164
|
-
openhands_sdk-1.5.
|
|
165
|
-
openhands_sdk-1.5.
|
|
162
|
+
openhands_sdk-1.5.2.dist-info/METADATA,sha256=XwPv6tZdwg8wHnixpnTWtNI3d_Df6V3w5xeYzdbnBtA,545
|
|
163
|
+
openhands_sdk-1.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
164
|
+
openhands_sdk-1.5.2.dist-info/top_level.txt,sha256=jHgVu9I0Blam8BXFgedoGKfglPF8XvW1TsJFIjcgP4E,10
|
|
165
|
+
openhands_sdk-1.5.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|