Compare commits
633 Commits
v2026.01.0
...
v2026.02.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb79bc9633 | ||
|
|
0c7d427b93 | ||
|
|
07bc5f027e | ||
|
|
701fc3e906 | ||
|
|
d392af66c9 | ||
|
|
67cf86fb26 | ||
|
|
fe98b0e007 | ||
|
|
3236d19e28 | ||
|
|
354c1eee6b | ||
|
|
6b3eb8064b | ||
|
|
f32e90e182 | ||
|
|
8001ab18ee | ||
|
|
dcfde9c0dc | ||
|
|
dbcf7421ea | ||
|
|
1705baf976 | ||
|
|
8e8d478ece | ||
|
|
acc9cd7ff2 | ||
|
|
e4582c3197 | ||
|
|
d0eb72467d | ||
|
|
6b6e62398a | ||
|
|
fd22ed8fa0 | ||
|
|
4fccd1893e | ||
|
|
a74e03fff8 | ||
|
|
850838226d | ||
|
|
a52ac34d59 | ||
|
|
3263ab9db6 | ||
|
|
7c7daef30b | ||
|
|
64754ba26b | ||
|
|
4188410d61 | ||
|
|
0c7201902c | ||
|
|
f47c3f6354 | ||
|
|
272245d911 | ||
|
|
ce93464f47 | ||
|
|
c658e379c0 | ||
|
|
ab28465dd5 | ||
|
|
d22ba88f33 | ||
|
|
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 |
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)
|
||||
64
.agent/skills/test-copilot-pipe/SKILL.md
Normal file
64
.agent/skills/test-copilot-pipe/SKILL.md
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
name: test-copilot-pipe
|
||||
description: Automotive deployment and testing of GitHub Copilot SDK Pipe plugin for frontend/backend status stability.
|
||||
---
|
||||
|
||||
# 🤖 Skill: Test Copilot Pipe
|
||||
|
||||
This is a **universal testing framework** for publishing the latest `github_copilot_sdk.py` (Pipe) code to a local OpenWebUI instance and verifying it via an automated agent (`browser_subagent`).
|
||||
|
||||
## 🎯 Core Principles
|
||||
|
||||
- **Fixed Infrastructure**: The deployment script and the test entry URL are always static.
|
||||
- **Dynamic Test Planning**: Specific test prompts and expectations (acceptance criteria) **must** be dynamically planned by you based on the code changes or specific user requests before execution.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Static Environment Info
|
||||
|
||||
| Attribute | Fixed Value |
|
||||
|------|--------|
|
||||
| **Deployment Script** | `/Users/fujie/app/python/oui/openwebui-extensions/scripts/deploy_pipe.py` |
|
||||
| **Python Path** | `/opt/homebrew/Caskroom/miniconda/base/envs/ai/bin/python3` |
|
||||
| **Test URL** | `http://localhost:3003/?model=github_copilot_official_sdk_pipe.github_copilot_sdk-gpt-4.1` |
|
||||
|
||||
---
|
||||
|
||||
## 📋 Standard Workflow
|
||||
|
||||
### Step 1: Analyze Changes & Plan Test (Plan)
|
||||
|
||||
Before triggering the test, you must define the purpose of this test turn.
|
||||
Example: *Modified tool calling logic -> Test prompt should trigger a specific tool; observe if the tool executes and returns the correct result.*
|
||||
|
||||
### Step 2: Deploy Latest Code (Deploy)
|
||||
|
||||
Use the `run_command` tool to execute the fixed update task:
|
||||
|
||||
```bash
|
||||
/opt/homebrew/Caskroom/miniconda/base/envs/ai/bin/python3 /Users/fujie/app/python/oui/openwebui-extensions/scripts/deploy_pipe.py
|
||||
```
|
||||
|
||||
> **Mechanism**: `deploy_pipe.py` automatically loads the API Key from `scripts/.env` in the same directory.
|
||||
> **Verification**: Look for `✅ Successfully updated... version X.X.X` or `✅ Successfully created...`. If a 401 error occurs, remind the user to generate a new API Key in OpenWebUI and update `.env`.
|
||||
|
||||
### Step 3: Verify via Browser Subagent (Verify)
|
||||
|
||||
Use the `browser_subagent` tool. **You must fill in the `[Dynamic Content]` slots based on Step 1**:
|
||||
|
||||
```text
|
||||
Task:
|
||||
1. Access The Fixed URL: http://localhost:3003/?model=github_copilot_official_sdk_pipe.github_copilot_sdk-gpt-4.1
|
||||
2. RELIABILITY WAIT: Wait until the page fully loads. Wait until the chat input text area (`#chat-input`) is present in the DOM.
|
||||
3. ACTION - FAST INPUT: Use the `execute_browser_javascript` tool to instantly inject the query and submit it. Use exactly this script format to ensure stability:
|
||||
`const input = document.getElementById('chat-input'); input.value = "[YOUR_DYNAMIC_TEST_PROMPT]"; input.dispatchEvent(new Event('input', { bubbles: true })); const e = new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true }); input.dispatchEvent(e);`
|
||||
4. WAITING: Wait patiently for the streaming response to stop completely. You should wait for the Stop button to disappear, or wait for the system to settle (approximately 10-15 seconds depending on the query).
|
||||
5. CHECK THE OUTCOME: [List the phenomena you expect to see, e.g., status bar shows specific text, tool card appears, result contains specific keywords, etc.]
|
||||
6. CAPTURE: Take a screenshot of the settled state to prove the outcome.
|
||||
7. REPORT: Report the EXACT outcome matching the criteria from step 5.
|
||||
```
|
||||
|
||||
### Step 4: Evaluate & Iterate (Evaluate)
|
||||
|
||||
- **PASS**: Screenshot and phenomena match expectations. Report success to the user.
|
||||
- **FAIL**: Analyze the issue based on screenshots/logs (e.g., race condition reappeared, API error). Modify the code and **re-run the entire skill workflow**.
|
||||
@@ -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. **Explicitly describe new features.**
|
||||
- `README_CN.md`: Update version, description, and usage. **Explicitly describe new features.**
|
||||
|
||||
- `README.md`: Update version, description, and usage.
|
||||
- **Key Capabilities**: **MUST** include ALL core functionalities and features. This is a cumulative section. Every release **MUST** verify that basic core descriptions are NOT lost or overwritten by new feature lists.
|
||||
- **What's New**: Explicitly describe only the latest changes/updates in a prominent position at the beginning. This section is dynamic and changes with versions.
|
||||
- `README_CN.md`: Update version, description, and usage.
|
||||
- **核心功能 (Key Capabilities)**: **必须**包含所有核心功能和特性。这是一个累积性的部分,每次版本更新**必须**确认基础核心功能的描述没有丢失或被新功能列表覆盖。
|
||||
- **最新更新 (What's New)**: 在开头显眼位置明确描述最新的更改/更新。此部分是动态的,随版本变化。
|
||||
|
||||
### Global Documentation (`docs/`)
|
||||
|
||||
- **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,18 +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)
|
||||
|
||||
**CRITICAL RULE FOR AGENTS**:
|
||||
|
||||
- **No Auto-Push**: Agents **MUST NOT** automatically push changes to the remote `main` branch.
|
||||
- **Local Commit Only**: All changes must be committed locally.
|
||||
- **User Approval**: Pushing to remote requires explicit user action or approval.
|
||||
|
||||
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"
|
||||
}
|
||||
71
.gemini/skills/README.md
Normal file
71
.gemini/skills/README.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# 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`
|
||||
|
||||
---
|
||||
|
||||
## Release Pipeline Skills
|
||||
|
||||
These four skills form a complete release pipeline and are designed to be used in sequence:
|
||||
|
||||
```
|
||||
release-prep → pr-submitter → pr-reviewer → release-finalizer
|
||||
(prepare) (push & PR) (respond to review) (merge & close issue)
|
||||
```
|
||||
|
||||
- **release-prep**
|
||||
- Purpose: Full release preparation — version sync across 7+ files, bilingual release notes creation, consistency check, and commit.
|
||||
- Entry: `release-prep/SKILL.md`
|
||||
|
||||
- **pr-submitter**
|
||||
- Purpose: Shell-escape-safe PR submission — writes body to temp file, validates sections, pushes branch, creates PR via `gh pr create --body-file`.
|
||||
- Entry: `pr-submitter/SKILL.md`
|
||||
|
||||
- **pr-reviewer**
|
||||
- Purpose: Fetch PR review comments, categorize feedback, implement fixes, commit and push, reply to reviewers.
|
||||
- Entry: `pr-reviewer/SKILL.md`
|
||||
|
||||
- **release-finalizer**
|
||||
- Purpose: Merge release PR to main with proper commit message, auto-link and close related issues, post closing messages.
|
||||
- Entry: `release-finalizer/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])
|
||||
59
.gemini/skills/openwebui-community-publisher/SKILL.md
Normal file
59
.gemini/skills/openwebui-community-publisher/SKILL.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
name: openwebui-community-publisher
|
||||
description: Automatically publishes plugin update posts to openwebui.com.
|
||||
---
|
||||
|
||||
# OpenWebUI Community Publisher
|
||||
|
||||
## Overview
|
||||
|
||||
This skill automates the process of creating **new** plugin release notes and announcements directly on the OpenWebUI Community (openwebui.com).
|
||||
|
||||
**Note**: This skill is exclusively for **new post creation**. Do NOT use this for updating existing posts, as updates are managed separately via dedicated scripts.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- User must be logged into [openwebui.com](https://openwebui.com) in the browser session.
|
||||
- The content must be prepared in Markdown format (typically following the structure of the plugin's changelog or a dedicated release `.md` file).
|
||||
|
||||
## Execution Workflow
|
||||
|
||||
### 1. Verification
|
||||
|
||||
- Use `browser_subagent` to navigate to `https://openwebui.com`.
|
||||
- Verify the logged-in user status (look for profile icons or "@Fu-Jie").
|
||||
|
||||
### 2. Post Creation
|
||||
|
||||
- Navigate to `https://openwebui.com/post`.
|
||||
- **Post Type Selection**:
|
||||
- Choose the appropriate tab based on content:
|
||||
- **Text**: General announcements and documentation.
|
||||
- **Tool**: Standalone tool plugins.
|
||||
- **Function**: Pipes, Filters, or Actions.
|
||||
- **Prompt**: Chat prompt templates.
|
||||
- **Model**: GGUF/Ollama model files.
|
||||
- Unless otherwise specified, default to **Text** for general release introductions.
|
||||
- **Community Selection**:
|
||||
- For general OpenWebUI related posts, select **o/openwebui**.
|
||||
- For specialized topics, select the relevant community (e.g., **o/ollama** for models).
|
||||
- **Metadata Mapping**:
|
||||
- **Title**: Use the main header from the source file.
|
||||
- **Content/Description**: Paste the Markdown body.
|
||||
- **Source Code (for Function/Pipe)**: If publishing a `Function`, retrieve the corresponding `.py` file content and paste it into the code area.
|
||||
- **Tags**: Leave empty by default unless relevant keywords are explicitly provided.
|
||||
- **Media**: Optional, only attach if provided.
|
||||
- **Settings**: Ensure "Adult content" is unchecked.
|
||||
|
||||
### 3. Submission & Validation
|
||||
|
||||
- Click the "Create" (创建) button.
|
||||
- Wait for redirection to the final post URL.
|
||||
- **CRITICAL**: Use `capture_browser_screenshot` to verify the rendering.
|
||||
- Return the final URL to the user.
|
||||
|
||||
## Design Standards
|
||||
|
||||
- **Rich Aesthetics**: Use emojis in titles.
|
||||
- **Structured Data**: Ensure tables and code blocks in the Markdown are preserved.
|
||||
- **Internal Linking**: Link back to the OpenWebUI market or GitHub repository where applicable.
|
||||
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])
|
||||
180
.gemini/skills/pr-reviewer/SKILL.md
Normal file
180
.gemini/skills/pr-reviewer/SKILL.md
Normal file
@@ -0,0 +1,180 @@
|
||||
---
|
||||
name: pr-reviewer
|
||||
description: Fetches PR review comments, analyzes requested changes, implements fixes, commits and pushes the resolution. Use after a reviewer has left comments on an open PR to close the feedback loop efficiently.
|
||||
---
|
||||
|
||||
# PR Reviewer
|
||||
|
||||
## Overview
|
||||
|
||||
This skill automates the response cycle for code review. When a reviewer leaves comments on a Pull Request, this skill fetches all pending feedback, categorizes issues by severity, implements fixes, and submits a follow-up commit with appropriate review response comments.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- An open PR exists with pending review comments
|
||||
- The local branch matches the PR's head branch
|
||||
- `gh` CLI is authenticated
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Fetch Review State
|
||||
|
||||
Retrieve all review comments and overall review status:
|
||||
|
||||
```bash
|
||||
# Get overall review decisions
|
||||
PAGER=cat GH_PAGER=cat gh pr view <PR-NUMBER> --json reviews,reviewDecision,headRefName \
|
||||
--jq '{decision: .reviewDecision, reviews: [.reviews[] | {author: .author.login, state: .state, body: .body}]}'
|
||||
|
||||
# Get inline code comments (specific line comments)
|
||||
PAGER=cat GH_PAGER=cat gh api repos/Fu-Jie/openwebui-extensions/pulls/<PR-NUMBER>/comments \
|
||||
--jq '[.[] | {path: .path, line: .line, body: .body, author: .user.login, id: .id}]'
|
||||
|
||||
# Get general issue comments
|
||||
PAGER=cat GH_PAGER=cat gh issue view <PR-NUMBER> --comments --json comments \
|
||||
--jq '[.comments[] | {author: .author.login, body: .body}]'
|
||||
```
|
||||
|
||||
Confirm the current local branch matches the PR head:
|
||||
```bash
|
||||
git branch --show-current
|
||||
```
|
||||
If mismatched, checkout the correct branch first.
|
||||
|
||||
### Step 2 — Categorize Review Feedback
|
||||
|
||||
Group feedback into categories:
|
||||
|
||||
| Category | Examples | Action |
|
||||
|----------|---------|--------|
|
||||
| **Code Bug** | Logic error, incorrect variable, broken condition | Fix code immediately |
|
||||
| **Style / Formatting** | Indentation, naming convention, missing blank line | Fix code |
|
||||
| **Documentation** | Missing i18n key, wrong version in README, typo | Fix docs |
|
||||
| **Design Question** | Suggestion to restructure, alternative approach | Discuss with user before implementing |
|
||||
| **Nitpick / Optional** | Minor style preferences reviewer marked as optional | Fix if quick; document if skipped |
|
||||
| **Blocking** | Reviewer explicitly blocks merge | Must fix before proceeding |
|
||||
|
||||
Present the full categorized list to the user and confirm the resolution plan.
|
||||
|
||||
### Step 3 — Implement Fixes
|
||||
|
||||
For each accepted fix:
|
||||
|
||||
1. Read the affected file at the commented line for context:
|
||||
```bash
|
||||
sed -n '<line-5>,<line+10>p' <file-path>
|
||||
```
|
||||
2. Apply the fix using appropriate file edit tools
|
||||
3. After editing, verify the specific area looks correct
|
||||
|
||||
**For code changes that might affect behavior:**
|
||||
- Check if tests exist: `ls tests/test_*.py`
|
||||
- If tests exist, run them: `python -m pytest tests/ -v`
|
||||
|
||||
**For documentation fixes:**
|
||||
- If modifying README.md, check if `docs/` mirror needs the same fix
|
||||
- Apply the same fix to both locations
|
||||
|
||||
### Step 4 — Run Consistency Checks
|
||||
|
||||
After all fixes are applied:
|
||||
|
||||
```bash
|
||||
# Version consistency (if any version files were touched)
|
||||
python3 scripts/check_version_consistency.py
|
||||
|
||||
# Quick syntax check for Python files
|
||||
python3 -m py_compile plugins/{type}/{name}/{name}.py && echo "✅ Syntax OK"
|
||||
```
|
||||
|
||||
### Step 5 — Stage and Commit
|
||||
|
||||
Create a new commit (do NOT amend if the branch has already been pushed, to avoid force-push):
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git status
|
||||
```
|
||||
|
||||
Draft a Conventional Commits message for the fixup:
|
||||
|
||||
Format: `fix(scope): address review feedback`
|
||||
|
||||
Body should list what was fixed, referencing reviewer concerns:
|
||||
```
|
||||
fix(github-copilot-sdk): address review feedback from @reviewer
|
||||
|
||||
- Fix X per review comment on line Y of file Z
|
||||
- Update README to clarify auth requirement
|
||||
- Correct edge case in _parse_mcp_servers logic
|
||||
```
|
||||
|
||||
```bash
|
||||
git commit -m "<fixup commit message>"
|
||||
```
|
||||
|
||||
### Step 6 — Push the Fix Commit
|
||||
|
||||
```bash
|
||||
git push origin $(git branch --show-current)
|
||||
```
|
||||
|
||||
**Force-push policy:**
|
||||
- Use `git push` (non-force) by default
|
||||
- Only use `git push --force-with-lease` if:
|
||||
1. The user explicitly requests it, AND
|
||||
2. The only change is an amended commit squash (cosmetic, no logic change)
|
||||
3. Never use `--force` (without `--lease`)
|
||||
|
||||
### Step 7 — Respond to Reviewers
|
||||
|
||||
For each addressed review comment, post a reply:
|
||||
|
||||
```bash
|
||||
# Reply to inline comment
|
||||
gh api repos/Fu-Jie/openwebui-extensions/pulls/<PR-NUMBER>/comments/<COMMENT-ID>/replies \
|
||||
-X POST -f body="Fixed in commit <SHORT-SHA>. <Brief explanation of what was changed.>"
|
||||
|
||||
# General comment to summarize all fixes
|
||||
gh issue comment <PR-NUMBER> --body "All review feedback addressed in commit <SHORT-SHA>:
|
||||
- Fixed: <item 1>
|
||||
- Fixed: <item 2>
|
||||
Ready for re-review. 🙏"
|
||||
```
|
||||
|
||||
### Step 8 — Re-Request Review (Optional)
|
||||
|
||||
If the reviewer had submitted a `CHANGES_REQUESTED` review, request a new review after fixes:
|
||||
|
||||
```bash
|
||||
PAGER=cat GH_PAGER=cat gh api repos/Fu-Jie/openwebui-extensions/pulls/<PR-NUMBER>/requested_reviewers \
|
||||
-X POST -f reviewers[]='<reviewer-login>'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Decision Guide
|
||||
|
||||
### When NOT to implement a suggestion immediately
|
||||
|
||||
- **Design questions**: "Should this be a separate class?" — Present to user for decision
|
||||
- **Optional nitpicks**: Reviewer marked as `nit:` — Ask user if they want to include it
|
||||
- **Large refactors**: If fix would require changing >50 lines, propose a separate follow-up issue instead
|
||||
|
||||
### When to ask the user before proceeding
|
||||
|
||||
- Any fix involving behavioral changes to plugin logic
|
||||
- Renaming Valve keys (breaking change — requires migration notes)
|
||||
- Changes that affect the bilingual release notes already committed
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- ❌ Do NOT `git commit --amend` on a pushed commit without user approval for force-push
|
||||
- ❌ Do NOT silently skip a reviewer's comment; always acknowledge it (implement or explain why not)
|
||||
- ❌ Do NOT use `--force` (only `--force-with-lease` when absolutely necessary)
|
||||
- ❌ Do NOT make unrelated changes in the fixup commit; keep scope focused on review feedback
|
||||
- ❌ Do NOT respond to reviewer comments in Chinese if the PR language context is English
|
||||
179
.gemini/skills/pr-submitter/SKILL.md
Normal file
179
.gemini/skills/pr-submitter/SKILL.md
Normal file
@@ -0,0 +1,179 @@
|
||||
---
|
||||
name: pr-submitter
|
||||
description: Submits a feature branch as a Pull Request with a validated, properly formatted bilingual PR body. Handles shell-escape-safe body writing via temp files. Use after release-prep has committed all changes.
|
||||
---
|
||||
|
||||
# PR Submitter
|
||||
|
||||
## Overview
|
||||
|
||||
This skill handles the final step of pushing a feature branch and creating a validated Pull Request on GitHub. Its primary purpose is to avoid the shell-escaping pitfalls (backticks, special characters in `gh pr create --body`) by always writing the PR body to a **temp file** first.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- All changes are committed (use `release-prep` skill first)
|
||||
- The `gh` CLI is authenticated (`gh auth status`)
|
||||
- Current branch is NOT `main` or `master`
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Pre-Flight Checks
|
||||
|
||||
Run these checks before any push:
|
||||
|
||||
```bash
|
||||
# 1. Confirm not on protected branch
|
||||
git branch --show-current
|
||||
|
||||
# 2. Verify there are commits to push
|
||||
git log origin/$(git branch --show-current)..HEAD --oneline 2>/dev/null || echo "No remote tracking branch yet"
|
||||
|
||||
# 3. Check gh CLI auth
|
||||
gh auth status
|
||||
```
|
||||
|
||||
If any check fails, stop and report clearly.
|
||||
|
||||
### Step 2 — Collect PR Metadata
|
||||
|
||||
Gather:
|
||||
- **PR Title**: Must follow Conventional Commits format, English only (e.g., `feat(github-copilot-sdk): release v0.8.0 with conditional tool filtering`)
|
||||
- **Target base branch**: Default is `main`
|
||||
- **Plugin name + version** (to build body sections)
|
||||
- **Key changes** (reuse from release-prep or the latest What's New section)
|
||||
|
||||
### Step 3 — Build PR Body File (Shell-Escape-Safe)
|
||||
|
||||
**Always write the body to a temp file.** Never embed multi-line markdown with special characters directly in a shell command.
|
||||
|
||||
```bash
|
||||
cat > /tmp/pr_body.md << 'HEREDOC'
|
||||
## Summary
|
||||
|
||||
Brief one-sentence description of what this PR accomplishes.
|
||||
|
||||
## Changes
|
||||
|
||||
### New Features
|
||||
- Feature 1 description
|
||||
- Feature 2 description
|
||||
|
||||
### Bug Fixes
|
||||
- Fix 1 description
|
||||
|
||||
## Plugin Version
|
||||
- `PluginName` bumped to `vX.X.X`
|
||||
|
||||
## Documentation
|
||||
- README.md / README_CN.md updated
|
||||
- docs/ mirrors synced
|
||||
|
||||
## Testing
|
||||
- [ ] Tested locally in OpenWebUI
|
||||
- [ ] i18n validated (all language keys present)
|
||||
- [ ] Version consistency check passed (`python3 scripts/check_version_consistency.py`)
|
||||
|
||||
---
|
||||
|
||||
## 变更摘要(中文)
|
||||
|
||||
简要描述本次 PR 的改动内容。
|
||||
|
||||
### 新功能
|
||||
- 功能1描述
|
||||
- 功能2描述
|
||||
|
||||
### 问题修复
|
||||
- 修复1描述
|
||||
HEREDOC
|
||||
```
|
||||
|
||||
**Critical rules for the body file:**
|
||||
- Use `<< 'HEREDOC'` (quoted heredoc) to prevent variable expansion
|
||||
- Keep all backticks literal — they are safe inside a heredoc
|
||||
- Paths like `/api/v1/files/` are safe too since heredoc doesn't interpret them as commands
|
||||
|
||||
### Step 4 — Validate PR Body
|
||||
|
||||
Before submitting, verify the body file contains expected sections:
|
||||
|
||||
```bash
|
||||
# Check key sections exist
|
||||
grep -q "## Summary" /tmp/pr_body.md && echo "✅ Summary" || echo "❌ Summary missing"
|
||||
grep -q "## Changes" /tmp/pr_body.md && echo "✅ Changes" || echo "❌ Changes missing"
|
||||
grep -q "## 变更摘要" /tmp/pr_body.md && echo "✅ CN Section" || echo "❌ CN Section missing"
|
||||
|
||||
# Preview the body
|
||||
cat /tmp/pr_body.md
|
||||
```
|
||||
|
||||
Ask the user to confirm the body content before proceeding.
|
||||
|
||||
### Step 5 — Push Branch
|
||||
|
||||
```bash
|
||||
git push -u origin $(git branch --show-current)
|
||||
```
|
||||
|
||||
If push is rejected (non-fast-forward), report to user and ask whether to force-push. **Do NOT force-push without explicit confirmation.**
|
||||
|
||||
### Step 6 — Create Pull Request
|
||||
|
||||
```bash
|
||||
gh pr create \
|
||||
--base main \
|
||||
--head $(git branch --show-current) \
|
||||
--title "<PR title from Step 2>" \
|
||||
--body-file /tmp/pr_body.md
|
||||
```
|
||||
|
||||
Always use `--body-file`, never `--body` with inline markdown.
|
||||
|
||||
### Step 7 — Verify PR Creation
|
||||
|
||||
```bash
|
||||
PAGER=cat GH_PAGER=cat gh pr view --json number,url,title,body --jq '{number: .number, url: .url, title: .title, body_preview: .body[:200]}'
|
||||
```
|
||||
|
||||
Confirm:
|
||||
- PR number and URL
|
||||
- Title matches intended Conventional Commits format
|
||||
- Body preview includes key sections (not truncated/corrupted)
|
||||
|
||||
If the body appears corrupted (empty sections, missing backtick content), use edit:
|
||||
|
||||
```bash
|
||||
gh pr edit <PR-NUMBER> --body-file /tmp/pr_body.md
|
||||
```
|
||||
|
||||
### Step 8 — Cleanup
|
||||
|
||||
```bash
|
||||
rm -f /tmp/pr_body.md
|
||||
```
|
||||
|
||||
Report final PR URL to the user.
|
||||
|
||||
---
|
||||
|
||||
## Shell-Escape Safety Rules
|
||||
|
||||
| Risk | Safe Approach |
|
||||
|------|--------------|
|
||||
| Backticks in `--body` | Write to file, use `--body-file` |
|
||||
| Paths like `/api/...` | Safe in heredoc; risky in inline `--body` |
|
||||
| Newlines in `--body` | File-based only |
|
||||
| `$variable` expansion | Use `<< 'HEREDOC'` (quoted) |
|
||||
| Double quotes in body | Safe in heredoc file |
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- ❌ Never use `--body "..."` with multi-line content directly in shell command
|
||||
- ❌ Never interpolate variables directly into heredoc without quoting the delimiter
|
||||
- ❌ Never force-push (`--force`) without explicit user confirmation
|
||||
- ❌ Never target `main` as the source branch (only as base)
|
||||
- ❌ Never skip the body validation step — a PR with empty body is worse than a delayed PR
|
||||
208
.gemini/skills/release-finalizer/SKILL.md
Normal file
208
.gemini/skills/release-finalizer/SKILL.md
Normal file
@@ -0,0 +1,208 @@
|
||||
---
|
||||
name: release-finalizer
|
||||
description: Merges a release PR, associates it with resolved issues, replies to issue reporters, and closes issues. Use after PR review is complete and ready for merge. Closes the release cycle.
|
||||
---
|
||||
|
||||
# Release Finalizer
|
||||
|
||||
## Overview
|
||||
|
||||
This skill completes the final step of the release cycle: merging the release PR to `main`, replying to all related issues with solutions, and automatically closing them using GitHub's issue linking mechanism.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- The PR is in `OPEN` state and ready to merge
|
||||
- All status checks have passed (CI green)
|
||||
- All review feedback has been addressed
|
||||
- The PR relates to one or more GitHub issues (either in PR description or through commits)
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Pre-Merge Verification
|
||||
|
||||
Verify that the PR is ready:
|
||||
|
||||
```bash
|
||||
PAGER=cat GH_PAGER=cat gh pr view <PR-NUMBER> --json state,statusCheckRollup,reviewDecision
|
||||
```
|
||||
|
||||
Checklist:
|
||||
- ✅ `state` is `OPEN`
|
||||
- ✅ `statusCheckRollup` all have `conclusion: SUCCESS`
|
||||
- ✅ `reviewDecision` is `APPROVED` or empty (no blocking reviews)
|
||||
|
||||
If any check fails, **do NOT merge**. Report the issue to the user.
|
||||
|
||||
### Step 2 — Identify Related Issues
|
||||
|
||||
Issues can be linked to a PR in multiple ways. Check the PR description and commit messages for keywords:
|
||||
|
||||
```bash
|
||||
PAGER=cat GH_PAGER=cat gh pr view <PR-NUMBER> --json body,commits
|
||||
```
|
||||
|
||||
Look for patterns like:
|
||||
- `Closes #XX`, `Fixes #XX`, `Resolves #XX` (in description or commit bodies)
|
||||
- `#XX` mentioned as "related to" or "addresses"
|
||||
|
||||
**Manual input**: If issue links are not in the PR, ask the user which issue(s) this PR resolves.
|
||||
|
||||
Extract all issue numbers into a list: `[#48, #52, ...]`
|
||||
|
||||
### Step 3 — Select Merge Strategy
|
||||
|
||||
Offer the user three options:
|
||||
|
||||
| Strategy | Git Behavior | Use Case |
|
||||
|----------|-------------|----------|
|
||||
| **Squash** | All commits squashed into one commit on main | Clean history, recommended for release PRs |
|
||||
| **Rebase** | Linear history, no merge commit | Preserve commit granularity |
|
||||
| **Merge** | Merge commit created | Preserve full PR context |
|
||||
|
||||
**Recommendation for release PRs**: Use `--squash` to create a single clean commit.
|
||||
|
||||
If user doesn't specify, default to `--squash`.
|
||||
|
||||
### Step 4 — Prepare Merge Commit Message
|
||||
|
||||
If using `--squash`, craft a single comprehensive commit message:
|
||||
|
||||
**Format** (Conventional Commits + Github linking):
|
||||
```
|
||||
type(scope): description
|
||||
|
||||
- Bullet point 1
|
||||
- Bullet point 2
|
||||
|
||||
Closes #48
|
||||
Closes #52
|
||||
```
|
||||
|
||||
The `Closes #XX` keyword tells GitHub to automatically close those issues when the commit lands on `main`.
|
||||
|
||||
Example:
|
||||
```
|
||||
feat(pipes,filters): release Copilot SDK Pipe v0.8.0 and Files Filter v0.1.3
|
||||
|
||||
- Implement P1~P4 conditional tool filtering system
|
||||
- Fix file publishing reliability across all storage backends
|
||||
- Add strict file URL validation
|
||||
- Update bilingual documentation
|
||||
|
||||
Closes #48
|
||||
```
|
||||
|
||||
### Step 5 — Execute Merge
|
||||
|
||||
```bash
|
||||
gh pr merge <PR-NUMBER> \
|
||||
--squash \
|
||||
--delete-branch \
|
||||
-m "type(scope): description" \
|
||||
-b "- Bullet 1\n- Bullet 2\n\nCloses #48"
|
||||
```
|
||||
|
||||
**Key flags:**
|
||||
- `--squash`: Squash commits (recommended for releases)
|
||||
- `--delete-branch`: Delete the feature branch after merge
|
||||
- `-m`: Commit subject
|
||||
- `-b`: Commit body (supports `\n` for newlines)
|
||||
|
||||
Confirm the merge is successful; GitHub will automatically close related issues with `Closes #XX` keyword.
|
||||
|
||||
### Step 6 — Verify Auto-Close
|
||||
|
||||
GitHub automatically closes issues when a commit with `Closes #XX` lands on the default branch (`main`).
|
||||
|
||||
To verify:
|
||||
```bash
|
||||
PAGER=cat GH_PAGER=cat gh issue view <ISSUE-NUMBER> --json state
|
||||
```
|
||||
|
||||
Should show `state: CLOSED`.
|
||||
|
||||
### Step 7 — Post Closing Message (Optional but Recommended)
|
||||
|
||||
For better UX, manually post a summary comment to **each issue** before it auto-closes (since auto-close happens silently):
|
||||
|
||||
```bash
|
||||
gh issue comment <ISSUE-NUMBER> --body "
|
||||
This has been fixed in PR #<PR-NUMBER>, which is now merged to main.
|
||||
|
||||
**Solution Summary:**
|
||||
- <Key fix 1>
|
||||
- <Key fix 2>
|
||||
|
||||
The fix will be available in the next plugin release. Thank you for reporting! ⭐
|
||||
"
|
||||
```
|
||||
|
||||
### Step 8 — (Optional) Regenerate Release Notes
|
||||
|
||||
If the merge revealed any final tweaks to release notes:
|
||||
|
||||
```bash
|
||||
# Re-export release notes from merged commit
|
||||
git log --oneline -1 <merged-commit-sha>
|
||||
```
|
||||
|
||||
If needed, create a follow-up PR with doc polish (do NOT force-push the merged commit).
|
||||
|
||||
---
|
||||
|
||||
## Merge Strategy Decision Tree
|
||||
|
||||
```
|
||||
Is this a patch/hotfix release?
|
||||
├─ YES → Use --squash
|
||||
└─ NO → Multi-feature release?
|
||||
├─ YES → Use --squash (cleaner history)
|
||||
└─ NO → Preserve detail?
|
||||
├─ YES → Use --rebase
|
||||
└─ NO → Use --merge (preserve PR context)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Issue Auto-Close Keywords
|
||||
|
||||
These keywords in commit/PR messages will auto-close issues when merged to `main`:
|
||||
|
||||
- `Closes #XX`
|
||||
- `Fixes #XX`
|
||||
- `Resolves #XX`
|
||||
- `close #XX` (case-insensitive)
|
||||
- `fix #XX`
|
||||
- `resolve #XX`
|
||||
|
||||
**Important**: The keyword must be on the **final commit that lands on** `main`. For squash merges, it must be in the squash commit message body.
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- ❌ Do NOT merge if any status checks are PENDING or FAILED
|
||||
- ❌ Do NOT merge if there are blocking reviews (reviewDecision: `CHANGES_REQUESTED`)
|
||||
- ❌ Do NOT merge without verifying the Conventional Commits format in the merge message
|
||||
- ❌ Do NOT merge without including `Closes #XX` keywords for all related issues
|
||||
- ❌ Do NOT assume issues will auto-close silently — post a courtesy comment first
|
||||
- ❌ Do NOT delete the branch if it might be needed for cherry-pick or hotfixes later
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue did not auto-close after merge
|
||||
- Verify the `Closes #XX` keyword is in the **final commit message** (use `git log` to check)
|
||||
- Ensure the commit is on the `main` branch
|
||||
- GitHub sometimes takes a few seconds to process; refresh the issue page
|
||||
|
||||
### Multiple issues to close
|
||||
- List all in separate `Closes #XX` lines in the commit body
|
||||
- Each one will be independently auto-closed
|
||||
|
||||
### Want to close issue without merge?
|
||||
- Use `gh issue close <ISSUE-NUMBER>` manually
|
||||
- Only recommended if the PR was manually reverted or deemed invalid
|
||||
137
.gemini/skills/release-prep/SKILL.md
Normal file
137
.gemini/skills/release-prep/SKILL.md
Normal file
@@ -0,0 +1,137 @@
|
||||
---
|
||||
name: release-prep
|
||||
description: Orchestrates the full release preparation flow for a plugin — version sync across 7+ files, bilingual release notes creation, and commit message drafting. Use before submitting a PR. Does NOT push or create a PR; that is handled by pr-submitter.
|
||||
---
|
||||
|
||||
# Release Prep
|
||||
|
||||
## Overview
|
||||
|
||||
This skill drives the complete pre-PR release pipeline. It enforces the repository rule that every release must synchronize the version number and changelog across **at least 7 locations** before a commit is created.
|
||||
|
||||
## Scope
|
||||
|
||||
This skill covers:
|
||||
1. Version sync (delegates detail to `version-bumper` if needed)
|
||||
2. Bilingual release notes file creation
|
||||
3. 7-location consistency verification
|
||||
4. Conventional Commits message drafting
|
||||
5. `git add -A && git commit` execution
|
||||
|
||||
It **stops before** `git push` or `gh pr create`. Use the `pr-submitter` skill for those steps.
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Collect Release Info
|
||||
|
||||
Ask the user (or infer from current state) the following:
|
||||
- **Plugin name** and **type** (actions / filters / pipes / tools)
|
||||
- **New version number** (e.g., `0.8.0`)
|
||||
- **Key changes** in English and Chinese (1-5 bullet points each)
|
||||
|
||||
If a `What's New` section already exists in README.md, extract it as the source of truth.
|
||||
|
||||
### Step 2 — Sync Version Across 7 Locations
|
||||
|
||||
Verify AND update the version string in all of the following. Mark each as ✅ or ❌:
|
||||
|
||||
| # | File | Location |
|
||||
|---|------|----------|
|
||||
| 1 | `plugins/{type}/{name}/{name}.py` | `version:` in docstring |
|
||||
| 2 | `plugins/{type}/{name}/README.md` | `**Version:** x.x.x` metadata line |
|
||||
| 3 | `plugins/{type}/{name}/README_CN.md` | `**Version:** x.x.x` metadata line |
|
||||
| 4 | `docs/plugins/{type}/{name}.md` | `**Version:** x.x.x` metadata line |
|
||||
| 5 | `docs/plugins/{type}/{name}.zh.md` | `**Version:** x.x.x` metadata line |
|
||||
| 6 | `docs/plugins/{type}/index.md` | version badge for this plugin |
|
||||
| 7 | `docs/plugins/{type}/index.zh.md` | version badge for this plugin |
|
||||
|
||||
Additionally update the root-level **updated date badge** in:
|
||||
- `README.md` — ``
|
||||
- `README_CN.md` — same badge format
|
||||
|
||||
Use today's date (`YYYY-MM-DD`) for the badge.
|
||||
|
||||
### Step 3 — Update What's New (All 4 Doc Files)
|
||||
|
||||
The `What's New` / `最新更新` section must contain **only the most recent release's changes**. Previous entries should be removed from this section (they live in CHANGELOG or release notes files).
|
||||
|
||||
Update these 4 files' `What's New` / `最新更新` block consistently:
|
||||
- `plugins/{type}/{name}/README.md`
|
||||
- `plugins/{type}/{name}/README_CN.md`
|
||||
- `docs/plugins/{type}/{name}.md`
|
||||
- `docs/plugins/{type}/{name}.zh.md`
|
||||
|
||||
### Step 4 — Create Bilingual Release Notes Files
|
||||
|
||||
Create two versioned release notes files:
|
||||
|
||||
**Path**: `plugins/{type}/{name}/v{version}.md`
|
||||
**Path**: `plugins/{type}/{name}/v{version}_CN.md`
|
||||
|
||||
#### Required Sections
|
||||
|
||||
Each file must include:
|
||||
1. **Title**: `# v{version} Release Notes` (EN) / `# v{version} 版本发布说明` (CN)
|
||||
2. **Overview**: One paragraph summarizing this release
|
||||
3. **New Features** / **新功能**: Bulleted list of features
|
||||
4. **Bug Fixes** / **问题修复**: Bulleted list of fixes
|
||||
5. **Migration Notes** / **迁移说明**: Breaking changes or Valve key renames (omit section if none)
|
||||
6. **Companion Plugins** / **配套插件** (optional): If a companion plugin was updated
|
||||
|
||||
If a release notes file already exists for this version, update it rather than creating a new one.
|
||||
|
||||
### Step 5 — Verify Consistency (Pre-Commit Check)
|
||||
|
||||
Run the consistency check script:
|
||||
|
||||
```bash
|
||||
python3 scripts/check_version_consistency.py
|
||||
```
|
||||
|
||||
If issues are found, fix them before proceeding. Do not commit with inconsistencies.
|
||||
|
||||
### Step 6 — Draft Conventional Commits Message
|
||||
|
||||
Generate the commit message following `commit-message.instructions.md` rules:
|
||||
- **Language**: English ONLY
|
||||
- **Format**: `type(scope): subject` + blank line + body bullets
|
||||
- **Scope**: use plugin folder name (e.g., `github-copilot-sdk`)
|
||||
- **Body**: 1-3 bullets summarizing key changes
|
||||
- Explicitly mention "READMEs and docs synced" if version was bumped
|
||||
|
||||
Present the full commit message to the user for review before executing.
|
||||
|
||||
### Step 7 — Stage and Commit
|
||||
|
||||
After user approval (or if user says "commit it"):
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "<approved commit message>"
|
||||
```
|
||||
|
||||
Confirm the commit hash and list the number of files changed.
|
||||
|
||||
---
|
||||
|
||||
## Checklist (Auto-Verify Before Commit)
|
||||
|
||||
- [ ] `version:` in `.py` docstring matches target version
|
||||
- [ ] `**Version:**` in all 4 README/docs files matches
|
||||
- [ ] Both `index.md` version badges updated
|
||||
- [ ] Root `README.md` and `README_CN.md` date badges updated to today
|
||||
- [ ] `What's New` / `最新更新` contains ONLY the latest release
|
||||
- [ ] `v{version}.md` and `v{version}_CN.md` created or updated
|
||||
- [ ] `python3 scripts/check_version_consistency.py` returns no errors
|
||||
- [ ] Commit message is English-only Conventional Commits format
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- ❌ Do NOT add extra features or refactor code during release prep — only version/doc updates
|
||||
- ❌ Do NOT push or create PR in this skill — use `pr-submitter`
|
||||
- ❌ Do NOT use today's date in commit messages; only in badge URLs
|
||||
- ❌ Do NOT leave stale What's New content from prior versions
|
||||
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])
|
||||
121
.github/TEMP_FILES_POLICY.md
vendored
Normal file
121
.github/TEMP_FILES_POLICY.md
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
# Temporary Files Handling Policy
|
||||
|
||||
**Last Updated**: 2026-02-26
|
||||
**Status**: Active Guideline
|
||||
|
||||
## Overview
|
||||
|
||||
All temporary files created during skill execution or development workflows must follow this centralized policy to maintain project cleanliness and workspace isolation alignment.
|
||||
|
||||
## Core Rule
|
||||
|
||||
**Temporary files MUST be stored in the project's `.temp/` directory, NOT in system directories like `/tmp`.**
|
||||
|
||||
## Rationale
|
||||
|
||||
1. **Workspace Isolation**: Aligns with OpenWebUI's workspace-per-user model
|
||||
2. **Project Cohesion**: All project artifacts (temporary or permanent) stay within project boundaries
|
||||
3. **Multi-User Safety**: Avoids conflicts between multiple developers using the same system
|
||||
4. **Cleanup Traceability**: Easy to verify all temp files are cleaned up via single `.temp/` directory
|
||||
5. **Debugging**: Inspectable before deletion if issues occur
|
||||
|
||||
## Usage Pattern
|
||||
|
||||
### Creating Temp File
|
||||
|
||||
```bash
|
||||
# Step 1: Ensure temp directory exists
|
||||
mkdir -p .temp
|
||||
|
||||
# Step 2: Write temp file
|
||||
cat > .temp/my_temp_file.md << 'EOF'
|
||||
...content...
|
||||
EOF
|
||||
|
||||
# Step 3: Use the file in your workflow
|
||||
# (e.g., pass to gh CLI, process with script, etc.)
|
||||
```
|
||||
|
||||
### Cleanup After Use
|
||||
|
||||
```bash
|
||||
# Remove individual temp files
|
||||
rm -f .temp/my_temp_file.md
|
||||
|
||||
# Or full cleanup of entire temp directory
|
||||
rm -rf .temp/
|
||||
```
|
||||
|
||||
## Skills Affected
|
||||
|
||||
| Skill | Implementation | Status |
|
||||
|-------|----------------|--------|
|
||||
| `pr-submitter` | PR body file (`.temp/pr_body.md`) | ✅ Updated |
|
||||
| `release-prep` | Draft notes (if any) | ✅ Policy Added |
|
||||
| `version-bumper` | Backup files (if any) | ℹ️ Check needed |
|
||||
| Future skills | TBD | 📋 Must follow policy |
|
||||
|
||||
## .gitignore Configuration
|
||||
|
||||
The following entry in `.gitignore` ensures temp files are never committed:
|
||||
|
||||
```
|
||||
# Temporary files
|
||||
.temp/
|
||||
.build/
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: PR Submitter Skill
|
||||
```bash
|
||||
# Create PR body in temp directory
|
||||
mkdir -p .temp
|
||||
cat > .temp/pr_body.md << 'EOF'
|
||||
## Summary
|
||||
New feature implementation
|
||||
EOF
|
||||
|
||||
# Use with gh CLI
|
||||
gh pr create --body-file .temp/pr_body.md --title "feat: new feature"
|
||||
|
||||
# Cleanup
|
||||
rm -f .temp/pr_body.md
|
||||
```
|
||||
|
||||
### Example 2: Release Prepare Workflow
|
||||
```bash
|
||||
# Create draft changelog
|
||||
mkdir -p .temp
|
||||
cat > .temp/changelog_draft.md << 'EOF'
|
||||
# v1.0.0 Release Notes
|
||||
EOF
|
||||
|
||||
# Edit, validate, then integrate into real files
|
||||
# ...
|
||||
|
||||
# Cleanup
|
||||
rm -f .temp/changelog_draft.md
|
||||
```
|
||||
|
||||
## Anti-Patterns (❌ Don't Do This)
|
||||
|
||||
- ❌ Writing temp files to `/tmp` — will be lost/orphaned
|
||||
- ❌ Writing to root directory or `plugins/` — pollutes repo
|
||||
- ❌ Not cleaning up temp files — accumulates clutter
|
||||
- ❌ Committing `.temp/` files to git — defeats the purpose
|
||||
- ❌ Using absolute paths — breaks workflow portability
|
||||
|
||||
## Enforcement
|
||||
|
||||
1. **Code Review**: PRs should verify no `/tmp` references in scripts
|
||||
2. **CI/CD**: Setup can validate `.temp/` cleanup via git status before commit
|
||||
3. **Documentation**: All skill docs must reference this policy (link to this file)
|
||||
4. **Automated**: Consider adding pre-commit hook to ensure `.temp/` is not staged
|
||||
|
||||
## Questions / Clarifications
|
||||
|
||||
For questions about this policy, refer to:
|
||||
- `.github/skills/pr-submitter/SKILL.md` — Practical example
|
||||
- `.github/skills/release-prep/SKILL.md` — Policy integration
|
||||
- `/memories/repo/temp-file-handling-convention.md` — Internal notes
|
||||
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.**
|
||||
1742
.github/copilot-instructions.md
vendored
1742
.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.
|
||||
71
.github/skills/README.md
vendored
Normal file
71
.github/skills/README.md
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
# 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`
|
||||
|
||||
---
|
||||
|
||||
## Release Pipeline Skills
|
||||
|
||||
These four skills form a complete release pipeline and are designed to be used in sequence:
|
||||
|
||||
```
|
||||
release-prep → pr-submitter → pr-reviewer → release-finalizer
|
||||
(prepare) (push & PR) (respond to review) (merge & close issue)
|
||||
```
|
||||
|
||||
- **release-prep**
|
||||
- Purpose: Full release preparation — version sync across 7+ files, bilingual release notes creation, consistency check, and commit.
|
||||
- Entry: `release-prep/SKILL.md`
|
||||
|
||||
- **pr-submitter**
|
||||
- Purpose: Shell-escape-safe PR submission — writes body to temp file, validates sections, pushes branch, creates PR via `gh pr create --body-file`.
|
||||
- Entry: `pr-submitter/SKILL.md`
|
||||
|
||||
- **pr-reviewer**
|
||||
- Purpose: Fetch PR review comments, categorize feedback, implement fixes, commit and push, reply to reviewers.
|
||||
- Entry: `pr-reviewer/SKILL.md`
|
||||
|
||||
- **release-finalizer**
|
||||
- Purpose: Merge release PR to main with proper commit message, auto-link and close related issues, post closing messages.
|
||||
- Entry: `release-finalizer/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])
|
||||
180
.github/skills/pr-reviewer/SKILL.md
vendored
Normal file
180
.github/skills/pr-reviewer/SKILL.md
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
---
|
||||
name: pr-reviewer
|
||||
description: Fetches PR review comments, analyzes requested changes, implements fixes, commits and pushes the resolution. Use after a reviewer has left comments on an open PR to close the feedback loop efficiently.
|
||||
---
|
||||
|
||||
# PR Reviewer
|
||||
|
||||
## Overview
|
||||
|
||||
This skill automates the response cycle for code review. When a reviewer leaves comments on a Pull Request, this skill fetches all pending feedback, categorizes issues by severity, implements fixes, and submits a follow-up commit with appropriate review response comments.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- An open PR exists with pending review comments
|
||||
- The local branch matches the PR's head branch
|
||||
- `gh` CLI is authenticated
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Fetch Review State
|
||||
|
||||
Retrieve all review comments and overall review status:
|
||||
|
||||
```bash
|
||||
# Get overall review decisions
|
||||
PAGER=cat GH_PAGER=cat gh pr view <PR-NUMBER> --json reviews,reviewDecision,headRefName \
|
||||
--jq '{decision: .reviewDecision, reviews: [.reviews[] | {author: .author.login, state: .state, body: .body}]}'
|
||||
|
||||
# Get inline code comments (specific line comments)
|
||||
PAGER=cat GH_PAGER=cat gh api repos/Fu-Jie/openwebui-extensions/pulls/<PR-NUMBER>/comments \
|
||||
--jq '[.[] | {path: .path, line: .line, body: .body, author: .user.login, id: .id}]'
|
||||
|
||||
# Get general issue comments
|
||||
PAGER=cat GH_PAGER=cat gh issue view <PR-NUMBER> --comments --json comments \
|
||||
--jq '[.comments[] | {author: .author.login, body: .body}]'
|
||||
```
|
||||
|
||||
Confirm the current local branch matches the PR head:
|
||||
```bash
|
||||
git branch --show-current
|
||||
```
|
||||
If mismatched, checkout the correct branch first.
|
||||
|
||||
### Step 2 — Categorize Review Feedback
|
||||
|
||||
Group feedback into categories:
|
||||
|
||||
| Category | Examples | Action |
|
||||
|----------|---------|--------|
|
||||
| **Code Bug** | Logic error, incorrect variable, broken condition | Fix code immediately |
|
||||
| **Style / Formatting** | Indentation, naming convention, missing blank line | Fix code |
|
||||
| **Documentation** | Missing i18n key, wrong version in README, typo | Fix docs |
|
||||
| **Design Question** | Suggestion to restructure, alternative approach | Discuss with user before implementing |
|
||||
| **Nitpick / Optional** | Minor style preferences reviewer marked as optional | Fix if quick; document if skipped |
|
||||
| **Blocking** | Reviewer explicitly blocks merge | Must fix before proceeding |
|
||||
|
||||
Present the full categorized list to the user and confirm the resolution plan.
|
||||
|
||||
### Step 3 — Implement Fixes
|
||||
|
||||
For each accepted fix:
|
||||
|
||||
1. Read the affected file at the commented line for context:
|
||||
```bash
|
||||
sed -n '<line-5>,<line+10>p' <file-path>
|
||||
```
|
||||
2. Apply the fix using appropriate file edit tools
|
||||
3. After editing, verify the specific area looks correct
|
||||
|
||||
**For code changes that might affect behavior:**
|
||||
- Check if tests exist: `ls tests/test_*.py`
|
||||
- If tests exist, run them: `python -m pytest tests/ -v`
|
||||
|
||||
**For documentation fixes:**
|
||||
- If modifying README.md, check if `docs/` mirror needs the same fix
|
||||
- Apply the same fix to both locations
|
||||
|
||||
### Step 4 — Run Consistency Checks
|
||||
|
||||
After all fixes are applied:
|
||||
|
||||
```bash
|
||||
# Version consistency (if any version files were touched)
|
||||
python3 scripts/check_version_consistency.py
|
||||
|
||||
# Quick syntax check for Python files
|
||||
python3 -m py_compile plugins/{type}/{name}/{name}.py && echo "✅ Syntax OK"
|
||||
```
|
||||
|
||||
### Step 5 — Stage and Commit
|
||||
|
||||
Create a new commit (do NOT amend if the branch has already been pushed, to avoid force-push):
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git status
|
||||
```
|
||||
|
||||
Draft a Conventional Commits message for the fixup:
|
||||
|
||||
Format: `fix(scope): address review feedback`
|
||||
|
||||
Body should list what was fixed, referencing reviewer concerns:
|
||||
```
|
||||
fix(github-copilot-sdk): address review feedback from @reviewer
|
||||
|
||||
- Fix X per review comment on line Y of file Z
|
||||
- Update README to clarify auth requirement
|
||||
- Correct edge case in _parse_mcp_servers logic
|
||||
```
|
||||
|
||||
```bash
|
||||
git commit -m "<fixup commit message>"
|
||||
```
|
||||
|
||||
### Step 6 — Push the Fix Commit
|
||||
|
||||
```bash
|
||||
git push origin $(git branch --show-current)
|
||||
```
|
||||
|
||||
**Force-push policy:**
|
||||
- Use `git push` (non-force) by default
|
||||
- Only use `git push --force-with-lease` if:
|
||||
1. The user explicitly requests it, AND
|
||||
2. The only change is an amended commit squash (cosmetic, no logic change)
|
||||
3. Never use `--force` (without `--lease`)
|
||||
|
||||
### Step 7 — Respond to Reviewers
|
||||
|
||||
For each addressed review comment, post a reply:
|
||||
|
||||
```bash
|
||||
# Reply to inline comment
|
||||
gh api repos/Fu-Jie/openwebui-extensions/pulls/<PR-NUMBER>/comments/<COMMENT-ID>/replies \
|
||||
-X POST -f body="Fixed in commit <SHORT-SHA>. <Brief explanation of what was changed.>"
|
||||
|
||||
# General comment to summarize all fixes
|
||||
gh issue comment <PR-NUMBER> --body "All review feedback addressed in commit <SHORT-SHA>:
|
||||
- Fixed: <item 1>
|
||||
- Fixed: <item 2>
|
||||
Ready for re-review. 🙏"
|
||||
```
|
||||
|
||||
### Step 8 — Re-Request Review (Optional)
|
||||
|
||||
If the reviewer had submitted a `CHANGES_REQUESTED` review, request a new review after fixes:
|
||||
|
||||
```bash
|
||||
PAGER=cat GH_PAGER=cat gh api repos/Fu-Jie/openwebui-extensions/pulls/<PR-NUMBER>/requested_reviewers \
|
||||
-X POST -f reviewers[]='<reviewer-login>'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Decision Guide
|
||||
|
||||
### When NOT to implement a suggestion immediately
|
||||
|
||||
- **Design questions**: "Should this be a separate class?" — Present to user for decision
|
||||
- **Optional nitpicks**: Reviewer marked as `nit:` — Ask user if they want to include it
|
||||
- **Large refactors**: If fix would require changing >50 lines, propose a separate follow-up issue instead
|
||||
|
||||
### When to ask the user before proceeding
|
||||
|
||||
- Any fix involving behavioral changes to plugin logic
|
||||
- Renaming Valve keys (breaking change — requires migration notes)
|
||||
- Changes that affect the bilingual release notes already committed
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- ❌ Do NOT `git commit --amend` on a pushed commit without user approval for force-push
|
||||
- ❌ Do NOT silently skip a reviewer's comment; always acknowledge it (implement or explain why not)
|
||||
- ❌ Do NOT use `--force` (only `--force-with-lease` when absolutely necessary)
|
||||
- ❌ Do NOT make unrelated changes in the fixup commit; keep scope focused on review feedback
|
||||
- ❌ Do NOT respond to reviewer comments in Chinese if the PR language context is English
|
||||
194
.github/skills/pr-submitter/SKILL.md
vendored
Normal file
194
.github/skills/pr-submitter/SKILL.md
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
---
|
||||
name: pr-submitter
|
||||
description: Submits a feature branch as a Pull Request with a validated, properly formatted bilingual PR body. Handles shell-escape-safe body writing via temp files. Use after release-prep has committed all changes.
|
||||
---
|
||||
|
||||
# PR Submitter
|
||||
|
||||
## Overview
|
||||
|
||||
This skill handles the final step of pushing a feature branch and creating a validated Pull Request on GitHub. Its primary purpose is to avoid the shell-escaping pitfalls (backticks, special characters in `gh pr create --body`) by always writing the PR body to a **temp file** first.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- All changes are committed (use `release-prep` skill first)
|
||||
- The `gh` CLI is authenticated (`gh auth status`)
|
||||
- Current branch is NOT `main` or `master`
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 0 — Initialize Temp Directory (Project-Based)
|
||||
|
||||
For all temporary files, use the project's `.temp/` directory instead of system `/tmp`:
|
||||
|
||||
```bash
|
||||
# Create temp directory if it doesn't exist
|
||||
mkdir -p .temp
|
||||
```
|
||||
|
||||
**Why**: All temporary files stay within the project workspace, avoiding system `/tmp` pollution and better aligning with OpenWebUI workspace isolation principles.
|
||||
|
||||
### Step 1 — Pre-Flight Checks
|
||||
|
||||
Run these checks before any push:
|
||||
|
||||
```bash
|
||||
# 1. Confirm not on protected branch
|
||||
git branch --show-current
|
||||
|
||||
# 2. Verify there are commits to push
|
||||
git log origin/$(git branch --show-current)..HEAD --oneline 2>/dev/null || echo "No remote tracking branch yet"
|
||||
|
||||
# 3. Check gh CLI auth
|
||||
gh auth status
|
||||
```
|
||||
|
||||
If any check fails, stop and report clearly.
|
||||
|
||||
### Step 2 — Collect PR Metadata
|
||||
|
||||
Gather:
|
||||
- **PR Title**: Must follow Conventional Commits format, English only (e.g., `feat(github-copilot-sdk): release v0.8.0 with conditional tool filtering`)
|
||||
- **Target base branch**: Default is `main`
|
||||
- **Plugin name + version** (to build body sections)
|
||||
- **Key changes** (reuse from release-prep or the latest What's New section)
|
||||
|
||||
### Step 3 — Build PR Body File (Shell-Escape-Safe)
|
||||
|
||||
**Always write the body to a temp file in `.temp/` directory.** Never embed multi-line markdown with special characters directly in a shell command.
|
||||
|
||||
```bash
|
||||
cat > .temp/pr_body.md << 'HEREDOC'
|
||||
## Summary
|
||||
|
||||
Brief one-sentence description of what this PR accomplishes.
|
||||
|
||||
## Changes
|
||||
|
||||
### New Features
|
||||
- Feature 1 description
|
||||
- Feature 2 description
|
||||
|
||||
### Bug Fixes
|
||||
- Fix 1 description
|
||||
|
||||
## Plugin Version
|
||||
- `PluginName` bumped to `vX.X.X`
|
||||
|
||||
## Documentation
|
||||
- README.md / README_CN.md updated
|
||||
- docs/ mirrors synced
|
||||
|
||||
## Testing
|
||||
- [ ] Tested locally in OpenWebUI
|
||||
- [ ] i18n validated (all language keys present)
|
||||
- [ ] Version consistency check passed (`python3 scripts/check_version_consistency.py`)
|
||||
|
||||
---
|
||||
|
||||
## 变更摘要(中文)
|
||||
|
||||
简要描述本次 PR 的改动内容。
|
||||
|
||||
### 新功能
|
||||
- 功能1描述
|
||||
- 功能2描述
|
||||
|
||||
### 问题修复
|
||||
- 修复1描述
|
||||
HEREDOC
|
||||
```
|
||||
|
||||
**Critical rules for the body file:**
|
||||
- Use `<< 'HEREDOC'` (quoted heredoc) to prevent variable expansion
|
||||
- Keep all backticks literal — they are safe inside a heredoc
|
||||
- Paths like `/api/v1/files/` are safe too since heredoc doesn't interpret them as commands
|
||||
|
||||
### Step 4 — Validate PR Body
|
||||
|
||||
Before submitting, verify the body file contains expected sections:
|
||||
|
||||
```bash
|
||||
# Check key sections exist
|
||||
grep -q "## Summary" .temp/pr_body.md && echo "✅ Summary" || echo "❌ Summary missing"
|
||||
grep -q "## Changes" .temp/pr_body.md && echo "✅ Changes" || echo "❌ Changes missing"
|
||||
grep -q "## 变更摘要" .temp/pr_body.md && echo "✅ CN Section" || echo "❌ CN Section missing"
|
||||
|
||||
# Preview the body
|
||||
cat .temp/pr_body.md
|
||||
```
|
||||
|
||||
Ask the user to confirm the body content before proceeding.
|
||||
|
||||
### Step 5 — Push Branch
|
||||
|
||||
```bash
|
||||
git push -u origin $(git branch --show-current)
|
||||
```
|
||||
|
||||
If push is rejected (non-fast-forward), report to user and ask whether to force-push. **Do NOT force-push without explicit confirmation.**
|
||||
|
||||
### Step 6 — Create Pull Request
|
||||
|
||||
```bash
|
||||
gh pr create \
|
||||
--base main \
|
||||
--head $(git branch --show-current) \
|
||||
--title "<PR title from Step 2>" \
|
||||
--body-file .temp/pr_body.md
|
||||
```
|
||||
|
||||
Always use `--body-file`, never `--body` with inline markdown.
|
||||
|
||||
### Step 7 — Verify PR Creation
|
||||
|
||||
```bash
|
||||
PAGER=cat GH_PAGER=cat gh pr view --json number,url,title,body --jq '{number: .number, url: .url, title: .title, body_preview: .body[:200]}'
|
||||
```
|
||||
|
||||
Confirm:
|
||||
- PR number and URL
|
||||
- Title matches intended Conventional Commits format
|
||||
- Body preview includes key sections (not truncated/corrupted)
|
||||
|
||||
If the body appears corrupted (empty sections, missing backtick content), use edit:
|
||||
|
||||
```bash
|
||||
gh pr edit <PR-NUMBER> --body-file /tmp/pr_body.md
|
||||
```
|
||||
|
||||
### Step 8 — Cleanup
|
||||
|
||||
```bash
|
||||
rm -f .temp/pr_body.md
|
||||
```
|
||||
|
||||
**Note**: The `.temp/` directory itself is preserved for reuse; only the individual PR body file is deleted. To fully clean up: `rm -rf .temp/`
|
||||
|
||||
Report final PR URL to the user.
|
||||
|
||||
---
|
||||
|
||||
## Shell-Escape Safety Rules
|
||||
|
||||
| Risk | Safe Approach |
|
||||
|------|--------------|
|
||||
| Backticks in `--body` | Write to file, use `--body-file` |
|
||||
| Paths like `/api/...` | Safe in heredoc; risky in inline `--body` |
|
||||
| Newlines in `--body` | File-based only |
|
||||
| `$variable` expansion | Use `<< 'HEREDOC'` (quoted) |
|
||||
| Double quotes in body | Safe in heredoc file |
|
||||
| Temp file storage | Use `.temp/` dir, not `/tmp` |
|
||||
| Cleanup after use | Always delete temp file (keep dir) |
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- ❌ Never use `--body "..."` with multi-line content directly in shell command
|
||||
- ❌ Never interpolate variables directly into heredoc without quoting the delimiter
|
||||
- ❌ Never force-push (`--force`) without explicit user confirmation
|
||||
- ❌ Never target `main` as the source branch (only as base)
|
||||
- ❌ Never skip the body validation step — a PR with empty body is worse than a delayed PR
|
||||
208
.github/skills/release-finalizer/SKILL.md
vendored
Normal file
208
.github/skills/release-finalizer/SKILL.md
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
---
|
||||
name: release-finalizer
|
||||
description: Merges a release PR, associates it with resolved issues, replies to issue reporters, and closes issues. Use after PR review is complete and ready for merge. Closes the release cycle.
|
||||
---
|
||||
|
||||
# Release Finalizer
|
||||
|
||||
## Overview
|
||||
|
||||
This skill completes the final step of the release cycle: merging the release PR to `main`, replying to all related issues with solutions, and automatically closing them using GitHub's issue linking mechanism.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- The PR is in `OPEN` state and ready to merge
|
||||
- All status checks have passed (CI green)
|
||||
- All review feedback has been addressed
|
||||
- The PR relates to one or more GitHub issues (either in PR description or through commits)
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Pre-Merge Verification
|
||||
|
||||
Verify that the PR is ready:
|
||||
|
||||
```bash
|
||||
PAGER=cat GH_PAGER=cat gh pr view <PR-NUMBER> --json state,statusCheckRollup,reviewDecision
|
||||
```
|
||||
|
||||
Checklist:
|
||||
- ✅ `state` is `OPEN`
|
||||
- ✅ `statusCheckRollup` all have `conclusion: SUCCESS`
|
||||
- ✅ `reviewDecision` is `APPROVED` or empty (no blocking reviews)
|
||||
|
||||
If any check fails, **do NOT merge**. Report the issue to the user.
|
||||
|
||||
### Step 2 — Identify Related Issues
|
||||
|
||||
Issues can be linked to a PR in multiple ways. Check the PR description and commit messages for keywords:
|
||||
|
||||
```bash
|
||||
PAGER=cat GH_PAGER=cat gh pr view <PR-NUMBER> --json body,commits
|
||||
```
|
||||
|
||||
Look for patterns like:
|
||||
- `Closes #XX`, `Fixes #XX`, `Resolves #XX` (in description or commit bodies)
|
||||
- `#XX` mentioned as "related to" or "addresses"
|
||||
|
||||
**Manual input**: If issue links are not in the PR, ask the user which issue(s) this PR resolves.
|
||||
|
||||
Extract all issue numbers into a list: `[#48, #52, ...]`
|
||||
|
||||
### Step 3 — Select Merge Strategy
|
||||
|
||||
Offer the user three options:
|
||||
|
||||
| Strategy | Git Behavior | Use Case |
|
||||
|----------|-------------|----------|
|
||||
| **Squash** | All commits squashed into one commit on main | Clean history, recommended for release PRs |
|
||||
| **Rebase** | Linear history, no merge commit | Preserve commit granularity |
|
||||
| **Merge** | Merge commit created | Preserve full PR context |
|
||||
|
||||
**Recommendation for release PRs**: Use `--squash` to create a single clean commit.
|
||||
|
||||
If user doesn't specify, default to `--squash`.
|
||||
|
||||
### Step 4 — Prepare Merge Commit Message
|
||||
|
||||
If using `--squash`, craft a single comprehensive commit message:
|
||||
|
||||
**Format** (Conventional Commits + Github linking):
|
||||
```
|
||||
type(scope): description
|
||||
|
||||
- Bullet point 1
|
||||
- Bullet point 2
|
||||
|
||||
Closes #48
|
||||
Closes #52
|
||||
```
|
||||
|
||||
The `Closes #XX` keyword tells GitHub to automatically close those issues when the commit lands on `main`.
|
||||
|
||||
Example:
|
||||
```
|
||||
feat(pipes,filters): release Copilot SDK Pipe v0.8.0 and Files Filter v0.1.3
|
||||
|
||||
- Implement P1~P4 conditional tool filtering system
|
||||
- Fix file publishing reliability across all storage backends
|
||||
- Add strict file URL validation
|
||||
- Update bilingual documentation
|
||||
|
||||
Closes #48
|
||||
```
|
||||
|
||||
### Step 5 — Execute Merge
|
||||
|
||||
```bash
|
||||
gh pr merge <PR-NUMBER> \
|
||||
--squash \
|
||||
--delete-branch \
|
||||
-m "type(scope): description" \
|
||||
-b "- Bullet 1\n- Bullet 2\n\nCloses #48"
|
||||
```
|
||||
|
||||
**Key flags:**
|
||||
- `--squash`: Squash commits (recommended for releases)
|
||||
- `--delete-branch`: Delete the feature branch after merge
|
||||
- `-m`: Commit subject
|
||||
- `-b`: Commit body (supports `\n` for newlines)
|
||||
|
||||
Confirm the merge is successful; GitHub will automatically close related issues with `Closes #XX` keyword.
|
||||
|
||||
### Step 6 — Verify Auto-Close
|
||||
|
||||
GitHub automatically closes issues when a commit with `Closes #XX` lands on the default branch (`main`).
|
||||
|
||||
To verify:
|
||||
```bash
|
||||
PAGER=cat GH_PAGER=cat gh issue view <ISSUE-NUMBER> --json state
|
||||
```
|
||||
|
||||
Should show `state: CLOSED`.
|
||||
|
||||
### Step 7 — Post Closing Message (Optional but Recommended)
|
||||
|
||||
For better UX, manually post a summary comment to **each issue** before it auto-closes (since auto-close happens silently):
|
||||
|
||||
```bash
|
||||
gh issue comment <ISSUE-NUMBER> --body "
|
||||
This has been fixed in PR #<PR-NUMBER>, which is now merged to main.
|
||||
|
||||
**Solution Summary:**
|
||||
- <Key fix 1>
|
||||
- <Key fix 2>
|
||||
|
||||
The fix will be available in the next plugin release. Thank you for reporting! ⭐
|
||||
"
|
||||
```
|
||||
|
||||
### Step 8 — (Optional) Regenerate Release Notes
|
||||
|
||||
If the merge revealed any final tweaks to release notes:
|
||||
|
||||
```bash
|
||||
# Re-export release notes from merged commit
|
||||
git log --oneline -1 <merged-commit-sha>
|
||||
```
|
||||
|
||||
If needed, create a follow-up PR with doc polish (do NOT force-push the merged commit).
|
||||
|
||||
---
|
||||
|
||||
## Merge Strategy Decision Tree
|
||||
|
||||
```
|
||||
Is this a patch/hotfix release?
|
||||
├─ YES → Use --squash
|
||||
└─ NO → Multi-feature release?
|
||||
├─ YES → Use --squash (cleaner history)
|
||||
└─ NO → Preserve detail?
|
||||
├─ YES → Use --rebase
|
||||
└─ NO → Use --merge (preserve PR context)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Issue Auto-Close Keywords
|
||||
|
||||
These keywords in commit/PR messages will auto-close issues when merged to `main`:
|
||||
|
||||
- `Closes #XX`
|
||||
- `Fixes #XX`
|
||||
- `Resolves #XX`
|
||||
- `close #XX` (case-insensitive)
|
||||
- `fix #XX`
|
||||
- `resolve #XX`
|
||||
|
||||
**Important**: The keyword must be on the **final commit that lands on** `main`. For squash merges, it must be in the squash commit message body.
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- ❌ Do NOT merge if any status checks are PENDING or FAILED
|
||||
- ❌ Do NOT merge if there are blocking reviews (reviewDecision: `CHANGES_REQUESTED`)
|
||||
- ❌ Do NOT merge without verifying the Conventional Commits format in the merge message
|
||||
- ❌ Do NOT merge without including `Closes #XX` keywords for all related issues
|
||||
- ❌ Do NOT assume issues will auto-close silently — post a courtesy comment first
|
||||
- ❌ Do NOT delete the branch if it might be needed for cherry-pick or hotfixes later
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue did not auto-close after merge
|
||||
- Verify the `Closes #XX` keyword is in the **final commit message** (use `git log` to check)
|
||||
- Ensure the commit is on the `main` branch
|
||||
- GitHub sometimes takes a few seconds to process; refresh the issue page
|
||||
|
||||
### Multiple issues to close
|
||||
- List all in separate `Closes #XX` lines in the commit body
|
||||
- Each one will be independently auto-closed
|
||||
|
||||
### Want to close issue without merge?
|
||||
- Use `gh issue close <ISSUE-NUMBER>` manually
|
||||
- Only recommended if the PR was manually reverted or deemed invalid
|
||||
144
.github/skills/release-prep/SKILL.md
vendored
Normal file
144
.github/skills/release-prep/SKILL.md
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
---
|
||||
name: release-prep
|
||||
description: Orchestrates the full release preparation flow for a plugin — version sync across 7+ files, bilingual release notes creation, and commit message drafting. Use before submitting a PR. Does NOT push or create a PR; that is handled by pr-submitter.
|
||||
---
|
||||
|
||||
# Release Prep
|
||||
|
||||
## Overview
|
||||
|
||||
This skill drives the complete pre-PR release pipeline. It enforces the repository rule that every release must synchronize the version number and changelog across **at least 7 locations** before a commit is created.
|
||||
|
||||
## Scope
|
||||
|
||||
This skill covers:
|
||||
1. Version sync (delegates detail to `version-bumper` if needed)
|
||||
2. Bilingual release notes file creation
|
||||
3. 7-location consistency verification
|
||||
4. Conventional Commits message drafting
|
||||
5. `git add -A && git commit` execution
|
||||
|
||||
It **stops before** `git push` or `gh pr create`. Use the `pr-submitter` skill for those steps.
|
||||
|
||||
### Temporary File Convention
|
||||
|
||||
Any temporary files created during release prep (e.g., draft changelogs) must:
|
||||
- Be written to the project's `.temp/` directory, **NOT** system `/tmp`
|
||||
- Be cleaned up before commit using `rm -f .temp/file_name`
|
||||
- Never be committed to git (add `.temp/` to `.gitignore`)
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Collect Release Info
|
||||
|
||||
Ask the user (or infer from current state) the following:
|
||||
- **Plugin name** and **type** (actions / filters / pipes / tools)
|
||||
- **New version number** (e.g., `0.8.0`)
|
||||
- **Key changes** in English and Chinese (1-5 bullet points each)
|
||||
|
||||
If a `What's New` section already exists in README.md, extract it as the source of truth.
|
||||
|
||||
### Step 2 — Sync Version Across 7 Locations
|
||||
|
||||
Verify AND update the version string in all of the following. Mark each as ✅ or ❌:
|
||||
|
||||
| # | File | Location |
|
||||
|---|------|----------|
|
||||
| 1 | `plugins/{type}/{name}/{name}.py` | `version:` in docstring |
|
||||
| 2 | `plugins/{type}/{name}/README.md` | `**Version:** x.x.x` metadata line |
|
||||
| 3 | `plugins/{type}/{name}/README_CN.md` | `**Version:** x.x.x` metadata line |
|
||||
| 4 | `docs/plugins/{type}/{name}.md` | `**Version:** x.x.x` metadata line |
|
||||
| 5 | `docs/plugins/{type}/{name}.zh.md` | `**Version:** x.x.x` metadata line |
|
||||
| 6 | `docs/plugins/{type}/index.md` | version badge for this plugin |
|
||||
| 7 | `docs/plugins/{type}/index.zh.md` | version badge for this plugin |
|
||||
|
||||
Additionally update the root-level **updated date badge** in:
|
||||
- `README.md` — ``
|
||||
- `README_CN.md` — same badge format
|
||||
|
||||
Use today's date (`YYYY-MM-DD`) for the badge.
|
||||
|
||||
### Step 3 — Update What's New (All 4 Doc Files)
|
||||
|
||||
The `What's New` / `最新更新` section must contain **only the most recent release's changes**. Previous entries should be removed from this section (they live in CHANGELOG or release notes files).
|
||||
|
||||
Update these 4 files' `What's New` / `最新更新` block consistently:
|
||||
- `plugins/{type}/{name}/README.md`
|
||||
- `plugins/{type}/{name}/README_CN.md`
|
||||
- `docs/plugins/{type}/{name}.md`
|
||||
- `docs/plugins/{type}/{name}.zh.md`
|
||||
|
||||
### Step 4 — Create Bilingual Release Notes Files
|
||||
|
||||
Create two versioned release notes files:
|
||||
|
||||
**Path**: `plugins/{type}/{name}/v{version}.md`
|
||||
**Path**: `plugins/{type}/{name}/v{version}_CN.md`
|
||||
|
||||
#### Required Sections
|
||||
|
||||
Each file must include:
|
||||
1. **Title**: `# v{version} Release Notes` (EN) / `# v{version} 版本发布说明` (CN)
|
||||
2. **Overview**: One paragraph summarizing this release
|
||||
3. **New Features** / **新功能**: Bulleted list of features
|
||||
4. **Bug Fixes** / **问题修复**: Bulleted list of fixes
|
||||
5. **Migration Notes** / **迁移说明**: Breaking changes or Valve key renames (omit section if none)
|
||||
6. **Companion Plugins** / **配套插件** (optional): If a companion plugin was updated
|
||||
|
||||
If a release notes file already exists for this version, update it rather than creating a new one.
|
||||
|
||||
### Step 5 — Verify Consistency (Pre-Commit Check)
|
||||
|
||||
Run the consistency check script:
|
||||
|
||||
```bash
|
||||
python3 scripts/check_version_consistency.py
|
||||
```
|
||||
|
||||
If issues are found, fix them before proceeding. Do not commit with inconsistencies.
|
||||
|
||||
### Step 6 — Draft Conventional Commits Message
|
||||
|
||||
Generate the commit message following `commit-message.instructions.md` rules:
|
||||
- **Language**: English ONLY
|
||||
- **Format**: `type(scope): subject` + blank line + body bullets
|
||||
- **Scope**: use plugin folder name (e.g., `github-copilot-sdk`)
|
||||
- **Body**: 1-3 bullets summarizing key changes
|
||||
- Explicitly mention "READMEs and docs synced" if version was bumped
|
||||
|
||||
Present the full commit message to the user for review before executing.
|
||||
|
||||
### Step 7 — Stage and Commit
|
||||
|
||||
After user approval (or if user says "commit it"):
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "<approved commit message>"
|
||||
```
|
||||
|
||||
Confirm the commit hash and list the number of files changed.
|
||||
|
||||
---
|
||||
|
||||
## Checklist (Auto-Verify Before Commit)
|
||||
|
||||
- [ ] `version:` in `.py` docstring matches target version
|
||||
- [ ] `**Version:**` in all 4 README/docs files matches
|
||||
- [ ] Both `index.md` version badges updated
|
||||
- [ ] Root `README.md` and `README_CN.md` date badges updated to today
|
||||
- [ ] `What's New` / `最新更新` contains ONLY the latest release
|
||||
- [ ] `v{version}.md` and `v{version}_CN.md` created or updated
|
||||
- [ ] `python3 scripts/check_version_consistency.py` returns no errors
|
||||
- [ ] Commit message is English-only Conventional Commits format
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- ❌ Do NOT add extra features or refactor code during release prep — only version/doc updates
|
||||
- ❌ Do NOT push or create PR in this skill — use `pr-submitter`
|
||||
- ❌ Do NOT use today's date in commit messages; only in badge URLs
|
||||
- ❌ Do NOT leave stale What's New content from prior versions
|
||||
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
|
||||
138
.github/workflows/release.yml
vendored
138
.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
|
||||
@@ -180,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"
|
||||
@@ -226,6 +244,52 @@ jobs:
|
||||
echo "=== Collected Files ==="
|
||||
find release_plugins -name "*.py" -type f | head -20
|
||||
|
||||
- name: Update plugin icon URLs
|
||||
run: |
|
||||
echo "Updating icon_url in plugins to use absolute GitHub URLs..."
|
||||
# Base URL for raw content using the release tag
|
||||
REPO_URL="https://raw.githubusercontent.com/${{ github.repository }}/${{ steps.version.outputs.version }}"
|
||||
|
||||
find release_plugins -name "*.py" | while read -r file; do
|
||||
# $file is like release_plugins/plugins/actions/infographic/infographic.py
|
||||
# Remove release_plugins/ prefix to get the path in the repo
|
||||
src_file="${file#release_plugins/}"
|
||||
src_dir=$(dirname "$src_file")
|
||||
base_name=$(basename "$src_file" .py)
|
||||
|
||||
# Check if a corresponding png exists in the source repository
|
||||
png_file="${src_dir}/${base_name}.png"
|
||||
|
||||
if [ -f "$png_file" ]; then
|
||||
echo "Found icon for $src_file: $png_file"
|
||||
TARGET_ICON_URL="${REPO_URL}/${png_file}"
|
||||
|
||||
# Use python for safe replacement
|
||||
python3 -c "
|
||||
import sys
|
||||
import re
|
||||
|
||||
file_path = '$file'
|
||||
icon_url = '$TARGET_ICON_URL'
|
||||
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Replace icon_url: ... with new url
|
||||
# Matches 'icon_url: ...' and replaces it
|
||||
new_content = re.sub(r'^icon_url:.*$', f'icon_url: {icon_url}', content, flags=re.MULTILINE)
|
||||
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
print(f'Successfully updated icon_url in {file_path}')
|
||||
except Exception as e:
|
||||
print(f'Error updating {file_path}: {e}', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Debug Filenames
|
||||
run: |
|
||||
python3 -c "import sys; print(f'Filesystem encoding: {sys.getfilesystemencoding()}')"
|
||||
@@ -258,15 +322,14 @@ jobs:
|
||||
|
||||
- name: Generate release notes
|
||||
id: notes
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
TITLE: ${{ github.event.inputs.release_title }}
|
||||
NOTES: ${{ github.event.inputs.release_notes }}
|
||||
DETECTED_CHANGES: ${{ needs.check-changes.outputs.release_notes }}
|
||||
COMMITS: ${{ steps.commits.outputs.commits }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
TITLE="${{ github.event.inputs.release_title }}"
|
||||
NOTES="${{ github.event.inputs.release_notes }}"
|
||||
DETECTED_CHANGES="${{ needs.check-changes.outputs.release_notes }}"
|
||||
COMMITS="${{ steps.commits.outputs.commits }}"
|
||||
|
||||
echo "# ${VERSION} Release / 发布" > release_notes.md
|
||||
echo "" >> release_notes.md
|
||||
> release_notes.md
|
||||
|
||||
if [ -n "$TITLE" ]; then
|
||||
echo "## $TITLE" >> release_notes.md
|
||||
@@ -274,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
|
||||
@@ -298,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
|
||||
@@ -310,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
|
||||
@@ -318,20 +381,41 @@ 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
|
||||
env:
|
||||
@@ -341,7 +425,13 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
find release_plugins -type f -name "*.py" -print0 | xargs -0 gh release upload ${{ steps.version.outputs.version }} --clobber
|
||||
# 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: |
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -136,6 +136,9 @@ logs/
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.temp
|
||||
.temp/
|
||||
.build/
|
||||
|
||||
# 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
|
||||
170
README.md
170
README.md
@@ -1,43 +1,140 @@
|
||||
# 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) |  |  |  |  |
|
||||
|
||||
### 📈 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 autonomous Agent for OpenWebUI.** Transforming your LLM into a powerful OS-level engineer with native code execution, deep tool autonomy, and professional skill management.
|
||||
|
||||
> [!TIP]
|
||||
> **No GitHub Copilot subscription required!** Supports **BYOK (Bring Your Own Key)** mode using your own OpenAI/Anthropic API keys.
|
||||
|
||||
#### 🚀 Key Leap (v0.9.0+)
|
||||
|
||||
- **🧩 Skills Revolution**: Native support for **SKILL directories** (scripts, templates, resources) coupled with a **Bidirectional Bridge** to OpenWebUI Workspace Skills.
|
||||
- **🛡️ Secure Isolation**: Strict user/session-level **Workspace Sandboxing** with persistent configuration.
|
||||
- **📊 Interactive Delivery**: Professional **File Delivery Protocol** for instant HTML artifacts and persistent downloadable results.
|
||||
- **🛠️ Deterministic Toolchain**: Built-in specialized tools for skill lifecycles (`manage_skills`) and system optimization.
|
||||
|
||||
#### 📺 Demo: Visual Skills & Data Analysis
|
||||
|
||||

|
||||
> *In this demo, the Agent installs a visual enhancement skill and automatically generates an interactive dashboard from World Cup data.*
|
||||
|
||||
#### 🌟 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)**
|
||||
|
||||
[](https://openwebui.com/blog/newsletter-january-28-2026): An all-in-one prompt management suite featuring AI-powered prompt generation, spotlight-style quick search, and advanced category organization.
|
||||
|
||||
## 📖 Documentation
|
||||
|
||||
@@ -60,14 +157,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!
|
||||
|
||||
174
README_CN.md
174
README_CN.md
@@ -1,100 +1,154 @@
|
||||
# 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)
|
||||

|
||||
|
||||
位于 `prompts/` 目录,包含精心调优的 System Prompts:
|
||||
*完整统计与趋势图请查看 [社区统计报告](./docs/community-stats.zh.md)*
|
||||
<!-- STATS_END -->
|
||||
|
||||
- **Coding**: 编程辅助类提示词。
|
||||
- **Marketing**: 营销文案类提示词。(`/prompts/marketing`): 内容创作、品牌策划、市场分析相关的提示词
|
||||
## 🌟 精选功能
|
||||
|
||||
每个提示词都独立保存为 Markdown 文件,可直接在 OpenWebUI 中使用。
|
||||
### 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)
|
||||
|
||||
### 🔧 插件 (Plugins)
|
||||
**OpenWebUI 终极自主 Agent 增强。** 将 LLM 转化为具备 OS 级操作能力的专业工程师,支持原生代码执行、深度工具自治以及专业技能管理。
|
||||
|
||||
{{ ... }}
|
||||
> [!TIP]
|
||||
> **无需 GitHub Copilot 订阅!** 支持 **BYOK (Bring Your Own Key)** 模式,使用你自己的 OpenAI/Anthropic API Key。
|
||||
|
||||
[贡献指南](./CONTRIBUTING.md) | [更新日志](./CHANGELOG.md)
|
||||
#### 🚀 核心进化 (v0.9.0+)
|
||||
|
||||
- **🧩 技能革命**: 原生支持 **SKILL 目录**(含脚本、模板与资源),并实现与 OpenWebUI **工作区 > Skills** 的深度双向桥接。
|
||||
- **🛡️ 安全沙箱**: 严格的用户/会话级 **工作区隔离** 与持久化配置环境。
|
||||
- **📊 交互交付**: 专业 **文件交付协议**,支持即时预览交互式 HTML Artifacts 与持久化结果下载。
|
||||
- **🛠️ 确定性工具链**: 内置 `manage_skills` 等专业工具,赋予 Agent 完整的技能生命周期管理能力。
|
||||
|
||||
#### 📺 演示:可视化技能与数据分析
|
||||
|
||||

|
||||
> *在此演示中,Agent 自动安装可视化增强技能,并根据世界杯表格数据瞬间生成交互式看板。*
|
||||
|
||||
#### 🌟 核心实战案例
|
||||
|
||||
- **[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)** [](https://openwebui.com/blog/newsletter-january-28-2026):一站式提示词管理套件,支持 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)
|
||||
BIN
docs/assets/videos/skill.gif
Normal file
BIN
docs/assets/videos/skill.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 MiB |
BIN
docs/assets/videos/skill_visual_agents.mov
Normal file
BIN
docs/assets/videos/skill_visual_agents.mov
Normal file
Binary file not shown.
7
docs/badges/downloads.json
Normal file
7
docs/badges/downloads.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"label": "downloads",
|
||||
"message": "6.4k",
|
||||
"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": "295",
|
||||
"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": "25",
|
||||
"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": "299",
|
||||
"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": "254",
|
||||
"color": "brightgreen"
|
||||
}
|
||||
431
docs/community-stats.json
Normal file
431
docs/community-stats.json
Normal file
@@ -0,0 +1,431 @@
|
||||
{
|
||||
"total_posts": 25,
|
||||
"total_downloads": 6379,
|
||||
"total_views": 67827,
|
||||
"total_upvotes": 254,
|
||||
"total_downvotes": 3,
|
||||
"total_saves": 337,
|
||||
"total_comments": 58,
|
||||
"by_type": {
|
||||
"post": 6,
|
||||
"pipe": 1,
|
||||
"action": 12,
|
||||
"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": 1328,
|
||||
"views": 11410,
|
||||
"upvotes": 23,
|
||||
"saves": 59,
|
||||
"comments": 15,
|
||||
"created_at": "2025-12-30",
|
||||
"updated_at": "2026-02-27",
|
||||
"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": 1076,
|
||||
"views": 10746,
|
||||
"upvotes": 25,
|
||||
"saves": 40,
|
||||
"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.7",
|
||||
"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. Including LaTeX command protection.",
|
||||
"downloads": 609,
|
||||
"views": 6795,
|
||||
"upvotes": 18,
|
||||
"saves": 37,
|
||||
"comments": 5,
|
||||
"created_at": "2026-01-12",
|
||||
"updated_at": "2026-02-27",
|
||||
"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": 578,
|
||||
"views": 4611,
|
||||
"upvotes": 16,
|
||||
"saves": 30,
|
||||
"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": 559,
|
||||
"views": 5452,
|
||||
"upvotes": 15,
|
||||
"saves": 41,
|
||||
"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": 492,
|
||||
"views": 2693,
|
||||
"upvotes": 10,
|
||||
"saves": 8,
|
||||
"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": 473,
|
||||
"views": 5498,
|
||||
"upvotes": 9,
|
||||
"saves": 14,
|
||||
"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": 285,
|
||||
"views": 4128,
|
||||
"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.9.0",
|
||||
"author": "Fu-Jie",
|
||||
"description": "Integrate GitHub Copilot SDK. Supports dynamic models, multi-turn conversation, streaming, multimodal input, infinite sessions, bidirectional OpenWebUI Skills bridge, and manage_skills tool.",
|
||||
"downloads": 263,
|
||||
"views": 4106,
|
||||
"upvotes": 14,
|
||||
"saves": 10,
|
||||
"comments": 6,
|
||||
"created_at": "2026-01-26",
|
||||
"updated_at": "2026-02-27",
|
||||
"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": 204,
|
||||
"views": 1631,
|
||||
"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": 153,
|
||||
"views": 2631,
|
||||
"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": 99,
|
||||
"views": 1839,
|
||||
"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": 65,
|
||||
"views": 1304,
|
||||
"upvotes": 10,
|
||||
"saves": 1,
|
||||
"comments": 0,
|
||||
"created_at": "2025-12-28",
|
||||
"updated_at": "2026-02-13",
|
||||
"url": "https://openwebui.com/posts/智能信息图_e04a48ff"
|
||||
},
|
||||
{
|
||||
"title": "GitHub Copilot SDK Files Filter",
|
||||
"slug": "github_copilot_sdk_files_filter_403a62ee",
|
||||
"type": "filter",
|
||||
"version": "0.1.3",
|
||||
"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": 54,
|
||||
"views": 2098,
|
||||
"upvotes": 3,
|
||||
"saves": 1,
|
||||
"comments": 0,
|
||||
"created_at": "2026-02-09",
|
||||
"updated_at": "2026-02-25",
|
||||
"url": "https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee"
|
||||
},
|
||||
{
|
||||
"title": "思维导图",
|
||||
"slug": "智能生成交互式思维导图帮助用户可视化知识_8d4b097b",
|
||||
"type": "action",
|
||||
"version": "0.9.2",
|
||||
"author": "Fu-Jie",
|
||||
"description": "智能分析文本内容,生成交互式思维导图,帮助用户结构化和可视化知识。",
|
||||
"downloads": 45,
|
||||
"views": 691,
|
||||
"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": 38,
|
||||
"views": 783,
|
||||
"upvotes": 7,
|
||||
"saves": 5,
|
||||
"comments": 0,
|
||||
"created_at": "2025-11-08",
|
||||
"updated_at": "2026-02-13",
|
||||
"url": "https://openwebui.com/posts/异步上下文压缩_5c0617cb"
|
||||
},
|
||||
{
|
||||
"title": "闪记卡 (Flash Card)",
|
||||
"slug": "闪记卡生成插件_4a31eac3",
|
||||
"type": "action",
|
||||
"version": "0.2.4",
|
||||
"author": "Fu-Jie",
|
||||
"description": "快速将文本提炼为精美的学习记忆卡片,支持核心要点提取与分类。",
|
||||
"downloads": 32,
|
||||
"views": 830,
|
||||
"upvotes": 7,
|
||||
"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": 26,
|
||||
"views": 581,
|
||||
"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.9.0: Copilot SDK Skills Core Capabilities & Extended Delivery",
|
||||
"slug": "github_copilot_sdk_pipe_v090_copilot_sdk_skills_co_99a42452",
|
||||
"type": "post",
|
||||
"version": "",
|
||||
"author": "",
|
||||
"description": "",
|
||||
"downloads": 0,
|
||||
"views": 7,
|
||||
"upvotes": 0,
|
||||
"saves": 0,
|
||||
"comments": 0,
|
||||
"created_at": "2026-02-27",
|
||||
"updated_at": "2026-02-27",
|
||||
"url": "https://openwebui.com/posts/github_copilot_sdk_pipe_v090_copilot_sdk_skills_co_99a42452"
|
||||
},
|
||||
{
|
||||
"title": "🚀 GitHub Copilot SDK Pipe v0.8.0: Conditional Tool Filtering & Publish Reliability 🎛️",
|
||||
"slug": "github_copilot_sdk_pipe_v080_conditional_tool_filt_a5a3322d",
|
||||
"type": "post",
|
||||
"version": "",
|
||||
"author": "",
|
||||
"description": "",
|
||||
"downloads": 0,
|
||||
"views": 1059,
|
||||
"upvotes": 2,
|
||||
"saves": 2,
|
||||
"comments": 0,
|
||||
"created_at": "2026-02-25",
|
||||
"updated_at": "2026-02-25",
|
||||
"url": "https://openwebui.com/posts/github_copilot_sdk_pipe_v080_conditional_tool_filt_a5a3322d"
|
||||
},
|
||||
{
|
||||
"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": 2162,
|
||||
"upvotes": 7,
|
||||
"saves": 2,
|
||||
"comments": 1,
|
||||
"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": 2257,
|
||||
"upvotes": 7,
|
||||
"saves": 4,
|
||||
"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": 1839,
|
||||
"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": 234,
|
||||
"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": 1502,
|
||||
"upvotes": 16,
|
||||
"saves": 11,
|
||||
"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": 295,
|
||||
"following": 6,
|
||||
"total_points": 299,
|
||||
"post_points": 251,
|
||||
"comment_points": 48,
|
||||
"contributions": 54
|
||||
}
|
||||
}
|
||||
63
docs/community-stats.md
Normal file
63
docs/community-stats.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# 📊 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-27 |
|
||||
| 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-27 |
|
||||
| 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-27 |
|
||||
| 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 | [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee) | filter |  |  |  |  |  | 2026-02-25 |
|
||||
| 15 | [思维导图](https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b) | action |  |  |  |  |  | 2026-02-13 |
|
||||
| 16 | [异步上下文压缩](https://openwebui.com/posts/异步上下文压缩_5c0617cb) | action |  |  |  |  |  | 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.9.0: Copilot SDK Skills Core Capabilities & Extended Delivery](https://openwebui.com/posts/github_copilot_sdk_pipe_v090_copilot_sdk_skills_co_99a42452) | post |  |  |  |  |  | 2026-02-27 |
|
||||
| 20 | [🚀 GitHub Copilot SDK Pipe v0.8.0: Conditional Tool Filtering & Publish Reliability 🎛️](https://openwebui.com/posts/github_copilot_sdk_pipe_v080_conditional_tool_filt_a5a3322d) | post |  |  |  |  |  | 2026-02-25 |
|
||||
| 21 | [🚀 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 |
|
||||
| 22 | [🚀 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 |
|
||||
| 23 | [🚀 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 |
|
||||
| 24 | [Review of Claude Haiku 4.5](https://openwebui.com/posts/review_of_claude_haiku_45_41b0db39) | review |  |  |  |  |  | 2026-01-14 |
|
||||
| 25 | [ 🛠️ Debug Open WebUI Plugins in Your Browser](https://openwebui.com/posts/debug_open_webui_plugins_in_your_browser_81bf7960) | post |  |  |  |  |  | 2026-01-10 |
|
||||
63
docs/community-stats.zh.md
Normal file
63
docs/community-stats.zh.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# 📊 OpenWebUI 社区统计报告
|
||||
|
||||
> 
|
||||
|
||||
### 📈 总下载量累计趋势
|
||||

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

|
||||
|
||||
|
||||
## 📈 总览
|
||||
|
||||
| 指标 | 数值 |
|
||||
|------|------|
|
||||
| 📝 发布数量 |  |
|
||||
| ⬇️ 总下载量 |  |
|
||||
| 👁️ 总浏览量 |  |
|
||||
| 👍 总点赞数 |  |
|
||||
| 💾 总收藏数 |  |
|
||||
| ⭐ 作者总积分 |  |
|
||||
| 👥 粉丝数量 |  |
|
||||
|
||||
## 📂 按类型分类
|
||||
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
|
||||
## 📋 发布列表
|
||||
|
||||
| 排名 | 标题 | 类型 | 版本 | 下载 | 浏览 | 点赞 | 收藏 | 更新日期 |
|
||||
|:---:|------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 1 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | action |  |  |  |  |  | 2026-02-27 |
|
||||
| 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-27 |
|
||||
| 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-27 |
|
||||
| 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 | [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee) | filter |  |  |  |  |  | 2026-02-25 |
|
||||
| 15 | [思维导图](https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b) | action |  |  |  |  |  | 2026-02-13 |
|
||||
| 16 | [异步上下文压缩](https://openwebui.com/posts/异步上下文压缩_5c0617cb) | action |  |  |  |  |  | 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.9.0: Copilot SDK Skills Core Capabilities & Extended Delivery](https://openwebui.com/posts/github_copilot_sdk_pipe_v090_copilot_sdk_skills_co_99a42452) | post |  |  |  |  |  | 2026-02-27 |
|
||||
| 20 | [🚀 GitHub Copilot SDK Pipe v0.8.0: Conditional Tool Filtering & Publish Reliability 🎛️](https://openwebui.com/posts/github_copilot_sdk_pipe_v080_conditional_tool_filt_a5a3322d) | post |  |  |  |  |  | 2026-02-25 |
|
||||
| 21 | [🚀 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 |
|
||||
| 22 | [🚀 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 |
|
||||
| 23 | [🚀 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 |
|
||||
| 24 | [Review of Claude Haiku 4.5](https://openwebui.com/posts/review_of_claude_haiku_45_41b0db39) | review |  |  |  |  |  | 2026-01-14 |
|
||||
| 25 | [ 🛠️ 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)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user