Compare commits
404 Commits
v2026.01.0
...
v2026.02.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7b244602f | ||
|
|
3343e73848 | ||
|
|
7ab3e51d6f | ||
|
|
303d21c73d | ||
|
|
054af87e6e | ||
|
|
991570d025 | ||
|
|
5a5261d184 | ||
|
|
8cdc7723d2 | ||
|
|
a167f51026 | ||
|
|
03ff69f9e0 | ||
|
|
62c69a9a41 | ||
|
|
9bf2a5d2a2 | ||
|
|
13aed46c05 | ||
|
|
02a1668979 | ||
|
|
9760ccb243 | ||
|
|
e2c705fe15 | ||
|
|
37c7bf73c0 | ||
|
|
447e791ab6 | ||
|
|
cde685a364 | ||
|
|
b00206b063 | ||
|
|
e0a838e512 | ||
|
|
b9f4f5f1d6 | ||
|
|
c1afd1fa23 | ||
|
|
eaeaadaf12 | ||
|
|
ee70c6629e | ||
|
|
692971c93c | ||
|
|
e17818bf6c | ||
|
|
c5c9fd9d57 | ||
|
|
5c9875d390 | ||
|
|
9185f88d40 | ||
|
|
1fcad993ea | ||
|
|
1594ea3a20 | ||
|
|
ee681ddad9 | ||
|
|
881597fd51 | ||
|
|
5772b1c65f | ||
|
|
3d4b4b96e8 | ||
|
|
409d2f663f | ||
|
|
de9948a5b0 | ||
|
|
0a51b4cfca | ||
|
|
f8eb096300 | ||
|
|
f6369a1591 | ||
|
|
81634f57fa | ||
|
|
70dc62aaab | ||
|
|
8762c05e04 | ||
|
|
db52ac1041 | ||
|
|
795ac34cd9 | ||
|
|
89a12a4fe8 | ||
|
|
f882997337 | ||
|
|
8e2c1b467e | ||
|
|
db2433afa1 | ||
|
|
fd125c9dea | ||
|
|
0a726aacb6 | ||
|
|
bf82e093d4 | ||
|
|
53ae7ef003 | ||
|
|
c2c7e3b2d3 | ||
|
|
26df5f5144 | ||
|
|
aebeb3677c | ||
|
|
076c1d62c6 | ||
|
|
21cf6ecc1d | ||
|
|
e6bdab54c9 | ||
|
|
2232e5adb3 | ||
|
|
d17089972a | ||
|
|
a7991147be | ||
|
|
be6eb35567 | ||
|
|
90bc295871 | ||
|
|
310ad5d1d3 | ||
|
|
00ce724430 | ||
|
|
43b68b3ff0 | ||
|
|
b6c1335335 | ||
|
|
8b0b33015f | ||
|
|
11ee9086b2 | ||
|
|
3578ffc543 | ||
|
|
b8531f1979 | ||
|
|
8607afd4c3 | ||
|
|
41d9963d35 | ||
|
|
bd5f3d3f7c | ||
|
|
dda8262bc0 | ||
|
|
0271013ec2 | ||
|
|
e1f210d600 | ||
|
|
eac8f6f355 | ||
|
|
3998b93034 | ||
|
|
ae04e95e13 | ||
|
|
12d638e134 | ||
|
|
4a9eb8ed3d | ||
|
|
af127bbfd5 | ||
|
|
db7bb6250a | ||
|
|
24b029e617 | ||
|
|
ff63ab3118 | ||
|
|
07e7e74fe1 | ||
|
|
55456775c1 | ||
|
|
05bb2e4644 | ||
|
|
83fa20ed08 | ||
|
|
ec69524357 | ||
|
|
829361da63 | ||
|
|
af05ecec6a | ||
|
|
1e08ae7d10 | ||
|
|
b24233ee07 | ||
|
|
e5d1550986 | ||
|
|
7f5deb603e | ||
|
|
55b2a28f79 | ||
|
|
db9bcb2c31 | ||
|
|
ad2773e8f1 | ||
|
|
7d4da3be8a | ||
|
|
28166728a4 | ||
|
|
4dfca903c4 | ||
|
|
b619d3f402 | ||
|
|
1e68f985fb | ||
|
|
596b571887 | ||
|
|
c4d36c32a0 | ||
|
|
6adbcd8d42 | ||
|
|
89c039fe33 | ||
|
|
3a73ccfaa7 | ||
|
|
7eff265e1c | ||
|
|
989b45fc16 | ||
|
|
163d8ce8bd | ||
|
|
4e32e1a1da | ||
|
|
070e9f2456 | ||
|
|
219ba83df3 | ||
|
|
e412aeb93d | ||
|
|
38102ca0c4 | ||
|
|
6ab69fba1c | ||
|
|
e0c0f69dc8 | ||
|
|
7921b14dae | ||
|
|
30cde9e871 | ||
|
|
ac50cd249a | ||
|
|
927db6dbaa | ||
|
|
376c398ac7 | ||
|
|
a167a3cf83 | ||
|
|
c51e7dfdf7 | ||
|
|
1d4d13b34b | ||
|
|
18e8775f38 | ||
|
|
813b019653 | ||
|
|
b0b1542939 | ||
|
|
15f19d8b8d | ||
|
|
82253b114c | ||
|
|
e0bfbf6dd4 | ||
|
|
4689e80e7a | ||
|
|
556e6c1c67 | ||
|
|
3ab84a526d | ||
|
|
bdce96f912 | ||
|
|
4811b99a4b | ||
|
|
fb2a64c07a | ||
|
|
e023e4f2e2 | ||
|
|
0b16b1e0f4 | ||
|
|
59073ad7ac | ||
|
|
8248644c45 | ||
|
|
f38e6394c9 | ||
|
|
0aaa529c6b | ||
|
|
b81a6562a1 | ||
|
|
c5b10db23a | ||
|
|
d16e444643 | ||
|
|
8202468099 | ||
|
|
766e8bd20f | ||
|
|
1214ab5a8c | ||
|
|
ebddbb25f8 | ||
|
|
59545e1110 | ||
|
|
500e090b11 | ||
|
|
a75ee555fa | ||
|
|
6a8c2164cd | ||
|
|
7f7efa325a | ||
|
|
9ba6cb08fc | ||
|
|
1872271a2d | ||
|
|
813b50864a | ||
|
|
b18cefe320 | ||
|
|
a54c359fcf | ||
|
|
8d83221a4a | ||
|
|
1879000720 | ||
|
|
ba92649a98 | ||
|
|
d2276dcaae | ||
|
|
25c9d20f3d | ||
|
|
0d853577df | ||
|
|
f91f3d8692 | ||
|
|
0f7cad8dfa | ||
|
|
db1a1e7ef0 | ||
|
|
e7de80a059 | ||
|
|
0d8c4e048e | ||
|
|
014a5a9d1f | ||
|
|
a6dd970859 | ||
|
|
aac730f5b1 | ||
|
|
ff95d9328e | ||
|
|
afe1d8cf52 | ||
|
|
67b819f3de | ||
|
|
9b6acb6b95 | ||
|
|
a9a59e1e34 | ||
|
|
5b05397356 | ||
|
|
7a7dbc0cfa | ||
|
|
6ac0ba6efe | ||
|
|
d3d008efb4 | ||
|
|
4f1528128a | ||
|
|
93c4326206 | ||
|
|
0fca7fe524 | ||
|
|
afdcab10c6 | ||
|
|
f8cc5eabe6 | ||
|
|
f304eb7633 | ||
|
|
827204e082 | ||
|
|
641d7ee8c8 | ||
|
|
3b11537b5e | ||
|
|
e51d87ae80 | ||
|
|
f16e7c996c | ||
|
|
55eb295c12 | ||
|
|
4767351c5e | ||
|
|
1d2502eb3f | ||
|
|
94540cc131 | ||
|
|
71bef146c8 | ||
|
|
87e47fd4b2 | ||
|
|
2da600838c | ||
|
|
4ee34c1dc6 | ||
|
|
9a854c33d3 | ||
|
|
ae19653a8f | ||
|
|
caf0acf2e1 | ||
|
|
b503ad6fd2 | ||
|
|
357e869a15 | ||
|
|
3035c79d91 | ||
|
|
a5e5e178a0 | ||
|
|
d20081d3ed | ||
|
|
e2d94ba5b5 | ||
|
|
49a19242a4 | ||
|
|
c26d3b30e5 | ||
|
|
60e681042d | ||
|
|
842d65b887 | ||
|
|
ff5cecca1c | ||
|
|
b447143a50 | ||
|
|
e4cbf231a6 | ||
|
|
8868b28a84 | ||
|
|
c4df24d2c2 | ||
|
|
70a96d0754 | ||
|
|
ab0daba80d | ||
|
|
505fb6ca96 | ||
|
|
385ee71bc8 | ||
|
|
cfa28e2c9a | ||
|
|
d08bede60e | ||
|
|
b686db353c | ||
|
|
2b543d51ff | ||
|
|
e8d09d79ec | ||
|
|
cdb544f891 | ||
|
|
3eff93e8c9 | ||
|
|
cdb03fce90 | ||
|
|
c1cecf0dbb | ||
|
|
08ecba3ee1 | ||
|
|
3b82f2364e | ||
|
|
a7b2032b20 | ||
|
|
3bc683dbf5 | ||
|
|
2a8065e80c | ||
|
|
ab60641265 | ||
|
|
9e88decc44 | ||
|
|
076598ba07 | ||
|
|
4f0c50db0f | ||
|
|
499690e30f | ||
|
|
12a531b9ae | ||
|
|
3a0e2ecc6e | ||
|
|
14954b03bf | ||
|
|
6f874db000 | ||
|
|
16cc45c0d5 | ||
|
|
ab96719ec4 | ||
|
|
e2be1b25b1 | ||
|
|
700a7fc27a | ||
|
|
06cc48bab1 | ||
|
|
498e433ed3 | ||
|
|
4e915ea7a9 | ||
|
|
825ea07f4b | ||
|
|
1a731c181b | ||
|
|
c59ba5e501 | ||
|
|
e21e3e2ffa | ||
|
|
d2abaa138e | ||
|
|
3843ae5bc7 | ||
|
|
02c7a87c63 | ||
|
|
1e59025535 | ||
|
|
46195791b6 | ||
|
|
85b6bcece1 | ||
|
|
fece7d9898 | ||
|
|
d41822911c | ||
|
|
7b1180a1c8 | ||
|
|
6d5c3f1415 | ||
|
|
f8157f92fc | ||
|
|
fa2e9f5344 | ||
|
|
9c37955cf2 | ||
|
|
261f74efe8 | ||
|
|
83727bdab1 | ||
|
|
3b1a8d795f | ||
|
|
f650c64ffe | ||
|
|
6000c880de | ||
|
|
048fbb26d7 | ||
|
|
a88eda62cc | ||
|
|
957fb2dfb7 | ||
|
|
d2be5109ad | ||
|
|
80fdc52598 | ||
|
|
2b90ead3cf | ||
|
|
2aa5d77586 | ||
|
|
2b1b1ef939 | ||
|
|
4e21e06617 | ||
|
|
82ce1cef29 | ||
|
|
533eace74e | ||
|
|
83b3dcda65 | ||
|
|
e89373e0ed | ||
|
|
4b66a2bb1c | ||
|
|
59ba23da63 | ||
|
|
f8a89e222c | ||
|
|
096568f3e6 | ||
|
|
e10e12ebc9 | ||
|
|
c4df5eba47 | ||
|
|
0da3d3d881 | ||
|
|
6f4a62d1bc | ||
|
|
5d71c2a4d3 | ||
|
|
097707c168 | ||
|
|
8f4cfceb50 | ||
|
|
4ab5fab7d0 | ||
|
|
0e293be8bc | ||
|
|
182c12f81a | ||
|
|
1337a90911 | ||
|
|
2f0a347ab3 | ||
|
|
4eda286512 | ||
|
|
0fead8158d | ||
|
|
031bef563a | ||
|
|
04c3fd2bf9 | ||
|
|
cbbf6118b5 | ||
|
|
4c529369ce | ||
|
|
797dea0d77 | ||
|
|
a91aee31de | ||
|
|
8511b7df80 | ||
|
|
afd1e7a444 | ||
|
|
34b2c3d6cf | ||
|
|
d5c099dd15 | ||
|
|
8810223693 | ||
|
|
84974a2fb9 | ||
|
|
af847293af | ||
|
|
a44e80ce5b | ||
|
|
c2815e13e9 | ||
|
|
56bfa3a3ef | ||
|
|
a13c915f27 | ||
|
|
fb2d35237e | ||
|
|
3f19ecfd20 | ||
|
|
2fd96f07aa | ||
|
|
a1c1ed9840 | ||
|
|
c63701d05f | ||
|
|
863805dc68 | ||
|
|
98f7dff458 | ||
|
|
08c0dd984c | ||
|
|
e870ad8823 | ||
|
|
d687fffdb5 | ||
|
|
d534d8b319 | ||
|
|
d5c5158726 | ||
|
|
888026876f | ||
|
|
06e8d30900 | ||
|
|
cbf2ff7f93 | ||
|
|
abbe3fb248 | ||
|
|
7e44dde979 | ||
|
|
3649d75539 | ||
|
|
d3b4219a9a | ||
|
|
9e98d55e11 | ||
|
|
4b8515f682 | ||
|
|
d2f35ce396 | ||
|
|
f479f23b38 | ||
|
|
51048f9e5d | ||
|
|
1118ae34c4 | ||
|
|
7a5e1a4e12 | ||
|
|
8e377e1794 | ||
|
|
d66360b02d | ||
|
|
1ece648006 | ||
|
|
a262a716a3 | ||
|
|
06fdfee182 | ||
|
|
7085e794a3 | ||
|
|
a9cae535eb | ||
|
|
bdbd0d98be | ||
|
|
51612ea783 | ||
|
|
baf364a85f | ||
|
|
f78e703a99 | ||
|
|
aabb24c9cd | ||
|
|
ef34cc326c | ||
|
|
5fa56ba88d | ||
|
|
b71df8ef43 | ||
|
|
8c6fe6784e | ||
|
|
29fa5bae29 | ||
|
|
dab465d924 | ||
|
|
77c0defe93 | ||
|
|
80cf2b5a52 | ||
|
|
96638d8092 | ||
|
|
21ad55ae55 | ||
|
|
530a6cd463 | ||
|
|
8615773b67 | ||
|
|
16eaec64b7 | ||
|
|
8558077dfe | ||
|
|
a15353ea52 | ||
|
|
5b44e3e688 | ||
|
|
a4b3628e01 | ||
|
|
bbb7db3878 | ||
|
|
dec2bbb4bf | ||
|
|
6a241b0ae0 | ||
|
|
51c53e0ed0 | ||
|
|
8cb6382e72 | ||
|
|
5889471e82 | ||
|
|
ca2e0b4fba | ||
|
|
10d24fbfa2 | ||
|
|
322bd6e167 | ||
|
|
3cc4478dd9 | ||
|
|
59f6f2ba97 | ||
|
|
172d9e0b41 | ||
|
|
de7086c9e1 | ||
|
|
5f63e8d1e2 | ||
|
|
3da0b894fd | ||
|
|
ad2d26aa16 | ||
|
|
a09f3e0bdb | ||
|
|
3a0faf27df | ||
|
|
cd3e7309a8 | ||
|
|
54cc10bb41 |
30
.agent/rules/plugin_standards.md
Normal file
30
.agent/rules/plugin_standards.md
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
description: Standards for OpenWebUI Plugin Development, specifically README formatting.
|
||||
globs: plugins/**
|
||||
always_on: true
|
||||
---
|
||||
# Plugin Development Standards
|
||||
|
||||
## README Documentation
|
||||
|
||||
All plugins MUST follow the standard README template.
|
||||
|
||||
**Reference Template**: @docs/PLUGIN_README_TEMPLATE.md
|
||||
|
||||
### Language Requirements
|
||||
- **English Version (`README.md`)**: The primary documentation source. Must follow the template strictly.
|
||||
- **Chinese Version (`README_CN.md`)**: MUST be translated based on the English version (`README.md`) to ensure consistency in structure and content.
|
||||
|
||||
### Metadata Requirements
|
||||
The metadata line must follow this format:
|
||||
`**Author:** [Name](Link) | **Version:** [X.Y.Z] | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **License:** MIT`
|
||||
|
||||
### Structure Checklist
|
||||
1. **Title & Description**
|
||||
2. **Metadata Line** (Author, Version, Project, License)
|
||||
3. **Preview** (Screenshots/GIFs)
|
||||
4. **What's New** (Keep last 3 versions)
|
||||
5. **Key Features**
|
||||
6. **How to Use**
|
||||
7. **Configuration (Valves)**
|
||||
8. **Troubleshooting** (Must include link to GitHub Issues)
|
||||
@@ -4,13 +4,14 @@ description: OpenWebUI Plugin Development & Release Workflow
|
||||
|
||||
# OpenWebUI Plugin Development Workflow
|
||||
|
||||
This workflow outlines the standard process for developing, documenting, and releasing plugins for OpenWebUI, ensuring compliance with project standards and CI/CD requirements.
|
||||
This workflow outlines the standard process for developing, documenting, and releasing plugins for OpenWebUI. **Crucially, the default goal of this workflow is "Preparation" (updating all relevant files) rather than automatic "Submission" (git commit/push), unless a release is explicitly requested.**
|
||||
|
||||
## 1. Development Standards
|
||||
|
||||
Reference: `.github/copilot-instructions.md`
|
||||
|
||||
### Bilingual Requirement
|
||||
|
||||
Every plugin **MUST** have bilingual versions for both code and documentation:
|
||||
|
||||
- **Code**:
|
||||
@@ -21,17 +22,21 @@ Every plugin **MUST** have bilingual versions for both code and documentation:
|
||||
- Chinese: `plugins/{type}/{name}/README_CN.md`
|
||||
|
||||
### Code Structure
|
||||
|
||||
- **Docstring**: Must include `title`, `author`, `version`, `description`, etc.
|
||||
- **Valves**: Use `pydantic` for configuration.
|
||||
- **Database**: Re-use `open_webui.internal.db` shared connection.
|
||||
- **User Context**: Use `_get_user_context` helper method.
|
||||
- **Chat Context**: Use `_get_chat_context` helper method for `chat_id` and `message_id`.
|
||||
- **Debugging**: Use `_emit_debug_log` for frontend console logging (requires `SHOW_DEBUG_LOG` valve).
|
||||
- **Chat API**: For message updates, follow the "OpenWebUI Chat API 更新规范" in `.github/copilot-instructions.md`.
|
||||
- Use Event API for immediate UI updates
|
||||
- Use Chat Persistence API for database storage
|
||||
- Always update both `messages[]` and `history.messages`
|
||||
|
||||
### Commit Messages
|
||||
- **Language**: **English ONLY**. Do not use Chinese in commit messages.
|
||||
### Commit Messages & Release Notes
|
||||
|
||||
- **Language**: **English ONLY**. Do not use Chinese in commit messages or release notes.
|
||||
- **Format**: Conventional Commits (e.g., `feat:`, `fix:`, `docs:`).
|
||||
|
||||
## 2. Documentation Updates
|
||||
@@ -39,10 +44,16 @@ Every plugin **MUST** have bilingual versions for both code and documentation:
|
||||
When adding or updating a plugin, you **MUST** update the following documentation files to maintain consistency:
|
||||
|
||||
### Plugin Directory
|
||||
- `README.md`: Update version, description, and usage. **Explicitly describe new features in a prominent position at the beginning.**
|
||||
- `README_CN.md`: Update version, description, and usage. **Explicitly describe new features in a prominent position at the beginning.**
|
||||
|
||||
- `README.md`: Update version, description, and usage.
|
||||
- **Key Capabilities**: **MUST** include ALL core functionalities and features. Do not only list new features in "What's New".
|
||||
- **What's New**: Explicitly describe only the latest changes/updates in a prominent position at the beginning. This section is dynamic and changes with versions.
|
||||
- `README_CN.md`: Update version, description, and usage.
|
||||
- **核心功能 (Key Capabilities)**: **必须**包含所有核心功能和特性,不能只在 "What's New" 中列出。
|
||||
- **最新更新 (What's New)**: 在开头显眼位置明确描述最新的更改/更新。此部分是动态的,随版本变化。
|
||||
|
||||
### Global Documentation (`docs/`)
|
||||
|
||||
- **Index Pages**:
|
||||
- `docs/plugins/{type}/index.md`: Add/Update list item with **correct version**.
|
||||
- `docs/plugins/{type}/index.zh.md`: Add/Update list item with **correct version**.
|
||||
@@ -51,6 +62,7 @@ When adding or updating a plugin, you **MUST** update the following documentatio
|
||||
- `docs/plugins/{type}/{name}.zh.md`: Ensure content matches README_CN.
|
||||
|
||||
### Root README
|
||||
|
||||
- `README.md`: Add to "Featured Plugins" if applicable.
|
||||
- `README_CN.md`: Add to "Featured Plugins" if applicable.
|
||||
|
||||
@@ -59,12 +71,15 @@ When adding or updating a plugin, you **MUST** update the following documentatio
|
||||
Reference: `.github/workflows/release.yml`
|
||||
|
||||
### Version Bumping
|
||||
|
||||
- **Rule**: Version bump is required **ONLY when the user explicitly requests a release**. Regular code changes do NOT require version bumps.
|
||||
- **Format**: Semantic Versioning (e.g., `1.0.0` -> `1.0.1`).
|
||||
- **When to Bump**: Only update the version when:
|
||||
- User says "发布" / "release" / "bump version"
|
||||
- User explicitly asks to prepare for release
|
||||
- **Agent Initiative**: After completing significant changes (new features, bug fixes, or multiple code modifications), the agent **SHOULD proactively ask** the user if they want to release a new version. If confirmed, update all version-related files.
|
||||
- **Agent Initiative**: After completing significant changes (new features, bug fixes, or multiple code modifications), the agent **SHOULD proactively ask** the user if they want to **prepare a new version** for release.
|
||||
- **Release Information Compliance**: When a release is requested, the agent must generate a standard release summary (English commit title + bilingual bullet points) as defined in Section 3 & 5.
|
||||
- **Default Action (Prepare Only)**: When performing a version bump or update, the agent should update all files locally but **STOP** before committing. Present the changes and the **proposed Release/Commit Message** to the user and wait for explicit confirmation to commit/push.
|
||||
- **Consistency**: When bumping, update version in **ALL** locations:
|
||||
1. English Code (`.py`)
|
||||
2. Chinese Code (`.py`)
|
||||
@@ -76,19 +91,40 @@ Reference: `.github/workflows/release.yml`
|
||||
8. Docs Detail CN (`docs/.../{name}.zh.md`)
|
||||
|
||||
### Automated Release Process
|
||||
1. **Trigger**: Push to `main` branch with changes in `plugins/**/*.py`.
|
||||
2. **Detection**: `scripts/extract_plugin_versions.py` detects changed plugins and compares versions.
|
||||
3. **Release**:
|
||||
|
||||
1. **Trigger**: Push to `main` branch with changes in `plugins/**/*.py`.
|
||||
2. **Detection**: `scripts/extract_plugin_versions.py` detects changed plugins and compares versions.
|
||||
3. **Release**:
|
||||
- Generates release notes based on changes.
|
||||
- Creates a GitHub Release tag (e.g., `v2024.01.01-1`).
|
||||
- Uploads individual `.py` files of **changed plugins only** as assets.
|
||||
4. **Market Publishing**:
|
||||
4. **Market Publishing**:
|
||||
- Workflow: `.github/workflows/publish_plugin.yml`
|
||||
- Trigger: Release published.
|
||||
- Action: Automatically updates the plugin code and metadata on OpenWebUI.com using `scripts/publish_plugin.py`.
|
||||
- **Auto-Sync**: If a local plugin has no ID but matches an existing published plugin by **Title**, the script will automatically fetch the ID, update the local file, and proceed with the update.
|
||||
- Requirement: `OPENWEBUI_API_KEY` secret must be set.
|
||||
- **README Link**: When announcing a release, always include the GitHub README URL for the plugin:
|
||||
- Format: `https://github.com/Fu-Jie/awesome-openwebui/blob/main/plugins/{type}/{name}/README.md`
|
||||
- Example: `https://github.com/Fu-Jie/awesome-openwebui/blob/main/plugins/filters/folder-memory/README.md`
|
||||
|
||||
### Release Content Standard
|
||||
|
||||
When the user confirms a release, the agent **MUST** follow these content standards:
|
||||
|
||||
1. **Commit Message**:
|
||||
- **Language**: English ONLY.
|
||||
- **Format**: `type(scope): description` (e.g., `feat(pipes): add streaming support for Copilot SDK`).
|
||||
- **Body**: List 1-3 key changes in bullet points.
|
||||
2. **Release Summary (for user review)**:
|
||||
- Before committing, present a "Release Draft" containing:
|
||||
- **Title**: e.g., `Release v0.1.1: [Plugin Name] - [Brief Summary]`
|
||||
- **Changes**: Bilingual bullet points (English/Chinese) describing the impact.
|
||||
- **Verification Status**: Confirm all 8+ files have been updated and synced.
|
||||
3. **Internal Documentation**: Ensure "What's New" sections in READMEs and `docs/` match exactly the changes being released.
|
||||
|
||||
### Pull Request Check
|
||||
|
||||
- Workflow: `.github/workflows/plugin-version-check.yml`
|
||||
- Checks if plugin files are modified.
|
||||
- **Fails** if version number is not updated.
|
||||
@@ -107,6 +143,7 @@ Before committing:
|
||||
|
||||
## 5. Git Operations (Agent Rules)
|
||||
|
||||
Strictly follow the rules defined in `.github/copilot-instructions.md` → **Git Operations (Agent Rules)** section.
|
||||
|
||||
|
||||
1. **Prepare-on-Demand**: Focus on file modifications and local verification first.
|
||||
2. **No Auto-Commit**: Never `git commit`, `git push`, or `create_pull_request` automatically after file updates unless the user explicitly says "commit this" or "release now".
|
||||
3. **Draft Mode**: If available, use PRs as drafts first.
|
||||
4. **Reference**: Strictly follow the rules defined in `.github/copilot-instructions.md` → **Git Operations (Agent Rules)** section.
|
||||
|
||||
66
.all-contributorsrc
Normal file
66
.all-contributorsrc
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"files": [
|
||||
"README.md"
|
||||
],
|
||||
"imageSize": 100,
|
||||
"commit": false,
|
||||
"commitType": "docs",
|
||||
"commitConvention": "angular",
|
||||
"contributors": [
|
||||
{
|
||||
"login": "rbb-dev",
|
||||
"name": "rbb-dev",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/37469229?v=4",
|
||||
"profile": "https://github.com/rbb-dev",
|
||||
"contributions": [
|
||||
"ideas",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dhaern",
|
||||
"name": "Raxxoor",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7317522?v=4",
|
||||
"profile": "https://trade.xyz/?ref=BZ1RJRXWO",
|
||||
"contributions": [
|
||||
"bug",
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "i-iooi-i",
|
||||
"name": "ZOLO",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1827701?v=4",
|
||||
"profile": "https://github.com/i-iooi-i",
|
||||
"contributions": [
|
||||
"bug",
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "nahoj",
|
||||
"name": "Johan Grande",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/469017?v=4",
|
||||
"profile": "https://perso.crans.org/grande/",
|
||||
"contributions": [
|
||||
"ideas"
|
||||
]
|
||||
}
|
||||
,
|
||||
{
|
||||
"login": "abaroni",
|
||||
"name": "Alessandro Baroni",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/21365486?v=4",
|
||||
"profile": "https://github.com/abaroni",
|
||||
"contributions": [
|
||||
"ideas"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
"skipCi": true,
|
||||
"repoType": "github",
|
||||
"repoHost": "https://github.com",
|
||||
"projectName": "awesome-openwebui",
|
||||
"projectOwner": "Fu-Jie"
|
||||
}
|
||||
2186
.github/copilot-instructions.md
vendored
2186
.github/copilot-instructions.md
vendored
File diff suppressed because it is too large
Load Diff
93
.github/workflows/community-stats.yml
vendored
93
.github/workflows/community-stats.yml
vendored
@@ -1,5 +1,9 @@
|
||||
# OpenWebUI 社区统计报告自动生成
|
||||
# 每小时自动获取并更新社区统计数据
|
||||
# 智能检测:只在有意义的变更时才 commit
|
||||
# - 新增插件 (total_posts)
|
||||
# - 插件版本变更 (version)
|
||||
# - 积分增加 (total_points)
|
||||
# - 粉丝增加 (followers)
|
||||
|
||||
name: Community Stats
|
||||
|
||||
@@ -31,24 +35,95 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install requests python-dotenv
|
||||
|
||||
|
||||
- name: Capture existing stats (before update)
|
||||
id: old_stats
|
||||
run: |
|
||||
if [ -f docs/community-stats.json ]; then
|
||||
echo "total_posts=$(jq -r '.total_posts // 0' docs/community-stats.json)" >> $GITHUB_OUTPUT
|
||||
echo "total_points=$(jq -r '.user.total_points // 0' docs/community-stats.json)" >> $GITHUB_OUTPUT
|
||||
echo "followers=$(jq -r '.user.followers // 0' docs/community-stats.json)" >> $GITHUB_OUTPUT
|
||||
# 提取所有插件的版本号,生成一个排序后的字符串用于比较
|
||||
echo "versions=$(jq -r '[.posts[].version] | sort | join(",")' docs/community-stats.json)" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "total_posts=0" >> $GITHUB_OUTPUT
|
||||
echo "total_points=0" >> $GITHUB_OUTPUT
|
||||
echo "followers=0" >> $GITHUB_OUTPUT
|
||||
echo "versions=" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Generate stats report
|
||||
env:
|
||||
OPENWEBUI_API_KEY: ${{ secrets.OPENWEBUI_API_KEY }}
|
||||
OPENWEBUI_USER_ID: ${{ secrets.OPENWEBUI_USER_ID }}
|
||||
run: |
|
||||
python scripts/openwebui_stats.py
|
||||
|
||||
- name: Check for changes
|
||||
|
||||
- name: Capture new stats (after update)
|
||||
id: new_stats
|
||||
run: |
|
||||
echo "total_posts=$(jq -r '.total_posts // 0' docs/community-stats.json)" >> $GITHUB_OUTPUT
|
||||
echo "total_points=$(jq -r '.user.total_points // 0' docs/community-stats.json)" >> $GITHUB_OUTPUT
|
||||
echo "followers=$(jq -r '.user.followers // 0' docs/community-stats.json)" >> $GITHUB_OUTPUT
|
||||
echo "versions=$(jq -r '[.posts[].version] | sort | join(",")' docs/community-stats.json)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check for significant changes
|
||||
id: check_changes
|
||||
run: |
|
||||
git diff --quiet docs/community-stats.zh.md docs/community-stats.md README.md README_CN.md || echo "changed=true" >> $GITHUB_OUTPUT
|
||||
|
||||
OLD_POSTS="${{ steps.old_stats.outputs.total_posts }}"
|
||||
NEW_POSTS="${{ steps.new_stats.outputs.total_posts }}"
|
||||
OLD_POINTS="${{ steps.old_stats.outputs.total_points }}"
|
||||
NEW_POINTS="${{ steps.new_stats.outputs.total_points }}"
|
||||
OLD_FOLLOWERS="${{ steps.old_stats.outputs.followers }}"
|
||||
NEW_FOLLOWERS="${{ steps.new_stats.outputs.followers }}"
|
||||
OLD_VERSIONS="${{ steps.old_stats.outputs.versions }}"
|
||||
NEW_VERSIONS="${{ steps.new_stats.outputs.versions }}"
|
||||
|
||||
SHOULD_COMMIT="false"
|
||||
CHANGE_REASON=""
|
||||
|
||||
# 检查新增插件
|
||||
if [ "$NEW_POSTS" -gt "$OLD_POSTS" ]; then
|
||||
SHOULD_COMMIT="true"
|
||||
CHANGE_REASON="new plugin added ($OLD_POSTS -> $NEW_POSTS)"
|
||||
echo "📦 New plugin detected: $OLD_POSTS -> $NEW_POSTS"
|
||||
fi
|
||||
|
||||
# 检查版本变更
|
||||
if [ "$OLD_VERSIONS" != "$NEW_VERSIONS" ]; then
|
||||
SHOULD_COMMIT="true"
|
||||
CHANGE_REASON="${CHANGE_REASON:+$CHANGE_REASON, }plugin version updated"
|
||||
echo "🔄 Plugin version changed"
|
||||
fi
|
||||
|
||||
# 检查积分增加
|
||||
if [ "$NEW_POINTS" -gt "$OLD_POINTS" ]; then
|
||||
SHOULD_COMMIT="true"
|
||||
CHANGE_REASON="${CHANGE_REASON:+$CHANGE_REASON, }points increased ($OLD_POINTS -> $NEW_POINTS)"
|
||||
echo "⭐ Points increased: $OLD_POINTS -> $NEW_POINTS"
|
||||
fi
|
||||
|
||||
# 检查粉丝增加
|
||||
if [ "$NEW_FOLLOWERS" -gt "$OLD_FOLLOWERS" ]; then
|
||||
SHOULD_COMMIT="true"
|
||||
CHANGE_REASON="${CHANGE_REASON:+$CHANGE_REASON, }followers increased ($OLD_FOLLOWERS -> $NEW_FOLLOWERS)"
|
||||
echo "👥 Followers increased: $OLD_FOLLOWERS -> $NEW_FOLLOWERS"
|
||||
fi
|
||||
|
||||
echo "should_commit=$SHOULD_COMMIT" >> $GITHUB_OUTPUT
|
||||
echo "change_reason=$CHANGE_REASON" >> $GITHUB_OUTPUT
|
||||
|
||||
if [ "$SHOULD_COMMIT" = "false" ]; then
|
||||
echo "ℹ️ No significant changes detected, skipping commit"
|
||||
else
|
||||
echo "✅ Significant changes detected: $CHANGE_REASON"
|
||||
fi
|
||||
|
||||
- name: Commit and push changes
|
||||
if: steps.check_changes.outputs.changed == 'true'
|
||||
if: steps.check_changes.outputs.should_commit == 'true'
|
||||
run: |
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add docs/community-stats.zh.md docs/community-stats.md docs/community-stats.json README.md README_CN.md
|
||||
git commit -m "chore: update community stats $(date +'%Y-%m-%d')"
|
||||
git add docs/community-stats.zh.md docs/community-stats.md docs/community-stats.json docs/badges README.md README_CN.md
|
||||
git diff --staged --quiet || git commit -m "chore: update community stats - ${{ steps.check_changes.outputs.change_reason }}"
|
||||
git push
|
||||
|
||||
68
.github/workflows/publish_new_plugin.yml
vendored
Normal file
68
.github/workflows/publish_new_plugin.yml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
name: Publish New Plugin
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
plugin_dir:
|
||||
description: 'Plugin directory (e.g., plugins/actions/deep-dive)'
|
||||
required: true
|
||||
type: string
|
||||
dry_run:
|
||||
description: 'Dry run mode (preview only)'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install requests
|
||||
|
||||
- name: Validate plugin directory
|
||||
run: |
|
||||
if [ ! -d "${{ github.event.inputs.plugin_dir }}" ]; then
|
||||
echo "❌ Error: Directory '${{ github.event.inputs.plugin_dir }}' does not exist"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Found plugin directory: ${{ github.event.inputs.plugin_dir }}"
|
||||
ls -la "${{ github.event.inputs.plugin_dir }}"
|
||||
|
||||
- name: Publish Plugin
|
||||
env:
|
||||
OPENWEBUI_API_KEY: ${{ secrets.OPENWEBUI_API_KEY }}
|
||||
run: |
|
||||
if [ "${{ github.event.inputs.dry_run }}" = "true" ]; then
|
||||
echo "🔍 Dry run mode - previewing..."
|
||||
python scripts/publish_plugin.py --new "${{ github.event.inputs.plugin_dir }}" --dry-run
|
||||
else
|
||||
echo "🚀 Publishing plugin..."
|
||||
python scripts/publish_plugin.py --new "${{ github.event.inputs.plugin_dir }}"
|
||||
fi
|
||||
|
||||
- name: Commit changes (if ID was added)
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Check if there are changes to commit
|
||||
if git diff --quiet; then
|
||||
echo "No changes to commit"
|
||||
else
|
||||
git add "${{ github.event.inputs.plugin_dir }}"
|
||||
git commit -m "feat: add openwebui_id to ${{ github.event.inputs.plugin_dir }}"
|
||||
git push
|
||||
echo "✅ Committed and pushed openwebui_id changes"
|
||||
fi
|
||||
6
.github/workflows/publish_plugin.yml
vendored
6
.github/workflows/publish_plugin.yml
vendored
@@ -1,6 +1,12 @@
|
||||
name: Publish Plugins to OpenWebUI Market
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'plugins/**/*.py'
|
||||
- '!plugins/debug/**'
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
58
.github/workflows/release.yml
vendored
58
.github/workflows/release.yml
vendored
@@ -246,6 +246,52 @@ jobs:
|
||||
echo "=== Collected Files ==="
|
||||
find release_plugins -name "*.py" -type f | head -20
|
||||
|
||||
- name: Update plugin icon URLs
|
||||
run: |
|
||||
echo "Updating icon_url in plugins to use absolute GitHub URLs..."
|
||||
# Base URL for raw content using the release tag
|
||||
REPO_URL="https://raw.githubusercontent.com/${{ github.repository }}/${{ steps.version.outputs.version }}"
|
||||
|
||||
find release_plugins -name "*.py" | while read -r file; do
|
||||
# $file is like release_plugins/plugins/actions/infographic/infographic.py
|
||||
# Remove release_plugins/ prefix to get the path in the repo
|
||||
src_file="${file#release_plugins/}"
|
||||
src_dir=$(dirname "$src_file")
|
||||
base_name=$(basename "$src_file" .py)
|
||||
|
||||
# Check if a corresponding png exists in the source repository
|
||||
png_file="${src_dir}/${base_name}.png"
|
||||
|
||||
if [ -f "$png_file" ]; then
|
||||
echo "Found icon for $src_file: $png_file"
|
||||
TARGET_ICON_URL="${REPO_URL}/${png_file}"
|
||||
|
||||
# Use python for safe replacement
|
||||
python3 -c "
|
||||
import sys
|
||||
import re
|
||||
|
||||
file_path = '$file'
|
||||
icon_url = '$TARGET_ICON_URL'
|
||||
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Replace icon_url: ... with new url
|
||||
# Matches 'icon_url: ...' and replaces it
|
||||
new_content = re.sub(r'^icon_url:.*$', f'icon_url: {icon_url}', content, flags=re.MULTILINE)
|
||||
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
print(f'Successfully updated icon_url in {file_path}')
|
||||
except Exception as e:
|
||||
print(f'Error updating {file_path}: {e}', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Debug Filenames
|
||||
run: |
|
||||
python3 -c "import sys; print(f'Filesystem encoding: {sys.getfilesystemencoding()}')"
|
||||
@@ -278,13 +324,13 @@ jobs:
|
||||
|
||||
- name: Generate release notes
|
||||
id: notes
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
TITLE: ${{ github.event.inputs.release_title }}
|
||||
NOTES: ${{ github.event.inputs.release_notes }}
|
||||
DETECTED_CHANGES: ${{ needs.check-changes.outputs.release_notes }}
|
||||
COMMITS: ${{ steps.commits.outputs.commits }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
TITLE="${{ github.event.inputs.release_title }}"
|
||||
NOTES="${{ github.event.inputs.release_notes }}"
|
||||
DETECTED_CHANGES="${{ needs.check-changes.outputs.release_notes }}"
|
||||
COMMITS="${{ steps.commits.outputs.commits }}"
|
||||
|
||||
echo "# ${VERSION} Release / 发布" > release_notes.md
|
||||
echo "" >> release_notes.md
|
||||
|
||||
|
||||
@@ -1,87 +1,16 @@
|
||||
# 贡献指南 (Contributing Guide)
|
||||
# Contributing Guide
|
||||
|
||||
感谢你对 **OpenWebUI Extras** 感兴趣!我们非常欢迎社区贡献更多的插件、提示词和创意。
|
||||
Thank you for your interest in **OpenWebUI Extras**!
|
||||
|
||||
## 🤝 如何贡献
|
||||
## 🚀 How to Contribute
|
||||
|
||||
### 1. 分享提示词 (Prompts)
|
||||
1. **Fork** this repository.
|
||||
2. **Add/Modify** the plugin file in the `plugins/` directory.
|
||||
3. **Submit PR**: We will review and merge it.
|
||||
|
||||
如果你有一个好用的提示词:
|
||||
1. 在 `prompts/` 目录下找到合适的分类(如 `coding/`, `writing/`)。如果没有合适的,可以新建一个文件夹。
|
||||
2. 创建一个新的 `.md` 或 `.json` 文件。
|
||||
3. 提交 Pull Request (PR)。
|
||||
## 💡 Important
|
||||
|
||||
### 2. 开发插件 (Plugins)
|
||||
- Ensure your plugin includes complete metadata (title, author, version, description).
|
||||
- If updating an existing plugin, please **increment the version number** (e.g., `0.1.0` -> `0.1.1`) to trigger the auto-update.
|
||||
|
||||
如果你开发了一个新的 OpenWebUI 插件 (Function/Tool):
|
||||
1. 确保你的插件代码包含完整的元数据(Frontmatter):
|
||||
```python
|
||||
"""
|
||||
title: 插件名称
|
||||
author: 你的名字
|
||||
version: 0.1.0
|
||||
description: 简短描述插件的功能
|
||||
"""
|
||||
```
|
||||
2. 将插件文件放入 `plugins/` 目录下的合适位置:
|
||||
- `plugins/actions/`: 用于添加按钮或修改消息的 Action 插件。
|
||||
- `plugins/filters/`: 用于拦截请求或响应的 Filter 插件。
|
||||
- `plugins/pipes/`: 用于自定义模型或 API 的 Pipe 插件。
|
||||
- `plugins/tools/`: 用于 LLM 调用的 Tool 插件。
|
||||
3. 建议在 `docs/` 下添加一个简单的使用说明。
|
||||
|
||||
### 3. 改进文档
|
||||
|
||||
如果你发现文档有错误或可以改进的地方,直接提交 PR 即可。
|
||||
|
||||
## 🛠️ 开发规范
|
||||
|
||||
- **代码风格**:Python 代码请遵循 PEP 8 规范。
|
||||
- **注释**:关键逻辑请添加注释,方便他人理解。
|
||||
- **测试**:提交前请在本地 OpenWebUI 环境中测试通过。
|
||||
|
||||
## 📝 提交 PR
|
||||
|
||||
1. Fork 本仓库。
|
||||
2. 创建一个新的分支 (`git checkout -b feature/AmazingFeature`)。
|
||||
3. 提交你的修改 (`git commit -m 'Add some AmazingFeature'`)。
|
||||
4. 推送到分支 (`git push origin feature/AmazingFeature`)。
|
||||
5. 开启一个 Pull Request。
|
||||
|
||||
## 📦 版本更新与发布
|
||||
|
||||
当你更新插件时,请遵循以下流程:
|
||||
|
||||
### 1. 更新版本号
|
||||
|
||||
在插件文件的 docstring 中更新版本号(遵循[语义化版本](https://semver.org/lang/zh-CN/)):
|
||||
|
||||
```python
|
||||
"""
|
||||
title: 我的插件
|
||||
version: 0.2.0 # 更新此处
|
||||
...
|
||||
"""
|
||||
```
|
||||
|
||||
### 2. 更新更新日志
|
||||
|
||||
在 `CHANGELOG.md` 的 `[Unreleased]` 部分添加你的更改:
|
||||
|
||||
```markdown
|
||||
### Added / 新增
|
||||
- 新功能描述
|
||||
|
||||
### Fixed / 修复
|
||||
- Bug 修复描述
|
||||
```
|
||||
|
||||
### 3. 发布流程
|
||||
|
||||
维护者会通过以下方式发布新版本:
|
||||
- 手动触发 GitHub Actions 中的 "Plugin Release" 工作流
|
||||
- 或创建版本标签 (`v*`)
|
||||
|
||||
详细说明请参阅 [发布工作流文档](docs/release-workflow.zh.md)。
|
||||
|
||||
再次感谢你的贡献!🚀
|
||||
Thank you! 🚀
|
||||
|
||||
16
CONTRIBUTING_CN.md
Normal file
16
CONTRIBUTING_CN.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# 贡献指南
|
||||
|
||||
感谢你对 **OpenWebUI Extras** 感兴趣!
|
||||
|
||||
## 🚀 贡献流程
|
||||
|
||||
1. **Fork** 本仓库。
|
||||
2. **修改/添加** `plugins/` 目录下的插件文件。
|
||||
3. **提交 PR**: 我们会尽快审核并合并。
|
||||
|
||||
## 💡 注意事项
|
||||
|
||||
- 请确保插件包含完整的元数据(title, author, version, description)。
|
||||
- 如果是更新已有插件,请记得**增加版本号**(如 `0.1.0` -> `0.1.1`),这样系统会自动同步更新。
|
||||
|
||||
再次感谢你的贡献!🚀
|
||||
131
README.md
131
README.md
@@ -1,68 +1,116 @@
|
||||
# OpenWebUI Extras
|
||||
|
||||
English | [中文](./README_CN.md)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[](#contributors-)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
A collection of enhancements, plugins, and prompts for [OpenWebUI](https://github.com/open-webui/open-webui), developed and curated for personal use to extend functionality and improve experience.
|
||||
|
||||
<!-- STATS_START -->
|
||||
## 📊 Community Stats
|
||||
|
||||
> 🕐 Auto-updated: 2026-01-08 00:11
|
||||
> 🕐 Auto-updated: 2026-02-10 12:51
|
||||
|
||||
| 👤 Author | 👥 Followers | ⭐ Points | 🏆 Contributions |
|
||||
|:---:|:---:|:---:|:---:|
|
||||
| [Fu-Jie](https://openwebui.com/u/Fu-Jie) | **49** | **63** | **18** |
|
||||
| :---: | :---: | :---: | :---: |
|
||||
| [Fu-Jie](https://openwebui.com/u/Fu-Jie) | **216** | **262** | **44** |
|
||||
|
||||
| 📝 Posts | ⬇️ Downloads | 👁️ Views | 👍 Upvotes | 💾 Saves |
|
||||
|:---:|:---:|:---:|:---:|:---:|
|
||||
| **11** | **889** | **9358** | **55** | **48** |
|
||||
| :---: | :---: | :---: | :---: | :---: |
|
||||
| **21** | **4031** | **47177** | **224** | **267** |
|
||||
|
||||
### 🔥 Top 6 Popular Plugins
|
||||
|
||||
| Rank | Plugin | Downloads | Views |
|
||||
|:---:|------|:---:|:---:|
|
||||
| 🥇 | [Turn Any Text into Beautiful Mind Maps](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | 283 | 2441 |
|
||||
| 🥈 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | 175 | 486 |
|
||||
| 🥉 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | 118 | 1287 |
|
||||
| 4️⃣ | [Flash Card ](https://openwebui.com/posts/flash_card_65a2ea8f) | 82 | 1528 |
|
||||
| 5️⃣ | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | 80 | 1081 |
|
||||
| 6️⃣ | [Export to Word (Enhanced Formatting)](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | 67 | 605 |
|
||||
> 🕐 Auto-updated: 2026-02-10 12:51
|
||||
|
||||
| Rank | Plugin | Version | Downloads | Views | Updated |
|
||||
| :---: | :--- | :---: | :---: | :---: | :---: |
|
||||
| 🥇 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | 0.9.2 | 935 | 8224 | 2026-01-28 |
|
||||
| 🥈 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | 1.5.0 | 678 | 6439 | 2026-01-30 |
|
||||
| 🥉 | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | 0.4.4 | 374 | 2931 | 2026-02-07 |
|
||||
| 4️⃣ | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | 1.2.2 | 360 | 3689 | 2026-01-28 |
|
||||
| 5️⃣ | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | 0.3.7 | 340 | 1631 | 2026-02-09 |
|
||||
| 6️⃣ | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | 1.2.4 | 323 | 4559 | 2026-01-29 |
|
||||
|
||||
*See full stats in [Community Stats Report](./docs/community-stats.md)*
|
||||
<!-- STATS_END -->
|
||||
|
||||
## 🌟 Star Features
|
||||
|
||||
### 1. [GitHub Copilot SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) [](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4)
|
||||
|
||||
**The ultimate Agent for OpenWebUI.** Supports native code execution (Python/Pandas), raw file analysis, and interactive artifacts.
|
||||
> [!TIP]
|
||||
> **No GitHub Copilot subscription required!** Supports **BYOK (Bring Your Own Key)** mode using your own OpenAI/Anthropic API keys.
|
||||
|
||||
### 2. [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) [](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a)
|
||||
|
||||
**Experience interactive thinking.** Seamlessly transforms complex chat sessions into structured, clickable mind maps for better visual modeling and rapid idea extraction.
|
||||
|
||||
### 3. [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) [](https://openwebui.com/posts/smart_infographic_ad6f0c7f)
|
||||
|
||||
**Professional data storytelling.** Converts raw information into sleek, boardroom-ready infographics powered by AntV, perfect for summarizing long-form content instantly.
|
||||
|
||||
### 4. [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) [](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315)
|
||||
|
||||
**High-fidelity reporting.** Export conversation history into professionally formatted Word documents with preserved headers, code blocks, and math formulas.
|
||||
|
||||
### 5. [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) [](https://openwebui.com/posts/async_context_compression_b1655bc8)
|
||||
|
||||
**Maximize your context window.** Intelligently compresses chat history using LLM logic to save tokens and costs while maintaining a high-quality reasoning chain.
|
||||
|
||||
## 📦 Project Contents
|
||||
|
||||
### 🧩 Plugins
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary><b>🧩 Plugins (Actions, Filters, Pipes, Pipelines)</b></summary>
|
||||
|
||||
Located in the `plugins/` directory, containing Python-based enhancements:
|
||||
|
||||
#### Actions
|
||||
### Actions
|
||||
|
||||
- **Smart Mind Map** (`smart-mind-map`): Generates interactive mind maps from text.
|
||||
- **Smart Infographic** (`infographic`): Transforms text into professional infographics using AntV.
|
||||
- **Knowledge Card** (`knowledge-card`): Creates beautiful flashcards for learning.
|
||||
- **Flash Card** (`flash-card`): Quickly generates beautiful flashcards for learning.
|
||||
- **Deep Dive** (`deep-dive`): A comprehensive thinking lens that dives deep into any content.
|
||||
- **Export to Excel** (`export_to_excel`): Exports chat history to Excel files.
|
||||
- **Export to Word** (`export_to_docx`): Exports chat history to Word documents.
|
||||
- **Summary** (`summary`): Text summarization tool.
|
||||
|
||||
#### Filters
|
||||
### Filters
|
||||
|
||||
- **GitHub Copilot SDK Files Filter** (`github_copilot_sdk_files_filter`): Essential companion for Copilot SDK. Bypasses RAG to ensure full file accessibility for Agents.
|
||||
- **Web Gemini Multimodal Filter** (`web_gemini_multimodel_filter`): Adds multimodal capabilities (PDF, Video, Office) to any model with intelligent routing.
|
||||
- **Async Context Compression** (`async-context-compression`): Optimizes token usage via context compression.
|
||||
- **Context Enhancement** (`context_enhancement_filter`): Enhances chat context.
|
||||
- **Gemini Manifold Companion** (`gemini_manifold_companion`): Companion filter for Gemini Manifold.
|
||||
- **Folder Memory** (`folder-memory`): Automatically extracts project rules from conversations and injects them into the folder's system prompt.
|
||||
- **Markdown Normalizer** (`markdown_normalizer`): Fixes common Markdown formatting issues in LLM outputs.
|
||||
|
||||
### Pipes
|
||||
|
||||
#### Pipes
|
||||
- **Gemini Manifold** (`gemini_mainfold`): Pipeline for Gemini model integration.
|
||||
- **GitHub Copilot SDK** (`github-copilot-sdk`): Official GitHub Copilot SDK integration. Supports dynamic models (GPT-4o, Claude 3.5, o1), multi-turn conversation, streaming, and infinite sessions.
|
||||
|
||||
### Pipelines
|
||||
|
||||
#### Pipelines
|
||||
- **MoE Prompt Refiner** (`moe_prompt_refiner`): Refines prompts for Mixture of Experts (MoE) summary requests to generate high-quality comprehensive reports.
|
||||
|
||||
### 🎯 Prompts
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
Located in the `prompts/` directory, containing fine-tuned System Prompts:
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary><b>🎯 Prompts (System Prompts for various roles)</b></summary>
|
||||
|
||||
- **Coding**: Programming assistance prompts.
|
||||
- **Marketing**: Marketing and copywriting prompts.
|
||||
System Prompts are managed in the `docs/prompts/` directory:
|
||||
|
||||
- **[Prompt Library](./docs/prompts/library.md)**: A curated collection of fine-tuned prompts for Coding, Marketing, and Analysis.
|
||||
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
## 🛠️ Extensions
|
||||
|
||||
Standalone frontend extensions to supercharge your Open WebUI:
|
||||
|
||||
- **[Open WebUI Prompt Plus](https://github.com/Fu-Jie/open-webui-prompt-plus)**: An all-in-one prompt management suite featuring AI-powered prompt generation, spotlight-style quick search, and advanced category organization.
|
||||
|
||||
## 📖 Documentation
|
||||
|
||||
@@ -99,8 +147,35 @@ This project is a collection of resources and does not require a Python environm
|
||||
### Contributing
|
||||
|
||||
If you have great prompts or plugins to share:
|
||||
|
||||
1. Fork this repository.
|
||||
2. Add your files to the appropriate `prompts/` or `plugins/` directory.
|
||||
3. Submit a Pull Request.
|
||||
|
||||
[Contributing](./CONTRIBUTING.md)
|
||||
|
||||
## Contributors ✨
|
||||
|
||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rbb-dev"><img src="https://avatars.githubusercontent.com/u/37469229?v=4?s=100" width="100px;" alt="rbb-dev"/><br /><sub><b>rbb-dev</b></sub></a><br /><a href="#ideas-rbb-dev" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/Fu-Jie/awesome-openwebui/commits?author=rbb-dev" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://trade.xyz/?ref=BZ1RJRXWO"><img src="https://avatars.githubusercontent.com/u/7317522?v=4?s=100" width="100px;" alt="Raxxoor"/><br /><sub><b>Raxxoor</b></sub></a><br /><a href="https://github.com/Fu-Jie/awesome-openwebui/issues?q=author%3Adhaern" title="Bug reports">🐛</a> <a href="#ideas-dhaern" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/i-iooi-i"><img src="https://avatars.githubusercontent.com/u/1827701?v=4?s=100" width="100px;" alt="ZOLO"/><br /><sub><b>ZOLO</b></sub></a><br /><a href="https://github.com/Fu-Jie/awesome-openwebui/issues?q=author%3Ai-iooi-i" title="Bug reports">🐛</a> <a href="#ideas-i-iooi-i" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://perso.crans.org/grande/"><img src="https://avatars.githubusercontent.com/u/469017?v=4?s=100" width="100px;" alt="Johan Grande"/><br /><sub><b>Johan Grande</b></sub></a><br /><a href="#ideas-nahoj" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/abaroni"><img src="https://avatars.githubusercontent.com/u/21365486?v=4?s=100" width="100px;" alt="Alessandro Baroni"/><br /><sub><b>Alessandro Baroni</b></sub></a><br /><a href="#ideas-abaroni" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-restore -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
136
README_CN.md
136
README_CN.md
@@ -7,66 +7,116 @@ OpenWebUI 增强功能集合。包含个人开发与收集的插件、提示词
|
||||
<!-- STATS_START -->
|
||||
## 📊 社区统计
|
||||
|
||||
> 🕐 自动更新于 2026-01-08 00:11
|
||||
> 🕐 自动更新于 2026-02-10 12:51
|
||||
|
||||
| 👤 作者 | 👥 粉丝 | ⭐ 积分 | 🏆 贡献 |
|
||||
|:---:|:---:|:---:|:---:|
|
||||
| [Fu-Jie](https://openwebui.com/u/Fu-Jie) | **49** | **63** | **18** |
|
||||
| :---: | :---: | :---: | :---: |
|
||||
| [Fu-Jie](https://openwebui.com/u/Fu-Jie) | **216** | **262** | **44** |
|
||||
|
||||
| 📝 发布 | ⬇️ 下载 | 👁️ 浏览 | 👍 点赞 | 💾 收藏 |
|
||||
|:---:|:---:|:---:|:---:|:---:|
|
||||
| **11** | **889** | **9358** | **55** | **48** |
|
||||
| :---: | :---: | :---: | :---: | :---: |
|
||||
| **21** | **4031** | **47177** | **224** | **267** |
|
||||
|
||||
### 🔥 热门插件 Top 6
|
||||
|
||||
| 排名 | 插件 | 下载 | 浏览 |
|
||||
|:---:|------|:---:|:---:|
|
||||
| 🥇 | [Turn Any Text into Beautiful Mind Maps](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | 283 | 2441 |
|
||||
| 🥈 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | 175 | 486 |
|
||||
| 🥉 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | 118 | 1287 |
|
||||
| 4️⃣ | [Flash Card ](https://openwebui.com/posts/flash_card_65a2ea8f) | 82 | 1528 |
|
||||
| 5️⃣ | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | 80 | 1081 |
|
||||
| 6️⃣ | [Export to Word (Enhanced Formatting)](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | 67 | 605 |
|
||||
> 🕐 自动更新于 2026-02-10 12:51
|
||||
|
||||
| 排名 | 插件 | 版本 | 下载 | 浏览 | 更新日期 |
|
||||
| :---: | :--- | :---: | :---: | :---: | :---: |
|
||||
| 🥇 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | 0.9.2 | 935 | 8224 | 2026-01-28 |
|
||||
| 🥈 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | 1.5.0 | 678 | 6439 | 2026-01-30 |
|
||||
| 🥉 | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | 0.4.4 | 374 | 2931 | 2026-02-07 |
|
||||
| 4️⃣ | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | 1.2.2 | 360 | 3689 | 2026-01-28 |
|
||||
| 5️⃣ | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | 0.3.7 | 340 | 1631 | 2026-02-09 |
|
||||
| 6️⃣ | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | 1.2.4 | 323 | 4559 | 2026-01-29 |
|
||||
|
||||
*完整统计请查看 [社区统计报告](./docs/community-stats.zh.md)*
|
||||
<!-- STATS_END -->
|
||||
|
||||
## 🌟 精选功能
|
||||
|
||||
### 1. [GitHub Copilot SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) [](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4)
|
||||
|
||||
**OpenWebUI 终极 Agent 增强。** 支持原生代码执行(Python/Pandas)、原始文件直接分析以及交互式 Artifacts。
|
||||
> [!TIP]
|
||||
> **无需 GitHub Copilot 订阅!** 支持 **BYOK (Bring Your Own Key)** 模式,使用你自己的 OpenAI/Anthropic API Key。
|
||||
|
||||
### 2. [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) [](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a)
|
||||
|
||||
**体验浸入式思维。** 将复杂的对话瞬间转化为结构化、可点击的交互式思维导图,助力知识建模与逻辑提取。
|
||||
|
||||
### 3. [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) [](https://openwebui.com/posts/smart_infographic_ad6f0c7f)
|
||||
|
||||
**专业数据叙事。** 将零散信息转化为精美的信息图表(由 AntV 驱动),一键生成学术/汇报级的可视化总结。
|
||||
|
||||
### 4. [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) [](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315)
|
||||
|
||||
**高保真文档导出。** 将对话历史导出为格式完美的 Word 文档,完美保留标题、代码块、LaTeX 公式及 Mermaid 流程图。
|
||||
|
||||
### 5. [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) [](https://openwebui.com/posts/async_context_compression_b1655bc8)
|
||||
|
||||
**挑战 Token 極限。** 采用多专家异步压缩逻辑,在保持高吞吐量推理链的同时,大幅降低 Token 消耗。
|
||||
|
||||
## 📦 项目内容
|
||||
|
||||
### 🧩 插件 (Plugins)
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary><b>🧩 插件 (Actions, Filters, Pipes, Pipelines)</b></summary>
|
||||
|
||||
位于 `plugins/` 目录,包含各类 Python 编写的功能增强插件:
|
||||
|
||||
#### Actions (交互增强)
|
||||
### Actions (交互增强)
|
||||
|
||||
- **Smart Mind Map** (`smart-mind-map`): 智能分析文本并生成交互式思维导图。
|
||||
- **Smart Infographic** (`infographic`): 基于 AntV 的智能信息图生成工具。
|
||||
- **Knowledge Card** (`knowledge-card`): 快速生成精美的学习记忆卡片。
|
||||
- **Flash Card** (`flash-card`): 快速生成精美的学习记忆卡片。
|
||||
- **Deep Dive** (`deep-dive`): 深度思考透镜,从背景、逻辑、洞察到行动路径的全方位分析。
|
||||
- **Export to Excel** (`export_to_excel`): 将对话内容导出为 Excel 文件。
|
||||
- **Export to Word** (`export_to_docx`): 将对话内容导出为 Word 文档。
|
||||
- **Summary** (`summary`): 文本摘要生成工具。
|
||||
|
||||
#### Filters (消息处理)
|
||||
### Filters (消息处理)
|
||||
|
||||
- **GitHub Copilot SDK Files Filter** (`github_copilot_sdk_files_filter`): Copilot SDK 必备搭档。绕过 RAG,确保 Agent 能真正看到你的每一个文件。
|
||||
- **Web Gemini Multimodal Filter** (`web_gemini_multimodel_filter`): 为任意模型提供多模态能力(PDF、Office、视频等),支持智能路由。
|
||||
- **Async Context Compression** (`async-context-compression`): 异步上下文压缩,优化 Token 使用。
|
||||
- **Context Enhancement** (`context_enhancement_filter`): 上下文增强过滤器。
|
||||
- **Gemini Manifold Companion** (`gemini_manifold_companion`): Gemini Manifold 配套增强。
|
||||
- **Folder Memory** (`folder-memory`): 自动从对话中提取项目规则并注入到文件夹系统提示词中。
|
||||
- **Markdown Normalizer** (`markdown_normalizer`): 修复 LLM 输出中常见的 Markdown 格式问题。
|
||||
|
||||
#### Pipes (模型管道)
|
||||
- **Gemini Manifold** (`gemini_mainfold`): 集成 Gemini 模型的管道。
|
||||
### Pipes (模型管道)
|
||||
|
||||
- **GitHub Copilot SDK** (`github-copilot-sdk`): GitHub Copilot SDK 官方集成。支持动态模型、多轮对话、流式输出、图片输入及无限会话。
|
||||
|
||||
### Pipelines (工作流管道)
|
||||
|
||||
#### Pipelines (工作流管道)
|
||||
- **MoE Prompt Refiner** (`moe_prompt_refiner`): 优化多模型 (MoE) 汇总请求的提示词,生成高质量的综合报告。
|
||||
|
||||
### 🎯 提示词 (Prompts)
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
位于 `prompts/` 目录,包含精心调优的 System Prompts:
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary><b>🎯 提示词 (Prompts - 多角色系统提示词)</b></summary>
|
||||
|
||||
- **Coding**: 编程辅助类提示词。
|
||||
- **Marketing**: 营销文案类提示词。
|
||||
位于 `docs/prompts/` 目录,包含精心调优的提示词集合:
|
||||
|
||||
每个提示词都独立保存为 Markdown 文件,可直接在 OpenWebUI 中使用。
|
||||
- **[Prompt Library](./docs/prompts/library.md)**: 编程、翻译、分析及营销等全领域提示词精选。
|
||||
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
## 🛠️ 扩展 (Extensions)
|
||||
|
||||
Open WebUI 的前端增强扩展:
|
||||
|
||||
- **[Open WebUI Prompt Plus](https://github.com/Fu-Jie/open-webui-prompt-plus)**: 一站式提示词管理套件,支持 AI 提示词生成、Spotlight 风格快速搜索及高级分类管理。
|
||||
|
||||
## 📖 开发文档
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary><b>📚 官方开发与运营指南</b></summary>
|
||||
|
||||
位于 `docs/zh/` 目录:
|
||||
|
||||
- **[插件开发权威指南](./docs/zh/plugin_development_guide.md)** - 整合了入门教程、核心 SDK 详解及最佳实践的系统化指南。 ⭐
|
||||
@@ -74,35 +124,11 @@ OpenWebUI 增强功能集合。包含个人开发与收集的插件、提示词
|
||||
|
||||
更多示例请查看 `docs/examples/` 目录。
|
||||
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
本项目是一个资源集合,无需安装 Python 环境。你只需要下载对应的文件并导入到你的 OpenWebUI 实例中即可。
|
||||
|
||||
### 使用提示词 (Prompts)
|
||||
|
||||
1. 在 `/prompts` 目录中浏览并选择你感兴趣的提示词文件 (`.md`)。
|
||||
2. 复制文件内容。
|
||||
3. 在 OpenWebUI 聊天界面中,点击输入框上方的 "Prompt" 按钮。
|
||||
4. 粘贴内容并保存。
|
||||
|
||||
### 使用插件 (Plugins)
|
||||
|
||||
1. **从 OpenWebUI 社区安装 (推荐)**:
|
||||
- 访问我的主页: [Fu-Jie's Profile](https://openwebui.com/u/Fu-Jie)
|
||||
- 浏览插件列表,选择你喜欢的插件。
|
||||
- 点击 "Get" 按钮,将其直接导入到你的 OpenWebUI 实例中。
|
||||
|
||||
2. **手动安装**:
|
||||
- 在 `/plugins` 目录中浏览并下载你需要的插件文件 (`.py`)。
|
||||
- 打开 OpenWebUI 的 **管理员面板 (Admin Panel)** -> **设置 (Settings)** -> **插件 (Plugins)**。
|
||||
- 点击上传按钮,选择刚才下载的 `.py` 文件。
|
||||
- 上传成功后,刷新页面,你就可以在聊天设置或工具栏中启用该插件了。
|
||||
|
||||
### 贡献代码
|
||||
|
||||
如果你有优质的提示词或插件想要分享:
|
||||
1. Fork 本仓库。
|
||||
2. 将你的文件添加到对应的 `prompts/` 或 `plugins/` 目录。
|
||||
3. 提交 Pull Request。
|
||||
|
||||
[贡献指南](./CONTRIBUTING.md) | [更新日志](./CHANGELOG.md)
|
||||
[贡献指南](./CONTRIBUTING_CN.md) | [更新日志](./CHANGELOG.md)
|
||||
|
||||
53
docs/PLUGIN_README_TEMPLATE.md
Normal file
53
docs/PLUGIN_README_TEMPLATE.md
Normal file
@@ -0,0 +1,53 @@
|
||||
<!--
|
||||
NOTE: This template is for the English version (README.md).
|
||||
The Chinese version (README_CN.md) MUST be translated based on this English version to ensure consistency in structure and content.
|
||||
-->
|
||||
# [Plugin Name] [Optional Emoji]
|
||||
|
||||
[Brief description of what the plugin does. Keep it concise and engaging.]
|
||||
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 1.0.0 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **License:** MIT
|
||||
|
||||
## What's New
|
||||
|
||||
<!-- Keep only the latest update here. Remove this section for the initial release. -->
|
||||
|
||||
### v1.0.0
|
||||
|
||||
- **Initial Release**: Released the first version of the plugin.
|
||||
- **[Feature Name]**: [Brief description of the feature].
|
||||
|
||||
## Key Features 🔑
|
||||
|
||||
- **[Feature 1]**: [Description of feature 1].
|
||||
- **[Feature 2]**: [Description of feature 2].
|
||||
- **[Feature 3]**: [Description of feature 3].
|
||||
|
||||
## How to Use 🛠️
|
||||
|
||||
1. **Install**: Add the plugin to your OpenWebUI instance.
|
||||
2. **Configure**: Adjust settings in the Valves menu (optional).
|
||||
3. **[Action Step]**: Describe how to trigger or use the plugin.
|
||||
4. **[Result Step]**: Describe the expected outcome.
|
||||
|
||||
## Configuration (Valves) ⚙️
|
||||
|
||||
| Valve | Default | Description |
|
||||
|-------|---------|-------------|
|
||||
| `VALVE_NAME` | `Default Value` | Description of what this setting does. |
|
||||
| `ANOTHER_VALVE` | `True` | Another setting description. |
|
||||
|
||||
## ⭐ Support
|
||||
|
||||
If this plugin has been useful, a star on [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) is a big motivation for me. Thank you for the support.
|
||||
|
||||
## Troubleshooting ❓
|
||||
|
||||
- **Plugin not working?**: Check if the filter/action is enabled in the model settings.
|
||||
- **Debug Logs**: Enable `SHOW_DEBUG_LOG` in Valves and check the browser console (F12) for detailed logs.
|
||||
- **Error Messages**: If you see an error, please copy the full error message and report it.
|
||||
- **Submit an Issue**: If you encounter any problems, please submit an issue on GitHub: [Awesome OpenWebUI Issues](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
|
||||
## Changelog
|
||||
|
||||
See the full history on GitHub: [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
7
docs/badges/downloads.json
Normal file
7
docs/badges/downloads.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"label": "downloads",
|
||||
"message": "4.0k",
|
||||
"color": "blue",
|
||||
"namedLogo": "openwebui"
|
||||
}
|
||||
6
docs/badges/followers.json
Normal file
6
docs/badges/followers.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"label": "followers",
|
||||
"message": "216",
|
||||
"color": "blue"
|
||||
}
|
||||
6
docs/badges/plugins.json
Normal file
6
docs/badges/plugins.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"label": "plugins",
|
||||
"message": "21",
|
||||
"color": "green"
|
||||
}
|
||||
6
docs/badges/points.json
Normal file
6
docs/badges/points.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"label": "points",
|
||||
"message": "262",
|
||||
"color": "orange"
|
||||
}
|
||||
6
docs/badges/upvotes.json
Normal file
6
docs/badges/upvotes.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"label": "upvotes",
|
||||
"message": "224",
|
||||
"color": "brightgreen"
|
||||
}
|
||||
@@ -1,191 +1,352 @@
|
||||
{
|
||||
"total_posts": 11,
|
||||
"total_downloads": 889,
|
||||
"total_views": 9358,
|
||||
"total_upvotes": 55,
|
||||
"total_downvotes": 1,
|
||||
"total_saves": 48,
|
||||
"total_comments": 15,
|
||||
"total_posts": 21,
|
||||
"total_downloads": 4031,
|
||||
"total_views": 47177,
|
||||
"total_upvotes": 224,
|
||||
"total_downvotes": 2,
|
||||
"total_saves": 267,
|
||||
"total_comments": 51,
|
||||
"by_type": {
|
||||
"action": 9,
|
||||
"filter": 2
|
||||
"action": 15,
|
||||
"filter": 2,
|
||||
"unknown": 4
|
||||
},
|
||||
"posts": [
|
||||
{
|
||||
"title": "Turn Any Text into Beautiful Mind Maps",
|
||||
"title": "Smart Mind Map",
|
||||
"slug": "turn_any_text_into_beautiful_mind_maps_3094c59a",
|
||||
"type": "action",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"author": "Fu-Jie",
|
||||
"description": "Intelligently analyzes text content and generates interactive mind maps to help users structure and visualize knowledge.",
|
||||
"downloads": 283,
|
||||
"views": 2441,
|
||||
"upvotes": 10,
|
||||
"saves": 15,
|
||||
"comments": 10,
|
||||
"downloads": 935,
|
||||
"views": 8224,
|
||||
"upvotes": 22,
|
||||
"saves": 50,
|
||||
"comments": 13,
|
||||
"created_at": "2025-12-30",
|
||||
"updated_at": "2026-01-06",
|
||||
"updated_at": "2026-01-28",
|
||||
"url": "https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a"
|
||||
},
|
||||
{
|
||||
"title": "Export to Excel",
|
||||
"slug": "export_mulit_table_to_excel_244b8f9d",
|
||||
"type": "action",
|
||||
"version": "0.3.6",
|
||||
"author": "Fu-Jie",
|
||||
"description": "Extracts tables from chat messages and exports them to Excel (.xlsx) files with smart formatting.",
|
||||
"downloads": 175,
|
||||
"views": 486,
|
||||
"upvotes": 3,
|
||||
"saves": 3,
|
||||
"comments": 0,
|
||||
"created_at": "2025-05-30",
|
||||
"updated_at": "2026-01-03",
|
||||
"url": "https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d"
|
||||
},
|
||||
{
|
||||
"title": "Async Context Compression",
|
||||
"slug": "async_context_compression_b1655bc8",
|
||||
"type": "filter",
|
||||
"version": "1.1.0",
|
||||
"author": "Fu-Jie",
|
||||
"description": "This filter automatically compresses long conversation contexts by intelligently summarizing and removing intermediate messages while preserving critical information, thereby significantly reducing token consumption.",
|
||||
"downloads": 118,
|
||||
"views": 1287,
|
||||
"upvotes": 5,
|
||||
"saves": 9,
|
||||
"comments": 0,
|
||||
"created_at": "2025-11-08",
|
||||
"updated_at": "2025-12-31",
|
||||
"url": "https://openwebui.com/posts/async_context_compression_b1655bc8"
|
||||
},
|
||||
{
|
||||
"title": "Flash Card ",
|
||||
"slug": "flash_card_65a2ea8f",
|
||||
"type": "action",
|
||||
"version": "0.2.4",
|
||||
"author": "Fu-Jie",
|
||||
"description": "Quickly generates beautiful flashcards from text, extracting key points and categories.",
|
||||
"downloads": 82,
|
||||
"views": 1528,
|
||||
"upvotes": 8,
|
||||
"saves": 5,
|
||||
"comments": 2,
|
||||
"created_at": "2025-12-30",
|
||||
"updated_at": "2026-01-03",
|
||||
"url": "https://openwebui.com/posts/flash_card_65a2ea8f"
|
||||
},
|
||||
{
|
||||
"title": "Smart Infographic",
|
||||
"slug": "smart_infographic_ad6f0c7f",
|
||||
"type": "action",
|
||||
"version": "1.4.0",
|
||||
"author": "jeff",
|
||||
"version": "1.5.0",
|
||||
"author": "Fu-Jie",
|
||||
"description": "AI-powered infographic generator based on AntV Infographic. Supports professional templates, auto-icon matching, and SVG/PNG downloads.",
|
||||
"downloads": 80,
|
||||
"views": 1081,
|
||||
"upvotes": 7,
|
||||
"saves": 8,
|
||||
"comments": 2,
|
||||
"downloads": 678,
|
||||
"views": 6439,
|
||||
"upvotes": 24,
|
||||
"saves": 34,
|
||||
"comments": 10,
|
||||
"created_at": "2025-12-28",
|
||||
"updated_at": "2026-01-07",
|
||||
"updated_at": "2026-01-30",
|
||||
"url": "https://openwebui.com/posts/smart_infographic_ad6f0c7f"
|
||||
},
|
||||
{
|
||||
"title": "Export to Word (Enhanced Formatting)",
|
||||
"title": "Export to Word Enhanced",
|
||||
"slug": "export_to_word_enhanced_formatting_fca6a315",
|
||||
"type": "action",
|
||||
"version": "0.4.2",
|
||||
"version": "0.4.4",
|
||||
"author": "Fu-Jie",
|
||||
"description": "Export the current conversation to a formatted Word doc with syntax highlighting, AI-generated titles, and perfect Markdown rendering (tables, quotes, lists).",
|
||||
"downloads": 67,
|
||||
"views": 605,
|
||||
"upvotes": 5,
|
||||
"saves": 4,
|
||||
"comments": 0,
|
||||
"description": "Export current conversation from Markdown to Word (.docx) with Mermaid diagrams rendered client-side (Mermaid.js, SVG+PNG), LaTeX math, real hyperlinks, improved tables, syntax highlighting, and blockquote support.",
|
||||
"downloads": 374,
|
||||
"views": 2931,
|
||||
"upvotes": 14,
|
||||
"saves": 26,
|
||||
"comments": 3,
|
||||
"created_at": "2026-01-03",
|
||||
"updated_at": "2026-01-07",
|
||||
"updated_at": "2026-02-07",
|
||||
"url": "https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315"
|
||||
},
|
||||
{
|
||||
"title": "Async Context Compression",
|
||||
"slug": "async_context_compression_b1655bc8",
|
||||
"type": "action",
|
||||
"version": "1.2.2",
|
||||
"author": "Fu-Jie",
|
||||
"description": "Reduces token consumption in long conversations while maintaining coherence through intelligent summarization and message compression.",
|
||||
"downloads": 360,
|
||||
"views": 3689,
|
||||
"upvotes": 14,
|
||||
"saves": 33,
|
||||
"comments": 0,
|
||||
"created_at": "2025-11-08",
|
||||
"updated_at": "2026-01-28",
|
||||
"url": "https://openwebui.com/posts/async_context_compression_b1655bc8"
|
||||
},
|
||||
{
|
||||
"title": "Export to Excel",
|
||||
"slug": "export_mulit_table_to_excel_244b8f9d",
|
||||
"type": "action",
|
||||
"version": "0.3.7",
|
||||
"author": "Fu-Jie",
|
||||
"description": "Extracts tables from chat messages and exports them to Excel (.xlsx) files with smart formatting.",
|
||||
"downloads": 340,
|
||||
"views": 1631,
|
||||
"upvotes": 7,
|
||||
"saves": 6,
|
||||
"comments": 0,
|
||||
"created_at": "2025-05-30",
|
||||
"updated_at": "2026-02-09",
|
||||
"url": "https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d"
|
||||
},
|
||||
{
|
||||
"title": "Markdown Normalizer",
|
||||
"slug": "markdown_normalizer_baaa8732",
|
||||
"type": "action",
|
||||
"version": "1.2.4",
|
||||
"author": "Fu-Jie",
|
||||
"description": "A content normalizer filter that fixes common Markdown formatting issues in LLM outputs, such as broken code blocks, LaTeX formulas, and list formatting.",
|
||||
"downloads": 323,
|
||||
"views": 4559,
|
||||
"upvotes": 17,
|
||||
"saves": 28,
|
||||
"comments": 5,
|
||||
"created_at": "2026-01-12",
|
||||
"updated_at": "2026-01-29",
|
||||
"url": "https://openwebui.com/posts/markdown_normalizer_baaa8732"
|
||||
},
|
||||
{
|
||||
"title": "Flash Card",
|
||||
"slug": "flash_card_65a2ea8f",
|
||||
"type": "action",
|
||||
"version": "0.2.4",
|
||||
"author": "Fu-Jie",
|
||||
"description": "Quickly generates beautiful flashcards from text, extracting key points and categories.",
|
||||
"downloads": 220,
|
||||
"views": 3342,
|
||||
"upvotes": 13,
|
||||
"saves": 14,
|
||||
"comments": 2,
|
||||
"created_at": "2025-12-30",
|
||||
"updated_at": "2026-01-28",
|
||||
"url": "https://openwebui.com/posts/flash_card_65a2ea8f"
|
||||
},
|
||||
{
|
||||
"title": "AI Task Instruction Generator",
|
||||
"slug": "ai_task_instruction_generator_9bab8b37",
|
||||
"type": "unknown",
|
||||
"version": "",
|
||||
"author": "",
|
||||
"description": "",
|
||||
"downloads": 184,
|
||||
"views": 2608,
|
||||
"upvotes": 9,
|
||||
"saves": 5,
|
||||
"comments": 0,
|
||||
"created_at": "2026-01-28",
|
||||
"updated_at": "2026-01-28",
|
||||
"url": "https://openwebui.com/posts/ai_task_instruction_generator_9bab8b37"
|
||||
},
|
||||
{
|
||||
"title": "Deep Dive",
|
||||
"slug": "deep_dive_c0b846e4",
|
||||
"type": "action",
|
||||
"version": "1.0.0",
|
||||
"author": "Fu-Jie",
|
||||
"description": "A comprehensive thinking lens that dives deep into any content - from context to logic, insights, and action paths.",
|
||||
"downloads": 146,
|
||||
"views": 1237,
|
||||
"upvotes": 6,
|
||||
"saves": 11,
|
||||
"comments": 0,
|
||||
"created_at": "2026-01-08",
|
||||
"updated_at": "2026-01-08",
|
||||
"url": "https://openwebui.com/posts/deep_dive_c0b846e4"
|
||||
},
|
||||
{
|
||||
"title": "导出为Word增强版",
|
||||
"slug": "导出为_word_支持公式流程图表格和代码块_8a6306c0",
|
||||
"type": "action",
|
||||
"version": "0.4.4",
|
||||
"author": "Fu-Jie",
|
||||
"description": "将对话导出为 Word (.docx),支持 Mermaid 图表 (客户端渲染 SVG+PNG)、LaTeX 数学公式、真实超链接、增强表格格式、代码高亮和引用块。",
|
||||
"downloads": 125,
|
||||
"views": 2187,
|
||||
"upvotes": 13,
|
||||
"saves": 7,
|
||||
"comments": 4,
|
||||
"created_at": "2026-01-04",
|
||||
"updated_at": "2026-02-07",
|
||||
"url": "https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0"
|
||||
},
|
||||
{
|
||||
"title": "GitHub Copilot Official SDK Pipe",
|
||||
"slug": "github_copilot_official_sdk_pipe_ce96f7b4",
|
||||
"type": "action",
|
||||
"version": "0.6.1",
|
||||
"author": "Fu-Jie",
|
||||
"description": "Integrate GitHub Copilot SDK. Supports dynamic models, multi-turn conversation, streaming, multimodal input, infinite sessions, and frontend debug logging.",
|
||||
"downloads": 95,
|
||||
"views": 2254,
|
||||
"upvotes": 13,
|
||||
"saves": 7,
|
||||
"comments": 4,
|
||||
"created_at": "2026-01-26",
|
||||
"updated_at": "2026-02-09",
|
||||
"url": "https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4"
|
||||
},
|
||||
{
|
||||
"title": "📂 Folder Memory – Auto-Evolving Project Context",
|
||||
"slug": "folder_memory_auto_evolving_project_context_4a9875b2",
|
||||
"type": "filter",
|
||||
"version": "0.1.0",
|
||||
"author": "Fu-Jie",
|
||||
"description": "Automatically extracts project rules from conversations and injects them into the folder's system prompt.",
|
||||
"downloads": 59,
|
||||
"views": 1278,
|
||||
"upvotes": 6,
|
||||
"saves": 8,
|
||||
"comments": 0,
|
||||
"created_at": "2026-01-20",
|
||||
"updated_at": "2026-01-20",
|
||||
"url": "https://openwebui.com/posts/folder_memory_auto_evolving_project_context_4a9875b2"
|
||||
},
|
||||
{
|
||||
"title": "智能信息图",
|
||||
"slug": "智能信息图_e04a48ff",
|
||||
"type": "action",
|
||||
"version": "1.3.1",
|
||||
"author": "jeff",
|
||||
"version": "1.5.0",
|
||||
"author": "Fu-Jie",
|
||||
"description": "基于 AntV Infographic 的智能信息图生成插件。支持多种专业模板,自动图标匹配,并提供 SVG/PNG 下载功能。",
|
||||
"downloads": 33,
|
||||
"views": 426,
|
||||
"upvotes": 3,
|
||||
"saves": 0,
|
||||
"downloads": 58,
|
||||
"views": 1048,
|
||||
"upvotes": 10,
|
||||
"saves": 1,
|
||||
"comments": 0,
|
||||
"created_at": "2025-12-28",
|
||||
"updated_at": "2025-12-29",
|
||||
"updated_at": "2026-01-29",
|
||||
"url": "https://openwebui.com/posts/智能信息图_e04a48ff"
|
||||
},
|
||||
{
|
||||
"title": "导出为 Word-支持公式、流程图、表格和代码块",
|
||||
"slug": "导出为_word_支持公式流程图表格和代码块_8a6306c0",
|
||||
"type": "action",
|
||||
"version": "0.4.1",
|
||||
"author": "Fu-Jie",
|
||||
"description": "将当前对话内容从 Markdown 转换并导出为 Word (.docx) 文件,支持中英文无乱码。",
|
||||
"downloads": 20,
|
||||
"views": 799,
|
||||
"upvotes": 7,
|
||||
"saves": 1,
|
||||
"comments": 1,
|
||||
"created_at": "2026-01-04",
|
||||
"updated_at": "2026-01-05",
|
||||
"url": "https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0"
|
||||
},
|
||||
{
|
||||
"title": "智能生成交互式思维导图,帮助用户可视化知识",
|
||||
"title": "思维导图",
|
||||
"slug": "智能生成交互式思维导图帮助用户可视化知识_8d4b097b",
|
||||
"type": "action",
|
||||
"version": "0.8.0",
|
||||
"author": "",
|
||||
"version": "0.9.2",
|
||||
"author": "Fu-Jie",
|
||||
"description": "智能分析文本内容,生成交互式思维导图,帮助用户结构化和可视化知识。",
|
||||
"downloads": 14,
|
||||
"views": 263,
|
||||
"upvotes": 2,
|
||||
"saves": 1,
|
||||
"downloads": 37,
|
||||
"views": 579,
|
||||
"upvotes": 6,
|
||||
"saves": 2,
|
||||
"comments": 0,
|
||||
"created_at": "2025-12-31",
|
||||
"updated_at": "2025-12-31",
|
||||
"updated_at": "2026-01-28",
|
||||
"url": "https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b"
|
||||
},
|
||||
{
|
||||
"title": "闪记卡生成插件",
|
||||
"slug": "闪记卡生成插件_4a31eac3",
|
||||
"type": "action",
|
||||
"version": "0.2.2",
|
||||
"author": "Fu-Jie",
|
||||
"description": "快速将文本提炼为精美的学习记忆卡片,支持核心要点提取与分类。",
|
||||
"downloads": 12,
|
||||
"views": 320,
|
||||
"upvotes": 3,
|
||||
"saves": 1,
|
||||
"comments": 0,
|
||||
"created_at": "2025-12-30",
|
||||
"updated_at": "2025-12-31",
|
||||
"url": "https://openwebui.com/posts/闪记卡生成插件_4a31eac3"
|
||||
},
|
||||
{
|
||||
"title": "异步上下文压缩",
|
||||
"slug": "异步上下文压缩_5c0617cb",
|
||||
"type": "filter",
|
||||
"version": "1.1.0",
|
||||
"type": "action",
|
||||
"version": "1.2.2",
|
||||
"author": "Fu-Jie",
|
||||
"description": "在 LLM 响应完成后进行上下文摘要和压缩",
|
||||
"downloads": 5,
|
||||
"views": 122,
|
||||
"upvotes": 2,
|
||||
"saves": 1,
|
||||
"description": "通过智能摘要和消息压缩,降低长对话的 token 消耗,同时保持对话连贯性。",
|
||||
"downloads": 33,
|
||||
"views": 656,
|
||||
"upvotes": 7,
|
||||
"saves": 5,
|
||||
"comments": 0,
|
||||
"created_at": "2025-11-08",
|
||||
"updated_at": "2025-12-31",
|
||||
"updated_at": "2026-01-28",
|
||||
"url": "https://openwebui.com/posts/异步上下文压缩_5c0617cb"
|
||||
},
|
||||
{
|
||||
"title": "闪记卡 (Flash Card)",
|
||||
"slug": "闪记卡生成插件_4a31eac3",
|
||||
"type": "action",
|
||||
"version": "0.2.4",
|
||||
"author": "Fu-Jie",
|
||||
"description": "快速将文本提炼为精美的学习记忆卡片,支持核心要点提取与分类。",
|
||||
"downloads": 27,
|
||||
"views": 680,
|
||||
"upvotes": 8,
|
||||
"saves": 1,
|
||||
"comments": 0,
|
||||
"created_at": "2025-12-30",
|
||||
"updated_at": "2026-01-28",
|
||||
"url": "https://openwebui.com/posts/闪记卡生成插件_4a31eac3"
|
||||
},
|
||||
{
|
||||
"title": "精读",
|
||||
"slug": "精读_99830b0f",
|
||||
"type": "action",
|
||||
"version": "1.0.0",
|
||||
"author": "Fu-Jie",
|
||||
"description": "全方位的思维透镜 —— 从背景全景到逻辑脉络,从深度洞察到行动路径。",
|
||||
"downloads": 24,
|
||||
"views": 425,
|
||||
"upvotes": 5,
|
||||
"saves": 1,
|
||||
"comments": 0,
|
||||
"created_at": "2026-01-08",
|
||||
"updated_at": "2026-01-08",
|
||||
"url": "https://openwebui.com/posts/精读_99830b0f"
|
||||
},
|
||||
{
|
||||
"title": "GitHub Copilot SDK Files Filter",
|
||||
"slug": "github_copilot_sdk_files_filter_403a62ee",
|
||||
"type": "filter",
|
||||
"version": "0.1.2",
|
||||
"author": "Fu-Jie",
|
||||
"description": "A specialized filter to bypass OpenWebUI's default RAG for GitHub Copilot SDK models. It moves uploaded files to a safe location ('copilot_files') so the Copilot Pipe can process them natively without interference.",
|
||||
"downloads": 13,
|
||||
"views": 370,
|
||||
"upvotes": 2,
|
||||
"saves": 0,
|
||||
"comments": 0,
|
||||
"created_at": "2026-02-09",
|
||||
"updated_at": "2026-02-09",
|
||||
"url": "https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee"
|
||||
},
|
||||
{
|
||||
"title": "🚀 Open WebUI Prompt Plus: AI-Powered Prompt Manager",
|
||||
"slug": "open_webui_prompt_plus_ai_powered_prompt_manager_s_15fa060e",
|
||||
"type": "unknown",
|
||||
"version": "",
|
||||
"author": "",
|
||||
"description": "",
|
||||
"downloads": 0,
|
||||
"views": 1481,
|
||||
"upvotes": 12,
|
||||
"saves": 18,
|
||||
"comments": 8,
|
||||
"created_at": "2026-01-25",
|
||||
"updated_at": "2026-01-28",
|
||||
"url": "https://openwebui.com/posts/open_webui_prompt_plus_ai_powered_prompt_manager_s_15fa060e"
|
||||
},
|
||||
{
|
||||
"title": "Review of Claude Haiku 4.5",
|
||||
"slug": "review_of_claude_haiku_45_41b0db39",
|
||||
"type": "unknown",
|
||||
"version": "",
|
||||
"author": "",
|
||||
"description": "",
|
||||
"downloads": 0,
|
||||
"views": 142,
|
||||
"upvotes": 2,
|
||||
"saves": 0,
|
||||
"comments": 0,
|
||||
"created_at": "2026-01-14",
|
||||
"updated_at": "2026-01-14",
|
||||
"url": "https://openwebui.com/posts/review_of_claude_haiku_45_41b0db39"
|
||||
},
|
||||
{
|
||||
"title": " 🛠️ Debug Open WebUI Plugins in Your Browser",
|
||||
"slug": "debug_open_webui_plugins_in_your_browser_81bf7960",
|
||||
"type": "unknown",
|
||||
"version": "",
|
||||
"author": "",
|
||||
"description": "",
|
||||
"downloads": 0,
|
||||
"views": 1417,
|
||||
"upvotes": 14,
|
||||
"saves": 10,
|
||||
"comments": 2,
|
||||
"created_at": "2026-01-10",
|
||||
"updated_at": "2026-01-10",
|
||||
"url": "https://openwebui.com/posts/debug_open_webui_plugins_in_your_browser_81bf7960"
|
||||
}
|
||||
],
|
||||
"user": {
|
||||
@@ -193,11 +354,11 @@
|
||||
"name": "Fu-Jie",
|
||||
"profile_url": "https://openwebui.com/u/Fu-Jie",
|
||||
"profile_image": "https://community.s3.openwebui.com/uploads/users/b15d1348-4347-42b4-b815-e053342d6cb0/profile_d9510745-4bd4-4f8f-a997-4a21847d9300.webp",
|
||||
"followers": 49,
|
||||
"following": 2,
|
||||
"total_points": 63,
|
||||
"post_points": 54,
|
||||
"comment_points": 9,
|
||||
"contributions": 18
|
||||
"followers": 216,
|
||||
"following": 4,
|
||||
"total_points": 262,
|
||||
"post_points": 222,
|
||||
"comment_points": 40,
|
||||
"contributions": 44
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,46 @@
|
||||
# 📊 OpenWebUI Community Stats Report
|
||||
|
||||
> 📅 Updated: 2026-01-08 00:11
|
||||
> 📅 Updated: 2026-02-10 12:51
|
||||
|
||||
## 📈 Overview
|
||||
|
||||
| Metric | Value |
|
||||
|------|------|
|
||||
| 📝 Total Posts | 11 |
|
||||
| ⬇️ Total Downloads | 889 |
|
||||
| 👁️ Total Views | 9358 |
|
||||
| 👍 Total Upvotes | 55 |
|
||||
| 💾 Total Saves | 48 |
|
||||
| 💬 Total Comments | 15 |
|
||||
| 📝 Total Posts | 21 |
|
||||
| ⬇️ Total Downloads | 4031 |
|
||||
| 👁️ Total Views | 47177 |
|
||||
| 👍 Total Upvotes | 224 |
|
||||
| 💾 Total Saves | 267 |
|
||||
| 💬 Total Comments | 51 |
|
||||
|
||||
## 📂 By Type
|
||||
|
||||
- **action**: 9
|
||||
- **action**: 15
|
||||
- **filter**: 2
|
||||
- **unknown**: 4
|
||||
|
||||
## 📋 Posts List
|
||||
|
||||
| Rank | Title | Type | Version | Downloads | Views | Upvotes | Saves | Updated |
|
||||
|:---:|------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 1 | [Turn Any Text into Beautiful Mind Maps](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | action | 0.9.1 | 283 | 2441 | 10 | 15 | 2026-01-06 |
|
||||
| 2 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | action | 0.3.6 | 175 | 486 | 3 | 3 | 2026-01-03 |
|
||||
| 3 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | filter | 1.1.0 | 118 | 1287 | 5 | 9 | 2025-12-31 |
|
||||
| 4 | [Flash Card ](https://openwebui.com/posts/flash_card_65a2ea8f) | action | 0.2.4 | 82 | 1528 | 8 | 5 | 2026-01-03 |
|
||||
| 5 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | action | 1.4.0 | 80 | 1081 | 7 | 8 | 2026-01-07 |
|
||||
| 6 | [Export to Word (Enhanced Formatting)](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | action | 0.4.2 | 67 | 605 | 5 | 4 | 2026-01-07 |
|
||||
| 7 | [智能信息图](https://openwebui.com/posts/智能信息图_e04a48ff) | action | 1.3.1 | 33 | 426 | 3 | 0 | 2025-12-29 |
|
||||
| 8 | [导出为 Word-支持公式、流程图、表格和代码块](https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0) | action | 0.4.1 | 20 | 799 | 7 | 1 | 2026-01-05 |
|
||||
| 9 | [智能生成交互式思维导图,帮助用户可视化知识](https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b) | action | 0.8.0 | 14 | 263 | 2 | 1 | 2025-12-31 |
|
||||
| 10 | [闪记卡生成插件](https://openwebui.com/posts/闪记卡生成插件_4a31eac3) | action | 0.2.2 | 12 | 320 | 3 | 1 | 2025-12-31 |
|
||||
| 11 | [异步上下文压缩](https://openwebui.com/posts/异步上下文压缩_5c0617cb) | filter | 1.1.0 | 5 | 122 | 2 | 1 | 2025-12-31 |
|
||||
| 1 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | action | 0.9.2 | 935 | 8224 | 22 | 50 | 2026-01-28 |
|
||||
| 2 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | action | 1.5.0 | 678 | 6439 | 24 | 34 | 2026-01-30 |
|
||||
| 3 | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | action | 0.4.4 | 374 | 2931 | 14 | 26 | 2026-02-07 |
|
||||
| 4 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | action | 1.2.2 | 360 | 3689 | 14 | 33 | 2026-01-28 |
|
||||
| 5 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | action | 0.3.7 | 340 | 1631 | 7 | 6 | 2026-02-09 |
|
||||
| 6 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | action | 1.2.4 | 323 | 4559 | 17 | 28 | 2026-01-29 |
|
||||
| 7 | [Flash Card](https://openwebui.com/posts/flash_card_65a2ea8f) | action | 0.2.4 | 220 | 3342 | 13 | 14 | 2026-01-28 |
|
||||
| 8 | [AI Task Instruction Generator](https://openwebui.com/posts/ai_task_instruction_generator_9bab8b37) | unknown | | 184 | 2608 | 9 | 5 | 2026-01-28 |
|
||||
| 9 | [Deep Dive](https://openwebui.com/posts/deep_dive_c0b846e4) | action | 1.0.0 | 146 | 1237 | 6 | 11 | 2026-01-08 |
|
||||
| 10 | [导出为Word增强版](https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0) | action | 0.4.4 | 125 | 2187 | 13 | 7 | 2026-02-07 |
|
||||
| 11 | [GitHub Copilot Official SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) | action | 0.6.1 | 95 | 2254 | 13 | 7 | 2026-02-09 |
|
||||
| 12 | [📂 Folder Memory – Auto-Evolving Project Context](https://openwebui.com/posts/folder_memory_auto_evolving_project_context_4a9875b2) | filter | 0.1.0 | 59 | 1278 | 6 | 8 | 2026-01-20 |
|
||||
| 13 | [智能信息图](https://openwebui.com/posts/智能信息图_e04a48ff) | action | 1.5.0 | 58 | 1048 | 10 | 1 | 2026-01-29 |
|
||||
| 14 | [思维导图](https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b) | action | 0.9.2 | 37 | 579 | 6 | 2 | 2026-01-28 |
|
||||
| 15 | [异步上下文压缩](https://openwebui.com/posts/异步上下文压缩_5c0617cb) | action | 1.2.2 | 33 | 656 | 7 | 5 | 2026-01-28 |
|
||||
| 16 | [闪记卡 (Flash Card)](https://openwebui.com/posts/闪记卡生成插件_4a31eac3) | action | 0.2.4 | 27 | 680 | 8 | 1 | 2026-01-28 |
|
||||
| 17 | [精读](https://openwebui.com/posts/精读_99830b0f) | action | 1.0.0 | 24 | 425 | 5 | 1 | 2026-01-08 |
|
||||
| 18 | [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee) | filter | 0.1.2 | 13 | 370 | 2 | 0 | 2026-02-09 |
|
||||
| 19 | [🚀 Open WebUI Prompt Plus: AI-Powered Prompt Manager](https://openwebui.com/posts/open_webui_prompt_plus_ai_powered_prompt_manager_s_15fa060e) | unknown | | 0 | 1481 | 12 | 18 | 2026-01-28 |
|
||||
| 20 | [Review of Claude Haiku 4.5](https://openwebui.com/posts/review_of_claude_haiku_45_41b0db39) | unknown | | 0 | 142 | 2 | 0 | 2026-01-14 |
|
||||
| 21 | [ 🛠️ Debug Open WebUI Plugins in Your Browser](https://openwebui.com/posts/debug_open_webui_plugins_in_your_browser_81bf7960) | unknown | | 0 | 1417 | 14 | 10 | 2026-01-10 |
|
||||
|
||||
@@ -1,35 +1,46 @@
|
||||
# 📊 OpenWebUI 社区统计报告
|
||||
|
||||
> 📅 更新时间: 2026-01-08 00:11
|
||||
> 📅 更新时间: 2026-02-10 12:51
|
||||
|
||||
## 📈 总览
|
||||
|
||||
| 指标 | 数值 |
|
||||
|------|------|
|
||||
| 📝 发布数量 | 11 |
|
||||
| ⬇️ 总下载量 | 889 |
|
||||
| 👁️ 总浏览量 | 9358 |
|
||||
| 👍 总点赞数 | 55 |
|
||||
| 💾 总收藏数 | 48 |
|
||||
| 💬 总评论数 | 15 |
|
||||
| 📝 发布数量 | 21 |
|
||||
| ⬇️ 总下载量 | 4031 |
|
||||
| 👁️ 总浏览量 | 47177 |
|
||||
| 👍 总点赞数 | 224 |
|
||||
| 💾 总收藏数 | 267 |
|
||||
| 💬 总评论数 | 51 |
|
||||
|
||||
## 📂 按类型分类
|
||||
|
||||
- **action**: 9
|
||||
- **action**: 15
|
||||
- **filter**: 2
|
||||
- **unknown**: 4
|
||||
|
||||
## 📋 发布列表
|
||||
|
||||
| 排名 | 标题 | 类型 | 版本 | 下载 | 浏览 | 点赞 | 收藏 | 更新日期 |
|
||||
|:---:|------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 1 | [Turn Any Text into Beautiful Mind Maps](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | action | 0.9.1 | 283 | 2441 | 10 | 15 | 2026-01-06 |
|
||||
| 2 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | action | 0.3.6 | 175 | 486 | 3 | 3 | 2026-01-03 |
|
||||
| 3 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | filter | 1.1.0 | 118 | 1287 | 5 | 9 | 2025-12-31 |
|
||||
| 4 | [Flash Card ](https://openwebui.com/posts/flash_card_65a2ea8f) | action | 0.2.4 | 82 | 1528 | 8 | 5 | 2026-01-03 |
|
||||
| 5 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | action | 1.4.0 | 80 | 1081 | 7 | 8 | 2026-01-07 |
|
||||
| 6 | [Export to Word (Enhanced Formatting)](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | action | 0.4.2 | 67 | 605 | 5 | 4 | 2026-01-07 |
|
||||
| 7 | [智能信息图](https://openwebui.com/posts/智能信息图_e04a48ff) | action | 1.3.1 | 33 | 426 | 3 | 0 | 2025-12-29 |
|
||||
| 8 | [导出为 Word-支持公式、流程图、表格和代码块](https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0) | action | 0.4.1 | 20 | 799 | 7 | 1 | 2026-01-05 |
|
||||
| 9 | [智能生成交互式思维导图,帮助用户可视化知识](https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b) | action | 0.8.0 | 14 | 263 | 2 | 1 | 2025-12-31 |
|
||||
| 10 | [闪记卡生成插件](https://openwebui.com/posts/闪记卡生成插件_4a31eac3) | action | 0.2.2 | 12 | 320 | 3 | 1 | 2025-12-31 |
|
||||
| 11 | [异步上下文压缩](https://openwebui.com/posts/异步上下文压缩_5c0617cb) | filter | 1.1.0 | 5 | 122 | 2 | 1 | 2025-12-31 |
|
||||
| 1 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | action | 0.9.2 | 935 | 8224 | 22 | 50 | 2026-01-28 |
|
||||
| 2 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | action | 1.5.0 | 678 | 6439 | 24 | 34 | 2026-01-30 |
|
||||
| 3 | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | action | 0.4.4 | 374 | 2931 | 14 | 26 | 2026-02-07 |
|
||||
| 4 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | action | 1.2.2 | 360 | 3689 | 14 | 33 | 2026-01-28 |
|
||||
| 5 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | action | 0.3.7 | 340 | 1631 | 7 | 6 | 2026-02-09 |
|
||||
| 6 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | action | 1.2.4 | 323 | 4559 | 17 | 28 | 2026-01-29 |
|
||||
| 7 | [Flash Card](https://openwebui.com/posts/flash_card_65a2ea8f) | action | 0.2.4 | 220 | 3342 | 13 | 14 | 2026-01-28 |
|
||||
| 8 | [AI Task Instruction Generator](https://openwebui.com/posts/ai_task_instruction_generator_9bab8b37) | unknown | | 184 | 2608 | 9 | 5 | 2026-01-28 |
|
||||
| 9 | [Deep Dive](https://openwebui.com/posts/deep_dive_c0b846e4) | action | 1.0.0 | 146 | 1237 | 6 | 11 | 2026-01-08 |
|
||||
| 10 | [导出为Word增强版](https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0) | action | 0.4.4 | 125 | 2187 | 13 | 7 | 2026-02-07 |
|
||||
| 11 | [GitHub Copilot Official SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) | action | 0.6.1 | 95 | 2254 | 13 | 7 | 2026-02-09 |
|
||||
| 12 | [📂 Folder Memory – Auto-Evolving Project Context](https://openwebui.com/posts/folder_memory_auto_evolving_project_context_4a9875b2) | filter | 0.1.0 | 59 | 1278 | 6 | 8 | 2026-01-20 |
|
||||
| 13 | [智能信息图](https://openwebui.com/posts/智能信息图_e04a48ff) | action | 1.5.0 | 58 | 1048 | 10 | 1 | 2026-01-29 |
|
||||
| 14 | [思维导图](https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b) | action | 0.9.2 | 37 | 579 | 6 | 2 | 2026-01-28 |
|
||||
| 15 | [异步上下文压缩](https://openwebui.com/posts/异步上下文压缩_5c0617cb) | action | 1.2.2 | 33 | 656 | 7 | 5 | 2026-01-28 |
|
||||
| 16 | [闪记卡 (Flash Card)](https://openwebui.com/posts/闪记卡生成插件_4a31eac3) | action | 0.2.4 | 27 | 680 | 8 | 1 | 2026-01-28 |
|
||||
| 17 | [精读](https://openwebui.com/posts/精读_99830b0f) | action | 1.0.0 | 24 | 425 | 5 | 1 | 2026-01-08 |
|
||||
| 18 | [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee) | filter | 0.1.2 | 13 | 370 | 2 | 0 | 2026-02-09 |
|
||||
| 19 | [🚀 Open WebUI Prompt Plus: AI-Powered Prompt Manager](https://openwebui.com/posts/open_webui_prompt_plus_ai_powered_prompt_manager_s_15fa060e) | unknown | | 0 | 1481 | 12 | 18 | 2026-01-28 |
|
||||
| 20 | [Review of Claude Haiku 4.5](https://openwebui.com/posts/review_of_claude_haiku_45_41b0db39) | unknown | | 0 | 142 | 2 | 0 | 2026-01-14 |
|
||||
| 21 | [ 🛠️ Debug Open WebUI Plugins in Your Browser](https://openwebui.com/posts/debug_open_webui_plugins_in_your_browser_81bf7960) | unknown | | 0 | 1417 | 14 | 10 | 2026-01-10 |
|
||||
|
||||
150
docs/development/frontend-console-debugging.md
Normal file
150
docs/development/frontend-console-debugging.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# 🛠️ Debugging Python Plugins with Frontend Console
|
||||
|
||||
When developing plugins for Open WebUI, debugging can be challenging. Standard `print()` statements or server-side logging might not always be accessible, especially in hosted environments or when you want to see the data flow in real-time alongside the UI interactions.
|
||||
|
||||
This guide introduces a powerful technique: **Frontend Console Debugging**. By injecting JavaScript from your Python plugin, you can print structured logs directly to the browser's Developer Tools console (F12).
|
||||
|
||||
## Why Frontend Debugging?
|
||||
|
||||
* **Real-time Feedback**: See logs immediately as actions happen in the browser.
|
||||
* **Rich Objects**: Inspect complex JSON objects (like `body` or `messages`) interactively, rather than reading massive text dumps.
|
||||
* **No Server Access Needed**: Debug issues even if you don't have SSH/Console access to the backend server.
|
||||
* **Clean Output**: Group logs using `console.group()` to keep your console organized.
|
||||
|
||||
## The Core Mechanism
|
||||
|
||||
Open WebUI plugins (both Actions and Filters) support an event system. We can leverage the `__event_call__` (or sometimes `__event_emitter__`) to send a special event of type `execute`. This tells the frontend to run the provided JavaScript code.
|
||||
|
||||
### The Helper Method
|
||||
|
||||
To make this easy to use, we recommend adding a helper method `_emit_debug_log` to your plugin class.
|
||||
|
||||
```python
|
||||
import json
|
||||
from typing import List
|
||||
|
||||
async def _emit_debug_log(
|
||||
self,
|
||||
__event_call__,
|
||||
title: str,
|
||||
data: dict
|
||||
):
|
||||
"""
|
||||
Emit debug log to browser console via JS execution.
|
||||
|
||||
Args:
|
||||
__event_call__: The event callable passed to action/outlet.
|
||||
title: A title for the log group.
|
||||
data: A dictionary of data to log.
|
||||
"""
|
||||
# 1. Check if debugging is enabled (recommended)
|
||||
if not getattr(self.valves, "show_debug_log", True) or not __event_call__:
|
||||
return
|
||||
|
||||
try:
|
||||
# 2. Construct the JavaScript code
|
||||
# We use an async IIFE (Immediately Invoked Function Expression)
|
||||
# to ensure a clean scope and support await if needed.
|
||||
js_code = f"""
|
||||
(async function() {{
|
||||
console.group("🛠️ Plugin Debug: {title}");
|
||||
console.log({json.dumps(data, ensure_ascii=False)});
|
||||
console.groupEnd();
|
||||
}})();
|
||||
"""
|
||||
|
||||
# 3. Send the execute event
|
||||
await __event_call__(
|
||||
{
|
||||
"type": "execute",
|
||||
"data": {"code": js_code},
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Error emitting debug log: {e}")
|
||||
```
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### 1. Add a Valve for Control
|
||||
|
||||
It's best practice to make debugging optional so it doesn't clutter the console for normal users.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class Filter:
|
||||
class Valves(BaseModel):
|
||||
show_debug_log: bool = Field(
|
||||
default=False,
|
||||
description="Print debug logs to browser console (F12)"
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.valves = self.Valves()
|
||||
```
|
||||
|
||||
### 2. Inject `__event_call__`
|
||||
|
||||
Ensure your `action` (for Actions) or `outlet` (for Filters) method accepts `__event_call__`.
|
||||
|
||||
**For Filters (`outlet`):**
|
||||
|
||||
```python
|
||||
async def outlet(
|
||||
self,
|
||||
body: dict,
|
||||
__user__: Optional[dict] = None,
|
||||
__event_call__=None, # <--- Add this
|
||||
__metadata__: Optional[dict] = None,
|
||||
) -> dict:
|
||||
```
|
||||
|
||||
**For Actions (`action`):**
|
||||
|
||||
```python
|
||||
async def action(
|
||||
self,
|
||||
body: dict,
|
||||
__user__=None,
|
||||
__event_call__=None, # <--- Add this
|
||||
__request__=None,
|
||||
):
|
||||
```
|
||||
|
||||
### 3. Call the Helper
|
||||
|
||||
Now you can log anything, anywhere in your logic!
|
||||
|
||||
```python
|
||||
# Inside your logic...
|
||||
new_content = self.process_content(content)
|
||||
|
||||
# Log the before and after
|
||||
await self._emit_debug_log(
|
||||
__event_call__,
|
||||
"Content Normalization",
|
||||
{
|
||||
"original": content,
|
||||
"processed": new_content,
|
||||
"changes": diff_list
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `json.dumps`**: Always serialize your Python dictionaries to JSON strings before embedding them in the f-string. This handles escaping quotes and special characters correctly.
|
||||
2. **Async IIFE**: Wrapping your JS in `(async function() { ... })();` is safer than raw code. It prevents variable collisions with other scripts and allows using `await` inside your debug script if you ever need to check DOM elements.
|
||||
3. **Check for None**: Always check if `__event_call__` is not None before using it, as it might not be available in all contexts (e.g., when running tests or in older Open WebUI versions).
|
||||
|
||||
## Example Output
|
||||
|
||||
When enabled, your browser console will show:
|
||||
|
||||
```text
|
||||
> 🛠️ Plugin Debug: Content Normalization
|
||||
> {original: "...", processed: "...", changes: [...]}
|
||||
```
|
||||
|
||||
You can expand the object to inspect every detail of your data. Happy debugging!
|
||||
64
docs/development/mermaid-syntax-standards.md
Normal file
64
docs/development/mermaid-syntax-standards.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Mermaid Syntax Standards & Best Practices
|
||||
|
||||
This document summarizes the official syntax standards for Mermaid flowcharts, focusing on node labels, quoting rules, and special character handling. It serves as a reference for the `markdown_normalizer` plugin logic.
|
||||
|
||||
## 1. Node Shapes & Syntax
|
||||
|
||||
Mermaid supports various node shapes defined by specific wrapping characters.
|
||||
|
||||
| Shape | Syntax | Example |
|
||||
| :--- | :--- | :--- |
|
||||
| **Rectangle** (Default) | `id[Label]` | `A[Start]` |
|
||||
| **Rounded** | `id(Label)` | `B(Process)` |
|
||||
| **Stadium** (Pill) | `id([Label])` | `C([End])` |
|
||||
| **Subroutine** | `id[[Label]]` | `D[[Subroutine]]` |
|
||||
| **Cylinder** (Database) | `id[(Label)]` | `E[(Database)]` |
|
||||
| **Circle** | `id((Label))` | `F((Point))` |
|
||||
| **Double Circle** | `id(((Label)))` | `G(((Endpoint)))` |
|
||||
| **Asymmetric** | `id>Label]` | `H>Flag]` |
|
||||
| **Rhombus** (Decision) | `id{Label}` | `I{Decision}` |
|
||||
| **Hexagon** | `id{{Label}}` | `J{{Prepare}}` |
|
||||
| **Parallelogram** | `id[/Label/]` | `K[/Input/]` |
|
||||
| **Parallelogram Alt** | `id[\Label\]` | `L[\Output\]` |
|
||||
| **Trapezoid** | `id[/Label\]` | `M[/Trap/]` |
|
||||
| **Trapezoid Alt** | `id[\Label/]` | `N[\TrapAlt/]` |
|
||||
|
||||
## 2. Quoting Rules (Critical)
|
||||
|
||||
### Why Quote?
|
||||
Quoting node labels is **highly recommended** and sometimes **mandatory** to prevent syntax errors.
|
||||
|
||||
### Mandatory Quoting Scenarios
|
||||
You **MUST** enclose labels in double quotes `"` if they contain:
|
||||
1. **Special Characters**: `()`, `[]`, `{}`, `;`, `"`, etc.
|
||||
2. **Keywords**: Words like `end`, `subgraph`, etc., if used in specific contexts.
|
||||
3. **Unicode/Emoji**: While often supported without quotes, quoting ensures consistent rendering across different environments.
|
||||
4. **Markdown**: If you want to use Markdown formatting (bold, italic) inside a label.
|
||||
|
||||
### Best Practice: Always Quote
|
||||
To ensure robustness, especially when processing LLM-generated content which may contain unpredictable characters, **always enclosing labels in double quotes is the safest strategy**.
|
||||
|
||||
**Examples:**
|
||||
* ❌ Risky: `id(Start: 15:00)` (Colon might be interpreted as style separator)
|
||||
* ✅ Safe: `id("Start: 15:00")`
|
||||
* ❌ Broken: `id(Func(x))` (Nested parentheses break parsing)
|
||||
* ✅ Safe: `id("Func(x)")`
|
||||
|
||||
## 3. Escape Characters
|
||||
|
||||
Inside a quoted string:
|
||||
* Double quotes `"` must be escaped as `\"`.
|
||||
* HTML entities (e.g., `#35;` for `#`) can be used.
|
||||
|
||||
## 4. Plugin Logic Verification
|
||||
|
||||
The `markdown_normalizer` plugin implements the following logic:
|
||||
|
||||
1. **Detection**: Identifies Mermaid node definitions using a comprehensive regex covering all shapes above.
|
||||
2. **Normalization**:
|
||||
* Checks if the label is already quoted.
|
||||
* If **NOT quoted**, it wraps the label in double quotes `""`.
|
||||
* Escapes any existing double quotes inside the label (`"` -> `\"`).
|
||||
3. **Shape Preservation**: The regex captures the specific opening and closing delimiters (e.g., `((` and `))`) to ensure the node shape is strictly preserved during normalization.
|
||||
|
||||
**Conclusion**: The plugin's behavior of automatically adding quotes to unquoted labels is **fully aligned with Mermaid's official best practices** for robustness and error prevention.
|
||||
@@ -7,10 +7,10 @@
|
||||
## 📚 Table of Contents
|
||||
|
||||
1. [Quick Start](#1-quick-start)
|
||||
2. [Core Concepts & SDK Details](#2-core-concepts--sdk-details)
|
||||
2. [Core Concepts & SDK Details](#2-core-concepts-sdk-details)
|
||||
3. [Deep Dive into Plugin Types](#3-deep-dive-into-plugin-types)
|
||||
4. [Advanced Development Patterns](#4-advanced-development-patterns)
|
||||
5. [Best Practices & Design Principles](#5-best-practices--design-principles)
|
||||
5. [Best Practices & Design Principles](#5-best-practices-design-principles)
|
||||
6. [Troubleshooting](#6-troubleshooting)
|
||||
|
||||
---
|
||||
@@ -351,8 +351,7 @@ async def action(self, body, __event_call__, __metadata__, ...):
|
||||
|
||||
#### Reference Implementations
|
||||
|
||||
- `plugins/actions/js-render-poc/infographic_markdown.py` - AntV Infographic + Data URL
|
||||
- `plugins/actions/js-render-poc/js_render_poc.py` - Basic proof of concept
|
||||
- `plugins/actions/infographic/infographic.py` - Production-ready implementation using AntV + Data URL
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -4,19 +4,19 @@
|
||||
|
||||
## 📚 目录
|
||||
|
||||
1. [插件开发快速入门](#1-插件开发快速入门)
|
||||
2. [核心概念与 SDK 详解](#2-核心概念与-sdk-详解)
|
||||
3. [插件类型深度解析](#3-插件类型深度解析)
|
||||
* [Action (动作)](#31-action-动作)
|
||||
* [Filter (过滤器)](#32-filter-过滤器)
|
||||
* [Pipe (管道)](#33-pipe-管道)
|
||||
4. [高级开发模式](#4-高级开发模式)
|
||||
5. [最佳实践与设计原则](#5-最佳实践与设计原则)
|
||||
6. [故障排查](#6-故障排查)
|
||||
1. [插件开发快速入门](#1-quick-start)
|
||||
2. [核心概念与 SDK 详解](#2-core-concepts-sdk-details)
|
||||
3. [插件类型深度解析](#3-plugin-types)
|
||||
* [Action (动作)](#31-action)
|
||||
* [Filter (过滤器)](#32-filter)
|
||||
* [Pipe (管道)](#33-pipe)
|
||||
4. [高级开发模式](#4-advanced-patterns)
|
||||
5. [最佳实践与设计原则](#5-best-practices)
|
||||
6. [故障排查](#6-troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## 1. 插件开发快速入门
|
||||
## 1. 插件开发快速入门 {: #1-quick-start }
|
||||
|
||||
### 1.1 什么是 OpenWebUI 插件?
|
||||
|
||||
@@ -64,7 +64,7 @@ class Action:
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心概念与 SDK 详解
|
||||
## 2. 核心概念与 SDK 详解 {: #2-core-concepts-sdk-details }
|
||||
|
||||
### 2.1 ⚠️ 重要:同步与异步
|
||||
|
||||
@@ -107,9 +107,9 @@ class Filter:
|
||||
|
||||
---
|
||||
|
||||
## 3. 插件类型深度解析
|
||||
## 3. 插件类型深度解析 {: #3-plugin-types }
|
||||
|
||||
### 3.1 Action (动作)
|
||||
### 3.1 Action (动作) {: #31-action }
|
||||
|
||||
**定位**:在消息下方添加按钮,用户点击触发。
|
||||
|
||||
@@ -134,7 +134,7 @@ async def action(self, body, __event_call__):
|
||||
await __event_call__({"type": "execute", "data": {"code": js}})
|
||||
```
|
||||
|
||||
### 3.2 Filter (过滤器)
|
||||
### 3.2 Filter (过滤器) {: #32-filter }
|
||||
|
||||
**定位**:中间件,拦截并修改请求/响应。
|
||||
|
||||
@@ -155,7 +155,7 @@ async def inlet(self, body, __metadata__):
|
||||
return body
|
||||
```
|
||||
|
||||
### 3.3 Pipe (管道)
|
||||
### 3.3 Pipe (管道) {: #33-pipe }
|
||||
|
||||
**定位**:自定义模型/代理。
|
||||
|
||||
@@ -177,7 +177,7 @@ class Pipe:
|
||||
|
||||
---
|
||||
|
||||
## 4. 高级开发模式
|
||||
## 4. 高级开发模式 {: #4-advanced-patterns }
|
||||
|
||||
### 4.1 Pipe 与 Filter 协同
|
||||
利用 `__request__.app.state` 在不同插件间共享数据。
|
||||
@@ -315,10 +315,9 @@ async def action(self, body, __event_call__, __metadata__, ...):
|
||||
|
||||
#### 参考实现
|
||||
|
||||
- `plugins/actions/js-render-poc/infographic_markdown.py` - AntV 信息图 + Data URL
|
||||
- `plugins/actions/js-render-poc/js_render_poc.py` - 基础概念验证
|
||||
- `plugins/actions/infographic/infographic.py` - 基于 AntV + Data URL 的生产级实现
|
||||
|
||||
## 5. 最佳实践与设计原则
|
||||
## 5. 最佳实践与设计原则 {: #5-best-practices }
|
||||
|
||||
### 5.1 命名与定位
|
||||
* **简短有力**:如 "闪记卡", "精读"。避免 "文本分析助手" 这种泛词。
|
||||
@@ -344,7 +343,7 @@ except Exception as e:
|
||||
|
||||
---
|
||||
|
||||
## 6. 故障排查
|
||||
## 6. 故障排查 {: #6-troubleshooting }
|
||||
|
||||
* **HTML 不显示?** 确保包裹在 ` ```html ... ``` ` 代码块中。
|
||||
* **数据库报错?** 检查是否在 `async` 函数中直接调用了同步的 DB 方法,请使用 `asyncio.to_thread`。
|
||||
|
||||
22
docs/extensions/index.md
Normal file
22
docs/extensions/index.md
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: Extensions
|
||||
---
|
||||
|
||||
# Extensions
|
||||
|
||||
Standalone frontend extensions to supercharge your Open WebUI.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Open WebUI Prompt Plus
|
||||
|
||||
**An all-in-one prompt management suite for power users.**
|
||||
|
||||
[Open WebUI Prompt Plus](https://github.com/Fu-Jie/open-webui-prompt-plus) elevates your experience with:
|
||||
|
||||
- **🤖 AI-Powered Prompt Generator**: Turn natural language into structured prompts.
|
||||
- **⚡ Quick Insert Panel**: Spotlight-style search (`Cmd/Ctrl + Shift + P`).
|
||||
- **📂 Advanced Management**: Dynamic categories, favorites, and usage stats.
|
||||
- **📝 Native Variable Support**: Visual form rendering for template variables.
|
||||
|
||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/open-webui-prompt-plus){ .md-button .md-button--primary }
|
||||
22
docs/extensions/index.zh.md
Normal file
22
docs/extensions/index.zh.md
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: 扩展 (Extensions)
|
||||
---
|
||||
|
||||
# 扩展 (Extensions)
|
||||
|
||||
Open WebUI 的独立前端增强扩展。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Open WebUI Prompt Plus
|
||||
|
||||
**专为高级用户打造的一站式提示词管理套件。**
|
||||
|
||||
[Open WebUI Prompt Plus](https://github.com/Fu-Jie/open-webui-prompt-plus) 通过以下功能提升您的体验:
|
||||
|
||||
- **🤖 AI 提示词生成器**: 将自然语言转化为结构化提示词。
|
||||
- **⚡ 快速插入面板**: Spotlight 风格的快速搜索 (`Cmd/Ctrl + Shift + P`)。
|
||||
- **📂 高级管理**: 动态分类、收藏夹及使用统计。
|
||||
- **📝 原生变量支持**: 模板变量的可视化表单渲染。
|
||||
|
||||
[:fontawesome-brands-github: 查看 GitHub](https://github.com/Fu-Jie/open-webui-prompt-plus){ .md-button .md-button--primary }
|
||||
@@ -349,6 +349,53 @@ await __event_emitter__(
|
||||
)
|
||||
```
|
||||
|
||||
#### Advanced Use Case: Retrieving Frontend Data
|
||||
|
||||
One of the most powerful capabilities of the `execute` event type is the ability to fetch data from the browser environment (JavaScript) and return it to your Python backend. This allows plugins to access information like:
|
||||
|
||||
- `localStorage` items (user preferences, tokens)
|
||||
- `navigator` properties (language, geolocation, platform)
|
||||
- `document` properties (cookies, URL parameters)
|
||||
|
||||
**How it works:**
|
||||
The JavaScript code you provide in the `"code"` field is executed in the browser. If your JS code includes a `return` statement, that value is sent back to Python as the result of `await __event_call__`.
|
||||
|
||||
**Example: Getting the User's UI Language**
|
||||
|
||||
```python
|
||||
try:
|
||||
# Execute JS on the frontend to get language settings
|
||||
response = await __event_call__(
|
||||
{
|
||||
"type": "execute",
|
||||
"data": {
|
||||
# This JS code runs in the browser.
|
||||
# The 'return' value is sent back to Python.
|
||||
"code": """
|
||||
return (
|
||||
localStorage.getItem('locale') ||
|
||||
localStorage.getItem('language') ||
|
||||
navigator.language ||
|
||||
'en-US'
|
||||
);
|
||||
""",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
# 'response' will contain the string returned by JS (e.g., "en-US", "zh-CN")
|
||||
# Note: Wrap in try-except to handle potential timeouts or JS errors
|
||||
logger.info(f"Frontend Language: {response}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get frontend data: {e}")
|
||||
```
|
||||
|
||||
**Key capabilities unlocked:**
|
||||
- **Context Awareness:** Adapt responses based on user time zone or language.
|
||||
- **Client-Side Storage:** Use `localStorage` to persist simple plugin settings without a database.
|
||||
- **Hardware Access:** Request geolocation or clipboard access (requires user permission).
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ When & Where to Use Events
|
||||
@@ -421,4 +468,4 @@ Refer to this document for common event types and structures, and explore Open W
|
||||
|
||||
---
|
||||
|
||||
**Happy event-driven coding in Open WebUI! 🚀**
|
||||
**Happy event-driven coding in Open WebUI! 🚀**
|
||||
|
||||
@@ -25,7 +25,7 @@ hide:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-puzzle:{ .lg .middle } **Plugin Center**
|
||||
- :material-puzzle:{ .lg .middle } **Plugin Center**
|
||||
|
||||
---
|
||||
|
||||
@@ -33,7 +33,7 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: Explore Plugins](plugins/index.md)
|
||||
|
||||
- :material-message-text:{ .lg .middle } **Prompt Library**
|
||||
- :material-message-text:{ .lg .middle } **Prompt Library**
|
||||
|
||||
---
|
||||
|
||||
@@ -41,7 +41,7 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: Browse Prompts](prompts/index.md)
|
||||
|
||||
- :material-tools:{ .lg .middle } **Enhancements**
|
||||
- :material-tools:{ .lg .middle } **Enhancements**
|
||||
|
||||
---
|
||||
|
||||
@@ -49,7 +49,15 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: View Guides](enhancements/index.md)
|
||||
|
||||
- :material-book-open-page-variant:{ .lg .middle } **Development**
|
||||
- :material-rocket-launch:{ .lg .middle } **Extensions**
|
||||
|
||||
---
|
||||
|
||||
Standalone frontend extensions to supercharge your Open WebUI interface.
|
||||
|
||||
[:octicons-arrow-right-24: Browse Extensions](extensions/index.md)
|
||||
|
||||
- :material-book-open-page-variant:{ .lg .middle } **Development**
|
||||
|
||||
---
|
||||
|
||||
@@ -65,7 +73,7 @@ hide:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-brain:{ .lg .middle } **Smart Mind Map**
|
||||
- :material-brain:{ .lg .middle } **Smart Mind Map**
|
||||
|
||||
---
|
||||
|
||||
@@ -73,15 +81,15 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: Learn More](plugins/actions/smart-mind-map.md)
|
||||
|
||||
- :material-card-text:{ .lg .middle } **Knowledge Card**
|
||||
- :material-card-text:{ .lg .middle } **Flash Card**
|
||||
|
||||
---
|
||||
|
||||
Quickly generates beautiful learning memory cards, perfect for studying and quick memorization.
|
||||
Quickly generates beautiful flashcards from text, extracting key points and categories.
|
||||
|
||||
[:octicons-arrow-right-24: Learn More](plugins/actions/knowledge-card.md)
|
||||
[:octicons-arrow-right-24: Learn More](plugins/actions/flash-card.md)
|
||||
|
||||
- :material-arrow-collapse-vertical:{ .lg .middle } **Async Context Compression**
|
||||
- :material-arrow-collapse-vertical:{ .lg .middle } **Async Context Compression**
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ hide:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-puzzle:{ .lg .middle } **插件中心**
|
||||
- :material-puzzle:{ .lg .middle } **插件中心**
|
||||
|
||||
---
|
||||
|
||||
@@ -33,7 +33,7 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: 探索插件](plugins/index.md)
|
||||
|
||||
- :material-message-text:{ .lg .middle } **提示词库**
|
||||
- :material-message-text:{ .lg .middle } **提示词库**
|
||||
|
||||
---
|
||||
|
||||
@@ -41,7 +41,7 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: 浏览提示词](prompts/index.md)
|
||||
|
||||
- :material-tools:{ .lg .middle } **增强功能**
|
||||
- :material-tools:{ .lg .middle } **增强功能**
|
||||
|
||||
---
|
||||
|
||||
@@ -49,7 +49,15 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: 查看指南](enhancements/index.md)
|
||||
|
||||
- :material-book-open-page-variant:{ .lg .middle } **开发指南**
|
||||
- :material-rocket-launch:{ .lg .middle } **扩展 (Extensions)**
|
||||
|
||||
---
|
||||
|
||||
独立的 OpenWebUI 前端增强扩展,全面提升交互体验。
|
||||
|
||||
[:octicons-arrow-right-24: 浏览扩展](extensions/index.md)
|
||||
|
||||
- :material-book-open-page-variant:{ .lg .middle } **开发指南**
|
||||
|
||||
---
|
||||
|
||||
@@ -65,7 +73,7 @@ hide:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-brain:{ .lg .middle } **智能思维导图**
|
||||
- :material-brain:{ .lg .middle } **智能思维导图**
|
||||
|
||||
---
|
||||
|
||||
@@ -73,15 +81,15 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: 了解更多](plugins/actions/smart-mind-map.md)
|
||||
|
||||
- :material-card-text:{ .lg .middle } **知识卡片**
|
||||
- :material-card-text:{ .lg .middle } **Flash Card(闪记卡)**
|
||||
|
||||
---
|
||||
|
||||
快速生成精美的学习记忆卡片,非常适合学习和快速记忆。
|
||||
|
||||
[:octicons-arrow-right-24: 了解更多](plugins/actions/knowledge-card.md)
|
||||
[:octicons-arrow-right-24: 了解更多](plugins/actions/flash-card.md)
|
||||
|
||||
- :material-arrow-collapse-vertical:{ .lg .middle } **异步上下文压缩**
|
||||
- :material-arrow-collapse-vertical:{ .lg .middle } **异步上下文压缩**
|
||||
|
||||
---
|
||||
|
||||
|
||||
111
docs/plugins/actions/deep-dive.md
Normal file
111
docs/plugins/actions/deep-dive.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Deep Dive
|
||||
|
||||
<span class="category-badge action">Action</span>
|
||||
<span class="version-badge">v1.0.0</span>
|
||||
|
||||
A comprehensive thinking lens that dives deep into any content - from context to logic, insights, and action paths.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Deep Dive plugin transforms how you understand complex content by guiding you through a structured thinking process. Rather than just summarizing, it deconstructs content across four phases:
|
||||
|
||||
- **🔍 The Context (What?)**: Panoramic view of the situation and background
|
||||
- **🧠 The Logic (Why?)**: Deconstruction of reasoning and mental models
|
||||
- **💎 The Insight (So What?)**: Non-obvious value and hidden implications
|
||||
- **🚀 The Path (Now What?)**: Specific, prioritized strategic actions
|
||||
|
||||
## Features
|
||||
|
||||
- :material-brain: **Thinking Chain**: Complete structured analysis process
|
||||
- :material-eye: **Deep Understanding**: Reveals hidden assumptions and blind spots
|
||||
- :material-lightbulb-on: **Insight Extraction**: Finds the "Aha!" moments
|
||||
- :material-rocket-launch: **Action Oriented**: Translates understanding into actionable steps
|
||||
- :material-theme-light-dark: **Theme Adaptive**: Auto-adapts to OpenWebUI light/dark theme
|
||||
- :material-translate: **Multi-language**: Outputs in user's preferred language
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the plugin file: [`deep_dive.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/deep-dive)
|
||||
2. Upload to OpenWebUI: **Admin Panel** → **Settings** → **Functions**
|
||||
3. Enable the plugin
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
1. Provide any long text, article, or meeting notes in the chat
|
||||
2. Click the **Deep Dive** button in the message action bar
|
||||
3. Follow the visual timeline from Context → Logic → Insight → Path
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `SHOW_STATUS` | boolean | `true` | Show status updates during processing |
|
||||
| `MODEL_ID` | string | `""` | LLM model for analysis (empty = current model) |
|
||||
| `MIN_TEXT_LENGTH` | integer | `200` | Minimum text length for analysis |
|
||||
| `CLEAR_PREVIOUS_HTML` | boolean | `true` | Clear previous plugin results |
|
||||
| `MESSAGE_COUNT` | integer | `1` | Number of recent messages to analyze |
|
||||
|
||||
---
|
||||
|
||||
## Theme Support
|
||||
|
||||
Deep Dive automatically adapts to OpenWebUI's light/dark theme:
|
||||
|
||||
- Detects theme from parent document `<meta name="theme-color">` tag
|
||||
- Falls back to `html/body` class or `data-theme` attribute
|
||||
- Uses system preference `prefers-color-scheme: dark` as last resort
|
||||
|
||||
!!! tip "For Best Results"
|
||||
Enable **iframe Sandbox Allow Same Origin** in OpenWebUI:
|
||||
**Settings** → **Interface** → **Artifacts** → Check **iframe Sandbox Allow Same Origin**
|
||||
|
||||
---
|
||||
|
||||
## Example Output
|
||||
|
||||
The plugin generates a beautiful structured timeline:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ 🌊 Deep Dive Analysis │
|
||||
│ 👤 User 📅 Date 📊 Word count │
|
||||
├─────────────────────────────────────┤
|
||||
│ 🔍 Phase 01: The Context │
|
||||
│ [High-level panoramic view] │
|
||||
│ │
|
||||
│ 🧠 Phase 02: The Logic │
|
||||
│ • Reasoning structure... │
|
||||
│ • Hidden assumptions... │
|
||||
│ │
|
||||
│ 💎 Phase 03: The Insight │
|
||||
│ • Non-obvious value... │
|
||||
│ • Blind spots revealed... │
|
||||
│ │
|
||||
│ 🚀 Phase 04: The Path │
|
||||
│ ▸ Priority Action 1... │
|
||||
│ ▸ Priority Action 2... │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
!!! note "Prerequisites"
|
||||
- OpenWebUI v0.3.0 or later
|
||||
- Uses the active LLM model for analysis
|
||||
- Requires `markdown` Python package
|
||||
|
||||
---
|
||||
|
||||
## Source Code
|
||||
|
||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/deep-dive){ .md-button }
|
||||
111
docs/plugins/actions/deep-dive.zh.md
Normal file
111
docs/plugins/actions/deep-dive.zh.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# 精读 (Deep Dive)
|
||||
|
||||
<span class="category-badge action">Action</span>
|
||||
<span class="version-badge">v1.0.0</span>
|
||||
|
||||
全方位的思维透镜 —— 从背景全景到逻辑脉络,从深度洞察到行动路径。
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
精读插件改变了您理解复杂内容的方式,通过结构化的思维过程引导您进行深度分析。它不仅仅是摘要,而是从四个阶段解构内容:
|
||||
|
||||
- **🔍 全景 (The Context)**: 情境与背景的高层级全景视图
|
||||
- **🧠 脉络 (The Logic)**: 解构底层推理逻辑与思维模型
|
||||
- **💎 洞察 (The Insight)**: 提取非显性价值与隐藏含义
|
||||
- **🚀 路径 (The Path)**: 具体的、按优先级排列的战略行动
|
||||
|
||||
## 功能特性
|
||||
|
||||
- :material-brain: **思维链**: 完整的结构化分析过程
|
||||
- :material-eye: **深度理解**: 揭示隐藏的假设和思维盲点
|
||||
- :material-lightbulb-on: **洞察提取**: 发现"原来如此"的时刻
|
||||
- :material-rocket-launch: **行动导向**: 将深度理解转化为可执行步骤
|
||||
- :material-theme-light-dark: **主题自适应**: 自动适配 OpenWebUI 深色/浅色主题
|
||||
- :material-translate: **多语言**: 以用户偏好语言输出
|
||||
|
||||
---
|
||||
|
||||
## 安装
|
||||
|
||||
1. 下载插件文件: [`deep_dive_cn.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/deep-dive)
|
||||
2. 上传到 OpenWebUI: **管理面板** → **设置** → **Functions**
|
||||
3. 启用插件
|
||||
|
||||
---
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 在聊天中提供任何长文本、文章或会议记录
|
||||
2. 点击消息操作栏中的 **精读** 按钮
|
||||
3. 沿着视觉时间轴从"全景"探索到"路径"
|
||||
|
||||
---
|
||||
|
||||
## 配置参数
|
||||
|
||||
| 选项 | 类型 | 默认值 | 描述 |
|
||||
|------|------|--------|------|
|
||||
| `SHOW_STATUS` | boolean | `true` | 处理过程中是否显示状态更新 |
|
||||
| `MODEL_ID` | string | `""` | 用于分析的 LLM 模型(空 = 当前模型) |
|
||||
| `MIN_TEXT_LENGTH` | integer | `200` | 分析所需的最小文本长度 |
|
||||
| `CLEAR_PREVIOUS_HTML` | boolean | `true` | 是否清除之前的插件结果 |
|
||||
| `MESSAGE_COUNT` | integer | `1` | 要分析的最近消息数量 |
|
||||
|
||||
---
|
||||
|
||||
## 主题支持
|
||||
|
||||
精读插件自动适配 OpenWebUI 的深色/浅色主题:
|
||||
|
||||
- 从父文档 `<meta name="theme-color">` 标签检测主题
|
||||
- 回退到 `html/body` 的 class 或 `data-theme` 属性
|
||||
- 最后使用系统偏好 `prefers-color-scheme: dark`
|
||||
|
||||
!!! tip "最佳效果"
|
||||
请在 OpenWebUI 中启用 **iframe Sandbox Allow Same Origin**:
|
||||
**设置** → **界面** → **Artifacts** → 勾选 **iframe Sandbox Allow Same Origin**
|
||||
|
||||
---
|
||||
|
||||
## 输出示例
|
||||
|
||||
插件生成精美的结构化时间轴:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ 📖 精读分析报告 │
|
||||
│ 👤 用户 📅 日期 📊 字数 │
|
||||
├─────────────────────────────────────┤
|
||||
│ 🔍 阶段 01: 全景 (The Context) │
|
||||
│ [高层级全景视图内容] │
|
||||
│ │
|
||||
│ 🧠 阶段 02: 脉络 (The Logic) │
|
||||
│ • 推理结构分析... │
|
||||
│ • 隐藏假设识别... │
|
||||
│ │
|
||||
│ 💎 阶段 03: 洞察 (The Insight) │
|
||||
│ • 非显性价值提取... │
|
||||
│ • 思维盲点揭示... │
|
||||
│ │
|
||||
│ 🚀 阶段 04: 路径 (The Path) │
|
||||
│ ▸ 优先级行动 1... │
|
||||
│ ▸ 优先级行动 2... │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 系统要求
|
||||
|
||||
!!! note "前提条件"
|
||||
- OpenWebUI v0.3.0 或更高版本
|
||||
- 使用当前活跃的 LLM 模型进行分析
|
||||
- 需要 `markdown` Python 包
|
||||
|
||||
---
|
||||
|
||||
## 源代码
|
||||
|
||||
[:fontawesome-brands-github: 在 GitHub 上查看](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/deep-dive){ .md-button }
|
||||
@@ -1,7 +1,7 @@
|
||||
# Export to Word
|
||||
|
||||
<span class="category-badge action">Action</span>
|
||||
<span class="version-badge">v0.4.3</span>
|
||||
<span class="version-badge">v0.4.4</span>
|
||||
|
||||
Export conversation to Word (.docx) with **syntax highlighting**, **native math equations**, **Mermaid diagrams**, **citations**, and **enhanced table formatting**.
|
||||
|
||||
@@ -53,11 +53,17 @@ You can configure the following settings via the **Valves** button in the plugin
|
||||
| `MATH_ENABLE` | Enable LaTeX math block conversion. | `True` |
|
||||
| `MATH_INLINE_DOLLAR_ENABLE` | Enable inline `$ ... $` math conversion. | `True` |
|
||||
|
||||
## 🔥 What's New in v0.4.3
|
||||
## 🔥 What's New in v0.4.4
|
||||
|
||||
- 🧹 **Content Cleanup**: Enhanced stripping of `<details>` blocks (often used for tool calls/thinking process) to ensure a clean final document.
|
||||
- 📄 **Standard Document Formatting**: Applied professional document formatting standards for titles and headings (centered title, bold, optimized font sizes and spacing), including GB/T compliance for Chinese content.
|
||||
- 🔠 **Font Rendering Fix**: Fixed an issue where CJK characters would fallback to MS Gothic in Word; now correctly uses the configured Asian font (e.g., SimSun).
|
||||
- ⚙️ **Title Alignment**: Added `TITLE_ALIGNMENT` valve to configure document title alignment (left, center, right).
|
||||
|
||||
### User-Level Configuration (UserValves)
|
||||
|
||||
Users can override the following settings in their personal settings:
|
||||
|
||||
- `TITLE_SOURCE`
|
||||
- `UI_LANGUAGE`
|
||||
- `FONT_LATIN`, `FONT_ASIAN`, `FONT_CODE`
|
||||
@@ -120,4 +126,4 @@ Users can override the following settings in their personal settings:
|
||||
## Source Code
|
||||
|
||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/export_to_docx){ .md-button }
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.4.3 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.4.4 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Export to Word(导出为 Word)
|
||||
|
||||
<span class="category-badge action">Action</span>
|
||||
<span class="version-badge">v0.4.3</span>
|
||||
<span class="version-badge">v0.4.4</span>
|
||||
|
||||
将当前对话导出为完美格式的 Word 文档,支持**代码语法高亮**、**原生数学公式**、**Mermaid 图表**、**引用资料**以及**增强表格**渲染。
|
||||
|
||||
@@ -53,9 +53,17 @@ Export to Word 插件会把聊天消息从 Markdown 转成精致的 Word 文档
|
||||
| `启用数学公式` | 启用 LaTeX 数学公式块转换。 | `True` |
|
||||
| `启用行内公式` | 启用行内 `$ ... $` 数学公式转换。 | `True` |
|
||||
|
||||
## 🔥 v0.4.4 更新内容
|
||||
|
||||
- 🧹 **内容清理加强**: 增强了对 `<details>` 块(通常包含工具调用或思考过程)的清理,确保最终文档整洁。
|
||||
- 📄 **文档格式标准化**: 采用了专业的文档排版标准(兼容中文 GB/T 规范),标题居中加粗,各级标题使用标准字号和间距。
|
||||
- 🔠 **字体渲染修复**: 修复了 CJK 字符在 Word 中回退到 MS Gothic 的问题;现在正确使用配置的中文字体(例如宋体)。
|
||||
- ⚙️ **标题对齐配置**: 新增 `标题对齐方式` Valve,支持配置文档标题的对齐方式(左对齐、居中、右对齐)。
|
||||
|
||||
### 用户级配置 (UserValves)
|
||||
|
||||
用户可以在个人设置中覆盖以下配置:
|
||||
|
||||
- `文档标题来源`
|
||||
- `界面语言`
|
||||
- `英文字体`, `中文字体`, `代码字体`
|
||||
@@ -117,4 +125,5 @@ Export to Word 插件会把聊天消息从 Markdown 转成精致的 Word 文档
|
||||
|
||||
## 源码
|
||||
|
||||
[:fontawes**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.4.3 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)/tree/main/plugins/actions/export_to_docx){ .md-button }
|
||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/export_to_docx){ .md-button }
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.4.4 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Knowledge Card
|
||||
# Flash Card
|
||||
|
||||
<span class="category-badge action">Action</span>
|
||||
<span class="version-badge">v0.2.2</span>
|
||||
<span class="version-badge">v0.2.4</span>
|
||||
|
||||
Quickly generates beautiful learning memory cards, perfect for studying and quick memorization.
|
||||
Quickly generates beautiful flashcards from text, extracting key points and categories.
|
||||
|
||||
---
|
||||
|
||||
@@ -23,7 +23,7 @@ The Knowledge Card plugin (also known as Flash Card / 闪记卡) transforms cont
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the plugin file: [`knowledge_card.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/knowledge-card)
|
||||
1. Download the plugin file: [`flash_card.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/flash-card)
|
||||
2. Upload to OpenWebUI: **Admin Panel** → **Settings** → **Functions**
|
||||
3. Enable the plugin
|
||||
|
||||
@@ -85,4 +85,4 @@ The Knowledge Card plugin (also known as Flash Card / 闪记卡) transforms cont
|
||||
|
||||
## Source Code
|
||||
|
||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/knowledge-card){ .md-button }
|
||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/flash-card){ .md-button }
|
||||
@@ -1,7 +1,7 @@
|
||||
# Knowledge Card(知识卡片)
|
||||
# Flash Card(闪记卡)
|
||||
|
||||
<span class="category-badge action">Action</span>
|
||||
<span class="version-badge">v0.2.0</span>
|
||||
<span class="version-badge">v0.2.4</span>
|
||||
|
||||
快速生成精美的学习记忆卡片,适合学习和速记。
|
||||
|
||||
@@ -23,7 +23,7 @@ Knowledge Card 插件(又名 Flash Card / 闪记卡)会把内容转成视觉
|
||||
|
||||
## 安装
|
||||
|
||||
1. 下载插件文件:[`knowledge_card.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/knowledge-card)
|
||||
1. 下载插件文件:[`flash_card.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/flash-card)
|
||||
2. 上传到 OpenWebUI:**Admin Panel** → **Settings** → **Functions**
|
||||
3. 启用插件
|
||||
|
||||
@@ -85,4 +85,4 @@ Knowledge Card 插件(又名 Flash Card / 闪记卡)会把内容转成视觉
|
||||
|
||||
## 源码
|
||||
|
||||
[:fontawesome-brands-github: 在 GitHub 查看](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/knowledge-card){ .md-button }
|
||||
[:fontawesome-brands-github: 在 GitHub 查看](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/flash-card){ .md-button }
|
||||
@@ -17,37 +17,37 @@ Actions are interactive plugins that:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-brain:{ .lg .middle } **Smart Mind Map**
|
||||
- :material-brain:{ .lg .middle } **Smart Mind Map**
|
||||
|
||||
---
|
||||
|
||||
Intelligently analyzes text content and generates interactive mind maps with beautiful visualizations.
|
||||
|
||||
**Version:** 0.8.0
|
||||
**Version:** 0.9.2
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](smart-mind-map.md)
|
||||
|
||||
- :material-chart-bar:{ .lg .middle } **Smart Infographic**
|
||||
- :material-chart-bar:{ .lg .middle } **Smart Infographic**
|
||||
|
||||
---
|
||||
|
||||
Transform text into professional infographics using AntV visualization engine with various templates.
|
||||
|
||||
**Version:** 1.4.1
|
||||
**Version:** 1.5.0
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](smart-infographic.md)
|
||||
|
||||
- :material-card-text:{ .lg .middle } **Knowledge Card**
|
||||
- :material-card-text:{ .lg .middle } **Flash Card**
|
||||
|
||||
---
|
||||
|
||||
Quickly generates beautiful learning memory cards, perfect for studying and memorization.
|
||||
Quickly generates beautiful flashcards from text, extracting key points and categories.
|
||||
|
||||
**Version:** 0.2.2
|
||||
**Version:** 0.2.4
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](knowledge-card.md)
|
||||
[:octicons-arrow-right-24: Documentation](flash-card.md)
|
||||
|
||||
- :material-file-excel:{ .lg .middle } **Export to Excel**
|
||||
- :material-file-excel:{ .lg .middle } **Export to Excel**
|
||||
|
||||
---
|
||||
|
||||
@@ -57,35 +57,25 @@ Actions are interactive plugins that:
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](export-to-excel.md)
|
||||
|
||||
- :material-file-word-box:{ .lg .middle } **Export to Word (Enhanced Formatting)**
|
||||
- :material-file-word-box:{ .lg .middle } **Export to Word (Enhanced Formatting)**
|
||||
|
||||
---
|
||||
|
||||
Export the current conversation to a formatted Word doc with **syntax highlighting**, **native math equations**, **Mermaid diagrams**, **citations**, and **enhanced table formatting**.
|
||||
|
||||
**Version:** 0.4.2
|
||||
|
||||
**Version:** 0.4.4
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](export-to-word.md)
|
||||
|
||||
- :material-text-box-search:{ .lg .middle } **Summary**
|
||||
- :material-brain:{ .lg .middle } **Deep Dive**
|
||||
|
||||
---
|
||||
|
||||
Generate concise summaries of long text content with key points extraction.
|
||||
|
||||
**Version:** 0.1.0
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](summary.md)
|
||||
|
||||
- :material-image-text:{ .lg .middle } **Infographic to Markdown**
|
||||
|
||||
---
|
||||
|
||||
AI-powered infographic generator that renders SVG and embeds it as Markdown Data URL image.
|
||||
A comprehensive thinking lens that dives deep into any content - Context → Logic → Insight → Path. Supports theme auto-adaptation.
|
||||
|
||||
**Version:** 1.0.0
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](infographic-markdown.md)
|
||||
[:octicons-arrow-right-24: Documentation](deep-dive.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ Actions 是交互式插件,能够:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-brain:{ .lg .middle } **Smart Mind Map**
|
||||
- :material-brain:{ .lg .middle } **Smart Mind Map**
|
||||
|
||||
---
|
||||
|
||||
@@ -27,27 +27,27 @@ Actions 是交互式插件,能够:
|
||||
|
||||
[:octicons-arrow-right-24: 查看文档](smart-mind-map.md)
|
||||
|
||||
- :material-chart-bar:{ .lg .middle } **Smart Infographic**
|
||||
- :material-chart-bar:{ .lg .middle } **Smart Infographic**
|
||||
|
||||
---
|
||||
|
||||
使用 AntV 可视化引擎,将文本转成专业的信息图。
|
||||
|
||||
**版本:** 1.4.1
|
||||
**版本:** 1.4.9
|
||||
|
||||
[:octicons-arrow-right-24: 查看文档](smart-infographic.md)
|
||||
|
||||
- :material-card-text:{ .lg .middle } **Knowledge Card**
|
||||
- :material-card-text:{ .lg .middle } **Flash Card(闪记卡)**
|
||||
|
||||
---
|
||||
|
||||
快速生成精美的学习记忆卡片,适合学习与记忆。
|
||||
快速生成精美的学习记忆卡片,非常适合学习和快速记忆。
|
||||
|
||||
**版本:** 0.2.2
|
||||
**版本:** 0.2.4
|
||||
|
||||
[:octicons-arrow-right-24: 查看文档](knowledge-card.md)
|
||||
[:octicons-arrow-right-24: 查看文档](flash-card.md)
|
||||
|
||||
- :material-file-excel:{ .lg .middle } **Export to Excel**
|
||||
- :material-file-excel:{ .lg .middle } **Export to Excel**
|
||||
|
||||
---
|
||||
|
||||
@@ -57,35 +57,25 @@ Actions 是交互式插件,能够:
|
||||
|
||||
[:octicons-arrow-right-24: 查看文档](export-to-excel.md)
|
||||
|
||||
- :material-file-word-box:{ .lg .middle } **Word 导出 (格式增强)**
|
||||
- :material-file-word-box:{ .lg .middle } **Word 导出 (格式增强)**
|
||||
|
||||
---
|
||||
|
||||
将当前对话导出为完美格式的 Word 文档,支持**代码语法高亮**、**原生数学公式**、**Mermaid 图表**、**引用资料**以及**增强表格**渲染。
|
||||
|
||||
**版本:** 0.4.2
|
||||
**版本:** 0.4.4
|
||||
|
||||
[:octicons-arrow-right-24: 查看文档](export-to-word.md)
|
||||
|
||||
- :material-text-box-search:{ .lg .middle } **Summary**
|
||||
- :material-brain:{ .lg .middle } **精读 (Deep Dive)**
|
||||
|
||||
---
|
||||
|
||||
对长文本进行精简总结,提取要点。
|
||||
|
||||
**版本:** 0.1.0
|
||||
|
||||
[:octicons-arrow-right-24: 查看文档](summary.md)
|
||||
|
||||
- :material-image-text:{ .lg .middle } **信息图转 Markdown**
|
||||
|
||||
---
|
||||
|
||||
AI 驱动的信息图生成器,渲染 SVG 并以 Markdown Data URL 图片嵌入。
|
||||
全方位的思维透镜 —— 全景 → 脉络 → 洞察 → 路径。支持主题自适应。
|
||||
|
||||
**版本:** 1.0.0
|
||||
|
||||
[:octicons-arrow-right-24: 查看文档](infographic-markdown.zh.md)
|
||||
[:octicons-arrow-right-24: 查看文档](deep-dive.zh.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Smart Infographic
|
||||
|
||||
<span class="category-badge action">Action</span>
|
||||
<span class="version-badge">v1.4.0</span>
|
||||
<span class="version-badge">v1.4.9</span>
|
||||
|
||||
An AntV Infographic engine powered plugin that transforms long text into professional, beautiful infographics with a single click.
|
||||
|
||||
@@ -14,8 +14,8 @@ The Smart Infographic plugin uses AI to analyze text content and generate profes
|
||||
## Features
|
||||
|
||||
- :material-robot: **AI-Powered Transformation**: Automatically analyzes text logic, extracts key points, and generates structured charts
|
||||
- :material-palette: **Professional Templates**: Includes various AntV official templates: Lists, Trees, Mindmaps, Comparison Tables, Flowcharts, and Statistical Charts
|
||||
- :material-magnify: **Auto-Icon Matching**: Built-in logic to search and match the most relevant Material Design Icons based on content
|
||||
- :material-palette: **70+ Professional Templates**: Includes various AntV official templates: Lists, Trees, Roadmaps, Timelines, Comparison Tables, SWOT, Quadrants, and Statistical Charts
|
||||
- :material-magnify: **Auto-Icon Matching**: Built-in logic to search and match the most relevant icons (Iconify) and illustrations (unDraw)
|
||||
- :material-download: **Multi-Format Export**: Download your infographics as **SVG**, **PNG**, or **Standalone HTML** file
|
||||
- :material-theme-light-dark: **Theme Support**: Supports Dark/Light modes, auto-adapts theme colors
|
||||
- :material-cellphone-link: **Responsive Design**: Generated charts look great on both desktop and mobile devices
|
||||
@@ -37,10 +37,11 @@ The Smart Infographic plugin uses AI to analyze text content and generate profes
|
||||
|
||||
| Category | Template Name | Use Case |
|
||||
|:---------|:--------------|:---------|
|
||||
| **Lists & Hierarchy** | `list-grid`, `tree-vertical`, `mindmap` | Features, Org Charts, Brainstorming |
|
||||
| **Sequence & Relation** | `sequence-roadmap`, `relation-circle` | Roadmaps, Circular Flows, Steps |
|
||||
| **Comparison & Analysis** | `compare-binary`, `compare-swot`, `quadrant-quarter` | Pros/Cons, SWOT, Quadrants |
|
||||
| **Charts & Data** | `chart-bar`, `chart-line`, `chart-pie` | Trends, Distributions, Metrics |
|
||||
| **Sequence** | `sequence-timeline-simple`, `sequence-roadmap-vertical-simple`, `sequence-snake-steps-compact-card` | Timelines, Roadmaps, Processes |
|
||||
| **Lists** | `list-grid-candy-card-lite`, `list-row-horizontal-icon-arrow`, `list-column-simple-vertical-arrow` | Features, Bullet Points, Lists |
|
||||
| **Comparison** | `compare-binary-horizontal-underline-text-vs`, `compare-swot`, `quadrant-quarter-simple-card` | Pros/Cons, SWOT, Quadrants |
|
||||
| **Hierarchy** | `hierarchy-tree-tech-style-capsule-item`, `hierarchy-structure` | Org Charts, Structures |
|
||||
| **Charts** | `chart-column-simple`, `chart-bar-plain-text`, `chart-line-plain-text`, `chart-wordcloud` | Trends, Distributions, Metrics |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Smart Infographic(智能信息图)
|
||||
|
||||
<span class="category-badge action">Action</span>
|
||||
<span class="version-badge">v1.4.0</span>
|
||||
<span class="version-badge">v1.4.9</span>
|
||||
|
||||
基于 AntV 信息图引擎,将长文本一键转成专业、美观的信息图。
|
||||
|
||||
@@ -14,8 +14,8 @@ Smart Infographic 使用 AI 分析文本,并基于 AntV 可视化引擎生成
|
||||
## 功能特性
|
||||
|
||||
- :material-robot: **AI 转换**:自动分析文本逻辑,提取要点并生成结构化图表
|
||||
- :material-palette: **专业模板**:内置 AntV 官方模板:列表、树、思维导图、对比表、流程图、统计图等
|
||||
- :material-magnify: **自动匹配图标**:根据内容自动选择最合适的 Material Design Icons
|
||||
- :material-palette: **70+ 专业模板**:内置多种 AntV 官方模板,包括列表、树图、路线图、时间线、对比图、SWOT、象限图及统计图表等
|
||||
- :material-magnify: **自动匹配图标**:内置图标搜索逻辑,支持 Iconify 图标和 unDraw 插图自动匹配
|
||||
- :material-download: **多格式导出**:支持下载 **SVG**、**PNG**、**独立 HTML**
|
||||
- :material-theme-light-dark: **主题支持**:适配深色/浅色模式
|
||||
- :material-cellphone-link: **响应式**:桌面与移动端都能良好展示
|
||||
@@ -37,10 +37,11 @@ Smart Infographic 使用 AI 分析文本,并基于 AntV 可视化引擎生成
|
||||
|
||||
| 分类 | 模板名称 | 典型场景 |
|
||||
|:---------|:--------------|:---------|
|
||||
| **列表与层级** | `list-grid`, `tree-vertical`, `mindmap` | 特性列表、组织结构、头脑风暴 |
|
||||
| **序列与关系** | `sequence-roadmap`, `relation-circle` | 路线图、循环流程、步骤拆解 |
|
||||
| **对比与分析** | `compare-binary`, `compare-swot`, `quadrant-quarter` | 优劣势、SWOT、象限分析 |
|
||||
| **图表与数据** | `chart-bar`, `chart-line`, `chart-pie` | 趋势、分布、指标对比 |
|
||||
| **时序与流程** | `sequence-timeline-simple`, `sequence-roadmap-vertical-simple`, `sequence-snake-steps-compact-card` | 时间线、路线图、步骤说明 |
|
||||
| **列表与网格** | `list-grid-candy-card-lite`, `list-row-horizontal-icon-arrow`, `list-column-simple-vertical-arrow` | 功能亮点、要点列举、清单 |
|
||||
| **对比与分析** | `compare-binary-horizontal-underline-text-vs`, `compare-swot`, `quadrant-quarter-simple-card` | 优劣势对比、SWOT 分析、象限图 |
|
||||
| **层级与结构** | `hierarchy-tree-tech-style-capsule-item`, `hierarchy-structure` | 组织架构、层级关系 |
|
||||
| **图表与数据** | `chart-column-simple`, `chart-bar-plain-text`, `chart-line-plain-text`, `chart-wordcloud` | 数据趋势、比例分布、数值对比 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Smart Mind Map
|
||||
|
||||
<span class="category-badge action">Action</span>
|
||||
<span class="version-badge">v0.8.0</span>
|
||||
<span class="version-badge">v0.9.2</span>
|
||||
|
||||
Intelligently analyzes text content and generates interactive mind maps for better visualization and understanding.
|
||||
|
||||
@@ -17,13 +17,13 @@ The Smart Mind Map plugin transforms text content into beautiful, interactive mi
|
||||
- :material-gesture-swipe: **Rich Controls**: Zoom, reset view, expand level selector (All/2/3) and fullscreen
|
||||
- :material-palette: **Theme Aware**: Auto-detects OpenWebUI light/dark theme with manual toggle
|
||||
- :material-download: **One-Click Export**: Download high-res PNG, copy SVG, or copy Markdown source
|
||||
- :material-translate: **Multi-language**: Adapts output language to the user context
|
||||
- :material-translate: **Multi-language**: Matches output language to the input text
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the plugin file: [`思维导图.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/smart-mind-map)
|
||||
1. Download the plugin file: [`smart_mind_map.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/smart-mind-map)
|
||||
2. Upload to OpenWebUI: **Admin Panel** → **Settings** → **Functions** (Actions)
|
||||
3. Enable the plugin, and optionally allow iframe same-origin access so theme auto-detection works
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Smart Mind Map(智能思维导图)
|
||||
|
||||
<span class="category-badge action">Action</span>
|
||||
<span class="version-badge">v0.8.0</span>
|
||||
<span class="version-badge">v0.9.2</span>
|
||||
|
||||
智能分析文本内容,生成交互式思维导图,帮助你更直观地理解信息结构。
|
||||
|
||||
@@ -17,13 +17,13 @@ Smart Mind Map 会将文本转换成漂亮的交互式思维导图。插件会
|
||||
- :material-gesture-swipe: **丰富控制**:缩放/重置、展开层级(全部/2/3 级)与全屏
|
||||
- :material-palette: **主题感知**:自动检测 OpenWebUI 亮/暗色主题并支持手动切换
|
||||
- :material-download: **一键导出**:下载高分辨率 PNG、复制 SVG 或 Markdown
|
||||
- :material-translate: **多语言**:根据用户语言自动输出
|
||||
- :material-translate: **多语言**:输出语言与输入文本一致
|
||||
|
||||
---
|
||||
|
||||
## 安装
|
||||
|
||||
1. 下载插件文件:[`思维导图.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/smart-mind-map)
|
||||
1. 下载插件文件:[`smart_mind_map.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/smart-mind-map)
|
||||
2. 上传到 OpenWebUI:**Admin Panel** → **Settings** → **Functions**(Actions)
|
||||
3. 启用插件,并可在设置中允许 iframe same-origin 以启用主题自动检测
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
# Summary
|
||||
|
||||
<span class="category-badge action">Action</span>
|
||||
<span class="version-badge">v0.1.0</span>
|
||||
|
||||
Generate concise summaries of long text content with key points extraction.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Summary plugin helps you quickly understand long pieces of text by generating concise summaries with extracted key points. It's perfect for:
|
||||
|
||||
- Summarizing long articles or documents
|
||||
- Extracting key points from conversations
|
||||
- Creating quick overviews of complex topics
|
||||
|
||||
## Features
|
||||
|
||||
- :material-text-box-search: **Smart Summarization**: AI-powered content analysis
|
||||
- :material-format-list-bulleted: **Key Points**: Extracted important highlights
|
||||
- :material-content-copy: **Easy Copy**: One-click copying of summaries
|
||||
- :material-tune: **Adjustable Length**: Control summary detail level
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the plugin file: [`summary.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/summary)
|
||||
2. Upload to OpenWebUI: **Admin Panel** → **Settings** → **Functions**
|
||||
3. Enable the plugin
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
1. Get a long response from the AI or paste long text
|
||||
2. Click the **Summary** button in the message action bar
|
||||
3. View the generated summary with key points
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `summary_length` | string | `"medium"` | Length of summary (short/medium/long) |
|
||||
| `include_key_points` | boolean | `true` | Extract and list key points |
|
||||
| `language` | string | `"auto"` | Output language |
|
||||
|
||||
---
|
||||
|
||||
## Example Output
|
||||
|
||||
```markdown
|
||||
## Summary
|
||||
|
||||
This document discusses the implementation of a new feature
|
||||
for the application, focusing on user experience improvements
|
||||
and performance optimizations.
|
||||
|
||||
### Key Points
|
||||
|
||||
- ✅ New user interface design improves accessibility
|
||||
- ✅ Backend optimizations reduce load times by 40%
|
||||
- ✅ Mobile responsiveness enhanced
|
||||
- ✅ Integration with third-party services simplified
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
!!! note "Prerequisites"
|
||||
- OpenWebUI v0.3.0 or later
|
||||
- Uses the active LLM model for summarization
|
||||
|
||||
---
|
||||
|
||||
## Source Code
|
||||
|
||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/summary){ .md-button }
|
||||
@@ -1,82 +0,0 @@
|
||||
# Summary(摘要)
|
||||
|
||||
<span class="category-badge action">Action</span>
|
||||
<span class="version-badge">v0.1.0</span>
|
||||
|
||||
为长文本生成简洁摘要,并提取关键要点。
|
||||
|
||||
---
|
||||
|
||||
## 概览
|
||||
|
||||
Summary 插件可以快速理解长文本,生成精炼摘要并列出关键点,适合:
|
||||
|
||||
- 总结长文章或文档
|
||||
- 从对话中提炼要点
|
||||
- 为复杂主题制作快速概览
|
||||
|
||||
## 功能特性
|
||||
|
||||
- :material-text-box-search: **智能摘要**:AI 驱动的内容分析
|
||||
- :material-format-list-bulleted: **关键点**:提取重要信息
|
||||
- :material-content-copy: **便捷复制**:一键复制摘要
|
||||
- :material-tune: **长度可调**:可选择摘要详略程度
|
||||
|
||||
---
|
||||
|
||||
## 安装
|
||||
|
||||
1. 下载插件文件:[`summary.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/summary)
|
||||
2. 上传到 OpenWebUI:**Admin Panel** → **Settings** → **Functions**
|
||||
3. 启用插件
|
||||
|
||||
---
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 获取一段较长的 AI 回复或粘贴长文本
|
||||
2. 点击消息操作栏的 **Summary** 按钮
|
||||
3. 查看生成的摘要与关键点
|
||||
|
||||
---
|
||||
|
||||
## 配置项
|
||||
|
||||
| 选项 | 类型 | 默认值 | 说明 |
|
||||
|--------|------|---------|-------------|
|
||||
| `summary_length` | string | `"medium"` | 摘要长度(short/medium/long) |
|
||||
| `include_key_points` | boolean | `true` | 是否提取并列出关键点 |
|
||||
| `language` | string | `"auto"` | 输出语言 |
|
||||
|
||||
---
|
||||
|
||||
## 输出示例
|
||||
|
||||
```markdown
|
||||
## Summary
|
||||
|
||||
This document discusses the implementation of a new feature
|
||||
for the application, focusing on user experience improvements
|
||||
and performance optimizations.
|
||||
|
||||
### Key Points
|
||||
|
||||
- ✅ New user interface design improves accessibility
|
||||
- ✅ Backend optimizations reduce load times by 40%
|
||||
- ✅ Mobile responsiveness enhanced
|
||||
- ✅ Integration with third-party services simplified
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 运行要求
|
||||
|
||||
!!! note "前置条件"
|
||||
- OpenWebUI v0.3.0 及以上
|
||||
- 使用当前会话的 LLM 模型进行摘要
|
||||
|
||||
---
|
||||
|
||||
## 源码
|
||||
|
||||
[:fontawesome-brands-github: 在 GitHub 查看](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/summary){ .md-button }
|
||||
@@ -1,7 +1,7 @@
|
||||
# Async Context Compression
|
||||
|
||||
<span class="category-badge filter">Filter</span>
|
||||
<span class="version-badge">v1.1.0</span>
|
||||
<span class="version-badge">v1.2.2</span>
|
||||
|
||||
Reduces token consumption in long conversations through intelligent summarization while maintaining conversational coherence.
|
||||
|
||||
@@ -29,6 +29,17 @@ This is especially useful for:
|
||||
- :material-clock-fast: **Async Processing**: Non-blocking background compression
|
||||
- :material-memory: **Context Preservation**: Keeps important information
|
||||
- :material-currency-usd-off: **Cost Reduction**: Minimize token usage
|
||||
- :material-console: **Frontend Debugging**: Debug logs in browser console
|
||||
- :material-alert-circle-check: **Enhanced Error Reporting**: Clear error status notifications
|
||||
- :material-check-all: **Open WebUI v0.7.x Compatibility**: Dynamic DB session handling
|
||||
- :material-account-convert: **Improved Compatibility**: Summary role changed to `assistant`
|
||||
- :material-shield-check: **Enhanced Stability**: Resolved race conditions in state management
|
||||
- :material-ruler: **Preflight Context Check**: Validates context fit before sending
|
||||
- :material-format-align-justify: **Structure-Aware Trimming**: Preserves document structure
|
||||
- :material-content-cut: **Native Tool Output Trimming**: Trims verbose tool outputs (Note: Non-native tool outputs are not fully injected into context)
|
||||
- :material-chart-bar: **Detailed Token Logging**: Granular token breakdown
|
||||
- :material-account-search: **Smart Model Matching**: Inherit config from base models
|
||||
- :material-image-off: **Multimodal Support**: Images are preserved but tokens are **NOT** calculated
|
||||
|
||||
---
|
||||
|
||||
@@ -59,10 +70,14 @@ graph TD
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `token_threshold` | integer | `4000` | Trigger compression above this token count |
|
||||
| `preserve_recent` | integer | `5` | Number of recent messages to keep uncompressed |
|
||||
| `summary_model` | string | `"auto"` | Model to use for summarization |
|
||||
| `compression_ratio` | float | `0.3` | Target compression ratio |
|
||||
| `compression_threshold_tokens` | integer | `64000` | Trigger compression above this token count |
|
||||
| `max_context_tokens` | integer | `128000` | Hard limit for context |
|
||||
| `keep_first` | integer | `1` | Always keep the first N messages |
|
||||
| `keep_last` | integer | `6` | Always keep the last N messages |
|
||||
| `summary_model` | string | `None` | Model to use for summarization |
|
||||
| `summary_model_max_context` | integer | `0` | Max context tokens for summary model |
|
||||
| `max_summary_tokens` | integer | `16384` | Maximum tokens for the summary |
|
||||
| `enable_tool_output_trimming` | boolean | `false` | Enable trimming of large tool outputs |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Async Context Compression(异步上下文压缩)
|
||||
|
||||
<span class="category-badge filter">Filter</span>
|
||||
<span class="version-badge">v1.1.0</span>
|
||||
<span class="version-badge">v1.2.2</span>
|
||||
|
||||
通过智能摘要减少长对话的 token 消耗,同时保持对话连贯。
|
||||
|
||||
@@ -29,6 +29,17 @@ Async Context Compression 过滤器通过以下方式帮助管理长对话的 to
|
||||
- :material-clock-fast: **异步处理**:后台非阻塞压缩
|
||||
- :material-memory: **保留上下文**:尽量保留重要信息
|
||||
- :material-currency-usd-off: **降低成本**:减少 token 使用
|
||||
- :material-console: **前端调试**:支持浏览器控制台日志
|
||||
- :material-alert-circle-check: **增强错误报告**:清晰的错误状态通知
|
||||
- :material-check-all: **Open WebUI v0.7.x 兼容性**:动态数据库会话处理
|
||||
- :material-account-convert: **兼容性提升**:摘要角色改为 `assistant`
|
||||
- :material-shield-check: **稳定性增强**:解决状态管理竞态条件
|
||||
- :material-ruler: **预检上下文检查**:发送前验证上下文是否超限
|
||||
- :material-format-align-justify: **结构感知裁剪**:保留文档结构的智能裁剪
|
||||
- :material-content-cut: **原生工具输出裁剪**:自动裁剪冗长的工具输出(注意:非原生工具调用输出不会完整注入上下文)
|
||||
- :material-chart-bar: **详细 Token 日志**:提供细粒度的 Token 统计
|
||||
- :material-account-search: **智能模型匹配**:自定义模型自动继承基础模型配置
|
||||
- :material-image-off: **多模态支持**:图片内容保留但 Token **不参与计算**
|
||||
|
||||
---
|
||||
|
||||
@@ -59,10 +70,14 @@ graph TD
|
||||
|
||||
| 选项 | 类型 | 默认值 | 说明 |
|
||||
|--------|------|---------|-------------|
|
||||
| `token_threshold` | integer | `4000` | 超过该 token 数触发压缩 |
|
||||
| `preserve_recent` | integer | `5` | 保留不压缩的最近消息数量 |
|
||||
| `summary_model` | string | `"auto"` | 用于摘要的模型 |
|
||||
| `compression_ratio` | float | `0.3` | 目标压缩比例 |
|
||||
| `compression_threshold_tokens` | integer | `64000` | 超过该 token 数触发压缩 |
|
||||
| `max_context_tokens` | integer | `128000` | 上下文硬性上限 |
|
||||
| `keep_first` | integer | `1` | 始终保留的前 N 条消息 |
|
||||
| `keep_last` | integer | `6` | 始终保留的后 N 条消息 |
|
||||
| `summary_model` | string | `None` | 用于摘要的模型 |
|
||||
| `summary_model_max_context` | integer | `0` | 摘要模型的最大上下文 Token 数 |
|
||||
| `max_summary_tokens` | integer | `16384` | 摘要的最大 token 数 |
|
||||
| `enable_tool_output_trimming` | boolean | `false` | 启用长工具输出裁剪 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
57
docs/plugins/filters/folder-memory.md
Normal file
57
docs/plugins/filters/folder-memory.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Folder Memory
|
||||
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **Version:** 0.1.0 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **License:** MIT
|
||||
|
||||
---
|
||||
|
||||
### 📌 What's new in 0.1.0
|
||||
- **Initial Release**: Automated "Project Rules" management for OpenWebUI folders.
|
||||
- **Folder-Level Persistence**: Automatically updates folder system prompts with extracted rules.
|
||||
- **Optimized Performance**: Runs asynchronously and supports `PRIORITY` configuration for seamless integration with other filters.
|
||||
|
||||
---
|
||||
|
||||
**Folder Memory** is an intelligent context filter plugin for OpenWebUI. It automatically extracts consistent "Project Rules" from ongoing conversations within a folder and injects them back into the folder's system prompt.
|
||||
|
||||
This ensures that all future conversations within that folder share the same evolved context and rules, without manual updates.
|
||||
|
||||
## Features
|
||||
|
||||
- **Automatic Extraction**: Analyzes chat history every N messages to extract project rules.
|
||||
- **Non-destructive Injection**: Updates only the specific "Project Rules" block in the system prompt, preserving other instructions.
|
||||
- **Async Processing**: Runs in the background without blocking the user's chat experience.
|
||||
- **ORM Integration**: Directly updates folder data using OpenWebUI's internal models for reliability.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Conversations must occur inside a folder.** This plugin only triggers when a chat belongs to a folder (i.e., you need to create a folder in OpenWebUI and start a conversation within it).
|
||||
|
||||
## Installation
|
||||
|
||||
1. Copy `folder_memory.py` to your OpenWebUI `plugins/filters/` directory (or upload via Admin UI).
|
||||
2. Enable the filter in your **Settings** -> **Filters**.
|
||||
3. (Optional) Configure the triggering threshold (default: every 10 messages).
|
||||
|
||||
## Configuration (Valves)
|
||||
|
||||
| Valve | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `PRIORITY` | `20` | Priority level for the filter operations. |
|
||||
| `MESSAGE_TRIGGER_COUNT` | `10` | The number of messages required to trigger a rule analysis. |
|
||||
| `MODEL_ID` | `""` | The model used to generate rules. If empty, uses the current chat model. |
|
||||
| `RULES_BLOCK_TITLE` | `## 📂 Project Rules` | The title displayed above the injected rules block. |
|
||||
| `SHOW_DEBUG_LOG` | `False` | Show detailed debug logs in the browser console. |
|
||||
| `UPDATE_ROOT_FOLDER` | `False` | If enabled, finds and updates the root folder rules instead of the current subfolder. |
|
||||
|
||||
## How It Works
|
||||
|
||||

|
||||
|
||||
1. **Trigger**: When a conversation reaches `MESSAGE_TRIGGER_COUNT` (e.g., 10, 20 messages).
|
||||
2. **Analysis**: The plugin sends the recent conversation + existing rules to the LLM.
|
||||
3. **Synthesis**: The LLM merges new insights with old rules, removing obsolete ones.
|
||||
4. **Update**: The new rule set replaces the `<!-- OWUI_PROJECT_RULES_START -->` block in the folder's system prompt.
|
||||
|
||||
## Roadmap
|
||||
|
||||
See [ROADMAP](https://github.com/Fu-Jie/awesome-openwebui/blob/main/plugins/filters/folder-memory/ROADMAP.md) for future plans, including "Project Knowledge" collection.
|
||||
57
docs/plugins/filters/folder-memory.zh.md
Normal file
57
docs/plugins/filters/folder-memory.zh.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# 文件夹记忆 (Folder Memory)
|
||||
|
||||
**作者:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **版本:** 0.1.0 | **项目:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **许可证:** MIT
|
||||
|
||||
---
|
||||
|
||||
### 📌 0.1.0 版本特性
|
||||
- **首个版本发布**:专注于自动化的“项目规则”管理。
|
||||
- **文件夹级持久化**:自动将提取的规则回写到文件夹系统提示词中。
|
||||
- **性能优化**:采用异步处理机制,并支持 `PRIORITY` 配置,确保与其他过滤器(如上下文压缩)完美协作。
|
||||
|
||||
---
|
||||
|
||||
**文件夹记忆 (Folder Memory)** 是一个 OpenWebUI 的智能上下文过滤器插件。它能自动从文件夹内的对话中提取一致性的“项目规则”,并将其回写到文件夹的系统提示词中。
|
||||
|
||||
这确保了该文件夹内的所有未来对话都能共享相同的进化上下文和规则,无需手动更新。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- **自动提取**:每隔 N 条消息分析一次聊天记录,提取项目规则。
|
||||
- **无损注入**:仅更新系统提示词中的特定“项目规则”块,保留其他指令。
|
||||
- **异步处理**:在后台运行,不阻塞用户的聊天体验。
|
||||
- **ORM 集成**:直接使用 OpenWebUI 的内部模型更新文件夹数据,确保可靠性。
|
||||
|
||||
## 前置条件
|
||||
|
||||
- **对话必须在文件夹内进行。** 此插件仅在聊天属于某个文件夹时触发(即您需要先在 OpenWebUI 中创建一个文件夹,并在其内部开始对话)。
|
||||
|
||||
## 安装指南
|
||||
|
||||
1. 将 `folder_memory.py` (或中文版 `folder_memory_cn.py`) 复制到 OpenWebUI 的 `plugins/filters/` 目录(或通过管理员 UI 上传)。
|
||||
2. 在 **设置** -> **过滤器** 中启用该插件。
|
||||
3. (可选)配置触发阈值(默认:每 10 条消息)。
|
||||
|
||||
## 配置 (Valves)
|
||||
|
||||
| 参数 | 默认值 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| `PRIORITY` | `20` | 过滤器操作的优先级。 |
|
||||
| `MESSAGE_TRIGGER_COUNT` | `10` | 触发规则分析的消息数量阈值。 |
|
||||
| `MODEL_ID` | `""` | 用于生成规则的模型 ID。若为空,则使用当前对话模型。 |
|
||||
| `RULES_BLOCK_TITLE` | `## 📂 项目规则` | 显示在注入规则块上方的标题。 |
|
||||
| `SHOW_DEBUG_LOG` | `False` | 在浏览器控制台显示详细调试日志。 |
|
||||
| `UPDATE_ROOT_FOLDER` | `False` | 如果启用,将向上查找并更新根文件夹的规则,而不是当前子文件夹。 |
|
||||
|
||||
## 工作原理
|
||||
|
||||

|
||||
|
||||
1. **触发**:当对话达到 `MESSAGE_TRIGGER_COUNT`(例如 10、20 条消息)时。
|
||||
2. **分析**:插件将最近的对话 + 现有规则发送给 LLM。
|
||||
3. **综合**:LLM 将新见解与旧规则合并,移除过时的规则。
|
||||
4. **更新**:新的规则集替换文件夹系统提示词中的 `<!-- OWUI_PROJECT_RULES_START -->` 块。
|
||||
|
||||
## 路线图
|
||||
|
||||
查看 [ROADMAP](https://github.com/Fu-Jie/awesome-openwebui/blob/main/plugins/filters/folder-memory/ROADMAP.md) 了解未来计划,包括“项目知识”收集功能。
|
||||
@@ -1,54 +0,0 @@
|
||||
# Gemini Manifold Companion
|
||||
|
||||
<span class="category-badge filter">Filter</span>
|
||||
<span class="version-badge">v0.3.2</span>
|
||||
|
||||
Companion filter for the Gemini Manifold pipe plugin, providing enhanced functionality.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Gemini Manifold Companion works alongside the [Gemini Manifold Pipe](../pipes/gemini-manifold.md) to provide additional processing and enhancement for Gemini model integrations.
|
||||
|
||||
## Features
|
||||
|
||||
- :material-handshake: **Seamless Integration**: Works with Gemini Manifold pipe
|
||||
- :material-format-text: **Message Formatting**: Optimizes messages for Gemini
|
||||
- :material-shield: **Error Handling**: Graceful handling of API issues
|
||||
- :material-tune: **Fine-tuning**: Additional configuration options
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
1. First, install the [Gemini Manifold Pipe](../pipes/gemini-manifold.md)
|
||||
2. Download the companion filter: [`gemini_manifold_companion.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/filters/gemini_manifold_companion)
|
||||
3. Upload to OpenWebUI: **Admin Panel** → **Settings** → **Functions**
|
||||
4. Enable the filter
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `auto_format` | boolean | `true` | Auto-format messages for Gemini |
|
||||
| `handle_errors` | boolean | `true` | Enable error handling |
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
!!! warning "Dependency"
|
||||
This filter requires the **Gemini Manifold Pipe** to be installed and configured.
|
||||
|
||||
!!! note "Prerequisites"
|
||||
- OpenWebUI v0.3.0 or later
|
||||
- Gemini Manifold Pipe installed
|
||||
|
||||
---
|
||||
|
||||
## Source Code
|
||||
|
||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/filters/gemini_manifold_companion){ .md-button }
|
||||
@@ -1,54 +0,0 @@
|
||||
# Gemini Manifold Companion
|
||||
|
||||
<span class="category-badge filter">Filter</span>
|
||||
<span class="version-badge">v0.3.2</span>
|
||||
|
||||
Gemini Manifold Pipe 的伴随过滤器,用于增强 Gemini 集成的处理效果。
|
||||
|
||||
---
|
||||
|
||||
## 概览
|
||||
|
||||
Gemini Manifold Companion 与 [Gemini Manifold Pipe](../pipes/gemini-manifold.md) 搭配使用,为 Gemini 模型集成提供额外的处理与优化。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- :material-handshake: **无缝协同**:与 Gemini Manifold Pipe 配合工作
|
||||
- :material-format-text: **消息格式化**:针对 Gemini 优化消息
|
||||
- :material-shield: **错误处理**:更友好的 API 异常处理
|
||||
- :material-tune: **精细配置**:提供额外调优选项
|
||||
|
||||
---
|
||||
|
||||
## 安装
|
||||
|
||||
1. 先安装 [Gemini Manifold Pipe](../pipes/gemini-manifold.md)
|
||||
2. 下载伴随过滤器:[`gemini_manifold_companion.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/filters/gemini_manifold_companion)
|
||||
3. 上传到 OpenWebUI:**Admin Panel** → **Settings** → **Functions**
|
||||
4. 启用过滤器
|
||||
|
||||
---
|
||||
|
||||
## 配置项
|
||||
|
||||
| 选项 | 类型 | 默认值 | 说明 |
|
||||
|--------|------|---------|-------------|
|
||||
| `auto_format` | boolean | `true` | 为 Gemini 自动格式化消息 |
|
||||
| `handle_errors` | boolean | `true` | 开启错误处理 |
|
||||
|
||||
---
|
||||
|
||||
## 运行要求
|
||||
|
||||
!!! warning "依赖"
|
||||
本过滤器需要先安装并配置 **Gemini Manifold Pipe**。
|
||||
|
||||
!!! note "前置条件"
|
||||
- OpenWebUI v0.3.0 及以上
|
||||
- 已安装 Gemini Manifold Pipe
|
||||
|
||||
---
|
||||
|
||||
## 源码
|
||||
|
||||
[:fontawesome-brands-github: 在 GitHub 查看](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/filters/gemini_manifold_companion){ .md-button }
|
||||
53
docs/plugins/filters/github-copilot-sdk-files-filter.md
Normal file
53
docs/plugins/filters/github-copilot-sdk-files-filter.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# GitHub Copilot SDK Files Filter (v0.1.2)
|
||||
|
||||
This is a dedicated **companion filter plugin** designed specifically for the [GitHub Copilot SDK Pipe](../pipes/github-copilot-sdk.md).
|
||||
|
||||
Its core mission is to **protect user-uploaded files from being "pre-processed" by the OpenWebUI core system, ensuring that the Copilot Agent receives the raw files for autonomous analysis.**
|
||||
|
||||
## 🎯 Why is this needed?
|
||||
|
||||
In OpenWebUI's default workflow, when you upload a file (e.g., PDF, Excel, Python script), OpenWebUI automatically initiates a **RAG (Retrieval-Augmented Generation)** process: parsing the file, vectorizing it, extracting text, and injecting it into the prompt.
|
||||
|
||||
While useful for standard models, this is often disruptive for a **Copilot SDK Agent**:
|
||||
|
||||
1. **Agent Needs Raw Files**: The Agent may need to run Python code to read an Excel file or analyze a full directory structure, not chopped-up text fragments.
|
||||
2. **Context Pollution**: Large amounts of text injected by RAG consume tokens and can confuse the Agent about "where the file is."
|
||||
3. **Conflicts**: If you have other multimodal plugins installed (like Gemini Filter), they might compete for file processing rights.
|
||||
|
||||
**This plugin acts as a "bodyguard" to solve these issues.**
|
||||
|
||||
## 🚀 How it Works
|
||||
|
||||
When you select a Copilot model (name containing `copilot_sdk`) in OpenWebUI and send a file:
|
||||
|
||||
1. **Intercept**: This plugin runs with high priority (Priority 0), before RAG and other filters.
|
||||
2. **Relocate**: Detecting a Copilot model, it moves the `files` list from the request to a secure custom field `copilot_files`.
|
||||
3. **Hide**: It clears the original `files` field.
|
||||
4. **Status Update**: It emits a status message "Managed X files for Copilot (RAG Bypassed)" to the UI.
|
||||
5. **Pass**: The OpenWebUI core sees an empty `files` list and **does not trigger RAG**.
|
||||
6. **Deliver**: The subsequent [Copilot SDK Pipe](../pipes/github-copilot-sdk.md) plugin checks `copilot_files`, retrieves file information, and automatically copies them into the Agent's isolated workspace.
|
||||
|
||||
## 📦 Installation & Configuration
|
||||
|
||||
### 1. Installation
|
||||
|
||||
Import this plugin on the OpenWebUI **Functions** page.
|
||||
|
||||
### 2. Enable
|
||||
|
||||
Ensure this Filter is enabled globally or in chat settings.
|
||||
|
||||
### 3. Configuration (Valves)
|
||||
|
||||
Default settings work for most users unless you have specific needs:
|
||||
|
||||
| Parameter | Description | Default |
|
||||
| :--- | :--- | :--- |
|
||||
| **priority** | Execution priority of the filter. **Must be lower than OpenWebUI RAG priority**. | `0` |
|
||||
| **target_model_keyword** | Keyword to identify Copilot models. Only models containing this keyword will trigger file interception. | `copilot_sdk` |
|
||||
|
||||
## ⚠️ Important Notes
|
||||
|
||||
* **Must be used with Copilot SDK Pipe**: If you install this plugin without the main Pipe plugin, uploaded files will simply "disappear" (as no subsequent plugin will look for them in `copilot_files`).
|
||||
* **Gemini Filter Compatibility**: This plugin is fully compatible with the Gemini Multimodal Filter. As long as the priority is set correctly (This Plugin < Gemini Plugin), they can coexist without interference.
|
||||
* **Physical File Path**: Ensure the `OPENWEBUI_UPLOAD_PATH` is correctly set in the Pipe plugin Valves for the actual file transport to work.
|
||||
53
docs/plugins/filters/github-copilot-sdk-files-filter.zh.md
Normal file
53
docs/plugins/filters/github-copilot-sdk-files-filter.zh.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# GitHub Copilot SDK 文件过滤器 (v0.1.2)
|
||||
|
||||
这是一个专门为 [GitHub Copilot SDK Pipe](../pipes/github-copilot-sdk.zh.md) 设计的**伴侣过滤器插件**。
|
||||
|
||||
它的核心使命是:**保护用户上传的文件不被 OpenWebUI 核心系统“抢先处理”,确保 Copilot Agent 能够接收到原始文件并进行自主分析。**
|
||||
|
||||
## 🎯 为什么需要它?
|
||||
|
||||
在 OpenWebUI 的默认流程中,当你上传一个文件(如 PDF、Excel、Python 脚本)时,OpenWebUI 会自动启动 **RAG(检索增强生成)** 流程:解析文件、向量化、提取文本并注入到提示词中。
|
||||
|
||||
虽然这对普通模型很有用,但对于 **Copilot SDK Agent** 来说,这往往是干扰:
|
||||
|
||||
1. **Agent 需要原始文件**:Agent 可能需要运行 Python 代码读取 Excel,或者分析完整的代码结构,而不是被切碎的文本片段。
|
||||
2. **上下文污染**:RAG 注入的大量文本会消耗 Token,且容易让 Agent 混淆“文件在哪里”。
|
||||
3. **冲突**:如果你同时安装了其他多模态插件(如 Gemini Filter),它们可能会争夺文件的处理权。
|
||||
|
||||
**本插件就是为了解决这个问题而生的“保镖”。**
|
||||
|
||||
## 🚀 功能原理
|
||||
|
||||
当你在 OpenWebUI 中选择了一个 Copilot 模型(名称包含 `copilot_sdk`)并发送文件时:
|
||||
|
||||
1. **拦截 (Intercept)**:本插件会以极高的优先级(Priority 0)运行,先于 RAG 和其他过滤器。
|
||||
2. **搬运 (Relocate)**:它检测到模型是 Copilot,便将请求中的 `files`(文件列表)移动到一个安全的自定义字段 `copilot_files` 中。
|
||||
3. **隐身 (Hide)**:它清空原始的 `files` 字段。
|
||||
4. **状态更新**: 它会在 UI 中发出“已为 Copilot 管理 X 个文件 (已绕过 RAG)”的状态消息。
|
||||
5. **放行 (Pass)**:OpenWebUI 核心看到 `files` 为空,便**不会触发 RAG**。
|
||||
6. **交付 (Deliver)**:后续的 [Copilot SDK Pipe](../pipes/github-copilot-sdk.zh.md) 插件会检查 `copilot_files`,从中获取文件信息,并自动将其复制到 Agent 的独立工作区中。
|
||||
|
||||
## 📦 安装与配置
|
||||
|
||||
### 1. 安装
|
||||
|
||||
在 OpenWebUI 的 **Functions** 页面导入此插件。
|
||||
|
||||
### 2. 启用
|
||||
|
||||
确保在全局或对话设置中启用了此 Filter。
|
||||
|
||||
### 3. 配置 (Valves)
|
||||
|
||||
通常不需要修改默认配置,除非你有特殊需求:
|
||||
|
||||
| 参数 | 说明 | 默认值 |
|
||||
| :--- | :--- | :--- |
|
||||
| **priority** | 过滤器的执行优先级。**必须小于 OpenWebUI RAG 的优先级**。 | `0` |
|
||||
| **target_model_keyword** | 用于识别 Copilot 模型的关键词。只有包含此关键词的模型才会触发 file 拦截。 | `copilot_sdk` |
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
* **必须配合 Copilot SDK Pipe 使用**:如果你没有安装主 Pipe 插件,本插件将导致上传的文件“凭空消失”(因为没有后续插件去 `copilot_files` 里找它们)。
|
||||
* **Gemini Filter 兼容性**:本插件已完美兼容 Gemini 多模态过滤器。只要优先级设置正确(本插件 < Gemini 插件),它们可以共存互不干扰。
|
||||
* **物理文件路径**: 确保在 Pipe 插件的 Valves 中正确设置了 `OPENWEBUI_UPLOAD_PATH`,以便文件自动搬运功能正常工作。
|
||||
@@ -16,17 +16,17 @@ Filters act as middleware in the message pipeline:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-arrow-collapse-vertical:{ .lg .middle } **Async Context Compression**
|
||||
- :material-arrow-collapse-vertical:{ .lg .middle } **Async Context Compression**
|
||||
|
||||
---
|
||||
|
||||
Reduces token consumption in long conversations through intelligent summarization while maintaining coherence.
|
||||
|
||||
**Version:** 1.1.0
|
||||
**Version:** 1.2.2
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](async-context-compression.md)
|
||||
|
||||
- :material-text-box-plus:{ .lg .middle } **Context Enhancement**
|
||||
- :material-text-box-plus:{ .lg .middle } **Context Enhancement**
|
||||
|
||||
---
|
||||
|
||||
@@ -36,15 +36,55 @@ Filters act as middleware in the message pipeline:
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](context-enhancement.md)
|
||||
|
||||
- :material-google:{ .lg .middle } **Gemini Manifold Companion**
|
||||
- :material-folder-refresh:{ .lg .middle } **Folder Memory**
|
||||
|
||||
---
|
||||
|
||||
Companion filter for the Gemini Manifold pipe plugin.
|
||||
Automatically extracts consistent "Project Rules" from ongoing conversations within a folder and injects them back into the folder's system prompt.
|
||||
|
||||
**Version:** 1.7.0
|
||||
**Version:** 0.1.0
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](gemini-manifold-companion.md)
|
||||
[:octicons-arrow-right-24: Documentation](folder-memory.md)
|
||||
|
||||
- :material-format-paint:{ .lg .middle } **Markdown Normalizer**
|
||||
|
||||
---
|
||||
|
||||
Fixes common Markdown formatting issues in LLM outputs, including Mermaid syntax, code blocks, and LaTeX formulas.
|
||||
|
||||
**Version:** 1.2.4
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](markdown_normalizer.md)
|
||||
|
||||
- :material-merge:{ .lg .middle } **Multi-Model Context Merger**
|
||||
|
||||
---
|
||||
|
||||
Automatically merges context from multiple model responses in the previous turn, enabling collaborative answers.
|
||||
|
||||
**Version:** 0.1.0
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](multi-model-context-merger.md)
|
||||
|
||||
- :material-file-document-multiple:{ .lg .middle } **Web Gemini Multimodal Filter**
|
||||
|
||||
---
|
||||
|
||||
A powerful filter that provides multimodal capabilities (PDF, Office, Images, Audio, Video) to any model in OpenWebUI.
|
||||
|
||||
**Version:** 0.3.2
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](web-gemini-multimodel.md)
|
||||
|
||||
- :material-file-shield:{ .lg .middle } **Copilot SDK Files Filter**
|
||||
|
||||
---
|
||||
|
||||
A specialized filter to bypass OpenWebUI's default RAG for GitHub Copilot SDK models. It ensures the Agent receives raw files for autonomous analysis.
|
||||
|
||||
**Version:** 0.1.2
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](github-copilot-sdk-files-filter.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -16,17 +16,17 @@ Filter 充当消息管线中的中间件:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-arrow-collapse-vertical:{ .lg .middle } **Async Context Compression**
|
||||
- :material-arrow-collapse-vertical:{ .lg .middle } **Async Context Compression**
|
||||
|
||||
---
|
||||
|
||||
通过智能总结减少长对话的 token 消耗,同时保持连贯性。
|
||||
|
||||
**版本:** 1.1.0
|
||||
**版本:** 1.2.2
|
||||
|
||||
[:octicons-arrow-right-24: 查看文档](async-context-compression.md)
|
||||
|
||||
- :material-text-box-plus:{ .lg .middle } **Context Enhancement**
|
||||
- :material-text-box-plus:{ .lg .middle } **Context Enhancement**
|
||||
|
||||
---
|
||||
|
||||
@@ -36,15 +36,35 @@ Filter 充当消息管线中的中间件:
|
||||
|
||||
[:octicons-arrow-right-24: 查看文档](context-enhancement.md)
|
||||
|
||||
- :material-google:{ .lg .middle } **Gemini Manifold Companion**
|
||||
- :material-folder-refresh:{ .lg .middle } **Folder Memory**
|
||||
|
||||
---
|
||||
|
||||
Gemini Manifold Pipe 插件的伴随过滤器。
|
||||
自动从文件夹内的对话中提取一致性的“项目规则”,并将其回写到文件夹的系统提示词中。
|
||||
|
||||
**版本:** 1.7.0
|
||||
**版本:** 0.1.0
|
||||
|
||||
[:octicons-arrow-right-24: 查看文档](gemini-manifold-companion.md)
|
||||
[:octicons-arrow-right-24: 查看文档](folder-memory.zh.md)
|
||||
|
||||
- :material-format-paint:{ .lg .middle } **Markdown Normalizer**
|
||||
|
||||
---
|
||||
|
||||
修复 LLM 输出中常见的 Markdown 格式问题,包括 Mermaid 语法、代码块和 LaTeX 公式。
|
||||
|
||||
**版本:** 1.2.4
|
||||
|
||||
[:octicons-arrow-right-24: 查看文档](markdown_normalizer.zh.md)
|
||||
|
||||
- :material-file-shield:{ .lg .middle } **Copilot SDK 文件过滤器**
|
||||
|
||||
---
|
||||
|
||||
专门用于绕过 OpenWebUI 默认 RAG 机制的过滤器,针对 GitHub Copilot SDK 模型。确保 Agent 能够接收到原始文件进行自主分析。
|
||||
|
||||
**版本:** 0.1.2
|
||||
|
||||
[:octicons-arrow-right-24: 查看文档](github-copilot-sdk-files-filter.zh.md)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
98
docs/plugins/filters/markdown_normalizer.md
Normal file
98
docs/plugins/filters/markdown_normalizer.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Markdown Normalizer Filter
|
||||
|
||||
A content normalizer filter for Open WebUI that fixes common Markdown formatting issues in LLM outputs. It ensures that code blocks, LaTeX formulas, Mermaid diagrams, and other Markdown elements are rendered correctly.
|
||||
|
||||
## Features
|
||||
|
||||
* **Details Tag Normalization**: Ensures proper spacing for `<details>` tags (used for thought chains). Adds a blank line after `</details>` and ensures a newline after self-closing `<details />` tags to prevent rendering issues.
|
||||
* **Emphasis Spacing Fix**: Fixes extra spaces inside emphasis markers (e.g., `** text **` -> `**text**`) which can cause rendering failures. Includes safeguards to protect math expressions (e.g., `2 * 3 * 4`) and list variables.
|
||||
* **Mermaid Syntax Fix**: Automatically fixes common Mermaid syntax errors, such as unquoted node labels (including multi-line labels and citations) and unclosed subgraphs. **New in v1.1.2**: Comprehensive protection for edge labels (text on connecting lines) across all link types (solid, dotted, thick).
|
||||
* **Frontend Console Debugging**: Supports printing structured debug logs directly to the browser console (F12) for easier troubleshooting.
|
||||
* **Code Block Formatting**: Fixes broken code block prefixes, suffixes, and indentation.
|
||||
* **LaTeX Normalization**: Standardizes LaTeX formula delimiters (`\[` -> `$$`, `\(` -> `$`).
|
||||
* **Thought Tag Normalization**: Unifies thought tags (`<think>`, `<thinking>` -> `<thought>`).
|
||||
* **Escape Character Fix**: Cleans up excessive escape characters (`\\n`, `\\t`).
|
||||
* **List Formatting**: Ensures proper newlines in list items.
|
||||
* **Heading Fix**: Adds missing spaces in headings (`#Heading` -> `# Heading`).
|
||||
* **Table Fix**: Adds missing closing pipes in tables.
|
||||
* **XML Cleanup**: Removes leftover XML artifacts.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Install the plugin in Open WebUI.
|
||||
2. Enable the filter globally or for specific models.
|
||||
3. Configure the enabled fixes in the **Valves** settings.
|
||||
4. (Optional) **Show Debug Log** is enabled by default in Valves. This prints structured logs to the browser console (F12).
|
||||
> [!WARNING]
|
||||
> As this is an initial version, some "negative fixes" might occur (e.g., breaking valid Markdown). If you encounter issues, please check the console logs, copy the "Original" vs "Normalized" content, and submit an issue.
|
||||
|
||||
## Configuration (Valves)
|
||||
|
||||
* `priority`: Filter priority (default: 50).
|
||||
* `enable_escape_fix`: Fix excessive escape characters.
|
||||
* `enable_thought_tag_fix`: Normalize thought tags.
|
||||
* `enable_details_tag_fix`: Normalize details tags (default: True).
|
||||
* `enable_code_block_fix`: Fix code block formatting.
|
||||
* `enable_latex_fix`: Normalize LaTeX formulas.
|
||||
* `enable_list_fix`: Fix list item newlines (Experimental).
|
||||
* `enable_unclosed_block_fix`: Auto-close unclosed code blocks.
|
||||
* `enable_fullwidth_symbol_fix`: Fix full-width symbols in code blocks.
|
||||
* `enable_mermaid_fix`: Fix Mermaid syntax errors.
|
||||
* `enable_heading_fix`: Fix missing space in headings.
|
||||
* `enable_table_fix`: Fix missing closing pipe in tables.
|
||||
* `enable_xml_tag_cleanup`: Cleanup leftover XML tags.
|
||||
* `enable_emphasis_spacing_fix`: Fix extra spaces in emphasis (default: True).
|
||||
* `show_status`: Show status notification when fixes are applied.
|
||||
* `show_debug_log`: Print debug logs to browser console.
|
||||
|
||||
## Troubleshooting ❓
|
||||
|
||||
* **Submit an Issue**: If you encounter any problems, please submit an issue on GitHub: [Awesome OpenWebUI Issues](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.2.4
|
||||
|
||||
* **Documentation Updates**: Synchronized version numbers across all documentation and code files.
|
||||
|
||||
### v1.2.3
|
||||
|
||||
* **List Marker Protection Enhancement**: Fixed a bug where list markers (`*`) followed by plain text and emphasis were having their spaces incorrectly stripped (e.g., `* U16 forward` became `*U16 forward`).
|
||||
* **Placeholder Support**: Confirmed that 4 or more underscores (e.g., `____`) are correctly treated as placeholders and not modified by the emphasis fix.
|
||||
|
||||
### v1.2.2
|
||||
|
||||
* **Code Block Indentation Fix**: Fixed an issue where code blocks nested inside lists were having their indentation incorrectly stripped. Now preserves proper indentation for nested code blocks.
|
||||
* **Underscore Emphasis Support**: Extended emphasis spacing fix to support `__` (double underscore for bold) and `___` (triple underscore for bold+italic) syntax.
|
||||
* **List Marker Protection**: Fixed a bug where list markers (`*`) followed by emphasis markers (`**`) were incorrectly merged (e.g., `* **Yes**` became `***Yes**`). Added safeguard to prevent this.
|
||||
* **Test Suite**: Added comprehensive pytest test suite with 56 test cases covering all major features.
|
||||
|
||||
### v1.2.1
|
||||
|
||||
* **Emphasis Spacing Fix**: Added a new fix for extra spaces inside emphasis markers (e.g., `** text **` -> `**text**`).
|
||||
* Uses a recursive approach to handle nested emphasis (e.g., `**bold _italic _**`).
|
||||
* Includes safeguards to prevent modifying math expressions (e.g., `2 * 3 * 4`) or list variables.
|
||||
* Controlled by the `enable_emphasis_spacing_fix` valve (default: True).
|
||||
|
||||
### v1.2.0
|
||||
|
||||
* **Details Tag Support**: Added normalization for `<details>` tags.
|
||||
* Ensures a blank line is added after `</details>` closing tags to separate thought content from the main response.
|
||||
* Ensures a newline is added after self-closing `<details ... />` tags to prevent them from interfering with subsequent Markdown headings (e.g., fixing `<details/>#Heading`).
|
||||
* Includes safeguard to prevent modification of `<details>` tags inside code blocks.
|
||||
|
||||
### v1.1.2
|
||||
|
||||
* **Mermaid Edge Label Protection**: Implemented comprehensive protection for edge labels (text on connecting lines) to prevent them from being incorrectly modified. Now supports all Mermaid link types including solid (`--`), dotted (`-.`), and thick (`==`) lines with or without arrows.
|
||||
* **Bug Fixes**: Fixed an issue where lines without arrows (e.g., `A -- text --- B`) were not correctly protected.
|
||||
|
||||
### v1.1.0
|
||||
|
||||
* **Mermaid Fix Refinement**: Improved regex to handle nested parentheses in node labels (e.g., `ID("Label (text)")`) and avoided matching connection labels.
|
||||
* **HTML Safeguard Optimization**: Refined `_contains_html` to allow common tags like `<br/>`, `<b>`, `<i>`, etc., ensuring Mermaid diagrams with these tags are still normalized.
|
||||
* **Full-width Symbol Cleanup**: Fixed duplicate keys and incorrect quote mapping in `FULLWIDTH_MAP`.
|
||||
* **Bug Fixes**: Fixed missing `Dict` import in Python files.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
98
docs/plugins/filters/markdown_normalizer.zh.md
Normal file
98
docs/plugins/filters/markdown_normalizer.zh.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Markdown 格式化过滤器 (Markdown Normalizer)
|
||||
|
||||
这是一个用于 Open WebUI 的内容格式化过滤器,旨在修复 LLM 输出中常见的 Markdown 格式问题。它能确保代码块、LaTeX 公式、Mermaid 图表和其他 Markdown 元素被正确渲染。
|
||||
|
||||
## 功能特性
|
||||
|
||||
* **Details 标签规范化**: 确保 `<details>` 标签(常用于思维链)有正确的间距。在 `</details>` 后添加空行,并在自闭合 `<details />` 标签后添加换行,防止渲染问题。
|
||||
* **强调空格修复**: 修复强调标记内部的多余空格(例如 `** 文本 **` -> `**文本**`),这会导致 Markdown 渲染失败。包含保护机制,防止误修改数学表达式(如 `2 * 3 * 4`)或列表变量。
|
||||
* **Mermaid 语法修复**: 自动修复常见的 Mermaid 语法错误,如未加引号的节点标签(支持多行标签和引用标记)和未闭合的子图 (Subgraph)。**v1.1.2 新增**: 全面保护各种类型的连线标签(实线、虚线、粗线),防止被误修改。
|
||||
* **前端控制台调试**: 支持将结构化的调试日志直接打印到浏览器控制台 (F12),方便排查问题。
|
||||
* **代码块格式化**: 修复破损的代码块前缀、后缀和缩进问题。
|
||||
* **LaTeX 规范化**: 标准化 LaTeX 公式定界符 (`\[` -> `$$`, `\(` -> `$`)。
|
||||
* **思维标签规范化**: 统一思维链标签 (`<think>`, `<thinking>` -> `<thought>`)。
|
||||
* **转义字符修复**: 清理过度的转义字符 (`\\n`, `\\t`)。
|
||||
* **列表格式化**: 确保列表项有正确的换行。
|
||||
* **标题修复**: 修复标题中缺失的空格 (`#标题` -> `# 标题`)。
|
||||
* **表格修复**: 修复表格中缺失的闭合管道符。
|
||||
* **XML 清理**: 移除残留的 XML 标签。
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 在 Open WebUI 中安装此插件。
|
||||
2. 全局启用或为特定模型启用此过滤器。
|
||||
3. 在 **Valves** 设置中配置需要启用的修复项。
|
||||
4. (可选) **显示调试日志 (Show Debug Log)** 在 Valves 中默认开启。这会将结构化的日志打印到浏览器控制台 (F12)。
|
||||
> [!WARNING]
|
||||
> 由于这是初版,可能会出现“负向修复”的情况(例如破坏了原本正确的格式)。如果您遇到问题,请务必查看控制台日志,复制“原始 (Original)”与“规范化 (Normalized)”的内容对比,并提交 Issue 反馈。
|
||||
|
||||
## 配置项 (Valves)
|
||||
|
||||
* `priority`: 过滤器优先级 (默认: 50)。
|
||||
* `enable_escape_fix`: 修复过度的转义字符。
|
||||
* `enable_thought_tag_fix`: 规范化思维标签。
|
||||
* `enable_details_tag_fix`: 规范化 Details 标签 (默认: True)。
|
||||
* `enable_code_block_fix`: 修复代码块格式。
|
||||
* `enable_latex_fix`: 规范化 LaTeX 公式。
|
||||
* `enable_list_fix`: 修复列表项换行 (实验性)。
|
||||
* `enable_unclosed_block_fix`: 自动闭合未闭合的代码块。
|
||||
* `enable_fullwidth_symbol_fix`: 修复代码块中的全角符号。
|
||||
* `enable_mermaid_fix`: 修复 Mermaid 语法错误。
|
||||
* `enable_heading_fix`: 修复标题中缺失的空格。
|
||||
* `enable_table_fix`: 修复表格中缺失的闭合管道符。
|
||||
* `enable_xml_tag_cleanup`: 清理残留的 XML 标签。
|
||||
* `enable_emphasis_spacing_fix`: 修复强调语法中的多余空格 (默认: True)。
|
||||
* `show_status`: 应用修复时显示状态通知。
|
||||
* `show_debug_log`: 在浏览器控制台打印调试日志。
|
||||
|
||||
## 故障排除 (Troubleshooting) ❓
|
||||
|
||||
* **提交 Issue**: 如果遇到任何问题,请在 GitHub 上提交 Issue:[Awesome OpenWebUI Issues](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
|
||||
## 更新日志
|
||||
|
||||
### v1.2.4
|
||||
|
||||
* **文档更新**: 同步了所有文档和代码文件的版本号。
|
||||
|
||||
### v1.2.3
|
||||
|
||||
* **列表标记保护增强**: 修复了列表标记 (`*`) 后跟普通文本和强调标记时,空格被错误剥离的问题(例如 `* U16 前锋` 变成 `*U16 前锋`)。
|
||||
* **占位符支持**: 确认 4 个或更多下划线(如 `____`)会被正确视为占位符,不会被强调修复逻辑修改。
|
||||
|
||||
### v1.2.2
|
||||
|
||||
* **代码块缩进修复**: 修复了列表中嵌套代码块的缩进被错误剥离的问题。现在会正确保留嵌套代码块的缩进。
|
||||
* **下划线强调语法支持**: 扩展强调空格修复以支持 `__` (双下划线加粗) 和 `___` (三下划线加粗斜体) 语法。
|
||||
* **列表标记保护**: 修复了列表标记 (`*`) 后跟强调标记 (`**`) 被错误合并的 Bug(例如 `* **是**` 变成 `***是**`)。添加了保护逻辑防止此问题。
|
||||
* **测试套件**: 新增完整的 pytest 测试套件,包含 56 个测试用例,覆盖所有主要功能。
|
||||
|
||||
### v1.2.1
|
||||
|
||||
* **强调空格修复**: 新增了对强调标记内部多余空格的修复(例如 `** 文本 **` -> `**文本**`)。
|
||||
* 采用递归方法处理嵌套强调(例如 `**加粗 _斜体 _**`)。
|
||||
* 包含保护机制,防止误修改数学表达式(如 `2 * 3 * 4`)或列表变量。
|
||||
* 通过 `enable_emphasis_spacing_fix` 开关控制(默认:开启)。
|
||||
|
||||
### v1.2.0
|
||||
|
||||
* **Details 标签支持**: 新增了对 `<details>` 标签的规范化支持。
|
||||
* 确保在 `</details>` 闭合标签后添加空行,将思维内容与正文分隔开。
|
||||
* 确保在自闭合 `<details ... />` 标签后添加换行,防止其干扰后续的 Markdown 标题(例如修复 `<details/>#标题`)。
|
||||
* 包含保护机制,防止修改代码块内部的 `<details>` 标签。
|
||||
|
||||
### v1.1.2
|
||||
|
||||
* **Mermaid 连线标签保护**: 实现了全面的连线标签保护机制,防止连接线上的文字被误修改。现在支持所有 Mermaid 连线类型,包括实线 (`--`)、虚线 (`-.`) 和粗线 (`==`),无论是否带有箭头。
|
||||
* **Bug 修复**: 修复了无箭头连线(如 `A -- text --- B`)未被正确保护的问题。
|
||||
|
||||
### v1.1.0
|
||||
|
||||
* **Mermaid 修复优化**: 改进了正则表达式以处理节点标签中的嵌套括号(如 `ID("标签 (文本)")`),并避免误匹配连接线上的文字。
|
||||
* **HTML 保护机制优化**: 优化了 `_contains_html` 检测,允许 `<br/>`, `<b>`, `<i>` 等常见标签,确保包含这些标签的 Mermaid 图表能被正常规范化。
|
||||
* **全角符号清理**: 修复了 `FULLWIDTH_MAP` 中的重复键名和错误的引号映射。
|
||||
* **Bug 修复**: 修复了 Python 文件中缺失的 `Dict` 类型导入。
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT
|
||||
35
docs/plugins/filters/multi-model-context-merger.md
Normal file
35
docs/plugins/filters/multi-model-context-merger.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Multi-Model Context Merger
|
||||
|
||||
<span class="category-badge filter">Filter</span>
|
||||
<span class="version-badge">v0.1.0</span>
|
||||
|
||||
Automatically merges context from multiple model responses in the previous turn, enabling collaborative answers.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This filter detects when multiple models have responded in the previous turn (e.g., using "Arena" mode or multiple models selected). It consolidates these responses and injects them as context for the current turn, allowing the next model to see what others have said.
|
||||
|
||||
## Features
|
||||
|
||||
- :material-merge: **Auto-Merge**: Consolidates responses from multiple models into a single context block.
|
||||
- :material-format-list-group: **Structured Injection**: Uses XML-like tags (`<response>`) to separate different model outputs.
|
||||
- :material-robot-confused: **Collaboration**: Enables models to build upon or critique each other's answers.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the plugin file: [`multi_model_context_merger.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/filters)
|
||||
2. Upload to OpenWebUI: **Admin Panel** → **Settings** → **Functions**
|
||||
3. Enable the filter.
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
1. Select **multiple models** in the chat (or use Arena mode).
|
||||
2. Ask a question. All models will respond.
|
||||
3. Ask a follow-up question.
|
||||
4. The filter will inject the previous responses from ALL models into the context of the current model(s).
|
||||
35
docs/plugins/filters/multi-model-context-merger.zh.md
Normal file
35
docs/plugins/filters/multi-model-context-merger.zh.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# 多模型上下文合并 (Multi-Model Context Merger)
|
||||
|
||||
<span class="category-badge filter">Filter</span>
|
||||
<span class="version-badge">v0.1.0</span>
|
||||
|
||||
自动合并上一轮中多个模型的回答上下文,实现协作问答。
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
此过滤器检测上一轮是否由多个模型回复(例如使用“竞技场”模式或选择了多个模型)。它将这些回复合并并作为上下文注入到当前轮次,使下一个模型能够看到其他模型之前所说的内容。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- :material-merge: **自动合并**: 将多个模型的回复合并为单个上下文块。
|
||||
- :material-format-list-group: **结构化注入**: 使用类似 XML 的标签 (`<response>`) 分隔不同模型的输出。
|
||||
- :material-robot-confused: **协作**: 允许模型基于彼此的回答进行构建或评论。
|
||||
|
||||
---
|
||||
|
||||
## 安装
|
||||
|
||||
1. 下载插件文件: [`multi_model_context_merger.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/filters)
|
||||
2. 上传到 OpenWebUI: **管理员面板** → **设置** → **函数**
|
||||
3. 启用过滤器。
|
||||
|
||||
---
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 在聊天中选择 **多个模型** (或使用竞技场模式)。
|
||||
2. 提问。所有模型都会回答。
|
||||
3. 提出后续问题。
|
||||
4. 过滤器会将所有模型之前的回答注入到当前模型的上下文中。
|
||||
51
docs/plugins/filters/web-gemini-multimodel.md
Normal file
51
docs/plugins/filters/web-gemini-multimodel.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Web Gemini Multimodal Filter
|
||||
|
||||
<span class="category-badge filter">Filter</span>
|
||||
<span class="version-badge">v0.3.2</span>
|
||||
|
||||
A powerful filter that provides multimodal capabilities (PDF, Office, Images, Audio, Video) to any model in OpenWebUI.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This plugin enables multimodal processing for any model by leveraging Gemini as an analyzer. It supports direct file processing for Gemini models and "Analyzer Mode" for other models (like DeepSeek, Llama), where Gemini analyzes the file and injects the result as context.
|
||||
|
||||
## Features
|
||||
|
||||
- :material-file-document-multiple: **Multimodal Support**: Process PDF, Word, Excel, PowerPoint, EPUB, MP3, MP4, and Images.
|
||||
- :material-router-network: **Smart Routing**:
|
||||
- **Direct Mode**: Files are passed directly to Gemini models.
|
||||
- **Analyzer Mode**: Files are analyzed by Gemini, and results are injected into the context for other models.
|
||||
- :material-history: **Persistent Context**: Maintains session history across multiple turns using OpenWebUI Chat ID.
|
||||
- :material-database-check: **Deduplication**: Automatically tracks analyzed file hashes to prevent redundant processing.
|
||||
- :material-subtitles: **Subtitle Enhancement**: Specialized mode for generating high-quality SRT subtitles from video/audio.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the plugin file: [`web_gemini_multimodel.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/filters/web_gemini_multimodel_filter)
|
||||
2. Upload to OpenWebUI: **Admin Panel** → **Settings** → **Functions**
|
||||
3. Configure the Gemini Adapter URL and other settings.
|
||||
4. Enable the filter globally or per chat.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `gemini_adapter_url` | string | `http://...` | URL of the Gemini Adapter service |
|
||||
| `target_model_keyword` | string | `"webgemini"` | Keyword to identify Gemini models |
|
||||
| `mode` | string | `"auto"` | `auto`, `direct`, or `analyzer` |
|
||||
| `analyzer_base_model_id` | string | `"gemini-3.0-pro"` | Model used for document analysis |
|
||||
| `subtitle_keywords` | string | `"字幕,srt"` | Keywords to trigger subtitle flow |
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
1. **Upload a file** (PDF, Image, Video, etc.) in the chat.
|
||||
2. **Ask a question** about the file.
|
||||
3. The plugin will automatically process the file and provide context to your selected model.
|
||||
51
docs/plugins/filters/web-gemini-multimodel.zh.md
Normal file
51
docs/plugins/filters/web-gemini-multimodel.zh.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Web Gemini 多模态过滤器
|
||||
|
||||
<span class="category-badge filter">Filter</span>
|
||||
<span class="version-badge">v0.3.2</span>
|
||||
|
||||
一个强大的过滤器,为 OpenWebUI 中的任何模型提供多模态能力:PDF、Office、图片、音频、视频等。
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
此插件利用 Gemini 作为分析器,为任何模型提供多模态处理能力。它支持 Gemini 模型的直接文件处理,以及其他模型(如 DeepSeek, Llama)的“分析器模式”,即由 Gemini 分析文件并将结果注入上下文。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- :material-file-document-multiple: **多模态支持**: 处理 PDF, Word, Excel, PowerPoint, EPUB, MP3, MP4 和图片。
|
||||
- :material-router-network: **智能路由**:
|
||||
- **直连模式 (Direct Mode)**: 对于 Gemini 模型,文件直接传递(原生多模态)。
|
||||
- **分析器模式 (Analyzer Mode)**: 对于非 Gemini 模型,文件由 Gemini 分析,结果注入为上下文。
|
||||
- :material-history: **持久上下文**: 利用 OpenWebUI 的 Chat ID 跨多轮对话维护会话历史。
|
||||
- :material-database-check: **数据库去重**: 自动记录已分析文件的哈希值,防止重复上传和分析。
|
||||
- :material-subtitles: **字幕增强**: 针对视频/音频上传的专用模式,生成高质量 SRT 字幕。
|
||||
|
||||
---
|
||||
|
||||
## 安装
|
||||
|
||||
1. 下载插件文件: [`web_gemini_multimodel.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/filters/web_gemini_multimodel_filter)
|
||||
2. 上传到 OpenWebUI: **管理员面板** → **设置** → **函数**
|
||||
3. 配置 Gemini Adapter URL 和其他设置。
|
||||
4. 启用过滤器。
|
||||
|
||||
---
|
||||
|
||||
## 配置
|
||||
|
||||
| 选项 | 类型 | 默认值 | 描述 |
|
||||
|------|------|--------|------|
|
||||
| `gemini_adapter_url` | string | `http://...` | Gemini Adapter 服务的 URL |
|
||||
| `target_model_keyword` | string | `"webgemini"` | 识别 Gemini 模型的关键字 |
|
||||
| `mode` | string | `"auto"` | `auto` (自动), `direct` (直连), 或 `analyzer` (分析器) |
|
||||
| `analyzer_base_model_id` | string | `"gemini-3.0-pro"` | 用于文档分析的模型 |
|
||||
| `subtitle_keywords` | string | `"字幕,srt"` | 触发字幕流程的关键字 |
|
||||
|
||||
---
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 在聊天中 **上传文件** (PDF, 图片, 视频等)。
|
||||
2. 关于文件 **提问**。
|
||||
3. 插件会自动处理文件并为所选模型提供上下文。
|
||||
@@ -48,16 +48,15 @@ OpenWebUI supports four types of plugins, each serving a different purpose:
|
||||
|
||||
| Plugin | Type | Description | Version |
|
||||
|--------|------|-------------|---------|
|
||||
| [Smart Mind Map](actions/smart-mind-map.md) | Action | Generate interactive mind maps from text | 0.8.0 |
|
||||
| [Smart Infographic](actions/smart-infographic.md) | Action | Transform text into professional infographics | 1.0.0 |
|
||||
| [Knowledge Card](actions/knowledge-card.md) | Action | Create beautiful learning flashcards | 0.2.0 |
|
||||
| [Export to Excel](actions/export-to-excel.md) | Action | Export chat history to Excel files | 1.0.0 |
|
||||
| [Export to Word](actions/export-to-word.md) | Action | Export chat content to Word (.docx) with formatting | 0.1.0 |
|
||||
| [Summary](actions/summary.md) | Action | Text summarization tool | 1.0.0 |
|
||||
| [Async Context Compression](filters/async-context-compression.md) | Filter | Intelligent context compression | 1.0.0 |
|
||||
| [Context Enhancement](filters/context-enhancement.md) | Filter | Enhance chat context | 1.0.0 |
|
||||
| [Gemini Manifold Companion](filters/gemini-manifold-companion.md) | Filter | Companion for Gemini Manifold | 1.0.0 |
|
||||
| [Gemini Manifold](pipes/gemini-manifold.md) | Pipe | Gemini model integration | 1.0.0 |
|
||||
| [Smart Mind Map](actions/smart-mind-map.md) | Action | Generate interactive mind maps from text | 0.9.2 |
|
||||
| [Smart Infographic](actions/smart-infographic.md) | Action | Transform text into professional infographics | 1.4.9 |
|
||||
| [Flash Card](actions/flash-card.md) | Action | Create beautiful learning flashcards | 0.2.4 |
|
||||
| [Export to Excel](actions/export-to-excel.md) | Action | Export chat history to Excel files | 0.3.7 |
|
||||
| [Export to Word](actions/export-to-word.md) | Action | Export chat content to Word (.docx) with formatting | 0.4.3 |
|
||||
| [Async Context Compression](filters/async-context-compression.md) | Filter | Intelligent context compression | 1.1.3 |
|
||||
| [Context Enhancement](filters/context-enhancement.md) | Filter | Enhance chat context | 0.3.0 |
|
||||
| [Multi-Model Context Merger](filters/multi-model-context-merger.md) | Filter | Merge context from multiple models | 0.1.0 |
|
||||
| [Web Gemini Multimodal Filter](filters/web-gemini-multimodel.md) | Filter | Multimodal capabilities for any model | 0.3.2 |
|
||||
| [MoE Prompt Refiner](pipelines/moe-prompt-refiner.md) | Pipeline | Multi-model prompt refinement | 1.0.0 |
|
||||
|
||||
---
|
||||
|
||||
@@ -48,16 +48,15 @@ OpenWebUI 支持四种类型的插件,每种都有不同的用途:
|
||||
|
||||
| 插件 | 类型 | 描述 | 版本 |
|
||||
|--------|------|-------------|---------|
|
||||
| [Smart Mind Map(智能思维导图)](actions/smart-mind-map.md) | Action | 从文本生成交互式思维导图 | 0.8.0 |
|
||||
| [Smart Infographic(智能信息图)](actions/smart-infographic.md) | Action | 将文本转成专业信息图 | 1.0.0 |
|
||||
| [Knowledge Card(知识卡片)](actions/knowledge-card.md) | Action | 生成精美学习卡片 | 0.2.0 |
|
||||
| [Export to Excel(导出到 Excel)](actions/export-to-excel.md) | Action | 导出聊天记录为 Excel | 1.0.0 |
|
||||
| [Export to Word(导出为 Word)](actions/export-to-word.md) | Action | 将聊天内容导出为 Word (.docx) 并保留格式 | 0.1.0 |
|
||||
| [Summary(摘要)](actions/summary.md) | Action | 文本摘要工具 | 1.0.0 |
|
||||
| [Async Context Compression(异步上下文压缩)](filters/async-context-compression.md) | Filter | 智能上下文压缩 | 1.0.0 |
|
||||
| [Context Enhancement(上下文增强)](filters/context-enhancement.md) | Filter | 提升对话上下文 | 1.0.0 |
|
||||
| [Gemini Manifold Companion](filters/gemini-manifold-companion.md) | Filter | Gemini Manifold 伴侣 | 1.0.0 |
|
||||
| [Gemini Manifold](pipes/gemini-manifold.md) | Pipe | Gemini 模型集成 | 1.0.0 |
|
||||
| [Smart Mind Map(智能思维导图)](actions/smart-mind-map.md) | Action | 从文本生成交互式思维导图 | 0.9.2 |
|
||||
| [Smart Infographic(智能信息图)](actions/smart-infographic.md) | Action | 将文本转成专业信息图 | 1.4.9 |
|
||||
| [Flash Card(闪记卡)](actions/flash-card.md) | Action | 生成精美学习卡片 | 0.2.4 |
|
||||
| [Export to Excel(导出到 Excel)](actions/export-to-excel.md) | Action | 导出聊天记录为 Excel | 0.3.7 |
|
||||
| [Export to Word(导出为 Word)](actions/export-to-word.md) | Action | 将聊天内容导出为 Word (.docx) 并保留格式 | 0.4.3 |
|
||||
| [Async Context Compression(异步上下文压缩)](filters/async-context-compression.md) | Filter | 智能上下文压缩 | 1.1.3 |
|
||||
| [Context Enhancement(上下文增强)](filters/context-enhancement.md) | Filter | 提升对话上下文 | 0.3.0 |
|
||||
| [Multi-Model Context Merger(多模型上下文合并)](filters/multi-model-context-merger.md) | Filter | 合并多个模型的上下文 | 0.1.0 |
|
||||
| [Web Gemini Multimodal Filter(Web Gemini 多模态过滤器)](filters/web-gemini-multimodel.md) | Filter | 为任何模型提供多模态能力 | 0.3.2 |
|
||||
| [MoE Prompt Refiner](pipelines/moe-prompt-refiner.md) | Pipeline | 多模型提示词优化 | 1.0.0 |
|
||||
|
||||
---
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
# Gemini Manifold
|
||||
|
||||
<span class="category-badge pipe">Pipe</span>
|
||||
<span class="version-badge">v1.0.0</span>
|
||||
|
||||
Integration pipeline for Google's Gemini models with full streaming support.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Gemini Manifold pipe provides seamless integration with Google's Gemini AI models. It exposes Gemini models as selectable options in OpenWebUI, allowing you to use them just like any other model.
|
||||
|
||||
## Features
|
||||
|
||||
- :material-google: **Full Gemini Support**: Access all Gemini model variants
|
||||
- :material-stream: **Streaming**: Real-time response streaming
|
||||
- :material-image: **Multimodal**: Support for images and text
|
||||
- :material-shield: **Error Handling**: Robust error management
|
||||
- :material-tune: **Configurable**: Customize model parameters
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the plugin file: [`gemini_manifold.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/pipes/gemini_mainfold)
|
||||
2. Upload to OpenWebUI: **Admin Panel** → **Settings** → **Functions**
|
||||
3. Configure your Gemini API key
|
||||
4. Select Gemini models from the model dropdown
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
| Option | Type | Required | Description |
|
||||
|--------|------|----------|-------------|
|
||||
| `GEMINI_API_KEY` | string | Yes | Your Google AI Studio API key |
|
||||
| `DEFAULT_MODEL` | string | No | Default Gemini model to use |
|
||||
| `TEMPERATURE` | float | No | Response temperature (0-1) |
|
||||
| `MAX_TOKENS` | integer | No | Maximum response tokens |
|
||||
|
||||
---
|
||||
|
||||
## Available Models
|
||||
|
||||
When configured, the following models become available:
|
||||
|
||||
- `gemini-pro` - Text-only model
|
||||
- `gemini-pro-vision` - Multimodal model
|
||||
- `gemini-1.5-pro` - Latest Pro model
|
||||
- `gemini-1.5-flash` - Fast response model
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
1. After installation, go to any chat
|
||||
2. Open the model selector dropdown
|
||||
3. Look for models prefixed with your pipe name
|
||||
4. Select a Gemini model
|
||||
5. Start chatting!
|
||||
|
||||
---
|
||||
|
||||
## Getting an API Key
|
||||
|
||||
1. Visit [Google AI Studio](https://makersuite.google.com/app/apikey)
|
||||
2. Create a new API key
|
||||
3. Copy the key and paste it in the plugin configuration
|
||||
|
||||
!!! warning "API Key Security"
|
||||
Keep your API key secure. Never share it publicly or commit it to version control.
|
||||
|
||||
---
|
||||
|
||||
## Companion Filter
|
||||
|
||||
For enhanced functionality, consider installing the [Gemini Manifold Companion](../filters/gemini-manifold-companion.md) filter.
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
!!! note "Prerequisites"
|
||||
- OpenWebUI v0.3.0 or later
|
||||
- Valid Gemini API key
|
||||
- Internet access to Google AI APIs
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
??? question "Models not appearing?"
|
||||
Ensure your API key is correctly configured and the plugin is enabled.
|
||||
|
||||
??? question "API errors?"
|
||||
Check your API key validity and quota limits in Google AI Studio.
|
||||
|
||||
??? question "Slow responses?"
|
||||
Consider using `gemini-1.5-flash` for faster response times.
|
||||
|
||||
---
|
||||
|
||||
## Source Code
|
||||
|
||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/pipes/gemini_mainfold){ .md-button }
|
||||
@@ -1,106 +0,0 @@
|
||||
# Gemini Manifold
|
||||
|
||||
<span class="category-badge pipe">Pipe</span>
|
||||
<span class="version-badge">v1.0.0</span>
|
||||
|
||||
面向 Google Gemini 模型的集成流水线,支持完整流式返回。
|
||||
|
||||
---
|
||||
|
||||
## 概览
|
||||
|
||||
Gemini Manifold Pipe 提供与 Google Gemini AI 模型的无缝集成。它会将 Gemini 模型作为可选项暴露在 OpenWebUI 中,你可以像使用其他模型一样使用它们。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- :material-google: **完整 Gemini 支持**:可使用所有 Gemini 模型变体
|
||||
- :material-stream: **流式输出**:实时流式响应
|
||||
- :material-image: **多模态**:支持图像与文本
|
||||
- :material-shield: **错误处理**:健壮的错误管理
|
||||
- :material-tune: **可配置**:可自定义模型参数
|
||||
|
||||
---
|
||||
|
||||
## 安装
|
||||
|
||||
1. 下载插件文件:[`gemini_manifold.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/pipes/gemini_mainfold)
|
||||
2. 上传到 OpenWebUI:**Admin Panel** → **Settings** → **Functions**
|
||||
3. 配置你的 Gemini API Key
|
||||
4. 在模型下拉中选择 Gemini 模型
|
||||
|
||||
---
|
||||
|
||||
## 配置
|
||||
|
||||
| 选项 | 类型 | 是否必填 | 说明 |
|
||||
|--------|------|----------|-------------|
|
||||
| `GEMINI_API_KEY` | string | 是 | 你的 Google AI Studio API Key |
|
||||
| `DEFAULT_MODEL` | string | 否 | 默认使用的 Gemini 模型 |
|
||||
| `TEMPERATURE` | float | 否 | 输出温度(0-1) |
|
||||
| `MAX_TOKENS` | integer | 否 | 最大回复 token 数 |
|
||||
|
||||
---
|
||||
|
||||
## 可用模型
|
||||
|
||||
配置完成后,你可以选择以下模型:
|
||||
|
||||
- `gemini-pro` —— 纯文本模型
|
||||
- `gemini-pro-vision` —— 多模态模型
|
||||
- `gemini-1.5-pro` —— 最新 Pro 模型
|
||||
- `gemini-1.5-flash` —— 快速响应模型
|
||||
|
||||
---
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 安装后进入任意对话
|
||||
2. 打开模型选择下拉
|
||||
3. 查找以 Pipe 名称前缀的模型
|
||||
4. 选择 Gemini 模型
|
||||
5. 开始聊天!
|
||||
|
||||
---
|
||||
|
||||
## 获取 API Key
|
||||
|
||||
1. 访问 [Google AI Studio](https://makersuite.google.com/app/apikey)
|
||||
2. 创建新的 API Key
|
||||
3. 复制并粘贴到插件配置中
|
||||
|
||||
!!! warning "API Key 安全"
|
||||
请妥善保管你的 API Key,不要公开或提交到版本库。
|
||||
|
||||
---
|
||||
|
||||
## 伴随过滤器
|
||||
|
||||
如需增强功能,可安装 [Gemini Manifold Companion](../filters/gemini-manifold-companion.md) 过滤器。
|
||||
|
||||
---
|
||||
|
||||
## 运行要求
|
||||
|
||||
!!! note "前置条件"
|
||||
- OpenWebUI v0.3.0 及以上
|
||||
- 有效的 Gemini API Key
|
||||
- 可访问 Google AI API 的网络
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
??? question "模型没有出现?"
|
||||
请确认 API Key 配置正确且插件已启用。
|
||||
|
||||
??? question "出现 API 错误?"
|
||||
检查 Google AI Studio 中的 Key 有效性和额度限制。
|
||||
|
||||
??? question "响应较慢?"
|
||||
可尝试使用 `gemini-1.5-flash` 获得更快速度。
|
||||
|
||||
---
|
||||
|
||||
## 源码
|
||||
|
||||
[:fontawesome-brands-github: 在 GitHub 查看](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/pipes/gemini_mainfold){ .md-button }
|
||||
80
docs/plugins/pipes/github-copilot-sdk-deep-dive.md
Normal file
80
docs/plugins/pipes/github-copilot-sdk-deep-dive.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# GitHub Copilot SDK Plugin Deep Dive
|
||||
|
||||
**Version:** 0.6.0 | **Author:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **Status:** Production-Ready
|
||||
|
||||
The GitHub Copilot SDK plugin is far more than a simple API proxy; it is a highly integrated **Intelligent Agent Runtime Environment**. This document explores the core features, implementation details, technical architecture, and security design that define its capabilities.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 1. Feature Catalog
|
||||
|
||||
The plugin implements advanced capabilities that go far beyond standard API calls:
|
||||
|
||||
- **✅ Physical Workspace Management**: Automatically creates isolated directories for each chat session to manage uploaded files and Agent-generated scripts.
|
||||
- **✅ Real-time TODO Sync**: Mounts to the OpenWebUI database to persist Agent plans directly into a UI progress bar, ensuring transparency for long-running tasks.
|
||||
- **✅ Cross-Ecosystem Tool Bridging**: Translates OpenWebUI Search, Python interpreters, and custom MCP tools into native Copilot capabilities.
|
||||
- **✅ Intelligent File Transport**: Ensures the Agent can access raw files (Excel, PDF, Code) like a local developer through physical file duplication.
|
||||
- **✅ Chain of Thought Streaming**: Full support for rendering the model's reasoning/thinking process in real-time.
|
||||
- **✅ BYOK with Plugin Power**: Connect external OpenAI/Anthropic models while retaining all the plugin's workspace and tool management features.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 2. Use Cases: Beyond Basic Chat
|
||||
|
||||
With these features, the plugin excels in complex, real-world scenarios:
|
||||
|
||||
### 📁 Autonomous Repository Maintenance (Agentic DevOps)
|
||||
>
|
||||
> **Action**: Upload a Zip archive containing a codebase with bugs.
|
||||
> **Utility**: The Agent extracts the archive, uses `bash` to navigate and search, applies fixes via the `edit` tool, and runs tests to verify the solution—all within its isolated sandbox.
|
||||
|
||||
### 📊 Deep Data Analysis (Data Scientist Agent)
|
||||
>
|
||||
> **Action**: Upload multiple heavy Excel spreadsheets.
|
||||
> **Utility**: By bypassing RAG (via the filter), the Agent loads raw files directly into its Python interpreter, performs cross-table calculations, and generates analytical charts presented via Artifacts.
|
||||
|
||||
### 📝 Strategic Task Management
|
||||
>
|
||||
> **Action**: "Develop a full architecture for a new mobile app."
|
||||
> **Utility**: The plugin captures the Agent's breakdown of 20+ sub-tasks. The persistent progress bar reflects ongoing progress (e.g., "Designing Schema", "Drafting API"), providing clarity during long marathons.
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ 3. Technical Architecture
|
||||
|
||||
### 3.1 Three-Layer Sandbox Isolation
|
||||
|
||||
To prevent data leakage in multi-user environments, the plugin enforces a strict physical directory structure:
|
||||
`/app/backend/data/copilot_workspace/{user_id}/{chat_id}/`
|
||||
|
||||
- **Constraint**: Code execution and file storage are confined to the session-specific folder.
|
||||
- **Persistence**: Data remains valid across container restarts due to volume mounting.
|
||||
|
||||
### 3.2 Dynamic Tool Bridging
|
||||
|
||||
How does Copilot "learn" to use OpenWebUI tools?
|
||||
|
||||
1. **Introspection**: Analyzes docstrings and type hints of OpenWebUI tools.
|
||||
2. **Schema Generation**: Dynamically creates JSON descriptions compliant with the GitHub Copilot specification.
|
||||
3. **Routing**: Handles parameter validation, identity injection, and result forwarding between systems.
|
||||
|
||||
### 3.3 Event-Driven TODO Hub
|
||||
|
||||
The plugin captures internal SDK events to power the UI progress bar:
|
||||
|
||||
- **Interceptor**: Listens for `tool.execution_complete` events for the `update_todo` tool.
|
||||
- **Storage**: Syncs project metrics directly to the `chat_todos` table in the OpenWebUI database (SQLite/PostgreSQL).
|
||||
|
||||
---
|
||||
|
||||
## ⚡ 4. Runtime Performance
|
||||
|
||||
- **Anti-Shake Logic**: Environment checks happen only once every 24 hours per process, preventing redundant system calls.
|
||||
- **Tool Caching**: Persists tool definitions across sessions to reduce overhead, improving initial response times by up to 40%.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 5. Development Best Practices
|
||||
|
||||
1. **Use the Filter**: Always pair with `github_copilot_sdk_files_filter` to ensure files reach the Agent in their original binary form.
|
||||
2. **File-First Execution**: Encourage the Agent to "write code to file and execute" rather than relying on direct interactive shell input for complex logic.
|
||||
80
docs/plugins/pipes/github-copilot-sdk-deep-dive.zh.md
Normal file
80
docs/plugins/pipes/github-copilot-sdk-deep-dive.zh.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# GitHub Copilot SDK 插件深度解析 (Deep Dive)
|
||||
|
||||
**版本:** 0.6.0 | **作者:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **状态:** 生产级
|
||||
|
||||
GitHub Copilot SDK 插件不仅仅是一个 API 转发器,它是一个高度集成的 **智能 Agent 运行环境**。本文将从功能实现、应用场景、技术架构及安全设计四个维度,深入解析这一插件的强大之处。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 1. 核心功能清单 (Feature Catalog)
|
||||
|
||||
插件通过深度集成实现了以下超越普通 API 调用的能力:
|
||||
|
||||
- **✅ 物理级工作区管理**: 自动为每个对话创建独立的物理目录,管理用户上传和 Agent 生成的所有文件。
|
||||
- **✅ 实时 TODO 状态同步**: 通过数据库挂载,实时将 Agent 的计划提取到 UI 进度条中,解决长任务透明度问题。
|
||||
- **✅ 跨生态工具桥接**: 自动将 OpenWebUI 的搜索、Python 运行环境和自定义 MCP 工具转化为 Copilot 原生工具。
|
||||
- **✅ 智能文件搬运**: 物理级文件副本传输,确保 Agent 可以像本地开发者一样访问 Excel、PDF 和代码仓库。
|
||||
- **✅ 思考过程可见性**: 完整模拟 GitHub Copilot 的思维链 (Thinking Process) 流式展示。
|
||||
- **✅ BYOK 二次鉴权**: 支持在 Copilot 框架内接入外部 OpenAI/Anthropic 模型,同时享受插件的所有增强功能。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 2. 这个插件能用来干什么? (Use Cases)
|
||||
|
||||
基于上述功能,该插件可以胜任以下复杂场景:
|
||||
|
||||
### 📁 场景 A:全自动代码仓库维护 (Agentic DevOps)
|
||||
>
|
||||
> **操作**: 上传一个包含 Bug 的 Zip 压缩包。
|
||||
> **用途**: Agent 会自动解密、解压,使用 `bash` 定位问题,调用 `edit` 修改代码,最后运行测试脚本。这一切都在隔离沙箱中完成,你只需要审核最终的补丁。
|
||||
|
||||
### 📊 场景 B:深度财务数据审计 (Data Analyst Agent)
|
||||
>
|
||||
> **操作**: 上传一年的 Excel 财务报表。
|
||||
> **用途**: 绕过传统 RAG 的文本切片限制,Agent 直接通过 Python 脚本加载原始表格,进行跨表计算和逻辑校验,并生成可视化图表。
|
||||
|
||||
### 📝 场景 C:复杂长任务进度追踪 (Project Manager)
|
||||
>
|
||||
> **操作**: 输入“请基于以下需求文档编写一个完整的 Web 后端方案”。
|
||||
> **用途**: 插件捕捉 Agent 拆解的 20+ 个子任务。顶部的实时进度条会告诉你它正在“设计数据库”还是“编写认证逻辑”,确保你对黑盒任务了如指掌。
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ 3. 技术架构设计 (Technical Architecture)
|
||||
|
||||
### 3.1 三层物理安全隔离 (Workspace Isolation)
|
||||
|
||||
为了确保多用户环境下的数据安全,插件强制执行以下物理路径:
|
||||
`/app/backend/data/copilot_workspace/{user_id}/{chat_id}/`
|
||||
|
||||
- **隔离性**: 进程内代码执行被严格约束在 `chat_id` 目录下。
|
||||
- **持久性**: 即使容器重启,挂载路径下的工作成果依然保留。
|
||||
|
||||
### 3.2 零配置工具桥接 (Dynamic Tool Bridging)
|
||||
|
||||
插件如何让 Copilot “学会”使用 OpenWebUI 的工具?
|
||||
|
||||
1. **内省 (Introspection)**: 实时读取工具的 `docstring` 和 `type hints`。
|
||||
2. **动态转换**: 生成符合 GitHub Copilot 规范的工具描述符。
|
||||
3. **双向路由**: 处理参数校验、身份注入(如认证头)以及结果回传。
|
||||
|
||||
### 3.3 数据库集成与事件驱动
|
||||
|
||||
插件通过监听 `NDJSON` 事件流,实现状态同步:
|
||||
|
||||
- **监听器**: 实时过滤 `tool.execution_complete` 事件。
|
||||
- **持久层**: 使用 OpenWebUI 核心相同的 `SQLAlchemy` 引擎操作 `chat_todos` 表。
|
||||
|
||||
---
|
||||
|
||||
## ⚡ 4. 性能优化 (Performance)
|
||||
|
||||
- **环境检查防抖**: 全局类变量保护,版本核对周期为 24 小时,避免高并发下的 I/O 争抢。
|
||||
- **工具定义缓存**: 仅在变更时刷新工具元数据,首包响应速度(TTFB)提升约 40%。
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 5. 开发建议 (Best Practices)
|
||||
|
||||
1. **协同工作**: 必须安装 `github_copilot_sdk_files_filter` 以确保文件以“二进制原文”而非“RAG 切片”传递。
|
||||
2. **Python 范式**: 鼓励 Agent 采取“写文件 -> 运行文件”的模式,而非交互式 Shell 输出,以获得更好的执行稳定性。
|
||||
118
docs/plugins/pipes/github-copilot-sdk.md
Normal file
118
docs/plugins/pipes/github-copilot-sdk.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# GitHub Copilot SDK Pipe for OpenWebUI
|
||||
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.6.2 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **License:** MIT
|
||||
|
||||
This is an advanced Pipe function for [OpenWebUI](https://github.com/open-webui/open-webui) that integrates the official [GitHub Copilot SDK](https://github.com/github/copilot-sdk). It enables you to use **GitHub Copilot models** (e.g., `gpt-5.2-codex`, `claude-sonnet-4.5`,`gemini-3-pro`, `gpt-5-mini`) **AND** your own models via **BYOK** (OpenAI, Anthropic) directly within OpenWebUI, providing a unified agentic experience with **strict User & Chat-level Workspace Isolation**.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Essential Companion**
|
||||
> To unlock file handling and data analysis capabilities, you must install the [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/403a62ee-a596-45e7-be65-fab9cc249dd6).
|
||||
|
||||
> [!TIP]
|
||||
> **No Subscription Required for BYOK**
|
||||
> If you are using your own API keys (BYOK mode with OpenAI/Anthropic), **you do NOT need a GitHub Copilot subscription.** A subscription is only required to access GitHub's official models.
|
||||
|
||||
---
|
||||
|
||||
## ✨ v0.6.2 Updates (What's New)
|
||||
|
||||
- **🛠️ New Workspace Artifacts Tool**: Introduced `publish_file_from_workspace`. Agents can now generate files (e.g., Python-generated Excel/CSV) and provide direct download links for the user to click and save.
|
||||
- **⚙️ Workflow Optimization**: Improved reliability of the internal agentic workspace management.
|
||||
- **🛡️ Enhanced Security**: Refined access control for system resources within the isolated environment.
|
||||
- **🔧 Performance Tuning**: Optimized stream processing for larger context windows.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Key Capabilities
|
||||
|
||||
- **🔑 Flexible Auth & BYOK**: Official Copilot subscriptions (PAT) or Bring Your Own Key (OpenAI/Anthropic).
|
||||
- **🔌 Universal Tool Protocol**: Native support for **MCP (Model Context Protocol)**, OpenAPI, and OpenWebUI built-in tools.
|
||||
- **🛡️ Sandbox Workspace Isolation**: Strict per-session sandboxing for data privacy and security.
|
||||
- **♾️ Infinite Session Management**: Smart context window management with automatic compaction for indefinite conversation capability.
|
||||
- **🧠 Deep Database Integration**: Real-time persistence of TOD·O lists for long-running workflows.
|
||||
- **🌊 Advanced Streaming**: Full support for thinking process/Chain of Thought visualization.
|
||||
- **🖼️ Intelligent Multimodal**: Vision capabilities and raw file analysis support.
|
||||
- **⚡ Full-Lifecycle File Agent**: Supports receiving uploaded files for raw bypass analysis and publishing generated results (e.g., analyzed Excel/reports) as downloadable links—a complete closed-loop agentic workflow.
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Core Configuration (Valves)
|
||||
|
||||
### 1. Administrator Settings (Base)
|
||||
|
||||
Administrators define the default behavior for all users in the function settings.
|
||||
|
||||
| Valve | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `GH_TOKEN` | `""` | Global GitHub Token (Requires 'Copilot Requests' permission). |
|
||||
| `ENABLE_OPENWEBUI_TOOLS` | `True` | Enable OpenWebUI Tools (includes defined Tools and Built-in Tools). |
|
||||
| `ENABLE_OPENAPI_SERVER` | `True` | Enable OpenAPI Tool Server connection. |
|
||||
| `ENABLE_MCP_SERVER` | `True` | Enable Direct MCP Client connection (Recommended). |
|
||||
| `REASONING_EFFORT` | `medium` | Reasoning effort level: low, medium, high. |
|
||||
| `SHOW_THINKING` | `True` | Show model reasoning/thinking process. |
|
||||
| `INFINITE_SESSION` | `True` | Enable Infinite Sessions (automatic context compaction). |
|
||||
| `MAX_MULTIPLIER` | `1.0` | Max allowed billing multiplier (0x for free models only). |
|
||||
| `EXCLUDE_KEYWORDS` | `""` | Exclude models containing these keywords (comma separated). |
|
||||
| `TIMEOUT` | `300` | Timeout for each stream chunk (seconds). |
|
||||
| `BYOK_TYPE` | `openai` | BYOK Provider Type: `openai`, `anthropic`. |
|
||||
| `BYOK_BASE_URL` | `""` | BYOK Base URL (e.g., <https://api.openai.com/v1>). |
|
||||
| `BYOK_MODELS` | `""` | BYOK Model List (comma separated). Leave empty to fetch from API. |
|
||||
| `CUSTOM_ENV_VARS` | `""` | Custom environment variables (JSON format). |
|
||||
| `DEBUG` | `False` | Enable this to see detailed logs in your browser console. |
|
||||
|
||||
### 2. User Settings (Individual Overrides)
|
||||
|
||||
Standard users can override these settings in their individual Profile/Function settings.
|
||||
|
||||
| Valve | Description |
|
||||
| :--- | :--- |
|
||||
| `GH_TOKEN` | Use your personal GitHub Token. |
|
||||
| `REASONING_EFFORT` | Individual reasoning effort preference. |
|
||||
| `SHOW_THINKING` | Show model reasoning/thinking process. |
|
||||
| `MAX_MULTIPLIER` | Maximum allowed billing multiplier override. |
|
||||
| `EXCLUDE_KEYWORDS` | Exclude models containing these keywords. |
|
||||
| `BYOK_API_KEY` | Use your personal OpenAI/Anthropic API Key. |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Use Cases (What can you do?)
|
||||
|
||||
- **📁 Fully Autonomous DevOps**: Agent analyzes code, runs tests, and applies patches within its isolated sandbox.
|
||||
- **📊 Deep Data Auditing**: Directly process raw Excel/CSV data via Python (bypassing RAG) and generate visual reports.
|
||||
- **📝 Long-Task Management**: Automatically decomposes complex requests and persists TOD·O progress across sessions.
|
||||
|
||||
---
|
||||
|
||||
## ⭐ Support
|
||||
|
||||
If this plugin has been useful, a **Star** on [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) would be a great motivation for me. Thank you!
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Installation & Configuration
|
||||
|
||||
### 1) Import Function
|
||||
|
||||
1. Open OpenWebUI, go to **Workspace** -> **Functions**.
|
||||
2. Click **+** (Create Function), paste the content of `github_copilot_sdk.py`.
|
||||
3. Save and ensure it is enabled.
|
||||
|
||||
### 2) Get Token
|
||||
|
||||
1. Visit [GitHub Token Settings](https://github.com/settings/tokens?type=beta).
|
||||
2. Create **Fine-grained token**, granting **Account permissions** -> **Copilot Requests** access.
|
||||
3. Paste the generated Token into the `GH_TOKEN` field in Valves.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Troubleshooting & Dependencies
|
||||
|
||||
- **Agent ignores files?**: Ensure the Files Filter is enabled, otherwise RAG will interfere with raw binaries.
|
||||
- **No progress bar?**: The bar only appears when the Agent uses the `update_todo` tool.
|
||||
- **Dependencies**: This Pipe automatically installs `github-copilot-sdk` (Python) and `github-copilot-cli` (Binary).
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
See the full history on GitHub: [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
118
docs/plugins/pipes/github-copilot-sdk.zh.md
Normal file
118
docs/plugins/pipes/github-copilot-sdk.zh.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# GitHub Copilot SDK 官方管道
|
||||
|
||||
**作者:** [Fu-Jie](https://github.com/Fu-Jie) | **版本:** 0.6.2 | **项目:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **许可证:** MIT
|
||||
|
||||
这是一个用于 [OpenWebUI](https://github.com/open-webui/open-webui) 的高级 Pipe 函数,深度集成了 **GitHub Copilot SDK**。它不仅支持 **GitHub Copilot 官方模型**(如 `gpt-5.2-codex`, `claude-sonnet-4.5`, `gemini-3-pro`, `gpt-5-mini`),还支持 **BYOK (自带 Key)** 模式对接自定义服务商(OpenAI, Anthropic),并具备**严格的用户与会话级工作区隔离**能力,提供统一且安全的 Agent 交互体验。
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **核心伴侣组件**
|
||||
> 如需启用文件处理与数据分析能力,请务必安装 [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/403a62ee-a596-45e7-be65-fab9cc249dd6)。
|
||||
|
||||
> [!TIP]
|
||||
> **BYOK 模式无需订阅**
|
||||
> 如果您使用自带的 API Key (BYOK 模式对接 OpenAI/Anthropic),**您不需要 GitHub Copilot 官方订阅**。只有在访问 GitHub 官方模型时才需要订阅。
|
||||
|
||||
---
|
||||
|
||||
## ✨ 0.6.2 更新内容 (What's New)
|
||||
|
||||
- **🛠️ 新增工作区产物工具**: 引入 `publish_file_from_workspace`。Agent 现在可以生成物理文件(如使用 Python 生成的 Excel/CSV 报表),并直接在聊天界面提供点击下载链接。
|
||||
- **⚙️ 工作流优化**: 提升了内部 Agent 物理工作区管理的可靠性与原子性。
|
||||
- **🛡️ 安全增强**: 精细化了隔离环境下系统资源的访问控制策略。
|
||||
- **🔧 性能微调**: 针对大上下文窗口优化了流式数据处理性能。
|
||||
|
||||
---
|
||||
|
||||
## ✨ 核心能力 (Key Capabilities)
|
||||
|
||||
- **🔑 灵活鉴权与 BYOK**: 支持 GitHub Copilot 官方订阅 (PAT) 或自带 Key (OpenAI/Anthropic)。
|
||||
- **🔌 通用工具协议**: 原生支持 **MCP (Model Context Protocol)**、OpenAPI 以及 OpenWebUI 内置工具。
|
||||
- **🛡️ 物理级工作区隔离**: 强制执行严格的用户特定沙箱,确保数据隐私与文件安全。
|
||||
- **♾️ 无限会话管理**: 智能上下文窗口管理与自动压缩算法,支持无限时长的对话交互。
|
||||
- **🧠 深度数据库集成**: 实时持久化 TOD·O 列表到 UI 进度条。
|
||||
- **🌊 深度推理展示**: 完整支持模型思考过程 (Thinking Process) 的流式渲染。
|
||||
- **🖼️ 智能多模态**: 完整支持图像识别与附件上传分析。
|
||||
- **⚡ 全生命周期文件 Agent**: 支持接收上传文件进行绕过 RAG 的深度分析,并将处理结果(如分析后的 Excel/报告)发布为可下载链接,实现完整的闭环 Agent 工作流。
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 核心配置参数 (Valves)
|
||||
|
||||
### 1. 管理员配置 (基础设置)
|
||||
|
||||
管理员可在函数设置中定义全局默认行为。
|
||||
|
||||
| 参数 | 默认值 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| `GH_TOKEN` | `""` | 全局 GitHub Token (需具备 'Copilot Requests' 权限)。 |
|
||||
| `ENABLE_OPENWEBUI_TOOLS` | `True` | 启用 OpenWebUI 工具 (包括定义工具和内置工具)。 |
|
||||
| `ENABLE_OPENAPI_SERVER` | `True` | 启用 OpenAPI 工具服务器连接。 |
|
||||
| `ENABLE_MCP_SERVER` | `True` | 启用直接 MCP 客户端连接 (推荐)。 |
|
||||
| `REASONING_EFFORT` | `medium` | 推理强度:low, medium, high。 |
|
||||
| `SHOW_THINKING` | `True` | 显示模型推理/思考过程。 |
|
||||
| `INFINITE_SESSION` | `True` | 启用无限会话 (自动上下文压缩)。 |
|
||||
| `MAX_MULTIPLIER` | `1.0` | 最大允许的模型计费倍率 (0x 为仅限免费模型)。 |
|
||||
| `EXCLUDE_KEYWORDS` | `""` | 排除包含这些关键字的模型 (逗号分隔)。 |
|
||||
| `TIMEOUT` | `300` | 每个流数据块的超时时间 (秒)。 |
|
||||
| `BYOK_TYPE` | `openai` | BYOK 服务商类型:`openai`, `anthropic`。 |
|
||||
| `BYOK_BASE_URL` | `""` | BYOK 基础 URL (例如: <https://api.openai.com/v1>)。 |
|
||||
| `BYOK_MODELS` | `""` | BYOK 模型列表 (逗号分隔)。留空则从 API 获取。 |
|
||||
| `CUSTOM_ENV_VARS` | `""` | 自定义环境变量 (JSON 格式)。 |
|
||||
| `DEBUG` | `False` | 开启此项以在前端控制台输出详细调试日志。 |
|
||||
|
||||
### 2. 用户配置 (个人覆盖)
|
||||
|
||||
普通用户可在各自的个人设置中根据需要覆盖以下参数。
|
||||
|
||||
| 参数 | 说明 |
|
||||
| :--- | :--- |
|
||||
| `GH_TOKEN` | 使用个人的 GitHub Token。 |
|
||||
| `REASONING_EFFORT` | 个人偏好的推理强度。 |
|
||||
| `SHOW_THINKING` | 显示模型推理/思考过程。 |
|
||||
| `MAX_MULTIPLIER` | 最大允许的模型计费倍率覆盖。 |
|
||||
| `EXCLUDE_KEYWORDS` | 排除包含这些关键字的模型。 |
|
||||
| `BYOK_API_KEY` | 使用个人的 OpenAI/Anthropic API Key。 |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 典型应用场景 (Use Cases)
|
||||
|
||||
- **📁 全自主仓库维护**: Agent 在隔离工作区内自动分析代码、运行测试并应用补丁。
|
||||
- **📊 深度财务数据审计**: 直接通过 Python 加载 Excel/CSV 原始数据(绕过 RAG),生成图表并实时预览。
|
||||
- **📝 长任务项目管理**: 自动拆解复杂任务并持久化 TOD·O 进度,跨会话跟踪执行状态。
|
||||
|
||||
---
|
||||
|
||||
## ⭐ 支持与交流 (Support)
|
||||
|
||||
如果这个插件对您有所帮助,请在 [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) 项目上点个 **Star** 💫,这是对我最大的鼓励。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 安装与配置 (Installation)
|
||||
|
||||
### 1) 导入函数
|
||||
|
||||
1. 打开 OpenWebUI,前往 **工作区** -> **函数**。
|
||||
2. 点击 **+** (创建函数),完整粘贴 `github_copilot_sdk_cn.py` 的内容。
|
||||
3. 点击保存并确保已启用。
|
||||
|
||||
### 2) 获取 Token (Get Token)
|
||||
|
||||
1. 访问 [GitHub Token 设置](https://github.com/settings/tokens?type=beta)。
|
||||
2. 创建 **Fine-grained token**,授予 **Account permissions** -> **Copilot Requests** 访问权限。
|
||||
3. 将生成的 Token 填入插件的 `GH_TOKEN` 配置项中。
|
||||
|
||||
---
|
||||
|
||||
## 📋 常见问题与依赖 (Troubleshooting)
|
||||
|
||||
- **Agent 无法识别文件?**: 请确保已安装并启用了 Files Filter 插件,否则原始文件会被 RAG 干扰。
|
||||
- **看不到 TODO 进度条?**: 进度条仅在 Agent 使用 `update_todo` 工具(通常是处理复杂任务)时出现。
|
||||
- **依赖安装**: 本管道会自动尝试安装 `github-copilot-sdk` (Python 包) 和 `github-copilot-cli` (官方二进制)。
|
||||
|
||||
---
|
||||
|
||||
## 更新日志 (Changelog)
|
||||
|
||||
完整历史记录请见 GitHub: [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
@@ -15,19 +15,7 @@ Pipes allow you to:
|
||||
|
||||
## Available Pipe Plugins
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-google:{ .lg .middle } **Gemini Manifold**
|
||||
|
||||
---
|
||||
|
||||
Integration pipeline for Google's Gemini models with full streaming support.
|
||||
|
||||
**Version:** 1.0.0
|
||||
|
||||
[:octicons-arrow-right-24: Documentation](gemini-manifold.md)
|
||||
|
||||
</div>
|
||||
- [GitHub Copilot SDK](github-copilot-sdk.md) (v0.6.2) - Official GitHub Copilot SDK integration. Features **Workspace Isolation**, **Database Persistence**, **Zero-config OpenWebUI Tool Bridge**, **BYOK** support, and **dynamic MCP discovery**. Supports streaming, multimodal, and infinite sessions.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -15,19 +15,7 @@ Pipes 可以用于:
|
||||
|
||||
## 可用的 Pipe 插件
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-google:{ .lg .middle } **Gemini Manifold**
|
||||
|
||||
---
|
||||
|
||||
面向 Google Gemini 的集成流水线,支持完整流式返回。
|
||||
|
||||
**版本:** 1.0.0
|
||||
|
||||
[:octicons-arrow-right-24: 查看文档](gemini-manifold.md)
|
||||
|
||||
</div>
|
||||
- [GitHub Copilot SDK](github-copilot-sdk.zh.md) (v0.6.2) - GitHub Copilot SDK 官方集成。具备**工作区安全隔离**、**数据库持久化**、**零配置工具桥接**与**BYOK (自带 Key) 支持**。支持流式输出、打字机思考过程及无限会话。[查看深度架构解析](github-copilot-sdk-deep-dive.zh.md)。
|
||||
|
||||
---
|
||||
|
||||
|
||||
13
mkdocs.yml
13
mkdocs.yml
@@ -97,14 +97,14 @@ plugins:
|
||||
Documentation Guide: 文档编写指南
|
||||
Smart Mind Map: 智能思维导图
|
||||
Smart Infographic: 智能信息图
|
||||
Knowledge Card: 知识卡片
|
||||
Flash Card: 闪记卡
|
||||
Export to Excel: 导出到 Excel
|
||||
Export to Word: 导出为 Word
|
||||
Summary: 摘要
|
||||
Async Context Compression: 异步上下文压缩
|
||||
Context Enhancement: 上下文增强
|
||||
Gemini Manifold Companion: Gemini Manifold 伴侣
|
||||
Gemini Manifold: Gemini Manifold
|
||||
Multi-Model Context Merger: 多模型上下文合并
|
||||
Web Gemini Multimodal Filter: Web Gemini 多模态过滤器
|
||||
MoE Prompt Refiner: MoE 提示词优化器
|
||||
- minify:
|
||||
minify_html: true
|
||||
@@ -184,18 +184,17 @@ nav:
|
||||
- plugins/actions/index.md
|
||||
- Smart Mind Map: plugins/actions/smart-mind-map.md
|
||||
- Smart Infographic: plugins/actions/smart-infographic.md
|
||||
- Knowledge Card: plugins/actions/knowledge-card.md
|
||||
- Flash Card: plugins/actions/flash-card.md
|
||||
- Export to Excel: plugins/actions/export-to-excel.md
|
||||
- Export to Word: plugins/actions/export-to-word.md
|
||||
- Summary: plugins/actions/summary.md
|
||||
- Filters:
|
||||
- plugins/filters/index.md
|
||||
- Async Context Compression: plugins/filters/async-context-compression.md
|
||||
- Context Enhancement: plugins/filters/context-enhancement.md
|
||||
- Gemini Manifold Companion: plugins/filters/gemini-manifold-companion.md
|
||||
- Multi-Model Context Merger: plugins/filters/multi-model-context-merger.md
|
||||
- Web Gemini Multimodal Filter: plugins/filters/web-gemini-multimodel.md
|
||||
- Pipes:
|
||||
- plugins/pipes/index.md
|
||||
- Gemini Manifold: plugins/pipes/gemini-manifold.md
|
||||
- Pipelines:
|
||||
- plugins/pipelines/index.md
|
||||
- MoE Prompt Refiner: plugins/pipelines/moe-prompt-refiner.md
|
||||
|
||||
@@ -124,10 +124,6 @@ Each plugin should include:
|
||||
Fu-Jie
|
||||
GitHub: [Fu-Jie/awesome-openwebui](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||
|
||||
---
|
||||
|
||||
> **Note**: For detailed information about each plugin type, see the respective README files in each plugin type directory.
|
||||
|
||||
@@ -124,10 +124,6 @@ plugins/
|
||||
Fu-Jie
|
||||
GitHub: [Fu-Jie/awesome-openwebui](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
||||
|
||||
---
|
||||
|
||||
> **注意**:有关每种插件类型的详细信息,请参阅每个插件类型目录中的相应 README 文件。
|
||||
|
||||
@@ -230,7 +230,3 @@ except Exception as e:
|
||||
|
||||
Fu-Jie
|
||||
GitHub: [Fu-Jie/awesome-openwebui](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||
|
||||
@@ -229,7 +229,3 @@ except Exception as e:
|
||||
|
||||
Fu-Jie
|
||||
GitHub: [Fu-Jie/awesome-openwebui](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
||||
|
||||
98
plugins/actions/deep-dive/README.md
Normal file
98
plugins/actions/deep-dive/README.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# 🌊 Deep Dive
|
||||
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **Version:** 1.0.0 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **License:** MIT
|
||||
|
||||
A comprehensive thinking lens that dives deep into any content - from context to logic, insights, and action paths.
|
||||
|
||||
## 🔥 What's New in v1.0.0
|
||||
|
||||
- ✨ **Thinking Chain Structure**: Moves from surface understanding to deep strategic action.
|
||||
- 🔍 **Phase 01: The Context**: Panoramic view of the situation and background.
|
||||
- 🧠 **Phase 02: The Logic**: Deconstruction of the underlying reasoning and mental models.
|
||||
- 💎 **Phase 03: The Insight**: Extraction of non-obvious value and hidden implications.
|
||||
- 🚀 **Phase 04: The Path**: Definition of specific, prioritized strategic directions.
|
||||
- 🎨 **Premium UI**: Modern, process-oriented design with a "Thinking Line" timeline.
|
||||
- 🌗 **Theme Adaptive**: Automatically adapts to OpenWebUI's light/dark theme.
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
- 🌊 **Deep Thinking**: Not just a summary, but a full deconstruction of content.
|
||||
- 🧠 **Logical Analysis**: Reveals how arguments are built and identifies hidden assumptions.
|
||||
- 💎 **Value Extraction**: Finds the "Aha!" moments and blind spots.
|
||||
- 🚀 **Action Oriented**: Translates deep understanding into immediate, actionable steps.
|
||||
- 🌍 **Multi-language**: Automatically adapts to the user's preferred language.
|
||||
- 🌗 **Theme Support**: Seamlessly switches between light and dark themes based on OpenWebUI settings.
|
||||
|
||||
## 🚀 How to Use
|
||||
|
||||
1. **Input Content**: Provide any text, article, or meeting notes in the chat.
|
||||
2. **Trigger Deep Dive**: Click the **Deep Dive** action button.
|
||||
3. **Explore the Chain**: Follow the visual timeline from Context to Path.
|
||||
|
||||
## ⚙️ Configuration (Valves)
|
||||
|
||||
| Parameter | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| **Show Status (SHOW_STATUS)** | `True` | Whether to show status updates during the thinking process. |
|
||||
| **Model ID (MODEL_ID)** | `Empty` | LLM model for analysis. Empty = use current model. |
|
||||
| **Min Text Length (MIN_TEXT_LENGTH)** | `200` | Minimum characters required for a meaningful deep dive. |
|
||||
| **Clear Previous HTML (CLEAR_PREVIOUS_HTML)** | `True` | Whether to clear previous plugin results. |
|
||||
| **Message Count (MESSAGE_COUNT)** | `1` | Number of recent messages to analyze. |
|
||||
|
||||
## ⭐ Support
|
||||
|
||||
If this plugin has been useful, a star on [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) is a big motivation for me. Thank you for the support.
|
||||
|
||||
## 🌗 Theme Support
|
||||
|
||||
The plugin automatically detects and adapts to OpenWebUI's theme settings:
|
||||
|
||||
- **Detection Priority**:
|
||||
1. Parent document `<meta name="theme-color">` tag
|
||||
2. Parent document `html/body` class or `data-theme` attribute
|
||||
3. System preference via `prefers-color-scheme: dark`
|
||||
|
||||
- **Requirements**: For best results, enable **iframe Sandbox Allow Same Origin** in OpenWebUI:
|
||||
- Go to **Settings** → **Interface** → **Artifacts** → Check **iframe Sandbox Allow Same Origin**
|
||||
|
||||
## 🎨 Visual Preview
|
||||
|
||||
The plugin generates a structured thinking timeline:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ 🌊 Deep Dive Analysis │
|
||||
│ 👤 User 📅 Date 📊 Word count │
|
||||
├─────────────────────────────────────┤
|
||||
│ 🔍 Phase 01: The Context │
|
||||
│ [High-level panoramic view] │
|
||||
│ │
|
||||
│ 🧠 Phase 02: The Logic │
|
||||
│ • Reasoning structure... │
|
||||
│ • Hidden assumptions... │
|
||||
│ │
|
||||
│ 💎 Phase 03: The Insight │
|
||||
│ • Non-obvious value... │
|
||||
│ • Blind spots revealed... │
|
||||
│ │
|
||||
│ 🚀 Phase 04: The Path │
|
||||
│ ▸ Priority Action 1... │
|
||||
│ ▸ Priority Action 2... │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 📂 Files
|
||||
|
||||
- `deep_dive.py` - English version
|
||||
- `deep_dive_cn.py` - Chinese version (精读)
|
||||
|
||||
## Troubleshooting ❓
|
||||
|
||||
- **Plugin not working?**: Check if the filter/action is enabled in the model settings.
|
||||
- **Debug Logs**: Enable `SHOW_STATUS` in Valves to see progress updates.
|
||||
- **Error Messages**: If you see an error, please copy the full error message and report it.
|
||||
- **Submit an Issue**: If you encounter any problems, please submit an issue on GitHub: [Awesome OpenWebUI Issues](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
|
||||
## Changelog
|
||||
|
||||
See the full history on GitHub: [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
98
plugins/actions/deep-dive/README_CN.md
Normal file
98
plugins/actions/deep-dive/README_CN.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# 📖 精读
|
||||
|
||||
**作者:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **版本:** 1.0.0 | **项目:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **许可证:** MIT
|
||||
|
||||
全方位的思维透镜 —— 从背景全景到逻辑脉络,从深度洞察到行动路径。
|
||||
|
||||
## 🔥 v1.0.0 更新内容
|
||||
|
||||
- ✨ **思维链结构**: 从表面理解一步步深入到战略行动。
|
||||
- 🔍 **阶段 01: 全景 (The Context)**: 提供情境与背景的高层级全景视图。
|
||||
- 🧠 **阶段 02: 脉络 (The Logic)**: 解构底层推理逻辑与思维模型。
|
||||
- 💎 **阶段 03: 洞察 (The Insight)**: 提取非显性价值与隐藏的深层含义。
|
||||
- 🚀 **阶段 04: 路径 (The Path)**: 定义具体的、按优先级排列的战略方向。
|
||||
- 🎨 **高端 UI**: 现代化的过程导向设计,带有"思维导火索"时间轴。
|
||||
- 🌗 **主题自适应**: 自动适配 OpenWebUI 的深色/浅色主题。
|
||||
|
||||
## ✨ 核心特性
|
||||
|
||||
- 📖 **深度思考**: 不仅仅是摘要,而是对内容的全面解构。
|
||||
- 🧠 **逻辑分析**: 揭示论点是如何构建的,识别隐藏的假设。
|
||||
- 💎 **价值提取**: 发现"原来如此"的时刻与思维盲点。
|
||||
- 🚀 **行动导向**: 将深度理解转化为立即、可执行的步骤。
|
||||
- 🌍 **多语言支持**: 自动适配用户的偏好语言。
|
||||
- 🌗 **主题支持**: 根据 OpenWebUI 设置自动切换深色/浅色主题。
|
||||
|
||||
## 🚀 如何使用
|
||||
|
||||
1. **输入内容**: 在聊天中提供任何文本、文章或会议记录。
|
||||
2. **触发精读**: 点击 **精读** 操作按钮。
|
||||
3. **探索思维链**: 沿着视觉时间轴从"全景"探索到"路径"。
|
||||
|
||||
## ⚙️ 配置参数 (Valves)
|
||||
|
||||
| 参数 | 默认值 | 描述 |
|
||||
| :--- | :--- | :--- |
|
||||
| **显示状态 (SHOW_STATUS)** | `True` | 是否在思维过程中显示状态更新。 |
|
||||
| **模型 ID (MODEL_ID)** | `空` | 用于分析的 LLM 模型。留空 = 使用当前模型。 |
|
||||
| **最小文本长度 (MIN_TEXT_LENGTH)** | `200` | 进行有意义的精读所需的最小字符数。 |
|
||||
| **清除旧 HTML (CLEAR_PREVIOUS_HTML)** | `True` | 是否清除之前的插件结果。 |
|
||||
| **消息数量 (MESSAGE_COUNT)** | `1` | 要分析的最近消息数量。 |
|
||||
|
||||
## ⭐ 支持
|
||||
|
||||
如果这个插件对你有帮助,欢迎到 [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) 点个 Star,这将是我持续改进的动力,感谢支持。
|
||||
|
||||
## 🌗 主题支持
|
||||
|
||||
插件会自动检测并适配 OpenWebUI 的主题设置:
|
||||
|
||||
- **检测优先级**:
|
||||
1. 父文档 `<meta name="theme-color">` 标签
|
||||
2. 父文档 `html/body` 的 class 或 `data-theme` 属性
|
||||
3. 系统偏好 `prefers-color-scheme: dark`
|
||||
|
||||
- **环境要求**: 为获得最佳效果,请在 OpenWebUI 中启用 **iframe Sandbox Allow Same Origin**:
|
||||
- 进入 **设置** → **界面** → **Artifacts** → 勾选 **iframe Sandbox Allow Same Origin**
|
||||
|
||||
## 🎨 视觉预览
|
||||
|
||||
插件生成结构化的思维时间轴:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ 📖 精读分析报告 │
|
||||
│ 👤 用户 📅 日期 📊 字数 │
|
||||
├─────────────────────────────────────┤
|
||||
│ 🔍 阶段 01: 全景 (The Context) │
|
||||
│ [高层级全景视图内容] │
|
||||
│ │
|
||||
│ 🧠 阶段 02: 脉络 (The Logic) │
|
||||
│ • 推理结构分析... │
|
||||
│ • 隐藏假设识别... │
|
||||
│ │
|
||||
│ 💎 阶段 03: 洞察 (The Insight) │
|
||||
│ • 非显性价值提取... │
|
||||
│ • 思维盲点揭示... │
|
||||
│ │
|
||||
│ 🚀 阶段 04: 路径 (The Path) │
|
||||
│ ▸ 优先级行动 1... │
|
||||
│ ▸ 优先级行动 2... │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 📂 文件说明
|
||||
|
||||
- `deep_dive.py` - 英文版 (Deep Dive)
|
||||
- `deep_dive_cn.py` - 中文版 (精读)
|
||||
|
||||
## 故障排除 (Troubleshooting) ❓
|
||||
|
||||
- **插件不工作?**: 请检查是否在模型设置中启用了该过滤器/动作。
|
||||
- **调试日志**: 在 Valves 中启用 `SHOW_STATUS` 以查看进度更新。
|
||||
- **错误信息**: 如果看到错误,请复制完整的错误信息并报告。
|
||||
- **提交 Issue**: 如果遇到任何问题,请在 GitHub 上提交 Issue:[Awesome OpenWebUI Issues](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
|
||||
## 更新日志
|
||||
|
||||
完整历史请查看 GitHub 项目: [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
BIN
plugins/actions/deep-dive/deep_dive.png
Normal file
BIN
plugins/actions/deep-dive/deep_dive.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 783 KiB |
944
plugins/actions/deep-dive/deep_dive.py
Normal file
944
plugins/actions/deep-dive/deep_dive.py
Normal file
@@ -0,0 +1,944 @@
|
||||
"""
|
||||
title: Deep Dive
|
||||
author: Fu-Jie
|
||||
author_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
funding_url: https://github.com/open-webui
|
||||
version: 1.0.0
|
||||
icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0xMiA3djE0Ii8+PHBhdGggZD0iTTMgMThhMSAxIDAgMCAxLTEtMVY0YTEgMSAwIDAgMSAxLTFoNWE0IDQgMCAwIDEgNCA0IDQgNCAwIDAgMSA0LTRoNWExIDEgMCAwIDEgMSAxdjEzYTEgMSAwIDAgMS0xIDFoLTZhMyAzIDAgMCAwLTMgMyAzIDMgMCAwIDAtMy0zeiIvPjxwYXRoIGQ9Ik02IDEyaDIiLz48cGF0aCBkPSJNMTYgMTJoMiIvPjwvc3ZnPg==
|
||||
requirements: markdown
|
||||
description: A comprehensive thinking lens that dives deep into any content - from context to logic, insights, and action paths.
|
||||
"""
|
||||
|
||||
# Standard library imports
|
||||
import re
|
||||
import logging
|
||||
from typing import Optional, Dict, Any, Callable, Awaitable
|
||||
from datetime import datetime
|
||||
|
||||
# Third-party imports
|
||||
from pydantic import BaseModel, Field
|
||||
from fastapi import Request
|
||||
import markdown
|
||||
|
||||
# OpenWebUI imports
|
||||
from open_webui.utils.chat import generate_chat_completion
|
||||
from open_webui.models.users import Users
|
||||
|
||||
# Logging setup
|
||||
logging.basicConfig(
|
||||
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# =================================================================
|
||||
# HTML Template - Process-Oriented Design with Theme Support
|
||||
# =================================================================
|
||||
HTML_WRAPPER_TEMPLATE = """
|
||||
<!-- OPENWEBUI_PLUGIN_OUTPUT -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="{user_language}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
:root {
|
||||
--dd-bg-primary: #ffffff;
|
||||
--dd-bg-secondary: #f8fafc;
|
||||
--dd-bg-tertiary: #f1f5f9;
|
||||
--dd-text-primary: #0f172a;
|
||||
--dd-text-secondary: #334155;
|
||||
--dd-text-dim: #64748b;
|
||||
--dd-border: #e2e8f0;
|
||||
--dd-accent: #3b82f6;
|
||||
--dd-accent-soft: #eff6ff;
|
||||
--dd-header-gradient: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
|
||||
--dd-shadow: 0 10px 40px rgba(0,0,0,0.06);
|
||||
--dd-code-bg: #f1f5f9;
|
||||
}
|
||||
.theme-dark {
|
||||
--dd-bg-primary: #1e293b;
|
||||
--dd-bg-secondary: #0f172a;
|
||||
--dd-bg-tertiary: #334155;
|
||||
--dd-text-primary: #f1f5f9;
|
||||
--dd-text-secondary: #e2e8f0;
|
||||
--dd-text-dim: #94a3b8;
|
||||
--dd-border: #475569;
|
||||
--dd-accent: #60a5fa;
|
||||
--dd-accent-soft: rgba(59, 130, 246, 0.15);
|
||||
--dd-header-gradient: linear-gradient(135deg, #0f172a 0%, #1e1e2e 100%);
|
||||
--dd-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
||||
--dd-code-bg: #334155;
|
||||
}
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
background-color: transparent;
|
||||
}
|
||||
#main-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.plugin-item {
|
||||
background: var(--dd-bg-primary);
|
||||
border-radius: 24px;
|
||||
box-shadow: var(--dd-shadow);
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--dd-border);
|
||||
}
|
||||
/* STYLES_INSERTION_POINT */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main-container">
|
||||
<!-- CONTENT_INSERTION_POINT -->
|
||||
</div>
|
||||
<!-- SCRIPTS_INSERTION_POINT -->
|
||||
<script>
|
||||
(function() {
|
||||
const parseColorLuma = (colorStr) => {
|
||||
if (!colorStr) return null;
|
||||
let m = colorStr.match(/^#?([0-9a-f]{6})$/i);
|
||||
if (m) {
|
||||
const hex = m[1];
|
||||
const r = parseInt(hex.slice(0, 2), 16);
|
||||
const g = parseInt(hex.slice(2, 4), 16);
|
||||
const b = parseInt(hex.slice(4, 6), 16);
|
||||
return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
|
||||
}
|
||||
m = colorStr.match(/rgba?\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/i);
|
||||
if (m) {
|
||||
const r = parseInt(m[1], 10);
|
||||
const g = parseInt(m[2], 10);
|
||||
const b = parseInt(m[3], 10);
|
||||
return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const getThemeFromMeta = (doc) => {
|
||||
const metas = Array.from((doc || document).querySelectorAll('meta[name="theme-color"]'));
|
||||
if (!metas.length) return null;
|
||||
const color = metas[metas.length - 1].content.trim();
|
||||
const luma = parseColorLuma(color);
|
||||
if (luma === null) return null;
|
||||
return luma < 0.5 ? 'dark' : 'light';
|
||||
};
|
||||
const getParentDocumentSafe = () => {
|
||||
try {
|
||||
if (!window.parent || window.parent === window) return null;
|
||||
const pDoc = window.parent.document;
|
||||
void pDoc.title;
|
||||
return pDoc;
|
||||
} catch (err) { return null; }
|
||||
};
|
||||
const getThemeFromParentClass = () => {
|
||||
try {
|
||||
if (!window.parent || window.parent === window) return null;
|
||||
const pDoc = window.parent.document;
|
||||
const html = pDoc.documentElement;
|
||||
const body = pDoc.body;
|
||||
const htmlClass = html ? html.className : '';
|
||||
const bodyClass = body ? body.className : '';
|
||||
const htmlDataTheme = html ? html.getAttribute('data-theme') : '';
|
||||
if (htmlDataTheme === 'dark' || bodyClass.includes('dark') || htmlClass.includes('dark')) return 'dark';
|
||||
if (htmlDataTheme === 'light' || bodyClass.includes('light') || htmlClass.includes('light')) return 'light';
|
||||
return null;
|
||||
} catch (err) { return null; }
|
||||
};
|
||||
const setTheme = () => {
|
||||
const parentDoc = getParentDocumentSafe();
|
||||
const metaTheme = parentDoc ? getThemeFromMeta(parentDoc) : null;
|
||||
const parentClassTheme = getThemeFromParentClass();
|
||||
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const chosen = metaTheme || parentClassTheme || (prefersDark ? 'dark' : 'light');
|
||||
document.documentElement.classList.toggle('theme-dark', chosen === 'dark');
|
||||
};
|
||||
setTheme();
|
||||
if (window.matchMedia) {
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', setTheme);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
# =================================================================
|
||||
# LLM Prompts - Deep Dive Thinking Chain
|
||||
# =================================================================
|
||||
|
||||
SYSTEM_PROMPT = """
|
||||
You are a Deep Dive Analyst. Your goal is to guide the user through a comprehensive thinking process, moving from surface understanding to deep strategic action.
|
||||
|
||||
## Thinking Structure (STRICT)
|
||||
|
||||
You MUST analyze the input across these four specific dimensions:
|
||||
|
||||
### 1. 🔍 The Context (What?)
|
||||
Provide a high-level panoramic view. What is this content about? What is the core situation, background, or problem being addressed? (2-3 paragraphs)
|
||||
|
||||
### 2. 🧠 The Logic (Why?)
|
||||
Deconstruct the underlying structure. How is the argument built? What is the reasoning, the hidden assumptions, or the mental models at play? (Bullet points)
|
||||
|
||||
### 3. 💎 The Insight (So What?)
|
||||
Extract the non-obvious value. What are the "Aha!" moments? What are the implications, the blind spots, or the unique perspectives revealed? (Bullet points)
|
||||
|
||||
### 4. 🚀 The Path (Now What?)
|
||||
Define the strategic direction. What are the specific, prioritized next steps? How can this knowledge be applied immediately? (Actionable steps)
|
||||
|
||||
## Rules
|
||||
- Output in the user's specified language.
|
||||
- Maintain a professional, analytical, yet inspiring tone.
|
||||
- Focus on the *process* of understanding, not just the result.
|
||||
- No greetings or meta-commentary.
|
||||
"""
|
||||
|
||||
USER_PROMPT = """
|
||||
Initiate a Deep Dive into the following content:
|
||||
|
||||
**User Context:**
|
||||
- User: {user_name}
|
||||
- Time: {current_date_time_str}
|
||||
- Language: {user_language}
|
||||
|
||||
**Content to Analyze:**
|
||||
```
|
||||
{long_text_content}
|
||||
```
|
||||
|
||||
Please execute the full thinking chain: Context → Logic → Insight → Path.
|
||||
"""
|
||||
|
||||
# =================================================================
|
||||
# Premium CSS Design - Deep Dive Theme
|
||||
# =================================================================
|
||||
|
||||
CSS_TEMPLATE = """
|
||||
.deep-dive {
|
||||
font-family: 'Inter', -apple-system, system-ui, sans-serif;
|
||||
color: var(--dd-text-secondary);
|
||||
}
|
||||
|
||||
.dd-header {
|
||||
background: var(--dd-header-gradient);
|
||||
padding: 40px 32px;
|
||||
color: white;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dd-header-badge {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
border: 1px solid rgba(255,255,255,0.2);
|
||||
border-radius: 100px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.dd-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
margin: 0 0 12px 0;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.dd-meta {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.dd-body {
|
||||
padding: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 40px;
|
||||
position: relative;
|
||||
background: var(--dd-bg-primary);
|
||||
}
|
||||
|
||||
/* The Thinking Line */
|
||||
.dd-body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 52px;
|
||||
top: 40px;
|
||||
bottom: 40px;
|
||||
width: 2px;
|
||||
background: var(--dd-border);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.dd-step {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.dd-step-icon {
|
||||
flex-shrink: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: var(--dd-bg-primary);
|
||||
border: 2px solid var(--dd-border);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.25rem;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.03);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.dd-step:hover .dd-step-icon {
|
||||
border-color: var(--dd-accent);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.dd-step-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dd-step-label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--dd-accent);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.dd-step-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: var(--dd-text-primary);
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
|
||||
.dd-text {
|
||||
line-height: 1.7;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.dd-text p { margin-bottom: 16px; }
|
||||
.dd-text p:last-child { margin-bottom: 0; }
|
||||
|
||||
.dd-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dd-list-item {
|
||||
background: var(--dd-bg-secondary);
|
||||
padding: 16px 20px;
|
||||
border-radius: 12px;
|
||||
border-left: 4px solid var(--dd-border);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.dd-list-item:hover {
|
||||
background: var(--dd-bg-tertiary);
|
||||
border-left-color: var(--dd-accent);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.dd-list-item strong {
|
||||
color: var(--dd-text-primary);
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.dd-path-item {
|
||||
background: var(--dd-accent-soft);
|
||||
border-left-color: var(--dd-accent);
|
||||
}
|
||||
|
||||
.dd-footer {
|
||||
padding: 24px 32px;
|
||||
background: var(--dd-bg-secondary);
|
||||
border-top: 1px solid var(--dd-border);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.8rem;
|
||||
color: var(--dd-text-dim);
|
||||
}
|
||||
|
||||
.dd-tag {
|
||||
padding: 2px 8px;
|
||||
background: var(--dd-bg-tertiary);
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.dd-text code,
|
||||
.dd-list-item code {
|
||||
background: var(--dd-code-bg);
|
||||
color: var(--dd-text-primary);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: 'SF Mono', 'Consolas', 'Monaco', monospace;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.dd-list-item em {
|
||||
font-style: italic;
|
||||
color: var(--dd-text-dim);
|
||||
}
|
||||
"""
|
||||
|
||||
CONTENT_TEMPLATE = """
|
||||
<div class="deep-dive">
|
||||
<div class="dd-header">
|
||||
<div class="dd-header-badge">Thinking Process</div>
|
||||
<h1 class="dd-title">Deep Dive Analysis</h1>
|
||||
<div class="dd-meta">
|
||||
<span>👤 {user_name}</span>
|
||||
<span>📅 {current_date_time_str}</span>
|
||||
<span>📊 {word_count} words</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dd-body">
|
||||
<!-- Step 1: Context -->
|
||||
<div class="dd-step">
|
||||
<div class="dd-step-icon">🔍</div>
|
||||
<div class="dd-step-content">
|
||||
<div class="dd-step-label">Phase 01</div>
|
||||
<h2 class="dd-step-title">The Context</h2>
|
||||
<div class="dd-text">{context_html}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Logic -->
|
||||
<div class="dd-step">
|
||||
<div class="dd-step-icon">🧠</div>
|
||||
<div class="dd-step-content">
|
||||
<div class="dd-step-label">Phase 02</div>
|
||||
<h2 class="dd-step-title">The Logic</h2>
|
||||
<div class="dd-text">{logic_html}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Insight -->
|
||||
<div class="dd-step">
|
||||
<div class="dd-step-icon">💎</div>
|
||||
<div class="dd-step-content">
|
||||
<div class="dd-step-label">Phase 03</div>
|
||||
<h2 class="dd-step-title">The Insight</h2>
|
||||
<div class="dd-text">{insight_html}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 4: Path -->
|
||||
<div class="dd-step">
|
||||
<div class="dd-step-icon">🚀</div>
|
||||
<div class="dd-step-content">
|
||||
<div class="dd-step-label">Phase 04</div>
|
||||
<h2 class="dd-step-title">The Path</h2>
|
||||
<div class="dd-text">{path_html}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dd-footer">
|
||||
<span>Deep Dive Engine v1.0</span>
|
||||
<span><span class="dd-tag">AI-Powered</span></span>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
|
||||
class Action:
|
||||
class Valves(BaseModel):
|
||||
SHOW_STATUS: bool = Field(
|
||||
default=True,
|
||||
description="Whether to show operation status updates.",
|
||||
)
|
||||
SHOW_DEBUG_LOG: bool = Field(
|
||||
default=False,
|
||||
description="Whether to print debug logs in the browser console.",
|
||||
)
|
||||
MODEL_ID: str = Field(
|
||||
default="",
|
||||
description="LLM Model ID for analysis. Empty = use current model.",
|
||||
)
|
||||
MIN_TEXT_LENGTH: int = Field(
|
||||
default=200,
|
||||
description="Minimum text length for deep dive (chars).",
|
||||
)
|
||||
CLEAR_PREVIOUS_HTML: bool = Field(
|
||||
default=True,
|
||||
description="Whether to clear previous plugin results.",
|
||||
)
|
||||
MESSAGE_COUNT: int = Field(
|
||||
default=1,
|
||||
description="Number of recent messages to analyze.",
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.valves = self.Valves()
|
||||
|
||||
def _get_user_context(self, __user__: Optional[Dict[str, Any]]) -> Dict[str, str]:
|
||||
"""Safely 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"),
|
||||
}
|
||||
|
||||
def _get_chat_context(
|
||||
self, body: dict, __metadata__: Optional[dict] = None
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Unified extraction of chat context information (chat_id, message_id).
|
||||
Prioritizes extraction from body, then metadata.
|
||||
"""
|
||||
chat_id = ""
|
||||
message_id = ""
|
||||
|
||||
# 1. Try to get from body
|
||||
if isinstance(body, dict):
|
||||
chat_id = body.get("chat_id", "")
|
||||
message_id = body.get("id", "") # message_id is usually 'id' in body
|
||||
|
||||
# Check body.metadata as fallback
|
||||
if not chat_id or not message_id:
|
||||
body_metadata = body.get("metadata", {})
|
||||
if isinstance(body_metadata, dict):
|
||||
if not chat_id:
|
||||
chat_id = body_metadata.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = body_metadata.get("message_id", "")
|
||||
|
||||
# 2. Try to get from __metadata__ (as supplement)
|
||||
if __metadata__ and isinstance(__metadata__, dict):
|
||||
if not chat_id:
|
||||
chat_id = __metadata__.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = __metadata__.get("message_id", "")
|
||||
|
||||
return {
|
||||
"chat_id": str(chat_id).strip(),
|
||||
"message_id": str(message_id).strip(),
|
||||
}
|
||||
|
||||
def _process_llm_output(self, llm_output: str) -> Dict[str, str]:
|
||||
"""Parse LLM output and convert to styled HTML."""
|
||||
# Extract sections using flexible regex
|
||||
context_match = re.search(
|
||||
r"###\s*1\.\s*🔍?\s*The Context\s*\((.*?)\)\s*\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
logic_match = re.search(
|
||||
r"###\s*2\.\s*🧠?\s*The Logic\s*\((.*?)\)\s*\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
insight_match = re.search(
|
||||
r"###\s*3\.\s*💎?\s*The Insight\s*\((.*?)\)\s*\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
path_match = re.search(
|
||||
r"###\s*4\.\s*🚀?\s*The Path\s*\((.*?)\)\s*\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
|
||||
# Fallback if numbering is different
|
||||
if not context_match:
|
||||
context_match = re.search(
|
||||
r"###\s*🔍?\s*The Context.*?\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
if not logic_match:
|
||||
logic_match = re.search(
|
||||
r"###\s*🧠?\s*The Logic.*?\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
if not insight_match:
|
||||
insight_match = re.search(
|
||||
r"###\s*💎?\s*The Insight.*?\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
if not path_match:
|
||||
path_match = re.search(
|
||||
r"###\s*🚀?\s*The Path.*?\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
|
||||
context_md = (
|
||||
context_match.group(1 if context_match.lastindex == 1 else 2).strip()
|
||||
if context_match
|
||||
else ""
|
||||
)
|
||||
logic_md = (
|
||||
logic_match.group(1 if logic_match.lastindex == 1 else 2).strip()
|
||||
if logic_match
|
||||
else ""
|
||||
)
|
||||
insight_md = (
|
||||
insight_match.group(1 if insight_match.lastindex == 1 else 2).strip()
|
||||
if insight_match
|
||||
else ""
|
||||
)
|
||||
path_md = (
|
||||
path_match.group(1 if path_match.lastindex == 1 else 2).strip()
|
||||
if path_match
|
||||
else ""
|
||||
)
|
||||
|
||||
if not any([context_md, logic_md, insight_md, path_md]):
|
||||
context_md = llm_output.strip()
|
||||
logger.warning("LLM output did not follow format. Using as context.")
|
||||
|
||||
md_extensions = ["nl2br"]
|
||||
|
||||
context_html = (
|
||||
markdown.markdown(context_md, extensions=md_extensions)
|
||||
if context_md
|
||||
else '<p class="dd-no-content">No context extracted.</p>'
|
||||
)
|
||||
logic_html = (
|
||||
self._process_list_items(logic_md, "logic")
|
||||
if logic_md
|
||||
else '<p class="dd-no-content">No logic deconstructed.</p>'
|
||||
)
|
||||
insight_html = (
|
||||
self._process_list_items(insight_md, "insight")
|
||||
if insight_md
|
||||
else '<p class="dd-no-content">No insights found.</p>'
|
||||
)
|
||||
path_html = (
|
||||
self._process_list_items(path_md, "path")
|
||||
if path_md
|
||||
else '<p class="dd-no-content">No path defined.</p>'
|
||||
)
|
||||
|
||||
return {
|
||||
"context_html": context_html,
|
||||
"logic_html": logic_html,
|
||||
"insight_html": insight_html,
|
||||
"path_html": path_html,
|
||||
}
|
||||
|
||||
def _process_list_items(self, md_content: str, section_type: str) -> str:
|
||||
"""Convert markdown list to styled HTML cards with full markdown support."""
|
||||
lines = md_content.strip().split("\n")
|
||||
items = []
|
||||
current_paragraph = []
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
|
||||
# Check for list item (bullet or numbered)
|
||||
bullet_match = re.match(r"^[-*]\s+(.+)$", line)
|
||||
numbered_match = re.match(r"^\d+\.\s+(.+)$", line)
|
||||
|
||||
if bullet_match or numbered_match:
|
||||
# Flush any accumulated paragraph
|
||||
if current_paragraph:
|
||||
para_text = " ".join(current_paragraph)
|
||||
para_html = self._convert_inline_markdown(para_text)
|
||||
items.append(f"<p>{para_html}</p>")
|
||||
current_paragraph = []
|
||||
|
||||
# Extract the list item content
|
||||
text = (
|
||||
bullet_match.group(1) if bullet_match else numbered_match.group(1)
|
||||
)
|
||||
|
||||
# Handle bold title pattern: **Title:** Description or **Title**: Description
|
||||
title_match = re.match(r"\*\*(.+?)\*\*[:\s]*(.*)$", text)
|
||||
if title_match:
|
||||
title = self._convert_inline_markdown(title_match.group(1))
|
||||
desc = self._convert_inline_markdown(title_match.group(2).strip())
|
||||
path_class = "dd-path-item" if section_type == "path" else ""
|
||||
item_html = f'<div class="dd-list-item {path_class}"><strong>{title}</strong>{desc}</div>'
|
||||
else:
|
||||
text_html = self._convert_inline_markdown(text)
|
||||
path_class = "dd-path-item" if section_type == "path" else ""
|
||||
item_html = (
|
||||
f'<div class="dd-list-item {path_class}">{text_html}</div>'
|
||||
)
|
||||
items.append(item_html)
|
||||
elif line and not line.startswith("#"):
|
||||
# Accumulate paragraph text
|
||||
current_paragraph.append(line)
|
||||
elif not line and current_paragraph:
|
||||
# Empty line ends paragraph
|
||||
para_text = " ".join(current_paragraph)
|
||||
para_html = self._convert_inline_markdown(para_text)
|
||||
items.append(f"<p>{para_html}</p>")
|
||||
current_paragraph = []
|
||||
|
||||
# Flush remaining paragraph
|
||||
if current_paragraph:
|
||||
para_text = " ".join(current_paragraph)
|
||||
para_html = self._convert_inline_markdown(para_text)
|
||||
items.append(f"<p>{para_html}</p>")
|
||||
|
||||
if items:
|
||||
return f'<div class="dd-list">{" ".join(items)}</div>'
|
||||
return f'<p class="dd-no-content">No items found.</p>'
|
||||
|
||||
def _convert_inline_markdown(self, text: str) -> str:
|
||||
"""Convert inline markdown (bold, italic, code) to HTML."""
|
||||
# Convert inline code: `code` -> <code>code</code>
|
||||
text = re.sub(r"`([^`]+)`", r"<code>\1</code>", text)
|
||||
# Convert bold: **text** -> <strong>text</strong>
|
||||
text = re.sub(r"\*\*(.+?)\*\*", r"<strong>\1</strong>", text)
|
||||
# Convert italic: *text* -> <em>text</em> (but not inside **)
|
||||
text = re.sub(r"(?<!\*)\*([^*]+)\*(?!\*)", r"<em>\1</em>", text)
|
||||
return text
|
||||
|
||||
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,
|
||||
ntype: str = "info",
|
||||
):
|
||||
"""Emits a notification event."""
|
||||
if emitter:
|
||||
await emitter(
|
||||
{"type": "notification", "data": {"type": ntype, "content": content}}
|
||||
)
|
||||
|
||||
async def _emit_debug_log(self, emitter, title: str, data: dict):
|
||||
"""Print structured debug logs in the browser console"""
|
||||
if not self.valves.SHOW_DEBUG_LOG or not emitter:
|
||||
return
|
||||
|
||||
try:
|
||||
import json
|
||||
|
||||
js_code = f"""
|
||||
(async function() {{
|
||||
console.group("🛠️ {title}");
|
||||
console.log({json.dumps(data, ensure_ascii=False)});
|
||||
console.groupEnd();
|
||||
}})();
|
||||
"""
|
||||
|
||||
await emitter({"type": "execute", "data": {"code": js_code}})
|
||||
except Exception as e:
|
||||
print(f"Error emitting debug log: {e}")
|
||||
|
||||
def _remove_existing_html(self, content: str) -> str:
|
||||
"""Removes existing plugin-generated HTML."""
|
||||
pattern = r"```html\s*<!-- OPENWEBUI_PLUGIN_OUTPUT -->[\s\S]*?```"
|
||||
return re.sub(pattern, "", content).strip()
|
||||
|
||||
def _extract_text_content(self, content) -> str:
|
||||
"""Extract text from message content."""
|
||||
if isinstance(content, str):
|
||||
return content
|
||||
elif isinstance(content, list):
|
||||
text_parts = []
|
||||
for item in content:
|
||||
if isinstance(item, dict) and item.get("type") == "text":
|
||||
text_parts.append(item.get("text", ""))
|
||||
elif isinstance(item, str):
|
||||
text_parts.append(item)
|
||||
return "\n".join(text_parts)
|
||||
return str(content) if content else ""
|
||||
|
||||
def _merge_html(
|
||||
self,
|
||||
existing_html: str,
|
||||
new_content: str,
|
||||
new_styles: str = "",
|
||||
user_language: str = "en-US",
|
||||
) -> str:
|
||||
"""Merges new content into HTML container."""
|
||||
if "<!-- OPENWEBUI_PLUGIN_OUTPUT -->" in existing_html:
|
||||
base_html = re.sub(r"^```html\s*", "", existing_html)
|
||||
base_html = re.sub(r"\s*```$", "", base_html)
|
||||
else:
|
||||
base_html = HTML_WRAPPER_TEMPLATE.replace("{user_language}", user_language)
|
||||
|
||||
wrapped = f'<div class="plugin-item">\n{new_content}\n</div>'
|
||||
|
||||
if new_styles:
|
||||
base_html = base_html.replace(
|
||||
"/* STYLES_INSERTION_POINT */",
|
||||
f"{new_styles}\n/* STYLES_INSERTION_POINT */",
|
||||
)
|
||||
|
||||
base_html = base_html.replace(
|
||||
"<!-- CONTENT_INSERTION_POINT -->",
|
||||
f"{wrapped}\n<!-- CONTENT_INSERTION_POINT -->",
|
||||
)
|
||||
|
||||
return base_html.strip()
|
||||
|
||||
def _build_content_html(self, context: dict) -> str:
|
||||
"""Build content HTML."""
|
||||
html = CONTENT_TEMPLATE
|
||||
for key, value in context.items():
|
||||
html = html.replace(f"{{{key}}}", str(value))
|
||||
return html
|
||||
|
||||
async def action(
|
||||
self,
|
||||
body: dict,
|
||||
__user__: Optional[Dict[str, Any]] = None,
|
||||
__event_emitter__: Optional[Callable[[Any], Awaitable[None]]] = None,
|
||||
__request__: Optional[Request] = None,
|
||||
) -> Optional[dict]:
|
||||
logger.info("Action: Deep Dive v1.0.0 started")
|
||||
|
||||
user_ctx = self._get_user_context(__user__)
|
||||
user_id = user_ctx["user_id"]
|
||||
user_name = user_ctx["user_name"]
|
||||
user_language = user_ctx["user_language"]
|
||||
|
||||
now = datetime.now()
|
||||
current_date_time_str = now.strftime("%b %d, %Y %H:%M")
|
||||
|
||||
original_content = ""
|
||||
try:
|
||||
messages = body.get("messages", [])
|
||||
if not messages:
|
||||
raise ValueError("No messages found.")
|
||||
|
||||
message_count = min(self.valves.MESSAGE_COUNT, len(messages))
|
||||
recent_messages = messages[-message_count:]
|
||||
|
||||
aggregated_parts = []
|
||||
for msg in recent_messages:
|
||||
text = self._extract_text_content(msg.get("content"))
|
||||
if text:
|
||||
aggregated_parts.append(text)
|
||||
|
||||
if not aggregated_parts:
|
||||
raise ValueError("No text content found.")
|
||||
|
||||
original_content = "\n\n---\n\n".join(aggregated_parts)
|
||||
word_count = len(original_content.split())
|
||||
|
||||
if len(original_content) < self.valves.MIN_TEXT_LENGTH:
|
||||
msg = f"Content too brief ({len(original_content)} chars). Deep Dive requires at least {self.valves.MIN_TEXT_LENGTH} chars for meaningful analysis."
|
||||
await self._emit_notification(__event_emitter__, msg, "warning")
|
||||
return {"messages": [{"role": "assistant", "content": f"⚠️ {msg}"}]}
|
||||
|
||||
await self._emit_notification(
|
||||
__event_emitter__, "🌊 Initiating Deep Dive thinking process...", "info"
|
||||
)
|
||||
await self._emit_status(
|
||||
__event_emitter__, "🌊 Deep Dive: Analyzing Context & Logic...", False
|
||||
)
|
||||
|
||||
prompt = USER_PROMPT.format(
|
||||
user_name=user_name,
|
||||
current_date_time_str=current_date_time_str,
|
||||
user_language=user_language,
|
||||
long_text_content=original_content,
|
||||
)
|
||||
|
||||
model = self.valves.MODEL_ID or body.get("model")
|
||||
payload = {
|
||||
"model": model,
|
||||
"messages": [
|
||||
{"role": "system", "content": SYSTEM_PROMPT},
|
||||
{"role": "user", "content": prompt},
|
||||
],
|
||||
"stream": False,
|
||||
}
|
||||
|
||||
user_obj = Users.get_user_by_id(user_id)
|
||||
if not user_obj:
|
||||
raise ValueError(f"User not found: {user_id}")
|
||||
|
||||
response = await generate_chat_completion(__request__, payload, user_obj)
|
||||
llm_output = response["choices"][0]["message"]["content"]
|
||||
|
||||
processed = self._process_llm_output(llm_output)
|
||||
|
||||
context = {
|
||||
"user_name": user_name,
|
||||
"current_date_time_str": current_date_time_str,
|
||||
"word_count": word_count,
|
||||
**processed,
|
||||
}
|
||||
|
||||
content_html = self._build_content_html(context)
|
||||
|
||||
# Handle existing HTML
|
||||
existing = ""
|
||||
match = re.search(
|
||||
r"```html\s*(<!-- OPENWEBUI_PLUGIN_OUTPUT -->[\s\S]*?)```",
|
||||
original_content,
|
||||
)
|
||||
if match:
|
||||
existing = match.group(1)
|
||||
|
||||
if self.valves.CLEAR_PREVIOUS_HTML or not existing:
|
||||
original_content = self._remove_existing_html(original_content)
|
||||
final_html = self._merge_html(
|
||||
"", content_html, CSS_TEMPLATE, user_language
|
||||
)
|
||||
else:
|
||||
original_content = self._remove_existing_html(original_content)
|
||||
final_html = self._merge_html(
|
||||
existing, content_html, CSS_TEMPLATE, user_language
|
||||
)
|
||||
|
||||
body["messages"][-1][
|
||||
"content"
|
||||
] = f"{original_content}\n\n```html\n{final_html}\n```"
|
||||
|
||||
await self._emit_status(__event_emitter__, "🌊 Deep Dive complete!", True)
|
||||
await self._emit_notification(
|
||||
__event_emitter__,
|
||||
f"🌊 Deep Dive complete, {user_name}! Thinking chain generated.",
|
||||
"success",
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Deep Dive Error: {e}", exc_info=True)
|
||||
body["messages"][-1][
|
||||
"content"
|
||||
] = f"{original_content}\n\n❌ **Error:** {str(e)}"
|
||||
await self._emit_status(__event_emitter__, "Deep Dive failed.", True)
|
||||
await self._emit_notification(
|
||||
__event_emitter__, f"Error: {str(e)}", "error"
|
||||
)
|
||||
|
||||
return body
|
||||
BIN
plugins/actions/deep-dive/deep_dive_cn.png
Normal file
BIN
plugins/actions/deep-dive/deep_dive_cn.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 997 KiB |
936
plugins/actions/deep-dive/deep_dive_cn.py
Normal file
936
plugins/actions/deep-dive/deep_dive_cn.py
Normal file
@@ -0,0 +1,936 @@
|
||||
"""
|
||||
title: 精读
|
||||
author: Fu-Jie
|
||||
author_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
funding_url: https://github.com/open-webui
|
||||
version: 1.0.0
|
||||
icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0xMiA3djE0Ii8+PHBhdGggZD0iTTMgMThhMSAxIDAgMCAxLTEtMVY0YTEgMSAwIDAgMSAxLTFoNWE0IDQgMCAwIDEgNCA0IDQgNCAwIDAgMSA0LTRoNWExIDEgMCAwIDEgMSAxdjEzYTEgMSAwIDAgMS0xIDFoLTZhMyAzIDAgMCAwLTMgMyAzIDMgMCAwIDAtMy0zeiIvPjxwYXRoIGQ9Ik02IDEyaDIiLz48cGF0aCBkPSJNMTYgMTJoMiIvPjwvc3ZnPg==
|
||||
requirements: markdown
|
||||
description: 全方位的思维透镜 —— 从背景全景到逻辑脉络,从深度洞察到行动路径。
|
||||
"""
|
||||
|
||||
# Standard library imports
|
||||
import re
|
||||
import logging
|
||||
from typing import Optional, Dict, Any, Callable, Awaitable
|
||||
from datetime import datetime
|
||||
|
||||
# Third-party imports
|
||||
from pydantic import BaseModel, Field
|
||||
from fastapi import Request
|
||||
import markdown
|
||||
|
||||
# OpenWebUI imports
|
||||
from open_webui.utils.chat import generate_chat_completion
|
||||
from open_webui.models.users import Users
|
||||
|
||||
# Logging setup
|
||||
logging.basicConfig(
|
||||
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# =================================================================
|
||||
# HTML 模板 - 过程导向设计,支持主题自适应
|
||||
# =================================================================
|
||||
HTML_WRAPPER_TEMPLATE = """
|
||||
<!-- OPENWEBUI_PLUGIN_OUTPUT -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="{user_language}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
:root {
|
||||
--dd-bg-primary: #ffffff;
|
||||
--dd-bg-secondary: #f8fafc;
|
||||
--dd-bg-tertiary: #f1f5f9;
|
||||
--dd-text-primary: #0f172a;
|
||||
--dd-text-secondary: #334155;
|
||||
--dd-text-dim: #64748b;
|
||||
--dd-border: #e2e8f0;
|
||||
--dd-accent: #3b82f6;
|
||||
--dd-accent-soft: #eff6ff;
|
||||
--dd-header-gradient: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
|
||||
--dd-shadow: 0 10px 40px rgba(0,0,0,0.06);
|
||||
--dd-code-bg: #f1f5f9;
|
||||
}
|
||||
.theme-dark {
|
||||
--dd-bg-primary: #1e293b;
|
||||
--dd-bg-secondary: #0f172a;
|
||||
--dd-bg-tertiary: #334155;
|
||||
--dd-text-primary: #f1f5f9;
|
||||
--dd-text-secondary: #e2e8f0;
|
||||
--dd-text-dim: #94a3b8;
|
||||
--dd-border: #475569;
|
||||
--dd-accent: #60a5fa;
|
||||
--dd-accent-soft: rgba(59, 130, 246, 0.15);
|
||||
--dd-header-gradient: linear-gradient(135deg, #0f172a 0%, #1e1e2e 100%);
|
||||
--dd-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
||||
--dd-code-bg: #334155;
|
||||
}
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
background-color: transparent;
|
||||
}
|
||||
#main-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.plugin-item {
|
||||
background: var(--dd-bg-primary);
|
||||
border-radius: 24px;
|
||||
box-shadow: var(--dd-shadow);
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--dd-border);
|
||||
}
|
||||
/* STYLES_INSERTION_POINT */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main-container">
|
||||
<!-- CONTENT_INSERTION_POINT -->
|
||||
</div>
|
||||
<!-- SCRIPTS_INSERTION_POINT -->
|
||||
<script>
|
||||
(function() {
|
||||
const parseColorLuma = (colorStr) => {
|
||||
if (!colorStr) return null;
|
||||
let m = colorStr.match(/^#?([0-9a-f]{6})$/i);
|
||||
if (m) {
|
||||
const hex = m[1];
|
||||
const r = parseInt(hex.slice(0, 2), 16);
|
||||
const g = parseInt(hex.slice(2, 4), 16);
|
||||
const b = parseInt(hex.slice(4, 6), 16);
|
||||
return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
|
||||
}
|
||||
m = colorStr.match(/rgba?\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/i);
|
||||
if (m) {
|
||||
const r = parseInt(m[1], 10);
|
||||
const g = parseInt(m[2], 10);
|
||||
const b = parseInt(m[3], 10);
|
||||
return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const getThemeFromMeta = (doc) => {
|
||||
const metas = Array.from((doc || document).querySelectorAll('meta[name="theme-color"]'));
|
||||
if (!metas.length) return null;
|
||||
const color = metas[metas.length - 1].content.trim();
|
||||
const luma = parseColorLuma(color);
|
||||
if (luma === null) return null;
|
||||
return luma < 0.5 ? 'dark' : 'light';
|
||||
};
|
||||
const getParentDocumentSafe = () => {
|
||||
try {
|
||||
if (!window.parent || window.parent === window) return null;
|
||||
const pDoc = window.parent.document;
|
||||
void pDoc.title;
|
||||
return pDoc;
|
||||
} catch (err) { return null; }
|
||||
};
|
||||
const getThemeFromParentClass = () => {
|
||||
try {
|
||||
if (!window.parent || window.parent === window) return null;
|
||||
const pDoc = window.parent.document;
|
||||
const html = pDoc.documentElement;
|
||||
const body = pDoc.body;
|
||||
const htmlClass = html ? html.className : '';
|
||||
const bodyClass = body ? body.className : '';
|
||||
const htmlDataTheme = html ? html.getAttribute('data-theme') : '';
|
||||
if (htmlDataTheme === 'dark' || bodyClass.includes('dark') || htmlClass.includes('dark')) return 'dark';
|
||||
if (htmlDataTheme === 'light' || bodyClass.includes('light') || htmlClass.includes('light')) return 'light';
|
||||
return null;
|
||||
} catch (err) { return null; }
|
||||
};
|
||||
const setTheme = () => {
|
||||
const parentDoc = getParentDocumentSafe();
|
||||
const metaTheme = parentDoc ? getThemeFromMeta(parentDoc) : null;
|
||||
const parentClassTheme = getThemeFromParentClass();
|
||||
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const chosen = metaTheme || parentClassTheme || (prefersDark ? 'dark' : 'light');
|
||||
document.documentElement.classList.toggle('theme-dark', chosen === 'dark');
|
||||
};
|
||||
setTheme();
|
||||
if (window.matchMedia) {
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', setTheme);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
# =================================================================
|
||||
# LLM 提示词 - 深度下潜思维链
|
||||
# =================================================================
|
||||
|
||||
SYSTEM_PROMPT = """
|
||||
你是一位“深度下潜 (Deep Dive)”分析专家。你的目标是引导用户完成一个全面的思维过程,从表面理解深入到战略行动。
|
||||
|
||||
## 思维结构 (严格遵守)
|
||||
|
||||
你必须从以下四个维度剖析输入内容:
|
||||
|
||||
### 1. 🔍 The Context (全景)
|
||||
提供一个高层级的全景视图。内容是关于什么的?核心情境、背景或正在解决的问题是什么?(2-3 段话)
|
||||
|
||||
### 2. 🧠 The Logic (脉络)
|
||||
解构底层结构。论点是如何构建的?其中的推理逻辑、隐藏假设或起作用的思维模型是什么?(列表形式)
|
||||
|
||||
### 3. 💎 The Insight (洞察)
|
||||
提取非显性的价值。有哪些“原来如此”的时刻?揭示了哪些深层含义、盲点或独特视角?(列表形式)
|
||||
|
||||
### 4. 🚀 The Path (路径)
|
||||
定义战略方向。具体的、按优先级排列的下一步行动是什么?如何立即应用这些知识?(可执行步骤)
|
||||
|
||||
## 规则
|
||||
- 使用用户指定的语言输出。
|
||||
- 保持专业、分析性且富有启发性的语调。
|
||||
- 聚焦于“理解的过程”,而不仅仅是结果。
|
||||
- 不要包含寒暄或元对话。
|
||||
"""
|
||||
|
||||
USER_PROMPT = """
|
||||
对以下内容发起“深度下潜”:
|
||||
|
||||
**用户上下文:**
|
||||
- 用户:{user_name}
|
||||
- 时间:{current_date_time_str}
|
||||
- 语言:{user_language}
|
||||
|
||||
**待分析内容:**
|
||||
```
|
||||
{long_text_content}
|
||||
```
|
||||
|
||||
请执行完整的思维链:全景 (Context) → 脉络 (Logic) → 洞察 (Insight) → 路径 (Path)。
|
||||
"""
|
||||
|
||||
# =================================================================
|
||||
# 现代 CSS 设计 - 深度下潜主题
|
||||
# =================================================================
|
||||
|
||||
CSS_TEMPLATE = """
|
||||
.deep-dive {
|
||||
font-family: 'Inter', -apple-system, system-ui, sans-serif;
|
||||
color: var(--dd-text-secondary);
|
||||
}
|
||||
|
||||
.dd-header {
|
||||
background: var(--dd-header-gradient);
|
||||
padding: 40px 32px;
|
||||
color: white;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dd-header-badge {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
border: 1px solid rgba(255,255,255,0.2);
|
||||
border-radius: 100px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.dd-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
margin: 0 0 12px 0;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.dd-meta {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.dd-body {
|
||||
padding: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 40px;
|
||||
position: relative;
|
||||
background: var(--dd-bg-primary);
|
||||
}
|
||||
|
||||
/* 思维导火索 */
|
||||
.dd-body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 52px;
|
||||
top: 40px;
|
||||
bottom: 40px;
|
||||
width: 2px;
|
||||
background: var(--dd-border);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.dd-step {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.dd-step-icon {
|
||||
flex-shrink: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: var(--dd-bg-primary);
|
||||
border: 2px solid var(--dd-border);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.25rem;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.03);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.dd-step:hover .dd-step-icon {
|
||||
border-color: var(--dd-accent);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.dd-step-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dd-step-label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--dd-accent);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.dd-step-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: var(--dd-text-primary);
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
|
||||
.dd-text {
|
||||
line-height: 1.7;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.dd-text p { margin-bottom: 16px; }
|
||||
.dd-text p:last-child { margin-bottom: 0; }
|
||||
|
||||
.dd-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dd-list-item {
|
||||
background: var(--dd-bg-secondary);
|
||||
padding: 16px 20px;
|
||||
border-radius: 12px;
|
||||
border-left: 4px solid var(--dd-border);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.dd-list-item:hover {
|
||||
background: var(--dd-bg-tertiary);
|
||||
border-left-color: var(--dd-accent);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.dd-list-item strong {
|
||||
color: var(--dd-text-primary);
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.dd-path-item {
|
||||
background: var(--dd-accent-soft);
|
||||
border-left-color: var(--dd-accent);
|
||||
}
|
||||
|
||||
.dd-footer {
|
||||
padding: 24px 32px;
|
||||
background: var(--dd-bg-secondary);
|
||||
border-top: 1px solid var(--dd-border);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.8rem;
|
||||
color: var(--dd-text-dim);
|
||||
}
|
||||
|
||||
.dd-tag {
|
||||
padding: 2px 8px;
|
||||
background: var(--dd-bg-tertiary);
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.dd-text code,
|
||||
.dd-list-item code {
|
||||
background: var(--dd-code-bg);
|
||||
color: var(--dd-text-primary);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: 'SF Mono', 'Consolas', 'Monaco', monospace;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.dd-list-item em {
|
||||
font-style: italic;
|
||||
color: var(--dd-text-dim);
|
||||
}
|
||||
"""
|
||||
|
||||
CONTENT_TEMPLATE = """
|
||||
<div class="deep-dive">
|
||||
<div class="dd-header">
|
||||
<div class="dd-header-badge">思维过程</div>
|
||||
<h1 class="dd-title">精读分析报告</h1>
|
||||
<div class="dd-meta">
|
||||
<span>👤 {user_name}</span>
|
||||
<span>📅 {current_date_time_str}</span>
|
||||
<span>📊 {word_count} 字</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dd-body">
|
||||
<!-- 第一步:全景 -->
|
||||
<div class="dd-step">
|
||||
<div class="dd-step-icon">🔍</div>
|
||||
<div class="dd-step-content">
|
||||
<div class="dd-step-label">Phase 01</div>
|
||||
<h2 class="dd-step-title">全景 (The Context)</h2>
|
||||
<div class="dd-text">{context_html}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第二步:脉络 -->
|
||||
<div class="dd-step">
|
||||
<div class="dd-step-icon">🧠</div>
|
||||
<div class="dd-step-content">
|
||||
<div class="dd-step-label">Phase 02</div>
|
||||
<h2 class="dd-step-title">脉络 (The Logic)</h2>
|
||||
<div class="dd-text">{logic_html}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第三步:洞察 -->
|
||||
<div class="dd-step">
|
||||
<div class="dd-step-icon">💎</div>
|
||||
<div class="dd-step-content">
|
||||
<div class="dd-step-label">Phase 03</div>
|
||||
<h2 class="dd-step-title">洞察 (The Insight)</h2>
|
||||
<div class="dd-text">{insight_html}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第四步:路径 -->
|
||||
<div class="dd-step">
|
||||
<div class="dd-step-icon">🚀</div>
|
||||
<div class="dd-step-content">
|
||||
<div class="dd-step-label">Phase 04</div>
|
||||
<h2 class="dd-step-title">路径 (The Path)</h2>
|
||||
<div class="dd-text">{path_html}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dd-footer">
|
||||
<span>Deep Dive Engine v1.0</span>
|
||||
<span><span class="dd-tag">AI 驱动分析</span></span>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
|
||||
class Action:
|
||||
class Valves(BaseModel):
|
||||
SHOW_STATUS: bool = Field(
|
||||
default=True,
|
||||
description="是否显示操作状态更新。",
|
||||
)
|
||||
SHOW_DEBUG_LOG: bool = Field(
|
||||
default=False,
|
||||
description="是否在浏览器控制台打印调试日志。",
|
||||
)
|
||||
MODEL_ID: str = Field(
|
||||
default="",
|
||||
description="用于分析的 LLM 模型 ID。留空则使用当前模型。",
|
||||
)
|
||||
MIN_TEXT_LENGTH: int = Field(
|
||||
default=200,
|
||||
description="深度下潜所需的最小文本长度(字符)。",
|
||||
)
|
||||
CLEAR_PREVIOUS_HTML: bool = Field(
|
||||
default=True,
|
||||
description="是否清除之前的插件结果。",
|
||||
)
|
||||
MESSAGE_COUNT: int = Field(
|
||||
default=1,
|
||||
description="要分析的最近消息数量。",
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.valves = self.Valves()
|
||||
|
||||
def _get_user_context(self, __user__: Optional[Dict[str, Any]]) -> Dict[str, str]:
|
||||
"""安全提取用户上下文信息。"""
|
||||
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_language": user_data.get("language", "zh-CN"),
|
||||
}
|
||||
|
||||
def _get_chat_context(
|
||||
self, body: dict, __metadata__: Optional[dict] = None
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
统一提取聊天上下文信息 (chat_id, message_id)。
|
||||
优先从 body 中提取,其次从 metadata 中提取。
|
||||
"""
|
||||
chat_id = ""
|
||||
message_id = ""
|
||||
|
||||
# 1. 尝试从 body 获取
|
||||
if isinstance(body, dict):
|
||||
chat_id = body.get("chat_id", "")
|
||||
message_id = body.get("id", "") # message_id 在 body 中通常是 id
|
||||
|
||||
# 再次检查 body.metadata
|
||||
if not chat_id or not message_id:
|
||||
body_metadata = body.get("metadata", {})
|
||||
if isinstance(body_metadata, dict):
|
||||
if not chat_id:
|
||||
chat_id = body_metadata.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = body_metadata.get("message_id", "")
|
||||
|
||||
# 2. 尝试从 __metadata__ 获取 (作为补充)
|
||||
if __metadata__ and isinstance(__metadata__, dict):
|
||||
if not chat_id:
|
||||
chat_id = __metadata__.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = __metadata__.get("message_id", "")
|
||||
|
||||
return {
|
||||
"chat_id": str(chat_id).strip(),
|
||||
"message_id": str(message_id).strip(),
|
||||
}
|
||||
|
||||
def _process_llm_output(self, llm_output: str) -> Dict[str, str]:
|
||||
"""解析 LLM 输出并转换为样式化 HTML。"""
|
||||
# 使用灵活的正则提取各部分
|
||||
context_match = re.search(
|
||||
r"###\s*1\.\s*🔍?\s*(?:全景|The Context)\s*(?:\((.*?)\))?\s*\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
logic_match = re.search(
|
||||
r"###\s*2\.\s*🧠?\s*(?:脉络|The Logic)\s*(?:\((.*?)\))?\s*\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
insight_match = re.search(
|
||||
r"###\s*3\.\s*💎?\s*(?:洞察|The Insight)\s*(?:\((.*?)\))?\s*\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
path_match = re.search(
|
||||
r"###\s*4\.\s*🚀?\s*(?:路径|The Path)\s*(?:\((.*?)\))?\s*\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
|
||||
# 兜底正则
|
||||
if not context_match:
|
||||
context_match = re.search(
|
||||
r"###\s*🔍?\s*(?:全景|The Context).*?\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
if not logic_match:
|
||||
logic_match = re.search(
|
||||
r"###\s*🧠?\s*(?:脉络|The Logic).*?\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
if not insight_match:
|
||||
insight_match = re.search(
|
||||
r"###\s*💎?\s*(?:洞察|The Insight).*?\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
if not path_match:
|
||||
path_match = re.search(
|
||||
r"###\s*🚀?\s*(?:路径|The Path).*?\n(.*?)(?=\n###|$)",
|
||||
llm_output,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
|
||||
context_md = (
|
||||
context_match.group(context_match.lastindex).strip()
|
||||
if context_match
|
||||
else ""
|
||||
)
|
||||
logic_md = (
|
||||
logic_match.group(logic_match.lastindex).strip() if logic_match else ""
|
||||
)
|
||||
insight_md = (
|
||||
insight_match.group(insight_match.lastindex).strip()
|
||||
if insight_match
|
||||
else ""
|
||||
)
|
||||
path_md = path_match.group(path_match.lastindex).strip() if path_match else ""
|
||||
|
||||
if not any([context_md, logic_md, insight_md, path_md]):
|
||||
context_md = llm_output.strip()
|
||||
logger.warning("LLM 输出未遵循格式,将作为全景处理。")
|
||||
|
||||
md_extensions = ["nl2br"]
|
||||
|
||||
context_html = (
|
||||
markdown.markdown(context_md, extensions=md_extensions)
|
||||
if context_md
|
||||
else '<p class="dd-no-content">未能提取全景信息。</p>'
|
||||
)
|
||||
logic_html = (
|
||||
self._process_list_items(logic_md, "logic")
|
||||
if logic_md
|
||||
else '<p class="dd-no-content">未能解构脉络。</p>'
|
||||
)
|
||||
insight_html = (
|
||||
self._process_list_items(insight_md, "insight")
|
||||
if insight_md
|
||||
else '<p class="dd-no-content">未能发现洞察。</p>'
|
||||
)
|
||||
path_html = (
|
||||
self._process_list_items(path_md, "path")
|
||||
if path_md
|
||||
else '<p class="dd-no-content">未能定义路径。</p>'
|
||||
)
|
||||
|
||||
return {
|
||||
"context_html": context_html,
|
||||
"logic_html": logic_html,
|
||||
"insight_html": insight_html,
|
||||
"path_html": path_html,
|
||||
}
|
||||
|
||||
def _process_list_items(self, md_content: str, section_type: str) -> str:
|
||||
"""将 markdown 列表转换为样式化卡片,支持完整的 markdown 格式。"""
|
||||
lines = md_content.strip().split("\n")
|
||||
items = []
|
||||
current_paragraph = []
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
|
||||
# 检查列表项(无序或有序)
|
||||
bullet_match = re.match(r"^[-*]\s+(.+)$", line)
|
||||
numbered_match = re.match(r"^\d+\.\s+(.+)$", line)
|
||||
|
||||
if bullet_match or numbered_match:
|
||||
# 清空累积的段落
|
||||
if current_paragraph:
|
||||
para_text = " ".join(current_paragraph)
|
||||
para_html = self._convert_inline_markdown(para_text)
|
||||
items.append(f"<p>{para_html}</p>")
|
||||
current_paragraph = []
|
||||
|
||||
# 提取列表项内容
|
||||
text = (
|
||||
bullet_match.group(1) if bullet_match else numbered_match.group(1)
|
||||
)
|
||||
|
||||
# 处理粗体标题模式:**标题:** 描述 或 **标题**: 描述
|
||||
title_match = re.match(r"\*\*(.+?)\*\*[:\s:]*(.*)$", text)
|
||||
if title_match:
|
||||
title = self._convert_inline_markdown(title_match.group(1))
|
||||
desc = self._convert_inline_markdown(title_match.group(2).strip())
|
||||
path_class = "dd-path-item" if section_type == "path" else ""
|
||||
item_html = f'<div class="dd-list-item {path_class}"><strong>{title}</strong>{desc}</div>'
|
||||
else:
|
||||
text_html = self._convert_inline_markdown(text)
|
||||
path_class = "dd-path-item" if section_type == "path" else ""
|
||||
item_html = (
|
||||
f'<div class="dd-list-item {path_class}">{text_html}</div>'
|
||||
)
|
||||
items.append(item_html)
|
||||
elif line and not line.startswith("#"):
|
||||
# 累积段落文本
|
||||
current_paragraph.append(line)
|
||||
elif not line and current_paragraph:
|
||||
# 空行结束段落
|
||||
para_text = " ".join(current_paragraph)
|
||||
para_html = self._convert_inline_markdown(para_text)
|
||||
items.append(f"<p>{para_html}</p>")
|
||||
current_paragraph = []
|
||||
|
||||
# 清空剩余段落
|
||||
if current_paragraph:
|
||||
para_text = " ".join(current_paragraph)
|
||||
para_html = self._convert_inline_markdown(para_text)
|
||||
items.append(f"<p>{para_html}</p>")
|
||||
|
||||
if items:
|
||||
return f'<div class="dd-list">{" ".join(items)}</div>'
|
||||
return f'<p class="dd-no-content">未找到条目。</p>'
|
||||
|
||||
def _convert_inline_markdown(self, text: str) -> str:
|
||||
"""将行内 markdown(粗体、斜体、代码)转换为 HTML。"""
|
||||
# 转换行内代码:`code` -> <code>code</code>
|
||||
text = re.sub(r"`([^`]+)`", r"<code>\1</code>", text)
|
||||
# 转换粗体:**text** -> <strong>text</strong>
|
||||
text = re.sub(r"\*\*(.+?)\*\*", r"<strong>\1</strong>", text)
|
||||
# 转换斜体:*text* -> <em>text</em>(但不在 ** 内部)
|
||||
text = re.sub(r"(?<!\*)\*([^*]+)\*(?!\*)", r"<em>\1</em>", text)
|
||||
return text
|
||||
|
||||
async def _emit_status(
|
||||
self,
|
||||
emitter: Optional[Callable[[Any], Awaitable[None]]],
|
||||
description: str,
|
||||
done: bool = False,
|
||||
):
|
||||
"""发送状态更新事件。"""
|
||||
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,
|
||||
ntype: str = "info",
|
||||
):
|
||||
"""发送通知事件。"""
|
||||
if emitter:
|
||||
await emitter(
|
||||
{"type": "notification", "data": {"type": ntype, "content": content}}
|
||||
)
|
||||
|
||||
async def _emit_debug_log(self, emitter, title: str, data: dict):
|
||||
"""在浏览器控制台打印结构化调试日志"""
|
||||
if not self.valves.SHOW_DEBUG_LOG or not emitter:
|
||||
return
|
||||
|
||||
try:
|
||||
import json
|
||||
|
||||
js_code = f"""
|
||||
(async function() {{
|
||||
console.group("🛠️ {title}");
|
||||
console.log({json.dumps(data, ensure_ascii=False)});
|
||||
console.groupEnd();
|
||||
}})();
|
||||
"""
|
||||
|
||||
await emitter({"type": "execute", "data": {"code": js_code}})
|
||||
except Exception as e:
|
||||
print(f"Error emitting debug log: {e}")
|
||||
|
||||
def _remove_existing_html(self, content: str) -> str:
|
||||
"""移除已有的插件生成的 HTML。"""
|
||||
pattern = r"```html\s*<!-- OPENWEBUI_PLUGIN_OUTPUT -->[\s\S]*?```"
|
||||
return re.sub(pattern, "", content).strip()
|
||||
|
||||
def _extract_text_content(self, content) -> str:
|
||||
"""从消息内容中提取文本。"""
|
||||
if isinstance(content, str):
|
||||
return content
|
||||
elif isinstance(content, list):
|
||||
text_parts = []
|
||||
for item in content:
|
||||
if isinstance(item, dict) and item.get("type") == "text":
|
||||
text_parts.append(item.get("text", ""))
|
||||
elif isinstance(item, str):
|
||||
text_parts.append(item)
|
||||
return "\n".join(text_parts)
|
||||
return str(content) if content else ""
|
||||
|
||||
def _merge_html(
|
||||
self,
|
||||
existing_html: str,
|
||||
new_content: str,
|
||||
new_styles: str = "",
|
||||
user_language: str = "zh-CN",
|
||||
) -> str:
|
||||
"""合并新内容到 HTML 容器。"""
|
||||
if "<!-- OPENWEBUI_PLUGIN_OUTPUT -->" in existing_html:
|
||||
base_html = re.sub(r"^```html\s*", "", existing_html)
|
||||
base_html = re.sub(r"\s*```$", "", base_html)
|
||||
else:
|
||||
base_html = HTML_WRAPPER_TEMPLATE.replace("{user_language}", user_language)
|
||||
|
||||
wrapped = f'<div class="plugin-item">\n{new_content}\n</div>'
|
||||
|
||||
if new_styles:
|
||||
base_html = base_html.replace(
|
||||
"/* STYLES_INSERTION_POINT */",
|
||||
f"{new_styles}\n/* STYLES_INSERTION_POINT */",
|
||||
)
|
||||
|
||||
base_html = base_html.replace(
|
||||
"<!-- CONTENT_INSERTION_POINT -->",
|
||||
f"{wrapped}\n<!-- CONTENT_INSERTION_POINT -->",
|
||||
)
|
||||
|
||||
return base_html.strip()
|
||||
|
||||
def _build_content_html(self, context: dict) -> str:
|
||||
"""构建内容 HTML。"""
|
||||
html = CONTENT_TEMPLATE
|
||||
for key, value in context.items():
|
||||
html = html.replace(f"{{{key}}}", str(value))
|
||||
return html
|
||||
|
||||
async def action(
|
||||
self,
|
||||
body: dict,
|
||||
__user__: Optional[Dict[str, Any]] = None,
|
||||
__event_emitter__: Optional[Callable[[Any], Awaitable[None]]] = None,
|
||||
__request__: Optional[Request] = None,
|
||||
) -> Optional[dict]:
|
||||
logger.info("Action: 精读 v1.0.0 启动")
|
||||
|
||||
user_ctx = self._get_user_context(__user__)
|
||||
user_id = user_ctx["user_id"]
|
||||
user_name = user_ctx["user_name"]
|
||||
user_language = user_ctx["user_language"]
|
||||
|
||||
now = datetime.now()
|
||||
current_date_time_str = now.strftime("%Y年%m月%d日 %H:%M")
|
||||
|
||||
original_content = ""
|
||||
try:
|
||||
messages = body.get("messages", [])
|
||||
if not messages:
|
||||
raise ValueError("未找到消息内容。")
|
||||
|
||||
message_count = min(self.valves.MESSAGE_COUNT, len(messages))
|
||||
recent_messages = messages[-message_count:]
|
||||
|
||||
aggregated_parts = []
|
||||
for msg in recent_messages:
|
||||
text = self._extract_text_content(msg.get("content"))
|
||||
if text:
|
||||
aggregated_parts.append(text)
|
||||
|
||||
if not aggregated_parts:
|
||||
raise ValueError("未找到文本内容。")
|
||||
|
||||
original_content = "\n\n---\n\n".join(aggregated_parts)
|
||||
word_count = len(original_content)
|
||||
|
||||
if len(original_content) < self.valves.MIN_TEXT_LENGTH:
|
||||
msg = f"内容过短({len(original_content)} 字符)。精读至少需要 {self.valves.MIN_TEXT_LENGTH} 字符才能进行有意义的分析。"
|
||||
await self._emit_notification(__event_emitter__, msg, "warning")
|
||||
return {"messages": [{"role": "assistant", "content": f"⚠️ {msg}"}]}
|
||||
|
||||
await self._emit_notification(
|
||||
__event_emitter__, "📖 正在发起精读分析...", "info"
|
||||
)
|
||||
await self._emit_status(
|
||||
__event_emitter__, "📖 精读:正在分析全景与脉络...", False
|
||||
)
|
||||
|
||||
prompt = USER_PROMPT.format(
|
||||
user_name=user_name,
|
||||
current_date_time_str=current_date_time_str,
|
||||
user_language=user_language,
|
||||
long_text_content=original_content,
|
||||
)
|
||||
|
||||
model = self.valves.MODEL_ID or body.get("model")
|
||||
payload = {
|
||||
"model": model,
|
||||
"messages": [
|
||||
{"role": "system", "content": SYSTEM_PROMPT},
|
||||
{"role": "user", "content": prompt},
|
||||
],
|
||||
"stream": False,
|
||||
}
|
||||
|
||||
user_obj = Users.get_user_by_id(user_id)
|
||||
if not user_obj:
|
||||
raise ValueError(f"未找到用户:{user_id}")
|
||||
|
||||
response = await generate_chat_completion(__request__, payload, user_obj)
|
||||
llm_output = response["choices"][0]["message"]["content"]
|
||||
|
||||
processed = self._process_llm_output(llm_output)
|
||||
|
||||
context = {
|
||||
"user_name": user_name,
|
||||
"current_date_time_str": current_date_time_str,
|
||||
"word_count": word_count,
|
||||
**processed,
|
||||
}
|
||||
|
||||
content_html = self._build_content_html(context)
|
||||
|
||||
# 处理已有 HTML
|
||||
existing = ""
|
||||
match = re.search(
|
||||
r"```html\s*(<!-- OPENWEBUI_PLUGIN_OUTPUT -->[\s\S]*?)```",
|
||||
original_content,
|
||||
)
|
||||
if match:
|
||||
existing = match.group(1)
|
||||
|
||||
if self.valves.CLEAR_PREVIOUS_HTML or not existing:
|
||||
original_content = self._remove_existing_html(original_content)
|
||||
final_html = self._merge_html(
|
||||
"", content_html, CSS_TEMPLATE, user_language
|
||||
)
|
||||
else:
|
||||
original_content = self._remove_existing_html(original_content)
|
||||
final_html = self._merge_html(
|
||||
existing, content_html, CSS_TEMPLATE, user_language
|
||||
)
|
||||
|
||||
body["messages"][-1][
|
||||
"content"
|
||||
] = f"{original_content}\n\n```html\n{final_html}\n```"
|
||||
|
||||
await self._emit_status(__event_emitter__, "📖 精读完成!", True)
|
||||
await self._emit_notification(
|
||||
__event_emitter__,
|
||||
f"📖 精读完成,{user_name}!思维链已生成。",
|
||||
"success",
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Deep Dive 错误:{e}", exc_info=True)
|
||||
body["messages"][-1][
|
||||
"content"
|
||||
] = f"{original_content}\n\n❌ **错误:** {str(e)}"
|
||||
await self._emit_status(__event_emitter__, "精读失败。", True)
|
||||
await self._emit_notification(__event_emitter__, f"错误:{str(e)}", "error")
|
||||
|
||||
return body
|
||||
@@ -1,14 +1,15 @@
|
||||
# 📝 Export to Word (Enhanced)
|
||||
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.4.3 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **Version:** 0.4.4 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **License:** MIT
|
||||
|
||||
Export conversation to Word (.docx) with **syntax highlighting**, **native math equations**, **Mermaid diagrams**, **citations**, and **enhanced table formatting**.
|
||||
|
||||
## 🔥 What's New in v0.4.3
|
||||
## 🔥 What's New in v0.4.4
|
||||
|
||||
- ✨ **S3 Object Storage Support**: Direct access to images stored in S3/MinIO via boto3, bypassing API layer for faster exports.
|
||||
- 🔧 **Multi-level File Fallback**: 6-level fallback mechanism for file retrieval (DB → S3 → Local → URL → API → Attributes).
|
||||
- 🛡️ **Improved Error Handling**: Better logging and error messages for file retrieval failures.
|
||||
- 🧹 **Content Cleanup**: Enhanced stripping of `<details>` blocks (often used for tool calls/thinking process) to ensure a clean final document.
|
||||
- 📄 **Standard Document Formatting**: Applied professional document formatting standards for titles and headings (centered title, bold, optimized font sizes and spacing), including GB/T compliance for Chinese content.
|
||||
- 🔠 **Font Rendering Fix**: Fixed an issue where CJK characters would fallback to MS Gothic in Word; now correctly uses the configured Asian font (e.g., SimSun).
|
||||
- ⚙️ **Title Alignment**: Added `TITLE_ALIGNMENT` valve to configure document title alignment (left, center, right).
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
@@ -44,6 +45,10 @@ Export conversation to Word (.docx) with **syntax highlighting**, **native math
|
||||
| **Mermaid PNG Scale** | `3.0` | Resolution multiplier for Mermaid images |
|
||||
| **Math Enable** | `True` | Enable LaTeX math conversion |
|
||||
|
||||
## ⭐ Support
|
||||
|
||||
If this plugin has been useful, a star on [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) is a big motivation for me. Thank you for the support.
|
||||
|
||||
## 🛠️ Supported Markdown Syntax
|
||||
|
||||
| Syntax | Word Result |
|
||||
@@ -71,18 +76,13 @@ Export conversation to Word (.docx) with **syntax highlighting**, **native math
|
||||
- `latex2mathml` - LaTeX to MathML conversion
|
||||
- `mathml2omml` - MathML to Office Math (OMML) conversion
|
||||
|
||||
## Troubleshooting ❓
|
||||
|
||||
- **Plugin not working?**: Check if the filter/action is enabled in the model settings.
|
||||
- **Debug Logs**: Check the browser console (F12) for detailed logs if available.
|
||||
- **Error Messages**: If you see an error, please copy the full error message and report it.
|
||||
- **Submit an Issue**: If you encounter any problems, please submit an issue on GitHub: [Awesome OpenWebUI Issues](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
|
||||
## 📝 Changelog
|
||||
|
||||
### v0.4.3
|
||||
- **S3 Object Storage**: Direct S3/MinIO access via boto3 for faster image retrieval.
|
||||
- **6-Level Fallback**: Robust file retrieval: DB → S3 → Local → URL → API → Attributes.
|
||||
- **Better Logging**: Improved error messages for debugging file access issues.
|
||||
|
||||
### v0.4.1
|
||||
- **Chinese Parameter Names**: Localized configuration names for Chinese version.
|
||||
|
||||
### v0.4.0
|
||||
- **Multi-language Support**: UI language switching (English/Chinese).
|
||||
- **Font & Style Configuration**: Customizable fonts and table colors.
|
||||
- **Mermaid Enhancements**: Hybrid SVG+PNG rendering, background color config.
|
||||
- **Performance**: Real-time progress updates for large exports.
|
||||
See the full history on GitHub: [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
# 📝 导出为 Word (增强版)
|
||||
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.4.3 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **Version:** 0.4.4 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **许可证:** MIT
|
||||
|
||||
将对话导出为 Word (.docx),支持**代码语法高亮**、**原生数学公式**、**Mermaid 图表**、**引用参考**和**增强表格格式**。
|
||||
|
||||
## 🔥 v0.4.3 更新内容
|
||||
## 🔥 v0.4.4 更新内容
|
||||
|
||||
- ✨ **S3 对象存储支持**: 通过 boto3 直连 S3/MinIO,绕过 API 层,导出速度更快。
|
||||
- 🔧 **多级文件回退**: 6 级文件获取机制(数据库 → S3 → 本地 → URL → API → 属性)。
|
||||
- 🛡️ **错误处理优化**: 更完善的日志记录和错误提示,便于调试文件访问问题。
|
||||
- 🧹 **内容清理加强**: 增强了对 `<details>` 块(通常包含工具调用或思考过程)的清理,确保最终文档整洁。
|
||||
- 📄 **文档格式标准化**: 采用了专业的文档排版标准(兼容中文 GB/T 规范),标题居中加粗,各级标题使用标准字号和间距。
|
||||
- 🔠 **字体渲染修复**: 修复了 CJK 字符在 Word 中回退到 MS Gothic 的问题;现在正确使用配置的中文字体(例如宋体)。
|
||||
- ⚙️ **标题对齐配置**: 新增 `标题对齐方式` Valve,支持配置文档标题的对齐方式(左对齐、居中、右对齐)。
|
||||
|
||||
## ✨ 核心特性
|
||||
|
||||
@@ -44,6 +45,10 @@
|
||||
| **Mermaid_PNG缩放比例** | `3.0` | Mermaid 图片分辨率倍数 |
|
||||
| **启用数学公式** | `True` | 启用 LaTeX 公式转换 |
|
||||
|
||||
## ⭐ 支持
|
||||
|
||||
如果这个插件对你有帮助,欢迎到 [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) 点个 Star,这将是我持续改进的动力,感谢支持。
|
||||
|
||||
## 🛠️ 支持的 Markdown 语法
|
||||
|
||||
| 语法 | Word 效果 |
|
||||
@@ -71,18 +76,13 @@
|
||||
- `latex2mathml` - LaTeX 转 MathML
|
||||
- `mathml2omml` - MathML 转 Office Math (OMML)
|
||||
|
||||
## 故障排除 (Troubleshooting) ❓
|
||||
|
||||
- **插件不工作?**: 请检查是否在模型设置中启用了该过滤器/动作。
|
||||
- **调试日志**: 请查看浏览器控制台 (F12) 获取详细日志(如果可用)。
|
||||
- **错误信息**: 如果看到错误,请复制完整的错误信息并报告。
|
||||
- **提交 Issue**: 如果遇到任何问题,请在 GitHub 上提交 Issue:[Awesome OpenWebUI Issues](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
|
||||
## 📝 更新日志
|
||||
|
||||
### v0.4.3
|
||||
- **S3 对象存储**: 通过 boto3 直连 S3/MinIO,图片获取速度更快。
|
||||
- **6 级回退机制**: 稳健的文件获取:数据库 → S3 → 本地 → URL → API → 属性。
|
||||
- **日志优化**: 改进错误提示,便于调试文件访问问题。
|
||||
|
||||
### v0.4.1
|
||||
- **中文参数名**: 配置项名称和描述全部汉化。
|
||||
|
||||
### v0.4.0
|
||||
- **多语言支持**: 界面语言切换(中文/英文)。
|
||||
- **字体与样式配置**: 支持自定义中英文字体、代码字体以及表格颜色。
|
||||
- **Mermaid 增强**: 混合 SVG+PNG 渲染,支持背景色配置。
|
||||
- **性能优化**: 导出大型文档时提供实时进度反馈。
|
||||
完整历史请查看 GitHub 项目: [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
BIN
plugins/actions/export_to_docx/export_to_word.png
Normal file
BIN
plugins/actions/export_to_docx/export_to_word.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
@@ -1,9 +1,9 @@
|
||||
"""
|
||||
title: Export to Word (Enhanced)
|
||||
title: Export to Word Enhanced
|
||||
author: Fu-Jie
|
||||
author_url: https://github.com/Fu-Jie
|
||||
funding_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
version: 0.4.3
|
||||
author_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
funding_url: https://github.com/open-webui
|
||||
version: 0.4.4
|
||||
openwebui_id: fca6a315-2a45-42cc-8c96-55cbc85f87f2
|
||||
icon_url: data:image/svg+xml;base64,PHN2ZwogIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICB3aWR0aD0iMjQiCiAgaGVpZ2h0PSIyNCIKICB2aWV3Qm94PSIwIDAgMjQgMjQiCiAgZmlsbD0ibm9uZSIKICBzdHJva2U9ImN1cnJlbnRDb2xvciIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNNiAyMmEyIDIgMCAwIDEtMi0yVjRhMiAyIDAgMCAxIDItMmg4YTIuNCAyLjQgMCAwIDEgMS43MDQuNzA2bDMuNTg4IDMuNTg4QTIuNCAyLjQgMCAwIDEgMjAgOHYxMmEyIDIgMCAwIDEtMiAyeiIgLz4KICA8cGF0aCBkPSJNMTQgMnY1YTEgMSAwIDAgMCAxIDFoNSIgLz4KICA8cGF0aCBkPSJNMTAgOUg4IiAvPgogIDxwYXRoIGQ9Ik0xNiAxM0g4IiAvPgogIDxwYXRoIGQ9Ik0xNiAxN0g4IiAvPgo8L3N2Zz4K
|
||||
requirements: python-docx, Pygments, latex2mathml, mathml2omml
|
||||
@@ -101,9 +101,8 @@ _TRANSPARENT_1PX_PNG = base64.b64decode(
|
||||
_ASVG_NS = "http://schemas.microsoft.com/office/drawing/2016/SVG/main"
|
||||
nsmap.setdefault("asvg", _ASVG_NS)
|
||||
|
||||
_REASONING_DETAILS_RE = re.compile(
|
||||
r"<details\b[^>]*\btype\s*=\s*(?:\"reasoning\"|'reasoning'|reasoning)[^>]*>.*?</details\s*>",
|
||||
re.IGNORECASE | re.DOTALL,
|
||||
_ALL_DETAILS_RE = re.compile(
|
||||
r"<details\b[^>]*>.*?</details\s*>", re.IGNORECASE | re.DOTALL
|
||||
)
|
||||
_THINK_RE = re.compile(r"<think\b[^>]*>.*?</think\s*>", re.IGNORECASE | re.DOTALL)
|
||||
_ANALYSIS_RE = re.compile(
|
||||
@@ -150,6 +149,14 @@ class Action:
|
||||
default="chat_title",
|
||||
description="Title Source: 'chat_title' (Chat Title), 'ai_generated' (AI Generated), 'markdown_title' (Markdown Title)",
|
||||
)
|
||||
SHOW_STATUS: bool = Field(
|
||||
default=True,
|
||||
description="Whether to show operation status updates.",
|
||||
)
|
||||
SHOW_DEBUG_LOG: bool = Field(
|
||||
default=False,
|
||||
description="Whether to print debug logs in the browser console.",
|
||||
)
|
||||
|
||||
MAX_EMBED_IMAGE_MB: int = Field(
|
||||
default=20,
|
||||
@@ -170,6 +177,12 @@ class Action:
|
||||
description="Font for code blocks and inline code (e.g., 'Consolas', 'Courier New', 'Monaco')",
|
||||
)
|
||||
|
||||
# Title alignment
|
||||
TITLE_ALIGNMENT: str = Field(
|
||||
default="center",
|
||||
description="Title alignment: 'left', 'center', or 'right'",
|
||||
)
|
||||
|
||||
# Table styling
|
||||
TABLE_HEADER_COLOR: str = Field(
|
||||
default="F2F2F2",
|
||||
@@ -234,60 +247,60 @@ class Action:
|
||||
)
|
||||
|
||||
class UserValves(BaseModel):
|
||||
TITLE_SOURCE: str = Field(
|
||||
default="chat_title",
|
||||
TITLE_SOURCE: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Title Source: 'chat_title' (Chat Title), 'ai_generated' (AI Generated), 'markdown_title' (Markdown Title)",
|
||||
)
|
||||
UI_LANGUAGE: str = Field(
|
||||
default="en",
|
||||
UI_LANGUAGE: Optional[str] = Field(
|
||||
default=None,
|
||||
description="UI language for export messages. Options: 'en' (English), 'zh' (Chinese)",
|
||||
)
|
||||
FONT_LATIN: str = Field(
|
||||
default="Times New Roman",
|
||||
FONT_LATIN: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Font for Latin characters (e.g., 'Times New Roman', 'Calibri', 'Arial')",
|
||||
)
|
||||
FONT_ASIAN: str = Field(
|
||||
default="SimSun",
|
||||
FONT_ASIAN: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Font for Asian characters (e.g., 'SimSun', 'Microsoft YaHei', 'PingFang SC')",
|
||||
)
|
||||
FONT_CODE: str = Field(
|
||||
default="Consolas",
|
||||
FONT_CODE: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Font for code blocks and inline code (e.g., 'Consolas', 'Courier New', 'Monaco')",
|
||||
)
|
||||
TABLE_HEADER_COLOR: str = Field(
|
||||
default="F2F2F2",
|
||||
TABLE_HEADER_COLOR: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Table header background color (hex, without #)",
|
||||
)
|
||||
TABLE_ZEBRA_COLOR: str = Field(
|
||||
default="FBFBFB",
|
||||
TABLE_ZEBRA_COLOR: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Table zebra stripe background color for alternate rows (hex, without #)",
|
||||
)
|
||||
MERMAID_PNG_SCALE: float = Field(
|
||||
default=3.0,
|
||||
MERMAID_PNG_SCALE: Optional[float] = Field(
|
||||
default=None,
|
||||
description="PNG render resolution multiplier (higher = clearer, larger file)",
|
||||
)
|
||||
MERMAID_DISPLAY_SCALE: float = Field(
|
||||
default=1.0,
|
||||
MERMAID_DISPLAY_SCALE: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Diagram width relative to available page width (<=1 recommended)",
|
||||
)
|
||||
MERMAID_OPTIMIZE_LAYOUT: bool = Field(
|
||||
default=False,
|
||||
MERMAID_OPTIMIZE_LAYOUT: Optional[bool] = Field(
|
||||
default=None,
|
||||
description="Optimize Mermaid layout: convert LR to TD for graph/flowchart",
|
||||
)
|
||||
MERMAID_BACKGROUND: str = Field(
|
||||
default="",
|
||||
MERMAID_BACKGROUND: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Mermaid background color. Empty = transparent (recommended for Word dark mode). Used only for optional PNG fill.",
|
||||
)
|
||||
MERMAID_CAPTIONS_ENABLE: bool = Field(
|
||||
default=True,
|
||||
MERMAID_CAPTIONS_ENABLE: Optional[bool] = Field(
|
||||
default=None,
|
||||
description="Add figure captions under Mermaid images/charts",
|
||||
)
|
||||
MATH_ENABLE: bool = Field(
|
||||
default=True,
|
||||
MATH_ENABLE: Optional[bool] = Field(
|
||||
default=None,
|
||||
description="Enable LaTeX math block conversion (\\\\[...\\\\] and $$...$$) into Word equations",
|
||||
)
|
||||
MATH_INLINE_DOLLAR_ENABLE: bool = Field(
|
||||
default=True,
|
||||
MATH_INLINE_DOLLAR_ENABLE: Optional[bool] = Field(
|
||||
default=None,
|
||||
description="Enable inline $...$ math conversion into Word equations (conservative parsing to reduce false positives)",
|
||||
)
|
||||
|
||||
@@ -320,10 +333,100 @@ class Action:
|
||||
return msg
|
||||
return msg
|
||||
|
||||
async def _send_notification(self, emitter: Callable, type: str, content: str):
|
||||
await emitter(
|
||||
{"type": "notification", "data": {"type": type, "content": content}}
|
||||
)
|
||||
def _get_user_context(self, __user__: Optional[Dict[str, Any]]) -> Dict[str, str]:
|
||||
"""Safely 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"),
|
||||
}
|
||||
|
||||
def _get_chat_context(
|
||||
self, body: dict, __metadata__: Optional[dict] = None
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Unified extraction of chat context information (chat_id, message_id).
|
||||
Prioritizes extraction from body, then metadata.
|
||||
"""
|
||||
chat_id = ""
|
||||
message_id = ""
|
||||
|
||||
# 1. Try to get from body
|
||||
if isinstance(body, dict):
|
||||
chat_id = body.get("chat_id", "")
|
||||
message_id = body.get("id", "") # message_id is usually 'id' in body
|
||||
|
||||
# Check body.metadata as fallback
|
||||
if not chat_id or not message_id:
|
||||
body_metadata = body.get("metadata", {})
|
||||
if isinstance(body_metadata, dict):
|
||||
if not chat_id:
|
||||
chat_id = body_metadata.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = body_metadata.get("message_id", "")
|
||||
|
||||
# 2. Try to get from __metadata__ (as supplement)
|
||||
if __metadata__ and isinstance(__metadata__, dict):
|
||||
if not chat_id:
|
||||
chat_id = __metadata__.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = __metadata__.get("message_id", "")
|
||||
|
||||
return {
|
||||
"chat_id": str(chat_id).strip(),
|
||||
"message_id": str(message_id).strip(),
|
||||
}
|
||||
|
||||
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,
|
||||
ntype: str = "info",
|
||||
):
|
||||
"""Emits a notification event (info, success, warning, error)."""
|
||||
if emitter:
|
||||
await emitter(
|
||||
{"type": "notification", "data": {"type": ntype, "content": content}}
|
||||
)
|
||||
|
||||
async def _emit_debug_log(self, emitter, title: str, data: dict):
|
||||
"""Print structured debug logs in the browser console"""
|
||||
if not self.valves.SHOW_DEBUG_LOG or not emitter:
|
||||
return
|
||||
|
||||
try:
|
||||
import json
|
||||
|
||||
js_code = f"""
|
||||
(async function() {{
|
||||
console.group("🛠️ {title}");
|
||||
console.log({json.dumps(data, ensure_ascii=False)});
|
||||
console.groupEnd();
|
||||
}})();
|
||||
"""
|
||||
|
||||
await emitter({"type": "execute", "data": {"code": js_code}})
|
||||
except Exception as e:
|
||||
print(f"Error emitting debug log: {e}")
|
||||
|
||||
async def action(
|
||||
self,
|
||||
@@ -351,13 +454,21 @@ class Action:
|
||||
user_id = __user__.get("id", "unknown_user")
|
||||
|
||||
# Apply UserValves if present
|
||||
if __user__ and "valves" in __user__:
|
||||
# Update self.valves with user-specific values
|
||||
# Note: This assumes per-request instantiation or that we are okay with modifying the singleton.
|
||||
# Given the plugin architecture, we'll update it for this execution.
|
||||
for key, value in __user__["valves"].model_dump().items():
|
||||
if hasattr(self.valves, key):
|
||||
setattr(self.valves, key, value)
|
||||
if __user__:
|
||||
# Robustly parse UserValves whether it's a dict or Pydantic model
|
||||
raw_valves = __user__.get("valves", {})
|
||||
if isinstance(raw_valves, self.UserValves):
|
||||
user_valves = raw_valves
|
||||
elif isinstance(raw_valves, dict):
|
||||
user_valves = self.UserValves(**raw_valves)
|
||||
else:
|
||||
user_valves = None
|
||||
|
||||
if user_valves:
|
||||
for key, value in user_valves.model_dump(exclude_unset=True).items():
|
||||
# Only override if the value is not None (and explicitly set)
|
||||
if hasattr(self.valves, key) and value is not None:
|
||||
setattr(self.valves, key, value)
|
||||
|
||||
# Get user language from Valves configuration
|
||||
self._user_lang = self._get_lang_key(self.valves.UI_LANGUAGE)
|
||||
@@ -394,17 +505,49 @@ class Action:
|
||||
try:
|
||||
message_content = last_assistant_message["content"]
|
||||
if isinstance(message_content, str):
|
||||
if __event_emitter__ and self.valves.SHOW_DEBUG_LOG:
|
||||
debug_data = {}
|
||||
for name, regex in [
|
||||
("Details Block", _ALL_DETAILS_RE),
|
||||
("Think Block", _THINK_RE),
|
||||
("Analysis Block", _ANALYSIS_RE),
|
||||
]:
|
||||
matches = regex.findall(message_content)
|
||||
if matches:
|
||||
debug_data[name] = [
|
||||
(m[:200] + "...") if len(m) > 200 else m
|
||||
for m in matches
|
||||
]
|
||||
if debug_data:
|
||||
await self._emit_debug_log(
|
||||
__event_emitter__,
|
||||
"Context Stripping Analysis",
|
||||
debug_data,
|
||||
)
|
||||
|
||||
# Log font configuration
|
||||
await self._emit_debug_log(
|
||||
__event_emitter__,
|
||||
"Font Configuration",
|
||||
{
|
||||
"Latin Font": self.valves.FONT_LATIN,
|
||||
"Asian Font": self.valves.FONT_ASIAN,
|
||||
"Code Font": self.valves.FONT_CODE,
|
||||
},
|
||||
)
|
||||
|
||||
message_content = self._strip_reasoning_blocks(message_content)
|
||||
|
||||
if not message_content or not message_content.strip():
|
||||
await self._send_notification(
|
||||
__event_emitter__, "error", self._get_msg("error_no_content")
|
||||
await self._emit_notification(
|
||||
__event_emitter__, self._get_msg("error_no_content"), "error"
|
||||
)
|
||||
return
|
||||
|
||||
# Generate filename
|
||||
title = ""
|
||||
chat_id = self.extract_chat_id(body, __metadata__)
|
||||
chat_ctx = self._get_chat_context(body, __metadata__)
|
||||
chat_id = chat_ctx["chat_id"]
|
||||
|
||||
# Fetch chat_title directly via chat_id as it's usually missing in body
|
||||
chat_title = ""
|
||||
@@ -873,10 +1016,10 @@ class Action:
|
||||
}
|
||||
)
|
||||
|
||||
await self._send_notification(
|
||||
await self._emit_notification(
|
||||
__event_emitter__,
|
||||
"success",
|
||||
self._get_msg("success", filename=filename),
|
||||
"success",
|
||||
)
|
||||
|
||||
return {"message": "Download triggered"}
|
||||
@@ -892,10 +1035,10 @@ class Action:
|
||||
},
|
||||
}
|
||||
)
|
||||
await self._send_notification(
|
||||
await self._emit_notification(
|
||||
__event_emitter__,
|
||||
"error",
|
||||
self._get_msg("error_export", error=str(e)),
|
||||
"error",
|
||||
)
|
||||
|
||||
async def generate_title_using_ai(
|
||||
@@ -1008,30 +1151,7 @@ class Action:
|
||||
if not isinstance(name, str):
|
||||
return ""
|
||||
|
||||
def _is_emoji_codepoint(codepoint: int) -> bool:
|
||||
# Common emoji ranges + flag regional indicators.
|
||||
return (
|
||||
0x1F000 <= codepoint <= 0x1FAFF
|
||||
or 0x1F1E6 <= codepoint <= 0x1F1FF
|
||||
or 0x2600 <= codepoint <= 0x26FF
|
||||
or 0x2700 <= codepoint <= 0x27BF
|
||||
or 0x2300 <= codepoint <= 0x23FF
|
||||
or 0x2B00 <= codepoint <= 0x2BFF
|
||||
)
|
||||
|
||||
def _is_emoji_modifier(codepoint: int) -> bool:
|
||||
# VS15/VS16, ZWJ, keycap, skin tones, and tag characters used in some emoji sequences.
|
||||
return (
|
||||
codepoint in (0x200D, 0xFE0E, 0xFE0F, 0x20E3)
|
||||
or 0x1F3FB <= codepoint <= 0x1F3FF
|
||||
or 0xE0020 <= codepoint <= 0xE007F
|
||||
)
|
||||
|
||||
without_emoji = "".join(
|
||||
ch
|
||||
for ch in name
|
||||
if not (_is_emoji_codepoint(ord(ch)) or _is_emoji_modifier(ord(ch)))
|
||||
)
|
||||
without_emoji = self._remove_emojis(name)
|
||||
cleaned = re.sub(r'[\\/*?:"<>|]', "", without_emoji)
|
||||
cleaned = re.sub(r"\s+", " ", cleaned).strip().strip(".")
|
||||
return cleaned[:50].strip()
|
||||
@@ -1399,7 +1519,10 @@ class Action:
|
||||
|
||||
# If there is no h1 in content, prepend chat title as h1 when provided
|
||||
if top_heading and not has_h1:
|
||||
self.add_heading(doc, top_heading, 1)
|
||||
# Remove emojis from title for a professional look
|
||||
clean_title = self._remove_emojis(top_heading)
|
||||
# Use Title style (level 0) for the main document title
|
||||
self.add_heading(doc, clean_title, 0)
|
||||
|
||||
lines = markdown_text.split("\n")
|
||||
i = 0
|
||||
@@ -1659,7 +1782,7 @@ class Action:
|
||||
cur = text
|
||||
for _ in range(10):
|
||||
prev = cur
|
||||
cur = _REASONING_DETAILS_RE.sub("", cur)
|
||||
cur = _ALL_DETAILS_RE.sub("", cur)
|
||||
cur = _THINK_RE.sub("", cur)
|
||||
cur = _ANALYSIS_RE.sub("", cur)
|
||||
if cur == prev:
|
||||
@@ -2143,14 +2266,155 @@ class Action:
|
||||
font = style.font
|
||||
font.name = self.valves.FONT_LATIN
|
||||
font.size = Pt(11)
|
||||
# Set Asian font
|
||||
style._element.rPr.rFonts.set(qn("w:eastAsia"), self.valves.FONT_ASIAN)
|
||||
|
||||
# Ensure rPr element exists
|
||||
rPr = style._element.get_or_add_rPr()
|
||||
rFonts = rPr.get_or_add_rFonts()
|
||||
|
||||
# Set Latin and Asian fonts explicitly
|
||||
rFonts.set(qn("w:ascii"), self.valves.FONT_LATIN)
|
||||
rFonts.set(qn("w:hAnsi"), self.valves.FONT_LATIN)
|
||||
rFonts.set(qn("w:eastAsia"), self.valves.FONT_ASIAN)
|
||||
|
||||
# Set language to zh-CN to prevent MS Gothic fallback (Japanese font)
|
||||
# Even for English interface, we want to prioritize Chinese glyphs over Japanese for CJK
|
||||
lang = rPr.find(qn("w:lang"))
|
||||
if lang is None:
|
||||
lang = OxmlElement("w:lang")
|
||||
rPr.append(lang)
|
||||
lang.set(qn("w:val"), "en-US")
|
||||
lang.set(qn("w:eastAsia"), "zh-CN")
|
||||
|
||||
logger.info(
|
||||
f"[Font Config] Latin: {self.valves.FONT_LATIN}, Asian: {self.valves.FONT_ASIAN}"
|
||||
)
|
||||
|
||||
# Set paragraph format
|
||||
paragraph_format = style.paragraph_format
|
||||
paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE
|
||||
paragraph_format.space_after = Pt(6)
|
||||
|
||||
# Configure Title style (used for document title)
|
||||
# Standard format: 22pt (二号), bold, centered, 24pt space after
|
||||
if "Title" in doc.styles:
|
||||
title_style = doc.styles["Title"]
|
||||
title_font = title_style.font
|
||||
title_font.name = self.valves.FONT_LATIN
|
||||
title_font.size = Pt(22) # 二号字体
|
||||
title_font.bold = True
|
||||
title_font.color.rgb = RGBColor(0, 0, 0)
|
||||
|
||||
# Set paragraph format: alignment based on configuration
|
||||
title_pf = title_style.paragraph_format
|
||||
alignment_map = {
|
||||
"left": WD_ALIGN_PARAGRAPH.LEFT,
|
||||
"center": WD_ALIGN_PARAGRAPH.CENTER,
|
||||
"right": WD_ALIGN_PARAGRAPH.RIGHT,
|
||||
}
|
||||
title_pf.alignment = alignment_map.get(
|
||||
self.valves.TITLE_ALIGNMENT.lower(), WD_ALIGN_PARAGRAPH.CENTER
|
||||
)
|
||||
title_pf.space_before = Pt(0)
|
||||
title_pf.space_after = Pt(24)
|
||||
|
||||
t_rPr = title_style._element.get_or_add_rPr()
|
||||
t_rFonts = t_rPr.get_or_add_rFonts()
|
||||
t_rFonts.set(qn("w:ascii"), self.valves.FONT_LATIN)
|
||||
t_rFonts.set(qn("w:hAnsi"), self.valves.FONT_LATIN)
|
||||
t_rFonts.set(qn("w:eastAsia"), self.valves.FONT_ASIAN)
|
||||
|
||||
# Set language to zh-CN
|
||||
t_lang = t_rPr.find(qn("w:lang"))
|
||||
if t_lang is None:
|
||||
t_lang = OxmlElement("w:lang")
|
||||
t_rPr.append(t_lang)
|
||||
t_lang.set(qn("w:val"), "en-US")
|
||||
t_lang.set(qn("w:eastAsia"), "zh-CN")
|
||||
|
||||
# Standard heading sizes based on Chinese document standards:
|
||||
# Heading 1: 16pt (三号), bold, space before 24pt, space after 12pt
|
||||
# Heading 2: 15pt (小三), bold, space before 18pt, space after 6pt
|
||||
# Heading 3: 14pt (四号), bold, space before 12pt, space after 6pt
|
||||
# Heading 4-9: 12pt (小四), bold, gradually reduced spacing
|
||||
heading_formats = {
|
||||
1: {"size": 16, "space_before": 24, "space_after": 12},
|
||||
2: {"size": 15, "space_before": 18, "space_after": 6},
|
||||
3: {"size": 14, "space_before": 12, "space_after": 6},
|
||||
4: {"size": 12, "space_before": 12, "space_after": 6},
|
||||
5: {"size": 12, "space_before": 6, "space_after": 6},
|
||||
6: {"size": 11, "space_before": 6, "space_after": 3},
|
||||
7: {"size": 11, "space_before": 6, "space_after": 3},
|
||||
8: {"size": 10.5, "space_before": 6, "space_after": 3},
|
||||
9: {"size": 10.5, "space_before": 6, "space_after": 3},
|
||||
}
|
||||
|
||||
# Apply font settings to Heading 1-9
|
||||
for i in range(1, 10):
|
||||
style_id = f"Heading {i}"
|
||||
if style_id in doc.styles:
|
||||
heading_style = doc.styles[style_id]
|
||||
heading_font = heading_style.font
|
||||
heading_font.name = self.valves.FONT_LATIN
|
||||
heading_font.color.rgb = RGBColor(0, 0, 0)
|
||||
|
||||
# Apply standard formatting
|
||||
fmt = heading_formats.get(
|
||||
i, {"size": 11, "space_before": 6, "space_after": 3}
|
||||
)
|
||||
heading_font.size = Pt(fmt["size"])
|
||||
heading_font.bold = True
|
||||
|
||||
heading_pf = heading_style.paragraph_format
|
||||
heading_pf.space_before = Pt(fmt["space_before"])
|
||||
heading_pf.space_after = Pt(fmt["space_after"])
|
||||
|
||||
# Ensure rPr exists
|
||||
h_rPr = heading_style._element.get_or_add_rPr()
|
||||
h_rFonts = h_rPr.get_or_add_rFonts()
|
||||
|
||||
# Set fonts
|
||||
h_rFonts.set(qn("w:ascii"), self.valves.FONT_LATIN)
|
||||
h_rFonts.set(qn("w:hAnsi"), self.valves.FONT_LATIN)
|
||||
h_rFonts.set(qn("w:eastAsia"), self.valves.FONT_ASIAN)
|
||||
|
||||
# Set language to zh-CN
|
||||
h_lang = h_rPr.find(qn("w:lang"))
|
||||
if h_lang is None:
|
||||
h_lang = OxmlElement("w:lang")
|
||||
h_rPr.append(h_lang)
|
||||
h_lang.set(qn("w:val"), "en-US")
|
||||
h_lang.set(qn("w:eastAsia"), "zh-CN")
|
||||
|
||||
def _remove_emojis(self, text: str) -> str:
|
||||
"""Remove emojis from text based on unicode ranges."""
|
||||
if not isinstance(text, str):
|
||||
return ""
|
||||
|
||||
def _is_emoji_codepoint(codepoint: int) -> bool:
|
||||
# Common emoji ranges + flag regional indicators.
|
||||
return (
|
||||
0x1F000 <= codepoint <= 0x1FAFF
|
||||
or 0x1F1E6 <= codepoint <= 0x1F1FF
|
||||
or 0x2600 <= codepoint <= 0x26FF
|
||||
or 0x2700 <= codepoint <= 0x27BF
|
||||
or 0x2300 <= codepoint <= 0x23FF
|
||||
or 0x2B00 <= codepoint <= 0x2BFF
|
||||
)
|
||||
|
||||
def _is_emoji_modifier(codepoint: int) -> bool:
|
||||
# VS15/VS16, ZWJ, keycap, skin tones, and tag characters used in some emoji sequences.
|
||||
return (
|
||||
codepoint in (0x200D, 0xFE0E, 0xFE0F, 0x20E3)
|
||||
or 0x1F3FB <= codepoint <= 0x1F3FF
|
||||
or 0xE0020 <= codepoint <= 0xE007F
|
||||
)
|
||||
|
||||
return "".join(
|
||||
ch
|
||||
for ch in text
|
||||
if not (_is_emoji_codepoint(ord(ch)) or _is_emoji_modifier(ord(ch)))
|
||||
)
|
||||
|
||||
def add_heading(self, doc: Document, text: str, level: int):
|
||||
"""Add heading"""
|
||||
# Word heading levels start from 0, Markdown from 1
|
||||
@@ -2186,6 +2450,12 @@ class Action:
|
||||
if strike:
|
||||
run.font.strike = True
|
||||
|
||||
# Explicitly set East Asian font to prevent MS Gothic fallback
|
||||
# Word may not inherit w:eastAsia from style, causing Japanese font fallback for CJK
|
||||
rPr = run._element.get_or_add_rPr()
|
||||
rFonts = rPr.get_or_add_rFonts()
|
||||
rFonts.set(qn("w:eastAsia"), self.valves.FONT_ASIAN)
|
||||
|
||||
def _add_inline_code(self, paragraph, s: str):
|
||||
if s == "":
|
||||
return
|
||||
@@ -2585,7 +2855,11 @@ class Action:
|
||||
):
|
||||
u = self._normalize_url(url)
|
||||
if not u:
|
||||
paragraph.add_run(display_text or text)
|
||||
run = paragraph.add_run(display_text or text)
|
||||
# Set East Asian font to prevent MS Gothic fallback
|
||||
rPr = run._element.get_or_add_rPr()
|
||||
rFonts = rPr.get_or_add_rFonts()
|
||||
rFonts.set(qn("w:eastAsia"), self.valves.FONT_ASIAN)
|
||||
return
|
||||
|
||||
part = getattr(paragraph, "part", None)
|
||||
@@ -2594,6 +2868,10 @@ class Action:
|
||||
run = paragraph.add_run(display_text or text)
|
||||
run.font.color.rgb = RGBColor(0, 0, 255)
|
||||
run.font.underline = True
|
||||
# Set East Asian font to prevent MS Gothic fallback
|
||||
rPr = run._element.get_or_add_rPr()
|
||||
rFonts = rPr.get_or_add_rFonts()
|
||||
rFonts.set(qn("w:eastAsia"), self.valves.FONT_ASIAN)
|
||||
return
|
||||
|
||||
r_id = part.relate_to(u, RT.HYPERLINK, is_external=True)
|
||||
@@ -2607,6 +2885,11 @@ class Action:
|
||||
rStyle.set(qn("w:val"), "Hyperlink")
|
||||
rPr.append(rStyle)
|
||||
|
||||
# Set East Asian font to prevent MS Gothic fallback
|
||||
rFonts = OxmlElement("w:rFonts")
|
||||
rFonts.set(qn("w:eastAsia"), self.valves.FONT_ASIAN)
|
||||
rPr.append(rFonts)
|
||||
|
||||
color = OxmlElement("w:color")
|
||||
color.set(qn("w:val"), "0000FF")
|
||||
rPr.append(color)
|
||||
|
||||
BIN
plugins/actions/export_to_docx/export_to_word_cn.png
Normal file
BIN
plugins/actions/export_to_docx/export_to_word_cn.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
@@ -1,9 +1,9 @@
|
||||
"""
|
||||
title: 导出为 Word (增强版)
|
||||
title: 导出为Word增强版
|
||||
author: Fu-Jie
|
||||
author_url: https://github.com/Fu-Jie
|
||||
funding_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
version: 0.4.3
|
||||
author_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
funding_url: https://github.com/open-webui
|
||||
version: 0.4.4
|
||||
openwebui_id: 8a6306c0-d005-4e46-aaae-8db3532c9ed5
|
||||
icon_url: data:image/svg+xml;base64,PHN2ZwogIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICB3aWR0aD0iMjQiCiAgaGVpZ2h0PSIyNCIKICB2aWV3Qm94PSIwIDAgMjQgMjQiCiAgZmlsbD0ibm9uZSIKICBzdHJva2U9ImN1cnJlbnRDb2xvciIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNNiAyMmEyIDIgMCAwIDEtMi0yVjRhMiAyIDAgMCAxIDItMmg4YTIuNCAyLjQgMCAwIDEgMS43MDQuNzA2bDMuNTg4IDMuNTg4QTIuNCAyLjQgMCAwIDEgMjAgOHYxMmEyIDIgMCAwIDEtMiAyeiIgLz4KICA8cGF0aCBkPSJNMTQgMnY1YTEgMSAwIDAgMCAxIDFoNSIgLz4KICA8cGF0aCBkPSJNMTAgOUg4IiAvPgogIDxwYXRoIGQ9Ik0xNiAxM0g4IiAvPgogIDxwYXRoIGQ9Ik0xNiAxN0g4IiAvPgo8L3N2Zz4K
|
||||
requirements: python-docx, Pygments, latex2mathml, mathml2omml
|
||||
@@ -101,9 +101,8 @@ _TRANSPARENT_1PX_PNG = base64.b64decode(
|
||||
_ASVG_NS = "http://schemas.microsoft.com/office/drawing/2016/SVG/main"
|
||||
nsmap.setdefault("asvg", _ASVG_NS)
|
||||
|
||||
_REASONING_DETAILS_RE = re.compile(
|
||||
r"<details\b[^>]*\btype\s*=\s*(?:\"reasoning\"|'reasoning'|reasoning)[^>]*>.*?</details\s*>",
|
||||
re.IGNORECASE | re.DOTALL,
|
||||
_ALL_DETAILS_RE = re.compile(
|
||||
r"<details\b[^>]*>.*?</details\s*>", re.IGNORECASE | re.DOTALL
|
||||
)
|
||||
_THINK_RE = re.compile(r"<think\b[^>]*>.*?</think\s*>", re.IGNORECASE | re.DOTALL)
|
||||
_ANALYSIS_RE = re.compile(
|
||||
@@ -150,6 +149,14 @@ class Action:
|
||||
default="chat_title",
|
||||
description="Title Source: 'chat_title' (Chat Title), 'ai_generated' (AI Generated), 'markdown_title' (Markdown Title)",
|
||||
)
|
||||
SHOW_STATUS: bool = Field(
|
||||
default=True,
|
||||
description="是否显示操作状态更新。",
|
||||
)
|
||||
SHOW_DEBUG_LOG: bool = Field(
|
||||
default=False,
|
||||
description="是否在浏览器控制台打印调试日志。",
|
||||
)
|
||||
|
||||
最大嵌入图片大小MB: int = Field(
|
||||
default=20,
|
||||
@@ -170,6 +177,12 @@ class Action:
|
||||
description="Font for code blocks and inline code (e.g., 'Consolas', 'Courier New', 'Monaco')",
|
||||
)
|
||||
|
||||
# Title alignment
|
||||
标题对齐方式: str = Field(
|
||||
default="center",
|
||||
description="标题对齐方式: 'left' (左对齐), 'center' (居中), 或 'right' (右对齐)",
|
||||
)
|
||||
|
||||
# Table styling
|
||||
表头背景色: str = Field(
|
||||
default="F2F2F2",
|
||||
@@ -234,60 +247,60 @@ class Action:
|
||||
)
|
||||
|
||||
class UserValves(BaseModel):
|
||||
文档标题来源: str = Field(
|
||||
default="chat_title",
|
||||
文档标题来源: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Title Source: 'chat_title' (Chat Title), 'ai_generated' (AI Generated), 'markdown_title' (Markdown Title)",
|
||||
)
|
||||
界面语言: str = Field(
|
||||
default="zh",
|
||||
界面语言: Optional[str] = Field(
|
||||
default=None,
|
||||
description="UI language for export messages. Options: 'en' (English), 'zh' (Chinese)",
|
||||
)
|
||||
英文字体: str = Field(
|
||||
default="Calibri",
|
||||
英文字体: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Font for Latin characters (e.g., 'Times New Roman', '', 'Arial')",
|
||||
)
|
||||
中文字体: str = Field(
|
||||
default="SimSun",
|
||||
中文字体: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Font for Asian characters (e.g., 'SimSun', 'Microsoft YaHei', 'PingFang SC')",
|
||||
)
|
||||
代码字体: str = Field(
|
||||
default="Consolas",
|
||||
代码字体: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Font for code blocks and inline code (e.g., 'Consolas', 'Courier New', 'Monaco')",
|
||||
)
|
||||
表头背景色: str = Field(
|
||||
default="F2F2F2",
|
||||
表头背景色: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Table header background color (hex, without #)",
|
||||
)
|
||||
表格隔行背景色: str = Field(
|
||||
default="FBFBFB",
|
||||
表格隔行背景色: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Table zebra stripe background color for alternate rows (hex, without #)",
|
||||
)
|
||||
Mermaid_PNG缩放比例: float = Field(
|
||||
default=3.0,
|
||||
Mermaid_PNG缩放比例: Optional[float] = Field(
|
||||
default=None,
|
||||
description="PNG render resolution multiplier (higher = clearer, larger file)",
|
||||
)
|
||||
Mermaid显示比例: float = Field(
|
||||
default=1.0,
|
||||
Mermaid显示比例: Optional[float] = Field(
|
||||
default=None,
|
||||
description="Diagram width relative to available page width (<=1 recommended)",
|
||||
)
|
||||
Mermaid布局优化: bool = Field(
|
||||
default=False,
|
||||
Mermaid布局优化: Optional[bool] = Field(
|
||||
default=None,
|
||||
description="Optimize Mermaid layout: convert LR to TD for graph/flowchart",
|
||||
)
|
||||
Mermaid背景色: str = Field(
|
||||
default="",
|
||||
Mermaid背景色: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Mermaid background color. Empty = transparent (recommended for Word dark mode). Used only for optional PNG fill.",
|
||||
)
|
||||
启用Mermaid图注: bool = Field(
|
||||
default=True,
|
||||
启用Mermaid图注: Optional[bool] = Field(
|
||||
default=None,
|
||||
description="Add figure captions under Mermaid images/charts",
|
||||
)
|
||||
启用数学公式: bool = Field(
|
||||
default=True,
|
||||
启用数学公式: Optional[bool] = Field(
|
||||
default=None,
|
||||
description="Enable LaTeX math block conversion (\\\\[...\\\\] and $$...$$) into Word equations",
|
||||
)
|
||||
启用行内公式: bool = Field(
|
||||
default=True,
|
||||
启用行内公式: Optional[bool] = Field(
|
||||
default=None,
|
||||
description="Enable inline $...$ math conversion into Word equations (conservative parsing to reduce false positives)",
|
||||
)
|
||||
|
||||
@@ -320,10 +333,100 @@ class Action:
|
||||
return msg
|
||||
return msg
|
||||
|
||||
async def _send_notification(self, emitter: Callable, type: str, content: str):
|
||||
await emitter(
|
||||
{"type": "notification", "data": {"type": type, "content": content}}
|
||||
)
|
||||
def _get_user_context(self, __user__: Optional[Dict[str, Any]]) -> Dict[str, str]:
|
||||
"""安全提取用户上下文信息。"""
|
||||
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_language": user_data.get("language", "zh-CN"),
|
||||
}
|
||||
|
||||
def _get_chat_context(
|
||||
self, body: dict, __metadata__: Optional[dict] = None
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
统一提取聊天上下文信息 (chat_id, message_id)。
|
||||
优先从 body 中提取,其次从 metadata 中提取。
|
||||
"""
|
||||
chat_id = ""
|
||||
message_id = ""
|
||||
|
||||
# 1. 尝试从 body 获取
|
||||
if isinstance(body, dict):
|
||||
chat_id = body.get("chat_id", "")
|
||||
message_id = body.get("id", "") # message_id 在 body 中通常是 id
|
||||
|
||||
# 再次检查 body.metadata
|
||||
if not chat_id or not message_id:
|
||||
body_metadata = body.get("metadata", {})
|
||||
if isinstance(body_metadata, dict):
|
||||
if not chat_id:
|
||||
chat_id = body_metadata.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = body_metadata.get("message_id", "")
|
||||
|
||||
# 2. 尝试从 __metadata__ 获取 (作为补充)
|
||||
if __metadata__ and isinstance(__metadata__, dict):
|
||||
if not chat_id:
|
||||
chat_id = __metadata__.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = __metadata__.get("message_id", "")
|
||||
|
||||
return {
|
||||
"chat_id": str(chat_id).strip(),
|
||||
"message_id": str(message_id).strip(),
|
||||
}
|
||||
|
||||
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,
|
||||
ntype: str = "info",
|
||||
):
|
||||
"""Emits a notification event (info, success, warning, error)."""
|
||||
if emitter:
|
||||
await emitter(
|
||||
{"type": "notification", "data": {"type": ntype, "content": content}}
|
||||
)
|
||||
|
||||
async def _emit_debug_log(self, emitter, title: str, data: dict):
|
||||
"""在浏览器控制台打印结构化调试日志"""
|
||||
if not self.valves.SHOW_DEBUG_LOG or not emitter:
|
||||
return
|
||||
|
||||
try:
|
||||
import json
|
||||
|
||||
js_code = f"""
|
||||
(async function() {{
|
||||
console.group("🛠️ {title}");
|
||||
console.log({json.dumps(data, ensure_ascii=False)});
|
||||
console.groupEnd();
|
||||
}})();
|
||||
"""
|
||||
|
||||
await emitter({"type": "execute", "data": {"code": js_code}})
|
||||
except Exception as e:
|
||||
print(f"Error emitting debug log: {e}")
|
||||
|
||||
async def action(
|
||||
self,
|
||||
@@ -351,11 +454,21 @@ class Action:
|
||||
user_id = __user__.get("id", "unknown_user")
|
||||
|
||||
# Apply UserValves if present
|
||||
if __user__ and "valves" in __user__:
|
||||
# Update self.valves with user-specific values
|
||||
for key, value in __user__["valves"].model_dump().items():
|
||||
if hasattr(self.valves, key):
|
||||
setattr(self.valves, key, value)
|
||||
if __user__:
|
||||
# Robustly parse UserValves whether it's a dict or Pydantic model
|
||||
raw_valves = __user__.get("valves", {})
|
||||
if isinstance(raw_valves, self.UserValves):
|
||||
user_valves = raw_valves
|
||||
elif isinstance(raw_valves, dict):
|
||||
user_valves = self.UserValves(**raw_valves)
|
||||
else:
|
||||
user_valves = None
|
||||
|
||||
if user_valves:
|
||||
for key, value in user_valves.model_dump(exclude_unset=True).items():
|
||||
# Only override if the value is not None (and explicitly set)
|
||||
if hasattr(self.valves, key) and value is not None:
|
||||
setattr(self.valves, key, value)
|
||||
|
||||
# Get user language from Valves configuration
|
||||
self._user_lang = self._get_lang_key(self.valves.界面语言)
|
||||
@@ -392,17 +505,49 @@ class Action:
|
||||
try:
|
||||
message_content = last_assistant_message["content"]
|
||||
if isinstance(message_content, str):
|
||||
if __event_emitter__ and self.valves.SHOW_DEBUG_LOG:
|
||||
debug_data = {}
|
||||
for name, regex in [
|
||||
("Details Block (详情块)", _ALL_DETAILS_RE),
|
||||
("Think Block (思考块)", _THINK_RE),
|
||||
("Analysis Block (分析块)", _ANALYSIS_RE),
|
||||
]:
|
||||
matches = regex.findall(message_content)
|
||||
if matches:
|
||||
debug_data[name] = [
|
||||
(m[:200] + "...") if len(m) > 200 else m
|
||||
for m in matches
|
||||
]
|
||||
if debug_data:
|
||||
await self._emit_debug_log(
|
||||
__event_emitter__,
|
||||
"上下文内容清理分析 (Context Stripping Analysis)",
|
||||
debug_data,
|
||||
)
|
||||
|
||||
# Log font configuration
|
||||
await self._emit_debug_log(
|
||||
__event_emitter__,
|
||||
"字体配置 (Font Configuration)",
|
||||
{
|
||||
"英文字体 (Latin Font)": self.valves.英文字体,
|
||||
"中文字体 (Asian Font)": self.valves.中文字体,
|
||||
"代码字体 (Code Font)": self.valves.代码字体,
|
||||
},
|
||||
)
|
||||
|
||||
message_content = self._strip_reasoning_blocks(message_content)
|
||||
|
||||
if not message_content or not message_content.strip():
|
||||
await self._send_notification(
|
||||
__event_emitter__, "error", self._get_msg("error_no_content")
|
||||
await self._emit_notification(
|
||||
__event_emitter__, self._get_msg("error_no_content"), "error"
|
||||
)
|
||||
return
|
||||
|
||||
# Generate filename
|
||||
title = ""
|
||||
chat_id = self.extract_chat_id(body, __metadata__)
|
||||
chat_ctx = self._get_chat_context(body, __metadata__)
|
||||
chat_id = chat_ctx["chat_id"]
|
||||
|
||||
# Fetch chat_title directly via chat_id as it's usually missing in body
|
||||
chat_title = ""
|
||||
@@ -871,10 +1016,10 @@ class Action:
|
||||
}
|
||||
)
|
||||
|
||||
await self._send_notification(
|
||||
await self._emit_notification(
|
||||
__event_emitter__,
|
||||
"success",
|
||||
self._get_msg("success", filename=filename),
|
||||
"success",
|
||||
)
|
||||
|
||||
return {"message": "Download triggered"}
|
||||
@@ -890,10 +1035,10 @@ class Action:
|
||||
},
|
||||
}
|
||||
)
|
||||
await self._send_notification(
|
||||
await self._emit_notification(
|
||||
__event_emitter__,
|
||||
"error",
|
||||
self._get_msg("error_export", error=str(e)),
|
||||
"error",
|
||||
)
|
||||
|
||||
async def generate_title_using_ai(
|
||||
@@ -1002,34 +1147,11 @@ class Action:
|
||||
return title.strip() if isinstance(title, str) else ""
|
||||
|
||||
def clean_filename(self, name: str) -> str:
|
||||
"""Clean illegal characters from filename and strip emoji."""
|
||||
"""清理文件名中的非法字符并移除 Emoji"""
|
||||
if not isinstance(name, str):
|
||||
return ""
|
||||
|
||||
def _is_emoji_codepoint(codepoint: int) -> bool:
|
||||
# Common emoji ranges + flag regional indicators.
|
||||
return (
|
||||
0x1F000 <= codepoint <= 0x1FAFF
|
||||
or 0x1F1E6 <= codepoint <= 0x1F1FF
|
||||
or 0x2600 <= codepoint <= 0x26FF
|
||||
or 0x2700 <= codepoint <= 0x27BF
|
||||
or 0x2300 <= codepoint <= 0x23FF
|
||||
or 0x2B00 <= codepoint <= 0x2BFF
|
||||
)
|
||||
|
||||
def _is_emoji_modifier(codepoint: int) -> bool:
|
||||
# VS15/VS16, ZWJ, keycap, skin tones, and tag characters used in some emoji sequences.
|
||||
return (
|
||||
codepoint in (0x200D, 0xFE0E, 0xFE0F, 0x20E3)
|
||||
or 0x1F3FB <= codepoint <= 0x1F3FF
|
||||
or 0xE0020 <= codepoint <= 0xE007F
|
||||
)
|
||||
|
||||
without_emoji = "".join(
|
||||
ch
|
||||
for ch in name
|
||||
if not (_is_emoji_codepoint(ord(ch)) or _is_emoji_modifier(ord(ch)))
|
||||
)
|
||||
without_emoji = self._remove_emojis(name)
|
||||
cleaned = re.sub(r'[\\/*?:"<>|]', "", without_emoji)
|
||||
cleaned = re.sub(r"\s+", " ", cleaned).strip().strip(".")
|
||||
return cleaned[:50].strip()
|
||||
@@ -1397,7 +1519,10 @@ class Action:
|
||||
|
||||
# If there is no h1 in content, prepend chat title as h1 when provided
|
||||
if top_heading and not has_h1:
|
||||
self.add_heading(doc, top_heading, 1)
|
||||
# Remove emojis from title for a professional look
|
||||
clean_title = self._remove_emojis(top_heading)
|
||||
# Use Title style (level 0) for the main document title
|
||||
self.add_heading(doc, clean_title, 0)
|
||||
|
||||
lines = markdown_text.split("\n")
|
||||
i = 0
|
||||
@@ -1657,7 +1782,7 @@ class Action:
|
||||
cur = text
|
||||
for _ in range(10):
|
||||
prev = cur
|
||||
cur = _REASONING_DETAILS_RE.sub("", cur)
|
||||
cur = _ALL_DETAILS_RE.sub("", cur)
|
||||
cur = _THINK_RE.sub("", cur)
|
||||
cur = _ANALYSIS_RE.sub("", cur)
|
||||
if cur == prev:
|
||||
@@ -2141,14 +2266,154 @@ class Action:
|
||||
font = style.font
|
||||
font.name = self.valves.英文字体
|
||||
font.size = Pt(11)
|
||||
# Set Asian font
|
||||
style._element.rPr.rFonts.set(qn("w:eastAsia"), self.valves.中文字体)
|
||||
|
||||
# Ensure rPr element exists
|
||||
rPr = style._element.get_or_add_rPr()
|
||||
rFonts = rPr.get_or_add_rFonts()
|
||||
|
||||
# Set Latin and Asian fonts explicitly
|
||||
rFonts.set(qn("w:ascii"), self.valves.英文字体)
|
||||
rFonts.set(qn("w:hAnsi"), self.valves.英文字体)
|
||||
rFonts.set(qn("w:eastAsia"), self.valves.中文字体)
|
||||
|
||||
# Set language to zh-CN to prevent MS Gothic fallback (Japanese font)
|
||||
lang = rPr.find(qn("w:lang"))
|
||||
if lang is None:
|
||||
lang = OxmlElement("w:lang")
|
||||
rPr.append(lang)
|
||||
lang.set(qn("w:val"), "en-US")
|
||||
lang.set(qn("w:eastAsia"), "zh-CN")
|
||||
|
||||
logger.info(
|
||||
f"[Font Config] Latin: {self.valves.英文字体}, Asian: {self.valves.中文字体}"
|
||||
)
|
||||
|
||||
# Set paragraph format
|
||||
paragraph_format = style.paragraph_format
|
||||
paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE
|
||||
paragraph_format.space_after = Pt(6)
|
||||
|
||||
# 配置 Title 样式 (用于文档标题)
|
||||
# 标准格式: 22pt (二号), 加粗, 居中, 段后 24pt
|
||||
if "Title" in doc.styles:
|
||||
title_style = doc.styles["Title"]
|
||||
title_font = title_style.font
|
||||
title_font.name = self.valves.英文字体
|
||||
title_font.size = Pt(22) # 二号字体
|
||||
title_font.bold = True
|
||||
title_font.color.rgb = RGBColor(0, 0, 0)
|
||||
|
||||
# 段落格式: 根据配置设置对齐方式和间距
|
||||
title_pf = title_style.paragraph_format
|
||||
alignment_map = {
|
||||
"left": WD_ALIGN_PARAGRAPH.LEFT,
|
||||
"center": WD_ALIGN_PARAGRAPH.CENTER,
|
||||
"right": WD_ALIGN_PARAGRAPH.RIGHT,
|
||||
}
|
||||
title_pf.alignment = alignment_map.get(
|
||||
self.valves.标题对齐方式.lower(), WD_ALIGN_PARAGRAPH.CENTER
|
||||
)
|
||||
title_pf.space_before = Pt(0)
|
||||
title_pf.space_after = Pt(24)
|
||||
|
||||
t_rPr = title_style._element.get_or_add_rPr()
|
||||
t_rFonts = t_rPr.get_or_add_rFonts()
|
||||
t_rFonts.set(qn("w:ascii"), self.valves.英文字体)
|
||||
t_rFonts.set(qn("w:hAnsi"), self.valves.英文字体)
|
||||
t_rFonts.set(qn("w:eastAsia"), self.valves.中文字体)
|
||||
|
||||
# Set language for Title
|
||||
t_lang = t_rPr.find(qn("w:lang"))
|
||||
if t_lang is None:
|
||||
t_lang = OxmlElement("w:lang")
|
||||
t_rPr.append(t_lang)
|
||||
t_lang.set(qn("w:val"), "en-US")
|
||||
t_lang.set(qn("w:eastAsia"), "zh-CN")
|
||||
|
||||
# 标准标题字号 (基于中文文档规范):
|
||||
# Heading 1: 16pt (三号), 加粗, 段前 24pt, 段后 12pt
|
||||
# Heading 2: 15pt (小三), 加粗, 段前 18pt, 段后 6pt
|
||||
# Heading 3: 14pt (四号), 加粗, 段前 12pt, 段后 6pt
|
||||
# Heading 4-9: 12pt (小四), 加粗, 逐级减小间距
|
||||
heading_formats = {
|
||||
1: {"size": 16, "space_before": 24, "space_after": 12},
|
||||
2: {"size": 15, "space_before": 18, "space_after": 6},
|
||||
3: {"size": 14, "space_before": 12, "space_after": 6},
|
||||
4: {"size": 12, "space_before": 12, "space_after": 6},
|
||||
5: {"size": 12, "space_before": 6, "space_after": 6},
|
||||
6: {"size": 11, "space_before": 6, "space_after": 3},
|
||||
7: {"size": 11, "space_before": 6, "space_after": 3},
|
||||
8: {"size": 10.5, "space_before": 6, "space_after": 3},
|
||||
9: {"size": 10.5, "space_before": 6, "space_after": 3},
|
||||
}
|
||||
|
||||
# Apply font settings to Heading 1-9
|
||||
for i in range(1, 10):
|
||||
style_id = f"Heading {i}"
|
||||
if style_id in doc.styles:
|
||||
heading_style = doc.styles[style_id]
|
||||
heading_font = heading_style.font
|
||||
heading_font.name = self.valves.英文字体
|
||||
heading_font.color.rgb = RGBColor(0, 0, 0)
|
||||
|
||||
# 应用标准格式
|
||||
fmt = heading_formats.get(
|
||||
i, {"size": 11, "space_before": 6, "space_after": 3}
|
||||
)
|
||||
heading_font.size = Pt(fmt["size"])
|
||||
heading_font.bold = True
|
||||
|
||||
heading_pf = heading_style.paragraph_format
|
||||
heading_pf.space_before = Pt(fmt["space_before"])
|
||||
heading_pf.space_after = Pt(fmt["space_after"])
|
||||
|
||||
# Ensure rPr exists
|
||||
h_rPr = heading_style._element.get_or_add_rPr()
|
||||
h_rFonts = h_rPr.get_or_add_rFonts()
|
||||
|
||||
# Set fonts
|
||||
h_rFonts.set(qn("w:ascii"), self.valves.英文字体)
|
||||
h_rFonts.set(qn("w:hAnsi"), self.valves.英文字体)
|
||||
h_rFonts.set(qn("w:eastAsia"), self.valves.中文字体)
|
||||
|
||||
# Set language for Heading
|
||||
h_lang = h_rPr.find(qn("w:lang"))
|
||||
if h_lang is None:
|
||||
h_lang = OxmlElement("w:lang")
|
||||
h_rPr.append(h_lang)
|
||||
h_lang.set(qn("w:val"), "en-US")
|
||||
h_lang.set(qn("w:eastAsia"), "zh-CN")
|
||||
|
||||
def _remove_emojis(self, text: str) -> str:
|
||||
"""从文本中移除 Emoji (基于 Unicode 范围)"""
|
||||
if not isinstance(text, str):
|
||||
return ""
|
||||
|
||||
def _is_emoji_codepoint(codepoint: int) -> bool:
|
||||
# Common emoji ranges + flag regional indicators.
|
||||
return (
|
||||
0x1F000 <= codepoint <= 0x1FAFF
|
||||
or 0x1F1E6 <= codepoint <= 0x1F1FF
|
||||
or 0x2600 <= codepoint <= 0x26FF
|
||||
or 0x2700 <= codepoint <= 0x27BF
|
||||
or 0x2300 <= codepoint <= 0x23FF
|
||||
or 0x2B00 <= codepoint <= 0x2BFF
|
||||
)
|
||||
|
||||
def _is_emoji_modifier(codepoint: int) -> bool:
|
||||
# VS15/VS16, ZWJ, keycap, skin tones, and tag characters used in some emoji sequences.
|
||||
return (
|
||||
codepoint in (0x200D, 0xFE0E, 0xFE0F, 0x20E3)
|
||||
or 0x1F3FB <= codepoint <= 0x1F3FF
|
||||
or 0xE0020 <= codepoint <= 0xE007F
|
||||
)
|
||||
|
||||
return "".join(
|
||||
ch
|
||||
for ch in text
|
||||
if not (_is_emoji_codepoint(ord(ch)) or _is_emoji_modifier(ord(ch)))
|
||||
)
|
||||
|
||||
def add_heading(self, doc: Document, text: str, level: int):
|
||||
"""Add heading"""
|
||||
# Word heading levels start from 0, Markdown from 1
|
||||
@@ -2184,6 +2449,12 @@ class Action:
|
||||
if strike:
|
||||
run.font.strike = True
|
||||
|
||||
# Explicitly set East Asian font to prevent MS Gothic fallback
|
||||
# Word may not inherit w:eastAsia from style, causing Japanese font fallback for CJK
|
||||
rPr = run._element.get_or_add_rPr()
|
||||
rFonts = rPr.get_or_add_rFonts()
|
||||
rFonts.set(qn("w:eastAsia"), self.valves.中文字体)
|
||||
|
||||
def _add_inline_code(self, paragraph, s: str):
|
||||
if s == "":
|
||||
return
|
||||
@@ -2579,7 +2850,11 @@ class Action:
|
||||
):
|
||||
u = self._normalize_url(url)
|
||||
if not u:
|
||||
paragraph.add_run(display_text or text)
|
||||
run = paragraph.add_run(display_text or text)
|
||||
# Set East Asian font to prevent MS Gothic fallback
|
||||
rPr = run._element.get_or_add_rPr()
|
||||
rFonts = rPr.get_or_add_rFonts()
|
||||
rFonts.set(qn("w:eastAsia"), self.valves.中文字体)
|
||||
return
|
||||
|
||||
part = getattr(paragraph, "part", None)
|
||||
@@ -2588,6 +2863,10 @@ class Action:
|
||||
run = paragraph.add_run(display_text or text)
|
||||
run.font.color.rgb = RGBColor(0, 0, 255)
|
||||
run.font.underline = True
|
||||
# Set East Asian font to prevent MS Gothic fallback
|
||||
rPr = run._element.get_or_add_rPr()
|
||||
rFonts = rPr.get_or_add_rFonts()
|
||||
rFonts.set(qn("w:eastAsia"), self.valves.中文字体)
|
||||
return
|
||||
|
||||
r_id = part.relate_to(u, RT.HYPERLINK, is_external=True)
|
||||
@@ -2601,6 +2880,11 @@ class Action:
|
||||
rStyle.set(qn("w:val"), "Hyperlink")
|
||||
rPr.append(rStyle)
|
||||
|
||||
# Set East Asian font to prevent MS Gothic fallback
|
||||
rFonts = OxmlElement("w:rFonts")
|
||||
rFonts.set(qn("w:eastAsia"), self.valves.中文字体)
|
||||
rPr.append(rFonts)
|
||||
|
||||
color = OxmlElement("w:color")
|
||||
color.set(qn("w:val"), "0000FF")
|
||||
rPr.append(color)
|
||||
|
||||
@@ -1,4 +1,59 @@
|
||||
# Export to Excel
|
||||
# 📊 Export to Excel
|
||||
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **Version:** 0.3.7 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **License:** MIT
|
||||
|
||||
Export chat history to an Excel (.xlsx) file directly from the chat interface.
|
||||
|
||||
## 🔥 What's New in v0.3.6
|
||||
|
||||
- **OpenWebUI-Style Theme**: Modern dark header (#1f2937) with light gray zebra striping for better readability.
|
||||
- **Zebra Striping**: Alternating row colors (#ffffff / #f3f4f6) for improved visual scanning.
|
||||
- **Smart Data Type Conversion**: Automatically converts columns to numeric or datetime types with fallback to string.
|
||||
- **Full Cell Bold/Italic**: Supports full cell Markdown bold (`**text**`) and italic (`*text*`) formatting in Excel.
|
||||
- **Partial Markdown Cleanup**: Removes partial Markdown formatting symbols for cleaner Excel output.
|
||||
- **Export Scope**: Added `EXPORT_SCOPE` to choose between the last message or all messages.
|
||||
- **Smart Sheet Naming**: Names sheets based on Markdown headers, AI titles, or message index.
|
||||
- **Multiple Tables Support**: Improved handling of multiple tables across messages.
|
||||
- **Smart Filename Generation**: Supports filenames based on chat title, AI summary, or Markdown headers.
|
||||
- **Configuration Options**: Added `TITLE_SOURCE` to control filename strategy.
|
||||
- **AI Title Generation**: Added `MODEL_ID` to use AI for filename generation with progress notifications.
|
||||
|
||||
## ✨ Core Features
|
||||
|
||||
- 🚀 **One-Click Export**: Adds an “Export to Excel” action button to the chat.
|
||||
- 🧠 **Automatic Header Extraction**: Intelligently identifies table headers from chat content.
|
||||
- 📊 **Multi-Table Support**: Handles multiple tables within a single chat session.
|
||||
|
||||
## 🚀 How to Use
|
||||
|
||||
1. **Install**: Search for “Export to Excel” in the Open WebUI Community and install.
|
||||
2. **Trigger**: In any chat, click the “Export to Excel” action button.
|
||||
3. **Download**: The .xlsx file will be automatically downloaded.
|
||||
|
||||
## ⚙️ Configuration (Valves)
|
||||
|
||||
| Parameter | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `TITLE_SOURCE` | `chat_title` | Filename source: `chat_title`, `ai_generated`, or `markdown_title`. |
|
||||
| `EXPORT_SCOPE` | `last_message` | Export scope: `last_message` or `all_messages`. |
|
||||
| `MODEL_ID` | `""` | Model ID for AI title generation. Empty uses current chat model. |
|
||||
| `SHOW_STATUS` | `True` | Show operation status updates. |
|
||||
| `SHOW_DEBUG_LOG` | `False` | Print debug logs in the browser console (F12). |
|
||||
|
||||
## ⭐ Support
|
||||
|
||||
If this plugin has been useful, a star on [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) is a big motivation for me. Thank you for the support.
|
||||
|
||||
## Troubleshooting ❓
|
||||
|
||||
- **Plugin not working?**: Check if the filter/action is enabled in the model settings.
|
||||
- **Debug Logs**: Enable `SHOW_STATUS` and check the browser console (F12) if needed.
|
||||
- **Error Messages**: If you see an error, please copy the full error message and report it.
|
||||
- **Submit an Issue**: If you encounter any problems, please submit an issue on GitHub: [Awesome OpenWebUI Issues](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
|
||||
## Changelog
|
||||
|
||||
See the full history on GitHub: [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)# Export to Excel
|
||||
|
||||
This plugin allows you to export your chat history to an Excel (.xlsx) file directly from the chat interface.
|
||||
|
||||
|
||||
@@ -1,4 +1,59 @@
|
||||
# 导出为 Excel
|
||||
# 📊 导出为 Excel
|
||||
|
||||
**作者:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **版本:** 0.3.6 | **项目:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **许可证:** MIT
|
||||
|
||||
将对话历史直接导出为 Excel (.xlsx) 文件。
|
||||
|
||||
## 🔥 最新更新 v0.3.6
|
||||
|
||||
- **OpenWebUI 风格主题**:现代深灰表头(#1f2937)与浅灰斑马纹,提升可读性。
|
||||
- **斑马纹效果**:隔行变色(#ffffff / #f3f4f6),方便视觉扫描。
|
||||
- **智能数据类型转换**:自动将列转换为数字或日期类型,无法转换时保持字符串。
|
||||
- **全单元格粗体/斜体**:支持全单元格 Markdown 粗体与斜体格式。
|
||||
- **部分 Markdown 清理**:移除部分 Markdown 格式符号,输出更整洁。
|
||||
- **导出范围**:新增 `EXPORT_SCOPE`,可选择导出最后一条或所有消息。
|
||||
- **智能 Sheet 命名**:按 Markdown 标题、AI 标题或消息索引命名。
|
||||
- **多表格支持**:优化了多表格处理能力。
|
||||
- **智能文件名生成**:支持对话标题 / AI 总结 / Markdown 标题命名。
|
||||
- **配置选项**:新增 `TITLE_SOURCE` 控制文件名策略。
|
||||
- **AI 标题生成**:新增 `MODEL_ID`,支持 AI 标题生成与进度提示。
|
||||
|
||||
## ✨ 核心特性
|
||||
|
||||
- 🚀 **一键导出**:在聊天界面添加“导出为 Excel”按钮。
|
||||
- 🧠 **自动表头提取**:智能识别聊天内容中的表格标题。
|
||||
- 📊 **多表支持**:支持单次对话中的多个表格。
|
||||
|
||||
## 🚀 使用方法
|
||||
|
||||
1. **安装**:在 Open WebUI 社区搜索“导出为 Excel”并安装。
|
||||
2. **触发**:在任意对话中,点击“导出为 Excel”动作按钮。
|
||||
3. **下载**:.xlsx 文件将自动下载到你的设备。
|
||||
|
||||
## ⚙️ 配置参数 (Valves)
|
||||
|
||||
| 参数 | 默认值 | 描述 |
|
||||
| :--- | :--- | :--- |
|
||||
| `TITLE_SOURCE` | `chat_title` | 文件名来源:`chat_title`、`ai_generated`、`markdown_title`。 |
|
||||
| `EXPORT_SCOPE` | `last_message` | 导出范围:`last_message` 或 `all_messages`。 |
|
||||
| `MODEL_ID` | `""` | AI 标题生成的模型 ID。为空则使用当前对话模型。 |
|
||||
| `SHOW_STATUS` | `True` | 是否显示操作状态更新。 |
|
||||
| `SHOW_DEBUG_LOG` | `False` | 是否在浏览器控制台输出调试日志 (F12)。 |
|
||||
|
||||
## ⭐ 支持
|
||||
|
||||
如果这个插件对你有帮助,欢迎到 [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) 点个 Star,这将是我持续改进的动力,感谢支持。
|
||||
|
||||
## 故障排除 (Troubleshooting) ❓
|
||||
|
||||
- **插件不工作?**: 请检查是否在模型设置中启用了该过滤器/动作。
|
||||
- **调试日志**: 如需排查,启用 `SHOW_STATUS` 并查看浏览器控制台 (F12)。
|
||||
- **错误信息**: 如果看到错误,请复制完整的错误信息并报告。
|
||||
- **提交 Issue**: 如果遇到任何问题,请在 GitHub 上提交 Issue:[Awesome OpenWebUI Issues](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
|
||||
## 更新日志
|
||||
|
||||
完整历史请查看 GitHub 项目: [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)# 导出为 Excel
|
||||
|
||||
此插件允许你直接从聊天界面将对话历史导出为 Excel (.xlsx) 文件。
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""
|
||||
title: Export to Excel
|
||||
author: Fu-Jie
|
||||
author_url: https://github.com/Fu-Jie
|
||||
funding_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
author_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
funding_url: https://github.com/open-webui
|
||||
version: 0.3.7
|
||||
openwebui_id: 244b8f9d-7459-47d6-84d3-c7ae8e3ec710
|
||||
icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0xNSAySDZhMiAyIDAgMCAwLTIgMnYxNmEyIDIgMCAwIDAgMiAyaDEyYTIgMiAwIDAgMCAyLTJWN1oiLz48cGF0aCBkPSJNMTQgMnY0YTIgMiAwIDAgMCAyIDJoNCIvPjxwYXRoIGQ9Ik04IDEzaDIiLz48cGF0aCBkPSJNMTQgMTNoMiIvPjxwYXRoIGQ9Ik04IDE3aDIiLz48cGF0aCBkPSJNMTQgMTdoMiIvPjwvc3ZnPg==
|
||||
@@ -32,6 +32,10 @@ class Action:
|
||||
default="chat_title",
|
||||
description="Title Source: 'chat_title' (Chat Title), 'ai_generated' (AI Generated), 'markdown_title' (Markdown Title)",
|
||||
)
|
||||
SHOW_STATUS: bool = Field(
|
||||
default=True,
|
||||
description="Whether to show operation status updates.",
|
||||
)
|
||||
EXPORT_SCOPE: Literal["last_message", "all_messages"] = Field(
|
||||
default="last_message",
|
||||
description="Export Scope: 'last_message' (Last Message Only), 'all_messages' (All Messages)",
|
||||
@@ -40,14 +44,57 @@ class Action:
|
||||
default="",
|
||||
description="Model ID for AI title generation. Leave empty to use the current chat model.",
|
||||
)
|
||||
SHOW_DEBUG_LOG: bool = Field(
|
||||
default=False,
|
||||
description="Whether to print debug logs in the browser console.",
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.valves = self.Valves()
|
||||
|
||||
async def _send_notification(self, emitter: Callable, type: str, content: str):
|
||||
await emitter(
|
||||
{"type": "notification", "data": {"type": type, "content": content}}
|
||||
)
|
||||
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,
|
||||
ntype: str = "info",
|
||||
):
|
||||
"""Emits a notification event (info, success, warning, error)."""
|
||||
if emitter:
|
||||
await emitter(
|
||||
{"type": "notification", "data": {"type": ntype, "content": content}}
|
||||
)
|
||||
|
||||
async def _emit_debug_log(self, emitter, title: str, data: dict):
|
||||
"""Print structured debug logs in the browser console"""
|
||||
if not self.valves.SHOW_DEBUG_LOG or not emitter:
|
||||
return
|
||||
|
||||
try:
|
||||
import json
|
||||
|
||||
js_code = f"""
|
||||
(async function() {{
|
||||
console.group("🛠️ {title}");
|
||||
console.log({json.dumps(data, ensure_ascii=False)});
|
||||
console.groupEnd();
|
||||
}})();
|
||||
"""
|
||||
|
||||
await emitter({"type": "execute", "data": {"code": js_code}})
|
||||
except Exception as e:
|
||||
print(f"Error emitting debug log: {e}")
|
||||
|
||||
async def action(
|
||||
self,
|
||||
@@ -190,17 +237,18 @@ class Action:
|
||||
# Notify user about the number of tables found
|
||||
table_count = len(all_tables)
|
||||
if self.valves.EXPORT_SCOPE == "all_messages":
|
||||
await self._send_notification(
|
||||
await self._emit_notification(
|
||||
__event_emitter__,
|
||||
"info",
|
||||
f"Found {table_count} table(s) in all messages.",
|
||||
"info",
|
||||
)
|
||||
# Wait a moment for user to see the notification before download dialog
|
||||
await asyncio.sleep(1.5)
|
||||
# Generate Workbook Title (Filename)
|
||||
# Use the title of the chat, or the first header of the first message with tables
|
||||
title = ""
|
||||
chat_id = self.extract_chat_id(body, None)
|
||||
chat_ctx = self._get_chat_context(body, None)
|
||||
chat_id = chat_ctx["chat_id"]
|
||||
chat_title = ""
|
||||
if chat_id:
|
||||
chat_title = await self.fetch_chat_title(chat_id, user_id)
|
||||
@@ -330,8 +378,8 @@ class Action:
|
||||
},
|
||||
}
|
||||
)
|
||||
await self._send_notification(
|
||||
__event_emitter__, "error", "No tables found to export!"
|
||||
await self._emit_notification(
|
||||
__event_emitter__, "No tables found to export!", "error"
|
||||
)
|
||||
raise e
|
||||
except Exception as e:
|
||||
@@ -345,8 +393,8 @@ class Action:
|
||||
},
|
||||
}
|
||||
)
|
||||
await self._send_notification(
|
||||
__event_emitter__, "error", "No tables found to export!"
|
||||
await self._emit_notification(
|
||||
__event_emitter__, "No tables found to export!", "error"
|
||||
)
|
||||
|
||||
async def generate_title_using_ai(
|
||||
@@ -389,20 +437,20 @@ class Action:
|
||||
async def notification_task():
|
||||
# Send initial notification immediately
|
||||
if event_emitter:
|
||||
await self._send_notification(
|
||||
await self._emit_notification(
|
||||
event_emitter,
|
||||
"info",
|
||||
"AI is generating a filename for your Excel file...",
|
||||
"info",
|
||||
)
|
||||
|
||||
# Subsequent notifications every 5 seconds
|
||||
while True:
|
||||
await asyncio.sleep(5)
|
||||
if event_emitter:
|
||||
await self._send_notification(
|
||||
await self._emit_notification(
|
||||
event_emitter,
|
||||
"info",
|
||||
"Still generating filename, please be patient...",
|
||||
"info",
|
||||
)
|
||||
|
||||
# Run tasks concurrently
|
||||
@@ -432,10 +480,10 @@ class Action:
|
||||
except Exception as e:
|
||||
print(f"Error generating title: {e}")
|
||||
if event_emitter:
|
||||
await self._send_notification(
|
||||
await self._emit_notification(
|
||||
event_emitter,
|
||||
"warning",
|
||||
f"AI title generation failed, using default title. Error: {str(e)}",
|
||||
"warning",
|
||||
)
|
||||
|
||||
return ""
|
||||
@@ -450,24 +498,56 @@ class Action:
|
||||
return match.group(1).strip()
|
||||
return ""
|
||||
|
||||
def extract_chat_id(self, body: dict, metadata: Optional[dict]) -> str:
|
||||
"""Extract chat_id from body or metadata"""
|
||||
if isinstance(body, dict):
|
||||
chat_id = body.get("chat_id") or body.get("id")
|
||||
if isinstance(chat_id, str) and chat_id.strip():
|
||||
return chat_id.strip()
|
||||
def _get_user_context(self, __user__: Optional[Dict[str, Any]]) -> Dict[str, str]:
|
||||
"""Safely 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 = {}
|
||||
|
||||
for key in ("chat", "conversation"):
|
||||
nested = body.get(key)
|
||||
if isinstance(nested, dict):
|
||||
nested_id = nested.get("id") or nested.get("chat_id")
|
||||
if isinstance(nested_id, str) and nested_id.strip():
|
||||
return nested_id.strip()
|
||||
if isinstance(metadata, dict):
|
||||
chat_id = metadata.get("chat_id")
|
||||
if isinstance(chat_id, str) and chat_id.strip():
|
||||
return chat_id.strip()
|
||||
return ""
|
||||
return {
|
||||
"user_id": user_data.get("id", "unknown_user"),
|
||||
"user_name": user_data.get("name", "User"),
|
||||
"user_language": user_data.get("language", "en-US"),
|
||||
}
|
||||
|
||||
def _get_chat_context(
|
||||
self, body: dict, __metadata__: Optional[dict] = None
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Unified extraction of chat context information (chat_id, message_id).
|
||||
Prioritizes extraction from body, then metadata.
|
||||
"""
|
||||
chat_id = ""
|
||||
message_id = ""
|
||||
|
||||
# 1. Try to get from body
|
||||
if isinstance(body, dict):
|
||||
chat_id = body.get("chat_id", "")
|
||||
message_id = body.get("id", "") # message_id is usually 'id' in body
|
||||
|
||||
# Check body.metadata as fallback
|
||||
if not chat_id or not message_id:
|
||||
body_metadata = body.get("metadata", {})
|
||||
if isinstance(body_metadata, dict):
|
||||
if not chat_id:
|
||||
chat_id = body_metadata.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = body_metadata.get("message_id", "")
|
||||
|
||||
# 2. Try to get from __metadata__ (as supplement)
|
||||
if __metadata__ and isinstance(__metadata__, dict):
|
||||
if not chat_id:
|
||||
chat_id = __metadata__.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = __metadata__.get("message_id", "")
|
||||
|
||||
return {
|
||||
"chat_id": str(chat_id).strip(),
|
||||
"message_id": str(message_id).strip(),
|
||||
}
|
||||
|
||||
async def fetch_chat_title(self, chat_id: str, user_id: str = "") -> str:
|
||||
"""Fetch chat title from database by chat_id"""
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""
|
||||
title: 导出为 Excel
|
||||
author: Fu-Jie
|
||||
author_url: https://github.com/Fu-Jie
|
||||
funding_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
author_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
funding_url: https://github.com/open-webui
|
||||
version: 0.3.7
|
||||
icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0xNSAySDZhMiAyIDAgMCAwLTIgMnYxNmEyIDIgMCAwIDAgMiAyaDEyYTIgMiAwIDAgMCAyLTJWN1oiLz48cGF0aCBkPSJNMTQgMnY0YTIgMiAwIDAgMCAyIDJoNCIvPjxwYXRoIGQ9Ik04IDEzaDIiLz48cGF0aCBkPSJNMTQgMTNoMiIvPjxwYXRoIGQ9Ik04IDE3aDIiLz48cGF0aCBkPSJNMTQgMTdoMiIvPjwvc3ZnPg==
|
||||
description: 从聊天消息中提取表格并导出为 Excel (.xlsx) 文件,支持智能格式化。
|
||||
@@ -31,6 +31,10 @@ class Action:
|
||||
default="chat_title",
|
||||
description="标题来源: 'chat_title' (对话标题), 'ai_generated' (AI生成), 'markdown_title' (Markdown标题)",
|
||||
)
|
||||
SHOW_STATUS: bool = Field(
|
||||
default=True,
|
||||
description="是否显示操作状态更新。",
|
||||
)
|
||||
EXPORT_SCOPE: Literal["last_message", "all_messages"] = Field(
|
||||
default="last_message",
|
||||
description="导出范围: 'last_message' (仅最后一条消息), 'all_messages' (所有消息)",
|
||||
@@ -39,14 +43,57 @@ class Action:
|
||||
default="",
|
||||
description="AI 标题生成模型 ID。留空则使用当前对话模型。",
|
||||
)
|
||||
SHOW_DEBUG_LOG: bool = Field(
|
||||
default=False,
|
||||
description="是否在浏览器控制台打印调试日志。",
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.valves = self.Valves()
|
||||
|
||||
async def _send_notification(self, emitter: Callable, type: str, content: str):
|
||||
await emitter(
|
||||
{"type": "notification", "data": {"type": type, "content": content}}
|
||||
)
|
||||
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,
|
||||
ntype: str = "info",
|
||||
):
|
||||
"""Emits a notification event (info, success, warning, error)."""
|
||||
if emitter:
|
||||
await emitter(
|
||||
{"type": "notification", "data": {"type": ntype, "content": content}}
|
||||
)
|
||||
|
||||
async def _emit_debug_log(self, emitter, title: str, data: dict):
|
||||
"""在浏览器控制台打印结构化调试日志"""
|
||||
if not self.valves.SHOW_DEBUG_LOG or not emitter:
|
||||
return
|
||||
|
||||
try:
|
||||
import json
|
||||
|
||||
js_code = f"""
|
||||
(async function() {{
|
||||
console.group("🛠️ {title}");
|
||||
console.log({json.dumps(data, ensure_ascii=False)});
|
||||
console.groupEnd();
|
||||
}})();
|
||||
"""
|
||||
|
||||
await emitter({"type": "execute", "data": {"code": js_code}})
|
||||
except Exception as e:
|
||||
print(f"Error emitting debug log: {e}")
|
||||
|
||||
async def action(
|
||||
self,
|
||||
@@ -180,17 +227,18 @@ class Action:
|
||||
# 通知用户提取到的表格数量
|
||||
table_count = len(all_tables)
|
||||
if self.valves.EXPORT_SCOPE == "all_messages":
|
||||
await self._send_notification(
|
||||
await self._emit_notification(
|
||||
__event_emitter__,
|
||||
"info",
|
||||
f"从所有消息中提取到 {table_count} 个表格。",
|
||||
"info",
|
||||
)
|
||||
# 等待片刻让用户看到通知,再触发下载
|
||||
await asyncio.sleep(1.5)
|
||||
|
||||
# Generate Workbook Title (Filename)
|
||||
title = ""
|
||||
chat_id = self.extract_chat_id(body, None)
|
||||
chat_ctx = self._get_chat_context(body, None)
|
||||
chat_id = chat_ctx["chat_id"]
|
||||
chat_title = ""
|
||||
if chat_id:
|
||||
chat_title = await self.fetch_chat_title(chat_id, user_id)
|
||||
@@ -318,8 +366,8 @@ class Action:
|
||||
},
|
||||
}
|
||||
)
|
||||
await self._send_notification(
|
||||
__event_emitter__, "error", "未找到可导出的表格!"
|
||||
await self._emit_notification(
|
||||
__event_emitter__, "未找到可导出的表格!", "error"
|
||||
)
|
||||
raise e
|
||||
except Exception as e:
|
||||
@@ -333,8 +381,8 @@ class Action:
|
||||
},
|
||||
}
|
||||
)
|
||||
await self._send_notification(
|
||||
__event_emitter__, "error", "未找到可导出的表格!"
|
||||
await self._emit_notification(
|
||||
__event_emitter__, "未找到可导出的表格!", "error"
|
||||
)
|
||||
|
||||
async def generate_title_using_ai(
|
||||
@@ -377,20 +425,20 @@ class Action:
|
||||
async def notification_task():
|
||||
# 立即发送首次通知
|
||||
if event_emitter:
|
||||
await self._send_notification(
|
||||
await self._emit_notification(
|
||||
event_emitter,
|
||||
"info",
|
||||
"AI 正在为您生成文件名,请稍候...",
|
||||
"info",
|
||||
)
|
||||
|
||||
# 之后每5秒通知一次
|
||||
while True:
|
||||
await asyncio.sleep(5)
|
||||
if event_emitter:
|
||||
await self._send_notification(
|
||||
await self._emit_notification(
|
||||
event_emitter,
|
||||
"info",
|
||||
"文件名生成中,请耐心等待...",
|
||||
"info",
|
||||
)
|
||||
|
||||
# 并发运行任务
|
||||
@@ -420,10 +468,10 @@ class Action:
|
||||
except Exception as e:
|
||||
print(f"生成标题时出错: {e}")
|
||||
if event_emitter:
|
||||
await self._send_notification(
|
||||
await self._emit_notification(
|
||||
event_emitter,
|
||||
"warning",
|
||||
f"AI 文件名生成失败,将使用默认名称。错误: {str(e)}",
|
||||
"warning",
|
||||
)
|
||||
|
||||
return ""
|
||||
@@ -438,24 +486,56 @@ class Action:
|
||||
return match.group(1).strip()
|
||||
return ""
|
||||
|
||||
def extract_chat_id(self, body: dict, metadata: Optional[dict]) -> str:
|
||||
"""从 body 或 metadata 中提取 chat_id"""
|
||||
if isinstance(body, dict):
|
||||
chat_id = body.get("chat_id") or body.get("id")
|
||||
if isinstance(chat_id, str) and chat_id.strip():
|
||||
return chat_id.strip()
|
||||
def _get_user_context(self, __user__: Optional[Dict[str, Any]]) -> Dict[str, str]:
|
||||
"""安全提取用户上下文信息。"""
|
||||
if isinstance(__user__, (list, tuple)):
|
||||
user_data = __user__[0] if __user__ else {}
|
||||
elif isinstance(__user__, dict):
|
||||
user_data = __user__
|
||||
else:
|
||||
user_data = {}
|
||||
|
||||
for key in ("chat", "conversation"):
|
||||
nested = body.get(key)
|
||||
if isinstance(nested, dict):
|
||||
nested_id = nested.get("id") or nested.get("chat_id")
|
||||
if isinstance(nested_id, str) and nested_id.strip():
|
||||
return nested_id.strip()
|
||||
if isinstance(metadata, dict):
|
||||
chat_id = metadata.get("chat_id")
|
||||
if isinstance(chat_id, str) and chat_id.strip():
|
||||
return chat_id.strip()
|
||||
return ""
|
||||
return {
|
||||
"user_id": user_data.get("id", "unknown_user"),
|
||||
"user_name": user_data.get("name", "用户"),
|
||||
"user_language": user_data.get("language", "zh-CN"),
|
||||
}
|
||||
|
||||
def _get_chat_context(
|
||||
self, body: dict, __metadata__: Optional[dict] = None
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
统一提取聊天上下文信息 (chat_id, message_id)。
|
||||
优先从 body 中提取,其次从 metadata 中提取。
|
||||
"""
|
||||
chat_id = ""
|
||||
message_id = ""
|
||||
|
||||
# 1. 尝试从 body 获取
|
||||
if isinstance(body, dict):
|
||||
chat_id = body.get("chat_id", "")
|
||||
message_id = body.get("id", "") # message_id 在 body 中通常是 id
|
||||
|
||||
# 再次检查 body.metadata
|
||||
if not chat_id or not message_id:
|
||||
body_metadata = body.get("metadata", {})
|
||||
if isinstance(body_metadata, dict):
|
||||
if not chat_id:
|
||||
chat_id = body_metadata.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = body_metadata.get("message_id", "")
|
||||
|
||||
# 2. 尝试从 __metadata__ 获取 (作为补充)
|
||||
if __metadata__ and isinstance(__metadata__, dict):
|
||||
if not chat_id:
|
||||
chat_id = __metadata__.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = __metadata__.get("message_id", "")
|
||||
|
||||
return {
|
||||
"chat_id": str(chat_id).strip(),
|
||||
"message_id": str(message_id).strip(),
|
||||
}
|
||||
|
||||
async def fetch_chat_title(self, chat_id: str, user_id: str = "") -> str:
|
||||
"""通过 chat_id 从数据库获取对话标题"""
|
||||
|
||||
@@ -2,9 +2,15 @@
|
||||
|
||||
Generate polished learning flashcards from any text—title, summary, key points, tags, and category—ready for review and sharing.
|
||||
|
||||

|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **Version:** 0.2.4 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **License:** MIT
|
||||
|
||||
## Highlights
|
||||
## What's New
|
||||
|
||||
### v0.2.4
|
||||
|
||||
- **Clean Output**: Removed debug messages from output.
|
||||
|
||||
## Key Features 🔑
|
||||
|
||||
- **One-click generation**: Drop in text, get a structured card.
|
||||
- **Concise extraction**: 3–5 key points and 2–4 tags automatically surfaced.
|
||||
@@ -12,7 +18,14 @@ Generate polished learning flashcards from any text—title, summary, key points
|
||||
- **Progressive merge**: Multiple runs append cards into the same HTML container; enable clearing to reset.
|
||||
- **Status updates**: Live notifications for generating/done/error.
|
||||
|
||||
## Parameters
|
||||
## How to Use 🛠️
|
||||
|
||||
1. **Install**: Add the plugin to your OpenWebUI instance.
|
||||
2. **Configure**: Adjust settings in the Valves menu (optional).
|
||||
3. **Trigger**: Send text to the chat.
|
||||
4. **Result**: Watch status updates; the card HTML is embedded into the latest message.
|
||||
|
||||
## Configuration (Valves) ⚙️
|
||||
|
||||
| Param | Description | Default |
|
||||
| ------------------- | ------------------------------------------------------------ | ------- |
|
||||
@@ -23,34 +36,21 @@ Generate polished learning flashcards from any text—title, summary, key points
|
||||
| CLEAR_PREVIOUS_HTML | Whether to clear previous card HTML (otherwise append/merge) | false |
|
||||
| MESSAGE_COUNT | Use the latest N messages to build the card | 1 |
|
||||
|
||||
## How to Use
|
||||
## ⭐ Support
|
||||
|
||||
1. Install and enable “Flash Card”.
|
||||
2. Send the text to the chat (multi-turn supported; governed by MESSAGE_COUNT).
|
||||
3. Watch status updates; the card HTML is embedded into the latest message.
|
||||
4. To regenerate from scratch, toggle CLEAR_PREVIOUS_HTML or resend text.
|
||||
If this plugin has been useful, a star on [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) is a big motivation for me. Thank you for the support.
|
||||
|
||||
## Output Format
|
||||
## Troubleshooting ❓
|
||||
|
||||
- JSON fields: `title`, `summary`, `key_points` (3–5), `tags` (2–4), `category`.
|
||||
- UI: gradient-styled card with tags, key-point list; supports stacking multiple cards.
|
||||
- **Plugin not working?**: Check if the filter/action is enabled in the model settings.
|
||||
- **Debug Logs**: Enable `SHOW_STATUS` in Valves to see progress updates.
|
||||
- **Error Messages**: If you see an error, please copy the full error message and report it.
|
||||
- **Submit an Issue**: If you encounter any problems, please submit an issue on GitHub: [Awesome OpenWebUI Issues](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
|
||||
## Tips
|
||||
## Preview 📸
|
||||
|
||||
- Very short text triggers a prompt to add more; consider summarizing first.
|
||||
- Long text is accepted; for deep analysis, pre-condense with other tools before card creation.
|
||||
|
||||
## Author
|
||||
|
||||
Fu-Jie
|
||||
GitHub: [Fu-Jie/awesome-openwebui](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||

|
||||
|
||||
## Changelog
|
||||
|
||||
### v0.2.4
|
||||
|
||||
- Removed debug messages from output
|
||||
See the full history on GitHub: [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
@@ -2,17 +2,28 @@
|
||||
|
||||
快速将文本提炼为精美的学习记忆卡片,自动抽取标题、摘要、关键要点、标签和分类,适合复习与分享。
|
||||
|
||||

|
||||
**作者:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **版本:** 0.2.4 | **项目:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **许可证:** MIT
|
||||
|
||||
## 功能亮点
|
||||
## 🔥 最新更新 v0.2.4
|
||||
|
||||
- **一键生成**:输入任意文本,直接产出结构化卡片。
|
||||
- **要点聚合**:自动提取 3-5 个记忆要点与 2-4 个标签。
|
||||
- **多语言支持**:可设定目标语言(默认中文)。
|
||||
- **渐进合并**:多次调用会将新卡片合并到同一 HTML 容器中;如需重置可启用清空选项。
|
||||
- **状态提示**:实时推送“生成中/完成/错误”等状态与通知。
|
||||
* **输出优化**: 移除输出中的调试信息。
|
||||
|
||||
## 参数说明
|
||||
## 核心特性 🔑
|
||||
|
||||
* **一键生成**:输入任意文本,直接产出结构化卡片。
|
||||
* **要点聚合**:自动提取 3-5 个记忆要点与 2-4 个标签。
|
||||
* **多语言支持**:可设定目标语言(默认中文)。
|
||||
* **渐进合并**:多次调用会将新卡片合并到同一 HTML 容器中;如需重置可启用清空选项。
|
||||
* **状态提示**:实时推送“生成中/完成/错误”等状态与通知。
|
||||
|
||||
## 使用方法 🛠️
|
||||
|
||||
1. **安装**: 在插件市场安装并启用“闪记卡”。
|
||||
2. **配置**: 根据需要调整 Valves 设置(可选)。
|
||||
3. **触发**: 将待整理的文本发送到聊天框。
|
||||
4. **结果**: 等待状态提示,卡片将以 HTML 形式嵌入到最新消息中。
|
||||
|
||||
## 配置参数 (Valves) ⚙️
|
||||
|
||||
| 参数 | 说明 | 默认值 |
|
||||
| ------------------- | ------------------------------------- | ------ |
|
||||
@@ -23,34 +34,21 @@
|
||||
| CLEAR_PREVIOUS_HTML | 是否清空旧的卡片 HTML(否则合并追加) | false |
|
||||
| MESSAGE_COUNT | 取最近 N 条消息生成卡片 | 1 |
|
||||
|
||||
## 使用步骤
|
||||
## ⭐ 支持
|
||||
|
||||
1. 在插件市场安装并启用“闪记卡”。
|
||||
2. 将待整理的文本发送到聊天框(可多轮对话,受 MESSAGE_COUNT 控制)。
|
||||
3. 等待状态提示,卡片将以 HTML 形式嵌入到最新消息中。
|
||||
4. 若需重新生成,开启 CLEAR_PREVIOUS_HTML 或直接重发文本。
|
||||
如果这个插件对你有帮助,欢迎到 [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) 点个 Star,这将是我持续改进的动力,感谢支持。
|
||||
|
||||
## 输出格式
|
||||
## 故障排除 (Troubleshooting) ❓
|
||||
|
||||
- JSON 字段:`title`、`summary`、`key_points`(3-5 条)、`tags`(2-4 条)、`category`。
|
||||
- 前端呈现:单卡片带渐变主题、标签胶囊、要点列表,可连续追加多张卡片。
|
||||
* **插件不工作?**: 请检查是否在模型设置中启用了该过滤器/动作。
|
||||
* **调试日志**: 在 Valves 中启用 `SHOW_STATUS` 以查看进度更新。
|
||||
* **错误信息**: 如果看到错误,请复制完整的错误信息并报告。
|
||||
* **提交 Issue**: 如果遇到任何问题,请在 GitHub 上提交 Issue:[Awesome OpenWebUI Issues](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
|
||||
## 使用建议
|
||||
## 预览 📸
|
||||
|
||||
- 文本过短会提醒补充,可先汇总再生成卡片。
|
||||
- 长文本无需截断,直接生成;如需深度分析可先用其他工具精炼后再制作卡片。
|
||||
|
||||
## 作者
|
||||
|
||||
Fu-Jie
|
||||
GitHub: [Fu-Jie/awesome-openwebui](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
||||

|
||||
|
||||
## 更新日志
|
||||
|
||||
### v0.2.4
|
||||
|
||||
- 移除输出中的调试信息
|
||||
完整历史请查看 GitHub 项目: [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""
|
||||
title: Flash Card
|
||||
author: Fu-Jie
|
||||
author_url: https://github.com/Fu-Jie
|
||||
funding_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
author_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
funding_url: https://github.com/open-webui
|
||||
version: 0.2.4
|
||||
openwebui_id: 65a2ea8f-2a13-4587-9d76-55eea0035cc8
|
||||
icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwb2x5Z29uIHBvaW50cz0iMTIgMiAyIDcgMTIgMTIgMjIgNyAxMiAyIi8+PHBvbHlsaW5lIHBvaW50cz0iMiAxNyAxMiAyMiAyMiAxNyIvPjxwb2x5bGluZSBwb2ludHM9IjIgMTIgMTIgMTcgMjIgMTIiLz48L3N2Zz4=
|
||||
@@ -89,6 +89,10 @@ class Action:
|
||||
default=True,
|
||||
description="Whether to show status updates in the chat interface.",
|
||||
)
|
||||
SHOW_DEBUG_LOG: bool = Field(
|
||||
default=False,
|
||||
description="Whether to print debug logs in the browser console.",
|
||||
)
|
||||
CLEAR_PREVIOUS_HTML: bool = Field(
|
||||
default=False,
|
||||
description="Whether to force clear previous plugin results (if True, overwrites instead of merging).",
|
||||
@@ -116,6 +120,42 @@ class Action:
|
||||
"user_language": user_data.get("language", "en-US"),
|
||||
}
|
||||
|
||||
def _get_chat_context(
|
||||
self, body: dict, __metadata__: Optional[dict] = None
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Unified extraction of chat context information (chat_id, message_id).
|
||||
Prioritizes extraction from body, then metadata.
|
||||
"""
|
||||
chat_id = ""
|
||||
message_id = ""
|
||||
|
||||
# 1. Try to get from body
|
||||
if isinstance(body, dict):
|
||||
chat_id = body.get("chat_id", "")
|
||||
message_id = body.get("id", "") # message_id is usually 'id' in body
|
||||
|
||||
# Check body.metadata as fallback
|
||||
if not chat_id or not message_id:
|
||||
body_metadata = body.get("metadata", {})
|
||||
if isinstance(body_metadata, dict):
|
||||
if not chat_id:
|
||||
chat_id = body_metadata.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = body_metadata.get("message_id", "")
|
||||
|
||||
# 2. Try to get from __metadata__ (as supplement)
|
||||
if __metadata__ and isinstance(__metadata__, dict):
|
||||
if not chat_id:
|
||||
chat_id = __metadata__.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = __metadata__.get("message_id", "")
|
||||
|
||||
return {
|
||||
"chat_id": str(chat_id).strip(),
|
||||
"message_id": str(message_id).strip(),
|
||||
}
|
||||
|
||||
async def action(
|
||||
self,
|
||||
body: dict,
|
||||
@@ -331,6 +371,26 @@ Important Principles:
|
||||
{"type": "notification", "data": {"type": ntype, "content": content}}
|
||||
)
|
||||
|
||||
async def _emit_debug_log(self, emitter, title: str, data: dict):
|
||||
"""Print structured debug logs in the browser console"""
|
||||
if not self.valves.SHOW_DEBUG_LOG or not emitter:
|
||||
return
|
||||
|
||||
try:
|
||||
import json
|
||||
|
||||
js_code = f"""
|
||||
(async function() {{
|
||||
console.group("🛠️ {title}");
|
||||
console.log({json.dumps(data, ensure_ascii=False)});
|
||||
console.groupEnd();
|
||||
}})();
|
||||
"""
|
||||
|
||||
await emitter({"type": "execute", "data": {"code": js_code}})
|
||||
except Exception as e:
|
||||
print(f"Error emitting debug log: {e}")
|
||||
|
||||
def _remove_existing_html(self, content: str) -> str:
|
||||
"""Removes existing plugin-generated HTML code blocks from the content."""
|
||||
pattern = r"```html\s*<!-- OPENWEBUI_PLUGIN_OUTPUT -->[\s\S]*?```"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""
|
||||
title: 闪记卡 (Flash Card)
|
||||
author: Fu-Jie
|
||||
author_url: https://github.com/Fu-Jie
|
||||
funding_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
author_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
funding_url: https://github.com/open-webui
|
||||
version: 0.2.4
|
||||
openwebui_id: 4a31eac3-a3c4-4c30-9ca5-dab36b5fac65
|
||||
icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwb2x5Z29uIHBvaW50cz0iMTIgMiAyIDcgMTIgMTIgMjIgNyAxMiAyIi8+PHBvbHlsaW5lIHBvaW50cz0iMiAxNyAxMiAyMiAyMiAxNyIvPjxwb2x5bGluZSBwb2ludHM9IjIgMTIgMTIgMTcgMjIgMTIiLz48L3N2Zz4=
|
||||
@@ -86,6 +86,10 @@ class Action:
|
||||
SHOW_STATUS: bool = Field(
|
||||
default=True, description="是否在聊天界面显示状态更新。"
|
||||
)
|
||||
SHOW_DEBUG_LOG: bool = Field(
|
||||
default=False,
|
||||
description="是否在浏览器控制台打印调试日志。",
|
||||
)
|
||||
CLEAR_PREVIOUS_HTML: bool = Field(
|
||||
default=False,
|
||||
description="是否强制清除旧的插件结果(如果为 True,则不合并,直接覆盖)。",
|
||||
@@ -113,6 +117,42 @@ class Action:
|
||||
"user_language": user_data.get("language", "zh-CN"),
|
||||
}
|
||||
|
||||
def _get_chat_context(
|
||||
self, body: dict, __metadata__: Optional[dict] = None
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
统一提取聊天上下文信息 (chat_id, message_id)。
|
||||
优先从 body 中提取,其次从 metadata 中提取。
|
||||
"""
|
||||
chat_id = ""
|
||||
message_id = ""
|
||||
|
||||
# 1. 尝试从 body 获取
|
||||
if isinstance(body, dict):
|
||||
chat_id = body.get("chat_id", "")
|
||||
message_id = body.get("id", "") # message_id 在 body 中通常是 id
|
||||
|
||||
# 再次检查 body.metadata
|
||||
if not chat_id or not message_id:
|
||||
body_metadata = body.get("metadata", {})
|
||||
if isinstance(body_metadata, dict):
|
||||
if not chat_id:
|
||||
chat_id = body_metadata.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = body_metadata.get("message_id", "")
|
||||
|
||||
# 2. 尝试从 __metadata__ 获取 (作为补充)
|
||||
if __metadata__ and isinstance(__metadata__, dict):
|
||||
if not chat_id:
|
||||
chat_id = __metadata__.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = __metadata__.get("message_id", "")
|
||||
|
||||
return {
|
||||
"chat_id": str(chat_id).strip(),
|
||||
"message_id": str(message_id).strip(),
|
||||
}
|
||||
|
||||
async def action(
|
||||
self,
|
||||
body: dict,
|
||||
@@ -314,6 +354,26 @@ class Action:
|
||||
{"type": "notification", "data": {"type": ntype, "content": content}}
|
||||
)
|
||||
|
||||
async def _emit_debug_log(self, emitter, title: str, data: dict):
|
||||
"""在浏览器控制台打印结构化调试日志"""
|
||||
if not self.valves.SHOW_DEBUG_LOG or not emitter:
|
||||
return
|
||||
|
||||
try:
|
||||
import json
|
||||
|
||||
js_code = f"""
|
||||
(async function() {{
|
||||
console.group("🛠️ {title}");
|
||||
console.log({json.dumps(data, ensure_ascii=False)});
|
||||
console.groupEnd();
|
||||
}})();
|
||||
"""
|
||||
|
||||
await emitter({"type": "execute", "data": {"code": js_code}})
|
||||
except Exception as e:
|
||||
print(f"Error emitting debug log: {e}")
|
||||
|
||||
def _remove_existing_html(self, content: str) -> str:
|
||||
"""移除内容中已有的插件生成 HTML 代码块 (通过标记识别)。"""
|
||||
pattern = r"```html\s*<!-- OPENWEBUI_PLUGIN_OUTPUT -->[\s\S]*?```"
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
# 📊 Smart Infographic (AntV)
|
||||
# Smart Infographic
|
||||
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 1.4.1 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **Version:** 1.5.0 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **License:** MIT
|
||||
|
||||
An Open WebUI plugin powered by the AntV Infographic engine. It transforms long text into professional, beautiful infographics with a single click.
|
||||
|
||||
## 🔥 What's New in v1.4.1
|
||||
## 🔥 What's New in v1.5.0
|
||||
|
||||
- ✨ **PNG Upload**: Infographics now upload as PNG format for better Word export compatibility.
|
||||
- 🔧 **Canvas Conversion**: Uses browser canvas for high-quality SVG to PNG conversion (2x scale).
|
||||
|
||||
### Previous: v1.4.0
|
||||
|
||||
- ✨ **Default Mode Change**: Default output mode is now `image` (static image) for better compatibility.
|
||||
- 📱 **Responsive Sizing**: Images now auto-adapt to the chat container width.
|
||||
- 🌐 **Smart Language Detection**: Automatically detects the accurate UI language from your browser.
|
||||
- 🗣️ **Context-Aware Generation**: Generated infographics now strictly follow the language of your input content (e.g., input Japanese -> output Japanese infographic).
|
||||
- 🐛 **Bug Fixes**: Fixed issues with language synchronization between the UI and generated content.
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
- 🚀 **AI-Powered Transformation**: Automatically analyzes text logic, extracts key points, and generates structured charts.
|
||||
- 🎨 **Professional Templates**: Includes various AntV official templates: Lists, Trees, Mindmaps, Comparison Tables, Flowcharts, and Statistical Charts.
|
||||
- 🔍 **Auto-Icon Matching**: Built-in logic to search and match the most relevant Material Design Icons based on content.
|
||||
- 🎨 **70+ Professional Templates**: Includes various AntV official templates: Lists, Trees, Roadmaps, Timelines, Comparison Tables, SWOT, Quadrants, and Statistical Charts.
|
||||
- 🔍 **Auto-Icon Matching**: Built-in logic to search and match the most relevant icons (Iconify) and illustrations (unDraw).
|
||||
- 📥 **Multi-Format Export**: Download your infographics as **SVG**, **PNG**, or a **Standalone HTML** file.
|
||||
- 🌈 **Highly Customizable**: Supports Dark/Light modes, auto-adapts theme colors, with bold titles and refined card layouts.
|
||||
- 📱 **Responsive Design**: Generated charts look great on both desktop and mobile devices.
|
||||
@@ -43,14 +39,30 @@ You can adjust the following parameters in the plugin settings to optimize the g
|
||||
| **Message Count (MESSAGE_COUNT)** | `1` | Number of recent messages to use for analysis. Increase this for more context. |
|
||||
| **Output Mode (OUTPUT_MODE)** | `image` | `image` for static image embedding (default, better compatibility), `html` for interactive chart. |
|
||||
|
||||
## ⭐ Support
|
||||
|
||||
If this plugin has been useful, a star on [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) is a big motivation for me. Thank you for the support.
|
||||
|
||||
## 🛠️ Supported Template Types
|
||||
|
||||
| Category | Template Name | Use Case |
|
||||
| :--- | :--- | :--- |
|
||||
| **Lists & Hierarchy** | `list-grid`, `tree-vertical`, `mindmap` | Features, Org Charts, Brainstorming |
|
||||
| **Sequence & Relation** | `sequence-roadmap`, `relation-circle` | Roadmaps, Circular Flows, Steps |
|
||||
| **Comparison & Analysis** | `compare-binary`, `compare-swot`, `quadrant-quarter` | Pros/Cons, SWOT, Quadrants |
|
||||
| **Charts & Data** | `chart-bar`, `chart-line`, `chart-pie` | Trends, Distributions, Metrics |
|
||||
| **Sequence** | `sequence-timeline-simple`, `sequence-roadmap-vertical-simple`, `sequence-snake-steps-compact-card` | Timelines, Roadmaps, Processes |
|
||||
| **Lists** | `list-grid-candy-card-lite`, `list-row-horizontal-icon-arrow`, `list-column-simple-vertical-arrow` | Features, Bullet Points, Lists |
|
||||
| **Comparison** | `compare-binary-horizontal-underline-text-vs`, `compare-swot`, `quadrant-quarter-simple-card` | Pros/Cons, SWOT, Quadrants |
|
||||
| **Hierarchy** | `hierarchy-tree-tech-style-capsule-item`, `hierarchy-structure` | Org Charts, Structures |
|
||||
| **Charts** | `chart-column-simple`, `chart-bar-plain-text`, `chart-line-plain-text`, `chart-wordcloud` | Trends, Distributions, Metrics |
|
||||
|
||||
## Troubleshooting ❓
|
||||
|
||||
- **Plugin not working?**: Check if the filter/action is enabled in the model settings.
|
||||
- **Debug Logs**: Enable `SHOW_STATUS` in Valves to see progress updates.
|
||||
- **Error Messages**: If you see an error, please copy the full error message and report it.
|
||||
- **Submit an Issue**: If you encounter any problems, please submit an issue on GitHub: [Awesome OpenWebUI Issues](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
|
||||
## Changelog
|
||||
|
||||
See the full history on GitHub: [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
## 📝 Syntax Example (For Advanced Users)
|
||||
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
# 📊 智能信息图 (AntV Infographic)
|
||||
# 智能信息图
|
||||
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 1.4.1 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
**作者:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **版本:** 1.5.0 | **项目:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **许可证:** MIT
|
||||
|
||||
基于 AntV Infographic 引擎的 Open WebUI 插件,能够将长文本内容一键转换为专业、美观的信息图表。
|
||||
|
||||
## 🔥 v1.4.1 更新日志
|
||||
## 🔥 最新更新 v1.5.0
|
||||
|
||||
- ✨ **PNG 上传**:信息图现在以 PNG 格式上传,与 Word 导出完美兼容。
|
||||
- 🔧 **Canvas 转换**:使用浏览器 Canvas 高质量转换 SVG 为 PNG(2倍缩放)。
|
||||
|
||||
### 此前: v1.4.0
|
||||
|
||||
- ✨ **默认模式变更**:默认输出模式调整为 `image`(静态图片)。
|
||||
- 📱 **响应式尺寸**:图片模式下自动适应聊天容器宽度。
|
||||
- 🌐 **智能语言检测**:自动从浏览器准确识别当前界面语言设置。
|
||||
- 🗣️ **上下文感知生成**:生成的信息图内容现在严格跟随用户输入内容的语言(例如:输入日语 -> 生成日语信息图)。
|
||||
- 🐛 **问题修复**:修复了界面语言与生成内容语言不同步的问题。
|
||||
|
||||
## ✨ 核心特性
|
||||
|
||||
- 🚀 **智能转换**:自动分析文本核心逻辑,提取关键点并生成结构化图表。
|
||||
- 🎨 **专业模板**:内置多种 AntV 官方模板,包括列表、树图、思维导图、对比图、流程图及统计图表等。
|
||||
- 🔍 **自动图标匹配**:内置图标搜索逻辑,根据内容自动匹配最相关的 Material Design Icons。
|
||||
- 🎨 **70+ 专业模板**:内置多种 AntV 官方模板,包括列表、树图、路线图、时间线、对比图、SWOT、象限图及统计图表等。
|
||||
- 🔍 **自动图标匹配**:内置图标搜索逻辑,支持 Iconify 图标和 unDraw 插图自动匹配。
|
||||
- 📥 **多格式导出**:支持一键下载为 **SVG**、**PNG** 或 **独立 HTML** 文件。
|
||||
- 🌈 **高度自定义**:支持深色/浅色模式,自动适配主题颜色,主标题加粗突出,卡片布局精美。
|
||||
- 📱 **响应式设计**:生成的图表在桌面端和移动端均有良好的展示效果。
|
||||
@@ -43,14 +39,30 @@
|
||||
| **上下文消息数 (MESSAGE_COUNT)** | `1` | 用于分析的最近消息条数。增加此值可让 AI 参考更多对话背景。 |
|
||||
| **输出模式 (OUTPUT_MODE)** | `image` | `image` 为静态图片嵌入(默认,兼容性好),`html` 为交互式图表。 |
|
||||
|
||||
## ⭐ 支持
|
||||
|
||||
如果这个插件对你有帮助,欢迎到 [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) 点个 Star,这将是我持续改进的动力,感谢支持。
|
||||
|
||||
## 🛠️ 支持的模板类型
|
||||
|
||||
| 分类 | 模板名称 | 适用场景 |
|
||||
| :--- | :--- | :--- |
|
||||
| **列表与层级** | `list-grid`, `tree-vertical`, `mindmap` | 功能亮点、组织架构、思维导图 |
|
||||
| **顺序与关系** | `sequence-roadmap`, `relation-circle` | 发展历程、循环关系、步骤说明 |
|
||||
| **对比与分析** | `compare-binary`, `compare-swot`, `quadrant-quarter` | 优劣势对比、SWOT 分析、象限图 |
|
||||
| **图表与数据** | `chart-bar`, `chart-line`, `chart-pie` | 数据趋势、比例分布、数值对比 |
|
||||
| **时序与流程** | `sequence-timeline-simple`, `sequence-roadmap-vertical-simple`, `sequence-snake-steps-compact-card` | 时间线、路线图、步骤说明 |
|
||||
| **列表与网格** | `list-grid-candy-card-lite`, `list-row-horizontal-icon-arrow`, `list-column-simple-vertical-arrow` | 功能亮点、要点列举、清单 |
|
||||
| **对比与分析** | `compare-binary-horizontal-underline-text-vs`, `compare-swot`, `quadrant-quarter-simple-card` | 优劣势对比、SWOT 分析、象限图 |
|
||||
| **层级与结构** | `hierarchy-tree-tech-style-capsule-item`, `hierarchy-structure` | 组织架构、层级关系 |
|
||||
| **图表与数据** | `chart-column-simple`, `chart-bar-plain-text`, `chart-line-plain-text`, `chart-wordcloud` | 数据趋势、比例分布、数值对比 |
|
||||
|
||||
## 故障排除 (Troubleshooting) ❓
|
||||
|
||||
- **插件不工作?**: 请检查是否在模型设置中启用了该过滤器/动作。
|
||||
- **调试日志**: 在 Valves 中启用 `SHOW_STATUS` 以查看进度更新。
|
||||
- **错误信息**: 如果看到错误,请复制完整的错误信息并报告。
|
||||
- **提交 Issue**: 如果遇到任何问题,请在 GitHub 上提交 Issue:[Awesome OpenWebUI Issues](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
|
||||
## 更新日志
|
||||
|
||||
完整历史请查看 GitHub 项目: [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
## 📝 语法示例 (高级用户)
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
# 📊 Smart Infographic (AntV)
|
||||
|
||||
An Open WebUI plugin powered by the AntV Infographic engine. It transforms long text into professional, beautiful infographics with a single click.
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
- 🚀 **AI-Powered Transformation**: Automatically analyzes text logic, extracts key points, and generates structured charts.
|
||||
- 🎨 **Professional Templates**: Includes various AntV official templates: Lists, Trees, Mindmaps, Comparison Tables, Flowcharts, and Statistical Charts.
|
||||
- 🔍 **Auto-Icon Matching**: Built-in logic to search and match the most relevant Material Design Icons based on content.
|
||||
- 📥 **Multi-Format Export**: Download your infographics as **SVG**, **PNG**, or a **Standalone HTML** file.
|
||||
- 🌈 **Highly Customizable**: Supports Dark/Light modes, auto-adapts theme colors, with bold titles and refined card layouts.
|
||||
- 📱 **Responsive Design**: Generated charts look great on both desktop and mobile devices.
|
||||
|
||||
## 🛠️ Supported Template Types
|
||||
|
||||
| Category | Template Name | Use Case |
|
||||
| :--- | :--- | :--- |
|
||||
| **Lists & Hierarchy** | `list-grid`, `tree-vertical`, `mindmap` | Features, Org Charts, Brainstorming |
|
||||
| **Sequence & Relation** | `sequence-roadmap`, `relation-circle` | Roadmaps, Circular Flows, Steps |
|
||||
| **Comparison & Analysis** | `compare-binary`, `compare-swot`, `quadrant-quarter` | Pros/Cons, SWOT, Quadrants |
|
||||
| **Charts & Data** | `chart-bar`, `chart-line`, `chart-pie` | Trends, Distributions, Metrics |
|
||||
|
||||
## 🚀 How to Use
|
||||
|
||||
1. **Install**: Search for "Smart Infographic" in the Open WebUI Community and install.
|
||||
2. **Trigger**: Enter your text in the chat, then click the **Action Button** (📊 icon) next to the input box.
|
||||
3. **AI Processing**: The AI analyzes the text and generates the infographic syntax.
|
||||
4. **Preview & Download**: Preview the result and use the download buttons below to save your infographic.
|
||||
|
||||
## ⚙️ Configuration (Valves)
|
||||
|
||||
You can adjust the following parameters in the plugin settings to optimize the generation:
|
||||
|
||||
| Parameter | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| **Show Status (SHOW_STATUS)** | `True` | Whether to show real-time AI analysis and generation status in the chat. |
|
||||
| **Model ID (MODEL_ID)** | `Empty` | Specify the LLM model for text analysis. If empty, the current chat model is used. |
|
||||
| **Min Text Length (MIN_TEXT_LENGTH)** | `100` | Minimum characters required to trigger analysis, preventing accidental triggers on short text. |
|
||||
| **Clear Previous (CLEAR_PREVIOUS_HTML)** | `False` | Whether to clear previous charts. If `False`, new charts will be appended below. |
|
||||
| **Message Count (MESSAGE_COUNT)** | `1` | Number of recent messages to use for analysis. Increase this for more context. |
|
||||
|
||||
## 📝 Syntax Example (For Advanced Users)
|
||||
|
||||
You can also input this syntax directly for AI to render:
|
||||
|
||||
```infographic
|
||||
infographic list-grid
|
||||
data
|
||||
title 🚀 Plugin Benefits
|
||||
desc Why use the Smart Infographic plugin
|
||||
items
|
||||
- label Fast Generation
|
||||
desc Convert text to charts in seconds
|
||||
- label Beautiful Design
|
||||
desc Uses AntV professional design standards
|
||||
```
|
||||
|
||||
## 👨💻 Author
|
||||
|
||||
**jeff**
|
||||
- GitHub: [Fu-Jie/awesome-openwebui](https://github.com/Fu-Jie/awesome-openwebui)
|
||||
|
||||
## 📄 License
|
||||
|
||||
MIT License
|
||||
BIN
plugins/actions/infographic/infographic.png
Normal file
BIN
plugins/actions/infographic/infographic.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 234 KiB |
@@ -1,9 +1,10 @@
|
||||
"""
|
||||
title: 📊 Smart Infographic (AntV)
|
||||
author: jeff
|
||||
title: Smart Infographic
|
||||
author: Fu-Jie
|
||||
author_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
funding_url: https://github.com/open-webui
|
||||
icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPgogIDxsaW5lIHgxPSIxMiIgeTE9IjIwIiB4Mj0iMTIiIHkyPSIxMCIgLz4KICA8bGluZSB4MT0iMTgiIHkxPSIyMCIgeDI9IjE4IiB5Mj0iNCIgLz4KICA8bGluZSB4MT0iNiIgeTE9IjIwIiB4Mj0iNiIgeTI9IjE2IiAvPgo8L3N2Zz4=
|
||||
version: 1.4.1
|
||||
version: 1.5.0
|
||||
openwebui_id: ad6f0c7f-c571-4dea-821d-8e71697274cf
|
||||
description: AI-powered infographic generator based on AntV Infographic. Supports professional templates, auto-icon matching, and SVG/PNG downloads.
|
||||
"""
|
||||
@@ -31,6 +32,10 @@ logger = logging.getLogger(__name__)
|
||||
SYSTEM_PROMPT_INFOGRAPHIC_ASSISTANT = """
|
||||
You are a professional infographic design expert who can analyze user-provided text content and convert it into AntV Infographic syntax format.
|
||||
|
||||
## Important Language Rule
|
||||
- **GENERATE CONTENT IN INPUT LANGUAGE**: You must generate the text content of the infographic in the **exact same language** as the user's input content (the text you are analyzing).
|
||||
- **Format Consistency**: Even if this system prompt is in English, if the user input is in Chinese, the infographic content must be in Chinese. If input is Japanese, output Japanese.
|
||||
|
||||
## Infographic Syntax Specification
|
||||
|
||||
Infographic syntax is a Mermaid-like declarative syntax for describing infographic templates, data, and themes.
|
||||
@@ -47,24 +52,63 @@ Infographic syntax is a Mermaid-like declarative syntax for describing infograph
|
||||
|
||||
### Template Library & Selection Guide
|
||||
|
||||
Choose the most appropriate template based on the content structure:
|
||||
Choose the most appropriate template based on content structure.
|
||||
|
||||
#### 1. List & Hierarchy
|
||||
- **List**: `list-grid` (Grid Cards), `list-vertical` (Vertical List)
|
||||
- **Tree**: `tree-vertical` (Vertical Tree), `tree-horizontal` (Horizontal Tree)
|
||||
- **Mindmap**: `mindmap` (Mind Map)
|
||||
**Template Selection Guidelines (Official):**
|
||||
- Strict sequential order (processes/steps/trends) → `sequence-*` series
|
||||
- Timeline → `sequence-timeline-simple`
|
||||
- Roadmap → `sequence-roadmap-vertical-simple`
|
||||
- Zigzag steps → `sequence-horizontal-zigzag-underline-text`
|
||||
- Snake steps → `sequence-snake-steps-compact-card`
|
||||
- Listing viewpoints → `list-row-horizontal-icon-arrow` or `list-column-simple-vertical-arrow`
|
||||
- Comparative analysis (A vs B) → `compare-binary-horizontal-underline-text-vs`
|
||||
- SWOT analysis → `compare-swot`
|
||||
- Hierarchical structure (tree) → `hierarchy-tree-tech-style-capsule-item`
|
||||
- Data charts → `chart-*` series
|
||||
- Quadrant analysis → `quadrant-quarter-simple-card`
|
||||
- Grid lists (bullet points) → `list-grid-candy-card-lite`
|
||||
- Relationship display → `relation-circle-icon-badge`
|
||||
|
||||
#### 2. Sequence & Relationship
|
||||
- **Process**: `sequence-roadmap` (Roadmap), `sequence-zigzag` (Zigzag Process), `sequence-horizontal` (Horizontal Process)
|
||||
- **Relationship**: `relation-sankey` (Sankey Diagram), `relation-circle` (Circular Relationship)
|
||||
**Available Templates:**
|
||||
|
||||
#### 3. Comparison & Analysis
|
||||
- **Comparison**: `compare-binary` (Binary Comparison), `list-grid` (Multi-item Grid Comparison)
|
||||
- **Analysis**: `compare-swot` (SWOT Analysis), `quadrant-quarter` (Quadrant Chart)
|
||||
*Sequence (时序/流程):*
|
||||
`sequence-timeline-simple`, `sequence-roadmap-vertical-simple`, `sequence-horizontal-zigzag-underline-text`,
|
||||
`sequence-snake-steps-compact-card`, `sequence-zigzag-steps-underline-text`, `sequence-circular-simple`,
|
||||
`sequence-pyramid-simple`, `sequence-ascending-steps`
|
||||
|
||||
#### 4. Charts & Data
|
||||
- **Statistics**: `statistic-card` (Statistic Cards)
|
||||
- **Charts**: `chart-bar` (Bar Chart), `chart-column` (Column Chart), `chart-line` (Line Chart), `chart-pie` (Pie Chart), `chart-doughnut` (Doughnut Chart), `chart-area` (Area Chart)
|
||||
*List (列表):*
|
||||
`list-grid-candy-card-lite`, `list-grid-badge-card`, `list-row-horizontal-icon-arrow`,
|
||||
`list-column-simple-vertical-arrow`, `list-column-done-list`
|
||||
|
||||
*Compare (对比):*
|
||||
`compare-binary-horizontal-underline-text-vs`, `compare-binary-horizontal-simple-fold`,
|
||||
`compare-hierarchy-left-right-circle-node-pill-badge`, `compare-swot`
|
||||
|
||||
*Hierarchy (层级):*
|
||||
`hierarchy-tree-tech-style-capsule-item`, `hierarchy-tree-curved-line-rounded-rect-node`, `hierarchy-structure`
|
||||
|
||||
*Chart (图表):*
|
||||
`chart-column-simple`, `chart-bar-plain-text`, `chart-line-plain-text`,
|
||||
`chart-pie-plain-text`, `chart-pie-donut-plain-text`, `chart-wordcloud`
|
||||
|
||||
*Other:*
|
||||
`quadrant-quarter-simple-card`, `relation-circle-icon-badge`
|
||||
|
||||
**Text Capacity by Template Type:**
|
||||
- HIGH capacity (long descriptions OK): `list-column-*`, `compare-binary-*`, `sequence-timeline-*`
|
||||
- MEDIUM capacity: `list-row-*`, `sequence-roadmap-*`
|
||||
- LOW capacity (short text only): `list-grid-*`, `hierarchy-*`, `sequence-steps`
|
||||
|
||||
### Icon and Illustration Resources
|
||||
|
||||
**Icons (Iconify):**
|
||||
- Format: `<collection>/<icon-name>`, e.g., `mdi/rocket-launch`
|
||||
- Popular: `mdi/*` (Material Design), `fa/*` (Font Awesome), `bi/*` (Bootstrap)
|
||||
- Examples: `mdi/code-tags`, `mdi/chart-line`, `mdi/account-group`, `mdi/cloud`
|
||||
|
||||
**Illustrations (unDraw):**
|
||||
- Format: filename without .svg, e.g., `coding`, `team-work`
|
||||
- Use `illus` field instead of `icon`
|
||||
|
||||
### Data Structure Examples
|
||||
|
||||
@@ -211,6 +255,12 @@ data
|
||||
- `children`: Nested items (for trees, SWOT, etc.)
|
||||
- `illus`: Illustration icon (specific to some templates like Quadrant)
|
||||
|
||||
### Content Refinement Principles
|
||||
1. **Brevity is King**: Infographics are visual. Keep text to a minimum.
|
||||
2. **Title Limit**: Keep `label` (item titles) under 15 characters (approx. 10 Chinese characters).
|
||||
3. **Description Limit**: Keep `desc` (item descriptions) under 40 characters (approx. 20 Chinese characters / 2 lines).
|
||||
4. **Impact**: Use strong verbs and nouns. Avoid filler words.
|
||||
|
||||
## Output Requirements
|
||||
1. **Language**: Output content in the user's language.
|
||||
2. **Format**: Wrap output in ```infographic ... ```.
|
||||
@@ -218,6 +268,8 @@ data
|
||||
4. **Indentation**: Use 2 spaces.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
USER_PROMPT_GENERATE_INFOGRAPHIC = """
|
||||
Please analyze the following text content and convert its core information into AntV Infographic syntax format.
|
||||
|
||||
@@ -233,9 +285,18 @@ User Language: {user_language}
|
||||
|
||||
Please select the most appropriate infographic template based on text characteristics and output standard infographic syntax. Pay attention to correct indentation format (two spaces).
|
||||
|
||||
**Important Note:**
|
||||
- If using `list-grid` format, ensure each card's `desc` description is limited to **maximum 30 Chinese characters** (or **approximately 60 English characters**) to maintain visual consistency with all descriptions fitting in 2 lines.
|
||||
- Descriptions should be concise and highlight key points.
|
||||
**Visual Optimization Guide (MUST FOLLOW):**
|
||||
- **Point-based Generation:** Infographics are not articles. Extract KEYWORDS ONLY, avoid complete sentences.
|
||||
- **Main Title (`data.title`):** **MUST** be ≤ **15 Chinese characters** (or ≤30 English characters). Trim version numbers or details if needed.
|
||||
- **Subtitle (`data.desc`):** **MUST** be ≤ **20 Chinese characters** (or ≤40 English characters).
|
||||
- **Card Title (`label`):** **MUST** be ≤ **6 Chinese characters** (or ≤12 English characters). Use 2-4 keywords only.
|
||||
- **Card Description (`desc`):** **MUST** be ≤ **12 Chinese characters** (or ≤24 English characters). Use short phrases.
|
||||
|
||||
⚠️ **CRITICAL**: If the original text is too long, you MUST rephrase and shorten it. Do NOT simply truncate with "...".
|
||||
Examples:
|
||||
- ❌ "多步任务与工具协作能力" → ✅ "多步任务协作"
|
||||
- ❌ "Open WebUI v0.7.x 重大版本更新" → ✅ "v0.7 核心更新"
|
||||
- ❌ "自动查找历史聊天记录" → ✅ "历史检索"
|
||||
"""
|
||||
|
||||
# =================================================================
|
||||
@@ -340,8 +401,9 @@ CSS_TEMPLATE_INFOGRAPHIC = """
|
||||
.infographic-container-wrapper .infographic-render-container {
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
min-height: 600px;
|
||||
background: #fff;
|
||||
overflow: visible; /* Ensure content is visible */
|
||||
overflow: visible;
|
||||
transition: height 0.3s ease;
|
||||
}
|
||||
.infographic-render-container svg text {
|
||||
@@ -349,35 +411,59 @@ CSS_TEMPLATE_INFOGRAPHIC = """
|
||||
}
|
||||
.infographic-render-container svg foreignObject {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif !important;
|
||||
line-height: 1.4 !important;
|
||||
line-height: 1.3 !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
/* Main title styles */
|
||||
.infographic-render-container svg foreignObject[data-element-type="title"] > * {
|
||||
font-size: 1.5em !important;
|
||||
font-weight: bold !important;
|
||||
line-height: 1.4 !important;
|
||||
white-space: nowrap !important;
|
||||
font-size: 1.3em !important;
|
||||
font-weight: 800 !important;
|
||||
line-height: 1.3 !important;
|
||||
white-space: normal !important;
|
||||
word-break: break-word !important;
|
||||
display: -webkit-box !important;
|
||||
-webkit-line-clamp: 2 !important;
|
||||
-webkit-box-orient: vertical !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
text-align: center !important;
|
||||
}
|
||||
/* Page subtitle and card title styles */
|
||||
.infographic-render-container svg foreignObject[data-element-type="desc"] > *,
|
||||
.infographic-render-container svg foreignObject[data-element-type="item-label"] > * {
|
||||
font-size: 0.6em !important;
|
||||
line-height: 1.4 !important;
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
}
|
||||
/* Card title with extra bottom spacing */
|
||||
.infographic-render-container svg foreignObject[data-element-type="item-label"] > * {
|
||||
padding-bottom: 8px !important;
|
||||
/* Page subtitle styles */
|
||||
.infographic-render-container svg foreignObject[data-element-type="desc"] > * {
|
||||
font-size: 0.85em !important;
|
||||
line-height: 1.3 !important;
|
||||
white-space: normal !important;
|
||||
word-break: break-word !important;
|
||||
overflow: visible !important;
|
||||
text-align: center !important;
|
||||
display: block !important;
|
||||
color: var(--ig-muted-text-color) !important;
|
||||
}
|
||||
/* Card description text keeps normal wrapping */
|
||||
/* Card title styles */
|
||||
.infographic-render-container svg foreignObject[data-element-type="item-label"] > * {
|
||||
font-size: 0.9em !important;
|
||||
font-weight: 600 !important;
|
||||
line-height: 1.3 !important;
|
||||
white-space: normal !important;
|
||||
word-break: break-word !important;
|
||||
display: -webkit-box !important;
|
||||
-webkit-line-clamp: 2 !important;
|
||||
-webkit-box-orient: vertical !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
padding-bottom: 2px !important;
|
||||
}
|
||||
/* Card description text */
|
||||
.infographic-render-container svg foreignObject[data-element-type="item-desc"] > * {
|
||||
font-size: 0.8em !important;
|
||||
line-height: 1.4 !important;
|
||||
white-space: normal !important;
|
||||
word-break: break-word !important;
|
||||
display: -webkit-box !important;
|
||||
-webkit-line-clamp: 2 !important;
|
||||
-webkit-box-orient: vertical !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
}
|
||||
.infographic-container-wrapper .download-area {
|
||||
text-align: center;
|
||||
@@ -533,37 +619,41 @@ SCRIPT_TEMPLATE_INFOGRAPHIC = """
|
||||
}}
|
||||
}}
|
||||
|
||||
// 2. Template Mapping Configuration
|
||||
// 2. Template Mapping Configuration (Official AntV Structure IDs)
|
||||
const TEMPLATE_MAPPING = {{
|
||||
// List & Hierarchy
|
||||
// List & Hierarchy - map short names to full template names
|
||||
'list-grid': 'list-grid-compact-card',
|
||||
'list-column': 'list-column-simple-vertical-arrow',
|
||||
'list-row': 'list-row-simple-horizontal-arrow',
|
||||
'hierarchy-tree': 'hierarchy-tree-tech-style-capsule-item',
|
||||
|
||||
// Sequence & Timeline
|
||||
'sequence-roadmap-vertical': 'sequence-roadmap-vertical-simple',
|
||||
'sequence-timeline': 'sequence-timeline-simple',
|
||||
'sequence-steps': 'sequence-steps-simple',
|
||||
'sequence-horizontal-zigzag': 'sequence-horizontal-zigzag-simple',
|
||||
|
||||
// Comparison
|
||||
'compare-binary-horizontal': 'compare-binary-horizontal-simple-vs',
|
||||
'compare-hierarchy-row': 'compare-hierarchy-row-simple',
|
||||
|
||||
// Charts
|
||||
'chart-column': 'chart-column-simple',
|
||||
'quadrant': 'quadrant-quarter-simple-card',
|
||||
|
||||
// Legacy mappings for backward compatibility
|
||||
'list-vertical': 'list-column-simple-vertical-arrow',
|
||||
'tree-vertical': 'hierarchy-tree-tech-style-capsule-item',
|
||||
'tree-horizontal': 'hierarchy-tree-lr-tech-style-capsule-item',
|
||||
'mindmap': 'hierarchy-mindmap-branch-gradient-capsule-item',
|
||||
|
||||
// Sequence & Relationship
|
||||
'sequence-roadmap': 'sequence-roadmap-vertical-simple',
|
||||
'sequence-zigzag': 'sequence-horizontal-zigzag-simple',
|
||||
'sequence-horizontal': 'sequence-horizontal-zigzag-simple',
|
||||
'relation-sankey': 'relation-sankey-simple',
|
||||
'relation-circle': 'relation-circle-icon-badge',
|
||||
|
||||
// Comparison & Analysis
|
||||
'compare-binary': 'compare-binary-horizontal-simple-vs',
|
||||
'compare-swot': 'compare-swot',
|
||||
'quadrant-quarter': 'quadrant-quarter-simple-card',
|
||||
|
||||
// Charts & Data
|
||||
'statistic-card': 'list-grid-compact-card',
|
||||
'chart-bar': 'chart-bar-plain-text',
|
||||
'chart-column': 'chart-column-simple',
|
||||
'chart-line': 'chart-line-plain-text',
|
||||
'chart-area': 'chart-area-simple',
|
||||
'chart-pie': 'chart-pie-plain-text',
|
||||
'chart-doughnut': 'chart-pie-donut-plain-text'
|
||||
}};
|
||||
|
||||
|
||||
// 3. Apply Mapping Strategy
|
||||
for (const [key, value] of Object.entries(TEMPLATE_MAPPING)) {{
|
||||
const regex = new RegExp(`infographic\\\\s+${{key}}(?=\\\\s|$)`, 'i');
|
||||
@@ -629,10 +719,48 @@ SCRIPT_TEMPLATE_INFOGRAPHIC = """
|
||||
containerEl.dataset.infographicRendered = 'true';
|
||||
console.log('[Infographic] Rendering complete');
|
||||
|
||||
// Auto-adjust height
|
||||
// Auto-adjust height and tag elements
|
||||
setTimeout(() => {
|
||||
const svg = containerEl.querySelector('svg');
|
||||
if (svg) {
|
||||
// 1. Tag elements for CSS styling
|
||||
const fos = Array.from(svg.querySelectorAll('foreignObject'));
|
||||
let titleFound = false;
|
||||
let descFound = false;
|
||||
|
||||
fos.forEach((fo) => {
|
||||
const text = fo.textContent.trim();
|
||||
if (!text || fo.querySelector('i') || (fo.querySelector('svg') && fo.querySelectorAll('*').length < 5)) {
|
||||
fo.setAttribute('data-element-type', 'icon');
|
||||
return;
|
||||
}
|
||||
|
||||
// Dynamically increase height and width to accommodate wrapped text
|
||||
const currentHeight = parseInt(fo.getAttribute('height') || '0');
|
||||
if (currentHeight > 0 && currentHeight < 200) {
|
||||
fo.setAttribute('height', Math.round(currentHeight * 1.8).toString());
|
||||
}
|
||||
const currentWidth = parseInt(fo.getAttribute('width') || '0');
|
||||
if (currentWidth > 0 && currentWidth < 300) {
|
||||
fo.setAttribute('width', Math.max(Math.round(currentWidth * 1.2), 180).toString());
|
||||
}
|
||||
|
||||
if (!titleFound) {
|
||||
fo.setAttribute('data-element-type', 'title');
|
||||
titleFound = true;
|
||||
} else if (!descFound) {
|
||||
fo.setAttribute('data-element-type', 'desc');
|
||||
descFound = true;
|
||||
} else {
|
||||
if (fo.querySelector('strong') || fo.style.fontWeight === 'bold' || text.length < 15) {
|
||||
fo.setAttribute('data-element-type', 'item-label');
|
||||
} else {
|
||||
fo.setAttribute('data-element-type', 'item-desc');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 2. Adjust height
|
||||
const bbox = svg.getBoundingClientRect();
|
||||
let contentHeight = bbox.height;
|
||||
if (svg.viewBox && svg.viewBox.baseVal && svg.viewBox.baseVal.height) {
|
||||
@@ -826,49 +954,90 @@ class Action:
|
||||
default="image",
|
||||
description="Output mode: 'html' for interactive HTML, or 'image' to embed as Markdown image (default).",
|
||||
)
|
||||
SHOW_DEBUG_LOG: bool = Field(
|
||||
default=False,
|
||||
description="Whether to print debug logs in the browser console.",
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.valves = self.Valves()
|
||||
|
||||
def _extract_chat_id(self, body: dict, metadata: Optional[dict]) -> str:
|
||||
"""Extract chat_id from body or metadata"""
|
||||
async def _get_user_context(
|
||||
self,
|
||||
__user__: Optional[Dict[str, Any]],
|
||||
__event_call__: Optional[Callable[[Any], Awaitable[None]]] = None,
|
||||
) -> Dict[str, str]:
|
||||
"""Safely 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 = {}
|
||||
|
||||
user_id = user_data.get("id", "unknown_user")
|
||||
user_name = user_data.get("name", "User")
|
||||
user_language = user_data.get("language", "en-US")
|
||||
|
||||
if __event_call__:
|
||||
try:
|
||||
js_code = """
|
||||
return (
|
||||
localStorage.getItem('locale') ||
|
||||
localStorage.getItem('language') ||
|
||||
navigator.language ||
|
||||
'en-US'
|
||||
);
|
||||
"""
|
||||
frontend_lang = await __event_call__(
|
||||
{"type": "execute", "data": {"code": js_code}}
|
||||
)
|
||||
if frontend_lang and isinstance(frontend_lang, str):
|
||||
user_language = frontend_lang
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to retrieve frontend language: {e}")
|
||||
|
||||
return {
|
||||
"user_id": user_id,
|
||||
"user_name": user_name,
|
||||
"user_language": user_language,
|
||||
}
|
||||
|
||||
def _get_chat_context(
|
||||
self, body: dict, __metadata__: Optional[dict] = None
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Unified extraction of chat context information (chat_id, message_id).
|
||||
Prioritizes extraction from body, then metadata.
|
||||
"""
|
||||
chat_id = ""
|
||||
message_id = ""
|
||||
|
||||
# 1. Try to get from body
|
||||
if isinstance(body, dict):
|
||||
chat_id = body.get("chat_id")
|
||||
if isinstance(chat_id, str) and chat_id.strip():
|
||||
return chat_id.strip()
|
||||
chat_id = body.get("chat_id", "")
|
||||
message_id = body.get("id", "") # message_id is usually 'id' in body
|
||||
|
||||
body_metadata = body.get("metadata", {})
|
||||
if isinstance(body_metadata, dict):
|
||||
chat_id = body_metadata.get("chat_id")
|
||||
if isinstance(chat_id, str) and chat_id.strip():
|
||||
return chat_id.strip()
|
||||
# Check body.metadata as fallback
|
||||
if not chat_id or not message_id:
|
||||
body_metadata = body.get("metadata", {})
|
||||
if isinstance(body_metadata, dict):
|
||||
if not chat_id:
|
||||
chat_id = body_metadata.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = body_metadata.get("message_id", "")
|
||||
|
||||
if isinstance(metadata, dict):
|
||||
chat_id = metadata.get("chat_id")
|
||||
if isinstance(chat_id, str) and chat_id.strip():
|
||||
return chat_id.strip()
|
||||
# 2. Try to get from __metadata__ (as supplement)
|
||||
if __metadata__ and isinstance(__metadata__, dict):
|
||||
if not chat_id:
|
||||
chat_id = __metadata__.get("chat_id", "")
|
||||
if not message_id:
|
||||
message_id = __metadata__.get("message_id", "")
|
||||
|
||||
return ""
|
||||
|
||||
def _extract_message_id(self, body: dict, metadata: Optional[dict]) -> str:
|
||||
"""Extract message_id from body or metadata"""
|
||||
if isinstance(body, dict):
|
||||
message_id = body.get("id")
|
||||
if isinstance(message_id, str) and message_id.strip():
|
||||
return message_id.strip()
|
||||
|
||||
body_metadata = body.get("metadata", {})
|
||||
if isinstance(body_metadata, dict):
|
||||
message_id = body_metadata.get("message_id")
|
||||
if isinstance(message_id, str) and message_id.strip():
|
||||
return message_id.strip()
|
||||
|
||||
if isinstance(metadata, dict):
|
||||
message_id = metadata.get("message_id")
|
||||
if isinstance(message_id, str) and message_id.strip():
|
||||
return message_id.strip()
|
||||
|
||||
return ""
|
||||
return {
|
||||
"chat_id": str(chat_id).strip(),
|
||||
"message_id": str(message_id).strip(),
|
||||
}
|
||||
|
||||
def _extract_infographic_syntax(self, llm_output: str) -> str:
|
||||
"""Extract infographic syntax from LLM output"""
|
||||
@@ -897,6 +1066,24 @@ class Action:
|
||||
{"type": "notification", "data": {"type": ntype, "content": content}}
|
||||
)
|
||||
|
||||
async def _emit_debug_log(self, emitter, title: str, data: dict):
|
||||
"""Print structured debug logs in the browser console"""
|
||||
if not self.valves.SHOW_DEBUG_LOG or not emitter:
|
||||
return
|
||||
|
||||
try:
|
||||
js_code = f"""
|
||||
(async function() {{
|
||||
console.group("🛠️ {title}");
|
||||
console.log({json.dumps(data, ensure_ascii=False)});
|
||||
console.groupEnd();
|
||||
}})();
|
||||
"""
|
||||
|
||||
await emitter({"type": "execute", "data": {"code": js_code}})
|
||||
except Exception as e:
|
||||
print(f"Error emitting debug log: {e}")
|
||||
|
||||
def _remove_existing_html(self, content: str) -> str:
|
||||
"""Remove existing plugin-generated HTML code blocks from content"""
|
||||
pattern = r"```html\s*<!-- OPENWEBUI_PLUGIN_OUTPUT -->[\s\S]*?```"
|
||||
@@ -1312,18 +1499,10 @@ class Action:
|
||||
logger.info("Action: Infographic started (v1.4.0)")
|
||||
|
||||
# Get user information
|
||||
if isinstance(__user__, (list, tuple)):
|
||||
user_language = __user__[0].get("language", "en") if __user__ else "en"
|
||||
user_name = __user__[0].get("name", "User") if __user__[0] else "User"
|
||||
user_id = (
|
||||
__user__[0]["id"]
|
||||
if __user__ and "id" in __user__[0]
|
||||
else "unknown_user"
|
||||
)
|
||||
elif isinstance(__user__, dict):
|
||||
user_language = __user__.get("language", "en")
|
||||
user_name = __user__.get("name", "User")
|
||||
user_id = __user__.get("id", "unknown_user")
|
||||
user_ctx = await self._get_user_context(__user__, __event_call__)
|
||||
user_name = user_ctx["user_name"]
|
||||
user_id = user_ctx["user_id"]
|
||||
user_language = user_ctx["user_language"]
|
||||
|
||||
# Get current time
|
||||
now = datetime.now()
|
||||
@@ -1507,8 +1686,9 @@ class Action:
|
||||
# Check output mode
|
||||
if self.valves.OUTPUT_MODE == "image":
|
||||
# Image mode: use JavaScript to render and embed as Markdown image
|
||||
chat_id = self._extract_chat_id(body, body.get("metadata"))
|
||||
message_id = self._extract_message_id(body, body.get("metadata"))
|
||||
chat_ctx = self._get_chat_context(body, __metadata__)
|
||||
chat_id = chat_ctx["chat_id"]
|
||||
message_id = chat_ctx["message_id"]
|
||||
|
||||
await self._emit_status(
|
||||
__event_emitter__,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user