Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions plugins/seedream/.difyignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
tests/
htmlcov/
.tox/
.nox/
Expand Down Expand Up @@ -126,6 +127,7 @@ celerybeat.pid

# Environments
.env
.env.*
.venv
env/
venv/
Expand Down
107 changes: 22 additions & 85 deletions plugins/seedream/PRIVACY.md
Original file line number Diff line number Diff line change
@@ -1,102 +1,39 @@
## Privacy
# Privacy

This document describes how the seedream plugin handles your data.
This policy describes the data flow of the Ace Data Cloud Seedream plugin for Dify.

### Scope
## Data processed

This privacy statement applies only to the seedream plugin and to data
processed through its features. It does not cover any other plugins, tools,
or services you may use alongside it.
When a tool runs, the plugin processes:

### Data We Collect
- the Ace Data Cloud bearer token configured in Dify;
- the prompt and selected generation options;
- image URLs or image strings supplied for editing or layer decomposition;
- callback URL when the user explicitly provides one;
- generated image metadata, task ID, error details, and trace ID returned by the API.

Depending on how you use the plugin, seedream may process the following
categories of data:
Prompts and images may contain personal or confidential information. Do not submit content that you are not authorized to process.

- **Configuration data**: Settings and preferences you configure for the
plugin (for example, enabling or disabling features, thresholds, or
integration options).
- **Operational data**: Information necessary for the plugin to function,
such as file paths, project identifiers, plugin state, and error messages.
- **Usage data (telemetry)**: Anonymous or aggregated metrics about which
features are used and basic performance information, if telemetry is
enabled in your host environment.
## Data transfer

The plugin is not designed to intentionally collect directly identifying
personal information (such as your name, email address, or payment details)
beyond what is already handled by the platform or service hosting the
plugin. However, personal information could be present in the content of
files or project metadata you work with; such data is processed only to
provide the plugin's functionality.
The plugin sends the selected inputs over HTTPS only to the fixed endpoint `https://api.acedata.cloud/seedream/images`. The bearer token is sent only in the `Authorization` header. The plugin does not expose a configurable destination host.

### How We Use Data
The response is returned to the Dify workflow. The plugin does not send data to advertising or analytics services and contains no telemetry code. A callback URL is sent only when the user explicitly configures it; that destination then receives data according to the user's workflow design.

We use data processed by the plugin solely for the following purposes:
## Storage and retention

