agentrun-inner-test 0.0.62__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.
Potentially problematic release.
This version of agentrun-inner-test might be problematic. Click here for more details.
- agentrun/__init__.py +358 -0
- agentrun/agent_runtime/__client_async_template.py +466 -0
- agentrun/agent_runtime/__endpoint_async_template.py +345 -0
- agentrun/agent_runtime/__init__.py +53 -0
- agentrun/agent_runtime/__runtime_async_template.py +477 -0
- agentrun/agent_runtime/api/__data_async_template.py +58 -0
- agentrun/agent_runtime/api/__init__.py +6 -0
- agentrun/agent_runtime/api/control.py +1362 -0
- agentrun/agent_runtime/api/data.py +98 -0
- agentrun/agent_runtime/client.py +868 -0
- agentrun/agent_runtime/endpoint.py +649 -0
- agentrun/agent_runtime/model.py +362 -0
- agentrun/agent_runtime/runtime.py +904 -0
- agentrun/credential/__client_async_template.py +177 -0
- agentrun/credential/__credential_async_template.py +216 -0
- agentrun/credential/__init__.py +28 -0
- agentrun/credential/api/__init__.py +5 -0
- agentrun/credential/api/control.py +606 -0
- agentrun/credential/client.py +319 -0
- agentrun/credential/credential.py +381 -0
- agentrun/credential/model.py +248 -0
- agentrun/integration/__init__.py +21 -0
- agentrun/integration/agentscope/__init__.py +13 -0
- agentrun/integration/agentscope/adapter.py +17 -0
- agentrun/integration/agentscope/builtin.py +88 -0
- agentrun/integration/agentscope/message_adapter.py +185 -0
- agentrun/integration/agentscope/model_adapter.py +60 -0
- agentrun/integration/agentscope/tool_adapter.py +59 -0
- agentrun/integration/builtin/__init__.py +18 -0
- agentrun/integration/builtin/knowledgebase.py +137 -0
- agentrun/integration/builtin/model.py +93 -0
- agentrun/integration/builtin/sandbox.py +1234 -0
- agentrun/integration/builtin/toolset.py +47 -0
- agentrun/integration/crewai/__init__.py +13 -0
- agentrun/integration/crewai/adapter.py +9 -0
- agentrun/integration/crewai/builtin.py +88 -0
- agentrun/integration/crewai/model_adapter.py +31 -0
- agentrun/integration/crewai/tool_adapter.py +26 -0
- agentrun/integration/google_adk/__init__.py +13 -0
- agentrun/integration/google_adk/adapter.py +15 -0
- agentrun/integration/google_adk/builtin.py +88 -0
- agentrun/integration/google_adk/message_adapter.py +144 -0
- agentrun/integration/google_adk/model_adapter.py +46 -0
- agentrun/integration/google_adk/tool_adapter.py +235 -0
- agentrun/integration/langchain/__init__.py +31 -0
- agentrun/integration/langchain/adapter.py +15 -0
- agentrun/integration/langchain/builtin.py +94 -0
- agentrun/integration/langchain/message_adapter.py +141 -0
- agentrun/integration/langchain/model_adapter.py +37 -0
- agentrun/integration/langchain/tool_adapter.py +50 -0
- agentrun/integration/langgraph/__init__.py +36 -0
- agentrun/integration/langgraph/adapter.py +20 -0
- agentrun/integration/langgraph/agent_converter.py +1073 -0
- agentrun/integration/langgraph/builtin.py +88 -0
- agentrun/integration/pydantic_ai/__init__.py +13 -0
- agentrun/integration/pydantic_ai/adapter.py +13 -0
- agentrun/integration/pydantic_ai/builtin.py +88 -0
- agentrun/integration/pydantic_ai/model_adapter.py +44 -0
- agentrun/integration/pydantic_ai/tool_adapter.py +19 -0
- agentrun/integration/utils/__init__.py +112 -0
- agentrun/integration/utils/adapter.py +560 -0
- agentrun/integration/utils/canonical.py +164 -0
- agentrun/integration/utils/converter.py +134 -0
- agentrun/integration/utils/model.py +110 -0
- agentrun/integration/utils/tool.py +1759 -0
- agentrun/knowledgebase/__client_async_template.py +173 -0
- agentrun/knowledgebase/__init__.py +53 -0
- agentrun/knowledgebase/__knowledgebase_async_template.py +438 -0
- agentrun/knowledgebase/api/__data_async_template.py +414 -0
- agentrun/knowledgebase/api/__init__.py +19 -0
- agentrun/knowledgebase/api/control.py +606 -0
- agentrun/knowledgebase/api/data.py +624 -0
- agentrun/knowledgebase/client.py +311 -0
- agentrun/knowledgebase/knowledgebase.py +748 -0
- agentrun/knowledgebase/model.py +270 -0
- agentrun/memory_collection/__client_async_template.py +178 -0
- agentrun/memory_collection/__init__.py +37 -0
- agentrun/memory_collection/__memory_collection_async_template.py +457 -0
- agentrun/memory_collection/api/__init__.py +5 -0
- agentrun/memory_collection/api/control.py +610 -0
- agentrun/memory_collection/client.py +323 -0
- agentrun/memory_collection/memory_collection.py +844 -0
- agentrun/memory_collection/model.py +162 -0
- agentrun/model/__client_async_template.py +357 -0
- agentrun/model/__init__.py +57 -0
- agentrun/model/__model_proxy_async_template.py +270 -0
- agentrun/model/__model_service_async_template.py +267 -0
- agentrun/model/api/__init__.py +6 -0
- agentrun/model/api/control.py +1173 -0
- agentrun/model/api/data.py +196 -0
- agentrun/model/client.py +674 -0
- agentrun/model/model.py +235 -0
- agentrun/model/model_proxy.py +439 -0
- agentrun/model/model_service.py +438 -0
- agentrun/sandbox/__aio_sandbox_async_template.py +523 -0
- agentrun/sandbox/__browser_sandbox_async_template.py +110 -0
- agentrun/sandbox/__client_async_template.py +491 -0
- agentrun/sandbox/__code_interpreter_sandbox_async_template.py +463 -0
- agentrun/sandbox/__init__.py +69 -0
- agentrun/sandbox/__sandbox_async_template.py +463 -0
- agentrun/sandbox/__template_async_template.py +152 -0
- agentrun/sandbox/aio_sandbox.py +912 -0
- agentrun/sandbox/api/__aio_data_async_template.py +335 -0
- agentrun/sandbox/api/__browser_data_async_template.py +140 -0
- agentrun/sandbox/api/__code_interpreter_data_async_template.py +206 -0
- agentrun/sandbox/api/__init__.py +19 -0
- agentrun/sandbox/api/__sandbox_data_async_template.py +107 -0
- agentrun/sandbox/api/aio_data.py +551 -0
- agentrun/sandbox/api/browser_data.py +172 -0
- agentrun/sandbox/api/code_interpreter_data.py +396 -0
- agentrun/sandbox/api/control.py +1051 -0
- agentrun/sandbox/api/playwright_async.py +492 -0
- agentrun/sandbox/api/playwright_sync.py +492 -0
- agentrun/sandbox/api/sandbox_data.py +154 -0
- agentrun/sandbox/browser_sandbox.py +185 -0
- agentrun/sandbox/client.py +925 -0
- agentrun/sandbox/code_interpreter_sandbox.py +823 -0
- agentrun/sandbox/model.py +384 -0
- agentrun/sandbox/sandbox.py +848 -0
- agentrun/sandbox/template.py +217 -0
- agentrun/server/__init__.py +191 -0
- agentrun/server/agui_normalizer.py +180 -0
- agentrun/server/agui_protocol.py +797 -0
- agentrun/server/invoker.py +309 -0
- agentrun/server/model.py +427 -0
- agentrun/server/openai_protocol.py +535 -0
- agentrun/server/protocol.py +140 -0
- agentrun/server/server.py +208 -0
- agentrun/toolset/__client_async_template.py +62 -0
- agentrun/toolset/__init__.py +51 -0
- agentrun/toolset/__toolset_async_template.py +204 -0
- agentrun/toolset/api/__init__.py +17 -0
- agentrun/toolset/api/control.py +262 -0
- agentrun/toolset/api/mcp.py +100 -0
- agentrun/toolset/api/openapi.py +1251 -0
- agentrun/toolset/client.py +102 -0
- agentrun/toolset/model.py +321 -0
- agentrun/toolset/toolset.py +271 -0
- agentrun/utils/__data_api_async_template.py +721 -0
- agentrun/utils/__init__.py +5 -0
- agentrun/utils/__resource_async_template.py +158 -0
- agentrun/utils/config.py +270 -0
- agentrun/utils/control_api.py +105 -0
- agentrun/utils/data_api.py +1121 -0
- agentrun/utils/exception.py +151 -0
- agentrun/utils/helper.py +108 -0
- agentrun/utils/log.py +77 -0
- agentrun/utils/model.py +168 -0
- agentrun/utils/resource.py +291 -0
- agentrun_inner_test-0.0.62.dist-info/METADATA +265 -0
- agentrun_inner_test-0.0.62.dist-info/RECORD +154 -0
- agentrun_inner_test-0.0.62.dist-info/WHEEL +5 -0
- agentrun_inner_test-0.0.62.dist-info/licenses/LICENSE +201 -0
- agentrun_inner_test-0.0.62.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
"""All-in-One 沙箱高层API模板 / All-in-One Sandbox High-Level API Template
|
|
2
|
+
|
|
3
|
+
此模板用于生成 All-in-One 沙箱资源的高级API代码,结合了浏览器和代码解释器的功能。
|
|
4
|
+
This template is used to generate high-level API code for All-in-One sandbox resources,
|
|
5
|
+
combining browser and code interpreter capabilities.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import time # noqa: F401
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from agentrun.sandbox.api.aio_data import AioDataAPI
|
|
13
|
+
from agentrun.sandbox.model import CodeLanguage, TemplateType
|
|
14
|
+
from agentrun.utils.exception import ServerError
|
|
15
|
+
from agentrun.utils.log import logger
|
|
16
|
+
|
|
17
|
+
from .sandbox import Sandbox
|
|
18
|
+
|
|
19
|
+
# ========================================
|
|
20
|
+
# Code Interpreter Helper Classes
|
|
21
|
+
# ========================================
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class FileOperations:
|
|
25
|
+
"""File upload/download operations."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, sandbox: "AioSandbox"):
|
|
28
|
+
self._sandbox = sandbox
|
|
29
|
+
|
|
30
|
+
async def read_async(self, path: str):
|
|
31
|
+
"""Read a file from the sandbox (async).
|
|
32
|
+
Args:
|
|
33
|
+
path: Remote file path in the sandbox
|
|
34
|
+
Returns:
|
|
35
|
+
File content
|
|
36
|
+
"""
|
|
37
|
+
return await self._sandbox.data_api.read_file_async(path=path)
|
|
38
|
+
|
|
39
|
+
async def write_async(
|
|
40
|
+
self,
|
|
41
|
+
path: str,
|
|
42
|
+
content: str,
|
|
43
|
+
mode: str = "644",
|
|
44
|
+
encoding: str = "utf-8",
|
|
45
|
+
create_dir=True,
|
|
46
|
+
):
|
|
47
|
+
"""Write a file to the sandbox (async).
|
|
48
|
+
Args:
|
|
49
|
+
path: Remote file path in the sandbox
|
|
50
|
+
content: File content
|
|
51
|
+
"""
|
|
52
|
+
return await self._sandbox.data_api.write_file_async(
|
|
53
|
+
path=path,
|
|
54
|
+
content=content,
|
|
55
|
+
mode=mode,
|
|
56
|
+
encoding=encoding,
|
|
57
|
+
create_dir=create_dir,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class FileSystemOperations:
|
|
62
|
+
"""File system operations (list, move, remove, stat, mkdir)."""
|
|
63
|
+
|
|
64
|
+
def __init__(self, sandbox: "AioSandbox"):
|
|
65
|
+
self._sandbox = sandbox
|
|
66
|
+
|
|
67
|
+
async def list_async(
|
|
68
|
+
self, path: Optional[str] = None, depth: Optional[int] = None
|
|
69
|
+
):
|
|
70
|
+
"""List directory contents (async).
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
path: Directory path (optional)
|
|
74
|
+
depth: Traversal depth (optional)
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Directory contents
|
|
78
|
+
"""
|
|
79
|
+
return await self._sandbox.data_api.list_directory_async(
|
|
80
|
+
path=path, depth=depth
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
async def move_async(self, source: str, destination: str):
|
|
84
|
+
"""Move a file or directory (async).
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
source: Source file or directory path
|
|
88
|
+
destination: Target file or directory path
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Move operation result
|
|
92
|
+
"""
|
|
93
|
+
return await self._sandbox.data_api.move_file_async(
|
|
94
|
+
source=source, destination=destination
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
async def remove_async(self, path: str):
|
|
98
|
+
"""Remove a file or directory (async).
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
path: File or directory path to remove
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Remove operation result
|
|
105
|
+
"""
|
|
106
|
+
return await self._sandbox.data_api.remove_file_async(path=path)
|
|
107
|
+
|
|
108
|
+
async def stat_async(self, path: str):
|
|
109
|
+
"""Get file or directory statistics (async).
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
path: File or directory path
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
File/directory statistics
|
|
116
|
+
"""
|
|
117
|
+
return await self._sandbox.data_api.stat_async(path=path)
|
|
118
|
+
|
|
119
|
+
async def mkdir_async(
|
|
120
|
+
self,
|
|
121
|
+
path: str,
|
|
122
|
+
parents: Optional[bool] = True,
|
|
123
|
+
mode: Optional[str] = "0755",
|
|
124
|
+
):
|
|
125
|
+
"""Create a directory (async).
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
path: Directory path to create
|
|
129
|
+
parents: Whether to create parent directories (default: True)
|
|
130
|
+
mode: Directory permissions mode (default: "0755")
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Mkdir operation result
|
|
134
|
+
"""
|
|
135
|
+
return await self._sandbox.data_api.mkdir_async(
|
|
136
|
+
path=path, parents=parents, mode=mode
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
async def upload_async(
|
|
140
|
+
self,
|
|
141
|
+
local_file_path: str,
|
|
142
|
+
target_file_path: str,
|
|
143
|
+
):
|
|
144
|
+
"""Upload a file to the sandbox (async).
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
local_file_path: Local file path to upload
|
|
148
|
+
target_file_path: Target file path in sandbox
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Upload result
|
|
152
|
+
"""
|
|
153
|
+
return await self._sandbox.data_api.upload_file_async(
|
|
154
|
+
local_file_path=local_file_path, target_file_path=target_file_path
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
async def download_async(self, path: str, save_path: str):
|
|
158
|
+
"""Download a file from the sandbox (async).
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
path: Remote file path in the sandbox
|
|
162
|
+
save_path: Local file path to save the downloaded file
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Download result with 'saved_path' and 'size'
|
|
166
|
+
"""
|
|
167
|
+
return await self._sandbox.data_api.download_file_async(
|
|
168
|
+
path=path, save_path=save_path
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class ProcessOperations:
|
|
173
|
+
"""Process management operations."""
|
|
174
|
+
|
|
175
|
+
def __init__(self, sandbox: "AioSandbox"):
|
|
176
|
+
self._sandbox = sandbox
|
|
177
|
+
|
|
178
|
+
async def cmd_async(
|
|
179
|
+
self, command: str, cwd: str, timeout: Optional[int] = 30
|
|
180
|
+
):
|
|
181
|
+
"""Execute a command in the sandbox (async).
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
command: Command to execute
|
|
185
|
+
cwd: Working directory
|
|
186
|
+
timeout: Execution timeout in seconds (default: 30)
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Command execution result
|
|
190
|
+
"""
|
|
191
|
+
return await self._sandbox.data_api.cmd_async(
|
|
192
|
+
command=command, cwd=cwd, timeout=timeout
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
async def list_async(self):
|
|
196
|
+
"""List all processes (async).
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
List of processes
|
|
200
|
+
"""
|
|
201
|
+
return await self._sandbox.data_api.list_processes_async()
|
|
202
|
+
|
|
203
|
+
async def get_async(self, pid: str):
|
|
204
|
+
"""Get a specific process by PID (async).
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
pid: Process ID
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
Process information
|
|
211
|
+
"""
|
|
212
|
+
return await self._sandbox.data_api.get_process_async(pid=pid)
|
|
213
|
+
|
|
214
|
+
async def kill_async(self, pid: str):
|
|
215
|
+
"""Kill a specific process by PID (async).
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
pid: Process ID
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Kill operation result
|
|
222
|
+
"""
|
|
223
|
+
return await self._sandbox.data_api.kill_process_async(pid=pid)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class ContextOperations:
|
|
227
|
+
"""Context management operations."""
|
|
228
|
+
|
|
229
|
+
def __init__(self, sandbox: "AioSandbox"):
|
|
230
|
+
self._sandbox = sandbox
|
|
231
|
+
self._context_id: Optional[str] = None
|
|
232
|
+
self._language: Optional[str] = None
|
|
233
|
+
self._cwd: Optional[str] = None
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def context_id(self) -> Optional[str]:
|
|
237
|
+
"""Get the current context ID."""
|
|
238
|
+
return self._context_id
|
|
239
|
+
|
|
240
|
+
async def list_async(self):
|
|
241
|
+
"""List all contexts (async)."""
|
|
242
|
+
return await self._sandbox.data_api.list_contexts_async()
|
|
243
|
+
|
|
244
|
+
async def create_async(
|
|
245
|
+
self,
|
|
246
|
+
language: Optional[CodeLanguage] = CodeLanguage.PYTHON,
|
|
247
|
+
cwd: str = "/home/user",
|
|
248
|
+
) -> "ContextOperations":
|
|
249
|
+
"""Create a new context and save its ID (async).
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
language: Programming language (default: "python")
|
|
253
|
+
cwd: Working directory (default: "/home/user")
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
ContextOperations: Returns self for chaining and context manager support
|
|
257
|
+
"""
|
|
258
|
+
result = await self._sandbox.data_api.create_context_async(
|
|
259
|
+
language=language, cwd=cwd
|
|
260
|
+
)
|
|
261
|
+
if all(result.get(key) for key in ("id", "cwd", "language")):
|
|
262
|
+
self._context_id = result["id"]
|
|
263
|
+
self._language = result["language"]
|
|
264
|
+
self._cwd = result["cwd"]
|
|
265
|
+
return self
|
|
266
|
+
raise ServerError(500, "Failed to create context")
|
|
267
|
+
|
|
268
|
+
async def get_async(
|
|
269
|
+
self, context_id: Optional[str] = None
|
|
270
|
+
) -> "ContextOperations":
|
|
271
|
+
"""Get a specific context by ID (async).
|
|
272
|
+
Args:
|
|
273
|
+
context_id: Context ID
|
|
274
|
+
Returns:
|
|
275
|
+
ContextOperations: Returns self for chaining and context manager support
|
|
276
|
+
"""
|
|
277
|
+
if context_id is None:
|
|
278
|
+
context_id = self._context_id
|
|
279
|
+
if context_id is None:
|
|
280
|
+
logger.error(f"context id is not set")
|
|
281
|
+
raise ValueError("context id is not set,")
|
|
282
|
+
result = await self._sandbox.data_api.get_context_async(
|
|
283
|
+
context_id=context_id
|
|
284
|
+
)
|
|
285
|
+
if all(result.get(key) for key in ("id", "cwd", "language")):
|
|
286
|
+
self._context_id = result["id"]
|
|
287
|
+
self._language = result["language"]
|
|
288
|
+
self._cwd = result["cwd"]
|
|
289
|
+
return self
|
|
290
|
+
raise ServerError(500, "Failed to create context")
|
|
291
|
+
|
|
292
|
+
async def execute_async(
|
|
293
|
+
self,
|
|
294
|
+
code: str,
|
|
295
|
+
language: Optional[CodeLanguage] = None,
|
|
296
|
+
context_id: Optional[str] = None,
|
|
297
|
+
timeout: Optional[int] = 30,
|
|
298
|
+
):
|
|
299
|
+
"""Execute code in a context (async).
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
code: Code to execute
|
|
303
|
+
language: Programming language (optional)
|
|
304
|
+
context_id: Context ID (optional, uses saved context_id if not provided)
|
|
305
|
+
timeout: Execution timeout in seconds (default: 30)
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
Execution result
|
|
309
|
+
|
|
310
|
+
Raises:
|
|
311
|
+
ValueError: If no context_id is provided and none is saved
|
|
312
|
+
"""
|
|
313
|
+
if context_id is None:
|
|
314
|
+
context_id = self._context_id
|
|
315
|
+
if context_id is None and language is None:
|
|
316
|
+
logger.debug("context id is not set, use default language: python")
|
|
317
|
+
language = CodeLanguage.PYTHON
|
|
318
|
+
return await self._sandbox.data_api.execute_code_async(
|
|
319
|
+
context_id=context_id, language=language, code=code, timeout=timeout
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
async def delete_async(self, context_id: Optional[str] = None):
|
|
323
|
+
"""Delete a context (async).
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
context_id: Context ID (optional, uses saved context_id if not provided)
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
Delete result
|
|
330
|
+
|
|
331
|
+
Raises:
|
|
332
|
+
ValueError: If no context_id is provided and none is saved
|
|
333
|
+
"""
|
|
334
|
+
if context_id is None:
|
|
335
|
+
context_id = self._context_id
|
|
336
|
+
if context_id is None:
|
|
337
|
+
raise ValueError(
|
|
338
|
+
"context_id is required. Either pass it as parameter or create"
|
|
339
|
+
" a context first."
|
|
340
|
+
)
|
|
341
|
+
result = await self._sandbox.data_api.delete_context_async(
|
|
342
|
+
context_id=context_id
|
|
343
|
+
)
|
|
344
|
+
# Clear the saved context_id after deletion
|
|
345
|
+
self._context_id = None
|
|
346
|
+
return result
|
|
347
|
+
|
|
348
|
+
async def __aenter__(self):
|
|
349
|
+
"""Asynchronous context manager entry."""
|
|
350
|
+
if self._context_id is None:
|
|
351
|
+
raise ValueError(
|
|
352
|
+
"No context has been created. Call create() first or use: "
|
|
353
|
+
"async with await sandbox.context.create_async(...) as ctx:"
|
|
354
|
+
)
|
|
355
|
+
return self
|
|
356
|
+
|
|
357
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
358
|
+
"""Asynchronous context manager exit - deletes the context."""
|
|
359
|
+
if self._context_id is not None:
|
|
360
|
+
try:
|
|
361
|
+
await self._sandbox.data_api.delete_context_async(
|
|
362
|
+
self._context_id
|
|
363
|
+
)
|
|
364
|
+
except Exception as e:
|
|
365
|
+
logger.error(
|
|
366
|
+
f"Warning: Failed to delete context {self._context_id}: {e}"
|
|
367
|
+
)
|
|
368
|
+
return False
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
class AioSandbox(Sandbox):
|
|
372
|
+
"""All-in-One Sandbox combining Browser and Code Interpreter capabilities.
|
|
373
|
+
|
|
374
|
+
This class combines the functionality of BrowserSandbox and CodeInterpreterSandbox,
|
|
375
|
+
providing a unified interface for all-in-one sandbox operations.
|
|
376
|
+
"""
|
|
377
|
+
|
|
378
|
+
_template_type = TemplateType.AIO
|
|
379
|
+
|
|
380
|
+
_data_api: Optional["AioDataAPI"] = None
|
|
381
|
+
_file: Optional[FileOperations] = None
|
|
382
|
+
_file_system: Optional[FileSystemOperations] = None
|
|
383
|
+
_context: Optional[ContextOperations] = None
|
|
384
|
+
_process: Optional[ProcessOperations] = None
|
|
385
|
+
|
|
386
|
+
async def __aenter__(self):
|
|
387
|
+
"""Asynchronous context manager entry."""
|
|
388
|
+
# Poll health check asynchronously
|
|
389
|
+
max_retries = 60 # Maximum 60 seconds
|
|
390
|
+
retry_count = 0
|
|
391
|
+
|
|
392
|
+
logger.debug("Waiting for All-in-One sandbox to be ready...")
|
|
393
|
+
|
|
394
|
+
while retry_count < max_retries:
|
|
395
|
+
retry_count += 1
|
|
396
|
+
|
|
397
|
+
try:
|
|
398
|
+
health = await self.check_health_async()
|
|
399
|
+
|
|
400
|
+
if health["status"] == "ok":
|
|
401
|
+
logger.debug(
|
|
402
|
+
"✓ All-in-One sandbox is ready! (took"
|
|
403
|
+
f" {retry_count} seconds)"
|
|
404
|
+
)
|
|
405
|
+
return self
|
|
406
|
+
|
|
407
|
+
logger.debug(
|
|
408
|
+
"[%d/%d] Health status: %d %s",
|
|
409
|
+
retry_count,
|
|
410
|
+
max_retries,
|
|
411
|
+
health.get("code", 0),
|
|
412
|
+
health.get("message", "not ready"),
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
except Exception as e:
|
|
416
|
+
logger.error(
|
|
417
|
+
f"[{retry_count}/{max_retries}] Health check failed: {e}"
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
if retry_count < max_retries:
|
|
421
|
+
await asyncio.sleep(1)
|
|
422
|
+
|
|
423
|
+
raise RuntimeError(
|
|
424
|
+
f"Health check timeout after {max_retries} seconds. "
|
|
425
|
+
"All-in-One sandbox did not become ready in time."
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
429
|
+
"""Asynchronous context manager exit."""
|
|
430
|
+
if self.sandbox_id is None:
|
|
431
|
+
raise ValueError("Sandbox ID is not set")
|
|
432
|
+
logger.debug(f"Deleting All-in-One sandbox {self.sandbox_id}...")
|
|
433
|
+
await self.delete_async()
|
|
434
|
+
|
|
435
|
+
@property
|
|
436
|
+
def data_api(self) -> "AioDataAPI":
|
|
437
|
+
"""Get data client."""
|
|
438
|
+
if self._data_api is None:
|
|
439
|
+
if self.sandbox_id is None:
|
|
440
|
+
raise ValueError("Sandbox ID is not set")
|
|
441
|
+
|
|
442
|
+
self._data_api = AioDataAPI(
|
|
443
|
+
sandbox_id=self.sandbox_id, config=self._config
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
return self._data_api
|
|
447
|
+
|
|
448
|
+
async def check_health_async(self):
|
|
449
|
+
"""Check sandbox health status (async)."""
|
|
450
|
+
return await self.data_api.check_health_async()
|
|
451
|
+
|
|
452
|
+
# ========================================
|
|
453
|
+
# Browser API Methods
|
|
454
|
+
# ========================================
|
|
455
|
+
|
|
456
|
+
def get_cdp_url(self, record: Optional[bool] = False):
|
|
457
|
+
"""Get CDP WebSocket URL for browser automation."""
|
|
458
|
+
return self.data_api.get_cdp_url(record=record)
|
|
459
|
+
|
|
460
|
+
def get_vnc_url(self, record: Optional[bool] = False):
|
|
461
|
+
"""Get VNC WebSocket URL for live view."""
|
|
462
|
+
return self.data_api.get_vnc_url(record=record)
|
|
463
|
+
|
|
464
|
+
def sync_playwright(self, record: Optional[bool] = False):
|
|
465
|
+
"""Get synchronous Playwright browser instance."""
|
|
466
|
+
return self.data_api.sync_playwright(record=record)
|
|
467
|
+
|
|
468
|
+
def async_playwright(self, record: Optional[bool] = False):
|
|
469
|
+
"""Get asynchronous Playwright browser instance."""
|
|
470
|
+
return self.data_api.async_playwright(record=record)
|
|
471
|
+
|
|
472
|
+
async def list_recordings_async(self):
|
|
473
|
+
"""List all recordings (async)."""
|
|
474
|
+
return await self.data_api.list_recordings_async()
|
|
475
|
+
|
|
476
|
+
async def download_recording_async(self, filename: str, save_path: str):
|
|
477
|
+
"""
|
|
478
|
+
Asynchronously download a recording video file and save it to local path.
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
filename: The name of the recording file to download
|
|
482
|
+
save_path: Local file path to save the downloaded video file (.mkv)
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
Dictionary with 'saved_path' and 'size' keys
|
|
486
|
+
"""
|
|
487
|
+
return await self.data_api.download_recording_async(filename, save_path)
|
|
488
|
+
|
|
489
|
+
async def delete_recording_async(self, filename: str):
|
|
490
|
+
"""Delete a recording file (async)."""
|
|
491
|
+
return await self.data_api.delete_recording_async(filename)
|
|
492
|
+
|
|
493
|
+
# ========================================
|
|
494
|
+
# Code Interpreter API Properties
|
|
495
|
+
# ========================================
|
|
496
|
+
|
|
497
|
+
@property
|
|
498
|
+
def file(self) -> FileOperations:
|
|
499
|
+
"""Access file upload/download operations."""
|
|
500
|
+
if self._file is None:
|
|
501
|
+
self._file = FileOperations(self)
|
|
502
|
+
return self._file
|
|
503
|
+
|
|
504
|
+
@property
|
|
505
|
+
def file_system(self) -> FileSystemOperations:
|
|
506
|
+
"""Access file system operations."""
|
|
507
|
+
if self._file_system is None:
|
|
508
|
+
self._file_system = FileSystemOperations(self)
|
|
509
|
+
return self._file_system
|
|
510
|
+
|
|
511
|
+
@property
|
|
512
|
+
def context(self) -> ContextOperations:
|
|
513
|
+
"""Access context management operations."""
|
|
514
|
+
if self._context is None:
|
|
515
|
+
self._context = ContextOperations(self)
|
|
516
|
+
return self._context
|
|
517
|
+
|
|
518
|
+
@property
|
|
519
|
+
def process(self) -> ProcessOperations:
|
|
520
|
+
"""Access process management operations."""
|
|
521
|
+
if self._process is None:
|
|
522
|
+
self._process = ProcessOperations(self)
|
|
523
|
+
return self._process
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""浏览器沙箱高层API模板 / Browser Sandbox High-Level API Template
|
|
2
|
+
|
|
3
|
+
此模板用于生成浏览器沙箱资源的高级API代码。
|
|
4
|
+
This template is used to generate high-level API code for browser sandbox resources.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import time # noqa: F401
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from agentrun.sandbox.api import BrowserDataAPI
|
|
12
|
+
from agentrun.sandbox.model import TemplateType
|
|
13
|
+
from agentrun.utils.log import logger
|
|
14
|
+
|
|
15
|
+
from .sandbox import Sandbox
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BrowserSandbox(Sandbox):
|
|
19
|
+
_template_type = TemplateType.BROWSER
|
|
20
|
+
|
|
21
|
+
_data_api: Optional["BrowserDataAPI"] = None
|
|
22
|
+
|
|
23
|
+
async def __aenter__(self):
|
|
24
|
+
# Poll health check asynchronously
|
|
25
|
+
max_retries = 60 # Maximum 60 seconds
|
|
26
|
+
retry_count = 0
|
|
27
|
+
|
|
28
|
+
logger.debug("Waiting for browser to be ready...")
|
|
29
|
+
|
|
30
|
+
while retry_count < max_retries:
|
|
31
|
+
retry_count += 1
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
health = await self.check_health_async()
|
|
35
|
+
|
|
36
|
+
if health["status"] == "ok":
|
|
37
|
+
logger.debug(
|
|
38
|
+
f"✓ Browser is ready! (took {retry_count} seconds)"
|
|
39
|
+
)
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
logger.debug(
|
|
43
|
+
f"[{retry_count}/{max_retries}] Health status:"
|
|
44
|
+
f" {health.get('code')} {health.get('message')}",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
except Exception as e:
|
|
48
|
+
logger.error(
|
|
49
|
+
f"[{retry_count}/{max_retries}] Health check failed: {e}"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if retry_count < max_retries:
|
|
53
|
+
await asyncio.sleep(1)
|
|
54
|
+
|
|
55
|
+
raise RuntimeError(
|
|
56
|
+
f"Health check timeout after {max_retries} seconds. "
|
|
57
|
+
"Browser did not become ready in time."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
61
|
+
if self.sandbox_id is None:
|
|
62
|
+
raise ValueError("Sandbox ID is not set")
|
|
63
|
+
logger.debug(f"Deleting browser sandbox {self.sandbox_id}...")
|
|
64
|
+
await self.delete_async()
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def data_api(self):
|
|
68
|
+
if self._data_api is None:
|
|
69
|
+
if self.sandbox_id is None:
|
|
70
|
+
raise ValueError("Sandbox ID is not set")
|
|
71
|
+
|
|
72
|
+
self._data_api = BrowserDataAPI(
|
|
73
|
+
sandbox_id=self.sandbox_id, config=self._config
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return self._data_api
|
|
77
|
+
|
|
78
|
+
async def check_health_async(self):
|
|
79
|
+
return await self.data_api.check_health_async()
|
|
80
|
+
|
|
81
|
+
def get_cdp_url(self, record: Optional[bool] = False):
|
|
82
|
+
return self.data_api.get_cdp_url(record=record)
|
|
83
|
+
|
|
84
|
+
def get_vnc_url(self, record: Optional[bool] = False):
|
|
85
|
+
return self.data_api.get_vnc_url(record=record)
|
|
86
|
+
|
|
87
|
+
def sync_playwright(self, record: Optional[bool] = False):
|
|
88
|
+
return self.data_api.sync_playwright(record=record)
|
|
89
|
+
|
|
90
|
+
def async_playwright(self, record: Optional[bool] = False):
|
|
91
|
+
return self.data_api.async_playwright(record=record)
|
|
92
|
+
|
|
93
|
+
async def list_recordings_async(self):
|
|
94
|
+
return await self.data_api.list_recordings_async()
|
|
95
|
+
|
|
96
|
+
async def download_recording_async(self, filename: str, save_path: str):
|
|
97
|
+
"""
|
|
98
|
+
Asynchronously download a recording video file and save it to local path.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
filename: The name of the recording file to download
|
|
102
|
+
save_path: Local file path to save the downloaded video file (.mkv)
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Dictionary with 'saved_path' and 'size' keys
|
|
106
|
+
"""
|
|
107
|
+
return await self.data_api.download_recording_async(filename, save_path)
|
|
108
|
+
|
|
109
|
+
async def delete_recording_async(self, filename: str):
|
|
110
|
+
return await self.data_api.delete_recording_async(filename)
|