agentscope-runtime 1.0.3__py3-none-any.whl → 1.0.4__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.
Files changed (49) hide show
  1. agentscope_runtime/adapters/agentscope/stream.py +2 -9
  2. agentscope_runtime/adapters/ms_agent_framework/__init__.py +0 -0
  3. agentscope_runtime/adapters/ms_agent_framework/message.py +205 -0
  4. agentscope_runtime/adapters/ms_agent_framework/stream.py +418 -0
  5. agentscope_runtime/adapters/utils.py +6 -0
  6. agentscope_runtime/cli/commands/deploy.py +371 -0
  7. agentscope_runtime/common/container_clients/knative_client.py +466 -0
  8. agentscope_runtime/engine/__init__.py +4 -0
  9. agentscope_runtime/engine/constant.py +1 -0
  10. agentscope_runtime/engine/deployers/__init__.py +12 -0
  11. agentscope_runtime/engine/deployers/adapter/a2a/__init__.py +26 -51
  12. agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +19 -10
  13. agentscope_runtime/engine/deployers/adapter/a2a/a2a_registry.py +4 -201
  14. agentscope_runtime/engine/deployers/adapter/a2a/nacos_a2a_registry.py +134 -25
  15. agentscope_runtime/engine/deployers/agentrun_deployer.py +2 -2
  16. agentscope_runtime/engine/deployers/fc_deployer.py +1506 -0
  17. agentscope_runtime/engine/deployers/knative_deployer.py +290 -0
  18. agentscope_runtime/engine/runner.py +12 -0
  19. agentscope_runtime/engine/services/agent_state/redis_state_service.py +2 -2
  20. agentscope_runtime/engine/services/memory/redis_memory_service.py +2 -2
  21. agentscope_runtime/engine/services/session_history/redis_session_history_service.py +2 -2
  22. agentscope_runtime/engine/tracing/wrapper.py +18 -4
  23. agentscope_runtime/sandbox/__init__.py +14 -6
  24. agentscope_runtime/sandbox/box/base/__init__.py +2 -2
  25. agentscope_runtime/sandbox/box/base/base_sandbox.py +51 -1
  26. agentscope_runtime/sandbox/box/browser/__init__.py +2 -2
  27. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +198 -2
  28. agentscope_runtime/sandbox/box/filesystem/__init__.py +2 -2
  29. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +99 -2
  30. agentscope_runtime/sandbox/box/gui/__init__.py +2 -2
  31. agentscope_runtime/sandbox/box/gui/gui_sandbox.py +117 -1
  32. agentscope_runtime/sandbox/box/mobile/__init__.py +2 -2
  33. agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +247 -100
  34. agentscope_runtime/sandbox/box/sandbox.py +98 -65
  35. agentscope_runtime/sandbox/box/shared/routers/generic.py +36 -29
  36. agentscope_runtime/sandbox/client/__init__.py +6 -1
  37. agentscope_runtime/sandbox/client/async_http_client.py +339 -0
  38. agentscope_runtime/sandbox/client/base.py +74 -0
  39. agentscope_runtime/sandbox/client/http_client.py +108 -329
  40. agentscope_runtime/sandbox/enums.py +7 -0
  41. agentscope_runtime/sandbox/manager/sandbox_manager.py +264 -4
  42. agentscope_runtime/sandbox/manager/server/app.py +7 -1
  43. agentscope_runtime/version.py +1 -1
  44. {agentscope_runtime-1.0.3.dist-info → agentscope_runtime-1.0.4.dist-info}/METADATA +102 -28
  45. {agentscope_runtime-1.0.3.dist-info → agentscope_runtime-1.0.4.dist-info}/RECORD +49 -40
  46. {agentscope_runtime-1.0.3.dist-info → agentscope_runtime-1.0.4.dist-info}/WHEEL +0 -0
  47. {agentscope_runtime-1.0.3.dist-info → agentscope_runtime-1.0.4.dist-info}/entry_points.txt +0 -0
  48. {agentscope_runtime-1.0.3.dist-info → agentscope_runtime-1.0.4.dist-info}/licenses/LICENSE +0 -0
  49. {agentscope_runtime-1.0.3.dist-info → agentscope_runtime-1.0.4.dist-info}/top_level.txt +0 -0
