From 3f5f893e6ca17896c7926db921ce99b446a607e5 Mon Sep 17 00:00:00 2001 From: Harry Date: Fri, 30 Jan 2026 17:32:12 +0800 Subject: [PATCH] feat: add exists method to sandbox sources for existence checks - Implemented the `exists` method in `SandboxFileSource` and its subclasses to verify the availability of sandbox sources. - Updated `SandboxFileService` to utilize the new `exists` method for improved error handling when listing files and downloading files. - Removed the previous check for storage existence in `archive_source.py` and replaced it with the new method. --- api/core/sandbox/inspector/archive_source.py | 8 +++++--- api/core/sandbox/inspector/base.py | 9 +++++++++ api/core/sandbox/inspector/browser.py | 4 ++++ api/core/sandbox/inspector/runtime_source.py | 4 ++++ api/services/sandbox/sandbox_file_service.py | 10 ++++++++++ 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/api/core/sandbox/inspector/archive_source.py b/api/core/sandbox/inspector/archive_source.py index d164e9d050..b53ec57a19 100644 --- a/api/core/sandbox/inspector/archive_source.py +++ b/api/core/sandbox/inspector/archive_source.py @@ -69,8 +69,6 @@ print(json.dumps(entries)) from extensions.storage.file_presign_storage import FilePresignStorage storage_key = f"sandbox_archives/{self._tenant_id}/{self._sandbox_id}.tar.gz" - if not storage.exists(storage_key): - raise ValueError("Sandbox archive not found") presign_storage = FilePresignStorage(storage.storage_runner) return presign_storage.get_download_url(storage_key, self._EXPORT_EXPIRES_IN_SECONDS) @@ -80,9 +78,13 @@ print(json.dumps(entries)) return ZipSandbox(tenant_id=self._tenant_id, user_id="system", app_id="sandbox-archive-browser") + def exists(self) -> bool: + """Check if the sandbox archive exists in storage.""" + storage_key = f"sandbox_archives/{self._tenant_id}/{self._sandbox_id}.tar.gz" + return storage.exists(storage_key) + def list_files(self, *, path: str, recursive: bool) -> list[SandboxFileNode]: archive_url = self._get_archive_download_url() - with self._create_zip_sandbox() as zs: # Download and extract the archive archive_path = zs.download_archive(archive_url, path="workspace.tar.gz") diff --git a/api/core/sandbox/inspector/base.py b/api/core/sandbox/inspector/base.py index 028f3e6e83..3c894305ec 100644 --- a/api/core/sandbox/inspector/base.py +++ b/api/core/sandbox/inspector/base.py @@ -14,6 +14,15 @@ class SandboxFileSource(abc.ABC): self._tenant_id = tenant_id self._sandbox_id = sandbox_id + @abc.abstractmethod + def exists(self) -> bool: + """Check if the sandbox source exists and is available. + + Returns: + True if the sandbox source exists and can be accessed, False otherwise. + """ + raise NotImplementedError + @abc.abstractmethod def list_files(self, *, path: str, recursive: bool) -> list[SandboxFileNode]: raise NotImplementedError diff --git a/api/core/sandbox/inspector/browser.py b/api/core/sandbox/inspector/browser.py index 0c29c31570..bfa7ae985b 100644 --- a/api/core/sandbox/inspector/browser.py +++ b/api/core/sandbox/inspector/browser.py @@ -35,6 +35,10 @@ class SandboxFileBrowser: return SandboxFileRuntimeSource(tenant_id=self._tenant_id, sandbox_id=self._sandbox_id, runtime=runtime) return SandboxFileArchiveSource(tenant_id=self._tenant_id, sandbox_id=self._sandbox_id) + def exists(self) -> bool: + """Check if the sandbox source exists and is available.""" + return self._backend().exists() + def list_files(self, *, path: str | None = None, recursive: bool = False) -> list[SandboxFileNode]: workspace_path = self._normalize_workspace_path(path) return self._backend().list_files(path=workspace_path, recursive=recursive) diff --git a/api/core/sandbox/inspector/runtime_source.py b/api/core/sandbox/inspector/runtime_source.py index d6a5518aed..72f28fc669 100644 --- a/api/core/sandbox/inspector/runtime_source.py +++ b/api/core/sandbox/inspector/runtime_source.py @@ -20,6 +20,10 @@ class SandboxFileRuntimeSource(SandboxFileSource): super().__init__(tenant_id=tenant_id, sandbox_id=sandbox_id) self._runtime = runtime + def exists(self) -> bool: + """Check if the sandbox runtime exists and is available.""" + return self._runtime is not None + def list_files(self, *, path: str, recursive: bool) -> list[SandboxFileNode]: script = r""" import json diff --git a/api/services/sandbox/sandbox_file_service.py b/api/services/sandbox/sandbox_file_service.py index 79e0ef473e..9b36108a7c 100644 --- a/api/services/sandbox/sandbox_file_service.py +++ b/api/services/sandbox/sandbox_file_service.py @@ -20,6 +20,12 @@ class SandboxFileService: cache_key_prefix="sandbox_files", ) + @classmethod + def exists(cls, *, tenant_id: str, sandbox_id: str) -> bool: + """Check if the sandbox source exists and is available.""" + browser = SandboxFileBrowser(tenant_id=tenant_id, sandbox_id=sandbox_id) + return browser.exists() + @classmethod def list_files( cls, @@ -30,9 +36,13 @@ class SandboxFileService: recursive: bool = False, ) -> list[SandboxFileNode]: browser = SandboxFileBrowser(tenant_id=tenant_id, sandbox_id=sandbox_id) + if not browser.exists(): + return [] return browser.list_files(path=path, recursive=recursive) @classmethod def download_file(cls, *, tenant_id: str, sandbox_id: str, path: str) -> SandboxFileDownloadTicket: browser = SandboxFileBrowser(tenant_id=tenant_id, sandbox_id=sandbox_id) + if not browser.exists(): + raise ValueError("Sandbox source not found") return browser.download_file(path=path)