diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5b97899..a9a49fc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,7 @@ jobs: echo "Extracted version: ${VERSION}" - name: Update pyproject.toml run: | - sed -i 's/version = "[^"]*"/version = "'${VERSION}'"/' pyproject.toml + sed -i 's/^version = "[^"]*"/version = "'${VERSION}'"/' pyproject.toml echo "Updated pyproject.toml version to ${VERSION}" - name: Update __version__ in __init__.py run: | diff --git a/agentrun/sandbox/__sandbox_async_template.py b/agentrun/sandbox/__sandbox_async_template.py index 016711c..734214f 100644 --- a/agentrun/sandbox/__sandbox_async_template.py +++ b/agentrun/sandbox/__sandbox_async_template.py @@ -23,6 +23,7 @@ from agentrun.sandbox.aio_sandbox import AioSandbox from agentrun.sandbox.browser_sandbox import BrowserSandbox from agentrun.sandbox.code_interpreter_sandbox import CodeInterpreterSandbox + from agentrun.sandbox.custom_sandbox import CustomSandbox from agentrun.sandbox.model import ( ListSandboxesInput, ListSandboxesOutput, @@ -117,6 +118,20 @@ async def create_async( ) -> "AioSandbox": ... + @classmethod + @overload + async def create_async( + cls, + template_type: Literal[TemplateType.CUSTOM], + template_name: Optional[str] = None, + sandbox_idle_timeout_seconds: Optional[int] = 600, + nas_config: Optional["NASConfig"] = None, + oss_mount_config: Optional["OSSMountConfig"] = None, + polar_fs_config: Optional["PolarFsConfig"] = None, + config: Optional[Config] = None, + ) -> "CustomSandbox": + ... + @classmethod async def create_async( cls, @@ -127,7 +142,12 @@ async def create_async( oss_mount_config: Optional["OSSMountConfig"] = None, polar_fs_config: Optional["PolarFsConfig"] = None, config: Optional[Config] = None, - ) -> Union["CodeInterpreterSandbox", "BrowserSandbox", "AioSandbox"]: + ) -> Union[ + "CodeInterpreterSandbox", + "BrowserSandbox", + "AioSandbox", + "CustomSandbox", + ]: if template_name is None: # todo 可以考虑为用户创建一个模板? @@ -142,6 +162,7 @@ async def create_async( from agentrun.sandbox.code_interpreter_sandbox import ( CodeInterpreterSandbox, ) + from agentrun.sandbox.custom_sandbox import CustomSandbox if template_type != template.template_type: raise ValueError( @@ -172,6 +193,10 @@ async def create_async( sandbox = AioSandbox.model_validate( base_sandbox.model_dump(by_alias=False) ) + elif template.template_type == TemplateType.CUSTOM: + sandbox = CustomSandbox.model_validate( + base_sandbox.model_dump(by_alias=False) + ) else: raise ValueError( f"template_type {template.template_type} is not supported" @@ -194,7 +219,7 @@ async def stop_by_id_async(cls, sandbox_id: str): if sandbox_id is None: raise ValueError("sandbox_id is required") # todo 后续适配后使用 stop() - return await cls.__get_client().delete_sandbox_async(sandbox_id) + return await cls.__get_client().stop_sandbox_async(sandbox_id) @classmethod async def delete_by_id_async(cls, sandbox_id: str): @@ -460,4 +485,4 @@ async def stop_async(self): if self.sandbox_id is None: raise ValueError("sandbox_id is required to stop a Sandbox") # todo 后续适配后使用 stop() - return await self.delete_by_id_async(self.sandbox_id) + return await self.stop_by_id_async(self.sandbox_id) diff --git a/agentrun/sandbox/custom_sandbox.py b/agentrun/sandbox/custom_sandbox.py new file mode 100644 index 0000000..5e8eadd --- /dev/null +++ b/agentrun/sandbox/custom_sandbox.py @@ -0,0 +1,24 @@ +from typing import Optional + +from agentrun.sandbox.model import TemplateType +from agentrun.utils.config import Config +from agentrun.utils.data_api import DataAPI, ResourceType + +from .sandbox import Sandbox + + +class CustomSandbox(Sandbox): + """Custom Sandbox""" + + _template_type = TemplateType.CUSTOM + + def get_base_url(self, config: Optional[Config] = None): + """Get the base URL for the custom sandbox template.""" + api = DataAPI( + resource_name="", + resource_type=ResourceType.Template, + namespace="sandboxes", + config=config, + ) + + return api.with_path("") diff --git a/agentrun/sandbox/model.py b/agentrun/sandbox/model.py index c662771..f79e1b2 100644 --- a/agentrun/sandbox/model.py +++ b/agentrun/sandbox/model.py @@ -34,6 +34,8 @@ class TemplateType(str, Enum): """浏览器 / Browser""" AIO = "AllInOne" """All-in-One 沙箱 / All-in-One Sandbox""" + CUSTOM = "CustomImage" + """自定义镜像 / Custom Image""" class TemplateNetworkMode(str, Enum): @@ -218,6 +220,12 @@ class TemplateContainerConfiguration(BaseModel): """容器镜像地址 / Container Image Address""" command: Optional[List[str]] = None """容器启动命令 / Container Start Command""" + acr_instance_id: Optional[str] = None + """ACR 实例 ID / ACR Instance ID""" + image_registry_type: Optional[str] = None + """镜像注册表类型 / Image Registry Type""" + port: Optional[int] = None + """端口 / Port""" class TemplateMcpOptions(BaseModel): diff --git a/agentrun/sandbox/sandbox.py b/agentrun/sandbox/sandbox.py index dcffab0..3bf229a 100644 --- a/agentrun/sandbox/sandbox.py +++ b/agentrun/sandbox/sandbox.py @@ -33,6 +33,7 @@ from agentrun.sandbox.aio_sandbox import AioSandbox from agentrun.sandbox.browser_sandbox import BrowserSandbox from agentrun.sandbox.code_interpreter_sandbox import CodeInterpreterSandbox + from agentrun.sandbox.custom_sandbox import CustomSandbox from agentrun.sandbox.model import ( ListSandboxesInput, ListSandboxesOutput, @@ -169,6 +170,34 @@ def create( ) -> "AioSandbox": ... + @classmethod + @overload + async def create_async( + cls, + template_type: Literal[TemplateType.CUSTOM], + template_name: Optional[str] = None, + sandbox_idle_timeout_seconds: Optional[int] = 600, + nas_config: Optional["NASConfig"] = None, + oss_mount_config: Optional["OSSMountConfig"] = None, + polar_fs_config: Optional["PolarFsConfig"] = None, + config: Optional[Config] = None, + ) -> "CustomSandbox": + ... + + @classmethod + @overload + def create( + cls, + template_type: Literal[TemplateType.CUSTOM], + template_name: Optional[str] = None, + sandbox_idle_timeout_seconds: Optional[int] = 600, + nas_config: Optional["NASConfig"] = None, + oss_mount_config: Optional["OSSMountConfig"] = None, + polar_fs_config: Optional["PolarFsConfig"] = None, + config: Optional[Config] = None, + ) -> "CustomSandbox": + ... + @classmethod async def create_async( cls, @@ -179,7 +208,12 @@ async def create_async( oss_mount_config: Optional["OSSMountConfig"] = None, polar_fs_config: Optional["PolarFsConfig"] = None, config: Optional[Config] = None, - ) -> Union["CodeInterpreterSandbox", "BrowserSandbox", "AioSandbox"]: + ) -> Union[ + "CodeInterpreterSandbox", + "BrowserSandbox", + "AioSandbox", + "CustomSandbox", + ]: if template_name is None: # todo 可以考虑为用户创建一个模板? @@ -194,6 +228,7 @@ async def create_async( from agentrun.sandbox.code_interpreter_sandbox import ( CodeInterpreterSandbox, ) + from agentrun.sandbox.custom_sandbox import CustomSandbox if template_type != template.template_type: raise ValueError( @@ -224,6 +259,10 @@ async def create_async( sandbox = AioSandbox.model_validate( base_sandbox.model_dump(by_alias=False) ) + elif template.template_type == TemplateType.CUSTOM: + sandbox = CustomSandbox.model_validate( + base_sandbox.model_dump(by_alias=False) + ) else: raise ValueError( f"template_type {template.template_type} is not supported" @@ -242,7 +281,12 @@ def create( oss_mount_config: Optional["OSSMountConfig"] = None, polar_fs_config: Optional["PolarFsConfig"] = None, config: Optional[Config] = None, - ) -> Union["CodeInterpreterSandbox", "BrowserSandbox", "AioSandbox"]: + ) -> Union[ + "CodeInterpreterSandbox", + "BrowserSandbox", + "AioSandbox", + "CustomSandbox", + ]: if template_name is None: # todo 可以考虑为用户创建一个模板? @@ -257,6 +301,7 @@ def create( from agentrun.sandbox.code_interpreter_sandbox import ( CodeInterpreterSandbox, ) + from agentrun.sandbox.custom_sandbox import CustomSandbox if template_type != template.template_type: raise ValueError( @@ -287,6 +332,10 @@ def create( sandbox = AioSandbox.model_validate( base_sandbox.model_dump(by_alias=False) ) + elif template.template_type == TemplateType.CUSTOM: + sandbox = CustomSandbox.model_validate( + base_sandbox.model_dump(by_alias=False) + ) else: raise ValueError( f"template_type {template.template_type} is not supported" @@ -309,7 +358,7 @@ async def stop_by_id_async(cls, sandbox_id: str): if sandbox_id is None: raise ValueError("sandbox_id is required") # todo 后续适配后使用 stop() - return await cls.__get_client().delete_sandbox_async(sandbox_id) + return await cls.__get_client().stop_sandbox_async(sandbox_id) @classmethod def stop_by_id(cls, sandbox_id: str): @@ -325,7 +374,7 @@ def stop_by_id(cls, sandbox_id: str): if sandbox_id is None: raise ValueError("sandbox_id is required") # todo 后续适配后使用 stop() - return cls.__get_client().delete_sandbox(sandbox_id) + return cls.__get_client().stop_sandbox(sandbox_id) @classmethod async def delete_by_id_async(cls, sandbox_id: str): @@ -839,10 +888,10 @@ async def stop_async(self): if self.sandbox_id is None: raise ValueError("sandbox_id is required to stop a Sandbox") # todo 后续适配后使用 stop() - return await self.delete_by_id_async(self.sandbox_id) + return await self.stop_by_id_async(self.sandbox_id) def stop(self): if self.sandbox_id is None: raise ValueError("sandbox_id is required to stop a Sandbox") # todo 后续适配后使用 stop() - return self.delete_by_id(self.sandbox_id) + return self.stop_by_id(self.sandbox_id) diff --git a/pyproject.toml b/pyproject.toml index de1fbc7..66fa854 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ dependencies = [ "litellm>=1.79.3", "alibabacloud-devs20230714>=2.4.1", "pydash>=8.0.5", - "alibabacloud-agentrun20250910>=5.2.0", + "alibabacloud-agentrun20250910>=5.3.1", "alibabacloud_tea_openapi>=0.4.2", "alibabacloud_bailian20231229>=2.6.2", "agentrun-mem0ai>=0.0.10", @@ -104,7 +104,7 @@ known_third_party = ["alibabacloud_tea_openapi", "alibabacloud_devs20230714", "a sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] [tool.mypy] -python_version = "0.0.15" +python_version = "3.10" exclude = "tests/" plugins = ["pydantic.mypy"] # Start with non-strict mode, and switch to strict mode later.