agentscope-runtime 0.1.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.
Files changed (131) hide show
  1. agentscope_runtime/__init__.py +4 -0
  2. agentscope_runtime/engine/__init__.py +9 -0
  3. agentscope_runtime/engine/agents/__init__.py +2 -0
  4. agentscope_runtime/engine/agents/agentscope_agent/__init__.py +6 -0
  5. agentscope_runtime/engine/agents/agentscope_agent/agent.py +342 -0
  6. agentscope_runtime/engine/agents/agentscope_agent/hooks.py +156 -0
  7. agentscope_runtime/engine/agents/agno_agent.py +220 -0
  8. agentscope_runtime/engine/agents/base_agent.py +29 -0
  9. agentscope_runtime/engine/agents/langgraph_agent.py +59 -0
  10. agentscope_runtime/engine/agents/llm_agent.py +51 -0
  11. agentscope_runtime/engine/deployers/__init__.py +3 -0
  12. agentscope_runtime/engine/deployers/adapter/__init__.py +0 -0
  13. agentscope_runtime/engine/deployers/adapter/a2a/__init__.py +2 -0
  14. agentscope_runtime/engine/deployers/adapter/a2a/a2a_adapter_utils.py +425 -0
  15. agentscope_runtime/engine/deployers/adapter/a2a/a2a_agent_adapter.py +69 -0
  16. agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +60 -0
  17. agentscope_runtime/engine/deployers/adapter/protocol_adapter.py +24 -0
  18. agentscope_runtime/engine/deployers/base.py +17 -0
  19. agentscope_runtime/engine/deployers/local_deployer.py +586 -0
  20. agentscope_runtime/engine/helpers/helper.py +127 -0
  21. agentscope_runtime/engine/llms/__init__.py +3 -0
  22. agentscope_runtime/engine/llms/base_llm.py +60 -0
  23. agentscope_runtime/engine/llms/qwen_llm.py +47 -0
  24. agentscope_runtime/engine/misc/__init__.py +0 -0
  25. agentscope_runtime/engine/runner.py +186 -0
  26. agentscope_runtime/engine/schemas/__init__.py +0 -0
  27. agentscope_runtime/engine/schemas/agent_schemas.py +551 -0
  28. agentscope_runtime/engine/schemas/context.py +54 -0
  29. agentscope_runtime/engine/services/__init__.py +9 -0
  30. agentscope_runtime/engine/services/base.py +77 -0
  31. agentscope_runtime/engine/services/context_manager.py +129 -0
  32. agentscope_runtime/engine/services/environment_manager.py +50 -0
  33. agentscope_runtime/engine/services/manager.py +174 -0
  34. agentscope_runtime/engine/services/memory_service.py +270 -0
  35. agentscope_runtime/engine/services/sandbox_service.py +198 -0
  36. agentscope_runtime/engine/services/session_history_service.py +256 -0
  37. agentscope_runtime/engine/tracing/__init__.py +40 -0
  38. agentscope_runtime/engine/tracing/base.py +309 -0
  39. agentscope_runtime/engine/tracing/local_logging_handler.py +356 -0
  40. agentscope_runtime/engine/tracing/tracing_metric.py +69 -0
  41. agentscope_runtime/engine/tracing/wrapper.py +321 -0
  42. agentscope_runtime/sandbox/__init__.py +14 -0
  43. agentscope_runtime/sandbox/box/__init__.py +0 -0
  44. agentscope_runtime/sandbox/box/base/__init__.py +0 -0
  45. agentscope_runtime/sandbox/box/base/base_sandbox.py +37 -0
  46. agentscope_runtime/sandbox/box/base/box/__init__.py +0 -0
  47. agentscope_runtime/sandbox/box/browser/__init__.py +0 -0
  48. agentscope_runtime/sandbox/box/browser/box/__init__.py +0 -0
  49. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +176 -0
  50. agentscope_runtime/sandbox/box/dummy/__init__.py +0 -0
  51. agentscope_runtime/sandbox/box/dummy/dummy_sandbox.py +26 -0
  52. agentscope_runtime/sandbox/box/filesystem/__init__.py +0 -0
  53. agentscope_runtime/sandbox/box/filesystem/box/__init__.py +0 -0
  54. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +87 -0
  55. agentscope_runtime/sandbox/box/sandbox.py +115 -0
  56. agentscope_runtime/sandbox/box/shared/__init__.py +0 -0
  57. agentscope_runtime/sandbox/box/shared/app.py +44 -0
  58. agentscope_runtime/sandbox/box/shared/dependencies/__init__.py +5 -0
  59. agentscope_runtime/sandbox/box/shared/dependencies/deps.py +22 -0
  60. agentscope_runtime/sandbox/box/shared/routers/__init__.py +12 -0
  61. agentscope_runtime/sandbox/box/shared/routers/generic.py +173 -0
  62. agentscope_runtime/sandbox/box/shared/routers/mcp.py +207 -0
  63. agentscope_runtime/sandbox/box/shared/routers/mcp_utils.py +153 -0
  64. agentscope_runtime/sandbox/box/shared/routers/runtime_watcher.py +187 -0
  65. agentscope_runtime/sandbox/box/shared/routers/workspace.py +325 -0
  66. agentscope_runtime/sandbox/box/training_box/__init__.py +0 -0
  67. agentscope_runtime/sandbox/box/training_box/base.py +120 -0
  68. agentscope_runtime/sandbox/box/training_box/env_service.py +752 -0
  69. agentscope_runtime/sandbox/box/training_box/environments/__init__.py +0 -0
  70. agentscope_runtime/sandbox/box/training_box/environments/appworld/appworld_env.py +987 -0
  71. agentscope_runtime/sandbox/box/training_box/registry.py +54 -0
  72. agentscope_runtime/sandbox/box/training_box/src/trajectory.py +278 -0
  73. agentscope_runtime/sandbox/box/training_box/training_box.py +219 -0
  74. agentscope_runtime/sandbox/build.py +213 -0
  75. agentscope_runtime/sandbox/client/__init__.py +5 -0
  76. agentscope_runtime/sandbox/client/http_client.py +527 -0
  77. agentscope_runtime/sandbox/client/training_client.py +265 -0
  78. agentscope_runtime/sandbox/constant.py +5 -0
  79. agentscope_runtime/sandbox/custom/__init__.py +16 -0
  80. agentscope_runtime/sandbox/custom/custom_sandbox.py +40 -0
  81. agentscope_runtime/sandbox/custom/example.py +37 -0
  82. agentscope_runtime/sandbox/enums.py +68 -0
  83. agentscope_runtime/sandbox/manager/__init__.py +4 -0
  84. agentscope_runtime/sandbox/manager/collections/__init__.py +22 -0
  85. agentscope_runtime/sandbox/manager/collections/base_mapping.py +20 -0
  86. agentscope_runtime/sandbox/manager/collections/base_queue.py +25 -0
  87. agentscope_runtime/sandbox/manager/collections/base_set.py +25 -0
  88. agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +22 -0
  89. agentscope_runtime/sandbox/manager/collections/in_memory_queue.py +28 -0
  90. agentscope_runtime/sandbox/manager/collections/in_memory_set.py +27 -0
  91. agentscope_runtime/sandbox/manager/collections/redis_mapping.py +26 -0
  92. agentscope_runtime/sandbox/manager/collections/redis_queue.py +27 -0
  93. agentscope_runtime/sandbox/manager/collections/redis_set.py +23 -0
  94. agentscope_runtime/sandbox/manager/container_clients/__init__.py +8 -0
  95. agentscope_runtime/sandbox/manager/container_clients/base_client.py +39 -0
  96. agentscope_runtime/sandbox/manager/container_clients/docker_client.py +170 -0
  97. agentscope_runtime/sandbox/manager/sandbox_manager.py +694 -0
  98. agentscope_runtime/sandbox/manager/server/__init__.py +0 -0
  99. agentscope_runtime/sandbox/manager/server/app.py +194 -0
  100. agentscope_runtime/sandbox/manager/server/config.py +68 -0
  101. agentscope_runtime/sandbox/manager/server/models.py +17 -0
  102. agentscope_runtime/sandbox/manager/storage/__init__.py +10 -0
  103. agentscope_runtime/sandbox/manager/storage/data_storage.py +16 -0
  104. agentscope_runtime/sandbox/manager/storage/local_storage.py +44 -0
  105. agentscope_runtime/sandbox/manager/storage/oss_storage.py +89 -0
  106. agentscope_runtime/sandbox/manager/utils.py +78 -0
  107. agentscope_runtime/sandbox/mcp_server.py +192 -0
  108. agentscope_runtime/sandbox/model/__init__.py +12 -0
  109. agentscope_runtime/sandbox/model/api.py +16 -0
  110. agentscope_runtime/sandbox/model/container.py +72 -0
  111. agentscope_runtime/sandbox/model/manager_config.py +158 -0
  112. agentscope_runtime/sandbox/registry.py +129 -0
  113. agentscope_runtime/sandbox/tools/__init__.py +12 -0
  114. agentscope_runtime/sandbox/tools/base/__init__.py +8 -0
  115. agentscope_runtime/sandbox/tools/base/tool.py +52 -0
  116. agentscope_runtime/sandbox/tools/browser/__init__.py +57 -0
  117. agentscope_runtime/sandbox/tools/browser/tool.py +597 -0
  118. agentscope_runtime/sandbox/tools/filesystem/__init__.py +32 -0
  119. agentscope_runtime/sandbox/tools/filesystem/tool.py +319 -0
  120. agentscope_runtime/sandbox/tools/function_tool.py +321 -0
  121. agentscope_runtime/sandbox/tools/mcp_tool.py +191 -0
  122. agentscope_runtime/sandbox/tools/sandbox_tool.py +104 -0
  123. agentscope_runtime/sandbox/tools/tool.py +123 -0
  124. agentscope_runtime/sandbox/tools/utils.py +68 -0
  125. agentscope_runtime/version.py +2 -0
  126. agentscope_runtime-0.1.0.dist-info/METADATA +327 -0
  127. agentscope_runtime-0.1.0.dist-info/RECORD +131 -0
  128. agentscope_runtime-0.1.0.dist-info/WHEEL +5 -0
  129. agentscope_runtime-0.1.0.dist-info/entry_points.txt +4 -0
  130. agentscope_runtime-0.1.0.dist-info/licenses/LICENSE +202 -0
  131. agentscope_runtime-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,527 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint: disable=unused-argument
