├── .github └── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── .gitignore ├── README.md ├── db_query ├── .difyignore ├── .env.example ├── .gitignore ├── GUIDE.md ├── PRIVACY.md ├── README.md ├── _assets │ ├── db_query.png │ ├── db_query_chatflow.png │ ├── db_query_sql_query.png │ ├── icon.svg │ ├── llm_riddles1.png │ ├── llm_riddles2.png │ └── llm_riddles3.png ├── main.py ├── manifest.yaml ├── provider │ ├── db_query.py │ └── db_query.yaml ├── requirements.txt └── tools │ ├── db_util.py │ ├── sql_query.py │ └── sql_query.yaml ├── db_query_pre_auth ├── .difyignore ├── .env.example ├── .gitignore ├── GUIDE.md ├── PRIVACY.md ├── README.md ├── _assets │ ├── db_query_pre_auth.png │ ├── db_query_pre_auth_setup_auth.png │ ├── icon.svg │ ├── llm_riddles1.png │ ├── llm_riddles2.png │ └── llm_riddles3.png ├── main.py ├── manifest.yaml ├── provider │ ├── db_query.py │ └── db_query.yaml ├── requirements.txt └── tools │ ├── db_util.py │ ├── sql_query.py │ └── sql_query.yaml ├── examples ├── 完蛋!我被LLM包围了!(Dify1.0战绩排行+留言版).yml └── 完蛋!我被LLM包围了!(Dify1.0战绩排行版).yml └── images ├── db_query.png ├── db_query_pre_auth.png ├── dify_plugin_daemon_update.png ├── install_plugin_via_github.png └── install_plugin_via_local.png /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: "🕷️ Bug report Bug报告" 2 | description: "Report errors or unexpected behavior. 创建一个 Bug 报告以帮助我们改进。" 3 | labels: 4 | - bug 5 | body: 6 | - type: checkboxes 7 | attributes: 8 | label: "Please confirm before submission 在提交之前,请确认" 9 | options: 10 | - label: | 11 | I have searched for existing issues [search for existing issues](https://github.com/junjiem/dify-plugin-tools-dbquery/issues), including closed ones. 12 | 我已经搜索了现有问题[搜索现有问题](https://github.com/junjiem/dify-plugin-tools-dbquery/issues),包括已关闭的问题。" 13 | required: true 14 | - type: input 15 | attributes: 16 | label: "Dify version Dify版本" 17 | validations: 18 | required: true 19 | - type: dropdown 20 | attributes: 21 | label: db_query or db_query_pre_auth 22 | description: Which plugin? 哪个插件? 23 | multiple: true 24 | options: 25 | - Database Query 数据库查询 26 | - Database Query (Pre-authorization) 数据库查询(预授权) 27 | validations: 28 | required: true 29 | - type: input 30 | attributes: 31 | label: "Plugin version 插件版本" 32 | validations: 33 | required: true 34 | - type: input 35 | attributes: 36 | label: database and version 数据库及版本 37 | validations: 38 | required: true 39 | - type: textarea 40 | attributes: 41 | label: "Problem description 问题描述" 42 | description: "Please describe the problem you have encountered clearly and concisely. 请清晰简洁地描述你遇到的问题。" 43 | validations: 44 | required: true 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: "\U0001F4E7 Discussions 讨论" 4 | url: https://github.com/junjiem/dify-plugin-tools-dbquery/discussions 5 | about: General discussions and request help. 一般性讨论和请求帮助。 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: "⭐ Feature or enhancement request 特性或增强请求" 2 | description: Propose something new. 提出一些新的建议。 3 | labels: 4 | - enhancement 5 | body: 6 | - type: checkboxes 7 | attributes: 8 | label: "Please confirm before submission 在提交之前,请确认" 9 | options: 10 | - label: | 11 | I have searched for existing issues [search for existing issues](https://github.com/junjiem/dify-plugin-tools-dbquery/issues), including closed ones. 12 | 我已经搜索了现有问题[搜索现有问题](https://github.com/junjiem/dify-plugin-tools-dbquery/issues),包括已关闭的问题。" 13 | required: true 14 | - type: textarea 15 | attributes: 16 | label: Is this request related to a challenge you're experiencing? Tell me about your story. 这个请求与你正在经历的挑战有关吗?给我讲讲你的故事。 17 | placeholder: | 18 | Please describe the specific scenario or problem you're facing as clearly as possible. For instance "I was trying to use [feature] for [specific task], and [what happened]... It was frustrating because...." 19 | 请尽可能清楚地描述你所面临的具体场景或问题。例如:“我试图将[功能]用于[特定任务],但[发生了什么]……这很令人沮丧,因为....” 20 | validations: 21 | required: true 22 | - type: checkboxes 23 | attributes: 24 | label: Can you help us with this feature? 你能帮我们实现这个功能吗? 25 | description: Let us know! This is not a commitment, but a starting point for collaboration. 让我们知道!这不是承诺,而是合作的起点。 26 | options: 27 | - label: I am interested in contributing to this feature. 我有兴趣为这个特性做贡献。 28 | required: false 29 | - type: markdown 30 | attributes: 31 | value: Please limit one request per issue. 请限制每个问题一个请求。 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | **/*.log 3 | *.iml 4 | target/ 5 | 6 | # Editor directories and files 7 | .idea 8 | .vscode 9 | */.env 10 | *.suo 11 | *.ntvs* 12 | *.njsproj 13 | *.sln 14 | *.local 15 | 16 | .gradle/ 17 | tmp/ 18 | build/ 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Dify 1.0 Plugin Database Query Tools 2 | 3 | 4 | --- 5 | 6 | 7 | ### Demonstration 8 | 9 | Currently supported database types: mysql, oracle, [oracle11g](./db_query/README.md#2-how-to-connect-to-oracle-11g--如何连接oracle-11g), postgresql, or mssql. 10 | 11 | 目前支持的数据库类型:mysql、oracle、[oracle11g](./db_query/README.md#2-how-to-connect-to-oracle-11g--如何连接oracle-11g)、postgresql、mssql。 12 | 13 | ![db_query](db_query/_assets/db_query.png) 14 | 15 | ![db_query_sql_query](db_query/_assets/db_query_sql_query.png) 16 | 17 | ![db_query_chatflow](db_query/_assets/db_query_chatflow.png) 18 | 19 | 20 | --- 21 | 22 | 23 | 24 | ### Tools 25 | 26 | #### Database Query Utils 数据库查询工具 27 | 28 | ![db_query](./images/db_query.png) 29 | 30 | 31 | 32 | #### Database Query Utils (Pre-authorization) 数据库查询工具(预授权) 33 | 34 | ![db_query_pre_auth](./images/db_query_pre_auth.png) 35 | 36 | 37 | --- 38 | 39 | 40 | 41 | ### Installing Plugins via GitHub 通过 GitHub 安装插件 42 | 43 | Can install the plugin using the GitHub repository address. Visit the Dify platform's plugin management page, choose to install via GitHub, enter the repository address, select version number and package file to complete installation. 44 | 45 | 可以通过 GitHub 仓库地址安装该插件。访问 Dify 平台的插件管理页,选择通过 GitHub 安装插件,输入仓库地址后,选择版本号和包文件完成安装。 46 | 47 | ![install_plugin_via_github](./images/install_plugin_via_github.png) 48 | 49 | 50 | 51 | --- 52 | 53 | 54 | ### Installing Plugins via Local 通过本地安装插件 55 | 56 | If the plugin cannot be installed via GitHub due to network issues, you can [manually download plugin package](https://github.com/junjiem/dify-plugin-tools-dbquery/releases/latest). 57 | Visit the Dify platform's plugin management page, choose Local Package File to complete installation. 58 | 59 | 如果由于网络问题导致无法通过 GitHub 安装插件,可以 [手动下载插件包](https://github.com/junjiem/dify-plugin-tools-dbquery/releases/latest),访问 Dify 平台的插件管理页,选择通过本地插件完成安装。 60 | 61 | ![install_plugin_via_local](./images/install_plugin_via_local.png) 62 | 63 | 64 | 65 | --- 66 | 67 | 68 | 69 | ### FAQ 70 | 71 | #### How to Handle Errors When Installing Plugins? 安装插件时遇到异常应如何处理? 72 | 73 | **Issue**: If you encounter the error message: plugin verification has been enabled, and the plugin you want to install has a bad signature, how to handle the issue? 74 | 75 | **Solution**: Add the following line to the end of your .env configuration file: FORCE_VERIFYING_SIGNATURE=false 76 | Once this field is added, the Dify platform will allow the installation of all plugins that are not listed (and thus not verified) in the Dify Marketplace. 77 | 78 | **问题描述**:安装插件时遇到异常信息:plugin verification has been enabled, and the plugin you want to install has a bad signature,应该如何处理? 79 | 80 | **解决办法**:在 .env 配置文件的末尾添加 FORCE_VERIFYING_SIGNATURE=false 字段即可解决该问题。 81 | 添加该字段后,Dify 平台将允许安装所有未在 Dify Marketplace 上架(审核)的插件,可能存在安全隐患。 82 | 83 | 84 | 85 | --- 86 | 87 | 88 | 89 | ### Examples 示例 90 | 91 | - [完蛋!我被LLM包围了!(Dify1.0战绩排行版)](./examples/完蛋!我被LLM包围了!(Dify1.0战绩排行版).yml) 92 | 93 | - [完蛋!我被LLM包围了!(Dify1.0战绩排行+留言版)](./examples/完蛋!我被LLM包围了!(Dify1.0战绩排行+留言版).yml) 94 | 95 | 96 | ![](./db_query_pre_auth/_assets/llm_riddles1.png) 97 | 98 | ![](./db_query_pre_auth/_assets/llm_riddles2.png) 99 | 100 | ![](./db_query_pre_auth/_assets/llm_riddles3.png) 101 | 102 | 103 | 104 | --- 105 | 106 | 107 | 108 | ### Dify Plugin Downloading and Repackaging Scripting Tool Dify插件下载并重打包脚本工具 109 | 110 | Scripting tool for downloading Dify plugin package from Dify Marketplace and Github and repackaging [true] offline package (contains dependencies, no need to be connected to the Internet). 111 | 112 | 从Dify市场和Github下载Dify插件包并重新打【真】离线包(包含依赖,不需要再联网)的脚本工具。 113 | 114 | Github Repo: https://github.com/junjiem/dify-plugin-repackaging 115 | 116 | 117 | 118 | --- 119 | 120 | 121 | 122 | ### Star history 123 | 124 | [![Star History Chart](https://api.star-history.com/svg?repos=junjiem/dify-plugin-tools-dbquery&type=Date)](https://star-history.com/#junjiem/dify-plugin-tools-dbquery&Date) 125 | 126 | 127 | -------------------------------------------------------------------------------- /db_query/.difyignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # Distribution / packaging 7 | .Python 8 | build/ 9 | develop-eggs/ 10 | dist/ 11 | downloads/ 12 | eggs/ 13 | .eggs/ 14 | lib/ 15 | lib64/ 16 | parts/ 17 | sdist/ 18 | var/ 19 | wheels/ 20 | share/python-wheels/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | MANIFEST 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .nox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *.cover 46 | *.py,cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | cover/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | db.sqlite3-journal 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | .pybuilder/ 73 | target/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # IPython 79 | profile_default/ 80 | ipython_config.py 81 | 82 | # pyenv 83 | # For a library or package, you might want to ignore these files since the code is 84 | # intended to run in multiple environments; otherwise, check them in: 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | Pipfile.lock 93 | 94 | # UV 95 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 96 | # This is especially recommended for binary packages to ensure reproducibility, and is more 97 | # commonly ignored for libraries. 98 | uv.lock 99 | 100 | # poetry 101 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 102 | # This is especially recommended for binary packages to ensure reproducibility, and is more 103 | # commonly ignored for libraries. 104 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 105 | poetry.lock 106 | 107 | # pdm 108 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 109 | #pdm.lock 110 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 111 | # in version control. 112 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 113 | .pdm.toml 114 | .pdm-python 115 | .pdm-build/ 116 | 117 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 118 | __pypackages__/ 119 | 120 | # Celery stuff 121 | celerybeat-schedule 122 | celerybeat.pid 123 | 124 | # SageMath parsed files 125 | *.sage.py 126 | 127 | # Environments 128 | .env 129 | .venv 130 | env/ 131 | venv/ 132 | ENV/ 133 | env.bak/ 134 | venv.bak/ 135 | 136 | # Spyder project settings 137 | .spyderproject 138 | .spyproject 139 | 140 | # Rope project settings 141 | .ropeproject 142 | 143 | # mkdocs documentation 144 | /site 145 | 146 | # mypy 147 | .mypy_cache/ 148 | .dmypy.json 149 | dmypy.json 150 | 151 | # Pyre type checker 152 | .pyre/ 153 | 154 | # pytype static type analyzer 155 | .pytype/ 156 | 157 | # Cython debug symbols 158 | cython_debug/ 159 | 160 | # PyCharm 161 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 162 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 163 | # and can be added to the global gitignore or merged into this file. For a more nuclear 164 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 165 | .idea/ 166 | 167 | # Vscode 168 | .vscode/ 169 | 170 | # Git 171 | .git/ 172 | .gitignore 173 | 174 | # Mac 175 | .DS_Store 176 | 177 | # Windows 178 | Thumbs.db 179 | -------------------------------------------------------------------------------- /db_query/.env.example: -------------------------------------------------------------------------------- 1 | INSTALL_METHOD=remote 2 | REMOTE_INSTALL_HOST=debug-plugin.dify.dev 3 | REMOTE_INSTALL_PORT=5003 4 | REMOTE_INSTALL_KEY=********-****-****-****-************ 5 | -------------------------------------------------------------------------------- /db_query/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # UV 98 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | #uv.lock 102 | 103 | # poetry 104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 105 | # This is especially recommended for binary packages to ensure reproducibility, and is more 106 | # commonly ignored for libraries. 107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 108 | #poetry.lock 109 | 110 | # pdm 111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 112 | #pdm.lock 113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 114 | # in version control. 115 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 116 | .pdm.toml 117 | .pdm-python 118 | .pdm-build/ 119 | 120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 121 | __pypackages__/ 122 | 123 | # Celery stuff 124 | celerybeat-schedule 125 | celerybeat.pid 126 | 127 | # SageMath parsed files 128 | *.sage.py 129 | 130 | # Environments 131 | .env 132 | .venv 133 | env/ 134 | venv/ 135 | ENV/ 136 | env.bak/ 137 | venv.bak/ 138 | 139 | # Spyder project settings 140 | .spyderproject 141 | .spyproject 142 | 143 | # Rope project settings 144 | .ropeproject 145 | 146 | # mkdocs documentation 147 | /site 148 | 149 | # mypy 150 | .mypy_cache/ 151 | .dmypy.json 152 | dmypy.json 153 | 154 | # Pyre type checker 155 | .pyre/ 156 | 157 | # pytype static type analyzer 158 | .pytype/ 159 | 160 | # Cython debug symbols 161 | cython_debug/ 162 | 163 | # PyCharm 164 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 165 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 166 | # and can be added to the global gitignore or merged into this file. For a more nuclear 167 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 168 | .idea/ 169 | 170 | # Vscode 171 | .vscode/ 172 | -------------------------------------------------------------------------------- /db_query/GUIDE.md: -------------------------------------------------------------------------------- 1 | ## User Guide of how to develop a Dify Plugin 2 | 3 | Hi there, looks like you have already created a Plugin, now let's get you started with the development! 4 | 5 | ### Choose a Plugin type you want to develop 6 | 7 | Before start, you need some basic knowledge about the Plugin types, Plugin supports to extend the following abilities in Dify: 8 | - **Tool**: Tool Providers like Google Search, Stable Diffusion, etc. it can be used to perform a specific task. 9 | - **Model**: Model Providers like OpenAI, Anthropic, etc. you can use their models to enhance the AI capabilities. 10 | - **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. 11 | 12 | Based on the ability you want to extend, we have divided the Plugin into three types: **Tool**, **Model**, and **Extension**. 13 | 14 | - **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. 15 | - **Model**: Just a model provider, extending others is not allowed. 16 | - **Extension**: Other times, you may only need a simple http service to extend the functionalities, **Extension** is the right choice for you. 17 | 18 | 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. 19 | 20 | ### Manifest 21 | 22 | Now you can edit the `manifest.yaml` file to describe your Plugin, here is the basic structure of it: 23 | 24 | - version(version, required):Plugin's version 25 | - type(type, required):Plugin's type, currently only supports `plugin`, future support `bundle` 26 | - author(string, required):Author, it's the organization name in Marketplace and should also equals to the owner of the repository 27 | - label(label, required):Multi-language name 28 | - created_at(RFC3339, required):Creation time, Marketplace requires that the creation time must be less than the current time 29 | - icon(asset, required):Icon path 30 | - resource (object):Resources to be applied 31 | - memory (int64):Maximum memory usage, mainly related to resource application on SaaS for serverless, unit bytes 32 | - permission(object):Permission application 33 | - tool(object):Reverse call tool permission 34 | - enabled (bool) 35 | - model(object):Reverse call model permission 36 | - enabled(bool) 37 | - llm(bool) 38 | - text_embedding(bool) 39 | - rerank(bool) 40 | - tts(bool) 41 | - speech2text(bool) 42 | - moderation(bool) 43 | - node(object):Reverse call node permission 44 | - enabled(bool) 45 | - endpoint(object):Allow to register endpoint permission 46 | - enabled(bool) 47 | - app(object):Reverse call app permission 48 | - enabled(bool) 49 | - storage(object):Apply for persistent storage permission 50 | - enabled(bool) 51 | - size(int64):Maximum allowed persistent memory, unit bytes 52 | - 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. 53 | - Format 54 | - tools(list[string]): Extended tool suppliers, as for the detailed format, please refer to [Tool Guide](https://docs.dify.ai/docs/plugins/standard/tool_provider) 55 | - models(list[string]):Extended model suppliers, as for the detailed format, please refer to [Model Guide](https://docs.dify.ai/docs/plugins/standard/model_provider) 56 | - endpoints(list[string]):Extended Endpoints suppliers, as for the detailed format, please refer to [Endpoint Guide](https://docs.dify.ai/docs/plugins/standard/endpoint_group) 57 | - Restrictions 58 | - Not allowed to extend both tools and models 59 | - Not allowed to have no extension 60 | - Not allowed to extend both models and endpoints 61 | - Currently only supports up to one supplier of each type of extension 62 | - meta(object) 63 | - version(version, required):manifest format version, initial version 0.0.1 64 | - arch(list[string], required):Supported architectures, currently only supports amd64 arm64 65 | - runner(object, required):Runtime configuration 66 | - language(string):Currently only supports python 67 | - version(string):Language version, currently only supports 3.12 68 | - entrypoint(string):Program entry, in python it should be main 69 | 70 | ### Install Dependencies 71 | 72 | - First of all, you need a Python 3.11+ environment, as our SDK requires that. 73 | - Then, install the dependencies: 74 | ```bash 75 | pip install -r requirements.txt 76 | ``` 77 | - 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. 78 | 79 | ### Implement the Plugin 80 | 81 | Now you can start to implement your Plugin, by following these examples, you can quickly understand how to implement your own Plugin: 82 | 83 | - [OpenAI](https://github.com/langgenius/dify-plugin-sdks/tree/main/python/examples/openai): best practice for model provider 84 | - [Google Search](https://github.com/langgenius/dify-plugin-sdks/tree/main/python/examples/google): a simple example for tool provider 85 | - [Neko](https://github.com/langgenius/dify-plugin-sdks/tree/main/python/examples/neko): a funny example for endpoint group 86 | 87 | ### Test and Debug the Plugin 88 | 89 | 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. 90 | 91 | - `INSTALL_METHOD`: Set this to `remote`, your plugin will connect to a Dify instance through the network. 92 | - `REMOTE_INSTALL_HOST`: The host of your Dify instance, you can use our SaaS instance `https://debug.dify.ai`, or self-hosted Dify instance. 93 | - `REMOTE_INSTALL_PORT`: The port of your Dify instance, default is 5003 94 | - `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. 95 | 96 | Run the following command to start your Plugin: 97 | 98 | ```bash 99 | python -m main 100 | ``` 101 | 102 | 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. 103 | 104 | ### Package the Plugin 105 | 106 | After all, just package your Plugin by running the following command: 107 | 108 | ```bash 109 | dify-plugin plugin package ./ROOT_DIRECTORY_OF_YOUR_PLUGIN 110 | ``` 111 | 112 | 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! 113 | 114 | 115 | ## User Privacy Policy 116 | 117 | 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. -------------------------------------------------------------------------------- /db_query/PRIVACY.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | This tool is designed with privacy in mind and does not collect any user data. We are committed to maintaining your privacy and ensuring your data remains secure. 4 | 5 | ## Data Collection 6 | 7 | - **No Personal Information**: We do not collect, store, or process any personal information. 8 | - **No Usage Data**: We do not track or monitor how you use the tool. 9 | - **No Analytics**: We do not implement any analytics or tracking mechanisms. 10 | 11 | ## Third-Party Services 12 | 13 | This tool does not integrate with or utilize any third-party services that might collect user data. 14 | 15 | ## Changes to Privacy Policy 16 | 17 | If there are any changes to our privacy practices, we will update this document accordingly. -------------------------------------------------------------------------------- /db_query/README.md: -------------------------------------------------------------------------------- 1 | ## Dify 1.0 Plugin Database Query Tools 2 | 3 | 4 | **Author:** [Junjie.M](https://github.com/junjiem) 5 | **Type:** tool 6 | **Github Repo:** [https://github.com/junjiem/dify-plugin-tools-dbquery](https://github.com/junjiem/dify-plugin-tools-dbquery) 7 | **Github Issues:** [issues](https://github.com/junjiem/dify-plugin-tools-dbquery/issues) 8 | 9 | 10 | --- 11 | 12 | 13 | ### Demonstration 14 | 15 | Database Query Tools 16 | 17 | 数据库查询工具 18 | 19 | Currently supported database types: mysql, oracle, [oracle11g](#2-how-to-connect-to-oracle-11g--如何连接oracle-11g), postgresql, or mssql. 20 | 21 | 目前支持的数据库类型:mysql、oracle、[oracle11g](#2-how-to-connect-to-oracle-11g--如何连接oracle-11g)、postgresql、mssql。 22 | 23 | ![db_query](_assets/db_query.png) 24 | 25 | ![db_query_sql_query](_assets/db_query_sql_query.png) 26 | 27 | ![db_query_chatflow](_assets/db_query_chatflow.png) 28 | 29 | 30 | 31 | --- 32 | 33 | 34 | 35 | ### Examples 示例 36 | 37 | - [完蛋!我被LLM包围了!(Dify1.0战绩排行版)](https://github.com/junjiem/dify-plugin-tools-dbquery/blob/main/examples/完蛋!我被LLM包围了!(Dify1.0战绩排行版).yml) 38 | 39 | - [完蛋!我被LLM包围了!(Dify1.0战绩排行+留言版)](https://github.com/junjiem/dify-plugin-tools-dbquery/blob/main/examples/完蛋!我被LLM包围了!(Dify1.0战绩排行+留言版).yml) 40 | 41 | 42 | ![](_assets/llm_riddles1.png) 43 | 44 | ![](_assets/llm_riddles2.png) 45 | 46 | ![](_assets/llm_riddles3.png) 47 | 48 | 49 | 50 | --- 51 | 52 | 53 | 54 | ### FAQ 55 | 56 | #### 1. How to Handle Errors When Installing Plugins? 安装插件时遇到异常应如何处理? 57 | 58 | **Issue**: If you encounter the error message: plugin verification has been enabled, and the plugin you want to install has a bad signature, how to handle the issue? 59 | 60 | **Solution**: Add the following line to the end of your .env configuration file: FORCE_VERIFYING_SIGNATURE=false 61 | Once this field is added, the Dify platform will allow the installation of all plugins that are not listed (and thus not verified) in the Dify Marketplace. 62 | 63 | **问题描述**:安装插件时遇到异常信息:plugin verification has been enabled, and the plugin you want to install has a bad signature,应该如何处理? 64 | 65 | **解决办法**:在 .env 配置文件的末尾添加 FORCE_VERIFYING_SIGNATURE=false 字段即可解决该问题。 66 | 添加该字段后,Dify 平台将允许安装所有未在 Dify Marketplace 上架(审核)的插件,可能存在安全隐患。 67 | 68 | 69 | #### 2. How to connect to oracle 11g 如何连接Oracle 11g 70 | 71 | 2.1、下载 oracle11g 的 client,这里下的是 11.2.0.4.0 版本 72 | 73 | https://www.oracle.com/database/technologies/instant-client/downloads.html 74 | 75 | 比如:instantclient-basic-linux.x64-11.2.0.4.0.zip 76 | 77 | 78 | 2.2、上传 oracle 的 client 到宿主机的 dify 的挂载目录 79 | 80 | 将 instantclient-basic-linux.x64-11.2.0.4.0.zip 解压后的 instantclient_11_2 目录放到 docker/volumes 下面 81 | > 注:需要将 `instantclient_11_2` 中`libclntsh.so.11.1` 改成 `libclntsh.so`、 `libocci.so.11.1` 改成 `libocci.so` 82 | 83 | 2.3、在 `docker/docker-compose.yml` 中进行配置 84 | ``` 85 | # 在 plugin_daemon 下,添加一个变量 86 | environment: 87 | ...... 88 | LD_LIBRARY_PATH: "/root/instantclient_11_2:$LD_LIBRARY_PATH" 89 | ``` 90 | 91 | Red Hat 系(如 CentOS、RHEL)64 位: 92 | ``` 93 | # 在 plugin_daemon 下,添加三个挂载 94 | volumes: 95 | ...... 96 | - ./volumes/instantclient_11_2:/root/instantclient_11_2 97 | - /usr/lib64/libaio.so.1.0.1:/usr/lib/x86_64-linux-gnu/libaio.so.1.0.1 98 | - /usr/lib64/libaio.so.1:/usr/lib/x86_64-linux-gnu/libaio.so.1 99 | ``` 100 | 101 | Debian 系(如 Ubuntu)64 位: 102 | ``` 103 | # 在 plugin_daemon 下,添加三个挂载 104 | volumes: 105 | ...... 106 | - ./volumes/instantclient_11_2:/root/instantclient_11_2 107 | - /usr/lib/x86_64-linux-gnu/libaio.so.1.0.1:/usr/lib/x86_64-linux-gnu/libaio.so.1.0.1 108 | - /usr/lib/x86_64-linux-gnu/libaio.so.1:/usr/lib/x86_64-linux-gnu/libaio.so.1 109 | ``` 110 | 111 | 2.4、重启 plugin_daemon 112 | ```shell 113 | docker stop docker-plugin_daemon-1 114 | docker compose up -d plugin_daemon 115 | ``` 116 | 117 | 118 | #### 3. How to install the offline version 如何安装离线版本 119 | 120 | Scripting tool for downloading Dify plugin package from Dify Marketplace and Github and repackaging [true] offline package (contains dependencies, no need to be connected to the Internet). 121 | 122 | 从Dify市场和Github下载Dify插件包并重新打【真】离线包(包含依赖,不需要再联网)的脚本工具。 123 | 124 | Github Repo: https://github.com/junjiem/dify-plugin-repackaging 125 | 126 | -------------------------------------------------------------------------------- /db_query/_assets/db_query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/db_query/_assets/db_query.png -------------------------------------------------------------------------------- /db_query/_assets/db_query_chatflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/db_query/_assets/db_query_chatflow.png -------------------------------------------------------------------------------- /db_query/_assets/db_query_sql_query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/db_query/_assets/db_query_sql_query.png -------------------------------------------------------------------------------- /db_query/_assets/icon.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 7 | 9 | -------------------------------------------------------------------------------- /db_query/_assets/llm_riddles1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/db_query/_assets/llm_riddles1.png -------------------------------------------------------------------------------- /db_query/_assets/llm_riddles2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/db_query/_assets/llm_riddles2.png -------------------------------------------------------------------------------- /db_query/_assets/llm_riddles3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/db_query/_assets/llm_riddles3.png -------------------------------------------------------------------------------- /db_query/main.py: -------------------------------------------------------------------------------- 1 | from dify_plugin import Plugin, DifyPluginEnv 2 | 3 | plugin = Plugin(DifyPluginEnv(MAX_REQUEST_TIMEOUT=120)) 4 | 5 | if __name__ == "__main__": 6 | plugin.run() 7 | -------------------------------------------------------------------------------- /db_query/manifest.yaml: -------------------------------------------------------------------------------- 1 | author: junjiem 2 | created_at: "2025-01-13T09:55:01.5573645+08:00" 3 | description: 4 | en_US: Database Query Utils. 5 | zh_Hans: 数据库查询工具。 6 | icon: icon.svg 7 | label: 8 | en_US: Database Query 9 | zh_Hans: 数据库查询 10 | meta: 11 | arch: 12 | - amd64 13 | - arm64 14 | runner: 15 | entrypoint: main 16 | language: python 17 | version: "3.12" 18 | version: 0.0.1 19 | name: db_query 20 | plugins: 21 | tools: 22 | - provider/db_query.yaml 23 | resource: 24 | memory: 1048576 25 | permission: 26 | model: 27 | enabled: true 28 | llm: true 29 | tool: 30 | enabled: true 31 | tags: 32 | - search 33 | - utilities 34 | type: plugin 35 | version: 0.0.9 36 | privacy: PRIVACY.md 37 | verified: false -------------------------------------------------------------------------------- /db_query/provider/db_query.py: -------------------------------------------------------------------------------- 1 | from dify_plugin import ToolProvider 2 | 3 | 4 | class DbQueryProvider(ToolProvider): 5 | def _validate_credentials(self, credentials: dict) -> None: 6 | pass 7 | -------------------------------------------------------------------------------- /db_query/provider/db_query.yaml: -------------------------------------------------------------------------------- 1 | extra: 2 | python: 3 | source: provider/db_query.py 4 | identity: 5 | name: db_query 6 | author: junjiem 7 | label: 8 | en_US: Database Query 9 | zh_Hans: 数据库查询 10 | description: 11 | en_US: Database Query Utils. 12 | zh_Hans: 数据库查询工具。 13 | icon: icon.svg 14 | tags: 15 | - search 16 | - utilities 17 | tools: 18 | - tools/sql_query.yaml -------------------------------------------------------------------------------- /db_query/requirements.txt: -------------------------------------------------------------------------------- 1 | dify_plugin==0.2.1 2 | sqlparse==0.5.3 3 | tabulate==0.9.0 4 | pandas==2.2.3 5 | SQLAlchemy==2.0.35 6 | PyMySQL==1.1.1 7 | oracledb==2.2.1 8 | psycopg2-binary==2.9.10 9 | pymssql==2.3.4 -------------------------------------------------------------------------------- /db_query/tools/db_util.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import logging 3 | from typing import Optional 4 | from urllib import parse 5 | from uuid import UUID 6 | 7 | import oracledb 8 | import pandas as pd 9 | from pandas import Timestamp 10 | from sqlalchemy import create_engine 11 | 12 | 13 | class DbUtil: 14 | 15 | def __init__(self, db_type: str, 16 | username: str, password: str, 17 | host: str, port: Optional[str] = None, 18 | database: Optional[str] = None, 19 | properties: Optional[str] = None) -> None: 20 | self.db_type = db_type.lower() 21 | self.username = username 22 | self.password = password 23 | self.host = host 24 | self.port = port 25 | self.database = database 26 | self.properties = properties 27 | if self.db_type == 'oracle11g': 28 | # To change from the default python-oracledb Thin mode to Thick mode 29 | oracledb.init_oracle_client() 30 | self.engine = create_engine(self.get_url(), pool_size=100, pool_recycle=36) 31 | 32 | def __enter__(self): 33 | return self 34 | 35 | def __exit__(self, exc_type, exc_val, exc_tb): 36 | self.close() 37 | 38 | def get_driver_name(self): 39 | driver_name = self.db_type 40 | if self.db_type == 'mysql': 41 | driver_name = 'mysql+pymysql' 42 | elif self.db_type in {'oracle', 'oracle11g'}: 43 | driver_name = 'oracle+oracledb' 44 | elif self.db_type == 'postgresql': 45 | driver_name = 'postgresql+psycopg2' 46 | elif self.db_type == 'mssql': 47 | driver_name = 'mssql+pymssql' 48 | return driver_name 49 | 50 | def get_url(self): 51 | ''' 52 | Get url 53 | ''' 54 | parsed_username = parse.quote_plus(self.username) 55 | parsed_password = parse.quote_plus(self.password) 56 | parsed_host = parse.quote_plus(self.host) 57 | url = f"{self.get_driver_name()}://{parsed_username}:{parsed_password}@{parsed_host}" 58 | if self.is_not_empty(self.port): 59 | url = f"{url}:{str(self.port)}" 60 | url = f"{url}/" 61 | if self.is_not_empty(self.database): 62 | parsed_database = parse.quote_plus(self.database) 63 | url = f"{url}{parsed_database}" 64 | if self.is_not_empty(self.properties): 65 | url = f"{url}?{self.properties}" 66 | logging.info(f"url: {url}") 67 | return url 68 | 69 | def close(self): 70 | """Close all connections in the engine.""" 71 | self.engine.dispose() 72 | 73 | def run_query(self, query_sql: str) -> list[dict]: 74 | ''' 75 | Run SQL Query 76 | ''' 77 | query_sql = query_sql.replace('%', '%%') 78 | df = pd.read_sql_query(sql=query_sql, con=self.engine, parse_dates="%Y-%m-%d %H:%M:%S") 79 | df = df.fillna('') 80 | records = [] 81 | if len(df) > 0: 82 | records = df.to_dict(orient="records") 83 | for record in records: 84 | for key in record: 85 | if type(record[key]) is Timestamp: 86 | record[key] = record[key].strftime('%Y-%m-%d %H:%M:%S') 87 | if type(record[key]) is datetime.date: 88 | record[key] = record[key].strftime('%Y-%m-%d') 89 | if type(record[key]) is UUID: 90 | record[key] = str(record[key]) 91 | return records 92 | 93 | def test_sql(self): 94 | if self.db_type in {'oracle', 'oracle11g'}: 95 | return "SELECT 1 FROM DUAL" 96 | else: 97 | return "SELECT 1" 98 | 99 | @staticmethod 100 | def is_not_empty(s: str): 101 | return s is not None and s.strip() != "" 102 | -------------------------------------------------------------------------------- /db_query/tools/sql_query.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Any, Generator 3 | 4 | import sqlparse 5 | import tabulate 6 | from dify_plugin import Tool 7 | from dify_plugin.entities.tool import ToolInvokeMessage 8 | 9 | from tools.db_util import DbUtil 10 | 11 | 12 | class SqlQueryTool(Tool): 13 | def _invoke( 14 | self, tool_parameters: dict[str, Any] 15 | ) -> Generator[ToolInvokeMessage, None, None]: 16 | """ 17 | invoke tools 18 | """ 19 | db_type = tool_parameters.get("db_type", "") 20 | if not db_type: 21 | raise ValueError("Please select the database type") 22 | db_host = tool_parameters.get("db_host", "") 23 | if not db_host: 24 | raise ValueError("Please fill in the database host") 25 | db_port = tool_parameters.get("db_port", "") 26 | if db_port is not None: 27 | db_port = str(db_port) 28 | db_username = tool_parameters.get("db_username", "") 29 | if not db_username: 30 | raise ValueError("Please fill in the database username") 31 | db_password = tool_parameters.get("db_password", "") 32 | if not db_password: 33 | raise ValueError("Please fill in the database password") 34 | db_name = tool_parameters.get("db_name", "") 35 | db_properties = tool_parameters.get("db_properties", "") 36 | 37 | query_sql = tool_parameters.get("query_sql", "") 38 | if not query_sql: 39 | raise ValueError("Please fill in the query SQL, for example: select * from tbl_name") 40 | statements = sqlparse.parse(query_sql) 41 | if len(statements) != 1: 42 | raise ValueError("Only a single query SQL can be filled") 43 | statement = statements[0] 44 | if statement.get_type() != 'SELECT': 45 | raise ValueError("Query SQL can only be a single SELECT statement") 46 | 47 | output_format = tool_parameters.get("output_format", "markdown").lower() 48 | 49 | try: 50 | with DbUtil(db_type=db_type, 51 | username=db_username, password=db_password, 52 | host=db_host, port=db_port, 53 | database=db_name, properties=db_properties) as db: 54 | records = db.run_query(query_sql) 55 | except Exception as e: 56 | logging.exception("SQL query execution failed: %s", str(e)) 57 | raise RuntimeError(f"Error executing SQL: {e}") from e 58 | 59 | if output_format == "json": 60 | yield self.create_json_message({"records": records}) 61 | else: 62 | text = tabulate.tabulate(records, headers="keys", tablefmt="github", floatfmt="") 63 | yield self.create_text_message(text) 64 | -------------------------------------------------------------------------------- /db_query/tools/sql_query.yaml: -------------------------------------------------------------------------------- 1 | identity: 2 | name: sql_query 3 | author: junjiem 4 | label: 5 | en_US: SQL Query 6 | zh_Hans: SQL查询 7 | description: 8 | human: 9 | en_US: A tool for Database SQL query. 10 | zh_Hans: 数据库SQL查询工具。 11 | llm: A tool for executing SQL query. Input should be a search query. 12 | extra: 13 | python: 14 | source: tools/sql_query.py 15 | parameters: 16 | - name: db_type 17 | type: select 18 | required: true 19 | options: 20 | - value: mysql 21 | label: 22 | en_US: MySQL 23 | zh_Hans: MySQL 24 | - value: oracle 25 | label: 26 | en_US: Oracle 27 | zh_Hans: Oracle 28 | - value: oracle11g 29 | label: 30 | en_US: Oracle11g 31 | zh_Hans: Oracle11g 32 | - value: postgresql 33 | label: 34 | en_US: PostgreSQL 35 | zh_Hans: PostgreSQL 36 | - value: mssql 37 | label: 38 | en_US: Microsoft SQL Server 39 | zh_Hans: Microsoft SQL Server 40 | default: mysql 41 | label: 42 | en_US: Database type 43 | zh_Hans: 数据库类型 44 | human_description: 45 | en_US: Used for selecting the database type, mysql, oracle, oracle11g, postgresql or mssql. 46 | zh_Hans: 用于选择数据库类型,mysql、oracle、oracle11g、postgresql或mssql。 47 | form: llm 48 | - name: db_host 49 | type: string 50 | required: true 51 | default: localhost 52 | label: 53 | en_US: Database Host 54 | zh_Hans: 数据库地址 55 | human_description: 56 | en_US: Database hostname or IP address (Original string, not URL-encoded string). 57 | zh_Hans: 数据库的主机名或IP地址(原始字符串,非URL编码字符串)。 58 | form: llm 59 | - name: db_port 60 | type: number 61 | required: false 62 | label: 63 | en_US: Port 64 | zh_Hans: 端口 65 | human_description: 66 | en_US: Database port. 67 | zh_Hans: 数据库的端口。 68 | form: llm 69 | - name: db_username 70 | type: string 71 | required: true 72 | label: 73 | en_US: Username 74 | zh_Hans: 用户名 75 | human_description: 76 | en_US: Database username (Original string, not URL-encoded string). 77 | zh_Hans: 数据库的用户名(原始字符串,非URL编码字符串)。 78 | form: llm 79 | - name: db_password 80 | type: secret-input 81 | required: true 82 | label: 83 | en_US: Password 84 | zh_Hans: 密码 85 | human_description: 86 | en_US: Database password (Original string, not URL-encoded string). 87 | zh_Hans: 数据库的密码(原始字符串,非URL编码字符串)。 88 | form: llm 89 | - name: db_name 90 | type: string 91 | required: false 92 | label: 93 | en_US: Database name 94 | zh_Hans: 库名 95 | human_description: 96 | en_US: Database name (Original string, not URL-encoded string). 97 | zh_Hans: 数据库的名称(原始字符串,非URL编码字符串)。 98 | form: llm 99 | - name: db_properties 100 | type: string 101 | required: false 102 | label: 103 | en_US: Database properties 104 | zh_Hans: 数据库属性 105 | human_description: 106 | en_US: 'Database properties, for example: alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt' 107 | zh_Hans: '数据库属性,例如:alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt' 108 | form: llm 109 | - name: query_sql 110 | type: string 111 | required: true 112 | label: 113 | en_US: Query SQL 114 | zh_Hans: SQL查询语句 115 | human_description: 116 | en_US: 'SQL query statement, for example: select * from tbl_name' 117 | zh_Hans: SQL查询语句,例如:select * from tbl_name 118 | llm_description: 'SQL query statement, for example: select * from tbl_name' 119 | form: llm 120 | - name: output_format 121 | type: select 122 | required: true 123 | label: 124 | en_US: Output format 125 | zh_Hans: 输出格式 126 | human_description: 127 | en_US: Used for selecting the output format, markdown or json. 128 | zh_Hans: 用于选择输出格式,markdown或json。 129 | options: 130 | - value: markdown 131 | label: 132 | en_US: MARKDOWN 133 | zh_Hans: MARKDOWN 134 | - value: json 135 | label: 136 | en_US: JSON 137 | zh_Hans: JSON 138 | default: markdown 139 | form: form -------------------------------------------------------------------------------- /db_query_pre_auth/.difyignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # Distribution / packaging 7 | .Python 8 | build/ 9 | develop-eggs/ 10 | dist/ 11 | downloads/ 12 | eggs/ 13 | .eggs/ 14 | lib/ 15 | lib64/ 16 | parts/ 17 | sdist/ 18 | var/ 19 | wheels/ 20 | share/python-wheels/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | MANIFEST 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .nox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *.cover 46 | *.py,cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | cover/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | db.sqlite3-journal 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | .pybuilder/ 73 | target/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # IPython 79 | profile_default/ 80 | ipython_config.py 81 | 82 | # pyenv 83 | # For a library or package, you might want to ignore these files since the code is 84 | # intended to run in multiple environments; otherwise, check them in: 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | Pipfile.lock 93 | 94 | # UV 95 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 96 | # This is especially recommended for binary packages to ensure reproducibility, and is more 97 | # commonly ignored for libraries. 98 | uv.lock 99 | 100 | # poetry 101 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 102 | # This is especially recommended for binary packages to ensure reproducibility, and is more 103 | # commonly ignored for libraries. 104 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 105 | poetry.lock 106 | 107 | # pdm 108 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 109 | #pdm.lock 110 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 111 | # in version control. 112 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 113 | .pdm.toml 114 | .pdm-python 115 | .pdm-build/ 116 | 117 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 118 | __pypackages__/ 119 | 120 | # Celery stuff 121 | celerybeat-schedule 122 | celerybeat.pid 123 | 124 | # SageMath parsed files 125 | *.sage.py 126 | 127 | # Environments 128 | .env 129 | .venv 130 | env/ 131 | venv/ 132 | ENV/ 133 | env.bak/ 134 | venv.bak/ 135 | 136 | # Spyder project settings 137 | .spyderproject 138 | .spyproject 139 | 140 | # Rope project settings 141 | .ropeproject 142 | 143 | # mkdocs documentation 144 | /site 145 | 146 | # mypy 147 | .mypy_cache/ 148 | .dmypy.json 149 | dmypy.json 150 | 151 | # Pyre type checker 152 | .pyre/ 153 | 154 | # pytype static type analyzer 155 | .pytype/ 156 | 157 | # Cython debug symbols 158 | cython_debug/ 159 | 160 | # PyCharm 161 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 162 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 163 | # and can be added to the global gitignore or merged into this file. For a more nuclear 164 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 165 | .idea/ 166 | 167 | # Vscode 168 | .vscode/ 169 | 170 | # Git 171 | .git/ 172 | .gitignore 173 | 174 | # Mac 175 | .DS_Store 176 | 177 | # Windows 178 | Thumbs.db 179 | -------------------------------------------------------------------------------- /db_query_pre_auth/.env.example: -------------------------------------------------------------------------------- 1 | INSTALL_METHOD=remote 2 | REMOTE_INSTALL_HOST=debug-plugin.dify.dev 3 | REMOTE_INSTALL_PORT=5003 4 | REMOTE_INSTALL_KEY=********-****-****-****-************ 5 | -------------------------------------------------------------------------------- /db_query_pre_auth/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # UV 98 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | #uv.lock 102 | 103 | # poetry 104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 105 | # This is especially recommended for binary packages to ensure reproducibility, and is more 106 | # commonly ignored for libraries. 107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 108 | #poetry.lock 109 | 110 | # pdm 111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 112 | #pdm.lock 113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 114 | # in version control. 115 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 116 | .pdm.toml 117 | .pdm-python 118 | .pdm-build/ 119 | 120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 121 | __pypackages__/ 122 | 123 | # Celery stuff 124 | celerybeat-schedule 125 | celerybeat.pid 126 | 127 | # SageMath parsed files 128 | *.sage.py 129 | 130 | # Environments 131 | .env 132 | .venv 133 | env/ 134 | venv/ 135 | ENV/ 136 | env.bak/ 137 | venv.bak/ 138 | 139 | # Spyder project settings 140 | .spyderproject 141 | .spyproject 142 | 143 | # Rope project settings 144 | .ropeproject 145 | 146 | # mkdocs documentation 147 | /site 148 | 149 | # mypy 150 | .mypy_cache/ 151 | .dmypy.json 152 | dmypy.json 153 | 154 | # Pyre type checker 155 | .pyre/ 156 | 157 | # pytype static type analyzer 158 | .pytype/ 159 | 160 | # Cython debug symbols 161 | cython_debug/ 162 | 163 | # PyCharm 164 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 165 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 166 | # and can be added to the global gitignore or merged into this file. For a more nuclear 167 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 168 | .idea/ 169 | 170 | # Vscode 171 | .vscode/ 172 | -------------------------------------------------------------------------------- /db_query_pre_auth/GUIDE.md: -------------------------------------------------------------------------------- 1 | ## User Guide of how to develop a Dify Plugin 2 | 3 | Hi there, looks like you have already created a Plugin, now let's get you started with the development! 4 | 5 | ### Choose a Plugin type you want to develop 6 | 7 | Before start, you need some basic knowledge about the Plugin types, Plugin supports to extend the following abilities in Dify: 8 | - **Tool**: Tool Providers like Google Search, Stable Diffusion, etc. it can be used to perform a specific task. 9 | - **Model**: Model Providers like OpenAI, Anthropic, etc. you can use their models to enhance the AI capabilities. 10 | - **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. 11 | 12 | Based on the ability you want to extend, we have divided the Plugin into three types: **Tool**, **Model**, and **Extension**. 13 | 14 | - **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. 15 | - **Model**: Just a model provider, extending others is not allowed. 16 | - **Extension**: Other times, you may only need a simple http service to extend the functionalities, **Extension** is the right choice for you. 17 | 18 | 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. 19 | 20 | ### Manifest 21 | 22 | Now you can edit the `manifest.yaml` file to describe your Plugin, here is the basic structure of it: 23 | 24 | - version(version, required):Plugin's version 25 | - type(type, required):Plugin's type, currently only supports `plugin`, future support `bundle` 26 | - author(string, required):Author, it's the organization name in Marketplace and should also equals to the owner of the repository 27 | - label(label, required):Multi-language name 28 | - created_at(RFC3339, required):Creation time, Marketplace requires that the creation time must be less than the current time 29 | - icon(asset, required):Icon path 30 | - resource (object):Resources to be applied 31 | - memory (int64):Maximum memory usage, mainly related to resource application on SaaS for serverless, unit bytes 32 | - permission(object):Permission application 33 | - tool(object):Reverse call tool permission 34 | - enabled (bool) 35 | - model(object):Reverse call model permission 36 | - enabled(bool) 37 | - llm(bool) 38 | - text_embedding(bool) 39 | - rerank(bool) 40 | - tts(bool) 41 | - speech2text(bool) 42 | - moderation(bool) 43 | - node(object):Reverse call node permission 44 | - enabled(bool) 45 | - endpoint(object):Allow to register endpoint permission 46 | - enabled(bool) 47 | - app(object):Reverse call app permission 48 | - enabled(bool) 49 | - storage(object):Apply for persistent storage permission 50 | - enabled(bool) 51 | - size(int64):Maximum allowed persistent memory, unit bytes 52 | - 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. 53 | - Format 54 | - tools(list[string]): Extended tool suppliers, as for the detailed format, please refer to [Tool Guide](https://docs.dify.ai/docs/plugins/standard/tool_provider) 55 | - models(list[string]):Extended model suppliers, as for the detailed format, please refer to [Model Guide](https://docs.dify.ai/docs/plugins/standard/model_provider) 56 | - endpoints(list[string]):Extended Endpoints suppliers, as for the detailed format, please refer to [Endpoint Guide](https://docs.dify.ai/docs/plugins/standard/endpoint_group) 57 | - Restrictions 58 | - Not allowed to extend both tools and models 59 | - Not allowed to have no extension 60 | - Not allowed to extend both models and endpoints 61 | - Currently only supports up to one supplier of each type of extension 62 | - meta(object) 63 | - version(version, required):manifest format version, initial version 0.0.1 64 | - arch(list[string], required):Supported architectures, currently only supports amd64 arm64 65 | - runner(object, required):Runtime configuration 66 | - language(string):Currently only supports python 67 | - version(string):Language version, currently only supports 3.12 68 | - entrypoint(string):Program entry, in python it should be main 69 | 70 | ### Install Dependencies 71 | 72 | - First of all, you need a Python 3.11+ environment, as our SDK requires that. 73 | - Then, install the dependencies: 74 | ```bash 75 | pip install -r requirements.txt 76 | ``` 77 | - 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. 78 | 79 | ### Implement the Plugin 80 | 81 | Now you can start to implement your Plugin, by following these examples, you can quickly understand how to implement your own Plugin: 82 | 83 | - [OpenAI](https://github.com/langgenius/dify-plugin-sdks/tree/main/python/examples/openai): best practice for model provider 84 | - [Google Search](https://github.com/langgenius/dify-plugin-sdks/tree/main/python/examples/google): a simple example for tool provider 85 | - [Neko](https://github.com/langgenius/dify-plugin-sdks/tree/main/python/examples/neko): a funny example for endpoint group 86 | 87 | ### Test and Debug the Plugin 88 | 89 | 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. 90 | 91 | - `INSTALL_METHOD`: Set this to `remote`, your plugin will connect to a Dify instance through the network. 92 | - `REMOTE_INSTALL_HOST`: The host of your Dify instance, you can use our SaaS instance `https://debug.dify.ai`, or self-hosted Dify instance. 93 | - `REMOTE_INSTALL_PORT`: The port of your Dify instance, default is 5003 94 | - `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. 95 | 96 | Run the following command to start your Plugin: 97 | 98 | ```bash 99 | python -m main 100 | ``` 101 | 102 | 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. 103 | 104 | ### Package the Plugin 105 | 106 | After all, just package your Plugin by running the following command: 107 | 108 | ```bash 109 | dify-plugin plugin package ./ROOT_DIRECTORY_OF_YOUR_PLUGIN 110 | ``` 111 | 112 | 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! 113 | 114 | 115 | ## User Privacy Policy 116 | 117 | 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. -------------------------------------------------------------------------------- /db_query_pre_auth/PRIVACY.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | This tool is designed with privacy in mind and does not collect any user data. We are committed to maintaining your privacy and ensuring your data remains secure. 4 | 5 | ## Data Collection 6 | 7 | - **No Personal Information**: We do not collect, store, or process any personal information. 8 | - **No Usage Data**: We do not track or monitor how you use the tool. 9 | - **No Analytics**: We do not implement any analytics or tracking mechanisms. 10 | 11 | ## Third-Party Services 12 | 13 | This tool does not integrate with or utilize any third-party services that might collect user data. 14 | 15 | ## Changes to Privacy Policy 16 | 17 | If there are any changes to our privacy practices, we will update this document accordingly. -------------------------------------------------------------------------------- /db_query_pre_auth/README.md: -------------------------------------------------------------------------------- 1 | ## Dify 1.0 Plugin Database Query Tools (Pre-authorization) 2 | 3 | 4 | **Author:** [Junjie.M](https://github.com/junjiem) 5 | **Type:** tool 6 | **Github Repo:** [https://github.com/junjiem/dify-plugin-tools-dbquery](https://github.com/junjiem/dify-plugin-tools-dbquery) 7 | **Github Issues:** [issues](https://github.com/junjiem/dify-plugin-tools-dbquery/issues) 8 | 9 | 10 | --- 11 | 12 | 13 | ### Demonstration 14 | 15 | Database Query Tools (Pre-authorization) 16 | 17 | 数据库查询工具(预授权) 18 | 19 | Currently supported database types: mysql, oracle, [oracle11g](#2-how-to-connect-to-oracle-11g--如何连接oracle-11g), postgresql, or mssql. 20 | 21 | 目前支持的数据库类型:mysql、oracle、[oracle11g](#2-how-to-connect-to-oracle-11g--如何连接oracle-11g)、postgresql、mssql。 22 | 23 | ![db_query_pre_auth](_assets/db_query_pre_auth.png) 24 | 25 | ![db_query_pre_auth_setup_auth](_assets/db_query_pre_auth_setup_auth.png) 26 | 27 | 28 | 29 | --- 30 | 31 | 32 | 33 | ### Examples 示例 34 | 35 | - [完蛋!我被LLM包围了!(Dify1.0战绩排行版)](https://github.com/junjiem/dify-plugin-tools-dbquery/blob/main/examples/完蛋!我被LLM包围了!(Dify1.0战绩排行版).yml) 36 | 37 | - [完蛋!我被LLM包围了!(Dify1.0战绩排行+留言版)](https://github.com/junjiem/dify-plugin-tools-dbquery/blob/main/examples/完蛋!我被LLM包围了!(Dify1.0战绩排行+留言版).yml) 38 | 39 | 40 | ![](_assets/llm_riddles1.png) 41 | 42 | ![](_assets/llm_riddles2.png) 43 | 44 | ![](_assets/llm_riddles3.png) 45 | 46 | 47 | 48 | --- 49 | 50 | 51 | 52 | ### FAQ 53 | 54 | #### 1. How to Handle Errors When Installing Plugins? 安装插件时遇到异常应如何处理? 55 | 56 | **Issue**: If you encounter the error message: plugin verification has been enabled, and the plugin you want to install has a bad signature, how to handle the issue? 57 | 58 | **Solution**: Add the following line to the end of your .env configuration file: FORCE_VERIFYING_SIGNATURE=false 59 | Once this field is added, the Dify platform will allow the installation of all plugins that are not listed (and thus not verified) in the Dify Marketplace. 60 | 61 | **问题描述**:安装插件时遇到异常信息:plugin verification has been enabled, and the plugin you want to install has a bad signature,应该如何处理? 62 | 63 | **解决办法**:在 .env 配置文件的末尾添加 FORCE_VERIFYING_SIGNATURE=false 字段即可解决该问题。 64 | 添加该字段后,Dify 平台将允许安装所有未在 Dify Marketplace 上架(审核)的插件,可能存在安全隐患。 65 | 66 | 67 | #### 2. How to connect to oracle 11g 如何连接Oracle 11g 68 | 69 | 2.1、下载 oracle11g 的 client,这里下的是 11.2.0.4.0 版本 70 | 71 | https://www.oracle.com/database/technologies/instant-client/downloads.html 72 | 73 | 比如:instantclient-basic-linux.x64-11.2.0.4.0.zip 74 | 75 | 76 | 2.2、上传 oracle 的 client 到宿主机的 dify 的挂载目录 77 | 78 | 将 instantclient-basic-linux.x64-11.2.0.4.0.zip 解压后的 instantclient_11_2 目录放到 docker/volumes 下面 79 | > 注:需要将 `instantclient_11_2` 中`libclntsh.so.11.1` 改成 `libclntsh.so`、 `libocci.so.11.1` 改成 `libocci.so` 80 | 81 | 2.3、在 `docker/docker-compose.yml` 中进行配置 82 | ``` 83 | # 在 plugin_daemon 下,添加一个变量 84 | environment: 85 | ...... 86 | LD_LIBRARY_PATH: "/root/instantclient_11_2:$LD_LIBRARY_PATH" 87 | ``` 88 | 89 | Red Hat 系(如 CentOS、RHEL)64 位: 90 | ``` 91 | # 在 plugin_daemon 下,添加三个挂载 92 | volumes: 93 | ...... 94 | - ./volumes/instantclient_11_2:/root/instantclient_11_2 95 | - /usr/lib64/libaio.so.1.0.1:/usr/lib/x86_64-linux-gnu/libaio.so.1.0.1 96 | - /usr/lib64/libaio.so.1:/usr/lib/x86_64-linux-gnu/libaio.so.1 97 | ``` 98 | 99 | Debian 系(如 Ubuntu)64 位: 100 | ``` 101 | # 在 plugin_daemon 下,添加三个挂载 102 | volumes: 103 | ...... 104 | - ./volumes/instantclient_11_2:/root/instantclient_11_2 105 | - /usr/lib/x86_64-linux-gnu/libaio.so.1.0.1:/usr/lib/x86_64-linux-gnu/libaio.so.1.0.1 106 | - /usr/lib/x86_64-linux-gnu/libaio.so.1:/usr/lib/x86_64-linux-gnu/libaio.so.1 107 | ``` 108 | 109 | 2.4、重启 plugin_daemon 110 | ```shell 111 | docker stop docker-plugin_daemon-1 112 | docker compose up -d plugin_daemon 113 | ``` 114 | 115 | 116 | #### 3. How to install the offline version 如何安装离线版本 117 | 118 | Scripting tool for downloading Dify plugin package from Dify Marketplace and Github and repackaging [true] offline package (contains dependencies, no need to be connected to the Internet). 119 | 120 | 从Dify市场和Github下载Dify插件包并重新打【真】离线包(包含依赖,不需要再联网)的脚本工具。 121 | 122 | Github Repo: https://github.com/junjiem/dify-plugin-repackaging 123 | 124 | -------------------------------------------------------------------------------- /db_query_pre_auth/_assets/db_query_pre_auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/db_query_pre_auth/_assets/db_query_pre_auth.png -------------------------------------------------------------------------------- /db_query_pre_auth/_assets/db_query_pre_auth_setup_auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/db_query_pre_auth/_assets/db_query_pre_auth_setup_auth.png -------------------------------------------------------------------------------- /db_query_pre_auth/_assets/icon.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | -------------------------------------------------------------------------------- /db_query_pre_auth/_assets/llm_riddles1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/db_query_pre_auth/_assets/llm_riddles1.png -------------------------------------------------------------------------------- /db_query_pre_auth/_assets/llm_riddles2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/db_query_pre_auth/_assets/llm_riddles2.png -------------------------------------------------------------------------------- /db_query_pre_auth/_assets/llm_riddles3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/db_query_pre_auth/_assets/llm_riddles3.png -------------------------------------------------------------------------------- /db_query_pre_auth/main.py: -------------------------------------------------------------------------------- 1 | from dify_plugin import Plugin, DifyPluginEnv 2 | 3 | plugin = Plugin(DifyPluginEnv(MAX_REQUEST_TIMEOUT=120)) 4 | 5 | if __name__ == "__main__": 6 | plugin.run() 7 | -------------------------------------------------------------------------------- /db_query_pre_auth/manifest.yaml: -------------------------------------------------------------------------------- 1 | name: db_query_pre_auth 2 | author: junjiem 3 | icon: icon.svg 4 | created_at: "2025-01-13T09:55:01.5573645+08:00" 5 | label: 6 | en_US: Database Query (Pre-authorization) 7 | zh_Hans: 数据库查询(预授权) 8 | description: 9 | en_US: Database Query Utils (Pre-authorization). 10 | zh_Hans: 数据库查询工具(预授权)。 11 | meta: 12 | arch: 13 | - amd64 14 | - arm64 15 | runner: 16 | entrypoint: main 17 | language: python 18 | version: "3.12" 19 | version: 0.0.1 20 | plugins: 21 | tools: 22 | - provider/db_query.yaml 23 | resource: 24 | memory: 1048576 25 | permission: 26 | model: 27 | enabled: true 28 | llm: true 29 | tool: 30 | enabled: true 31 | tags: 32 | - search 33 | - utilities 34 | type: plugin 35 | version: 0.0.9 36 | privacy: PRIVACY.md 37 | verified: false -------------------------------------------------------------------------------- /db_query_pre_auth/provider/db_query.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from dify_plugin import ToolProvider 4 | 5 | from tools.db_util import DbUtil 6 | 7 | 8 | class DbQueryProvider(ToolProvider): 9 | def _validate_credentials(self, credentials: dict) -> None: 10 | db_type = credentials.get("db_type", "") 11 | if not db_type: 12 | raise ValueError("Please select the database type") 13 | db_host = credentials.get("db_host", "") 14 | if not db_host: 15 | raise ValueError("Please fill in the database host") 16 | db_username = credentials.get("db_username", "") 17 | if not db_username: 18 | raise ValueError("Please fill in the database username") 19 | db_password = credentials.get("db_password", "") 20 | if not db_password: 21 | raise ValueError("Please fill in the database password") 22 | db_port = credentials.get("db_port", "") 23 | if DbUtil.is_not_empty(db_port) and not db_port.isdigit(): 24 | raise ValueError("Database port can be empty or fill with integer value") 25 | db_name = credentials.get("db_name", "") 26 | db_properties = credentials.get("db_properties", "") 27 | 28 | try: 29 | with DbUtil(db_type=db_type, 30 | username=db_username, password=db_password, 31 | host=db_host, port=db_port, 32 | database=db_name, properties=db_properties) as db: 33 | db.run_query(db.test_sql()) 34 | except Exception as e: 35 | logging.exception("Database connection failed: %s", str(e)) 36 | raise RuntimeError(f"Error connection database: {e}") from e 37 | -------------------------------------------------------------------------------- /db_query_pre_auth/provider/db_query.yaml: -------------------------------------------------------------------------------- 1 | extra: 2 | python: 3 | source: provider/db_query.py 4 | identity: 5 | name: db_query_pre_auth 6 | author: junjiem 7 | label: 8 | en_US: Database Query (Pre-authorization) 9 | zh_Hans: 数据库查询(预授权) 10 | description: 11 | en_US: Database Query Utils (Pre-authorization). 12 | zh_Hans: 数据库查询工具(预授权)。 13 | icon: icon.svg 14 | tags: 15 | - search 16 | - utilities 17 | tools: 18 | - tools/sql_query.yaml 19 | credentials_for_provider: 20 | db_type: 21 | type: select 22 | required: true 23 | options: 24 | - value: mysql 25 | label: 26 | en_US: MySQL 27 | zh_Hans: MySQL 28 | - value: oracle 29 | label: 30 | en_US: Oracle 31 | zh_Hans: Oracle 32 | - value: oracle11g 33 | label: 34 | en_US: Oracle11g 35 | zh_Hans: Oracle11g 36 | - value: postgresql 37 | label: 38 | en_US: PostgreSQL 39 | zh_Hans: PostgreSQL 40 | - value: mssql 41 | label: 42 | en_US: Microsoft SQL Server 43 | zh_Hans: Microsoft SQL Server 44 | default: mysql 45 | label: 46 | en_US: Database type 47 | zh_Hans: 数据库类型 48 | help: 49 | en_US: Used for selecting the database type, mysql, oracle, oracle11g, postgresql or mssql. 50 | zh_Hans: 用于选择数据库类型,mysql、oracle、oracle11g、postgresql或mssql。 51 | db_host: 52 | type: text-input 53 | required: true 54 | default: localhost 55 | label: 56 | en_US: Database Host 57 | zh_Hans: 数据库地址 58 | human_description: 59 | en_US: Database hostname or IP address (Original string, not URL-encoded string). 60 | zh_Hans: 数据库的主机名或IP地址(原始字符串,非URL编码字符串)。 61 | db_port: 62 | type: text-input 63 | required: false 64 | label: 65 | en_US: Port 66 | zh_Hans: 端口 67 | human_description: 68 | en_US: Database port (Original string, not URL-encoded string). 69 | zh_Hans: 数据库的端口(原始字符串,非URL编码字符串)。 70 | db_username: 71 | type: text-input 72 | required: true 73 | label: 74 | en_US: Username 75 | zh_Hans: 用户名 76 | human_description: 77 | en_US: Database username (Original string, not URL-encoded string). 78 | zh_Hans: 数据库的用户名(原始字符串,非URL编码字符串)。 79 | db_password: 80 | type: secret-input 81 | required: true 82 | label: 83 | en_US: Password 84 | zh_Hans: 密码 85 | human_description: 86 | en_US: Database password (Original string, not URL-encoded string). 87 | zh_Hans: 数据库的密码(原始字符串,非URL编码字符串)。 88 | db_name: 89 | type: text-input 90 | required: false 91 | label: 92 | en_US: Database name 93 | zh_Hans: 库名 94 | human_description: 95 | en_US: Database name. 96 | zh_Hans: 数据库的名称。 97 | db_properties: 98 | type: text-input 99 | required: false 100 | label: 101 | en_US: Database properties 102 | zh_Hans: 数据库属性 103 | human_description: 104 | en_US: 'Database properties, for example: alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt' 105 | zh_Hans: '数据库属性,例如:alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt' 106 | help: 107 | en_US: 'Database properties, for example: alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt' 108 | zh_Hans: '数据库属性,例如:alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt' 109 | -------------------------------------------------------------------------------- /db_query_pre_auth/requirements.txt: -------------------------------------------------------------------------------- 1 | dify_plugin==0.2.1 2 | sqlparse==0.5.3 3 | tabulate==0.9.0 4 | pandas==2.2.3 5 | SQLAlchemy==2.0.35 6 | PyMySQL==1.1.1 7 | oracledb==2.2.1 8 | psycopg2-binary==2.9.10 9 | pymssql==2.3.4 -------------------------------------------------------------------------------- /db_query_pre_auth/tools/db_util.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import logging 3 | from typing import Optional 4 | from urllib import parse 5 | from uuid import UUID 6 | 7 | import oracledb 8 | import pandas as pd 9 | from pandas import Timestamp 10 | from sqlalchemy import create_engine 11 | 12 | 13 | class DbUtil: 14 | 15 | def __init__(self, db_type: str, 16 | username: str, password: str, 17 | host: str, port: Optional[str] = None, 18 | database: Optional[str] = None, 19 | properties: Optional[str] = None) -> None: 20 | self.db_type = db_type.lower() 21 | self.username = username 22 | self.password = password 23 | self.host = host 24 | self.port = port 25 | self.database = database 26 | self.properties = properties 27 | if self.db_type == 'oracle11g': 28 | # To change from the default python-oracledb Thin mode to Thick mode 29 | oracledb.init_oracle_client() 30 | self.engine = create_engine(self.get_url(), pool_size=100, pool_recycle=36) 31 | 32 | def __enter__(self): 33 | return self 34 | 35 | def __exit__(self, exc_type, exc_val, exc_tb): 36 | self.close() 37 | 38 | def get_driver_name(self): 39 | driver_name = self.db_type 40 | if self.db_type == 'mysql': 41 | driver_name = 'mysql+pymysql' 42 | elif self.db_type in {'oracle', 'oracle11g'}: 43 | driver_name = 'oracle+oracledb' 44 | elif self.db_type == 'postgresql': 45 | driver_name = 'postgresql+psycopg2' 46 | elif self.db_type == 'mssql': 47 | driver_name = 'mssql+pymssql' 48 | return driver_name 49 | 50 | def get_url(self): 51 | ''' 52 | Get url 53 | ''' 54 | parsed_username = parse.quote_plus(self.username) 55 | parsed_password = parse.quote_plus(self.password) 56 | parsed_host = parse.quote_plus(self.host) 57 | url = f"{self.get_driver_name()}://{parsed_username}:{parsed_password}@{parsed_host}" 58 | if self.is_not_empty(self.port): 59 | url = f"{url}:{str(self.port)}" 60 | url = f"{url}/" 61 | if self.is_not_empty(self.database): 62 | parsed_database = parse.quote_plus(self.database) 63 | url = f"{url}{parsed_database}" 64 | if self.is_not_empty(self.properties): 65 | url = f"{url}?{self.properties}" 66 | logging.info(f"url: {url}") 67 | return url 68 | 69 | def close(self): 70 | """Close all connections in the engine.""" 71 | self.engine.dispose() 72 | 73 | def run_query(self, query_sql: str) -> list[dict]: 74 | ''' 75 | Run SQL Query 76 | ''' 77 | query_sql = query_sql.replace('%', '%%') 78 | df = pd.read_sql_query(sql=query_sql, con=self.engine, parse_dates="%Y-%m-%d %H:%M:%S") 79 | df = df.fillna('') 80 | records = [] 81 | if len(df) > 0: 82 | records = df.to_dict(orient="records") 83 | for record in records: 84 | for key in record: 85 | if type(record[key]) is Timestamp: 86 | record[key] = record[key].strftime('%Y-%m-%d %H:%M:%S') 87 | if type(record[key]) is datetime.date: 88 | record[key] = record[key].strftime('%Y-%m-%d') 89 | if type(record[key]) is UUID: 90 | record[key] = str(record[key]) 91 | return records 92 | 93 | def test_sql(self): 94 | if self.db_type in {'oracle', 'oracle11g'}: 95 | return "SELECT 1 FROM DUAL" 96 | else: 97 | return "SELECT 1" 98 | 99 | @staticmethod 100 | def is_not_empty(s: str): 101 | return s is not None and s.strip() != "" 102 | -------------------------------------------------------------------------------- /db_query_pre_auth/tools/sql_query.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Any, Generator 3 | 4 | import sqlparse 5 | import tabulate 6 | from dify_plugin import Tool 7 | from dify_plugin.entities.tool import ToolInvokeMessage 8 | 9 | from tools.db_util import DbUtil 10 | 11 | 12 | class SqlQueryTool(Tool): 13 | def _invoke( 14 | self, tool_parameters: dict[str, Any] 15 | ) -> Generator[ToolInvokeMessage, None, None]: 16 | """ 17 | invoke tools 18 | """ 19 | db_type = self.runtime.credentials["db_type"] 20 | if not db_type: 21 | raise ValueError("Please select the database type") 22 | db_host = self.runtime.credentials["db_host"] 23 | if not db_host: 24 | raise ValueError("Please fill in the database host") 25 | db_port = self.runtime.credentials["db_port"] 26 | if DbUtil.is_not_empty(db_port) and not db_port.isdigit(): 27 | raise ValueError("Database port can be empty or fill with integer value") 28 | db_username = self.runtime.credentials["db_username"] 29 | if not db_username: 30 | raise ValueError("Please fill in the database username") 31 | db_password = self.runtime.credentials["db_password"] 32 | if not db_password: 33 | raise ValueError("Please fill in the database password") 34 | db_name = self.runtime.credentials["db_name"] 35 | db_properties = self.runtime.credentials["db_properties"] 36 | 37 | query_sql = tool_parameters.get("query_sql", "") 38 | if not query_sql: 39 | raise ValueError("Please fill in the query SQL, for example: select * from tbl_name") 40 | statements = sqlparse.parse(query_sql) 41 | if len(statements) != 1: 42 | raise ValueError("Only a single query SQL can be filled") 43 | statement = statements[0] 44 | if statement.get_type() != 'SELECT': 45 | raise ValueError("Query SQL can only be a single SELECT statement") 46 | 47 | output_format = tool_parameters.get("output_format", "markdown").lower() 48 | 49 | try: 50 | with DbUtil(db_type=db_type, 51 | username=db_username, password=db_password, 52 | host=db_host, port=db_port, 53 | database=db_name, properties=db_properties) as db: 54 | records = db.run_query(query_sql) 55 | except Exception as e: 56 | logging.exception("SQL query execution failed: %s", str(e)) 57 | raise RuntimeError(f"Error executing SQL: {e}") from e 58 | 59 | if output_format == "json": 60 | yield self.create_json_message({"records": records}) 61 | else: 62 | text = tabulate.tabulate(records, headers="keys", tablefmt="github", floatfmt="") 63 | yield self.create_text_message(text) -------------------------------------------------------------------------------- /db_query_pre_auth/tools/sql_query.yaml: -------------------------------------------------------------------------------- 1 | identity: 2 | name: sql_query_pre_auth 3 | author: junjiem 4 | label: 5 | en_US: SQL Query (Pre-authorization) 6 | zh_Hans: SQL查询(预授权) 7 | description: 8 | human: 9 | en_US: A tool for Database SQL query (Pre-authorization). 10 | zh_Hans: 数据库SQL查询工具(预授权)。 11 | llm: A tool for executing SQL query. Input should be a search query. 12 | extra: 13 | python: 14 | source: tools/sql_query.py 15 | parameters: 16 | - name: query_sql 17 | type: string 18 | required: true 19 | label: 20 | en_US: Query SQL 21 | zh_Hans: SQL查询语句 22 | human_description: 23 | en_US: 'SQL query statement, for example: select * from tbl_name' 24 | zh_Hans: SQL查询语句,例如:select * from tbl_name 25 | llm_description: 'SQL query statement, for example: select * from tbl_name' 26 | form: llm 27 | - name: output_format 28 | type: select 29 | required: true 30 | label: 31 | en_US: Output format 32 | zh_Hans: 输出格式 33 | human_description: 34 | en_US: Used for selecting the output format, markdown or json. 35 | zh_Hans: 用于选择输出格式,markdown或json。 36 | options: 37 | - value: markdown 38 | label: 39 | en_US: MARKDOWN 40 | zh_Hans: MARKDOWN 41 | - value: json 42 | label: 43 | en_US: JSON 44 | zh_Hans: JSON 45 | default: markdown 46 | form: form -------------------------------------------------------------------------------- /examples/完蛋!我被LLM包围了!(Dify1.0战绩排行版).yml: -------------------------------------------------------------------------------- 1 | app: 2 | description: '你可以和大模型斗智斗勇,通过你的聪明才智,让大模型遵循你的指令,“老老实实”地回答问题。 3 | 4 | 通过本游戏对大型语言模型产生更深刻的理解。一起来感受玩“坏”大模型的乐趣吧! 5 | 6 | 目前总共有五个章节:对话之趣、数字游戏、巅峰挑战、无人之境、登堂入室。每个章节有多个关卡,试试看你能通过几个章节,可能你连第一章都无法通过。' 7 | icon: video_game 8 | icon_background: '#FBE8FF' 9 | mode: advanced-chat 10 | name: 完蛋!我被LLM包围了!(战绩排行版) 11 | use_icon_as_answer_icon: true 12 | dependencies: 13 | - current_identifier: null 14 | type: marketplace 15 | value: 16 | marketplace_plugin_unique_identifier: junjiem/db_query_pre_auth:0.0.4@42ff23da07a60b140fbd3e2fbe1c282f8ecaf4cd7d4ba9a12874f02995d1fceb 17 | - current_identifier: null 18 | type: marketplace 19 | value: 20 | marketplace_plugin_unique_identifier: langgenius/tongyi:0.0.13@5a4c38f32498ab259b96833608a6a90c0b29671ecf14af272fbc7cde97e813d1 21 | kind: app 22 | version: 0.1.5 23 | workflow: 24 | conversation_variables: 25 | - description: 查看排行榜的时间戳(用于限制频繁查看排行榜消耗太多元数据库的计算资源) 26 | id: dcd81eb0-fbc0-4fce-88bc-a19ec6826f24 27 | name: show_ranking_ts 28 | selector: 29 | - conversation 30 | - show_ranking_ts 31 | value: 0 32 | value_type: number 33 | - description: 问题类型,0:普通问题,1:回文问题,2:互惠问题 34 | id: 025bffa8-5502-4639-b4c5-78e006217829 35 | name: question_type 36 | selector: 37 | - conversation 38 | - question_type 39 | value: 0 40 | value_type: number 41 | - description: 游戏状态(pending\start\running\restart\success) 42 | id: b380e461-c8e1-4d5f-be5f-439ddf84fc5a 43 | name: status 44 | selector: 45 | - conversation 46 | - status 47 | value: pending 48 | value_type: string 49 | - description: 问题序号 50 | id: 90d13fef-affa-4660-883f-00881df92f4c 51 | name: question_id 52 | selector: 53 | - conversation 54 | - question_id 55 | value: 0 56 | value_type: number 57 | - description: 游戏关卡 58 | id: d05c7f90-2e56-4296-9b91-4a481d44682e 59 | name: game_level 60 | selector: 61 | - conversation 62 | - game_level 63 | value: 0 64 | value_type: number 65 | environment_variables: 66 | - description: '' 67 | id: 81051d42-e73b-4d85-8d5d-59eed29f488c 68 | name: top_n 69 | selector: 70 | - env 71 | - top_n 72 | value: 50 73 | value_type: number 74 | features: 75 | file_upload: 76 | allowed_file_extensions: 77 | - .JPG 78 | - .JPEG 79 | - .PNG 80 | - .GIF 81 | - .WEBP 82 | - .SVG 83 | allowed_file_types: 84 | - image 85 | allowed_file_upload_methods: 86 | - local_file 87 | - remote_url 88 | enabled: false 89 | fileUploadConfig: 90 | audio_file_size_limit: 50 91 | batch_count_limit: 5 92 | file_size_limit: 15 93 | image_file_size_limit: 10 94 | video_file_size_limit: 100 95 | workflow_file_upload_limit: 10 96 | image: 97 | enabled: false 98 | number_limits: 3 99 | transfer_methods: 100 | - local_file 101 | - remote_url 102 | number_limits: 3 103 | opening_statement: '
完蛋!我被LLM包围了!
104 | 105 |
欢迎来玩 Dify 1.0 复刻版的 【完蛋!我被LLM包围了!】
106 | 107 |
你将通过本游戏对大型语言模型产生更深刻的理解。
108 | 109 |
在本游戏中,你需要构造一个提给大型语言模型的问题,使得它回复的答案符合要求。
' 110 | retriever_resource: 111 | enabled: false 112 | sensitive_word_avoidance: 113 | enabled: false 114 | speech_to_text: 115 | enabled: false 116 | suggested_questions: 117 | - '#开始' 118 | - '#战绩' 119 | suggested_questions_after_answer: 120 | enabled: false 121 | text_to_speech: 122 | enabled: false 123 | language: '' 124 | voice: '' 125 | graph: 126 | edges: 127 | - data: 128 | isInIteration: false 129 | sourceType: start 130 | targetType: if-else 131 | id: start-source-1735183356205-target 132 | selected: false 133 | source: start 134 | sourceHandle: source 135 | target: '1735183356205' 136 | targetHandle: target 137 | type: custom 138 | zIndex: 0 139 | - data: 140 | isInIteration: false 141 | sourceType: if-else 142 | targetType: assigner 143 | id: 1735183356205-true-1735183411877-target 144 | selected: false 145 | source: '1735183356205' 146 | sourceHandle: 'true' 147 | target: '1735183411877' 148 | targetHandle: target 149 | type: custom 150 | zIndex: 0 151 | - data: 152 | isInIteration: false 153 | sourceType: if-else 154 | targetType: assigner 155 | id: 1735183356205-faa33c03-dbfd-4fc5-b85d-11d3a519e455-1735183489389-target 156 | selected: false 157 | source: '1735183356205' 158 | sourceHandle: faa33c03-dbfd-4fc5-b85d-11d3a519e455 159 | target: '1735183489389' 160 | targetHandle: target 161 | type: custom 162 | zIndex: 0 163 | - data: 164 | isInIteration: false 165 | sourceType: if-else 166 | targetType: answer 167 | id: 1735197213688-true-1735197253934-target 168 | selected: false 169 | source: '1735197213688' 170 | sourceHandle: 'true' 171 | target: '1735197253934' 172 | targetHandle: target 173 | type: custom 174 | zIndex: 0 175 | - data: 176 | isInIteration: false 177 | sourceType: llm 178 | targetType: answer 179 | id: 1735195760496-source-1735198227518-target 180 | selected: false 181 | source: '1735195760496' 182 | sourceHandle: source 183 | target: '1735198227518' 184 | targetHandle: target 185 | type: custom 186 | zIndex: 0 187 | - data: 188 | isInIteration: false 189 | sourceType: llm 190 | targetType: answer 191 | id: 1735198964742-source-1735199090032-target 192 | selected: false 193 | source: '1735198964742' 194 | sourceHandle: source 195 | target: '1735199090032' 196 | targetHandle: target 197 | type: custom 198 | zIndex: 0 199 | - data: 200 | isInIteration: false 201 | sourceType: answer 202 | targetType: code 203 | id: 1735199090032-source-1735195133945-target 204 | selected: false 205 | source: '1735199090032' 206 | sourceHandle: source 207 | target: '1735195133945' 208 | targetHandle: target 209 | type: custom 210 | zIndex: 0 211 | - data: 212 | isInIteration: false 213 | sourceType: if-else 214 | targetType: code 215 | id: 1735198861231-true-1735199302294-target 216 | selected: false 217 | source: '1735198861231' 218 | sourceHandle: 'true' 219 | target: '1735199302294' 220 | targetHandle: target 221 | type: custom 222 | zIndex: 0 223 | - data: 224 | isInIteration: false 225 | sourceType: code 226 | targetType: llm 227 | id: 1735199302294-source-1735198964742-target 228 | selected: false 229 | source: '1735199302294' 230 | sourceHandle: source 231 | target: '1735198964742' 232 | targetHandle: target 233 | type: custom 234 | zIndex: 0 235 | - data: 236 | isInIteration: false 237 | sourceType: answer 238 | targetType: answer 239 | id: 1735197253934-source-1735192250756-target 240 | selected: false 241 | source: '1735197253934' 242 | sourceHandle: source 243 | target: '1735192250756' 244 | targetHandle: target 245 | type: custom 246 | zIndex: 0 247 | - data: 248 | isInIteration: false 249 | sourceType: answer 250 | targetType: answer 251 | id: 1735199727654-source-1735192250756-target 252 | selected: false 253 | source: '1735199727654' 254 | sourceHandle: source 255 | target: '1735192250756' 256 | targetHandle: target 257 | type: custom 258 | zIndex: 0 259 | - data: 260 | isInIteration: false 261 | sourceType: if-else 262 | targetType: if-else 263 | id: 1735201048213-true-1735197213688-target 264 | selected: false 265 | source: '1735201048213' 266 | sourceHandle: 'true' 267 | target: '1735197213688' 268 | targetHandle: target 269 | type: custom 270 | zIndex: 0 271 | - data: 272 | isInIteration: false 273 | sourceType: if-else 274 | targetType: answer 275 | id: 1735201048213-ef6ca92c-8822-4f18-b219-ae4bae7698a0-1735199809830-target 276 | selected: false 277 | source: '1735201048213' 278 | sourceHandle: ef6ca92c-8822-4f18-b219-ae4bae7698a0 279 | target: '1735199809830' 280 | targetHandle: target 281 | type: custom 282 | zIndex: 0 283 | - data: 284 | isInIteration: false 285 | sourceType: if-else 286 | targetType: answer 287 | id: 1735201048213-false-1735192250756-target 288 | selected: false 289 | source: '1735201048213' 290 | sourceHandle: 'false' 291 | target: '1735192250756' 292 | targetHandle: target 293 | type: custom 294 | zIndex: 0 295 | - data: 296 | isInIteration: false 297 | sourceType: code 298 | targetType: assigner 299 | id: 1735195133945-source-1735201350381-target 300 | selected: false 301 | source: '1735195133945' 302 | sourceHandle: source 303 | target: '1735201350381' 304 | targetHandle: target 305 | type: custom 306 | zIndex: 0 307 | - data: 308 | isInIteration: false 309 | sourceType: assigner 310 | targetType: if-else 311 | id: 1735201350381-source-1735201048213-target 312 | selected: false 313 | source: '1735201350381' 314 | sourceHandle: source 315 | target: '1735201048213' 316 | targetHandle: target 317 | type: custom 318 | zIndex: 0 319 | - data: 320 | isInIteration: false 321 | sourceType: if-else 322 | targetType: answer 323 | id: 1735197213688-false-1735199727654-target 324 | selected: false 325 | source: '1735197213688' 326 | sourceHandle: 'false' 327 | target: '1735199727654' 328 | targetHandle: target 329 | type: custom 330 | zIndex: 0 331 | - data: 332 | isInIteration: false 333 | sourceType: if-else 334 | targetType: answer 335 | id: 1735197213688-25972869-6d46-4e39-9a88-e019825edf57-1735192250756-target 336 | selected: false 337 | source: '1735197213688' 338 | sourceHandle: 25972869-6d46-4e39-9a88-e019825edf57 339 | target: '1735192250756' 340 | targetHandle: target 341 | type: custom 342 | zIndex: 0 343 | - data: 344 | isInIteration: false 345 | sourceType: answer 346 | targetType: if-else 347 | id: 1735198227518-source-1735213477600-target 348 | source: '1735198227518' 349 | sourceHandle: source 350 | target: '1735213477600' 351 | targetHandle: target 352 | type: custom 353 | zIndex: 0 354 | - data: 355 | isInIteration: false 356 | sourceType: if-else 357 | targetType: code 358 | id: 1735213477600-false-1735195133945-target 359 | source: '1735213477600' 360 | sourceHandle: 'false' 361 | target: '1735195133945' 362 | targetHandle: target 363 | type: custom 364 | zIndex: 0 365 | - data: 366 | isInIteration: false 367 | sourceType: if-else 368 | targetType: llm 369 | id: 1735213477600-true-1735213513838-target 370 | source: '1735213477600' 371 | sourceHandle: 'true' 372 | target: '1735213513838' 373 | targetHandle: target 374 | type: custom 375 | zIndex: 0 376 | - data: 377 | isInIteration: false 378 | sourceType: llm 379 | targetType: answer 380 | id: 1735213513838-source-1735213525623-target 381 | source: '1735213513838' 382 | sourceHandle: source 383 | target: '1735213525623' 384 | targetHandle: target 385 | type: custom 386 | zIndex: 0 387 | - data: 388 | isInIteration: false 389 | sourceType: answer 390 | targetType: code 391 | id: 1735213525623-source-1735195133945-target 392 | source: '1735213525623' 393 | sourceHandle: source 394 | target: '1735195133945' 395 | targetHandle: target 396 | type: custom 397 | zIndex: 0 398 | - data: 399 | isInIteration: false 400 | sourceType: if-else 401 | targetType: answer 402 | id: 1735197213688-9d4ecd94-c9d7-4f53-a44e-e5b3a62827a6-1735264591657-target 403 | source: '1735197213688' 404 | sourceHandle: 9d4ecd94-c9d7-4f53-a44e-e5b3a62827a6 405 | target: '1735264591657' 406 | targetHandle: target 407 | type: custom 408 | zIndex: 0 409 | - data: 410 | isInIteration: false 411 | sourceType: answer 412 | targetType: answer 413 | id: 1735264591657-source-1735192250756-target 414 | source: '1735264591657' 415 | sourceHandle: source 416 | target: '1735192250756' 417 | targetHandle: target 418 | type: custom 419 | zIndex: 0 420 | - data: 421 | isInIteration: false 422 | sourceType: assigner 423 | targetType: template-transform 424 | id: 1735183411877-source-1736306672174-target 425 | source: '1735183411877' 426 | sourceHandle: source 427 | target: '1736306672174' 428 | targetHandle: target 429 | type: custom 430 | zIndex: 0 431 | - data: 432 | isInIteration: false 433 | sourceType: template-transform 434 | targetType: variable-aggregator 435 | id: 1736306672174-source-1736306267206-target 436 | source: '1736306672174' 437 | sourceHandle: source 438 | target: '1736306267206' 439 | targetHandle: target 440 | type: custom 441 | zIndex: 0 442 | - data: 443 | isInIteration: false 444 | sourceType: assigner 445 | targetType: template-transform 446 | id: 1735183489389-source-1736306689285-target 447 | source: '1735183489389' 448 | sourceHandle: source 449 | target: '1736306689285' 450 | targetHandle: target 451 | type: custom 452 | zIndex: 0 453 | - data: 454 | isInIteration: false 455 | sourceType: template-transform 456 | targetType: variable-aggregator 457 | id: 1736306689285-source-1736306267206-target 458 | source: '1736306689285' 459 | sourceHandle: source 460 | target: '1736306267206' 461 | targetHandle: target 462 | type: custom 463 | zIndex: 0 464 | - data: 465 | isInIteration: false 466 | sourceType: if-else 467 | targetType: template-transform 468 | id: 1735183356205-false-1736306700014-target 469 | source: '1735183356205' 470 | sourceHandle: 'false' 471 | target: '1736306700014' 472 | targetHandle: target 473 | type: custom 474 | zIndex: 0 475 | - data: 476 | isInIteration: false 477 | sourceType: template-transform 478 | targetType: variable-aggregator 479 | id: 1736306700014-source-1736306267206-target 480 | source: '1736306700014' 481 | sourceHandle: source 482 | target: '1736306267206' 483 | targetHandle: target 484 | type: custom 485 | zIndex: 0 486 | - data: 487 | isInIteration: false 488 | sourceType: variable-aggregator 489 | targetType: if-else 490 | id: 1736306267206-source-1735183233430-target 491 | source: '1736306267206' 492 | sourceHandle: source 493 | target: '1735183233430' 494 | targetHandle: target 495 | type: custom 496 | zIndex: 0 497 | - data: 498 | isInIteration: false 499 | sourceType: if-else 500 | targetType: answer 501 | id: 1735183233430-true-1735183663452-target 502 | source: '1735183233430' 503 | sourceHandle: 'true' 504 | target: '1735183663452' 505 | targetHandle: target 506 | type: custom 507 | zIndex: 0 508 | - data: 509 | isInIteration: false 510 | sourceType: if-else 511 | targetType: answer 512 | id: 1735183233430-9ca767d9-54ea-407e-a0a3-a8dc3e55e118-1735200055854-target 513 | source: '1735183233430' 514 | sourceHandle: 9ca767d9-54ea-407e-a0a3-a8dc3e55e118 515 | target: '1735200055854' 516 | targetHandle: target 517 | type: custom 518 | zIndex: 0 519 | - data: 520 | isInIteration: false 521 | sourceType: if-else 522 | targetType: llm 523 | id: 1735183233430-false-1735195760496-target 524 | source: '1735183233430' 525 | sourceHandle: 'false' 526 | target: '1735195760496' 527 | targetHandle: target 528 | type: custom 529 | zIndex: 0 530 | - data: 531 | isInIteration: false 532 | sourceType: if-else 533 | targetType: if-else 534 | id: 1735183233430-false-1735198861231-target 535 | source: '1735183233430' 536 | sourceHandle: 'false' 537 | target: '1735198861231' 538 | targetHandle: target 539 | type: custom 540 | zIndex: 0 541 | - data: 542 | isInIteration: false 543 | sourceType: if-else 544 | targetType: code 545 | id: 1735183233430-c2d2ac2c-c148-48ce-b71d-822359b17e3d-1735195133945-target 546 | source: '1735183233430' 547 | sourceHandle: c2d2ac2c-c148-48ce-b71d-822359b17e3d 548 | target: '1735195133945' 549 | targetHandle: target 550 | type: custom 551 | zIndex: 0 552 | - data: 553 | isInIteration: false 554 | sourceType: if-else 555 | targetType: code 556 | id: 1735183233430-4e65ee00-65e5-4b72-bfb7-f353ce3860e4-1735195133945-target 557 | source: '1735183233430' 558 | sourceHandle: 4e65ee00-65e5-4b72-bfb7-f353ce3860e4 559 | target: '1735195133945' 560 | targetHandle: target 561 | type: custom 562 | zIndex: 0 563 | - data: 564 | isInIteration: false 565 | sourceType: if-else 566 | targetType: code 567 | id: 1735183356205-8fa9b63d-81e8-4fd3-b703-095b704d3162-1741569725917-target 568 | source: '1735183356205' 569 | sourceHandle: 8fa9b63d-81e8-4fd3-b703-095b704d3162 570 | target: '1741569725917' 571 | targetHandle: target 572 | type: custom 573 | zIndex: 0 574 | - data: 575 | isInIteration: false 576 | sourceType: code 577 | targetType: if-else 578 | id: 1741569725917-source-1741569780133-target 579 | source: '1741569725917' 580 | sourceHandle: source 581 | target: '1741569780133' 582 | targetHandle: target 583 | type: custom 584 | zIndex: 0 585 | - data: 586 | isInIteration: false 587 | sourceType: if-else 588 | targetType: assigner 589 | id: 1741569780133-true-1741569807700-target 590 | source: '1741569780133' 591 | sourceHandle: 'true' 592 | target: '1741569807700' 593 | targetHandle: target 594 | type: custom 595 | zIndex: 0 596 | - data: 597 | isInIteration: false 598 | sourceType: assigner 599 | targetType: tool 600 | id: 1741569807700-source-1741171122348-target 601 | source: '1741569807700' 602 | sourceHandle: source 603 | target: '1741171122348' 604 | targetHandle: target 605 | type: custom 606 | zIndex: 0 607 | - data: 608 | isInIteration: false 609 | sourceType: if-else 610 | targetType: answer 611 | id: 1741569780133-false-1741569868709-target 612 | source: '1741569780133' 613 | sourceHandle: 'false' 614 | target: '1741569868709' 615 | targetHandle: target 616 | type: custom 617 | zIndex: 0 618 | - data: 619 | isInIteration: false 620 | sourceType: tool 621 | targetType: if-else 622 | id: 1741171122348-source-1741569900565-target 623 | source: '1741171122348' 624 | sourceHandle: source 625 | target: '1741569900565' 626 | targetHandle: target 627 | type: custom 628 | zIndex: 0 629 | - data: 630 | isInIteration: false 631 | sourceType: if-else 632 | targetType: answer 633 | id: 1741569900565-true-1735447955439-target 634 | source: '1741569900565' 635 | sourceHandle: 'true' 636 | target: '1735447955439' 637 | targetHandle: target 638 | type: custom 639 | zIndex: 0 640 | - data: 641 | isInIteration: false 642 | sourceType: if-else 643 | targetType: answer 644 | id: 1741569900565-false-1741569961830-target 645 | source: '1741569900565' 646 | sourceHandle: 'false' 647 | target: '1741569961830' 648 | targetHandle: target 649 | type: custom 650 | zIndex: 0 651 | nodes: 652 | - data: 653 | selected: false 654 | title: START 655 | type: start 656 | variables: [] 657 | height: 54 658 | id: start 659 | position: 660 | x: 30 661 | y: 311 662 | positionAbsolute: 663 | x: 30 664 | y: 311 665 | selected: false 666 | type: custom 667 | width: 244 668 | - data: 669 | cases: 670 | - case_id: 'true' 671 | conditions: 672 | - comparison_operator: is 673 | id: f449f8ad-94c3-4813-baeb-7ec9542192eb 674 | value: pending 675 | varType: string 676 | variable_selector: 677 | - '1736306267206' 678 | - output 679 | id: 'true' 680 | logical_operator: and 681 | - case_id: 9ca767d9-54ea-407e-a0a3-a8dc3e55e118 682 | conditions: 683 | - comparison_operator: is 684 | id: 2ecc2ad2-1566-4aef-90f9-2359c0230e3f 685 | value: success 686 | varType: string 687 | variable_selector: 688 | - '1736306267206' 689 | - output 690 | id: 9ca767d9-54ea-407e-a0a3-a8dc3e55e118 691 | logical_operator: and 692 | - case_id: c2d2ac2c-c148-48ce-b71d-822359b17e3d 693 | conditions: 694 | - comparison_operator: is 695 | id: 896357e6-2180-43dd-ab1c-d018c6987f0a 696 | value: start 697 | varType: string 698 | variable_selector: 699 | - '1736306267206' 700 | - output 701 | id: c2d2ac2c-c148-48ce-b71d-822359b17e3d 702 | logical_operator: and 703 | - case_id: 4e65ee00-65e5-4b72-bfb7-f353ce3860e4 704 | conditions: 705 | - comparison_operator: is 706 | id: 596ec20f-8dfb-44cf-9f32-7c3e233484b9 707 | value: restart 708 | varType: string 709 | variable_selector: 710 | - '1736306267206' 711 | - output 712 | id: 4e65ee00-65e5-4b72-bfb7-f353ce3860e4 713 | logical_operator: and 714 | desc: '' 715 | selected: false 716 | title: 状态判断 717 | type: if-else 718 | height: 270 719 | id: '1735183233430' 720 | position: 721 | x: 1550 722 | y: 439 723 | positionAbsolute: 724 | x: 1550 725 | y: 439 726 | selected: false 727 | sourcePosition: right 728 | targetPosition: left 729 | type: custom 730 | width: 244 731 | - data: 732 | cases: 733 | - case_id: 'true' 734 | conditions: 735 | - comparison_operator: is 736 | id: f202373c-1552-45b4-bc8f-f8999835ac9d 737 | value: '#开始' 738 | varType: string 739 | variable_selector: 740 | - sys 741 | - query 742 | id: 'true' 743 | logical_operator: and 744 | - case_id: faa33c03-dbfd-4fc5-b85d-11d3a519e455 745 | conditions: 746 | - comparison_operator: is 747 | id: 2b704d2e-71fd-470e-8a87-b68fb2ee0a61 748 | value: '#重启' 749 | varType: string 750 | variable_selector: 751 | - sys 752 | - query 753 | id: faa33c03-dbfd-4fc5-b85d-11d3a519e455 754 | logical_operator: and 755 | - case_id: 8fa9b63d-81e8-4fd3-b703-095b704d3162 756 | conditions: 757 | - comparison_operator: is 758 | id: f71d027c-52ed-4e3a-94de-1aca34c60c3c 759 | value: '#战绩' 760 | varType: string 761 | variable_selector: 762 | - sys 763 | - query 764 | id: 8fa9b63d-81e8-4fd3-b703-095b704d3162 765 | logical_operator: and 766 | desc: '' 767 | selected: false 768 | title: 输入判断 769 | type: if-else 770 | height: 222 771 | id: '1735183356205' 772 | position: 773 | x: 334 774 | y: 311 775 | positionAbsolute: 776 | x: 334 777 | y: 311 778 | selected: false 779 | sourcePosition: right 780 | targetPosition: left 781 | type: custom 782 | width: 244 783 | - data: 784 | desc: '' 785 | items: 786 | - input_type: constant 787 | operation: set 788 | value: start 789 | variable_selector: 790 | - conversation 791 | - status 792 | write_mode: over-write 793 | selected: false 794 | title: 启动状态 795 | type: assigner 796 | version: '2' 797 | height: 88 798 | id: '1735183411877' 799 | position: 800 | x: 638 801 | y: 311 802 | positionAbsolute: 803 | x: 638 804 | y: 311 805 | selected: false 806 | sourcePosition: right 807 | targetPosition: left 808 | type: custom 809 | width: 244 810 | - data: 811 | desc: '' 812 | items: 813 | - input_type: constant 814 | operation: set 815 | value: restart 816 | variable_selector: 817 | - conversation 818 | - status 819 | write_mode: over-write 820 | selected: false 821 | title: 重启状态 822 | type: assigner 823 | version: '2' 824 | height: 88 825 | id: '1735183489389' 826 | position: 827 | x: 638 828 | y: 439 829 | positionAbsolute: 830 | x: 638 831 | y: 439 832 | selected: false 833 | sourcePosition: right 834 | targetPosition: left 835 | type: custom 836 | width: 244 837 | - data: 838 | answer: '
839 | 840 | 还未开始游戏,请点击 开始游戏! 842 | 843 |
' 844 | desc: '' 845 | selected: false 846 | title: 提示点击开始 847 | type: answer 848 | variables: [] 849 | height: 180 850 | id: '1735183663452' 851 | position: 852 | x: 1854 853 | y: 439 854 | positionAbsolute: 855 | x: 1854 856 | y: 439 857 | selected: false 858 | sourcePosition: right 859 | targetPosition: left 860 | type: custom 861 | width: 244 862 | - data: 863 | answer: '你可以点击 864 | ,查看当前排行榜! 865 | 866 | 867 | 868 | ## {{#1735195133945.game_level_name#}} 869 | 870 | ### {{#1735195133945.question_title#}} 871 | 872 | **当前挑战:** {{#1735195133945.question_description#}}' 873 | desc: '' 874 | selected: false 875 | title: 当前挑战 876 | type: answer 877 | variables: [] 878 | height: 221 879 | id: '1735192250756' 880 | position: 881 | x: 4894 882 | y: 1255.5 883 | positionAbsolute: 884 | x: 4894 885 | y: 1255.5 886 | selected: false 887 | sourcePosition: right 888 | targetPosition: left 889 | type: custom 890 | width: 244 891 | - data: 892 | code: "import re\nimport math\nimport unicodedata\n\n\ndef main(status: str,\n\ 893 | \ game_level: int, question_id: int,\n query: str, text:\ 894 | \ str,\n reversed_text: str, reciprocal_text: str\n ) ->\ 895 | \ dict:\n challenge1 = {\n 'name':\n '【第一章】 对话之趣',\n\ 896 | \ 'problems': [\n {\n 'title': '第1题 初来乍到',\n\ 897 | \ 'description': '请你构造一个问题使模型的回答是一字不差的“1+1=3”(不需要引号)。',\n\ 898 | \ 'validator': lambda query, response: response.strip() ==\ 899 | \ '1+1=3'\n },\n {\n 'title':\n \ 900 | \ '第2题 小试牛刀',\n 'description':\n \ 901 | \ '请输入三个字以内的问题,使模型的回答在30个字以上。',\n 'validator':\n\ 902 | \ lambda query, response: len(query) <= 3 and len(response)\ 903 | \ > 30\n },\n {\n 'title':\n \ 904 | \ '第3题 短说长话',\n 'description':\n \ 905 | \ '请输入一个字的问题,使模型的回答在100个字以上。',\n 'validator':\n \ 906 | \ lambda query, response: len(query) == 1 and len(response)\ 907 | \ > 100\n },\n {\n 'title':\n \ 908 | \ '第4题 短说短话',\n 'description':\n \ 909 | \ '请输入一个字的问题,使模型的回答在20个字以内。',\n 'validator':\n \ 910 | \ lambda query, response: len(query) == 1 and len(response)\ 911 | \ <= 20\n },\n {\n 'title':\n \ 912 | \ '第5题 回文不变',\n 'description':\n \ 913 | \ '请输入一个本身不是回文串的问题,使无论正着问还是倒着问,模型的回答是一样的。',\n 'question_type':\ 914 | \ 1, # 回文问题\n 'validator':\n lambda query,\ 915 | \ response, new_response: validate_palindrome_invariance(\n \ 916 | \ query, response, new_response)\n },\n \ 917 | \ {\n 'title':\n '第6题 无中生狗',\n \ 918 | \ 'description':\n '请提一个不包含“狗”这个字的问题,但是回答中至少出现3次“狗”这个字。',\n\ 919 | \ 'validator':\n lambda query, response:\ 920 | \ '狗' not in query and response.count('狗') \\\n \ 921 | \ >= 3\n },\n {\n \ 922 | \ 'title':\n '第7题 事半功倍',\n 'description':\n\ 923 | \ '请输入一个本身不是回文串的问题,使模型回答的长度刚好是问题长度的两倍。',\n \ 924 | \ 'validator':\n lambda query, response: query !=\ 925 | \ query[::-1] and len(response.strip()) == 2 * len(query)\n },\n\ 926 | \ {\n 'title':\n '第8题 产生疑惑',\n\ 927 | \ 'description':\n '请提出一个问题,不包含“不”“知”“道”三个字符,但要使模型的回答是一字不差的“不知道”',\n\ 928 | \ 'validator':\n lambda query, response:\n\ 929 | \ all(char not in query for char in ['不', '知', '道'])\ 930 | \ and response.strip() == '不知道'\n },\n {\n \ 931 | \ 'title':\n '第9题 无中生有',\n 'description':\n\ 932 | \ '请提出一个问题,使模型只回复“永动机是真实存在的。”',\n 'validator':\n\ 933 | \ lambda query, response: response.strip() == '永动机是真实存在的。'\n\ 934 | \ },\n ]\n }\n\n challenge2 = {\n 'name':\n\ 935 | \ '【第二章】 数字游戏',\n 'problems': [\n {\n \ 936 | \ 'title':\n '第1题 质数长度',\n 'description':\n\ 937 | \ '你需要提出一个字数是质数的问题,使回答的长度刚好是它的下一个质数。',\n \ 938 | \ 'validator':\n lambda query, response:\n \ 939 | \ (is_prime(len(query)) and len(response) == next_prime(len(query)))\n\ 940 | \ },\n {\n 'title':\n \ 941 | \ '第2题 越说越大',\n 'description':\n \ 942 | \ '请输入一个大于一的正整数作为问题,使回答里包含至少比它大一千的数。',\n 'validator':\n\ 943 | \ lambda query, response:\n (query.isdigit()\ 944 | \ and int(query) > 1 and any(\n int(num) > int(query)\ 945 | \ + 1000\n for num in re.findall(r'\\d+', response)))\n\ 946 | \ },\n {\n 'title':\n \ 947 | \ '第3题 越说越小',\n 'description':\n \ 948 | \ '请输入一个大于一的正整数作为问题,使回答里包含至少10个不同的至少比它小一千的自然数。',\n 'validator':\n\ 949 | \ lambda query, response:\n (query.isdigit()\ 950 | \ and int(query) > 1 and (len({\n int(num)\n \ 951 | \ for num in re.findall(r'\\d+', response)\n \ 952 | \ if int(num) < int(query) - 1000\n })\ 953 | \ >= 10))\n },\n {\n 'title':\n \ 954 | \ '第4题 警世名言',\n 'description':\n \ 955 | \ '请在不提及1,4,5这三个字符的情况下让模型回答114514,不要有多余的字符。',\n \ 956 | \ 'validator':\n lambda query, response: response.strip()\ 957 | \ == '114514' and all(\n c not in query for c in\ 958 | \ ['1', '4', '5'])\n },\n {\n 'title':\n\ 959 | \ '第5题 开平方数',\n 'description':\n \ 960 | \ '请输入一个大于一的平方数作为问题,使回答里包含它的平方根对应的数。',\n 'validator':\n\ 961 | \ lambda query, response: (query.isdigit() and\n \ 962 | \ 1 < int(query) == math.isqrt(int(query))\ 963 | \ ** 2 and\n str(int(math.sqrt(int(query))))\ 964 | \ in response)\n },\n {\n 'title':\n\ 965 | \ '第6题 得寸进狗',\n 'description':\n \ 966 | \ '请提一个不包含“狗”这个字的10个字以内的问题,使回答中“狗”这个字出现的次数至少是问题字数的2倍。',\n\ 967 | \ 'validator':\n lambda query, response:\ 968 | \ (\n '狗' not in query and len(query) <= 10 and\n\ 969 | \ response.count('狗') >= 2 * len(query))\n \ 970 | \ },\n {\n 'title':\n \ 971 | \ '第7题 暗藏玄机',\n 'description':\n '请输入一个大于一的正整数作为问题,使回答包含一个两位数,其十位上的数字比个位上的数字小5。',\n\ 972 | \ 'validator':\n lambda query, response:\n\ 973 | \ (query.isdigit() and int(query) > 1 and (len({\n \ 974 | \ int(num)\n for num in re.findall(r'\\\ 975 | d+', response)\n if len(num) == 2 and int(num[1])\ 976 | \ - int(num[0]) == 5\n }) >= 1))\n },\n \ 977 | \ {\n 'title':\n '第8题 点兵点将',\n\ 978 | \ 'description':\n '请输入十个字以内的问题,问题中不能包含0到9的数字以及零到十的汉字,使模型的回答中包含数字0到9。',\n\ 979 | \ 'validator':\n lambda query, response:\n\ 980 | \ (len(query) <= 10 and not re.search(r'\\d', query)\ 981 | \ and all(\n char not in query for char in '零一二三四五六七八九十')\ 982 | \ and len(\n set(re.findall(r'\\d', response))) ==\ 983 | \ 10)\n },\n {\n 'title': '第9题 微言大义',\n\ 984 | \ 'description': '请输入10个字以内的问题,使得模型的回答是一个超过一亿的素数',\n \ 985 | \ 'validator': level2_validate_9\n }\n ]\n\ 986 | \ }\n\n challenge3 = {\n 'name':\n '【第三章】 巅峰挑战',\n\ 987 | \ 'problems': [\n {\n 'title':\n \ 988 | \ '第1题 自说自话',\n 'description':\n \ 989 | \ '请做出一个至少10个字的提问,使回答和提问完全一样。',\n 'validator':\n\ 990 | \ lambda query, response: query == response and len(query)\ 991 | \ >= 10\n },\n {\n 'title':\n \ 992 | \ '第2题 话自说自',\n 'description':\n \ 993 | \ '请做出一个至少10个字的提问,使回答是提问的逆序。',\n 'validator':\n\ 994 | \ lambda query, response:\n (query[::-1]\ 995 | \ == response and len(query) >= 10)\n },\n {\n \ 996 | \ 'title':\n '第3题 就是敢说',\n \ 997 | \ 'description':\n '请在不提及1,3这两个字符的情况下让模型回答1+1=3,不要有多余的字符。',\n\ 998 | \ 'validator':\n lambda query, response:\ 999 | \ response.strip() == '1+1=3' and all(\n c not in\ 1000 | \ query for c in ['1', '3'])\n },\n {\n \ 1001 | \ 'title':\n '第4题 回文协变',\n 'description':\n\ 1002 | \ '请输入一个本身不是回文串的问题,使正着问和倒着问的时候,模型的回答本身不回文且也是逆序的关系。',\n\ 1003 | \ 'question_type': 1, # 回文问题\n 'validator':\n\ 1004 | \ lambda query, response, new_response:\n \ 1005 | \ validate_palindrome_inverse(query, response, new_response)\n \ 1006 | \ },\n {\n 'title':\n \ 1007 | \ '第5题 答非所问',\n 'description':\n \ 1008 | \ '请输入一个以“为什么”开头,以“?”(中文问号)结尾的不包含数字的问题,使回答中只包含数字。',\n 'validator':\n\ 1009 | \ lambda query, response: '为什么' == query[:3] and '?'\ 1010 | \ == query[-1]\n and\n \ 1011 | \ len(re.findall(r'\\d+', query)) ==\ 1012 | \ 0 and response.isdigit()\n },\n {\n \ 1013 | \ 'title':\n '第6题 与狗交谈',\n 'description':\n\ 1014 | \ '请在不提及“狗”、“汪”、“dog”的情况下,让模型的回答是一字不差的“汪~”',\n \ 1015 | \ 'validator':\n lambda query, response:\n \ 1016 | \ (all(char not in query\n for\ 1017 | \ char in ['狗', '汪', 'dog']) and response.strip() == '汪~')\n \ 1018 | \ },\n {\n 'title':\n '第7题\ 1019 | \ 俊杰 (\U0001F436)',\n 'description':\n \ 1020 | \ '请提一个不包含任何中文字符、换行或空格的问题,使模型正好回答\"俊杰\"。',\n 'validator':\n\ 1021 | \ lambda query, response: not any(\n \ 1022 | \ 'CJK' in unicodedata.name(char, '') or char in '\\t\\n '\n \ 1023 | \ for char in query) and (response.strip() == '俊杰')\n\ 1024 | \ },\n ]\n }\n\n challenge4 = {\n 'name':\n\ 1025 | \ '【第四章】 无人之境',\n 'problems': [\n {\n \ 1026 | \ 'title':\n '第1题 循环往复',\n 'description':\n\ 1027 | \ '请构造一个问题A,它的回复是不同于A的B,然后以B提问能再次得到A.',\n \ 1028 | \ 'question_type': 2, # 互惠问题\n 'validator':\n \ 1029 | \ lambda query, response, new_response:\n \ 1030 | \ validate_reciprocal_question(query, response, new_response)\n \ 1031 | \ },\n {\n 'title':\n \ 1032 | \ '第2题 惜字如金',\n 'description':\n '请输入一个字的问题,使模型的回答在16个字以内。',\n\ 1033 | \ 'validator':\n lambda query, response:\ 1034 | \ len(query) == 1 and len(response) <= 16\n },\n {\n\ 1035 | \ 'title':\n '第3题 自然之密',\n \ 1036 | \ 'description':\n '请输入一个大于一的正整数作为问题,使回答里包含和它刚好相差1的数。',\n\ 1037 | \ 'validator':\n lambda query, response:\n\ 1038 | \ (query.isdigit() and int(query) > 1 and any(\n \ 1039 | \ abs(int(num) - int(query)) == 1\n \ 1040 | \ for num in re.findall(r'\\d+', response)))\n },\n \ 1041 | \ {\n 'title':\n '第4题 八仙过海',\n\ 1042 | \ 'description':\n '请提出8个字的问题,不包含“八”或“8”或“eight”,使得模型的回答也是8个字。',\n\ 1043 | \ 'validator':\n lambda query, response:\n\ 1044 | \ (all(char not in query\n for\ 1045 | \ char in ['八', '8', 'eight']) and len(response) == 8)\n },\n\ 1046 | \ ]\n }\n\n challenge5 = {\n 'name':\n '【第五章】\ 1047 | \ 登堂入室',\n 'problems': [\n {\n 'title':\n\ 1048 | \ '第1题 盛夏少年',\n 'description':\n \ 1049 | \ '模型的回答应该包含“盛夏”、“蝉鸣”、“少年”、“橘子味汽水”这几个词,同时输入的问题不能包含其中任一个词。',\n\ 1050 | \ 'validator':\n lambda query, response:\ 1051 | \ all(\n check_word_in_sentence(['盛夏', '蝉鸣', '少年',\ 1052 | \ '橘子味汽水'], response))\n and\ 1053 | \ not any(\n check_word_in_sentence(['盛夏', '蝉鸣',\ 1054 | \ '少年', '橘子味汽水'], query))\n },\n {\n \ 1055 | \ 'title':\n '第2题 蝉鸣日出',\n 'description':\n\ 1056 | \ '模型的回答应该包含“盛夏”、“蝉鸣”、“少年”、“橘子味汽水”、“日出”这几个词,同时输入的问题不能包含其中任一个字。',\n\ 1057 | \ 'validator':\n lambda query, response:\ 1058 | \ all(\n check_word_in_sentence(\n \ 1059 | \ ['盛夏', '蝉鸣', '少年', '橘子味汽水', '日出'], response)) and not any(\n\ 1060 | \ check_word_in_sentence([\n \ 1061 | \ '盛', '夏', '蝉', '鸣', '少', '年', '橘', '子', '味', '汽',\n \ 1062 | \ '水', '日', '出'\n ], query))\n \ 1063 | \ },\n ]\n }\n\n challenges = [\n challenge1,\n\ 1064 | \ challenge2,\n challenge3,\n challenge4,\n \ 1065 | \ challenge5,\n ]\n\n if status in ['start', 'restart']:\n \ 1066 | \ game_level = 0\n question_id = 0\n\n challenge = challenges[game_level]\n\ 1067 | \ problem = challenge['problems'][question_id]\n\n question_type =\ 1068 | \ 0\n if 'question_type' in problem:\n question_type = problem['question_type']\n\ 1069 | \n valid_pass = -1\n if status == 'running':\n valid_pass =\ 1070 | \ 0\n validate_fn = problem['validator']\n if question_type\ 1071 | \ == 1:\n valid_result = validate_fn(query=query, response=text,\ 1072 | \ new_response=reversed_text)\n elif question_type == 2:\n \ 1073 | \ valid_result = validate_fn(query=query, response=text, new_response=reciprocal_text)\n\ 1074 | \ else:\n valid_result = validate_fn(query=query, response=text)\n\ 1075 | \ if valid_result:\n valid_pass = 1\n if question_id\ 1076 | \ < len(challenge['problems']) - 1:\n question_id += 1\n\ 1077 | \ else:\n if game_level < len(challenges) - 1:\n\ 1078 | \ valid_pass = 2\n question_id = 0\n\ 1079 | \ game_level += 1\n else:\n \ 1080 | \ status = 'success'\n\n if status in ['start', 'restart']:\n\ 1081 | \ status = 'running'\n\n challenge = challenges[game_level]\n\ 1082 | \ problem = challenge['problems'][question_id]\n\n question_type =\ 1083 | \ 0\n if 'question_type' in problem:\n question_type = problem['question_type']\n\ 1084 | \n return {\n \"game_level\": game_level,\n \"question_id\"\ 1085 | : question_id,\n \"status\": status,\n \"valid_pass\": valid_pass,\n\ 1086 | \ \"question_type\": question_type,\n \"game_level_name\"\ 1087 | : challenge['name'],\n \"question_title\": problem['title'],\n \ 1088 | \ \"question_description\": problem['description'],\n }\n\n\ndef\ 1089 | \ validate_palindrome_invariance(query, response, new_response):\n #\ 1090 | \ 检查原始输入不是回文\n if query == query[::-1]:\n return False\n #\ 1091 | \ 比较两个回答是否一致\n return response.strip() == new_response.strip()\n\n\n\ 1092 | def is_prime(n):\n \"\"\"检查一个数是否为质数\"\"\"\n if n < 2:\n return\ 1093 | \ False\n for i in range(2, int(math.sqrt(n)) + 1):\n if n % i\ 1094 | \ == 0:\n return False\n return True\n\n\ndef next_prime(n):\n\ 1095 | \ \"\"\"找到大于n的下一个质数\"\"\"\n current = n + 1\n while True:\n \ 1096 | \ if is_prime(current):\n return current\n current\ 1097 | \ += 1\n\n\ndef level2_validate_9(query, response):\n query_yes = len(query)\ 1098 | \ <= 10\n output_yes = len(response) >= 9 and response.isdigit() and\ 1099 | \ is_prime(\n int(response))\n return query_yes and output_yes\n\ 1100 | \n\ndef validate_palindrome_inverse(query, response, new_response):\n \ 1101 | \ # 检查原始输入不是回文\n if query == query[::-1]:\n return False\n \ 1102 | \ # 检查原始回答不是回文\n if response == response[::-1]:\n return False\n\ 1103 | \ # 比较正着问和倒着问的回答是否是逆序关系\n return response == new_response[::-1]\n\n\ 1104 | \ndef validate_reciprocal_question(query, response, new_response):\n \ 1105 | \ # 确保回答B不等于原始问题A\n if response == query:\n return False\n \ 1106 | \ # 检查新的回答是否与原始问题A相等\n return new_response.strip() == query.strip()\n\ 1107 | \n\ndef check_word_in_sentence(words, sentence):\n return [word in sentence\ 1108 | \ for word in words]\n" 1109 | code_language: python3 1110 | desc: '' 1111 | outputs: 1112 | game_level: 1113 | children: null 1114 | type: number 1115 | game_level_name: 1116 | children: null 1117 | type: string 1118 | question_description: 1119 | children: null 1120 | type: string 1121 | question_id: 1122 | children: null 1123 | type: number 1124 | question_title: 1125 | children: null 1126 | type: string 1127 | question_type: 1128 | children: null 1129 | type: number 1130 | status: 1131 | children: null 1132 | type: string 1133 | valid_pass: 1134 | children: null 1135 | type: number 1136 | selected: false 1137 | title: 获取问题和验证结果 1138 | type: code 1139 | variables: 1140 | - value_selector: 1141 | - sys 1142 | - query 1143 | variable: query 1144 | - value_selector: 1145 | - conversation 1146 | - game_level 1147 | variable: game_level 1148 | - value_selector: 1149 | - conversation 1150 | - question_id 1151 | variable: question_id 1152 | - value_selector: 1153 | - '1735195760496' 1154 | - text 1155 | variable: text 1156 | - value_selector: 1157 | - conversation 1158 | - status 1159 | variable: status 1160 | - value_selector: 1161 | - '1735198964742' 1162 | - text 1163 | variable: reversed_text 1164 | - value_selector: 1165 | - '1735213513838' 1166 | - text 1167 | variable: reciprocal_text 1168 | height: 54 1169 | id: '1735195133945' 1170 | position: 1171 | x: 3374 1172 | y: 987.5 1173 | positionAbsolute: 1174 | x: 3374 1175 | y: 987.5 1176 | selected: false 1177 | sourcePosition: right 1178 | targetPosition: left 1179 | type: custom 1180 | width: 244 1181 | - data: 1182 | context: 1183 | enabled: false 1184 | variable_selector: [] 1185 | desc: '' 1186 | model: 1187 | completion_params: 1188 | temperature: 0.7 1189 | mode: chat 1190 | name: qwen-max 1191 | provider: langgenius/tongyi/tongyi 1192 | prompt_template: 1193 | - id: f612da1a-6ab3-49d7-ab1e-7729e4aa2fe4 1194 | role: system 1195 | text: '' 1196 | - id: 80df3b71-6ea6-4263-b2e0-ac0b0b3c77ca 1197 | role: user 1198 | text: '{{#sys.query#}}' 1199 | selected: false 1200 | title: LLM问答 1201 | type: llm 1202 | variables: [] 1203 | vision: 1204 | enabled: false 1205 | height: 90 1206 | id: '1735195760496' 1207 | position: 1208 | x: 1854 1209 | y: 943 1210 | positionAbsolute: 1211 | x: 1854 1212 | y: 943 1213 | selected: false 1214 | sourcePosition: right 1215 | targetPosition: left 1216 | type: custom 1217 | width: 244 1218 | - data: 1219 | cases: 1220 | - case_id: 'true' 1221 | conditions: 1222 | - comparison_operator: '=' 1223 | id: 698999da-d4c8-4f18-a69d-c32d98c8183c 1224 | value: '0' 1225 | varType: number 1226 | variable_selector: 1227 | - '1735195133945' 1228 | - valid_pass 1229 | id: 'true' 1230 | logical_operator: and 1231 | - case_id: 25972869-6d46-4e39-9a88-e019825edf57 1232 | conditions: 1233 | - comparison_operator: '=' 1234 | id: 39d7f6a0-9513-4c04-9204-748d2482b4d7 1235 | value: '-1' 1236 | varType: number 1237 | variable_selector: 1238 | - '1735195133945' 1239 | - valid_pass 1240 | id: 25972869-6d46-4e39-9a88-e019825edf57 1241 | logical_operator: and 1242 | - case_id: 9d4ecd94-c9d7-4f53-a44e-e5b3a62827a6 1243 | conditions: 1244 | - comparison_operator: '=' 1245 | id: 91c02f18-ab0b-48eb-90c1-f8a042ca914a 1246 | value: '2' 1247 | varType: number 1248 | variable_selector: 1249 | - '1735195133945' 1250 | - valid_pass 1251 | id: 9d4ecd94-c9d7-4f53-a44e-e5b3a62827a6 1252 | logical_operator: and 1253 | desc: '' 1254 | selected: false 1255 | title: 验证通过判断 1256 | type: if-else 1257 | height: 222 1258 | id: '1735197213688' 1259 | position: 1260 | x: 4286 1261 | y: 987.5 1262 | positionAbsolute: 1263 | x: 4286 1264 | y: 987.5 1265 | selected: false 1266 | sourcePosition: right 1267 | targetPosition: left 1268 | type: custom 1269 | width: 244 1270 | - data: 1271 | answer: '**挑战结果:** 挑战失败,请再试一次。 1272 | 1273 | 1274 | ' 1275 | desc: '' 1276 | selected: false 1277 | title: 挑战失败 1278 | type: answer 1279 | variables: [] 1280 | height: 132 1281 | id: '1735197253934' 1282 | position: 1283 | x: 4590 1284 | y: 987.5 1285 | positionAbsolute: 1286 | x: 4590 1287 | y: 987.5 1288 | selected: false 1289 | sourcePosition: right 1290 | targetPosition: left 1291 | type: custom 1292 | width: 244 1293 | - data: 1294 | answer: '**模型的回答:** 1295 | 1296 | {{#1735195760496.text#}} 1297 | 1298 | 1299 | ' 1300 | desc: '' 1301 | selected: false 1302 | title: LLM回答 1303 | type: answer 1304 | variables: [] 1305 | height: 119 1306 | id: '1735198227518' 1307 | position: 1308 | x: 2158 1309 | y: 898 1310 | positionAbsolute: 1311 | x: 2158 1312 | y: 898 1313 | selected: false 1314 | sourcePosition: right 1315 | targetPosition: left 1316 | type: custom 1317 | width: 244 1318 | - data: 1319 | cases: 1320 | - case_id: 'true' 1321 | conditions: 1322 | - comparison_operator: '=' 1323 | id: e66c5ac9-c4df-4bee-9758-b7dd2a028ae9 1324 | value: '1' 1325 | varType: number 1326 | variable_selector: 1327 | - conversation 1328 | - question_type 1329 | id: 'true' 1330 | logical_operator: and 1331 | desc: '' 1332 | selected: false 1333 | title: 回文问题判断 1334 | type: if-else 1335 | height: 126 1336 | id: '1735198861231' 1337 | position: 1338 | x: 2158 1339 | y: 1143 1340 | positionAbsolute: 1341 | x: 2158 1342 | y: 1143 1343 | selected: false 1344 | sourcePosition: right 1345 | targetPosition: left 1346 | type: custom 1347 | width: 244 1348 | - data: 1349 | context: 1350 | enabled: false 1351 | variable_selector: [] 1352 | desc: '' 1353 | model: 1354 | completion_params: 1355 | temperature: 0.7 1356 | mode: chat 1357 | name: qwen-max 1358 | provider: langgenius/tongyi/tongyi 1359 | prompt_template: 1360 | - id: 7066eb04-8562-4b3f-b682-467971f1b047 1361 | role: system 1362 | text: '' 1363 | - id: 379d7a22-f167-4c45-bd24-f951ad2c3aa2 1364 | role: user 1365 | text: '{{#1735199302294.reversed_query#}}' 1366 | selected: false 1367 | title: 倒序问题的LLM问答 1368 | type: llm 1369 | variables: [] 1370 | vision: 1371 | enabled: false 1372 | height: 90 1373 | id: '1735198964742' 1374 | position: 1375 | x: 2766 1376 | y: 1146.5 1377 | positionAbsolute: 1378 | x: 2766 1379 | y: 1146.5 1380 | selected: false 1381 | sourcePosition: right 1382 | targetPosition: left 1383 | type: custom 1384 | width: 244 1385 | - data: 1386 | answer: '**模型的回答(倒序问题):** 1387 | 1388 | {{#1735198964742.text#}} 1389 | 1390 | 1391 | ' 1392 | desc: '' 1393 | selected: false 1394 | title: 倒序问题的LLM回答 1395 | type: answer 1396 | variables: [] 1397 | height: 119 1398 | id: '1735199090032' 1399 | position: 1400 | x: 3070 1401 | y: 1146.5 1402 | positionAbsolute: 1403 | x: 3070 1404 | y: 1146.5 1405 | selected: false 1406 | sourcePosition: right 1407 | targetPosition: left 1408 | type: custom 1409 | width: 244 1410 | - data: 1411 | code: "\ndef main(query: str) -> dict:\n return {\n \"reversed_query\"\ 1412 | : query[::-1],\n }\n" 1413 | code_language: python3 1414 | desc: '' 1415 | outputs: 1416 | reversed_query: 1417 | children: null 1418 | type: string 1419 | selected: false 1420 | title: 问题倒序 1421 | type: code 1422 | variables: 1423 | - value_selector: 1424 | - sys 1425 | - query 1426 | variable: query 1427 | height: 54 1428 | id: '1735199302294' 1429 | position: 1430 | x: 2462 1431 | y: 1182.5 1432 | positionAbsolute: 1433 | x: 2462 1434 | y: 1182.5 1435 | selected: false 1436 | sourcePosition: right 1437 | targetPosition: left 1438 | type: custom 1439 | width: 244 1440 | - data: 1441 | answer: '**挑战结果:** 挑战成功!进入下一关。🎉 1442 | 1443 | 1444 | ' 1445 | desc: '' 1446 | selected: false 1447 | title: 问题挑战成功 1448 | type: answer 1449 | variables: [] 1450 | height: 132 1451 | id: '1735199727654' 1452 | position: 1453 | x: 4590 1454 | y: 1159.5 1455 | positionAbsolute: 1456 | x: 4590 1457 | y: 1159.5 1458 | selected: false 1459 | sourcePosition: right 1460 | targetPosition: left 1461 | type: custom 1462 | width: 244 1463 | - data: 1464 | answer: '**挑战结果:** 恭喜你完成了所有挑战!🎉🎉🎉' 1465 | desc: '' 1466 | selected: false 1467 | title: 挑战完成 1468 | type: answer 1469 | variables: [] 1470 | height: 132 1471 | id: '1735199809830' 1472 | position: 1473 | x: 4286 1474 | y: 1396.5 1475 | positionAbsolute: 1476 | x: 4286 1477 | y: 1396.5 1478 | selected: false 1479 | sourcePosition: right 1480 | targetPosition: left 1481 | type: custom 1482 | width: 244 1483 | - data: 1484 | answer: '你已经完成了所有挑战!!!🎉🎉🎉 1485 | 1486 | 可以点击 1487 | ,开始重新挑战! 1488 | 1489 | 可以点击 1490 | ,查看当前排行榜!' 1491 | desc: '' 1492 | selected: false 1493 | title: 提示点击重启和战绩 1494 | type: answer 1495 | variables: [] 1496 | height: 244 1497 | id: '1735200055854' 1498 | position: 1499 | x: 1854 1500 | y: 659 1501 | positionAbsolute: 1502 | x: 1854 1503 | y: 659 1504 | selected: false 1505 | sourcePosition: right 1506 | targetPosition: left 1507 | type: custom 1508 | width: 244 1509 | - data: 1510 | cases: 1511 | - case_id: 'true' 1512 | conditions: 1513 | - comparison_operator: is 1514 | id: 2e521a81-0515-4900-8d11-ea3f29e48564 1515 | value: running 1516 | varType: string 1517 | variable_selector: 1518 | - '1735195133945' 1519 | - status 1520 | id: 'true' 1521 | logical_operator: and 1522 | - case_id: ef6ca92c-8822-4f18-b219-ae4bae7698a0 1523 | conditions: 1524 | - comparison_operator: is 1525 | id: c1f27c36-bf70-48ad-b1d0-b019e95bf0ca 1526 | value: success 1527 | varType: string 1528 | variable_selector: 1529 | - '1735195133945' 1530 | - status 1531 | id: ef6ca92c-8822-4f18-b219-ae4bae7698a0 1532 | logical_operator: and 1533 | desc: '' 1534 | selected: false 1535 | title: 状态判断 1536 | type: if-else 1537 | height: 174 1538 | id: '1735201048213' 1539 | position: 1540 | x: 3982 1541 | y: 987.5 1542 | positionAbsolute: 1543 | x: 3982 1544 | y: 987.5 1545 | selected: false 1546 | sourcePosition: right 1547 | targetPosition: left 1548 | type: custom 1549 | width: 244 1550 | - data: 1551 | desc: '' 1552 | items: 1553 | - input_type: variable 1554 | operation: over-write 1555 | value: 1556 | - '1735195133945' 1557 | - status 1558 | variable_selector: 1559 | - conversation 1560 | - status 1561 | write_mode: over-write 1562 | - input_type: variable 1563 | operation: over-write 1564 | value: 1565 | - '1735195133945' 1566 | - game_level 1567 | variable_selector: 1568 | - conversation 1569 | - game_level 1570 | write_mode: over-write 1571 | - input_type: variable 1572 | operation: over-write 1573 | value: 1574 | - '1735195133945' 1575 | - question_id 1576 | variable_selector: 1577 | - conversation 1578 | - question_id 1579 | write_mode: over-write 1580 | - input_type: variable 1581 | operation: over-write 1582 | value: 1583 | - '1735195133945' 1584 | - question_type 1585 | variable_selector: 1586 | - conversation 1587 | - question_type 1588 | write_mode: over-write 1589 | selected: false 1590 | title: 设置游戏关卡、问题序号和状态 1591 | type: assigner 1592 | version: '2' 1593 | height: 172 1594 | id: '1735201350381' 1595 | position: 1596 | x: 3678 1597 | y: 987.5 1598 | positionAbsolute: 1599 | x: 3678 1600 | y: 987.5 1601 | selected: false 1602 | sourcePosition: right 1603 | targetPosition: left 1604 | type: custom 1605 | width: 244 1606 | - data: 1607 | cases: 1608 | - case_id: 'true' 1609 | conditions: 1610 | - comparison_operator: '=' 1611 | id: b96274d7-3114-41f8-a6ea-e9ce10e241a9 1612 | value: '2' 1613 | varType: number 1614 | variable_selector: 1615 | - conversation 1616 | - question_type 1617 | id: 'true' 1618 | logical_operator: and 1619 | desc: '' 1620 | selected: false 1621 | title: 互惠问题判断 1622 | type: if-else 1623 | height: 126 1624 | id: '1735213477600' 1625 | position: 1626 | x: 2462 1627 | y: 898 1628 | positionAbsolute: 1629 | x: 2462 1630 | y: 898 1631 | selected: false 1632 | sourcePosition: right 1633 | targetPosition: left 1634 | type: custom 1635 | width: 244 1636 | - data: 1637 | context: 1638 | enabled: false 1639 | variable_selector: [] 1640 | desc: '' 1641 | model: 1642 | completion_params: 1643 | temperature: 0.7 1644 | mode: chat 1645 | name: qwen-max 1646 | provider: langgenius/tongyi/tongyi 1647 | prompt_template: 1648 | - id: b1768388-7978-4910-a5f4-3cac84223557 1649 | role: system 1650 | text: '' 1651 | - id: dbf05685-f460-4edf-a015-41eea35d77bc 1652 | role: user 1653 | text: '{{#1735195760496.text#}}' 1654 | selected: false 1655 | title: 互惠问题的LLM问答 1656 | type: llm 1657 | variables: [] 1658 | vision: 1659 | enabled: false 1660 | height: 90 1661 | id: '1735213513838' 1662 | position: 1663 | x: 2766 1664 | y: 987.5 1665 | positionAbsolute: 1666 | x: 2766 1667 | y: 987.5 1668 | selected: false 1669 | sourcePosition: right 1670 | targetPosition: left 1671 | type: custom 1672 | width: 244 1673 | - data: 1674 | answer: '**模型的回答(互惠问题):** 1675 | 1676 | {{#1735213513838.text#}} 1677 | 1678 | 1679 | ' 1680 | desc: '' 1681 | selected: false 1682 | title: 互惠问题的LLM回答 1683 | type: answer 1684 | variables: [] 1685 | height: 119 1686 | id: '1735213525623' 1687 | position: 1688 | x: 3070 1689 | y: 987.5 1690 | positionAbsolute: 1691 | x: 3070 1692 | y: 987.5 1693 | selected: false 1694 | sourcePosition: right 1695 | targetPosition: left 1696 | type: custom 1697 | width: 244 1698 | - data: 1699 | author: Junjie.M 1700 | desc: '' 1701 | height: 290 1702 | selected: false 1703 | showAuthor: true 1704 | text: '{"root":{"children":[{"children":[{"detail":0,"format":1,"mode":"normal","style":"","text":"【完蛋!我被LLM包围了!(战绩排行版)】","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":1,"textStyle":""},{"children":[{"detail":0,"format":1,"mode":"normal","style":"","text":"借鉴了:","type":"text","version":1},{"type":"linebreak","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":"https://github.com/modelscope/modelscope/tree/master/examples/apps/llm_riddles","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":1,"textStyle":""},{"children":[],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":1,"mode":"normal","style":"","text":"注意事项:","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":1,"textStyle":""},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Dify版本必须>=1.0.0","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":1}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"需要安装插件:数据库查询工具(预授权)","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":1}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"1、.env 1705 | 修改 FORCE_VERIFYING_SIGNATURE=false","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"2、插件地址:https://github.com/junjiem/dify-plugin-tools-dbquery","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"3、选择最新版本的db_query_pre_auth.difypkg包","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"4、授权Dify自身的数据库主机、用户名、密码、库名","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}' 1706 | theme: blue 1707 | title: '' 1708 | type: '' 1709 | width: 650 1710 | height: 290 1711 | id: '1735262381674' 1712 | position: 1713 | x: 30 1714 | y: -3.1806073574777045 1715 | positionAbsolute: 1716 | x: 30 1717 | y: -3.1806073574777045 1718 | selected: true 1719 | sourcePosition: right 1720 | targetPosition: left 1721 | type: custom-note 1722 | width: 650 1723 | - data: 1724 | answer: '**挑战结果:** 恭喜章节挑战成功!进入下一章。🎉🎉 1725 | 1726 | 1727 | ' 1728 | desc: '' 1729 | selected: false 1730 | title: 关卡挑战成功 1731 | type: answer 1732 | variables: [] 1733 | height: 132 1734 | id: '1735264591657' 1735 | position: 1736 | x: 4590 1737 | y: 1351.5 1738 | positionAbsolute: 1739 | x: 4590 1740 | y: 1351.5 1741 | selected: false 1742 | sourcePosition: right 1743 | targetPosition: left 1744 | type: custom 1745 | width: 244 1746 | - data: 1747 | answer: '## 当前排行榜 TOP{{#env.top_n#}} 1748 | 及其战绩如下(查看自己是否上榜👉): 1749 | 1750 | {{#1741171122348.text#}}' 1751 | desc: '' 1752 | selected: false 1753 | title: 战绩排行 1754 | type: answer 1755 | variables: [] 1756 | height: 154 1757 | id: '1735447955439' 1758 | position: 1759 | x: 2158 1760 | y: 1329 1761 | positionAbsolute: 1762 | x: 2158 1763 | y: 1329 1764 | selected: false 1765 | sourcePosition: right 1766 | targetPosition: left 1767 | type: custom 1768 | width: 244 1769 | - data: 1770 | desc: 为了规避会话变量在条件分支中作为判断条件时下游回复都会触发的BUG,临时这样处理 1771 | output_type: string 1772 | selected: false 1773 | title: 变量聚合器 1774 | type: variable-aggregator 1775 | variables: 1776 | - - '1736306672174' 1777 | - output 1778 | - - '1736306689285' 1779 | - output 1780 | - - '1736306700014' 1781 | - output 1782 | height: 212 1783 | id: '1736306267206' 1784 | position: 1785 | x: 1246 1786 | y: 439 1787 | positionAbsolute: 1788 | x: 1246 1789 | y: 439 1790 | selected: false 1791 | sourcePosition: right 1792 | targetPosition: left 1793 | type: custom 1794 | width: 244 1795 | - data: 1796 | desc: '' 1797 | selected: false 1798 | template: '{{ status }}' 1799 | title: 模板转换 1800 | type: template-transform 1801 | variables: 1802 | - value_selector: 1803 | - conversation 1804 | - status 1805 | variable: status 1806 | height: 54 1807 | id: '1736306672174' 1808 | position: 1809 | x: 942 1810 | y: 311 1811 | positionAbsolute: 1812 | x: 942 1813 | y: 311 1814 | selected: false 1815 | sourcePosition: right 1816 | targetPosition: left 1817 | type: custom 1818 | width: 244 1819 | - data: 1820 | desc: '' 1821 | selected: false 1822 | template: '{{ status }}' 1823 | title: 模板转换 2 1824 | type: template-transform 1825 | variables: 1826 | - value_selector: 1827 | - conversation 1828 | - status 1829 | variable: status 1830 | height: 54 1831 | id: '1736306689285' 1832 | position: 1833 | x: 942 1834 | y: 439 1835 | positionAbsolute: 1836 | x: 942 1837 | y: 439 1838 | selected: false 1839 | sourcePosition: right 1840 | targetPosition: left 1841 | type: custom 1842 | width: 244 1843 | - data: 1844 | desc: '' 1845 | selected: false 1846 | template: '{{ status }}' 1847 | title: 模板转换 3 1848 | type: template-transform 1849 | variables: 1850 | - value_selector: 1851 | - conversation 1852 | - status 1853 | variable: status 1854 | height: 54 1855 | id: '1736306700014' 1856 | position: 1857 | x: 942 1858 | y: 533.6428571428571 1859 | positionAbsolute: 1860 | x: 942 1861 | y: 533.6428571428571 1862 | selected: false 1863 | sourcePosition: right 1864 | targetPosition: left 1865 | type: custom 1866 | width: 244 1867 | - data: 1868 | desc: '' 1869 | is_team_authorization: false 1870 | output_schema: null 1871 | paramSchemas: 1872 | - auto_generate: null 1873 | default: null 1874 | form: llm 1875 | human_description: 1876 | en_US: 'SQL query statement, for example: select * from tbl_name' 1877 | ja_JP: 'SQL query statement, for example: select * from tbl_name' 1878 | pt_BR: 'SQL query statement, for example: select * from tbl_name' 1879 | zh_Hans: SQL查询语句,例如:select * from tbl_name 1880 | label: 1881 | en_US: Query SQL 1882 | ja_JP: Query SQL 1883 | pt_BR: Query SQL 1884 | zh_Hans: SQL查询语句 1885 | llm_description: 'SQL query statement, for example: select * from tbl_name' 1886 | max: null 1887 | min: null 1888 | name: query_sql 1889 | options: [] 1890 | placeholder: null 1891 | precision: null 1892 | required: true 1893 | scope: null 1894 | template: null 1895 | type: string 1896 | params: 1897 | query_sql: '' 1898 | provider_id: junjiem/db_query_pre_auth/db_query_pre_auth 1899 | provider_name: junjiem/db_query_pre_auth/db_query_pre_auth 1900 | provider_type: builtin 1901 | selected: false 1902 | title: SQL查询(预授权) 1903 | tool_configurations: {} 1904 | tool_label: SQL查询(预授权) 1905 | tool_name: sql_query_pre_auth 1906 | tool_parameters: 1907 | query_sql: 1908 | type: mixed 1909 | value: "WITH game_progress AS (\n\tSELECT\n\t\tt2.conversation_id,\n\t\ 1910 | \tt2.is_game_through,\n\t\tt2.game_level,\n\t\tt2.question_id,\n\t\t\ 1911 | t2.updated_at\n\tfrom (\n\t\tSELECT\n\t\tt1.conversation_id,\n\t\tt1.is_game_through,\n\ 1912 | \t\tt1.game_level,\n\t\tt1.question_id,\n\t\tt1.updated_at,\n\t\tROW_NUMBER()\ 1913 | \ OVER (\n\t\t\tPARTITION BY t1.conversation_id\n\t\t\tORDER BY t1.is_game_through\ 1914 | \ DESC, t1.game_level DESC, t1.question_id DESC\n\t\t) AS row_num\n\t\ 1915 | \tFROM (\n\t\t\tSELECT\n\t\t\tm.conversation_id,\n\t\t\tm.answer LIKE\ 1916 | \ '%**挑战结果:** 恭喜你完成了所有挑战!\U0001F389\ 1917 | \U0001F389\U0001F389%' AS is_game_through,\n\t\t\tCASE \n\t\t\t\tWHEN\ 1918 | \ m.answer LIKE '%## 【第一章】 对话之趣%' THEN 1\n\t\t\t\tWHEN m.answer LIKE\ 1919 | \ '%## 【第二章】 数字游戏%' THEN 2\n\t\t\t\tWHEN m.answer LIKE '%## 【第三章】 巅峰挑战%'\ 1920 | \ THEN 3\n\t\t\t\tWHEN m.answer LIKE '%## 【第四章】 无人之境%' THEN 4\n\t\t\t\ 1921 | \tWHEN m.answer LIKE '%## 【第五章】 登堂入室%' THEN 5\n\t\t\t\tELSE 0\n\t\t\t\ 1922 | END AS game_level,\n\t\t\tCASE \n\t\t\t\tWHEN m.answer LIKE '%### 第1题\ 1923 | \ %' THEN 1\n\t\t\t\tWHEN m.answer LIKE '%### 第2题 %' THEN 2\n\t\t\t\t\ 1924 | WHEN m.answer LIKE '%### 第3题 %' THEN 3\n\t\t\t\tWHEN m.answer LIKE '%###\ 1925 | \ 第4题 %' THEN 4\n\t\t\t\tWHEN m.answer LIKE '%### 第5题 %' THEN 5\n\t\t\ 1926 | \t\tWHEN m.answer LIKE '%### 第6题 %' THEN 6\n\t\t\t\tWHEN m.answer LIKE\ 1927 | \ '%### 第7题 %' THEN 7\n\t\t\t\tWHEN m.answer LIKE '%### 第8题 %' THEN\ 1928 | \ 8\n\t\t\t\tWHEN m.answer LIKE '%### 第9题 %' THEN 9\n\t\t\t\tELSE 0\n\ 1929 | \t\t\tEND AS question_id,\n\t\t\tm.updated_at\n\t\t\tFROM messages m\n\ 1930 | \t\t\tJOIN end_users u\n\t\t\tON m.app_id = '{{#sys.app_id#}}'\n\t\t\ 1931 | \tAND m.from_end_user_id = u.id\n\t\t\tAND (\n\t\t\t\tm.answer LIKE\ 1932 | \ '%**挑战结果:** 挑战成功!进入下一关。\U0001F389\ 1933 | %'\n\t\t\t\tOR m.answer LIKE '%**挑战结果:** 恭喜章节挑战成功!进入下一章。\U0001F389\U0001F389%'\n\t\t\t\tOR m.answer LIKE\ 1935 | \ '%**挑战结果:** 恭喜你完成了所有挑战!\U0001F389\ 1936 | \U0001F389\U0001F389%'\n\t\t\t)\n\t\t) t1\n\t\tWHERE t1.is_game_through\ 1937 | \ = True\n\t\tOR (t1.game_level > 0 AND t1.question_id > 0) \n\t) t2\n\ 1938 | \tWHERE t2.row_num = 1\n),\ngame_duration AS (\n\tSELECT \n\t\tconversation_id,\n\ 1939 | \t\tMAX(updated_at) AS max_updated_at,\n\t\tMAX(updated_at) - MIN(created_at)\ 1940 | \ AS duration\n\tFROM messages\n\tWHERE app_id = '{{#sys.app_id#}}'\n\ 1941 | \tAND (\n\t\tanswer LIKE '%### 第1题 初来乍到%'\n\t\tOR answer LIKE '%**挑战结果:**\ 1942 | \ 挑战成功!进入下一关。\U0001F389%'\n\t\t\ 1943 | OR answer LIKE '%**挑战结果:** 恭喜章节挑战成功!进入下一章。\U0001F389\ 1944 | \U0001F389%'\n\t\tOR answer LIKE '%**挑战结果:** 恭喜你完成了所有挑战!\U0001F389\U0001F389\U0001F389%'\n\t)\n\tGROUP BY\ 1946 | \ conversation_id\n),\ngame_fail_counts AS (\n\tSELECT\n\t\tm.conversation_id,\n\ 1947 | \t\tCOUNT(m.id) AS fail_count\n\tFROM messages m\n\tJOIN game_duration\ 1948 | \ gd ON m.conversation_id = gd.conversation_id\n\tWHERE m.app_id = '{{#sys.app_id#}}'\n\ 1949 | \tAND m.answer LIKE '%**挑战结果:** 挑战失败,请再试一次。%'\n\ 1950 | \tAND m.updated_at <= gd.max_updated_at\n\tGROUP BY m.conversation_id\n\ 1951 | )\nSELECT\n\tCASE \n\t\tWHEN t.conversation_id = '{{#sys.conversation_id#}}'\n\ 1952 | \t\tTHEN '\U0001F449' || t.rank || ''\n\ 1953 | \t\tELSE t.rank::text\n\tEND AS 排名,\n\tCASE \n\t\tWHEN t.conversation_id\ 1954 | \ = '{{#sys.conversation_id#}}'\n\t\tTHEN '' || t.through || '\ 1955 | \ \U0001F389\U0001F389\U0001F389'\n\t\tELSE t.through\n\tEND AS 挑战成功关卡,\n\ 1956 | \tCASE \n\t\tWHEN t.conversation_id = '{{#sys.conversation_id#}}'\n\t\ 1957 | \tTHEN '' || t.fail_count || ''\n\ 1958 | \t\tELSE t.fail_count::text\n\tEND AS 挑战失败次数,\n\tCASE \n\t\tWHEN t.conversation_id\ 1959 | \ = '{{#sys.conversation_id#}}'\n\t\tTHEN '' || t.duration || ''\n\t\tELSE t.duration::text\n\tEND\ 1961 | \ AS 挑战成功耗时,\n\tCASE \n\t\tWHEN t.conversation_id = '{{#sys.conversation_id#}}'\n\ 1962 | \t\tTHEN '' || t.updated_time || ''\n\ 1963 | \t\tELSE t.updated_time::text\n\tEND AS 挑战成功时间\nFROM (\n\tSELECT\n\t\ 1964 | \tRANK() OVER (ORDER BY gp.is_game_through DESC, gp.game_level DESC,\ 1965 | \ gp.question_id DESC, gfc.fail_count ASC, gd.duration ASC, gp.updated_at\ 1966 | \ ASC) AS rank,\n\t\tgp.conversation_id,\n\t\tCASE \n\t\t\tWHEN gp.is_game_through\ 1967 | \ = True THEN '\U0001F451通关'\n\ 1968 | \t\t\tWHEN gp.game_level >= 4 THEN '\U0001F31F' || gp.game_level || '-' || gp.question_id || ''\n\t\t\tWHEN\ 1970 | \ gp.game_level >= 3 THEN '\U0001F4AA'\ 1971 | \ || gp.game_level || '-' || gp.question_id || ''\n\t\t\tELSE\ 1972 | \ gp.game_level || '-' || gp.question_id\n\t\tEND AS through,\n\t\t\ 1973 | COALESCE(gfc.fail_count::integer, 0) AS fail_count,\n\t\tgd.duration,\n\ 1974 | \t\tgp.updated_at AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Shanghai' AS\ 1975 | \ updated_time\n\tFROM game_progress gp\n\tLEFT JOIN game_duration gd\ 1976 | \ ON gp.conversation_id = gd.conversation_id\n\tLEFT JOIN game_fail_counts\ 1977 | \ gfc ON gp.conversation_id = gfc.conversation_id\n\tORDER BY gp.is_game_through\ 1978 | \ DESC, gp.game_level DESC, gp.question_id DESC, gfc.fail_count ASC,\ 1979 | \ gd.duration ASC, gp.updated_at ASC\n) t\nLIMIT {{#env.top_n#}}" 1980 | type: tool 1981 | height: 54 1982 | id: '1741171122348' 1983 | position: 1984 | x: 1550 1985 | y: 1454.5 1986 | positionAbsolute: 1987 | x: 1550 1988 | y: 1454.5 1989 | selected: false 1990 | sourcePosition: right 1991 | targetPosition: left 1992 | type: custom 1993 | width: 244 1994 | - data: 1995 | code: "import time\n\ndef main(show_ranking_ts: int) -> dict:\n now_ts\ 1996 | \ = time.time()\n allow_show = 0\n if abs(now_ts - show_ranking_ts)\ 1997 | \ > 5*60:\n allow_show = 1\n return {\n \"now_ts\": now_ts,\n\ 1998 | \ \"allow_show\": allow_show,\n }\n" 1999 | code_language: python3 2000 | desc: '' 2001 | outputs: 2002 | allow_show: 2003 | children: null 2004 | type: number 2005 | now_ts: 2006 | children: null 2007 | type: number 2008 | selected: false 2009 | title: 当前的时间戳和是否允许查看战绩 2010 | type: code 2011 | variables: 2012 | - value_selector: 2013 | - conversation 2014 | - show_ranking_ts 2015 | variable: show_ranking_ts 2016 | height: 54 2017 | id: '1741569725917' 2018 | position: 2019 | x: 638 2020 | y: 1363.5 2021 | positionAbsolute: 2022 | x: 638 2023 | y: 1363.5 2024 | sourcePosition: right 2025 | targetPosition: left 2026 | type: custom 2027 | width: 244 2028 | - data: 2029 | cases: 2030 | - case_id: 'true' 2031 | conditions: 2032 | - comparison_operator: '=' 2033 | id: b9416050-ea4e-464d-9c4b-86addea2e274 2034 | value: '1' 2035 | varType: number 2036 | variable_selector: 2037 | - '1741569725917' 2038 | - allow_show 2039 | id: 'true' 2040 | logical_operator: and 2041 | desc: '' 2042 | selected: false 2043 | title: 是否允许查看战绩 2044 | type: if-else 2045 | height: 126 2046 | id: '1741569780133' 2047 | position: 2048 | x: 942 2049 | y: 1310.5 2050 | positionAbsolute: 2051 | x: 942 2052 | y: 1310.5 2053 | sourcePosition: right 2054 | targetPosition: left 2055 | type: custom 2056 | width: 244 2057 | - data: 2058 | desc: '' 2059 | items: 2060 | - input_type: variable 2061 | operation: over-write 2062 | value: 2063 | - '1741569725917' 2064 | - now_ts 2065 | variable_selector: 2066 | - conversation 2067 | - show_ranking_ts 2068 | write_mode: over-write 2069 | selected: false 2070 | title: 设置查看战绩的时间戳 2071 | type: assigner 2072 | version: '2' 2073 | height: 88 2074 | id: '1741569807700' 2075 | position: 2076 | x: 1246 2077 | y: 1408.5 2078 | positionAbsolute: 2079 | x: 1246 2080 | y: 1408.5 2081 | selected: false 2082 | sourcePosition: right 2083 | targetPosition: left 2084 | type: custom 2085 | width: 244 2086 | - data: 2087 | answer: '查看战绩太频繁了!不利于你的挑战,请过几分钟再查看!!!' 2088 | desc: '' 2089 | selected: false 2090 | title: 查看战绩太频繁 2091 | type: answer 2092 | variables: [] 2093 | height: 132 2094 | id: '1741569868709' 2095 | position: 2096 | x: 1246 2097 | y: 1536.5 2098 | positionAbsolute: 2099 | x: 1246 2100 | y: 1536.5 2101 | selected: false 2102 | sourcePosition: right 2103 | targetPosition: left 2104 | type: custom 2105 | width: 244 2106 | - data: 2107 | cases: 2108 | - case_id: 'true' 2109 | conditions: 2110 | - comparison_operator: not empty 2111 | id: e42cc097-059c-4806-b4a8-5afb07871581 2112 | value: '' 2113 | varType: string 2114 | variable_selector: 2115 | - '1741171122348' 2116 | - text 2117 | id: 'true' 2118 | logical_operator: and 2119 | desc: '' 2120 | selected: false 2121 | title: 战绩为空判断 2122 | type: if-else 2123 | height: 126 2124 | id: '1741569900565' 2125 | position: 2126 | x: 1854 2127 | y: 1373.5 2128 | positionAbsolute: 2129 | x: 1854 2130 | y: 1373.5 2131 | selected: false 2132 | sourcePosition: right 2133 | targetPosition: left 2134 | type: custom 2135 | width: 244 2136 | - data: 2137 | answer: '当前还未有排行榜及其战绩!请先进行你的挑战,过几分钟后再查看!!!' 2138 | desc: '' 2139 | selected: false 2140 | title: 未有战绩排行 2141 | type: answer 2142 | variables: [] 2143 | height: 132 2144 | id: '1741569961830' 2145 | position: 2146 | x: 2158 2147 | y: 1523 2148 | positionAbsolute: 2149 | x: 2158 2150 | y: 1523 2151 | selected: false 2152 | sourcePosition: right 2153 | targetPosition: left 2154 | type: custom 2155 | width: 244 2156 | viewport: 2157 | x: -756 2158 | y: -46.99999999999997 2159 | zoom: 0.7 2160 | -------------------------------------------------------------------------------- /images/db_query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/images/db_query.png -------------------------------------------------------------------------------- /images/db_query_pre_auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/images/db_query_pre_auth.png -------------------------------------------------------------------------------- /images/dify_plugin_daemon_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/images/dify_plugin_daemon_update.png -------------------------------------------------------------------------------- /images/install_plugin_via_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/images/install_plugin_via_github.png -------------------------------------------------------------------------------- /images/install_plugin_via_local.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junjiem/dify-plugin-tools-dbquery/23d3f2983a97dbb3bbbaaa4bf9067e2c2a38b7f2/images/install_plugin_via_local.png --------------------------------------------------------------------------------