refactor(agent): configure e2b storage with opendal uri

This commit is contained in:
盐粒 Yanli
2026-07-29 03:39:42 +08:00
parent 60f0d09735
commit fc83e23000
20 changed files with 130 additions and 261 deletions
+4 -12
View File
@@ -47,19 +47,11 @@ DIFY_AGENT_E2B_TEMPLATE=difys-default-team/dify-agent-local-sandbox
DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS=3600
DIFY_AGENT_E2B_SHELLCTL_AUTH_TOKEN=
DIFY_AGENT_E2B_SHELLCTL_PORT=5004
# e2b_s3 reuses the E2B settings above and stores immutable Home archives in S3-compatible storage.
# Bucket is required; root defaults to dify-agent. Region and HTTP(S) endpoint are optional.
# e2b_s3 reuses the E2B settings above and stores immutable Home archives in OpenDAL storage.
# The URI selects and configures the OpenDAL service. It must support read, write,
# multi-write, conditional create, and delete.
# It also requires an HTTP(S) DIFY_AGENT_STUB_API_BASE_URL and DIFY_AGENT_SERVER_SECRET_KEY below.
DIFY_AGENT_E2B_S3_BUCKET=
DIFY_AGENT_E2B_S3_ROOT=dify-agent
DIFY_AGENT_E2B_S3_REGION=
DIFY_AGENT_E2B_S3_ENDPOINT=
# Explicit access and secret keys must be configured together. A session token
# is allowed only with that explicit pair; leave all three empty for OpenDAL's
# standard S3 environment/config credential loading.
DIFY_AGENT_E2B_S3_ACCESS_KEY_ID=
DIFY_AGENT_E2B_S3_SECRET_ACCESS_KEY=
DIFY_AGENT_E2B_S3_SESSION_TOKEN=
DIFY_AGENT_E2B_S3_URI=
# Maximum whole-file size read through /workspace/files for an Agent Stub ToolFile upload (50 MiB).
# The environment variable keeps its existing SANDBOX name for deployment compatibility.
DIFY_AGENT_SANDBOX_FILE_UPLOAD_MAX_BYTES=52428800
@@ -187,7 +187,7 @@ though sibling Home content cannot be read or written.
| --- | --- | --- | --- |
| Local | Supported | Supported, including default empty Homes and attaching multiple Bindings to one Workspace | Snapshot directory, per-Binding materialized Home, and Workspace directory are separate. |
| E2B | Supported | Supported with template-backed default Homes, without shared-Workspace attachment | Binding and Workspace refs map to the same E2B resource; checkpoints use E2B snapshots. |
| E2B + S3 (`e2b_s3`) | Supported as immutable OpenDAL/S3 tar.zst streams | Multiple private Binding Homes attach to one shared E2B Workspace Sandbox | Workspace ref is the Sandbox id, Binding ref is `sandbox-id:binding-id`, and Home Snapshot ref is an OpenDAL path relative to `DIFY_AGENT_E2B_S3_ROOT`; the physical S3 key combines the root and ref. |
| E2B + S3 (`e2b_s3`) | Supported as immutable OpenDAL tar.zst streams | Multiple private Binding Homes attach to one shared E2B Workspace Sandbox | Workspace ref is the Sandbox id, Binding ref is `sandbox-id:binding-id`, and Home Snapshot ref is an OpenDAL path relative to the operator root configured by `DIFY_AGENT_E2B_S3_URI`. |
| Enterprise | Not implemented | Default-Home Binding creation, acquire, and coupled destroy are supported | Binding and Workspace refs map to one Gateway sandbox. Explicit Home Snapshot materialization fails fast. |
Local creates a new Home for every Binding id. Destroying one Binding without
+10 -18
View File
@@ -51,13 +51,7 @@ also reads `.env` and `dify-agent/.env` when present.
| `DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS` | `3600` | Maximum continuous active time, up to 3600 seconds. Binding resources pause on timeout. This is not a retention TTL. |
| `DIFY_AGENT_E2B_SHELLCTL_AUTH_TOKEN` | empty | Optional bearer token expected by shellctl inside the E2B template. |
| `DIFY_AGENT_E2B_SHELLCTL_PORT` | `5004` | shellctl port exposed by the E2B template. |
| `DIFY_AGENT_E2B_S3_BUCKET` | empty | S3-compatible bucket required by `e2b_s3`. |
| `DIFY_AGENT_E2B_S3_ROOT` | `dify-agent` | OpenDAL root prefix containing immutable Home Snapshot objects. Snapshot refs are paths relative to this root; the physical object key combines the root and ref. |
| `DIFY_AGENT_E2B_S3_REGION` | empty | Optional S3 region. When omitted, OpenDAL uses its standard S3 environment/config loading. |
| `DIFY_AGENT_E2B_S3_ENDPOINT` | empty | Optional HTTP(S) endpoint for S3-compatible storage. |
| `DIFY_AGENT_E2B_S3_ACCESS_KEY_ID` | empty | Optional explicit S3 access key; must be paired with the secret key. |
| `DIFY_AGENT_E2B_S3_SECRET_ACCESS_KEY` | empty | Optional explicit S3 secret key; must be paired with the access key. |
| `DIFY_AGENT_E2B_S3_SESSION_TOKEN` | empty | Optional session token, allowed only with the explicit credential pair. |
| `DIFY_AGENT_E2B_S3_URI` | empty | OpenDAL scheme URI required by `e2b_s3`. The selected service must support `read`, `write`, `write_can_multi`, `write_with_if_not_exists`, and `delete`; startup fails and lists missing capabilities otherwise. |
| `DIFY_AGENT_SANDBOX_FILE_UPLOAD_MAX_BYTES` | `52428800` | Standalone Dify Agent maximum for whole-file Workspace upload capture; 50 MiB by default. Docker Compose derives it from `PLUGIN_MAX_FILE_SIZE`. |
| `DIFY_AGENT_SHELL_REDACT_PATTERNS` | empty | JSON array of additional regex patterns redacted from Shell output. |
| `DIFY_AGENT_STUB_API_BASE_URL` | empty | Public Agent Stub API base URL reachable from shellctl-managed remote machines. HTTP may be the service root or `/agent-stub`; gRPC must be `grpc://host:port`. Enables `DIFY_AGENT_STUB_*` env injection for user `shell.run` jobs. |
@@ -188,15 +182,15 @@ The physical resource behind a Binding pauses when that timeout fires, preservin
the current Workspace. The setting is not a resource-age TTL and does not delete
paused resources or immutable snapshots.
For `e2b_s3`, export `DIFY_AGENT_RUNTIME_BACKEND=e2b_s3` plus the S3 bucket
settings before starting the same overlay. `DIFY_AGENT_STUB_API_BASE_URL` must
be an HTTP(S) URL reachable from the E2B Sandbox and
For `e2b_s3`, export `DIFY_AGENT_RUNTIME_BACKEND=e2b_s3` plus
`DIFY_AGENT_E2B_S3_URI` before starting the same overlay. The URI selects and
configures an OpenDAL service; its scheme is not restricted to `s3`.
`DIFY_AGENT_STUB_API_BASE_URL` must be an HTTP(S) URL reachable from the E2B Sandbox and
`DIFY_AGENT_SERVER_SECRET_KEY` must be configured. The template must enable
`SHELLCTL_ENABLE_PATH_ISOLATION=true`. Compose does not start MinIO or another
object store. A Home Snapshot ref such as
`home-snapshots/<tenant>/<agent>/<snapshot>.tar.zst` is an OpenDAL path relative
to `DIFY_AGENT_E2B_S3_ROOT`; its physical object key is the configured root
joined with that ref.
to the operator root configured by the URI.
## Run runtime-backend integration contracts
@@ -239,16 +233,14 @@ test time; it is not a post-test retention TTL. Both contracts create unique
resources and perform explicit cleanup in `finally` blocks.
The `e2b_s3` integration path additionally requires a public HTTP Agent Stub
server configured with the same bucket/root/credentials and server secret.
`DIFY_AGENT_TEST_E2B_S3_ROOT` must exactly match the server's
`DIFY_AGENT_E2B_S3_ROOT`; the example uses `dify-agent-integration` for both:
server configured with the same OpenDAL storage URI and server secret.
`DIFY_AGENT_TEST_E2B_S3_URI` must address the same storage and root as the
server's `DIFY_AGENT_E2B_S3_URI`:
```bash
cd dify-agent
DIFY_AGENT_TEST_E2B_API_KEY="$E2B_API_TOKEN" \
DIFY_AGENT_TEST_E2B_S3_BUCKET=integration-bucket \
DIFY_AGENT_TEST_E2B_S3_ROOT=dify-agent-integration \
DIFY_AGENT_TEST_E2B_S3_REGION=us-east-1 \
DIFY_AGENT_TEST_E2B_S3_URI='s3://integration-bucket/dify-agent-integration?region=us-east-1' \
DIFY_AGENT_TEST_E2B_S3_STUB_API_BASE_URL=https://agent.example.com/agent-stub \
DIFY_AGENT_TEST_SERVER_SECRET_KEY="$DIFY_AGENT_SERVER_SECRET_KEY" \
pdm run pytest --import-mode=importlib \
+9 -1
View File
@@ -25,7 +25,7 @@ server = [
"jsonschema>=4.23.0,<5.0.0",
"jwcrypto>=1.5.6,<2",
"logfire[fastapi,httpx,redis]>=4.37.0,<5.0.0",
"opendal==0.47.3",
"opendal==0.47.5",
"pydantic-ai-slim[anthropic,google,openai]>=1.85.1,<2.0.0",
"pydantic-settings>=2.12.0,<3.0.0",
"redis>=7.4.0,<8.0.0",
@@ -51,6 +51,14 @@ testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
markers = ["integration: requires a real external service or exercises multiple concrete adapters"]
[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple"
explicit = true
[tool.uv.sources]
opendal = { index = "testpypi" }
[tool.ruff]
line-length = 120
target-version = "py312"
@@ -71,6 +71,13 @@ _BASELINE_EXCLUDES = (
".dify-agent-runtime",
".local/share/shellctl",
)
_REQUIRED_HOME_ARCHIVE_CAPABILITIES = (
"read",
"write",
"write_can_multi",
"write_with_if_not_exists",
"delete",
)
logger = logging.getLogger(__name__)
@@ -118,27 +125,17 @@ class OpenDALHomeArchiveStore:
operator: opendal.AsyncOperator
@classmethod
def create_s3(
cls,
*,
bucket: str,
root: str,
region: str | None = None,
endpoint: str | None = None,
access_key_id: str | None = None,
secret_access_key: str | None = None,
session_token: str | None = None,
) -> "OpenDALHomeArchiveStore":
options = {"bucket": bucket, "root": root}
optional = {
"region": region,
"endpoint": endpoint,
"access_key_id": access_key_id,
"secret_access_key": secret_access_key,
"session_token": session_token,
}
options.update({key: value for key, value in optional.items() if value is not None and value.strip()})
return cls(operator=opendal.AsyncOperator("s3", **options))
def create_from_uri(cls, uri: str) -> "OpenDALHomeArchiveStore":
operator = opendal.AsyncOperator.from_uri(uri)
capability = operator.capability()
missing = [
name for name in _REQUIRED_HOME_ARCHIVE_CAPABILITIES if not getattr(capability, name, False)
]
if missing:
raise ValueError(
"OpenDAL Home Snapshot storage is missing required capabilities: " + ", ".join(missing)
)
return cls(operator=operator)
async def open_writer(self, snapshot_ref: str) -> OpenDALAsyncFile:
path = validate_home_snapshot_ref(snapshot_ref)
@@ -62,13 +62,7 @@ class RuntimeBackendSettings(BaseSettings):
)
e2b_shellctl_auth_token: str = ""
e2b_shellctl_port: int = Field(default=5004, ge=1, le=65535)
e2b_s3_bucket: str | None = None
e2b_s3_root: str = "dify-agent"
e2b_s3_region: str | None = None
e2b_s3_endpoint: str | None = None
e2b_s3_access_key_id: str | None = None
e2b_s3_secret_access_key: str | None = None
e2b_s3_session_token: str | None = None
e2b_s3_uri: str | None = None
agent_stub_api_base_url: str | None = None
server_secret_key: str | None = None
@@ -81,12 +75,7 @@ class RuntimeBackendSettings(BaseSettings):
@field_validator(
"e2b_api_key",
"e2b_s3_bucket",
"e2b_s3_region",
"e2b_s3_endpoint",
"e2b_s3_access_key_id",
"e2b_s3_secret_access_key",
"e2b_s3_session_token",
"e2b_s3_uri",
"agent_stub_api_base_url",
"server_secret_key",
mode="before",
@@ -121,18 +110,8 @@ class RuntimeBackendSettings(BaseSettings):
return self
def _validate_e2b_s3(self) -> None:
if not self.e2b_s3_bucket or not self.e2b_s3_bucket.strip():
raise ValueError("e2b_s3_bucket is required for the e2b_s3 runtime backend")
if not self.e2b_s3_root.strip():
raise ValueError("e2b_s3_root must not be blank")
if self.e2b_s3_endpoint is not None:
_validate_http_url(self.e2b_s3_endpoint, field_name="e2b_s3_endpoint")
has_access_key = bool(self.e2b_s3_access_key_id and self.e2b_s3_access_key_id.strip())
has_secret_key = bool(self.e2b_s3_secret_access_key and self.e2b_s3_secret_access_key.strip())
if has_access_key != has_secret_key:
raise ValueError("e2b_s3_access_key_id and e2b_s3_secret_access_key must be configured together")
if self.e2b_s3_session_token and self.e2b_s3_session_token.strip() and not has_access_key:
raise ValueError("e2b_s3_session_token requires explicit S3 credentials")
if not self.e2b_s3_uri:
raise ValueError("e2b_s3_uri is required for the e2b_s3 runtime backend")
if not self.agent_stub_api_base_url:
raise ValueError("agent_stub_api_base_url is required for the e2b_s3 runtime backend")
_validate_http_url(self.agent_stub_api_base_url, field_name="agent_stub_api_base_url")
@@ -179,15 +158,7 @@ def create_runtime_backend_profile(settings: RuntimeBackendSettings) -> RuntimeB
)
case "e2b_s3":
control_plane = E2BSDKControlPlane(api_key=settings.e2b_api_key or "")
archive_store = OpenDALHomeArchiveStore.create_s3(
bucket=settings.e2b_s3_bucket or "",
root=settings.e2b_s3_root,
region=settings.e2b_s3_region,
endpoint=settings.e2b_s3_endpoint,
access_key_id=settings.e2b_s3_access_key_id,
secret_access_key=settings.e2b_s3_secret_access_key,
session_token=settings.e2b_s3_session_token,
)
archive_store = OpenDALHomeArchiveStore.create_from_uri(settings.e2b_s3_uri or "")
token_codec = HomeSnapshotTransferTokenCodec.from_server_secret(settings.server_secret_key or "")
lifecycle_cli = E2BHomeSnapshotCLI(
token_codec=token_codec,
+8 -49
View File
@@ -10,10 +10,9 @@ optional gRPC bind override, and optional Dify inner API bridge settings all
live here under the ``DIFY_AGENT_...`` environment-variable namespace.
"""
import httpx
from typing import ClassVar, Literal, cast
from urllib.parse import urlparse
import httpx
from pydantic import AliasChoices, AnyHttpUrl, Field, TypeAdapter, field_validator, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -67,13 +66,7 @@ class ServerSettings(BaseSettings):
)
e2b_shellctl_auth_token: str = ""
e2b_shellctl_port: int = Field(default=5004, ge=1, le=65535)
e2b_s3_bucket: str | None = None
e2b_s3_root: str = "dify-agent"
e2b_s3_region: str | None = None
e2b_s3_endpoint: str | None = None
e2b_s3_access_key_id: str | None = None
e2b_s3_secret_access_key: str | None = None
e2b_s3_session_token: str | None = None
e2b_s3_uri: str | None = None
sandbox_file_upload_max_bytes: int = Field(default=50 * 1024 * 1024, ge=1)
agent_stub_api_base_url: str | None = Field(default=None, validation_alias="DIFY_AGENT_STUB_API_BASE_URL")
agent_stub_grpc_bind_address: str | None = Field(default=None, validation_alias="DIFY_AGENT_STUB_GRPC_BIND_ADDRESS")
@@ -97,12 +90,7 @@ class ServerSettings(BaseSettings):
@field_validator(
"e2b_api_key",
"e2b_s3_bucket",
"e2b_s3_region",
"e2b_s3_endpoint",
"e2b_s3_access_key_id",
"e2b_s3_secret_access_key",
"e2b_s3_session_token",
"e2b_s3_uri",
mode="before",
)
@classmethod
@@ -199,23 +187,8 @@ class ServerSettings(BaseSettings):
raise ValueError("DIFY_AGENT_E2B_API_KEY is required for the e2b_s3 runtime backend.")
if not self.e2b_template.strip():
raise ValueError("DIFY_AGENT_E2B_TEMPLATE must not be blank.")
if not self.e2b_s3_bucket or not self.e2b_s3_bucket.strip():
raise ValueError("DIFY_AGENT_E2B_S3_BUCKET is required for the e2b_s3 runtime backend.")
if not self.e2b_s3_root.strip():
raise ValueError("DIFY_AGENT_E2B_S3_ROOT must not be blank.")
if self.e2b_s3_endpoint is not None:
parsed_s3_endpoint = urlparse(self.e2b_s3_endpoint.strip())
if parsed_s3_endpoint.scheme not in {"http", "https"} or not parsed_s3_endpoint.netloc:
raise ValueError("DIFY_AGENT_E2B_S3_ENDPOINT must be a valid HTTP(S) URL.")
has_access_key = bool(self.e2b_s3_access_key_id and self.e2b_s3_access_key_id.strip())
has_secret_key = bool(self.e2b_s3_secret_access_key and self.e2b_s3_secret_access_key.strip())
if has_access_key != has_secret_key:
raise ValueError(
"DIFY_AGENT_E2B_S3_ACCESS_KEY_ID and DIFY_AGENT_E2B_S3_SECRET_ACCESS_KEY "
+ "must be configured together."
)
if self.e2b_s3_session_token and self.e2b_s3_session_token.strip() and not has_access_key:
raise ValueError("DIFY_AGENT_E2B_S3_SESSION_TOKEN requires explicit S3 credentials.")
if not self.e2b_s3_uri:
raise ValueError("DIFY_AGENT_E2B_S3_URI is required for the e2b_s3 runtime backend.")
if self.agent_stub_api_base_url is None:
raise ValueError("DIFY_AGENT_STUB_API_BASE_URL is required for the e2b_s3 runtime backend.")
if parse_agent_stub_endpoint(self.agent_stub_api_base_url).is_grpc:
@@ -242,13 +215,7 @@ class ServerSettings(BaseSettings):
e2b_active_timeout_seconds=self.e2b_active_timeout_seconds,
e2b_shellctl_auth_token=self.e2b_shellctl_auth_token,
e2b_shellctl_port=self.e2b_shellctl_port,
e2b_s3_bucket=self.e2b_s3_bucket,
e2b_s3_root=self.e2b_s3_root,
e2b_s3_region=self.e2b_s3_region,
e2b_s3_endpoint=self.e2b_s3_endpoint,
e2b_s3_access_key_id=self.e2b_s3_access_key_id,
e2b_s3_secret_access_key=self.e2b_s3_secret_access_key,
e2b_s3_session_token=self.e2b_s3_session_token,
e2b_s3_uri=self.e2b_s3_uri,
agent_stub_api_base_url=self.agent_stub_api_base_url,
server_secret_key=self.server_secret_key,
)
@@ -258,15 +225,7 @@ class ServerSettings(BaseSettings):
"""Build the e2b_s3 HTTP byte-stream gateway from independent resources."""
if self.runtime_backend != "e2b_s3":
return None
store = OpenDALHomeArchiveStore.create_s3(
bucket=self.e2b_s3_bucket or "",
root=self.e2b_s3_root,
region=self.e2b_s3_region,
endpoint=self.e2b_s3_endpoint,
access_key_id=self.e2b_s3_access_key_id,
secret_access_key=self.e2b_s3_secret_access_key,
session_token=self.e2b_s3_session_token,
)
store = OpenDALHomeArchiveStore.create_from_uri(self.e2b_s3_uri or "")
codec = HomeSnapshotTransferTokenCodec.from_server_secret(self.server_secret_key or "")
return HomeSnapshotGatewayService(token_codec=codec, archive_store=store)
@@ -209,7 +209,7 @@ async def test_e2b_binding_checkpoint_and_collection() -> None:
@pytest.mark.anyio
async def test_e2b_s3_shared_workspace_checkpoint_and_collection() -> None:
api_key = _required_env("DIFY_AGENT_TEST_E2B_API_KEY", "real E2B + S3")
bucket = _required_env("DIFY_AGENT_TEST_E2B_S3_BUCKET", "real E2B + S3")
storage_uri = _required_env("DIFY_AGENT_TEST_E2B_S3_URI", "real E2B + OpenDAL storage")
stub_url = _required_env("DIFY_AGENT_TEST_E2B_S3_STUB_API_BASE_URL", "public Home Snapshot gateway")
server_secret = _required_env("DIFY_AGENT_TEST_SERVER_SECRET_KEY", "Home Snapshot transfer JWE")
template = os.environ.get(
@@ -218,15 +218,7 @@ async def test_e2b_s3_shared_workspace_checkpoint_and_collection() -> None:
)
marker = uuid.uuid4().hex
control = E2BSDKControlPlane(api_key=api_key)
store = OpenDALHomeArchiveStore.create_s3(
bucket=bucket,
root=os.environ.get("DIFY_AGENT_TEST_E2B_S3_ROOT", "dify-agent-integration"),
region=os.environ.get("DIFY_AGENT_TEST_E2B_S3_REGION") or None,
endpoint=os.environ.get("DIFY_AGENT_TEST_E2B_S3_ENDPOINT") or None,
access_key_id=os.environ.get("DIFY_AGENT_TEST_E2B_S3_ACCESS_KEY_ID") or None,
secret_access_key=os.environ.get("DIFY_AGENT_TEST_E2B_S3_SECRET_ACCESS_KEY") or None,
session_token=os.environ.get("DIFY_AGENT_TEST_E2B_S3_SESSION_TOKEN") or None,
)
store = OpenDALHomeArchiveStore.create_from_uri(storage_uri)
lifecycle_cli = E2BHomeSnapshotCLI(
token_codec=HomeSnapshotTransferTokenCodec.from_server_secret(server_secret),
agent_stub_api_base_url=stub_url,
@@ -76,7 +76,7 @@ def test_create_agent_stub_app_injects_e2b_s3_home_snapshot_gateway(monkeypatch)
settings = ServerSettings(
runtime_backend="e2b_s3",
e2b_api_key="e2b-secret",
e2b_s3_bucket="snapshots",
e2b_s3_uri="s3://snapshots/dify-agent",
agent_stub_api_base_url="https://agent.example.com/agent-stub",
server_secret_key=_base64url_secret(b"1" * 32),
)
@@ -3,6 +3,7 @@ from __future__ import annotations
import asyncio
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
from typing import cast
import opendal
@@ -950,6 +951,23 @@ async def test_home_snapshot_cli_failure_always_closes_control_lease(
assert fake_e2b_lease[0].close_calls == 1
def test_opendal_store_constructs_from_uri_when_required_capabilities_are_supported(tmp_path: Path) -> None:
store = OpenDALHomeArchiveStore.create_from_uri(f"fs://{tmp_path}")
assert isinstance(store.operator, opendal.AsyncOperator)
def test_opendal_store_rejects_uri_with_missing_required_capabilities() -> None:
with pytest.raises(
ValueError,
match=(
"OpenDAL Home Snapshot storage is missing required capabilities: "
"write, write_can_multi, write_with_if_not_exists, delete"
),
):
_ = OpenDALHomeArchiveStore.create_from_uri("http://example.com/root")
@pytest.mark.anyio
async def test_opendal_store_conditionally_writes_streams_and_deletes_idempotently() -> None:
store = OpenDALHomeArchiveStore(operator=opendal.AsyncOperator("memory"))
@@ -40,8 +40,8 @@ def test_local_backend_requires_shellctl_endpoint() -> None:
_ = RuntimeBackendSettings(runtime_backend="local")
def test_e2b_s3_requires_bucket_http_gateway_and_server_secret() -> None:
with pytest.raises(ValidationError, match="e2b_s3_bucket"):
def test_e2b_s3_requires_uri_http_gateway_and_server_secret() -> None:
with pytest.raises(ValidationError, match="e2b_s3_uri"):
_ = RuntimeBackendSettings(
runtime_backend="e2b_s3",
e2b_api_key="secret",
@@ -52,14 +52,14 @@ def test_e2b_s3_requires_bucket_http_gateway_and_server_secret() -> None:
_ = RuntimeBackendSettings(
runtime_backend="e2b_s3",
e2b_api_key="secret",
e2b_s3_bucket="bucket",
e2b_s3_uri="s3://bucket/dify-agent",
server_secret_key=_secret(),
)
with pytest.raises(ValidationError, match="http\\(s\\)"):
_ = RuntimeBackendSettings(
runtime_backend="e2b_s3",
e2b_api_key="secret",
e2b_s3_bucket="bucket",
e2b_s3_uri="s3://bucket/dify-agent",
agent_stub_api_base_url="grpc://agent.example:9091",
server_secret_key=_secret(),
)
@@ -67,34 +67,18 @@ def test_e2b_s3_requires_bucket_http_gateway_and_server_secret() -> None:
_ = RuntimeBackendSettings(
runtime_backend="e2b_s3",
e2b_api_key="secret",
e2b_s3_bucket="bucket",
e2b_s3_uri="s3://bucket/dify-agent",
agent_stub_api_base_url="https://agent.example/agent-stub",
)
def test_e2b_s3_validates_explicit_credentials_and_session_token() -> None:
common: dict[str, object] = {
"runtime_backend": "e2b_s3",
"e2b_api_key": "secret",
"e2b_s3_bucket": "bucket",
"agent_stub_api_base_url": "https://agent.example/agent-stub",
"server_secret_key": _secret(),
}
with pytest.raises(ValidationError, match="configured together"):
_ = RuntimeBackendSettings.model_validate({**common, "e2b_s3_access_key_id": "access"})
with pytest.raises(ValidationError, match="requires explicit"):
_ = RuntimeBackendSettings.model_validate({**common, "e2b_s3_session_token": "session"})
settings = RuntimeBackendSettings.model_validate(
{
**common,
"e2b_s3_region": " ",
"e2b_s3_endpoint": "",
"e2b_s3_access_key_id": "access",
"e2b_s3_secret_access_key": "secret",
"e2b_s3_session_token": "session",
}
def test_e2b_s3_normalizes_uri() -> None:
settings = RuntimeBackendSettings(
runtime_backend="e2b_s3",
e2b_api_key="secret",
e2b_s3_uri=" s3://bucket/dify-agent?region=us-east-1 ",
agent_stub_api_base_url="https://agent.example/agent-stub",
server_secret_key=_secret(),
)
assert settings.e2b_s3_root == "dify-agent"
assert settings.e2b_s3_region is None
assert settings.e2b_s3_endpoint is None
assert settings.e2b_s3_uri == "s3://bucket/dify-agent?region=us-east-1"
@@ -330,7 +330,7 @@ def test_create_app_injects_e2b_s3_home_snapshot_gateway(monkeypatch: pytest.Mon
settings = ServerSettings(
runtime_backend="e2b_s3",
e2b_api_key="e2b-secret",
e2b_s3_bucket="snapshots",
e2b_s3_uri="s3://snapshots/dify-agent",
agent_stub_api_base_url="https://agent.example.com/agent-stub",
server_secret_key=_base64url_secret(b"1" * 32),
)
@@ -310,30 +310,25 @@ def test_build_runtime_backend_profile_passes_e2b_active_timeout() -> None:
def test_build_runtime_backend_profile_and_gateway_for_e2b_s3(monkeypatch: pytest.MonkeyPatch) -> None:
store_calls: list[dict[str, object]] = []
store_calls: list[str] = []
stores: list[OpenDALHomeArchiveStore] = []
def create_s3_store(
def create_store_from_uri(
cls: type[OpenDALHomeArchiveStore],
**kwargs: object,
uri: str,
) -> OpenDALHomeArchiveStore:
del cls
store_calls.append(kwargs)
store_calls.append(uri)
store = cast(OpenDALHomeArchiveStore, object())
stores.append(store)
return store
monkeypatch.setattr(OpenDALHomeArchiveStore, "create_s3", classmethod(create_s3_store))
monkeypatch.setattr(OpenDALHomeArchiveStore, "create_from_uri", classmethod(create_store_from_uri))
storage_uri = "s3://snapshots/tenant-root?region=us-east-1"
settings = ServerSettings(
runtime_backend="e2b_s3",
e2b_api_key="e2b-secret",
e2b_s3_bucket="snapshots",
e2b_s3_root="tenant-root",
e2b_s3_region="us-east-1",
e2b_s3_endpoint="https://s3.example.test",
e2b_s3_access_key_id="access-key",
e2b_s3_secret_access_key="secret-key",
e2b_s3_session_token="session-token",
e2b_s3_uri=storage_uri,
agent_stub_api_base_url="https://agent.example/agent-stub",
server_secret_key=_base64url_secret(b"s" * 32),
)
@@ -347,16 +342,7 @@ def test_build_runtime_backend_profile_and_gateway_for_e2b_s3(monkeypatch: pytes
assert profile.home_snapshots.control_plane is profile.execution_bindings.control_plane
assert profile.home_snapshots.lifecycle_cli is profile.execution_bindings.lifecycle_cli
assert gateway is not None
expected_store_settings = {
"bucket": "snapshots",
"root": "tenant-root",
"region": "us-east-1",
"endpoint": "https://s3.example.test",
"access_key_id": "access-key",
"secret_access_key": "secret-key",
"session_token": "session-token",
}
assert store_calls == [expected_store_settings, expected_store_settings]
assert store_calls == [storage_uri, storage_uri]
assert profile.home_snapshots.archive_store is stores[0]
assert gateway.archive_store is stores[1]
assert gateway.token_codec is not profile.home_snapshots.lifecycle_cli.token_codec
@@ -366,8 +352,8 @@ def test_home_snapshot_gateway_is_not_built_for_other_profiles() -> None:
assert ServerSettings().build_home_snapshot_gateway() is None
def test_server_settings_rejects_e2b_s3_without_required_s3_or_http_settings() -> None:
with pytest.raises(ValidationError, match="DIFY_AGENT_E2B_S3_BUCKET"):
def test_server_settings_rejects_e2b_s3_without_required_storage_or_http_settings() -> None:
with pytest.raises(ValidationError, match="DIFY_AGENT_E2B_S3_URI"):
_ = ServerSettings(
runtime_backend="e2b_s3",
e2b_api_key="e2b-secret",
@@ -378,7 +364,7 @@ def test_server_settings_rejects_e2b_s3_without_required_s3_or_http_settings() -
_ = ServerSettings(
runtime_backend="e2b_s3",
e2b_api_key="e2b-secret",
e2b_s3_bucket="snapshots",
e2b_s3_uri="s3://snapshots/dify-agent",
agent_stub_api_base_url="grpc://agent.example:9091",
server_secret_key=_base64url_secret(b"s" * 32),
)
+1 -1
View File
@@ -21,7 +21,7 @@ SERVER_RUNTIME_DEPENDENCIES = {
"jsonschema>=4.23.0,<5.0.0",
"jwcrypto>=1.5.6,<2",
"logfire[fastapi,httpx,redis]>=4.37.0,<5.0.0",
"opendal==0.47.3",
"opendal==0.47.5",
"pydantic-ai-slim[anthropic,google,openai]>=1.85.1,<2.0.0",
"pydantic-settings>=2.12.0,<3.0.0",
"redis>=7.4.0,<8.0.0",
+22 -22
View File
@@ -647,7 +647,7 @@ requires-dist = [
{ name = "jsonschema", marker = "extra == 'server'", specifier = ">=4.23.0,<5.0.0" },
{ name = "jwcrypto", marker = "extra == 'server'", specifier = ">=1.5.6,<2" },
{ name = "logfire", extras = ["fastapi", "httpx", "redis"], marker = "extra == 'server'", specifier = ">=4.37.0,<5.0.0" },
{ name = "opendal", marker = "extra == 'server'", specifier = "==0.47.3" },
{ name = "opendal", marker = "extra == 'server'", specifier = "==0.47.5", index = "https://test.pypi.org/simple" },
{ name = "protobuf", marker = "extra == 'grpc'", specifier = ">=6.33.5,<7.0.0" },
{ name = "pydantic", specifier = ">=2.12.5,<2.13" },
{ name = "pydantic-ai-slim", specifier = ">=1.102.0,<2.0.0" },
@@ -2052,28 +2052,28 @@ wheels = [
[[package]]
name = "opendal"
version = "0.47.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8e/56/048e7013047c2b80c4415c6f41c3099c676da34ac914e463204f18091645/opendal-0.47.3.tar.gz", hash = "sha256:f91873645c6ce391755a0352fe9e469699419e61d60c89d3c8f5c3b262451430", size = 1419363, upload-time = "2026-07-10T11:07:31.103Z" }
version = "0.47.5"
source = { registry = "https://test.pypi.org/simple" }
sdist = { url = "https://test-files.pythonhosted.org/packages/48/65/44f2816c43e01a637ac8e77a2dde99c0f131b7617200af97c87637fd63fb/opendal-0.47.5.tar.gz", hash = "sha256:ca44d9eb5bf27d905e55d480d656dab1955b1528718c75dcb5d279fd19634b57", size = 1766937, upload-time = "2026-07-28T08:10:01.164Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/46/58/2bd9961bbbc18b106e5e64cc6cb7093abd21465b10e0bd6fca515bfec52f/opendal-0.47.3-cp311-abi3-macosx_10_12_x86_64.whl", hash = "sha256:8187ad7124e909dc7b89f7227506bedddfeef389d376a16667bffeb8d80ebaa3", size = 17819057, upload-time = "2026-07-10T11:06:46.182Z" },
{ url = "https://files.pythonhosted.org/packages/21/b1/39c09024f179344c603a784c8d16ee80ac7345d4eccc42bda052be4b364c/opendal-0.47.3-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:356cf2f677d94f241bcca84a2e6e4d165458ef694bd31591eb39c9cd534e8e87", size = 16436832, upload-time = "2026-07-10T11:06:48.849Z" },
{ url = "https://files.pythonhosted.org/packages/de/98/230bff458603fca13f8f5277e9e96c4a56603093c0bb97153bbd0dc914ef/opendal-0.47.3-cp311-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1960a653ba38e6d7ca91109a815c5549b53ffa6f69237a0190abb5a511475114", size = 17180352, upload-time = "2026-07-10T11:06:51.398Z" },
{ url = "https://files.pythonhosted.org/packages/c4/fb/9f69d6543d84db3369985d28d0918ec8357bf2b65eb90e21b2266910b629/opendal-0.47.3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e4e5a87a903f2b30ee0fb2626cddb08450fc2669a6df5f6a56225425476377a", size = 18259558, upload-time = "2026-07-10T11:06:53.766Z" },
{ url = "https://files.pythonhosted.org/packages/3c/8b/9b6ccf5d4f486f5672d200b0daef8666bf1ee1e43bf321ecedda43789b2e/opendal-0.47.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4b864c02dc84be808d6e98011863b74fe2050db396c7dd061c940e3f3f6e294b", size = 17372540, upload-time = "2026-07-10T11:06:56.289Z" },
{ url = "https://files.pythonhosted.org/packages/95/a7/cc170bd00a187460399cfb219d3d52fbdf345ad0416bcd87aa69c880169e/opendal-0.47.3-cp311-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:dcbbd4825b2d90a747b8f3ccd8f5208328930cb7cb9cb7ef13dd6b58a81f5093", size = 17731303, upload-time = "2026-07-10T11:06:58.769Z" },
{ url = "https://files.pythonhosted.org/packages/bc/9b/1f5bd9f67a11ad0d86def760cea189386f1fd061d10a61936cfe8010dc94/opendal-0.47.3-cp311-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:df9e9d7d510165ea62447b3e7488b1dff06323dc8d2617f80bd7d984ad798405", size = 17470164, upload-time = "2026-07-10T11:07:01.322Z" },
{ url = "https://files.pythonhosted.org/packages/d0/b5/b067b0055d1fb331a7c9b28bce142d3d3a83dd0f56480d9f735091784bc2/opendal-0.47.3-cp311-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:47a529f229976495e5229f6b916fcffc4c4f950c04380406f531d5b4735507b7", size = 18501611, upload-time = "2026-07-10T11:07:03.774Z" },
{ url = "https://files.pythonhosted.org/packages/ea/38/ef8fbfd6d21158d5ac2ea4c694f04790cd122c29da54933b712d5cdacdd4/opendal-0.47.3-cp311-abi3-win_amd64.whl", hash = "sha256:cca2d8d9a40e6151529ba8eab3b15acbb15497f41514139b53dfe227cb514bd2", size = 19671465, upload-time = "2026-07-10T11:07:06.622Z" },
{ url = "https://files.pythonhosted.org/packages/10/40/6b89a058d58193c6146a4a3a3646dadb2608afe80b6c8c79814cd11033f3/opendal-0.47.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:051a02d56a823e1993d7686cec8bab2174dd3ffd82d8531b6fd0daa6adec807a", size = 17817945, upload-time = "2026-07-10T11:07:08.941Z" },
{ url = "https://files.pythonhosted.org/packages/02/55/7a836c0a114ace48c69168f7257663baaf825df9d47d3d6a1179934a1a6e/opendal-0.47.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4c04dc93789c4140df94341598553dce23f43b20f2f709282998b927bccd8a2a", size = 16414489, upload-time = "2026-07-10T11:07:11.416Z" },
{ url = "https://files.pythonhosted.org/packages/3d/47/03bb7a6141841e373bd310f6b7e03057675437f9b9a9056c0147d0698c8d/opendal-0.47.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cd43422502d77ad153b041b6bfa7c6831c95e16f0d783e69e80afcf237250ef8", size = 17163020, upload-time = "2026-07-10T11:07:13.721Z" },
{ url = "https://files.pythonhosted.org/packages/8e/0c/5c5185b5c5e63fefa99138e69304e51a7eb35cbb6396e022d94b1d71c598/opendal-0.47.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a61bc5b3e6c0d8702f3e7605b68d41a20c89a8e59e6f461882fa7b2142ba49a2", size = 18242953, upload-time = "2026-07-10T11:07:16.484Z" },
{ url = "https://files.pythonhosted.org/packages/73/e6/8b8a683835708e37cd85739487f90f3fdd84f03cbecbc5262f9942eab6f8/opendal-0.47.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b719146ebefa6f3c52e329c1c9beec54b8f26aa04c4789197d4aa9e12d9aab33", size = 17358553, upload-time = "2026-07-10T11:07:18.742Z" },
{ url = "https://files.pythonhosted.org/packages/20/f4/2e45e9647f307cb7e3e0e81c2140f0dadf1f96672ff7d58caee377e5f837/opendal-0.47.3-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:375b585a9eafc119d50c1dbb3034dc0e35db6dbb87fbacbb6505067433f21782", size = 17714742, upload-time = "2026-07-10T11:07:21.769Z" },
{ url = "https://files.pythonhosted.org/packages/fc/da/37ca6f8d8500dbf72e01974eba68981639aaa3dec077d0cdcfbfa8519930/opendal-0.47.3-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:9841083c98e8896dd208dc40cc4a7905eb0b0a8af8422b2a94dd3d45b0a31383", size = 17455237, upload-time = "2026-07-10T11:07:24.249Z" },
{ url = "https://files.pythonhosted.org/packages/1c/23/72d566b06778c1af4aa59ea43c6c4f28011f77d4f55a0cefd87b62c319e9/opendal-0.47.3-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:376c85766b973fe423dfa0fc03245557b27ffcb88f087d62f70c96585938fb5c", size = 18485329, upload-time = "2026-07-10T11:07:26.564Z" },
{ url = "https://files.pythonhosted.org/packages/f2/44/0e57e3804af4a5ae45e78a06794d072d1635b09445bcb98a797afdd322bf/opendal-0.47.3-cp314-cp314t-win_amd64.whl", hash = "sha256:14b53bb4d188da150be15cd6702d6ef866885d452474e9d4b96c39cf3c8bc055", size = 19644607, upload-time = "2026-07-10T11:07:29.029Z" },
{ url = "https://test-files.pythonhosted.org/packages/17/91/b572904880ee8998031fdc7afbaf292e52bf20e523c6d13968e6b3baa009/opendal-0.47.5-cp311-abi3-macosx_10_12_x86_64.whl", hash = "sha256:57a453e32dee21d4d943a59c997b6a41ac658c94b2b40a656bd9f4613a61f8be", size = 17529259, upload-time = "2026-07-28T08:09:17.218Z" },
{ url = "https://test-files.pythonhosted.org/packages/31/e2/caa9c622d1c4604f5e6ee9a3aaf2cc39bf8e3889b2326528b959db0fc3ff/opendal-0.47.5-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:fb2efabdb98beb8eb8ce9bbdb33662f4430a95df9716320f90dd8608b826022b", size = 16118000, upload-time = "2026-07-28T08:09:19.679Z" },
{ url = "https://test-files.pythonhosted.org/packages/28/e3/463213e2fa8e98a8ca4699f2fe13159290ac80bf242bbe70f119d36473c3/opendal-0.47.5-cp311-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:342a51b8e5fae9f049492af72fa4f16aaf6d16177f31edd0ee5178b57067be64", size = 16803733, upload-time = "2026-07-28T08:09:22.393Z" },
{ url = "https://test-files.pythonhosted.org/packages/2e/4f/a2441d286c2534fb2f00d23f18b684b6bf327e74da8a3a4367ebf766dccf/opendal-0.47.5-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5101c9b75ad3f4a24593def41f7ce723962b79cb3e601151e808f5d78f78e87", size = 17955349, upload-time = "2026-07-28T08:09:24.774Z" },
{ url = "https://test-files.pythonhosted.org/packages/d8/0e/a63cdeec6ba72270c10d93361f97dcc60105aa06c91b127328badb962aa7/opendal-0.47.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3a403748c68b9587405ede6236d3618a8f5833ccede623be7386f24d4e5c31a8", size = 17026301, upload-time = "2026-07-28T08:09:27.847Z" },
{ url = "https://test-files.pythonhosted.org/packages/8a/5a/86d009a6bab0c02e636b5087b47bb9a7fd7b7b1785f2362d88c846ef9485/opendal-0.47.5-cp311-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:90703efb7f365639dc843d475f2b2477ec561824c0a68ad6a75c35f5c20e7751", size = 17375804, upload-time = "2026-07-28T08:09:30.801Z" },
{ url = "https://test-files.pythonhosted.org/packages/99/11/ff7365587b241aacba5537c78864790b60dc8bb660796bea1fa201a97ca0/opendal-0.47.5-cp311-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:c28395331c92839f91a162ba2b92edf2e92f023ff13adb1bb46b234da8172eb8", size = 17102402, upload-time = "2026-07-28T08:09:33.085Z" },
{ url = "https://test-files.pythonhosted.org/packages/9b/3f/b24d881b970a41133be418876c79ba5b34c6e9811029b5d4fb9c04ccf203/opendal-0.47.5-cp311-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ee27f1df07608a4ba30c9a5f09341850f7f6c8695d6559522302c2c410db161f", size = 18187917, upload-time = "2026-07-28T08:09:35.408Z" },
{ url = "https://test-files.pythonhosted.org/packages/90/b8/0b387e569b098e923977e36ec1b243c15dfe387eba9026984068bd87cd05/opendal-0.47.5-cp311-abi3-win_amd64.whl", hash = "sha256:1016ad511d8c61611719c9ffe509571ee216791bfcc07dc6aec47a756c30aa67", size = 19199295, upload-time = "2026-07-28T08:09:37.91Z" },
{ url = "https://test-files.pythonhosted.org/packages/3c/9f/63a687163b2a9ba66af8b4206ae4a7ae226e9c54355298cbef15cc54bfa5/opendal-0.47.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:012eeed30870754e7edc4895ee75306e32ff82b1d340d897b504ab8b7d02cfaf", size = 17536831, upload-time = "2026-07-28T08:09:41.003Z" },
{ url = "https://test-files.pythonhosted.org/packages/d5/d9/e1cb2dcb2c4d919f30b93ec6c3d777525e476074f6938b7aa9018f8305f9/opendal-0.47.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:99cadd469dcecb17866a20826e080e58c9dc0b4205340870603b58faae1e0f97", size = 16101302, upload-time = "2026-07-28T08:09:43.3Z" },
{ url = "https://test-files.pythonhosted.org/packages/d9/c0/d179754422478d879a732a81d9ea6d89607bbc645d6ec07f7036fe4d528d/opendal-0.47.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4968311d0f483311273192b90c79e0731eea32598ef253bb41de9c51bff70af8", size = 16797051, upload-time = "2026-07-28T08:09:45.311Z" },
{ url = "https://test-files.pythonhosted.org/packages/a0/2f/fcb7f079badbc7306d778f8b20a0ab83ff34d74bd4fe7e0c3a118dd5a05c/opendal-0.47.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47ecf76aa397cb06329d4e69e6c0afdfc34c5b6ae7c6da87efd33c006b38cde5", size = 17949096, upload-time = "2026-07-28T08:09:47.875Z" },
{ url = "https://test-files.pythonhosted.org/packages/c1/0c/bfdd05726f5c1c15b0b4e26e6337b8d8a1cd2a0aa7f5cdfa617b1041c141/opendal-0.47.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b0a31a17f67ad59b7f3baf0c1cec7e60949ce3f7c7ebf5baba493015d8ac89e3", size = 17016235, upload-time = "2026-07-28T08:09:50.176Z" },
{ url = "https://test-files.pythonhosted.org/packages/2c/ef/210fc93d5173cd7e5634b31ebee87466845b90eb92483c50d8f76d240ba7/opendal-0.47.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:307f095d31056162a16ce02a44253f1b3de60cf638f837c5bfca5bbc06c53bdb", size = 17369912, upload-time = "2026-07-28T08:09:52.298Z" },
{ url = "https://test-files.pythonhosted.org/packages/31/8e/0a615f7344d85e07b39ba577ebe15211816611f032770d8c7ace6422338a/opendal-0.47.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:c526449e91b8442116acc77ec8f3dc7c87bbdd63f06acefc3e1ff880a06eb3a2", size = 17094062, upload-time = "2026-07-28T08:09:54.96Z" },
{ url = "https://test-files.pythonhosted.org/packages/f1/89/103514cebe8fc760741378772657eadfcf156412d72064344e19f53f10a3/opendal-0.47.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:b77948ec4ae48b3e9ce4fc6e6e196f82324916458305a01d81282ed520f303a0", size = 18180436, upload-time = "2026-07-28T08:09:57.267Z" },
{ url = "https://test-files.pythonhosted.org/packages/08/85/9e00b103f5c0bbc58e46c506d68b1179bafc6ab24104b62999dda3842082/opendal-0.47.5-cp314-cp314t-win_amd64.whl", hash = "sha256:d03f8dba373bcb1d080bf0b09165c9b6b1e546c30d0a1718bbe00d01e576688f", size = 19183036, upload-time = "2026-07-28T08:09:59.304Z" },
]
[[package]]
+1 -7
View File
@@ -281,13 +281,7 @@ DIFY_AGENT_E2B_TEMPLATE=difys-default-team/dify-agent-local-sandbox
DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS=3600
DIFY_AGENT_E2B_SHELLCTL_AUTH_TOKEN=
DIFY_AGENT_E2B_SHELLCTL_PORT=5004
DIFY_AGENT_E2B_S3_BUCKET=
DIFY_AGENT_E2B_S3_ROOT=dify-agent
DIFY_AGENT_E2B_S3_REGION=
DIFY_AGENT_E2B_S3_ENDPOINT=
DIFY_AGENT_E2B_S3_ACCESS_KEY_ID=
DIFY_AGENT_E2B_S3_SECRET_ACCESS_KEY=
DIFY_AGENT_E2B_S3_SESSION_TOKEN=
DIFY_AGENT_E2B_S3_URI=
DIFY_AGENT_STUB_API_BASE_URL=http://agent_backend:5050/agent-stub
# This is security-sensitive: it derives the JWE encryption key for Agent Stub bearer tokens.
# Replace this development default in production.
+1 -7
View File
@@ -676,13 +676,7 @@ services:
DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS: ${DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS:-3600}
DIFY_AGENT_E2B_SHELLCTL_AUTH_TOKEN: ${DIFY_AGENT_E2B_SHELLCTL_AUTH_TOKEN:-}
DIFY_AGENT_E2B_SHELLCTL_PORT: ${DIFY_AGENT_E2B_SHELLCTL_PORT:-5004}
DIFY_AGENT_E2B_S3_BUCKET: ${DIFY_AGENT_E2B_S3_BUCKET:-}
DIFY_AGENT_E2B_S3_ROOT: ${DIFY_AGENT_E2B_S3_ROOT:-dify-agent}
DIFY_AGENT_E2B_S3_REGION: ${DIFY_AGENT_E2B_S3_REGION:-}
DIFY_AGENT_E2B_S3_ENDPOINT: ${DIFY_AGENT_E2B_S3_ENDPOINT:-}
DIFY_AGENT_E2B_S3_ACCESS_KEY_ID: ${DIFY_AGENT_E2B_S3_ACCESS_KEY_ID:-}
DIFY_AGENT_E2B_S3_SECRET_ACCESS_KEY: ${DIFY_AGENT_E2B_S3_SECRET_ACCESS_KEY:-}
DIFY_AGENT_E2B_S3_SESSION_TOKEN: ${DIFY_AGENT_E2B_S3_SESSION_TOKEN:-}
DIFY_AGENT_E2B_S3_URI: ${DIFY_AGENT_E2B_S3_URI:-}
DIFY_AGENT_SANDBOX_FILE_UPLOAD_MAX_BYTES: ${PLUGIN_MAX_FILE_SIZE:-52428800}
DIFY_AGENT_STUB_API_BASE_URL: ${DIFY_AGENT_STUB_API_BASE_URL:-http://agent_backend:5050/agent-stub}
# This is security-sensitive: it derives the JWE encryption key for Agent Stub bearer tokens.
+1 -7
View File
@@ -28,13 +28,7 @@ services:
DIFY_AGENT_RUNTIME_BACKEND: ${DIFY_AGENT_RUNTIME_BACKEND:-e2b}
DIFY_AGENT_E2B_API_KEY: ${DIFY_AGENT_E2B_API_KEY:-${E2B_API_KEY:-${E2B_API_TOKEN:-}}}
DIFY_AGENT_E2B_TEMPLATE: ${DIFY_AGENT_E2B_TEMPLATE:-difys-default-team/dify-agent-local-sandbox}
DIFY_AGENT_E2B_S3_BUCKET: ${DIFY_AGENT_E2B_S3_BUCKET:-}
DIFY_AGENT_E2B_S3_ROOT: ${DIFY_AGENT_E2B_S3_ROOT:-dify-agent}
DIFY_AGENT_E2B_S3_REGION: ${DIFY_AGENT_E2B_S3_REGION:-}
DIFY_AGENT_E2B_S3_ENDPOINT: ${DIFY_AGENT_E2B_S3_ENDPOINT:-}
DIFY_AGENT_E2B_S3_ACCESS_KEY_ID: ${DIFY_AGENT_E2B_S3_ACCESS_KEY_ID:-}
DIFY_AGENT_E2B_S3_SECRET_ACCESS_KEY: ${DIFY_AGENT_E2B_S3_SECRET_ACCESS_KEY:-}
DIFY_AGENT_E2B_S3_SESSION_TOKEN: ${DIFY_AGENT_E2B_S3_SESSION_TOKEN:-}
DIFY_AGENT_E2B_S3_URI: ${DIFY_AGENT_E2B_S3_URI:-}
ports:
- "${EXPOSE_AGENT_BACKEND_PORT:-15050}:5050"
+1 -7
View File
@@ -682,13 +682,7 @@ services:
DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS: ${DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS:-3600}
DIFY_AGENT_E2B_SHELLCTL_AUTH_TOKEN: ${DIFY_AGENT_E2B_SHELLCTL_AUTH_TOKEN:-}
DIFY_AGENT_E2B_SHELLCTL_PORT: ${DIFY_AGENT_E2B_SHELLCTL_PORT:-5004}
DIFY_AGENT_E2B_S3_BUCKET: ${DIFY_AGENT_E2B_S3_BUCKET:-}
DIFY_AGENT_E2B_S3_ROOT: ${DIFY_AGENT_E2B_S3_ROOT:-dify-agent}
DIFY_AGENT_E2B_S3_REGION: ${DIFY_AGENT_E2B_S3_REGION:-}
DIFY_AGENT_E2B_S3_ENDPOINT: ${DIFY_AGENT_E2B_S3_ENDPOINT:-}
DIFY_AGENT_E2B_S3_ACCESS_KEY_ID: ${DIFY_AGENT_E2B_S3_ACCESS_KEY_ID:-}
DIFY_AGENT_E2B_S3_SECRET_ACCESS_KEY: ${DIFY_AGENT_E2B_S3_SECRET_ACCESS_KEY:-}
DIFY_AGENT_E2B_S3_SESSION_TOKEN: ${DIFY_AGENT_E2B_S3_SESSION_TOKEN:-}
DIFY_AGENT_E2B_S3_URI: ${DIFY_AGENT_E2B_S3_URI:-}
DIFY_AGENT_SANDBOX_FILE_UPLOAD_MAX_BYTES: ${PLUGIN_MAX_FILE_SIZE:-52428800}
DIFY_AGENT_STUB_API_BASE_URL: ${DIFY_AGENT_STUB_API_BASE_URL:-http://agent_backend:5050/agent-stub}
# This is security-sensitive: it derives the JWE encryption key for Agent Stub bearer tokens.
@@ -32,13 +32,7 @@ DIFY_AGENT_E2B_TEMPLATE=difys-default-team/dify-agent-local-sandbox
DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS=3600
DIFY_AGENT_E2B_SHELLCTL_AUTH_TOKEN=
DIFY_AGENT_E2B_SHELLCTL_PORT=5004
DIFY_AGENT_E2B_S3_BUCKET=
DIFY_AGENT_E2B_S3_ROOT=dify-agent
DIFY_AGENT_E2B_S3_REGION=
DIFY_AGENT_E2B_S3_ENDPOINT=
DIFY_AGENT_E2B_S3_ACCESS_KEY_ID=
DIFY_AGENT_E2B_S3_SECRET_ACCESS_KEY=
DIFY_AGENT_E2B_S3_SESSION_TOKEN=
DIFY_AGENT_E2B_S3_URI=
# Standalone/direct-container byte limit. Full Docker Compose derives this from
# PLUGIN_MAX_FILE_SIZE in docker/.env.
DIFY_AGENT_SANDBOX_FILE_UPLOAD_MAX_BYTES=52428800