13 KiB
Copilot Instructions for awesome-openwebui
本文档定义了 OpenWebUI 插件开发的标准规范和最佳实践。Copilot 在生成代码或文档时应遵循这些准则。
This document defines the standard conventions and best practices for OpenWebUI plugin development. Copilot should follow these guidelines when generating code or documentation.
📚 双语版本要求 (Bilingual Version Requirements)
插件代码 (Plugin Code)
每个插件必须提供两个版本:
- 英文版本:
plugin_name.py- 英文界面、提示词和注释 - 中文版本:
plugin_name_cn.py或插件中文名.py- 中文界面、提示词和注释
示例:
plugins/actions/export_to_docx/
├── export_to_word.py # English version
├── 导出为Word.py # Chinese version
├── README.md # English documentation
└── README_CN.md # Chinese documentation
文档 (Documentation)
每个插件目录必须包含双语 README 文件:
README.md- English documentationREADME_CN.md- 中文文档
README 文件应包含以下内容:
- 功能描述 / Feature description
- 配置参数及默认值 / Configuration parameters with defaults
- 安装和设置说明 / Installation and setup instructions
- 使用示例 / Usage examples
- 故障排除指南 / Troubleshooting guide
- 版本和作者信息 / Version and author information
官方文档 (Official Documentation)
如果插件被合并到主仓库,还需更新 docs/ 目录下的相关文档:
docs/plugins/{type}/plugin-name.mddocs/plugins/{type}/plugin-name.zh.md
其中 {type} 对应插件类型(如 actions, filters, pipes 等)。
📝 文档字符串规范 (Docstring Standard)
每个插件文件必须以标准化的文档字符串开头:
"""
title: 插件名称 (Plugin Name)
author: Fu-Jie
author_url: https://github.com/Fu-Jie
funding_url: https://github.com/Fu-Jie/awesome-openwebui
version: 0.1.0
icon_url: data:image/svg+xml;base64,<base64-encoded-svg>
requirements: dependency1==1.0.0, dependency2>=2.0.0
description: 插件功能的简短描述。Brief description of plugin functionality.
"""
字段说明 (Field Descriptions)
| 字段 (Field) | 说明 (Description) | 示例 (Example) |
|---|---|---|
title |
插件显示名称 | Export to Word / 导出为 Word |
author |
作者名称 | Fu-Jie |
author_url |
作者主页链接 | https://github.com/Fu-Jie |
funding_url |
赞助/项目链接 | https://github.com/Fu-Jie/awesome-openwebui |
version |
语义化版本号 | 0.1.0, 1.2.3 |
icon_url |
图标 (Base64 编码的 SVG) | 见下方图标规范 |
requirements |
额外依赖 (仅 OpenWebUI 环境未安装的) | python-docx==1.1.2 |
description |
功能描述 | 将对话导出为 Word 文档 |
图标规范 (Icon Guidelines)
- 图标来源:从 Lucide Icons 获取符合插件功能的图标
- 格式:Base64 编码的 SVG
- 获取方法:从 Lucide 下载 SVG,然后使用 Base64 编码
- 示例格式:
icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0i...(完整的 Base64 编码字符串)
👤 作者和许可证信息 (Author and License)
所有 README 文件和主要文档必须包含以下统一信息:
## Author
Fu-Jie
GitHub: [Fu-Jie/awesome-openwebui](https://github.com/Fu-Jie/awesome-openwebui)
## License
MIT License
中文版本:
## 作者
Fu-Jie
GitHub: [Fu-Jie/awesome-openwebui](https://github.com/Fu-Jie/awesome-openwebui)
## 许可证
MIT License
🏗️ 插件目录结构 (Plugin Directory Structure)
plugins/
├── actions/ # Action 插件 (用户触发的功能)
│ ├── my_action/
│ │ ├── my_action.py # English version
│ │ ├── 我的动作.py # Chinese version
│ │ ├── README.md # English documentation
│ │ └── README_CN.md # Chinese documentation
│ ├── ACTION_PLUGIN_TEMPLATE.py # English template
│ ├── ACTION_PLUGIN_TEMPLATE_CN.py # Chinese template
│ └── README.md
├── filters/ # Filter 插件 (输入处理)
│ ├── my_filter/
│ │ ├── my_filter.py
│ │ ├── 我的过滤器.py
│ │ ├── README.md
│ │ └── README_CN.md
│ └── README.md
├── pipes/ # Pipe 插件 (输出处理)
│ └── ...
└── pipelines/ # Pipeline 插件
└── ...
⚙️ Valves 配置规范 (Valves Configuration)
使用 Pydantic BaseModel 定义可配置参数:
from pydantic import BaseModel, Field
class Action:
class Valves(BaseModel):
SHOW_STATUS: bool = Field(
default=True,
description="Whether to show operation status updates."
)
MODEL_ID: str = Field(
default="",
description="Built-in LLM Model ID. If empty, uses current conversation model."
)
MIN_TEXT_LENGTH: int = Field(
default=50,
description="Minimum text length required for processing (characters)."
)
CLEAR_PREVIOUS_HTML: bool = Field(
default=False,
description="Whether to clear previous plugin results."
)
MESSAGE_COUNT: int = Field(
default=1,
description="Number of recent messages to use for generation."
)
def __init__(self):
self.valves = self.Valves()
命名规则 (Naming Convention)
- 所有 Valves 字段使用 大写下划线 (UPPER_SNAKE_CASE)
- 示例:
SHOW_STATUS,MODEL_ID,MIN_TEXT_LENGTH
📤 事件发送规范 (Event Emission)
必须实现以下辅助方法:
async def _emit_status(
self,
emitter: Optional[Callable[[Any], Awaitable[None]]],
description: str,
done: bool = False,
):
"""Emits a status update event."""
if self.valves.SHOW_STATUS and emitter:
await emitter(
{"type": "status", "data": {"description": description, "done": done}}
)
async def _emit_notification(
self,
emitter: Optional[Callable[[Any], Awaitable[None]]],
content: str,
type: str = "info",
):
"""Emits a notification event (info, success, warning, error)."""
if emitter:
await emitter(
{"type": "notification", "data": {"type": type, "content": content}}
)
📋 日志规范 (Logging Standard)
- 禁止使用
print()语句 - 必须使用 Python 标准库
logging
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
# 记录关键操作
logger.info(f"Action: {__name__} started")
# 记录异常 (包含堆栈信息)
logger.error(f"Processing failed: {e}", exc_info=True)
🎨 HTML 注入规范 (HTML Injection)
使用统一的标记和结构:
# HTML 包装器标记
HTML_WRAPPER_TEMPLATE = """
<!-- OPENWEBUI_PLUGIN_OUTPUT -->
<!DOCTYPE html>
<html lang="{user_language}">
<head>
<meta charset="UTF-8">
<style>
/* STYLES_INSERTION_POINT */
</style>
</head>
<body>
<div id="main-container">
<!-- CONTENT_INSERTION_POINT -->
</div>
<!-- SCRIPTS_INSERTION_POINT -->
</body>
</html>
"""
必须实现 HTML 合并方法以支持多次运行插件:
def _remove_existing_html(self, content: str) -> str:
"""Removes existing plugin-generated HTML code blocks."""
pattern = r"```html\s*<!-- OPENWEBUI_PLUGIN_OUTPUT -->[\s\S]*?```"
return re.sub(pattern, "", content).strip()
def _merge_html(
self,
existing_html: str,
new_content: str,
new_styles: str = "",
new_scripts: str = "",
user_language: str = "en-US",
) -> str:
"""
Merges new content into existing HTML container.
See ACTION_PLUGIN_TEMPLATE.py for full implementation.
"""
pass # Implement based on template
🌍 多语言支持 (Internationalization)
从用户上下文获取语言偏好:
def _get_user_context(self, __user__: Optional[Dict[str, Any]]) -> Dict[str, str]:
"""Extracts user context information."""
if isinstance(__user__, (list, tuple)):
user_data = __user__[0] if __user__ else {}
elif isinstance(__user__, dict):
user_data = __user__
else:
user_data = {}
return {
"user_id": user_data.get("id", "unknown_user"),
"user_name": user_data.get("name", "User"),
"user_language": user_data.get("language", "en-US"),
}
中文版插件默认值:
user_language:"zh-CN"user_name:"用户"
英文版插件默认值:
user_language:"en-US"user_name:"User"
📦 依赖管理 (Dependencies)
requirements 字段规则
- 仅列出 OpenWebUI 环境中未安装的依赖
- 使用精确版本号
- 多个依赖用逗号分隔
"""
requirements: python-docx==1.1.2, openpyxl==3.1.2
"""
常见 OpenWebUI 已安装依赖(无需在 requirements 中声明):
pydanticfastapiloggingre,json,datetime,io,base64
🔧 代码规范 (Code Style)
Python 规范
- 遵循 PEP 8 规范
- 使用 Black 格式化代码
- 关键逻辑添加注释
导入顺序
# 1. Standard library imports
import os
import re
import json
import logging
from typing import Optional, Dict, Any, Callable, Awaitable
# 2. Third-party imports
from pydantic import BaseModel, Field
from fastapi import Request
# 3. OpenWebUI imports
from open_webui.utils.chat import generate_chat_completion
from open_webui.models.users import Users
📄 文件导出与命名规范 (File Export and Naming)
对于涉及文件导出的插件(通常是 Action),必须提供灵活的标题生成策略。
Valves 配置 (Valves Configuration)
应包含 TITLE_SOURCE 选项:
class Valves(BaseModel):
TITLE_SOURCE: str = Field(
default="chat_title",
description="Title Source: 'chat_title', 'ai_generated', 'markdown_title'",
)
标题获取逻辑 (Title Retrieval Logic)
- chat_title: 尝试从
body获取,若失败且有chat_id,则从数据库获取 (Chats.get_chat_by_id)。 - markdown_title: 从 Markdown 内容提取第一个 H1 或 H2。
- ai_generated: 使用轻量级 Prompt 让 AI 生成简短标题。
优先级与回退 (Priority and Fallback)
代码应根据 TITLE_SOURCE 优先尝试指定方法,若失败则按以下顺序回退:
chat_title -> markdown_title -> user_name + date
# 核心逻辑示例
if self.valves.TITLE_SOURCE == "chat_title":
title = chat_title
elif self.valves.TITLE_SOURCE == "markdown_title":
title = self.extract_title(content)
elif self.valves.TITLE_SOURCE == "ai_generated":
title = await self.generate_title_using_ai(...)
AI 标题生成实现 (AI Title Generation Implementation)
如果支持 ai_generated 选项,应实现类似以下的方法:
async def generate_title_using_ai(
self,
body: dict,
content: str,
user_id: str,
request: Any
) -> str:
"""Generates a short title using the current LLM model."""
if not request:
return ""
try:
# 获取当前用户和模型
user_obj = Users.get_user_by_id(user_id)
model = body.get("model")
# 构造请求
payload = {
"model": model,
"messages": [
{
"role": "system",
"content": "You are a helpful assistant. Generate a short, concise title (max 10 words) for the following text. Do not use quotes. Only output the title."
},
{
"role": "user",
"content": content[:2000] # 限制上下文长度以节省 Token
}
],
"stream": False,
}
# 调用 OpenWebUI 内部生成接口
response = await generate_chat_completion(request, payload, user_obj)
if response and "choices" in response:
return response["choices"][0]["message"]["content"].strip()
except Exception as e:
logger.error(f"Error generating title: {e}")
return ""
✅ 开发检查清单 (Development Checklist)
开发新插件时,请确保完成以下检查:
- 创建英文版插件代码 (
plugin_name.py) - 创建中文版插件代码 (
插件名.py或plugin_name_cn.py) - 编写英文 README (
README.md) - 编写中文 README (
README_CN.md) - 包含标准化文档字符串
- 添加 Author 和 License 信息
- 使用 Lucide 图标 (Base64 编码)
- 实现 Valves 配置
- 使用 logging 而非 print
- 测试双语界面
📚 参考资源 (Reference Resources)
Author
Fu-Jie
GitHub: Fu-Jie/awesome-openwebui
License
MIT License