feat: 新增插件系统、多种插件类型、开发指南及多语言文档。

This commit is contained in:
fujie
2025-12-20 12:34:49 +08:00
commit eaa6319991
74 changed files with 28409 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
# Gemini Manifold 插件开发哲学
## 概览
- 源码位于 `plugins/pipes/gemini_mainfold/gemini_manifold.py`,作为 Open WebUI 的 Pipe 插件,主要负责把前端的请求转化成 Google Gemini/Vertex AI 的调用,并将结果通过 `AsyncGenerator` 回流给前端。
- 插件采用了 `Valves + UserValves` 的配置模式、异步事件与状态汇报、细粒度日志、文件缓存与上传中间件,以及统一响应处理器,充分体现了 Open WebUI 通用插件的开发模式。
## 核心模块
1. **`Pipe` 类(入口)**
- `pipes` 方法注册可选模型,缓存模型列表并仅在配置变更时刷新。
- `pipe` 方法为每个请求建立 Google GenAI 客户端、`EventEmitter``FilesAPIManager`,构建 `GeminiContentBuilder`,并统一把返回值交给 `_unified_response_processor`
2. **`GeminiContentBuilder`(请求构建)**
- 解析 Open WebUI 消息、引用历史、文件上传、YouTube/Markdown 媒体等内容,并通过 `UploadStatusManager``FilesAPIManager` 协作,确保上传进度可视。
3. **`FilesAPIManager`(文件缓存+上传)**
- 采用 xxHash 内容地址、热/暖/冷路径、自定义锁、 TTL 缓存等手段防止重复上传,同时会在发生错误时用 `FilesAPIError` 抛出并告知前端。
4. **`EventEmitter` + `UploadStatusManager`(状态反馈)**
- 抽象 Toast/Status/Completion 的交互,按需异步发送,赋予前端实时反馈能力,避免阻塞主流程。
5. **统一响应处理与后置处理**
- `_unified_response_processor` 兼容流式/一次性响应,调用 `_process_part``_disable_special_tags` 保护前端,再在 `_do_post_processing` 发出 usage、grounding 等数据。
## 与 Open WebUI 插件哲学契合的实践
- **配置层叠与安全**`Valves` 提供 admin 默认,`UserValves` 允许用户按需覆盖。`USER_MUST_PROVIDE_AUTH_CONFIG` + `AUTH_WHITELIST` 确保敏感场景必须使用各自凭证。
- **异步状态与进度可视**:所有上传/调用都在 `EventEmitter` 中报告 toast/status`UploadStatusManager` 用 queue 追踪并呈现进度,流式响应直接产出 `choices` chunk 与 `[DONE]`,前端无需额外猜测。
- **功能可拓展性**:基于 `Functions.get_function_by_id` 检查 filter、依据 `features`/`toggle` 开启 Search、Code Execution、URL Context、Maps grounding体现 Open WebUI 组件可组合的插件模型。
- **文件与资源复用**`FilesAPIManager` 通过 deterministic name、缓存、stateless GET提高效率生成图片也回写到 Open WebUI 的 `Files` 模型,为前端提供可持久化的 URL。
- **透明日志与错误可控**:自定义 loguru handler截断 `payload`)、统一的异常类、对 SDK 错误的 toast+error emission、对特殊 tag 的 ZWS 处理,确保插件运行时的状态始终可追踪并兼容前端。
- **统一流程**:全链路从 request -> builder -> client -> response processor -> post-processing严格对齐 Open WebUI pipe+filter 结构,便于扩展和维护。
## 下一步建议
- 如果需要,可将上述内容整理到 Plugin README/开发指南中。也可以基于该文档再绘制流程图或生成寄语性文档,供团队使用。
## 进一步参考
详细的代码示例与使用场景请参见 `docs/gemini_manifold_plugin_examples.md`,包括:
- 配置层叠、异步事件、文件缓存等基础模式
- 响应处理、标签防护、异常管理等中级技巧
- 后处理流程、日志控制等高级实践

View File

