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.
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.
+
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 @@
+
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
diff --git a/biosolver/coingecko-price/provider/coingecko-price.yaml b/biosolver/coingecko-price/provider/coingecko-price.yaml
new file mode 100644
index 000000000..3740df240
--- /dev/null
+++ b/biosolver/coingecko-price/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
diff --git a/biosolver/coingecko-price/provider/coingecko.py b/biosolver/coingecko-price/provider/coingecko.py
new file mode 100644
index 000000000..63511f16b
--- /dev/null
+++ b/biosolver/coingecko-price/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()
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
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)
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
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=********-****-****-****-************
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
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).
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
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.
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 @@
+
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()
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
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()
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
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
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})
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
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})
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
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})
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
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)
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