From 1db581d19815116c6cf0ad99bb05877c00b79051 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 08:02:13 +0700 Subject: [PATCH 01/30] Create manifest.yaml --- biosolver/coingecko-price/manifest.yaml | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 biosolver/coingecko-price/manifest.yaml diff --git a/biosolver/coingecko-price/manifest.yaml b/biosolver/coingecko-price/manifest.yaml new file mode 100644 index 000000000..0fa1f73e9 --- /dev/null +++ b/biosolver/coingecko-price/manifest.yaml @@ -0,0 +1,35 @@ +version: 0.0.1 +type: plugin +author: biosolver +name: coingecko-price +label: + en_US: coingecko-price + ja_JP: coingecko-price + zh_Hans: coingecko-price + pt_BR: coingecko-price +description: + en_US: Get cryptocurrency price from CoinGecko + ja_JP: Get cryptocurrency price from CoinGecko + zh_Hans: Get cryptocurrency price from CoinGecko + pt_BR: Get cryptocurrency price from CoinGecko +icon: icon.svg +icon_dark: icon-dark.svg +resource: + memory: 268435456 + permission: {} +plugins: + tools: + - provider/coingecko-price.yaml +meta: + version: 0.0.1 + arch: + - amd64 + - arm64 + runner: + language: python + version: "3.12" + entrypoint: main + minimum_dify_version: null +created_at: 2026-05-16T05:28:18.2680656+07:00 +privacy: PRIVACY.md +verified: false From 3c653d1e0385b6dd7382e64ecc4534d75d60e35e Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 08:06:01 +0700 Subject: [PATCH 02/30] Create README.md --- biosolver/coingecko-price/README.md | 82 +++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 biosolver/coingecko-price/README.md diff --git a/biosolver/coingecko-price/README.md b/biosolver/coingecko-price/README.md new file mode 100644 index 000000000..cc0726c90 --- /dev/null +++ b/biosolver/coingecko-price/README.md @@ -0,0 +1,82 @@ +## \# CoinGecko Price Plugin + +## + +## Get real-time cryptocurrency prices using the free CoinGecko API. No API key required. + +## + +## \## Features + +## + +## \- Real-time prices in USD, EUR, and BTC + +## \- 24h price change percentage + +## \- 24h trading volume + +## \- Market capitalization + +## \- Supports any coin listed on CoinGecko + +## + +## \## Usage + +## + +## Input the CoinGecko coin ID: + +## + +## | Coin | ID | + +## |------|----| + +## | Bitcoin | bitcoin | + +## | Ethereum | ethereum | + +## | Solana | solana | + +## | Dogecoin | dogecoin | + +## | Chainlink | chainlink | + +## + +## \## Example Output + +## + +## ```json + +## { + +## "coin": "bitcoin", + +## "price\_usd": 103500.0, + +## "price\_eur": 95000.0, + +## "price\_btc": 1.0, + +## "change\_24h\_percent": -2.5, + +## "volume\_24h\_usd": 35000000000, + +## "market\_cap\_usd": 2050000000000 + +## } + +## ``` + +## + +## \## No API Key Needed + +## + +## This plugin uses the free public CoinGecko API — no registration or API key required. + From b4ace40c0cf7832395e26faf76efaf99b7afae41 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 08:06:40 +0700 Subject: [PATCH 03/30] Create requirements.txt --- biosolver/coingecko-price/requirements.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 biosolver/coingecko-price/requirements.txt diff --git a/biosolver/coingecko-price/requirements.txt b/biosolver/coingecko-price/requirements.txt new file mode 100644 index 000000000..adfcb3d11 --- /dev/null +++ b/biosolver/coingecko-price/requirements.txt @@ -0,0 +1,2 @@ +requests +dify-plugin From bed56d9ac73294f7d72842466a3341dd8791cd6b Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 08:07:47 +0700 Subject: [PATCH 04/30] Create PRIVACY.md --- biosolver/coingecko-price/PRIVACY.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 biosolver/coingecko-price/PRIVACY.md diff --git a/biosolver/coingecko-price/PRIVACY.md b/biosolver/coingecko-price/PRIVACY.md new file mode 100644 index 000000000..b75e45a50 --- /dev/null +++ b/biosolver/coingecko-price/PRIVACY.md @@ -0,0 +1,3 @@ +[PRIVACY.md](https://github.com/user-attachments/files/27838677/PRIVACY.md)## Privacy + +!!! Please fill in the privacy policy of the plugin. From a40488a702c69aeede54986f8f0bec008b344818 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 08:09:11 +0700 Subject: [PATCH 05/30] Create coingecko-price.yaml --- .../provider/provider/coingecko-price.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 biosolver/coingecko-price/provider/provider/coingecko-price.yaml diff --git a/biosolver/coingecko-price/provider/provider/coingecko-price.yaml b/biosolver/coingecko-price/provider/provider/coingecko-price.yaml new file mode 100644 index 000000000..3740df240 --- /dev/null +++ b/biosolver/coingecko-price/provider/provider/coingecko-price.yaml @@ -0,0 +1,17 @@ +identity: + author: biosolver + name: coingecko-price + label: + en_US: CoinGecko Price + zh_Hans: CoinGecko 价格 + description: + en_US: Get real-time cryptocurrency prices from CoinGecko (no API key needed) + zh_Hans: 从 CoinGecko 获取实时加密货币价格 + icon: icon.svg + tags: + - finance +tools: + - tools/get_price.yaml +extra: + python: + source: provider/coingecko.py From ab88c495a4e013c4af5981def37c7074c40790b4 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 08:09:40 +0700 Subject: [PATCH 06/30] Create coingecko.py --- .../provider/provider/provider/coingecko.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 biosolver/coingecko-price/provider/provider/provider/coingecko.py diff --git a/biosolver/coingecko-price/provider/provider/provider/coingecko.py b/biosolver/coingecko-price/provider/provider/provider/coingecko.py new file mode 100644 index 000000000..63511f16b --- /dev/null +++ b/biosolver/coingecko-price/provider/provider/provider/coingecko.py @@ -0,0 +1,12 @@ +from typing import Any +from dify_plugin import ToolProvider + + +class CoinGeckoProvider(ToolProvider): + def _validate_credentials(self, credentials: dict[str, Any]) -> None: + import requests + response = requests.get( + "https://api.coingecko.com/api/v3/ping", + timeout=5 + ) + response.raise_for_status() From 06971b6e45095ab7954f335207802c856c7bf274 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 08:10:25 +0700 Subject: [PATCH 07/30] Rename biosolver/coingecko-price/provider/provider/coingecko-price.yaml to biosolver/coingecko-price/provider/coingecko-price.yaml --- .../coingecko-price/provider/{provider => }/coingecko-price.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename biosolver/coingecko-price/provider/{provider => }/coingecko-price.yaml (100%) diff --git a/biosolver/coingecko-price/provider/provider/coingecko-price.yaml b/biosolver/coingecko-price/provider/coingecko-price.yaml similarity index 100% rename from biosolver/coingecko-price/provider/provider/coingecko-price.yaml rename to biosolver/coingecko-price/provider/coingecko-price.yaml From f502bb33c72789fd51c1afea514858d5d416a56f Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 08:11:24 +0700 Subject: [PATCH 08/30] Rename biosolver/coingecko-price/provider/provider/provider/coingecko.py to biosolver/coingecko-price/provider/coingecko.py --- .../coingecko-price/provider/{provider/provider => }/coingecko.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename biosolver/coingecko-price/provider/{provider/provider => }/coingecko.py (100%) diff --git a/biosolver/coingecko-price/provider/provider/provider/coingecko.py b/biosolver/coingecko-price/provider/coingecko.py similarity index 100% rename from biosolver/coingecko-price/provider/provider/provider/coingecko.py rename to biosolver/coingecko-price/provider/coingecko.py From 21988e43376942edbdcc8f8459d3291f2ceb0e7e Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 08:12:18 +0700 Subject: [PATCH 09/30] Create get_price.yaml --- .../coingecko-price/tools/get_price.yaml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 biosolver/coingecko-price/tools/get_price.yaml diff --git a/biosolver/coingecko-price/tools/get_price.yaml b/biosolver/coingecko-price/tools/get_price.yaml new file mode 100644 index 000000000..d04ea7a52 --- /dev/null +++ b/biosolver/coingecko-price/tools/get_price.yaml @@ -0,0 +1,26 @@ +identity: + name: get_price + author: biosolver + label: + en_US: Get Crypto Price + zh_Hans: 获取加密货币价格 +description: + human: + en_US: Get the current price of any cryptocurrency in USD using CoinGecko. Input the coin ID (e.g. bitcoin, ethereum, solana). + zh_Hans: 通过CoinGecko获取任意加密货币的当前美元价格。 + llm: Get real-time cryptocurrency price from CoinGecko API. Use coin ID like 'bitcoin', 'ethereum', 'solana'. +parameters: + - name: coin_id + type: string + required: true + label: + en_US: Coin ID + zh_Hans: 代币ID + human_description: + en_US: CoinGecko coin ID, e.g. bitcoin, ethereum, solana, dogecoin + zh_Hans: CoinGecko代币ID + llm_description: The CoinGecko coin ID for the cryptocurrency. Examples - bitcoin, ethereum, solana, dogecoin + form: llm +extra: + python: + source: tools/get_price.py From 08e56069a6210b22e089eca88ebbc70afb2d8c9f Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 08:12:49 +0700 Subject: [PATCH 10/30] Create get_price.py --- biosolver/coingecko-price/tools/get_price.py | 51 ++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 biosolver/coingecko-price/tools/get_price.py diff --git a/biosolver/coingecko-price/tools/get_price.py b/biosolver/coingecko-price/tools/get_price.py new file mode 100644 index 000000000..815d538f2 --- /dev/null +++ b/biosolver/coingecko-price/tools/get_price.py @@ -0,0 +1,51 @@ +from collections.abc import Generator +from typing import Any +import requests +from dify_plugin import Tool +from dify_plugin.entities.tool import ToolInvokeMessage + + +class GetCryptoPriceTool(Tool): + def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]: + coin_id = tool_parameters["coin_id"].lower().strip() + + try: + url = "https://api.coingecko.com/api/v3/simple/price" + params = { + "ids": coin_id, + "vs_currencies": "usd,eur,btc", + "include_24hr_change": "true", + "include_market_cap": "true", + "include_24hr_vol": "true" + } + response = requests.get(url=url, params=params, timeout=10) + response.raise_for_status() + data = response.json() + except requests.exceptions.Timeout: + yield self.create_text_message("Error: CoinGecko API timeout. Please try again.") + return + except requests.exceptions.ConnectionError: + yield self.create_text_message("Error: Cannot connect to CoinGecko API. Check your internet connection.") + return + except requests.exceptions.HTTPError as e: + yield self.create_text_message(f"Error: CoinGecko API returned error {e.response.status_code}.") + return + + if coin_id not in data: + yield self.create_text_message( + f"Coin '{coin_id}' not found on CoinGecko. Try IDs like: bitcoin, ethereum, solana, dogecoin, chainlink." + ) + return + + coin_data = data[coin_id] + result = { + "coin": coin_id, + "price_usd": coin_data.get("usd"), + "price_eur": coin_data.get("eur"), + "price_btc": coin_data.get("btc"), + "change_24h_percent": round(coin_data.get("usd_24h_change", 0), 2), + "volume_24h_usd": coin_data.get("usd_24h_vol"), + "market_cap_usd": coin_data.get("usd_market_cap") + } + + yield self.create_json_message(result) From 044cfc88cae85873e32b3a259ea6cddc85523a44 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 08:13:40 +0700 Subject: [PATCH 11/30] Create icon.svg --- biosolver/coingecko-price/_assets/icon.svg | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 biosolver/coingecko-price/_assets/icon.svg diff --git a/biosolver/coingecko-price/_assets/icon.svg b/biosolver/coingecko-price/_assets/icon.svg new file mode 100644 index 000000000..68de3373c --- /dev/null +++ b/biosolver/coingecko-price/_assets/icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + CG + From 74dab0de0a87ab8ddfe3177572e1add95a8c534d Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:17:28 +0700 Subject: [PATCH 12/30] Create manifest.yaml --- biosolver/github-analyzer/manifest.yaml | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 biosolver/github-analyzer/manifest.yaml diff --git a/biosolver/github-analyzer/manifest.yaml b/biosolver/github-analyzer/manifest.yaml new file mode 100644 index 000000000..a833df352 --- /dev/null +++ b/biosolver/github-analyzer/manifest.yaml @@ -0,0 +1,35 @@ +version: 0.0.1 +type: plugin +author: biosolver +name: github-analyzer +label: + en_US: github-analyzer + ja_JP: github-analyzer + zh_Hans: github-analyzer + pt_BR: github-analyzer +description: + en_US: Analyze any GitHub repository - stats, issues, PRs, contributors + ja_JP: Analyze any GitHub repository - stats, issues, PRs, contributors + zh_Hans: Analyze any GitHub repository - stats, issues, PRs, contributors + pt_BR: Analyze any GitHub repository - stats, issues, PRs, contributors +icon: icon.svg +icon_dark: icon-dark.svg +resource: + memory: 268435456 + permission: {} +plugins: + tools: + - provider/github-analyzer.yaml +meta: + version: 0.0.1 + arch: + - amd64 + - arm64 + runner: + language: python + version: "3.12" + entrypoint: main + minimum_dify_version: null +created_at: 2026-05-16T20:48:58.5783593+07:00 +privacy: PRIVACY.md +verified: false From 9ece88d0e79fc1d1fa077eb6be55ea60bd61ca3e Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:20:32 +0700 Subject: [PATCH 13/30] Create README.md --- biosolver/github-analyzer/README.md | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 biosolver/github-analyzer/README.md diff --git a/biosolver/github-analyzer/README.md b/biosolver/github-analyzer/README.md new file mode 100644 index 000000000..073901ec1 --- /dev/null +++ b/biosolver/github-analyzer/README.md @@ -0,0 +1,34 @@ +# GitHub Analyzer Plugin + +Analyze any GitHub repository - get stats, issues, pull requests and contributors using the free GitHub API. + +## Features + +- Repository statistics (stars, forks, watchers, language, topics) +- Recent open issues with labels +- Top contributors with contribution count +- Recent open pull requests +- No API key required (60 requests/hour free) + +## Tools + +| Tool | Description | +|------|-------------| +| Get Repository Stats | Stars, forks, language, description, license | +| Get Issues | Recent open issues with labels | +| Get Contributors | Top contributors and their contribution count | +| Get Pull Requests | Recent open pull requests | + +## Usage + +Input the repository in format `owner/repo`: + +| Example | Input | +|---------|-------| +| Dify | langgenius/dify | +| VS Code | microsoft/vscode | +| React | facebook/react | + +## No API Key Needed + +Uses the free public GitHub API. For higher rate limits (5000/hour), add a GitHub Personal Access Token. From c4de2e71cb3d60b8225651ae8fae34b05d5f4f8c Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:21:52 +0700 Subject: [PATCH 14/30] Create PRIVACY.md --- biosolver/github-analyzer/PRIVACY.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 biosolver/github-analyzer/PRIVACY.md diff --git a/biosolver/github-analyzer/PRIVACY.md b/biosolver/github-analyzer/PRIVACY.md new file mode 100644 index 000000000..8359a55d2 --- /dev/null +++ b/biosolver/github-analyzer/PRIVACY.md @@ -0,0 +1,8 @@ +# Privacy Policy + +This plugin uses the public GitHub API to fetch repository data. + +- No personal data is collected or stored +- No API keys are required by default +- All data is fetched directly from api.github.com +- Only public repository information is accessed From 0e920c548573937e3d6f89999d376432a06b8bcb Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:22:41 +0700 Subject: [PATCH 15/30] Create requirements.txt --- biosolver/github-analyzer/requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 biosolver/github-analyzer/requirements.txt diff --git a/biosolver/github-analyzer/requirements.txt b/biosolver/github-analyzer/requirements.txt new file mode 100644 index 000000000..69adc2d92 --- /dev/null +++ b/biosolver/github-analyzer/requirements.txt @@ -0,0 +1 @@ +dify_plugin>=0.4.0,<0.7.0 From ef113184f29f2350dd36d4325a2d9ec06605e6b4 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:24:31 +0700 Subject: [PATCH 16/30] Create .gitignore --- biosolver/github-analyzer/.gitignore | 176 +++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 biosolver/github-analyzer/.gitignore diff --git a/biosolver/github-analyzer/.gitignore b/biosolver/github-analyzer/.gitignore new file mode 100644 index 000000000..e03470d7c --- /dev/null +++ b/biosolver/github-analyzer/.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 From e9646bac34798eb52ad43b39b8820e57c255cf15 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:25:15 +0700 Subject: [PATCH 17/30] Create .env.example --- biosolver/github-analyzer/.env.example | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 biosolver/github-analyzer/.env.example diff --git a/biosolver/github-analyzer/.env.example b/biosolver/github-analyzer/.env.example new file mode 100644 index 000000000..60358af87 --- /dev/null +++ b/biosolver/github-analyzer/.env.example @@ -0,0 +1,3 @@ +INSTALL_METHOD=remote +REMOTE_INSTALL_URL=debug.dify.ai:5003 +REMOTE_INSTALL_KEY=********-****-****-****-************ From c97a2b9e725d7e52faf2b2631e27a2708fe25eb5 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:25:42 +0700 Subject: [PATCH 18/30] Create main.py --- biosolver/github-analyzer/main.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 biosolver/github-analyzer/main.py diff --git a/biosolver/github-analyzer/main.py b/biosolver/github-analyzer/main.py new file mode 100644 index 000000000..7e1a983db --- /dev/null +++ b/biosolver/github-analyzer/main.py @@ -0,0 +1,6 @@ +from dify_plugin import Plugin, DifyPluginEnv + +plugin = Plugin(DifyPluginEnv(MAX_REQUEST_TIMEOUT=120)) + +if __name__ == '__main__': + plugin.run() From 7f8ce9aa349291f2907caac352e4e17482ef1f0d Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:26:36 +0700 Subject: [PATCH 19/30] Create GUIDE.md --- biosolver/github-analyzer/GUIDE.md | 137 +++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 biosolver/github-analyzer/GUIDE.md diff --git a/biosolver/github-analyzer/GUIDE.md b/biosolver/github-analyzer/GUIDE.md new file mode 100644 index 000000000..bf24686e1 --- /dev/null +++ b/biosolver/github-analyzer/GUIDE.md @@ -0,0 +1,137 @@ +# Dify Plugin Development Guide + +Welcome to Dify plugin development! This guide will help you get started quickly. + +## Plugin Types + +Dify plugins extend three main capabilities: + +| Type | Description | Example | +|------|-------------|---------| +| **Tool** | Perform specific tasks | Google Search, Stable Diffusion | +| **Model** | AI model integrations | OpenAI, Anthropic | +| **Endpoint** | HTTP services | Custom APIs, integrations | + +You can create: +- **Tool**: Tool provider with optional endpoints (e.g., Discord bot) +- **Model**: Model provider only +- **Extension**: Simple HTTP service + +## Setup + +### Requirements +- Python 3.11+ +- Dependencies: `pip install -r requirements.txt` + +## Development Process + +
+1. Manifest Structure + +Edit `manifest.yaml` to describe your plugin: + +```yaml +version: 0.1.0 # Required: Plugin version +type: plugin # Required: plugin or bundle +author: YourOrganization # Required: Organization name +label: # Required: Multi-language names + en_US: Plugin Name + zh_Hans: 插件名称 +created_at: 2023-01-01T00:00:00Z # Required: Creation time (RFC3339) +icon: assets/icon.png # Required: Icon path + +# Resources and permissions +resource: + memory: 268435456 # Max memory (bytes) + permission: + tool: + enabled: true # Tool permission + model: + enabled: true # Model permission + llm: true + text_embedding: false + # Other model types... + # Other permissions... + +# Extensions definition +plugins: + tools: + - tools/my_tool.yaml # Tool definition files + models: + - models/my_model.yaml # Model definition files + endpoints: + - endpoints/my_api.yaml # Endpoint definition files + +# Runtime metadata +meta: + version: 0.0.1 # Manifest format version + arch: + - amd64 + - arm64 + runner: + language: python + version: "3.12" + entrypoint: main +``` + +**Restrictions:** +- Cannot extend both tools and models +- Must have at least one extension +- Cannot extend both models and endpoints +- Limited to one supplier per extension type +
+ +
+2. Implementation Examples + +Study these examples to understand plugin implementation: + +- [OpenAI](https://github.com/langgenius/dify-plugin-sdks/tree/main/python/examples/openai) - Model provider +- [Google Search](https://github.com/langgenius/dify-plugin-sdks/tree/main/python/examples/google) - Tool provider +- [Neko](https://github.com/langgenius/dify-plugin-sdks/tree/main/python/examples/neko) - Endpoint group +
+ +
+3. Testing & Debugging + +1. Copy `.env.example` to `.env` and configure: + ``` + INSTALL_METHOD=remote + REMOTE_INSTALL_URL=debug.dify.ai:5003 + REMOTE_INSTALL_KEY=your-debug-key + ``` + +2. Run your plugin: + ```bash + python -m main + ``` + +3. Refresh your Dify instance to see the plugin (marked as "debugging") +
+ +
+4. Publishing + +#### Manual Packaging +```bash +dify-plugin plugin package ./YOUR_PLUGIN_DIR +``` + +#### Automated GitHub Workflow + +Configure GitHub Actions to automate PR creation: + +1. Create a Personal Access Token for your forked repository +2. Add it as `PLUGIN_ACTION` secret in your source repo +3. Create `.github/workflows/plugin-publish.yml` + +When you create a release, the action will: +- Package your plugin +- Create a PR to your fork + +[Detailed workflow documentation](https://docs.dify.ai/plugins/publish-plugins/plugin-auto-publish-pr) +
+ +## Privacy Policy + +If publishing to the Marketplace, provide a privacy policy in [PRIVACY.md](PRIVACY.md). From ae27a256794d43c8a93ce72c90b21df5c9095093 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:27:26 +0700 Subject: [PATCH 20/30] Create github-analyzer.yaml --- .../provider/github-analyzer.yaml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 biosolver/github-analyzer/provider/github-analyzer.yaml diff --git a/biosolver/github-analyzer/provider/github-analyzer.yaml b/biosolver/github-analyzer/provider/github-analyzer.yaml new file mode 100644 index 000000000..5747590e1 --- /dev/null +++ b/biosolver/github-analyzer/provider/github-analyzer.yaml @@ -0,0 +1,20 @@ +identity: + author: biosolver + name: github-analyzer + label: + en_US: GitHub Analyzer + zh_Hans: GitHub 分析器 + description: + en_US: Analyze any GitHub repository - stats, issues, pull requests and contributors + zh_Hans: 分析任何GitHub仓库 + icon: icon.svg + tags: + - utilities +tools: + - tools/get_repo_stats.yaml + - tools/get_issues.yaml + - tools/get_contributors.yaml + - tools/get_pull_requests.yaml +extra: + python: + source: provider/github-analyzer.py From c4e9db0f0642d3ee69db0ab82b06433ee8cecaac Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:27:57 +0700 Subject: [PATCH 21/30] Add GitHubAnalyzerProvider class for GitHub API --- .../github-analyzer/provider/github-analyzer.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 biosolver/github-analyzer/provider/github-analyzer.py diff --git a/biosolver/github-analyzer/provider/github-analyzer.py b/biosolver/github-analyzer/provider/github-analyzer.py new file mode 100644 index 000000000..cec02776e --- /dev/null +++ b/biosolver/github-analyzer/provider/github-analyzer.py @@ -0,0 +1,17 @@ +from typing import Any +from dify_plugin import ToolProvider +import requests + + +class GitHubAnalyzerProvider(ToolProvider): + def _validate_credentials(self, credentials: dict[str, Any]) -> None: + token = credentials.get("github_token", "") + headers = {"Accept": "application/vnd.github.v3+json"} + if token: + headers["Authorization"] = f"token {token}" + response = requests.get( + "https://api.github.com/repos/langgenius/dify", + headers=headers, + timeout=10 + ) + response.raise_for_status() From 0811df34ea17c465c41496dce1351469faa4de56 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:28:50 +0700 Subject: [PATCH 22/30] Create icon.svg --- biosolver/github-analyzer/_assets/icon.svg | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 biosolver/github-analyzer/_assets/icon.svg diff --git a/biosolver/github-analyzer/_assets/icon.svg b/biosolver/github-analyzer/_assets/icon.svg new file mode 100644 index 000000000..a7638867f --- /dev/null +++ b/biosolver/github-analyzer/_assets/icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + GH + From 289f0cd276f9bad149fe1f96b0ea0aaf078cb3f9 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:29:38 +0700 Subject: [PATCH 23/30] Create get_repo_stats.yaml --- .../github-analyzer/tools/get_repo_stats.yaml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 biosolver/github-analyzer/tools/get_repo_stats.yaml diff --git a/biosolver/github-analyzer/tools/get_repo_stats.yaml b/biosolver/github-analyzer/tools/get_repo_stats.yaml new file mode 100644 index 000000000..4fc90e5c4 --- /dev/null +++ b/biosolver/github-analyzer/tools/get_repo_stats.yaml @@ -0,0 +1,22 @@ +identity: + name: get_repo_stats + author: biosolver + label: + en_US: Get Repository Stats +description: + human: + en_US: Get general statistics of a GitHub repository - stars, forks, watchers, language, description. + llm: Get GitHub repository statistics including stars, forks, watchers, open issues count, primary language and description. +parameters: + - name: repo + type: string + required: true + label: + en_US: Repository + human_description: + en_US: GitHub repository in format owner/repo, e.g. langgenius/dify + llm_description: GitHub repository in format owner/repo, e.g. langgenius/dify, microsoft/vscode + form: llm +extra: + python: + source: tools/get_repo_stats.py From 8cee7a435831de184cece40f092706ec8f43dc87 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:30:26 +0700 Subject: [PATCH 24/30] Create get_repo_stats.py --- .../github-analyzer/tools/get_repo_stats.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 biosolver/github-analyzer/tools/get_repo_stats.py diff --git a/biosolver/github-analyzer/tools/get_repo_stats.py b/biosolver/github-analyzer/tools/get_repo_stats.py new file mode 100644 index 000000000..8c96c2117 --- /dev/null +++ b/biosolver/github-analyzer/tools/get_repo_stats.py @@ -0,0 +1,45 @@ +from collections.abc import Generator +from typing import Any +import requests +from dify_plugin import Tool +from dify_plugin.entities.tool import ToolInvokeMessage + + +class GetRepoStatsTool(Tool): + def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]: + repo = tool_parameters["repo"].strip() + headers = {"Accept": "application/vnd.github.v3+json"} + + try: + response = requests.get( + f"https://api.github.com/repos/{repo}", + headers=headers, + timeout=10 + ) + response.raise_for_status() + data = response.json() + except requests.exceptions.Timeout: + yield self.create_text_message("Error: GitHub API timeout.") + return + except requests.exceptions.HTTPError as e: + if e.response.status_code == 404: + yield self.create_text_message(f"Repository '{repo}' not found.") + else: + yield self.create_text_message(f"GitHub API error: {e.response.status_code}") + return + + result = { + "name": data.get("full_name"), + "description": data.get("description"), + "stars": data.get("stargazers_count"), + "forks": data.get("forks_count"), + "watchers": data.get("watchers_count"), + "open_issues": data.get("open_issues_count"), + "language": data.get("language"), + "created_at": data.get("created_at"), + "updated_at": data.get("updated_at"), + "license": data.get("license", {}).get("name") if data.get("license") else None, + "topics": data.get("topics", []), + "url": data.get("html_url") + } + yield self.create_json_message(result) From 2c7ac926beccce0f1da469f1f8c7d52070f92065 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:30:59 +0700 Subject: [PATCH 25/30] Create get_pull_requests.yaml --- .../tools/get_pull_requests.yaml | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 biosolver/github-analyzer/tools/get_pull_requests.yaml diff --git a/biosolver/github-analyzer/tools/get_pull_requests.yaml b/biosolver/github-analyzer/tools/get_pull_requests.yaml new file mode 100644 index 000000000..105515791 --- /dev/null +++ b/biosolver/github-analyzer/tools/get_pull_requests.yaml @@ -0,0 +1,31 @@ +identity: + name: get_pull_requests + author: biosolver + label: + en_US: Get Pull Requests +description: + human: + en_US: Get recent pull requests from a GitHub repository. + llm: Get list of recent open pull requests from a GitHub repository. +parameters: + - name: repo + type: string + required: true + label: + en_US: Repository + human_description: + en_US: GitHub repository in format owner/repo + llm_description: GitHub repository in format owner/repo + form: llm + - name: limit + type: number + required: false + label: + en_US: Limit + human_description: + en_US: Number of PRs to return (default 10, max 30) + llm_description: Number of pull requests to return, default 10 + form: llm +extra: + python: + source: tools/get_pull_requests.py From 05936aa303b5f1616c22bb16e391dd625b670930 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:31:34 +0700 Subject: [PATCH 26/30] Create get_pull_requests.py --- .../tools/get_pull_requests.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 biosolver/github-analyzer/tools/get_pull_requests.py diff --git a/biosolver/github-analyzer/tools/get_pull_requests.py b/biosolver/github-analyzer/tools/get_pull_requests.py new file mode 100644 index 000000000..d0c43e74b --- /dev/null +++ b/biosolver/github-analyzer/tools/get_pull_requests.py @@ -0,0 +1,41 @@ +from collections.abc import Generator +from typing import Any +import requests +from dify_plugin import Tool +from dify_plugin.entities.tool import ToolInvokeMessage + + +class GetPullRequestsTool(Tool): + def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]: + repo = tool_parameters["repo"].strip() + limit = int(tool_parameters.get("limit") or 10) + limit = min(limit, 30) + headers = {"Accept": "application/vnd.github.v3+json"} + + try: + response = requests.get( + f"https://api.github.com/repos/{repo}/pulls", + headers=headers, + params={"state": "open", "per_page": limit}, + timeout=10 + ) + response.raise_for_status() + data = response.json() + except requests.exceptions.Timeout: + yield self.create_text_message("Error: GitHub API timeout.") + return + except requests.exceptions.HTTPError as e: + yield self.create_text_message(f"GitHub API error: {e.response.status_code}") + return + + prs = [] + for pr in data: + prs.append({ + "number": pr.get("number"), + "title": pr.get("title"), + "author": pr.get("user", {}).get("login"), + "created_at": pr.get("created_at"), + "url": pr.get("html_url") + }) + + yield self.create_json_message({"total": len(prs), "pull_requests": prs}) From 95cfe7fd749876e73316a8aaeb92c7af49258bda Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:32:00 +0700 Subject: [PATCH 27/30] Create get_issues.yaml --- .../github-analyzer/tools/get_issues.yaml | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 biosolver/github-analyzer/tools/get_issues.yaml diff --git a/biosolver/github-analyzer/tools/get_issues.yaml b/biosolver/github-analyzer/tools/get_issues.yaml new file mode 100644 index 000000000..f7be37a3d --- /dev/null +++ b/biosolver/github-analyzer/tools/get_issues.yaml @@ -0,0 +1,31 @@ +identity: + name: get_issues + author: biosolver + label: + en_US: Get Issues +description: + human: + en_US: Get recent issues from a GitHub repository. + llm: Get list of recent open issues from a GitHub repository with titles, labels and creation dates. +parameters: + - name: repo + type: string + required: true + label: + en_US: Repository + human_description: + en_US: GitHub repository in format owner/repo + llm_description: GitHub repository in format owner/repo + form: llm + - name: limit + type: number + required: false + label: + en_US: Limit + human_description: + en_US: Number of issues to return (default 10, max 30) + llm_description: Number of issues to return, default 10 + form: llm +extra: + python: + source: tools/get_issues.py From ee85ac05c3ef0d5dbecb3314019d628c216dad92 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:32:31 +0700 Subject: [PATCH 28/30] Create get_issues.py --- biosolver/github-analyzer/tools/get_issues.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 biosolver/github-analyzer/tools/get_issues.py diff --git a/biosolver/github-analyzer/tools/get_issues.py b/biosolver/github-analyzer/tools/get_issues.py new file mode 100644 index 000000000..df63bcb7a --- /dev/null +++ b/biosolver/github-analyzer/tools/get_issues.py @@ -0,0 +1,43 @@ +from collections.abc import Generator +from typing import Any +import requests +from dify_plugin import Tool +from dify_plugin.entities.tool import ToolInvokeMessage + + +class GetIssuesTool(Tool): + def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]: + repo = tool_parameters["repo"].strip() + limit = int(tool_parameters.get("limit") or 10) + limit = min(limit, 30) + headers = {"Accept": "application/vnd.github.v3+json"} + + try: + response = requests.get( + f"https://api.github.com/repos/{repo}/issues", + headers=headers, + params={"state": "open", "per_page": limit}, + timeout=10 + ) + response.raise_for_status() + data = response.json() + except requests.exceptions.Timeout: + yield self.create_text_message("Error: GitHub API timeout.") + return + except requests.exceptions.HTTPError as e: + yield self.create_text_message(f"GitHub API error: {e.response.status_code}") + return + + issues = [] + for issue in data: + if "pull_request" not in issue: + issues.append({ + "number": issue.get("number"), + "title": issue.get("title"), + "state": issue.get("state"), + "labels": [l["name"] for l in issue.get("labels", [])], + "created_at": issue.get("created_at"), + "url": issue.get("html_url") + }) + + yield self.create_json_message({"total": len(issues), "issues": issues}) From 0140ad5dac7f3449d5c569951a1135f43e2786a9 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:33:03 +0700 Subject: [PATCH 29/30] Create get_contributors.yaml --- .../tools/get_contributors.yaml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 biosolver/github-analyzer/tools/get_contributors.yaml diff --git a/biosolver/github-analyzer/tools/get_contributors.yaml b/biosolver/github-analyzer/tools/get_contributors.yaml new file mode 100644 index 000000000..9561b1d37 --- /dev/null +++ b/biosolver/github-analyzer/tools/get_contributors.yaml @@ -0,0 +1,22 @@ +identity: + name: get_contributors + author: biosolver + label: + en_US: Get Contributors +description: + human: + en_US: Get top contributors of a GitHub repository. + llm: Get list of top contributors of a GitHub repository with their contribution count. +parameters: + - name: repo + type: string + required: true + label: + en_US: Repository + human_description: + en_US: GitHub repository in format owner/repo + llm_description: GitHub repository in format owner/repo + form: llm +extra: + python: + source: tools/get_contributors.py From 18032e7e49ffe1f0f9352adf62847e81e63340d9 Mon Sep 17 00:00:00 2001 From: Biosolver Date: Sat, 16 May 2026 22:33:32 +0700 Subject: [PATCH 30/30] Create get_contributors.py --- .../github-analyzer/tools/get_contributors.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 biosolver/github-analyzer/tools/get_contributors.py diff --git a/biosolver/github-analyzer/tools/get_contributors.py b/biosolver/github-analyzer/tools/get_contributors.py new file mode 100644 index 000000000..810cd154f --- /dev/null +++ b/biosolver/github-analyzer/tools/get_contributors.py @@ -0,0 +1,37 @@ +from collections.abc import Generator +from typing import Any +import requests +from dify_plugin import Tool +from dify_plugin.entities.tool import ToolInvokeMessage + + +class GetContributorsTool(Tool): + def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]: + repo = tool_parameters["repo"].strip() + headers = {"Accept": "application/vnd.github.v3+json"} + + try: + response = requests.get( + f"https://api.github.com/repos/{repo}/contributors", + headers=headers, + params={"per_page": 10}, + timeout=10 + ) + response.raise_for_status() + data = response.json() + except requests.exceptions.Timeout: + yield self.create_text_message("Error: GitHub API timeout.") + return + except requests.exceptions.HTTPError as e: + yield self.create_text_message(f"GitHub API error: {e.response.status_code}") + return + + contributors = [] + for c in data: + contributors.append({ + "username": c.get("login"), + "contributions": c.get("contributions"), + "profile": c.get("html_url") + }) + + yield self.create_json_message({"contributors": contributors})