feat(batch-install-plugins): initial release v1.0.0
Add Batch Install Plugins from GitHub tool with: - One-click installation of plugins from GitHub repositories - Smart plugin discovery with metadata extraction and validation - Confirmation dialog with plugin list preview - Selective installation with keyword-based filtering - Smart fallback: auto-retry with localhost:8080 on connection failure - Enhanced debugging with frontend and backend logging - 120-second confirmation timeout for user convenience - Async httpx client for non-blocking I/O - Complete i18n support across 11 languages - Event emitter handling with fallback support - Timeout guards on frontend JavaScript execution - Filtered list consistency for confirmation and installation - Auto-exclusion of tool itself from batch operations - 6 regression tests with 100% pass rate Documentation includes: - English and Chinese READMEs with flow diagrams - Popular repository examples (iChristGit, Haervwe, Classic298, suurt8ll) - Mirrored docs for official documentation site - Plugin index entries in both languages - Comprehensive release notes (v1.0.0.md and v1.0.0_CN.md) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
139
docs/plugins/tools/batch-install-plugins-tool.md
Normal file
139
docs/plugins/tools/batch-install-plugins-tool.md
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
# Batch Install Plugins from GitHub
|
||||||
|
|
||||||
|
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 1.0.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
One-click batch install plugins from GitHub repositories to your OpenWebUI instance.
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
- **One-Click Install**: Install all plugins with a single command
|
||||||
|
- **Auto-Update**: Automatically updates previously installed plugins
|
||||||
|
- **GitHub Support**: Install plugins from any GitHub repository
|
||||||
|
- **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins
|
||||||
|
- **Confirmation**: Shows plugin list before installing, allows selective installation
|
||||||
|
- **i18n**: Supports 11 languages
|
||||||
|
|
||||||
|
## Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
User Input
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Discover Plugins from GitHub │
|
||||||
|
│ (fetch file tree + parse .py) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Filter by Type & Keywords │
|
||||||
|
│ (tool/filter/pipe/action) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Show Confirmation Dialog │
|
||||||
|
│ (list plugins + exclude hint) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
├── [Cancel] → End
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Install to OpenWebUI │
|
||||||
|
│ (update or create each plugin) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Done
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to Use
|
||||||
|
|
||||||
|
1. Open OpenWebUI and go to **Workspace > Tools**
|
||||||
|
2. Install **Batch Install Plugins from GitHub** from the official marketplace
|
||||||
|
3. Enable this tool for your model/chat
|
||||||
|
4. Ask the model to install plugins
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
"Install all plugins"
|
||||||
|
"Install all plugins from github.com/username/repo"
|
||||||
|
"Install only pipe plugins"
|
||||||
|
"Install action and filter plugins"
|
||||||
|
"Install all plugins, exclude_keywords=copilot"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Popular Plugin Repositories
|
||||||
|
|
||||||
|
Here are some popular repositories with many plugins you can install:
|
||||||
|
|
||||||
|
### Community Collections
|
||||||
|
|
||||||
|
```
|
||||||
|
# Install all plugins from iChristGit's collection
|
||||||
|
"Install all plugins from iChristGit/OpenWebui-Tools"
|
||||||
|
|
||||||
|
# Install all tools from Haervwe's tools collection
|
||||||
|
"Install all plugins from Haervwe/open-webui-tools"
|
||||||
|
|
||||||
|
# Install all plugins from Classic298's repository
|
||||||
|
"Install all plugins from Classic298/open-webui-plugins"
|
||||||
|
|
||||||
|
# Install all functions from suurt8ll's collection
|
||||||
|
"Install all plugins from suurt8ll/open_webui_functions"
|
||||||
|
|
||||||
|
# Install only specific types (e.g., only tools)
|
||||||
|
"Install only tool plugins from iChristGit/OpenWebui-Tools"
|
||||||
|
|
||||||
|
# Exclude certain keywords while installing
|
||||||
|
"Install all plugins from Haervwe/open-webui-tools, exclude_keywords=test,deprecated"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Supported Repositories
|
||||||
|
|
||||||
|
- `Fu-Jie/openwebui-extensions` - Default, official plugin collection
|
||||||
|
- `iChristGit/OpenWebui-Tools` - Comprehensive tool and plugin collection
|
||||||
|
- `Haervwe/open-webui-tools` - Specialized tools and utilities
|
||||||
|
- `Classic298/open-webui-plugins` - Various plugin implementations
|
||||||
|
- `suurt8ll/open_webui_functions` - Function-based plugins
|
||||||
|
|
||||||
|
## Default Repository
|
||||||
|
|
||||||
|
When no repository is specified, defaults to `Fu-Jie/openwebui-extensions`.
|
||||||
|
|
||||||
|
## Plugin Detection Rules
|
||||||
|
|
||||||
|
### Fu-Jie/openwebui-extensions (Strict)
|
||||||
|
|
||||||
|
For the default repository, plugins must have:
|
||||||
|
1. A `.py` file containing `class Tools:`, `class Filter:`, `class Pipe:`, or `class Action:`
|
||||||
|
2. A docstring with `title:`, `description:`, and **`openwebui_id:`** fields
|
||||||
|
3. Filename must not end with `_cn`
|
||||||
|
|
||||||
|
### Other GitHub Repositories
|
||||||
|
|
||||||
|
For other repositories:
|
||||||
|
1. A `.py` file containing `class Tools:`, `class Filter:`, `class Pipe:`, or `class Action:`
|
||||||
|
2. A docstring with `title:` and `description:` fields
|
||||||
|
|
||||||
|
## Configuration (Valves)
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `SKIP_KEYWORDS` | `test,verify,example,template,mock` | Comma-separated keywords to skip |
|
||||||
|
| `TIMEOUT` | `20` | Request timeout in seconds |
|
||||||
|
|
||||||
|
## Confirmation Timeout
|
||||||
|
|
||||||
|
User confirmation dialogs have a default timeout of **2 minutes (120 seconds)**, allowing sufficient time for users to:
|
||||||
|
- Read and review the plugin list
|
||||||
|
- Make installation decisions
|
||||||
|
- Handle network delays
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
If this plugin has been useful, a star on [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) is a big motivation for me. Thank you for the support.
|
||||||
139
docs/plugins/tools/batch-install-plugins-tool.zh.md
Normal file
139
docs/plugins/tools/batch-install-plugins-tool.zh.md
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
# Batch Install Plugins from GitHub - 从 GitHub 批量安装插件
|
||||||
|
|
||||||
|
**作者:** [Fu-Jie](https://github.com/Fu-Jie) | **版本:** 1.0.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可:** MIT
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
一键从 GitHub 仓库批量安装插件到你的 OpenWebUI 实例。
|
||||||
|
|
||||||
|
## ✨ 主要特性
|
||||||
|
|
||||||
|
- **一键安装**: 一条命令安装所有插件
|
||||||
|
- **自动更新**: 自动更新之前已安装的插件
|
||||||
|
- **GitHub 支持**: 支持从任何 GitHub 仓库安装插件
|
||||||
|
- **多类型支持**: 支持 Pipe、Action、Filter 和 Tool 插件
|
||||||
|
- **确认机制**: 安装前显示插件列表,允许选择性安装
|
||||||
|
- **国际化**: 支持 11 种语言
|
||||||
|
|
||||||
|
## 工作流
|
||||||
|
|
||||||
|
```
|
||||||
|
用户输入
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ 从 GitHub 发现插件 │
|
||||||
|
│ (获取文件树 + 解析 .py) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ 按类型和关键词过滤 │
|
||||||
|
│ (tool/filter/pipe/action) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ 显示确认对话框 │
|
||||||
|
│ (插件列表 + 排除提示) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
├── [取消] → 结束
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ 安装到 OpenWebUI │
|
||||||
|
│ (更新或创建每个插件) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
完成
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 使用方法
|
||||||
|
|
||||||
|
1. 打开 OpenWebUI,进入 **工作区 > 工具**
|
||||||
|
2. 从官方市场安装 **Batch Install Plugins from GitHub**
|
||||||
|
3. 为你的模型/聊天启用此工具
|
||||||
|
4. 让模型安装插件
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
```
|
||||||
|
"安装所有插件"
|
||||||
|
"从 github.com/username/repo 安装所有插件"
|
||||||
|
"仅安装 pipe 插件"
|
||||||
|
"安装 action 和 filter 插件"
|
||||||
|
"安装所有插件,exclude_keywords=copilot"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 热门插件仓库
|
||||||
|
|
||||||
|
这些是包含大量插件的热门仓库,你可以从中安装插件:
|
||||||
|
|
||||||
|
### 社区合集
|
||||||
|
|
||||||
|
```
|
||||||
|
# 从 iChristGit 的集合安装所有插件
|
||||||
|
"从 iChristGit/OpenWebui-Tools 安装所有插件"
|
||||||
|
|
||||||
|
# 从 Haervwe 的工具集合只安装工具
|
||||||
|
"从 Haervwe/open-webui-tools 安装所有插件"
|
||||||
|
|
||||||
|
# 从 Classic298 的仓库安装所有插件
|
||||||
|
"从 Classic298/open-webui-plugins 安装所有插件"
|
||||||
|
|
||||||
|
# 从 suurt8ll 的集合安装所有函数
|
||||||
|
"从 suurt8ll/open_webui_functions 安装所有插件"
|
||||||
|
|
||||||
|
# 仅安装特定类型的插件(比如只安装工具)
|
||||||
|
"从 iChristGit/OpenWebui-Tools 仅安装 tool 插件"
|
||||||
|
|
||||||
|
# 安装时排除特定关键词
|
||||||
|
"从 Haervwe/open-webui-tools 安装所有插件,exclude_keywords=test,deprecated"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 支持的仓库
|
||||||
|
|
||||||
|
- `Fu-Jie/openwebui-extensions` - 默认的官方插件集合
|
||||||
|
- `iChristGit/OpenWebui-Tools` - 全面的工具和插件集合
|
||||||
|
- `Haervwe/open-webui-tools` - 专业的工具和实用程序
|
||||||
|
- `Classic298/open-webui-plugins` - 各种插件实现
|
||||||
|
- `suurt8ll/open_webui_functions` - 基于函数的插件
|
||||||
|
|
||||||
|
## 默认仓库
|
||||||
|
|
||||||
|
未指定仓库时,默认使用 `Fu-Jie/openwebui-extensions`。
|
||||||
|
|
||||||
|
## 插件检测规则
|
||||||
|
|
||||||
|
### Fu-Jie/openwebui-extensions(严格模式)
|
||||||
|
|
||||||
|
对于默认仓库,插件必须有:
|
||||||
|
1. 包含 `class Tools:`、`class Filter:`、`class Pipe:` 或 `class Action:` 的 `.py` 文件
|
||||||
|
2. 包含 `title:`、`description:` 和 **`openwebui_id:`** 字段的文档字符串
|
||||||
|
3. 文件名不能以 `_cn` 结尾
|
||||||
|
|
||||||
|
### 其他 GitHub 仓库
|
||||||
|
|
||||||
|
对于其他仓库:
|
||||||
|
1. 包含 `class Tools:`、`class Filter:`、`class Pipe:` 或 `class Action:` 的 `.py` 文件
|
||||||
|
2. 包含 `title:` 和 `description:` 字段的文档字符串
|
||||||
|
|
||||||
|
## 配置 (Valves)
|
||||||
|
|
||||||
|
| 参数 | 默认值 | 描述 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `SKIP_KEYWORDS` | `test,verify,example,template,mock` | 要跳过的关键词,用逗号分隔 |
|
||||||
|
| `TIMEOUT` | `20` | 请求超时时间(秒) |
|
||||||
|
|
||||||
|
## 确认超时时间
|
||||||
|
|
||||||
|
用户确认对话框的默认超时时间为 **2 分钟(120 秒)**,为用户提供充足的时间来:
|
||||||
|
- 阅读和查看插件列表
|
||||||
|
- 做出安装决定
|
||||||
|
- 处理网络延迟
|
||||||
|
|
||||||
|
## 支持
|
||||||
|
|
||||||
|
如果这个插件对你有帮助,欢迎到 [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) 点个 Star,这将是我持续改进的动力,感谢支持。
|
||||||
@@ -4,5 +4,6 @@ OpenWebUI native Tool plugins that can be used across models.
|
|||||||
|
|
||||||
## Available Tool Plugins
|
## Available Tool Plugins
|
||||||
|
|
||||||
|
- [Batch Install Plugins from GitHub](batch-install-plugins-tool.md) (v1.0.0) - One-click batch install plugins from GitHub repositories with confirmation and multi-language support.
|
||||||
- [OpenWebUI Skills Manager Tool](openwebui-skills-manager-tool.md) (v0.3.0) - Simple native skill management (`list/show/install/create/update/delete`).
|
- [OpenWebUI Skills Manager Tool](openwebui-skills-manager-tool.md) (v0.3.0) - Simple native skill management (`list/show/install/create/update/delete`).
|
||||||
- [Smart Mind Map Tool](smart-mind-map-tool.md) (v1.0.0) - Intelligently analyzes text content and proactively generates interactive mind maps to help users structure and visualize knowledge.
|
- [Smart Mind Map Tool](smart-mind-map-tool.md) (v1.0.0) - Intelligently analyzes text content and proactively generates interactive mind maps to help users structure and visualize knowledge.
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
|
|
||||||
## 可用 Tool 插件
|
## 可用 Tool 插件
|
||||||
|
|
||||||
|
- [Batch Install Plugins from GitHub](batch-install-plugins-tool.zh.md) (v1.0.0) - 一键从 GitHub 仓库批量安装插件,支持确认和多语言。
|
||||||
- [OpenWebUI Skills 管理工具](openwebui-skills-manager-tool.zh.md) (v0.3.0) - 简化技能管理(`list/show/install/create/update/delete`)。
|
- [OpenWebUI Skills 管理工具](openwebui-skills-manager-tool.zh.md) (v0.3.0) - 简化技能管理(`list/show/install/create/update/delete`)。
|
||||||
- [智能思维导图工具 (Smart Mind Map Tool)](smart-mind-map-tool.zh.md) (v1.0.0) - 智能分析文本内容并主动生成交互式思维导图,帮助用户结构化与可视化知识。
|
- [智能思维导图工具 (Smart Mind Map Tool)](smart-mind-map-tool.zh.md) (v1.0.0) - 智能分析文本内容并主动生成交互式思维导图,帮助用户结构化与可视化知识。
|
||||||
|
|||||||
137
plugins/tools/batch-install-plugins/README.md
Normal file
137
plugins/tools/batch-install-plugins/README.md
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
# Batch Install Plugins from GitHub
|
||||||
|
|
||||||
|
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 1.0.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions)
|
||||||
|
|
||||||
|
One-click batch install plugins from GitHub repositories to your OpenWebUI instance.
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
- **One-Click Install**: Install all plugins with a single command
|
||||||
|
- **Auto-Update**: Automatically updates previously installed plugins
|
||||||
|
- **GitHub Support**: Install plugins from any GitHub repository
|
||||||
|
- **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins
|
||||||
|
- **Confirmation**: Shows plugin list before installing, allows selective installation
|
||||||
|
- **i18n**: Supports 11 languages
|
||||||
|
|
||||||
|
## Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
User Input
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Discover Plugins from GitHub │
|
||||||
|
│ (fetch file tree + parse .py) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Filter by Type & Keywords │
|
||||||
|
│ (tool/filter/pipe/action) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Show Confirmation Dialog │
|
||||||
|
│ (list plugins + exclude hint) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
├── [Cancel] → End
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Install to OpenWebUI │
|
||||||
|
│ (update or create each plugin) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Done
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to Use
|
||||||
|
|
||||||
|
1. Open OpenWebUI and go to **Workspace > Tools**
|
||||||
|
2. Install **Batch Install Plugins from GitHub** from the official marketplace
|
||||||
|
3. Enable this tool for your model/chat
|
||||||
|
4. Ask the model to install plugins
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
"Install all plugins"
|
||||||
|
"Install all plugins from github.com/username/repo"
|
||||||
|
"Install only pipe plugins"
|
||||||
|
"Install action and filter plugins"
|
||||||
|
"Install all plugins, exclude_keywords=copilot"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Popular Plugin Repositories
|
||||||
|
|
||||||
|
Here are some popular repositories with many plugins you can install:
|
||||||
|
|
||||||
|
### Community Collections
|
||||||
|
|
||||||
|
```
|
||||||
|
# Install all plugins from iChristGit's collection
|
||||||
|
"Install all plugins from iChristGit/OpenWebui-Tools"
|
||||||
|
|
||||||
|
# Install all tools from Haervwe's tools collection
|
||||||
|
"Install all plugins from Haervwe/open-webui-tools"
|
||||||
|
|
||||||
|
# Install all plugins from Classic298's repository
|
||||||
|
"Install all plugins from Classic298/open-webui-plugins"
|
||||||
|
|
||||||
|
# Install all functions from suurt8ll's collection
|
||||||
|
"Install all plugins from suurt8ll/open_webui_functions"
|
||||||
|
|
||||||
|
# Install only specific types (e.g., only tools)
|
||||||
|
"Install only tool plugins from iChristGit/OpenWebui-Tools"
|
||||||
|
|
||||||
|
# Exclude certain keywords while installing
|
||||||
|
"Install all plugins from Haervwe/open-webui-tools, exclude_keywords=test,deprecated"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Supported Repositories
|
||||||
|
|
||||||
|
- `Fu-Jie/openwebui-extensions` - Default, official plugin collection
|
||||||
|
- `iChristGit/OpenWebui-Tools` - Comprehensive tool and plugin collection
|
||||||
|
- `Haervwe/open-webui-tools` - Specialized tools and utilities
|
||||||
|
- `Classic298/open-webui-plugins` - Various plugin implementations
|
||||||
|
- `suurt8ll/open_webui_functions` - Function-based plugins
|
||||||
|
|
||||||
|
## Default Repository
|
||||||
|
|
||||||
|
When no repository is specified, defaults to `Fu-Jie/openwebui-extensions`.
|
||||||
|
|
||||||
|
## Plugin Detection Rules
|
||||||
|
|
||||||
|
### Fu-Jie/openwebui-extensions (Strict)
|
||||||
|
|
||||||
|
For the default repository, plugins must have:
|
||||||
|
1. A `.py` file containing `class Tools:`, `class Filter:`, `class Pipe:`, or `class Action:`
|
||||||
|
2. A docstring with `title:`, `description:`, and **`openwebui_id:`** fields
|
||||||
|
3. Filename must not end with `_cn`
|
||||||
|
|
||||||
|
### Other GitHub Repositories
|
||||||
|
|
||||||
|
For other repositories:
|
||||||
|
1. A `.py` file containing `class Tools:`, `class Filter:`, `class Pipe:`, or `class Action:`
|
||||||
|
2. A docstring with `title:` and `description:` fields
|
||||||
|
|
||||||
|
## Configuration (Valves)
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `SKIP_KEYWORDS` | `test,verify,example,template,mock` | Comma-separated keywords to skip |
|
||||||
|
| `TIMEOUT` | `20` | Request timeout in seconds |
|
||||||
|
|
||||||
|
## Confirmation Timeout
|
||||||
|
|
||||||
|
User confirmation dialogs have a default timeout of **2 minutes (120 seconds)**, allowing sufficient time for users to:
|
||||||
|
- Read and review the plugin list
|
||||||
|
- Make installation decisions
|
||||||
|
- Handle network delays
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
If this plugin has been useful, a star on [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) is a big motivation for me. Thank you for the support.
|
||||||
137
plugins/tools/batch-install-plugins/README_CN.md
Normal file
137
plugins/tools/batch-install-plugins/README_CN.md
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
# Batch Install Plugins from GitHub
|
||||||
|
|
||||||
|
**作者:** [Fu-Jie](https://github.com/Fu-Jie) | **版本:** 1.0.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions)
|
||||||
|
|
||||||
|
一键将 GitHub 仓库中的插件批量安装到你的 OpenWebUI 实例。
|
||||||
|
|
||||||
|
## 主要功能
|
||||||
|
|
||||||
|
- 一键安装:单个命令安装所有插件
|
||||||
|
- 自动更新:自动更新之前安装过的插件
|
||||||
|
- GitHub 支持:从任意 GitHub 仓库安装插件
|
||||||
|
- 多类型支持:支持 Pipe、Action、Filter 和 Tool 插件
|
||||||
|
- 安装确认:安装前显示插件列表,支持选择性安装
|
||||||
|
- 国际化:支持 11 种语言
|
||||||
|
|
||||||
|
## 流程
|
||||||
|
|
||||||
|
```
|
||||||
|
用户输入
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ 从 GitHub 发现插件 │
|
||||||
|
│ (获取文件树 + 解析 .py 文件) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ 按类型和关键词过滤 │
|
||||||
|
│ (tool/filter/pipe/action) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ 显示确认对话框 │
|
||||||
|
│ (插件列表 + 排除提示) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
├── [取消] → 结束
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ 安装到 OpenWebUI │
|
||||||
|
│ (更新或创建每个插件) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
完成
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
1. 打开 OpenWebUI,进入 **Workspace > Tools**
|
||||||
|
2. 从官方市场安装 **Batch Install Plugins from GitHub**
|
||||||
|
3. 为你的模型/对话启用此工具
|
||||||
|
4. 让模型调用工具方法
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
```
|
||||||
|
"安装所有插件"
|
||||||
|
"从 github.com/username/repo 安装所有插件"
|
||||||
|
"只安装 pipe 插件"
|
||||||
|
"安装 action 和 filter 插件"
|
||||||
|
"安装所有插件, exclude_keywords=copilot"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 热门插件仓库
|
||||||
|
|
||||||
|
这些是包含大量插件的热门仓库,你可以从中安装插件:
|
||||||
|
|
||||||
|
### 社区合集
|
||||||
|
|
||||||
|
```
|
||||||
|
# 从 iChristGit 的集合安装所有插件
|
||||||
|
"从 iChristGit/OpenWebui-Tools 安装所有插件"
|
||||||
|
|
||||||
|
# 从 Haervwe 的工具集合只安装工具
|
||||||
|
"从 Haervwe/open-webui-tools 安装所有插件"
|
||||||
|
|
||||||
|
# 从 Classic298 的仓库安装所有插件
|
||||||
|
"从 Classic298/open-webui-plugins 安装所有插件"
|
||||||
|
|
||||||
|
# 从 suurt8ll 的集合安装所有函数
|
||||||
|
"从 suurt8ll/open_webui_functions 安装所有插件"
|
||||||
|
|
||||||
|
# 只安装特定类型的插件(比如只安装工具)
|
||||||
|
"从 iChristGit/OpenWebui-Tools 只安装 tool 插件"
|
||||||
|
|
||||||
|
# 安装时排除特定关键词
|
||||||
|
"从 Haervwe/open-webui-tools 安装所有插件, exclude_keywords=test,deprecated"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 支持的仓库
|
||||||
|
|
||||||
|
- `Fu-Jie/openwebui-extensions` - 默认的官方插件集合
|
||||||
|
- `iChristGit/OpenWebui-Tools` - 全面的工具和插件集合
|
||||||
|
- `Haervwe/open-webui-tools` - 专业的工具和实用程序
|
||||||
|
- `Classic298/open-webui-plugins` - 各种插件实现
|
||||||
|
- `suurt8ll/open_webui_functions` - 基于函数的插件
|
||||||
|
|
||||||
|
## 默认仓库
|
||||||
|
|
||||||
|
未指定仓库时,默认为 `Fu-Jie/openwebui-extensions`。
|
||||||
|
|
||||||
|
## 插件检测规则
|
||||||
|
|
||||||
|
### Fu-Jie/openwebui-extensions(严格模式)
|
||||||
|
|
||||||
|
默认仓库的插件必须满足:
|
||||||
|
1. 包含 `class Tools:`、`class Filter:`、`class Pipe:` 或 `class Action:` 的 `.py` 文件
|
||||||
|
2. Docstring 中包含 `title:`、`description:` 和 **`openwebui_id:`** 字段
|
||||||
|
3. 文件名不能以 `_cn` 结尾
|
||||||
|
|
||||||
|
### 其他 GitHub 仓库
|
||||||
|
|
||||||
|
其他仓库的插件必须满足:
|
||||||
|
1. 包含 `class Tools:`、`class Filter:`、`class Pipe:` 或 `class Action:` 的 `.py` 文件
|
||||||
|
2. Docstring 中包含 `title:` 和 `description:` 字段
|
||||||
|
|
||||||
|
## 配置(Valves)
|
||||||
|
|
||||||
|
| 参数 | 默认值 | 描述 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `SKIP_KEYWORDS` | `test,verify,example,template,mock` | 逗号分隔的跳过关键词 |
|
||||||
|
| `TIMEOUT` | `20` | 请求超时时间(秒)|
|
||||||
|
|
||||||
|
## 确认超时时间
|
||||||
|
|
||||||
|
用户确认对话框的默认超时时间为 **2 分钟(120 秒)**,为用户提供充足的时间来:
|
||||||
|
- 阅读和查看插件列表
|
||||||
|
- 做出安装决定
|
||||||
|
- 处理网络延迟
|
||||||
|
|
||||||
|
## 支持
|
||||||
|
|
||||||
|
如果这个插件对你有帮助,欢迎到 [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) 点个 Star,这将是我持续改进的动力,感谢支持。
|
||||||
1262
plugins/tools/batch-install-plugins/batch_install_plugins.py
Normal file
1262
plugins/tools/batch-install-plugins/batch_install_plugins.py
Normal file
File diff suppressed because it is too large
Load Diff
67
plugins/tools/batch-install-plugins/v1.0.0.md
Normal file
67
plugins/tools/batch-install-plugins/v1.0.0.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
[](https://openwebui.com/t/fujie/batch_install_plugins)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Batch Install Plugins from GitHub is a new tool for OpenWebUI that enables one-click installation of multiple plugins directly from GitHub repositories. This initial release includes comprehensive features for discovering, filtering, and installing plugins with user confirmation, extensive multi-language support, and robust debugging capabilities for container deployments.
|
||||||
|
|
||||||
|
**[📖 README](https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/tools/batch-install-plugins/README.md)**
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **One-Click Installation**: Install all plugins from a repository with a single command
|
||||||
|
- **Smart Plugin Discovery**: Parse Python files to extract metadata and validate plugins automatically
|
||||||
|
- **Multi-Type Support**: Support Pipe, Action, Filter, and Tool plugins in a single operation
|
||||||
|
- **Confirmation Dialog**: Display plugin list before installation for user review and approval
|
||||||
|
- **Selective Installation**: Exclude specific plugins using keyword-based filtering
|
||||||
|
- **Smart Fallback**: Container deployments auto-retry with localhost:8080 if primary connection fails
|
||||||
|
- **Enhanced Debugging**: Rich frontend JavaScript and backend Python logs for troubleshooting
|
||||||
|
- **Extended Timeout**: 120-second confirmation window for thoughtful decision-making
|
||||||
|
- **Async Architecture**: Non-blocking I/O operations for better performance
|
||||||
|
- **Full Internationalization**: Complete support for 11 languages with proper fallback maps
|
||||||
|
- **Auto-Update**: Automatically updates previously installed plugins
|
||||||
|
- **Self-Exclusion**: Automatically excludes the tool itself from batch operations
|
||||||
|
|
||||||
|
## Technical Highlights
|
||||||
|
|
||||||
|
- **httpx Integration**: Modern async HTTP client for reliable, non-blocking requests
|
||||||
|
- **Event Emitter Support**: Proper handling of OpenWebUI event injection with fallbacks
|
||||||
|
- **Timeout Protection**: Wrapped frontend execution with timeout guards to prevent hanging
|
||||||
|
- **Filtered List Consistency**: Uses single source of truth for confirmation and installation
|
||||||
|
- **Error Localization**: All error messages are user-facing and properly localized across languages
|
||||||
|
- **Deployment Resilience**: Intelligent base URL resolution handles domain, localhost, and containerized environments
|
||||||
|
|
||||||
|
## Supported Repositories
|
||||||
|
|
||||||
|
- **Default**: Fu-Jie/openwebui-extensions (strict validation)
|
||||||
|
- **Custom**: Any GitHub repository with Python plugin files
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Comprehensive regression tests included:
|
||||||
|
- Filtered installation list consistency
|
||||||
|
- Missing event emitter handling
|
||||||
|
- Confirmation timeout verification
|
||||||
|
- Full failure scenarios
|
||||||
|
- Localization completeness
|
||||||
|
- Connection error debug logging and smart fallback
|
||||||
|
|
||||||
|
All 6 tests pass successfully.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- English README with flow diagrams and usage examples
|
||||||
|
- Chinese README (README_CN.md) with complete translations
|
||||||
|
- Mirrored documentation for official docs site
|
||||||
|
- Plugin index entries in both English and Chinese
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
- OpenWebUI: 0.2.x - 0.8.x
|
||||||
|
- Python: 3.9+
|
||||||
|
- Dependencies: httpx (async HTTP client), pydantic (type validation)
|
||||||
|
|
||||||
|
## Release Notes
|
||||||
|
|
||||||
|
- This initial v1.0.0 release includes complete plugin infrastructure with smart deployment handling.
|
||||||
|
- The plugin is designed to handle diverse deployment scenarios (domain, localhost, containerized) with minimal configuration.
|
||||||
|
|
||||||
67
plugins/tools/batch-install-plugins/v1.0.0_CN.md
Normal file
67
plugins/tools/batch-install-plugins/v1.0.0_CN.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
[](https://openwebui.com/t/fujie/batch_install_plugins)
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
从 GitHub 批量安装插件是一款全新的 OpenWebUI 工具,支持直接从 GitHub 仓库一键安装多个插件。此首个发布版本包含了全面的插件发现、过滤和安装功能,支持用户确认流程、广泛的多语言支持,以及针对容器部署的健壮调试能力。
|
||||||
|
|
||||||
|
**[📖 README](https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/tools/batch-install-plugins/README_CN.md)**
|
||||||
|
|
||||||
|
## 主要功能
|
||||||
|
|
||||||
|
- **一键安装**:通过单个命令安装仓库中的所有插件
|
||||||
|
- **智能插件发现**:解析 Python 文件提取元数据并自动验证插件
|
||||||
|
- **多类型支持**:在单个操作中支持 Pipe、Action、Filter 和 Tool 插件
|
||||||
|
- **确认对话框**:安装前显示插件列表供用户审查和批准
|
||||||
|
- **选择性安装**:通过基于关键词的过滤排除特定插件
|
||||||
|
- **智能降级**:容器环境中主 URL 连接失败时自动重试 localhost:8080
|
||||||
|
- **增强调试**:前端 JavaScript 和后端 Python 富日志输出,便于排查问题
|
||||||
|
- **延长超时**:120 秒确认窗口,给用户充分的思考时间
|
||||||
|
- **异步架构**:非阻塞 I/O 操作,性能更优
|
||||||
|
- **完整国际化**:支持 11 种语言,包含适当的回退机制
|
||||||
|
- **自动更新**:自动更新之前安装过的插件
|
||||||
|
- **自排除机制**:自动排除工具自身,避免在批量操作中重复安装
|
||||||
|
|
||||||
|
## 技术亮点
|
||||||
|
|
||||||
|
- **httpx 集成**:现代化的异步 HTTP 客户端,请求更可靠且非阻塞
|
||||||
|
- **事件注入支持**:正确处理 OpenWebUI 事件注入,提供回退支持
|
||||||
|
- **超时保护**:前端执行周围包装了超时保护,防止进程挂起
|
||||||
|
- **过滤列表一致性**:确认和安装使用同一份过滤列表
|
||||||
|
- **错误本地化**:所有错误消息都是面向用户的,已正确本地化到各语言
|
||||||
|
- **部署弹性**:智能 Base URL 解析处理域名、localhost 和容器化环境
|
||||||
|
|
||||||
|
## 支持的仓库
|
||||||
|
|
||||||
|
- **默认**:Fu-Jie/openwebui-extensions(严格验证)
|
||||||
|
- **自定义**:任意 GitHub 仓库中的 Python 插件文件
|
||||||
|
|
||||||
|
## 测试覆盖
|
||||||
|
|
||||||
|
包含全面的回归测试:
|
||||||
|
- 过滤安装列表一致性
|
||||||
|
- 缺少事件注入器时的处理
|
||||||
|
- 确认超时验证
|
||||||
|
- 完全失败场景
|
||||||
|
- 本地化完整性
|
||||||
|
- 连接错误调试日志和智能降级
|
||||||
|
|
||||||
|
所有 6 个测试均通过。
|
||||||
|
|
||||||
|
## 文档
|
||||||
|
|
||||||
|
- 英文 README,包含流程图和使用示例
|
||||||
|
- 中文 README (README_CN.md),完整翻译
|
||||||
|
- 官方文档站点的镜像文档
|
||||||
|
- 英文和中文的插件索引条目
|
||||||
|
|
||||||
|
## 兼容性
|
||||||
|
|
||||||
|
- OpenWebUI:0.2.x - 0.8.x
|
||||||
|
- Python:3.9+
|
||||||
|
- 依赖:httpx(异步 HTTP 客户端)、pydantic(类型验证)
|
||||||
|
|
||||||
|
## 发布说明
|
||||||
|
|
||||||
|
- 本首发 v1.0.0 版本包含完整的插件基础设施和智能部署处理能力。
|
||||||
|
- 该插件设计用于处理多种部署场景(域名、localhost、容器化),配置最少。
|
||||||
|
|
||||||
302
tests/plugins/tools/test_batch_install_plugins.py
Normal file
302
tests/plugins/tools/test_batch_install_plugins.py
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
import asyncio
|
||||||
|
import importlib.util
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
MODULE_PATH = (
|
||||||
|
Path(__file__).resolve().parents[3]
|
||||||
|
/ "plugins"
|
||||||
|
/ "tools"
|
||||||
|
/ "batch-install-plugins"
|
||||||
|
/ "batch_install_plugins.py"
|
||||||
|
)
|
||||||
|
SPEC = importlib.util.spec_from_file_location("batch_install_plugins", MODULE_PATH)
|
||||||
|
batch_install_plugins = importlib.util.module_from_spec(SPEC)
|
||||||
|
assert SPEC.loader is not None
|
||||||
|
sys.modules[SPEC.name] = batch_install_plugins
|
||||||
|
SPEC.loader.exec_module(batch_install_plugins)
|
||||||
|
|
||||||
|
|
||||||
|
def make_candidate(title: str, file_path: str, function_id: str):
|
||||||
|
return batch_install_plugins.PluginCandidate(
|
||||||
|
plugin_type="tool",
|
||||||
|
file_path=file_path,
|
||||||
|
metadata={"title": title, "description": f"{title} description"},
|
||||||
|
content="class Tools:\n pass\n",
|
||||||
|
function_id=function_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def make_request():
|
||||||
|
class Request:
|
||||||
|
base_url = "http://localhost:3000/"
|
||||||
|
headers = {"Authorization": "Bearer token"}
|
||||||
|
|
||||||
|
return Request()
|
||||||
|
|
||||||
|
|
||||||
|
class DummyResponse:
|
||||||
|
def __init__(self, status_code: int, json_data=None, text: str = ""):
|
||||||
|
self.status_code = status_code
|
||||||
|
self._json_data = json_data
|
||||||
|
self.text = text
|
||||||
|
|
||||||
|
def json(self):
|
||||||
|
if self._json_data is None:
|
||||||
|
raise ValueError("no json body")
|
||||||
|
return self._json_data
|
||||||
|
|
||||||
|
|
||||||
|
class FakeAsyncClient:
|
||||||
|
posts = []
|
||||||
|
responses = []
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def __aenter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(self, exc_type, exc, tb):
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def post(self, url, headers=None, json=None):
|
||||||
|
type(self).posts.append((url, headers, json))
|
||||||
|
if not type(self).responses:
|
||||||
|
raise AssertionError("No fake response configured for POST request")
|
||||||
|
response = type(self).responses.pop(0)
|
||||||
|
if isinstance(response, Exception):
|
||||||
|
raise response
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_install_all_plugins_only_installs_filtered_candidates(monkeypatch):
|
||||||
|
keep = make_candidate("Keep Plugin", "plugins/tools/keep/keep.py", "keep_plugin")
|
||||||
|
exclude = make_candidate(
|
||||||
|
"Exclude Me",
|
||||||
|
"plugins/tools/exclude-me/exclude_me.py",
|
||||||
|
"exclude_me",
|
||||||
|
)
|
||||||
|
self_plugin = make_candidate(
|
||||||
|
"Batch Install Plugins from GitHub",
|
||||||
|
"plugins/tools/batch-install-plugins/batch_install_plugins.py",
|
||||||
|
"batch_install_plugins",
|
||||||
|
)
|
||||||
|
|
||||||
|
async def fake_discover_plugins(url, skip_keywords):
|
||||||
|
return [keep, exclude, self_plugin], []
|
||||||
|
|
||||||
|
monkeypatch.setattr(batch_install_plugins, "discover_plugins", fake_discover_plugins)
|
||||||
|
FakeAsyncClient.posts = []
|
||||||
|
FakeAsyncClient.responses = [DummyResponse(404), DummyResponse(201)]
|
||||||
|
monkeypatch.setattr(batch_install_plugins.httpx, "AsyncClient", FakeAsyncClient)
|
||||||
|
|
||||||
|
events = []
|
||||||
|
captured = {}
|
||||||
|
|
||||||
|
async def event_call(payload):
|
||||||
|
if payload["type"] == "confirmation":
|
||||||
|
captured["message"] = payload["data"]["message"]
|
||||||
|
elif payload["type"] == "execute":
|
||||||
|
captured.setdefault("execute_codes", []).append(payload["data"]["code"])
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def emitter(event):
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
result = await batch_install_plugins.Tools().install_all_plugins(
|
||||||
|
__user__={"id": "u1", "language": "en-US"},
|
||||||
|
__event_call__=event_call,
|
||||||
|
__request__=make_request(),
|
||||||
|
__event_emitter__=emitter,
|
||||||
|
repo=batch_install_plugins.DEFAULT_REPO,
|
||||||
|
plugin_types=["tool"],
|
||||||
|
exclude_keywords="exclude",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "Created: Keep Plugin" in result
|
||||||
|
assert "Exclude Me" not in result
|
||||||
|
assert "1/1" in result
|
||||||
|
assert captured["message"].count("[tool]") == 1
|
||||||
|
assert "Keep Plugin" in captured["message"]
|
||||||
|
assert "Exclude Me" not in captured["message"]
|
||||||
|
assert "Batch Install Plugins from GitHub" not in captured["message"]
|
||||||
|
assert "exclude, batch-install-plugins" in captured["message"]
|
||||||
|
|
||||||
|
urls = [url for url, _, _ in FakeAsyncClient.posts]
|
||||||
|
assert urls == [
|
||||||
|
"http://localhost:3000/api/v1/tools/id/keep_plugin/update",
|
||||||
|
"http://localhost:3000/api/v1/tools/create",
|
||||||
|
]
|
||||||
|
assert any(
|
||||||
|
"Starting OpenWebUI install requests" in code
|
||||||
|
for code in captured.get("execute_codes", [])
|
||||||
|
)
|
||||||
|
assert events[-1]["type"] == "notification"
|
||||||
|
assert events[-1]["data"]["type"] == "success"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_install_all_plugins_supports_missing_event_emitter(monkeypatch):
|
||||||
|
keep = make_candidate("Keep Plugin", "plugins/tools/keep/keep.py", "keep_plugin")
|
||||||
|
|
||||||
|
async def fake_discover_plugins(url, skip_keywords):
|
||||||
|
return [keep], []
|
||||||
|
|
||||||
|
monkeypatch.setattr(batch_install_plugins, "discover_plugins", fake_discover_plugins)
|
||||||
|
FakeAsyncClient.posts = []
|
||||||
|
FakeAsyncClient.responses = [DummyResponse(404), DummyResponse(201)]
|
||||||
|
monkeypatch.setattr(batch_install_plugins.httpx, "AsyncClient", FakeAsyncClient)
|
||||||
|
|
||||||
|
result = await batch_install_plugins.Tools().install_all_plugins(
|
||||||
|
__user__={"id": "u1", "language": "en-US"},
|
||||||
|
__request__=make_request(),
|
||||||
|
repo="example/repo",
|
||||||
|
plugin_types=["tool"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "Created: Keep Plugin" in result
|
||||||
|
assert "1/1" in result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_install_all_plugins_handles_confirmation_timeout(monkeypatch):
|
||||||
|
keep = make_candidate("Keep Plugin", "plugins/tools/keep/keep.py", "keep_plugin")
|
||||||
|
|
||||||
|
async def fake_discover_plugins(url, skip_keywords):
|
||||||
|
return [keep], []
|
||||||
|
|
||||||
|
async def fake_wait_for(awaitable, timeout):
|
||||||
|
awaitable.close()
|
||||||
|
raise asyncio.TimeoutError
|
||||||
|
|
||||||
|
monkeypatch.setattr(batch_install_plugins, "discover_plugins", fake_discover_plugins)
|
||||||
|
monkeypatch.setattr(batch_install_plugins.asyncio, "wait_for", fake_wait_for)
|
||||||
|
|
||||||
|
events = []
|
||||||
|
|
||||||
|
async def event_call(payload):
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def emitter(event):
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
result = await batch_install_plugins.Tools().install_all_plugins(
|
||||||
|
__user__={"id": "u1", "language": "en-US"},
|
||||||
|
__event_call__=event_call,
|
||||||
|
__request__=make_request(),
|
||||||
|
__event_emitter__=emitter,
|
||||||
|
repo="example/repo",
|
||||||
|
plugin_types=["tool"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result == "Confirmation timed out or failed. Installation cancelled."
|
||||||
|
assert events[-1]["type"] == "notification"
|
||||||
|
assert events[-1]["data"]["type"] == "warning"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_install_all_plugins_marks_total_failure_as_error(monkeypatch):
|
||||||
|
keep = make_candidate("Keep Plugin", "plugins/tools/keep/keep.py", "keep_plugin")
|
||||||
|
|
||||||
|
async def fake_discover_plugins(url, skip_keywords):
|
||||||
|
return [keep], []
|
||||||
|
|
||||||
|
monkeypatch.setattr(batch_install_plugins, "discover_plugins", fake_discover_plugins)
|
||||||
|
FakeAsyncClient.posts = []
|
||||||
|
FakeAsyncClient.responses = [
|
||||||
|
DummyResponse(500, {"detail": "update failed"}, "update failed"),
|
||||||
|
DummyResponse(500, {"detail": "create failed"}, "create failed"),
|
||||||
|
]
|
||||||
|
monkeypatch.setattr(batch_install_plugins.httpx, "AsyncClient", FakeAsyncClient)
|
||||||
|
|
||||||
|
events = []
|
||||||
|
|
||||||
|
async def emitter(event):
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
result = await batch_install_plugins.Tools().install_all_plugins(
|
||||||
|
__user__={"id": "u1", "language": "en-US"},
|
||||||
|
__request__=make_request(),
|
||||||
|
__event_emitter__=emitter,
|
||||||
|
repo="example/repo",
|
||||||
|
plugin_types=["tool"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "Failed: Keep Plugin - status 500:" in result
|
||||||
|
assert "0/1" in result
|
||||||
|
assert events[-1]["type"] == "notification"
|
||||||
|
assert events[-1]["data"]["type"] == "error"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_install_all_plugins_localizes_timeout_errors(monkeypatch):
|
||||||
|
keep = make_candidate("Keep Plugin", "plugins/tools/keep/keep.py", "keep_plugin")
|
||||||
|
|
||||||
|
async def fake_discover_plugins(url, skip_keywords):
|
||||||
|
return [keep], []
|
||||||
|
|
||||||
|
monkeypatch.setattr(batch_install_plugins, "discover_plugins", fake_discover_plugins)
|
||||||
|
FakeAsyncClient.posts = []
|
||||||
|
FakeAsyncClient.responses = [httpx.TimeoutException("timed out")]
|
||||||
|
monkeypatch.setattr(batch_install_plugins.httpx, "AsyncClient", FakeAsyncClient)
|
||||||
|
|
||||||
|
result = await batch_install_plugins.Tools().install_all_plugins(
|
||||||
|
__user__={"id": "u1", "language": "zh-CN"},
|
||||||
|
__request__=make_request(),
|
||||||
|
repo="example/repo",
|
||||||
|
plugin_types=["tool"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "失败:Keep Plugin - 请求超时" in result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_install_all_plugins_emits_frontend_debug_logs_on_connect_error(
|
||||||
|
monkeypatch,
|
||||||
|
):
|
||||||
|
keep = make_candidate("Keep Plugin", "plugins/tools/keep/keep.py", "keep_plugin")
|
||||||
|
|
||||||
|
async def fake_discover_plugins(url, skip_keywords):
|
||||||
|
return [keep], []
|
||||||
|
|
||||||
|
monkeypatch.setattr(batch_install_plugins, "discover_plugins", fake_discover_plugins)
|
||||||
|
FakeAsyncClient.posts = []
|
||||||
|
# Both initial attempt and fallback retry should fail
|
||||||
|
FakeAsyncClient.responses = [httpx.ConnectError("connect failed"), httpx.ConnectError("connect failed")]
|
||||||
|
monkeypatch.setattr(batch_install_plugins.httpx, "AsyncClient", FakeAsyncClient)
|
||||||
|
|
||||||
|
execute_codes = []
|
||||||
|
events = []
|
||||||
|
|
||||||
|
async def event_call(payload):
|
||||||
|
if payload["type"] == "execute":
|
||||||
|
execute_codes.append(payload["data"]["code"])
|
||||||
|
return True
|
||||||
|
if payload["type"] == "confirmation":
|
||||||
|
return True
|
||||||
|
raise AssertionError(f"Unexpected event_call payload type: {payload['type']}")
|
||||||
|
|
||||||
|
async def emitter(event):
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
result = await batch_install_plugins.Tools().install_all_plugins(
|
||||||
|
__user__={"id": "u1", "language": "en-US"},
|
||||||
|
__event_call__=event_call,
|
||||||
|
__request__=make_request(),
|
||||||
|
__event_emitter__=emitter,
|
||||||
|
repo="example/repo",
|
||||||
|
plugin_types=["tool"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result == "Cannot connect to OpenWebUI. Is it running?"
|
||||||
|
assert any("OpenWebUI connection failed" in code for code in execute_codes)
|
||||||
|
assert any("console.error" in code for code in execute_codes)
|
||||||
|
assert any("http://localhost:3000" in code for code in execute_codes)
|
||||||
|
assert events[-1]["type"] == "notification"
|
||||||
|
assert events[-1]["data"]["type"] == "error"
|
||||||
Reference in New Issue
Block a user