- To provide and operate the plugin's core features.
- To maintain and improve reliability, performance, and security.
- To diagnose and fix bugs or issues you report.
- To understand, in aggregate, how features are used (if telemetry is
enabled in your environment).
The plugin does not write prompts, images, responses, or credentials to local files or its own database. Dify controls how plugin credentials, workflow inputs, outputs, and execution logs are stored and retained. Data processed by Ace Data Cloud is subject to the [Ace Data Cloud Privacy Policy](https://platform.acedata.cloud/privacy).

We do not use plugin data to build user profiles for advertising or
marketing.
Generated URLs may expire. Users who need durable artifacts must save them in storage they control and are authorized to use.

### Data Storage and Retention
## Logging and errors

- **Local data**: Configuration and operational data are typically stored
locally within your development environment, project, or host application.
Retention is controlled by that environment (for example, by deleting or
modifying configuration files or logs).
- **Remote services**: If the plugin is configured to communicate with
external services or APIs, any data sent to those services is subject to
their respective privacy policies and retention practices.
- **Diagnostic data**: If you choose to share logs or error reports with the
maintainers (for example, via issue trackers), these may be retained for as
long as needed to investigate and resolve the issue.
The plugin does not log the bearer token. It redacts the configured token from network and API error messages before returning them. Structured failures may include an error code, HTTP status, message, and trace ID for troubleshooting.

### Data Sharing
## Your choices

We do not sell your data.
You can stop further processing by disabling or uninstalling the plugin and removing its credential in Dify. Use Dify's controls to delete retained workflow data or logs. Omit `callback_url` to prevent callback delivery.

We may share data only in the following limited circumstances:

- With service providers or infrastructure used to host issue trackers,
crash reports, or similar tooling, and only to the extent necessary to
operate those services.
- When required by law, regulation, or legal process, or to protect the
rights, property, or safety of users or others.

Any third-party services you configure the plugin to interact with (for
example, source control platforms, CI/CD systems, or external APIs) will
process data under their own terms and privacy policies.

### Security

We take reasonable technical and organizational measures to help protect
data processed by the plugin from unauthorized access, alteration, or
destruction. However, no software system or transmission method is entirely
secure, and we cannot guarantee absolute security.

You are responsible for configuring and securing your own environment,
including access controls, network security, and backups.

### Your Choices and Rights

- You can configure or disable the plugin through your host environment's
extension or plugin management features.
- You can edit or remove configuration files used by the plugin.
- You may choose not to enable integrations with any optional third-party
services.

If you believe the plugin is handling data in a way that is inconsistent
with this policy, you should discontinue use of the plugin and contact the
maintainers.

### Contact

If you have questions about this privacy information, or if you wish to
raise a privacy-related concern, please contact the maintainers of the
seedream plugin through the official project repository or distribution
channel where you obtained this plugin.
## Contact

For plugin questions, open an issue in the [source repository](https://github.com/AceDataCloud/Dify/tree/main/plugins/seedream) or email [office@acedata.cloud](mailto:office@acedata.cloud).
57 changes: 40 additions & 17 deletions plugins/seedream/README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,55 @@
## Seedream
# Seedream

**Author:** Ace Data Cloud

**Type:** Tool provider plugin

**Author:** acedatacloud
**Type:** tool provider plugin
**API:** `https://api.acedata.cloud/seedream/images`

### What it does
**Source:** [AceDataCloud/Dify — Seedream plugin](https://github.com/AceDataCloud/Dify/tree/main/plugins/seedream)

## What it does

This plugin adds image generation, reference-image editing, and editable-layer decomposition to Dify workflows and agents.

## Tools

- `seedream_generate_image` generates one image or a related image set from a prompt.
- `seedream_edit_image` edits one or more referenced images from a prompt.
- `seedream_decompose_image` splits one image into a base image and positioned layers.

Each tool returns `success`, `task_id`, `trace_id`, `data`, and `error` variables. Successful synchronous results also emit image messages for returned image URLs.

## Setup and reviewer check

1. [Create an Ace Data Cloud API token](https://platform.acedata.cloud/console/credentials?utm_source=dify&utm_medium=plugin&utm_campaign=dify-seedream).
2. Install the plugin in Dify and open its provider credentials.
3. Paste the token into `acedata_bearer_token` without the `Bearer` scheme.
4. Add **Seedream Generate Image** to a private test workflow, provide a short non-sensitive prompt, and run it synchronously.
5. Confirm `success=true`, a non-empty `data` array, an image message, and a `trace_id` that can be shared with support if troubleshooting is required.

Image generation and editing can consume credits. Review the [current Seedream pricing](https://platform.acedata.cloud/services/seedream?tab=pricing) before running the test. Do not put production secrets or sensitive source images in a reviewer workflow.

This plugin integrates **Ace Data Cloud Seedream Images API** as Dify tools for:
When `async=true`, the tool may return a task ID before media is ready. This plugin does not expose task retrieval, so use synchronous mode for the reviewer check and for workflows that require an inline image result.

- Generating images from a prompt
- Editing images with a prompt and `image_urls`
## Data and support

### Tools
The plugin calls only `https://api.acedata.cloud/seedream/images`. It sends the configured token in the `Authorization` header and sends only the tool inputs needed for the selected operation. See [PRIVACY.md](PRIVACY.md) for exact processing and retention boundaries.

- `seedream_generate_image`
- Inputs: `prompt` (required), `model`, `size`, `sequential_image_generation`, `stream`, `response_format`, `watermark`, `callback_url`
- Outputs: `success`, `task_id`, `trace_id`, `data` (array of objects with `image_url`, optional `prompt`), `error`
- `seedream_edit_image`
- Inputs: `prompt` (required), `image_urls` (required), other optional params same as generate
- Outputs: same as generate
- [Privacy policy](https://platform.acedata.cloud/privacy)
- [Terms](https://platform.acedata.cloud/terms)
- [Support](https://platform.acedata.cloud/support?utm_source=dify&utm_medium=plugin&utm_campaign=dify-seedream-support)
- [Report an issue](https://github.com/AceDataCloud/Dify/issues)
- Email: [office@acedata.cloud](mailto:office@acedata.cloud)

### Credentials
Last contract review: **2026-09-10**. The manifest intentionally remains `verified: false`; local validation does not represent Dify Marketplace approval or publication.

Requires `acedata_bearer_token` (paste the token without the `Bearer ` prefix).
## Packaging

### Packaging
From the repository root:

```bash
dify plugin package plugins/seedream -o seedream.difypkg
```

The package excludes tests, local environment files, caches, and previously built packages through `.difyignore`.
102 changes: 102 additions & 0 deletions plugins/seedream/tests/test_marketplace_readiness.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from __future__ import annotations

import sys
import unittest
from pathlib import Path
from unittest.mock import Mock, patch

import yaml


PLUGIN_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(PLUGIN_ROOT))

from tools.acedata_client import AceDataSeedreamClient, AceDataSeedreamError # noqa: E402


class SeedreamClientSecurityTest(unittest.TestCase):
@patch("tools.acedata_client.requests.post")
def test_uses_fixed_public_endpoint_and_authorization_header(
self, post: Mock
) -> None:
response = Mock(status_code=200)
response.json.return_value = {"success": True, "trace_id": "trace", "data": []}
post.return_value = response

AceDataSeedreamClient(" Bearer secret-token ").generate_images(
payload={"action": "generate", "prompt": "test"}
)

post.assert_called_once_with(
"https://api.acedata.cloud/seedream/images",
json={"action": "generate", "prompt": "test"},
headers={
"authorization": "Bearer secret-token",
"accept": "application/json",
"content-type": "application/json",
},
timeout=150,
)

@patch("tools.acedata_client.requests.post")
def test_api_error_redacts_configured_token(self, post: Mock) -> None:
response = Mock(status_code=401)
response.json.return_value = {
"error": {"code": "invalid_token", "message": "Rejected secret-token"},
"trace_id": "trace-401",
}
post.return_value = response

with self.assertRaises(AceDataSeedreamError) as raised:
AceDataSeedreamClient("secret-token").generate_images(
payload={"prompt": "test"}
)

self.assertEqual(raised.exception.trace_id, "trace-401")
self.assertNotIn("secret-token", str(raised.exception))
self.assertIn("[redacted]", str(raised.exception))

def test_empty_token_fails_closed(self) -> None:
with self.assertRaises(AceDataSeedreamError) as raised:
AceDataSeedreamClient("Bearer ")
self.assertEqual(raised.exception.code, "token_empty")


class SeedreamMarketplaceContractTest(unittest.TestCase):
def test_manifest_does_not_claim_external_verification(self) -> None:
manifest = yaml.safe_load((PLUGIN_ROOT / "manifest.yaml").read_text())
self.assertIs(manifest["verified"], False)
self.assertEqual(manifest["privacy"], "PRIVACY.md")

def test_package_excludes_development_and_environment_files(self) -> None:
ignore = (PLUGIN_ROOT / ".difyignore").read_text().splitlines()
self.assertIn("tests/", ignore)
self.assertIn(".env.*", ignore)
self.assertIn("*.difypkg", ignore)

def test_readme_has_review_setup_cost_support_and_external_gate(self) -> None:
readme = (PLUGIN_ROOT / "README.md").read_text()
for text in (
"https://github.com/AceDataCloud/Dify/tree/main/plugins/seedream",
"utm_campaign=dify-seedream",
"can consume credits",
"current Seedream pricing",
"success=true",
"does not represent Dify Marketplace approval or publication",
"Last contract review: **2026-09-10**",
):
self.assertIn(text, readme)

def test_privacy_matches_fixed_endpoint_and_no_local_storage(self) -> None:
privacy = (PLUGIN_ROOT / "PRIVACY.md").read_text()
self.assertIn("https://api.acedata.cloud/seedream/images", privacy)
self.assertIn("contains no telemetry code", privacy)
self.assertIn(
"does not write prompts, images, responses, or credentials", privacy
)
self.assertIn("redacts the configured token", privacy)
self.assertNotIn("usage data (telemetry)", privacy.lower())


if __name__ == "__main__":
unittest.main()
Loading
Loading