mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
refactor: unify download item types and eliminate extension-based branching
Merge AssetDownloadItem, AssetInlineItem into SandboxDownloadItem with optional 'content' field. All consumers now follow a clean pipeline: get items → accessor.resolve_items() → AppAssetService.to_download_items() → download Key changes: - SandboxDownloadItem gains content: bytes | None (entities.py) - ZipSandbox.download_items() handles both inline (base64 heredoc) and remote (curl) via a single pipeline — no structural branching - AssetDownloadService.build_download_script() takes unified list - CachedContentAccessor.resolve_items() batch-enriches items from DB (extension-agnostic, no 'if md' checks needed) - AppAssetService.to_download_items() converts AssetItem → SandboxDownloadItem - DraftAppAssetsInitializer, package_and_upload, export_bundle simplified - file_upload/node.py switched to SandboxDownloadItem - Deleted AssetDownloadItem and AssetInlineItem classes
This commit is contained in:
@ -1,4 +1,11 @@
|
||||
from .zip_sandbox import SandboxDownloadItem, SandboxFile, SandboxUploadItem, ZipSandbox
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .entities import SandboxDownloadItem, SandboxFile, SandboxUploadItem
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .zip_sandbox import ZipSandbox
|
||||
|
||||
__all__ = [
|
||||
"SandboxDownloadItem",
|
||||
@ -6,3 +13,11 @@ __all__ = [
|
||||
"SandboxUploadItem",
|
||||
"ZipSandbox",
|
||||
]
|
||||
|
||||
|
||||
def __getattr__(name: str):
|
||||
if name == "ZipSandbox":
|
||||
from .zip_sandbox import ZipSandbox
|
||||
|
||||
return ZipSandbox
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
|
||||
39
api/core/zip_sandbox/entities.py
Normal file
39
api/core/zip_sandbox/entities.py
Normal file
@ -0,0 +1,39 @@
|
||||
"""Data classes for ZipSandbox file operations.
|
||||
|
||||
Separated from ``zip_sandbox.py`` so that lightweight consumers (tests,
|
||||
shell-script builders) can import the types without pulling in the full
|
||||
sandbox provider chain.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SandboxDownloadItem:
|
||||
"""Unified download/inline item for sandbox file operations.
|
||||
|
||||
For remote files, *url* is set and the item is fetched via ``curl``.
|
||||
For inline content, *content* is set and the bytes are written directly
|
||||
into the VM via ``upload_file`` — no network round-trip.
|
||||
"""
|
||||
|
||||
path: str
|
||||
url: str = ""
|
||||
content: bytes | None = field(default=None, repr=False)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SandboxUploadItem:
|
||||
"""Item for uploading: sandbox path -> URL."""
|
||||
|
||||
path: str
|
||||
url: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SandboxFile:
|
||||
"""A handle to a file in the sandbox."""
|
||||
|
||||
path: str
|
||||
@ -1,7 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import posixpath
|
||||
from dataclasses import dataclass
|
||||
import shlex
|
||||
from io import BytesIO
|
||||
from pathlib import PurePosixPath
|
||||
from types import TracebackType
|
||||
@ -20,34 +21,12 @@ from core.virtual_environment.__base.virtual_environment import VirtualEnvironme
|
||||
from services.sandbox.sandbox_provider_service import SandboxProviderService
|
||||
|
||||
from .cli_strategy import CliZipStrategy
|
||||
from .entities import SandboxDownloadItem, SandboxFile, SandboxUploadItem
|
||||
from .node_strategy import NodeZipStrategy
|
||||
from .python_strategy import PythonZipStrategy
|
||||
from .strategy import ZipStrategy
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SandboxDownloadItem:
|
||||
"""Item for downloading: URL -> sandbox path."""
|
||||
|
||||
url: str
|
||||
path: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SandboxUploadItem:
|
||||
"""Item for uploading: sandbox path -> URL."""
|
||||
|
||||
path: str
|
||||
url: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SandboxFile:
|
||||
"""A handle to a file in the sandbox."""
|
||||
|
||||
path: str
|
||||
|
||||
|
||||
class ZipSandbox:
|
||||
"""A sandbox for archive (zip) operations.
|
||||
|
||||
@ -221,6 +200,12 @@ class ZipSandbox:
|
||||
# ========== Download operations ==========
|
||||
|
||||
def download_items(self, items: list[SandboxDownloadItem], *, dest_dir: str = ".") -> list[str]:
|
||||
"""Download or write items into the sandbox via a single pipeline.
|
||||
|
||||
Remote items (with *url*) are fetched via ``curl``. Inline items
|
||||
(with *content*) are written via ``base64 -d`` heredoc. Both go
|
||||
through the same pipeline — no branching at the structural level.
|
||||
"""
|
||||
if not items:
|
||||
return []
|
||||
|
||||
@ -238,7 +223,10 @@ class ZipSandbox:
|
||||
out_dir = posixpath.dirname(out_path)
|
||||
if out_dir not in ("", "."):
|
||||
p.add(["mkdir", "-p", out_dir], error_message="Failed to create download directory")
|
||||
p.add(["curl", "-fsSL", item.url, "-o", out_path], error_message="Failed to download file")
|
||||
p.add(
|
||||
self.to_download_command(item, out_path),
|
||||
error_message=f"Failed to write {item.path}",
|
||||
)
|
||||
|
||||
try:
|
||||
p.execute(timeout=self._DEFAULT_TIMEOUT_SECONDS, raise_on_error=True)
|
||||
@ -247,6 +235,14 @@ class ZipSandbox:
|
||||
|
||||
return out_paths
|
||||
|
||||
@staticmethod
|
||||
def to_download_command(item: SandboxDownloadItem, out_path: str) -> list[str]:
|
||||
"""Return the shell command to materialise *item* at *out_path*."""
|
||||
if item.content is not None:
|
||||
encoded = base64.b64encode(item.content).decode("ascii")
|
||||
return ["sh", "-c", f"base64 -d <<'_B64_' > {shlex.quote(out_path)}\n{encoded}\n_B64_"]
|
||||
return ["curl", "-fsSL", item.url, "-o", out_path]
|
||||
|
||||
def download_archive(self, archive_url: str, *, path: str = "input.tar.gz") -> str:
|
||||
path = self._normalize_path(path)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user