diff --git a/Organization/filegenerator.signed.difypkg b/Organization/filegenerator.signed.difypkg new file mode 100644 index 0000000000..18d80e9250 Binary files /dev/null and b/Organization/filegenerator.signed.difypkg differ diff --git a/Organization/filegenerator/.difyignore b/Organization/filegenerator/.difyignore new file mode 100644 index 0000000000..4ea917564e --- /dev/null +++ b/Organization/filegenerator/.difyignore @@ -0,0 +1,179 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ + +# Vscode +.vscode/ + +# Git +.git/ +.gitignore +.github/ + +# Mac +.DS_Store + +# Windows +Thumbs.db diff --git a/Organization/filegenerator/.env.example b/Organization/filegenerator/.env.example new file mode 100644 index 0000000000..60358af873 --- /dev/null +++ b/Organization/filegenerator/.env.example @@ -0,0 +1,3 @@ +INSTALL_METHOD=remote +REMOTE_INSTALL_URL=debug.dify.ai:5003 +REMOTE_INSTALL_KEY=********-****-****-****-************ diff --git a/Organization/filegenerator/.github/workflows/plugin-publish.yml b/Organization/filegenerator/.github/workflows/plugin-publish.yml new file mode 100644 index 0000000000..d24c4dd51f --- /dev/null +++ b/Organization/filegenerator/.github/workflows/plugin-publish.yml @@ -0,0 +1,109 @@ +name: Plugin Publish Workflow + +on: + release: + types: [published] + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Download CLI tool + run: | + mkdir -p $RUNNER_TEMP/bin + cd $RUNNER_TEMP/bin + + wget https://github.com/langgenius/dify-plugin-daemon/releases/download/0.0.6/dify-plugin-linux-amd64 + chmod +x dify-plugin-linux-amd64 + + echo "CLI tool location:" + pwd + ls -la dify-plugin-linux-amd64 + + - name: Get basic info from manifest + id: get_basic_info + run: | + PLUGIN_NAME=$(grep "^name:" manifest.yaml | cut -d' ' -f2) + echo "Plugin name: $PLUGIN_NAME" + echo "plugin_name=$PLUGIN_NAME" >> $GITHUB_OUTPUT + + VERSION=$(grep "^version:" manifest.yaml | cut -d' ' -f2) + echo "Plugin version: $VERSION" + echo "version=$VERSION" >> $GITHUB_OUTPUT + + # If the author's name is not your github username, you can change the author here + AUTHOR=$(grep "^author:" manifest.yaml | cut -d' ' -f2) + echo "Plugin author: $AUTHOR" + echo "author=$AUTHOR" >> $GITHUB_OUTPUT + + - name: Package Plugin + id: package + run: | + cd $GITHUB_WORKSPACE + PACKAGE_NAME="${{ steps.get_basic_info.outputs.plugin_name }}-${{ steps.get_basic_info.outputs.version }}.difypkg" + $RUNNER_TEMP/bin/dify-plugin-linux-amd64 plugin package . -o "$PACKAGE_NAME" + + echo "Package result:" + ls -la "$PACKAGE_NAME" + echo "package_name=$PACKAGE_NAME" >> $GITHUB_OUTPUT + + echo "\nFull file path:" + pwd + echo "\nDirectory structure:" + tree || ls -R + + - name: Checkout target repo + uses: actions/checkout@v3 + with: + repository: ${{steps.get_basic_info.outputs.author}}/dify-plugins + path: dify-plugins + token: ${{ secrets.PLUGIN_ACTION }} + fetch-depth: 1 + persist-credentials: true + + - name: Prepare and create PR + run: | + PACKAGE_NAME="${{ steps.get_basic_info.outputs.plugin_name }}-${{ steps.get_basic_info.outputs.version }}.difypkg" + mkdir -p dify-plugins/${{ steps.get_basic_info.outputs.author }}/${{ steps.get_basic_info.outputs.plugin_name }} + mv "$PACKAGE_NAME" dify-plugins/${{ steps.get_basic_info.outputs.author }}/${{ steps.get_basic_info.outputs.plugin_name }}/ + + cd dify-plugins + + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + + git fetch origin main + git checkout main + git pull origin main + + BRANCH_NAME="bump-${{ steps.get_basic_info.outputs.plugin_name }}-plugin-${{ steps.get_basic_info.outputs.version }}" + git checkout -b "$BRANCH_NAME" + + git add . + git commit -m "bump ${{ steps.get_basic_info.outputs.plugin_name }} plugin to version ${{ steps.get_basic_info.outputs.version }}" + + git push -u origin "$BRANCH_NAME" --force + + git branch -a + echo "Waiting for branch to sync..." + sleep 10 # Wait 10 seconds for branch sync + + - name: Create PR via GitHub API + env: + # How to config the token: + # 1. Profile -> Settings -> Developer settings -> Personal access tokens -> Generate new token (with repo scope) -> Copy the token + # 2. Go to the target repository -> Settings -> Secrets and variables -> Actions -> New repository secret -> Add the token as PLUGIN_ACTION + GH_TOKEN: ${{ secrets.PLUGIN_ACTION }} + run: | + gh pr create \ + --repo langgenius/dify-plugins \ + --head "${{ steps.get_basic_info.outputs.author }}:${{ steps.get_basic_info.outputs.plugin_name }}-${{ steps.get_basic_info.outputs.version }}" \ + --base main \ + --title "bump ${{ steps.get_basic_info.outputs.plugin_name }} plugin to version ${{ steps.get_basic_info.outputs.version }}" \ + --body "bump ${{ steps.get_basic_info.outputs.plugin_name }} plugin package to version ${{ steps.get_basic_info.outputs.version }} + + Changes: + - Updated plugin package file" || echo "PR already exists or creation skipped." # Handle cases where PR already exists diff --git a/Organization/filegenerator/.gitignore b/Organization/filegenerator/.gitignore new file mode 100644 index 0000000000..a16dc9791a --- /dev/null +++ b/Organization/filegenerator/.gitignore @@ -0,0 +1,176 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ + +# Vscode +.vscode/ + +# macOS +.DS_Store +.AppleDouble +.LSOverride \ No newline at end of file diff --git a/Organization/filegenerator/GUIDE.md b/Organization/filegenerator/GUIDE.md new file mode 100644 index 0000000000..7b9b466d37 --- /dev/null +++ b/Organization/filegenerator/GUIDE.md @@ -0,0 +1,148 @@ +## User Guide of how to develop a Dify Plugin + +Hi there, looks like you have already created a Plugin, now let's get you started with the development! + +### Choose a Plugin type you want to develop + +Before start, you need some basic knowledge about the Plugin types, Plugin supports to extend the following abilities in Dify: +- **Tool**: Tool Providers like Google Search, Stable Diffusion, etc. it can be used to perform a specific task. +- **Model**: Model Providers like OpenAI, Anthropic, etc. you can use their models to enhance the AI capabilities. +- **Endpoint**: Like Service API in Dify and Ingress in Kubernetes, you can extend a http service as an endpoint and control its logics using your own code. + +Based on the ability you want to extend, we have divided the Plugin into three types: **Tool**, **Model**, and **Extension**. + +- **Tool**: It's a tool provider, but not only limited to tools, you can implement an endpoint there, for example, you need both `Sending Message` and `Receiving Message` if you are building a Discord Bot, **Tool** and **Endpoint** are both required. +- **Model**: Just a model provider, extending others is not allowed. +- **Extension**: Other times, you may only need a simple http service to extend the functionalities, **Extension** is the right choice for you. + +I believe you have chosen the right type for your Plugin while creating it, if not, you can change it later by modifying the `manifest.yaml` file. + +### Manifest + +Now you can edit the `manifest.yaml` file to describe your Plugin, here is the basic structure of it: + +- version(version, required):Plugin's version +- type(type, required):Plugin's type, currently only supports `plugin`, future support `bundle` +- author(string, required):Author, it's the organization name in Marketplace and should also equals to the owner of the repository +- label(label, required):Multi-language name +- created_at(RFC3339, required):Creation time, Marketplace requires that the creation time must be less than the current time +- icon(asset, required):Icon path +- resource (object):Resources to be applied + - memory (int64):Maximum memory usage, mainly related to resource application on SaaS for serverless, unit bytes + - permission(object):Permission application + - tool(object):Reverse call tool permission + - enabled (bool) + - model(object):Reverse call model permission + - enabled(bool) + - llm(bool) + - text_embedding(bool) + - rerank(bool) + - tts(bool) + - speech2text(bool) + - moderation(bool) + - node(object):Reverse call node permission + - enabled(bool) + - endpoint(object):Allow to register endpoint permission + - enabled(bool) + - app(object):Reverse call app permission + - enabled(bool) + - storage(object):Apply for persistent storage permission + - enabled(bool) + - size(int64):Maximum allowed persistent memory, unit bytes +- plugins(object, required):Plugin extension specific ability yaml file list, absolute path in the plugin package, if you need to extend the model, you need to define a file like openai.yaml, and fill in the path here, and the file on the path must exist, otherwise the packaging will fail. + - Format + - tools(list[string]): Extended tool suppliers, as for the detailed format, please refer to [Tool Guide](https://docs.dify.ai/plugins/schema-definition/tool) + - models(list[string]):Extended model suppliers, as for the detailed format, please refer to [Model Guide](https://docs.dify.ai/plugins/schema-definition/model) + - endpoints(list[string]):Extended Endpoints suppliers, as for the detailed format, please refer to [Endpoint Guide](https://docs.dify.ai/plugins/schema-definition/endpoint) + - Restrictions + - Not allowed to extend both tools and models + - Not allowed to have no extension + - Not allowed to extend both models and endpoints + - Currently only supports up to one supplier of each type of extension +- meta(object) + - version(version, required):manifest format version, initial version 0.0.1 + - arch(list[string], required):Supported architectures, currently only supports amd64 arm64 + - runner(object, required):Runtime configuration + - language(string):Currently only supports python + - version(string):Language version, currently only supports 3.12 + - entrypoint(string):Program entry, in python it should be main + +### Install Dependencies + +- First of all, you need a Python 3.11+ environment, as our SDK requires that. +- Then, install the dependencies: + ```bash + pip install -r requirements.txt + ``` +- If you want to add more dependencies, you can add them to the `requirements.txt` file, once you have set the runner to python in the `manifest.yaml` file, `requirements.txt` will be automatically generated and used for packaging and deployment. + +### Implement the Plugin + +Now you can start to implement your Plugin, by following these examples, you can quickly understand how to implement your own Plugin: + +- [OpenAI](https://github.com/langgenius/dify-plugin-sdks/tree/main/python/examples/openai): best practice for model provider +- [Google Search](https://github.com/langgenius/dify-plugin-sdks/tree/main/python/examples/google): a simple example for tool provider +- [Neko](https://github.com/langgenius/dify-plugin-sdks/tree/main/python/examples/neko): a funny example for endpoint group + +### Test and Debug the Plugin + +You may already noticed that a `.env.example` file in the root directory of your Plugin, just copy it to `.env` and fill in the corresponding values, there are some environment variables you need to set if you want to debug your Plugin locally. + +- `INSTALL_METHOD`: Set this to `remote`, your plugin will connect to a Dify instance through the network. +- `REMOTE_INSTALL_URL`: The URL of debugging host and port of plugin-daemon service from your Dify instance, eg. `debug.dify.ai:5003`. Either the [Dify SaaS](https://debug.dify.ai) or [self-hosted Dify instance](https://docs.dify.ai/en/getting-started/install-self-hosted/readme) can be used. +- `REMOTE_INSTALL_KEY`: You should get your debugging key from the Dify instance you used, at the right top of the plugin management page, you can see a button with a `debug` icon, click it and you will get the key. + +Run the following command to start your Plugin: + +```bash +python -m main +``` + +Refresh the page of your Dify instance, you should be able to see your Plugin in the list now, but it will be marked as `debugging`, you can use it normally, but not recommended for production. + +### Publish and Update the Plugin + +To streamline your plugin update workflow, you can configure GitHub Actions to automatically create PRs to the Dify plugin repository whenever you create a release. + +##### Prerequisites + +- Your plugin source repository +- A fork of the dify-plugins repository +- Proper plugin directory structure in your fork + +#### Configure GitHub Action + +1. Create a Personal Access Token with write permissions to your forked repository +2. Add it as a secret named `PLUGIN_ACTION` in your source repository settings +3. Create a workflow file at `.github/workflows/plugin-publish.yml` + +#### Usage + +1. Update your code and the version in your `manifest.yaml` +2. Create a release in your source repository +3. The action automatically packages your plugin and creates a PR to your forked repository + +#### Benefits + +- Eliminates manual packaging and PR creation steps +- Ensures consistency in your release process +- Saves time during frequent updates + +--- + +For detailed setup instructions and example configuration, visit: [GitHub Actions Workflow Documentation](https://docs.dify.ai/plugins/publish-plugins/plugin-auto-publish-pr) + +### Package the Plugin + +After all, just package your Plugin by running the following command: + +```bash +dify-plugin plugin package ./ROOT_DIRECTORY_OF_YOUR_PLUGIN +``` + +you will get a `plugin.difypkg` file, that's all, you can submit it to the Marketplace now, look forward to your Plugin being listed! + + +## User Privacy Policy + +Please fill in the privacy policy of the plugin if you want to make it published on the Marketplace, refer to [PRIVACY.md](PRIVACY.md) for more details. \ No newline at end of file diff --git a/Organization/filegenerator/PRIVACY.md b/Organization/filegenerator/PRIVACY.md new file mode 100644 index 0000000000..b088c73aa1 --- /dev/null +++ b/Organization/filegenerator/PRIVACY.md @@ -0,0 +1,3 @@ +## Privacy + +!!! Please fill in the privacy policy of the plugin. \ No newline at end of file diff --git a/Organization/filegenerator/README.md b/Organization/filegenerator/README.md new file mode 100644 index 0000000000..ecf5bebafd --- /dev/null +++ b/Organization/filegenerator/README.md @@ -0,0 +1,10 @@ +## filegenerator + +**Author:** randomradio +**Version:** 0.0.1 +**Type:** tool + +### Description + + + diff --git a/Organization/filegenerator/_assets/icon.svg b/Organization/filegenerator/_assets/icon.svg new file mode 100644 index 0000000000..375222f801 --- /dev/null +++ b/Organization/filegenerator/_assets/icon.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/Organization/filegenerator/main.py b/Organization/filegenerator/main.py new file mode 100644 index 0000000000..7e1a983dbd --- /dev/null +++ b/Organization/filegenerator/main.py @@ -0,0 +1,6 @@ +from dify_plugin import Plugin, DifyPluginEnv + +plugin = Plugin(DifyPluginEnv(MAX_REQUEST_TIMEOUT=120)) + +if __name__ == '__main__': + plugin.run() diff --git a/Organization/filegenerator/manifest.yaml b/Organization/filegenerator/manifest.yaml new file mode 100644 index 0000000000..0173d0c94c --- /dev/null +++ b/Organization/filegenerator/manifest.yaml @@ -0,0 +1,48 @@ +version: 0.0.1 +type: plugin +author: randomradio +name: filegenerator +label: + en_US: filegenerator + ja_JP: filegenerator + zh_Hans: filegenerator + pt_BR: filegenerator +description: + en_US: generate file with extension and content + ja_JP: generate file with extension and content + zh_Hans: generate file with extension and content + pt_BR: generate file with extension and content +icon: icon.svg +resource: + memory: 268435456 + permission: + tool: + enabled: true + model: + enabled: false + llm: false + text_embedding: false + rerank: false + tts: false + speech2text: false + moderation: false + node: + enabled: false + app: + enabled: false +plugins: + tools: + - provider/filegenerator.yaml +meta: + version: 0.0.1 + arch: + - amd64 + - arm64 + runner: + language: python + version: "3.12" + entrypoint: main + minimum_dify_version: null +created_at: 2025-05-30T14:51:13.938188+08:00 +privacy: PRIVACY.md +verified: false diff --git a/Organization/filegenerator/provider/filegenerator.py b/Organization/filegenerator/provider/filegenerator.py new file mode 100644 index 0000000000..be4a3ff661 --- /dev/null +++ b/Organization/filegenerator/provider/filegenerator.py @@ -0,0 +1,15 @@ +from typing import Any + +from dify_plugin import ToolProvider +from dify_plugin.errors.tool import ToolProviderCredentialValidationError + + +class FilegeneratorProvider(ToolProvider): + def _validate_credentials(self, credentials: dict[str, Any]) -> None: + try: + """ + IMPLEMENT YOUR VALIDATION HERE + """ + return None + except Exception as e: + raise ToolProviderCredentialValidationError(str(e)) diff --git a/Organization/filegenerator/provider/filegenerator.yaml b/Organization/filegenerator/provider/filegenerator.yaml new file mode 100644 index 0000000000..6217f26265 --- /dev/null +++ b/Organization/filegenerator/provider/filegenerator.yaml @@ -0,0 +1,17 @@ +identity: + author: randomradio + name: filegenerator + label: + en_US: filegenerator + zh_Hans: filegenerator + pt_BR: filegenerator + description: + en_US: generate file with extension and content + zh_Hans: generate file with extension and content + pt_BR: generate file with extension and content + icon: icon.svg +tools: + - tools/filegenerator.yaml +extra: + python: + source: provider/filegenerator.py diff --git a/Organization/filegenerator/requirements.txt b/Organization/filegenerator/requirements.txt new file mode 100644 index 0000000000..3e961fc2a1 --- /dev/null +++ b/Organization/filegenerator/requirements.txt @@ -0,0 +1,2 @@ +dify_plugin>=0.2.0,<0.3.0 +filegenerator diff --git a/Organization/filegenerator/tools/filegenerator.py b/Organization/filegenerator/tools/filegenerator.py new file mode 100644 index 0000000000..eebaa9452f --- /dev/null +++ b/Organization/filegenerator/tools/filegenerator.py @@ -0,0 +1,68 @@ +from collections.abc import Generator +from typing import Any +import re + +from dify_plugin import Tool +from dify_plugin.entities.tool import ToolInvokeMessage + + +import mimetypes + + +class FilegeneratorTool(Tool): + """Generate a downloadable text file based on user input.""" + + def _invoke( + self, tool_parameters: dict[str, Any] + ) -> Generator[ToolInvokeMessage, None, None]: + # 1. Retrieve parameters + content = tool_parameters.get("content") + title = tool_parameters.get("title") + ext = tool_parameters.get("ext", ".txt") + + # Normalize extension + if not ext.startswith("."): + ext = "." + ext + + # 2. Validate required parameters + if not content: + yield self.create_text_message("Error: content is required.") + return + if not title: + yield self.create_text_message("Error: title is required.") + return + + # Sanitize title to create a safe filename (remove/replace problematic characters) + sanitized_title = re.sub(r"[^\w\-. ]+", "_", title).strip() + if not sanitized_title: + sanitized_title = "file" + + filename = f"{sanitized_title}{ext}" + + # Guess mime type, default to text/plain + mime_type, _ = mimetypes.guess_type(filename) + if mime_type is None: + mime_type = "text/plain" + + try: + file_bytes = content.encode("utf-8") + + # 3. Return file as blob message (Dify will handle storage & provide download URL) + yield self.create_blob_message( + blob=file_bytes, + meta={ + "filename": filename, + "mime_type": mime_type, + }, + ) + + # 4. Provide workflow variables / JSON + yield self.create_variable_message("download_filename", filename) + yield self.create_variable_message("download_url", "{{files[0].url}}") + yield self.create_json_message({ + "download_url": "{{download_url}}", # Will be replaced by Dify + "filename": filename, + }) + + except Exception as e: + yield self.create_text_message(f"Error generating file: {str(e)}") diff --git a/Organization/filegenerator/tools/filegenerator.yaml b/Organization/filegenerator/tools/filegenerator.yaml new file mode 100644 index 0000000000..0b57234fbc --- /dev/null +++ b/Organization/filegenerator/tools/filegenerator.yaml @@ -0,0 +1,55 @@ +identity: + name: filegenerator + author: randomradio + label: + en_US: filegenerator + zh_Hans: filegenerator + pt_BR: filegenerator +description: + human: + en_US: Generate a TXT (or custom extension) file for download with given content and title. + zh_Hans: 根据输入内容和标题生成可下载的 TXT 或指定扩展名文件。 + llm: Generate a downloadable text file with given content, title and optional extension. +parameters: + - name: content + type: string + required: true + label: + en_US: File Content + zh_Hans: 文件内容 + human_description: + en_US: Body content of the file to generate. + zh_Hans: 要生成的文件正文内容。 + llm_description: The file body content. + form: llm + - name: title + type: string + required: true + label: + en_US: File Title + zh_Hans: 文件标题 + human_description: + en_US: File name (without extension). + zh_Hans: 文件名称(不含扩展名)。 + llm_description: The name of the file, not including extension. + form: llm + - name: ext + type: string + required: false + label: + en_US: File Extension + zh_Hans: 文件扩展名 + human_description: + en_US: File extension (default is .txt, e.g. .md/.csv). + zh_Hans: 文件扩展名(默认为 .txt,可自定义,如 .md/.csv)。 + llm_description: File extension to use, include the leading dot, e.g. ".txt" or ".md". Optional. + form: llm +extra: + python: + source: tools/filegenerator.py +output_schema: + type: object + properties: + download_url: + type: string + description: URL to download the generated file.