agentrun-sdk 0.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.
- agentrun/__init__.py +209 -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 +12 -0
- agentrun/integration/agentscope/adapter.py +17 -0
- agentrun/integration/agentscope/builtin.py +65 -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 +16 -0
- agentrun/integration/builtin/model.py +97 -0
- agentrun/integration/builtin/sandbox.py +276 -0
- agentrun/integration/builtin/toolset.py +47 -0
- agentrun/integration/crewai/__init__.py +12 -0
- agentrun/integration/crewai/adapter.py +9 -0
- agentrun/integration/crewai/builtin.py +65 -0
- agentrun/integration/crewai/model_adapter.py +27 -0
- agentrun/integration/crewai/tool_adapter.py +26 -0
- agentrun/integration/google_adk/__init__.py +12 -0
- agentrun/integration/google_adk/adapter.py +15 -0
- agentrun/integration/google_adk/builtin.py +65 -0
- agentrun/integration/google_adk/message_adapter.py +144 -0
- agentrun/integration/google_adk/model_adapter.py +43 -0
- agentrun/integration/google_adk/tool_adapter.py +25 -0
- agentrun/integration/langchain/__init__.py +9 -0
- agentrun/integration/langchain/adapter.py +15 -0
- agentrun/integration/langchain/builtin.py +71 -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 +13 -0
- agentrun/integration/langgraph/adapter.py +20 -0
- agentrun/integration/langgraph/builtin.py +65 -0
- agentrun/integration/pydantic_ai/__init__.py +12 -0
- agentrun/integration/pydantic_ai/adapter.py +13 -0
- agentrun/integration/pydantic_ai/builtin.py +65 -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 +167 -0
- agentrun/integration/utils/canonical.py +157 -0
- agentrun/integration/utils/converter.py +134 -0
- agentrun/integration/utils/model.py +107 -0
- agentrun/integration/utils/tool.py +1714 -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 +218 -0
- agentrun/model/model_proxy.py +439 -0
- agentrun/model/model_service.py +438 -0
- agentrun/sandbox/__browser_sandbox_async_template.py +113 -0
- agentrun/sandbox/__client_async_template.py +466 -0
- agentrun/sandbox/__code_interpreter_sandbox_async_template.py +466 -0
- agentrun/sandbox/__init__.py +54 -0
- agentrun/sandbox/__sandbox_async_template.py +398 -0
- agentrun/sandbox/__template_async_template.py +150 -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 +17 -0
- agentrun/sandbox/api/__sandbox_data_async_template.py +100 -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 +140 -0
- agentrun/sandbox/browser_sandbox.py +191 -0
- agentrun/sandbox/client.py +878 -0
- agentrun/sandbox/code_interpreter_sandbox.py +829 -0
- agentrun/sandbox/model.py +269 -0
- agentrun/sandbox/sandbox.py +737 -0
- agentrun/sandbox/template.py +215 -0
- agentrun/server/__init__.py +82 -0
- agentrun/server/invoker.py +131 -0
- agentrun/server/model.py +225 -0
- agentrun/server/openai_protocol.py +798 -0
- agentrun/server/protocol.py +96 -0
- agentrun/server/server.py +192 -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 +1184 -0
- agentrun/toolset/client.py +102 -0
- agentrun/toolset/model.py +160 -0
- agentrun/toolset/toolset.py +271 -0
- agentrun/utils/__data_api_async_template.py +715 -0
- agentrun/utils/__init__.py +5 -0
- agentrun/utils/__resource_async_template.py +158 -0
- agentrun/utils/config.py +258 -0
- agentrun/utils/control_api.py +78 -0
- agentrun/utils/data_api.py +1110 -0
- agentrun/utils/exception.py +149 -0
- agentrun/utils/helper.py +34 -0
- agentrun/utils/log.py +77 -0
- agentrun/utils/model.py +168 -0
- agentrun/utils/resource.py +291 -0
- agentrun_sdk-0.0.4.dist-info/METADATA +262 -0
- agentrun_sdk-0.0.4.dist-info/RECORD +128 -0
- agentrun_sdk-0.0.4.dist-info/WHEEL +5 -0
- agentrun_sdk-0.0.4.dist-info/licenses/LICENSE +201 -0
- agentrun_sdk-0.0.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"""Sandbox 高层 API / Sandbox High-Level API
|
|
2
|
+
|
|
3
|
+
此模块定义沙箱资源的高级API。
|
|
4
|
+
This module defines the high-level API for sandbox resources.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List, Literal, Optional, overload, TYPE_CHECKING, Union
|
|
8
|
+
|
|
9
|
+
from agentrun.sandbox.model import TemplateType
|
|
10
|
+
from agentrun.utils.config import Config
|
|
11
|
+
from agentrun.utils.model import BaseModel
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from agentrun.sandbox.browser_sandbox import BrowserSandbox
|
|
15
|
+
from agentrun.sandbox.code_interpreter_sandbox import CodeInterpreterSandbox
|
|
16
|
+
from agentrun.sandbox.model import (
|
|
17
|
+
ListSandboxesInput,
|
|
18
|
+
ListSandboxesOutput,
|
|
19
|
+
PageableInput,
|
|
20
|
+
TemplateInput,
|
|
21
|
+
)
|
|
22
|
+
from agentrun.sandbox.template import Template
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Sandbox(BaseModel):
|
|
26
|
+
"""Sandbox 实例
|
|
27
|
+
|
|
28
|
+
封装了 Sandbox 的基本信息和操作方法
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
_template_type: Optional[TemplateType]
|
|
32
|
+
|
|
33
|
+
created_at: Optional[str] = None
|
|
34
|
+
"""沙箱创建时间"""
|
|
35
|
+
last_updated_at: Optional[str] = None
|
|
36
|
+
"""最后更新时间"""
|
|
37
|
+
sandbox_arn: Optional[str] = None
|
|
38
|
+
"""沙箱全局唯一资源名称"""
|
|
39
|
+
sandbox_id: Optional[str] = None
|
|
40
|
+
"""沙箱 ID"""
|
|
41
|
+
sandbox_idle_timeout_seconds: Optional[int] = None
|
|
42
|
+
"""沙箱空闲超时时间(秒)"""
|
|
43
|
+
status: Optional[str] = None
|
|
44
|
+
"""沙箱状态"""
|
|
45
|
+
template_id: Optional[str] = None
|
|
46
|
+
"""模板 ID"""
|
|
47
|
+
template_name: Optional[str] = None
|
|
48
|
+
"""模板名称"""
|
|
49
|
+
_config: Optional[Config] = None
|
|
50
|
+
"""配置对象,用于子类的 data_api 初始化"""
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def __get_client(cls):
|
|
54
|
+
"""获取 Sandbox 客户端"""
|
|
55
|
+
from .client import SandboxClient
|
|
56
|
+
|
|
57
|
+
return SandboxClient()
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
@overload
|
|
61
|
+
async def create_async(
|
|
62
|
+
cls,
|
|
63
|
+
template_type: Literal[TemplateType.CODE_INTERPRETER],
|
|
64
|
+
template_name: Optional[str] = None,
|
|
65
|
+
sandbox_idle_timeout_seconds: Optional[int] = 600,
|
|
66
|
+
config: Optional[Config] = None,
|
|
67
|
+
) -> "CodeInterpreterSandbox":
|
|
68
|
+
...
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
@overload
|
|
72
|
+
async def create_async(
|
|
73
|
+
cls,
|
|
74
|
+
template_type: Literal[TemplateType.BROWSER],
|
|
75
|
+
template_name: Optional[str] = None,
|
|
76
|
+
sandbox_idle_timeout_seconds: Optional[int] = 600,
|
|
77
|
+
config: Optional[Config] = None,
|
|
78
|
+
) -> "BrowserSandbox":
|
|
79
|
+
...
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
async def create_async(
|
|
83
|
+
cls,
|
|
84
|
+
template_type: TemplateType,
|
|
85
|
+
template_name: Optional[str] = None,
|
|
86
|
+
sandbox_idle_timeout_seconds: Optional[int] = 600,
|
|
87
|
+
config: Optional[Config] = None,
|
|
88
|
+
) -> Union["CodeInterpreterSandbox", "BrowserSandbox"]:
|
|
89
|
+
|
|
90
|
+
if template_name is None:
|
|
91
|
+
# todo 可以考虑为用户创建一个模板?
|
|
92
|
+
raise ValueError("template_name is required")
|
|
93
|
+
|
|
94
|
+
# 先根据传入的template_name,获取template的类型
|
|
95
|
+
template = await cls.get_template_async(template_name, config=config)
|
|
96
|
+
|
|
97
|
+
# 根据 template 类型创建相应的 Sandbox 子类
|
|
98
|
+
from agentrun.sandbox.browser_sandbox import BrowserSandbox
|
|
99
|
+
from agentrun.sandbox.code_interpreter_sandbox import (
|
|
100
|
+
CodeInterpreterSandbox,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if template_type != template.template_type:
|
|
104
|
+
raise ValueError(
|
|
105
|
+
f"template_type of {template_name} is {template.template_type},"
|
|
106
|
+
f" not {template_type}"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# 创建 Sandbox(返回基类实例)
|
|
110
|
+
base_sandbox = await cls.__get_client().create_sandbox_async(
|
|
111
|
+
template_name=template_name,
|
|
112
|
+
sandbox_idle_timeout_seconds=sandbox_idle_timeout_seconds,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# 根据 template 类型转换为对应的子类实例
|
|
116
|
+
sandbox = None
|
|
117
|
+
if template.template_type == TemplateType.CODE_INTERPRETER:
|
|
118
|
+
sandbox = CodeInterpreterSandbox.model_validate(
|
|
119
|
+
base_sandbox.model_dump(by_alias=False)
|
|
120
|
+
)
|
|
121
|
+
elif template.template_type == TemplateType.BROWSER:
|
|
122
|
+
sandbox = BrowserSandbox.model_validate(
|
|
123
|
+
base_sandbox.model_dump(by_alias=False)
|
|
124
|
+
)
|
|
125
|
+
else:
|
|
126
|
+
raise ValueError(
|
|
127
|
+
f"template_type {template.template_type} is not supported"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
sandbox._config = config
|
|
131
|
+
return sandbox
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
async def stop_by_id_async(cls, sandbox_id: str):
|
|
135
|
+
"""通过 ID 停止 Sandbox(异步)
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
sandbox_id: Sandbox ID
|
|
139
|
+
config: 配置对象
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Sandbox: Sandbox 对象
|
|
143
|
+
"""
|
|
144
|
+
if sandbox_id is None:
|
|
145
|
+
raise ValueError("sandbox_id is required")
|
|
146
|
+
# todo 后续适配后使用 stop()
|
|
147
|
+
return await cls.__get_client().delete_sandbox_async(sandbox_id)
|
|
148
|
+
|
|
149
|
+
@classmethod
|
|
150
|
+
async def delete_by_id_async(cls, sandbox_id: str):
|
|
151
|
+
"""通过 ID 删除 Sandbox(异步)
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
sandbox_id: Sandbox ID
|
|
155
|
+
config: 配置对象
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Sandbox: Sandbox 对象
|
|
159
|
+
"""
|
|
160
|
+
if sandbox_id is None:
|
|
161
|
+
raise ValueError("sandbox_id is required")
|
|
162
|
+
return await cls.__get_client().delete_sandbox_async(sandbox_id)
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
async def list_async(
|
|
166
|
+
cls,
|
|
167
|
+
input: Optional["ListSandboxesInput"] = None,
|
|
168
|
+
config: Optional[Config] = None,
|
|
169
|
+
) -> "ListSandboxesOutput":
|
|
170
|
+
"""列出 Sandboxes(异步)
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
input: 列表查询配置
|
|
174
|
+
config: 配置对象
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
ListSandboxesOutput: Sandbox 列表结果
|
|
178
|
+
"""
|
|
179
|
+
return await cls.__get_client().list_sandboxes_async(input, config)
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
@overload
|
|
183
|
+
async def connect_async(
|
|
184
|
+
cls,
|
|
185
|
+
sandbox_id: str,
|
|
186
|
+
template_type: Literal[TemplateType.CODE_INTERPRETER],
|
|
187
|
+
config: Optional[Config] = None,
|
|
188
|
+
) -> "CodeInterpreterSandbox":
|
|
189
|
+
...
|
|
190
|
+
|
|
191
|
+
@classmethod
|
|
192
|
+
@overload
|
|
193
|
+
async def connect_async(
|
|
194
|
+
cls,
|
|
195
|
+
sandbox_id: str,
|
|
196
|
+
template_type: Literal[TemplateType.BROWSER],
|
|
197
|
+
config: Optional[Config] = None,
|
|
198
|
+
) -> "BrowserSandbox":
|
|
199
|
+
...
|
|
200
|
+
|
|
201
|
+
@classmethod
|
|
202
|
+
@overload
|
|
203
|
+
async def connect_async(
|
|
204
|
+
cls,
|
|
205
|
+
sandbox_id: str,
|
|
206
|
+
template_type: None = None,
|
|
207
|
+
config: Optional[Config] = None,
|
|
208
|
+
) -> Union["CodeInterpreterSandbox", "BrowserSandbox"]:
|
|
209
|
+
...
|
|
210
|
+
|
|
211
|
+
@classmethod
|
|
212
|
+
async def connect_async(
|
|
213
|
+
cls,
|
|
214
|
+
sandbox_id: str,
|
|
215
|
+
template_type: Optional[TemplateType] = None,
|
|
216
|
+
config: Optional[Config] = None,
|
|
217
|
+
) -> Union["CodeInterpreterSandbox", "BrowserSandbox"]:
|
|
218
|
+
"""连接一个SandBox(异步)
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
sandbox_id: Sandbox ID
|
|
222
|
+
type: 可选的类型参数,用于类型提示和运行时验证
|
|
223
|
+
config: 配置对象
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Sandbox: 根据模板类型返回对应的 Sandbox 子类对象
|
|
227
|
+
|
|
228
|
+
Raises:
|
|
229
|
+
ValueError: 如果模板类型不支持或与预期类型不匹配
|
|
230
|
+
"""
|
|
231
|
+
if sandbox_id is None:
|
|
232
|
+
raise ValueError("sandbox_id is required")
|
|
233
|
+
|
|
234
|
+
# 先获取 sandbox 信息
|
|
235
|
+
sandbox = await cls.__get_client().get_sandbox_async(
|
|
236
|
+
sandbox_id, config=config
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# 根据 template_name 获取 template 类型
|
|
240
|
+
if sandbox.template_name is None:
|
|
241
|
+
raise ValueError(f"Sandbox {sandbox_id} has no template_name")
|
|
242
|
+
|
|
243
|
+
template = await cls.get_template_async(
|
|
244
|
+
sandbox.template_name, config=config
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# 如果提供了 type 参数,验证类型是否匹配
|
|
248
|
+
if (
|
|
249
|
+
template_type is not None
|
|
250
|
+
and template.template_type != template_type
|
|
251
|
+
):
|
|
252
|
+
raise ValueError(
|
|
253
|
+
f"Sandbox {sandbox_id} has template type"
|
|
254
|
+
f" {template.template_type}, but expected {template_type}"
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# 根据 template 类型创建相应的 Sandbox 子类
|
|
258
|
+
from agentrun.sandbox.browser_sandbox import BrowserSandbox
|
|
259
|
+
from agentrun.sandbox.code_interpreter_sandbox import (
|
|
260
|
+
CodeInterpreterSandbox,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
result = None
|
|
264
|
+
if template.template_type == TemplateType.CODE_INTERPRETER:
|
|
265
|
+
result = CodeInterpreterSandbox.model_validate(
|
|
266
|
+
sandbox.model_dump(by_alias=False)
|
|
267
|
+
)
|
|
268
|
+
elif template.template_type == TemplateType.BROWSER:
|
|
269
|
+
result = BrowserSandbox.model_validate(
|
|
270
|
+
sandbox.model_dump(by_alias=False)
|
|
271
|
+
)
|
|
272
|
+
else:
|
|
273
|
+
raise ValueError(
|
|
274
|
+
f"Unsupported template type: {template.template_type}. "
|
|
275
|
+
"Expected 'code-interpreter' or 'browser'"
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
result._config = config
|
|
279
|
+
return result
|
|
280
|
+
|
|
281
|
+
# ==================== Template 相关类方法 ====================
|
|
282
|
+
|
|
283
|
+
@classmethod
|
|
284
|
+
async def create_template_async(
|
|
285
|
+
cls, input: "TemplateInput", config: Optional[Config] = None
|
|
286
|
+
) -> "Template":
|
|
287
|
+
"""创建 Template(异步)
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
input: Template 配置
|
|
291
|
+
config: 配置对象
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
Template: 创建的 Template 对象
|
|
295
|
+
"""
|
|
296
|
+
if input.template_type is None:
|
|
297
|
+
raise ValueError("template_type is required")
|
|
298
|
+
return await cls.__get_client().create_template_async(
|
|
299
|
+
input, config=config
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
@classmethod
|
|
303
|
+
async def get_template_async(
|
|
304
|
+
cls, template_name: str, config: Optional[Config] = None
|
|
305
|
+
) -> "Template":
|
|
306
|
+
"""获取 Template(异步)
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
template_name: Template 名称
|
|
310
|
+
config: 配置对象
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
Template: Template 对象
|
|
314
|
+
"""
|
|
315
|
+
if template_name is None:
|
|
316
|
+
raise ValueError("template_name is required")
|
|
317
|
+
return await cls.__get_client().get_template_async(
|
|
318
|
+
template_name, config=config
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
@classmethod
|
|
322
|
+
async def update_template_async(
|
|
323
|
+
cls,
|
|
324
|
+
template_name: str,
|
|
325
|
+
input: "TemplateInput",
|
|
326
|
+
config: Optional[Config] = None,
|
|
327
|
+
) -> "Template":
|
|
328
|
+
"""更新 Template(异步)
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
template_name: Template 名称
|
|
332
|
+
input: Template 更新配置
|
|
333
|
+
config: 配置对象
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
Template: 更新后的 Template 对象
|
|
337
|
+
"""
|
|
338
|
+
if template_name is None:
|
|
339
|
+
raise ValueError("template_name is required")
|
|
340
|
+
return await cls.__get_client().update_template_async(
|
|
341
|
+
template_name, input, config=config
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
@classmethod
|
|
345
|
+
async def delete_template_async(
|
|
346
|
+
cls, template_name: str, config: Optional[Config] = None
|
|
347
|
+
) -> "Template":
|
|
348
|
+
"""删除 Template(异步)
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
template_name: Template 名称
|
|
352
|
+
config: 配置对象
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
Template: 删除的 Template 对象
|
|
356
|
+
"""
|
|
357
|
+
if template_name is None:
|
|
358
|
+
raise ValueError("template_name is required")
|
|
359
|
+
return await cls.__get_client().delete_template_async(
|
|
360
|
+
template_name, config=config
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
@classmethod
|
|
364
|
+
async def list_templates_async(
|
|
365
|
+
cls,
|
|
366
|
+
input: Optional["PageableInput"] = None,
|
|
367
|
+
config: Optional[Config] = None,
|
|
368
|
+
) -> List["Template"]:
|
|
369
|
+
"""列出 Templates(异步)
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
input: 分页配置
|
|
373
|
+
config: 配置对象
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
List[Template]: Template 列表
|
|
377
|
+
"""
|
|
378
|
+
return await cls.__get_client().list_templates_async(
|
|
379
|
+
input, config=config
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
async def get_async(self):
|
|
383
|
+
if self.sandbox_id is None:
|
|
384
|
+
raise ValueError("sandbox_id is required to get a Sandbox")
|
|
385
|
+
|
|
386
|
+
return await self.connect_async(self.sandbox_id)
|
|
387
|
+
|
|
388
|
+
async def delete_async(self):
|
|
389
|
+
if self.sandbox_id is None:
|
|
390
|
+
raise ValueError("sandbox_id is required to delete a Sandbox")
|
|
391
|
+
|
|
392
|
+
return await self.delete_by_id_async(self.sandbox_id)
|
|
393
|
+
|
|
394
|
+
async def stop_async(self):
|
|
395
|
+
if self.sandbox_id is None:
|
|
396
|
+
raise ValueError("sandbox_id is required to stop a Sandbox")
|
|
397
|
+
# todo 后续适配后使用 stop()
|
|
398
|
+
return await self.delete_by_id_async(self.sandbox_id)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""Template 高层 API / Template High-Level API
|
|
2
|
+
|
|
3
|
+
此模块定义沙箱模板资源的高级API。
|
|
4
|
+
This module defines the high-level API for sandbox template resources.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
from agentrun.sandbox.model import (
|
|
10
|
+
PageableInput,
|
|
11
|
+
TemplateContainerConfiguration,
|
|
12
|
+
TemplateCredentialConfiguration,
|
|
13
|
+
TemplateInput,
|
|
14
|
+
TemplateLogConfiguration,
|
|
15
|
+
TemplateMcpOptions,
|
|
16
|
+
TemplateMcpState,
|
|
17
|
+
TemplateNetworkConfiguration,
|
|
18
|
+
TemplateOssConfiguration,
|
|
19
|
+
TemplateType,
|
|
20
|
+
)
|
|
21
|
+
from agentrun.utils.config import Config
|
|
22
|
+
from agentrun.utils.model import BaseModel
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Template(BaseModel):
|
|
26
|
+
"""Template 实例
|
|
27
|
+
|
|
28
|
+
封装了 Template 的基本信息和操作方法
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
template_id: Optional[str] = None
|
|
32
|
+
"""模板 ID"""
|
|
33
|
+
template_name: Optional[str] = None
|
|
34
|
+
"""模板名称"""
|
|
35
|
+
template_version: Optional[str] = None
|
|
36
|
+
"""模板版本"""
|
|
37
|
+
template_arn: Optional[str] = None
|
|
38
|
+
"""模板 ARN"""
|
|
39
|
+
resource_name: Optional[str] = None
|
|
40
|
+
"""资源名称"""
|
|
41
|
+
template_type: Optional[TemplateType] = None
|
|
42
|
+
"""模板类型"""
|
|
43
|
+
cpu: Optional[float] = None
|
|
44
|
+
"""CPU 核数"""
|
|
45
|
+
memory: Optional[int] = None
|
|
46
|
+
"""内存大小(MB)"""
|
|
47
|
+
disk_size: Optional[int] = None
|
|
48
|
+
"""磁盘大小(GB)"""
|
|
49
|
+
description: Optional[str] = None
|
|
50
|
+
"""描述"""
|
|
51
|
+
execution_role_arn: Optional[str] = None
|
|
52
|
+
"""执行角色 ARN"""
|
|
53
|
+
sandbox_idle_timeout_in_seconds: Optional[int] = None
|
|
54
|
+
"""沙箱空闲超时时间(秒)"""
|
|
55
|
+
sandbox_ttlin_seconds: Optional[int] = None
|
|
56
|
+
"""沙箱存活时间(秒)"""
|
|
57
|
+
share_concurrency_limit_per_sandbox: Optional[int] = None
|
|
58
|
+
"""每个沙箱的最大并发会话数"""
|
|
59
|
+
template_configuration: Optional[Dict] = None
|
|
60
|
+
"""模板配置"""
|
|
61
|
+
environment_variables: Optional[Dict] = None
|
|
62
|
+
"""环境变量"""
|
|
63
|
+
network_configuration: Optional[TemplateNetworkConfiguration] = None
|
|
64
|
+
"""网络配置"""
|
|
65
|
+
oss_configuration: Optional[List[TemplateOssConfiguration]] = None
|
|
66
|
+
"""OSS 配置列表"""
|
|
67
|
+
log_configuration: Optional[TemplateLogConfiguration] = None
|
|
68
|
+
"""日志配置"""
|
|
69
|
+
credential_configuration: Optional[TemplateCredentialConfiguration] = None
|
|
70
|
+
"""凭证配置"""
|
|
71
|
+
container_configuration: Optional[TemplateContainerConfiguration] = None
|
|
72
|
+
"""容器配置"""
|
|
73
|
+
mcp_options: Optional[TemplateMcpOptions] = None
|
|
74
|
+
"""MCP 选项"""
|
|
75
|
+
mcp_state: Optional[TemplateMcpState] = None
|
|
76
|
+
"""MCP 状态"""
|
|
77
|
+
created_at: Optional[str] = None
|
|
78
|
+
"""创建时间"""
|
|
79
|
+
last_updated_at: Optional[str] = None
|
|
80
|
+
"""最后更新时间"""
|
|
81
|
+
status: Optional[str] = None
|
|
82
|
+
"""状态"""
|
|
83
|
+
status_reason: Optional[str] = None
|
|
84
|
+
"""状态原因"""
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
def __get_client(cls, config: Optional[Config] = None):
|
|
88
|
+
"""获取 Sandbox 客户端"""
|
|
89
|
+
from .client import SandboxClient
|
|
90
|
+
|
|
91
|
+
return SandboxClient(config)
|
|
92
|
+
|
|
93
|
+
@classmethod
|
|
94
|
+
async def create_async(
|
|
95
|
+
cls, input: TemplateInput, config: Optional[Config] = None
|
|
96
|
+
):
|
|
97
|
+
return await cls.__get_client(config=config).create_template_async(
|
|
98
|
+
input, config=config
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
async def delete_by_name_async(
|
|
103
|
+
cls, template_name: str, config: Optional[Config] = None
|
|
104
|
+
):
|
|
105
|
+
return await cls.__get_client(config=config).delete_template_async(
|
|
106
|
+
template_name=template_name, config=config
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
@classmethod
|
|
110
|
+
async def update_by_name_async(
|
|
111
|
+
cls,
|
|
112
|
+
template_name: str,
|
|
113
|
+
input: TemplateInput,
|
|
114
|
+
config: Optional[Config] = None,
|
|
115
|
+
):
|
|
116
|
+
return await cls.__get_client(config=config).update_template_async(
|
|
117
|
+
template_name=template_name, input=input, config=config
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
@classmethod
|
|
121
|
+
async def get_by_name_async(
|
|
122
|
+
cls, template_name: str, config: Optional[Config] = None
|
|
123
|
+
):
|
|
124
|
+
return await cls.__get_client(config=config).get_template_async(
|
|
125
|
+
template_name=template_name, config=config
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
async def list_templates_async(
|
|
130
|
+
cls,
|
|
131
|
+
input: Optional[PageableInput] = None,
|
|
132
|
+
config: Optional[Config] = None,
|
|
133
|
+
):
|
|
134
|
+
return await cls.__get_client(config=config).list_templates_async(
|
|
135
|
+
input, config=config
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
async def create_sandbox_async(
|
|
139
|
+
self,
|
|
140
|
+
sandbox_idle_timeout_seconds: Optional[int] = None,
|
|
141
|
+
config: Optional[Config] = None,
|
|
142
|
+
):
|
|
143
|
+
if self.template_name is None:
|
|
144
|
+
raise ValueError("Template name is required")
|
|
145
|
+
|
|
146
|
+
return await self.__get_client(config=config).create_sandbox_async(
|
|
147
|
+
self.template_name,
|
|
148
|
+
sandbox_idle_timeout_seconds=sandbox_idle_timeout_seconds,
|
|
149
|
+
config=config,
|
|
150
|
+
)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""浏览器沙箱数据API模板 / Browser Sandbox Data API Template
|
|
2
|
+
|
|
3
|
+
此模板用于生成浏览器沙箱数据API代码。
|
|
4
|
+
This template is used to generate browser sandbox data API code.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from urllib.parse import parse_qs, urlencode, urlparse
|
|
9
|
+
|
|
10
|
+
from agentrun.utils.config import Config
|
|
11
|
+
|
|
12
|
+
from .sandbox_data import SandboxDataAPI
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BrowserDataAPI(SandboxDataAPI):
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
sandbox_id: str,
|
|
20
|
+
config: Optional[Config] = None,
|
|
21
|
+
):
|
|
22
|
+
self.sandbox_id = sandbox_id
|
|
23
|
+
super().__init__(
|
|
24
|
+
sandbox_id=sandbox_id,
|
|
25
|
+
config=config,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def get_cdp_url(self, record: Optional[bool] = False):
|
|
29
|
+
"""
|
|
30
|
+
Generate the WebSocket URL for Chrome DevTools Protocol (CDP) connection.
|
|
31
|
+
|
|
32
|
+
This method constructs a WebSocket URL by:
|
|
33
|
+
1. Converting the HTTP endpoint to WebSocket protocol (ws://)
|
|
34
|
+
2. Parsing the existing URL and query parameters
|
|
35
|
+
3. Adding the session ID to the query parameters
|
|
36
|
+
4. Reconstructing the complete WebSocket URL
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
str: The complete WebSocket URL for CDP automation connection,
|
|
40
|
+
including the session ID in the query parameters.
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
>>> api = BrowserDataAPI("browser123", "session456")
|
|
44
|
+
>>> api.get_cdp_url()
|
|
45
|
+
'ws://example.com/ws/automation?sessionId=session456'
|
|
46
|
+
"""
|
|
47
|
+
cdp_url = self.with_path("/ws/automation").replace("http", "ws")
|
|
48
|
+
u = urlparse(cdp_url)
|
|
49
|
+
query_dict = parse_qs(u.query)
|
|
50
|
+
query_dict["tenantId"] = [self.config.get_account_id()]
|
|
51
|
+
if record:
|
|
52
|
+
query_dict["recording"] = ["true"]
|
|
53
|
+
new_query = urlencode(query_dict, doseq=True)
|
|
54
|
+
new_u = u._replace(query=new_query)
|
|
55
|
+
return new_u.geturl()
|
|
56
|
+
|
|
57
|
+
def get_vnc_url(self, record: Optional[bool] = False):
|
|
58
|
+
"""
|
|
59
|
+
Generate the WebSocket URL for VNC (Virtual Network Computing) live view connection.
|
|
60
|
+
|
|
61
|
+
This method constructs a WebSocket URL for real-time browser viewing by:
|
|
62
|
+
1. Converting the HTTP endpoint to WebSocket protocol (ws://)
|
|
63
|
+
2. Parsing the existing URL and query parameters
|
|
64
|
+
3. Adding the session ID to the query parameters
|
|
65
|
+
4. Reconstructing the complete WebSocket URL
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
str: The complete WebSocket URL for VNC live view connection,
|
|
69
|
+
including the session ID in the query parameters.
|
|
70
|
+
|
|
71
|
+
Example:
|
|
72
|
+
>>> api = BrowserDataAPI("browser123", "session456")
|
|
73
|
+
>>> api.get_vnc_url()
|
|
74
|
+
'ws://example.com/ws/liveview?sessionId=session456'
|
|
75
|
+
"""
|
|
76
|
+
vnc_url = self.with_path("/ws/liveview").replace("http", "ws")
|
|
77
|
+
u = urlparse(vnc_url)
|
|
78
|
+
query_dict = parse_qs(u.query)
|
|
79
|
+
query_dict["tenantId"] = [self.config.get_account_id()]
|
|
80
|
+
if record:
|
|
81
|
+
query_dict["recording"] = ["true"]
|
|
82
|
+
new_query = urlencode(query_dict, doseq=True)
|
|
83
|
+
new_u = u._replace(query=new_query)
|
|
84
|
+
return new_u.geturl()
|
|
85
|
+
|
|
86
|
+
def sync_playwright(
|
|
87
|
+
self,
|
|
88
|
+
browser_type: str = "chrome",
|
|
89
|
+
record: Optional[bool] = False,
|
|
90
|
+
config: Optional[Config] = None,
|
|
91
|
+
):
|
|
92
|
+
from .playwright_sync import BrowserPlaywrightSync
|
|
93
|
+
|
|
94
|
+
cfg = Config.with_configs(self.config, config)
|
|
95
|
+
_, headers, _ = self.auth(headers=cfg.get_headers(), config=cfg)
|
|
96
|
+
return BrowserPlaywrightSync(
|
|
97
|
+
self.get_cdp_url(record=record),
|
|
98
|
+
browser_type=browser_type,
|
|
99
|
+
headers=headers,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def async_playwright(
|
|
103
|
+
self,
|
|
104
|
+
browser_type: str = "chrome",
|
|
105
|
+
record: Optional[bool] = False,
|
|
106
|
+
config: Optional[Config] = None,
|
|
107
|
+
):
|
|
108
|
+
from .playwright_async import BrowserPlaywrightAsync
|
|
109
|
+
|
|
110
|
+
cfg = Config.with_configs(self.config, config)
|
|
111
|
+
_, headers, _ = self.auth(headers=cfg.get_headers(), config=cfg)
|
|
112
|
+
return BrowserPlaywrightAsync(
|
|
113
|
+
self.get_cdp_url(record=record),
|
|
114
|
+
browser_type=browser_type,
|
|
115
|
+
headers=headers,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
async def list_recordings_async(self):
|
|
119
|
+
return await self.get_async("/recordings")
|
|
120
|
+
|
|
121
|
+
async def delete_recording_async(self, filename: str):
|
|
122
|
+
return await self.delete_async(f"/recordings/{filename}")
|
|
123
|
+
|
|
124
|
+
async def download_recording_async(self, filename: str, save_path: str):
|
|
125
|
+
"""
|
|
126
|
+
Asynchronously download a recording video file and save it to local path.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
filename: The name of the recording file to download
|
|
130
|
+
save_path: Local file path to save the downloaded video file (.mkv)
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Dictionary with 'saved_path' and 'size' keys
|
|
134
|
+
|
|
135
|
+
Examples:
|
|
136
|
+
>>> await api.download_recording_async("recording.mp4", "/local/video.mkv")
|
|
137
|
+
"""
|
|
138
|
+
return await self.get_video_async(
|
|
139
|
+
f"/recordings/{filename}", save_path=save_path
|
|
140
|
+
)
|