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.
Files changed (128) hide show
  1. agentrun/__init__.py +209 -0
  2. agentrun/agent_runtime/__client_async_template.py +466 -0
  3. agentrun/agent_runtime/__endpoint_async_template.py +345 -0
  4. agentrun/agent_runtime/__init__.py +53 -0
  5. agentrun/agent_runtime/__runtime_async_template.py +477 -0
  6. agentrun/agent_runtime/api/__data_async_template.py +58 -0
  7. agentrun/agent_runtime/api/__init__.py +6 -0
  8. agentrun/agent_runtime/api/control.py +1362 -0
  9. agentrun/agent_runtime/api/data.py +98 -0
  10. agentrun/agent_runtime/client.py +868 -0
  11. agentrun/agent_runtime/endpoint.py +649 -0
  12. agentrun/agent_runtime/model.py +362 -0
  13. agentrun/agent_runtime/runtime.py +904 -0
  14. agentrun/credential/__client_async_template.py +177 -0
  15. agentrun/credential/__credential_async_template.py +216 -0
  16. agentrun/credential/__init__.py +28 -0
  17. agentrun/credential/api/__init__.py +5 -0
  18. agentrun/credential/api/control.py +606 -0
  19. agentrun/credential/client.py +319 -0
  20. agentrun/credential/credential.py +381 -0
  21. agentrun/credential/model.py +248 -0
  22. agentrun/integration/__init__.py +21 -0
  23. agentrun/integration/agentscope/__init__.py +12 -0
  24. agentrun/integration/agentscope/adapter.py +17 -0
  25. agentrun/integration/agentscope/builtin.py +65 -0
  26. agentrun/integration/agentscope/message_adapter.py +185 -0
  27. agentrun/integration/agentscope/model_adapter.py +60 -0
  28. agentrun/integration/agentscope/tool_adapter.py +59 -0
  29. agentrun/integration/builtin/__init__.py +16 -0
  30. agentrun/integration/builtin/model.py +97 -0
  31. agentrun/integration/builtin/sandbox.py +276 -0
  32. agentrun/integration/builtin/toolset.py +47 -0
  33. agentrun/integration/crewai/__init__.py +12 -0
  34. agentrun/integration/crewai/adapter.py +9 -0
  35. agentrun/integration/crewai/builtin.py +65 -0
  36. agentrun/integration/crewai/model_adapter.py +27 -0
  37. agentrun/integration/crewai/tool_adapter.py +26 -0
  38. agentrun/integration/google_adk/__init__.py +12 -0
  39. agentrun/integration/google_adk/adapter.py +15 -0
  40. agentrun/integration/google_adk/builtin.py +65 -0
  41. agentrun/integration/google_adk/message_adapter.py +144 -0
  42. agentrun/integration/google_adk/model_adapter.py +43 -0
  43. agentrun/integration/google_adk/tool_adapter.py +25 -0
  44. agentrun/integration/langchain/__init__.py +9 -0
  45. agentrun/integration/langchain/adapter.py +15 -0
  46. agentrun/integration/langchain/builtin.py +71 -0
  47. agentrun/integration/langchain/message_adapter.py +141 -0
  48. agentrun/integration/langchain/model_adapter.py +37 -0
  49. agentrun/integration/langchain/tool_adapter.py +50 -0
  50. agentrun/integration/langgraph/__init__.py +13 -0
  51. agentrun/integration/langgraph/adapter.py +20 -0
  52. agentrun/integration/langgraph/builtin.py +65 -0
  53. agentrun/integration/pydantic_ai/__init__.py +12 -0
  54. agentrun/integration/pydantic_ai/adapter.py +13 -0
  55. agentrun/integration/pydantic_ai/builtin.py +65 -0
  56. agentrun/integration/pydantic_ai/model_adapter.py +44 -0
  57. agentrun/integration/pydantic_ai/tool_adapter.py +19 -0
  58. agentrun/integration/utils/__init__.py +112 -0
  59. agentrun/integration/utils/adapter.py +167 -0
  60. agentrun/integration/utils/canonical.py +157 -0
  61. agentrun/integration/utils/converter.py +134 -0
  62. agentrun/integration/utils/model.py +107 -0
  63. agentrun/integration/utils/tool.py +1714 -0
  64. agentrun/model/__client_async_template.py +357 -0
  65. agentrun/model/__init__.py +57 -0
  66. agentrun/model/__model_proxy_async_template.py +270 -0
  67. agentrun/model/__model_service_async_template.py +267 -0
  68. agentrun/model/api/__init__.py +6 -0
  69. agentrun/model/api/control.py +1173 -0
  70. agentrun/model/api/data.py +196 -0
  71. agentrun/model/client.py +674 -0
  72. agentrun/model/model.py +218 -0
  73. agentrun/model/model_proxy.py +439 -0
  74. agentrun/model/model_service.py +438 -0
  75. agentrun/sandbox/__browser_sandbox_async_template.py +113 -0
  76. agentrun/sandbox/__client_async_template.py +466 -0
  77. agentrun/sandbox/__code_interpreter_sandbox_async_template.py +466 -0
  78. agentrun/sandbox/__init__.py +54 -0
  79. agentrun/sandbox/__sandbox_async_template.py +398 -0
  80. agentrun/sandbox/__template_async_template.py +150 -0
  81. agentrun/sandbox/api/__browser_data_async_template.py +140 -0
  82. agentrun/sandbox/api/__code_interpreter_data_async_template.py +206 -0
  83. agentrun/sandbox/api/__init__.py +17 -0
  84. agentrun/sandbox/api/__sandbox_data_async_template.py +100 -0
  85. agentrun/sandbox/api/browser_data.py +172 -0
  86. agentrun/sandbox/api/code_interpreter_data.py +396 -0
  87. agentrun/sandbox/api/control.py +1051 -0
  88. agentrun/sandbox/api/playwright_async.py +492 -0
  89. agentrun/sandbox/api/playwright_sync.py +492 -0
  90. agentrun/sandbox/api/sandbox_data.py +140 -0
  91. agentrun/sandbox/browser_sandbox.py +191 -0
  92. agentrun/sandbox/client.py +878 -0
  93. agentrun/sandbox/code_interpreter_sandbox.py +829 -0
  94. agentrun/sandbox/model.py +269 -0
  95. agentrun/sandbox/sandbox.py +737 -0
  96. agentrun/sandbox/template.py +215 -0
  97. agentrun/server/__init__.py +82 -0
  98. agentrun/server/invoker.py +131 -0
  99. agentrun/server/model.py +225 -0
  100. agentrun/server/openai_protocol.py +798 -0
  101. agentrun/server/protocol.py +96 -0
  102. agentrun/server/server.py +192 -0
  103. agentrun/toolset/__client_async_template.py +62 -0
  104. agentrun/toolset/__init__.py +51 -0
  105. agentrun/toolset/__toolset_async_template.py +204 -0
  106. agentrun/toolset/api/__init__.py +17 -0
  107. agentrun/toolset/api/control.py +262 -0
  108. agentrun/toolset/api/mcp.py +100 -0
  109. agentrun/toolset/api/openapi.py +1184 -0
  110. agentrun/toolset/client.py +102 -0
  111. agentrun/toolset/model.py +160 -0
  112. agentrun/toolset/toolset.py +271 -0
  113. agentrun/utils/__data_api_async_template.py +715 -0
  114. agentrun/utils/__init__.py +5 -0
  115. agentrun/utils/__resource_async_template.py +158 -0
  116. agentrun/utils/config.py +258 -0
  117. agentrun/utils/control_api.py +78 -0
  118. agentrun/utils/data_api.py +1110 -0
  119. agentrun/utils/exception.py +149 -0
  120. agentrun/utils/helper.py +34 -0
  121. agentrun/utils/log.py +77 -0
  122. agentrun/utils/model.py +168 -0
  123. agentrun/utils/resource.py +291 -0
  124. agentrun_sdk-0.0.4.dist-info/METADATA +262 -0
  125. agentrun_sdk-0.0.4.dist-info/RECORD +128 -0
  126. agentrun_sdk-0.0.4.dist-info/WHEEL +5 -0
  127. agentrun_sdk-0.0.4.dist-info/licenses/LICENSE +201 -0
  128. 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
+ )