Compare commits
613 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 | ||
|
|
856f76cd27 | ||
|
|
28bb9000d8 | ||
|
|
d0b9e46b74 | ||
|
|
a0a4d31715 | ||
|
|
d5f394f5f1 | ||
|
|
a477d2baad | ||
|
|
8471680efe | ||
|
|
4d44b72dab | ||
|
|
88e14d251a | ||
|
|
e446b6474d | ||
|
|
a2eda6e5af | ||
|
|
fe80c8bee3 | ||
|
|
133315d0c6 | ||
|
|
3907644282 | ||
|
|
d8cde2115f | ||
|
|
0ce63b548f | ||
|
|
06e81c0194 | ||
|
|
3763e6501d | ||
|
|
5911f75641 | ||
|
|
f936181a37 | ||
|
|
a7651f33a4 | ||
|
|
45ddf5092b | ||
|
|
61294e90e4 | ||
|
|
8619405802 | ||
|
|
f0017ffacd | ||
|
|
65fe16e185 | ||
|
|
136e7e9021 | ||
|
|
c1a660a2a1 | ||
|
|
53f04debaf | ||
|
|
4b9790df00 | ||
|
|
58452a8441 | ||
|
|
e104161007 | ||
|
|
6de0d6fbe4 | ||
|
|
28d55c1469 | ||
|
|
59933e9361 | ||
|
|
7cbd0e2920 | ||
|
|
88038b35cc | ||
|
|
1fd7d90284 | ||
|
|
aee9c93bfb | ||
|
|
3951f7f91d | ||
|
|
3680fcf39f | ||
|
|
593a9ce22b | ||
|
|
fe497cccb7 | ||
|
|
88aa7e156a | ||
|
|
dbfce27986 | ||
|
|
9be6fe08fa | ||
|
|
782378eed8 | ||
|
|
4e59bb6518 | ||
|
|
3e73fcb3f0 | ||
|
|
c460337c43 | ||
|
|
e775b23503 | ||
|
|
b3cdb8e26e | ||
|
|
0e6f902d16 | ||
|
|
c15c73897f | ||
|
|
035439ce02 | ||
|
|
b84ff4a3a2 | ||
|
|
e22744abd0 | ||
|
|
54c90238f7 | ||
|
|
40d77121bd | ||
|
|
3795976a79 |
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,30 +4,39 @@ description: OpenWebUI Plugin Development & Release Workflow
|
||||
|
||||
# OpenWebUI Plugin Development Workflow
|
||||
|
||||
This workflow outlines the standard process for developing, documenting, and releasing plugins for OpenWebUI, ensuring compliance with project standards and CI/CD requirements.
|
||||
This workflow outlines the standard process for developing, documenting, and releasing plugins for OpenWebUI. **Crucially, the default goal of this workflow is "Preparation" (updating all relevant files) rather than automatic "Submission" (git commit/push), unless a release is explicitly requested.**
|
||||
|
||||
## 1. Development Standards
|
||||
|
||||
Reference: `.github/copilot-instructions.md`
|
||||
|
||||
### Bilingual Requirement
|
||||
Every plugin **MUST** have bilingual versions for both code and documentation:
|
||||
|
||||
- **Code**:
|
||||
- English: `plugins/{type}/{name}/{name}.py`
|
||||
- Chinese: `plugins/{type}/{name}/{name_cn}.py` (or `中文名.py`)
|
||||
Every plugin **MUST** have a single internationalized code file and bilingual documentation:
|
||||
|
||||
- **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**:
|
||||
- English: `plugins/{type}/{name}/README.md`
|
||||
- Chinese: `plugins/{type}/{name}/README_CN.md`
|
||||
|
||||
### Code Structure
|
||||
|
||||
- **Docstring**: Must include `title`, `author`, `version`, `description`, etc.
|
||||
- **Valves**: Use `pydantic` for configuration.
|
||||
- **Database**: Re-use `open_webui.internal.db` shared connection.
|
||||
- **User Context**: Use `_get_user_context` helper method.
|
||||
- **Chat Context**: Use `_get_chat_context` helper method for `chat_id` and `message_id`.
|
||||
- **Debugging**: Use `_emit_debug_log` for frontend console logging (requires `SHOW_DEBUG_LOG` valve).
|
||||
- **Chat API**: For message updates, follow the "OpenWebUI Chat API 更新规范" in `.github/copilot-instructions.md`.
|
||||
- Use Event API for immediate UI updates
|
||||
- Use Chat Persistence API for database storage
|
||||
- Always update both `messages[]` and `history.messages`
|
||||
|
||||
### Commit Messages
|
||||
- **Language**: **English ONLY**. Do not use Chinese in commit messages.
|
||||
### Commit Messages & Release Notes
|
||||
|
||||
- **Language**: **English ONLY**. Do not use Chinese in commit messages or release notes.
|
||||
- **Format**: Conventional Commits (e.g., `feat:`, `fix:`, `docs:`).
|
||||
|
||||
## 2. Documentation Updates
|
||||
@@ -35,10 +44,16 @@ Every plugin **MUST** have bilingual versions for both code and documentation:
|
||||
When adding or updating a plugin, you **MUST** update the following documentation files to maintain consistency:
|
||||
|
||||
### Plugin Directory
|
||||
|
||||
- `README.md`: Update version, description, and usage.
|
||||
- **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/`)
|
||||
|
||||
- **Index Pages**:
|
||||
- `docs/plugins/{type}/index.md`: Add/Update list item with **correct version**.
|
||||
- `docs/plugins/{type}/index.zh.md`: Add/Update list item with **correct version**.
|
||||
@@ -47,6 +62,7 @@ When adding or updating a plugin, you **MUST** update the following documentatio
|
||||
- `docs/plugins/{type}/{name}.zh.md`: Ensure content matches README_CN.
|
||||
|
||||
### Root README
|
||||
|
||||
- `README.md`: Add to "Featured Plugins" if applicable.
|
||||
- `README_CN.md`: Add to "Featured Plugins" if applicable.
|
||||
|
||||
@@ -55,27 +71,59 @@ When adding or updating a plugin, you **MUST** update the following documentatio
|
||||
Reference: `.github/workflows/release.yml`
|
||||
|
||||
### Version Bumping
|
||||
- **Rule**: Any change to plugin logic **MUST** be accompanied by a version bump in the docstring.
|
||||
|
||||
- **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`).
|
||||
- **Consistency**: Update version in **ALL** locations:
|
||||
1. English Code (`.py`)
|
||||
2. Chinese Code (`.py`)
|
||||
3. English README (`README.md`)
|
||||
4. Chinese README (`README_CN.md`)
|
||||
5. Docs Index (`docs/.../index.md`)
|
||||
6. Docs Index CN (`docs/.../index.zh.md`)
|
||||
7. Docs Detail (`docs/.../{name}.md`)
|
||||
8. Docs Detail CN (`docs/.../{name}.zh.md`)
|
||||
- **When to Bump**: Only update the version when:
|
||||
- User says "发布" / "release" / "bump version"
|
||||
- User explicitly asks to prepare for release
|
||||
- **Agent Initiative**: After completing significant changes (new features, bug fixes, or multiple code modifications), the agent **SHOULD proactively ask** the user if they want to **prepare a new version** for release.
|
||||
- **Release Information Compliance**: When a release is requested, the agent must generate a standard release summary (English commit title + bilingual bullet points) as defined in Section 3 & 5.
|
||||
- **Default Action (Prepare Only)**: When performing a version bump or update, the agent should update all files locally but **STOP** before committing. Present the changes and the **proposed Release/Commit Message** to the user and wait for explicit confirmation to commit/push.
|
||||
- **Consistency**: When bumping, update version in **ALL** locations:
|
||||
1. Code (`.py`)
|
||||
2. English README (`README.md`)
|
||||
3. Chinese README (`README_CN.md`)
|
||||
4. Docs Index (`docs/.../index.md`)
|
||||
5. Docs Index CN (`docs/.../index.zh.md`)
|
||||
6. Docs Detail (`docs/.../{name}.md`)
|
||||
7. Docs Detail CN (`docs/.../{name}.zh.md`)
|
||||
|
||||
### Automated Release Process
|
||||
1. **Trigger**: Push to `main` branch with changes in `plugins/**/*.py`.
|
||||
2. **Detection**: `scripts/extract_plugin_versions.py` detects changed plugins and compares versions.
|
||||
3. **Release**:
|
||||
|
||||
1. **Trigger**: Push to `main` branch with changes in `plugins/**/*.py`.
|
||||
2. **Detection**: `scripts/extract_plugin_versions.py` detects changed plugins and compares versions.
|
||||
3. **Release**:
|
||||
- Generates release notes based on changes.
|
||||
- Creates a GitHub Release tag (e.g., `v2024.01.01-1`).
|
||||
- Uploads individual `.py` files of **changed plugins only** as assets.
|
||||
4. **Market Publishing**:
|
||||
- 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
|
||||
|
||||
- Workflow: `.github/workflows/plugin-version-check.yml`
|
||||
- Checks if plugin files are modified.
|
||||
- **Fails** if version number is not updated.
|
||||
@@ -85,9 +133,17 @@ Reference: `.github/workflows/release.yml`
|
||||
|
||||
Before committing:
|
||||
|
||||
- [ ] Code is bilingual and functional?
|
||||
- [ ] Code is internal i18n supported (`.py`) and fully functional?
|
||||
- [ ] Docstrings have updated version?
|
||||
- [ ] READMEs are updated and bilingual?
|
||||
- [ ] **Key Capabilities** in READMEs still cover all legacy core features + new features?
|
||||
- [ ] `docs/` index and detail pages are updated?
|
||||
- [ ] Root `README.md` is updated?
|
||||
- [ ] All version numbers match exactly?
|
||||
|
||||
## 5. Git Operations (Agent Rules)
|
||||
|
||||
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.**
|
||||
1740
.github/copilot-instructions.md
vendored
1740
.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])
|
||||
91
.github/workflows/community-stats.yml
vendored
Normal file
91
.github/workflows/community-stats.yml
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
# OpenWebUI 社区统计报告自动生成
|
||||
# 智能检测:只在有意义的变更时才 commit
|
||||
# - 新增插件 (total_posts)
|
||||
# - 插件版本变更 (version)
|
||||
# - 积分增加 (total_points)
|
||||
# - 粉丝增加 (followers)
|
||||
|
||||
name: Community Stats
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 * * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-stats:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install requests python-dotenv
|
||||
|
||||
- name: Capture existing stats (before update)
|
||||
id: old_stats
|
||||
run: |
|
||||
if [ -f docs/community-stats.json ]; then
|
||||
echo "total_posts=$(jq -r '.total_posts // 0' docs/community-stats.json)" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "total_posts=0" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Generate stats report
|
||||
env:
|
||||
OPENWEBUI_API_KEY: ${{ secrets.OPENWEBUI_API_KEY }}
|
||||
OPENWEBUI_USER_ID: ${{ secrets.OPENWEBUI_USER_ID }}
|
||||
GIST_TOKEN: ${{ secrets.GIST_TOKEN }}
|
||||
GIST_ID: ${{ secrets.GIST_ID }}
|
||||
run: |
|
||||
python scripts/openwebui_stats.py
|
||||
|
||||
- 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
|
||||
run: |
|
||||
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
|
||||
if: steps.check_changes.outputs.should_commit == 'true'
|
||||
run: |
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add docs/ README.md README_CN.md
|
||||
git diff --staged --quiet || git commit -m "chore: update community stats - ${{ steps.check_changes.outputs.change_reason }}"
|
||||
git push
|
||||
68
.github/workflows/publish_new_plugin.yml
vendored
Normal file
68
.github/workflows/publish_new_plugin.yml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
name: Publish New Plugin
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
plugin_dir:
|
||||
description: 'Plugin directory (e.g., plugins/actions/deep-dive)'
|
||||
required: true
|
||||
type: string
|
||||
dry_run:
|
||||
description: 'Dry run mode (preview only)'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install requests
|
||||
|
||||
- name: Validate plugin directory
|
||||
run: |
|
||||
if [ ! -d "${{ github.event.inputs.plugin_dir }}" ]; then
|
||||
echo "❌ Error: Directory '${{ github.event.inputs.plugin_dir }}' does not exist"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Found plugin directory: ${{ github.event.inputs.plugin_dir }}"
|
||||
ls -la "${{ github.event.inputs.plugin_dir }}"
|
||||
|
||||
- name: Publish Plugin
|
||||
env:
|
||||
OPENWEBUI_API_KEY: ${{ secrets.OPENWEBUI_API_KEY }}
|
||||
run: |
|
||||
if [ "${{ github.event.inputs.dry_run }}" = "true" ]; then
|
||||
echo "🔍 Dry run mode - previewing..."
|
||||
python scripts/publish_plugin.py --new "${{ github.event.inputs.plugin_dir }}" --dry-run
|
||||
else
|
||||
echo "🚀 Publishing plugin..."
|
||||
python scripts/publish_plugin.py --new "${{ github.event.inputs.plugin_dir }}"
|
||||
fi
|
||||
|
||||
- name: Commit changes (if ID was added)
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Check if there are changes to commit
|
||||
if git diff --quiet; then
|
||||
echo "No changes to commit"
|
||||
else
|
||||
git add "${{ github.event.inputs.plugin_dir }}"
|
||||
git commit -m "feat: add openwebui_id to ${{ github.event.inputs.plugin_dir }}"
|
||||
git push
|
||||
echo "✅ Committed and pushed openwebui_id changes"
|
||||
fi
|
||||
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
|
||||
178
.github/workflows/release.yml
vendored
178
.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.
|
||||
# 此工作流自动化 OpenWebUI 插件的发布流程。
|
||||
#
|
||||
# Triggers:
|
||||
# - 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
|
||||
# 4. Supports multiple plugin updates in a single release
|
||||
|
||||
name: Plugin Release / 插件发布
|
||||
name: Plugin Release
|
||||
|
||||
on:
|
||||
# Auto-trigger on push to main when plugins are modified
|
||||
@@ -54,6 +52,9 @@ permissions:
|
||||
jobs:
|
||||
check-changes:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
LANG: en_US.UTF-8
|
||||
LC_ALL: en_US.UTF-8
|
||||
outputs:
|
||||
has_changes: ${{ steps.detect.outputs.has_changes }}
|
||||
changed_plugins: ${{ steps.detect.outputs.changed_plugins }}
|
||||
@@ -65,6 +66,12 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config --global core.quotepath false
|
||||
git config --global i18n.commitencoding utf-8
|
||||
git config --global i18n.logoutputencoding utf-8
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
@@ -131,6 +138,7 @@ jobs:
|
||||
|
||||
echo "changed_plugins<<EOF" >> $GITHUB_OUTPUT
|
||||
cat changed_files.txt >> $GITHUB_OUTPUT
|
||||
echo "" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
@@ -138,6 +146,7 @@ jobs:
|
||||
{
|
||||
echo 'release_notes<<EOF'
|
||||
cat changes.md
|
||||
echo ""
|
||||
echo 'EOF'
|
||||
} >> $GITHUB_OUTPUT
|
||||
|
||||
@@ -145,6 +154,10 @@ jobs:
|
||||
needs: check-changes
|
||||
if: needs.check-changes.outputs.has_changes == 'true' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
LANG: en_US.UTF-8
|
||||
LC_ALL: en_US.UTF-8
|
||||
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -152,6 +165,12 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config --global core.quotepath false
|
||||
git config --global i18n.commitencoding utf-8
|
||||
git config --global i18n.logoutputencoding utf-8
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
@@ -159,14 +178,34 @@ jobs:
|
||||
|
||||
- name: Determine version
|
||||
id: version
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ github.event.inputs.version }}" ]; then
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
||||
VERSION="${GITHUB_REF#refs/tags/}"
|
||||
else
|
||||
# Auto-generate version based on date and run number
|
||||
VERSION="v$(date +'%Y.%m.%d')-${{ github.run_number }}"
|
||||
# Auto-generate version based on date and daily release count
|
||||
TODAY=$(date +'%Y.%m.%d')
|
||||
TODAY_PREFIX="v${TODAY}-"
|
||||
|
||||
# Count existing releases with today's date prefix
|
||||
# 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))
|
||||
|
||||
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
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Release version: $VERSION"
|
||||
@@ -205,6 +244,63 @@ jobs:
|
||||
echo "=== Collected Files ==="
|
||||
find release_plugins -name "*.py" -type f | head -20
|
||||
|
||||
- name: Update plugin icon URLs
|
||||
run: |
|
||||
echo "Updating icon_url in plugins to use absolute GitHub URLs..."
|
||||
# Base URL for raw content using the release tag
|
||||
REPO_URL="https://raw.githubusercontent.com/${{ github.repository }}/${{ steps.version.outputs.version }}"
|
||||
|
||||
find release_plugins -name "*.py" | while read -r file; do
|
||||
# $file is like release_plugins/plugins/actions/infographic/infographic.py
|
||||
# Remove release_plugins/ prefix to get the path in the repo
|
||||
src_file="${file#release_plugins/}"
|
||||
src_dir=$(dirname "$src_file")
|
||||
base_name=$(basename "$src_file" .py)
|
||||
|
||||
# Check if a corresponding png exists in the source repository
|
||||
png_file="${src_dir}/${base_name}.png"
|
||||
|
||||
if [ -f "$png_file" ]; then
|
||||
echo "Found icon for $src_file: $png_file"
|
||||
TARGET_ICON_URL="${REPO_URL}/${png_file}"
|
||||
|
||||
# Use python for safe replacement
|
||||
python3 -c "
|
||||
import sys
|
||||
import re
|
||||
|
||||
file_path = '$file'
|
||||
icon_url = '$TARGET_ICON_URL'
|
||||
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Replace icon_url: ... with new url
|
||||
# Matches 'icon_url: ...' and replaces it
|
||||
new_content = re.sub(r'^icon_url:.*$', f'icon_url: {icon_url}', content, flags=re.MULTILINE)
|
||||
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
print(f'Successfully updated icon_url in {file_path}')
|
||||
except Exception as e:
|
||||
print(f'Error updating {file_path}: {e}', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Debug Filenames
|
||||
run: |
|
||||
python3 -c "import sys; print(f'Filesystem encoding: {sys.getfilesystemencoding()}')"
|
||||
ls -R release_plugins
|
||||
|
||||
- name: Upload Debug Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debug-plugins
|
||||
path: release_plugins/
|
||||
|
||||
- name: Get commit messages
|
||||
id: commits
|
||||
if: github.event_name == 'push'
|
||||
@@ -220,20 +316,20 @@ jobs:
|
||||
{
|
||||
echo 'commits<<EOF'
|
||||
echo "$COMMITS"
|
||||
echo ""
|
||||
echo 'EOF'
|
||||
} >> $GITHUB_OUTPUT
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Generate release notes
|
||||
id: notes
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
TITLE: ${{ github.event.inputs.release_title }}
|
||||
NOTES: ${{ github.event.inputs.release_notes }}
|
||||
DETECTED_CHANGES: ${{ needs.check-changes.outputs.release_notes }}
|
||||
COMMITS: ${{ steps.commits.outputs.commits }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
TITLE="${{ github.event.inputs.release_title }}"
|
||||
NOTES="${{ github.event.inputs.release_notes }}"
|
||||
DETECTED_CHANGES="${{ needs.check-changes.outputs.release_notes }}"
|
||||
COMMITS="${{ steps.commits.outputs.commits }}"
|
||||
|
||||
echo "# ${VERSION} Release / 发布" > release_notes.md
|
||||
echo "" >> release_notes.md
|
||||
> release_notes.md
|
||||
|
||||
if [ -n "$TITLE" ]; then
|
||||
echo "## $TITLE" >> release_notes.md
|
||||
@@ -241,21 +337,21 @@ jobs:
|
||||
fi
|
||||
|
||||
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 "$DETECTED_CHANGES" >> release_notes.md
|
||||
echo "" >> release_notes.md
|
||||
fi
|
||||
|
||||
if [ -n "$COMMITS" ]; then
|
||||
echo "## Commits / 提交记录" >> release_notes.md
|
||||
echo "## Commits" >> release_notes.md
|
||||
echo "" >> release_notes.md
|
||||
echo "$COMMITS" >> release_notes.md
|
||||
echo "" >> release_notes.md
|
||||
fi
|
||||
|
||||
if [ -n "$NOTES" ]; then
|
||||
echo "## Additional Notes / 附加说明" >> release_notes.md
|
||||
echo "## Additional Notes" >> release_notes.md
|
||||
echo "" >> release_notes.md
|
||||
echo "$NOTES" >> release_notes.md
|
||||
echo "" >> release_notes.md
|
||||
@@ -265,11 +361,11 @@ jobs:
|
||||
|
||||
cat >> release_notes.md << 'EOF'
|
||||
|
||||
## Download / 下载
|
||||
## Download
|
||||
|
||||
📦 **Download the updated plugin files below** / 请在下方下载更新的插件文件
|
||||
📦 **Download the updated plugin files below**
|
||||
|
||||
### Installation / 安装
|
||||
### Installation
|
||||
|
||||
#### From OpenWebUI Community
|
||||
1. Open OpenWebUI Admin Panel
|
||||
@@ -277,7 +373,7 @@ jobs:
|
||||
3. Search for the plugin name
|
||||
4. Click Install
|
||||
|
||||
#### Manual Installation / 手动安装
|
||||
#### Manual Installation
|
||||
1. Download the plugin file (`.py`) from the assets below
|
||||
2. Open OpenWebUI Admin Panel → Functions
|
||||
3. Click "Create Function" → Import
|
||||
@@ -285,26 +381,58 @@ jobs:
|
||||
|
||||
---
|
||||
|
||||
📚 [Documentation / 文档](https://fu-jie.github.io/awesome-openwebui/)
|
||||
🐛 [Report Issues / 报告问题](https://github.com/Fu-Jie/awesome-openwebui/issues)
|
||||
📚 [Documentation](https://fu-jie.github.io/openwebui-extensions/)
|
||||
🐛 [Report Issues](https://github.com/Fu-Jie/openwebui-extensions/issues)
|
||||
EOF
|
||||
|
||||
echo "=== Release Notes ==="
|
||||
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
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ steps.version.outputs.version }}
|
||||
target_commitish: ${{ github.sha }}
|
||||
name: ${{ github.event.inputs.release_title || steps.version.outputs.version }}
|
||||
body_path: release_notes.md
|
||||
prerelease: ${{ github.event.inputs.prerelease || false }}
|
||||
make_latest: true
|
||||
files: |
|
||||
plugin_versions.json
|
||||
release_plugins/**/*.py
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload Release Assets
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Check if there are any .py files to upload
|
||||
if [ -d release_plugins ] && [ -n "$(find release_plugins -type f -name '*.py' 2>/dev/null)" ]; then
|
||||
echo "Uploading plugin files..."
|
||||
find release_plugins -type f -name "*.py" -print0 | xargs -0 gh release upload ${{ steps.version.outputs.version }} --clobber
|
||||
else
|
||||
echo "No plugin files to upload. Skipping asset upload."
|
||||
fi
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "## 🚀 Release Created Successfully!" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -139,3 +139,4 @@ logs/
|
||||
|
||||
# OpenWebUI specific
|
||||
# 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.
|
||||
|
||||
如果你有一个好用的提示词:
|
||||
1. 在 `prompts/` 目录下找到合适的分类(如 `coding/`, `writing/`)。如果没有合适的,可以新建一个文件夹。
|
||||
2. 创建一个新的 `.md` 或 `.json` 文件。
|
||||
3. 提交 Pull Request (PR)。
|
||||
## 💡 Important
|
||||
|
||||
### 2. 开发插件 (Plugins)
|
||||
- Ensure your plugin includes complete metadata (title, author, version, description).
|
||||
- If updating an existing plugin, please **increment the version number** (e.g., `0.1.0` -> `0.1.1`) to trigger the auto-update.
|
||||
|
||||
如果你开发了一个新的 OpenWebUI 插件 (Function/Tool):
|
||||
1. 确保你的插件代码包含完整的元数据(Frontmatter):
|
||||
```python
|
||||
"""
|
||||
title: 插件名称
|
||||
author: 你的名字
|
||||
version: 0.1.0
|
||||
description: 简短描述插件的功能
|
||||
"""
|
||||
```
|
||||
2. 将插件文件放入 `plugins/` 目录下的合适位置:
|
||||
- `plugins/actions/`: 用于添加按钮或修改消息的 Action 插件。
|
||||
- `plugins/filters/`: 用于拦截请求或响应的 Filter 插件。
|
||||
- `plugins/pipes/`: 用于自定义模型或 API 的 Pipe 插件。
|
||||
- `plugins/tools/`: 用于 LLM 调用的 Tool 插件。
|
||||
3. 建议在 `docs/` 下添加一个简单的使用说明。
|
||||
|
||||
### 3. 改进文档
|
||||
|
||||
如果你发现文档有错误或可以改进的地方,直接提交 PR 即可。
|
||||
|
||||
## 🛠️ 开发规范
|
||||
|
||||
- **代码风格**:Python 代码请遵循 PEP 8 规范。
|
||||
- **注释**:关键逻辑请添加注释,方便他人理解。
|
||||
- **测试**:提交前请在本地 OpenWebUI 环境中测试通过。
|
||||
|
||||
## 📝 提交 PR
|
||||
|
||||
1. Fork 本仓库。
|
||||
2. 创建一个新的分支 (`git checkout -b feature/AmazingFeature`)。
|
||||
3. 提交你的修改 (`git commit -m 'Add some AmazingFeature'`)。
|
||||
4. 推送到分支 (`git push origin feature/AmazingFeature`)。
|
||||
5. 开启一个 Pull Request。
|
||||
|
||||
## 📦 版本更新与发布
|
||||
|
||||
当你更新插件时,请遵循以下流程:
|
||||
|
||||
### 1. 更新版本号
|
||||
|
||||
在插件文件的 docstring 中更新版本号(遵循[语义化版本](https://semver.org/lang/zh-CN/)):
|
||||
|
||||
```python
|
||||
"""
|
||||
title: 我的插件
|
||||
version: 0.2.0 # 更新此处
|
||||
...
|
||||
"""
|
||||
```
|
||||
|
||||
### 2. 更新更新日志
|
||||
|
||||
在 `CHANGELOG.md` 的 `[Unreleased]` 部分添加你的更改:
|
||||
|
||||
```markdown
|
||||
### Added / 新增
|
||||
- 新功能描述
|
||||
|
||||
### Fixed / 修复
|
||||
- Bug 修复描述
|
||||
```
|
||||
|
||||
### 3. 发布流程
|
||||
|
||||
维护者会通过以下方式发布新版本:
|
||||
- 手动触发 GitHub Actions 中的 "Plugin Release" 工作流
|
||||
- 或创建版本标签 (`v*`)
|
||||
|
||||
详细说明请参阅 [发布工作流文档](docs/release-workflow.zh.md)。
|
||||
|
||||
再次感谢你的贡献!🚀
|
||||
Thank you! 🚀
|
||||
|
||||
16
CONTRIBUTING_CN.md
Normal file
16
CONTRIBUTING_CN.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# 贡献指南
|
||||
|
||||
感谢你对 **OpenWebUI 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
|
||||
161
README.md
161
README.md
@@ -1,43 +1,131 @@
|
||||
# OpenWebUI Extras
|
||||
# OpenWebUI Extensions
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[](#contributors-)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
English | [中文](./README_CN.md)
|
||||
|
||||
A collection of enhancements, plugins, and prompts for [OpenWebUI](https://github.com/open-webui/open-webui), developed and curated for personal use to extend functionality and improve experience.
|
||||
|
||||
[Contributing](./CONTRIBUTING.md)
|
||||
<!-- STATS_START -->
|
||||
## 📊 Community Stats
|
||||
>
|
||||
> 
|
||||
|
||||
| 👤 Author | 👥 Followers | ⭐ Points | 🏆 Contributions |
|
||||
| :---: | :---: | :---: | :---: |
|
||||
| [Fu-Jie](https://openwebui.com/u/Fu-Jie) |  |  |  |
|
||||
|
||||
| 📝 Posts | ⬇️ Downloads | 👁️ Views | 👍 Upvotes | 💾 Saves |
|
||||
| :---: | :---: | :---: | :---: | :---: |
|
||||
|  |  |  |  |  |
|
||||
|
||||
### 🔥 Top 6 Popular Plugins
|
||||
|
||||
| Rank | Plugin | Version | Downloads | Views | 📅 Updated |
|
||||
| :---: | :--- | :---: | :---: | :---: | :---: |
|
||||
| 🥇 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) |  |  |  |  |
|
||||
| 🥈 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) |  |  |  |  |
|
||||
| 🥉 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) |  |  |  |  |
|
||||
| 4️⃣ | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) |  |  |  |  |
|
||||
| 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) |  |  |  |  |
|
||||
|
||||
### 🛠️ 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 -->
|
||||
|
||||
## 🌟 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
|
||||
|
||||
### 🧩 Plugins
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary><b>🧩 Plugins (Actions, Filters, Pipes, Pipelines)</b></summary>
|
||||
|
||||
Located in the `plugins/` directory, containing Python-based enhancements:
|
||||
|
||||
#### Actions
|
||||
### Actions
|
||||
|
||||
- **Smart Mind Map** (`smart-mind-map`): Generates interactive mind maps from text.
|
||||
- **Smart Infographic** (`infographic`): Transforms text into professional infographics using AntV.
|
||||
- **Knowledge Card** (`knowledge-card`): Creates beautiful flashcards for learning.
|
||||
- **Flash Card** (`flash-card`): Quickly generates beautiful flashcards for learning.
|
||||
- **Deep Dive** (`deep-dive`): A comprehensive thinking lens that dives deep into any content.
|
||||
- **Export to Excel** (`export_to_excel`): Exports chat history to Excel files.
|
||||
- **Export to Word** (`export_to_docx`): Exports chat history to Word documents.
|
||||
- **Summary** (`summary`): Text summarization tool.
|
||||
|
||||
#### Filters
|
||||
### Filters
|
||||
|
||||
- **GitHub Copilot SDK Files Filter** (`github_copilot_sdk_files_filter`): Essential companion for Copilot SDK. Bypasses RAG to ensure full file accessibility for Agents.
|
||||
- **Web Gemini Multimodal Filter** (`web_gemini_multimodel_filter`): Adds multimodal capabilities (PDF, Video, Office) to any model with intelligent routing.
|
||||
- **Async Context Compression** (`async-context-compression`): Optimizes token usage via context compression.
|
||||
- **Context Enhancement** (`context_enhancement_filter`): Enhances chat context.
|
||||
- **Gemini Manifold Companion** (`gemini_manifold_companion`): Companion filter for Gemini Manifold.
|
||||
- **Folder Memory** (`folder-memory`): Automatically extracts project rules from conversations and injects them into the folder's system prompt.
|
||||
- **Markdown Normalizer** (`markdown_normalizer`): Fixes common Markdown formatting issues in LLM outputs.
|
||||
|
||||
### Pipes
|
||||
|
||||
#### Pipes
|
||||
- **Gemini Manifold** (`gemini_mainfold`): Pipeline for Gemini model integration.
|
||||
- **GitHub Copilot SDK** (`github-copilot-sdk`): Official GitHub Copilot SDK integration. Supports dynamic models (GPT-4o, Claude 3.5, o1), multi-turn conversation, streaming, and infinite sessions.
|
||||
|
||||
### Pipelines
|
||||
|
||||
#### Pipelines
|
||||
- **MoE Prompt Refiner** (`moe_prompt_refiner`): Refines prompts for Mixture of Experts (MoE) summary requests to generate high-quality comprehensive reports.
|
||||
|
||||
### 🎯 Prompts
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
Located in the `prompts/` directory, containing fine-tuned System Prompts:
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary><b>🎯 Prompts (System Prompts for various roles)</b></summary>
|
||||
|
||||
- **Coding**: Programming assistance prompts.
|
||||
- **Marketing**: Marketing and copywriting prompts.
|
||||
System Prompts are managed in the `docs/prompts/` directory:
|
||||
|
||||
- **[Prompt Library](./docs/prompts/library.md)**: A curated collection of fine-tuned prompts for Coding, Marketing, and Analysis.
|
||||
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
## 🛠️ Extensions
|
||||
|
||||
Standalone frontend extensions to supercharge your Open WebUI:
|
||||
|
||||
- **[Open WebUI Prompt Plus](https://github.com/Fu-Jie/open-webui-prompt-plus)**: An all-in-one prompt management suite featuring AI-powered prompt generation, spotlight-style quick search, and advanced category organization.
|
||||
|
||||
## 📖 Documentation
|
||||
|
||||
@@ -60,14 +148,49 @@ This project is a collection of resources and does not require a Python environm
|
||||
|
||||
### Using Plugins
|
||||
|
||||
1. Browse the `/plugins` directory and download the plugin file (`.py`) you need.
|
||||
2. Go to OpenWebUI **Admin Panel** -> **Settings** -> **Plugins**.
|
||||
3. Click the upload button and select the `.py` file you just downloaded.
|
||||
4. Once uploaded, refresh the page to enable the plugin in your chat settings or toolbar.
|
||||
1. **Install from OpenWebUI Community (Recommended)**:
|
||||
- Visit my profile: [Fu-Jie's Profile](https://openwebui.com/u/Fu-Jie)
|
||||
- Browse the plugins and select the one you like.
|
||||
- Click "Get" to import it directly into your OpenWebUI instance.
|
||||
|
||||
2. **Manual Installation**:
|
||||
- Browse the `/plugins` directory and download the plugin file (`.py`) you need.
|
||||
- Go to OpenWebUI **Admin Panel** -> **Settings** -> **Plugins**.
|
||||
- Click the upload button and select the `.py` file you just downloaded.
|
||||
- Once uploaded, refresh the page to enable the plugin in your chat settings or toolbar.
|
||||
|
||||
### Contributing
|
||||
|
||||
If you have great prompts or plugins to share:
|
||||
|
||||
1. Fork this repository.
|
||||
2. Add your files to the appropriate `prompts/` or `plugins/` directory.
|
||||
3. Submit a Pull Request.
|
||||
|
||||
[Contributing](./CONTRIBUTING.md)
|
||||
|
||||
## Contributors ✨
|
||||
|
||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rbb-dev"><img src="https://avatars.githubusercontent.com/u/37469229?v=4?s=100" width="100px;" alt="rbb-dev"/><br /><sub><b>rbb-dev</b></sub></a><br /><a href="#ideas-rbb-dev" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/Fu-Jie/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!
|
||||
|
||||
167
README_CN.md
167
README_CN.md
@@ -1,100 +1,147 @@
|
||||
# OpenWebUI Extras
|
||||
# OpenWebUI Extensions
|
||||
|
||||
[English](./README.md) | 中文
|
||||
|
||||
OpenWebUI 增强功能集合。包含个人开发与收集的### 🧩 插件 (Plugins)
|
||||
OpenWebUI 增强功能集合。包含个人开发与收集的插件、提示词等资源。
|
||||
|
||||
位于 `plugins/` 目录,包含各类 Python 编写的功能增强插件:
|
||||
<!-- STATS_START -->
|
||||
## 📊 社区统计
|
||||
>
|
||||
> 
|
||||
|
||||
#### Actions (交互增强)
|
||||
- **Smart Mind Map** (`smart-mind-map`): 智能分析文本并生成交互式思维导图。
|
||||
- **Smart Infographic** (`infographic`): 基于 AntV 的智能信息图生成工具。
|
||||
- **Knowledge Card** (`knowledge-card`): 快速生成精美的学习记忆卡片。
|
||||
- **Export to Excel** (`export_to_excel`): 将对话内容导出为 Excel 文件。
|
||||
- **Export to Word** (`export_to_docx`): 将对话内容导出为 Word 文档。
|
||||
- **Summary** (`summary`): 文本摘要生成工具。
|
||||
| 👤 作者 | 👥 粉丝 | ⭐ 积分 | 🏆 贡献 |
|
||||
| :---: | :---: | :---: | :---: |
|
||||
| [Fu-Jie](https://openwebui.com/u/Fu-Jie) |  |  |  |
|
||||
|
||||
#### Filters (消息处理)
|
||||
- **Async Context Compression** (`async-context-compression`): 异步上下文压缩,优化 Token 使用。
|
||||
- **Context Enhancement** (`context_enhancement_filter`): 上下文增强过滤器。
|
||||
- **Gemini Manifold Companion** (`gemini_manifold_companion`): Gemini Manifold 配套增强。
|
||||
| 📝 发布 | ⬇️ 下载 | 👁️ 浏览 | 👍 点赞 | 💾 收藏 |
|
||||
| :---: | :---: | :---: | :---: | :---: |
|
||||
|  |  |  |  |  |
|
||||
|
||||
### 🔥 热门插件 Top 6
|
||||
|
||||
#### Pipes (模型管道)
|
||||
- **Gemini Manifold** (`gemini_mainfold`): 集成 Gemini 模型的管道。
|
||||
| 排名 | 插件 | 版本 | 下载 | 浏览 | 📅 更新 |
|
||||
| :---: | :--- | :---: | :---: | :---: | :---: |
|
||||
| 🥇 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) |  |  |  |  |
|
||||
| 🥈 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) |  |  |  |  |
|
||||
| 🥉 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) |  |  |  |  |
|
||||
| 4️⃣ | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) |  |  |  |  |
|
||||
| 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) |  |  |  |  |
|
||||
|
||||
#### Pipelines (工作流管道)
|
||||
- **MoE Prompt Refiner** (`moe_prompt_refiner`): 优化多模型 (MoE) 汇总请求的提示词,生成高质量的综合报告。
|
||||
### 🛠️ 积极开发的项目
|
||||
|
||||
### 🎯 提示词 (Prompts)
|
||||
| 状态 | 插件 | 版本 | 下载 | 浏览 | 📅 更新 |
|
||||
| :---: | :--- | :---: | :---: | :---: | :---: |
|
||||
| 🆕 | [GitHub Copilot SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) |  |  |  |  |
|
||||
|
||||
位于 `prompts/` 目录,包含精心调优的 System Prompts:
|
||||
### 📈 总下载量累计趋势
|
||||
|
||||
- **Coding**: 编程辅助类提示词。
|
||||
- **Marketing**: 营销文案类提示词。(`/prompts/marketing`): 内容创作、品牌策划、市场分析相关的提示词
|
||||

|
||||
|
||||
每个提示词都独立保存为 Markdown 文件,可直接在 OpenWebUI 中使用。
|
||||
*完整统计与趋势图请查看 [社区统计报告](./docs/community-stats.zh.md)*
|
||||
<!-- STATS_END -->
|
||||
|
||||
### 🔧 插件 (Plugins)
|
||||
## 🌟 精选功能
|
||||
|
||||
{{ ... }}
|
||||
### 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)
|
||||
|
||||
[贡献指南](./CONTRIBUTING.md) | [更新日志](./CHANGELOG.md)
|
||||
**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 消耗。
|
||||
|
||||
## 📦 项目内容
|
||||
|
||||
### 🎯 提示词 (Prompts)
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary><b>🧩 插件 (Actions, Filters, Pipes, Pipelines)</b></summary>
|
||||
|
||||
位于 `/prompts` 目录,包含针对不同领域的优质提示词模板:
|
||||
位于 `plugins/` 目录,包含各类 Python 编写的功能增强插件:
|
||||
|
||||
- **编程类** (`/prompts/coding`): 代码生成、调试、优化相关的提示词
|
||||
- **营销类** (`/prompts/marketing`): 内容创作、品牌策划、市场分析相关的提示词
|
||||
### Actions (交互增强)
|
||||
|
||||
每个提示词都独立保存为 Markdown 文件,可直接在 OpenWebUI 中使用。
|
||||
- **Smart Mind Map** (`smart-mind-map`): 智能分析文本并生成交互式思维导图。
|
||||
- **Smart Infographic** (`infographic`): 基于 AntV 的智能信息图生成工具。
|
||||
- **Flash Card** (`flash-card`): 快速生成精美的学习记忆卡片。
|
||||
- **Deep Dive** (`deep-dive`): 深度思考透镜,从背景、逻辑、洞察到行动路径的全方位分析。
|
||||
- **Export to Excel** (`export_to_excel`): 将对话内容导出为 Excel 文件。
|
||||
- **Export to Word** (`export_to_docx`): 将对话内容导出为 Word 文档。
|
||||
|
||||
### 🔧 插件 (Plugins)
|
||||
### Filters (消息处理)
|
||||
|
||||
位于 `/plugins` 目录,提供三种类型的插件扩展:
|
||||
- **GitHub Copilot SDK Files Filter** (`github_copilot_sdk_files_filter`): Copilot SDK 必备搭档。绕过 RAG,确保 Agent 能真正看到你的每一个文件。
|
||||
- **Web Gemini Multimodal Filter** (`web_gemini_multimodel_filter`): 为任意模型提供多模态能力(PDF、Office、视频等),支持智能路由。
|
||||
- **Async Context Compression** (`async-context-compression`): 异步上下文压缩,优化 Token 使用。
|
||||
- **Context Enhancement** (`context_enhancement_filter`): 上下文增强过滤器。
|
||||
- **Folder Memory** (`folder-memory`): 自动从对话中提取项目规则并注入到文件夹系统提示词中。
|
||||
- **Markdown Normalizer** (`markdown_normalizer`): 修复 LLM 输出中常见的 Markdown 格式问题。
|
||||
|
||||
- **过滤器 (Filters)** - 在用户输入发送给 LLM 前进行处理和优化
|
||||
- 异步上下文压缩:智能压缩长上下文,优化 token 使用效率
|
||||
### Pipes (模型管道)
|
||||
|
||||
- **动作 (Actions)** - 自定义功能,从聊天中触发
|
||||
- 思维导图生成:快速生成和导出思维导图
|
||||
- **GitHub Copilot SDK** (`github-copilot-sdk`): GitHub Copilot SDK 官方集成。支持动态模型、多轮对话、流式输出、图片输入及无限会话。
|
||||
|
||||
- **管道 (Pipes)** - 对 LLM 响应进行处理和增强
|
||||
- 各类响应处理和格式化插件
|
||||
### Pipelines (工作流管道)
|
||||
|
||||
- **MoE Prompt Refiner** (`moe_prompt_refiner`): 优化多模型 (MoE) 汇总请求的提示词,生成高质量的综合报告。
|
||||
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary><b>🎯 提示词 (Prompts - 多角色系统提示词)</b></summary>
|
||||
|
||||
位于 `docs/prompts/` 目录,包含精心调优的提示词集合:
|
||||
|
||||
- **[Prompt Library](./docs/prompts/library.md)**: 编程、翻译、分析及营销等全领域提示词精选。
|
||||
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
## 🛠️ 扩展 (Extensions)
|
||||
|
||||
Open WebUI 的前端增强扩展:
|
||||
|
||||
- **[Open WebUI Prompt Plus](https://github.com/Fu-Jie/open-webui-prompt-plus)**: 一站式提示词管理套件,支持 AI 提示词生成、Spotlight 风格快速搜索及高级分类管理。
|
||||
|
||||
## 📖 开发文档
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary><b>📚 官方开发与运营指南</b></summary>
|
||||
|
||||
位于 `docs/zh/` 目录:
|
||||
|
||||
- **[插件开发权威指南](./docs/zh/plugin_development_guide.md)** - 整合了入门教程、核心 SDK 详解及最佳实践的系统化指南。 ⭐
|
||||
- **[从问一个AI到运营一支AI团队](./docs/zh/从问一个AI到运营一支AI团队.md)** - 深度运营经验分享。
|
||||
|
||||
更多示例请查看 `docs/examples/` 目录。
|
||||
|
||||
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
本项目是一个资源集合,无需安装 Python 环境。你只需要下载对应的文件并导入到你的 OpenWebUI 实例中即可。
|
||||
|
||||
### 使用提示词 (Prompts)
|
||||
|
||||
1. 在 `/prompts` 目录中浏览并选择你感兴趣的提示词文件 (`.md`)。
|
||||
2. 复制文件内容。
|
||||
3. 在 OpenWebUI 聊天界面中,点击输入框上方的 "Prompt" 按钮。
|
||||
4. 粘贴内容并保存。
|
||||
|
||||
### 使用插件 (Plugins)
|
||||
|
||||
1. 在 `/plugins` 目录中浏览并下载你需要的插件文件 (`.py`)。
|
||||
2. 打开 OpenWebUI 的 **管理员面板 (Admin Panel)** -> **设置 (Settings)** -> **插件 (Plugins)**。
|
||||
3. 点击上传按钮,选择刚才下载的 `.py` 文件。
|
||||
4. 上传成功后,刷新页面,你就可以在聊天设置或工具栏中启用该插件了。
|
||||
|
||||
### 贡献代码
|
||||
|
||||
如果你有优质的提示词或插件想要分享:
|
||||
1. Fork 本仓库。
|
||||
2. 将你的文件添加到对应的 `prompts/` 或 `plugins/` 目录。
|
||||
3. 提交 Pull Request。
|
||||
[贡献指南](./CONTRIBUTING_CN.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"
|
||||
}
|
||||
399
docs/community-stats.json
Normal file
399
docs/community-stats.json
Normal file
@@ -0,0 +1,399 @@
|
||||
{
|
||||
"total_posts": 23,
|
||||
"total_downloads": 5629,
|
||||
"total_views": 61059,
|
||||
"total_upvotes": 241,
|
||||
"total_downvotes": 3,
|
||||
"total_saves": 321,
|
||||
"total_comments": 55,
|
||||
"by_type": {
|
||||
"action": 12,
|
||||
"post": 4,
|
||||
"pipe": 1,
|
||||
"filter": 4,
|
||||
"prompt": 1,
|
||||
"review": 1
|
||||
},
|
||||
"posts": [
|
||||
{
|
||||
"title": "Smart Mind Map",
|
||||
"slug": "turn_any_text_into_beautiful_mind_maps_3094c59a",
|
||||
"type": "action",
|
||||
"version": "1.0.0",
|
||||
"author": "Fu-Jie",
|
||||
"description": "Intelligently analyzes text content and generates interactive mind maps to help users structure and visualize knowledge.",
|
||||
"downloads": 1207,
|
||||
"views": 10434,
|
||||
"upvotes": 22,
|
||||
"saves": 59,
|
||||
"comments": 13,
|
||||
"created_at": "2025-12-30",
|
||||
"updated_at": "2026-02-22",
|
||||
"url": "https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a"
|
||||
},
|
||||
{
|
||||
"title": "Smart Infographic",
|
||||
"slug": "smart_infographic_ad6f0c7f",
|
||||
"type": "action",
|
||||
"version": "1.5.0",
|
||||
"author": "Fu-Jie",
|
||||
"description": "AI-powered infographic generator based on AntV Infographic. Supports professional templates, auto-icon matching, and SVG/PNG downloads.",
|
||||
"downloads": 966,
|
||||
"views": 9501,
|
||||
"upvotes": 24,
|
||||
"saves": 39,
|
||||
"comments": 10,
|
||||
"created_at": "2025-12-28",
|
||||
"updated_at": "2026-02-13",
|
||||
"url": "https://openwebui.com/posts/smart_infographic_ad6f0c7f"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"type": "action",
|
||||
"version": "0.4.4",
|
||||
"author": "Fu-Jie",
|
||||
"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": 511,
|
||||
"views": 4090,
|
||||
"upvotes": 15,
|
||||
"saves": 29,
|
||||
"comments": 5,
|
||||
"created_at": "2026-01-03",
|
||||
"updated_at": "2026-02-13",
|
||||
"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": "智能信息图",
|
||||
"slug": "智能信息图_e04a48ff",
|
||||
"type": "action",
|
||||
"version": "1.5.0",
|
||||
"author": "Fu-Jie",
|
||||
"description": "基于 AntV Infographic 的智能信息图生成插件。支持多种专业模板,自动图标匹配,并提供 SVG/PNG 下载功能。",
|
||||
"downloads": 62,
|
||||
"views": 1235,
|
||||
"upvotes": 10,
|
||||
"saves": 1,
|
||||
"comments": 0,
|
||||
"created_at": "2025-12-28",
|
||||
"updated_at": "2026-02-13",
|
||||
"url": "https://openwebui.com/posts/智能信息图_e04a48ff"
|
||||
},
|
||||
{
|
||||
"title": "思维导图",
|
||||
"slug": "智能生成交互式思维导图帮助用户可视化知识_8d4b097b",
|
||||
"type": "action",
|
||||
"version": "0.9.2",
|
||||
"author": "Fu-Jie",
|
||||
"description": "智能分析文本内容,生成交互式思维导图,帮助用户结构化和可视化知识。",
|
||||
"downloads": 41,
|
||||
"views": 658,
|
||||
"upvotes": 6,
|
||||
"saves": 2,
|
||||
"comments": 0,
|
||||
"created_at": "2025-12-31",
|
||||
"updated_at": "2026-02-13",
|
||||
"url": "https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b"
|
||||
},
|
||||
{
|
||||
"title": "异步上下文压缩",
|
||||
"slug": "异步上下文压缩_5c0617cb",
|
||||
"type": "action",
|
||||
"version": "1.2.2",
|
||||
"author": "Fu-Jie",
|
||||
"description": "通过智能摘要和消息压缩,降低长对话的 token 消耗,同时保持对话连贯性。",
|
||||
"downloads": 36,
|
||||
"views": 748,
|
||||
"upvotes": 7,
|
||||
"saves": 5,
|
||||
"comments": 0,
|
||||
"created_at": "2025-11-08",
|
||||
"updated_at": "2026-02-13",
|
||||
"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": {
|
||||
"username": "Fu-Jie",
|
||||
"name": "Fu-Jie",
|
||||
"profile_url": "https://openwebui.com/u/Fu-Jie",
|
||||
"profile_image": "https://community.s3.openwebui.com/uploads/users/b15d1348-4347-42b4-b815-e053342d6cb0/profile_d9510745-4bd4-4f8f-a997-4a21847d9300.webp",
|
||||
"followers": 270,
|
||||
"following": 5,
|
||||
"total_points": 285,
|
||||
"post_points": 238,
|
||||
"comment_points": 47,
|
||||
"contributions": 51
|
||||
}
|
||||
}
|
||||
61
docs/community-stats.md
Normal file
61
docs/community-stats.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# 📊 OpenWebUI Community Stats Report
|
||||
|
||||
> 
|
||||
|
||||
### 📈 Total Downloads Trend
|
||||

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

|
||||
|
||||
|
||||
## 📈 Overview
|
||||
|
||||
| Metric | Value |
|
||||
|------|------|
|
||||
| 📝 Total Posts |  |
|
||||
| ⬇️ Total Downloads |  |
|
||||
| 👁️ Total Views |  |
|
||||
| 👍 Total Upvotes |  |
|
||||
| 💾 Total Saves |  |
|
||||
| ⭐ Author Points |  |
|
||||
| 👥 Followers |  |
|
||||
|
||||
## 📂 By Type
|
||||
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
|
||||
## 📋 Posts List
|
||||
|
||||
| Rank | Title | Type | Version | Downloads | Views | Upvotes | Saves | Updated |
|
||||
|:---:|------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 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 |
|
||||
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
|
||||
|
||||
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!
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
# Clone the repository
|
||||
git clone https://github.com/Fu-Jie/awesome-openwebui.git
|
||||
cd awesome-openwebui
|
||||
git clone https://github.com/Fu-Jie/openwebui-extensions.git
|
||||
cd openwebui-extensions
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
@@ -122,7 +122,7 @@ Step-by-step usage instructions.
|
||||
|
||||
## 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
|
||||
# 克隆仓库
|
||||
git clone https://github.com/Fu-Jie/awesome-openwebui.git
|
||||
cd awesome-openwebui
|
||||
git clone https://github.com/Fu-Jie/openwebui-extensions.git
|
||||
cd openwebui-extensions
|
||||
|
||||
# 安装依赖
|
||||
pip install -r requirements.txt
|
||||
@@ -122,7 +122,7 @@ Step-by-step usage instructions.
|
||||
|
||||
## 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
|
||||
|
||||
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)
|
||||
|
||||
- :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**
|
||||
|
||||
---
|
||||
@@ -164,5 +172,6 @@ user_language = __user__.get("language", "en-US")
|
||||
## Resources
|
||||
|
||||
- [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/)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 开发指南
|
||||
|
||||
了解如何开发插件并为 OpenWebUI Extras 做出贡献。
|
||||
了解如何开发插件并为 OpenWebUI Extensions 做出贡献。
|
||||
|
||||
---
|
||||
|
||||
@@ -24,6 +24,14 @@
|
||||
|
||||
[: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 } **贡献指南**
|
||||
|
||||
---
|
||||
@@ -164,5 +172,6 @@ user_language = __user__.get("language", "en-US")
|
||||
## 资源
|
||||
|
||||
- [完整开发指南](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/)
|
||||
|
||||
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
|
||||
|
||||
1. [Quick Start](#1-quick-start)
|
||||
2. [Core Concepts & SDK Details](#2-core-concepts--sdk-details)
|
||||
3. [Deep Dive into Plugin Types](#3-deep-dive-into-plugin-types)
|
||||
4. [Advanced Development Patterns](#4-advanced-development-patterns)
|
||||
5. [Best Practices & Design Principles](#5-best-practices--design-principles)
|
||||
6. [Troubleshooting](#6-troubleshooting)
|
||||
2. [Project Structure & Naming](#2-project-structure--naming)
|
||||
3. [Core Concepts & SDK Details](#3-core-concepts--sdk-details)
|
||||
4. [Deep Dive into Plugin Types](#4-deep-dive-into-plugin-types)
|
||||
5. [Advanced Development Patterns](#5-advanced-development-patterns)
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
- **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:
|
||||
|
||||
@@ -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_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
|
||||
- **`UserValves`**: User-level configuration (higher priority, overrides global)
|
||||
Use Pydantic BaseModel to define configurable parameters. All Valves fields must use **UPPER_SNAKE_CASE**.
|
||||
|
||||
```python
|
||||
class Filter:
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class Action:
|
||||
class Valves(BaseModel):
|
||||
API_KEY: str = Field(default="", description="Global API Key")
|
||||
|
||||
class UserValves(BaseModel):
|
||||
API_KEY: str = Field(default="", description="User Private API Key")
|
||||
|
||||
def inlet(self, body, __user__):
|
||||
# Prioritize user's Key
|
||||
user_valves = __user__.get("valves", self.UserValves())
|
||||
api_key = user_valves.API_KEY or self.valves.API_KEY
|
||||
SHOW_STATUS: bool = Field(default=True, description="Whether to show operation status updates.")
|
||||
# ...
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
@@ -136,7 +181,7 @@ async def action(self, body, __event_call__):
|
||||
await __event_call__({"type": "execute", "data": {"code": js}})
|
||||
```
|
||||
|
||||
### 3.2 Filter
|
||||
### 4.2 Filter
|
||||
|
||||
**Role**: Middleware that intercepts and modifies requests/responses.
|
||||
|
||||
@@ -157,7 +202,7 @@ async def inlet(self, body, __metadata__):
|
||||
return body
|
||||
```
|
||||
|
||||
### 3.3 Pipe
|
||||
### 4.3 Pipe
|
||||
|
||||
**Role**: Custom Model/Agent.
|
||||
|
||||
@@ -182,18 +227,27 @@ class Pipe:
|
||||
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:
|
||||
|
||||
- **Pipe**: `__request__.app.state.search_results = [...]`
|
||||
- **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:
|
||||
|
||||
@@ -209,7 +263,7 @@ async def background_job(self, chat_id):
|
||||
pass
|
||||
```
|
||||
|
||||
### 4.3 Calling Built-in LLM
|
||||
### 5.3 Calling Built-in LLM
|
||||
|
||||
```python
|
||||
from open_webui.utils.chat import generate_chat_completion
|
||||
@@ -235,22 +289,52 @@ llm_response = await generate_chat_completion(
|
||||
)
|
||||
```
|
||||
|
||||
### 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:
|
||||
|
||||
#### Workflow
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ 1. Python Action │
|
||||
│ ├── Analyze message content │
|
||||
│ ├── Call LLM to generate structured data (optional) │
|
||||
│ └── Send JS code to frontend via __event_call__ │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ 2. Browser JS (via __event_call__) │
|
||||
│ ├── Dynamically load visualization library │
|
||||
│ ├── Render SVG/Canvas offscreen │
|
||||
│ ├── Export to Base64 Data URL via toDataURL() │
|
||||
│ └── Update message content via REST API │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ 3. Markdown Rendering │
|
||||
│ └── Display  │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 5.5 Agent File Delivery Standards (3-Step Delivery Protocol)
|
||||
|
||||
1. **Write Local**: Create files in the current execution directory (`.`).
|
||||
2. **Publish**: Call `publish_file_from_workspace(filename='name.ext')`.
|
||||
3. **Display Link**: Present the returned `download_url` as a Markdown link.
|
||||
|
||||
---
|
||||
|
||||
## 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"
|
||||
- **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
|
||||
- **Visual Appeal**: When Action outputs HTML, use modern CSS (rounded corners, shadows, gradients)
|
||||
- **Smart Guidance**: If text is too short, prompt the user: "Suggest entering more content for better results"
|
||||
|
||||
### 5.3 Error Handling
|
||||
### 6.3 Error Handling
|
||||
|
||||
!!! danger "Never fail silently"
|
||||
Always catch exceptions and inform the user via `__event_emitter__`.
|
||||
@@ -266,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?"
|
||||
Ensure it's wrapped in a ` ```html ... ``` ` code block.
|
||||
@@ -288,6 +402,6 @@ except Exception as e:
|
||||
|
||||
## 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-forum: Community Discussions](https://github.com/open-webui/open-webui/discussions)
|
||||
|
||||
@@ -4,26 +4,28 @@
|
||||
|
||||
## 📚 目录
|
||||
|
||||
1. [插件开发快速入门](#1-插件开发快速入门)
|
||||
2. [核心概念与 SDK 详解](#2-核心概念与-sdk-详解)
|
||||
3. [插件类型深度解析](#3-插件类型深度解析)
|
||||
* [Action (动作)](#31-action-动作)
|
||||
* [Filter (过滤器)](#32-filter-过滤器)
|
||||
* [Pipe (管道)](#33-pipe-管道)
|
||||
4. [高级开发模式](#4-高级开发模式)
|
||||
5. [最佳实践与设计原则](#5-最佳实践与设计原则)
|
||||
6. [故障排查](#6-故障排查)
|
||||
1. [插件开发快速入门](#1-quick-start)
|
||||
2. [核心概念与 SDK 详解](#2-core-concepts-sdk-details)
|
||||
3. [插件类型深度解析](#3-plugin-types)
|
||||
* [Action (动作)](#31-action)
|
||||
* [Filter (过滤器)](#32-filter)
|
||||
* [Pipe (管道)](#33-pipe)
|
||||
4. [高级开发模式](#4-advanced-patterns)
|
||||
5. [最佳实践与设计原则](#5-best-practices)
|
||||
6. [仓库规范(openwebui-extensions)](#6-repo-standards)
|
||||
7. [故障排查](#7-troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## 1. 插件开发快速入门
|
||||
## 1. 插件开发快速入门 {: #1-quick-start }
|
||||
|
||||
### 1.1 什么是 OpenWebUI 插件?
|
||||
|
||||
OpenWebUI 插件(官方称为 "Functions")是扩展平台功能的主要方式。它们运行在后端 Python 环境中,允许你:
|
||||
* 🔌 **集成新模型**:通过 Pipe 接入 Claude、Gemini 或自定义 RAG。
|
||||
* 🎨 **增强交互**:通过 Action 在消息旁添加按钮(如"导出"、"生成图表")。
|
||||
* 🔧 **干预流程**:通过 Filter 在请求前后修改数据(如注入上下文、敏感词过滤)。
|
||||
|
||||
* 🔌 **集成新模型**:通过 Pipe 接入 Claude、Gemini 或自定义 RAG。
|
||||
* 🎨 **增强交互**:通过 Action 在消息旁添加按钮(如"导出"、"生成图表")。
|
||||
* 🔧 **干预流程**:通过 Filter 在请求前后修改数据(如注入上下文、敏感词过滤)。
|
||||
|
||||
### 1.2 你的第一个插件 (Hello World)
|
||||
|
||||
@@ -64,14 +66,15 @@ class Action:
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心概念与 SDK 详解
|
||||
## 2. 核心概念与 SDK 详解 {: #2-core-concepts-sdk-details }
|
||||
|
||||
### 2.1 ⚠️ 重要:同步与异步
|
||||
|
||||
OpenWebUI 插件运行在 `asyncio` 事件循环中。
|
||||
* **原则**:所有 I/O 操作(数据库、文件、网络)必须非阻塞。
|
||||
* **陷阱**:直接调用同步方法(如 `time.sleep`, `requests.get`)会卡死整个服务器。
|
||||
* **解决**:使用 `await asyncio.to_thread(sync_func, ...)` 包装同步调用。
|
||||
|
||||
* **原则**:所有 I/O 操作(数据库、文件、网络)必须非阻塞。
|
||||
* **陷阱**:直接调用同步方法(如 `time.sleep`, `requests.get`)会卡死整个服务器。
|
||||
* **解决**:使用 `await asyncio.to_thread(sync_func, ...)` 包装同步调用。
|
||||
|
||||
### 2.2 核心参数详解
|
||||
|
||||
@@ -88,8 +91,8 @@ OpenWebUI 插件运行在 `asyncio` 事件循环中。
|
||||
|
||||
### 2.3 配置系统 (Valves)
|
||||
|
||||
* **`Valves`**: 管理员全局配置。
|
||||
* **`UserValves`**: 用户级配置(优先级更高,可覆盖全局)。
|
||||
* **`Valves`**: 管理员全局配置。
|
||||
* **`UserValves`**: 用户级配置(优先级更高,可覆盖全局)。
|
||||
|
||||
```python
|
||||
class Filter:
|
||||
@@ -107,13 +110,13 @@ class Filter:
|
||||
|
||||
---
|
||||
|
||||
## 3. 插件类型深度解析
|
||||
## 3. 插件类型深度解析 {: #3-plugin-types }
|
||||
|
||||
### 3.1 Action (动作)
|
||||
### 3.1 Action (动作) {: #31-action }
|
||||
|
||||
**定位**:在消息下方添加按钮,用户点击触发。
|
||||
|
||||
**高级用法:前端执行 JavaScript (文件下载示例)**
|
||||
#### 高级用法:前端执行 JavaScript (文件下载示例)
|
||||
|
||||
```python
|
||||
import base64
|
||||
@@ -134,15 +137,15 @@ async def action(self, body, __event_call__):
|
||||
await __event_call__({"type": "execute", "data": {"code": js}})
|
||||
```
|
||||
|
||||
### 3.2 Filter (过滤器)
|
||||
### 3.2 Filter (过滤器) {: #32-filter }
|
||||
|
||||
**定位**:中间件,拦截并修改请求/响应。
|
||||
|
||||
* **`inlet`**: 请求前。用于注入上下文、修改模型参数。
|
||||
* **`outlet`**: 响应后。用于格式化输出、保存日志。
|
||||
* **`stream`**: 流式处理中。用于实时敏感词过滤。
|
||||
* **`inlet`**: 请求前。用于注入上下文、修改模型参数。
|
||||
* **`outlet`**: 响应后。用于格式化输出、保存日志。
|
||||
* **`stream`**: 流式处理中。用于实时敏感词过滤。
|
||||
|
||||
**示例:注入环境变量**
|
||||
#### 示例:注入环境变量
|
||||
|
||||
```python
|
||||
async def inlet(self, body, __metadata__):
|
||||
@@ -155,11 +158,11 @@ async def inlet(self, body, __metadata__):
|
||||
return body
|
||||
```
|
||||
|
||||
### 3.3 Pipe (管道)
|
||||
### 3.3 Pipe (管道) {: #33-pipe }
|
||||
|
||||
**定位**:自定义模型/代理。
|
||||
|
||||
**示例:简单的 OpenAI 代理**
|
||||
#### 示例:简单的 OpenAI 代理
|
||||
|
||||
```python
|
||||
import requests
|
||||
@@ -177,14 +180,17 @@ class Pipe:
|
||||
|
||||
---
|
||||
|
||||
## 4. 高级开发模式
|
||||
## 4. 高级开发模式 {: #4-advanced-patterns }
|
||||
|
||||
### 4.1 Pipe 与 Filter 协同
|
||||
|
||||
利用 `__request__.app.state` 在不同插件间共享数据。
|
||||
* **Pipe**: `__request__.app.state.search_results = [...]`
|
||||
* **Filter (Outlet)**: 读取 `search_results` 并将其格式化为引用链接附加到回复末尾。
|
||||
|
||||
* **Pipe**: `__request__.app.state.search_results = [...]`
|
||||
* **Filter (Outlet)**: 读取 `search_results` 并将其格式化为引用链接附加到回复末尾。
|
||||
|
||||
### 4.2 异步后台任务
|
||||
|
||||
不阻塞用户响应,在后台执行耗时操作(如生成总结、存库)。
|
||||
|
||||
```python
|
||||
@@ -199,20 +205,139 @@ async def background_job(self, chat_id):
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
### 4.3 JS 渲染并嵌入 Markdown (Data URL 嵌入)
|
||||
|
||||
## 5. 最佳实践与设计原则
|
||||
对于需要复杂前端渲染(如 AntV 图表、Mermaid 图表)但希望结果**持久化为纯 Markdown 格式**的场景,推荐使用 Data URL 嵌入模式:
|
||||
|
||||
#### 工作流程
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ 1. Python Action │
|
||||
│ ├── 分析消息内容 │
|
||||
│ ├── 调用 LLM 生成结构化数据(可选) │
|
||||
│ └── 通过 __event_call__ 发送 JS 代码到前端 │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ 2. Browser JS (通过 __event_call__) │
|
||||
│ ├── 动态加载可视化库 │
|
||||
│ ├── 离屏渲染 SVG/Canvas │
|
||||
│ ├── 使用 toDataURL() 导出 Base64 Data URL │
|
||||
│ └── 通过 REST API 更新消息内容 │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ 3. Markdown 渲染 │
|
||||
│ └── 显示  │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Python 端(发送 JS 执行)
|
||||
|
||||
```python
|
||||
async def action(self, body, __event_call__, __metadata__, ...):
|
||||
chat_id = self._extract_chat_id(body, __metadata__)
|
||||
message_id = self._extract_message_id(body, __metadata__)
|
||||
|
||||
# 生成 JS 代码
|
||||
js_code = self._generate_js_code(
|
||||
chat_id=chat_id,
|
||||
message_id=message_id,
|
||||
data=processed_data,
|
||||
)
|
||||
|
||||
# 执行 JS
|
||||
if __event_call__:
|
||||
await __event_call__({
|
||||
"type": "execute",
|
||||
"data": {"code": js_code}
|
||||
})
|
||||
```
|
||||
|
||||
#### JavaScript 端(渲染并回写)
|
||||
|
||||
```javascript
|
||||
(async function() {
|
||||
// 1. 加载可视化库
|
||||
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. 创建离屏容器
|
||||
const container = document.createElement('div');
|
||||
container.style.cssText = 'position:absolute;left:-9999px;';
|
||||
document.body.appendChild(container);
|
||||
|
||||
// 3. 渲染可视化
|
||||
const instance = new VisualizationLib({ container });
|
||||
instance.render(data);
|
||||
|
||||
// 4. 导出为 Data URL
|
||||
const dataUrl = await instance.toDataURL({ type: 'svg', embedResources: true });
|
||||
|
||||
// 5. 清理
|
||||
instance.destroy();
|
||||
document.body.removeChild(container);
|
||||
|
||||
// 6. 生成 Markdown 图片
|
||||
const markdownImage = ``;
|
||||
|
||||
// 7. 通过 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 }
|
||||
})
|
||||
});
|
||||
})();
|
||||
```
|
||||
|
||||
#### 优势
|
||||
|
||||
* **纯 Markdown 输出**:结果是标准的 Markdown 图片语法,无需 HTML 代码块
|
||||
* **自包含**:图片以 Base64 Data URL 嵌入,无外部依赖
|
||||
* **持久化**:通过 API 回写,消息重新加载后图片仍然存在
|
||||
* **跨平台**:任何支持 Markdown 图片的客户端都能显示
|
||||
|
||||
#### HTML 注入 vs JS 渲染嵌入 Markdown
|
||||
|
||||
| 特性 | HTML 注入 | JS 渲染 + Markdown 图片 |
|
||||
|------|----------|------------------------|
|
||||
| 输出格式 | HTML 代码块 | Markdown 图片 |
|
||||
| 交互性 | ✅ 支持按钮、动画 | ❌ 静态图片 |
|
||||
| 外部依赖 | 需要加载 JS 库 | 无(图片自包含) |
|
||||
| 持久化 | 依赖浏览器渲染 | ✅ 永久可见 |
|
||||
| 文件导出 | 需特殊处理 | ✅ 直接导出 |
|
||||
| 适用场景 | 交互式内容 | 信息图、图表快照 |
|
||||
|
||||
#### 参考实现
|
||||
|
||||
* `plugins/actions/infographic/infographic.py` - 基于 AntV + Data URL 的生产级实现
|
||||
|
||||
## 5. 最佳实践与设计原则 {: #5-best-practices }
|
||||
|
||||
### 5.1 命名与定位
|
||||
* **简短有力**:如 "闪记卡", "精读"。避免 "文本分析助手" 这种泛词。
|
||||
* **功能互补**:不要重复造轮子,明确你的插件解决了什么特定问题。
|
||||
|
||||
* **简短有力**:如 "闪记卡", "精读"。避免 "文本分析助手" 这种泛词。
|
||||
* **功能互补**:不要重复造轮子,明确你的插件解决了什么特定问题。
|
||||
|
||||
### 5.2 用户体验 (UX)
|
||||
* **反馈及时**:耗时操作前先发送 `notification` ("正在生成...")。
|
||||
* **视觉美观**:Action 输出 HTML 时,使用现代化的 CSS(圆角、阴影、渐变)。
|
||||
* **智能引导**:检测到文本过短时,提示用户"建议输入更多内容以获得更好结果"。
|
||||
|
||||
* **反馈及时**:耗时操作前先发送 `notification` ("正在生成...")。
|
||||
* **视觉美观**:Action 输出 HTML 时,使用现代化的 CSS(圆角、阴影、渐变)。
|
||||
* **智能引导**:检测到文本过短时,提示用户"建议输入更多内容以获得更好结果"。
|
||||
|
||||
### 5.3 错误处理
|
||||
|
||||
永远不要让插件静默失败。捕获异常并通过 `__event_emitter__` 告知用户。
|
||||
|
||||
```python
|
||||
@@ -227,8 +352,76 @@ except Exception as e:
|
||||
|
||||
---
|
||||
|
||||
## 6. 故障排查
|
||||
## 6. 仓库规范(openwebui-extensions) {: #6-repo-standards }
|
||||
|
||||
* **HTML 不显示?** 确保包裹在 ` ```html ... ``` ` 代码块中。
|
||||
* **数据库报错?** 检查是否在 `async` 函数中直接调用了同步的 DB 方法,请使用 `asyncio.to_thread`。
|
||||
* **参数未生效?** 检查 `Valves` 定义是否正确,以及是否被 `UserValves` 覆盖。
|
||||
### 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 ... ``` ` 代码块中。
|
||||
* **数据库报错?** 检查是否在 `async` 函数中直接调用了同步的 DB 方法,请使用 `asyncio.to_thread`。
|
||||
* **参数未生效?** 检查 `Valves` 定义是否正确,以及是否被 `UserValves` 覆盖。
|
||||
|
||||
@@ -1,50 +1,46 @@
|
||||
# 开源项目重组实施计划
|
||||
# Implementation Plan (Current)
|
||||
|
||||
## 1. 目标
|
||||
将 `openwebui-extras` 打造为一个 **OpenWebUI 增强功能集合库**,专注于分享个人开发和收集的优质插件、提示词,而非作为一个独立的 Python 应用程序发布。
|
||||
## 1. Objective
|
||||
|
||||
## 2. 当前状态分析
|
||||
- **定位明确**:项目核心价值在于内容(Plugins, Prompts, Docs),而非运行环境。
|
||||
- **结构已优化**:
|
||||
- `plugins/`:核心插件资源。
|
||||
- `prompts/`:提示词资源。
|
||||
- `docs/`:详细的使用和开发文档。
|
||||
- `scripts/`:辅助工具脚本(如本地测试用的 `run.py`)。
|
||||
- **已移除不必要文件**:移除了 `requirements.txt`,避免用户误以为需要配置 Python 环境。
|
||||
Define an engineering-ready design baseline for OpenWebUI plugin development that:
|
||||
|
||||
## 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
|
||||
|
||||
```
|
||||
openwebui-extras/
|
||||
├── docs/ # 文档与教程
|
||||
├── plugins/ # 插件库 (核心资源)
|
||||
│ ├── actions/
|
||||
│ ├── filters/
|
||||
│ ├── pipelines/
|
||||
│ └── pipes/
|
||||
├── prompts/ # 提示词库 (核心资源)
|
||||
├── scripts/ # 维护者工具 (非用户必须)
|
||||
├── LICENSE # MIT 许可证
|
||||
├── README.md # 项目入口与资源索引
|
||||
└── index.html # 项目展示页
|
||||
```
|
||||
The active design document is:
|
||||
|
||||
### 3.2 核心调整
|
||||
1. **移除依赖管理**:删除了 `requirements.txt`。用户不需要 `pip install` 任何东西,只需下载对应的 `.py` 或 `.md` 文件导入 OpenWebUI 即可。
|
||||
2. **文档侧重**:README 和文档将侧重于“如何下载”和“如何导入”,而不是“如何安装项目”。
|
||||
- `docs/development/copilot-engineering-plan.md`
|
||||
- `docs/development/copilot-engineering-plan.zh.md`
|
||||
|
||||
### 3.3 后续建议
|
||||
1. **资源索引**:建议在 `README.md` 中维护一个高质量的插件/提示词索引表,方便用户快速查找。
|
||||
2. **贡献指南**:制定简单的 `CONTRIBUTING.md`,告诉其他人如何提交他们的插件或提示词(例如:只需提交文件到对应目录)。
|
||||
3. **版本控制**:虽然不需要 Python 环境,但建议在插件文件的头部注释中保留版本号和兼容性说明(如 `Compatible with OpenWebUI v0.3.x`)。
|
||||
This file is retained as a stable pointer to avoid design drift.
|
||||
|
||||
## 4. 发布流程
|
||||
1. **提交更改**:`git add . && git commit -m "Update project structure for resource sharing"`
|
||||
2. **推送到 GitHub**。
|
||||
3. **宣传**:在 OpenWebUI 社区分享此仓库链接。
|
||||
## 3. Engineering Baseline
|
||||
|
||||
- Single-file i18n plugin code architecture.
|
||||
- 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
|
||||
|
||||
@@ -4,15 +4,17 @@
|
||||
|
||||
## 📚 Table of Contents
|
||||
|
||||
1. [Quick Start](#1-quick-start)
|
||||
2. [Core Concepts & SDK Details](#2-core-concepts--sdk-details)
|
||||
3. [Deep Dive into Plugin Types](#3-deep-dive-into-plugin-types)
|
||||
* [Action](#31-action)
|
||||
* [Filter](#32-filter)
|
||||
* [Pipe](#33-pipe)
|
||||
4. [Advanced Development Patterns](#4-advanced-development-patterns)
|
||||
5. [Best Practices & Design Principles](#5-best-practices--design-principles)
|
||||
6. [Troubleshooting](#6-troubleshooting)
|
||||
1. [Quick Start](#1-quick-start)
|
||||
2. [Core Concepts & SDK Details](#2-core-concepts--sdk-details)
|
||||
3. [Deep Dive into Plugin Types](#3-deep-dive-into-plugin-types)
|
||||
* [Action](#31-action)
|
||||
* [Filter](#32-filter)
|
||||
* [Pipe](#33-pipe)
|
||||
4. [Advanced Development Patterns](#4-advanced-development-patterns)
|
||||
5. [Best Practices & Design Principles](#5-best-practices--design-principles)
|
||||
6. [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?
|
||||
|
||||
OpenWebUI Plugins (officially called "Functions") are the primary way to extend the platform's capabilities. Running in a backend Python environment, they allow you to:
|
||||
* 🔌 **Integrate New Models**: Connect to Claude, Gemini, or custom RAGs via Pipes.
|
||||
* 🎨 **Enhance Interaction**: Add buttons (e.g., "Export", "Generate Chart") next to messages via Actions.
|
||||
* 🔧 **Intervene in Processes**: Modify data before requests or after responses (e.g., inject context, filter sensitive words) via Filters.
|
||||
|
||||
* 🔌 **Integrate New Models**: Connect to Claude, Gemini, or custom RAGs via Pipes.
|
||||
* 🎨 **Enhance Interaction**: Add buttons (e.g., "Export", "Generate Chart") next to messages via Actions.
|
||||
* 🔧 **Intervene in Processes**: modify data before requests or after responses (e.g., inject context, filter sensitive words) via Filters.
|
||||
|
||||
### 1.2 Your First Plugin (Hello World)
|
||||
|
||||
@@ -69,9 +72,10 @@ class Action:
|
||||
### 2.1 ⚠️ Important: Sync vs Async
|
||||
|
||||
OpenWebUI plugins run within an `asyncio` event loop.
|
||||
* **Principle**: All I/O operations (database, file, network) must be non-blocking.
|
||||
* **Pitfall**: Calling synchronous methods directly (e.g., `time.sleep`, `requests.get`) will freeze the entire server.
|
||||
* **Solution**: Wrap synchronous calls using `await asyncio.to_thread(sync_func, ...)`.
|
||||
|
||||
* **Principle**: All I/O operations (database, file, network) must be non-blocking.
|
||||
* **Pitfall**: Calling synchronous methods directly (e.g., `time.sleep`, `requests.get`) will freeze the entire server.
|
||||
* **Solution**: Wrap synchronous calls using `await asyncio.to_thread(sync_func, ...)`.
|
||||
|
||||
### 2.2 Core Parameters
|
||||
|
||||
@@ -88,8 +92,8 @@ All plugin methods (`inlet`, `outlet`, `pipe`, `action`) support injecting the f
|
||||
|
||||
### 2.3 Configuration System (Valves)
|
||||
|
||||
* **`Valves`**: Global admin configuration.
|
||||
* **`UserValves`**: User-level configuration (higher priority, overrides global).
|
||||
* **`Valves`**: Global admin configuration.
|
||||
* **`UserValves`**: User-level configuration (higher priority, overrides global).
|
||||
|
||||
```python
|
||||
class Filter:
|
||||
@@ -113,7 +117,7 @@ class Filter:
|
||||
|
||||
**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
|
||||
import base64
|
||||
@@ -138,11 +142,11 @@ async def action(self, body, __event_call__):
|
||||
|
||||
**Role**: Middleware that intercepts and modifies requests/responses.
|
||||
|
||||
* **`inlet`**: Before request. Used for injecting context, modifying model parameters.
|
||||
* **`outlet`**: After response. Used for formatting output, logging.
|
||||
* **`stream`**: During streaming. Used for real-time sensitive word filtering.
|
||||
* **`inlet`**: Before request. Used for injecting context, modifying model parameters.
|
||||
* **`outlet`**: After response. Used for formatting output, logging.
|
||||
* **`stream`**: During streaming. Used for real-time sensitive word filtering.
|
||||
|
||||
**Example: Injecting Environment Variables**
|
||||
#### Example: Injecting Environment Variables
|
||||
|
||||
```python
|
||||
async def inlet(self, body, __metadata__):
|
||||
@@ -159,7 +163,7 @@ async def inlet(self, body, __metadata__):
|
||||
|
||||
**Role**: Custom Model/Agent.
|
||||
|
||||
**Example: Simple OpenAI Wrapper**
|
||||
#### Example: Simple OpenAI Wrapper
|
||||
|
||||
```python
|
||||
import requests
|
||||
@@ -180,11 +184,14 @@ class Pipe:
|
||||
## 4. Advanced Development Patterns
|
||||
|
||||
### 4.1 Pipe & Filter Collaboration
|
||||
|
||||
Use `__request__.app.state` to share data between plugins.
|
||||
* **Pipe**: `__request__.app.state.search_results = [...]`
|
||||
* **Filter (Outlet)**: Read `search_results` and format them as citation links appended to the response.
|
||||
|
||||
* **Pipe**: `__request__.app.state.search_results = [...]`
|
||||
* **Filter (Outlet)**: Read `search_results` and format them as citation links appended to the response.
|
||||
|
||||
### 4.2 Async Background Tasks
|
||||
|
||||
Execute time-consuming operations (e.g., summarization, database storage) in the background without blocking the user response.
|
||||
|
||||
```python
|
||||
@@ -204,15 +211,18 @@ async def background_job(self, chat_id):
|
||||
## 5. Best Practices & Design Principles
|
||||
|
||||
### 5.1 Naming & Positioning
|
||||
* **Short & Punchy**: e.g., "FlashCard", "DeepRead". Avoid generic terms like "Text Analysis Assistant".
|
||||
* **Complementary**: Don't reinvent the wheel; clarify what specific problem your plugin solves.
|
||||
|
||||
* **Short & Punchy**: e.g., "FlashCard", "DeepRead". Avoid generic terms like "Text Analysis Assistant".
|
||||
* **Complementary**: Don't reinvent the wheel; clarify what specific problem your plugin solves.
|
||||
|
||||
### 5.2 User Experience (UX)
|
||||
* **Timely Feedback**: Send a `notification` ("Generating...") before time-consuming operations.
|
||||
* **Visual Appeal**: When Action outputs HTML, use modern CSS (rounded corners, shadows, gradients).
|
||||
* **Smart Guidance**: If text is too short, prompt the user: "Suggest entering more content for better results".
|
||||
|
||||
* **Timely Feedback**: Send a `notification` ("Generating...") before time-consuming operations.
|
||||
* **Visual Appeal**: When Action outputs HTML, use modern CSS (rounded corners, shadows, gradients).
|
||||
* **Smart Guidance**: If text is too short, prompt the user: "Suggest entering more content for better results".
|
||||
|
||||
### 5.3 Error Handling
|
||||
|
||||
Never let a plugin fail silently. Catch exceptions and inform the user via `__event_emitter__`.
|
||||
|
||||
```python
|
||||
@@ -227,8 +237,127 @@ except Exception as e:
|
||||
|
||||
---
|
||||
|
||||
## 6. Troubleshooting
|
||||
## 6. Repository Standards (openwebui-extensions)
|
||||
|
||||
* **HTML not showing?** Ensure it's wrapped in a ` ```html ... ``` ` code block.
|
||||
* **Database error?** Check if you called synchronous DB methods directly in an `async` function; use `asyncio.to_thread`.
|
||||
* **Parameters not working?** Check if `Valves` are defined correctly and if they are being overridden by `UserValves`.
|
||||
### 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.
|
||||
* **Database error?** Check if you called synchronous DB methods directly in an `async` function; use `asyncio.to_thread`.
|
||||
* **Parameters not working?** Check if `Valves` are defined correctly and if they are being overridden by `UserValves`.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
Open WebUI 通过文件顶部的特定格式注释来识别和展示插件信息。
|
||||
|
||||
**代码示例 (`思维导图.py`):**
|
||||
**代码示例 (`smart_mind_map_cn.py`):**
|
||||
|
||||
```python
|
||||
"""
|
||||
@@ -45,7 +45,7 @@ description: 智能分析文本内容,生成交互式思维导图,帮助用户
|
||||
|
||||
通过在 `Action` 类内部定义一个 `Valves` Pydantic 模型,可以为插件创建可在 Web UI 中配置的参数。
|
||||
|
||||
**代码示例 (`思维导图.py`):**
|
||||
**代码示例 (`smart_mind_map_cn.py`):**
|
||||
|
||||
```python
|
||||
class Action:
|
||||
@@ -83,7 +83,7 @@ class Action:
|
||||
|
||||
`action` 方法是插件的执行入口,它是一个异步函数,接收 Open WebUI 传入的上下文信息。
|
||||
|
||||
**代码示例 (`思维导图.py`):**
|
||||
**代码示例 (`smart_mind_map_cn.py`):**
|
||||
|
||||
```python
|
||||
async def action(
|
||||
|
||||
22
docs/extensions/index.md
Normal file
22
docs/extensions/index.md
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: Extensions
|
||||
---
|
||||
|
||||
# Extensions
|
||||
|
||||
Standalone frontend extensions to supercharge your Open WebUI.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Open WebUI Prompt Plus
|
||||
|
||||
**An all-in-one prompt management suite for power users.**
|
||||
|
||||
[Open WebUI Prompt Plus](https://github.com/Fu-Jie/open-webui-prompt-plus) elevates your experience with:
|
||||
|
||||
- **🤖 AI-Powered Prompt Generator**: Turn natural language into structured prompts.
|
||||
- **⚡ Quick Insert Panel**: Spotlight-style search (`Cmd/Ctrl + Shift + P`).
|
||||
- **📂 Advanced Management**: Dynamic categories, favorites, and usage stats.
|
||||
- **📝 Native Variable Support**: Visual form rendering for template variables.
|
||||
|
||||
[:fontawesome-brands-github: View on GitHub](https://github.com/Fu-Jie/open-webui-prompt-plus){ .md-button .md-button--primary }
|
||||
22
docs/extensions/index.zh.md
Normal file
22
docs/extensions/index.zh.md
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: 扩展 (Extensions)
|
||||
---
|
||||
|
||||
# 扩展 (Extensions)
|
||||
|
||||
Open WebUI 的独立前端增强扩展。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Open WebUI Prompt Plus
|
||||
|
||||
**专为高级用户打造的一站式提示词管理套件。**
|
||||
|
||||
[Open WebUI Prompt Plus](https://github.com/Fu-Jie/open-webui-prompt-plus) 通过以下功能提升您的体验:
|
||||
|
||||
- **🤖 AI 提示词生成器**: 将自然语言转化为结构化提示词。
|
||||
- **⚡ 快速插入面板**: Spotlight 风格的快速搜索 (`Cmd/Ctrl + Shift + P`)。
|
||||
- **📂 高级管理**: 动态分类、收藏夹及使用统计。
|
||||
- **📝 原生变量支持**: 模板变量的可视化表单渲染。
|
||||
|
||||
[:fontawesome-brands-github: 查看 GitHub](https://github.com/Fu-Jie/open-webui-prompt-plus){ .md-button .md-button--primary }
|
||||
@@ -349,6 +349,53 @@ await __event_emitter__(
|
||||
)
|
||||
```
|
||||
|
||||
#### Advanced Use Case: Retrieving Frontend Data
|
||||
|
||||
One of the most powerful capabilities of the `execute` event type is the ability to fetch data from the browser environment (JavaScript) and return it to your Python backend. This allows plugins to access information like:
|
||||
|
||||
- `localStorage` items (user preferences, tokens)
|
||||
- `navigator` properties (language, geolocation, platform)
|
||||
- `document` properties (cookies, URL parameters)
|
||||
|
||||
**How it works:**
|
||||
The JavaScript code you provide in the `"code"` field is executed in the browser. If your JS code includes a `return` statement, that value is sent back to Python as the result of `await __event_call__`.
|
||||
|
||||
**Example: Getting the User's UI Language**
|
||||
|
||||
```python
|
||||
try:
|
||||
# Execute JS on the frontend to get language settings
|
||||
response = await __event_call__(
|
||||
{
|
||||
"type": "execute",
|
||||
"data": {
|
||||
# This JS code runs in the browser.
|
||||
# The 'return' value is sent back to Python.
|
||||
"code": """
|
||||
return (
|
||||
localStorage.getItem('locale') ||
|
||||
localStorage.getItem('language') ||
|
||||
navigator.language ||
|
||||
'en-US'
|
||||
);
|
||||
""",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
# 'response' will contain the string returned by JS (e.g., "en-US", "zh-CN")
|
||||
# Note: Wrap in try-except to handle potential timeouts or JS errors
|
||||
logger.info(f"Frontend Language: {response}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get frontend data: {e}")
|
||||
```
|
||||
|
||||
**Key capabilities unlocked:**
|
||||
- **Context Awareness:** Adapt responses based on user time zone or language.
|
||||
- **Client-Side Storage:** Use `localStorage` to persist simple plugin settings without a database.
|
||||
- **Hardware Access:** Request geolocation or clipboard access (requires user permission).
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ When & Where to Use Events
|
||||
@@ -421,4 +468,4 @@ Refer to this document for common event types and structures, and explore Open W
|
||||
|
||||
---
|
||||
|
||||
**Happy event-driven coding in Open WebUI! 🚀**
|
||||
**Happy event-driven coding in Open WebUI! 🚀**
|
||||
|
||||
@@ -4,17 +4,17 @@ hide:
|
||||
- toc
|
||||
---
|
||||
|
||||
# Welcome to OpenWebUI Extras
|
||||
# Welcome to OpenWebUI Extensions
|
||||
|
||||
<div class="hero-section" markdown>
|
||||
<div class="hero-content" markdown>
|
||||
|
||||
# 🚀 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 }
|
||||
[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>
|
||||
@@ -25,7 +25,7 @@ hide:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-puzzle:{ .lg .middle } **Plugin Center**
|
||||
- :material-puzzle:{ .lg .middle } **Plugin Center**
|
||||
|
||||
---
|
||||
|
||||
@@ -33,7 +33,7 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: Explore Plugins](plugins/index.md)
|
||||
|
||||
- :material-message-text:{ .lg .middle } **Prompt Library**
|
||||
- :material-message-text:{ .lg .middle } **Prompt Library**
|
||||
|
||||
---
|
||||
|
||||
@@ -41,7 +41,7 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: Browse Prompts](prompts/index.md)
|
||||
|
||||
- :material-tools:{ .lg .middle } **Enhancements**
|
||||
- :material-tools:{ .lg .middle } **Enhancements**
|
||||
|
||||
---
|
||||
|
||||
@@ -49,7 +49,15 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: View Guides](enhancements/index.md)
|
||||
|
||||
- :material-book-open-page-variant:{ .lg .middle } **Development**
|
||||
- :material-rocket-launch:{ .lg .middle } **Extensions**
|
||||
|
||||
---
|
||||
|
||||
Standalone frontend extensions to supercharge your Open WebUI interface.
|
||||
|
||||
[:octicons-arrow-right-24: Browse Extensions](extensions/index.md)
|
||||
|
||||
- :material-book-open-page-variant:{ .lg .middle } **Development**
|
||||
|
||||
---
|
||||
|
||||
@@ -65,7 +73,7 @@ hide:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-brain:{ .lg .middle } **Smart Mind Map**
|
||||
- :material-brain:{ .lg .middle } **Smart Mind Map**
|
||||
|
||||
---
|
||||
|
||||
@@ -73,15 +81,15 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: Learn More](plugins/actions/smart-mind-map.md)
|
||||
|
||||
- :material-card-text:{ .lg .middle } **Knowledge Card**
|
||||
- :material-card-text:{ .lg .middle } **Flash Card**
|
||||
|
||||
---
|
||||
|
||||
Quickly generates beautiful learning memory cards, perfect for studying and quick memorization.
|
||||
Quickly generates beautiful flashcards from text, extracting key points and categories.
|
||||
|
||||
[:octicons-arrow-right-24: Learn More](plugins/actions/knowledge-card.md)
|
||||
[:octicons-arrow-right-24: Learn More](plugins/actions/flash-card.md)
|
||||
|
||||
- :material-arrow-collapse-vertical:{ .lg .middle } **Async Context Compression**
|
||||
- :material-arrow-collapse-vertical:{ .lg .middle } **Async Context Compression**
|
||||
|
||||
---
|
||||
|
||||
@@ -104,10 +112,16 @@ hide:
|
||||
|
||||
### Using Plugins
|
||||
|
||||
1. Browse the [Plugin Center](plugins/index.md) and download the plugin file (`.py`)
|
||||
2. Open OpenWebUI **Admin Panel** → **Settings** → **Plugins**
|
||||
3. Click the upload button and select the `.py` file
|
||||
4. Refresh the page and enable the plugin in your chat settings
|
||||
1. **Install from OpenWebUI Community (Recommended)**:
|
||||
- Visit my profile: [Fu-Jie's Profile](https://openwebui.com/u/Fu-Jie)
|
||||
- Browse the plugins and select the one you like.
|
||||
- Click "Get" to import it directly into your OpenWebUI instance.
|
||||
|
||||
2. **Manual Installation**:
|
||||
- Browse the [Plugin Center](plugins/index.md) and download the plugin file (`.py`)
|
||||
- Open OpenWebUI **Admin Panel** → **Settings** → **Plugins**
|
||||
- Click the upload button and select the `.py` file
|
||||
- Refresh the page and enable the plugin in your chat settings
|
||||
|
||||
---
|
||||
|
||||
@@ -121,6 +135,6 @@ We welcome contributions! Whether it's a new plugin, a helpful prompt, or docume
|
||||
|
||||
<div class="footer-stats" markdown>
|
||||
|
||||
**OpenWebUI Extras** - Making AI interactions more powerful and productive.
|
||||
**OpenWebUI Extensions** - Making AI interactions more powerful and productive.
|
||||
|
||||
</div>
|
||||
|
||||
@@ -4,17 +4,17 @@ hide:
|
||||
- toc
|
||||
---
|
||||
|
||||
# 欢迎来到 OpenWebUI Extras
|
||||
# 欢迎来到 OpenWebUI Extensions
|
||||
|
||||
<div class="hero-section" markdown>
|
||||
<div class="hero-content" markdown>
|
||||
|
||||
# 🚀 增强你的 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 }
|
||||
[在 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>
|
||||
@@ -25,7 +25,7 @@ hide:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-puzzle:{ .lg .middle } **插件中心**
|
||||
- :material-puzzle:{ .lg .middle } **插件中心**
|
||||
|
||||
---
|
||||
|
||||
@@ -33,7 +33,7 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: 探索插件](plugins/index.md)
|
||||
|
||||
- :material-message-text:{ .lg .middle } **提示词库**
|
||||
- :material-message-text:{ .lg .middle } **提示词库**
|
||||
|
||||
---
|
||||
|
||||
@@ -41,7 +41,7 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: 浏览提示词](prompts/index.md)
|
||||
|
||||
- :material-tools:{ .lg .middle } **增强功能**
|
||||
- :material-tools:{ .lg .middle } **增强功能**
|
||||
|
||||
---
|
||||
|
||||
@@ -49,7 +49,15 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: 查看指南](enhancements/index.md)
|
||||
|
||||
- :material-book-open-page-variant:{ .lg .middle } **开发指南**
|
||||
- :material-rocket-launch:{ .lg .middle } **扩展 (Extensions)**
|
||||
|
||||
---
|
||||
|
||||
独立的 OpenWebUI 前端增强扩展,全面提升交互体验。
|
||||
|
||||
[:octicons-arrow-right-24: 浏览扩展](extensions/index.md)
|
||||
|
||||
- :material-book-open-page-variant:{ .lg .middle } **开发指南**
|
||||
|
||||
---
|
||||
|
||||
@@ -65,7 +73,7 @@ hide:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-brain:{ .lg .middle } **智能思维导图**
|
||||
- :material-brain:{ .lg .middle } **智能思维导图**
|
||||
|
||||
---
|
||||
|
||||
@@ -73,15 +81,15 @@ hide:
|
||||
|
||||
[:octicons-arrow-right-24: 了解更多](plugins/actions/smart-mind-map.md)
|
||||
|
||||
- :material-card-text:{ .lg .middle } **知识卡片**
|
||||
- :material-card-text:{ .lg .middle } **Flash Card(闪记卡)**
|
||||
|
||||
---
|
||||
|
||||
快速生成精美的学习记忆卡片,非常适合学习和快速记忆。
|
||||
|
||||
[:octicons-arrow-right-24: 了解更多](plugins/actions/knowledge-card.md)
|
||||
[:octicons-arrow-right-24: 了解更多](plugins/actions/flash-card.md)
|
||||
|
||||
- :material-arrow-collapse-vertical:{ .lg .middle } **异步上下文压缩**
|
||||
- :material-arrow-collapse-vertical:{ .lg .middle } **异步上下文压缩**
|
||||
|
||||
---
|
||||
|
||||
@@ -104,10 +112,16 @@ hide:
|
||||
|
||||
### 使用插件
|
||||
|
||||
1. 浏览[插件中心](plugins/index.md)并下载插件文件(`.py`)
|
||||
2. 打开 OpenWebUI **管理面板** → **设置** → **插件**
|
||||
3. 点击上传按钮并选择 `.py` 文件
|
||||
4. 刷新页面并在聊天设置中启用插件
|
||||
1. **从 OpenWebUI 社区安装 (推荐)**:
|
||||
- 访问我的主页: [Fu-Jie's Profile](https://openwebui.com/u/Fu-Jie)
|
||||
- 浏览插件列表,选择你喜欢的插件。
|
||||
- 点击 "Get" 按钮,将其直接导入到你的 OpenWebUI 实例中。
|
||||
|
||||
2. **手动安装**:
|
||||
- 浏览[插件中心](plugins/index.md)并下载插件文件(`.py`)
|
||||
- 打开 OpenWebUI **管理面板** → **设置** → **插件**
|
||||
- 点击上传按钮并选择 `.py` 文件
|
||||
- 刷新页面并在聊天设置中启用插件
|
||||
|
||||
---
|
||||
|
||||
@@ -121,6 +135,6 @@ hide:
|
||||
|
||||
<div class="footer-stats" markdown>
|
||||
|
||||
**OpenWebUI Extras** - 让 AI 交互更强大、更高效。
|
||||
**OpenWebUI Extensions** - 让 AI 交互更强大、更高效。
|
||||
|
||||
</div>
|
||||
|
||||
290
docs/js-visualization-guide.md
Normal file
290
docs/js-visualization-guide.md
Normal file
@@ -0,0 +1,290 @@
|
||||
# 使用 JavaScript 生成可视化内容的技术方案
|
||||
|
||||
## 概述
|
||||
|
||||
本文档描述了在 OpenWebUI Action 插件中使用浏览器端 JavaScript 代码生成可视化内容(如思维导图、信息图等)并将结果保存到消息中的技术方案。
|
||||
|
||||
## 核心架构
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Plugin as Python 插件
|
||||
participant EventCall as __event_call__
|
||||
participant Browser as 浏览器 (JS)
|
||||
participant API as OpenWebUI API
|
||||
participant DB as 数据库
|
||||
|
||||
Plugin->>EventCall: 1. 发送 execute 事件 (含 JS 代码)
|
||||
EventCall->>Browser: 2. 执行 JS 代码
|
||||
Browser->>Browser: 3. 加载可视化库 (D3/Markmap/AntV)
|
||||
Browser->>Browser: 4. 渲染可视化内容
|
||||
Browser->>Browser: 5. 转换为 Base64 Data URI
|
||||
Browser->>API: 6. GET 获取当前消息内容
|
||||
API-->>Browser: 7. 返回消息数据
|
||||
Browser->>API: 8. POST 追加 Markdown 图片到消息
|
||||
API->>DB: 9. 保存更新后的消息
|
||||
```
|
||||
|
||||
## 关键步骤
|
||||
|
||||
### 1. Python 端通过 `__event_call__` 执行 JS
|
||||
|
||||
Python 插件**不直接修改 `body["messages"]`**,而是通过 `__event_call__` 发送 JS 代码让浏览器执行:
|
||||
|
||||
```python
|
||||
async def action(
|
||||
self,
|
||||
body: dict,
|
||||
__user__: dict = None,
|
||||
__event_emitter__=None,
|
||||
__event_call__: Optional[Callable[[Any], Awaitable[None]]] = None,
|
||||
__metadata__: Optional[dict] = None,
|
||||
__request__: Request = None,
|
||||
) -> dict:
|
||||
# 从 body 获取 chat_id 和 message_id
|
||||
chat_id = body.get("chat_id", "")
|
||||
message_id = body.get("id", "") # 注意:body["id"] 是 message_id
|
||||
|
||||
# 通过 __event_call__ 执行 JS 代码
|
||||
if __event_call__:
|
||||
await __event_call__({
|
||||
"type": "execute",
|
||||
"data": {
|
||||
"code": f"""
|
||||
(async function() {{
|
||||
const chatId = "{chat_id}";
|
||||
const messageId = "{message_id}";
|
||||
// ... JS 渲染和 API 更新逻辑 ...
|
||||
}})();
|
||||
"""
|
||||
},
|
||||
})
|
||||
|
||||
# 不修改 body,直接返回
|
||||
return body
|
||||
```
|
||||
|
||||
### 2. JavaScript 加载可视化库
|
||||
|
||||
在浏览器端动态加载所需的 JS 库:
|
||||
|
||||
```javascript
|
||||
// 加载 D3.js
|
||||
if (!window.d3) {
|
||||
await new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://cdn.jsdelivr.net/npm/d3@7';
|
||||
script.onload = resolve;
|
||||
script.onerror = reject;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
// 加载 Markmap (思维导图)
|
||||
if (!window.markmap) {
|
||||
await loadScript('https://cdn.jsdelivr.net/npm/markmap-lib@0.17');
|
||||
await loadScript('https://cdn.jsdelivr.net/npm/markmap-view@0.17');
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 渲染并转换为 Data URI
|
||||
|
||||
```javascript
|
||||
// 创建 SVG 元素
|
||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
svg.setAttribute('width', '800');
|
||||
svg.setAttribute('height', '600');
|
||||
svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
||||
|
||||
// ... 执行渲染逻辑 (添加图形元素) ...
|
||||
|
||||
// 转换为 Base64 Data URI
|
||||
const svgData = new XMLSerializer().serializeToString(svg);
|
||||
const base64 = btoa(unescape(encodeURIComponent(svgData)));
|
||||
const dataUri = 'data:image/svg+xml;base64,' + base64;
|
||||
```
|
||||
|
||||
### 4. 获取当前消息内容
|
||||
|
||||
由于 Python 端不传递原始内容,JS 需要通过 API 获取:
|
||||
|
||||
```javascript
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
// 获取当前聊天数据
|
||||
const getResponse = await fetch(`/api/v1/chats/${chatId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
const chatData = await getResponse.json();
|
||||
|
||||
// 查找目标消息
|
||||
let originalContent = '';
|
||||
if (chatData.chat && chatData.chat.messages) {
|
||||
const targetMsg = chatData.chat.messages.find(m => m.id === messageId);
|
||||
if (targetMsg && targetMsg.content) {
|
||||
originalContent = targetMsg.content;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 调用 API 更新消息
|
||||
|
||||
```javascript
|
||||
// 构造新内容:原始内容 + Markdown 图片
|
||||
const markdownImage = ``;
|
||||
const newContent = originalContent + '\n\n' + markdownImage;
|
||||
|
||||
// 调用 API 更新消息
|
||||
const response = 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: newContent }
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
console.log('消息更新成功!');
|
||||
}
|
||||
```
|
||||
|
||||
## 完整示例
|
||||
|
||||
参考 [js_render_poc.py](https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/actions/js-render-poc/js_render_poc.py) 获取完整的 PoC 实现。
|
||||
|
||||
## 事件类型
|
||||
|
||||
| 类型 | 用途 |
|
||||
|------|------|
|
||||
| `chat:message:delta` | 增量更新(追加文本) |
|
||||
| `chat:message` | 完全替换消息内容 |
|
||||
|
||||
```javascript
|
||||
// 增量更新
|
||||
{ type: "chat:message:delta", data: { content: "追加的内容" } }
|
||||
|
||||
// 完全替换
|
||||
{ type: "chat:message", data: { content: "完整的新内容" } }
|
||||
```
|
||||
|
||||
## 关键数据来源
|
||||
|
||||
| 数据 | 来源 | 说明 |
|
||||
|------|------|------|
|
||||
| `chat_id` | `body["chat_id"]` | 聊天会话 ID |
|
||||
| `message_id` | `body["id"]` | ⚠️ 注意:是 `body["id"]`,不是 `body["message_id"]` |
|
||||
| `token` | `localStorage.getItem('token')` | 用户认证 Token |
|
||||
| `originalContent` | 通过 API `GET /api/v1/chats/{chatId}` 获取 | 当前消息内容 |
|
||||
|
||||
## Python 端 API
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `__event_emitter__` | Callable | 发送状态/通知事件 |
|
||||
| `__event_call__` | Callable | 执行 JS 代码(用于可视化渲染) |
|
||||
| `__metadata__` | dict | 元数据(可能为 None) |
|
||||
| `body` | dict | 请求体,包含 messages、chat_id、id 等 |
|
||||
|
||||
### body 结构示例
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "gemini-3-flash-preview",
|
||||
"messages": [...],
|
||||
"chat_id": "ac2633a3-5731-4944-98e3-bf9b3f0ef0ab",
|
||||
"id": "2e0bb7d4-dfc0-43d7-b028-fd9e06c6fdc8",
|
||||
"session_id": "bX30sHI8r4_CKxCdAAAL"
|
||||
}
|
||||
```
|
||||
|
||||
### 常用事件
|
||||
|
||||
```python
|
||||
# 发送状态更新
|
||||
await __event_emitter__({
|
||||
"type": "status",
|
||||
"data": {"description": "正在渲染...", "done": False}
|
||||
})
|
||||
|
||||
# 执行 JS 代码
|
||||
await __event_call__({
|
||||
"type": "execute",
|
||||
"data": {"code": "console.log('Hello from Python!')"}
|
||||
})
|
||||
|
||||
# 发送通知
|
||||
await __event_emitter__({
|
||||
"type": "notification",
|
||||
"data": {"type": "success", "content": "渲染完成!"}
|
||||
})
|
||||
```
|
||||
|
||||
## 适用场景
|
||||
|
||||
- **思维导图** (Markmap)
|
||||
- **信息图** (AntV Infographic)
|
||||
- **流程图** (Mermaid)
|
||||
- **数据图表** (ECharts, Chart.js)
|
||||
- **任何需要 JS 渲染的可视化内容**
|
||||
|
||||
## 注意事项
|
||||
|
||||
### 1. 竞态条件问题
|
||||
|
||||
⚠️ **多次快速点击会导致内容覆盖问题**
|
||||
|
||||
由于 API 调用是异步的,如果用户快速多次触发 Action:
|
||||
- 第一次点击:获取原始内容 A → 渲染 → 更新为 A+图片1
|
||||
- 第二次点击:可能获取到旧内容 A(第一次还没保存完)→ 更新为 A+图片2
|
||||
|
||||
结果:图片1 被覆盖丢失!
|
||||
|
||||
**解决方案**:
|
||||
- 添加防抖(debounce)机制
|
||||
- 使用锁/标志位防止重复执行
|
||||
- 或使用 `chat:message:delta` 增量更新
|
||||
|
||||
### 2. 不要直接修改 `body["messages"]`
|
||||
|
||||
消息更新应由 JS 通过 API 完成,确保获取最新内容。
|
||||
|
||||
### 3. f-string 限制
|
||||
|
||||
Python f-string 内不能直接使用反斜杠,需要将转义字符串预先处理:
|
||||
|
||||
```python
|
||||
# 转义 JSON 中的特殊字符
|
||||
body_json = json.dumps(data, ensure_ascii=False)
|
||||
escaped = body_json.replace("\\", "\\\\").replace("`", "\\`").replace("${", "\\${")
|
||||
```
|
||||
|
||||
### 4. Data URI 大小限制
|
||||
|
||||
Base64 编码会增加约 33% 的体积,复杂图片可能导致消息过大。
|
||||
|
||||
### 5. 跨域问题
|
||||
|
||||
确保 CDN 资源支持 CORS。
|
||||
|
||||
### 6. API 权限
|
||||
|
||||
确保用户 token 有权限访问和更新目标消息。
|
||||
|
||||
## 与传统方式对比
|
||||
|
||||
| 特性 | 传统方式 (修改 body) | 新方式 (__event_call__) |
|
||||
|------|---------------------|------------------------|
|
||||
| 消息更新 | Python 直接修改 | JS 通过 API 更新 |
|
||||
| 原始内容 | Python 传递给 JS | JS 通过 API 获取 |
|
||||
| 灵活性 | 低 | 高 |
|
||||
| 实时性 | 一次性 | 可多次更新 |
|
||||
| 复杂度 | 简单 | 中等 |
|
||||
| 竞态风险 | 低 | ⚠️ 需要处理 |
|
||||
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 }
|
||||
@@ -1,12 +1,25 @@
|
||||
# Export to Excel
|
||||
|
||||
<span class="category-badge action">Action</span>
|
||||
<span class="version-badge">v0.3.3</span>
|
||||
<span class="version-badge">v0.3.7</span>
|
||||
|
||||
Export chat conversations to Excel spreadsheet format for analysis, archiving, and sharing.
|
||||
|
||||
|
||||
### What's New in v0.3.6
|
||||
- **OpenWebUI-Style Theme**: Modern dark header with light gray zebra striping for better readability.
|
||||
- **Zebra Striping**: Alternating row colors for improved visual scanning.
|
||||
- **Smart Data Type Conversion**: Automatically converts columns to numeric or datetime types.
|
||||
- **Full Cell Bold/Italic**: Supports Markdown bold/italic formatting in Excel.
|
||||
- **Partial Markdown Cleanup**: Removes partial Markdown symbols for cleaner output.
|
||||
- **Export Scope**: Choose between "Last Message" or "All Messages".
|
||||
- **Smart Sheet Naming**: Names sheets based on Markdown headers or message index.
|
||||
- **Smart Filename Generation**: Generates filenames based on Chat Title, AI Summary, or Markdown Headers.
|
||||
- **AI Title Generation**: Supports using a specific model (`MODEL_ID`) for title generation with progress notifications.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
The Export to Excel plugin allows you to download your chat conversations as Excel files. This is useful for:
|
||||
@@ -23,11 +36,18 @@ The Export to Excel plugin allows you to download your chat conversations as Exc
|
||||
- :material-download: **One-Click Download**: Instant file generation
|
||||
- :material-history: **Full History**: Exports complete conversation
|
||||
|
||||
## Configuration
|
||||
|
||||
- **Title Source**: Choose how the filename is generated:
|
||||
- `chat_title`: Use the chat title (default).
|
||||
- `ai_generated`: Use AI to generate a concise title from the content.
|
||||
- `markdown_title`: Extract the first H1/H2 header from the markdown content.
|
||||
|
||||
---
|
||||
|
||||
## 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**
|
||||
3. Enable the plugin
|
||||
|
||||
@@ -64,4 +84,4 @@ The exported Excel file contains:
|
||||
|
||||
## 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