@@ -0,0 +1,50 @@
# 开源项目重组实施计划
## 1. 目标
`openwebui-extras` 打造为一个 **OpenWebUI 增强功能集合库**,专注于分享个人开发和收集的优质插件、提示词,而非作为一个独立的 Python 应用程序发布。
## 2. 当前状态分析
- **定位明确**项目核心价值在于内容Plugins, Prompts, Docs而非运行环境。
- **结构已优化**
- `plugins/`:核心插件资源。
- `prompts/`:提示词资源。
- `docs/`:详细的使用和开发文档。
- `scripts/`:辅助工具脚本(如本地测试用的 `run.py`)。
- **已移除不必要文件**:移除了 `requirements.txt`,避免用户误以为需要配置 Python 环境。
## 3. 重组方案
### 3.1 目录结构
保持当前的清晰结构,强调“拿来即用”:
```
openwebui-extras/
├── docs/ # 文档与教程
├── plugins/ # 插件库 (核心资源)
│ ├── actions/
│ ├── filters/
│ ├── pipelines/
│ └── pipes/
├── prompts/ # 提示词库 (核心资源)
├── scripts/ # 维护者工具 (非用户必须)
├── LICENSE # MIT 许可证
├── README.md # 项目入口与资源索引
└── index.html # 项目展示页
```
### 3.2 核心调整
1. **移除依赖管理**:删除了 `requirements.txt`。用户不需要 `pip install` 任何东西,只需下载对应的 `.py``.md` 文件导入 OpenWebUI 即可。
2. **文档侧重**README 和文档将侧重于“如何下载”和“如何导入”,而不是“如何安装项目”。
### 3.3 后续建议
1. **资源索引**:建议在 `README.md` 中维护一个高质量的插件/提示词索引表,方便用户快速查找。
2. **贡献指南**:制定简单的 `CONTRIBUTING.md`,告诉其他人如何提交他们的插件或提示词(例如:只需提交文件到对应目录)。
3. **版本控制**:虽然不需要 Python 环境,但建议在插件文件的头部注释中保留版本号和兼容性说明(如 `Compatible with OpenWebUI v0.3.x`)。
## 4. 发布流程
1. **提交更改**`git add . && git commit -m "Update project structure for resource sharing"`
2. **推送到 GitHub**
3. **宣传**:在 OpenWebUI 社区分享此仓库链接。
---
*生成时间2025-12-19*

View File