@@ -3,11 +3,11 @@
3
3
  import logging
4
4
  import time
5
5
  from typing import Any, Optional
6
- from urllib.parse import urljoin
7
6
 
8
7
  import requests
9
8
  from pydantic import Field
10
9
 
10
+ from .base import SandboxHttpBase
11
11
  from ..model import ContainerModel
12
12
 
13
13
 
@@ -18,55 +18,12 @@ logging.basicConfig(level=logging.INFO)
18
18
  logger = logging.getLogger(__name__)
19
19
 
20
20
 
21
- class SandboxHttpClient:
21
+ class SandboxHttpClient(SandboxHttpBase):
22
22
  """
23
23
  A Python client for interacting with the runtime API. Connect with
24
24
  container directly.
25
25
  """
26
26
 
27
- _generic_tools = {
28
- "run_ipython_cell": {
29
- "name": "run_ipython_cell",
30
- "json_schema": {
31
- "type": "function",
32
- "function": {
33
- "name": "run_ipython_cell",
34
- "description": "Run an IPython cell.",
35
- "parameters": {
36
- "type": "object",
37
- "properties": {
38
- "code": {
39
- "type": "string",
40
- "description": "IPython code to execute",
41
- },
42
- },
43
- "required": ["code"],
44
- },
45
- },
46
- },
47
- },
48
- "run_shell_command": {
49
- "name": "run_shell_command",
50
- "json_schema": {
51
- "type": "function",
52
- "function": {
53
- "name": "run_shell_command",
54
- "description": "Run a shell command.",
55
- "parameters": {
56
- "type": "object",
57
- "properties": {
58
- "command": {
59
- "type": "string",
60
- "description": "Shell command to execute",
61
- },
62
- },
63
- "required": ["command"],
64
- },
65
- },
66
- },
67
- },
68
- }
69
-
70
27
  def __init__(
71
28
  self,
72
29
  model: Optional[ContainerModel] = None,
@@ -80,27 +37,9 @@ class SandboxHttpClient:
80
37
  model (ContainerModel): The pydantic model representing the
81
38
  runtime sandbox.
82
39
  """
83
- self.session_id = model.session_id
84
- self.base_url = urljoin(
85
- model.url.replace("localhost", domain),
86
- "fastapi",
87
- )
88
-
89
- self.start_timeout = timeout
90
- self.timeout = model.timeout or DEFAULT_TIMEOUT
40
+ super().__init__(model, timeout, domain)
91
41
  self.session = requests.Session()
92
- self.built_in_tools = []
93
- self.secret = model.runtime_token
94
-
95
- # Update headers with secret if provided
96
- headers = {
97
- "Content-Type": "application/json",
98
- "x-agentrun-session-id": "s" + self.session_id,
99
- "x-agentscope-runtime-session-id": "s" + self.session_id,
100
- }
101
- if self.secret:
102
- headers["Authorization"] = f"Bearer {self.secret}"
103
- self.session.headers.update(headers)
42
+ self.session.headers.update(self.headers)
104
43
 
105
44
  def __enter__(self):
106
45
  # Wait for the runtime api server to be healthy
@@ -115,6 +54,21 @@ class SandboxHttpClient:
115
54
  kwargs["timeout"] = self.timeout
116
55
  return self.session.request(method, url, **kwargs)
117
56
 
57
+ def safe_request(self, method, url, **kwargs):
58
+ try:
59
+ r = self._request(method, url, **kwargs)
60
+ r.raise_for_status()
61
+ try:
62
+ return r.json()
63
+ except ValueError:
64
+ return r.text
65
+ except requests.exceptions.RequestException as e:
66
+ logger.error(f"HTTP error: {e}")
67
+ return {
68
+ "isError": True,
69
+ "content": [{"type": "text", "text": str(e)}],
70
+ }
71
+
118
72
  def check_health(self) -> bool:
119
73
  """
120
74
  Checks if the runtime service is running by verifying the health
@@ -123,9 +77,8 @@ class SandboxHttpClient:
123
77
  Returns:
124
78
  bool: True if the service is reachable, False otherwise
125
79
  """
126
- endpoint = f"{self.base_url}/healthz"
127
80
  try:
128
- response_api = self.session.get(endpoint)
81
+ response_api = self._request("get", f"{self.base_url}/healthz")
129
82
  return response_api.status_code == 200
130
83
  except requests.RequestException:
131
84
  return False
@@ -147,50 +100,36 @@ class SandboxHttpClient:
147
100
  """
148
101
  Add MCP servers to runtime.
149
102
  """
150
- try:
151
- endpoint = f"{self.base_url}/mcp/add_servers"
152
- response = self._request(
153
- "post",
154
- endpoint,
155
- json={
156
- "server_configs": server_configs,
157
- "overwrite": overwrite,
158
- },
159
- )
160
- response.raise_for_status()
161
- return response.text
162
- except requests.exceptions.RequestException as e:
163
- logger.error(f"An error occurred while adding MCP servers: {e}")
164
- return {
165
- "isError": True,
166
- "content": [{"type": "text", "text": str(e)}],
167
- }
103
+ return self.safe_request(
104
+ "post",
105
+ f"{self.base_url}/mcp/add_servers",
106
+ json={
107
+ "server_configs": server_configs,
108
+ "overwrite": overwrite,
109
+ },
110
+ )
168
111
 
169
112
  def list_tools(self, tool_type=None, **kwargs) -> dict:
170
- try:
171
- endpoint = f"{self.base_url}/mcp/list_tools"
172
- response = self._request(
173
- "get",
174
- endpoint,
175
- )
176
- response.raise_for_status()
177
- mcp_tools = response.json()
178
- mcp_tools["generic"] = self.generic_tools
113
+ """
114
+ List available MCP tools plus generic built-in tools.
115
+ """
116
+ data = self.safe_request("get", f"{self.base_url}/mcp/list_tools")
117
+ if isinstance(data, dict) and "isError" not in data:
118
+ data["generic"] = self.generic_tools
179
119
  if tool_type:
180
- return {tool_type: mcp_tools.get(tool_type, {})}
181
- return mcp_tools
182
- except requests.exceptions.RequestException as e:
183
- logging.error(f"An error occurred: {e}")
184
- return {
185
- "isError": True,
186
- "content": [{"type": "text", "text": str(e)}],
187
- }
120
+ return {tool_type: data.get(tool_type, {})}
121
+ return data
188
122
 
189
123
  def call_tool(
190
124
  self,
191
125
  name: str,
192
126
  arguments: Optional[dict[str, Any]] = None,
193
127
  ) -> dict:
128
+ """
129
+ Call a specific MCP tool.
130
+
131
+ If it's a generic tool, call the corresponding local method.
132
+ """
194
133
  if arguments is None:
195
134
  arguments = {}
196
135
 
@@ -200,25 +139,14 @@ class SandboxHttpClient:
200
139
  elif name == "run_shell_command":
201
140
  return self.run_shell_command(**arguments)
202
141
 
203
- try:
204
- endpoint = f"{self.base_url}/mcp/call_tool"
205
- response = self._request(
206
- "post",
207
- endpoint,
208
- json={
209
- "tool_name": name,
210
- "arguments": arguments,
211
- },
212
- )
213
- response.raise_for_status()
214
-
215
- return response.json()
216
- except requests.exceptions.RequestException as e:
217
- logger.error(f"An error occurred: {e}")
218
- return {
219
- "isError": True,
220
- "content": [{"type": "text", "text": str(e)}],
221
- }
142
+ return self.safe_request(
143
+ "post",
144
+ f"{self.base_url}/mcp/call_tool",
145
+ json={
146
+ "tool_name": name,
147
+ "arguments": arguments,
148
+ },
149
+ )
222
150
 
223
151
  def run_ipython_cell(
224
152
  self,
@@ -227,21 +155,11 @@ class SandboxHttpClient:
227
155
  ),
228
156
  ) -> dict:
229
157
  """Run an IPython cell."""
230
- try:
231
- endpoint = f"{self.base_url}/tools/run_ipython_cell"
232
- response = self._request(
233
- "post",
234
- endpoint,
235
- json={"code": code},
236
- )
237
- response.raise_for_status()
238
- return response.json()
239
- except requests.exceptions.RequestException as e:
240
- logger.error(f"An error occurred: {e}")
241
- return {
242
- "isError": True,
243
- "content": [{"type": "text", "text": str(e)}],
244
- }
158
+ return self.safe_request(
159
+ "post",
160
+ f"{self.base_url}/tools/run_ipython_cell",
161
+ json={"code": code},
162
+ )
245
163
 
246
164
  def run_shell_command(
247
165
  self,
@@ -250,46 +168,22 @@ class SandboxHttpClient:
250
168
  ),
251
169
  ) -> dict:
252
170
  """Run a shell command."""
253
- try:
254
- endpoint = f"{self.base_url}/tools/run_shell_command"
255
- response = self._request(
256
- "post",
257
- endpoint,
258
- json={"command": command},
259
- )
260
- response.raise_for_status()
261
- return response.json()
262
- except requests.exceptions.RequestException as e:
263
- logger.error(f"An error occurred: {e}")
264
- return {
265
- "isError": True,
266
- "content": [{"type": "text", "text": str(e)}],
267
- }
268
-
269
- @property
270
- def generic_tools(self) -> dict:
271
- return self._generic_tools
171
+ return self.safe_request(
172
+ "post",
173
+ f"{self.base_url}/tools/run_shell_command",
174
+ json={"command": command},
175
+ )
272
176
 
273
177
  # Below the method is used by API Server
274
178
  def commit_changes(self, commit_message: str = "Automated commit") -> dict:
275
179
  """
276
180
  Commit the uncommitted changes with a given commit message.
277
181
  """
278
- try:
279
- endpoint = f"{self.base_url}/watcher/commit_changes"
280
- response = self._request(
281
- "post",
282
- endpoint,
283
- json={"commit_message": commit_message},
284
- )
285
- response.raise_for_status()
286
- return response.json()
287
- except requests.exceptions.RequestException as e:
288
- logger.error(f"An error occurred while committing changes: {e}")
289
- return {
290
- "isError": True,
291
- "content": [{"type": "text", "text": str(e)}],
292
- }
182
+ return self.safe_request(
183
+ "post",
184
+ f"{self.base_url}/watcher/commit_changes",
185
+ json={"commit_message": commit_message},
186
+ )
293
187
 
294
188
  def generate_diff(
295
189
  self,
@@ -300,40 +194,17 @@ class SandboxHttpClient:
300
194
  Generate the diff between two commits or between uncommitted changes
301
195
  and the latest commit.
302
196
  """
303
- try:
304
- endpoint = f"{self.base_url}/watcher/generate_diff"
305
- response = self._request(
306
- "post",
307
- endpoint,
308
- json={"commit_a": commit_a, "commit_b": commit_b},
309
- )
310
- response.raise_for_status()
311
- return response.json()
312
- except requests.exceptions.RequestException as e:
313
- logger.error(f"An error occurred while generating diff: {e}")
314
- return {
315
- "isError": True,
316
- "content": [{"type": "text", "text": str(e)}],
317
- }
197
+ return self.safe_request(
198
+ "post",
199
+ f"{self.base_url}/watcher/generate_diff",
200
+ json={"commit_a": commit_a, "commit_b": commit_b},
201
+ )
318
202
 
319
203
  def git_logs(self) -> dict:
320
204
  """
321
205
  Retrieve the git logs.
322
206
  """
323
- try:
324
- endpoint = f"{self.base_url}/watcher/git_logs"
325
- response = self._request(
326
- "get",
327
- endpoint,
328
- )
329
- response.raise_for_status()
330
- return response.json()
331
- except requests.exceptions.RequestException as e:
332
- logger.error(f"An error occurred while retrieving git logs: {e}")
333
- return {
334
- "isError": True,
335
- "content": [{"type": "text", "text": str(e)}],
336
- }
207
+ return self.safe_request("get", f"{self.base_url}/watcher/git_logs")
337
208
 
338
209
  def get_workspace_file(self, file_path: str) -> dict:
339
210
  """
@@ -375,27 +246,12 @@ class SandboxHttpClient:
375
246
  """
376
247
  Create or edit a file within the /workspace directory.
377
248
  """
378
- try:
379
- endpoint = f"{self.base_url}/workspace/files"
380
- params = {"file_path": file_path}
381
- data = {"content": content}
382
- response = self._request(
383
- "post",
384
- endpoint,
385
- params=params,
386
- json=data,
387
- )
388
- response.raise_for_status()
389
- return response.json()
390
- except requests.exceptions.RequestException as e:
391
- logger.error(
392
- f"An error occurred while creating or editing a workspace "
393
- f"file: {e}",
394
- )
395
- return {
396
- "isError": True,
397
- "content": [{"type": "text", "text": str(e)}],
398
- }
249
+ return self.safe_request(
250
+ "post",
251
+ f"{self.base_url}/workspace/files",
252
+ params={"file_path": file_path},
253
+ json={"content": content},
254
+ )
399
255
 
400
256
  def list_workspace_directories(
401
257
  self,
@@ -404,68 +260,31 @@ class SandboxHttpClient:
404
260
  """
405
261
  List files in the specified directory within the /workspace.
406
262
  """
407
- try:
408
- endpoint = f"{self.base_url}/workspace/list-directories"
409
- params = {"directory": directory}
410
- response = self._request(
411
- "get",
412
- endpoint,
413
- params=params,
414
- )
415
- response.raise_for_status()
416
- return response.json()
417
- except requests.exceptions.RequestException as e:
418
- logger.error(f"An error occurred while listing files: {e}")
419
- return {
420
- "isError": True,
421
- "content": [{"type": "text", "text": str(e)}],
422
- }
263
+ return self.safe_request(
264
+ "get",
265
+ f"{self.base_url}/workspace/list-directories",
266
+ params={"directory": directory},
267
+ )
423
268
 
424
269
  def create_workspace_directory(self, directory_path: str) -> dict:
425
270
  """
426
271
  Create a directory within the /workspace directory.
427
272
  """
428
- try:
429
- endpoint = f"{self.base_url}/workspace/directories"
430
- params = {"directory_path": directory_path}
431
- response = self._request(
432
- "post",
433
- endpoint,
434
- params=params,
435
- )
436
- response.raise_for_status()
437
- return response.json()
438
- except requests.exceptions.RequestException as e:
439
- logger.error(
440
- f"An error occurred while creating a workspace directory: {e}",
441
- )
442
- return {
443
- "isError": True,
444
- "content": [{"type": "text", "text": str(e)}],
445
- }
273
+ return self.safe_request(
274
+ "post",
275
+ f"{self.base_url}/workspace/directories",
276
+ params={"directory_path": directory_path},
277
+ )
446
278
 
447
279
  def delete_workspace_file(self, file_path: str) -> dict:
448
280
  """
449
281
  Delete a file within the /workspace directory.
450
282
  """
451
- try:
452
- endpoint = f"{self.base_url}/workspace/files"
453
- params = {"file_path": file_path}
454
- response = self._request(
455
- "delete",
456
- endpoint,
457
- params=params,
458
- )
459
- response.raise_for_status()
460
- return response.json()
461
- except requests.exceptions.RequestException as e:
462
- logger.error(
463
- f"An error occurred while deleting a workspace file: {e}",
464
- )
465
- return {
466
- "isError": True,
467
- "content": [{"type": "text", "text": str(e)}],
468
- }
283
+ return self.safe_request(
284
+ "delete",
285
+ f"{self.base_url}/workspace/files",
286
+ params={"file_path": file_path},
287
+ )
469
288
 
470
289
  def delete_workspace_directory(
471
290
  self,
@@ -475,24 +294,11 @@ class SandboxHttpClient:
475
294
  """
476
295
  Delete a directory within the /workspace directory.
477
296
  """
478
- try:
479
- endpoint = f"{self.base_url}/workspace/directories"
480
- params = {"directory_path": directory_path, "recursive": recursive}
481
- response = self._request(
482
- "delete",
483
- endpoint,
484
- params=params,
485
- )
486
- response.raise_for_status()
487
- return response.json()
488
- except requests.exceptions.RequestException as e:
489
- logger.error(
490
- f"An error occurred while deleting a workspace directory: {e}",
491
- )
492
- return {
493
- "isError": True,
494
- "content": [{"type": "text", "text": str(e)}],
495
- }
297
+ return self.safe_request(
298
+ "delete",
299
+ f"{self.base_url}/workspace/directories",
300
+ params={"directory_path": directory_path, "recursive": recursive},
301
+ )
496
302
 
497
303
  def move_or_rename_workspace_item(
498
304
  self,
@@ -502,28 +308,14 @@ class SandboxHttpClient:
502
308
  """
503
309
  Move or rename a file or directory within the /workspace directory.
504
310
  """
505
- try:
506
- endpoint = f"{self.base_url}/workspace/move"
507
- params = {
311
+ return self.safe_request(
312
+ "put",
313
+ f"{self.base_url}/workspace/move",
314
+ params={
508
315
  "source_path": source_path,
509
316
  "destination_path": destination_path,
510
- }
511
- response = self._request(
512
- "put",
513
- endpoint,
514
- params=params,
515
- )
516
- response.raise_for_status()
517
- return response.json()
518
- except requests.exceptions.RequestException as e:
519
- logger.error(
520
- f"An error occurred while moving or renaming a workspace "
521
- f"item: {e}",
522
- )
523
- return {
524
- "isError": True,
525
- "content": [{"type": "text", "text": str(e)}],
526
- }
317
+ },
318
+ )
527
319
 
528
320
  def copy_workspace_item(
529
321
  self,
@@ -533,24 +325,11 @@ class SandboxHttpClient:
533
325
  """
534
326
  Copy a file or directory within the /workspace directory.
535
327
  """
536
- try:
537
- endpoint = f"{self.base_url}/workspace/copy"
538
- params = {
328
+ return self.safe_request(
329
+ "post",
330
+ f"{self.base_url}/workspace/copy",
331
+ params={
539
332
  "source_path": source_path,
540
333
  "destination_path": destination_path,
541
- }
542
- response = self._request(
543
- "post",
544
- endpoint,
545
- params=params,
546
- )
547
- response.raise_for_status()
548
- return response.json()
549
- except requests.exceptions.RequestException as e:
550
- logger.error(
551
- f"An error occurred while copying a workspace item: {e}",
552
- )
553
- return {
554
- "isError": True,
555
- "content": [{"type": "text", "text": str(e)}],
556
- }
334
+ },
335
+ )
@@ -70,3 +70,10 @@ class SandboxType(DynamicEnum):
70
70
  APPWORLD = "appworld"
71
71
  BFCL = "bfcl"
72
72
  AGENTBAY = "agentbay"
73
+
74
+ # Async sandbox
75
+ BASE_ASYNC = "base_async"
76
+ BROWSER_ASYNC = "browser_async"
77
+ FILESYSTEM_ASYNC = "filesystem_async"
78
+ GUI_ASYNC = "gui_async"
79
+ MOBILE_ASYNC = "mobile_async"