3
+ import logging
4
+ import time
5
+ from typing import Any, Optional
6
+
7
+ import requests
8
+ from pydantic import Field
9
+ from steel import Steel
10
+
11
+ from ..model import ContainerModel
12
+ from ..constant import BROWSER_SESSION_ID
13
+
14
+
15
+ logging.getLogger("httpx").setLevel(logging.CRITICAL)
16
+ logging.basicConfig(level=logging.INFO)
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class SandboxHttpClient:
21
+ """
22
+ A Python client for interacting with the runtime API. Connect with
23
+ container directly.
24
+ """
25
+
26
+ _generic_tools = {
27
+ "run_ipython_cell": {
28
+ "name": "run_ipython_cell",
29
+ "json_schema": {
30
+ "type": "function",
31
+ "function": {
32
+ "name": "run_ipython_cell",
33
+ "description": "Run an IPython cell.",
34
+ "parameters": {
35
+ "type": "object",
36
+ "properties": {
37
+ "code": {
38
+ "type": "string",
39
+ "description": "IPython code to execute",
40
+ },
41
+ },
42
+ "required": ["code"],
43
+ },
44
+ },
45
+ },
46
+ },
47
+ "run_shell_command": {
48
+ "name": "run_shell_command",
49
+ "json_schema": {
50
+ "type": "function",
51
+ "function": {
52
+ "name": "run_shell_command",
53
+ "description": "Run a shell command.",
54
+ "parameters": {
55
+ "type": "object",
56
+ "properties": {
57
+ "command": {
58
+ "type": "string",
59
+ "description": "Shell command to execute",
60
+ },
61
+ },
62
+ "required": ["command"],
63
+ },
64
+ },
65
+ },
66
+ },
67
+ }
68
+
69
+ def __init__(
70
+ self,
71
+ model: Optional[ContainerModel] = None,
72
+ timeout: int = 30,
73
+ enable_browser: bool = True,
74
+ domain: str = "localhost",
75
+ ) -> None:
76
+ """
77
+ Initialize the Python client.
78
+
79
+ Args:
80
+ model (ContainerModel): The pydantic model representing the
81
+ runtime sandbox.
82
+ """
83
+ self.session_id = model.session_id
84
+ self.base_url = model.base_url.replace("localhost", domain)
85
+ self.browser_url = model.browser_url.replace("localhost", domain)
86
+ self.client_browser_ws = model.client_browser_ws.replace(
87
+ "localhost",
88
+ domain,
89
+ )
90
+
91
+ self.enable_browser = enable_browser
92
+ self.timeout = timeout
93
+ self.session = requests.Session()
94
+ self.built_in_tools = []
95
+ self.secret = model.runtime_token
96
+
97
+ # Update headers with secret if provided
98
+ headers = {"Content-Type": "application/json"}
99
+ if self.secret:
100
+ headers["Authorization"] = f"Bearer {self.secret}"
101
+ self.session.headers.update(headers)
102
+
103
+ self.steel_client = None
104
+
105
+ def __enter__(self):
106
+ # Wait for the runtime api server to be healthy
107
+ self.wait_until_healthy()
108
+
109
+ if self.enable_browser:
110
+ self.steel_client = Steel(
111
+ steel_api_key="dummy",
112
+ base_url=self.browser_url,
113
+ )
114
+
115
+ # Create a new browser session if it doesn't exist
116
+ try:
117
+ # Try to connet to existing session
118
+ self.steel_client.sessions.retrieve(
119
+ BROWSER_SESSION_ID,
120
+ )
121
+ except Exception:
122
+ # Session not found, create a new one
123
+ self.steel_client.sessions.create(
124
+ session_id=BROWSER_SESSION_ID,
125
+ )
126
+
127
+ return self
128
+
129
+ def __exit__(self, exc_type, exc_value, traceback):
130
+ pass
131
+
132
+ def check_health(self) -> bool:
133
+ """
134
+ Checks if the runtime service is running by verifying the health
135
+ endpoint.
136
+
137
+ Returns:
138
+ bool: True if the service is reachable, False otherwise
139
+ """
140
+ endpoint = f"{self.base_url}/healthz"
141
+ browser_endpoint = f"{self.browser_url}/v1/health"
142
+ try:
143
+ response_api = self.session.get(endpoint)
144
+ if self.enable_browser:
145
+ response_browser = self.session.get(browser_endpoint)
146
+ return (
147
+ response_api.status_code == 200
148
+ and response_browser.status_code == 200
149
+ )
150
+ else:
151
+ return response_api.status_code == 200
152
+ except requests.RequestException:
153
+ return False
154
+
155
+ def wait_until_healthy(self) -> None:
156
+ """
157
+ Waits until the runtime service is running for a specified timeout.
158
+ """
159
+ start_time = time.time()
160
+ while time.time() - start_time < self.timeout:
161
+ if self.check_health():
162
+ return
163
+ time.sleep(1)
164
+ raise TimeoutError(
165
+ "Runtime service did not start within the specified timeout.",
166
+ )
167
+
168
+ def add_mcp_servers(self, server_configs, overwrite=False):
169
+ """
170
+ Add MCP servers to runtime.
171
+ """
172
+ try:
173
+ endpoint = f"{self.base_url}/mcp/add_servers"
174
+ response = self.session.post(
175
+ endpoint,
176
+ json={
177
+ "server_configs": server_configs,
178
+ "overwrite": overwrite,
179
+ },
180
+ )
181
+ response.raise_for_status()
182
+ return response.text
183
+ except requests.exceptions.RequestException as e:
184
+ logger.error(f"An error occurred while adding MCP servers: {e}")
185
+ return {
186
+ "isError": True,
187
+ "content": [{"type": "text", "text": str(e)}],
188
+ }
189
+
190
+ def list_tools(self, tool_type=None, **kwargs) -> dict:
191
+ try:
192
+ endpoint = f"{self.base_url}/mcp/list_tools"
193
+ response = self.session.get(endpoint)
194
+ response.raise_for_status()
195
+ mcp_tools = response.json()
196
+ mcp_tools["generic"] = self.generic_tools
197
+ if tool_type:
198
+ return {tool_type: mcp_tools.get(tool_type, [])}
199
+ return mcp_tools
200
+ except requests.exceptions.RequestException as e:
201
+ logging.error(f"An error occurred: {e}")
202
+ return {
203
+ "isError": True,
204
+ "content": [{"type": "text", "text": str(e)}],
205
+ }
206
+
207
+ def call_tool(
208
+ self,
209
+ name: str,
210
+ arguments: Optional[dict[str, Any]] = None,
211
+ ) -> dict:
212
+ if arguments is None:
213
+ arguments = {}
214
+
215
+ if name in self.generic_tools:
216
+ if name == "run_ipython_cell":
217
+ return self.run_ipython_cell(**arguments)
218
+ elif name == "run_shell_command":
219
+ return self.run_shell_command(**arguments)
220
+
221
+ try:
222
+ endpoint = f"{self.base_url}/mcp/call_tool"
223
+ response = self.session.post(
224
+ endpoint,
225
+ json={
226
+ "tool_name": name,
227
+ "arguments": arguments,
228
+ },
229
+ )
230
+ response.raise_for_status()
231
+
232
+ return response.json()
233
+ except requests.exceptions.RequestException as e:
234
+ logger.error(f"An error occurred: {e}")
235
+ return {
236
+ "isError": True,
237
+ "content": [{"type": "text", "text": str(e)}],
238
+ }
239
+
240
+ def run_ipython_cell(
241
+ self,
242
+ code: str = Field(
243
+ description="IPython code to execute",
244
+ ),
245
+ ) -> dict:
246
+ """Run an IPython cell."""
247
+ try:
248
+ endpoint = f"{self.base_url}/tools/run_ipython_cell"
249
+ response = self.session.post(endpoint, json={"code": code})
250
+ response.raise_for_status()
251
+ return response.json()
252
+ except requests.exceptions.RequestException as e:
253
+ logger.error(f"An error occurred: {e}")
254
+ return {
255
+ "isError": True,
256
+ "content": [{"type": "text", "text": str(e)}],
257
+ }
258
+
259
+ def run_shell_command(
260
+ self,
261
+ command: str = Field(
262
+ description="Shell command to execute",
263
+ ),
264
+ ) -> dict:
265
+ """Run a shell command."""
266
+ try:
267
+ endpoint = f"{self.base_url}/tools/run_shell_command"
268
+ response = self.session.post(endpoint, json={"command": command})
269
+ response.raise_for_status()
270
+ return response.json()
271
+ except requests.exceptions.RequestException as e:
272
+ logger.error(f"An error occurred: {e}")
273
+ return {
274
+ "isError": True,
275
+ "content": [{"type": "text", "text": str(e)}],
276
+ }
277
+
278
+ @property
279
+ def generic_tools(self) -> dict:
280
+ return self._generic_tools
281
+
282
+ # Below the method is used by API Server
283
+ def commit_changes(self, commit_message: str = "Automated commit") -> dict:
284
+ """
285
+ Commit the uncommitted changes with a given commit message.
286
+ """
287
+ try:
288
+ endpoint = f"{self.base_url}/watcher/commit_changes"
289
+ response = self.session.post(
290
+ endpoint,
291
+ json={"commit_message": commit_message},
292
+ )
293
+ response.raise_for_status()
294
+ return response.json()
295
+ except requests.exceptions.RequestException as e:
296
+ logger.error(f"An error occurred while committing changes: {e}")
297
+ return {
298
+ "isError": True,
299
+ "content": [{"type": "text", "text": str(e)}],
300
+ }
301
+
302
+ def generate_diff(
303
+ self,
304
+ commit_a: Optional[str] = None,
305
+ commit_b: Optional[str] = None,
306
+ ) -> dict:
307
+ """
308
+ Generate the diff between two commits or between uncommitted changes
309
+ and the latest commit.
310
+ """
311
+ try:
312
+ endpoint = f"{self.base_url}/watcher/generate_diff"
313
+ response = self.session.post(
314
+ endpoint,
315
+ json={"commit_a": commit_a, "commit_b": commit_b},
316
+ )
317
+ response.raise_for_status()
318
+ return response.json()
319
+ except requests.exceptions.RequestException as e:
320
+ logger.error(f"An error occurred while generating diff: {e}")
321
+ return {
322
+ "isError": True,
323
+ "content": [{"type": "text", "text": str(e)}],
324
+ }
325
+
326
+ def git_logs(self) -> dict:
327
+ """
328
+ Retrieve the git logs.
329
+ """
330
+ try:
331
+ endpoint = f"{self.base_url}/watcher/git_logs"
332
+ response = self.session.get(endpoint)
333
+ response.raise_for_status()
334
+ return response.json()
335
+ except requests.exceptions.RequestException as e:
336
+ logger.error(f"An error occurred while retrieving git logs: {e}")
337
+ return {
338
+ "isError": True,
339
+ "content": [{"type": "text", "text": str(e)}],
340
+ }
341
+
342
+ def get_workspace_file(self, file_path: str) -> dict:
343
+ """
344
+ Retrieve a file from the /workspace directory.
345
+ """
346
+ try:
347
+ endpoint = f"{self.base_url}/workspace/files"
348
+ params = {"file_path": file_path}
349
+ response = self.session.get(endpoint, params=params)
350
+ response.raise_for_status()
351
+ # Return the binary content of the file
352
+ # Check for empty content
353
+ if response.headers.get("Content-Length") == "0":
354
+ logger.warning(f"The file {file_path} is empty.")
355
+ return {"data": b""}
356
+
357
+ # Accumulate the content in chunks
358
+ file_content = bytearray()
359
+ for chunk in response.iter_content(chunk_size=4096):
360
+ file_content.extend(chunk)
361
+
362
+ return {"data": bytes(file_content)}
363
+ except requests.exceptions.RequestException as e:
364
+ logger.error(f"An error occurred while retrieving the file: {e}")
365
+ return {
366
+ "isError": True,
367
+ "content": [{"type": "text", "text": str(e)}],
368
+ }
369
+
370
+ def create_or_edit_workspace_file(
371
+ self,
372
+ file_path: str,
373
+ content: str,
374
+ ) -> dict:
375
+ """
376
+ Create or edit a file within the /workspace directory.
377
+ """
378
+ try:
379
+ endpoint = f"{self.base_url}/workspace/files"
380
+ params = {"file_path": file_path}
381
+ data = {"content": content}
382
+ response = self.session.post(endpoint, params=params, json=data)
383
+ response.raise_for_status()
384
+ return response.json()
385
+ except requests.exceptions.RequestException as e:
386
+ logger.error(
387
+ f"An error occurred while creating or editing a workspace "
388
+ f"file: {e}",
389
+ )
390
+ return {
391
+ "isError": True,
392
+ "content": [{"type": "text", "text": str(e)}],
393
+ }
394
+
395
+ def list_workspace_directories(
396
+ self,
397
+ directory: str = "/workspace",
398
+ ) -> dict:
399
+ """
400
+ List files in the specified directory within the /workspace.
401
+ """
402
+ try:
403
+ endpoint = f"{self.base_url}/workspace/list-directories"
404
+ params = {"directory": directory}
405
+ response = self.session.get(endpoint, params=params)
406
+ response.raise_for_status()
407
+ return response.json()
408
+ except requests.exceptions.RequestException as e:
409
+ logger.error(f"An error occurred while listing files: {e}")
410
+ return {
411
+ "isError": True,
412
+ "content": [{"type": "text", "text": str(e)}],
413
+ }
414
+
415
+ def create_workspace_directory(self, directory_path: str) -> dict:
416
+ """
417
+ Create a directory within the /workspace directory.
418
+ """
419
+ try:
420
+ endpoint = f"{self.base_url}/workspace/directories"
421
+ params = {"directory_path": directory_path}
422
+ response = self.session.post(endpoint, params=params)
423
+ response.raise_for_status()
424
+ return response.json()
425
+ except requests.exceptions.RequestException as e:
426
+ logger.error(
427
+ f"An error occurred while creating a workspace directory: {e}",
428
+ )
429
+ return {
430
+ "isError": True,
431
+ "content": [{"type": "text", "text": str(e)}],
432
+ }
433
+
434
+ def delete_workspace_file(self, file_path: str) -> dict:
435
+ """
436
+ Delete a file within the /workspace directory.
437
+ """
438
+ try:
439
+ endpoint = f"{self.base_url}/workspace/files"
440
+ params = {"file_path": file_path}
441
+ response = self.session.delete(endpoint, params=params)
442
+ response.raise_for_status()
443
+ return response.json()
444
+ except requests.exceptions.RequestException as e:
445
+ logger.error(
446
+ f"An error occurred while deleting a workspace file: {e}",
447
+ )
448
+ return {
449
+ "isError": True,
450
+ "content": [{"type": "text", "text": str(e)}],
451
+ }
452
+
453
+ def delete_workspace_directory(
454
+ self,
455
+ directory_path: str,
456
+ recursive: bool = False,
457
+ ) -> dict:
458
+ """
459
+ Delete a directory within the /workspace directory.
460
+ """
461
+ try:
462
+ endpoint = f"{self.base_url}/workspace/directories"
463
+ params = {"directory_path": directory_path, "recursive": recursive}
464
+ response = self.session.delete(endpoint, params=params)
465
+ response.raise_for_status()
466
+ return response.json()
467
+ except requests.exceptions.RequestException as e:
468
+ logger.error(
469
+ f"An error occurred while deleting a workspace directory: {e}",
470
+ )
471
+ return {
472
+ "isError": True,
473
+ "content": [{"type": "text", "text": str(e)}],
474
+ }
475
+
476
+ def move_or_rename_workspace_item(
477
+ self,
478
+ source_path: str,
479
+ destination_path: str,
480
+ ) -> dict:
481
+ """
482
+ Move or rename a file or directory within the /workspace directory.
483
+ """
484
+ try:
485
+ endpoint = f"{self.base_url}/workspace/move"
486
+ params = {
487
+ "source_path": source_path,
488
+ "destination_path": destination_path,
489
+ }
490
+ response = self.session.put(endpoint, params=params)
491
+ response.raise_for_status()
492
+ return response.json()
493
+ except requests.exceptions.RequestException as e:
494
+ logger.error(
495
+ f"An error occurred while moving or renaming a workspace "
496
+ f"item: {e}",
497
+ )
498
+ return {
499
+ "isError": True,
500
+ "content": [{"type": "text", "text": str(e)}],
501
+ }
502
+
503
+ def copy_workspace_item(
504
+ self,
505
+ source_path: str,
506
+ destination_path: str,
507
+ ) -> dict:
508
+ """
509
+ Copy a file or directory within the /workspace directory.
510
+ """
511
+ try:
512
+ endpoint = f"{self.base_url}/workspace/copy"
513
+ params = {
514
+ "source_path": source_path,
515
+ "destination_path": destination_path,
516
+ }
517
+ response = self.session.post(endpoint, params=params)
518
+ response.raise_for_status()
519
+ return response.json()
520
+ except requests.exceptions.RequestException as e:
521
+ logger.error(
522
+ f"An error occurred while copying a workspace item: {e}",
523
+ )
524
+ return {
525
+ "isError": True,
526
+ "content": [{"type": "text", "text": str(e)}],
527
+ }