@@ -0,0 +1,234 @@
# OpenWebUI Plugin Development Guide
> This guide consolidates official documentation, SDK details, and best practices to provide a systematic tutorial for developers, from beginner to expert.
## 📚 Table of Contents
1. [Quick Start](#1-quick-start)
2. [Core Concepts & SDK Details](#2-core-concepts--sdk-details)
3. [Deep Dive into Plugin Types](#3-deep-dive-into-plugin-types)
* [Action](#31-action)
* [Filter](#32-filter)
* [Pipe](#33-pipe)
4. [Advanced Development Patterns](#4-advanced-development-patterns)
5. [Best Practices & Design Principles](#5-best-practices--design-principles)
6. [Troubleshooting](#6-troubleshooting)
---
## 1. Quick Start
### 1.1 What are OpenWebUI Plugins?
OpenWebUI Plugins (officially called "Functions") are the primary way to extend the platform's capabilities. Running in a backend Python environment, they allow you to:
* 🔌 **Integrate New Models**: Connect to Claude, Gemini, or custom RAGs via Pipes.
* 🎨 **Enhance Interaction**: Add buttons (e.g., "Export", "Generate Chart") next to messages via Actions.
* 🔧 **Intervene in Processes**: Modify data before requests or after responses (e.g., inject context, filter sensitive words) via Filters.
### 1.2 Your First Plugin (Hello World)
Save the following code as `hello.py` and upload it to the **Functions** panel in OpenWebUI:
```python
"""
title: Hello World Action
author: Demo
version: 1.0.0
"""
from pydantic import BaseModel, Field
from typing import Optional
class Action:
class Valves(BaseModel):
greeting: str = Field(default="Hello", description="Greeting message")
def __init__(self):
self.valves = self.Valves()
async def action(
self,
body: dict,
__event_emitter__=None,
__user__=None
) -> Optional[dict]:
user_name = __user__.get("name", "Friend") if __user__ else "Friend"
if __event_emitter__:
await __event_emitter__({
"type": "notification",
"data": {"type": "success", "content": f"{self.valves.greeting}, {user_name}!"}
})
return body
```
---
## 2. Core Concepts & SDK Details
### 2.1 ⚠️ Important: Sync vs Async
OpenWebUI plugins run within an `asyncio` event loop.
* **Principle**: All I/O operations (database, file, network) must be non-blocking.
* **Pitfall**: Calling synchronous methods directly (e.g., `time.sleep`, `requests.get`) will freeze the entire server.
* **Solution**: Wrap synchronous calls using `await asyncio.to_thread(sync_func, ...)`.
### 2.2 Core Parameters
All plugin methods (`inlet`, `outlet`, `pipe`, `action`) support injecting the following special parameters:
| Parameter | Type | Description |
| :--- | :--- | :--- |
| `body` | `dict` | **Core Data**. Contains request info like `messages`, `model`, `stream`. |
| `__user__` | `dict` | **Current User**. Contains `id`, `name`, `role`, `valves` (user config), etc. |
| `__metadata__` | `dict` | **Metadata**. Contains `chat_id`, `message_id`. The `variables` field holds preset variables like `{{USER_NAME}}`, `{{CURRENT_TIME}}`. |
| `__request__` | `Request` | **FastAPI Request Object**. Access `app.state` for cross-plugin communication. |
| `__event_emitter__` | `func` | **One-way Notification**. Used to send Toast notifications or status bar updates. |
| `__event_call__` | `func` | **Two-way Interaction**. Used to execute JS code, show confirmation dialogs, or input boxes on the frontend. |
### 2.3 Configuration System (Valves)
* **`Valves`**: Global admin configuration.
* **`UserValves`**: User-level configuration (higher priority, overrides global).
```python
class Filter:
class Valves(BaseModel):
API_KEY: str = Field(default="", description="Global API Key")
class UserValves(BaseModel):
API_KEY: str = Field(default="", description="User Private API Key")
def inlet(self, body, __user__):
# Prioritize user's Key
user_valves = __user__.get("valves", self.UserValves())
api_key = user_valves.API_KEY or self.valves.API_KEY
```
---
## 3. Deep Dive into Plugin Types
### 3.1 Action
**Role**: Adds buttons below messages that trigger upon user click.
**Advanced Usage: Execute JavaScript on Frontend (File Download Example)**
```python
import base64
async def action(self, body, __event_call__):
# 1. Generate content on backend
content = "Hello OpenWebUI".encode()
b64 = base64.b64encode(content).decode()
# 2. Send JS to frontend for execution
js = f"""
const blob = new Blob([atob('{b64}')], {{type: 'text/plain'}});
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'hello.txt';
a.click();
"""
await __event_call__({"type": "execute", "data": {"code": js}})
```
### 3.2 Filter
**Role**: Middleware that intercepts and modifies requests/responses.
* **`inlet`**: Before request. Used for injecting context, modifying model parameters.
* **`outlet`**: After response. Used for formatting output, logging.
* **`stream`**: During streaming. Used for real-time sensitive word filtering.
**Example: Injecting Environment Variables**
```python
async def inlet(self, body, __metadata__):
vars = __metadata__.get("variables", {})
context = f"Current Time: {vars.get('{{CURRENT_DATETIME}}')}"
# Inject into System Prompt or first message
if body.get("messages"):
body["messages"][0]["content"] += f"\n\n{context}"
return body
```
### 3.3 Pipe
**Role**: Custom Model/Agent.
**Example: Simple OpenAI Wrapper**
```python
import requests
class Pipe:
def pipes(self):
return [{"id": "my-gpt", "name": "My GPT Wrapper"}]
def pipe(self, body):
# Modify body here, e.g., force add prompt
headers = {"Authorization": f"Bearer {self.valves.API_KEY}"}
r = requests.post("https://api.openai.com/v1/chat/completions", json=body, headers=headers, stream=True)
return r.iter_lines()
```
---
## 4. Advanced Development Patterns
### 4.1 Pipe & Filter Collaboration
Use `__request__.app.state` to share data between plugins.
* **Pipe**: `__request__.app.state.search_results = [...]`
* **Filter (Outlet)**: Read `search_results` and format them as citation links appended to the response.
### 4.2 Async Background Tasks
Execute time-consuming operations (e.g., summarization, database storage) in the background without blocking the user response.
```python
import asyncio
async def outlet(self, body, __metadata__):
asyncio.create_task(self.background_job(__metadata__["chat_id"]))
return body
async def background_job(self, chat_id):
# Execute time-consuming operation...
pass
```
---
## 5. Best Practices & Design Principles
### 5.1 Naming & Positioning
* **Short & Punchy**: e.g., "FlashCard", "DeepRead". Avoid generic terms like "Text Analysis Assistant".
* **Complementary**: Don't reinvent the wheel; clarify what specific problem your plugin solves.
### 5.2 User Experience (UX)
* **Timely Feedback**: Send a `notification` ("Generating...") before time-consuming operations.
* **Visual Appeal**: When Action outputs HTML, use modern CSS (rounded corners, shadows, gradients).
* **Smart Guidance**: If text is too short, prompt the user: "Suggest entering more content for better results".
### 5.3 Error Handling
Never let a plugin fail silently. Catch exceptions and inform the user via `__event_emitter__`.
```python
try:
# Business logic
except Exception as e:
await __event_emitter__({
"type": "notification",
"data": {"type": "error", "content": f"Processing failed: {str(e)}"}
})
```
---
## 6. Troubleshooting
* **HTML not showing?** Ensure it's wrapped in a ` ```html ... ``` ` code block.
* **Database error?** Check if you called synchronous DB methods directly in an `async` function; use `asyncio.to_thread`.
* **Parameters not working?** Check if `Valves` are defined correctly and if they are being overridden by `UserValves`.