openhands-agent-server 1.22.0__tar.gz → 1.22.1__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.
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/PKG-INFO +1 -1
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/__main__.py +55 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/bash_service.py +2 -1
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/config.py +9 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/conversation_router.py +3 -15
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/conversation_router_acp.py +37 -10
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/conversation_service.py +144 -125
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/docker/Dockerfile +99 -45
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/event_service.py +123 -102
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/models.py +13 -23
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/persistence/__init__.py +2 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/persistence/models.py +40 -19
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/persistence/store.py +1 -1
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/pub_sub.py +28 -4
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/skills_service.py +4 -7
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/sockets.py +36 -7
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands_agent_server.egg-info/PKG-INFO +1 -1
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/pyproject.toml +1 -1
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/__init__.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/_secrets_exposure.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/api.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/auth_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/bash_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/cloud_proxy_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/conversation_lease.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/dependencies.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/desktop_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/desktop_service.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/docker/build.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/docker/wallpaper.svg +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/env_parser.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/event_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/file_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/git_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/hooks_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/hooks_service.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/llm_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/logging_config.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/middleware.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/openapi.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/profiles_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/py.typed +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/server_details_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/settings_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/skills_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/tool_preload_service.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/tool_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/utils.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/vscode_extensions/openhands-settings/extension.js +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/vscode_extensions/openhands-settings/package.json +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/vscode_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/vscode_service.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/workspace_router.py +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands_agent_server.egg-info/SOURCES.txt +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands_agent_server.egg-info/dependency_links.txt +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands_agent_server.egg-info/entry_points.txt +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands_agent_server.egg-info/requires.txt +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands_agent_server.egg-info/top_level.txt +0 -0
- {openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-agent-server
|
|
3
|
-
Version: 1.22.
|
|
3
|
+
Version: 1.22.1
|
|
4
4
|
Summary: OpenHands Agent Server - REST/WebSocket interface for OpenHands AI Agent
|
|
5
5
|
Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
|
|
6
6
|
Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
|
{openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/__main__.py
RENAMED
|
@@ -18,6 +18,7 @@ logger = get_logger(__name__)
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
_INTERNAL_SERVER_URL_ENV = "OH_INTERNAL_SERVER_URL"
|
|
21
|
+
_EXTRA_PYTHON_PATH_ENV = "OH_EXTRA_PYTHON_PATH"
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
def _get_internal_server_url(host: str, port: int) -> str:
|
|
@@ -43,6 +44,46 @@ def _get_internal_server_url(host: str, port: int) -> str:
|
|
|
43
44
|
return f"http://{resolved_host}:{port}"
|
|
44
45
|
|
|
45
46
|
|
|
47
|
+
def extend_python_path(extra_paths: str | None) -> None:
|
|
48
|
+
"""Add directories to ``sys.path`` so ``importlib.import_module`` can find
|
|
49
|
+
external custom-tool modules — even when running from a PyInstaller binary.
|
|
50
|
+
|
|
51
|
+
Paths are read from *extra_paths* (``--extra-python-path`` CLI arg) **and**
|
|
52
|
+
the ``OH_EXTRA_PYTHON_PATH`` environment variable. Both use the
|
|
53
|
+
platform path separator (``':'`` on POSIX, ``';'`` on Windows).
|
|
54
|
+
|
|
55
|
+
Non-existent directories are skipped with a warning; duplicates and paths
|
|
56
|
+
already on ``sys.path`` are silently ignored.
|
|
57
|
+
"""
|
|
58
|
+
raw_parts: list[str] = []
|
|
59
|
+
for source in (extra_paths, os.environ.get(_EXTRA_PYTHON_PATH_ENV)):
|
|
60
|
+
if source:
|
|
61
|
+
raw_parts.extend(source.split(os.pathsep))
|
|
62
|
+
|
|
63
|
+
added = 0
|
|
64
|
+
for part in raw_parts:
|
|
65
|
+
part = part.strip()
|
|
66
|
+
if not part:
|
|
67
|
+
continue
|
|
68
|
+
resolved = os.path.abspath(part)
|
|
69
|
+
if not os.path.isdir(resolved):
|
|
70
|
+
logger.warning(
|
|
71
|
+
"Ignoring non-existent --extra-python-path entry: %s", resolved
|
|
72
|
+
)
|
|
73
|
+
continue
|
|
74
|
+
if resolved not in sys.path:
|
|
75
|
+
sys.path.insert(0, resolved)
|
|
76
|
+
logger.info("Added to sys.path: %s", resolved)
|
|
77
|
+
added += 1
|
|
78
|
+
|
|
79
|
+
if added:
|
|
80
|
+
logger.info(
|
|
81
|
+
"Extended sys.path with %d director%s for custom tool imports",
|
|
82
|
+
added,
|
|
83
|
+
"y" if added == 1 else "ies",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
46
87
|
def preload_modules(modules_arg: str | None) -> None:
|
|
47
88
|
"""Import user-specified modules so their top-level side effects run.
|
|
48
89
|
|
|
@@ -171,6 +212,16 @@ def main() -> None:
|
|
|
171
212
|
"(e.g. 'myapp.tools,myapp.plugins')"
|
|
172
213
|
),
|
|
173
214
|
)
|
|
215
|
+
parser.add_argument(
|
|
216
|
+
"--extra-python-path",
|
|
217
|
+
type=str,
|
|
218
|
+
default=None,
|
|
219
|
+
help=(
|
|
220
|
+
"Additional directories to add to sys.path for custom tool imports "
|
|
221
|
+
f"('{os.pathsep}'-separated). Also reads from the "
|
|
222
|
+
f"{_EXTRA_PYTHON_PATH_ENV} environment variable."
|
|
223
|
+
),
|
|
224
|
+
)
|
|
174
225
|
|
|
175
226
|
args = parser.parse_args()
|
|
176
227
|
|
|
@@ -181,6 +232,10 @@ def main() -> None:
|
|
|
181
232
|
else:
|
|
182
233
|
sys.exit(1)
|
|
183
234
|
|
|
235
|
+
# Extend sys.path before importing user modules so external .py files
|
|
236
|
+
# are reachable — critical for PyInstaller binary builds.
|
|
237
|
+
extend_python_path(args.extra_python_path)
|
|
238
|
+
|
|
184
239
|
# Import user modules after early-exit checks
|
|
185
240
|
preload_modules(args.import_modules)
|
|
186
241
|
|
|
@@ -32,7 +32,8 @@ class BashEventService:
|
|
|
32
32
|
|
|
33
33
|
bash_events_dir: Path = field()
|
|
34
34
|
_pub_sub: PubSub[BashEventBase] = field(
|
|
35
|
-
default_factory=lambda: PubSub[BashEventBase](),
|
|
35
|
+
default_factory=lambda: PubSub[BashEventBase](max_subscribers=50),
|
|
36
|
+
init=False,
|
|
36
37
|
)
|
|
37
38
|
|
|
38
39
|
def _ensure_bash_events_dir(self) -> None:
|
{openhands_agent_server-1.22.0 → openhands_agent_server-1.22.1}/openhands/agent_server/config.py
RENAMED
|
@@ -174,6 +174,15 @@ class Config(BaseModel):
|
|
|
174
174
|
default=True,
|
|
175
175
|
description="Whether to preload tools",
|
|
176
176
|
)
|
|
177
|
+
max_concurrent_runs: int = Field(
|
|
178
|
+
default=10,
|
|
179
|
+
ge=1,
|
|
180
|
+
description=(
|
|
181
|
+
"Maximum number of conversations that can execute agent steps "
|
|
182
|
+
"concurrently. Controls the size of the dedicated thread pool "
|
|
183
|
+
"used for conversation.run() calls."
|
|
184
|
+
),
|
|
185
|
+
)
|
|
177
186
|
secret_key: SecretStr | None = Field(
|
|
178
187
|
default_factory=_default_secret_key,
|
|
179
188
|
description=(
|
|
@@ -19,10 +19,7 @@ from openhands.agent_server._secrets_exposure import (
|
|
|
19
19
|
decrypt_incoming_llm_secrets,
|
|
20
20
|
get_cipher,
|
|
21
21
|
)
|
|
22
|
-
from openhands.agent_server.conversation_service import
|
|
23
|
-
ConversationContractMismatchError,
|
|
24
|
-
ConversationService,
|
|
25
|
-
)
|
|
22
|
+
from openhands.agent_server.conversation_service import ConversationService
|
|
26
23
|
from openhands.agent_server.dependencies import get_conversation_service
|
|
27
24
|
from openhands.agent_server.models import (
|
|
28
25
|
AgentResponseResult,
|
|
@@ -162,10 +159,7 @@ async def batch_get_conversations(
|
|
|
162
159
|
# Write Methods
|
|
163
160
|
|
|
164
161
|
|
|
165
|
-
@conversation_router.post(
|
|
166
|
-
"",
|
|
167
|
-
responses={409: {"description": "Conversation contract mismatch"}},
|
|
168
|
-
)
|
|
162
|
+
@conversation_router.post("")
|
|
169
163
|
async def start_conversation(
|
|
170
164
|
request: Annotated[
|
|
171
165
|
StartConversationRequest, Body(examples=START_CONVERSATION_EXAMPLES)
|
|
@@ -174,13 +168,7 @@ async def start_conversation(
|
|
|
174
168
|
conversation_service: ConversationService = Depends(get_conversation_service),
|
|
175
169
|
) -> ConversationInfo:
|
|
176
170
|
"""Start a conversation in the local environment."""
|
|
177
|
-
|
|
178
|
-
info, is_new = await conversation_service.start_conversation(request)
|
|
179
|
-
except ConversationContractMismatchError as e:
|
|
180
|
-
raise HTTPException(
|
|
181
|
-
status_code=status.HTTP_409_CONFLICT,
|
|
182
|
-
detail=str(e),
|
|
183
|
-
) from e
|
|
171
|
+
info, is_new = await conversation_service.start_conversation(request)
|
|
184
172
|
response.status_code = status.HTTP_201_CREATED if is_new else status.HTTP_200_OK
|
|
185
173
|
return info
|
|
186
174
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
"""ACP-capable conversation routes for the schema-sensitive endpoints."""
|
|
2
2
|
|
|
3
|
+
# Deprecated REST contract: all /api/acp/conversations routes were deprecated
|
|
4
|
+
# in v1.22.0 and are scheduled for removal in v1.27.0. The standard
|
|
5
|
+
# FastAPI/OpenAPI deprecation marker for routes is ``deprecated=True`` on each
|
|
6
|
+
# route decorator; keep matching docstring notices for CI deprecation checks.
|
|
7
|
+
|
|
3
8
|
from typing import Annotated
|
|
4
9
|
from uuid import UUID
|
|
5
10
|
|
|
@@ -53,7 +58,7 @@ START_ACP_CONVERSATION_EXAMPLES = [
|
|
|
53
58
|
]
|
|
54
59
|
|
|
55
60
|
|
|
56
|
-
@conversation_router_acp.get("/search")
|
|
61
|
+
@conversation_router_acp.get("/search", deprecated=True)
|
|
57
62
|
async def search_acp_conversations(
|
|
58
63
|
page_id: Annotated[
|
|
59
64
|
str | None,
|
|
@@ -73,7 +78,11 @@ async def search_acp_conversations(
|
|
|
73
78
|
] = ConversationSortOrder.CREATED_AT_DESC,
|
|
74
79
|
conversation_service: ConversationService = Depends(get_conversation_service),
|
|
75
80
|
) -> ACPConversationPage:
|
|
76
|
-
"""Search conversations using the ACP-capable contract.
|
|
81
|
+
"""Search conversations using the ACP-capable contract.
|
|
82
|
+
|
|
83
|
+
Deprecated since v1.22.0 and scheduled for removal in v1.27.0.
|
|
84
|
+
Use ``/api/conversations/search`` instead.
|
|
85
|
+
"""
|
|
77
86
|
assert limit > 0
|
|
78
87
|
assert limit <= 100
|
|
79
88
|
return await conversation_service.search_acp_conversations(
|
|
@@ -81,7 +90,7 @@ async def search_acp_conversations(
|
|
|
81
90
|
)
|
|
82
91
|
|
|
83
92
|
|
|
84
|
-
@conversation_router_acp.get("/count")
|
|
93
|
+
@conversation_router_acp.get("/count", deprecated=True)
|
|
85
94
|
async def count_acp_conversations(
|
|
86
95
|
status: Annotated[
|
|
87
96
|
ConversationExecutionStatus | None,
|
|
@@ -89,36 +98,49 @@ async def count_acp_conversations(
|
|
|
89
98
|
] = None,
|
|
90
99
|
conversation_service: ConversationService = Depends(get_conversation_service),
|
|
91
100
|
) -> int:
|
|
92
|
-
"""Count conversations using the ACP-capable contract.
|
|
93
|
-
|
|
101
|
+
"""Count conversations using the ACP-capable contract.
|
|
102
|
+
|
|
103
|
+
Deprecated since v1.22.0 and scheduled for removal in v1.27.0.
|
|
104
|
+
Use ``/api/conversations/count`` instead.
|
|
105
|
+
"""
|
|
106
|
+
return await conversation_service.count_conversations(status)
|
|
94
107
|
|
|
95
108
|
|
|
96
109
|
@conversation_router_acp.get(
|
|
97
110
|
"/{conversation_id}",
|
|
98
111
|
responses={404: {"description": "Item not found"}},
|
|
112
|
+
deprecated=True,
|
|
99
113
|
)
|
|
100
114
|
async def get_acp_conversation(
|
|
101
115
|
conversation_id: UUID,
|
|
102
116
|
conversation_service: ConversationService = Depends(get_conversation_service),
|
|
103
117
|
) -> ACPConversationInfo:
|
|
104
|
-
"""Get a conversation using the ACP-capable contract.
|
|
118
|
+
"""Get a conversation using the ACP-capable contract.
|
|
119
|
+
|
|
120
|
+
Deprecated since v1.22.0 and scheduled for removal in v1.27.0.
|
|
121
|
+
Use ``/api/conversations/{conversation_id}`` instead.
|
|
122
|
+
"""
|
|
105
123
|
conversation = await conversation_service.get_acp_conversation(conversation_id)
|
|
106
124
|
if conversation is None:
|
|
107
125
|
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
|
108
126
|
return conversation
|
|
109
127
|
|
|
110
128
|
|
|
111
|
-
@conversation_router_acp.get("")
|
|
129
|
+
@conversation_router_acp.get("", deprecated=True)
|
|
112
130
|
async def batch_get_acp_conversations(
|
|
113
131
|
ids: Annotated[list[UUID], Query()],
|
|
114
132
|
conversation_service: ConversationService = Depends(get_conversation_service),
|
|
115
133
|
) -> list[ACPConversationInfo | None]:
|
|
116
|
-
"""Batch get conversations using the ACP-capable contract.
|
|
134
|
+
"""Batch get conversations using the ACP-capable contract.
|
|
135
|
+
|
|
136
|
+
Deprecated since v1.22.0 and scheduled for removal in v1.27.0.
|
|
137
|
+
Use ``/api/conversations`` instead.
|
|
138
|
+
"""
|
|
117
139
|
assert len(ids) < 100
|
|
118
140
|
return await conversation_service.batch_get_acp_conversations(ids)
|
|
119
141
|
|
|
120
142
|
|
|
121
|
-
@conversation_router_acp.post("")
|
|
143
|
+
@conversation_router_acp.post("", deprecated=True)
|
|
122
144
|
async def start_acp_conversation(
|
|
123
145
|
request: Annotated[
|
|
124
146
|
StartACPConversationRequest,
|
|
@@ -127,7 +149,12 @@ async def start_acp_conversation(
|
|
|
127
149
|
response: Response,
|
|
128
150
|
conversation_service: ConversationService = Depends(get_conversation_service),
|
|
129
151
|
) -> ACPConversationInfo:
|
|
130
|
-
"""Start a conversation using the ACP-capable contract.
|
|
152
|
+
"""Start a conversation using the ACP-capable contract.
|
|
153
|
+
|
|
154
|
+
Deprecated since v1.22.0 and scheduled for removal in v1.27.0.
|
|
155
|
+
Use ``/api/conversations`` instead; it now accepts ACP agents and
|
|
156
|
+
``agent_settings`` payloads.
|
|
157
|
+
"""
|
|
131
158
|
info, is_new = await conversation_service.start_acp_conversation(request)
|
|
132
159
|
response.status_code = status.HTTP_201_CREATED if is_new else status.HTTP_200_OK
|
|
133
160
|
return info
|