Compare commits
553 Commits
v2026.01.0
...
v2026.02.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db33f44cbc | ||
|
|
5b6dddd517 | ||
|
|
c8ba7c99af | ||
|
|
81279845e2 | ||
|
|
6b39531fbc | ||
|
|
377534e6c9 | ||
|
|
2da934dd92 | ||
|
|
18ada2a177 | ||
|
|
a5fbc8bed9 | ||
|
|
904eeb0ad0 | ||
|
|
9c70110e1d | ||
|
|
9af6a8c9bd | ||
|
|
ac948023e3 | ||
|
|
512b606827 | ||
|
|
fc9f1ccb43 | ||
|
|
272b959a44 | ||
|
|
0bde066088 | ||
|
|
6334660e8d | ||
|
|
c29d84f97a | ||
|
|
aac2e89022 | ||
|
|
fea812d4f4 | ||
|
|
b570cbfcde | ||
|
|
adc5e0a1f4 | ||
|
|
04b8108890 | ||
|
|
f1ba03e3bd | ||
|
|
cdd9950973 | ||
|
|
473012fa6f | ||
|
|
1bbddb2222 | ||
|
|
dc66610cb2 | ||
|
|
655b5311cf | ||
|
|
4390ee2085 | ||
|
|
6bf3656d30 | ||
|
|
bfb2039095 | ||
|
|
86091f77cf | ||
|
|
eb223e3e75 | ||
|
|
d1bbbd9071 | ||
|
|
840c77ea2f | ||
|
|
91ba7df086 | ||
|
|
fa636c7bc5 | ||
|
|
a5ad295d38 | ||
|
|
e1f70d52a5 | ||
|
|
015c22063a | ||
|
|
0ea95ceefa | ||
|
|
1192c71453 | ||
|
|
0b5663636f | ||
|
|
1736a1bfbf | ||
|
|
d7c25aa973 | ||
|
|
d71b4a7351 | ||
|
|
a05697df70 | ||
|
|
b355c3f0c1 | ||
|
|
30f2aed68a | ||
|
|
50c549b260 | ||
|
|
2a75580831 | ||
|
|
4032746243 | ||
|
|
871e76b1df | ||
|
|
954ebad8b2 | ||
|
|
fa5d962152 | ||
|
|
2ac8d4b14f | ||
|
|
c86b27a0c1 | ||
|
|
2381c5080e | ||
|
|
0394be7d16 | ||
|
|
7c9bf4082a | ||
|
|
8621d178ae | ||
|
|
20a7d57b5b | ||
|
|
8f72e25671 | ||
|
|
b7e62e63e0 | ||
|
|
bbccffa95b | ||
|
|
b9104702ac | ||
|
|
015b0d98ec | ||
|
|
fc56ea7faa | ||
|
|
e1c2261537 | ||
|
|
93e8e3bee2 | ||
|
|
1fb2cccd58 | ||
|
|
b34ce0b075 | ||
|
|
49efcb7e4d | ||
|
|
8d334a48b9 | ||
|
|
16882bf9e5 | ||
|
|
dc0366aab2 | ||
|
|
6d080d3a28 | ||
|
|
e50a55ee11 | ||
|
|
edbd75e5dc | ||
|
|
387ca8788b | ||
|
|
cfb15808ef | ||
|
|
0424521380 | ||
|
|
c2ea3b2479 | ||
|
|
a96a588141 | ||
|
|
f34da0b263 | ||
|
|
80dce6e1de | ||
|
|
080534d03b | ||
|
|
a63d3e89ff | ||
|
|
0b9c242c0f | ||
|
|
6f7ae4a304 | ||
|
|
571adf33c5 | ||
|
|
813e2b8320 | ||
|
|
21ec586c68 | ||
|
|
e95a786420 | ||
|
|
7773723b18 | ||
|
|
c547c1cee5 | ||
|
|
b04112a261 | ||
|
|
df977a7ccc | ||
|
|
3986eb854f | ||
|
|
fe170bedb9 | ||
|
|
3504313f15 | ||
|
|
e5e0f4cbcc | ||
|
|
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 | ||
|
|
24e7d34524 | ||
|
|
a58ce9e99e | ||
|
|
4a42dcf8de | ||
|
|
5903ea0e40 | ||
|
|
6d7a5b45cf | ||
|
|
10433d38b3 | ||
|
|
bf2bc80b22 | ||
|
|
1e0f5fb65a | ||
|
|
7d5a696106 | ||
|
|
cf86012d4d | ||
|
|
961c1cbca6 | ||
|
|
7fb5c243fa | ||
|
|
f845281b72 | ||
|
|
0b2c6a2d36 | ||
|
|
245c37b2c3 | ||
|
|
d2a915a514 | ||
|
|
ae731f9bd6 | ||
|
|
2a8a8c5805 | ||
|
|
deb1272f62 | ||
|
|
51c41b8628 | ||
|
|
37893ded00 | ||
|
|
38fe50a898 | ||
|
|
1c731e70dc | ||
|
|
a55aa4d8fd | ||
|
|
6c79cb2f11 | ||
|
|
ba7943bd6f | ||
|
|
6eb09c3eaa | ||
|
|
63c5257162 | ||
|
|
a2422262b5 | ||
|
|
4f49b111fd | ||
|
|
1d066fc1f0 | ||
|
|
e960c40351 | ||
|
|
96284a3652 | ||
|
|
ad2f38ec1f | ||
|
|
87fc34d505 | ||
|
|
2aafd3cef7 | ||
|
|
afec54c4e0 | ||
|
|
905a9e67ca | ||
|
|
ce56815e77 | ||
|
|
2684098be1 | ||
|
|
57ebf24c75 | ||
|
|
9375df709f | ||
|
|
255e48bd33 | ||
|
|
18993c7fbe | ||
|
|
f3cf2b52fd |
147
.agent/rules/antigravity.md
Normal file
147
.agent/rules/antigravity.md
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
---
|
||||||
|
description: >
|
||||||
|
Antigravity development mode rules. Apply when the user requests high-speed iteration,
|
||||||
|
a quick prototype, or says "反重力开发" / "antigravity mode".
|
||||||
|
globs: "plugins/**/*.py"
|
||||||
|
always_on: false
|
||||||
|
---
|
||||||
|
# Antigravity Development Mode
|
||||||
|
|
||||||
|
> High-speed delivery + strict reversibility. Every decision must keep both roll-forward and rollback feasible.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Principles
|
||||||
|
|
||||||
|
1. **Small, isolated edits** — one logical change per operation; no mixing refactor + feature.
|
||||||
|
2. **Deterministic interfaces** — function signatures and return shapes must not change without an explicit contract update.
|
||||||
|
3. **Multi-level fallback** — every I/O path has a degraded alternative (e.g., S3 → local → DB).
|
||||||
|
4. **Reversible by default** — every file write, API call, or schema change must have an undo path recorded or be idempotent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mandatory Safety Patterns
|
||||||
|
|
||||||
|
### 1. Timeout Guards on All Frontend Calls
|
||||||
|
|
||||||
|
Any `__event_call__` or `__event_emitter__` JS execution MUST be wrapped:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = await asyncio.wait_for(
|
||||||
|
__event_call__({"type": "execute", "data": {"code": js_code}}),
|
||||||
|
timeout=2.0
|
||||||
|
)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logger.warning("Frontend JS execution timed out; falling back.")
|
||||||
|
result = fallback_value
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Frontend call failed: {e}", exc_info=True)
|
||||||
|
result = fallback_value
|
||||||
|
```
|
||||||
|
|
||||||
|
JS side must also guard internally:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
try {
|
||||||
|
return (localStorage.getItem('locale') || navigator.language || 'en-US');
|
||||||
|
} catch (e) {
|
||||||
|
return 'en-US';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Path Sandbox Validation
|
||||||
|
|
||||||
|
Resolve every workspace path and verify it stays inside the repo root:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
|
||||||
|
def _validate_workspace_path(path: str, workspace_root: str) -> str:
|
||||||
|
resolved = os.path.realpath(os.path.abspath(path))
|
||||||
|
root = os.path.realpath(workspace_root)
|
||||||
|
if not resolved.startswith(root + os.sep) and resolved != root:
|
||||||
|
raise PermissionError(f"Path escape detected: {resolved} is outside {root}")
|
||||||
|
return resolved
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Dual-Channel Upload Fallback
|
||||||
|
|
||||||
|
Always try API first; fall back to DB/local on failure:
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def _upload_file(self, filename: str, content: bytes) -> str:
|
||||||
|
# Channel 1: API upload (S3-compatible)
|
||||||
|
try:
|
||||||
|
url = await self._api_upload(filename, content)
|
||||||
|
if url:
|
||||||
|
return url
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"API upload failed: {e}; falling back to local.")
|
||||||
|
|
||||||
|
# Channel 2: Local file + DB registration
|
||||||
|
return await self._local_db_upload(filename, content)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Progressive Status Reporting
|
||||||
|
|
||||||
|
For tasks > 3 seconds, emit staged updates:
|
||||||
|
|
||||||
|
```python
|
||||||
|
await self._emit_status(emitter, "正在分析内容...", done=False)
|
||||||
|
# ... phase 1 ...
|
||||||
|
await self._emit_status(emitter, "正在生成输出...", done=False)
|
||||||
|
# ... phase 2 ...
|
||||||
|
await self._emit_status(emitter, "完成", done=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
Always emit `done=True` on completion and `notification(error)` on failure.
|
||||||
|
|
||||||
|
### 5. Emitter Guard
|
||||||
|
|
||||||
|
Check before every emit to prevent crashes on missing emitter:
|
||||||
|
|
||||||
|
```python
|
||||||
|
if emitter and self.valves.SHOW_STATUS:
|
||||||
|
await emitter({"type": "status", "data": {"description": msg, "done": done}})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Exception Surface Rule
|
||||||
|
|
||||||
|
Never swallow exceptions silently. Every `except` block must:
|
||||||
|
|
||||||
|
- Log to backend: `logger.error(f"...: {e}", exc_info=True)`
|
||||||
|
- Notify user: `await self._emit_notification(emitter, f"处理失败: {str(e)}", "error")`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Edit Discipline
|
||||||
|
|
||||||
|
| ✅ DO | ❌ DO NOT |
|
||||||
|
|-------|-----------|
|
||||||
|
| One function / one Valve / one method per edit | Mix multiple unrelated changes in one operation |
|
||||||
|
| Validate input at the function boundary | Assume upstream data is well-formed |
|
||||||
|
| Return early on invalid state | Nest complex logic beyond 3 levels |
|
||||||
|
| Check fallback availability before primary path | Assume primary path always succeeds |
|
||||||
|
| Log before and after expensive I/O | Skip logging for "obvious" operations |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback Checklist
|
||||||
|
|
||||||
|
Before completing an antigravity operation, confirm:
|
||||||
|
|
||||||
|
- [ ] No global state mutated on `self` (filter singleton rule)
|
||||||
|
- [ ] File writes are atomic or can be recreated
|
||||||
|
- [ ] Database changes are idempotent (safe to re-run)
|
||||||
|
- [ ] Timeout guards are in place for all async calls to external systems
|
||||||
|
- [ ] The user can observe progress through status/notification events
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Full engineering spec: `.github/copilot-instructions.md` → Section: **Antigravity Development Mode**
|
||||||
|
- Design document: `docs/development/copilot-engineering-plan.md` → Section 5
|
||||||
33
.agent/rules/plugin_standards.md
Normal file
33
.agent/rules/plugin_standards.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
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:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **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,34 +4,39 @@ description: OpenWebUI Plugin Development & Release Workflow
|
|||||||
|
|
||||||
# OpenWebUI Plugin Development 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
|
## 1. Development Standards
|
||||||
|
|
||||||
Reference: `.github/copilot-instructions.md`
|
Reference: `.github/copilot-instructions.md`
|
||||||
|
|
||||||
### Bilingual Requirement
|
### Bilingual Requirement
|
||||||
Every plugin **MUST** have bilingual versions for both code and documentation:
|
|
||||||
|
|
||||||
- **Code**:
|
Every plugin **MUST** have a single internationalized code file and bilingual documentation:
|
||||||
- English: `plugins/{type}/{name}/{name}.py`
|
|
||||||
- Chinese: `plugins/{type}/{name}/{name_cn}.py` (or `中文名.py`)
|
- **Code (i18n)**:
|
||||||
|
- `plugins/{type}/{name}/{name}.py`
|
||||||
|
- The single `.py` file must implement internal i18n (e.g., using `navigator.language` or backend headers) to support multiple languages natively, rather than splitting into separate files.
|
||||||
- **README**:
|
- **README**:
|
||||||
- English: `plugins/{type}/{name}/README.md`
|
- English: `plugins/{type}/{name}/README.md`
|
||||||
- Chinese: `plugins/{type}/{name}/README_CN.md`
|
- Chinese: `plugins/{type}/{name}/README_CN.md`
|
||||||
|
|
||||||
### Code Structure
|
### Code Structure
|
||||||
|
|
||||||
- **Docstring**: Must include `title`, `author`, `version`, `description`, etc.
|
- **Docstring**: Must include `title`, `author`, `version`, `description`, etc.
|
||||||
- **Valves**: Use `pydantic` for configuration.
|
- **Valves**: Use `pydantic` for configuration.
|
||||||
- **Database**: Re-use `open_webui.internal.db` shared connection.
|
- **Database**: Re-use `open_webui.internal.db` shared connection.
|
||||||
- **User Context**: Use `_get_user_context` helper method.
|
- **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`.
|
- **Chat API**: For message updates, follow the "OpenWebUI Chat API 更新规范" in `.github/copilot-instructions.md`.
|
||||||
- Use Event API for immediate UI updates
|
- Use Event API for immediate UI updates
|
||||||
- Use Chat Persistence API for database storage
|
- Use Chat Persistence API for database storage
|
||||||
- Always update both `messages[]` and `history.messages`
|
- Always update both `messages[]` and `history.messages`
|
||||||
|
|
||||||
### Commit Messages
|
### Commit Messages & Release Notes
|
||||||
- **Language**: **English ONLY**. Do not use Chinese in commit messages.
|
|
||||||
|
- **Language**: **English ONLY**. Do not use Chinese in commit messages or release notes.
|
||||||
- **Format**: Conventional Commits (e.g., `feat:`, `fix:`, `docs:`).
|
- **Format**: Conventional Commits (e.g., `feat:`, `fix:`, `docs:`).
|
||||||
|
|
||||||
## 2. Documentation Updates
|
## 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:
|
When adding or updating a plugin, you **MUST** update the following documentation files to maintain consistency:
|
||||||
|
|
||||||
### Plugin Directory
|
### Plugin Directory
|
||||||
- `README.md`: Update version, description, and usage. **Explicitly describe new features.**
|
|
||||||
- `README_CN.md`: Update version, description, and usage. **Explicitly describe new features.**
|
- `README.md`: Update version, description, and usage.
|
||||||
|
- **Key Capabilities**: **MUST** include ALL core functionalities and features. This is a cumulative section. Every release **MUST** verify that basic core descriptions are NOT lost or overwritten by new feature lists.
|
||||||
|
- **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)**: 在开头显眼位置明确描述最新的更改/更新。此部分是动态的,随版本变化。
|
||||||
|
|
||||||
### Global Documentation (`docs/`)
|
### Global Documentation (`docs/`)
|
||||||
|
|
||||||
- **Index Pages**:
|
- **Index Pages**:
|
||||||
- `docs/plugins/{type}/index.md`: Add/Update list item with **correct version**.
|
- `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**.
|
- `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.
|
- `docs/plugins/{type}/{name}.zh.md`: Ensure content matches README_CN.
|
||||||
|
|
||||||
### Root README
|
### Root README
|
||||||
|
|
||||||
- `README.md`: Add to "Featured Plugins" if applicable.
|
- `README.md`: Add to "Featured Plugins" if applicable.
|
||||||
- `README_CN.md`: Add to "Featured Plugins" if applicable.
|
- `README_CN.md`: Add to "Featured Plugins" if applicable.
|
||||||
|
|
||||||
@@ -59,31 +71,59 @@ When adding or updating a plugin, you **MUST** update the following documentatio
|
|||||||
Reference: `.github/workflows/release.yml`
|
Reference: `.github/workflows/release.yml`
|
||||||
|
|
||||||
### Version Bumping
|
### Version Bumping
|
||||||
|
|
||||||
- **Rule**: Version bump is required **ONLY when the user explicitly requests a release**. Regular code changes do NOT require version bumps.
|
- **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`).
|
- **Format**: Semantic Versioning (e.g., `1.0.0` -> `1.0.1`).
|
||||||
- **When to Bump**: Only update the version when:
|
- **When to Bump**: Only update the version when:
|
||||||
- User says "发布" / "release" / "bump version"
|
- User says "发布" / "release" / "bump version"
|
||||||
- User explicitly asks to prepare for release
|
- 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:
|
- **Consistency**: When bumping, update version in **ALL** locations:
|
||||||
1. English Code (`.py`)
|
1. Code (`.py`)
|
||||||
2. Chinese Code (`.py`)
|
2. English README (`README.md`)
|
||||||
3. English README (`README.md`)
|
3. Chinese README (`README_CN.md`)
|
||||||
4. Chinese README (`README_CN.md`)
|
4. Docs Index (`docs/.../index.md`)
|
||||||
5. Docs Index (`docs/.../index.md`)
|
5. Docs Index CN (`docs/.../index.zh.md`)
|
||||||
6. Docs Index CN (`docs/.../index.zh.md`)
|
6. Docs Detail (`docs/.../{name}.md`)
|
||||||
7. Docs Detail (`docs/.../{name}.md`)
|
7. Docs Detail CN (`docs/.../{name}.zh.md`)
|
||||||
8. Docs Detail CN (`docs/.../{name}.zh.md`)
|
|
||||||
|
|
||||||
### Automated Release Process
|
### Automated Release Process
|
||||||
|
|
||||||
1. **Trigger**: Push to `main` branch with changes in `plugins/**/*.py`.
|
1. **Trigger**: Push to `main` branch with changes in `plugins/**/*.py`.
|
||||||
2. **Detection**: `scripts/extract_plugin_versions.py` detects changed plugins and compares versions.
|
2. **Detection**: `scripts/extract_plugin_versions.py` detects changed plugins and compares versions.
|
||||||
3. **Release**:
|
3. **Release**:
|
||||||
- Generates release notes based on changes.
|
- Generates release notes based on changes.
|
||||||
- Creates a GitHub Release tag (e.g., `v2024.01.01-1`).
|
- Creates a GitHub Release tag (e.g., `v2024.01.01-1`).
|
||||||
- Uploads individual `.py` files of **changed plugins only** as assets.
|
- Uploads individual `.py` files of **changed plugins only** as assets.
|
||||||
|
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/openwebui-extensions/blob/main/plugins/{type}/{name}/README.md`
|
||||||
|
- Example: `https://github.com/Fu-Jie/openwebui-extensions/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]`
|
||||||
|
- **Changelog**: English-only list of commits since the last release, including hashes (e.g., `896de02 docs(config): reorder antigravity model alias example`).
|
||||||
|
- **Verification Status**: Confirm all 7+ 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
|
### Pull Request Check
|
||||||
|
|
||||||
- Workflow: `.github/workflows/plugin-version-check.yml`
|
- Workflow: `.github/workflows/plugin-version-check.yml`
|
||||||
- Checks if plugin files are modified.
|
- Checks if plugin files are modified.
|
||||||
- **Fails** if version number is not updated.
|
- **Fails** if version number is not updated.
|
||||||
@@ -93,15 +133,17 @@ Reference: `.github/workflows/release.yml`
|
|||||||
|
|
||||||
Before committing:
|
Before committing:
|
||||||
|
|
||||||
- [ ] Code is bilingual and functional?
|
- [ ] Code is internal i18n supported (`.py`) and fully functional?
|
||||||
- [ ] Docstrings have updated version?
|
- [ ] Docstrings have updated version?
|
||||||
- [ ] READMEs are updated and bilingual?
|
- [ ] READMEs are updated and bilingual?
|
||||||
|
- [ ] **Key Capabilities** in READMEs still cover all legacy core features + new features?
|
||||||
- [ ] `docs/` index and detail pages are updated?
|
- [ ] `docs/` index and detail pages are updated?
|
||||||
- [ ] Root `README.md` is updated?
|
- [ ] Root `README.md` is updated?
|
||||||
- [ ] All version numbers match exactly?
|
- [ ] All version numbers match exactly?
|
||||||
|
|
||||||
## 5. Git Operations (Agent Rules)
|
## 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.
|
||||||
|
|||||||
65
.all-contributorsrc
Normal file
65
.all-contributorsrc
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"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": "openwebui-extensions",
|
||||||
|
"projectOwner": "Fu-Jie"
|
||||||
|
}
|
||||||
44
.gemini/skills/README.md
Normal file
44
.gemini/skills/README.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Agent Skills Index
|
||||||
|
|
||||||
|
This folder contains reusable Agent Skills for GitHub Copilot / VS Code custom agent workflows.
|
||||||
|
|
||||||
|
## Available Skills
|
||||||
|
|
||||||
|
- **community-announcer**
|
||||||
|
- Purpose: Generate community announcement content and related assets.
|
||||||
|
- Entry: `community-announcer/SKILL.md`
|
||||||
|
|
||||||
|
- **doc-mirror-sync**
|
||||||
|
- Purpose: Sync mirrored documentation content and helper scripts.
|
||||||
|
- Entry: `doc-mirror-sync/SKILL.md`
|
||||||
|
|
||||||
|
- **gh-issue-replier**
|
||||||
|
- Purpose: Draft standardized issue replies with templates.
|
||||||
|
- Entry: `gh-issue-replier/SKILL.md`
|
||||||
|
|
||||||
|
- **gh-issue-scheduler**
|
||||||
|
- Purpose: Schedule and discover unanswered issues for follow-up.
|
||||||
|
- Entry: `gh-issue-scheduler/SKILL.md`
|
||||||
|
|
||||||
|
- **i18n-validator**
|
||||||
|
- Purpose: Validate translation key consistency across i18n dictionaries.
|
||||||
|
- Entry: `i18n-validator/SKILL.md`
|
||||||
|
|
||||||
|
- **plugin-scaffolder**
|
||||||
|
- Purpose: Scaffold OpenWebUI plugin boilerplate with repository standards.
|
||||||
|
- Entry: `plugin-scaffolder/SKILL.md`
|
||||||
|
|
||||||
|
- **version-bumper**
|
||||||
|
- Purpose: Assist with semantic version bumping workflows.
|
||||||
|
- Entry: `version-bumper/SKILL.md`
|
||||||
|
|
||||||
|
- **xlsx-single-file**
|
||||||
|
- Purpose: Single-file spreadsheet operations workflow without LibreOffice.
|
||||||
|
- Entry: `xlsx-single-file/SKILL.md`
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Skill definitions follow the expected location pattern:
|
||||||
|
- `.github/skills/<skill-name>/SKILL.md`
|
||||||
|
- Each skill may include optional `assets/`, `references/`, and `scripts/` folders.
|
||||||
|
- This directory mirrors `.gemini/skills` for compatibility.
|
||||||
23
.gemini/skills/community-announcer/SKILL.md
Normal file
23
.gemini/skills/community-announcer/SKILL.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
name: community-announcer
|
||||||
|
description: Drafts engaging English and Chinese update announcements for the OpenWebUI Community and other social platforms. Use when a new version is released.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Community Announcer
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Automates the drafting of high-impact update announcements.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
1. **Source Intel**: Read the latest version's `What's New` section from `README.md`.
|
||||||
|
2. **Drafting**: Create two versions:
|
||||||
|
- **Community Post**: Professional, structured, technical.
|
||||||
|
- **Catchy Short**: For Discord/Twitter, use emojis and bullet points.
|
||||||
|
3. **Multi-language**: Generate BOTH English and Chinese versions automatically.
|
||||||
|
|
||||||
|
## Announcement Structure (Recommended)
|
||||||
|
- **Headline**: "Update vX.X.X - [Main Feature]"
|
||||||
|
- **Introduction**: Brief context.
|
||||||
|
- **Key Highlights**: Bulleted list of fixes/features.
|
||||||
|
- **Action**: "Download from [Market Link]"
|
||||||
|
- **Closing**: Thanks and Star request.
|
||||||
14
.gemini/skills/doc-mirror-sync/SKILL.md
Normal file
14
.gemini/skills/doc-mirror-sync/SKILL.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: doc-mirror-sync
|
||||||
|
description: Automatically synchronizes plugin READMEs to the official documentation directory (docs/). Use after editing a plugin's local documentation to keep the MkDocs site up to date.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Doc Mirror Sync
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Automates the mirroring of `plugins/{type}/{name}/README.md` to `docs/plugins/{type}/{name}.md`.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
1. Identify changed READMEs.
|
||||||
|
2. Copy content to corresponding mirror paths.
|
||||||
|
3. Update version badges in `docs/plugins/{type}/index.md`.
|
||||||
38
.gemini/skills/doc-mirror-sync/scripts/sync.py
Normal file
38
.gemini/skills/doc-mirror-sync/scripts/sync.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import re
|
||||||
|
|
||||||
|
def sync_mirrors():
|
||||||
|
plugins_root = "plugins"
|
||||||
|
docs_root = "docs/plugins"
|
||||||
|
|
||||||
|
types = ["actions", "filters", "pipes", "pipelines", "tools"]
|
||||||
|
|
||||||
|
for t in types:
|
||||||
|
src_type_dir = os.path.join(plugins_root, t)
|
||||||
|
dest_type_dir = os.path.join(docs_root, t)
|
||||||
|
|
||||||
|
if not os.path.exists(src_type_dir): continue
|
||||||
|
os.makedirs(dest_type_dir, exist_ok=True)
|
||||||
|
|
||||||
|
for name in os.listdir(src_type_dir):
|
||||||
|
plugin_dir = os.path.join(src_type_dir, name)
|
||||||
|
if not os.path.isdir(plugin_dir): continue
|
||||||
|
|
||||||
|
# Sync README.md -> docs/plugins/{type}/{name}.md
|
||||||
|
src_readme = os.path.join(plugin_dir, "README.md")
|
||||||
|
if os.path.exists(src_readme):
|
||||||
|
dest_readme = os.path.join(dest_type_dir, f"{name}.md")
|
||||||
|
shutil.copy(src_readme, dest_readme)
|
||||||
|
print(f"✅ Mirrored: {t}/{name} (EN)")
|
||||||
|
|
||||||
|
# Sync README_CN.md -> docs/plugins/{type}/{name}.zh.md
|
||||||
|
src_readme_cn = os.path.join(plugin_dir, "README_CN.md")
|
||||||
|
if os.path.exists(src_readme_cn):
|
||||||
|
dest_readme_zh = os.path.join(dest_type_dir, f"{name}.zh.md")
|
||||||
|
shutil.copy(src_readme_cn, dest_readme_zh)
|
||||||
|
print(f"✅ Mirrored: {t}/{name} (ZH)")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sync_mirrors()
|
||||||
51
.gemini/skills/gh-issue-replier/SKILL.md
Normal file
51
.gemini/skills/gh-issue-replier/SKILL.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
name: gh-issue-replier
|
||||||
|
description: Professional English replier for GitHub issues. Use when a task is completed, a bug is fixed, or more info is needed from the user. Automates replying using the 'gh' CLI tool.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Gh Issue Replier
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `gh-issue-replier` skill enables Gemini CLI to interact with GitHub issues professionally. It enforces English for all communications and leverages the `gh` CLI to post comments.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. **Identify the Issue**: Find the issue number (e.g., #49).
|
||||||
|
2. **Check Star Status**: Run the bundled script to check if the author has starred the repo.
|
||||||
|
* Command: `bash scripts/check_star.sh <issue-number>`
|
||||||
|
* Interpretation:
|
||||||
|
* Exit code **0**: User has starred. Use "Already Starred" templates.
|
||||||
|
* Exit code **1**: User has NOT starred. Include "Star Request" in the reply.
|
||||||
|
3. **Select a Template**: Load [templates.md](references/templates.md) to choose a suitable English response pattern.
|
||||||
|
4. **Draft the Reply**: Compose a concise message based on the star status.
|
||||||
|
5. **Post the Comment**: Use the `gh` tool to submit the reply.
|
||||||
|
|
||||||
|
## Tool Integration
|
||||||
|
|
||||||
|
### Check Star Status
|
||||||
|
```bash
|
||||||
|
bash scripts/check_star.sh <issue-number>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Post Comment
|
||||||
|
```bash
|
||||||
|
gh issue comment <issue-number> --body "<message-body>"
|
||||||
|
```
|
||||||
|
|
||||||
|
Example (if user has NOT starred):
|
||||||
|
```bash
|
||||||
|
gh issue comment 49 --body "This has been fixed in v1.2.7. If you find this helpful, a star on the repo would be much appreciated! ⭐"
|
||||||
|
```
|
||||||
|
|
||||||
|
Example (if user HAS starred):
|
||||||
|
```bash
|
||||||
|
gh issue comment 49 --body "This has been fixed in v1.2.7. Thanks for your support!"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
|
||||||
|
- **Language**: ALWAYS use English for the comment body, even if the system prompt or user conversation is in another language.
|
||||||
|
- **Tone**: Professional, helpful, and appreciative.
|
||||||
|
- **Precision**: When announcing a fix, mention the specific version or the logic change (e.g., "Updated regex pattern").
|
||||||
|
- **Closing**: If the issue is resolved and you have permission, you can also use `gh issue close <number>`.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Reference Documentation for Gh Issue Replier
|
||||||
|
|
||||||
|
This is a placeholder for detailed reference documentation.
|
||||||
|
Replace with actual reference content or delete if not needed.
|
||||||
|
|
||||||
|
## Structure Suggestions
|
||||||
|
|
||||||
|
### API Reference Example
|
||||||
|
- Overview
|
||||||
|
- Authentication
|
||||||
|
- Endpoints with examples
|
||||||
|
- Error codes
|
||||||
|
|
||||||
|
### Workflow Guide Example
|
||||||
|
- Prerequisites
|
||||||
|
- Step-by-step instructions
|
||||||
|
- Best practices
|
||||||
45
.gemini/skills/gh-issue-replier/references/templates.md
Normal file
45
.gemini/skills/gh-issue-replier/references/templates.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Issue Reply Templates
|
||||||
|
|
||||||
|
Use these templates to craft professional English replies. Adjust placeholders like `@username`, `v1.2.x`, and `[commit hash]` as needed.
|
||||||
|
|
||||||
|
## 1. Acknowledging a New Issue
|
||||||
|
Use when you first see an issue and want to let the user know you are working on it.
|
||||||
|
|
||||||
|
- "Thank you for reporting this! I'm looking into it right now."
|
||||||
|
- "Thanks for bringing this to my attention. I'll try to reproduce this behavior and get back to you shortly."
|
||||||
|
|
||||||
|
## 2. Requesting More Information
|
||||||
|
Use when you need logs or specific details to fix the bug.
|
||||||
|
|
||||||
|
- "Could you please provide the **'Original'** vs **'Normalized'** content from your browser console logs (F12)? It would help a lot in debugging."
|
||||||
|
- "It would be very helpful if you could share the specific Markdown text that triggered this issue."
|
||||||
|
|
||||||
|
## 3. Announcing a Fix
|
||||||
|
Use when you have pushed the fix to the repository.
|
||||||
|
|
||||||
|
- "This has been fixed in version **v1.2.x**. You can update the plugin to resolve it."
|
||||||
|
- "I've just pushed a fix for this in [commit hash]. Please let me know if it works for you after updating."
|
||||||
|
- "The issue was caused by a greedy regex pattern. I've updated it to use a tempered greedy token to prevent incorrect merging."
|
||||||
|
|
||||||
|
## 4. Guiding to Official Market
|
||||||
|
Always provide the official market link to ensure the user gets the latest verified version.
|
||||||
|
|
||||||
|
- "The fix is now live! You can download the latest version from the official OpenWebUI Community page here: [Plugin Market Link]. Simply update the function in your OpenWebUI instance to apply the changes."
|
||||||
|
- "I recommend getting the updated version from the official store: [Link]. It includes the fix for the spacing issue we discussed."
|
||||||
|
|
||||||
|
## 5. Closing the Issue
|
||||||
|
Use when the issue is confirmed resolved.
|
||||||
|
|
||||||
|
- "Glad to hear it's working now! Closing this for now. Feel free to reopen it if the problem persists."
|
||||||
|
- "Since this is resolved, I'm closing this issue. Thanks again for your feedback!"
|
||||||
|
|
||||||
|
## 5. Pro-tip: Star Request
|
||||||
|
Gently handle star requests based on the user's current status.
|
||||||
|
|
||||||
|
### If User has NOT starred:
|
||||||
|
- "If you find this plugin helpful, a star on the repo would be much appreciated! ⭐"
|
||||||
|
- "We'd love your support! If this fixed your issue, please consider starring the repository. ⭐"
|
||||||
|
|
||||||
|
### If User HAS already starred:
|
||||||
|
- "Thanks again for starring the project and for your continuous support!"
|
||||||
|
- "I appreciate your support and for being a stargazer of this project!"
|
||||||
31
.gemini/skills/gh-issue-replier/scripts/check_star.sh
Executable file
31
.gemini/skills/gh-issue-replier/scripts/check_star.sh
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Robust Star Checker v2
|
||||||
|
# Usage: ./check_star.sh <issue_number>
|
||||||
|
|
||||||
|
ISSUE_NUM=$1
|
||||||
|
if [ -z "$ISSUE_NUM" ]; then exit 2; fi
|
||||||
|
|
||||||
|
# 1. Get Repo and Author info
|
||||||
|
REPO_FULL=$(gh repo view --json owner,name -q ".owner.login + \"/\" + .name")
|
||||||
|
USER_LOGIN=$(gh issue view "$ISSUE_NUM" --json author -q ".author.login")
|
||||||
|
|
||||||
|
# 2. Use GraphQL for high precision (Detects stars even when REST 404s)
|
||||||
|
IS_STARRED=$(gh api graphql -f query='
|
||||||
|
query($owner:String!, $repo:String!, $user:String!) {
|
||||||
|
repository(owner:$owner, name:$repo) {
|
||||||
|
stargazers(query:$user, first:1) {
|
||||||
|
nodes {
|
||||||
|
login
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}' -f owner="${REPO_FULL%/*}" -f repo="${REPO_FULL#*/}" -f user="$USER_LOGIN" -q ".data.repository.stargazers.nodes[0].login")
|
||||||
|
|
||||||
|
if [ "$IS_STARRED" == "$USER_LOGIN" ]; then
|
||||||
|
echo "Confirmed: @$USER_LOGIN HAS starred $REPO_FULL. ⭐"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "Confirmed: @$USER_LOGIN has NOT starred $REPO_FULL."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
42
.gemini/skills/gh-issue-scheduler/SKILL.md
Normal file
42
.gemini/skills/gh-issue-scheduler/SKILL.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
name: gh-issue-scheduler
|
||||||
|
description: Finds all open GitHub issues that haven't been replied to by the owner, summarizes them, and generates a solution plan. Use when the user wants to audit pending tasks or plan maintenance work.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Gh Issue Scheduler
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `gh-issue-scheduler` skill helps maintainers track community feedback by identifying unaddressed issues and drafting actionable technical plans to resolve them.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. **Identify Unanswered Issues**: Run the bundled script to fetch issues without owner replies.
|
||||||
|
* Command: `bash scripts/find_unanswered.sh`
|
||||||
|
2. **Analyze and Summarize**: For each identified issue, summarize the core problem and the user's intent.
|
||||||
|
3. **Generate Solution Plans**: Draft a technical "Action Plan" for each issue, including:
|
||||||
|
* **Root Cause Analysis** (if possible)
|
||||||
|
* **Proposed Fix/Implementation**
|
||||||
|
* **Verification Strategy**
|
||||||
|
4. **Present to User**: Display a structured report of all pending issues and their respective plans.
|
||||||
|
|
||||||
|
## Tool Integration
|
||||||
|
|
||||||
|
### Find Unanswered Issues
|
||||||
|
```bash
|
||||||
|
bash scripts/find_unanswered.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Report Format
|
||||||
|
|
||||||
|
When presenting the summary, use the following Markdown structure:
|
||||||
|
|
||||||
|
### 📋 Unanswered Issues Audit
|
||||||
|
|
||||||
|
#### Issue #[Number]: [Title]
|
||||||
|
- **Author**: @username
|
||||||
|
- **Summary**: Concise description of the problem.
|
||||||
|
- **Action Plan**:
|
||||||
|
1. Step 1 (e.g., Investigate file X)
|
||||||
|
2. Step 2 (e.g., Apply fix Y)
|
||||||
|
3. Verification (e.g., Run test Z)
|
||||||
42
.gemini/skills/gh-issue-scheduler/scripts/find_unanswered.sh
Executable file
42
.gemini/skills/gh-issue-scheduler/scripts/find_unanswered.sh
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Fetch all open issues and filter for those without responses from the owner/collaborators.
|
||||||
|
# Uses 'gh' CLI.
|
||||||
|
|
||||||
|
REPO_FULL=$(gh repo view --json owner,name -q ".owner.login + "/" + .name")
|
||||||
|
OWNER=${REPO_FULL%/*}
|
||||||
|
|
||||||
|
# 1. Get all open issues
|
||||||
|
OPEN_ISSUES=$(gh issue list --state open --json number,title,author,createdAt --limit 100)
|
||||||
|
|
||||||
|
echo "Analysis for repository: $REPO_FULL"
|
||||||
|
echo "------------------------------------"
|
||||||
|
|
||||||
|
# Process each issue
|
||||||
|
echo "$OPEN_ISSUES" | jq -c '.[]' | while read -r issue; do
|
||||||
|
NUMBER=$(echo "$issue" | jq -r '.number')
|
||||||
|
TITLE=$(echo "$issue" | jq -r '.title')
|
||||||
|
AUTHOR=$(echo "$issue" | jq -r '.author.login')
|
||||||
|
|
||||||
|
# Check comments for owner responses
|
||||||
|
# We look for comments where the author is the repo owner
|
||||||
|
COMMENTS=$(gh issue view "$NUMBER" --json comments -q ".comments[].author.login" 2>/dev/null)
|
||||||
|
|
||||||
|
HAS_OWNER_REPLY=false
|
||||||
|
for COMMENT_AUTHOR in $COMMENTS; do
|
||||||
|
if [ "$COMMENT_AUTHOR" == "$OWNER" ]; then
|
||||||
|
HAS_OWNER_REPLY=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$HAS_OWNER_REPLY" == "false" ]; then
|
||||||
|
echo "ISSUE_START"
|
||||||
|
echo "ID: $NUMBER"
|
||||||
|
echo "Title: $TITLE"
|
||||||
|
echo "Author: $AUTHOR"
|
||||||
|
echo "Description:"
|
||||||
|
gh issue view "$NUMBER" --json body -q ".body"
|
||||||
|
echo "ISSUE_END"
|
||||||
|
fi
|
||||||
|
done
|
||||||
14
.gemini/skills/i18n-validator/SKILL.md
Normal file
14
.gemini/skills/i18n-validator/SKILL.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: i18n-validator
|
||||||
|
description: Validates multi-language consistency in the TRANSLATIONS dictionary of a plugin. Use to check if any language keys are missing or if translations need updating.
|
||||||
|
---
|
||||||
|
|
||||||
|
# I18n Validator
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Ensures all 12 supported languages (en-US, zh-CN, etc.) have aligned translation keys.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Detects missing keys in non-English dictionaries.
|
||||||
|
- Suggests translations using the core AI engine.
|
||||||
|
- Validates the `fallback_map` for variant redirects.
|
||||||
54
.gemini/skills/i18n-validator/scripts/validate_i18n.py
Normal file
54
.gemini/skills/i18n-validator/scripts/validate_i18n.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import ast
|
||||||
|
import os
|
||||||
|
|
||||||
|
def check_i18n(file_path):
|
||||||
|
if not os.path.exists(file_path):
|
||||||
|
print(f"Error: File not found {file_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
|
tree = ast.parse(f.read())
|
||||||
|
|
||||||
|
translations = {}
|
||||||
|
for node in tree.body:
|
||||||
|
if isinstance(node, ast.Assign):
|
||||||
|
for target in node.targets:
|
||||||
|
if isinstance(target, ast.Name) and target.id == "TRANSLATIONS":
|
||||||
|
translations = ast.literal_eval(node.value)
|
||||||
|
break
|
||||||
|
|
||||||
|
if not translations:
|
||||||
|
print("⚠️ No TRANSLATIONS dictionary found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Base keys from English
|
||||||
|
base_lang = "en-US"
|
||||||
|
if base_lang not in translations:
|
||||||
|
print(f"❌ Error: {base_lang} missing in TRANSLATIONS.")
|
||||||
|
return
|
||||||
|
|
||||||
|
base_keys = set(translations[base_lang].keys())
|
||||||
|
print(f"🔍 Analyzing {file_path}...")
|
||||||
|
print(f"Standard keys ({len(base_keys)}): {', '.join(sorted(base_keys))}
|
||||||
|
")
|
||||||
|
|
||||||
|
for lang, keys in translations.items():
|
||||||
|
if lang == base_lang: continue
|
||||||
|
lang_keys = set(keys.keys())
|
||||||
|
missing = base_keys - lang_keys
|
||||||
|
extra = lang_keys - base_keys
|
||||||
|
|
||||||
|
if missing:
|
||||||
|
print(f"❌ {lang}: Missing {len(missing)} keys: {', '.join(missing)}")
|
||||||
|
if extra:
|
||||||
|
print(f"⚠️ {lang}: Has {len(extra)} extra keys: {', '.join(extra)}")
|
||||||
|
if not missing and not extra:
|
||||||
|
print(f"✅ {lang}: Aligned.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: validate_i18n.py <path_to_plugin.py>")
|
||||||
|
sys.exit(1)
|
||||||
|
check_i18n(sys.argv[1])
|
||||||
19
.gemini/skills/plugin-scaffolder/SKILL.md
Normal file
19
.gemini/skills/plugin-scaffolder/SKILL.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
name: plugin-scaffolder
|
||||||
|
description: Generates a standardized single-file i18n Python plugin template based on project standards. Use when starting a new plugin development to skip boilerplate writing.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Plugin Scaffolder
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Generates compliant OpenWebUI plugin templates with built-in i18n, common utility methods, and required docstring fields.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
1. Provide the **Plugin Name** and **Type** (action/filter/pipe).
|
||||||
|
2. The skill will generate the `.py` file and the bilingual `README` files.
|
||||||
|
|
||||||
|
## Template Standard
|
||||||
|
- `Valves(BaseModel)` with `UPPER_SNAKE_CASE`
|
||||||
|
- `_get_user_context` with JS fallback and timeout
|
||||||
|
- `_emit_status` and `_emit_debug_log` methods
|
||||||
|
- Standardized docstring metadata
|
||||||
34
.gemini/skills/plugin-scaffolder/assets/README_template.md
Normal file
34
.gemini/skills/plugin-scaffolder/assets/README_template.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# {{TITLE}}
|
||||||
|
|
||||||
|
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 0.1.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
|
||||||
|
|
||||||
|
{{DESCRIPTION}}
|
||||||
|
|
||||||
|
## 🔥 What's New in v0.1.0
|
||||||
|
|
||||||
|
* Initial release of {{TITLE}}.
|
||||||
|
|
||||||
|
## 🌐 Multilingual Support
|
||||||
|
|
||||||
|
Supports automatic interface and status switching for the following languages:
|
||||||
|
`English`, `简体中文`, `繁體中文 (香港)`, `繁體中文 (台灣)`, `한국어`, `日本語`, `Français`, `Deutsch`, `Español`, `Italiano`, `Tiếng Việt`, `Bahasa Indonesia`.
|
||||||
|
|
||||||
|
## ✨ Core Features
|
||||||
|
|
||||||
|
* Feature 1
|
||||||
|
* Feature 2
|
||||||
|
|
||||||
|
## How to Use 🛠️
|
||||||
|
|
||||||
|
1. Install the plugin in Open WebUI.
|
||||||
|
2. Configure settings in Valves.
|
||||||
|
|
||||||
|
## Configuration (Valves) ⚙️
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `priority` | `50` | Execution priority. |
|
||||||
|
|
||||||
|
## ⭐ Support
|
||||||
|
|
||||||
|
If this plugin has been useful, a star on [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) is a big motivation for me. Thank you for the support.
|
||||||
80
.gemini/skills/plugin-scaffolder/assets/template.py
Normal file
80
.gemini/skills/plugin-scaffolder/assets/template.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
"""
|
||||||
|
title: {{TITLE}}
|
||||||
|
author: Fu-Jie
|
||||||
|
author_url: https://github.com/Fu-Jie/openwebui-extensions
|
||||||
|
funding_url: https://github.com/open-webui
|
||||||
|
version: 0.1.0
|
||||||
|
description: {{DESCRIPTION}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from typing import Optional, Dict, Any, List, Callable, Awaitable
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from fastapi import Request
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
TRANSLATIONS = {
|
||||||
|
"en-US": {"status_starting": "Starting {{TITLE}}..."},
|
||||||
|
"zh-CN": {"status_starting": "正在启动 {{TITLE}}..."},
|
||||||
|
"zh-HK": {"status_starting": "正在啟動 {{TITLE}}..."},
|
||||||
|
"zh-TW": {"status_starting": "正在啟動 {{TITLE}}..."},
|
||||||
|
"ko-KR": {"status_starting": "{{TITLE}} 시작 중..."},
|
||||||
|
"ja-JP": {"status_starting": "{{TITLE}} を起動中..."},
|
||||||
|
"fr-FR": {"status_starting": "Démarrage de {{TITLE}}..."},
|
||||||
|
"de-DE": {"status_starting": "{{TITLE}} wird gestartet..."},
|
||||||
|
"es-ES": {"status_starting": "Iniciando {{TITLE}}..."},
|
||||||
|
"it-IT": {"status_starting": "Avvio di {{TITLE}}..."},
|
||||||
|
"vi-VN": {"status_starting": "Đang khởi động {{TITLE}}..."},
|
||||||
|
"id-ID": {"status_starting": "Memulai {{TITLE}}..."},
|
||||||
|
}
|
||||||
|
|
||||||
|
class {{CLASS_NAME}}:
|
||||||
|
class Valves(BaseModel):
|
||||||
|
priority: int = Field(default=50, description="Priority level (lower = earlier).")
|
||||||
|
show_status: bool = Field(default=True, description="Show status updates in UI.")
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.valves = self.Valves()
|
||||||
|
self.fallback_map = {
|
||||||
|
"zh": "zh-CN", "en": "en-US", "ko": "ko-KR", "ja": "ja-JP",
|
||||||
|
"fr": "fr-FR", "de": "de-DE", "es": "es-ES", "it": "it-IT",
|
||||||
|
"vi": "vi-VN", "id": "id-ID"
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_translation(self, lang: str, key: str, **kwargs) -> str:
|
||||||
|
target_lang = lang
|
||||||
|
if target_lang not in TRANSLATIONS:
|
||||||
|
base = target_lang.split("-")[0]
|
||||||
|
target_lang = self.fallback_map.get(base, "en-US")
|
||||||
|
|
||||||
|
lang_dict = TRANSLATIONS.get(target_lang, TRANSLATIONS["en-US"])
|
||||||
|
text = lang_dict.get(key, TRANSLATIONS["en-US"].get(key, key))
|
||||||
|
return text.format(**kwargs) if kwargs else text
|
||||||
|
|
||||||
|
async def _get_user_context(self, __user__: Optional[dict], __event_call__: Optional[Callable] = None, __request__: Optional[Request] = None) -> dict:
|
||||||
|
user_data = __user__ if isinstance(__user__, dict) else {}
|
||||||
|
user_language = user_data.get("language", "en-US")
|
||||||
|
if __event_call__:
|
||||||
|
try:
|
||||||
|
js = "try { return (document.documentElement.lang || localStorage.getItem('locale') || navigator.language || 'en-US'); } catch (e) { return 'en-US'; }"
|
||||||
|
frontend_lang = await asyncio.wait_for(__event_call__({"type": "execute", "data": {"code": js}}), timeout=2.0)
|
||||||
|
if frontend_lang: user_language = frontend_lang
|
||||||
|
except: pass
|
||||||
|
return {"user_language": user_language}
|
||||||
|
|
||||||
|
async def {{METHOD_NAME}}(self, body: dict, __user__: Optional[dict] = None, __event_emitter__=None, __event_call__=None, __request__: Optional[Request] = None) -> dict:
|
||||||
|
if self.valves.show_status and __event_emitter__:
|
||||||
|
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
|
||||||
|
msg = self._get_translation(user_ctx["user_language"], "status_starting")
|
||||||
|
await __event_emitter__({"type": "status", "data": {"description": msg, "done": False}})
|
||||||
|
|
||||||
|
# Implement core logic here
|
||||||
|
|
||||||
|
if self.valves.show_status and __event_emitter__:
|
||||||
|
await __event_emitter__({"type": "status", "data": {"description": "Done", "done": True}})
|
||||||
|
return body
|
||||||
80
.gemini/skills/plugin-scaffolder/assets/template.py.j2
Normal file
80
.gemini/skills/plugin-scaffolder/assets/template.py.j2
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
"""
|
||||||
|
title: {{TITLE}}
|
||||||
|
author: Fu-Jie
|
||||||
|
author_url: https://github.com/Fu-Jie/openwebui-extensions
|
||||||
|
funding_url: https://github.com/open-webui
|
||||||
|
version: 0.1.0
|
||||||
|
description: {{DESCRIPTION}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from typing import Optional, Dict, Any, List, Callable, Awaitable
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from fastapi import Request
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
TRANSLATIONS = {
|
||||||
|
"en-US": {"status_starting": "Starting {{TITLE}}..."},
|
||||||
|
"zh-CN": {"status_starting": "正在启动 {{TITLE}}..."},
|
||||||
|
"zh-HK": {"status_starting": "正在啟動 {{TITLE}}..."},
|
||||||
|
"zh-TW": {"status_starting": "正在啟動 {{TITLE}}..."},
|
||||||
|
"ko-KR": {"status_starting": "{{TITLE}} 시작 중..."},
|
||||||
|
"ja-JP": {"status_starting": "{{TITLE}} を起動中..."},
|
||||||
|
"fr-FR": {"status_starting": "Démarrage de {{TITLE}}..."},
|
||||||
|
"de-DE": {"status_starting": "{{TITLE}} wird gestartet..."},
|
||||||
|
"es-ES": {"status_starting": "Iniciando {{TITLE}}..."},
|
||||||
|
"it-IT": {"status_starting": "Avvio di {{TITLE}}..."},
|
||||||
|
"vi-VN": {"status_starting": "Đang khởi động {{TITLE}}..."},
|
||||||
|
"id-ID": {"status_starting": "Memulai {{TITLE}}..."},
|
||||||
|
}
|
||||||
|
|
||||||
|
class {{CLASS_NAME}}:
|
||||||
|
class Valves(BaseModel):
|
||||||
|
priority: int = Field(default=50, description="Priority level (lower = earlier).")
|
||||||
|
show_status: bool = Field(default=True, description="Show status updates in UI.")
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.valves = self.Valves()
|
||||||
|
self.fallback_map = {
|
||||||
|
"zh": "zh-CN", "en": "en-US", "ko": "ko-KR", "ja": "ja-JP",
|
||||||
|
"fr": "fr-FR", "de": "de-DE", "es": "es-ES", "it": "it-IT",
|
||||||
|
"vi": "vi-VN", "id": "id-ID"
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_translation(self, lang: str, key: str, **kwargs) -> str:
|
||||||
|
target_lang = lang
|
||||||
|
if target_lang not in TRANSLATIONS:
|
||||||
|
base = target_lang.split("-")[0]
|
||||||
|
target_lang = self.fallback_map.get(base, "en-US")
|
||||||
|
|
||||||
|
lang_dict = TRANSLATIONS.get(target_lang, TRANSLATIONS["en-US"])
|
||||||
|
text = lang_dict.get(key, TRANSLATIONS["en-US"].get(key, key))
|
||||||
|
return text.format(**kwargs) if kwargs else text
|
||||||
|
|
||||||
|
async def _get_user_context(self, __user__: Optional[dict], __event_call__: Optional[Callable] = None, __request__: Optional[Request] = None) -> dict:
|
||||||
|
user_data = __user__ if isinstance(__user__, dict) else {}
|
||||||
|
user_language = user_data.get("language", "en-US")
|
||||||
|
if __event_call__:
|
||||||
|
try:
|
||||||
|
js = "try { return (document.documentElement.lang || localStorage.getItem('locale') || navigator.language || 'en-US'); } catch (e) { return 'en-US'; }"
|
||||||
|
frontend_lang = await asyncio.wait_for(__event_call__({"type": "execute", "data": {"code": js}}), timeout=2.0)
|
||||||
|
if frontend_lang: user_language = frontend_lang
|
||||||
|
except: pass
|
||||||
|
return {"user_language": user_language}
|
||||||
|
|
||||||
|
async def {{METHOD_NAME}}(self, body: dict, __user__: Optional[dict] = None, __event_emitter__=None, __event_call__=None, __request__: Optional[Request] = None) -> dict:
|
||||||
|
if self.valves.show_status and __event_emitter__:
|
||||||
|
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
|
||||||
|
msg = self._get_translation(user_ctx["user_language"], "status_starting")
|
||||||
|
await __event_emitter__({"type": "status", "data": {"description": msg, "done": False}})
|
||||||
|
|
||||||
|
# Implement core logic here
|
||||||
|
|
||||||
|
if self.valves.show_status and __event_emitter__:
|
||||||
|
await __event_emitter__({"type": "status", "data": {"description": "Done", "done": True}})
|
||||||
|
return body
|
||||||
66
.gemini/skills/plugin-scaffolder/scripts/scaffold.py
Normal file
66
.gemini/skills/plugin-scaffolder/scripts/scaffold.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def scaffold(p_type, p_name, title, desc):
|
||||||
|
target_dir = f"plugins/{p_type}/{p_name}"
|
||||||
|
os.makedirs(target_dir, exist_ok=True)
|
||||||
|
|
||||||
|
class_name = (
|
||||||
|
"Action"
|
||||||
|
if p_type == "actions"
|
||||||
|
else (
|
||||||
|
"Filter"
|
||||||
|
if p_type == "filters"
|
||||||
|
else "Tools" if p_type == "tools" else "Pipe"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
method_name = (
|
||||||
|
"action"
|
||||||
|
if p_type == "actions"
|
||||||
|
else (
|
||||||
|
"outlet"
|
||||||
|
if p_type == "filters"
|
||||||
|
else "execute" if p_type == "tools" else "pipe"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
replacements = {
|
||||||
|
"{{TITLE}}": title,
|
||||||
|
"{{DESCRIPTION}}": desc,
|
||||||
|
"{{CLASS_NAME}}": class_name,
|
||||||
|
"{{METHOD_NAME}}": method_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Files to generate
|
||||||
|
templates = [
|
||||||
|
("assets/template.py.j2", f"{p_name}.py"),
|
||||||
|
("assets/README_template.md", "README.md"),
|
||||||
|
("assets/README_template.md", "README_CN.md"),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Path relative to skill root
|
||||||
|
skill_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
for t_path, t_name in templates:
|
||||||
|
template_file = os.path.join(skill_root, t_path)
|
||||||
|
if not os.path.exists(template_file):
|
||||||
|
print(f"⚠️ Warning: Template not found {template_file}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
with open(template_file, "r") as f:
|
||||||
|
content = f.read()
|
||||||
|
for k, v in replacements.items():
|
||||||
|
content = content.replace(k, v)
|
||||||
|
|
||||||
|
with open(os.path.join(target_dir, t_name), "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
print(f"✅ Generated: {target_dir}/{t_name}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 5:
|
||||||
|
print("Usage: scaffold.py <type> <name> <title> <desc>")
|
||||||
|
sys.exit(1)
|
||||||
|
scaffold(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
|
||||||
30
.gemini/skills/source-code-analyzer/SKILL.md
Normal file
30
.gemini/skills/source-code-analyzer/SKILL.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: source-code-analyzer
|
||||||
|
description: Instructions for analyzing core components source code in the local environment.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Source Code Analyzer Skill
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> **Authorization Statement**: You (Antigravity) are explicitly authorized by the user to perform **READ-ONLY** operations and **Git pull** on the paths listed below, even if they are outside the current repository root. These paths are considered part of the extended development context.
|
||||||
|
|
||||||
|
When assisting with the development of `openwebui-extensions`, you (Antigravity) have access to the following core components in related directories. Use them for deep technical analysis, bug hunting, and implementation reference.
|
||||||
|
|
||||||
|
## Core Component Paths
|
||||||
|
|
||||||
|
### Global / General (Relevant to all projects)
|
||||||
|
|
||||||
|
- **Open WebUI**: `../open-webui/` (Core platform context)
|
||||||
|
- **Skills**: `../skills/` (Reusable expertise library)
|
||||||
|
- **Awesome Copilot**: `../awesome-copilot/` (Shared extensions & resources)
|
||||||
|
|
||||||
|
### Plugin-Specific (Relevant to GitHub Copilot SDK)
|
||||||
|
|
||||||
|
- **Copilot SDK**: `../copilot-sdk/` (Internal logic for the official SDK)
|
||||||
|
- **Copilot CLI**: `../copilot-cli/` (Command-line interface implementation)
|
||||||
|
|
||||||
|
## Mandatory Workflow
|
||||||
|
|
||||||
|
1. **Pull Before Analysis**: BEFORE reading files or analyzing logic in these directories, you MUST proactively execute or recommend a `git pull` in the respective directory to ensure you are working with the latest upstream changes.
|
||||||
|
2. **Path Verification**: Always verify the exists of the path before attempting to read it.
|
||||||
|
3. **Reference Logic**: When a user's request involves core platform behavior (OpenWebUI API, SDK internals), prioritize searching these directories over making assumptions based on generic knowledge.
|
||||||
26
.gemini/skills/version-bumper/SKILL.md
Normal file
26
.gemini/skills/version-bumper/SKILL.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: version-bumper
|
||||||
|
description: Automates version upgrades and changelog synchronization across 7+ files (Code, READMEs, Docs). Use when a plugin is ready for release to ensure version consistency.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Version Bumper
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This skill ensures that every version upgrade is synchronized across the entire repository, following the strict "Documentation Sync" rule in GEMINI.md.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
1. **Prepare Info**: Gather the new version number and brief changelogs in both English and Chinese.
|
||||||
|
2. **Auto-Patch**: The skill will help you identify and update:
|
||||||
|
- `plugins/.../name.py` (docstring version)
|
||||||
|
- `plugins/.../README.md` (metadata & What's New)
|
||||||
|
- `plugins/.../README_CN.md` (metadata & 最新更新)
|
||||||
|
- `docs/plugins/...md` (mirrors)
|
||||||
|
- `docs/plugins/index.md` (version badge)
|
||||||
|
- `README.md` (updated date badge)
|
||||||
|
3. **Verify**: Check the diffs to ensure no formatting was broken.
|
||||||
|
|
||||||
|
## Tool Integration
|
||||||
|
Execute the bump script (draft):
|
||||||
|
```bash
|
||||||
|
python3 scripts/bump.py <version> "<message_en>" "<message_zh>"
|
||||||
|
```
|
||||||
70
.gemini/skills/version-bumper/scripts/bump.py
Normal file
70
.gemini/skills/version-bumper/scripts/bump.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
def patch_file(file_path, old_pattern, new_content, is_regex=False):
|
||||||
|
if not os.path.exists(file_path):
|
||||||
|
print(f"Warning: File not found: {file_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
if is_regex:
|
||||||
|
new_content_result = re.sub(old_pattern, new_content, content, flags=re.MULTILINE)
|
||||||
|
else:
|
||||||
|
new_content_result = content.replace(old_pattern, new_content)
|
||||||
|
|
||||||
|
if new_content_result != content:
|
||||||
|
with open(file_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(new_content_result)
|
||||||
|
print(f"✅ Patched: {file_path}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"ℹ️ No change needed: {file_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def bump_version(plugin_type, plugin_name, new_version, msg_en, msg_zh):
|
||||||
|
print(f"🚀 Bumping {plugin_name} ({plugin_type}) to {new_version}...")
|
||||||
|
|
||||||
|
today = datetime.now().strftime("%Y-%m-%d")
|
||||||
|
today_badge = today.replace("-", "--")
|
||||||
|
|
||||||
|
# 1. Patch Plugin Python File
|
||||||
|
py_file = f"plugins/{plugin_type}/{plugin_name}/{plugin_name}.py"
|
||||||
|
patch_file(py_file, r"version: \d+\.\d+\.\d+", f"version: {new_version}", is_regex=True)
|
||||||
|
|
||||||
|
# 2. Patch Plugin READMEs
|
||||||
|
readme_en = f"plugins/{plugin_type}/{plugin_name}/README.md"
|
||||||
|
readme_zh = f"plugins/{plugin_type}/{plugin_name}/README_CN.md"
|
||||||
|
|
||||||
|
# Update version in metadata
|
||||||
|
patch_file(readme_en, r"\*\*Version:\*\* \d+\.\d+\.\d+", f"**Version:** {new_version}", is_regex=True)
|
||||||
|
patch_file(readme_zh, r"\*\*版本:\*\* \d+\.\d+\.\d+", f"**版本:** {new_version}", is_regex=True)
|
||||||
|
|
||||||
|
# Update What's New (Assuming standard headers)
|
||||||
|
patch_file(readme_en, r"## 🔥 What's New in v.*?\n", f"## 🔥 What's New in v{new_version}\n\n* {msg_en}\n", is_regex=True)
|
||||||
|
patch_file(readme_zh, r"## 🔥 最新更新 v.*?\n", f"## 🔥 最新更新 v{new_version}\n\n* {msg_zh}\n", is_regex=True)
|
||||||
|
|
||||||
|
# 3. Patch Docs Mirrors
|
||||||
|
doc_en = f"docs/plugins/{plugin_type}/{plugin_name}.md"
|
||||||
|
doc_zh = f"docs/plugins/{plugin_type}/{plugin_name}.zh.md"
|
||||||
|
patch_file(doc_en, r"\*\*Version:\*\* \d+\.\d+\.\d+", f"**Version:** {new_version}", is_regex=True)
|
||||||
|
patch_file(doc_zh, r"\*\*版本:\*\* \d+\.\d+\.\d+", f"**版本:** {new_version}", is_regex=True)
|
||||||
|
|
||||||
|
# 4. Patch Root READMEs (Updated Date Badge)
|
||||||
|
patch_file("README.md", r"badge/202\d--\d\d--\d\d-gray", f"badge/{today_badge}-gray", is_regex=True)
|
||||||
|
patch_file("README_CN.md", r"badge/202\d--\d\d--\d\d-gray", f"badge/{today_badge}-gray", is_regex=True)
|
||||||
|
|
||||||
|
print("\n✨ All synchronization tasks completed.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 6:
|
||||||
|
print("Usage: bump.py <type> <name> <version> <msg_en> <msg_zh>")
|
||||||
|
print("Example: bump.py filters markdown_normalizer 1.2.8 'Fix bug' '修复错误'")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
bump_version(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5])
|
||||||
62
.github/agents/plugin-implementer.agent.md
vendored
Normal file
62
.github/agents/plugin-implementer.agent.md
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
name: Plugin Implementer
|
||||||
|
description: Implement OpenWebUI plugin and docs updates with strict project standards
|
||||||
|
argument-hint: Provide approved plan or feature request to implement
|
||||||
|
tools: ['execute/getTerminalOutput', 'execute/runInTerminal', 'read/readFile', 'read/terminalSelection', 'read/terminalLastCommand', 'edit/createFile', 'edit/editFiles', 'search', 'web', 'web/fetch', 'web/githubRepo', 'agent']
|
||||||
|
infer: true
|
||||||
|
handoffs:
|
||||||
|
- label: Run Review
|
||||||
|
agent: Plugin Reviewer
|
||||||
|
prompt: Review the implementation for i18n, safety, and consistency issues.
|
||||||
|
send: false
|
||||||
|
---
|
||||||
|
You are the **implementation specialist** for the `openwebui-extensions` repository.
|
||||||
|
|
||||||
|
## Execution Rules
|
||||||
|
1. **Minimal diffs**: Change only what the approved plan specifies.
|
||||||
|
2. **Single-file i18n**: Every plugin is one `.py` file with built-in `TRANSLATIONS` dict. Never create `_cn.py` split files.
|
||||||
|
3. **Context helpers**: Always use `_get_user_context(__user__)` and `_get_chat_context(body, __metadata__)` — never access dict keys directly.
|
||||||
|
4. **Emitter guards**: Every `await emitter(...)` must be guarded by `if emitter:`.
|
||||||
|
5. **Logging**: Use `logging.getLogger(__name__)` — no bare `print()` in production code.
|
||||||
|
6. **Async safety**: Wrap all `__event_call__` with `asyncio.wait_for(..., timeout=2.0)` + inner JS `try { ... } catch(e) { return fallback; }`.
|
||||||
|
|
||||||
|
## Required Plugin Pattern
|
||||||
|
```python
|
||||||
|
# Docstring: title, author, author_url, funding_url, version, description
|
||||||
|
# icon_url is REQUIRED for Action plugins (Lucide SVG, base64)
|
||||||
|
|
||||||
|
class Action: # or Filter / Pipe
|
||||||
|
class Valves(BaseModel):
|
||||||
|
SHOW_STATUS: bool = Field(default=True, description="...")
|
||||||
|
# All fields UPPER_SNAKE_CASE
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.valves = self.Valves()
|
||||||
|
|
||||||
|
def _get_user_context(self, __user__): ... # always implement
|
||||||
|
def _get_chat_context(self, body, __metadata__=None): ... # always implement
|
||||||
|
async def _emit_status(self, emitter, description, done=False): ...
|
||||||
|
async def _emit_notification(self, emitter, content, ntype="info"): ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Known Split-File Plugins (Legacy — Do NOT Add More)
|
||||||
|
These still have `_cn.py` files. When touching any of them, migrate CN content into `TRANSLATIONS` dict:
|
||||||
|
- `plugins/actions/deep-dive/deep_dive_cn.py`
|
||||||
|
- `plugins/actions/export_to_docx/export_to_word_cn.py`
|
||||||
|
- `plugins/actions/export_to_excel/export_to_excel_cn.py`
|
||||||
|
- `plugins/actions/flash-card/flash_card_cn.py`
|
||||||
|
- `plugins/actions/infographic/infographic_cn.py`
|
||||||
|
- `plugins/filters/folder-memory/folder_memory_cn.py`
|
||||||
|
|
||||||
|
## Version Bump Rule
|
||||||
|
Only bump version when user explicitly says "发布" / "release" / "bump version".
|
||||||
|
When bumping, update ALL 7+ files (code docstring + 2× README + 2× doc detail + 2× doc index + 2× root README date badge).
|
||||||
|
|
||||||
|
## Git Policy
|
||||||
|
- Never run `git commit`, `git push`, or create PRs automatically.
|
||||||
|
- After all edits, list what changed and why, then stop.
|
||||||
|
|
||||||
|
## Completion Output
|
||||||
|
- Modified files (full relative paths, one-line descriptions)
|
||||||
|
- Remaining manual checks
|
||||||
|
- Suggested handoff to **Plugin Reviewer**
|
||||||
78
.github/agents/plugin-planner.agent.md
vendored
Normal file
78
.github/agents/plugin-planner.agent.md
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
name: Plugin Planner
|
||||||
|
description: Analyze requirements and produce a safe implementation plan for OpenWebUI plugins
|
||||||
|
argument-hint: Describe the plugin goal, constraints, and target files
|
||||||
|
tools: ['read/readFile', 'search', 'web', 'web/githubRepo', 'read/terminalLastCommand', 'read/terminalSelection', 'agent']
|
||||||
|
infer: true
|
||||||
|
handoffs:
|
||||||
|
- label: Start Implementation
|
||||||
|
agent: Plugin Implementer
|
||||||
|
prompt: Implement the approved plan step by step with minimal diffs.
|
||||||
|
send: false
|
||||||
|
---
|
||||||
|
You are the **planning specialist** for the `openwebui-extensions` repository.
|
||||||
|
|
||||||
|
## Your Responsibilities
|
||||||
|
1. Read existing plugin code and docs **before** proposing any change.
|
||||||
|
2. Produce a **small, reversible** plan (one logical change per file per step).
|
||||||
|
3. Clearly list all impacted files including the docs sync chain.
|
||||||
|
4. Flag risks: breaking changes, release implications, version bumps needed.
|
||||||
|
|
||||||
|
## Hard Rules
|
||||||
|
- Never propose `git commit`, `git push`, or PR creation.
|
||||||
|
- Every plan must end with an acceptance checklist for the user to approve before handing off.
|
||||||
|
- Reference `.github/copilot-instructions.md` as the authoritative spec.
|
||||||
|
|
||||||
|
## Repository Plugin Inventory
|
||||||
|
|
||||||
|
### Actions (`plugins/actions/`)
|
||||||
|
| Dir | Main file | Version | i18n status |
|
||||||
|
|-----|-----------|---------|-------------|
|
||||||
|
| `deep-dive` | `deep_dive.py` | 1.0.0 | ⚠️ has `deep_dive_cn.py` split |
|
||||||
|
| `export_to_docx` | `export_to_word.py` | 0.4.4 | ⚠️ has `export_to_word_cn.py` split |
|
||||||
|
| `export_to_excel` | `export_to_excel.py` | 0.3.7 | ⚠️ has `export_to_excel_cn.py` split |
|
||||||
|
| `flash-card` | `flash_card.py` | 0.2.4 | ⚠️ has `flash_card_cn.py` split |
|
||||||
|
| `infographic` | `infographic.py` | 1.5.0 | ⚠️ has `infographic_cn.py` split |
|
||||||
|
| `smart-mind-map` | `smart_mind_map.py` | 1.0.0 | ✅ single file |
|
||||||
|
| `smart-mermaid` | _(empty stub)_ | — | — |
|
||||||
|
|
||||||
|
### Filters (`plugins/filters/`)
|
||||||
|
| Dir | Main file | Version | i18n status |
|
||||||
|
|-----|-----------|---------|-------------|
|
||||||
|
| `async-context-compression` | `async_context_compression.py` | 1.3.0 | ✅ |
|
||||||
|
| `context_enhancement_filter` | `context_enhancement_filter.py` | 0.3 | ⚠️ non-SemVer version |
|
||||||
|
| `copilot_files_preprocessor` | _(empty stub)_ | — | — |
|
||||||
|
| `folder-memory` | `folder_memory.py` | 0.1.0 | ⚠️ has `folder_memory_cn.py` split |
|
||||||
|
| `github_copilot_sdk_files_filter` | `github_copilot_sdk_files_filter.py` | 0.1.2 | ✅ |
|
||||||
|
| `markdown_normalizer` | `markdown_normalizer.py` | 1.2.4 | ✅ |
|
||||||
|
| `web_gemini_multimodel_filter` | `web_gemini_multimodel.py` | 0.3.2 | ✅ |
|
||||||
|
|
||||||
|
### Pipes / Pipelines / Tools
|
||||||
|
| Path | Main file | Version |
|
||||||
|
|------|-----------|---------|
|
||||||
|
| `pipes/github-copilot-sdk` | `github_copilot_sdk.py` | 0.7.0 |
|
||||||
|
| `pipelines/moe_prompt_refiner` | `moe_prompt_refiner.py` | — |
|
||||||
|
| `tools/workspace-file-manager` | `workspace_file_manager.py` | 0.2.0 |
|
||||||
|
|
||||||
|
## Naming Conventions (Actual Mix)
|
||||||
|
- Action dirs: some use **dashes** (`deep-dive`, `flash-card`, `smart-mind-map`), some **underscores** (`export_to_docx`, `export_to_excel`, `infographic`)
|
||||||
|
- Filter dirs: similarly mixed — prefer underscores for new plugins
|
||||||
|
- Main `.py` filenames always use **underscores**
|
||||||
|
|
||||||
|
## Docs Sync Chain (for every plugin change)
|
||||||
|
For `plugins/{type}/{name}/`, these 7+ files must stay in sync:
|
||||||
|
1. `plugins/{type}/{name}/{name}.py` — version in docstring
|
||||||
|
2. `plugins/{type}/{name}/README.md` — version + What's New
|
||||||
|
3. `plugins/{type}/{name}/README_CN.md` — version + 最新更新
|
||||||
|
4. `docs/plugins/{type}/{name}.md`
|
||||||
|
5. `docs/plugins/{type}/{name}.zh.md`
|
||||||
|
6. `docs/plugins/{type}/index.md` — version badge
|
||||||
|
7. `docs/plugins/{type}/index.zh.md` — version badge
|
||||||
|
8. Root `README.md` / `README_CN.md` — date badge
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
- **Scope summary**
|
||||||
|
- **Affected files** (full relative paths)
|
||||||
|
- **Step-by-step plan** (numbered, ≤10 steps)
|
||||||
|
- **Risk flags** (version bump? breaking change? split-file migration needed?)
|
||||||
|
- **Acceptance checklist** → user must approve before handoff to Implementer
|
||||||
71
.github/agents/plugin-reviewer.agent.md
vendored
Normal file
71
.github/agents/plugin-reviewer.agent.md
vendored
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
name: Plugin Reviewer
|
||||||
|
description: Perform strict repository-aligned code review for OpenWebUI plugin changes
|
||||||
|
argument-hint: Share changed files or branch diff to review
|
||||||
|
tools: ['search', 'read/readFile', 'web', 'web/fetch', 'web/githubRepo', 'execute/getTerminalOutput', 'read/terminalLastCommand', 'read/terminalSelection']
|
||||||
|
infer: true
|
||||||
|
handoffs:
|
||||||
|
- label: Prepare Release Draft
|
||||||
|
agent: Release Prep
|
||||||
|
prompt: Create a bilingual release draft and commit message based on reviewed changes.
|
||||||
|
send: false
|
||||||
|
---
|
||||||
|
You are the **review specialist** for the `openwebui-extensions` repository.
|
||||||
|
|
||||||
|
Full review rules are in .github/instructions/code-review.instructions.md.
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
|
||||||
|
### 🔴 Blocking (must fix before release)
|
||||||
|
|
||||||
|
**1. Single-file i18n Architecture**
|
||||||
|
- [ ] No new `_cn.py` split files created.
|
||||||
|
- [ ] All user-visible strings go through `TRANSLATIONS[lang].get(key, fallback)`.
|
||||||
|
- [ ] `FALLBACK_MAP` covers at least `zh → zh-CN` and `en → en-US`.
|
||||||
|
- [ ] `format(**kwargs)` on translations wrapped in `try/except KeyError`.
|
||||||
|
|
||||||
|
**2. Context Helpers**
|
||||||
|
- [ ] Uses `_get_user_context(__user__)` (not `__user__["name"]` directly).
|
||||||
|
- [ ] Uses `_get_chat_context(body, __metadata__)` (not ad-hoc `body.get("chat_id")`).
|
||||||
|
|
||||||
|
**3. Antigravity Safety**
|
||||||
|
- [ ] Every `__event_call__` wrapped: `asyncio.wait_for(..., timeout=2.0)`.
|
||||||
|
- [ ] JS code passed to `__event_call__` has `try { ... } catch(e) { return fallback; }`.
|
||||||
|
- [ ] File path operations validated against workspace root (no traversal).
|
||||||
|
- [ ] Upload paths have dual-channel fallback (API → DB/local).
|
||||||
|
|
||||||
|
**4. Emitter Guards**
|
||||||
|
- [ ] Every `await emitter(...)` guarded by `if emitter:`.
|
||||||
|
- [ ] `_emit_status(done=False)` on start, `done=True` on success, `_emit_notification("error")` on failure.
|
||||||
|
- [ ] No bare `print()` — use `logging.getLogger(__name__)`.
|
||||||
|
|
||||||
|
**5. Filter Singleton Safety**
|
||||||
|
- [ ] No mutable per-request state stored on `self` in Filter plugins.
|
||||||
|
|
||||||
|
**6. Streaming Compatibility (OpenWebUI 0.8.x)**
|
||||||
|
- [ ] `</think>` tag closed before any normal text or tool cards.
|
||||||
|
- [ ] `<details type="tool_calls" ...>` attributes escape `"` as `"`.
|
||||||
|
- [ ] `<details ...>` block has newline immediately after `>`.
|
||||||
|
|
||||||
|
**7. Version & Docs Sync**
|
||||||
|
- [ ] Version bumped in docstring (if release).
|
||||||
|
- [ ] `README.md` + `README_CN.md` updated (What's New + version).
|
||||||
|
- [ ] `docs/plugins/{type}/{name}.md` and `.zh.md` match README.
|
||||||
|
- [ ] `docs/plugins/{type}/index.md` and `.zh.md` version badges updated.
|
||||||
|
- [ ] Root `README.md` / `README_CN.md` date badge updated.
|
||||||
|
|
||||||
|
### 🟡 Non-blocking (suggestions)
|
||||||
|
- Copilot SDK tools: `params_type=MyParams` in `define_tool()`.
|
||||||
|
- Long tasks (>3s): periodic `_emit_notification("info")` every 5s.
|
||||||
|
- `icon_url` present for Action plugins (Lucide SVG, base64).
|
||||||
|
|
||||||
|
## Known Pre-existing Issues (Don't block on unless the PR introduces new ones)
|
||||||
|
- `_cn.py` splits in: `deep-dive`, `export_to_docx`, `export_to_excel`, `flash-card`, `infographic`, `folder-memory` — legacy, not new.
|
||||||
|
- `context_enhancement_filter` version is `0.3` (non-SemVer) — pre-existing.
|
||||||
|
- `copilot_files_preprocessor` and `smart-mermaid` are empty stubs — pre-existing.
|
||||||
|
|
||||||
|
## Review Output
|
||||||
|
- **Blocking issues** (file:line references)
|
||||||
|
- **Non-blocking suggestions**
|
||||||
|
- **Pass / Fail verdict**
|
||||||
|
- **Next step**: Pass → handoff to Release Prep; Fail → return to Implementer with fix list
|
||||||
82
.github/agents/release-prep.agent.md
vendored
Normal file
82
.github/agents/release-prep.agent.md
vendored
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
---
|
||||||
|
name: Release Prep
|
||||||
|
description: Prepare release-ready summaries and Conventional Commit drafts without pushing
|
||||||
|
argument-hint: Provide final change list and target version (optional)
|
||||||
|
tools: ['search', 'read/readFile', 'web', 'web/fetch', 'web/githubRepo', 'execute/getTerminalOutput', 'read/terminalLastCommand', 'read/terminalSelection']
|
||||||
|
infer: true
|
||||||
|
---
|
||||||
|
You are the **release preparation specialist** for the `openwebui-extensions` repository.
|
||||||
|
|
||||||
|
Full commit message rules: .github/instructions/commit-message.instructions.md
|
||||||
|
Full release workflow: .agent/workflows/plugin-development.md
|
||||||
|
|
||||||
|
## Responsibilities
|
||||||
|
1. Generate a Conventional Commit message (English only).
|
||||||
|
2. Draft bilingual release notes (EN + 中文).
|
||||||
|
3. Verify ALL file sync locations are updated.
|
||||||
|
4. **Stop before any commit or push** — wait for explicit user confirmation.
|
||||||
|
|
||||||
|
## Commit Message Format
|
||||||
|
```text
|
||||||
|
type(scope): brief imperative description
|
||||||
|
|
||||||
|
- Key change 1
|
||||||
|
- Key change 2 (include migration note if needed)
|
||||||
|
```
|
||||||
|
- `type`: `feat` / `fix` / `docs` / `refactor` / `chore`
|
||||||
|
- `scope`: plugin folder name (e.g., `smart-mind-map`, `github-copilot-sdk`, `folder-memory`)
|
||||||
|
- Title ≤ 72 chars, imperative mood, no trailing period, no capital first letter
|
||||||
|
|
||||||
|
## 9-File Sync Checklist (fill in for each changed plugin)
|
||||||
|
```text
|
||||||
|
Plugin: {type}/{name} → v{new_version}
|
||||||
|
[ ] 1. plugins/{type}/{name}/{name}.py → version in docstring
|
||||||
|
[ ] 2. plugins/{type}/{name}/README.md → version + What's New
|
||||||
|
[ ] 3. plugins/{type}/{name}/README_CN.md → version + 最新更新
|
||||||
|
[ ] 4. docs/plugins/{type}/{name}.md → mirrors README
|
||||||
|
[ ] 5. docs/plugins/{type}/{name}.zh.md → mirrors README_CN
|
||||||
|
[ ] 6. docs/plugins/{type}/index.md → version badge updated
|
||||||
|
[ ] 7. docs/plugins/{type}/index.zh.md → version badge updated
|
||||||
|
[ ] 8. README.md (root) → date badge updated
|
||||||
|
[ ] 9. README_CN.md (root) → date badge updated
|
||||||
|
```
|
||||||
|
|
||||||
|
## Current Plugin Versions (as of last audit — 2026-02-23)
|
||||||
|
| Plugin | Type | Version | Note |
|
||||||
|
|--------|------|---------|------|
|
||||||
|
| deep-dive | action | 1.0.0 | has `_cn.py` split |
|
||||||
|
| export_to_docx | action | 0.4.4 | has `_cn.py` split |
|
||||||
|
| export_to_excel | action | 0.3.7 | has `_cn.py` split |
|
||||||
|
| flash-card | action | 0.2.4 | has `_cn.py` split |
|
||||||
|
| infographic | action | 1.5.0 | has `_cn.py` split |
|
||||||
|
| smart-mind-map | action | 1.0.0 | ✅ |
|
||||||
|
| async-context-compression | filter | 1.3.0 | ✅ |
|
||||||
|
| context_enhancement_filter | filter | 0.3 | ⚠️ non-SemVer |
|
||||||
|
| folder-memory | filter | 0.1.0 | has `_cn.py` split |
|
||||||
|
| github_copilot_sdk_files_filter | filter | 0.1.2 | ✅ |
|
||||||
|
| markdown_normalizer | filter | 1.2.4 | ✅ |
|
||||||
|
| web_gemini_multimodel_filter | filter | 0.3.2 | ✅ |
|
||||||
|
| github-copilot-sdk | pipe | 0.7.0 | ✅ |
|
||||||
|
| workspace-file-manager | tool | 0.2.0 | ✅ |
|
||||||
|
|
||||||
|
## Output Template
|
||||||
|
|
||||||
|
### Commit Message
|
||||||
|
```text
|
||||||
|
{type}({scope}): {description}
|
||||||
|
|
||||||
|
- {change 1}
|
||||||
|
- {change 2}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Change Summary (EN)
|
||||||
|
- {bullet list of user-visible changes}
|
||||||
|
|
||||||
|
### 变更摘要(中文)
|
||||||
|
- {中文要点列表}
|
||||||
|
|
||||||
|
### Verification Status
|
||||||
|
{filled-in 9-file checklist for each changed plugin}
|
||||||
|
|
||||||
|
---
|
||||||
|
⚠️ **Waiting for user confirmation — no git operations will run until explicitly approved.**
|
||||||
2181
.github/copilot-instructions.md
vendored
2181
.github/copilot-instructions.md
vendored
File diff suppressed because it is too large
Load Diff
54
.github/instructions/code-review.instructions.md
vendored
Normal file
54
.github/instructions/code-review.instructions.md
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
name: Plugin Code Review
|
||||||
|
description: Comprehensive OpenWebUI plugin review checklist covering i18n, context helpers, antigravity safety, and streaming compatibility
|
||||||
|
applyTo: "plugins/**/*.py"
|
||||||
|
---
|
||||||
|
# Code Review Instructions — OpenWebUI Plugins
|
||||||
|
|
||||||
|
You are an expert Senior Software Engineer reviewing OpenWebUI plugins for the `openwebui-extensions` repository.
|
||||||
|
When reviewing plugin code, you MUST verify each point below to ensure the code meets the strict repository standards.
|
||||||
|
|
||||||
|
## 1. Single-file i18n Pattern (CRITICAL)
|
||||||
|
- **One File Rule**: One `.py` file per plugin. No `_cn.py` or language-split files.
|
||||||
|
- **Translations**: All user-visible strings (status, notification, UI text) MUST go through a `TRANSLATIONS` dictionary and a `FALLBACK_MAP`.
|
||||||
|
- **Safety**: `format(**kwargs)` calls on translated strings MUST be wrapped in `try/except KeyError` to prevent crashes if a translation is missing a placeholder.
|
||||||
|
|
||||||
|
## 2. Context Helpers (CRITICAL)
|
||||||
|
- **User Context**: MUST use `_get_user_context(__user__)` instead of direct `__user__["name"]` access. `__user__` can be a list, dict, or None.
|
||||||
|
- **Chat Context**: MUST use `_get_chat_context(body, __metadata__)` instead of ad-hoc `body.get("chat_id")` calls.
|
||||||
|
|
||||||
|
## 3. Event & Logging
|
||||||
|
- **No Print**: No bare `print()` in production code. Use `logging.getLogger(__name__)`.
|
||||||
|
- **Emitter Safety**: Every `await emitter(...)` call MUST be guarded by `if emitter:` (or equivalent).
|
||||||
|
- **Status Lifecycle**:
|
||||||
|
- `_emit_status(done=False)` at task start.
|
||||||
|
- `_emit_status(done=True)` on completion.
|
||||||
|
- `_emit_notification("error")` on failure.
|
||||||
|
|
||||||
|
## 4. Antigravity Safety (CRITICAL)
|
||||||
|
- **Timeout Guards**: All `__event_call__` JS executions MUST be wrapped with `asyncio.wait_for(..., timeout=2.0)`. Failure to do this can hang the entire backend.
|
||||||
|
- **JS Fallbacks**: JS code executed via `__event_call__` MUST have an internal `try { ... } catch (e) { return fallback; }` block.
|
||||||
|
- **Path Sandboxing**: File path operations MUST be validated against the workspace root (no directory traversal vulnerabilities).
|
||||||
|
- **Upload Fallbacks**: Upload paths MUST have a dual-channel fallback (API → local/DB).
|
||||||
|
|
||||||
|
## 5. Filter Singleton Safety
|
||||||
|
- **No Mutable State**: Filter plugins are singletons. There MUST be NO request-scoped mutable state stored on `self` (e.g., `self.current_user = ...`).
|
||||||
|
- **Statelessness**: Per-request values MUST be computed from `body` and context helpers on each call.
|
||||||
|
|
||||||
|
## 6. Copilot SDK Tools
|
||||||
|
- **Pydantic Models**: Tool parameters MUST be defined as a `pydantic.BaseModel` subclass.
|
||||||
|
- **Explicit Params**: `define_tool(...)` MUST include `params_type=MyParams`.
|
||||||
|
- **Naming**: Tool names MUST match `^[a-zA-Z0-9_-]+$` (ASCII only).
|
||||||
|
|
||||||
|
## 7. Streaming Compatibility (OpenWebUI 0.8.x)
|
||||||
|
- **Thinking Tags**: The `</think>` tag MUST be closed before any normal content or tool cards are emitted.
|
||||||
|
- **Attribute Escaping**: Tool card `arguments` and `result` attributes MUST escape `"` as `"`.
|
||||||
|
- **Newline Requirement**: The `<details type="tool_calls" ...>` block MUST be followed by a newline immediately after `>`.
|
||||||
|
|
||||||
|
## 8. Performance & Memory
|
||||||
|
- **Streaming**: Large files or responses MUST be streamed, not loaded entirely into memory.
|
||||||
|
- **Database Connections**: Plugins MUST reuse the OpenWebUI internal database connection (`owui_engine`, `owui_Session`) rather than creating new ones.
|
||||||
|
|
||||||
|
## 9. HTML Injection (Action Plugins)
|
||||||
|
- **Wrapper Template**: HTML output MUST use the standard `<!-- OPENWEBUI_PLUGIN_OUTPUT -->` wrapper.
|
||||||
|
- **Idempotency**: The plugin MUST implement `_remove_existing_html` to ensure multiple runs do not duplicate the HTML output.
|
||||||
90
.github/instructions/commit-message.instructions.md
vendored
Normal file
90
.github/instructions/commit-message.instructions.md
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
name: Commit Message
|
||||||
|
description: Comprehensive Conventional Commits guidelines for openwebui-extensions
|
||||||
|
---
|
||||||
|
# Commit Message Instructions
|
||||||
|
|
||||||
|
You are an expert developer generating commit messages for the `openwebui-extensions` repository.
|
||||||
|
Always adhere to the following strict guidelines based on the Conventional Commits specification.
|
||||||
|
|
||||||
|
## 1. General Rules
|
||||||
|
- **Language**: **English ONLY**. Never use Chinese or other languages in the commit title or body.
|
||||||
|
- **Structure**:
|
||||||
|
```text
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
<BLANK LINE>
|
||||||
|
<body>
|
||||||
|
<BLANK LINE>
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Type (`<type>`)
|
||||||
|
Must be one of the following:
|
||||||
|
- `feat`: A new feature or a new plugin.
|
||||||
|
- `fix`: A bug fix.
|
||||||
|
- `docs`: Documentation only changes (e.g., README, MkDocs).
|
||||||
|
- `refactor`: A code change that neither fixes a bug nor adds a feature.
|
||||||
|
- `perf`: A code change that improves performance.
|
||||||
|
- `test`: Adding missing tests or correcting existing tests.
|
||||||
|
- `chore`: Changes to the build process, CI/CD, or auxiliary tools/scripts.
|
||||||
|
- `style`: Changes that do not affect the meaning of the code (white-space, formatting, etc.).
|
||||||
|
|
||||||
|
## 3. Scope (`<scope>`)
|
||||||
|
The scope should indicate the affected component or plugin.
|
||||||
|
- **For Plugins**: Use the plugin's folder name or category (e.g., `actions`, `filters`, `pipes`, `export-to-docx`, `github-copilot-sdk`).
|
||||||
|
- **For Docs**: Use `docs` or the specific doc section (e.g., `guide`, `readme`).
|
||||||
|
- **For Infra**: Use `ci`, `scripts`, `deps`.
|
||||||
|
- *Note*: Omit the scope if the change is global or spans too many components.
|
||||||
|
|
||||||
|
## 4. Subject Line (`<subject>`)
|
||||||
|
- **Length**: Keep it under 50-72 characters.
|
||||||
|
- **Mood**: Use the imperative, present tense: "change" not "changed" nor "changes" (e.g., "add feature" instead of "added feature").
|
||||||
|
- **Formatting**: Do not capitalize the first letter. Do not place a period `.` at the end.
|
||||||
|
- **Clarity**: State exactly *what* changed. Avoid vague terms like "update code" or "fix bug".
|
||||||
|
|
||||||
|
## 5. Body (`<body>`)
|
||||||
|
- **When to use**: Highly recommended for `feat`, `fix`, and `refactor`.
|
||||||
|
- **Content**: Explain *what* and *why* (motivation), not just *how* (the code diff shows the how).
|
||||||
|
- **Format**: Use a bulleted list (1-3 points) for readability.
|
||||||
|
- **Repo Specifics**: If the commit involves a plugin version bump, explicitly mention that the READMEs and docs were synced.
|
||||||
|
|
||||||
|
## 6. Footer / Breaking Changes (`<footer>`)
|
||||||
|
- If the commit introduces a breaking change (e.g., changing a Valve configuration key, altering a plugin's output format), append `!` after the scope/type: `feat(pipes)!: ...`
|
||||||
|
- Add a `BREAKING CHANGE:` block in the footer explaining the migration path.
|
||||||
|
|
||||||
|
## 7. Examples
|
||||||
|
|
||||||
|
**Example 1: Feature with Body**
|
||||||
|
```text
|
||||||
|
feat(github-copilot-sdk): add antigravity timeout guards
|
||||||
|
|
||||||
|
- Wrap all __event_call__ JS executions with asyncio.wait_for(timeout=2.0)
|
||||||
|
- Add dual-channel upload fallback (API → local/DB)
|
||||||
|
- Emit staged status events for tasks > 3 seconds
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example 2: Bug Fix**
|
||||||
|
```text
|
||||||
|
fix(export-to-docx): resolve missing title fallback
|
||||||
|
|
||||||
|
- Fallback to user_name + date when chat_title is empty
|
||||||
|
- Prevent crash when metadata variables are missing
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example 3: Documentation Sync**
|
||||||
|
```text
|
||||||
|
docs(plugins): sync bilingual READMEs for v1.2.0 release
|
||||||
|
|
||||||
|
- Update What's New section for folder-memory plugin
|
||||||
|
- Sync docs/plugins/filters/folder-memory.md
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example 4: Breaking Change**
|
||||||
|
```text
|
||||||
|
refactor(core)!: rename global configuration valves
|
||||||
|
|
||||||
|
- Standardize all valve keys to UPPER_SNAKE_CASE
|
||||||
|
- Remove deprecated legacy_api_key field
|
||||||
|
|
||||||
|
BREAKING CHANGE: Users must reconfigure their API keys in the UI as the old `api_key` field is no longer read.
|
||||||
|
```
|
||||||
54
.github/instructions/test-generation.instructions.md
vendored
Normal file
54
.github/instructions/test-generation.instructions.md
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
name: Test Generation
|
||||||
|
description: Comprehensive Pytest conventions, mocking strategies, and model usage rules for OpenWebUI plugins
|
||||||
|
applyTo: "plugins/debug/**,tests/**"
|
||||||
|
---
|
||||||
|
# Test Generation Instructions
|
||||||
|
|
||||||
|
You are an expert SDET (Software Development Engineer in Test) writing tests for the `openwebui-extensions` repository.
|
||||||
|
Follow these comprehensive guidelines to ensure robust, reliable, and cost-effective testing.
|
||||||
|
|
||||||
|
## 1. Test Structure & Placement
|
||||||
|
- **Unit Tests**: Place in the `tests/` directory at the root of the repository. Name files `test_<module>.py`.
|
||||||
|
- **Integration/Debug Scripts**: Place in `plugins/debug/<plugin_name>/`. Name files `debug_<feature>.py` or `inspect_<feature>.py`.
|
||||||
|
- **Fixtures**: Use `conftest.py` for shared fixtures (e.g., mock users, mock emitters, mock database sessions).
|
||||||
|
|
||||||
|
## 2. Model Usage & Cost Control (CRITICAL)
|
||||||
|
When writing tests or debug scripts that actually invoke an LLM:
|
||||||
|
- **Allowed Models**: You MUST use low-cost models: `gpt-5-mini` (Preferred) or `gpt-4.1`.
|
||||||
|
- **Forbidden**: NEVER use expensive models (like `gpt-4-turbo`, `claude-3-opus`) in automated tests unless explicitly requested by the user.
|
||||||
|
- **API Keys**: Never hardcode API keys. Always read from environment variables (`os.getenv("OPENAI_API_KEY")`) or a `.env` file.
|
||||||
|
|
||||||
|
## 3. Mocking OpenWebUI Internals
|
||||||
|
OpenWebUI plugins rely heavily on injected runtime variables. Your tests must mock these accurately:
|
||||||
|
- **`__user__`**: Mock as a dictionary. Always test both with a full user object and an empty/None object to ensure fallback logic works.
|
||||||
|
```python
|
||||||
|
mock_user_en = {"id": "u1", "name": "Alice", "language": "en-US"}
|
||||||
|
mock_user_zh = {"id": "u2", "name": "Bob", "language": "zh-CN"}
|
||||||
|
mock_user_none = None
|
||||||
|
```
|
||||||
|
- **`__event_emitter__`**: Mock as an async callable that records emitted events for assertion.
|
||||||
|
```python
|
||||||
|
async def mock_emitter(event: dict):
|
||||||
|
emitted_events.append(event)
|
||||||
|
```
|
||||||
|
- **`__event_call__`**: Mock as an async callable. To test Antigravity timeout guards, you must occasionally mock this to sleep longer than the timeout (e.g., `await asyncio.sleep(3.0)`) to ensure `asyncio.wait_for` catches it.
|
||||||
|
|
||||||
|
## 4. Testing Async Code
|
||||||
|
- All OpenWebUI plugin entry points (`action`, `inlet`, `outlet`) are asynchronous.
|
||||||
|
- Use `@pytest.mark.asyncio` for all test functions.
|
||||||
|
- Ensure you `await` all plugin method calls.
|
||||||
|
|
||||||
|
## 5. i18n (Internationalization) Testing
|
||||||
|
Since all plugins must be single-file i18n:
|
||||||
|
- **Mandatory**: Write parameterized tests to verify output in both English (`en-US`) and Chinese (`zh-CN`).
|
||||||
|
- Verify that if an unsupported language is passed (e.g., `fr-FR`), the system correctly falls back to the default (usually `en-US`).
|
||||||
|
|
||||||
|
## 6. Antigravity & Safety Testing
|
||||||
|
- **Timeout Guards**: Explicitly test that frontend JS executions (`__event_call__`) do not hang the backend. Assert that a `TimeoutError` is caught and handled gracefully.
|
||||||
|
- **State Leakage**: For `Filter` plugins (which are singletons), write tests that simulate concurrent requests from different users to ensure no state leaks across `self`.
|
||||||
|
|
||||||
|
## 7. Assertions & Best Practices
|
||||||
|
- Assert on **behavior and output**, not internal implementation details.
|
||||||
|
- For HTML/Markdown generation plugins, use Regex or BeautifulSoup to assert the presence of required tags (e.g., `<!-- OPENWEBUI_PLUGIN_OUTPUT -->`) rather than exact string matching.
|
||||||
|
- Keep tests isolated. Do not rely on the execution order of tests.
|
||||||
44
.github/skills/README.md
vendored
Normal file
44
.github/skills/README.md
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Agent Skills Index
|
||||||
|
|
||||||
|
This folder contains reusable Agent Skills for GitHub Copilot / VS Code custom agent workflows.
|
||||||
|
|
||||||
|
## Available Skills
|
||||||
|
|
||||||
|
- **community-announcer**
|
||||||
|
- Purpose: Generate community announcement content and related assets.
|
||||||
|
- Entry: `community-announcer/SKILL.md`
|
||||||
|
|
||||||
|
- **doc-mirror-sync**
|
||||||
|
- Purpose: Sync mirrored documentation content and helper scripts.
|
||||||
|
- Entry: `doc-mirror-sync/SKILL.md`
|
||||||
|
|
||||||
|
- **gh-issue-replier**
|
||||||
|
- Purpose: Draft standardized issue replies with templates.
|
||||||
|
- Entry: `gh-issue-replier/SKILL.md`
|
||||||
|
|
||||||
|
- **gh-issue-scheduler**
|
||||||
|
- Purpose: Schedule and discover unanswered issues for follow-up.
|
||||||
|
- Entry: `gh-issue-scheduler/SKILL.md`
|
||||||
|
|
||||||
|
- **i18n-validator**
|
||||||
|
- Purpose: Validate translation key consistency across i18n dictionaries.
|
||||||
|
- Entry: `i18n-validator/SKILL.md`
|
||||||
|
|
||||||
|
- **plugin-scaffolder**
|
||||||
|
- Purpose: Scaffold OpenWebUI plugin boilerplate with repository standards.
|
||||||
|
- Entry: `plugin-scaffolder/SKILL.md`
|
||||||
|
|
||||||
|
- **version-bumper**
|
||||||
|
- Purpose: Assist with semantic version bumping workflows.
|
||||||
|
- Entry: `version-bumper/SKILL.md`
|
||||||
|
|
||||||
|
- **xlsx-single-file**
|
||||||
|
- Purpose: Single-file spreadsheet operations workflow without LibreOffice.
|
||||||
|
- Entry: `xlsx-single-file/SKILL.md`
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Skill definitions follow the expected location pattern:
|
||||||
|
- `.github/skills/<skill-name>/SKILL.md`
|
||||||
|
- Each skill may include optional `assets/`, `references/`, and `scripts/` folders.
|
||||||
|
- This directory mirrors `.gemini/skills` for compatibility.
|
||||||
23
.github/skills/community-announcer/SKILL.md
vendored
Normal file
23
.github/skills/community-announcer/SKILL.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
name: community-announcer
|
||||||
|
description: Drafts engaging English and Chinese update announcements for the OpenWebUI Community and other social platforms. Use when a new version is released.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Community Announcer
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Automates the drafting of high-impact update announcements.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
1. **Source Intel**: Read the latest version's `What's New` section from `README.md`.
|
||||||
|
2. **Drafting**: Create two versions:
|
||||||
|
- **Community Post**: Professional, structured, technical.
|
||||||
|
- **Catchy Short**: For Discord/Twitter, use emojis and bullet points.
|
||||||
|
3. **Multi-language**: Generate BOTH English and Chinese versions automatically.
|
||||||
|
|
||||||
|
## Announcement Structure (Recommended)
|
||||||
|
- **Headline**: "Update vX.X.X - [Main Feature]"
|
||||||
|
- **Introduction**: Brief context.
|
||||||
|
- **Key Highlights**: Bulleted list of fixes/features.
|
||||||
|
- **Action**: "Download from [Market Link]"
|
||||||
|
- **Closing**: Thanks and Star request.
|
||||||
14
.github/skills/doc-mirror-sync/SKILL.md
vendored
Normal file
14
.github/skills/doc-mirror-sync/SKILL.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: doc-mirror-sync
|
||||||
|
description: Automatically synchronizes plugin READMEs to the official documentation directory (docs/). Use after editing a plugin's local documentation to keep the MkDocs site up to date.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Doc Mirror Sync
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Automates the mirroring of `plugins/{type}/{name}/README.md` to `docs/plugins/{type}/{name}.md`.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
1. Identify changed READMEs.
|
||||||
|
2. Copy content to corresponding mirror paths.
|
||||||
|
3. Update version badges in `docs/plugins/{type}/index.md`.
|
||||||
38
.github/skills/doc-mirror-sync/scripts/sync.py
vendored
Normal file
38
.github/skills/doc-mirror-sync/scripts/sync.py
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import re
|
||||||
|
|
||||||
|
def sync_mirrors():
|
||||||
|
plugins_root = "plugins"
|
||||||
|
docs_root = "docs/plugins"
|
||||||
|
|
||||||
|
types = ["actions", "filters", "pipes", "pipelines", "tools"]
|
||||||
|
|
||||||
|
for t in types:
|
||||||
|
src_type_dir = os.path.join(plugins_root, t)
|
||||||
|
dest_type_dir = os.path.join(docs_root, t)
|
||||||
|
|
||||||
|
if not os.path.exists(src_type_dir): continue
|
||||||
|
os.makedirs(dest_type_dir, exist_ok=True)
|
||||||
|
|
||||||
|
for name in os.listdir(src_type_dir):
|
||||||
|
plugin_dir = os.path.join(src_type_dir, name)
|
||||||
|
if not os.path.isdir(plugin_dir): continue
|
||||||
|
|
||||||
|
# Sync README.md -> docs/plugins/{type}/{name}.md
|
||||||
|
src_readme = os.path.join(plugin_dir, "README.md")
|
||||||
|
if os.path.exists(src_readme):
|
||||||
|
dest_readme = os.path.join(dest_type_dir, f"{name}.md")
|
||||||
|
shutil.copy(src_readme, dest_readme)
|
||||||
|
print(f"✅ Mirrored: {t}/{name} (EN)")
|
||||||
|
|
||||||
|
# Sync README_CN.md -> docs/plugins/{type}/{name}.zh.md
|
||||||
|
src_readme_cn = os.path.join(plugin_dir, "README_CN.md")
|
||||||
|
if os.path.exists(src_readme_cn):
|
||||||
|
dest_readme_zh = os.path.join(dest_type_dir, f"{name}.zh.md")
|
||||||
|
shutil.copy(src_readme_cn, dest_readme_zh)
|
||||||
|
print(f"✅ Mirrored: {t}/{name} (ZH)")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sync_mirrors()
|
||||||
51
.github/skills/gh-issue-replier/SKILL.md
vendored
Normal file
51
.github/skills/gh-issue-replier/SKILL.md
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
name: gh-issue-replier
|
||||||
|
description: Professional English replier for GitHub issues. Use when a task is completed, a bug is fixed, or more info is needed from the user. Automates replying using the 'gh' CLI tool.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Gh Issue Replier
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `gh-issue-replier` skill enables Gemini CLI to interact with GitHub issues professionally. It enforces English for all communications and leverages the `gh` CLI to post comments.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. **Identify the Issue**: Find the issue number (e.g., #49).
|
||||||
|
2. **Check Star Status**: Run the bundled script to check if the author has starred the repo.
|
||||||
|
* Command: `bash scripts/check_star.sh <issue-number>`
|
||||||
|
* Interpretation:
|
||||||
|
* Exit code **0**: User has starred. Use "Already Starred" templates.
|
||||||
|
* Exit code **1**: User has NOT starred. Include "Star Request" in the reply.
|
||||||
|
3. **Select a Template**: Load [templates.md](references/templates.md) to choose a suitable English response pattern.
|
||||||
|
4. **Draft the Reply**: Compose a concise message based on the star status.
|
||||||
|
5. **Post the Comment**: Use the `gh` tool to submit the reply.
|
||||||
|
|
||||||
|
## Tool Integration
|
||||||
|
|
||||||
|
### Check Star Status
|
||||||
|
```bash
|
||||||
|
bash scripts/check_star.sh <issue-number>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Post Comment
|
||||||
|
```bash
|
||||||
|
gh issue comment <issue-number> --body "<message-body>"
|
||||||
|
```
|
||||||
|
|
||||||
|
Example (if user has NOT starred):
|
||||||
|
```bash
|
||||||
|
gh issue comment 49 --body "This has been fixed in v1.2.7. If you find this helpful, a star on the repo would be much appreciated! ⭐"
|
||||||
|
```
|
||||||
|
|
||||||
|
Example (if user HAS starred):
|
||||||
|
```bash
|
||||||
|
gh issue comment 49 --body "This has been fixed in v1.2.7. Thanks for your support!"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
|
||||||
|
- **Language**: ALWAYS use English for the comment body, even if the system prompt or user conversation is in another language.
|
||||||
|
- **Tone**: Professional, helpful, and appreciative.
|
||||||
|
- **Precision**: When announcing a fix, mention the specific version or the logic change (e.g., "Updated regex pattern").
|
||||||
|
- **Closing**: If the issue is resolved and you have permission, you can also use `gh issue close <number>`.
|
||||||
17
.github/skills/gh-issue-replier/references/example_reference.md
vendored
Normal file
17
.github/skills/gh-issue-replier/references/example_reference.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Reference Documentation for Gh Issue Replier
|
||||||
|
|
||||||
|
This is a placeholder for detailed reference documentation.
|
||||||
|
Replace with actual reference content or delete if not needed.
|
||||||
|
|
||||||
|
## Structure Suggestions
|
||||||
|
|
||||||
|
### API Reference Example
|
||||||
|
- Overview
|
||||||
|
- Authentication
|
||||||
|
- Endpoints with examples
|
||||||
|
- Error codes
|
||||||
|
|
||||||
|
### Workflow Guide Example
|
||||||
|
- Prerequisites
|
||||||
|
- Step-by-step instructions
|
||||||
|
- Best practices
|
||||||
45
.github/skills/gh-issue-replier/references/templates.md
vendored
Normal file
45
.github/skills/gh-issue-replier/references/templates.md
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Issue Reply Templates
|
||||||
|
|
||||||
|
Use these templates to craft professional English replies. Adjust placeholders like `@username`, `v1.2.x`, and `[commit hash]` as needed.
|
||||||
|
|
||||||
|
## 1. Acknowledging a New Issue
|
||||||
|
Use when you first see an issue and want to let the user know you are working on it.
|
||||||
|
|
||||||
|
- "Thank you for reporting this! I'm looking into it right now."
|
||||||
|
- "Thanks for bringing this to my attention. I'll try to reproduce this behavior and get back to you shortly."
|
||||||
|
|
||||||
|
## 2. Requesting More Information
|
||||||
|
Use when you need logs or specific details to fix the bug.
|
||||||
|
|
||||||
|
- "Could you please provide the **'Original'** vs **'Normalized'** content from your browser console logs (F12)? It would help a lot in debugging."
|
||||||
|
- "It would be very helpful if you could share the specific Markdown text that triggered this issue."
|
||||||
|
|
||||||
|
## 3. Announcing a Fix
|
||||||
|
Use when you have pushed the fix to the repository.
|
||||||
|
|
||||||
|
- "This has been fixed in version **v1.2.x**. You can update the plugin to resolve it."
|
||||||
|
- "I've just pushed a fix for this in [commit hash]. Please let me know if it works for you after updating."
|
||||||
|
- "The issue was caused by a greedy regex pattern. I've updated it to use a tempered greedy token to prevent incorrect merging."
|
||||||
|
|
||||||
|
## 4. Guiding to Official Market
|
||||||
|
Always provide the official market link to ensure the user gets the latest verified version.
|
||||||
|
|
||||||
|
- "The fix is now live! You can download the latest version from the official OpenWebUI Community page here: [Plugin Market Link]. Simply update the function in your OpenWebUI instance to apply the changes."
|
||||||
|
- "I recommend getting the updated version from the official store: [Link]. It includes the fix for the spacing issue we discussed."
|
||||||
|
|
||||||
|
## 5. Closing the Issue
|
||||||
|
Use when the issue is confirmed resolved.
|
||||||
|
|
||||||
|
- "Glad to hear it's working now! Closing this for now. Feel free to reopen it if the problem persists."
|
||||||
|
- "Since this is resolved, I'm closing this issue. Thanks again for your feedback!"
|
||||||
|
|
||||||
|
## 5. Pro-tip: Star Request
|
||||||
|
Gently handle star requests based on the user's current status.
|
||||||
|
|
||||||
|
### If User has NOT starred:
|
||||||
|
- "If you find this plugin helpful, a star on the repo would be much appreciated! ⭐"
|
||||||
|
- "We'd love your support! If this fixed your issue, please consider starring the repository. ⭐"
|
||||||
|
|
||||||
|
### If User HAS already starred:
|
||||||
|
- "Thanks again for starring the project and for your continuous support!"
|
||||||
|
- "I appreciate your support and for being a stargazer of this project!"
|
||||||
31
.github/skills/gh-issue-replier/scripts/check_star.sh
vendored
Executable file
31
.github/skills/gh-issue-replier/scripts/check_star.sh
vendored
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Robust Star Checker v2
|
||||||
|
# Usage: ./check_star.sh <issue_number>
|
||||||
|
|
||||||
|
ISSUE_NUM=$1
|
||||||
|
if [ -z "$ISSUE_NUM" ]; then exit 2; fi
|
||||||
|
|
||||||
|
# 1. Get Repo and Author info
|
||||||
|
REPO_FULL=$(gh repo view --json owner,name -q ".owner.login + \"/\" + .name")
|
||||||
|
USER_LOGIN=$(gh issue view "$ISSUE_NUM" --json author -q ".author.login")
|
||||||
|
|
||||||
|
# 2. Use GraphQL for high precision (Detects stars even when REST 404s)
|
||||||
|
IS_STARRED=$(gh api graphql -f query='
|
||||||
|
query($owner:String!, $repo:String!, $user:String!) {
|
||||||
|
repository(owner:$owner, name:$repo) {
|
||||||
|
stargazers(query:$user, first:1) {
|
||||||
|
nodes {
|
||||||
|
login
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}' -f owner="${REPO_FULL%/*}" -f repo="${REPO_FULL#*/}" -f user="$USER_LOGIN" -q ".data.repository.stargazers.nodes[0].login")
|
||||||
|
|
||||||
|
if [ "$IS_STARRED" == "$USER_LOGIN" ]; then
|
||||||
|
echo "Confirmed: @$USER_LOGIN HAS starred $REPO_FULL. ⭐"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "Confirmed: @$USER_LOGIN has NOT starred $REPO_FULL."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
42
.github/skills/gh-issue-scheduler/SKILL.md
vendored
Normal file
42
.github/skills/gh-issue-scheduler/SKILL.md
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
name: gh-issue-scheduler
|
||||||
|
description: Finds all open GitHub issues that haven't been replied to by the owner, summarizes them, and generates a solution plan. Use when the user wants to audit pending tasks or plan maintenance work.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Gh Issue Scheduler
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `gh-issue-scheduler` skill helps maintainers track community feedback by identifying unaddressed issues and drafting actionable technical plans to resolve them.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. **Identify Unanswered Issues**: Run the bundled script to fetch issues without owner replies.
|
||||||
|
* Command: `bash scripts/find_unanswered.sh`
|
||||||
|
2. **Analyze and Summarize**: For each identified issue, summarize the core problem and the user's intent.
|
||||||
|
3. **Generate Solution Plans**: Draft a technical "Action Plan" for each issue, including:
|
||||||
|
* **Root Cause Analysis** (if possible)
|
||||||
|
* **Proposed Fix/Implementation**
|
||||||
|
* **Verification Strategy**
|
||||||
|
4. **Present to User**: Display a structured report of all pending issues and their respective plans.
|
||||||
|
|
||||||
|
## Tool Integration
|
||||||
|
|
||||||
|
### Find Unanswered Issues
|
||||||
|
```bash
|
||||||
|
bash scripts/find_unanswered.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Report Format
|
||||||
|
|
||||||
|
When presenting the summary, use the following Markdown structure:
|
||||||
|
|
||||||
|
### 📋 Unanswered Issues Audit
|
||||||
|
|
||||||
|
#### Issue #[Number]: [Title]
|
||||||
|
- **Author**: @username
|
||||||
|
- **Summary**: Concise description of the problem.
|
||||||
|
- **Action Plan**:
|
||||||
|
1. Step 1 (e.g., Investigate file X)
|
||||||
|
2. Step 2 (e.g., Apply fix Y)
|
||||||
|
3. Verification (e.g., Run test Z)
|
||||||
42
.github/skills/gh-issue-scheduler/scripts/find_unanswered.sh
vendored
Executable file
42
.github/skills/gh-issue-scheduler/scripts/find_unanswered.sh
vendored
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Fetch all open issues and filter for those without responses from the owner/collaborators.
|
||||||
|
# Uses 'gh' CLI.
|
||||||
|
|
||||||
|
REPO_FULL=$(gh repo view --json owner,name -q ".owner.login + "/" + .name")
|
||||||
|
OWNER=${REPO_FULL%/*}
|
||||||
|
|
||||||
|
# 1. Get all open issues
|
||||||
|
OPEN_ISSUES=$(gh issue list --state open --json number,title,author,createdAt --limit 100)
|
||||||
|
|
||||||
|
echo "Analysis for repository: $REPO_FULL"
|
||||||
|
echo "------------------------------------"
|
||||||
|
|
||||||
|
# Process each issue
|
||||||
|
echo "$OPEN_ISSUES" | jq -c '.[]' | while read -r issue; do
|
||||||
|
NUMBER=$(echo "$issue" | jq -r '.number')
|
||||||
|
TITLE=$(echo "$issue" | jq -r '.title')
|
||||||
|
AUTHOR=$(echo "$issue" | jq -r '.author.login')
|
||||||
|
|
||||||
|
# Check comments for owner responses
|
||||||
|
# We look for comments where the author is the repo owner
|
||||||
|
COMMENTS=$(gh issue view "$NUMBER" --json comments -q ".comments[].author.login" 2>/dev/null)
|
||||||
|
|
||||||
|
HAS_OWNER_REPLY=false
|
||||||
|
for COMMENT_AUTHOR in $COMMENTS; do
|
||||||
|
if [ "$COMMENT_AUTHOR" == "$OWNER" ]; then
|
||||||
|
HAS_OWNER_REPLY=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$HAS_OWNER_REPLY" == "false" ]; then
|
||||||
|
echo "ISSUE_START"
|
||||||
|
echo "ID: $NUMBER"
|
||||||
|
echo "Title: $TITLE"
|
||||||
|
echo "Author: $AUTHOR"
|
||||||
|
echo "Description:"
|
||||||
|
gh issue view "$NUMBER" --json body -q ".body"
|
||||||
|
echo "ISSUE_END"
|
||||||
|
fi
|
||||||
|
done
|
||||||
14
.github/skills/i18n-validator/SKILL.md
vendored
Normal file
14
.github/skills/i18n-validator/SKILL.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: i18n-validator
|
||||||
|
description: Validates multi-language consistency in the TRANSLATIONS dictionary of a plugin. Use to check if any language keys are missing or if translations need updating.
|
||||||
|
---
|
||||||
|
|
||||||
|
# I18n Validator
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Ensures all 12 supported languages (en-US, zh-CN, etc.) have aligned translation keys.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Detects missing keys in non-English dictionaries.
|
||||||
|
- Suggests translations using the core AI engine.
|
||||||
|
- Validates the `fallback_map` for variant redirects.
|
||||||
54
.github/skills/i18n-validator/scripts/validate_i18n.py
vendored
Normal file
54
.github/skills/i18n-validator/scripts/validate_i18n.py
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import ast
|
||||||
|
import os
|
||||||
|
|
||||||
|
def check_i18n(file_path):
|
||||||
|
if not os.path.exists(file_path):
|
||||||
|
print(f"Error: File not found {file_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
|
tree = ast.parse(f.read())
|
||||||
|
|
||||||
|
translations = {}
|
||||||
|
for node in tree.body:
|
||||||
|
if isinstance(node, ast.Assign):
|
||||||
|
for target in node.targets:
|
||||||
|
if isinstance(target, ast.Name) and target.id == "TRANSLATIONS":
|
||||||
|
translations = ast.literal_eval(node.value)
|
||||||
|
break
|
||||||
|
|
||||||
|
if not translations:
|
||||||
|
print("⚠️ No TRANSLATIONS dictionary found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Base keys from English
|
||||||
|
base_lang = "en-US"
|
||||||
|
if base_lang not in translations:
|
||||||
|
print(f"❌ Error: {base_lang} missing in TRANSLATIONS.")
|
||||||
|
return
|
||||||
|
|
||||||
|
base_keys = set(translations[base_lang].keys())
|
||||||
|
print(f"🔍 Analyzing {file_path}...")
|
||||||
|
print(f"Standard keys ({len(base_keys)}): {', '.join(sorted(base_keys))}
|
||||||
|
")
|
||||||
|
|
||||||
|
for lang, keys in translations.items():
|
||||||
|
if lang == base_lang: continue
|
||||||
|
lang_keys = set(keys.keys())
|
||||||
|
missing = base_keys - lang_keys
|
||||||
|
extra = lang_keys - base_keys
|
||||||
|
|
||||||
|
if missing:
|
||||||
|
print(f"❌ {lang}: Missing {len(missing)} keys: {', '.join(missing)}")
|
||||||
|
if extra:
|
||||||
|
print(f"⚠️ {lang}: Has {len(extra)} extra keys: {', '.join(extra)}")
|
||||||
|
if not missing and not extra:
|
||||||
|
print(f"✅ {lang}: Aligned.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: validate_i18n.py <path_to_plugin.py>")
|
||||||
|
sys.exit(1)
|
||||||
|
check_i18n(sys.argv[1])
|
||||||
19
.github/skills/plugin-scaffolder/SKILL.md
vendored
Normal file
19
.github/skills/plugin-scaffolder/SKILL.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
name: plugin-scaffolder
|
||||||
|
description: Generates a standardized single-file i18n Python plugin template based on project standards. Use when starting a new plugin development to skip boilerplate writing.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Plugin Scaffolder
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Generates compliant OpenWebUI plugin templates with built-in i18n, common utility methods, and required docstring fields.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
1. Provide the **Plugin Name** and **Type** (action/filter/pipe).
|
||||||
|
2. The skill will generate the `.py` file and the bilingual `README` files.
|
||||||
|
|
||||||
|
## Template Standard
|
||||||
|
- `Valves(BaseModel)` with `UPPER_SNAKE_CASE`
|
||||||
|
- `_get_user_context` with JS fallback and timeout
|
||||||
|
- `_emit_status` and `_emit_debug_log` methods
|
||||||
|
- Standardized docstring metadata
|
||||||
34
.github/skills/plugin-scaffolder/assets/README_template.md
vendored
Normal file
34
.github/skills/plugin-scaffolder/assets/README_template.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# {{TITLE}}
|
||||||
|
|
||||||
|
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 0.1.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
|
||||||
|
|
||||||
|
{{DESCRIPTION}}
|
||||||
|
|
||||||
|
## 🔥 What's New in v0.1.0
|
||||||
|
|
||||||
|
* Initial release of {{TITLE}}.
|
||||||
|
|
||||||
|
## 🌐 Multilingual Support
|
||||||
|
|
||||||
|
Supports automatic interface and status switching for the following languages:
|
||||||
|
`English`, `简体中文`, `繁體中文 (香港)`, `繁體中文 (台灣)`, `한국어`, `日本語`, `Français`, `Deutsch`, `Español`, `Italiano`, `Tiếng Việt`, `Bahasa Indonesia`.
|
||||||
|
|
||||||
|
## ✨ Core Features
|
||||||
|
|
||||||
|
* Feature 1
|
||||||
|
* Feature 2
|
||||||
|
|
||||||
|
## How to Use 🛠️
|
||||||
|
|
||||||
|
1. Install the plugin in Open WebUI.
|
||||||
|
2. Configure settings in Valves.
|
||||||
|
|
||||||
|
## Configuration (Valves) ⚙️
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `priority` | `50` | Execution priority. |
|
||||||
|
|
||||||
|
## ⭐ Support
|
||||||
|
|
||||||
|
If this plugin has been useful, a star on [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) is a big motivation for me. Thank you for the support.
|
||||||
80
.github/skills/plugin-scaffolder/assets/template.py
vendored
Normal file
80
.github/skills/plugin-scaffolder/assets/template.py
vendored
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
"""
|
||||||
|
title: {{TITLE}}
|
||||||
|
author: Fu-Jie
|
||||||
|
author_url: https://github.com/Fu-Jie/openwebui-extensions
|
||||||
|
funding_url: https://github.com/open-webui
|
||||||
|
version: 0.1.0
|
||||||
|
description: {{DESCRIPTION}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from typing import Optional, Dict, Any, List, Callable, Awaitable
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from fastapi import Request
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
TRANSLATIONS = {
|
||||||
|
"en-US": {"status_starting": "Starting {{TITLE}}..."},
|
||||||
|
"zh-CN": {"status_starting": "正在启动 {{TITLE}}..."},
|
||||||
|
"zh-HK": {"status_starting": "正在啟動 {{TITLE}}..."},
|
||||||
|
"zh-TW": {"status_starting": "正在啟動 {{TITLE}}..."},
|
||||||
|
"ko-KR": {"status_starting": "{{TITLE}} 시작 중..."},
|
||||||
|
"ja-JP": {"status_starting": "{{TITLE}} を起動中..."},
|
||||||
|
"fr-FR": {"status_starting": "Démarrage de {{TITLE}}..."},
|
||||||
|
"de-DE": {"status_starting": "{{TITLE}} wird gestartet..."},
|
||||||
|
"es-ES": {"status_starting": "Iniciando {{TITLE}}..."},
|
||||||
|
"it-IT": {"status_starting": "Avvio di {{TITLE}}..."},
|
||||||
|
"vi-VN": {"status_starting": "Đang khởi động {{TITLE}}..."},
|
||||||
|
"id-ID": {"status_starting": "Memulai {{TITLE}}..."},
|
||||||
|
}
|
||||||
|
|
||||||
|
class {{CLASS_NAME}}:
|
||||||
|
class Valves(BaseModel):
|
||||||
|
priority: int = Field(default=50, description="Priority level (lower = earlier).")
|
||||||
|
show_status: bool = Field(default=True, description="Show status updates in UI.")
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.valves = self.Valves()
|
||||||
|
self.fallback_map = {
|
||||||
|
"zh": "zh-CN", "en": "en-US", "ko": "ko-KR", "ja": "ja-JP",
|
||||||
|
"fr": "fr-FR", "de": "de-DE", "es": "es-ES", "it": "it-IT",
|
||||||
|
"vi": "vi-VN", "id": "id-ID"
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_translation(self, lang: str, key: str, **kwargs) -> str:
|
||||||
|
target_lang = lang
|
||||||
|
if target_lang not in TRANSLATIONS:
|
||||||
|
base = target_lang.split("-")[0]
|
||||||
|
target_lang = self.fallback_map.get(base, "en-US")
|
||||||
|
|
||||||
|
lang_dict = TRANSLATIONS.get(target_lang, TRANSLATIONS["en-US"])
|
||||||
|
text = lang_dict.get(key, TRANSLATIONS["en-US"].get(key, key))
|
||||||
|
return text.format(**kwargs) if kwargs else text
|
||||||
|
|
||||||
|
async def _get_user_context(self, __user__: Optional[dict], __event_call__: Optional[Callable] = None, __request__: Optional[Request] = None) -> dict:
|
||||||
|
user_data = __user__ if isinstance(__user__, dict) else {}
|
||||||
|
user_language = user_data.get("language", "en-US")
|
||||||
|
if __event_call__:
|
||||||
|
try:
|
||||||
|
js = "try { return (document.documentElement.lang || localStorage.getItem('locale') || navigator.language || 'en-US'); } catch (e) { return 'en-US'; }"
|
||||||
|
frontend_lang = await asyncio.wait_for(__event_call__({"type": "execute", "data": {"code": js}}), timeout=2.0)
|
||||||
|
if frontend_lang: user_language = frontend_lang
|
||||||
|
except: pass
|
||||||
|
return {"user_language": user_language}
|
||||||
|
|
||||||
|
async def {{METHOD_NAME}}(self, body: dict, __user__: Optional[dict] = None, __event_emitter__=None, __event_call__=None, __request__: Optional[Request] = None) -> dict:
|
||||||
|
if self.valves.show_status and __event_emitter__:
|
||||||
|
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
|
||||||
|
msg = self._get_translation(user_ctx["user_language"], "status_starting")
|
||||||
|
await __event_emitter__({"type": "status", "data": {"description": msg, "done": False}})
|
||||||
|
|
||||||
|
# Implement core logic here
|
||||||
|
|
||||||
|
if self.valves.show_status and __event_emitter__:
|
||||||
|
await __event_emitter__({"type": "status", "data": {"description": "Done", "done": True}})
|
||||||
|
return body
|
||||||
80
.github/skills/plugin-scaffolder/assets/template.py.j2
vendored
Normal file
80
.github/skills/plugin-scaffolder/assets/template.py.j2
vendored
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
"""
|
||||||
|
title: {{TITLE}}
|
||||||
|
author: Fu-Jie
|
||||||
|
author_url: https://github.com/Fu-Jie/openwebui-extensions
|
||||||
|
funding_url: https://github.com/open-webui
|
||||||
|
version: 0.1.0
|
||||||
|
description: {{DESCRIPTION}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from typing import Optional, Dict, Any, List, Callable, Awaitable
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from fastapi import Request
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
TRANSLATIONS = {
|
||||||
|
"en-US": {"status_starting": "Starting {{TITLE}}..."},
|
||||||
|
"zh-CN": {"status_starting": "正在启动 {{TITLE}}..."},
|
||||||
|
"zh-HK": {"status_starting": "正在啟動 {{TITLE}}..."},
|
||||||
|
"zh-TW": {"status_starting": "正在啟動 {{TITLE}}..."},
|
||||||
|
"ko-KR": {"status_starting": "{{TITLE}} 시작 중..."},
|
||||||
|
"ja-JP": {"status_starting": "{{TITLE}} を起動中..."},
|
||||||
|
"fr-FR": {"status_starting": "Démarrage de {{TITLE}}..."},
|
||||||
|
"de-DE": {"status_starting": "{{TITLE}} wird gestartet..."},
|
||||||
|
"es-ES": {"status_starting": "Iniciando {{TITLE}}..."},
|
||||||
|
"it-IT": {"status_starting": "Avvio di {{TITLE}}..."},
|
||||||
|
"vi-VN": {"status_starting": "Đang khởi động {{TITLE}}..."},
|
||||||
|
"id-ID": {"status_starting": "Memulai {{TITLE}}..."},
|
||||||
|
}
|
||||||
|
|
||||||
|
class {{CLASS_NAME}}:
|
||||||
|
class Valves(BaseModel):
|
||||||
|
priority: int = Field(default=50, description="Priority level (lower = earlier).")
|
||||||
|
show_status: bool = Field(default=True, description="Show status updates in UI.")
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.valves = self.Valves()
|
||||||
|
self.fallback_map = {
|
||||||
|
"zh": "zh-CN", "en": "en-US", "ko": "ko-KR", "ja": "ja-JP",
|
||||||
|
"fr": "fr-FR", "de": "de-DE", "es": "es-ES", "it": "it-IT",
|
||||||
|
"vi": "vi-VN", "id": "id-ID"
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_translation(self, lang: str, key: str, **kwargs) -> str:
|
||||||
|
target_lang = lang
|
||||||
|
if target_lang not in TRANSLATIONS:
|
||||||
|
base = target_lang.split("-")[0]
|
||||||
|
target_lang = self.fallback_map.get(base, "en-US")
|
||||||
|
|
||||||
|
lang_dict = TRANSLATIONS.get(target_lang, TRANSLATIONS["en-US"])
|
||||||
|
text = lang_dict.get(key, TRANSLATIONS["en-US"].get(key, key))
|
||||||
|
return text.format(**kwargs) if kwargs else text
|
||||||
|
|
||||||
|
async def _get_user_context(self, __user__: Optional[dict], __event_call__: Optional[Callable] = None, __request__: Optional[Request] = None) -> dict:
|
||||||
|
user_data = __user__ if isinstance(__user__, dict) else {}
|
||||||
|
user_language = user_data.get("language", "en-US")
|
||||||
|
if __event_call__:
|
||||||
|
try:
|
||||||
|
js = "try { return (document.documentElement.lang || localStorage.getItem('locale') || navigator.language || 'en-US'); } catch (e) { return 'en-US'; }"
|
||||||
|
frontend_lang = await asyncio.wait_for(__event_call__({"type": "execute", "data": {"code": js}}), timeout=2.0)
|
||||||
|
if frontend_lang: user_language = frontend_lang
|
||||||
|
except: pass
|
||||||
|
return {"user_language": user_language}
|
||||||
|
|
||||||
|
async def {{METHOD_NAME}}(self, body: dict, __user__: Optional[dict] = None, __event_emitter__=None, __event_call__=None, __request__: Optional[Request] = None) -> dict:
|
||||||
|
if self.valves.show_status and __event_emitter__:
|
||||||
|
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
|
||||||
|
msg = self._get_translation(user_ctx["user_language"], "status_starting")
|
||||||
|
await __event_emitter__({"type": "status", "data": {"description": msg, "done": False}})
|
||||||
|
|
||||||
|
# Implement core logic here
|
||||||
|
|
||||||
|
if self.valves.show_status and __event_emitter__:
|
||||||
|
await __event_emitter__({"type": "status", "data": {"description": "Done", "done": True}})
|
||||||
|
return body
|
||||||
66
.github/skills/plugin-scaffolder/scripts/scaffold.py
vendored
Normal file
66
.github/skills/plugin-scaffolder/scripts/scaffold.py
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def scaffold(p_type, p_name, title, desc):
|
||||||
|
target_dir = f"plugins/{p_type}/{p_name}"
|
||||||
|
os.makedirs(target_dir, exist_ok=True)
|
||||||
|
|
||||||
|
class_name = (
|
||||||
|
"Action"
|
||||||
|
if p_type == "actions"
|
||||||
|
else (
|
||||||
|
"Filter"
|
||||||
|
if p_type == "filters"
|
||||||
|
else "Tools" if p_type == "tools" else "Pipe"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
method_name = (
|
||||||
|
"action"
|
||||||
|
if p_type == "actions"
|
||||||
|
else (
|
||||||
|
"outlet"
|
||||||
|
if p_type == "filters"
|
||||||
|
else "execute" if p_type == "tools" else "pipe"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
replacements = {
|
||||||
|
"{{TITLE}}": title,
|
||||||
|
"{{DESCRIPTION}}": desc,
|
||||||
|
"{{CLASS_NAME}}": class_name,
|
||||||
|
"{{METHOD_NAME}}": method_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Files to generate
|
||||||
|
templates = [
|
||||||
|
("assets/template.py.j2", f"{p_name}.py"),
|
||||||
|
("assets/README_template.md", "README.md"),
|
||||||
|
("assets/README_template.md", "README_CN.md"),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Path relative to skill root
|
||||||
|
skill_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
for t_path, t_name in templates:
|
||||||
|
template_file = os.path.join(skill_root, t_path)
|
||||||
|
if not os.path.exists(template_file):
|
||||||
|
print(f"⚠️ Warning: Template not found {template_file}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
with open(template_file, "r") as f:
|
||||||
|
content = f.read()
|
||||||
|
for k, v in replacements.items():
|
||||||
|
content = content.replace(k, v)
|
||||||
|
|
||||||
|
with open(os.path.join(target_dir, t_name), "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
print(f"✅ Generated: {target_dir}/{t_name}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 5:
|
||||||
|
print("Usage: scaffold.py <type> <name> <title> <desc>")
|
||||||
|
sys.exit(1)
|
||||||
|
scaffold(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
|
||||||
30
.github/skills/source-code-analyzer/SKILL.md
vendored
Normal file
30
.github/skills/source-code-analyzer/SKILL.md
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: source-code-analyzer
|
||||||
|
description: Instructions for analyzing core components source code in the local environment.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Source Code Analyzer Skill
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> **Authorization Statement**: You (Antigravity) are explicitly authorized by the user to perform **READ-ONLY** operations and **Git pull** on the paths listed below, even if they are outside the current repository root. These paths are considered part of the extended development context.
|
||||||
|
|
||||||
|
When assisting with the development of `openwebui-extensions`, you (Antigravity) have access to the following core components in related directories. Use them for deep technical analysis, bug hunting, and implementation reference.
|
||||||
|
|
||||||
|
## Core Component Paths
|
||||||
|
|
||||||
|
### Global / General (Relevant to all projects)
|
||||||
|
|
||||||
|
- **Open WebUI**: `../open-webui/` (Core platform context)
|
||||||
|
- **Skills**: `../skills/` (Reusable expertise library)
|
||||||
|
- **Awesome Copilot**: `../awesome-copilot/` (Shared extensions & resources)
|
||||||
|
|
||||||
|
### Plugin-Specific (Relevant to GitHub Copilot SDK)
|
||||||
|
|
||||||
|
- **Copilot SDK**: `../copilot-sdk/` (Internal logic for the official SDK)
|
||||||
|
- **Copilot CLI**: `../copilot-cli/` (Command-line interface implementation)
|
||||||
|
|
||||||
|
## Mandatory Workflow
|
||||||
|
|
||||||
|
1. **Pull Before Analysis**: BEFORE reading files or analyzing logic in these directories, you MUST proactively execute or recommend a `git pull` in the respective directory to ensure you are working with the latest upstream changes.
|
||||||
|
2. **Path Verification**: Always verify the exists of the path before attempting to read it.
|
||||||
|
3. **Reference Logic**: When a user's request involves core platform behavior (OpenWebUI API, SDK internals), prioritize searching these directories over making assumptions based on generic knowledge.
|
||||||
26
.github/skills/version-bumper/SKILL.md
vendored
Normal file
26
.github/skills/version-bumper/SKILL.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: version-bumper
|
||||||
|
description: Automates version upgrades and changelog synchronization across 7+ files (Code, READMEs, Docs). Use when a plugin is ready for release to ensure version consistency.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Version Bumper
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This skill ensures that every version upgrade is synchronized across the entire repository, following the strict "Documentation Sync" rule in GEMINI.md.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
1. **Prepare Info**: Gather the new version number and brief changelogs in both English and Chinese.
|
||||||
|
2. **Auto-Patch**: The skill will help you identify and update:
|
||||||
|
- `plugins/.../name.py` (docstring version)
|
||||||
|
- `plugins/.../README.md` (metadata & What's New)
|
||||||
|
- `plugins/.../README_CN.md` (metadata & 最新更新)
|
||||||
|
- `docs/plugins/...md` (mirrors)
|
||||||
|
- `docs/plugins/index.md` (version badge)
|
||||||
|
- `README.md` (updated date badge)
|
||||||
|
3. **Verify**: Check the diffs to ensure no formatting was broken.
|
||||||
|
|
||||||
|
## Tool Integration
|
||||||
|
Execute the bump script (draft):
|
||||||
|
```bash
|
||||||
|
python3 scripts/bump.py <version> "<message_en>" "<message_zh>"
|
||||||
|
```
|
||||||
70
.github/skills/version-bumper/scripts/bump.py
vendored
Normal file
70
.github/skills/version-bumper/scripts/bump.py
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
def patch_file(file_path, old_pattern, new_content, is_regex=False):
|
||||||
|
if not os.path.exists(file_path):
|
||||||
|
print(f"Warning: File not found: {file_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
if is_regex:
|
||||||
|
new_content_result = re.sub(old_pattern, new_content, content, flags=re.MULTILINE)
|
||||||
|
else:
|
||||||
|
new_content_result = content.replace(old_pattern, new_content)
|
||||||
|
|
||||||
|
if new_content_result != content:
|
||||||
|
with open(file_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(new_content_result)
|
||||||
|
print(f"✅ Patched: {file_path}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"ℹ️ No change needed: {file_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def bump_version(plugin_type, plugin_name, new_version, msg_en, msg_zh):
|
||||||
|
print(f"🚀 Bumping {plugin_name} ({plugin_type}) to {new_version}...")
|
||||||
|
|
||||||
|
today = datetime.now().strftime("%Y-%m-%d")
|
||||||
|
today_badge = today.replace("-", "--")
|
||||||
|
|
||||||
|
# 1. Patch Plugin Python File
|
||||||
|
py_file = f"plugins/{plugin_type}/{plugin_name}/{plugin_name}.py"
|
||||||
|
patch_file(py_file, r"version: \d+\.\d+\.\d+", f"version: {new_version}", is_regex=True)
|
||||||
|
|
||||||
|
# 2. Patch Plugin READMEs
|
||||||
|
readme_en = f"plugins/{plugin_type}/{plugin_name}/README.md"
|
||||||
|
readme_zh = f"plugins/{plugin_type}/{plugin_name}/README_CN.md"
|
||||||
|
|
||||||
|
# Update version in metadata
|
||||||
|
patch_file(readme_en, r"\*\*Version:\*\* \d+\.\d+\.\d+", f"**Version:** {new_version}", is_regex=True)
|
||||||
|
patch_file(readme_zh, r"\*\*版本:\*\* \d+\.\d+\.\d+", f"**版本:** {new_version}", is_regex=True)
|
||||||
|
|
||||||
|
# Update What's New (Assuming standard headers)
|
||||||
|
patch_file(readme_en, r"## 🔥 What's New in v.*?\n", f"## 🔥 What's New in v{new_version}\n\n* {msg_en}\n", is_regex=True)
|
||||||
|
patch_file(readme_zh, r"## 🔥 最新更新 v.*?\n", f"## 🔥 最新更新 v{new_version}\n\n* {msg_zh}\n", is_regex=True)
|
||||||
|
|
||||||
|
# 3. Patch Docs Mirrors
|
||||||
|
doc_en = f"docs/plugins/{plugin_type}/{plugin_name}.md"
|
||||||
|
doc_zh = f"docs/plugins/{plugin_type}/{plugin_name}.zh.md"
|
||||||
|
patch_file(doc_en, r"\*\*Version:\*\* \d+\.\d+\.\d+", f"**Version:** {new_version}", is_regex=True)
|
||||||
|
patch_file(doc_zh, r"\*\*版本:\*\* \d+\.\d+\.\d+", f"**版本:** {new_version}", is_regex=True)
|
||||||
|
|
||||||
|
# 4. Patch Root READMEs (Updated Date Badge)
|
||||||
|
patch_file("README.md", r"badge/202\d--\d\d--\d\d-gray", f"badge/{today_badge}-gray", is_regex=True)
|
||||||
|
patch_file("README_CN.md", r"badge/202\d--\d\d--\d\d-gray", f"badge/{today_badge}-gray", is_regex=True)
|
||||||
|
|
||||||
|
print("\n✨ All synchronization tasks completed.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 6:
|
||||||
|
print("Usage: bump.py <type> <name> <version> <msg_en> <msg_zh>")
|
||||||
|
print("Example: bump.py filters markdown_normalizer 1.2.8 'Fix bug' '修复错误'")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
bump_version(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5])
|
||||||
53
.github/workflows/community-stats.yml
vendored
53
.github/workflows/community-stats.yml
vendored
@@ -1,13 +1,15 @@
|
|||||||
# OpenWebUI 社区统计报告自动生成
|
# OpenWebUI 社区统计报告自动生成
|
||||||
# 每小时自动获取并更新社区统计数据
|
# 智能检测:只在有意义的变更时才 commit
|
||||||
|
# - 新增插件 (total_posts)
|
||||||
|
# - 插件版本变更 (version)
|
||||||
|
# - 积分增加 (total_points)
|
||||||
|
# - 粉丝增加 (followers)
|
||||||
|
|
||||||
name: Community Stats
|
name: Community Stats
|
||||||
|
|
||||||
on:
|
on:
|
||||||
# 每小时整点运行
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 * * * *'
|
- cron: '0 * * * *'
|
||||||
# 手动触发
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -32,23 +34,58 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pip install requests python-dotenv
|
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
|
||||||
|
else
|
||||||
|
echo "total_posts=0" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Generate stats report
|
- name: Generate stats report
|
||||||
env:
|
env:
|
||||||
OPENWEBUI_API_KEY: ${{ secrets.OPENWEBUI_API_KEY }}
|
OPENWEBUI_API_KEY: ${{ secrets.OPENWEBUI_API_KEY }}
|
||||||
OPENWEBUI_USER_ID: ${{ secrets.OPENWEBUI_USER_ID }}
|
OPENWEBUI_USER_ID: ${{ secrets.OPENWEBUI_USER_ID }}
|
||||||
|
GIST_TOKEN: ${{ secrets.GIST_TOKEN }}
|
||||||
|
GIST_ID: ${{ secrets.GIST_ID }}
|
||||||
run: |
|
run: |
|
||||||
python scripts/openwebui_stats.py
|
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
|
||||||
|
|
||||||
|
- name: Check for significant changes
|
||||||
id: check_changes
|
id: check_changes
|
||||||
run: |
|
run: |
|
||||||
git diff --quiet docs/community-stats.md docs/community-stats.en.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 }}"
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
- name: Commit and push changes
|
||||||
if: steps.check_changes.outputs.changed == 'true'
|
if: steps.check_changes.outputs.should_commit == 'true'
|
||||||
run: |
|
run: |
|
||||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
git config --local user.name "github-actions[bot]"
|
git config --local user.name "github-actions[bot]"
|
||||||
git add docs/community-stats.md docs/community-stats.en.md docs/community-stats.json README.md README_CN.md
|
git add docs/ README.md README_CN.md
|
||||||
git commit -m "📊 更新社区统计数据 $(date +'%Y-%m-%d')"
|
git diff --staged --quiet || git commit -m "chore: update community stats - ${{ steps.check_changes.outputs.change_reason }}"
|
||||||
git push
|
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
|
||||||
34
.github/workflows/publish_plugin.yml
vendored
Normal file
34
.github/workflows/publish_plugin.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
name: Publish Plugins to OpenWebUI Market
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'plugins/**/*.py'
|
||||||
|
- '!plugins/debug/**'
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
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: Publish Plugins
|
||||||
|
env:
|
||||||
|
OPENWEBUI_API_KEY: ${{ secrets.OPENWEBUI_API_KEY }}
|
||||||
|
run: python scripts/publish_plugin.py
|
||||||
119
.github/workflows/release.yml
vendored
119
.github/workflows/release.yml
vendored
@@ -1,8 +1,6 @@
|
|||||||
# GitHub Actions Workflow for Plugin Release
|
# Plugin Release Workflow
|
||||||
# 插件发布工作流
|
|
||||||
#
|
#
|
||||||
# This workflow automates the release process for OpenWebUI plugins.
|
# This workflow automates the release process for OpenWebUI plugins.
|
||||||
# 此工作流自动化 OpenWebUI 插件的发布流程。
|
|
||||||
#
|
#
|
||||||
# Triggers:
|
# Triggers:
|
||||||
# - Push to main branch when plugins are modified (auto-release)
|
# - Push to main branch when plugins are modified (auto-release)
|
||||||
@@ -15,7 +13,7 @@
|
|||||||
# 3. Creates a GitHub Release with plugin files as downloadable assets
|
# 3. Creates a GitHub Release with plugin files as downloadable assets
|
||||||
# 4. Supports multiple plugin updates in a single release
|
# 4. Supports multiple plugin updates in a single release
|
||||||
|
|
||||||
name: Plugin Release / 插件发布
|
name: Plugin Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
# Auto-trigger on push to main when plugins are modified
|
# Auto-trigger on push to main when plugins are modified
|
||||||
@@ -193,10 +191,21 @@ jobs:
|
|||||||
TODAY_PREFIX="v${TODAY}-"
|
TODAY_PREFIX="v${TODAY}-"
|
||||||
|
|
||||||
# Count existing releases with today's date prefix
|
# Count existing releases with today's date prefix
|
||||||
EXISTING_COUNT=$(gh release list --limit 100 | grep -c "^${TODAY_PREFIX}" || echo "0")
|
# grep -c returns 1 if count is 0, so we use || true to avoid script failure
|
||||||
|
EXISTING_COUNT=$(gh release list --limit 100 2>/dev/null | grep -c "^${TODAY_PREFIX}" || true)
|
||||||
|
|
||||||
|
# Clean up output (handle potential newlines or fallback issues)
|
||||||
|
EXISTING_COUNT=$(echo "$EXISTING_COUNT" | tr -cd '0-9')
|
||||||
|
if [ -z "$EXISTING_COUNT" ]; then EXISTING_COUNT=0; fi
|
||||||
|
|
||||||
NEXT_NUM=$((EXISTING_COUNT + 1))
|
NEXT_NUM=$((EXISTING_COUNT + 1))
|
||||||
|
|
||||||
VERSION="${TODAY_PREFIX}${NEXT_NUM}"
|
VERSION="${TODAY_PREFIX}${NEXT_NUM}"
|
||||||
|
|
||||||
|
# Final fallback to ensure VERSION is never empty
|
||||||
|
if [ -z "$VERSION" ]; then
|
||||||
|
VERSION="v$(date +'%Y.%m.%d-%H%M%S')"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
echo "Release version: $VERSION"
|
echo "Release version: $VERSION"
|
||||||
@@ -235,6 +244,52 @@ jobs:
|
|||||||
echo "=== Collected Files ==="
|
echo "=== Collected Files ==="
|
||||||
find release_plugins -name "*.py" -type f | head -20
|
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
|
- name: Debug Filenames
|
||||||
run: |
|
run: |
|
||||||
python3 -c "import sys; print(f'Filesystem encoding: {sys.getfilesystemencoding()}')"
|
python3 -c "import sys; print(f'Filesystem encoding: {sys.getfilesystemencoding()}')"
|
||||||
@@ -267,15 +322,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate release notes
|
- name: Generate release notes
|
||||||
id: 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: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
> release_notes.md
|
||||||
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
|
|
||||||
|
|
||||||
if [ -n "$TITLE" ]; then
|
if [ -n "$TITLE" ]; then
|
||||||
echo "## $TITLE" >> release_notes.md
|
echo "## $TITLE" >> release_notes.md
|
||||||
@@ -283,21 +337,21 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$DETECTED_CHANGES" ] && ! echo "$DETECTED_CHANGES" | grep -q "No changes detected"; then
|
if [ -n "$DETECTED_CHANGES" ] && ! echo "$DETECTED_CHANGES" | grep -q "No changes detected"; then
|
||||||
echo "## What's Changed / 更新内容" >> release_notes.md
|
echo "## What's Changed" >> release_notes.md
|
||||||
echo "" >> release_notes.md
|
echo "" >> release_notes.md
|
||||||
echo "$DETECTED_CHANGES" >> release_notes.md
|
echo "$DETECTED_CHANGES" >> release_notes.md
|
||||||
echo "" >> release_notes.md
|
echo "" >> release_notes.md
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$COMMITS" ]; then
|
if [ -n "$COMMITS" ]; then
|
||||||
echo "## Commits / 提交记录" >> release_notes.md
|
echo "## Commits" >> release_notes.md
|
||||||
echo "" >> release_notes.md
|
echo "" >> release_notes.md
|
||||||
echo "$COMMITS" >> release_notes.md
|
echo "$COMMITS" >> release_notes.md
|
||||||
echo "" >> release_notes.md
|
echo "" >> release_notes.md
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$NOTES" ]; then
|
if [ -n "$NOTES" ]; then
|
||||||
echo "## Additional Notes / 附加说明" >> release_notes.md
|
echo "## Additional Notes" >> release_notes.md
|
||||||
echo "" >> release_notes.md
|
echo "" >> release_notes.md
|
||||||
echo "$NOTES" >> release_notes.md
|
echo "$NOTES" >> release_notes.md
|
||||||
echo "" >> release_notes.md
|
echo "" >> release_notes.md
|
||||||
@@ -307,11 +361,11 @@ jobs:
|
|||||||
|
|
||||||
cat >> release_notes.md << 'EOF'
|
cat >> release_notes.md << 'EOF'
|
||||||
|
|
||||||
## Download / 下载
|
## Download
|
||||||
|
|
||||||
📦 **Download the updated plugin files below** / 请在下方下载更新的插件文件
|
📦 **Download the updated plugin files below**
|
||||||
|
|
||||||
### Installation / 安装
|
### Installation
|
||||||
|
|
||||||
#### From OpenWebUI Community
|
#### From OpenWebUI Community
|
||||||
1. Open OpenWebUI Admin Panel
|
1. Open OpenWebUI Admin Panel
|
||||||
@@ -319,7 +373,7 @@ jobs:
|
|||||||
3. Search for the plugin name
|
3. Search for the plugin name
|
||||||
4. Click Install
|
4. Click Install
|
||||||
|
|
||||||
#### Manual Installation / 手动安装
|
#### Manual Installation
|
||||||
1. Download the plugin file (`.py`) from the assets below
|
1. Download the plugin file (`.py`) from the assets below
|
||||||
2. Open OpenWebUI Admin Panel → Functions
|
2. Open OpenWebUI Admin Panel → Functions
|
||||||
3. Click "Create Function" → Import
|
3. Click "Create Function" → Import
|
||||||
@@ -327,20 +381,41 @@ jobs:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
📚 [Documentation / 文档](https://fu-jie.github.io/awesome-openwebui/)
|
📚 [Documentation](https://fu-jie.github.io/openwebui-extensions/)
|
||||||
🐛 [Report Issues / 报告问题](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
🐛 [Report Issues](https://github.com/Fu-Jie/openwebui-extensions/issues)
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "=== Release Notes ==="
|
echo "=== Release Notes ==="
|
||||||
cat release_notes.md
|
cat release_notes.md
|
||||||
|
|
||||||
|
- name: Create Git Tag
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
|
||||||
|
if [ -z "$VERSION" ]; then
|
||||||
|
echo "Error: Version is empty!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! git rev-parse "$VERSION" >/dev/null 2>&1; then
|
||||||
|
echo "Creating tag $VERSION"
|
||||||
|
git tag "$VERSION"
|
||||||
|
git push origin "$VERSION"
|
||||||
|
else
|
||||||
|
echo "Tag $VERSION already exists"
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Create GitHub Release
|
- name: Create GitHub Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ steps.version.outputs.version }}
|
tag_name: ${{ steps.version.outputs.version }}
|
||||||
|
target_commitish: ${{ github.sha }}
|
||||||
name: ${{ github.event.inputs.release_title || steps.version.outputs.version }}
|
name: ${{ github.event.inputs.release_title || steps.version.outputs.version }}
|
||||||
body_path: release_notes.md
|
body_path: release_notes.md
|
||||||
prerelease: ${{ github.event.inputs.prerelease || false }}
|
prerelease: ${{ github.event.inputs.prerelease || false }}
|
||||||
|
make_latest: true
|
||||||
files: |
|
files: |
|
||||||
plugin_versions.json
|
plugin_versions.json
|
||||||
env:
|
env:
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -139,3 +139,4 @@ logs/
|
|||||||
|
|
||||||
# OpenWebUI specific
|
# OpenWebUI specific
|
||||||
# Add any specific ignores for OpenWebUI plugins if needed
|
# Add any specific ignores for OpenWebUI plugins if needed
|
||||||
|
.git-worktrees/
|
||||||
|
|||||||
7
.markdownlint.json
Normal file
7
.markdownlint.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"default": true,
|
||||||
|
"MD013": false,
|
||||||
|
"MD033": false,
|
||||||
|
"MD030": false,
|
||||||
|
"MD051": false
|
||||||
|
}
|
||||||
@@ -1,87 +1,16 @@
|
|||||||
# 贡献指南 (Contributing Guide)
|
# Contributing Guide
|
||||||
|
|
||||||
感谢你对 **OpenWebUI Extras** 感兴趣!我们非常欢迎社区贡献更多的插件、提示词和创意。
|
Thank you for your interest in **OpenWebUI Extensions**!
|
||||||
|
|
||||||
## 🤝 如何贡献
|
## 🚀 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.
|
||||||
|
|
||||||
如果你有一个好用的提示词:
|
## 💡 Important
|
||||||
1. 在 `prompts/` 目录下找到合适的分类(如 `coding/`, `writing/`)。如果没有合适的,可以新建一个文件夹。
|
|
||||||
2. 创建一个新的 `.md` 或 `.json` 文件。
|
|
||||||
3. 提交 Pull Request (PR)。
|
|
||||||
|
|
||||||
### 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):
|
Thank you! 🚀
|
||||||
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)。
|
|
||||||
|
|
||||||
再次感谢你的贡献!🚀
|
|
||||||
|
|||||||
16
CONTRIBUTING_CN.md
Normal file
16
CONTRIBUTING_CN.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# 贡献指南
|
||||||
|
|
||||||
|
感谢你对 **OpenWebUI Extensions** 感兴趣!
|
||||||
|
|
||||||
|
## 🚀 贡献流程
|
||||||
|
|
||||||
|
1. **Fork** 本仓库。
|
||||||
|
2. **修改/添加** `plugins/` 目录下的插件文件。
|
||||||
|
3. **提交 PR**: 我们会尽快审核并合并。
|
||||||
|
|
||||||
|
## 💡 注意事项
|
||||||
|
|
||||||
|
- 请确保插件包含完整的元数据(title, author, version, description)。
|
||||||
|
- 如果是更新已有插件,请记得**增加版本号**(如 `0.1.0` -> `0.1.1`),这样系统会自动同步更新。
|
||||||
|
|
||||||
|
再次感谢你的贡献!🚀
|
||||||
157
GEMINI.md
Normal file
157
GEMINI.md
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# OpenWebUI Extensions — Gemini CLI Project Context
|
||||||
|
|
||||||
|
> This file is loaded automatically by Gemini CLI as project-level instructions.
|
||||||
|
> Full engineering spec: `.github/copilot-instructions.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
**openwebui-extensions** is a collection of OpenWebUI plugins authored by Fu-Jie.
|
||||||
|
|
||||||
|
Repository: `https://github.com/Fu-Jie/openwebui-extensions`
|
||||||
|
|
||||||
|
Plugin types: `actions` / `filters` / `pipes` / `pipelines` / `tools`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Non-Negotiable Rules
|
||||||
|
|
||||||
|
1. **No auto-commit.** Never run `git commit`, `git push`, or `gh pr create` unless the user says "发布" / "release" / "commit it". Default output = local file changes only.
|
||||||
|
2. **No silent failures.** All errors must surface via `__event_emitter__` notification or backend `logging`.
|
||||||
|
3. **No hardcoded model IDs.** Default to the current conversation model; let `Valves` override.
|
||||||
|
4. **Chinese responses.** Reply in Simplified Chinese for all planning, explanations, and status summaries. English only for code, commit messages, and docstrings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Plugin File Contract
|
||||||
|
|
||||||
|
Every plugin MUST be a **single-file i18n** Python module:
|
||||||
|
|
||||||
|
```text
|
||||||
|
plugins/{type}/{name}/{name}.py ← single source file, built-in i18n
|
||||||
|
plugins/{type}/{name}/README.md ← English docs
|
||||||
|
plugins/{type}/{name}/README_CN.md ← Chinese docs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docstring (required fields)
|
||||||
|
|
||||||
|
```python
|
||||||
|
"""
|
||||||
|
title: Plugin Display Name
|
||||||
|
author: Fu-Jie
|
||||||
|
author_url: https://github.com/Fu-Jie/openwebui-extensions
|
||||||
|
funding_url: https://github.com/open-webui
|
||||||
|
version: 0.1.0
|
||||||
|
description: One-line description.
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
### Required patterns
|
||||||
|
|
||||||
|
- `Valves(BaseModel)` with `UPPER_SNAKE_CASE` fields
|
||||||
|
- `_get_user_context(__user__)` — never access `__user__` directly
|
||||||
|
- `_get_chat_context(body, __metadata__)` — never infer IDs ad-hoc
|
||||||
|
- `_emit_status(emitter, msg, done)` / `_emit_notification(emitter, content, type)`
|
||||||
|
- Async I/O only — wrap sync calls with `asyncio.to_thread`
|
||||||
|
- `logging` for backend logs — no bare `print()` in production
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Antigravity Development Rules
|
||||||
|
|
||||||
|
When the user invokes antigravity mode (high-speed iteration), enforce these safeguards automatically:
|
||||||
|
|
||||||
|
| Rule | Detail |
|
||||||
|
|------|--------|
|
||||||
|
| Small reversible edits | One logical change per file per operation |
|
||||||
|
| Timeout guards | `asyncio.wait_for(..., timeout=2.0)` on all `__event_call__` JS executions |
|
||||||
|
| Path sandbox | Verify every workspace path stays inside the repo root before read/write |
|
||||||
|
| Source Sensing | Use `source-code-analyzer` skill for `../open-webui`, `../copilot-sdk` etc. `git pull` before analysis. |
|
||||||
|
| External Auth | **AUTHORIZED** to read (RO) from specified external paths (open-webui, copilot-sdk, etc.) for analysis. |
|
||||||
|
| Fallback chains | API upload → DB + local copy; never a single point of failure |
|
||||||
|
| Progressive status | `status(done=False)` at start, `status(done=True)` on end, `notification(error)` on failure |
|
||||||
|
| Validate before emit | Check `emitter is not None` before every `await emitter(...)` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Creation & Delivery Protocol (3-Step)
|
||||||
|
|
||||||
|
1. `local write` — create artifact inside workspace scope
|
||||||
|
2. `publish_file_from_workspace(filename='...')` — migrate to OpenWebUI storage (S3-compatible)
|
||||||
|
3. Return `/api/v1/files/{id}/content` download link in Markdown
|
||||||
|
|
||||||
|
Set `skip_rag=true` metadata on generated downloadable artifacts.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Copilot SDK Tool Definition (critical)
|
||||||
|
|
||||||
|
```python
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from copilot import define_tool
|
||||||
|
|
||||||
|
class MyToolParams(BaseModel):
|
||||||
|
query: str = Field(..., description="Search query")
|
||||||
|
|
||||||
|
my_tool = define_tool(
|
||||||
|
name="my_tool",
|
||||||
|
description="...",
|
||||||
|
params_type=MyToolParams, # REQUIRED — prevents empty schema hallucination
|
||||||
|
)(async_impl_fn)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Streaming Output Format (OpenWebUI 0.8.x)
|
||||||
|
|
||||||
|
- Reasoning: `<think>\n...\n</think>\n` — close BEFORE normal content or tool cards
|
||||||
|
- Tool cards: `<details type="tool_calls" id="..." name="..." arguments=""..."" result=""..."" done="true">\n<summary>Tool Executed</summary>\n</details>\n\n`
|
||||||
|
- Escape ALL `"` inside `arguments`/`result` attributes as `"`
|
||||||
|
- Status events via `__event_emitter__` — do NOT pollute the content stream with debug text
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Documentation Sync (when changing a plugin)
|
||||||
|
|
||||||
|
Must update ALL of these or the PR check fails:
|
||||||
|
|
||||||
|
1. `plugins/{type}/{name}/{name}.py` — version in docstring
|
||||||
|
2. `plugins/{type}/{name}/README.md` — version, What's New
|
||||||
|
3. `plugins/{type}/{name}/README_CN.md` — same
|
||||||
|
4. `docs/plugins/{type}/{name}.md` — mirror README
|
||||||
|
5. `docs/plugins/{type}/{name}.zh.md` — mirror README_CN
|
||||||
|
6. `docs/plugins/{type}/index.md` — version badge
|
||||||
|
7. `docs/plugins/{type}/index.zh.md` — version badge
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## i18n & Language Standards
|
||||||
|
|
||||||
|
1. **Alignment**: Keep the number of supported languages in `TRANSLATIONS` consistent with major plugins (e.g., `smart-mind-map`).
|
||||||
|
2. **Supported Languages**: en-US, zh-CN, zh-HK, zh-TW, ko-KR, ja-JP, fr-FR, de-DE, es-ES, it-IT, vi-VN, id-ID.
|
||||||
|
3. **Fallback Map**: Must include variant redirects (e.g., `es-MX` -> `es-ES`, `fr-CA` -> `fr-FR`).
|
||||||
|
4. **Tooltips**: All `description` fields in `Valves` must be **English only** to maintain clean UI.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commit Message Format
|
||||||
|
|
||||||
|
```text
|
||||||
|
type(scope): brief English description
|
||||||
|
|
||||||
|
- Key change 1
|
||||||
|
- Key change 2
|
||||||
|
```
|
||||||
|
|
||||||
|
Types: `feat` / `fix` / `docs` / `refactor` / `chore`
|
||||||
|
Scope: plugin folder name (e.g., `github-copilot-sdk`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Full Reference
|
||||||
|
|
||||||
|
`.github/copilot-instructions.md` — complete engineering specification (1000+ lines)
|
||||||
|
`.agent/workflows/plugin-development.md` — step-by-step development workflow
|
||||||
|
`.agent/rules/antigravity.md` — antigravity mode detailed rules
|
||||||
|
`docs/development/copilot-engineering-plan.md` — design baseline
|
||||||
149
README.md
149
README.md
@@ -1,4 +1,7 @@
|
|||||||
# OpenWebUI Extras
|
# OpenWebUI Extensions
|
||||||
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||||
|
[](#contributors-)
|
||||||
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||||
|
|
||||||
English | [中文](./README_CN.md)
|
English | [中文](./README_CN.md)
|
||||||
|
|
||||||
@@ -6,62 +9,123 @@ A collection of enhancements, plugins, and prompts for [OpenWebUI](https://githu
|
|||||||
|
|
||||||
<!-- STATS_START -->
|
<!-- STATS_START -->
|
||||||
## 📊 Community Stats
|
## 📊 Community Stats
|
||||||
|
>
|
||||||
> 🕐 Auto-updated: 2026-01-06 21:19
|
> 
|
||||||
|
|
||||||
| 👤 Author | 👥 Followers | ⭐ Points | 🏆 Contributions |
|
| 👤 Author | 👥 Followers | ⭐ Points | 🏆 Contributions |
|
||||||
|:---:|:---:|:---:|:---:|
|
| :---: | :---: | :---: | :---: |
|
||||||
| [Fu-Jie](https://openwebui.com/u/Fu-Jie) | **43** | **62** | **17** |
|
| [Fu-Jie](https://openwebui.com/u/Fu-Jie) |  |  |  |
|
||||||
|
|
||||||
| 📝 Posts | ⬇️ Downloads | 👁️ Views | 👍 Upvotes | 💾 Saves |
|
| 📝 Posts | ⬇️ Downloads | 👁️ Views | 👍 Upvotes | 💾 Saves |
|
||||||
|:---:|:---:|:---:|:---:|:---:|
|
| :---: | :---: | :---: | :---: | :---: |
|
||||||
| **11** | **794** | **8481** | **54** | **48** |
|
|  |  |  |  |  |
|
||||||
|
|
||||||
### 🔥 Top 5 Popular Plugins
|
### 🔥 Top 6 Popular Plugins
|
||||||
|
|
||||||
| Rank | Plugin | Downloads | Views |
|
| Rank | Plugin | Version | Downloads | Views | 📅 Updated |
|
||||||
|:---:|------|:---:|:---:|
|
| :---: | :--- | :---: | :---: | :---: | :---: |
|
||||||
| 🥇 | [Turn Any Text into Beautiful Mind Maps](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | 240 | 2133 |
|
| 🥇 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) |  |  |  |  |
|
||||||
| 🥈 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | 171 | 459 |
|
| 🥈 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) |  |  |  |  |
|
||||||
| 🥉 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | 112 | 1236 |
|
| 🥉 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) |  |  |  |  |
|
||||||
| 4️⃣ | [Flash Card ](https://openwebui.com/posts/flash_card_65a2ea8f) | 76 | 1421 |
|
| 4️⃣ | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) |  |  |  |  |
|
||||||
| 5️⃣ | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | 65 | 909 |
|
| 5️⃣ | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) |  |  |  |  |
|
||||||
|
| 6️⃣ | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) |  |  |  |  |
|
||||||
|
|
||||||
*See full stats in [Community Stats Report](./docs/community-stats.md)*
|
### 🛠️ Actively Developed Projects
|
||||||
|
|
||||||
|
| Status | Plugin | Version | Downloads | Views | 📅 Updated |
|
||||||
|
| :---: | :--- | :---: | :---: | :---: | :---: |
|
||||||
|
| 🆕 | [GitHub Copilot SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) |  |  |  |  |
|
||||||
|
|
||||||
|
### 📈 Total Downloads Trend
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
*See full stats and charts in [Community Stats Report](./docs/community-stats.md)*
|
||||||
<!-- STATS_END -->
|
<!-- 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.
|
||||||
|
|
||||||
|
#### 🌟 Featured Real-World Cases
|
||||||
|
|
||||||
|
- **[GitHub Star Forecasting](./docs/plugins/pipes/star-prediction-example.md)**: Automatically parsing CSV data, writing analysis scripts, and generating interactive growth dashboards.
|
||||||
|
- **[Video Optimization](./docs/plugins/pipes/video-processing-example.md)**: Direct control of system-level tools (FFmpeg) to accelerate and compress media with professional color optimization.
|
||||||
|
|
||||||
|
### 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
|
## 📦 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:
|
Located in the `plugins/` directory, containing Python-based enhancements:
|
||||||
|
|
||||||
#### Actions
|
### Actions
|
||||||
|
|
||||||
- **Smart Mind Map** (`smart-mind-map`): Generates interactive mind maps from text.
|
- **Smart Mind Map** (`smart-mind-map`): Generates interactive mind maps from text.
|
||||||
- **Smart Infographic** (`infographic`): Transforms text into professional infographics using AntV.
|
- **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 Excel** (`export_to_excel`): Exports chat history to Excel files.
|
||||||
- **Export to Word** (`export_to_docx`): Exports chat history to Word documents.
|
- **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.
|
- **Async Context Compression** (`async-context-compression`): Optimizes token usage via context compression.
|
||||||
- **Context Enhancement** (`context_enhancement_filter`): Enhances chat context.
|
- **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
|
- **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.
|
||||||
- **Gemini Manifold** (`gemini_mainfold`): Pipeline for Gemini model integration.
|
|
||||||
|
### Pipelines
|
||||||
|
|
||||||
#### Pipelines
|
|
||||||
- **MoE Prompt Refiner** (`moe_prompt_refiner`): Refines prompts for Mixture of Experts (MoE) summary requests to generate high-quality comprehensive reports.
|
- **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.
|
System Prompts are managed in the `docs/prompts/` directory:
|
||||||
- **Marketing**: Marketing and copywriting prompts.
|
|
||||||
|
- **[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
|
## 📖 Documentation
|
||||||
|
|
||||||
@@ -98,8 +162,35 @@ This project is a collection of resources and does not require a Python environm
|
|||||||
### Contributing
|
### Contributing
|
||||||
|
|
||||||
If you have great prompts or plugins to share:
|
If you have great prompts or plugins to share:
|
||||||
|
|
||||||
1. Fork this repository.
|
1. Fork this repository.
|
||||||
2. Add your files to the appropriate `prompts/` or `plugins/` directory.
|
2. Add your files to the appropriate `prompts/` or `plugins/` directory.
|
||||||
3. Submit a Pull Request.
|
3. Submit a Pull Request.
|
||||||
|
|
||||||
[Contributing](./CONTRIBUTING.md)
|
[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/openwebui-extensions/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/openwebui-extensions/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/openwebui-extensions/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!
|
||||||
|
|||||||
156
README_CN.md
156
README_CN.md
@@ -1,4 +1,4 @@
|
|||||||
# OpenWebUI Extras
|
# OpenWebUI Extensions
|
||||||
|
|
||||||
[English](./README.md) | 中文
|
[English](./README.md) | 中文
|
||||||
|
|
||||||
@@ -6,66 +6,130 @@ OpenWebUI 增强功能集合。包含个人开发与收集的插件、提示词
|
|||||||
|
|
||||||
<!-- STATS_START -->
|
<!-- STATS_START -->
|
||||||
## 📊 社区统计
|
## 📊 社区统计
|
||||||
|
>
|
||||||
> 🕐 自动更新于 2026-01-06 21:19
|
> 
|
||||||
|
|
||||||
| 👤 作者 | 👥 粉丝 | ⭐ 积分 | 🏆 贡献 |
|
| 👤 作者 | 👥 粉丝 | ⭐ 积分 | 🏆 贡献 |
|
||||||
|:---:|:---:|:---:|:---:|
|
| :---: | :---: | :---: | :---: |
|
||||||
| [Fu-Jie](https://openwebui.com/u/Fu-Jie) | **43** | **62** | **17** |
|
| [Fu-Jie](https://openwebui.com/u/Fu-Jie) |  |  |  |
|
||||||
|
|
||||||
| 📝 发布 | ⬇️ 下载 | 👁️ 浏览 | 👍 点赞 | 💾 收藏 |
|
| 📝 发布 | ⬇️ 下载 | 👁️ 浏览 | 👍 点赞 | 💾 收藏 |
|
||||||
|:---:|:---:|:---:|:---:|:---:|
|
| :---: | :---: | :---: | :---: | :---: |
|
||||||
| **11** | **794** | **8481** | **54** | **48** |
|
|  |  |  |  |  |
|
||||||
|
|
||||||
### 🔥 热门插件 Top 5
|
### 🔥 热门插件 Top 6
|
||||||
|
|
||||||
| 排名 | 插件 | 下载 | 浏览 |
|
| 排名 | 插件 | 版本 | 下载 | 浏览 | 📅 更新 |
|
||||||
|:---:|------|:---:|:---:|
|
| :---: | :--- | :---: | :---: | :---: | :---: |
|
||||||
| 🥇 | [Turn Any Text into Beautiful Mind Maps](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | 240 | 2133 |
|
| 🥇 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) |  |  |  |  |
|
||||||
| 🥈 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | 171 | 459 |
|
| 🥈 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) |  |  |  |  |
|
||||||
| 🥉 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | 112 | 1236 |
|
| 🥉 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) |  |  |  |  |
|
||||||
| 4️⃣ | [Flash Card ](https://openwebui.com/posts/flash_card_65a2ea8f) | 76 | 1421 |
|
| 4️⃣ | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) |  |  |  |  |
|
||||||
| 5️⃣ | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | 65 | 909 |
|
| 5️⃣ | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) |  |  |  |  |
|
||||||
|
| 6️⃣ | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) |  |  |  |  |
|
||||||
|
|
||||||
*完整统计请查看 [社区统计报告](./docs/community-stats.md)*
|
### 🛠️ 积极开发的项目
|
||||||
|
|
||||||
|
| 状态 | 插件 | 版本 | 下载 | 浏览 | 📅 更新 |
|
||||||
|
| :---: | :--- | :---: | :---: | :---: | :---: |
|
||||||
|
| 🆕 | [GitHub Copilot SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) |  |  |  |  |
|
||||||
|
|
||||||
|
### 📈 总下载量累计趋势
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
*完整统计与趋势图请查看 [社区统计报告](./docs/community-stats.zh.md)*
|
||||||
<!-- STATS_END -->
|
<!-- 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。
|
||||||
|
|
||||||
|
#### 🌟 核心实战案例
|
||||||
|
|
||||||
|
- **[GitHub Star 增长预测](./docs/plugins/pipes/star-prediction-example.zh.md)**:自动解析 CSV 数据,编写 Python 分析脚本并生成动态增长看板。
|
||||||
|
- **[视频高质量转换与压缩](./docs/plugins/pipes/video-processing-example.zh.md)**:直接调用系统级 FFmpeg 工具,实现录屏的加速、缩放及双阶段色彩优化。
|
||||||
|
|
||||||
|
### 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 编写的功能增强插件:
|
位于 `plugins/` 目录,包含各类 Python 编写的功能增强插件:
|
||||||
|
|
||||||
#### Actions (交互增强)
|
### Actions (交互增强)
|
||||||
|
|
||||||
- **Smart Mind Map** (`smart-mind-map`): 智能分析文本并生成交互式思维导图。
|
- **Smart Mind Map** (`smart-mind-map`): 智能分析文本并生成交互式思维导图。
|
||||||
- **Smart Infographic** (`infographic`): 基于 AntV 的智能信息图生成工具。
|
- **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 Excel** (`export_to_excel`): 将对话内容导出为 Excel 文件。
|
||||||
- **Export to Word** (`export_to_docx`): 将对话内容导出为 Word 文档。
|
- **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 使用。
|
- **Async Context Compression** (`async-context-compression`): 异步上下文压缩,优化 Token 使用。
|
||||||
- **Context Enhancement** (`context_enhancement_filter`): 上下文增强过滤器。
|
- **Context Enhancement** (`context_enhancement_filter`): 上下文增强过滤器。
|
||||||
- **Gemini Manifold Companion** (`gemini_manifold_companion`): Gemini Manifold 配套增强。
|
- **Folder Memory** (`folder-memory`): 自动从对话中提取项目规则并注入到文件夹系统提示词中。
|
||||||
|
- **Markdown Normalizer** (`markdown_normalizer`): 修复 LLM 输出中常见的 Markdown 格式问题。
|
||||||
|
|
||||||
#### Pipes (模型管道)
|
### Pipes (模型管道)
|
||||||
- **Gemini Manifold** (`gemini_mainfold`): 集成 Gemini 模型的管道。
|
|
||||||
|
- **GitHub Copilot SDK** (`github-copilot-sdk`): GitHub Copilot SDK 官方集成。支持动态模型、多轮对话、流式输出、图片输入及无限会话。
|
||||||
|
|
||||||
|
### Pipelines (工作流管道)
|
||||||
|
|
||||||
#### Pipelines (工作流管道)
|
|
||||||
- **MoE Prompt Refiner** (`moe_prompt_refiner`): 优化多模型 (MoE) 汇总请求的提示词,生成高质量的综合报告。
|
- **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**: 编程辅助类提示词。
|
位于 `docs/prompts/` 目录,包含精心调优的提示词集合:
|
||||||
- **Marketing**: 营销文案类提示词。
|
|
||||||
|
|
||||||
每个提示词都独立保存为 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/` 目录:
|
||||||
|
|
||||||
- **[插件开发权威指南](./docs/zh/plugin_development_guide.md)** - 整合了入门教程、核心 SDK 详解及最佳实践的系统化指南。 ⭐
|
- **[插件开发权威指南](./docs/zh/plugin_development_guide.md)** - 整合了入门教程、核心 SDK 详解及最佳实践的系统化指南。 ⭐
|
||||||
@@ -73,35 +137,11 @@ OpenWebUI 增强功能集合。包含个人开发与收集的插件、提示词
|
|||||||
|
|
||||||
更多示例请查看 `docs/examples/` 目录。
|
更多示例请查看 `docs/examples/` 目录。
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<!-- markdownlint-enable MD033 -->
|
||||||
|
|
||||||
## 🚀 快速开始
|
## 🚀 快速开始
|
||||||
|
|
||||||
本项目是一个资源集合,无需安装 Python 环境。你只需要下载对应的文件并导入到你的 OpenWebUI 实例中即可。
|
本项目是一个资源集合,无需安装 Python 环境。你只需要下载对应的文件并导入到你的 OpenWebUI 实例中即可。
|
||||||
|
|
||||||
### 使用提示词 (Prompts)
|
[贡献指南](./CONTRIBUTING_CN.md) | [更新日志](./CHANGELOG.md)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|||||||
0
docs/.!55042!.DS_Store
Normal file
0
docs/.!55042!.DS_Store
Normal file
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:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **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 [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) 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: [OpenWebUI Extensions Issues](https://github.com/Fu-Jie/openwebui-extensions/issues)
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
See the full history on GitHub: [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions)
|
||||||
7
docs/badges/downloads.json
Normal file
7
docs/badges/downloads.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"label": "downloads",
|
||||||
|
"message": "5.6k",
|
||||||
|
"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": "270",
|
||||||
|
"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": "23",
|
||||||
|
"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": "285",
|
||||||
|
"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": "241",
|
||||||
|
"color": "brightgreen"
|
||||||
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# 📊 OpenWebUI Community Stats Report
|
|
||||||
|
|
||||||
> 📅 Updated: 2026-01-06 21:19
|
|
||||||
|
|
||||||
## 📈 Overview
|
|
||||||
|
|
||||||
| Metric | Value |
|
|
||||||
|------|------|
|
|
||||||
| 📝 Total Posts | 11 |
|
|
||||||
| ⬇️ Total Downloads | 794 |
|
|
||||||
| 👁️ Total Views | 8481 |
|
|
||||||
| 👍 Total Upvotes | 54 |
|
|
||||||
| 💾 Total Saves | 48 |
|
|
||||||
| 💬 Total Comments | 13 |
|
|
||||||
|
|
||||||
## 📂 By Type
|
|
||||||
|
|
||||||
- **action**: 9
|
|
||||||
- **filter**: 2
|
|
||||||
|
|
||||||
## 📋 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.8.2 | 240 | 2133 | 10 | 15 | 2026-01-03 |
|
|
||||||
| 2 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | action | 0.3.6 | 171 | 459 | 3 | 3 | 2026-01-03 |
|
|
||||||
| 3 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | filter | 1.1.0 | 112 | 1236 | 5 | 9 | 2025-12-31 |
|
|
||||||
| 4 | [Flash Card ](https://openwebui.com/posts/flash_card_65a2ea8f) | action | 0.2.4 | 76 | 1421 | 8 | 5 | 2026-01-03 |
|
|
||||||
| 5 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | action | 1.3.2 | 65 | 909 | 6 | 8 | 2026-01-03 |
|
|
||||||
| 6 | [Export to Word (Enhanced Formatting)](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | action | 0.4.0 | 51 | 504 | 5 | 4 | 2026-01-05 |
|
|
||||||
| 7 | [智能信息图](https://openwebui.com/posts/智能信息图_e04a48ff) | action | 1.3.1 | 33 | 398 | 3 | 0 | 2025-12-29 |
|
|
||||||
| 8 | [导出为 Word-支持公式、流程图、表格和代码块](https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0) | action | 0.4.1 | 15 | 752 | 7 | 1 | 2026-01-05 |
|
|
||||||
| 9 | [智能生成交互式思维导图,帮助用户可视化知识](https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b) | action | 0.8.0 | 14 | 249 | 2 | 1 | 2025-12-31 |
|
|
||||||
| 10 | [闪记卡生成插件](https://openwebui.com/posts/闪记卡生成插件_4a31eac3) | action | 0.2.2 | 12 | 309 | 3 | 1 | 2025-12-31 |
|
|
||||||
| 11 | [异步上下文压缩](https://openwebui.com/posts/异步上下文压缩_5c0617cb) | filter | 1.1.0 | 5 | 111 | 2 | 1 | 2025-12-31 |
|
|
||||||
@@ -1,191 +1,387 @@
|
|||||||
{
|
{
|
||||||
"total_posts": 11,
|
"total_posts": 23,
|
||||||
"total_downloads": 794,
|
"total_downloads": 5629,
|
||||||
"total_views": 8481,
|
"total_views": 61059,
|
||||||
"total_upvotes": 54,
|
"total_upvotes": 241,
|
||||||
"total_downvotes": 1,
|
"total_downvotes": 3,
|
||||||
"total_saves": 48,
|
"total_saves": 321,
|
||||||
"total_comments": 13,
|
"total_comments": 55,
|
||||||
"by_type": {
|
"by_type": {
|
||||||
"action": 9,
|
"action": 12,
|
||||||
"filter": 2
|
"post": 4,
|
||||||
|
"pipe": 1,
|
||||||
|
"filter": 4,
|
||||||
|
"prompt": 1,
|
||||||
|
"review": 1
|
||||||
},
|
},
|
||||||
"posts": [
|
"posts": [
|
||||||
{
|
{
|
||||||
"title": "Turn Any Text into Beautiful Mind Maps",
|
"title": "Smart Mind Map",
|
||||||
"slug": "turn_any_text_into_beautiful_mind_maps_3094c59a",
|
"slug": "turn_any_text_into_beautiful_mind_maps_3094c59a",
|
||||||
"type": "action",
|
"type": "action",
|
||||||
"version": "0.8.2",
|
"version": "1.0.0",
|
||||||
"author": "Fu-Jie",
|
"author": "Fu-Jie",
|
||||||
"description": "Intelligently analyzes text content and generates interactive mind maps to help users structure and visualize knowledge.",
|
"description": "Intelligently analyzes text content and generates interactive mind maps to help users structure and visualize knowledge.",
|
||||||
"downloads": 240,
|
"downloads": 1207,
|
||||||
"views": 2133,
|
"views": 10434,
|
||||||
"upvotes": 10,
|
"upvotes": 22,
|
||||||
"saves": 15,
|
"saves": 59,
|
||||||
"comments": 8,
|
"comments": 13,
|
||||||
"created_at": "2025-12-30",
|
"created_at": "2025-12-30",
|
||||||
"updated_at": "2026-01-03",
|
"updated_at": "2026-02-22",
|
||||||
"url": "https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a"
|
"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": 171,
|
|
||||||
"views": 459,
|
|
||||||
"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": 112,
|
|
||||||
"views": 1236,
|
|
||||||
"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": 76,
|
|
||||||
"views": 1421,
|
|
||||||
"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",
|
"title": "Smart Infographic",
|
||||||
"slug": "smart_infographic_ad6f0c7f",
|
"slug": "smart_infographic_ad6f0c7f",
|
||||||
"type": "action",
|
"type": "action",
|
||||||
"version": "1.3.2",
|
"version": "1.5.0",
|
||||||
"author": "jeff",
|
"author": "Fu-Jie",
|
||||||
"description": "AI-powered infographic generator based on AntV Infographic. Supports professional templates, auto-icon matching, and SVG/PNG downloads.",
|
"description": "AI-powered infographic generator based on AntV Infographic. Supports professional templates, auto-icon matching, and SVG/PNG downloads.",
|
||||||
"downloads": 65,
|
"downloads": 966,
|
||||||
"views": 909,
|
"views": 9501,
|
||||||
"upvotes": 6,
|
"upvotes": 24,
|
||||||
"saves": 8,
|
"saves": 39,
|
||||||
"comments": 2,
|
"comments": 10,
|
||||||
"created_at": "2025-12-28",
|
"created_at": "2025-12-28",
|
||||||
"updated_at": "2026-01-03",
|
"updated_at": "2026-02-13",
|
||||||
"url": "https://openwebui.com/posts/smart_infographic_ad6f0c7f"
|
"url": "https://openwebui.com/posts/smart_infographic_ad6f0c7f"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Export to Word (Enhanced Formatting)",
|
"title": "Markdown Normalizer",
|
||||||
|
"slug": "markdown_normalizer_baaa8732",
|
||||||
|
"type": "filter",
|
||||||
|
"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": 513,
|
||||||
|
"views": 6087,
|
||||||
|
"upvotes": 17,
|
||||||
|
"saves": 36,
|
||||||
|
"comments": 5,
|
||||||
|
"created_at": "2026-01-12",
|
||||||
|
"updated_at": "2026-02-13",
|
||||||
|
"url": "https://openwebui.com/posts/markdown_normalizer_baaa8732"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Export to Word Enhanced",
|
||||||
"slug": "export_to_word_enhanced_formatting_fca6a315",
|
"slug": "export_to_word_enhanced_formatting_fca6a315",
|
||||||
"type": "action",
|
"type": "action",
|
||||||
"version": "0.4.0",
|
"version": "0.4.4",
|
||||||
"author": "Fu-Jie",
|
"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).",
|
"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": 51,
|
"downloads": 511,
|
||||||
"views": 504,
|
"views": 4090,
|
||||||
"upvotes": 5,
|
"upvotes": 15,
|
||||||
"saves": 4,
|
"saves": 29,
|
||||||
"comments": 0,
|
"comments": 5,
|
||||||
"created_at": "2026-01-03",
|
"created_at": "2026-01-03",
|
||||||
"updated_at": "2026-01-05",
|
"updated_at": "2026-02-13",
|
||||||
"url": "https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315"
|
"url": "https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Async Context Compression",
|
||||||
|
"slug": "async_context_compression_b1655bc8",
|
||||||
|
"type": "filter",
|
||||||
|
"version": "1.3.0",
|
||||||
|
"author": "Fu-Jie",
|
||||||
|
"description": "Reduces token consumption in long conversations while maintaining coherence through intelligent summarization and message compression.",
|
||||||
|
"downloads": 486,
|
||||||
|
"views": 4865,
|
||||||
|
"upvotes": 15,
|
||||||
|
"saves": 40,
|
||||||
|
"comments": 0,
|
||||||
|
"created_at": "2025-11-08",
|
||||||
|
"updated_at": "2026-02-21",
|
||||||
|
"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": 445,
|
||||||
|
"views": 2377,
|
||||||
|
"upvotes": 9,
|
||||||
|
"saves": 7,
|
||||||
|
"comments": 0,
|
||||||
|
"created_at": "2025-05-30",
|
||||||
|
"updated_at": "2026-02-13",
|
||||||
|
"url": "https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "AI Task Instruction Generator",
|
||||||
|
"slug": "ai_task_instruction_generator_9bab8b37",
|
||||||
|
"type": "prompt",
|
||||||
|
"version": "",
|
||||||
|
"author": "",
|
||||||
|
"description": "",
|
||||||
|
"downloads": 381,
|
||||||
|
"views": 4636,
|
||||||
|
"upvotes": 9,
|
||||||
|
"saves": 11,
|
||||||
|
"comments": 0,
|
||||||
|
"created_at": "2026-01-28",
|
||||||
|
"updated_at": "2026-01-28",
|
||||||
|
"url": "https://openwebui.com/posts/ai_task_instruction_generator_9bab8b37"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": 264,
|
||||||
|
"views": 3924,
|
||||||
|
"upvotes": 13,
|
||||||
|
"saves": 18,
|
||||||
|
"comments": 2,
|
||||||
|
"created_at": "2025-12-30",
|
||||||
|
"updated_at": "2026-02-13",
|
||||||
|
"url": "https://openwebui.com/posts/flash_card_65a2ea8f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "GitHub Copilot Official SDK Pipe",
|
||||||
|
"slug": "github_copilot_official_sdk_pipe_ce96f7b4",
|
||||||
|
"type": "pipe",
|
||||||
|
"version": "0.7.0",
|
||||||
|
"author": "Fu-Jie",
|
||||||
|
"description": "Integrate GitHub Copilot SDK. Supports dynamic models, multi-turn conversation, streaming, multimodal input, infinite sessions, and frontend debug logging.",
|
||||||
|
"downloads": 205,
|
||||||
|
"views": 3522,
|
||||||
|
"upvotes": 14,
|
||||||
|
"saves": 9,
|
||||||
|
"comments": 6,
|
||||||
|
"created_at": "2026-01-26",
|
||||||
|
"updated_at": "2026-02-22",
|
||||||
|
"url": "https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": 185,
|
||||||
|
"views": 1519,
|
||||||
|
"upvotes": 6,
|
||||||
|
"saves": 13,
|
||||||
|
"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": 145,
|
||||||
|
"views": 2507,
|
||||||
|
"upvotes": 14,
|
||||||
|
"saves": 7,
|
||||||
|
"comments": 4,
|
||||||
|
"created_at": "2026-01-04",
|
||||||
|
"updated_at": "2026-02-13",
|
||||||
|
"url": "https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": 92,
|
||||||
|
"views": 1721,
|
||||||
|
"upvotes": 7,
|
||||||
|
"saves": 11,
|
||||||
|
"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": "智能信息图",
|
"title": "智能信息图",
|
||||||
"slug": "智能信息图_e04a48ff",
|
"slug": "智能信息图_e04a48ff",
|
||||||
"type": "action",
|
"type": "action",
|
||||||
"version": "1.3.1",
|
"version": "1.5.0",
|
||||||
"author": "jeff",
|
"author": "Fu-Jie",
|
||||||
"description": "基于 AntV Infographic 的智能信息图生成插件。支持多种专业模板,自动图标匹配,并提供 SVG/PNG 下载功能。",
|
"description": "基于 AntV Infographic 的智能信息图生成插件。支持多种专业模板,自动图标匹配,并提供 SVG/PNG 下载功能。",
|
||||||
"downloads": 33,
|
"downloads": 62,
|
||||||
"views": 398,
|
"views": 1235,
|
||||||
"upvotes": 3,
|
"upvotes": 10,
|
||||||
"saves": 0,
|
"saves": 1,
|
||||||
"comments": 0,
|
"comments": 0,
|
||||||
"created_at": "2025-12-28",
|
"created_at": "2025-12-28",
|
||||||
"updated_at": "2025-12-29",
|
"updated_at": "2026-02-13",
|
||||||
"url": "https://openwebui.com/posts/智能信息图_e04a48ff"
|
"url": "https://openwebui.com/posts/智能信息图_e04a48ff"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "导出为 Word-支持公式、流程图、表格和代码块",
|
"title": "思维导图",
|
||||||
"slug": "导出为_word_支持公式流程图表格和代码块_8a6306c0",
|
|
||||||
"type": "action",
|
|
||||||
"version": "0.4.1",
|
|
||||||
"author": "Fu-Jie",
|
|
||||||
"description": "将当前对话内容从 Markdown 转换并导出为 Word (.docx) 文件,支持中英文无乱码。",
|
|
||||||
"downloads": 15,
|
|
||||||
"views": 752,
|
|
||||||
"upvotes": 7,
|
|
||||||
"saves": 1,
|
|
||||||
"comments": 1,
|
|
||||||
"created_at": "2026-01-04",
|
|
||||||
"updated_at": "2026-01-05",
|
|
||||||
"url": "https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "智能生成交互式思维导图,帮助用户可视化知识",
|
|
||||||
"slug": "智能生成交互式思维导图帮助用户可视化知识_8d4b097b",
|
"slug": "智能生成交互式思维导图帮助用户可视化知识_8d4b097b",
|
||||||
"type": "action",
|
"type": "action",
|
||||||
"version": "0.8.0",
|
"version": "0.9.2",
|
||||||
"author": "",
|
"author": "Fu-Jie",
|
||||||
"description": "智能分析文本内容,生成交互式思维导图,帮助用户结构化和可视化知识。",
|
"description": "智能分析文本内容,生成交互式思维导图,帮助用户结构化和可视化知识。",
|
||||||
"downloads": 14,
|
"downloads": 41,
|
||||||
"views": 249,
|
"views": 658,
|
||||||
"upvotes": 2,
|
"upvotes": 6,
|
||||||
"saves": 1,
|
"saves": 2,
|
||||||
"comments": 0,
|
"comments": 0,
|
||||||
"created_at": "2025-12-31",
|
"created_at": "2025-12-31",
|
||||||
"updated_at": "2025-12-31",
|
"updated_at": "2026-02-13",
|
||||||
"url": "https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b"
|
"url": "https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"title": "闪记卡生成插件",
|
|
||||||
"slug": "闪记卡生成插件_4a31eac3",
|
|
||||||
"type": "action",
|
|
||||||
"version": "0.2.2",
|
|
||||||
"author": "Fu-Jie",
|
|
||||||
"description": "快速将文本提炼为精美的学习记忆卡片,支持核心要点提取与分类。",
|
|
||||||
"downloads": 12,
|
|
||||||
"views": 309,
|
|
||||||
"upvotes": 3,
|
|
||||||
"saves": 1,
|
|
||||||
"comments": 0,
|
|
||||||
"created_at": "2025-12-30",
|
|
||||||
"updated_at": "2025-12-31",
|
|
||||||
"url": "https://openwebui.com/posts/闪记卡生成插件_4a31eac3"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "异步上下文压缩",
|
"title": "异步上下文压缩",
|
||||||
"slug": "异步上下文压缩_5c0617cb",
|
"slug": "异步上下文压缩_5c0617cb",
|
||||||
"type": "filter",
|
"type": "action",
|
||||||
"version": "1.1.0",
|
"version": "1.2.2",
|
||||||
"author": "Fu-Jie",
|
"author": "Fu-Jie",
|
||||||
"description": "在 LLM 响应完成后进行上下文摘要和压缩",
|
"description": "通过智能摘要和消息压缩,降低长对话的 token 消耗,同时保持对话连贯性。",
|
||||||
"downloads": 5,
|
"downloads": 36,
|
||||||
"views": 111,
|
"views": 748,
|
||||||
"upvotes": 2,
|
"upvotes": 7,
|
||||||
"saves": 1,
|
"saves": 5,
|
||||||
"comments": 0,
|
"comments": 0,
|
||||||
"created_at": "2025-11-08",
|
"created_at": "2025-11-08",
|
||||||
"updated_at": "2025-12-31",
|
"updated_at": "2026-02-13",
|
||||||
"url": "https://openwebui.com/posts/异步上下文压缩_5c0617cb"
|
"url": "https://openwebui.com/posts/异步上下文压缩_5c0617cb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": 35,
|
||||||
|
"views": 1907,
|
||||||
|
"upvotes": 3,
|
||||||
|
"saves": 0,
|
||||||
|
"comments": 0,
|
||||||
|
"created_at": "2026-02-09",
|
||||||
|
"updated_at": "2026-02-13",
|
||||||
|
"url": "https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "闪记卡 (Flash Card)",
|
||||||
|
"slug": "闪记卡生成插件_4a31eac3",
|
||||||
|
"type": "action",
|
||||||
|
"version": "0.2.4",
|
||||||
|
"author": "Fu-Jie",
|
||||||
|
"description": "快速将文本提炼为精美的学习记忆卡片,支持核心要点提取与分类。",
|
||||||
|
"downloads": 30,
|
||||||
|
"views": 783,
|
||||||
|
"upvotes": 8,
|
||||||
|
"saves": 1,
|
||||||
|
"comments": 0,
|
||||||
|
"created_at": "2025-12-30",
|
||||||
|
"updated_at": "2026-02-13",
|
||||||
|
"url": "https://openwebui.com/posts/闪记卡生成插件_4a31eac3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "精读",
|
||||||
|
"slug": "精读_99830b0f",
|
||||||
|
"type": "action",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "Fu-Jie",
|
||||||
|
"description": "全方位的思维透镜 —— 从背景全景到逻辑脉络,从深度洞察到行动路径。",
|
||||||
|
"downloads": 25,
|
||||||
|
"views": 545,
|
||||||
|
"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 Pipe v0.7.0: Native Tool UI & Zero-Config CLI 🛠️",
|
||||||
|
"slug": "github_copilot_sdk_pipe_v070_native_tool_ui_zero_c_4af38131",
|
||||||
|
"type": "post",
|
||||||
|
"version": "",
|
||||||
|
"author": "",
|
||||||
|
"description": "",
|
||||||
|
"downloads": 0,
|
||||||
|
"views": 24,
|
||||||
|
"upvotes": 2,
|
||||||
|
"saves": 0,
|
||||||
|
"comments": 0,
|
||||||
|
"created_at": "2026-02-22",
|
||||||
|
"updated_at": "2026-02-22",
|
||||||
|
"url": "https://openwebui.com/posts/github_copilot_sdk_pipe_v070_native_tool_ui_zero_c_4af38131"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "🚀 GitHub Copilot SDK Pipe: AI That Executes, Not Just Talks",
|
||||||
|
"slug": "github_copilot_sdk_for_openwebui_elevate_your_ai_t_a140f293",
|
||||||
|
"type": "post",
|
||||||
|
"version": "",
|
||||||
|
"author": "",
|
||||||
|
"description": "",
|
||||||
|
"downloads": 0,
|
||||||
|
"views": 2108,
|
||||||
|
"upvotes": 7,
|
||||||
|
"saves": 3,
|
||||||
|
"comments": 0,
|
||||||
|
"created_at": "2026-02-10",
|
||||||
|
"updated_at": "2026-02-10",
|
||||||
|
"url": "https://openwebui.com/posts/github_copilot_sdk_for_openwebui_elevate_your_ai_t_a140f293"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "🚀 Open WebUI Prompt Plus: AI-Powered Prompt Manager",
|
||||||
|
"slug": "open_webui_prompt_plus_ai_powered_prompt_manager_s_15fa060e",
|
||||||
|
"type": "post",
|
||||||
|
"version": "",
|
||||||
|
"author": "",
|
||||||
|
"description": "",
|
||||||
|
"downloads": 0,
|
||||||
|
"views": 1767,
|
||||||
|
"upvotes": 12,
|
||||||
|
"saves": 19,
|
||||||
|
"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": "review",
|
||||||
|
"version": "",
|
||||||
|
"author": "",
|
||||||
|
"description": "",
|
||||||
|
"downloads": 0,
|
||||||
|
"views": 222,
|
||||||
|
"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": "post",
|
||||||
|
"version": "",
|
||||||
|
"author": "",
|
||||||
|
"description": "",
|
||||||
|
"downloads": 0,
|
||||||
|
"views": 1475,
|
||||||
|
"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": {
|
"user": {
|
||||||
@@ -193,11 +389,11 @@
|
|||||||
"name": "Fu-Jie",
|
"name": "Fu-Jie",
|
||||||
"profile_url": "https://openwebui.com/u/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",
|
"profile_image": "https://community.s3.openwebui.com/uploads/users/b15d1348-4347-42b4-b815-e053342d6cb0/profile_d9510745-4bd4-4f8f-a997-4a21847d9300.webp",
|
||||||
"followers": 43,
|
"followers": 270,
|
||||||
"following": 2,
|
"following": 5,
|
||||||
"total_points": 62,
|
"total_points": 285,
|
||||||
"post_points": 53,
|
"post_points": 238,
|
||||||
"comment_points": 9,
|
"comment_points": 47,
|
||||||
"contributions": 17
|
"contributions": 51
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,35 +1,61 @@
|
|||||||
# 📊 OpenWebUI 社区统计报告
|
# 📊 OpenWebUI Community Stats Report
|
||||||
|
|
||||||
> 📅 更新时间: 2026-01-06 21:19
|
> 
|
||||||
|
|
||||||
## 📈 总览
|
### 📈 Total Downloads Trend
|
||||||
|

|
||||||
|
|
||||||
| 指标 | 数值 |
|
> *Blue: Downloads | Purple: Views (Real-time dynamic)*
|
||||||
|
|
||||||
|
### 📂 Content Distribution
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## 📈 Overview
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 📝 发布数量 | 11 |
|
| 📝 Total Posts |  |
|
||||||
| ⬇️ 总下载量 | 794 |
|
| ⬇️ Total Downloads |  |
|
||||||
| 👁️ 总浏览量 | 8481 |
|
| 👁️ Total Views |  |
|
||||||
| 👍 总点赞数 | 54 |
|
| 👍 Total Upvotes |  |
|
||||||
| 💾 总收藏数 | 48 |
|
| 💾 Total Saves |  |
|
||||||
| 💬 总评论数 | 13 |
|
| ⭐ Author Points |  |
|
||||||
|
| 👥 Followers |  |
|
||||||
|
|
||||||
## 📂 按类型分类
|
## 📂 By Type
|
||||||
|
|
||||||
- **action**: 9
|
- 
|
||||||
- **filter**: 2
|
- 
|
||||||
|
- 
|
||||||
|
- 
|
||||||
|
- 
|
||||||
|
- 
|
||||||
|
|
||||||
## 📋 发布列表
|
## 📋 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.8.2 | 240 | 2133 | 10 | 15 | 2026-01-03 |
|
| 1 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | action |  |  |  |  |  | 2026-02-22 |
|
||||||
| 2 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | action | 0.3.6 | 171 | 459 | 3 | 3 | 2026-01-03 |
|
| 2 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
| 3 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | filter | 1.1.0 | 112 | 1236 | 5 | 9 | 2025-12-31 |
|
| 3 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | filter |  |  |  |  |  | 2026-02-13 |
|
||||||
| 4 | [Flash Card ](https://openwebui.com/posts/flash_card_65a2ea8f) | action | 0.2.4 | 76 | 1421 | 8 | 5 | 2026-01-03 |
|
| 4 | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
| 5 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | action | 1.3.2 | 65 | 909 | 6 | 8 | 2026-01-03 |
|
| 5 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | filter |  |  |  |  |  | 2026-02-21 |
|
||||||
| 6 | [Export to Word (Enhanced Formatting)](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | action | 0.4.0 | 51 | 504 | 5 | 4 | 2026-01-05 |
|
| 6 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
| 7 | [智能信息图](https://openwebui.com/posts/智能信息图_e04a48ff) | action | 1.3.1 | 33 | 398 | 3 | 0 | 2025-12-29 |
|
| 7 | [AI Task Instruction Generator](https://openwebui.com/posts/ai_task_instruction_generator_9bab8b37) | prompt |  |  |  |  |  | 2026-01-28 |
|
||||||
| 8 | [导出为 Word-支持公式、流程图、表格和代码块](https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0) | action | 0.4.1 | 15 | 752 | 7 | 1 | 2026-01-05 |
|
| 8 | [Flash Card](https://openwebui.com/posts/flash_card_65a2ea8f) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
| 9 | [智能生成交互式思维导图,帮助用户可视化知识](https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b) | action | 0.8.0 | 14 | 249 | 2 | 1 | 2025-12-31 |
|
| 9 | [GitHub Copilot Official SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) | pipe |  |  |  |  |  | 2026-02-22 |
|
||||||
| 10 | [闪记卡生成插件](https://openwebui.com/posts/闪记卡生成插件_4a31eac3) | action | 0.2.2 | 12 | 309 | 3 | 1 | 2025-12-31 |
|
| 10 | [Deep Dive](https://openwebui.com/posts/deep_dive_c0b846e4) | action |  |  |  |  |  | 2026-01-08 |
|
||||||
| 11 | [异步上下文压缩](https://openwebui.com/posts/异步上下文压缩_5c0617cb) | filter | 1.1.0 | 5 | 111 | 2 | 1 | 2025-12-31 |
|
| 11 | [导出为Word增强版](https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 12 | [📂 Folder Memory – Auto-Evolving Project Context](https://openwebui.com/posts/folder_memory_auto_evolving_project_context_4a9875b2) | filter |  |  |  |  |  | 2026-01-20 |
|
||||||
|
| 13 | [智能信息图](https://openwebui.com/posts/智能信息图_e04a48ff) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 14 | [思维导图](https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 15 | [异步上下文压缩](https://openwebui.com/posts/异步上下文压缩_5c0617cb) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 16 | [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee) | filter |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 17 | [闪记卡 (Flash Card)](https://openwebui.com/posts/闪记卡生成插件_4a31eac3) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 18 | [精读](https://openwebui.com/posts/精读_99830b0f) | action |  |  |  |  |  | 2026-01-08 |
|
||||||
|
| 19 | [🚀 GitHub Copilot SDK Pipe v0.7.0: Native Tool UI & Zero-Config CLI 🛠️](https://openwebui.com/posts/github_copilot_sdk_pipe_v070_native_tool_ui_zero_c_4af38131) | post |  |  |  |  |  | 2026-02-22 |
|
||||||
|
| 20 | [🚀 GitHub Copilot SDK Pipe: AI That Executes, Not Just Talks](https://openwebui.com/posts/github_copilot_sdk_for_openwebui_elevate_your_ai_t_a140f293) | post |  |  |  |  |  | 2026-02-10 |
|
||||||
|
| 21 | [🚀 Open WebUI Prompt Plus: AI-Powered Prompt Manager](https://openwebui.com/posts/open_webui_prompt_plus_ai_powered_prompt_manager_s_15fa060e) | post |  |  |  |  |  | 2026-01-28 |
|
||||||
|
| 22 | [Review of Claude Haiku 4.5](https://openwebui.com/posts/review_of_claude_haiku_45_41b0db39) | review |  |  |  |  |  | 2026-01-14 |
|
||||||
|
| 23 | [ 🛠️ Debug Open WebUI Plugins in Your Browser](https://openwebui.com/posts/debug_open_webui_plugins_in_your_browser_81bf7960) | post |  |  |  |  |  | 2026-01-10 |
|
||||||
|
|||||||
61
docs/community-stats.zh.md
Normal file
61
docs/community-stats.zh.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# 📊 OpenWebUI 社区统计报告
|
||||||
|
|
||||||
|
> 
|
||||||
|
|
||||||
|
### 📈 总下载量累计趋势
|
||||||
|

|
||||||
|
|
||||||
|
> *蓝色: 总下载量 | 紫色: 总浏览量 (实时动态生成)*
|
||||||
|
|
||||||
|
### 📂 内容分类占比 (Distribution)
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## 📈 总览
|
||||||
|
|
||||||
|
| 指标 | 数值 |
|
||||||
|
|------|------|
|
||||||
|
| 📝 发布数量 |  |
|
||||||
|
| ⬇️ 总下载量 |  |
|
||||||
|
| 👁️ 总浏览量 |  |
|
||||||
|
| 👍 总点赞数 |  |
|
||||||
|
| 💾 总收藏数 |  |
|
||||||
|
| ⭐ 作者总积分 |  |
|
||||||
|
| 👥 粉丝数量 |  |
|
||||||
|
|
||||||
|
## 📂 按类型分类
|
||||||
|
|
||||||
|
- 
|
||||||
|
- 
|
||||||
|
- 
|
||||||
|
- 
|
||||||
|
- 
|
||||||
|
- 
|
||||||
|
|
||||||
|
## 📋 发布列表
|
||||||
|
|
||||||
|
| 排名 | 标题 | 类型 | 版本 | 下载 | 浏览 | 点赞 | 收藏 | 更新日期 |
|
||||||
|
|:---:|------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
|
| 1 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | action |  |  |  |  |  | 2026-02-22 |
|
||||||
|
| 2 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 3 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | filter |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 4 | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 5 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | filter |  |  |  |  |  | 2026-02-21 |
|
||||||
|
| 6 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 7 | [AI Task Instruction Generator](https://openwebui.com/posts/ai_task_instruction_generator_9bab8b37) | prompt |  |  |  |  |  | 2026-01-28 |
|
||||||
|
| 8 | [Flash Card](https://openwebui.com/posts/flash_card_65a2ea8f) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 9 | [GitHub Copilot Official SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) | pipe |  |  |  |  |  | 2026-02-22 |
|
||||||
|
| 10 | [Deep Dive](https://openwebui.com/posts/deep_dive_c0b846e4) | action |  |  |  |  |  | 2026-01-08 |
|
||||||
|
| 11 | [导出为Word增强版](https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 12 | [📂 Folder Memory – Auto-Evolving Project Context](https://openwebui.com/posts/folder_memory_auto_evolving_project_context_4a9875b2) | filter |  |  |  |  |  | 2026-01-20 |
|
||||||
|
| 13 | [智能信息图](https://openwebui.com/posts/智能信息图_e04a48ff) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 14 | [思维导图](https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 15 | [异步上下文压缩](https://openwebui.com/posts/异步上下文压缩_5c0617cb) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 16 | [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee) | filter |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 17 | [闪记卡 (Flash Card)](https://openwebui.com/posts/闪记卡生成插件_4a31eac3) | action |  |  |  |  |  | 2026-02-13 |
|
||||||
|
| 18 | [精读](https://openwebui.com/posts/精读_99830b0f) | action |  |  |  |  |  | 2026-01-08 |
|
||||||
|
| 19 | [🚀 GitHub Copilot SDK Pipe v0.7.0: Native Tool UI & Zero-Config CLI 🛠️](https://openwebui.com/posts/github_copilot_sdk_pipe_v070_native_tool_ui_zero_c_4af38131) | post |  |  |  |  |  | 2026-02-22 |
|
||||||
|
| 20 | [🚀 GitHub Copilot SDK Pipe: AI That Executes, Not Just Talks](https://openwebui.com/posts/github_copilot_sdk_for_openwebui_elevate_your_ai_t_a140f293) | post |  |  |  |  |  | 2026-02-10 |
|
||||||
|
| 21 | [🚀 Open WebUI Prompt Plus: AI-Powered Prompt Manager](https://openwebui.com/posts/open_webui_prompt_plus_ai_powered_prompt_manager_s_15fa060e) | post |  |  |  |  |  | 2026-01-28 |
|
||||||
|
| 22 | [Review of Claude Haiku 4.5](https://openwebui.com/posts/review_of_claude_haiku_45_41b0db39) | review |  |  |  |  |  | 2026-01-14 |
|
||||||
|
| 23 | [ 🛠️ Debug Open WebUI Plugins in Your Browser](https://openwebui.com/posts/debug_open_webui_plugins_in_your_browser_81bf7960) | post |  |  |  |  |  | 2026-01-10 |
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Contributing Guide
|
# Contributing Guide
|
||||||
|
|
||||||
Thank you for your interest in contributing to **OpenWebUI Extras**! We welcome contributions of plugins, prompts, documentation, and more.
|
Thank you for your interest in contributing to **OpenWebUI Extensions**! We welcome contributions of plugins, prompts, documentation, and more.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -181,6 +181,6 @@ By contributing, you agree that your contributions will be licensed under the sa
|
|||||||
|
|
||||||
## 🙏 Thank You!
|
## 🙏 Thank You!
|
||||||
|
|
||||||
Every contribution, no matter how small, helps make OpenWebUI Extras better for everyone. Thank you for being part of our community!
|
Every contribution, no matter how small, helps make OpenWebUI Extensions better for everyone. Thank you for being part of our community!
|
||||||
|
|
||||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/awesome-openwebui){ .md-button .md-button--primary }
|
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/openwebui-extensions){ .md-button .md-button--primary }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 贡献指南
|
# 贡献指南
|
||||||
|
|
||||||
感谢你对 **OpenWebUI Extras** 的兴趣!我们欢迎各种形式的贡献,包括插件、提示词、文档等。
|
感谢你对 **OpenWebUI Extensions** 的兴趣!我们欢迎各种形式的贡献,包括插件、提示词、文档等。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -181,6 +181,6 @@ Update: 插件开发指南添加新示例
|
|||||||
|
|
||||||
## 🙏 感谢
|
## 🙏 感谢
|
||||||
|
|
||||||
每一份贡献,无论大小,都有助于让 OpenWebUI Extras 变得更好。感谢你成为我们社区的一员!
|
每一份贡献,无论大小,都有助于让 OpenWebUI Extensions 变得更好。感谢你成为我们社区的一员!
|
||||||
|
|
||||||
[:fontawesome-brands-github: 在 GitHub 上查看](https://github.com/Fu-Jie/awesome-openwebui){ .md-button .md-button--primary }
|
[:fontawesome-brands-github: 在 GitHub 上查看](https://github.com/Fu-Jie/openwebui-extensions){ .md-button .md-button--primary }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 贡献指南
|
# 贡献指南
|
||||||
|
|
||||||
感谢你对 **OpenWebUI Extras** 的兴趣!我们欢迎各种形式的贡献,包括插件、提示词、文档等。
|
感谢你对 **OpenWebUI Extensions** 的兴趣!我们欢迎各种形式的贡献,包括插件、提示词、文档等。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -181,6 +181,6 @@ Update: 插件开发指南添加新示例
|
|||||||
|
|
||||||
## 🙏 感谢
|
## 🙏 感谢
|
||||||
|
|
||||||
每一份贡献,无论大小,都有助于让 OpenWebUI Extras 变得更好。感谢你成为我们社区的一员!
|
每一份贡献,无论大小,都有助于让 OpenWebUI Extensions 变得更好。感谢你成为我们社区的一员!
|
||||||
|
|
||||||
[:fontawesome-brands-github: 在 GitHub 上查看](https://github.com/Fu-Jie/awesome-openwebui){ .md-button .md-button--primary }
|
[:fontawesome-brands-github: 在 GitHub 上查看](https://github.com/Fu-Jie/openwebui-extensions){ .md-button .md-button--primary }
|
||||||
|
|||||||
154
docs/development/copilot-engineering-plan.md
Normal file
154
docs/development/copilot-engineering-plan.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# Copilot Engineering Configuration Plan
|
||||||
|
|
||||||
|
> This document defines production-grade engineering configuration for plugin development with GitHub Copilot, Gemini CLI, and antigravity development mode.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Goals
|
||||||
|
|
||||||
|
- Standardize plugin engineering workflow for AI-assisted development.
|
||||||
|
- Support dual assistant stack:
|
||||||
|
- GitHub Copilot (primary in-editor agent)
|
||||||
|
- Gemini CLI (secondary external execution/research lane)
|
||||||
|
- Introduce antigravity development mode for safer, reversible, high-velocity iteration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Scope
|
||||||
|
|
||||||
|
This plan applies to:
|
||||||
|
|
||||||
|
- Plugin development (`actions`, `filters`, `pipes`, `pipelines`, `tools`)
|
||||||
|
- Documentation synchronization
|
||||||
|
- File generation/delivery workflow in OpenWebUI
|
||||||
|
- Streaming/tool-call rendering compatibility
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Engineering Configuration (Copilot-centered)
|
||||||
|
|
||||||
|
### 3.1 Source of truth
|
||||||
|
|
||||||
|
- Primary standard file: `.github/copilot-instructions.md`
|
||||||
|
- Agent workflow file: `.agent/workflows/plugin-development.md`
|
||||||
|
- Runtime guidance docs:
|
||||||
|
- `docs/development/plugin-guide.md`
|
||||||
|
- `docs/development/plugin-guide.zh.md`
|
||||||
|
|
||||||
|
### 3.2 Required development contract
|
||||||
|
|
||||||
|
- Single-file i18n plugin source.
|
||||||
|
- Bilingual README (`README.md` + `README_CN.md`).
|
||||||
|
- Safe context extraction (`_get_user_context`, `_get_chat_context`).
|
||||||
|
- Structured event handling (`status`, `notification`, `execute`).
|
||||||
|
- No silent failures; logging + user-visible status.
|
||||||
|
|
||||||
|
### 3.3 Tool definition contract (Copilot SDK)
|
||||||
|
|
||||||
|
- Define tool params explicitly with `pydantic.BaseModel`.
|
||||||
|
- Use `params_type` in tool registration.
|
||||||
|
- Preserve defaults by avoiding forced null overrides.
|
||||||
|
- Keep tool names normalized and deterministic.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Gemini CLI Compatibility Profile
|
||||||
|
|
||||||
|
Gemini CLI is treated as a parallel capability channel, not a replacement.
|
||||||
|
|
||||||
|
### 4.1 Usage boundary
|
||||||
|
|
||||||
|
- Use for:
|
||||||
|
- rapid drafts
|
||||||
|
- secondary reasoning
|
||||||
|
- cross-checking migration plans
|
||||||
|
- Do not bypass repository conventions or plugin contracts.
|
||||||
|
|
||||||
|
### 4.2 Output normalization
|
||||||
|
|
||||||
|
All Gemini CLI outputs must be normalized before merge:
|
||||||
|
|
||||||
|
- Match repository style and naming rules.
|
||||||
|
- Preserve OpenWebUI plugin signatures and context methods.
|
||||||
|
- Convert speculative outputs into explicit, testable implementation points.
|
||||||
|
|
||||||
|
### 4.3 Conflict policy
|
||||||
|
|
||||||
|
When Copilot and Gemini suggestions differ:
|
||||||
|
|
||||||
|
1. Prefer repository standard compliance.
|
||||||
|
2. Prefer safer fallback behavior.
|
||||||
|
3. Prefer lower integration risk.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Antigravity Development Mode
|
||||||
|
|
||||||
|
Antigravity mode means high-speed delivery with strict reversibility.
|
||||||
|
|
||||||
|
### 5.1 Core principles
|
||||||
|
|
||||||
|
- Small, isolated edits
|
||||||
|
- Deterministic interfaces
|
||||||
|
- Multi-level fallback paths
|
||||||
|
- Roll-forward and rollback both feasible
|
||||||
|
|
||||||
|
### 5.2 Required patterns
|
||||||
|
|
||||||
|
- Timeout guards on frontend execution calls.
|
||||||
|
- Path sandbox validation for all workspace file operations.
|
||||||
|
- Dual-channel upload fallback (API first, local/DB fallback).
|
||||||
|
- Progressive status reporting for long-running tasks.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. File Creation & Delivery Standard
|
||||||
|
|
||||||
|
### 6.1 Create files in controlled workspace
|
||||||
|
|
||||||
|
- Write artifacts in current workspace scope.
|
||||||
|
- Never use paths outside workspace boundary for deliverables.
|
||||||
|
|
||||||
|
### 6.2 Publish protocol
|
||||||
|
|
||||||
|
Use 3-step delivery:
|
||||||
|
|
||||||
|
1. local write
|
||||||
|
2. publish from workspace
|
||||||
|
3. present `/api/v1/files/{id}/content` link
|
||||||
|
|
||||||
|
### 6.3 Metadata policy
|
||||||
|
|
||||||
|
- Set `skip_rag=true` for generated downloadable artifacts where applicable.
|
||||||
|
- Keep filename generation deterministic (`chat_title -> markdown_title -> user+date`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Plugin Development Norms for Multi-Agent Stack
|
||||||
|
|
||||||
|
- Compatible with GitHub Copilot and Gemini CLI under same coding contract.
|
||||||
|
- Keep streaming compatible with OpenWebUI native blocks (`<think>`, `<details type="tool_calls">`).
|
||||||
|
- Escape tool card attributes safely (`"`) for parser stability.
|
||||||
|
- Preserve async non-blocking behavior.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Documentation Sync Rule
|
||||||
|
|
||||||
|
Any meaningful plugin engineering change must sync:
|
||||||
|
|
||||||
|
1. plugin code
|
||||||
|
2. plugin bilingual README
|
||||||
|
3. docs plugin detail pages (EN/ZH)
|
||||||
|
4. docs plugin indexes (EN/ZH)
|
||||||
|
5. root README update badge/date when release is prepared
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Acceptance Checklist
|
||||||
|
|
||||||
|
- Copilot config and workflow references are valid.
|
||||||
|
- Gemini CLI outputs can be merged without violating conventions.
|
||||||
|
- Antigravity safety mechanisms are present.
|
||||||
|
- File creation and publication flow is reproducible.
|
||||||
|
- Streaming/tool-card output format remains OpenWebUI-compatible.
|
||||||
149
docs/development/copilot-engineering-plan.zh.md
Normal file
149
docs/development/copilot-engineering-plan.zh.md
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
# Copilot 工程化配置设计
|
||||||
|
|
||||||
|
> 本文档定义面向插件开发的工程化配置方案:支持 GitHub Copilot、兼容 Gemini CLI,并引入“反重力开发”模式。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 目标
|
||||||
|
|
||||||
|
- 建立统一、可落地的 AI 协同开发标准。
|
||||||
|
- 支持双通道助手体系:
|
||||||
|
- GitHub Copilot(编辑器内主通道)
|
||||||
|
- Gemini CLI(外部补充通道)
|
||||||
|
- 在高迭代速度下保障可回滚、可审计、可发布。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 适用范围
|
||||||
|
|
||||||
|
适用于以下类型开发:
|
||||||
|
|
||||||
|
- 插件代码(`actions` / `filters` / `pipes` / `pipelines` / `tools`)
|
||||||
|
- 文档同步与发布准备
|
||||||
|
- OpenWebUI 文件创建与交付流程
|
||||||
|
- 流式输出与工具卡片兼容性
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Copilot 工程化主配置
|
||||||
|
|
||||||
|
### 3.1 规范来源
|
||||||
|
|
||||||
|
- 主规范:`.github/copilot-instructions.md`
|
||||||
|
- 工作流:`.agent/workflows/plugin-development.md`
|
||||||
|
- 运行时开发指南:
|
||||||
|
- `docs/development/plugin-guide.md`
|
||||||
|
- `docs/development/plugin-guide.zh.md`
|
||||||
|
|
||||||
|
### 3.2 强制开发契约
|
||||||
|
|
||||||
|
- 单文件 i18n 插件源码。
|
||||||
|
- README 双语(`README.md` + `README_CN.md`)。
|
||||||
|
- 上下文统一入口(`_get_user_context`、`_get_chat_context`)。
|
||||||
|
- 事件标准化(`status`、`notification`、`execute`)。
|
||||||
|
- 禁止静默失败(用户可见状态 + 后端日志)。
|
||||||
|
|
||||||
|
### 3.3 Copilot SDK 工具契约
|
||||||
|
|
||||||
|
- 参数必须 `pydantic.BaseModel` 显式定义。
|
||||||
|
- 工具注册必须声明 `params_type`。
|
||||||
|
- 保留默认值语义,避免把未传参数强制覆盖为 `null`。
|
||||||
|
- 工具名需可预测、可规范化。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Gemini CLI 兼容配置
|
||||||
|
|
||||||
|
Gemini CLI 作为补充通道,而不是替代主规范。
|
||||||
|
|
||||||
|
### 4.1 使用边界
|
||||||
|
|
||||||
|
- 适合:草案生成、方案对照、迁移校验。
|
||||||
|
- 不得绕过仓库规范与插件契约。
|
||||||
|
|
||||||
|
### 4.2 输出归一化
|
||||||
|
|
||||||
|
Gemini CLI 输出合入前必须完成:
|
||||||
|
|
||||||
|
- 命名与结构对齐仓库约束。
|
||||||
|
- 保留 OpenWebUI 插件标准签名与上下文方法。
|
||||||
|
- 将“建议性文字”转换为可执行、可验证实现点。
|
||||||
|
|
||||||
|
### 4.3 冲突决策
|
||||||
|
|
||||||
|
Copilot 与 Gemini 建议冲突时按优先级处理:
|
||||||
|
|
||||||
|
1. 规范一致性优先
|
||||||
|
2. 安全回退优先
|
||||||
|
3. 低集成风险优先
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 反重力开发(Antigravity)模式
|
||||||
|
|
||||||
|
反重力开发 = 高速迭代 + 强回退能力。
|
||||||
|
|
||||||
|
### 5.1 核心原则
|
||||||
|
|
||||||
|
- 小步、隔离、可逆变更
|
||||||
|
- 接口稳定、行为可预测
|
||||||
|
- 多级回退链路
|
||||||
|
- 支持前滚与回滚
|
||||||
|
|
||||||
|
### 5.2 强制模式
|
||||||
|
|
||||||
|
- 前端执行调用必须设置超时保护。
|
||||||
|
- 工作区文件操作必须做路径沙箱校验。
|
||||||
|
- 上传链路采用 API 优先 + 本地/DB 回退。
|
||||||
|
- 长任务必须分阶段状态反馈。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 文件创建与交付标准
|
||||||
|
|
||||||
|
### 6.1 创建范围
|
||||||
|
|
||||||
|
- 交付文件仅在受控 workspace 内创建。
|
||||||
|
- 禁止将交付产物写到 workspace 边界之外。
|
||||||
|
|
||||||
|
### 6.2 三步交付协议
|
||||||
|
|
||||||
|
1. 本地写入
|
||||||
|
2. 从 workspace 发布
|
||||||
|
3. 返回并展示 `/api/v1/files/{id}/content`
|
||||||
|
|
||||||
|
### 6.3 元数据策略
|
||||||
|
|
||||||
|
- 需要绕过检索时为产物标记 `skip_rag=true`。
|
||||||
|
- 文件名采用确定性策略:`chat_title -> markdown_title -> user+date`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 多助手并行下的插件开发规范
|
||||||
|
|
||||||
|
- 在统一契约下同时兼容 GitHub Copilot 与 Gemini CLI。
|
||||||
|
- 流式输出保持 OpenWebUI 原生兼容(`<think>`、`<details type="tool_calls">`)。
|
||||||
|
- 工具卡片属性严格转义(`"`)保证解析稳定。
|
||||||
|
- 全链路保持异步非阻塞。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 文档同步规则
|
||||||
|
|
||||||
|
插件工程化变更发生时,至少同步:
|
||||||
|
|
||||||
|
1. 插件代码
|
||||||
|
2. 插件双语 README
|
||||||
|
3. docs 详情页(EN/ZH)
|
||||||
|
4. docs 索引页(EN/ZH)
|
||||||
|
5. 发布准备阶段同步根 README 日期徽章
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 验收清单
|
||||||
|
|
||||||
|
- Copilot 配置与工作流引用有效。
|
||||||
|
- Gemini CLI 输出可无缝合入且不破坏规范。
|
||||||
|
- 反重力安全机制完整。
|
||||||
|
- 文件创建/发布流程可复现。
|
||||||
|
- 流式与工具卡片格式保持 OpenWebUI 兼容。
|
||||||
126
docs/development/copilot-sdk-tool-filtering.md
Normal file
126
docs/development/copilot-sdk-tool-filtering.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# GitHub Copilot SDK Tool Filtering Logic Documentation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The tool filtering logic ensures that changes made in the **OpenWebUI admin panel take effect on the very next chat message** — no restart or cache flush required. The design balances three goals: administrator control, user autonomy, and built-in feature availability.
|
||||||
|
|
||||||
|
## Priority Hierarchy
|
||||||
|
|
||||||
|
Filtering is applied top-to-bottom. A higher layer can fully block a lower one:
|
||||||
|
|
||||||
|
| Priority | Layer | Controls |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 (Highest) | **Plugin Valve toggles** | `ENABLE_OPENWEBUI_TOOLS`, `ENABLE_MCP_SERVER`, `ENABLE_OPENAPI_SERVER` — category master switches |
|
||||||
|
| 2 | **Admin backend server toggle** | Per-server `config.enable` in OpenWebUI Connections panel — blocks specific servers |
|
||||||
|
| 3 (Lowest) | **User Chat menu selection** | `tool_ids` from the chat UI — selects which enabled items to use |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Decision Logic (Flowchart)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[New message arrives] --> V{Plugin Valve enabled\nfor this category?}
|
||||||
|
V -- No --> VX[Drop all tools in this category]
|
||||||
|
V -- Yes --> B{Admin backend:\nconfig.enable = True?}
|
||||||
|
B -- No --> C[Skip this server]
|
||||||
|
B -- Yes --> F{Built-in or Custom/Server tool?}
|
||||||
|
|
||||||
|
F -- Built-in --> G{Any builtin: IDs\nselected in Chat?}
|
||||||
|
G -- Yes --> H[Enable ONLY the mapped categories\nunselected categories set to False]
|
||||||
|
G -- No --> I[Enable default 4 categories:\nweb_search, image_generation,\ncode_interpreter, memory]
|
||||||
|
|
||||||
|
F -- Custom / Server --> J{Any custom IDs\nselected in Chat?}
|
||||||
|
J -- Yes --> K[Load ONLY the selected IDs]
|
||||||
|
J -- No --> L[Load ALL admin-enabled custom tools]
|
||||||
|
|
||||||
|
H & I & K & L --> M[Always inject: publish_file_from_workspace]
|
||||||
|
M --> N[Start / Resume Copilot SDK Session]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Scenario Reference Table
|
||||||
|
|
||||||
|
| User selects in Chat | Custom tools loaded | Built-in tools loaded |
|
||||||
|
|---|---|---|
|
||||||
|
| Nothing | All admin-enabled | Default 4 (search, image, code, memory) |
|
||||||
|
| Only `builtin:xxx` | All admin-enabled (unaffected) | Only selected categories |
|
||||||
|
| Only custom/server IDs | Only selected IDs | Default 4 |
|
||||||
|
| Both builtin and custom | Only selected custom IDs | Only selected builtin categories |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Technical Implementation Details
|
||||||
|
|
||||||
|
### 1. Real-time Admin Sync (No Caching)
|
||||||
|
|
||||||
|
Every request re-reads `TOOL_SERVER_CONNECTIONS.value` live. There is **no in-memory cache** for server state. As a result:
|
||||||
|
|
||||||
|
- Enable a server in the admin panel → it appears on the **next message**.
|
||||||
|
- Disable a server → it is dropped on the **next message**.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Read live on every request — no cache
|
||||||
|
if hasattr(TOOL_SERVER_CONNECTIONS, "value"):
|
||||||
|
raw_connections = TOOL_SERVER_CONNECTIONS.value
|
||||||
|
|
||||||
|
for server in connections:
|
||||||
|
is_enabled = config.get("enable", False) # checked per-server, per-request
|
||||||
|
if not is_enabled:
|
||||||
|
continue # skipped immediately — hard block
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Built-in Tool Category Mapping
|
||||||
|
|
||||||
|
The plugin maps individual `builtin:func_name` IDs to one of 9 categories understood by `get_builtin_tools`. When the user selects specific builtins, **only those categories are enabled; unselected categories are explicitly set to `False`** (not omitted) to prevent OpenWebUI's default-`True` fallback:
|
||||||
|
|
||||||
|
```python
|
||||||
|
if builtin_selected:
|
||||||
|
# Strict mode: set every category explicitly
|
||||||
|
for cat in all_builtin_categories: # all 9
|
||||||
|
is_enabled = cat in enabled_categories # only selected ones are True
|
||||||
|
builtin_tools_meta[cat] = is_enabled # unselected are explicitly False
|
||||||
|
else:
|
||||||
|
# Default mode: only the 4 core categories
|
||||||
|
default_builtin_categories = [
|
||||||
|
"web_search", "image_generation", "code_interpreter", "memory"
|
||||||
|
]
|
||||||
|
for cat in all_builtin_categories:
|
||||||
|
builtin_tools_meta[cat] = cat in default_builtin_categories
|
||||||
|
features.update(req_features) # merge backend feature flags
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Custom Tool "Select-All" Fallback
|
||||||
|
|
||||||
|
The whitelist is activated **only when the user explicitly selects custom/server IDs**. Selecting only `builtin:` IDs does not trigger the custom whitelist, so all admin-enabled servers remain accessible:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# custom_selected contains only non-builtin: IDs
|
||||||
|
if custom_selected:
|
||||||
|
# Whitelist active: keep only what the user picked
|
||||||
|
tool_ids = [tid for tid in available_ids if tid in custom_selected]
|
||||||
|
else:
|
||||||
|
# No custom selection: load everything enabled in backend
|
||||||
|
tool_ids = available_ids
|
||||||
|
```
|
||||||
|
|
||||||
|
The same rule applies to MCP servers in `_parse_mcp_servers`.
|
||||||
|
|
||||||
|
### 4. Admin Backend Strict Validation
|
||||||
|
|
||||||
|
Applied uniformly to both OpenAPI and MCP servers, handling both dict and Pydantic object shapes:
|
||||||
|
|
||||||
|
```python
|
||||||
|
is_enabled = False
|
||||||
|
config = server.get("config", {}) if isinstance(server, dict) else getattr(server, "config", {})
|
||||||
|
is_enabled = config.get("enable", False) if isinstance(config, dict) else getattr(config, "enable", False)
|
||||||
|
|
||||||
|
if not is_enabled:
|
||||||
|
continue # hard skip — no user or valve setting can override this
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
- **SDK Internal Tools**: `available_tools = None` is passed to the session so SDK-native capabilities (`read_file`, `shell`, etc.) are never accidentally blocked by the custom tool list.
|
||||||
|
- **Persistent Tool**: `publish_file_from_workspace` is always injected after all filtering — it is required for the file delivery workflow regardless of any toggle.
|
||||||
206
docs/development/copilot-sdk-tool-filtering.zh.md
Normal file
206
docs/development/copilot-sdk-tool-filtering.zh.md
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
# GitHub Copilot SDK 工具过滤逻辑开发文档
|
||||||
|
|
||||||
|
## 核心需求
|
||||||
|
|
||||||
|
**管理员在后台修改工具服务的启用状态后,用户发送下一条消息时立即生效,无需重启服务或刷新缓存。**
|
||||||
|
|
||||||
|
过滤逻辑同时兼顾两个目标:管理员管控权、用户自主选择权。内置工具则完全独立,仅由模型配置决定。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 工具分类说明
|
||||||
|
|
||||||
|
本文档涉及两类完全独立的工具,权限控制机制不同:
|
||||||
|
|
||||||
|
| 工具类型 | 说明 | 权限控制来源 |
|
||||||
|
|---|---|---|
|
||||||
|
| **内置工具(Builtin Tools)** | OpenWebUI 原生能力:时间、知识库、记忆、联网搜索、图像生成、代码解释器等 | 仅由模型配置 `meta.builtinTools` 决定,**与 Chat 前端选择无关** |
|
||||||
|
| **OpenWebUI Tools** | 用户安装的 Python 工具插件 | 插件 Valve + Chat 工具选择(tool_ids) |
|
||||||
|
| **工具服务器(OpenAPI / MCP)** | 外部 OpenAPI Server、MCP Server | 插件 Valve + 管理员 `config.enable` + `function_name_filter_list` + Chat 工具选择(tool_ids) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 内置工具权限控制(模型配置驱动,与前端无关)
|
||||||
|
|
||||||
|
内置工具**完全由模型配置决定**,Chat 界面的工具选择对其没有任何影响。
|
||||||
|
|
||||||
|
### 模型 `meta.builtinTools` 字段
|
||||||
|
|
||||||
|
在模型(自定义模型或基础模型)的 `meta` 字段中有一个可选的 `builtinTools` 对象:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"capabilities": { "builtin_tools": true },
|
||||||
|
"builtinTools": {
|
||||||
|
"time": false,
|
||||||
|
"memory": true,
|
||||||
|
"chats": true,
|
||||||
|
"notes": true,
|
||||||
|
"knowledge": true,
|
||||||
|
"channels": true,
|
||||||
|
"web_search": true,
|
||||||
|
"image_generation": true,
|
||||||
|
"code_interpreter": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**判定规则(源码 `utils/tools.py`):**
|
||||||
|
|
||||||
|
```python
|
||||||
|
def is_builtin_tool_enabled(category: str) -> bool:
|
||||||
|
builtin_tools = model.get("info", {}).get("meta", {}).get("builtinTools", {})
|
||||||
|
return builtin_tools.get(category, True) # 缺省时默认 True
|
||||||
|
```
|
||||||
|
|
||||||
|
- `builtinTools` 字段**不存在** → 所有内置工具类别默认全部开启
|
||||||
|
- `builtinTools` 字段**存在** → 仅值为 `true` 的类别开启,其余关闭
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OpenWebUI Tools 和工具服务器的优先级层级
|
||||||
|
|
||||||
|
这两类工具的过滤从上到下依次执行,**受 Chat 前端选择影响**:
|
||||||
|
|
||||||
|
| 优先级 | 层级 | 控制范围 |
|
||||||
|
|---|---|---|
|
||||||
|
| 1(最高) | **插件 Valve 开关** | `ENABLE_OPENWEBUI_TOOLS` / `ENABLE_MCP_SERVER` / `ENABLE_OPENAPI_SERVER` — 类别总开关 |
|
||||||
|
| 2 | **管理员后端服务器开关** | OpenWebUI 连接面板中每个服务器的 `config.enable` — 控制具体服务器是否启用 |
|
||||||
|
| 3 | **管理员函数名过滤列表** | 工具服务器 `config.function_name_filter_list` — 限制该服务器对外暴露的函数列表(逗号分隔) |
|
||||||
|
| 4(最低) | **用户 Chat 工具选择** | Chat 界面的 `tool_ids` — 在已启用范围内进一步筛选:未选则全选,有选则仅选中的 |
|
||||||
|
|
||||||
|
### 管理员函数名过滤列表说明
|
||||||
|
|
||||||
|
OpenWebUI 后台的工具服务器连接配置中支持设置 `function_name_filter_list` 字段(逗号分隔的函数名),用于限制该服务器对外暴露的函数。源码逻辑:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# utils/tools.py
|
||||||
|
function_name_filter_list = tool_server_connection.get("config", {}).get("function_name_filter_list", "")
|
||||||
|
if isinstance(function_name_filter_list, str):
|
||||||
|
function_name_filter_list = function_name_filter_list.split(",")
|
||||||
|
|
||||||
|
for spec in specs:
|
||||||
|
function_name = spec["name"]
|
||||||
|
if function_name_filter_list:
|
||||||
|
if not is_string_allowed(function_name, function_name_filter_list):
|
||||||
|
continue # 不在列表中的函数被跳过
|
||||||
|
```
|
||||||
|
|
||||||
|
- 列表**为空** → 该服务器所有函数均可用
|
||||||
|
- 列表**有值** → 只有名称匹配的函数会被暴露给用户
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 核心判定流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[新消息到来] --> BT[内置工具:读模型 meta.builtinTools]
|
||||||
|
BT --> BT2{builtinTools 字段存在?}
|
||||||
|
BT2 -- 否 --> BT3[开启全部内置工具]
|
||||||
|
BT2 -- 是 --> BT4[仅开启值为 true 的类别]
|
||||||
|
|
||||||
|
A --> CT[OpenWebUI Tools / 工具服务器]
|
||||||
|
CT --> V{插件 Valve 开启了该类别?}
|
||||||
|
V -- 否 --> VX[丢弃该类别]
|
||||||
|
V -- 是 --> B{后端 config.enable = True?}
|
||||||
|
B -- 否 --> C[跳过该服务器]
|
||||||
|
B -- 是 --> FL{function_name_filter_list 有值?}
|
||||||
|
FL -- 是 --> FL2[过滤掉不在列表中的函数]
|
||||||
|
FL -- 否 --> J
|
||||||
|
FL2 --> J{Chat tool_ids 有勾选?}
|
||||||
|
J -- 有 --> K[仅加载勾选的 ID]
|
||||||
|
J -- 无 --> L[加载所有后台已启用工具]
|
||||||
|
|
||||||
|
BT3 & BT4 & K & L --> M[始终注入: publish_file_from_workspace]
|
||||||
|
M --> N[启动/恢复 Copilot SDK 会话]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 场景速查表
|
||||||
|
|
||||||
|
### 内置工具(与前端选择无关)
|
||||||
|
|
||||||
|
| 模型配置 | 结果 |
|
||||||
|
|---|---|
|
||||||
|
| `meta.builtinTools` 字段不存在 | 全部内置工具类别开启 |
|
||||||
|
| `meta.builtinTools` 字段存在 | 仅 `true` 的类别开启 |
|
||||||
|
|
||||||
|
### OpenWebUI Tools / 工具服务器(受 Chat 前端选择影响)
|
||||||
|
|
||||||
|
| Chat 工具选择情况 | 加载逻辑 |
|
||||||
|
|---|---|
|
||||||
|
| 什么都没选 | 加载所有 Valve 开启且后台已启用的工具(Python Tools + OpenAPI + MCP) |
|
||||||
|
| 选了部分 tool_ids | 仅加载勾选的 ID(必须同时通过 Valve 和 config.enable 校验) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 代码实现详述
|
||||||
|
|
||||||
|
### 1. 管理员后台变更即时同步
|
||||||
|
|
||||||
|
OpenWebUI 通过 `PersistentConfig` + Redis 保证多 worker 之间的配置同步,插件直接读取 `request.app.state.config.TOOL_SERVER_CONNECTIONS` 即可获取最新值:
|
||||||
|
|
||||||
|
- 后台**启用**一个服务器 → **下一条消息**就出现。
|
||||||
|
- 后台**禁用**一个服务器 → **下一条消息**就消失。
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 直接读取 OpenWebUI 的配置对象,有 Redis 时每次读取都会同步最新值
|
||||||
|
connections = request.app.state.config.TOOL_SERVER_CONNECTIONS # list
|
||||||
|
|
||||||
|
for server in connections:
|
||||||
|
config = server.get("config", {})
|
||||||
|
is_enabled = config.get("enable", False) # 每条服务器、每次请求都检查
|
||||||
|
if not is_enabled:
|
||||||
|
continue # 立即跳过,硬性拦截
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 内置工具直接透传给 OpenWebUI 处理
|
||||||
|
|
||||||
|
插件调用 OpenWebUI 的 `get_builtin_tools(request, extra_params, model)` 时,将 `model` 原样传入即可。OpenWebUI 内部会自动读取 `model.info.meta.builtinTools` 来决定哪些内置工具生效:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# OpenWebUI 源码 utils/tools.py 中的判定逻辑(开发参考,非插件代码)
|
||||||
|
def is_builtin_tool_enabled(category: str) -> bool:
|
||||||
|
builtin_tools = model.get("info", {}).get("meta", {}).get("builtinTools", {})
|
||||||
|
return builtin_tools.get(category, True) # 缺省值 True:未配置时全部开启
|
||||||
|
```
|
||||||
|
|
||||||
|
插件无需自行维护内置工具分类映射,也不需要向 `builtinTools` 注入任何值。
|
||||||
|
|
||||||
|
### 3. 自定义工具"默认全选"(白名单仅在显式勾选时激活)
|
||||||
|
|
||||||
|
白名单**只有在用户明确勾选了 tool_ids 时才启用**。未勾选任何工具时,加载所有后台已启用工具:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# tool_ids 来自 Chat 请求体
|
||||||
|
if tool_ids:
|
||||||
|
# 白名单模式:严格只保留勾选项
|
||||||
|
available_ids = [tid for tid in available_ids if tid in tool_ids]
|
||||||
|
else:
|
||||||
|
# 无勾选:加载所有后台已启用工具(免配置可用)
|
||||||
|
pass # available_ids 保持不变
|
||||||
|
```
|
||||||
|
|
||||||
|
MCP 服务器的 `_parse_mcp_servers` 遵循同样规则。
|
||||||
|
|
||||||
|
### 4. 后端状态硬校验(OpenAPI 和 MCP 统一处理)
|
||||||
|
|
||||||
|
```python
|
||||||
|
is_enabled = False
|
||||||
|
config = server.get("config", {}) if isinstance(server, dict) else getattr(server, "config", {})
|
||||||
|
is_enabled = config.get("enable", False) if isinstance(config, dict) else getattr(config, "enable", False)
|
||||||
|
|
||||||
|
if not is_enabled:
|
||||||
|
continue # 硬性跳过,任何用户或 Valve 设置都无法绕过
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- **SDK 内部工具**:向会话传 `available_tools = None`,保证 `read_file`、`shell` 等 SDK 原生能力不被自定义工具列表意外屏蔽。
|
||||||
|
- **始终注入的工具**:`publish_file_from_workspace` 在所有过滤完成后硬性追加,是文件交付工作流的必要依赖,不受任何开关影响。
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Documentation Writing Guide
|
# Documentation Writing Guide
|
||||||
|
|
||||||
This guide explains how to write and contribute documentation for OpenWebUI Extras.
|
This guide explains how to write and contribute documentation for OpenWebUI Extensions.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,8 +21,8 @@ Our documentation is built with [MkDocs](https://www.mkdocs.org/) and the [Mater
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone the repository
|
# Clone the repository
|
||||||
git clone https://github.com/Fu-Jie/awesome-openwebui.git
|
git clone https://github.com/Fu-Jie/openwebui-extensions.git
|
||||||
cd awesome-openwebui
|
cd openwebui-extensions
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
@@ -122,7 +122,7 @@ Step-by-step usage instructions.
|
|||||||
|
|
||||||
## Source Code
|
## Source Code
|
||||||
|
|
||||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/...){ .md-button }
|
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins/...){ .md-button }
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 文档编写指南
|
# 文档编写指南
|
||||||
|
|
||||||
本文介绍如何为 OpenWebUI Extras 编写与贡献文档。
|
本文介绍如何为 OpenWebUI Extensions 编写与贡献文档。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,8 +21,8 @@
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 克隆仓库
|
# 克隆仓库
|
||||||
git clone https://github.com/Fu-Jie/awesome-openwebui.git
|
git clone https://github.com/Fu-Jie/openwebui-extensions.git
|
||||||
cd awesome-openwebui
|
cd openwebui-extensions
|
||||||
|
|
||||||
# 安装依赖
|
# 安装依赖
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
@@ -122,7 +122,7 @@ Step-by-step usage instructions.
|
|||||||
|
|
||||||
## Source Code
|
## Source Code
|
||||||
|
|
||||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/...){ .md-button }
|
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins/...){ .md-button }
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
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!
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Development
|
# Development
|
||||||
|
|
||||||
Learn how to develop plugins and contribute to OpenWebUI Extras.
|
Learn how to develop plugins and contribute to OpenWebUI Extensions.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -24,6 +24,14 @@ Learn how to develop plugins and contribute to OpenWebUI Extras.
|
|||||||
|
|
||||||
[:octicons-arrow-right-24: Read the Guide](documentation-guide.md)
|
[:octicons-arrow-right-24: Read the Guide](documentation-guide.md)
|
||||||
|
|
||||||
|
- :material-robot:{ .lg .middle } **Copilot Engineering Plan**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Engineering configuration for GitHub Copilot + Gemini CLI + antigravity development mode.
|
||||||
|
|
||||||
|
[:octicons-arrow-right-24: Read the Plan](copilot-engineering-plan.md)
|
||||||
|
|
||||||
- :material-github:{ .lg .middle } **Contributing**
|
- :material-github:{ .lg .middle } **Contributing**
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -164,5 +172,6 @@ user_language = __user__.get("language", "en-US")
|
|||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
- [Full Development Guide](plugin-guide.md)
|
- [Full Development Guide](plugin-guide.md)
|
||||||
- [Plugin Examples](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins)
|
- [Copilot Engineering Plan](copilot-engineering-plan.md)
|
||||||
|
- [Plugin Examples](https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins)
|
||||||
- [OpenWebUI Documentation](https://docs.openwebui.com/)
|
- [OpenWebUI Documentation](https://docs.openwebui.com/)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 开发指南
|
# 开发指南
|
||||||
|
|
||||||
了解如何开发插件并为 OpenWebUI Extras 做出贡献。
|
了解如何开发插件并为 OpenWebUI Extensions 做出贡献。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -24,6 +24,14 @@
|
|||||||
|
|
||||||
[:octicons-arrow-right-24: 阅读指南](documentation-guide.md)
|
[:octicons-arrow-right-24: 阅读指南](documentation-guide.md)
|
||||||
|
|
||||||
|
- :material-robot:{ .lg .middle } **Copilot 工程化配置**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
面向 GitHub Copilot + Gemini CLI + 反重力开发模式的工程化设计文档。
|
||||||
|
|
||||||
|
[:octicons-arrow-right-24: 阅读文档](copilot-engineering-plan.md)
|
||||||
|
|
||||||
- :material-github:{ .lg .middle } **贡献指南**
|
- :material-github:{ .lg .middle } **贡献指南**
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -164,5 +172,6 @@ user_language = __user__.get("language", "en-US")
|
|||||||
## 资源
|
## 资源
|
||||||
|
|
||||||
- [完整开发指南](plugin-guide.md)
|
- [完整开发指南](plugin-guide.md)
|
||||||
- [插件示例](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins)
|
- [Copilot 工程化配置](copilot-engineering-plan.md)
|
||||||
|
- [插件示例](https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins)
|
||||||
- [OpenWebUI 文档](https://docs.openwebui.com/)
|
- [OpenWebUI 文档](https://docs.openwebui.com/)
|
||||||
|
|||||||
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,11 +7,13 @@
|
|||||||
## 📚 Table of Contents
|
## 📚 Table of Contents
|
||||||
|
|
||||||
1. [Quick Start](#1-quick-start)
|
1. [Quick Start](#1-quick-start)
|
||||||
2. [Core Concepts & SDK Details](#2-core-concepts--sdk-details)
|
2. [Project Structure & Naming](#2-project-structure--naming)
|
||||||
3. [Deep Dive into Plugin Types](#3-deep-dive-into-plugin-types)
|
3. [Core Concepts & SDK Details](#3-core-concepts--sdk-details)
|
||||||
4. [Advanced Development Patterns](#4-advanced-development-patterns)
|
4. [Deep Dive into Plugin Types](#4-deep-dive-into-plugin-types)
|
||||||
5. [Best Practices & Design Principles](#5-best-practices--design-principles)
|
5. [Advanced Development Patterns](#5-advanced-development-patterns)
|
||||||
6. [Troubleshooting](#6-troubleshooting)
|
6. [Best Practices & Design Principles](#6-best-practices--design-principles)
|
||||||
|
7. [Workflow & Process](#7-workflow--process)
|
||||||
|
8. [Troubleshooting](#8-troubleshooting)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -64,9 +66,39 @@ class Action:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Core Concepts & SDK Details
|
## 2. Project Structure & Naming
|
||||||
|
|
||||||
### 2.1 ⚠️ Important: Sync vs Async
|
### 2.1 Language & Code Requirements
|
||||||
|
|
||||||
|
- **Single Code File**: `plugins/{type}/{name}/{name}.py`. Never create separate source files for different languages.
|
||||||
|
- **Built-in i18n**: Must dynamically switch UI, prompts, and logs based on user language.
|
||||||
|
- **Documentation**: Must include both `README.md` (English) and `README_CN.md` (Chinese).
|
||||||
|
|
||||||
|
### 2.2 Docstring Standard
|
||||||
|
|
||||||
|
Each plugin file must start with a standardized docstring:
|
||||||
|
|
||||||
|
```python
|
||||||
|
"""
|
||||||
|
title: Plugin Name
|
||||||
|
author: Fu-Jie
|
||||||
|
author_url: https://github.com/Fu-Jie/openwebui-extensions
|
||||||
|
funding_url: https://github.com/open-webui
|
||||||
|
version: 0.1.0
|
||||||
|
icon_url: data:image/svg+xml;base64,<base64-encoded-svg>
|
||||||
|
requirements: dependency1==1.0.0, dependency2>=2.0.0
|
||||||
|
description: Brief description of plugin functionality.
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
- **icon_url**: Required for Action plugins. Must be Base64 encoded SVG from [Lucide Icons](https://lucide.dev/icons/).
|
||||||
|
- **requirements**: Only list dependencies not installed in the OpenWebUI environment.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Core Concepts & SDK Details
|
||||||
|
|
||||||
|
### 3.1 ⚠️ Important: Sync vs Async
|
||||||
|
|
||||||
OpenWebUI plugins run within an `asyncio` event loop.
|
OpenWebUI plugins run within an `asyncio` event loop.
|
||||||
|
|
||||||
@@ -75,7 +107,7 @@ OpenWebUI plugins run within an `asyncio` event loop.
|
|||||||
- **Pitfall**: Calling synchronous methods directly (e.g., `time.sleep`, `requests.get`) will freeze the entire server
|
- **Pitfall**: Calling synchronous methods directly (e.g., `time.sleep`, `requests.get`) will freeze the entire server
|
||||||
- **Solution**: Wrap synchronous calls using `await asyncio.to_thread(sync_func, ...)`
|
- **Solution**: Wrap synchronous calls using `await asyncio.to_thread(sync_func, ...)`
|
||||||
|
|
||||||
### 2.2 Core Parameters
|
### 3.2 Core Parameters
|
||||||
|
|
||||||
All plugin methods (`inlet`, `outlet`, `pipe`, `action`) support injecting the following special parameters:
|
All plugin methods (`inlet`, `outlet`, `pipe`, `action`) support injecting the following special parameters:
|
||||||
|
|
||||||
@@ -88,30 +120,43 @@ All plugin methods (`inlet`, `outlet`, `pipe`, `action`) support injecting the f
|
|||||||
| `__event_emitter__` | `func` | **One-way Notification**. Used to send Toast notifications or status bar updates |
|
| `__event_emitter__` | `func` | **One-way Notification**. Used to send Toast notifications or status bar updates |
|
||||||
| `__event_call__` | `func` | **Two-way Interaction**. Used to execute JS code, show confirmation dialogs, or input boxes |
|
| `__event_call__` | `func` | **Two-way Interaction**. Used to execute JS code, show confirmation dialogs, or input boxes |
|
||||||
|
|
||||||
### 2.3 Configuration System (Valves)
|
### 3.3 Configuration System (Valves)
|
||||||
|
|
||||||
- **`Valves`**: Global admin configuration
|
Use Pydantic BaseModel to define configurable parameters. All Valves fields must use **UPPER_SNAKE_CASE**.
|
||||||
- **`UserValves`**: User-level configuration (higher priority, overrides global)
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Filter:
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
class Action:
|
||||||
class Valves(BaseModel):
|
class Valves(BaseModel):
|
||||||
API_KEY: str = Field(default="", description="Global API Key")
|
SHOW_STATUS: bool = Field(default=True, description="Whether to show operation status updates.")
|
||||||
|
# ...
|
||||||
class UserValves(BaseModel):
|
|
||||||
API_KEY: str = Field(default="", description="User Private API Key")
|
|
||||||
|
|
||||||
def inlet(self, body, __user__):
|
|
||||||
# Prioritize user's Key
|
|
||||||
user_valves = __user__.get("valves", self.UserValves())
|
|
||||||
api_key = user_valves.API_KEY or self.valves.API_KEY
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 3.4 Context Access
|
||||||
|
|
||||||
|
All plugins **must** use `_get_user_context` and `_get_chat_context` methods to safely extract information, rather than accessing `__user__` or `body` directly.
|
||||||
|
|
||||||
|
### 3.5 Event Emission & Logging
|
||||||
|
|
||||||
|
- **Event Emission**: Implement helper methods `_emit_status` and `_emit_notification`.
|
||||||
|
- **Frontend Console Debugging**: Highly recommended for real-time data flow viewing. Use `_emit_debug_log` to print structured debug logs in the browser console.
|
||||||
|
- **Server-side Logging**: Use Python's standard `logging` module. Do not use `print()`.
|
||||||
|
|
||||||
|
### 3.6 Database & File Storage
|
||||||
|
|
||||||
|
- **Database**: Re-use Open WebUI's internal database connection (`open_webui.internal.db`).
|
||||||
|
- **File Storage**: Implement multi-level fallback mechanisms (DB -> S3 -> Local -> URL -> API) to ensure compatibility across all storage configurations.
|
||||||
|
|
||||||
|
### 3.7 Internationalization (i18n)
|
||||||
|
|
||||||
|
Define a `TRANSLATIONS` dictionary and use a robust language detection mechanism (Multi-level Fallback: JS localStorage -> HTTP Accept-Language -> User Profile -> en-US).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Deep Dive into Plugin Types
|
## 4. Deep Dive into Plugin Types
|
||||||
|
|
||||||
### 3.1 Action
|
### 4.1 Action
|
||||||
|
|
||||||
**Role**: Adds buttons below messages that trigger upon user click.
|
**Role**: Adds buttons below messages that trigger upon user click.
|
||||||
|
|
||||||
@@ -136,7 +181,7 @@ async def action(self, body, __event_call__):
|
|||||||
await __event_call__({"type": "execute", "data": {"code": js}})
|
await __event_call__({"type": "execute", "data": {"code": js}})
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.2 Filter
|
### 4.2 Filter
|
||||||
|
|
||||||
**Role**: Middleware that intercepts and modifies requests/responses.
|
**Role**: Middleware that intercepts and modifies requests/responses.
|
||||||
|
|
||||||
@@ -157,7 +202,7 @@ async def inlet(self, body, __metadata__):
|
|||||||
return body
|
return body
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.3 Pipe
|
### 4.3 Pipe
|
||||||
|
|
||||||
**Role**: Custom Model/Agent.
|
**Role**: Custom Model/Agent.
|
||||||
|
|
||||||
@@ -182,18 +227,27 @@ class Pipe:
|
|||||||
return r.iter_lines()
|
return r.iter_lines()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 4.4 Copilot SDK Tool Definition Standards
|
||||||
|
|
||||||
|
When developing custom tools for GitHub Copilot SDK, you **must** define a Pydantic `BaseModel` for parameters and explicitly reference it using `params_type` in `define_tool`.
|
||||||
|
|
||||||
|
### 4.5 Copilot SDK Streaming & Tool Card Standards
|
||||||
|
|
||||||
|
- **Reasoning Streaming**: Must use native `<think>` tags and ensure proper closure (`\n</think>\n`) before outputting main content or tool calls.
|
||||||
|
- **Native Tool Calls Block**: Output strictly formatted HTML `<details type="tool_calls"...>` blocks. Ensure all double quotes in attributes are escaped as `"`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Advanced Development Patterns
|
## 5. Advanced Development Patterns
|
||||||
|
|
||||||
### 4.1 Pipe & Filter Collaboration
|
### 5.1 Pipe & Filter Collaboration
|
||||||
|
|
||||||
Use `__request__.app.state` to share data between plugins:
|
Use `__request__.app.state` to share data between plugins:
|
||||||
|
|
||||||
- **Pipe**: `__request__.app.state.search_results = [...]`
|
- **Pipe**: `__request__.app.state.search_results = [...]`
|
||||||
- **Filter (Outlet)**: Read `search_results` and format them as citation links
|
- **Filter (Outlet)**: Read `search_results` and format them as citation links
|
||||||
|
|
||||||
### 4.2 Async Background Tasks
|
### 5.2 Async Background Tasks
|
||||||
|
|
||||||
Execute time-consuming operations without blocking the user response:
|
Execute time-consuming operations without blocking the user response:
|
||||||
|
|
||||||
@@ -209,7 +263,7 @@ async def background_job(self, chat_id):
|
|||||||
pass
|
pass
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.3 Calling Built-in LLM
|
### 5.3 Calling Built-in LLM
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from open_webui.utils.chat import generate_chat_completion
|
from open_webui.utils.chat import generate_chat_completion
|
||||||
@@ -235,13 +289,13 @@ llm_response = await generate_chat_completion(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.4 JS Render to Markdown (Data URL Embedding)
|
### 5.4 JS Render to Markdown (Data URL Embedding)
|
||||||
|
|
||||||
For scenarios requiring complex frontend rendering (e.g., AntV charts, Mermaid diagrams) but wanting **persistent pure Markdown output**, use the Data URL embedding pattern:
|
For scenarios requiring complex frontend rendering (e.g., AntV charts, Mermaid diagrams) but wanting **persistent pure Markdown output**, use the Data URL embedding pattern:
|
||||||
|
|
||||||
#### Workflow
|
#### Workflow
|
||||||
|
|
||||||
```
|
```text
|
||||||
┌──────────────────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ 1. Python Action │
|
│ 1. Python Action │
|
||||||
│ ├── Analyze message content │
|
│ ├── Analyze message content │
|
||||||
@@ -259,117 +313,28 @@ For scenarios requiring complex frontend rendering (e.g., AntV charts, Mermaid d
|
|||||||
└──────────────────────────────────────────────────────────────┘
|
└──────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Python Side (Send JS for Execution)
|
### 5.5 Agent File Delivery Standards (3-Step Delivery Protocol)
|
||||||
|
|
||||||
```python
|
1. **Write Local**: Create files in the current execution directory (`.`).
|
||||||
async def action(self, body, __event_call__, __metadata__, ...):
|
2. **Publish**: Call `publish_file_from_workspace(filename='name.ext')`.
|
||||||
chat_id = self._extract_chat_id(body, __metadata__)
|
3. **Display Link**: Present the returned `download_url` as a Markdown link.
|
||||||
message_id = self._extract_message_id(body, __metadata__)
|
|
||||||
|
|
||||||
# Generate JS code
|
|
||||||
js_code = self._generate_js_code(
|
|
||||||
chat_id=chat_id,
|
|
||||||
message_id=message_id,
|
|
||||||
data=processed_data,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Execute JS
|
|
||||||
if __event_call__:
|
|
||||||
await __event_call__({
|
|
||||||
"type": "execute",
|
|
||||||
"data": {"code": js_code}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
#### JavaScript Side (Render and Write-back)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
(async function() {
|
|
||||||
// 1. Load visualization library
|
|
||||||
if (typeof VisualizationLib === 'undefined') {
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
const script = document.createElement('script');
|
|
||||||
script.src = 'https://cdn.example.com/lib.min.js';
|
|
||||||
script.onload = resolve;
|
|
||||||
script.onerror = reject;
|
|
||||||
document.head.appendChild(script);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Create offscreen container
|
|
||||||
const container = document.createElement('div');
|
|
||||||
container.style.cssText = 'position:absolute;left:-9999px;';
|
|
||||||
document.body.appendChild(container);
|
|
||||||
|
|
||||||
// 3. Render visualization
|
|
||||||
const instance = new VisualizationLib({ container });
|
|
||||||
instance.render(data);
|
|
||||||
|
|
||||||
// 4. Export to Data URL
|
|
||||||
const dataUrl = await instance.toDataURL({ type: 'svg', embedResources: true });
|
|
||||||
|
|
||||||
// 5. Cleanup
|
|
||||||
instance.destroy();
|
|
||||||
document.body.removeChild(container);
|
|
||||||
|
|
||||||
// 6. Generate Markdown image
|
|
||||||
const markdownImage = ``;
|
|
||||||
|
|
||||||
// 7. Update message via API
|
|
||||||
const token = localStorage.getItem("token");
|
|
||||||
await fetch(`/api/v1/chats/${chatId}/messages/${messageId}/event`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Authorization": `Bearer ${token}`
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
type: "chat:message",
|
|
||||||
data: { content: originalContent + "\n\n" + markdownImage }
|
|
||||||
})
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Benefits
|
|
||||||
|
|
||||||
- **Pure Markdown Output**: Standard Markdown image syntax, no HTML code blocks
|
|
||||||
- **Self-Contained**: Images embedded as Base64 Data URL, no external dependencies
|
|
||||||
- **Persistent**: Via API write-back, images remain after page reload
|
|
||||||
- **Cross-Platform**: Works on any client supporting Markdown images
|
|
||||||
|
|
||||||
#### HTML Injection vs JS Render to Markdown
|
|
||||||
|
|
||||||
| Feature | HTML Injection | JS Render + Markdown |
|
|
||||||
|---------|----------------|----------------------|
|
|
||||||
| Output Format | HTML code block | Markdown image |
|
|
||||||
| Interactivity | ✅ Buttons, animations | ❌ Static image |
|
|
||||||
| External Deps | Requires JS libraries | None (self-contained) |
|
|
||||||
| Persistence | Depends on browser | ✅ Permanent |
|
|
||||||
| File Export | Needs special handling | ✅ Direct export |
|
|
||||||
| Use Case | Interactive content | Infographics, chart snapshots |
|
|
||||||
|
|
||||||
#### 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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. Best Practices & Design Principles
|
## 6. Best Practices & Design Principles
|
||||||
|
|
||||||
### 5.1 Naming & Positioning
|
### 6.1 Naming & Positioning
|
||||||
|
|
||||||
- **Short & Punchy**: e.g., "FlashCard", "DeepRead". Avoid generic terms like "Text Analysis Assistant"
|
- **Short & Punchy**: e.g., "FlashCard", "DeepRead". Avoid generic terms like "Text Analysis Assistant"
|
||||||
- **Complementary**: Don't reinvent the wheel; clarify what specific problem your plugin solves
|
- **Complementary**: Don't reinvent the wheel; clarify what specific problem your plugin solves
|
||||||
|
|
||||||
### 5.2 User Experience (UX)
|
### 6.2 User Experience (UX)
|
||||||
|
|
||||||
- **Timely Feedback**: Send a `notification` ("Generating...") before time-consuming operations
|
- **Timely Feedback**: Send a `notification` ("Generating...") before time-consuming operations
|
||||||
- **Visual Appeal**: When Action outputs HTML, use modern CSS (rounded corners, shadows, gradients)
|
- **Visual Appeal**: When Action outputs HTML, use modern CSS (rounded corners, shadows, gradients)
|
||||||
- **Smart Guidance**: If text is too short, prompt the user: "Suggest entering more content for better results"
|
- **Smart Guidance**: If text is too short, prompt the user: "Suggest entering more content for better results"
|
||||||
|
|
||||||
### 5.3 Error Handling
|
### 6.3 Error Handling
|
||||||
|
|
||||||
!!! danger "Never fail silently"
|
!!! danger "Never fail silently"
|
||||||
Always catch exceptions and inform the user via `__event_emitter__`.
|
Always catch exceptions and inform the user via `__event_emitter__`.
|
||||||
@@ -385,9 +350,39 @@ except Exception as e:
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 6.4 Long-running Task Notifications
|
||||||
|
|
||||||
|
If a foreground task is expected to take more than 3 seconds, implement a user notification mechanism (e.g., sending a notification every 5 seconds).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Troubleshooting
|
## 7. Workflow & Process
|
||||||
|
|
||||||
|
### 7.1 Source-derived Knowledge (from `plugins/`)
|
||||||
|
|
||||||
|
- **Input/context safety**: normalize multimodal text extraction, use `_get_user_context` / `_get_chat_context`, and protect frontend language detection with timeout guards.
|
||||||
|
- **Long task UX**: emit immediate `status/notification`, then staged progress updates; keep full exception detail in backend logs.
|
||||||
|
- **HTML merge strategy**: use stable wrapper markers (`OPENWEBUI_PLUGIN_OUTPUT`) and support both overwrite and merge modes.
|
||||||
|
- **Theme consistency**: detect parent/system theme and apply theme-aware rendering/export styles for iframe-based outputs.
|
||||||
|
- **Render-export-persist loop**: offscreen render (SVG/PNG) -> upload `/api/v1/files/` -> event update + persistence update to avoid refresh loss.
|
||||||
|
- **DOCX production path**: `TITLE_SOURCE` fallback naming, reasoning-block stripping, native Word math (`latex2mathml + mathml2omml`), and citation/reference anchoring.
|
||||||
|
- **File retrieval fallback chain**: DB inline -> S3 direct -> local path variants -> public URL -> internal API -> raw fields, with max-byte guards on each stage.
|
||||||
|
- **Filter singleton discipline**: do not store request-scoped mutable state on `self`; compute from request context each run.
|
||||||
|
- **Async compression pattern**: `inlet` summary injection + `outlet` background summary generation, with model-threshold override and system-message protection.
|
||||||
|
- **Workspace/tool hardening**: explicit `params_type` schemas, strict path-boundary validation, and publish flow returning `/api/v1/files/{id}/content` with `skip_rag=true` metadata.
|
||||||
|
- **MoE refinement pipeline**: detect aggregation prompts, parse segmented responses, and rewrite to synthesis-oriented master prompt with optional reroute model.
|
||||||
|
|
||||||
|
### 7.2 Copilot Engineering Configuration
|
||||||
|
|
||||||
|
- For repository-wide AI-assisted engineering setup (GitHub Copilot + Gemini CLI + antigravity mode), follow `docs/development/copilot-engineering-plan.md`.
|
||||||
|
- This plan defines the shared contract for tool parameter schema/routing, file creation/publish protocol, rollback-safe delivery patterns, and streaming/tool-card compatibility.
|
||||||
|
|
||||||
|
- **Consistency Maintenance**: Any addition, modification, or removal of a plugin must simultaneously update the plugin code, READMEs, project docs, doc indexes, and the root README.
|
||||||
|
- **Release Workflow**: Pushing to `main` triggers automatic release. Ensure version numbers are updated and follow SemVer. Use Conventional Commits.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Troubleshooting
|
||||||
|
|
||||||
??? question "HTML not showing?"
|
??? question "HTML not showing?"
|
||||||
Ensure it's wrapped in a ` ```html ... ``` ` code block.
|
Ensure it's wrapped in a ` ```html ... ``` ` code block.
|
||||||
@@ -407,6 +402,6 @@ except Exception as e:
|
|||||||
|
|
||||||
## Additional Resources
|
## Additional Resources
|
||||||
|
|
||||||
- [:fontawesome-brands-github: Plugin Examples](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins)
|
- [:fontawesome-brands-github: Plugin Examples](https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins)
|
||||||
- [:material-book-open-variant: OpenWebUI Official Docs](https://docs.openwebui.com/)
|
- [:material-book-open-variant: OpenWebUI Official Docs](https://docs.openwebui.com/)
|
||||||
- [:material-forum: Community Discussions](https://github.com/open-webui/open-webui/discussions)
|
- [:material-forum: Community Discussions](https://github.com/open-webui/open-webui/discussions)
|
||||||
|
|||||||
@@ -4,23 +4,25 @@
|
|||||||
|
|
||||||
## 📚 目录
|
## 📚 目录
|
||||||
|
|
||||||
1. [插件开发快速入门](#1-插件开发快速入门)
|
1. [插件开发快速入门](#1-quick-start)
|
||||||
2. [核心概念与 SDK 详解](#2-核心概念与-sdk-详解)
|
2. [核心概念与 SDK 详解](#2-core-concepts-sdk-details)
|
||||||
3. [插件类型深度解析](#3-插件类型深度解析)
|
3. [插件类型深度解析](#3-plugin-types)
|
||||||
* [Action (动作)](#31-action-动作)
|
* [Action (动作)](#31-action)
|
||||||
* [Filter (过滤器)](#32-filter-过滤器)
|
* [Filter (过滤器)](#32-filter)
|
||||||
* [Pipe (管道)](#33-pipe-管道)
|
* [Pipe (管道)](#33-pipe)
|
||||||
4. [高级开发模式](#4-高级开发模式)
|
4. [高级开发模式](#4-advanced-patterns)
|
||||||
5. [最佳实践与设计原则](#5-最佳实践与设计原则)
|
5. [最佳实践与设计原则](#5-best-practices)
|
||||||
6. [故障排查](#6-故障排查)
|
6. [仓库规范(openwebui-extensions)](#6-repo-standards)
|
||||||
|
7. [故障排查](#7-troubleshooting)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. 插件开发快速入门
|
## 1. 插件开发快速入门 {: #1-quick-start }
|
||||||
|
|
||||||
### 1.1 什么是 OpenWebUI 插件?
|
### 1.1 什么是 OpenWebUI 插件?
|
||||||
|
|
||||||
OpenWebUI 插件(官方称为 "Functions")是扩展平台功能的主要方式。它们运行在后端 Python 环境中,允许你:
|
OpenWebUI 插件(官方称为 "Functions")是扩展平台功能的主要方式。它们运行在后端 Python 环境中,允许你:
|
||||||
|
|
||||||
* 🔌 **集成新模型**:通过 Pipe 接入 Claude、Gemini 或自定义 RAG。
|
* 🔌 **集成新模型**:通过 Pipe 接入 Claude、Gemini 或自定义 RAG。
|
||||||
* 🎨 **增强交互**:通过 Action 在消息旁添加按钮(如"导出"、"生成图表")。
|
* 🎨 **增强交互**:通过 Action 在消息旁添加按钮(如"导出"、"生成图表")。
|
||||||
* 🔧 **干预流程**:通过 Filter 在请求前后修改数据(如注入上下文、敏感词过滤)。
|
* 🔧 **干预流程**:通过 Filter 在请求前后修改数据(如注入上下文、敏感词过滤)。
|
||||||
@@ -64,11 +66,12 @@ class Action:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. 核心概念与 SDK 详解
|
## 2. 核心概念与 SDK 详解 {: #2-core-concepts-sdk-details }
|
||||||
|
|
||||||
### 2.1 ⚠️ 重要:同步与异步
|
### 2.1 ⚠️ 重要:同步与异步
|
||||||
|
|
||||||
OpenWebUI 插件运行在 `asyncio` 事件循环中。
|
OpenWebUI 插件运行在 `asyncio` 事件循环中。
|
||||||
|
|
||||||
* **原则**:所有 I/O 操作(数据库、文件、网络)必须非阻塞。
|
* **原则**:所有 I/O 操作(数据库、文件、网络)必须非阻塞。
|
||||||
* **陷阱**:直接调用同步方法(如 `time.sleep`, `requests.get`)会卡死整个服务器。
|
* **陷阱**:直接调用同步方法(如 `time.sleep`, `requests.get`)会卡死整个服务器。
|
||||||
* **解决**:使用 `await asyncio.to_thread(sync_func, ...)` 包装同步调用。
|
* **解决**:使用 `await asyncio.to_thread(sync_func, ...)` 包装同步调用。
|
||||||
@@ -107,13 +110,13 @@ class Filter:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. 插件类型深度解析
|
## 3. 插件类型深度解析 {: #3-plugin-types }
|
||||||
|
|
||||||
### 3.1 Action (动作)
|
### 3.1 Action (动作) {: #31-action }
|
||||||
|
|
||||||
**定位**:在消息下方添加按钮,用户点击触发。
|
**定位**:在消息下方添加按钮,用户点击触发。
|
||||||
|
|
||||||
**高级用法:前端执行 JavaScript (文件下载示例)**
|
#### 高级用法:前端执行 JavaScript (文件下载示例)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import base64
|
import base64
|
||||||
@@ -134,7 +137,7 @@ async def action(self, body, __event_call__):
|
|||||||
await __event_call__({"type": "execute", "data": {"code": js}})
|
await __event_call__({"type": "execute", "data": {"code": js}})
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.2 Filter (过滤器)
|
### 3.2 Filter (过滤器) {: #32-filter }
|
||||||
|
|
||||||
**定位**:中间件,拦截并修改请求/响应。
|
**定位**:中间件,拦截并修改请求/响应。
|
||||||
|
|
||||||
@@ -142,7 +145,7 @@ async def action(self, body, __event_call__):
|
|||||||
* **`outlet`**: 响应后。用于格式化输出、保存日志。
|
* **`outlet`**: 响应后。用于格式化输出、保存日志。
|
||||||
* **`stream`**: 流式处理中。用于实时敏感词过滤。
|
* **`stream`**: 流式处理中。用于实时敏感词过滤。
|
||||||
|
|
||||||
**示例:注入环境变量**
|
#### 示例:注入环境变量
|
||||||
|
|
||||||
```python
|
```python
|
||||||
async def inlet(self, body, __metadata__):
|
async def inlet(self, body, __metadata__):
|
||||||
@@ -155,11 +158,11 @@ async def inlet(self, body, __metadata__):
|
|||||||
return body
|
return body
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.3 Pipe (管道)
|
### 3.3 Pipe (管道) {: #33-pipe }
|
||||||
|
|
||||||
**定位**:自定义模型/代理。
|
**定位**:自定义模型/代理。
|
||||||
|
|
||||||
**示例:简单的 OpenAI 代理**
|
#### 示例:简单的 OpenAI 代理
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import requests
|
import requests
|
||||||
@@ -177,14 +180,17 @@ class Pipe:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. 高级开发模式
|
## 4. 高级开发模式 {: #4-advanced-patterns }
|
||||||
|
|
||||||
### 4.1 Pipe 与 Filter 协同
|
### 4.1 Pipe 与 Filter 协同
|
||||||
|
|
||||||
利用 `__request__.app.state` 在不同插件间共享数据。
|
利用 `__request__.app.state` 在不同插件间共享数据。
|
||||||
|
|
||||||
* **Pipe**: `__request__.app.state.search_results = [...]`
|
* **Pipe**: `__request__.app.state.search_results = [...]`
|
||||||
* **Filter (Outlet)**: 读取 `search_results` 并将其格式化为引用链接附加到回复末尾。
|
* **Filter (Outlet)**: 读取 `search_results` 并将其格式化为引用链接附加到回复末尾。
|
||||||
|
|
||||||
### 4.2 异步后台任务
|
### 4.2 异步后台任务
|
||||||
|
|
||||||
不阻塞用户响应,在后台执行耗时操作(如生成总结、存库)。
|
不阻塞用户响应,在后台执行耗时操作(如生成总结、存库)。
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -205,7 +211,7 @@ async def background_job(self, chat_id):
|
|||||||
|
|
||||||
#### 工作流程
|
#### 工作流程
|
||||||
|
|
||||||
```
|
```text
|
||||||
┌──────────────────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ 1. Python Action │
|
│ 1. Python Action │
|
||||||
│ ├── 分析消息内容 │
|
│ ├── 分析消息内容 │
|
||||||
@@ -297,10 +303,10 @@ async def action(self, body, __event_call__, __metadata__, ...):
|
|||||||
|
|
||||||
#### 优势
|
#### 优势
|
||||||
|
|
||||||
- **纯 Markdown 输出**:结果是标准的 Markdown 图片语法,无需 HTML 代码块
|
* **纯 Markdown 输出**:结果是标准的 Markdown 图片语法,无需 HTML 代码块
|
||||||
- **自包含**:图片以 Base64 Data URL 嵌入,无外部依赖
|
* **自包含**:图片以 Base64 Data URL 嵌入,无外部依赖
|
||||||
- **持久化**:通过 API 回写,消息重新加载后图片仍然存在
|
* **持久化**:通过 API 回写,消息重新加载后图片仍然存在
|
||||||
- **跨平台**:任何支持 Markdown 图片的客户端都能显示
|
* **跨平台**:任何支持 Markdown 图片的客户端都能显示
|
||||||
|
|
||||||
#### HTML 注入 vs JS 渲染嵌入 Markdown
|
#### HTML 注入 vs JS 渲染嵌入 Markdown
|
||||||
|
|
||||||
@@ -315,21 +321,23 @@ async def action(self, body, __event_call__, __metadata__, ...):
|
|||||||
|
|
||||||
#### 参考实现
|
#### 参考实现
|
||||||
|
|
||||||
- `plugins/actions/js-render-poc/infographic_markdown.py` - AntV 信息图 + Data URL
|
* `plugins/actions/infographic/infographic.py` - 基于 AntV + Data URL 的生产级实现
|
||||||
- `plugins/actions/js-render-poc/js_render_poc.py` - 基础概念验证
|
|
||||||
|
|
||||||
## 5. 最佳实践与设计原则
|
## 5. 最佳实践与设计原则 {: #5-best-practices }
|
||||||
|
|
||||||
### 5.1 命名与定位
|
### 5.1 命名与定位
|
||||||
|
|
||||||
* **简短有力**:如 "闪记卡", "精读"。避免 "文本分析助手" 这种泛词。
|
* **简短有力**:如 "闪记卡", "精读"。避免 "文本分析助手" 这种泛词。
|
||||||
* **功能互补**:不要重复造轮子,明确你的插件解决了什么特定问题。
|
* **功能互补**:不要重复造轮子,明确你的插件解决了什么特定问题。
|
||||||
|
|
||||||
### 5.2 用户体验 (UX)
|
### 5.2 用户体验 (UX)
|
||||||
|
|
||||||
* **反馈及时**:耗时操作前先发送 `notification` ("正在生成...")。
|
* **反馈及时**:耗时操作前先发送 `notification` ("正在生成...")。
|
||||||
* **视觉美观**:Action 输出 HTML 时,使用现代化的 CSS(圆角、阴影、渐变)。
|
* **视觉美观**:Action 输出 HTML 时,使用现代化的 CSS(圆角、阴影、渐变)。
|
||||||
* **智能引导**:检测到文本过短时,提示用户"建议输入更多内容以获得更好结果"。
|
* **智能引导**:检测到文本过短时,提示用户"建议输入更多内容以获得更好结果"。
|
||||||
|
|
||||||
### 5.3 错误处理
|
### 5.3 错误处理
|
||||||
|
|
||||||
永远不要让插件静默失败。捕获异常并通过 `__event_emitter__` 告知用户。
|
永远不要让插件静默失败。捕获异常并通过 `__event_emitter__` 告知用户。
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -344,7 +352,75 @@ except Exception as e:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. 故障排查
|
## 6. 仓库规范(openwebui-extensions) {: #6-repo-standards }
|
||||||
|
|
||||||
|
### 6.1 单文件 i18n 规范
|
||||||
|
|
||||||
|
本仓库要求每个插件采用**单文件源码 + 内置多语言**方案,禁止按语言拆分多个 `.py` 文件。
|
||||||
|
|
||||||
|
* 代码路径规范:`plugins/{type}/{name}/{name}.py`
|
||||||
|
* 文档规范:必须同时提供 `README.md` 与 `README_CN.md`
|
||||||
|
|
||||||
|
### 6.2 上下文访问规范(必选)
|
||||||
|
|
||||||
|
优先通过 `_get_user_context` 与 `_get_chat_context` 提取上下文,避免直接硬编码读取 `__user__` 或 `body` 字段。
|
||||||
|
|
||||||
|
### 6.3 事件与日志规范
|
||||||
|
|
||||||
|
* 用状态/通知事件给用户反馈进度。
|
||||||
|
* 前端调试优先使用 `execute` 注入的控制台日志。
|
||||||
|
* 后端统一使用 Python `logging`,生产代码避免 `print()`。
|
||||||
|
|
||||||
|
### 6.4 前端语言探测防卡死
|
||||||
|
|
||||||
|
通过 `__event_call__` 获取前端语言时,必须同时满足:
|
||||||
|
|
||||||
|
* JS 端 `try...catch` 并保证返回值
|
||||||
|
* 后端 `asyncio.wait_for(..., timeout=2.0)`
|
||||||
|
|
||||||
|
这样可以避免前端异常导致后端永久等待。
|
||||||
|
|
||||||
|
### 6.5 Copilot SDK 工具参数定义
|
||||||
|
|
||||||
|
开发 Copilot SDK 工具时,应使用 `pydantic.BaseModel` 显式声明参数,并在 `define_tool(...)` 中通过 `params_type` 传入。
|
||||||
|
|
||||||
|
### 6.6 Copilot SDK 流式输出格式
|
||||||
|
|
||||||
|
* 思考过程使用原生 `<think>...</think>`。
|
||||||
|
* 正文或工具卡片输出前,必须先闭合 `</think>`。
|
||||||
|
* 工具卡片使用 `<details type="tool_calls" ...>` 原生结构。
|
||||||
|
* `arguments` 与 `result` 属性中的双引号必须转义为 `"`。
|
||||||
|
|
||||||
|
### 6.7 从 `plugins/` 全量提炼的关键开发知识
|
||||||
|
|
||||||
|
* **输入与上下文**:统一做多模态文本抽取;优先 `_get_user_context` / `_get_chat_context`;前端语言探测必须 `wait_for` 超时保护。
|
||||||
|
* **长任务反馈**:执行前立即发送 `status/notification`,过程分阶段汇报,失败时用户提示简洁、后台日志完整。
|
||||||
|
* **HTML/渲染输出**:使用固定包装器与插入点,支持覆盖与合并两种模式;主题跟随父页面与系统主题。
|
||||||
|
* **前端导出闭环**:离屏渲染 SVG/PNG -> 上传 `/api/v1/files/` -> 事件更新 + 持久化更新,避免刷新丢失。
|
||||||
|
* **DOCX 生产模式**:`TITLE_SOURCE` 多级回退;导出前剔除 reasoning;LaTeX 转 OMML;支持引用锚点与参考文献。
|
||||||
|
* **文件访问回退链**:DB 内联 -> S3 -> 本地路径变体 -> 公网 URL -> 内部 API -> 原始字段,并在每层限制字节上限。
|
||||||
|
* **Filter 单例安全**:禁止在 `self` 保存请求态;上下文压缩采用 inlet/outlet 双阶段 + 异步后台任务。
|
||||||
|
* **工具与工作区安全**:工具参数用 `params_type` 显式建模;路径解析后必须二次校验在 workspace 根目录内。
|
||||||
|
* **文件交付标准**:本地生成 -> 发布工具 -> 返回 `/api/v1/files/{id}/content`,并带 `skip_rag=true` 元数据。
|
||||||
|
* **MoE 聚合优化**:识别聚合提示词后重写为结构化综合分析任务,并可在聚合阶段切换模型。
|
||||||
|
|
||||||
|
### 6.8 Copilot 工程化配置(GitHub Copilot + Gemini CLI + 反重力开发)
|
||||||
|
|
||||||
|
统一工程化配置请参考:
|
||||||
|
|
||||||
|
* `docs/development/copilot-engineering-plan.md`
|
||||||
|
* `docs/development/copilot-engineering-plan.zh.md`
|
||||||
|
|
||||||
|
该设计文档规定了:
|
||||||
|
|
||||||
|
* 工具参数建模与路由规则
|
||||||
|
* 文件创建与发布协议
|
||||||
|
* 可回滚的高迭代交付模式
|
||||||
|
* 流式输出与工具卡片兼容要求
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 故障排查 {: #7-troubleshooting }
|
||||||
|
|
||||||
* **HTML 不显示?** 确保包裹在 ` ```html ... ``` ` 代码块中。
|
* **HTML 不显示?** 确保包裹在 ` ```html ... ``` ` 代码块中。
|
||||||
* **数据库报错?** 检查是否在 `async` 函数中直接调用了同步的 DB 方法,请使用 `asyncio.to_thread`。
|
* **数据库报错?** 检查是否在 `async` 函数中直接调用了同步的 DB 方法,请使用 `asyncio.to_thread`。
|
||||||
|
|||||||
@@ -1,50 +1,46 @@
|
|||||||
# 开源项目重组实施计划
|
# Implementation Plan (Current)
|
||||||
|
|
||||||
## 1. 目标
|
## 1. Objective
|
||||||
将 `openwebui-extras` 打造为一个 **OpenWebUI 增强功能集合库**,专注于分享个人开发和收集的优质插件、提示词,而非作为一个独立的 Python 应用程序发布。
|
|
||||||
|
|
||||||
## 2. 当前状态分析
|
Define an engineering-ready design baseline for OpenWebUI plugin development that:
|
||||||
- **定位明确**:项目核心价值在于内容(Plugins, Prompts, Docs),而非运行环境。
|
|
||||||
- **结构已优化**:
|
|
||||||
- `plugins/`:核心插件资源。
|
|
||||||
- `prompts/`:提示词资源。
|
|
||||||
- `docs/`:详细的使用和开发文档。
|
|
||||||
- `scripts/`:辅助工具脚本(如本地测试用的 `run.py`)。
|
|
||||||
- **已移除不必要文件**:移除了 `requirements.txt`,避免用户误以为需要配置 Python 环境。
|
|
||||||
|
|
||||||
## 3. 重组方案
|
- uses GitHub Copilot as the primary coding agent,
|
||||||
|
- supports Gemini CLI as a secondary execution/research lane,
|
||||||
|
- adopts antigravity development mode for reversible, low-risk iteration.
|
||||||
|
|
||||||
### 3.1 目录结构
|
## 2. Current Decision
|
||||||
保持当前的清晰结构,强调“拿来即用”:
|
|
||||||
|
|
||||||
```
|
The active design document is:
|
||||||
openwebui-extras/
|
|
||||||
├── docs/ # 文档与教程
|
|
||||||
├── plugins/ # 插件库 (核心资源)
|
|
||||||
│ ├── actions/
|
|
||||||
│ ├── filters/
|
|
||||||
│ ├── pipelines/
|
|
||||||
│ └── pipes/
|
|
||||||
├── prompts/ # 提示词库 (核心资源)
|
|
||||||
├── scripts/ # 维护者工具 (非用户必须)
|
|
||||||
├── LICENSE # MIT 许可证
|
|
||||||
├── README.md # 项目入口与资源索引
|
|
||||||
└── index.html # 项目展示页
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2 核心调整
|
- `docs/development/copilot-engineering-plan.md`
|
||||||
1. **移除依赖管理**:删除了 `requirements.txt`。用户不需要 `pip install` 任何东西,只需下载对应的 `.py` 或 `.md` 文件导入 OpenWebUI 即可。
|
- `docs/development/copilot-engineering-plan.zh.md`
|
||||||
2. **文档侧重**:README 和文档将侧重于“如何下载”和“如何导入”,而不是“如何安装项目”。
|
|
||||||
|
|
||||||
### 3.3 后续建议
|
This file is retained as a stable pointer to avoid design drift.
|
||||||
1. **资源索引**:建议在 `README.md` 中维护一个高质量的插件/提示词索引表,方便用户快速查找。
|
|
||||||
2. **贡献指南**:制定简单的 `CONTRIBUTING.md`,告诉其他人如何提交他们的插件或提示词(例如:只需提交文件到对应目录)。
|
|
||||||
3. **版本控制**:虽然不需要 Python 环境,但建议在插件文件的头部注释中保留版本号和兼容性说明(如 `Compatible with OpenWebUI v0.3.x`)。
|
|
||||||
|
|
||||||
## 4. 发布流程
|
## 3. Engineering Baseline
|
||||||
1. **提交更改**:`git add . && git commit -m "Update project structure for resource sharing"`
|
|
||||||
2. **推送到 GitHub**。
|
- Single-file i18n plugin code architecture.
|
||||||
3. **宣传**:在 OpenWebUI 社区分享此仓库链接。
|
- Bilingual documentation contract.
|
||||||
|
- Copilot SDK tool schema discipline (`params_type`).
|
||||||
|
- Gemini CLI output normalization before merge.
|
||||||
|
- Workspace file sandbox + publish protocol.
|
||||||
|
- Streaming compatibility with native `<think>` and `<details type="tool_calls">`.
|
||||||
|
|
||||||
|
## 4. File Creation & Delivery Baseline
|
||||||
|
|
||||||
|
- Create artifacts in workspace-scoped paths.
|
||||||
|
- Publish artifacts via workspace publish flow.
|
||||||
|
- Return and display `/api/v1/files/{id}/content` links for delivery.
|
||||||
|
|
||||||
|
## 5. Maintenance Rule
|
||||||
|
|
||||||
|
Any update to plugin engineering standards must be reflected in:
|
||||||
|
|
||||||
|
1. `docs/development/copilot-engineering-plan.md`
|
||||||
|
2. `docs/development/copilot-engineering-plan.zh.md`
|
||||||
|
3. `docs/development/plugin-guide.md`
|
||||||
|
4. `docs/development/plugin-guide.zh.md`
|
||||||
|
|
||||||
---
|
---
|
||||||
*生成时间:2025-12-19*
|
|
||||||
|
Updated: 2026-02-23
|
||||||
|
|||||||
@@ -12,7 +12,9 @@
|
|||||||
* [Pipe](#33-pipe)
|
* [Pipe](#33-pipe)
|
||||||
4. [Advanced Development Patterns](#4-advanced-development-patterns)
|
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)
|
6. [Repository Standards (openwebui-extensions)](#6-repository-standards-openwebui-extensions)
|
||||||
|
7. [Custom Agent Design Recommendations](#7-custom-agent-design-recommendations)
|
||||||
|
8. [Troubleshooting](#8-troubleshooting)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,9 +23,10 @@
|
|||||||
### 1.1 What are OpenWebUI Plugins?
|
### 1.1 What are OpenWebUI Plugins?
|
||||||
|
|
||||||
OpenWebUI Plugins (officially called "Functions") are the primary way to extend the platform's capabilities. Running in a backend Python environment, they allow you to:
|
OpenWebUI Plugins (officially called "Functions") are the primary way to extend the platform's capabilities. Running in a backend Python environment, they allow you to:
|
||||||
|
|
||||||
* 🔌 **Integrate New Models**: Connect to Claude, Gemini, or custom RAGs via Pipes.
|
* 🔌 **Integrate New Models**: Connect to Claude, Gemini, or custom RAGs via Pipes.
|
||||||
* 🎨 **Enhance Interaction**: Add buttons (e.g., "Export", "Generate Chart") next to messages via Actions.
|
* 🎨 **Enhance Interaction**: Add buttons (e.g., "Export", "Generate Chart") next to messages via Actions.
|
||||||
* 🔧 **Intervene in Processes**: Modify data before requests or after responses (e.g., inject context, filter sensitive words) via Filters.
|
* 🔧 **Intervene in Processes**: modify data before requests or after responses (e.g., inject context, filter sensitive words) via Filters.
|
||||||
|
|
||||||
### 1.2 Your First Plugin (Hello World)
|
### 1.2 Your First Plugin (Hello World)
|
||||||
|
|
||||||
@@ -69,6 +72,7 @@ class Action:
|
|||||||
### 2.1 ⚠️ Important: Sync vs Async
|
### 2.1 ⚠️ Important: Sync vs Async
|
||||||
|
|
||||||
OpenWebUI plugins run within an `asyncio` event loop.
|
OpenWebUI plugins run within an `asyncio` event loop.
|
||||||
|
|
||||||
* **Principle**: All I/O operations (database, file, network) must be non-blocking.
|
* **Principle**: All I/O operations (database, file, network) must be non-blocking.
|
||||||
* **Pitfall**: Calling synchronous methods directly (e.g., `time.sleep`, `requests.get`) will freeze the entire server.
|
* **Pitfall**: Calling synchronous methods directly (e.g., `time.sleep`, `requests.get`) will freeze the entire server.
|
||||||
* **Solution**: Wrap synchronous calls using `await asyncio.to_thread(sync_func, ...)`.
|
* **Solution**: Wrap synchronous calls using `await asyncio.to_thread(sync_func, ...)`.
|
||||||
@@ -113,7 +117,7 @@ class Filter:
|
|||||||
|
|
||||||
**Role**: Adds buttons below messages that trigger upon user click.
|
**Role**: Adds buttons below messages that trigger upon user click.
|
||||||
|
|
||||||
**Advanced Usage: Execute JavaScript on Frontend (File Download Example)**
|
#### Advanced Usage: Execute JavaScript on Frontend (File Download Example)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import base64
|
import base64
|
||||||
@@ -142,7 +146,7 @@ async def action(self, body, __event_call__):
|
|||||||
* **`outlet`**: After response. Used for formatting output, logging.
|
* **`outlet`**: After response. Used for formatting output, logging.
|
||||||
* **`stream`**: During streaming. Used for real-time sensitive word filtering.
|
* **`stream`**: During streaming. Used for real-time sensitive word filtering.
|
||||||
|
|
||||||
**Example: Injecting Environment Variables**
|
#### Example: Injecting Environment Variables
|
||||||
|
|
||||||
```python
|
```python
|
||||||
async def inlet(self, body, __metadata__):
|
async def inlet(self, body, __metadata__):
|
||||||
@@ -159,7 +163,7 @@ async def inlet(self, body, __metadata__):
|
|||||||
|
|
||||||
**Role**: Custom Model/Agent.
|
**Role**: Custom Model/Agent.
|
||||||
|
|
||||||
**Example: Simple OpenAI Wrapper**
|
#### Example: Simple OpenAI Wrapper
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import requests
|
import requests
|
||||||
@@ -180,11 +184,14 @@ class Pipe:
|
|||||||
## 4. Advanced Development Patterns
|
## 4. Advanced Development Patterns
|
||||||
|
|
||||||
### 4.1 Pipe & Filter Collaboration
|
### 4.1 Pipe & Filter Collaboration
|
||||||
|
|
||||||
Use `__request__.app.state` to share data between plugins.
|
Use `__request__.app.state` to share data between plugins.
|
||||||
|
|
||||||
* **Pipe**: `__request__.app.state.search_results = [...]`
|
* **Pipe**: `__request__.app.state.search_results = [...]`
|
||||||
* **Filter (Outlet)**: Read `search_results` and format them as citation links appended to the response.
|
* **Filter (Outlet)**: Read `search_results` and format them as citation links appended to the response.
|
||||||
|
|
||||||
### 4.2 Async Background Tasks
|
### 4.2 Async Background Tasks
|
||||||
|
|
||||||
Execute time-consuming operations (e.g., summarization, database storage) in the background without blocking the user response.
|
Execute time-consuming operations (e.g., summarization, database storage) in the background without blocking the user response.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -204,15 +211,18 @@ async def background_job(self, chat_id):
|
|||||||
## 5. Best Practices & Design Principles
|
## 5. Best Practices & Design Principles
|
||||||
|
|
||||||
### 5.1 Naming & Positioning
|
### 5.1 Naming & Positioning
|
||||||
|
|
||||||
* **Short & Punchy**: e.g., "FlashCard", "DeepRead". Avoid generic terms like "Text Analysis Assistant".
|
* **Short & Punchy**: e.g., "FlashCard", "DeepRead". Avoid generic terms like "Text Analysis Assistant".
|
||||||
* **Complementary**: Don't reinvent the wheel; clarify what specific problem your plugin solves.
|
* **Complementary**: Don't reinvent the wheel; clarify what specific problem your plugin solves.
|
||||||
|
|
||||||
### 5.2 User Experience (UX)
|
### 5.2 User Experience (UX)
|
||||||
|
|
||||||
* **Timely Feedback**: Send a `notification` ("Generating...") before time-consuming operations.
|
* **Timely Feedback**: Send a `notification` ("Generating...") before time-consuming operations.
|
||||||
* **Visual Appeal**: When Action outputs HTML, use modern CSS (rounded corners, shadows, gradients).
|
* **Visual Appeal**: When Action outputs HTML, use modern CSS (rounded corners, shadows, gradients).
|
||||||
* **Smart Guidance**: If text is too short, prompt the user: "Suggest entering more content for better results".
|
* **Smart Guidance**: If text is too short, prompt the user: "Suggest entering more content for better results".
|
||||||
|
|
||||||
### 5.3 Error Handling
|
### 5.3 Error Handling
|
||||||
|
|
||||||
Never let a plugin fail silently. Catch exceptions and inform the user via `__event_emitter__`.
|
Never let a plugin fail silently. Catch exceptions and inform the user via `__event_emitter__`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -227,7 +237,126 @@ except Exception as e:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Troubleshooting
|
## 6. Repository Standards (openwebui-extensions)
|
||||||
|
|
||||||
|
### 6.1 Single-file i18n Requirement
|
||||||
|
|
||||||
|
In this repository, each plugin must use a **single source file** with built-in i18n logic. Do not split source code by language.
|
||||||
|
|
||||||
|
* Required pattern: `plugins/{type}/{name}/{name}.py`
|
||||||
|
* Required docs: `README.md` + `README_CN.md`
|
||||||
|
|
||||||
|
### 6.2 Safe Context Access (Required)
|
||||||
|
|
||||||
|
Prefer helper methods like `_get_user_context` and `_get_chat_context` instead of direct, fragile field access from `__user__` / `body`.
|
||||||
|
|
||||||
|
### 6.3 Event and Logging Conventions
|
||||||
|
|
||||||
|
* Use status/notification events for user-visible progress.
|
||||||
|
* Use frontend console debug logs (`execute`) for live debugging during development.
|
||||||
|
* Use Python `logging` for backend logs; avoid `print()` in production plugin code.
|
||||||
|
|
||||||
|
### 6.4 Frontend Language Detection and Timeout Guard
|
||||||
|
|
||||||
|
When reading frontend language via `__event_call__`, always use:
|
||||||
|
|
||||||
|
* JS `try...catch` fallback return
|
||||||
|
* backend `asyncio.wait_for(..., timeout=2.0)`
|
||||||
|
|
||||||
|
This prevents deadlocks when frontend execution fails.
|
||||||
|
|
||||||
|
### 6.5 Copilot SDK Tool Definition
|
||||||
|
|
||||||
|
For custom Copilot SDK tools, define explicit parameter schema using a `pydantic.BaseModel` and pass it with `params_type` in `define_tool(...)`.
|
||||||
|
|
||||||
|
### 6.6 Copilot SDK Streaming Output Format
|
||||||
|
|
||||||
|
* Use native `<think>...</think>` for reasoning output.
|
||||||
|
* Ensure `</think>` is closed before normal content or tool cards.
|
||||||
|
* For tool result cards, use native `<details type="tool_calls" ...>` format.
|
||||||
|
* Escape attribute quotes in `arguments` and `result` as `"`.
|
||||||
|
|
||||||
|
### 6.7 Source-derived Production Patterns (Recommended)
|
||||||
|
|
||||||
|
The following patterns are extracted from `github_copilot_sdk.py` and `workspace_file_manager.py`:
|
||||||
|
|
||||||
|
* **Tool parameter anti-drift**: define tools with `params_type=BaseModel`, and execute with `model_dump(exclude_unset=True)` so missing params do not become explicit `None`.
|
||||||
|
* **Tool name normalization**: enforce `^[a-zA-Z0-9_-]+$`; if non-ASCII names collapse, use an `md5` suffix fallback to keep registration stable.
|
||||||
|
* **Workspace sandboxing**: resolve and verify every path stays inside the workspace root to prevent traversal.
|
||||||
|
* **3-step file delivery**: local write -> `publish_file_from_workspace` -> return `/api/v1/files/{id}/content`, with `skip_rag=true` metadata.
|
||||||
|
* **Dual upload channel**: prefer API upload (S3-compatible), fallback to DB + local copy.
|
||||||
|
* **Streaming stability**: close `<think>` before emitting `assistant.message_delta` content.
|
||||||
|
* **Native tool cards**: emit `<details type="tool_calls">` on `tool.execution_complete` with strict HTML escaping (`"`, newline escaping).
|
||||||
|
* **TODO persistence linkage**: on successful `update_todo`, sync both `TODO.md` and database state.
|
||||||
|
|
||||||
|
### 6.8 Full Source-derived Knowledge Base (from `plugins/`)
|
||||||
|
|
||||||
|
The following is a broader extraction from `actions/`, `filters/`, `pipes/`, `pipelines/`, and `tools/`:
|
||||||
|
|
||||||
|
* **Action input hygiene**: normalize multimodal message content, strip old plugin HTML blocks (`OPENWEBUI_PLUGIN_OUTPUT`), and enforce minimum text length before expensive model calls.
|
||||||
|
* **Action i18n hardening**: use `TRANSLATIONS + fallback_map + base-lang fallback` (`fr-CA -> fr-FR`, `en-GB -> en-US`), keep all status/UI/JS strings in i18n keys, and protect `format(**kwargs)` formatting.
|
||||||
|
* **Frontend language detection (production-safe)**: use priority chain `document.lang -> localStorage(locale/language) -> navigator.language -> profile/request`, and always wrap `__event_call__(execute)` with timeout.
|
||||||
|
* **Long-running UX pattern**: emit immediate `status + notification`, report staged progress (`analyzing/rendering/saving`), and keep detailed exception data in backend logs.
|
||||||
|
* **HTML plugin composability**: use insertion markers for style/content/script, support both overwrite (`CLEAR_PREVIOUS_HTML`) and merge mode, and keep wrappers deterministic.
|
||||||
|
* **Theme-aware iframe rendering**: detect theme from parent meta/class/data-theme with system fallback, and inject theme-aware colors for SVG/PNG export.
|
||||||
|
* **Client-side render-and-export pipeline**: render offscreen chart/mindmap, export SVG/PNG, upload via `/api/v1/files/`, and persist updates through event API + chat persistence API.
|
||||||
|
* **DOCX export production patterns**: apply `TITLE_SOURCE` fallback chain (`chat_title -> markdown_title -> user+date`), remove reasoning blocks, convert LaTeX via `latex2mathml + mathml2omml`, and emit citation-aware references/bookmarks.
|
||||||
|
* **OpenWebUI file retrieval fallback ladder**: DB inline bytes/base64 -> S3 direct read -> local path variants -> public URL -> internal `/api/v1/files/{id}/content` -> raw object attrs, with max-byte guards at every stage.
|
||||||
|
* **Filter singleton-safe design**: never store request-scoped mutable state on `self`; compute per-request values from `body` and context helpers.
|
||||||
|
* **Async context compression patterns**: two-phase flow (`inlet` apply summary, `outlet` async generate summary), model-level threshold overrides, fast estimate + precise count near limit, and system-message protection (`effective_keep_first`).
|
||||||
|
* **Model compatibility guardrails**: skip incompatible model families (e.g., `copilot_sdk` paths) and avoid hardcoded default model IDs.
|
||||||
|
* **Folder memory pattern**: trigger periodic rule extraction (`every N messages`), replace rules idempotently using block markers (`RULES_BLOCK_START/END`), and optionally update root folder.
|
||||||
|
* **Tool workspace hardening**: all file APIs (`list/read/write/delete/publish`) must re-check sandbox boundary, enforce size limits, and return user-ready download hints.
|
||||||
|
* **MoE prompt refiner pattern (pipeline)**: detect aggregation prompts via trigger prefix, parse original query + segmented responses, then rewrite to synthesis-oriented master prompt with optional aggregation model reroute.
|
||||||
|
|
||||||
|
### 6.9 Copilot-related Engineering Configuration
|
||||||
|
|
||||||
|
To support plugin engineering with **GitHub Copilot + Gemini CLI + antigravity mode**, adopt these controls:
|
||||||
|
|
||||||
|
* **Primary/secondary assistant lanes**: Copilot is primary implementation lane; Gemini CLI is secondary draft/verification lane.
|
||||||
|
* **Single merge contract**: both lanes must pass the same repository constraints (single-file i18n, context helpers, event conventions, release workflow rules).
|
||||||
|
* **Tool schema discipline**: all Copilot SDK tools use explicit `params_type` with Pydantic models.
|
||||||
|
* **Antigravity safety**: small reversible edits, timeout guards, fallback routing, and deterministic file/output paths.
|
||||||
|
* **File creation protocol**: write in workspace scope, publish via workspace publish flow, return `/api/v1/files/{id}/content` for delivery.
|
||||||
|
|
||||||
|
Detailed design document:
|
||||||
|
|
||||||
|
* `docs/development/copilot-engineering-plan.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Custom Agent Design Recommendations
|
||||||
|
|
||||||
|
### 7.1 Suggested architecture (for this repo)
|
||||||
|
|
||||||
|
* **Orchestrator Pipe**: session lifecycle, model routing, streaming events.
|
||||||
|
* **Tool Adapter Layer**: unify OpenWebUI Tools / OpenAPI / MCP with param validation and name normalization.
|
||||||
|
* **Workspace I/O Layer**: sandboxed file operations + publish pipeline.
|
||||||
|
* **Render Layer**: `<think>` lifecycle, tool cards, status/notification events.
|
||||||
|
|
||||||
|
### 7.2 MVP checklist
|
||||||
|
|
||||||
|
1. Dual config model: `Valves + UserValves` (user overrides first).
|
||||||
|
2. Unified context helpers: `_get_user_context` / `_get_chat_context`.
|
||||||
|
3. At least one artifact-delivery tool (e.g., `publish_file_from_workspace`).
|
||||||
|
4. Minimal streaming loop: `reasoning_delta`, `message_delta`, `tool.execution_complete`.
|
||||||
|
5. Unified error reporting via notification events.
|
||||||
|
|
||||||
|
### 7.3 Three high-impact agents you can build now
|
||||||
|
|
||||||
|
* **Repo Analyst Agent**: output architecture map, risk list, and refactor proposals.
|
||||||
|
* **Release Draft Agent**: generate Conventional Commit title/body + bilingual release summary.
|
||||||
|
* **Docs Sync Agent**: compare source/doc versions and output a concrete sync file list.
|
||||||
|
|
||||||
|
### 7.4 Implementation priority
|
||||||
|
|
||||||
|
* **P0**: Release Draft Agent (highest ROI, lowest risk).
|
||||||
|
* **P1**: Docs Sync Agent (reduces doc drift).
|
||||||
|
* **P2**: Repo Analyst Agent (medium/long-term evolution).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Troubleshooting
|
||||||
|
|
||||||
* **HTML not showing?** Ensure it's wrapped in a ` ```html ... ``` ` code block.
|
* **HTML not showing?** Ensure it's wrapped in a ` ```html ... ``` ` code block.
|
||||||
* **Database error?** Check if you called synchronous DB methods directly in an `async` function; use `asyncio.to_thread`.
|
* **Database error?** Check if you called synchronous DB methods directly in an `async` function; use `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
|
## 🏗️ When & Where to Use Events
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ hide:
|
|||||||
- toc
|
- toc
|
||||||
---
|
---
|
||||||
|
|
||||||
# Welcome to OpenWebUI Extras
|
# Welcome to OpenWebUI Extensions
|
||||||
|
|
||||||
<div class="hero-section" markdown>
|
<div class="hero-section" markdown>
|
||||||
<div class="hero-content" markdown>
|
<div class="hero-content" markdown>
|
||||||
|
|
||||||
# 🚀 Supercharge Your OpenWebUI Experience
|
# 🚀 Supercharge Your OpenWebUI Experience
|
||||||
|
|
||||||
**OpenWebUI Extras** is a curated collection of plugins, prompts, and enhancements designed to extend the functionality of [OpenWebUI](https://github.com/open-webui/open-webui).
|
**OpenWebUI Extensions** is a curated collection of plugins, prompts, and enhancements designed to extend the functionality of [OpenWebUI](https://github.com/open-webui/open-webui).
|
||||||
|
|
||||||
[Get Started :material-arrow-right:](#quick-navigation){ .md-button .md-button--primary }
|
[Get Started :material-arrow-right:](#quick-navigation){ .md-button .md-button--primary }
|
||||||
[View on GitHub :fontawesome-brands-github:](https://github.com/Fu-Jie/awesome-openwebui){ .md-button }
|
[View on GitHub :fontawesome-brands-github:](https://github.com/Fu-Jie/openwebui-extensions){ .md-button }
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,6 +49,14 @@ hide:
|
|||||||
|
|
||||||
[:octicons-arrow-right-24: View Guides](enhancements/index.md)
|
[:octicons-arrow-right-24: View Guides](enhancements/index.md)
|
||||||
|
|
||||||
|
- :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**
|
- :material-book-open-page-variant:{ .lg .middle } **Development**
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -73,13 +81,13 @@ hide:
|
|||||||
|
|
||||||
[:octicons-arrow-right-24: Learn More](plugins/actions/smart-mind-map.md)
|
[: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**
|
||||||
|
|
||||||
@@ -127,6 +135,6 @@ We welcome contributions! Whether it's a new plugin, a helpful prompt, or docume
|
|||||||
|
|
||||||
<div class="footer-stats" markdown>
|
<div class="footer-stats" markdown>
|
||||||
|
|
||||||
**OpenWebUI Extras** - Making AI interactions more powerful and productive.
|
**OpenWebUI Extensions** - Making AI interactions more powerful and productive.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ hide:
|
|||||||
- toc
|
- toc
|
||||||
---
|
---
|
||||||
|
|
||||||
# 欢迎来到 OpenWebUI Extras
|
# 欢迎来到 OpenWebUI Extensions
|
||||||
|
|
||||||
<div class="hero-section" markdown>
|
<div class="hero-section" markdown>
|
||||||
<div class="hero-content" markdown>
|
<div class="hero-content" markdown>
|
||||||
|
|
||||||
# 🚀 增强你的 OpenWebUI 体验
|
# 🚀 增强你的 OpenWebUI 体验
|
||||||
|
|
||||||
**OpenWebUI Extras** 是一个精心整理的插件、提示词和增强功能集合,旨在扩展 [OpenWebUI](https://github.com/open-webui/open-webui) 的功能。
|
**OpenWebUI Extensions** 是一个精心整理的插件、提示词和增强功能集合,旨在扩展 [OpenWebUI](https://github.com/open-webui/open-webui) 的功能。
|
||||||
|
|
||||||
[开始使用 :material-arrow-right:](#快速导航){ .md-button .md-button--primary }
|
[开始使用 :material-arrow-right:](#快速导航){ .md-button .md-button--primary }
|
||||||
[在 GitHub 上查看 :fontawesome-brands-github:](https://github.com/Fu-Jie/awesome-openwebui){ .md-button }
|
[在 GitHub 上查看 :fontawesome-brands-github:](https://github.com/Fu-Jie/openwebui-extensions){ .md-button }
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,6 +49,14 @@ hide:
|
|||||||
|
|
||||||
[:octicons-arrow-right-24: 查看指南](enhancements/index.md)
|
[:octicons-arrow-right-24: 查看指南](enhancements/index.md)
|
||||||
|
|
||||||
|
- :material-rocket-launch:{ .lg .middle } **扩展 (Extensions)**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
独立的 OpenWebUI 前端增强扩展,全面提升交互体验。
|
||||||
|
|
||||||
|
[:octicons-arrow-right-24: 浏览扩展](extensions/index.md)
|
||||||
|
|
||||||
- :material-book-open-page-variant:{ .lg .middle } **开发指南**
|
- :material-book-open-page-variant:{ .lg .middle } **开发指南**
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -73,13 +81,13 @@ hide:
|
|||||||
|
|
||||||
[:octicons-arrow-right-24: 了解更多](plugins/actions/smart-mind-map.md)
|
[: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 } **异步上下文压缩**
|
||||||
|
|
||||||
@@ -127,6 +135,6 @@ hide:
|
|||||||
|
|
||||||
<div class="footer-stats" markdown>
|
<div class="footer-stats" markdown>
|
||||||
|
|
||||||
**OpenWebUI Extras** - 让 AI 交互更强大、更高效。
|
**OpenWebUI Extensions** - 让 AI 交互更强大、更高效。
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ if (response.ok) {
|
|||||||
|
|
||||||
## 完整示例
|
## 完整示例
|
||||||
|
|
||||||
参考 [js_render_poc.py](https://github.com/Fu-Jie/awesome-openwebui/blob/main/plugins/actions/js-render-poc/js_render_poc.py) 获取完整的 PoC 实现。
|
参考 [js_render_poc.py](https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/actions/js-render-poc/js_render_poc.py) 获取完整的 PoC 实现。
|
||||||
|
|
||||||
## 事件类型
|
## 事件类型
|
||||||
|
|
||||||
|
|||||||
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/openwebui-extensions/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/openwebui-extensions/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/openwebui-extensions/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/openwebui-extensions/tree/main/plugins/actions/deep-dive){ .md-button }
|
||||||
@@ -47,7 +47,7 @@ The Export to Excel plugin allows you to download your chat conversations as Exc
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Download the plugin file: [`export_to_excel.py`](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/export_to_excel)
|
1. Download the plugin file: [`export_to_excel.py`](https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins/actions/export_to_excel)
|
||||||
2. Upload to OpenWebUI: **Admin Panel** → **Settings** → **Functions**
|
2. Upload to OpenWebUI: **Admin Panel** → **Settings** → **Functions**
|
||||||
3. Enable the plugin
|
3. Enable the plugin
|
||||||
|
|
||||||
@@ -84,4 +84,4 @@ The exported Excel file contains:
|
|||||||
|
|
||||||
## Source Code
|
## Source Code
|
||||||
|
|
||||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/awesome-openwebui/tree/main/plugins/actions/export_to_excel){ .md-button }
|
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins/actions/export_to_excel){ .md-button }
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user