Compare commits
557 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fed561471 | ||
|
|
07dfce9b28 | ||
|
|
da4b14fbf8 | ||
|
|
3993f520c7 | ||
|
|
48ab2d6338 | ||
|
|
93a04cc174 | ||
|
|
eb897daef2 | ||
|
|
5116bf7d25 | ||
|
|
6531af6c3a | ||
|
|
d3aca33ba2 | ||
|
|
5fc9041d46 | ||
|
|
3cf38930eb | ||
|
|
37fe67dc81 | ||
|
|
78906d8557 | ||
|
|
b142c04b95 | ||
|
|
a4c1a04900 | ||
|
|
a178de37de | ||
|
|
251f2225ee | ||
|
|
ce729dff1f | ||
|
|
206eb81bb4 | ||
|
|
90406fce9e | ||
|
|
89036f8aec | ||
|
|
c26be2e885 | ||
|
|
2f614ac40d | ||
|
|
e190ee92e8 | ||
|
|
b6e156db26 | ||
|
|
7fe20ef041 | ||
|
|
1964640d5c | ||
|
|
09879004be | ||
|
|
236e7d3c3f | ||
|
|
8d3ad943be | ||
|
|
bb824ab35e | ||
|
|
37a6293f5e | ||
|
|
761ae74b0a | ||
|
|
eb5a252654 | ||
|
|
a19c8b9106 | ||
|
|
9a31407a01 | ||
|
|
c7bfd773e3 | ||
|
|
25dc45c398 | ||
|
|
6f37d7b460 | ||
|
|
94a241bbb1 | ||
|
|
eb28bfb27b | ||
|
|
ffd4e194bf | ||
|
|
952da2a06e | ||
|
|
b7934ecc27 | ||
|
|
8862e353aa | ||
|
|
bf1f958dcd | ||
|
|
83d569df83 | ||
|
|
58d94e1018 | ||
|
|
ae95a8908e | ||
|
|
58153306c5 | ||
|
|
2f28f57d78 | ||
|
|
911c5c00ba | ||
|
|
bd137d7595 | ||
|
|
40200c7c71 | ||
|
|
502d8d8c73 | ||
|
|
5f5d439f55 | ||
|
|
b472127d3b | ||
|
|
53a082278b | ||
|
|
1704cf31de | ||
|
|
950dffaedf | ||
|
|
f764d81cdd | ||
|
|
5d0c8fa462 | ||
|
|
0a689b0ab8 | ||
|
|
a77e3880f4 | ||
|
|
fb8635a951 | ||
|
|
dfda5285bd | ||
|
|
7a56680935 | ||
|
|
f65a72821b | ||
|
|
36b234c4fd | ||
|
|
aebe789e9f | ||
|
|
1c4d2e92cf | ||
|
|
118e333600 | ||
|
|
97c7984dd1 | ||
|
|
aa7fb6a65c | ||
|
|
efd6448043 | ||
|
|
cd2fd77df1 | ||
|
|
8be1834cfb | ||
|
|
bf2310cc29 | ||
|
|
c06a9fb52b | ||
|
|
ffdef41bf2 | ||
|
|
248be38939 | ||
|
|
2b993b926a | ||
|
|
b367082d38 | ||
|
|
c5f50b65c9 | ||
|
|
815770467a | ||
|
|
67724f2fa6 | ||
|
|
e5e720e87e | ||
|
|
35718b1f26 | ||
|
|
c16e2b8dd6 | ||
|
|
3adb97b396 | ||
|
|
6fc6c99477 | ||
|
|
6fd83e1e75 | ||
|
|
ea35ad2144 | ||
|
|
1ffe1be562 | ||
|
|
ba965320d5 | ||
|
|
67e10d6f2c | ||
|
|
b7d18e38d1 | ||
|
|
5e0b147048 | ||
|
|
6027a966d2 | ||
|
|
8151350d9f | ||
|
|
323953462b | ||
|
|
f5fad6083a | ||
|
|
e49a831cc4 | ||
|
|
fdcf53ea38 | ||
|
|
b7b20a353f | ||
|
|
f362ba2589 | ||
|
|
e0b6860706 | ||
|
|
9fefaa8e18 | ||
|
|
6d358ef3e6 | ||
|
|
82e7776a77 | ||
|
|
75c8c42530 | ||
|
|
62ee28b130 | ||
|
|
c46a37541c | ||
|
|
47af8d1c3d | ||
|
|
7a76f54148 | ||
|
|
58cbf10c85 | ||
|
|
7fe2017ab6 | ||
|
|
51b98df4cb | ||
|
|
ba73762285 | ||
|
|
a993eba7f0 | ||
|
|
2330186a09 | ||
|
|
8a25aeabc4 | ||
|
|
a510f96b83 | ||
|
|
505aff3dbf | ||
|
|
f9d83c481f | ||
|
|
d346d38677 | ||
|
|
f71ce25c46 | ||
|
|
ecce182a20 | ||
|
|
509ca92f0a | ||
|
|
44e360b61b | ||
|
|
dc1599ba3c | ||
|
|
53a4d9db05 | ||
|
|
60a9dfb55f | ||
|
|
f546068354 | ||
|
|
ed42bb6ce8 | ||
|
|
246283ee1c | ||
|
|
98a5796592 | ||
|
|
877aab858b | ||
|
|
077ee9504f | ||
|
|
5a96e167ee | ||
|
|
358c4716f9 | ||
|
|
f3715731c4 | ||
|
|
726de0396b | ||
|
|
b4d46ff34d | ||
|
|
6c72c20317 | ||
|
|
eb68b35ddf | ||
|
|
b2e2f60e0d | ||
|
|
eb768d9c04 | ||
|
|
cd77d81135 | ||
|
|
aef42cef9d | ||
|
|
23642af6e2 | ||
|
|
46f20c7dc3 | ||
|
|
8e9816d648 | ||
|
|
6e1ef89d65 | ||
|
|
9bdd5f522d | ||
|
|
4c54e1821b | ||
|
|
7e6272ca1b | ||
|
|
70306295eb | ||
|
|
ac482eb9f9 | ||
|
|
002d53108f | ||
|
|
792e5bf7e4 | ||
|
|
cf201267af | ||
|
|
ea2b8b468c | ||
|
|
0be7a3e355 | ||
|
|
5f784e1def | ||
|
|
77dafe4337 | ||
|
|
22a5dea963 | ||
|
|
54542ba11d | ||
|
|
f10e8775fb | ||
|
|
a04b661864 | ||
|
|
569772148f | ||
|
|
63d1657f9b | ||
|
|
d00ac152b5 | ||
|
|
e979a55f19 | ||
|
|
026d87c61e | ||
|
|
2a45fe520b | ||
|
|
8635de866f | ||
|
|
982e36e79d | ||
|
|
dda7847f77 | ||
|
|
93fc9ee65d | ||
|
|
a4e2c6510f | ||
|
|
c411ca4bd4 | ||
|
|
8af10a7c9a | ||
|
|
65cab349b0 | ||
|
|
ca814dcaf4 | ||
|
|
1367ba9d32 | ||
|
|
62489ef12f | ||
|
|
3d2043c16f | ||
|
|
95066262b7 | ||
|
|
deb9be4160 | ||
|
|
f382b2194d | ||
|
|
a4744dd78f | ||
|
|
a9d258d992 | ||
|
|
f56a339ad1 | ||
|
|
68eca25df4 | ||
|
|
9eed321471 | ||
|
|
426176db47 | ||
|
|
cfb31afbd9 | ||
|
|
5be57da407 | ||
|
|
057c3411b9 | ||
|
|
83d755ad0e | ||
|
|
ec9852fc63 | ||
|
|
4e6f8aefe8 | ||
|
|
11352b754a | ||
|
|
965ad34283 | ||
|
|
986206b691 | ||
|
|
6787f19d78 | ||
|
|
64c35eaa3a | ||
|
|
41ada6ecda | ||
|
|
ae1f7a888e | ||
|
|
9aace871ff | ||
|
|
39739f9305 | ||
|
|
ce757d918b | ||
|
|
d592d4e99a | ||
|
|
11ce10cd80 | ||
|
|
6fb312ccfd | ||
|
|
3166376173 | ||
|
|
a02a528737 | ||
|
|
dd4ca27dc7 | ||
|
|
f2d37c30a5 | ||
|
|
1d236f87ae | ||
|
|
3b515c3c2d | ||
|
|
e95f83ec8e | ||
|
|
03793c66da | ||
|
|
84daf85393 | ||
|
|
6c62d80a4c | ||
|
|
ff2043c0fb | ||
|
|
ee9afa310a | ||
|
|
2b93ae2d00 | ||
|
|
00c93a63cd | ||
|
|
61447c60ac | ||
|
|
df2fda6176 | ||
|
|
bc2504832f | ||
|
|
33ffd9d7dd | ||
|
|
80578a08c8 | ||
|
|
2463e11cb9 | ||
|
|
4cbe4ebdc3 | ||
|
|
bb36e637e0 | ||
|
|
6f9e929298 | ||
|
|
bf1592d2c6 | ||
|
|
c6259fca78 | ||
|
|
cf3eb3b7b5 | ||
|
|
7c52cec0ea | ||
|
|
7c159d8aba | ||
|
|
07f8e18c10 | ||
|
|
e4aeee7be3 | ||
|
|
8036ed6143 | ||
|
|
85e6a0f38d | ||
|
|
dab70378bb | ||
|
|
0a0febd2e6 | ||
|
|
391332c8dd | ||
|
|
89e7c1abca | ||
|
|
fc3c360985 | ||
|
|
006ba3b877 | ||
|
|
5a534aa630 | ||
|
|
98e3c0a41f | ||
|
|
99e47849f5 | ||
|
|
ca4cd8af9d | ||
|
|
36a0ea7e43 | ||
|
|
71dd7f3e6c | ||
|
|
6ac7119edf | ||
|
|
daf1148bb1 | ||
|
|
82b05b3d94 | ||
|
|
9ab5cef516 | ||
|
|
1ac3edccab | ||
|
|
d3959a918c | ||
|
|
623018f408 | ||
|
|
6b6da76ac1 | ||
|
|
3c9f12bb94 | ||
|
|
d0c3d60751 | ||
|
|
d057d20c17 | ||
|
|
9a831b5b8b | ||
|
|
7957f96d2c | ||
|
|
fb1392565d | ||
|
|
b8a75921ed | ||
|
|
7dd8e7bea1 | ||
|
|
7f9899f7f3 | ||
|
|
eef6d7518e | ||
|
|
6fd49b0955 | ||
|
|
e19ac56fe5 | ||
|
|
2378615887 | ||
|
|
ba9d9c3d5f | ||
|
|
d9450bd7ee | ||
|
|
69bb1f3fa5 | ||
|
|
7a38a81e12 | ||
|
|
55d0ed9de6 | ||
|
|
941549ff04 | ||
|
|
1f170e1cd2 | ||
|
|
b0d0a76a8e | ||
|
|
16018a7e0b | ||
|
|
707be3362c | ||
|
|
942aeeac2e | ||
|
|
1111f07fa7 | ||
|
|
2f1506bf07 | ||
|
|
48fbc74168 | ||
|
|
92b592dd98 | ||
|
|
8cafebe26c | ||
|
|
d69554575d | ||
|
|
8817e4d2db | ||
|
|
c9ee6fabe4 | ||
|
|
24319fe860 | ||
|
|
87c5cb6bca | ||
|
|
746b9af2de | ||
|
|
176c5a4d79 | ||
|
|
0cde9a10a8 | ||
|
|
59ddf09b94 | ||
|
|
cdee91bec1 | ||
|
|
d36a7cb177 | ||
|
|
2fc31a706d | ||
|
|
2fce76202a | ||
|
|
7fe39c2515 | ||
|
|
e818cb037f | ||
|
|
403e1f2d92 | ||
|
|
d351a56e03 | ||
|
|
516618b0cd | ||
|
|
7e99f905bc | ||
|
|
a287ace126 | ||
|
|
4f0bd677f2 | ||
|
|
741381ecb0 | ||
|
|
f05b12975c | ||
|
|
85e94966ac | ||
|
|
dc1c1d1355 | ||
|
|
69f32a0861 | ||
|
|
c99d6998ea | ||
|
|
116e9c8d85 | ||
|
|
52920726d4 | ||
|
|
02caa57304 | ||
|
|
6014a56e54 | ||
|
|
b8f08eb33e | ||
|
|
944e876aaa | ||
|
|
ee2c259c3d | ||
|
|
1c8db69a5a | ||
|
|
5128bbcce4 | ||
|
|
51a5d450b7 | ||
|
|
98444fd04b | ||
|
|
e45c1eb1e0 | ||
|
|
bd9d83e630 | ||
|
|
b66952ad98 | ||
|
|
242b21263a | ||
|
|
2843178ede | ||
|
|
bb312441c6 | ||
|
|
d07e5b8501 | ||
|
|
246ee973ec | ||
|
|
a62a9c4067 | ||
|
|
7408db9cf6 | ||
|
|
5d4dd4a18c | ||
|
|
5bf95bd846 | ||
|
|
a79429fdcd | ||
|
|
021add2af4 | ||
|
|
371e0e36c6 | ||
|
|
e7d3a8e2e1 | ||
|
|
32a8d68c6c | ||
|
|
06ab718e6e | ||
|
|
1d74095739 | ||
|
|
ca99837dab | ||
|
|
d31bdf0ee0 | ||
|
|
d3e7923040 | ||
|
|
5f66f4523c | ||
|
|
4ec02c654b | ||
|
|
9a0c92629b | ||
|
|
651eb1bf6b | ||
|
|
4e0c876154 | ||
|
|
3e5118c4f7 | ||
|
|
bdd518fd3e | ||
|
|
340de071a9 | ||
|
|
1226c3efb7 | ||
|
|
39f9080eb2 | ||
|
|
3e4b165ed9 | ||
|
|
451f234f68 | ||
|
|
1ab45651e0 | ||
|
|
16f2ad7615 | ||
|
|
4ba4a99935 | ||
|
|
6f4471d2a0 | ||
|
|
250399a1aa | ||
|
|
1c8ce369b6 | ||
|
|
875f78b42c | ||
|
|
1e262a2198 | ||
|
|
25067a14a6 | ||
|
|
591cc21ff4 | ||
|
|
5a21eb9bc1 | ||
|
|
cdf4b9f324 | ||
|
|
e3c9b8179e | ||
|
|
9b683884cc | ||
|
|
4dc541e0a6 | ||
|
|
ef2de489be | ||
|
|
cb3b9efc6e | ||
|
|
a745993829 | ||
|
|
a837552b56 | ||
|
|
f52f514f5f | ||
|
|
fac53923dd | ||
|
|
b200731d17 | ||
|
|
de6ac0f589 | ||
|
|
18e0212d27 | ||
|
|
f20a5fe9a6 | ||
|
|
d351084688 | ||
|
|
d807f9d097 | ||
|
|
3ef6d3fe63 | ||
|
|
e2fccd391e | ||
|
|
aeef1fca0c | ||
|
|
b0402434e2 | ||
|
|
014fb504a4 | ||
|
|
4d043e0e46 | ||
|
|
d1ee3913eb | ||
|
|
a34a2b622c | ||
|
|
2d74fa8e10 | ||
|
|
c879905307 | ||
|
|
487ef670cd | ||
|
|
0d6897e180 | ||
|
|
3c8f38799c | ||
|
|
91b02bbfd9 | ||
|
|
17a42ac0cc | ||
|
|
e384893ae0 | ||
|
|
00a99261ae | ||
|
|
91decc3683 | ||
|
|
aa74625f96 | ||
|
|
9199e3e57d | ||
|
|
89234c197c | ||
|
|
a409db9578 | ||
|
|
11f42ad9ed | ||
|
|
90456301d2 | ||
|
|
b0d414ac12 | ||
|
|
89a67ca9c0 | ||
|
|
39869bc4ea | ||
|
|
f109f1cf60 | ||
|
|
c971adaabd | ||
|
|
ea100d84bf | ||
|
|
78762498eb | ||
|
|
cd9acab938 | ||
|
|
56b3ddc147 | ||
|
|
5969f5e0c5 | ||
|
|
ca8e940c9b | ||
|
|
75073a64fb | ||
|
|
5b9185159d | ||
|
|
08ae4073bd | ||
|
|
606105d633 | ||
|
|
3b8e5d2738 | ||
|
|
46eb96c72e | ||
|
|
0540c2e46a | ||
|
|
d13b823065 | ||
|
|
71f58b791f | ||
|
|
4b1cc6878c | ||
|
|
c6a5f16336 | ||
|
|
7ed3c91ac6 | ||
|
|
1f801d1464 | ||
|
|
d0e65431d0 | ||
|
|
a21b2ccdd0 | ||
|
|
8767c576be | ||
|
|
fb08f61eb5 | ||
|
|
ce68791c3c | ||
|
|
3294be5e7f | ||
|
|
ec86847280 | ||
|
|
dd2d93c953 | ||
|
|
e60c36b423 | ||
|
|
1f112f7715 | ||
|
|
adbaa8b37b | ||
|
|
29c95d24ae | ||
|
|
e0b1a78344 | ||
|
|
2774940851 | ||
|
|
c2c73ed23c | ||
|
|
9682c82713 | ||
|
|
e8d4933dc4 | ||
|
|
0b6020a9cd | ||
|
|
894beee266 | ||
|
|
405e453ed3 | ||
|
|
79d289e25b | ||
|
|
51054f5829 | ||
|
|
317fba1855 | ||
|
|
f61e467d04 | ||
|
|
27de1cad47 | ||
|
|
3ea2cf1dcb | ||
|
|
4397a0ad6b | ||
|
|
4f51839026 | ||
|
|
3c0fa30aaf | ||
|
|
02abe42afe | ||
|
|
088a90de10 | ||
|
|
a98c56f968 | ||
|
|
1e5714da1b | ||
|
|
867d69659f | ||
|
|
d44203bff1 | ||
|
|
629a147741 | ||
|
|
9e951fbc15 | ||
|
|
a540ee944a | ||
|
|
b064e704f3 | ||
|
|
7e54421190 | ||
|
|
647f701692 | ||
|
|
0db413ab52 | ||
|
|
426eceac22 | ||
|
|
1ee527ceb8 | ||
|
|
03f1ab1a2f | ||
|
|
faf722fa15 | ||
|
|
36dad6df33 | ||
|
|
6ff5db7b41 | ||
|
|
56a0b48b97 | ||
|
|
ff24042df5 | ||
|
|
c31d247f07 | ||
|
|
e903eb5b94 | ||
|
|
c605964fa8 | ||
|
|
1fe5cd751a | ||
|
|
488e2f476e | ||
|
|
915b104b8a | ||
|
|
aaa350a13e | ||
|
|
6a2b34cb92 | ||
|
|
7f26b31f53 | ||
|
|
2a597964a2 | ||
|
|
c1d3a46dc7 | ||
|
|
0c55beb72d | ||
|
|
9b1c0e1a3c | ||
|
|
a7988c164e | ||
|
|
99e5fbd0f5 | ||
|
|
5e4c4dd79b | ||
|
|
70584783a5 | ||
|
|
705ac1c27e | ||
|
|
52d00d0562 | ||
|
|
9a145f223f | ||
|
|
b7cd4dec89 | ||
|
|
33154a9c19 | ||
|
|
e1c7503611 | ||
|
|
d04c298132 | ||
|
|
eceda01c19 | ||
|
|
ea1681e1eb | ||
|
|
f6c4b4c96d | ||
|
|
22cc9c85be | ||
|
|
43f8d6008f | ||
|
|
29c5554f9e | ||
|
|
9b18a46456 | ||
|
|
d5923bc64f | ||
|
|
f19c2d2ca1 | ||
|
|
84d91f3f76 | ||
|
|
7811f7482b | ||
|
|
9c8ca7dd25 | ||
|
|
1409916bd0 | ||
|
|
fc7edcb54f | ||
|
|
87d35042de | ||
|
|
77dc961a07 | ||
|
|
9a45fb64c2 | ||
|
|
881c36542c | ||
|
|
f88c6031f5 | ||
|
|
8a02b3b04a | ||
|
|
d460305871 | ||
|
|
144bed5a77 | ||
|
|
96fc917bad | ||
|
|
794a3698ad | ||
|
|
fbbc32361b | ||
|
|
dc329041f3 | ||
|
|
5feb2e19bf | ||
|
|
ec22cd8320 | ||
|
|
8c7efcbd1a | ||
|
|
afc5947bfb | ||
|
|
40189a6899 | ||
|
|
b73829a25c | ||
|
|
a7c5d3cc05 | ||
|
|
cc36a13f17 | ||
|
|
943abbe0fb | ||
|
|
b13c3c4da5 | ||
|
|
c12aa7fdf7 | ||
|
|
85e11abc0a |
@@ -1,8 +0,0 @@
|
||||
AXIOS_PROXY_HOST=127.0.0.1
|
||||
AXIOS_PROXY_PORT=33210
|
||||
MONGODB_URI=
|
||||
MY_MAIL=
|
||||
MAILE_CODE=
|
||||
TOKEN_KEY=
|
||||
OPENAIKEY=
|
||||
REDIS_URL=
|
||||
BIN
.github/imgs/intro1.png
vendored
Normal file
|
After Width: | Height: | Size: 548 KiB |
BIN
.github/imgs/intro2.png
vendored
Normal file
|
After Width: | Height: | Size: 437 KiB |
BIN
.github/imgs/intro3.png
vendored
Normal file
|
After Width: | Height: | Size: 574 KiB |
BIN
.github/imgs/intro4.png
vendored
Normal file
|
After Width: | Height: | Size: 270 KiB |
79
.github/workflows/fastgpt-image.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
name: Build fastgpt images and copy image to docker hub
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'client/**'
|
||||
branches:
|
||||
- 'main'
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
jobs:
|
||||
build-fastgpt-images:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt update && sudo apt install -y nodejs npm
|
||||
- name: Set up QEMU (optional)
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
driver-opts: network=host
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GH_PAT }}
|
||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
||||
run: |
|
||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
|
||||
else
|
||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Build and publish image for main branch or tag push event
|
||||
env:
|
||||
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
||||
run: |
|
||||
cd client && \
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--label "org.opencontainers.image.source= https://github.com/ ${{ github.repository_owner }}/FastGPT" \
|
||||
--label "org.opencontainers.image.description=fastgpt image" \
|
||||
--label "org.opencontainers.image.licenses=MIT" \
|
||||
--push \
|
||||
-t ${DOCKER_REPO_TAGGED} \
|
||||
-f Dockerfile \
|
||||
.
|
||||
push-to-docker-hub:
|
||||
needs: build-fastgpt-images
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_NAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
||||
run: |
|
||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
||||
echo "IMAGE_TAG=latest" >> $GITHUB_ENV
|
||||
else
|
||||
echo "IMAGE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Pull image from GitHub Container Registry
|
||||
run: docker pull ghcr.io/${{ github.repository_owner }}/fastgpt:${{env.IMAGE_TAG}}
|
||||
- name: Tag image with Docker Hub repository name and version tag
|
||||
run: docker tag ghcr.io/${{ github.repository_owner }}/fastgpt:${{env.IMAGE_TAG}} ${{ secrets.DOCKER_IMAGE_NAME }}:${{env.IMAGE_TAG}}
|
||||
- name: Push image to Docker Hub
|
||||
run: docker push ${{ secrets.DOCKER_IMAGE_NAME }}:${{env.IMAGE_TAG}}
|
||||
25
.gitignore
vendored
@@ -1,19 +1,11 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
node_modules/
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
.next/
|
||||
out/
|
||||
# production
|
||||
/build
|
||||
build/
|
||||
.astro/
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
@@ -34,6 +26,7 @@ yarn-error.log*
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
/public/trainData/
|
||||
/.vscode/
|
||||
platform.json
|
||||
platform.json
|
||||
testApi/
|
||||
local/
|
||||
dist/
|
||||
15
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.mouseWheelZoom": true,
|
||||
"typescript.tsdk": "client/node_modules/typescript/lib",
|
||||
"prettier.prettierPath": "./node_modules/prettier",
|
||||
"i18n-ally.localesPaths": [
|
||||
"client/public/locales"
|
||||
],
|
||||
"i18n-ally.enabledParsers": ["json"],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.keepFulfilled": true,
|
||||
"i18n-ally.sourceLanguage": "zh", // 根据此语言文件翻译其他语言文件的变量和内容
|
||||
"i18n-ally.displayLanguage": "en", // 显示语言
|
||||
}
|
||||
201
LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
47
Makefile
@@ -1,47 +0,0 @@
|
||||
SERVICE_NAME=fast-gpt
|
||||
# Image URL to use all building/pushing image targets
|
||||
IMG ?= $(SERVICE_NAME):latest
|
||||
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
||||
##@ General
|
||||
|
||||
# The help target prints out all targets with their descriptions organized
|
||||
# beneath their categories. The categories are represented by '##@' and the
|
||||
# target descriptions by '##'. The awk commands is responsible for reading the
|
||||
# entire set of makefiles included in this invocation, looking for lines of the
|
||||
# file as xyz: ## something, and then pretty-format the target and help. Then,
|
||||
# if there's a line with ##@ something, that gets pretty-printed as a category.
|
||||
# More info on the usage of ANSI control characters for terminal formatting:
|
||||
# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
|
||||
# More info on the awk command:
|
||||
# http://linuxcommand.org/lc3_adv_awk.php
|
||||
|
||||
.PHONY: help
|
||||
help: ## Display this help.
|
||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
|
||||
|
||||
##@ Build
|
||||
|
||||
.PHONY: build
|
||||
build: ## Build desktop-frontend binary.
|
||||
pnpm run build
|
||||
|
||||
.PHONY: run
|
||||
run: ## Run a dev service from host.
|
||||
pnpm run start
|
||||
|
||||
.PHONY: docker-build
|
||||
docker-build: ## Build docker image with the desktop-frontend.
|
||||
docker build -t c121914yu/fast-gpt:latest .
|
||||
|
||||
##@ Deployment
|
||||
|
||||
.PHONY: docker-run
|
||||
docker-run: ## Push docker image.
|
||||
docker run -d -p 8008:3000 --name fast-gpt -v /web_project/yjl/fast-gpt/logs:/app/.next/logs c121914yu/fast-gpt:latest
|
||||
|
||||
#TODO: add support of docker push
|
||||
|
||||
#TODO: add support of sealos apply
|
||||
133
README.md
@@ -1,112 +1,51 @@
|
||||
# Fast GPT
|
||||
# FastGPT
|
||||
|
||||
Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接口,包括 GPT3 及其微调方法,以及最新的 gpt3.5 接口。
|
||||
FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景!
|
||||
|
||||
## 初始化
|
||||
复制 .env.template 成 .env.local ,填写核心参数
|
||||
## 🛸 在线体验
|
||||
|
||||
```
|
||||
AXIOS_PROXY_HOST=axios代理地址,目前 openai 接口都需要走代理,本机的话就填 127.0.0.1
|
||||
AXIOS_PROXY_PORT=代理端口
|
||||
MONGODB_URI=mongo数据库地址(例如:mongodb://username:password@ip:27017/?authSource=admin&readPreference=primary&appname=MongoDB%20Compass&directConnection=true&ssl=false)
|
||||
MY_MAIL=发送验证码邮箱
|
||||
MAILE_CODE=邮箱秘钥(代理里设置的是QQ邮箱,不知道怎么找这个 code 的,可以百度搜"nodemailer发送邮件")
|
||||
TOKEN_KEY=随便填一个,用于生成和校验 token
|
||||
```
|
||||
🎉 [fastgpt.run](https://fastgpt.run/)(服务器在新加坡,部分地区可能无法直连)
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
| | |
|
||||
| ---------------------------------- | ---------------------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
## 部署
|
||||
## 👨💻 开发
|
||||
|
||||
### docker 模式
|
||||
请准备好 docker, mongo,代理, 和nginx。 镜像走本机的代理,所以用 network=host,port 改成代理的端口,clash 一般都是 7890。
|
||||
项目技术栈: NextJs + TS + ChakraUI + Mongo + Postgres(Vector 插件)
|
||||
|
||||
#### docker 打包
|
||||
```bash
|
||||
docker build -t imageName:tag .
|
||||
docker push imageName:tag
|
||||
```
|
||||
- [快开始本地开发](https://doc.fastgpt.run/docs/develop/dev)
|
||||
- [部署 FastGPT](https://doc.fastgpt.run/docs/category/deploy)
|
||||
|
||||
#### 服务器拉取镜像和运行
|
||||
```bash
|
||||
# 服务器拉取部署, imageName 替换成镜像名
|
||||
docker pull imageName:tag
|
||||
docker stop fast-gpt || true
|
||||
docker rm fast-gpt || true
|
||||
docker run -d --network=host --name fast-gpt \
|
||||
-e AXIOS_PROXY_HOST=127.0.0.1 \
|
||||
-e AXIOS_PROXY_PORT=7890 \
|
||||
-e MY_MAIL=your email\
|
||||
-e MAILE_CODE=your email code \
|
||||
-e TOKEN_KEY=任意一个内容 \
|
||||
-e MONGODB_URI="mongodb://user:password@127.0.0.0:27017/?authSource=admin&readPreference=primary&appname=MongoDB%20Compass&ssl=false" \
|
||||
imageName:tag
|
||||
```
|
||||
## :point_right: RoadMap
|
||||
|
||||
#### 软件教程:docker 安装
|
||||
```bash
|
||||
# 安装docker
|
||||
curl -sSL https://get.daocloud.io/docker | sh
|
||||
sudo systemctl start docker
|
||||
```
|
||||
- [FastGpt RoadMap](https://kjqvjse66l.feishu.cn/docx/RVUxdqE2WolDYyxEKATcM0XXnte)
|
||||
|
||||
#### 软件教程:mongo 安装
|
||||
```bash
|
||||
docker pull mongo:6.0.4
|
||||
docker stop mongo
|
||||
docker rm mongo
|
||||
docker run -d --name mongo \
|
||||
-e MONGO_INITDB_ROOT_USERNAME= \
|
||||
-e MONGO_INITDB_ROOT_PASSWORD= \
|
||||
-v /root/service/mongo:/data/db \
|
||||
mongo:6.0.4
|
||||
## 🏘️ 社区交流群
|
||||
|
||||
# 检查 mongo 运行情况, 有成功的 logs 代表访问成功
|
||||
docker logs mongo
|
||||
```
|
||||
#### 软件教程: clash 代理
|
||||
```bash
|
||||
# 下载包
|
||||
curl https://glados.rocks/tools/clash-linux.zip -o clash.zip
|
||||
# 解压
|
||||
unzip clash.zip
|
||||
# 下载终端配置⽂件(改成自己配置文件路径)
|
||||
curl https://update.glados-config.com/clash/98980/8f30944/70870/glados-terminal.yaml > config.yaml
|
||||
# 赋予运行权限
|
||||
chmod +x ./clash-linux-amd64-v1.10.0
|
||||
# 记得配置端口变量:
|
||||
export ALL_PROXY=socks5://127.0.0.1:7891
|
||||
export http_proxy=http://127.0.0.1:7890
|
||||
export https_proxy=http://127.0.0.1:7890
|
||||
export HTTP_PROXY=http://127.0.0.1:7890
|
||||
export HTTPS_PROXY=http://127.0.0.1:7890
|
||||
| 交流群 | 小助手 |
|
||||
| ------------------------------------------------- | ---------------------------------------------- |
|
||||
|  |  |
|
||||
|
||||
# 运行脚本: 删除clash - 到 clash 目录 - 删除缓存 - 执行运行
|
||||
# 会生成一个 nohup.out 文件,可以看到 clash 的 logs
|
||||
OLD_PROCESS=$(pgrep clash)
|
||||
if [ ! -z "$OLD_PROCESS" ]; then
|
||||
echo "Killing old process: $OLD_PROCESS"
|
||||
kill $OLD_PROCESS
|
||||
fi
|
||||
sleep 2
|
||||
cd **/clash
|
||||
rm -f ./nohup.out || true
|
||||
rm -f ./cache.db || true
|
||||
nohup ./clash-linux-amd64-v1.10.0 -d ./ &
|
||||
echo "Restart clash"
|
||||
```
|
||||
## Powered by
|
||||
|
||||
#### 软件教程:Nginx
|
||||
...没写,这个百度吧。
|
||||
- [TuShan: 5 分钟搭建后台管理系统](https://github.com/msgbyte/tushan)
|
||||
- [Laf: 3 分钟快速接入三方应用](https://github.com/labring/laf)
|
||||
- [Sealos: 快速部署集群应用](https://github.com/labring/sealos)
|
||||
- [One API: 令牌管理 & 二次分发,支持 Azure](https://github.com/songquanpeng/one-api)
|
||||
|
||||
#### redis
|
||||
## 👀 其他
|
||||
|
||||
```bash
|
||||
# 索引
|
||||
# FT.CREATE idx:model:data ON JSON PREFIX 1 model:data: SCHEMA $.modelId AS modelId TAG $.dataId AS dataId TAG $.vector AS vector VECTOR FLAT 6 DIM 1536 DISTANCE_METRIC COSINE TYPE FLOAT32
|
||||
# FT.CREATE idx:model:data:hash ON HASH PREFIX 1 model:data: SCHEMA modelId TAG dataId TAG vector VECTOR FLAT 6 DIM 1536 DISTANCE_METRIC COSINE TYPE FLOAT32
|
||||
FT.CREATE idx:model:data ON HASH PREFIX 1 model:data: SCHEMA modelId TAG userId TAG q TEXT text TEXT vector VECTOR FLAT 6 DIM 1536 DISTANCE_METRIC COSINE TYPE FLOAT32
|
||||
```
|
||||
- [FastGpt 常见问题](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||
- [docker 部署教程视频](https://www.bilibili.com/video/BV1jo4y147fT/)
|
||||
- [公众号接入视频教程](https://www.bilibili.com/video/BV1xh4y1t7fy/)
|
||||
- [FastGpt 知识库演示](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
|
||||
|
||||
## 第三方生态
|
||||
|
||||
- [luolinAI: 企微机器人,开箱即用](https://github.com/luolin-ai/FastGPT-Enterprise-WeChatbot)
|
||||
|
||||
## 🌟 Star History
|
||||
|
||||
[](https://star-history.com/#labring/FastGPT&Date)
|
||||
|
||||
@@ -8,3 +8,4 @@ README.md
|
||||
|
||||
.yalc/
|
||||
yalc.lock
|
||||
testApi/
|
||||
26
client/.env.template
Normal file
@@ -0,0 +1,26 @@
|
||||
# 默认用户密码,用户名为 root,每次重启时会自动更新。
|
||||
DEFAULT_ROOT_PSW=123456
|
||||
# 代理
|
||||
# AXIOS_PROXY_HOST=127.0.0.1
|
||||
# AXIOS_PROXY_PORT=7890
|
||||
# 数据库最大连接数
|
||||
DB_MAX_LINK=5
|
||||
# token
|
||||
TOKEN_KEY=dfdasfdas
|
||||
# root key, 最高权限
|
||||
ROOT_KEY=fdafasd
|
||||
# openai 基本地址,可用作中转。
|
||||
OPENAI_BASE_URL=https://api.openai.com/v1
|
||||
# oneapi 地址,可以使用 oneapi 来实现多模型接入
|
||||
ONEAPI_URL=https://xxxx.cloud.sealos.io/openai/v1
|
||||
# 通用key。可以是 openai 的也可以是 oneapi 的。
|
||||
# 此处逻辑:优先走 ONEAPI_URL,如果填写了 ONEAPI_URL,key 也需要是 ONEAPI 的 key
|
||||
CHAT_API_KEY=sk-xxxx
|
||||
# db
|
||||
MONGODB_URI=mongodb://username:password@0.0.0.0:27017/?authSource=admin
|
||||
MONGODB_NAME=fastgpt
|
||||
PG_HOST=0.0.0.0
|
||||
PG_PORT=8100
|
||||
PG_USER=root
|
||||
PG_PASSWORD=psw
|
||||
PG_DB_NAME=dbname
|
||||
32
client/.gitignore
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# dependencies
|
||||
node_modules/
|
||||
# next.js
|
||||
.next/
|
||||
out/
|
||||
# production
|
||||
build/
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
platform.json
|
||||
testApi/
|
||||
local/
|
||||
.husky/
|
||||
data/*.local.*
|
||||
@@ -5,15 +5,18 @@ RUN apk add --no-cache libc6-compat && npm install -g pnpm
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies based on the preferred package manager
|
||||
COPY package.json pnpm-lock.yaml* ./
|
||||
COPY package.json ./
|
||||
COPY pnpm-lock.yaml* ./
|
||||
RUN \
|
||||
[ -f pnpm-lock.yaml ] && pnpm install || \
|
||||
[ -f pnpm-lock.yaml ] && pnpm fetch || \
|
||||
(echo "Lockfile not found." && exit 1)
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM node:current-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY pnpm-lock.yaml* ./
|
||||
COPY package.json ./
|
||||
COPY . .
|
||||
|
||||
# Next.js collects completely anonymous telemetry data about general usage.
|
||||
@@ -21,7 +24,10 @@ COPY . .
|
||||
# Uncomment the following line in case you want to disable telemetry during the build.
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
RUN npm install -g pnpm && pnpm run build
|
||||
RUN npm install -g pnpm
|
||||
RUN \
|
||||
[ -f pnpm-lock.yaml ] && (pnpm --offline install && pnpm run build) || \
|
||||
(echo "Lockfile not found." && exit 1)
|
||||
|
||||
# Production image, copy all the files and run next
|
||||
FROM node:current-alpine AS runner
|
||||
@@ -52,15 +58,8 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
|
||||
USER nextjs
|
||||
|
||||
ENV PORT=3000
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENV PORT 3000
|
||||
ENV MAX_USER ''
|
||||
ENV AXIOS_PROXY_HOST ''
|
||||
ENV AXIOS_PROXY_PORT ''
|
||||
ENV MONGODB_URI ''
|
||||
ENV MY_MAIL ''
|
||||
ENV MAILE_CODE ''
|
||||
ENV TOKEN_KEY ''
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
63
client/data/config.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"FeConfig": {
|
||||
"show_emptyChat": true,
|
||||
"show_register": false,
|
||||
"show_appStore": false,
|
||||
"show_userDetail": false,
|
||||
"show_git": true,
|
||||
"systemTitle": "FastAI",
|
||||
"authorText": "Made by FastAI Team.",
|
||||
"gitLoginKey": ""
|
||||
},
|
||||
"SystemParams": {
|
||||
"gitLoginSecret": "",
|
||||
"vectorMaxProcess": 15,
|
||||
"qaMaxProcess": 15,
|
||||
"pgIvfflatProbe": 20
|
||||
},
|
||||
"plugins": {},
|
||||
"ChatModels": [
|
||||
{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"name": "FastAI-4k",
|
||||
"contextMaxToken": 4000,
|
||||
"quoteMaxToken": 2000,
|
||||
"maxTemperature": 1.2,
|
||||
"price": 1.5,
|
||||
"defaultSystem": ""
|
||||
},
|
||||
{
|
||||
"model": "gpt-3.5-turbo-16k",
|
||||
"name": "FastAI-16k",
|
||||
"contextMaxToken": 16000,
|
||||
"quoteMaxToken": 8000,
|
||||
"maxTemperature": 1.2,
|
||||
"price": 3,
|
||||
"defaultSystem": ""
|
||||
},
|
||||
{
|
||||
"model": "gpt-4",
|
||||
"name": "FastAI-Plus",
|
||||
"contextMaxToken": 8000,
|
||||
"quoteMaxToken": 4000,
|
||||
"maxTemperature": 1.2,
|
||||
"price": 45,
|
||||
"defaultSystem": ""
|
||||
}
|
||||
],
|
||||
"QAModels": [
|
||||
{
|
||||
"model": "gpt-3.5-turbo-16k",
|
||||
"name": "FastAI-16k",
|
||||
"maxToken": 16000,
|
||||
"price": 3
|
||||
}
|
||||
],
|
||||
"VectorModels": [
|
||||
{
|
||||
"model": "text-embedding-ada-002",
|
||||
"name": "Embedding-2",
|
||||
"price": 0.2
|
||||
}
|
||||
]
|
||||
}
|
||||
5
client/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
12
client/next-i18next.config.js
Normal file
@@ -0,0 +1,12 @@
|
||||
//next-i18next.config.js
|
||||
/**
|
||||
* @type {import('next-i18next').UserConfig}
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'zh', 'zh-Hans'],
|
||||
localeDetection: false
|
||||
}
|
||||
};
|
||||
40
client/next.config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const { i18n } = require('./next-i18next.config');
|
||||
|
||||
const nextConfig = {
|
||||
i18n,
|
||||
output: 'standalone',
|
||||
reactStrictMode: false,
|
||||
compress: true,
|
||||
|
||||
webpack(config, { isServer }) {
|
||||
if (!isServer) {
|
||||
config.resolve = {
|
||||
...config.resolve,
|
||||
fallback: {
|
||||
...config.resolve.fallback,
|
||||
fs: false
|
||||
}
|
||||
};
|
||||
}
|
||||
config.experiments = {
|
||||
asyncWebAssembly: true,
|
||||
layers: true
|
||||
};
|
||||
config.module = {
|
||||
...config.module,
|
||||
rules: config.module.rules.concat([
|
||||
{
|
||||
test: /\.svg$/i,
|
||||
issuer: /\.[jt]sx?$/,
|
||||
use: ['@svgr/webpack']
|
||||
}
|
||||
]),
|
||||
exprContextCritical: false
|
||||
};
|
||||
|
||||
return config;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
85
client/package.json
Normal file
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"name": "fastgpt",
|
||||
"version": "3.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/icons": "^2.0.17",
|
||||
"@chakra-ui/react": "^2.7.0",
|
||||
"@chakra-ui/system": "^2.5.8",
|
||||
"@dqbd/tiktoken": "^1.0.7",
|
||||
"@emotion/react": "^11.10.6",
|
||||
"@emotion/styled": "^11.10.6",
|
||||
"@next/font": "13.1.6",
|
||||
"@tanstack/react-query": "^4.24.10",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"axios": "^1.3.3",
|
||||
"cookie": "^0.5.0",
|
||||
"crypto": "^1.0.1",
|
||||
"date-fns": "^2.30.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"echarts": "^5.4.1",
|
||||
"formidable": "^2.1.1",
|
||||
"framer-motion": "^9.0.6",
|
||||
"hyperdown": "^2.4.29",
|
||||
"i18next": "^22.5.1",
|
||||
"immer": "^9.0.19",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mammoth": "^1.5.1",
|
||||
"mermaid": "^10.2.3",
|
||||
"mongoose": "^6.10.0",
|
||||
"nanoid": "^4.0.1",
|
||||
"next": "13.1.6",
|
||||
"next-i18next": "^13.3.0",
|
||||
"nextjs-cors": "^2.1.2",
|
||||
"nprogress": "^0.2.0",
|
||||
"openai": "^3.3.0",
|
||||
"papaparse": "^5.4.1",
|
||||
"pg": "^8.10.0",
|
||||
"react": "18.2.0",
|
||||
"react-day-picker": "^8.7.1",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.43.1",
|
||||
"react-i18next": "^12.3.1",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"reactflow": "^11.7.4",
|
||||
"rehype-katex": "^6.0.2",
|
||||
"remark-breaks": "^3.0.3",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
"request-ip": "^3.3.0",
|
||||
"sass": "^1.58.3",
|
||||
"tunnel": "^0.0.6",
|
||||
"zustand": "^4.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@svgr/webpack": "^6.5.1",
|
||||
"@types/cookie": "^0.5.1",
|
||||
"@types/formidable": "^2.0.5",
|
||||
"@types/js-cookie": "^3.0.3",
|
||||
"@types/jsonwebtoken": "^9.0.1",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/node": "18.14.0",
|
||||
"@types/papaparse": "^5.3.7",
|
||||
"@types/pg": "^8.6.6",
|
||||
"@types/react": "18.0.28",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"@types/react-syntax-highlighter": "^15.5.6",
|
||||
"@types/request-ip": "^0.0.37",
|
||||
"@types/tunnel": "^0.0.3",
|
||||
"eslint": "8.34.0",
|
||||
"eslint-config-next": "13.1.6",
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
11672
client/pnpm-lock.yaml
generated
Normal file
22
client/public/docs/chatProblem.md
Normal file
@@ -0,0 +1,22 @@
|
||||
### 常见问题
|
||||
|
||||
**一键部署**:V4 版本未正式开源,目前仅提供一键部署方案:
|
||||
|
||||
[](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt)
|
||||
|
||||
**Git 地址**: [项目地址。V4-beta 暂未开源,在正式版发布后会开源。](https://github.com/labring/FastGPT)
|
||||
**反馈问卷**: 如果你遇到任何使用问题或有期望的功能,可以[填写该问卷](https://www.wjx.cn/vm/rLIw1uD.aspx#)
|
||||
**问题文档**: [先看文档,再提问](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||
**价格表**
|
||||
| 计费项 | 价格: 元/ 1K tokens(包含上下文)|
|
||||
| --- | --- |
|
||||
| 知识库 - 索引 | 0.002 |
|
||||
| FastAI4k - 对话 | 0.015 |
|
||||
| FastAI16k - 对话 | 0.03 |
|
||||
| FastAI-Plus - 对话 | 0.45 |
|
||||
| 文件拆分 | 0.03 |
|
||||
|
||||
**其他问题**
|
||||
| 交流群 | 小助手 |
|
||||
| ----------------------- | -------------------- |
|
||||
|  |  |
|
||||
8
client/public/docs/versionIntro.md
Normal file
@@ -0,0 +1,8 @@
|
||||
### Fast GPT V4.0-beta
|
||||
|
||||
1. 全新交互,增加采用模块组合的方式构建知识库,同时保留表单的简易模式。
|
||||
2. 问题分类 - 可以对用户的问题进行分类,再执行不同的操作。
|
||||
3. 对话引导 - 增加开场引导和变量,提高对话可玩性。
|
||||
4. 新增 - 每轮对话均可展示完整的上下文状态。
|
||||
5. 新增 - 网页嵌入(简单版),可直接接入现有网页。在 【应用详情-外部使用-创建新链接-嵌入网页】 中获取嵌入脚本。
|
||||
6. 新增 - 个人 openai 账号可以使用网页上 OpenAI 对话模型。
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
BIN
client/public/icon/human.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 201 KiB |
BIN
client/public/icon/logo.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
BIN
client/public/imgs/errImg.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
1
client/public/imgs/files/csv.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689493855879" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3538" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M145.6 0C100.8 0 64 36.8 64 81.6v860.8C64 987.2 100.8 1024 145.6 1024h732.8c44.8 0 81.6-36.8 81.6-81.6V324.8L656 0H145.6z" fill="#45B058" p-id="3539"></path><path d="M388.8 691.2c1.6 1.6 3.2 4.8 3.2 8 0 6.4-4.8 11.2-11.2 11.2-3.2 0-6.4 0-8-3.2-11.2-12.8-30.4-22.4-48-22.4-41.6 0-73.6 32-73.6 78.4 0 44.8 32 78.4 73.6 78.4 17.6 0 35.2-8 48-22.4 1.6-1.6 4.8-3.2 8-3.2 6.4 0 11.2 4.8 11.2 11.2 0 3.2-1.6 6.4-3.2 8-14.4 16-35.2 27.2-64 27.2-56 0-99.2-40-99.2-99.2s43.2-99.2 99.2-99.2c28.8 0 49.6 11.2 64 27.2z m108.8 171.2c-28.8 0-51.2-9.6-67.2-24-3.2-1.6-3.2-4.8-3.2-8 0-6.4 3.2-12.8 11.2-12.8 1.6 0 4.8 1.6 6.4 3.2 12.8 11.2 32 20.8 54.4 20.8 33.6 0 44.8-19.2 44.8-33.6 0-49.6-113.6-22.4-113.6-91.2 0-30.4 27.2-52.8 65.6-52.8 24 0 46.4 8 62.4 20.8 1.6 1.6 3.2 4.8 3.2 8 0 6.4-4.8 11.2-11.2 11.2-1.6 0-4.8 0-6.4-1.6-14.4-11.2-32-17.6-49.6-17.6-24 0-40 12.8-40 30.4 0 43.2 113.6 19.2 113.6 91.2 0 27.2-19.2 56-70.4 56z m272-179.2L702.4 848c-3.2 8-9.6 12.8-17.6 12.8h-1.6c-8 0-16-4.8-19.2-12.8l-65.6-164.8c-1.6-1.6-1.6-3.2-1.6-4.8 0-6.4 4.8-12.8 12.8-12.8 4.8 0 9.6 3.2 11.2 8l62.4 160 62.4-160c1.6-4.8 6.4-8 11.2-8 8 0 14.4 6.4 14.4 12.8 0 1.6-1.6 3.2-1.6 4.8z" fill="#FFFFFF" p-id="3540"></path><path d="M960 326.4v16H755.2s-100.8-20.8-97.6-108.8c0 0 3.2 92.8 96 92.8H960z" fill="#349C42" p-id="3541"></path><path d="M657.6 0v233.6c0 25.6 17.6 92.8 97.6 92.8H960L657.6 0z" fill="#FFFFFF" p-id="3542"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
1
client/public/imgs/files/doc.svg
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
1
client/public/imgs/files/html.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689494897388" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="958" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M146.56 0C101.76 0 66.56 35.2 66.56 80V940.8C66.56 985.6 101.76 1024 146.56 1024H879.36c44.8 0 80-35.2 80-80V323.2L658.56 0h-512z" fill="#F7622C" p-id="959"></path><path d="M962.56 326.4v16h-204.8s-99.2-19.2-99.2-108.8c0 0 3.2 92.8 96 92.8H962.56z" fill="#F54921" p-id="960"></path><path d="M658.56 0v233.6c0 25.6 16 92.8 96 92.8H962.56L658.56 0zM367.36 812.8H360.96l-121.6-54.4c-6.4-3.2-12.8-9.6-12.8-19.2 0-6.4 6.4-16 12.8-19.2L360.96 665.6h3.2c6.4 0 12.8 6.4 12.8 16 0 6.4-3.2 12.8-6.4 16L258.56 745.6 370.56 793.6c3.2 3.2 6.4 6.4 6.4 16 3.2 0-3.2 3.2-9.6 3.2zM501.76 636.8l-70.4 211.2c-3.2 6.4-6.4 9.6-12.8 9.6-9.6 0-16-6.4-16-16l3.2-3.2L476.16 627.2c3.2-6.4 6.4-12.8 12.8-12.8 9.6 0 12.8 6.4 12.8 16v6.4z m166.4 121.6l-121.6 54.4h-3.2c-6.4 0-16-3.2-16-12.8 0-6.4 3.2-12.8 6.4-16l112-48-112-48c-3.2-3.2-6.4-6.4-6.4-16 0-6.4 6.4-16 16-16h3.2l121.6 54.4c6.4 3.2 12.8 12.8 12.8 19.2-3.2 16-6.4 25.6-12.8 28.8z" fill="#FFFFFF" p-id="961"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
client/public/imgs/files/markdown.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689494956529" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3178" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M903.542857 256.8c6.857143 6.857143 10.742857 16.114286 10.742857 25.828571V987.428571c0 20.228571-16.342857 36.571429-36.571428 36.571429H146.285714c-20.228571 0-36.571429-16.342857-36.571428-36.571429V36.571429c0-20.228571 16.342857-36.571429 36.571428-36.571429h485.371429c9.714286 0 19.085714 3.885714 25.942857 10.742857l245.942857 246.057143zM829.942857 299.428571L614.857143 84.342857V299.428571h215.085714zM413.862857 613.634286l67.554286 151.965714a18.285714 18.285714 0 0 0 16.708571 10.857143h27.497143a18.285714 18.285714 0 0 0 16.72-10.868572l67.542857-152.4V793.142857a18.285714 18.285714 0 0 0 18.297143 18.285714H659.428571a18.285714 18.285714 0 0 0 18.285715-18.285714V482.285714a18.285714 18.285714 0 0 0-18.285715-18.285714h-39.714285a18.285714 18.285714 0 0 0-16.765715 10.994286L512.114286 683.657143l-90.834286-208.674286a18.285714 18.285714 0 0 0-16.765714-10.982857H364.571429a18.285714 18.285714 0 0 0-18.285715 18.285714v310.857143a18.285714 18.285714 0 0 0 18.285715 18.285714h31.005714a18.285714 18.285714 0 0 0 18.285714-18.285714V613.634286z" p-id="3179" data-spm-anchor-id="a313x.7781069.0.i3" class="selected" fill="#515151"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
client/public/imgs/files/pdf.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689494925506" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1120" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M149.76 0C104.96 0 66.56 35.2 66.56 80V940.8c0 48 38.4 83.2 83.2 83.2h732.8c44.8 0 80-35.2 80-80V323.2L658.56 0H149.76z" fill="#DD5154" p-id="1121"></path><path d="M962.56 326.4v16h-204.8s-99.2-19.2-96-105.6c0 0 3.2 89.6 96 89.6h204.8z" fill="#6B0D12" p-id="1122"></path><path d="M329.91232 652.3904l2.31424 0.02048c21.02272 0.33792 36.81792 5.3504 47.29856 15.15008 10.9056 10.19904 16.29184 26.112 16.29184 47.60064 0 41.71776-21.63712 63.2832-64.01536 63.90272l-1.90976 0.01536h-36.43904v63.45216l-0.03072 1.2544c-0.19968 3.59424-1.23392 6.5536-3.15904 8.8064-2.2784 2.65216-5.85216 3.90144-10.5472 3.90144-4.92544 0-8.63232-1.37728-10.9056-4.28032-1.85344-2.37056-2.87744-5.2224-3.08736-8.4992l-0.03584-1.24928v-173.78816l0.02048-1.28512c0.15872-5.4784 1.34656-9.3696 3.83488-11.61728 2.44736-2.21696 6.91712-3.24608 13.58336-3.36896l1.46432-0.01536h45.32224z m407.21408 0l1.0752 0.02048c8.66304 0.33792 13.47584 4.8384 13.47584 12.86144 0 4.57216-1.30048 7.98208-4.08064 9.96864-2.26304 1.61792-5.40672 2.45248-9.43616 2.60096l-1.37216 0.0256H659.3536v59.38688l69.74464 0.00512 1.17248 0.0256c6.43072 0.26112 10.69056 2.75456 12.00128 7.20896 1.28 2.80576 1.152 5.9392-0.128 8.82688-1.37216 5.7088-5.60128 8.8064-12.0576 9.10336l-1.03936 0.0256H659.3536v81.23904l-0.03584 1.13664c-0.2048 3.31776-1.28 6.0672-3.24608 8.1408-2.29376 2.42688-6.00064 3.52768-11.0336 3.52768-4.88448 0-8.448-1.1264-10.55744-3.6352-1.7408-2.06336-2.67776-4.75136-2.8672-8.00256l-0.03072-1.24416v-176.37888l0.02048-1.20832c0.16896-4.77184 1.3312-8.2688 3.69664-10.3936 2.28864-2.06848 5.88288-3.07712 10.78272-3.2256l1.24928-0.01536h89.79456z m-233.00096 0l2.65728 0.03072c13.3888 0.3072 25.0624 2.90816 35.00544 7.8336 10.57792 5.23776 19.34336 12.42112 26.27584 21.5296 6.89664 9.0624 12.0064 19.73248 15.33952 31.98976 3.31264 12.17024 4.9664 25.15456 4.9664 38.94784 0 13.98272-1.65376 27.06432-4.9664 39.23456-3.328 12.24704-8.38656 22.95296-15.17568 32.1024a72.28416 72.28416 0 0 1-25.85088 21.69344c-9.79968 4.95104-21.26336 7.55712-34.37056 7.84896l-2.47808 0.0256H451.59936l-1.14176-0.01024c-5.84704-0.11776-9.53856-1.09056-11.24352-3.51232-1.38752-1.96096-2.06336-4.92544-2.176-8.97536l-0.01536-1.24416v-173.78816l0.02048-1.25952c0.11264-4.01408 0.78336-6.9632 2.17088-8.92416 1.6896-2.39616 5.32992-3.38944 11.136-3.5072l1.27488-0.01536h52.50048z m-5.76512 24.33024h-33.85856v152.576h41.02656c17.3568 0 30.73536-6.5024 40.3712-19.59936 9.76896-13.27104 14.69952-32.53248 14.69952-57.83552 0-24.51456-5.06368-43.18208-15.08864-56.05888-9.56928-12.288-24.51968-18.6624-45.07648-19.06176l-2.0736-0.02048z m-168.46848 0.86016h-36.43904v76.88192h36.43904c14.08 0 23.92576-3.13344 29.67552-9.23136 5.79584-6.144 8.76544-16.04096 8.76544-29.78304 0-13.6704-3.29728-23.296-9.7536-29.056-6.22592-5.5552-15.13472-8.50944-26.81856-8.79104l-1.8688-0.02048zM658.56 0L962.56 326.4h-208c-80 0-96-64-96-92.8V0z" fill="#FFFFFF" p-id="1123"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
1
client/public/imgs/files/txt.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689495026020" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="958" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M146.56 0C104.96 0 66.56 35.2 66.56 80v864C66.56 988.8 101.76 1024 146.56 1024H879.36c44.8 0 80-35.2 80-80V323.2L658.56 0h-512z" fill="#99B7D2" p-id="959"></path><path d="M962.56 323.2v16h-204.8s-99.2-19.2-96-105.6c0 0 3.2 89.6 96 89.6h204.8z" fill="#527EA7" p-id="960"></path><path d="M449.07008 650.25024c3.00032 0.12288 5.84192 2.09408 8.72448 5.7344l0.7168 0.94208 47.79008 68.11136 45.7216-65.42848 0.64512-1.01376c2.82112-4.30592 5.89824-6.89664 9.33376-7.5776 3.56352-0.7168 7.1168 0.11264 10.54208 2.39616 6.32832 4.29568 7.2704 10.58304 2.944 17.8688l-0.5632 0.91136-52.87424 75.3664 62.83776 89.51808 0.65024 1.3824c1.07008 2.41152 1.7408 4.74624 2.01216 7.00416 0.41984 3.49184-1.56672 6.64064-5.55008 9.4464-7.39328 5.00736-14.1312 3.92192-19.03104-2.97984l-0.55296-0.8192L506.0096 770.6624l-56.43264 80.50688-0.73728 0.98816c-2.47296 3.13856-5.30432 5.04832-8.47872 5.59616-3.20512 0.55296-6.34368-0.24576-9.216-2.2272l-0.84992-0.62976c-3.16416-2.19136-4.85376-5.43744-4.9664-9.48224-0.1024-3.49184 0.8704-6.66112 2.7904-9.28768l0.67584-0.85504 61.19424-87.424-52.83328-75.58656-0.60416-0.8192c-1.67424-2.4576-2.39616-5.40672-2.19136-8.7552a11.42784 11.42784 0 0 1 5.43744-9.28768c3.2768-2.18624 6.36416-3.27168 9.2672-3.1488z m-62.98112 3.584l1.14688 0.03584c3.31264 0.21504 6.1696 1.31584 8.4736 3.30752 2.70336 2.33472 4.03968 5.67808 4.03968 9.82016 0 4.65408-1.58208 8.13568-4.82816 10.0864a17.16736 17.16736 0 0 1-7.74144 2.47296l-1.17248 0.03584h-43.60192v164.37248l-0.03072 1.30048c-0.17408 3.70688-1.08544 6.67136-2.82624 8.84224-2.10944 2.64192-5.7856 3.82464-10.88512 3.82464-5.12 0-8.81664-1.2544-10.92608-4.0192-1.6896-2.22208-2.60096-5.12-2.78016-8.6528l-0.03584-1.3568-0.00512-164.31104h-43.66336l-1.18784-0.03072c-3.39968-0.17408-6.2464-1.09056-8.46848-2.78528-2.73408-2.07872-4.0192-5.51424-4.0192-10.06592 0-4.36224 1.35168-7.75168 4.1472-9.9072 2.29376-1.77152 5.09952-2.74432 8.35072-2.93888l1.24416-0.03584h114.76992z m365.60896 0l1.14688 0.03584c3.31264 0.21504 6.1696 1.31584 8.4736 3.30752 2.70336 2.33472 4.03968 5.67808 4.03968 9.82016 0 4.65408-1.58208 8.13568-4.82816 10.0864a17.16736 17.16736 0 0 1-7.74144 2.47296l-1.17248 0.03584h-43.60192v164.37248l-0.03072 1.30048c-0.17408 3.70688-1.08544 6.67136-2.82624 8.84224-2.10944 2.64192-5.7856 3.82464-10.88512 3.82464-5.12 0-8.81664-1.2544-10.92608-4.0192-1.6896-2.22208-2.60096-5.12-2.78016-8.6528l-0.03584-1.3568v-164.31104h-43.66848l-1.18784-0.03072c-3.39968-0.17408-6.2464-1.09056-8.46848-2.78528-2.73408-2.07872-4.0192-5.51424-4.0192-10.06592 0-4.36224 1.35168-7.75168 4.1472-9.9072 2.29376-1.77152 5.09952-2.74432 8.35072-2.93888l1.24416-0.03584h114.76992zM658.56 0L962.56 323.2h-208c-80 0-96-64-96-92.8V0z" fill="#FFFFFF" p-id="961"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
BIN
client/public/imgs/home/advanced_settings.png
Normal file
|
After Width: | Height: | Size: 428 KiB |
BIN
client/public/imgs/home/ai_assiatant.png
Normal file
|
After Width: | Height: | Size: 596 KiB |
BIN
client/public/imgs/home/dataset_import.png
Normal file
|
After Width: | Height: | Size: 223 KiB |
1
client/public/imgs/home/icon_1.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1691391466028" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4715" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M511.957333 21.333333C241.024 21.333333 21.333333 240.981333 21.333333 512c0 216.832 140.544 400.725333 335.573334 465.664 24.490667 4.394667 32.256-10.069333 32.256-23.082667 0-11.690667 0.256-44.245333 0-85.205333-136.448 29.610667-164.736-64.64-164.736-64.64-22.314667-56.704-54.4-71.765333-54.4-71.765333-44.586667-30.464 3.285333-29.824 3.285333-29.824 49.194667 3.413333 75.178667 50.517333 75.178667 50.517333 43.776 75.008 114.816 53.333333 142.762666 40.789333 4.522667-31.658667 17.152-53.376 31.189334-65.536-108.970667-12.458667-223.488-54.485333-223.488-242.602666 0-53.546667 19.114667-97.322667 50.517333-131.669334-5.034667-12.330667-21.930667-62.293333 4.778667-129.834666 0 0 41.258667-13.184 134.912 50.346666a469.802667 469.802667 0 0 1 122.88-16.554666c41.642667 0.213333 83.626667 5.632 122.88 16.554666 93.653333-63.488 134.784-50.346667 134.784-50.346666 26.752 67.541333 9.898667 117.504 4.864 129.834666 31.402667 34.346667 50.474667 78.122667 50.474666 131.669334 0 188.586667-114.730667 230.016-224.042666 242.090666 17.578667 15.232 33.578667 44.672 33.578666 90.453334v135.850666c0 13.141333 7.936 27.605333 32.853334 22.869334C862.250667 912.597333 1002.666667 728.746667 1002.666667 512 1002.666667 240.981333 783.018667 21.333333 511.957333 21.333333z" p-id="4716"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
1
client/public/imgs/home/icon_2.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688889092975" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2652" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M932.15857778 820.62601482V639.06702222c-26.82121482 23.42305185-57.04059259 42.71976297-89.4445037 57.28331853-104.97896297 46.11792592-218.45333333 69.41961482-333.14133333 68.32734814-148.06281482 8.61677037-294.66927408-32.768-416.39632593-117.35798519v182.89398519C101.4290963 910.07051852 277.52675555 997.45185185 512.36408889 997.45185185c234.83733333 0 409.6-88.10951111 419.79448889-168.57315555v-8.25268148z" p-id="2653" fill="#4e83fd"></path><path d="M932.15857778 505.93185185v-3.39816296 8.25268148c-10.19448889 80.58500741-184.95715555 168.57315555-419.79448889 168.57315555-234.83733333 0-410.93499259-87.38133333-419.18767407-167.23816295V329.10601482C214.90346667 413.81736297 361.50992592 455.08077037 509.57274075 446.464c114.688 1.09226667 228.16237037-22.08805925 333.14133333-68.32734815 32.40391111-14.68491852 62.62328889-33.98162963 89.4445037-57.28331852V505.93185185z" p-id="2654" fill="#4e83fd"></path><path d="M816.13558518 85.40918518c69.66234075 30.70482963 111.28983703 74.3954963 111.28983704 113.35300741 0 38.22933333-42.96248889 80.58500741-111.28983704 113.35300741-97.09037037 41.26340741-201.70524445 61.53102222-307.16965926 59.34648889-238.96367408 0-417.1245037-90.7794963-417.1245037-171.97131852s178.88900741-172.6994963 418.4594963-172.69949629c104.97896297-2.3058963 209.10838518 17.71899259 305.83466666 58.6183111z" p-id="2655" fill="#4e83fd"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
1
client/public/imgs/home/icon_3.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1691459708057" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5077" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M934.423357 388.738675H636.152113v99.05297h-64.657602L413.227927 382.546649v-64.511269h32.679284c69.808926 0 126.410038-56.601112 126.410038-126.410038 0-69.807902-56.602136-126.409015-126.410038-126.409015H330.962296c-69.808926 0-126.410038 56.602136-126.410038 126.409015 0 69.809949 56.602136 126.410038 126.410038 126.410038h32.702819v64.536852L166.551782 513.623987l197.114356 131.054825v61.440327h-32.702819c-69.808926 0-126.410038 56.601112-126.410038 126.410038 0 69.807902 56.602136 126.410038 126.410038 126.410038h114.944915c60.205197 0 110.372783-42.185798 123.096573-98.54541h240.532215V636.430964h124.887358V388.738675zM759.973186 810.856575h-189.833533c-10.328229-59.431577-61.84965-104.737436-124.232442-104.737436h-32.679284v-61.414745l161.435763-107.349936h61.488423v99.075483h123.821073v174.426634z" p-id="5078" fill="#FC9663"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
1
client/public/imgs/home/icon_4.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1691460113549" class="icon" viewBox="0 0 1443 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2328" xmlns:xlink="http://www.w3.org/1999/xlink" width="90.1875" height="64"><path d="M1200.165345 1023.218368H349.208974c-192.401638 0-349.208974-215.369584-349.208974-406.207959a348.006463 348.006463 0 0 1 349.208974-346.022321 322.33287 322.33287 0 0 1 38.780955 2.344895C470.963136 141.056856 617.248506 0.002405 774.777348 0.002405a454.368494 454.368494 0 0 1 454.669122 423.403856c120.251024 14.309872 213.625944 168.952689 213.625944 359.189808a242.005186 242.005186 0 0 1-242.907069 240.622299zM349.148848 330.57247A288.061328 288.061328 0 0 0 60.125512 617.010409c0 158.009846 129.690729 346.623577 289.083462 346.623577h850.956371A182.120176 182.120176 0 0 0 1382.886776 782.596069c0-156.867461-81.951073-301.349066-182.661305-301.349066a30.062756 30.062756 0 0 1-30.062756-30.062756A394.062606 394.062606 0 0 0 774.717222 59.28616c-142.67784 0-274.893841 136.965916-344.940062 260.70422a29.581752 29.581752 0 0 1-31.265266 14.73075 297.080155 297.080155 0 0 0-49.30292-4.14866z m881.079253 120.912405zM300.62756 783.497952H240.502048v-240.502048h60.125512v60.125512h60.125512v-60.125512h60.125512v240.502048H360.753072v-120.251024H300.62756v120.251024z m180.376536-180.376536v-60.125512h180.376536v60.125512h-60.125512v180.376536H541.129608v-180.376536H481.004096z m240.502048 0v-60.125512h180.376536v60.125512h-60.125512v180.376536h-60.125512v-180.376536h-60.125512z m300.62756 180.376536h-60.125512v-240.502048h60.125512c71.368983-0.962008 127.225583-24.050205 126.263575 30.062756-1.863891 50.264928-7.936568 147.548006-66.138063 150.31378h-60.125512v60.125512z m0-180.376536v60.125512a35.293676 35.293676 0 0 0 12.025103 6.012551q56.39773 5.471422 54.954717-36.075307 0-37.818947-54.954717-36.075307a35.293676 35.293676 0 0 0-12.025103 6.012551z" fill="#7839ee" p-id="2329"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
1
client/public/imgs/home/icon_5.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1686561811905" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2855" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M992 528c0 273.9-222.1 496-496 496S0 801.9 0 528 222.1 32 496 32c86.2 0 167.3 22 238 60.7 2.3 1.3 2.8 4.4 0.9 6.3l-37 37.3-4.2 4.3c-1.2 1.2-3.1 1.5-4.6 0.8-8.2-4.1-16.5-7.9-24.9-11.5C610.9 107.4 554.3 96 496 96s-114.9 11.4-168.1 33.9c-51.4 21.8-97.7 52.9-137.3 92.6-39.7 39.7-70.9 85.9-92.6 137.3C75.4 413.1 64 469.6 64 528c0 58.3 11.4 114.9 33.9 168.1 21.8 51.4 52.9 97.6 92.6 137.3 39.7 39.7 85.9 70.9 137.3 92.6 53.3 22.6 109.9 34 168.2 34s114.9-11.4 168.1-33.9c51.4-21.8 97.7-52.9 137.3-92.6 39.7-39.7 70.9-85.9 92.6-137.3 22.6-53.3 34-109.9 34-168.2 0-58.4-11.4-114.9-33.9-168.1-3.6-8.5-7.4-16.8-11.5-25-0.8-1.5-0.5-3.4 0.8-4.6l4.3-4.2 37.3-37c1.9-1.9 5-1.4 6.3 0.9C970 360.6 992 441.7 992 528z" p-id="2856" fill="#6CD3CC"></path><path d="M781.4 397c-3.7-8-11.7-13.1-20.6-13.1H740c-6 0-11.8 2.4-16 6.6-7 7-8.6 17.6-4.1 26.4 2.6 5.1 5 10.3 7.3 15.7 13.2 31.2 19.9 64.3 19.9 98.5s-6.7 67.3-19.9 98.5c-12.7 30.1-31 57.2-54.2 80.4-23.3 23.3-50.3 41.5-80.4 54.2-31.3 13.1-64.4 19.8-98.6 19.8s-67.3-6.7-98.5-19.9c-30.1-12.7-57.2-31-80.4-54.2-23.3-23.3-41.5-50.3-54.2-80.4-13.2-31.2-19.9-64.3-19.9-98.5s6.7-67.3 19.9-98.5c12.7-30.1 31-57.2 54.2-80.4 23.3-23.3 50.3-41.5 80.4-54.2 31.2-13.2 64.3-19.9 98.5-19.9s67.3 6.7 98.5 19.9c4.9 2.1 9.8 4.3 14.6 6.7 8.8 4.4 19.4 2.6 26.3-4.4 4.3-4.3 6.7-10.1 6.7-16.2v-20.2c0-9-5.2-17.1-13.4-20.8-40.4-18.6-85.3-29-132.6-29-175.5 0-318 143.4-317 318.9C178 707.1 319.6 848 494 848c174.8 0 316.6-141.3 317-316.2 0.1-48.2-10.5-93.9-29.6-134.8z" p-id="2857" fill="#6CD3CC"></path><path d="M634.5 488.5c-0.8-2.9-4.5-3.9-6.7-1.7l-34.7 34.7-1.8 1.8c-9 9-15.7 20.1-20.1 32.1-11.5 31.6-42.4 54-78.3 52.7-41.6-1.6-75.3-35.3-76.9-76.9-1.4-35.9 21-66.8 52.7-78.3 12-4.4 23-11.1 32.1-20.1l1.8-1.8 34.7-34.7c2.2-2.2 1.2-5.8-1.7-6.7-12.9-3.7-26.5-5.6-40.6-5.5-79.4 0.5-143 64.5-143 143.9 0 79.5 64.5 144 144 144 79.4 0 143.4-63.6 144-142.9 0.1-14.1-1.8-27.8-5.5-40.6z" p-id="2858" fill="#6CD3CC"></path><path d="M1014.3 146H882c-2.2 0-4-1.8-4-4V9.8c0-2.4-2-4-4-4-1 0-2 0.4-2.8 1.2L766.8 112.4l-46.1 46.5-44 44.4c-3 3-4.6 7-4.6 11.3v85.5c0 4.3-1.7 8.3-4.7 11.3l-94.7 94.7-47.4 47.4-51.8 51.9c-12.5 12.5-12.5 32.8 0 45.3 6.3 6.3 14.4 9.4 22.6 9.4s16.4-3.1 22.6-9.4l51.8-51.9 123.2-123.2 19-19c3-3 7.1-4.7 11.3-4.7h85.5c4.2 0 8.3-1.7 11.3-4.6l44.3-43.9 46.5-46.1L1017 152.9c2.6-2.6 0.8-6.9-2.7-6.9zM864 214.3l-44 43.5-25.6 25.4c-3 3-7 4.6-11.3 4.6H744c-4.4 0-8-3.6-8-8v-39c0-4.2 1.7-8.3 4.6-11.3l25.5-25.7 43.5-43.9 1.6-1.6c1.6-1.7 4.5-0.7 4.8 1.6 4.8 25.8 23.5 41.6 48.6 47.7 2.1 0.5 2.9 3.2 1.3 4.7l-1.9 2z" p-id="2859" fill="#6CD3CC"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
1
client/public/imgs/home/icon_6.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1691460052375" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7219" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M950.676002 419.057175a255.346807 255.346807 0 0 0-22.014863-209.48251 257.949339 257.949339 0 0 0-277.74565-123.72694A258.759964 258.759964 0 0 0 212.538139 178.386943a255.346807 255.346807 0 0 0-170.572521 123.726941 257.949339 257.949339 0 0 0 31.699696 302.789689 255.133485 255.133485 0 0 0 21.758876 209.525175 258.162662 258.162662 0 0 0 277.958972 123.726941A255.346807 255.346807 0 0 0 565.757223 1023.996587a258.375984 258.375984 0 0 0 246.259276-179.446729 255.560129 255.560129 0 0 0 170.529856-123.726941 258.375984 258.375984 0 0 0-31.870353-301.765742zM565.757223 957.013381a190.966133 190.966133 0 0 1-122.702994-44.371041l6.015689-3.455821 203.893466-117.668587a33.918248 33.918248 0 0 0 16.724469-29.054499v-287.430483l86.182214 49.832092a3.029177 3.029177 0 0 1 1.621249 2.218552v238.195693a192.160738 192.160738 0 0 1-191.734093 191.734094zM153.618516 780.979809a190.710147 190.710147 0 0 1-22.825487-128.590689l6.058354 3.626479 204.064123 117.711252a32.8943 32.8943 0 0 0 33.278281 0l249.288453-143.736574v99.493526a3.413157 3.413157 0 0 1-1.407927 2.645197L415.578315 851.205514a191.990081 191.990081 0 0 1-261.959799-70.225705zM99.861294 336.928085a191.350114 191.350114 0 0 1 100.944118-84.176984V494.957254a32.680978 32.680978 0 0 0 16.553811 28.841176l248.093849 143.139272-86.182214 49.832092a3.242499 3.242499 0 0 1-3.029177 0l-206.069353-118.863193A192.160738 192.160738 0 0 1 99.861294 335.904138z m708.102081 164.471503l-248.861809-144.504534L645.070458 307.23362a3.242499 3.242499 0 0 1 3.029177 0l206.069353 119.076514a191.734094 191.734094 0 0 1-28.841177 345.795467v-242.248817a33.704925 33.704925 0 0 0-17.364436-28.457196z m85.75557-128.97467l-6.01569-3.626479-203.680143-118.692534a33.107623 33.107623 0 0 0-33.491603 0L401.456378 393.842478V294.306288a2.815855 2.815855 0 0 1 1.194605-2.602533l206.069353-118.905856a191.990081 191.990081 0 0 1 284.998609 198.816394z m-539.278804 176.417552l-86.182214-49.661434a3.413157 3.413157 0 0 1-1.62125-2.431875V259.236099a191.990081 191.990081 0 0 1 314.65041-147.320388l-6.058354 3.413157-203.850801 117.668587a33.918248 33.918248 0 0 0-16.767134 29.054499z m46.802915-100.901454l111.012931-63.996693 111.226253 63.996693v127.950723l-110.799609 63.996693-111.226253-63.996693z" p-id="7220"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
BIN
client/public/imgs/home/openapi.png
Normal file
|
After Width: | Height: | Size: 177 KiB |
BIN
client/public/imgs/home/test.png
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
client/public/imgs/home/videobgpc.png
Normal file
|
After Width: | Height: | Size: 455 KiB |
BIN
client/public/imgs/home/videobgphone.png
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
client/public/imgs/module/AI.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
client/public/imgs/module/cq.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
client/public/imgs/module/db.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
client/public/imgs/module/extract.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/public/imgs/module/history.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
client/public/imgs/module/http.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
client/public/imgs/module/reply.png
Normal file
|
After Width: | Height: | Size: 776 B |
BIN
client/public/imgs/module/userChatInput.png
Normal file
|
After Width: | Height: | Size: 572 B |
BIN
client/public/imgs/module/userGuide.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/public/imgs/module/variable.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
client/public/imgs/openai.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
3
client/public/js/html2pdf.bundle.min.js
vendored
Normal file
58
client/public/js/iframe.js
Normal file
@@ -0,0 +1,58 @@
|
||||
async function embedChatbot() {
|
||||
const chatBtnId = 'fastgpt-chatbot-button';
|
||||
const chatWindowId = 'fastgpt-chatbot-window';
|
||||
const script = document.getElementById('fastgpt-iframe');
|
||||
const botSrc = script?.getAttribute('data-src');
|
||||
const primaryColor = script?.getAttribute('data-color') || '#4e83fd';
|
||||
|
||||
if (!botSrc) {
|
||||
console.error(`Can't find appid`);
|
||||
return;
|
||||
}
|
||||
if (document.getElementById(chatBtnId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const MessageIcon = `<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1690532785664" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4132" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M512 32C247.04 32 32 224 32 464A410.24 410.24 0 0 0 172.48 768L160 965.12a25.28 25.28 0 0 0 39.04 22.4l168-112A528.64 528.64 0 0 0 512 896c264.96 0 480-192 480-432S776.96 32 512 32z m244.8 416l-361.6 301.76a12.48 12.48 0 0 1-19.84-12.48l59.2-233.92h-160a12.48 12.48 0 0 1-7.36-23.36l361.6-301.76a12.48 12.48 0 0 1 19.84 12.48l-59.2 233.92h160a12.48 12.48 0 0 1 8 22.08z" fill=${primaryColor} p-id="4133"></path></svg>`;
|
||||
|
||||
const CloseIcon = `<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1690535441526" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6367" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 1024A512 512 0 1 1 512 0a512 512 0 0 1 0 1024zM305.956571 370.395429L447.488 512 305.956571 653.604571a45.568 45.568 0 1 0 64.438858 64.438858L512 576.512l141.604571 141.531429a45.568 45.568 0 0 0 64.438858-64.438858L576.512 512l141.531429-141.604571a45.568 45.568 0 1 0-64.438858-64.438858L512 447.488 370.395429 305.956571a45.568 45.568 0 0 0-64.438858 64.438858z" fill=${primaryColor} p-id="6368"></path></svg>`;
|
||||
|
||||
const ChatBtn = document.createElement('div');
|
||||
ChatBtn.id = chatBtnId;
|
||||
ChatBtn.style.cssText =
|
||||
'position: fixed; bottom: 1rem; right: 1rem; width: 40px; height: 40px; cursor: pointer; z-index: 2147483647; ';
|
||||
|
||||
const ChatBtnDiv = document.createElement('div');
|
||||
ChatBtnDiv.style.cssText =
|
||||
'transition: all 0.2s ease-in-out 0s; left: unset; transform: scale(1); :hover {transform: scale(1.1);} display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; z-index: 9999;';
|
||||
ChatBtnDiv.innerHTML = MessageIcon;
|
||||
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.allow = 'fullscreen;microphone';
|
||||
iframe.title = 'FastGpt Chat Window';
|
||||
iframe.id = chatWindowId;
|
||||
iframe.src = botSrc;
|
||||
|
||||
iframe.style.cssText =
|
||||
'visibility: hidden; border: none; position: fixed; flex-direction: column; justify-content: space-between; box-shadow: rgba(150, 150, 150, 0.2) 0px 10px 30px 0px, rgba(150, 150, 150, 0.2) 0px 0px 0px 1px; bottom: 4rem; right: 1rem; width: 24rem; height: 40rem; max-width: 90vw; max-height: 85vh; border-radius: 0.75rem; display: flex; z-index: 2147483647; overflow: hidden; left: unset; background-color: #F3F4F6;';
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
ChatBtn.addEventListener('click', function () {
|
||||
const chatWindow = document.getElementById(chatWindowId);
|
||||
|
||||
if (!chatWindow) return;
|
||||
const visibilityVal = chatWindow.style.visibility;
|
||||
if (visibilityVal === 'hidden') {
|
||||
chatWindow.style.visibility = 'unset';
|
||||
ChatBtnDiv.innerHTML = CloseIcon;
|
||||
} else {
|
||||
chatWindow.style.visibility = 'hidden';
|
||||
ChatBtnDiv.innerHTML = MessageIcon;
|
||||
}
|
||||
});
|
||||
|
||||
ChatBtn.appendChild(ChatBtnDiv);
|
||||
document.body.appendChild(ChatBtn);
|
||||
}
|
||||
document.body.onload = embedChatbot;
|
||||
@@ -406,12 +406,12 @@ function getVerbosityLevel() {
|
||||
}
|
||||
function info(msg) {
|
||||
if (verbosity >= VerbosityLevel.INFOS) {
|
||||
console.log(`Info: ${msg}`);
|
||||
// console.log(`Info: ${msg}`);
|
||||
}
|
||||
}
|
||||
function warn(msg) {
|
||||
if (verbosity >= VerbosityLevel.WARNINGS) {
|
||||
console.log(`Warning: ${msg}`);
|
||||
// console.log(`Warning: ${msg}`);
|
||||
}
|
||||
}
|
||||
function unreachable(msg) {
|
||||
@@ -4206,7 +4206,7 @@ function loadScript(src, removeScriptElement = false) {
|
||||
});
|
||||
}
|
||||
function deprecated(details) {
|
||||
console.log("Deprecated API usage: " + details);
|
||||
// console.log("Deprecated API usage: " + details);
|
||||
}
|
||||
let pdfDateStringRegex;
|
||||
class PDFDateString {
|
||||
@@ -1008,12 +1008,12 @@ function getVerbosityLevel() {
|
||||
}
|
||||
function info(msg) {
|
||||
if (verbosity >= VerbosityLevel.INFOS) {
|
||||
console.log(`Info: ${msg}`);
|
||||
// console.log(`Info: ${msg}`);
|
||||
}
|
||||
}
|
||||
function warn(msg) {
|
||||
if (verbosity >= VerbosityLevel.WARNINGS) {
|
||||
console.log(`Warning: ${msg}`);
|
||||
// console.log(`Warning: ${msg}`);
|
||||
}
|
||||
}
|
||||
function unreachable(msg) {
|
||||
122
client/public/locales/en/common.json
Normal file
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"App": "App",
|
||||
"Cancel": "No",
|
||||
"Confirm": "Yes",
|
||||
"Running": "Running",
|
||||
"Warning": "Warning",
|
||||
"app": {
|
||||
"Advance App TestTip": "The current application is advanced editing mode \n. If you need to switch to [simple mode], please click the save button on the left",
|
||||
"App Detail": "App Detail",
|
||||
"Confirm Del App Tip": "Confirm to delete the app and all its chats",
|
||||
"Confirm Save App Tip": "The application may be in advanced orchestration mode, and the advanced orchestration configuration will be overwritten after saving, please confirm!",
|
||||
"Connection is invalid": "Connecting is invalid",
|
||||
"Connection type is different": "Connection type is different",
|
||||
"Input Field Settings": "Input Field Settings",
|
||||
"My Apps": "My Apps",
|
||||
"Output Field Settings": "Output Field Settings"
|
||||
},
|
||||
"chat": {
|
||||
"Confirm to clear history": "Confirm to clear history?",
|
||||
"Exit Chat": "Exit",
|
||||
"History": "History",
|
||||
"New Chat": "New Chat",
|
||||
"You need to a chat app": "You need to a chat app"
|
||||
},
|
||||
"common": {
|
||||
"Add": "Add",
|
||||
"Cancel": "Cancel",
|
||||
"Collect": "Collect",
|
||||
"Copy": "Copy",
|
||||
"Course": "",
|
||||
"Delete": "Delete",
|
||||
"Filed is repeat": "Filed is repeated",
|
||||
"Filed is repeated": "",
|
||||
"Input": "Input",
|
||||
"Output": "Output"
|
||||
},
|
||||
"file": {
|
||||
"Click to download CSV template": "Click to download CSV template",
|
||||
"Drag and drop": "Drag and drop files here, or click",
|
||||
"If the imported file is garbled, please convert CSV to UTF-8 encoding format": "If the imported file is garbled, please convert CSV to UTF-8 encoding format",
|
||||
"Release the mouse to upload the file": "Release the mouse to upload the file",
|
||||
"select a document": "select a document",
|
||||
"support": "support {{fileExtension}} file",
|
||||
"upload error description": "Only upload multiple files or one folder at a time"
|
||||
},
|
||||
"home": {
|
||||
"AI Assistant": "AI Assistant",
|
||||
"AI Assistant Desc": "",
|
||||
"Advanced Settings": "",
|
||||
"Advanced Settings Desc": "",
|
||||
"Choice Debug": "Convenient Debugging",
|
||||
"Choice Debug Desc": "Search testing, reference modification, full conversation preview and many other debugging ways",
|
||||
"Choice Desc": "FastGPT follows the Apache License 2.0 open source protocol",
|
||||
"Choice Extension": "Infinite Extension",
|
||||
"Choice Extension Desc": "HTTP based extension, easy to achieve custom functions",
|
||||
"Choice Models": "Multiple Models",
|
||||
"Choice Models Desc": "",
|
||||
"Choice Open": "Open",
|
||||
"Choice Open Desc": "",
|
||||
"Choice QA": "QA Struceture",
|
||||
"Choice QA Desc": "The index is constructed with the structure of QA pairs, and ADAPTS to various scenarios such as Q&A and reading",
|
||||
"Choice Visual": "Visual workflow",
|
||||
"Choice Visual Desc": "Visualize modular operations, easily implement complex workflows, and make your AI no longer monolithic",
|
||||
"Community": "Community",
|
||||
"Dateset": "",
|
||||
"Dateset Desc": "",
|
||||
"Docs": "Docs",
|
||||
"FastGPT Ability": "FastGPT Ability",
|
||||
"FastGPT Desc": "FastGPT is a knowledgebase question answering system based on LLM large language model, which provides out-of-the-box data processing, model invocation and other capabilities. At the same time, workflow orchestration can be performed through Flow visualization to achieve complex Q&A scenarios!",
|
||||
"Features": "Features",
|
||||
"Footer Developer": "Developer",
|
||||
"Footer Docs": "Docs",
|
||||
"Footer FastGPT Cloud": "FastGPT Cloud",
|
||||
"Footer Feedback": "Feedback",
|
||||
"Footer Git": "Code",
|
||||
"Footer Product": "Product",
|
||||
"Footer Support": "Support",
|
||||
"Login": "Login",
|
||||
"Open": "",
|
||||
"OpenAPI": "OpenAPI",
|
||||
"OpenAPI Desc": "",
|
||||
"Quickly build AI question and answer library": "Quickly build AI question and answer library",
|
||||
"Start Now": "Start Now",
|
||||
"Visual AI orchestration": "Visual AI orchestration",
|
||||
"Why FastGPT": "",
|
||||
"desc": "",
|
||||
"slogan": ""
|
||||
},
|
||||
"navbar": {
|
||||
"Account": "Account",
|
||||
"Apps": "Apps",
|
||||
"Chat": "Chat",
|
||||
"Datasets": "DataSets",
|
||||
"Store": "Store",
|
||||
"Tools": "Tools"
|
||||
},
|
||||
"user": {
|
||||
"Account": "Account",
|
||||
"Application Name": "Application Name",
|
||||
"Avatar": "Avatar",
|
||||
"Balance": "Balance",
|
||||
"Bill Detail": "Bill Detail",
|
||||
"Change": "Change",
|
||||
"Notice": "Notice",
|
||||
"Old password is error": "Old password is error",
|
||||
"OpenAI Account Setting": "OpenAI Account Setting",
|
||||
"Password": "Password",
|
||||
"Pay": "Pay",
|
||||
"Personal Information": "Personal",
|
||||
"Recharge Record": "Recharge",
|
||||
"Replace": "Replace",
|
||||
"Set OpenAI Account Failed": "Set OpenAI account failed",
|
||||
"Sign Out": "Sign Out",
|
||||
"Source": "Source",
|
||||
"Time": "Time",
|
||||
"Total Amount": "Total Amount",
|
||||
"Update Password": "Update Password",
|
||||
"Update password failed": "Update password failed",
|
||||
"Update password succseful": "Update password succseful",
|
||||
"Usage Record": "Usage"
|
||||
}
|
||||
}
|
||||
121
client/public/locales/zh/common.json
Normal file
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"App": "应用",
|
||||
"Cancel": "取消",
|
||||
"Confirm": "确认",
|
||||
"Running": "运行中",
|
||||
"Warning": "提示",
|
||||
"app": {
|
||||
"Advance App TestTip": "当前应用为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键",
|
||||
"App Detail": "应用详情",
|
||||
"Confirm Del App Tip": "确认删除该应用及其所有聊天记录?",
|
||||
"Confirm Save App Tip": "该应用可能为高级编排模式,保存后将会覆盖高级编排配置,请确认!",
|
||||
"Connection is invalid": "连接无效",
|
||||
"Connection type is different": "连接的类型不一致",
|
||||
"Input Field Settings": "输入字段编辑",
|
||||
"My Apps": "我的应用",
|
||||
"Output Field Settings": "输出字段编辑"
|
||||
},
|
||||
"chat": {
|
||||
"Confirm to clear history": "确认清空该应用的聊天记录?",
|
||||
"Exit Chat": "退出聊天",
|
||||
"History": "记录",
|
||||
"New Chat": "新对话",
|
||||
"You need to a chat app": "你需要创建一个应用"
|
||||
},
|
||||
"common": {
|
||||
"Add": "添加",
|
||||
"Cancel": "取消",
|
||||
"Collect": "收藏",
|
||||
"Copy": "复制",
|
||||
"Delete": "删除",
|
||||
"Filed is repeat": "",
|
||||
"Filed is repeated": "字段重复了",
|
||||
"Input": "输入",
|
||||
"Output": "输出"
|
||||
},
|
||||
"file": {
|
||||
"Click to download CSV template": "点击下载 CSV 模板",
|
||||
"Drag and drop": "拖拽文件至此,或点击",
|
||||
"If the imported file is garbled, please convert CSV to UTF-8 encoding format": "如果导入文件乱码,请将 CSV 转成 UTF-8 编码格式",
|
||||
"Release the mouse to upload the file": "松开鼠标上传文件",
|
||||
"select a document": "选择文件",
|
||||
"support": "支持 {{fileExtension}} 文件",
|
||||
"upload error description": "单次只支持上传多个文件或者一个文件夹"
|
||||
},
|
||||
"home": {
|
||||
"AI Assistant": "AI 客服",
|
||||
"AI Assistant Desc": "无论对内还是对外,AI 将 24 小时为您的用户提供服务",
|
||||
"Advanced Settings": "高级编排",
|
||||
"Advanced Settings Desc": "基于 Flow 的流程编排模式,让你的 AI 轻松实现数据库查询、IO 操作、联网通信等扩展能力",
|
||||
"Choice Debug": "调试便捷",
|
||||
"Choice Debug Desc": "拥有搜索测试、引用修改、完整对话预览等多种调试途径",
|
||||
"Choice Desc": "",
|
||||
"Choice Extension": "无限扩展",
|
||||
"Choice Extension Desc": "基于 HTTP 实现扩展,轻松实现定制功能",
|
||||
"Choice Models": "支持多种模型",
|
||||
"Choice Models Desc": "支持 GPT、Claude、文心一言等多模型",
|
||||
"Choice Open": "更开放",
|
||||
"Choice Open Desc": "FastGPT 遵循 Apache License 2.0 开源协议",
|
||||
"Choice QA": "独特的 QA 结构",
|
||||
"Choice QA Desc": "采用 QA 对的结构构建索引,适应问答、阅读等多种场景",
|
||||
"Choice Visual": "可视化工作流",
|
||||
"Choice Visual Desc": "可视化模块操作,轻松实现复杂工作流,让你的 AI 不再单一",
|
||||
"Community": "社区",
|
||||
"Dateset": "自动数据预处理",
|
||||
"Dateset Desc": "提供手动输入、直接分段、LLM 自动处理和 CSV 等多种数据导入途径",
|
||||
"Docs": "文档",
|
||||
"FastGPT Ability": "FastGPT 能力",
|
||||
"FastGPT Desc": "FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景!",
|
||||
"Features": "特点",
|
||||
"Footer Developer": "开发者",
|
||||
"Footer Docs": "文档",
|
||||
"Footer FastGPT Cloud": "FastGPT 线上服务",
|
||||
"Footer Feedback": "反馈",
|
||||
"Footer Git": "源码",
|
||||
"Footer Product": "产品",
|
||||
"Footer Support": "支持",
|
||||
"Login": "登录",
|
||||
"Open": "",
|
||||
"OpenAPI": "OpenAPI",
|
||||
"OpenAPI Desc": "与 GPT API 一致的对外接口,助你轻松接入已有应用",
|
||||
"Quickly build AI question and answer library": "快速搭建 AI 问答系统",
|
||||
"Start Now": "立即开始",
|
||||
"Visual AI orchestration": "可视化 AI 编排",
|
||||
"Why FastGPT": "为什么选择 FastGPT",
|
||||
"desc": "基于 LLM 大模型的 AI 知识库问答平台",
|
||||
"slogan": "让 AI 更懂你的知识"
|
||||
},
|
||||
"navbar": {
|
||||
"Account": "账号",
|
||||
"Apps": "应用",
|
||||
"Chat": "聊天",
|
||||
"Datasets": "知识库",
|
||||
"Store": "应用市场",
|
||||
"Tools": "工具"
|
||||
},
|
||||
"user": {
|
||||
"Account": "账号",
|
||||
"Application Name": "应用名",
|
||||
"Avatar": "头像",
|
||||
"Balance": "余额",
|
||||
"Bill Detail": "账单详情",
|
||||
"Change": "变更",
|
||||
"Notice": "通知",
|
||||
"Old password is error": "旧密码错误",
|
||||
"OpenAI Account Setting": "OpenAI 账号配置",
|
||||
"Password": "密码",
|
||||
"Pay": "充值",
|
||||
"Personal Information": "个人信息",
|
||||
"Recharge Record": "充值记录",
|
||||
"Replace": "更换",
|
||||
"Set OpenAI Account Failed": "设置 OpenAI 账号异常",
|
||||
"Sign Out": "登出",
|
||||
"Source": "来源",
|
||||
"Time": "时间",
|
||||
"Total Amount": "总金额",
|
||||
"Update Password": "修改密码",
|
||||
"Update password failed": "修改密码异常",
|
||||
"Update password succseful": "修改密码成功",
|
||||
"Usage Record": "使用记录"
|
||||
}
|
||||
}
|
||||
53
client/src/api/app.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { GET, POST, DELETE, PUT } from './request';
|
||||
import type { AppSchema } from '@/types/mongoSchema';
|
||||
import type { AppListItemType, AppUpdateParams } from '@/types/app';
|
||||
import { RequestPaging } from '../types/index';
|
||||
import type { Props as CreateAppProps } from '@/pages/api/app/create';
|
||||
import { addDays } from 'date-fns';
|
||||
|
||||
/**
|
||||
* 获取模型列表
|
||||
*/
|
||||
export const getMyModels = () => GET<AppListItemType[]>('/app/myApps');
|
||||
|
||||
/**
|
||||
* 创建一个模型
|
||||
*/
|
||||
export const postCreateApp = (data: CreateAppProps) => POST<string>('/app/create', data);
|
||||
|
||||
/**
|
||||
* 根据 ID 删除模型
|
||||
*/
|
||||
export const delModelById = (id: string) => DELETE(`/app/del?appId=${id}`);
|
||||
|
||||
/**
|
||||
* 根据 ID 获取模型
|
||||
*/
|
||||
export const getModelById = (id: string) => GET<AppSchema>(`/app/detail?appId=${id}`);
|
||||
|
||||
/**
|
||||
* 根据 ID 更新模型
|
||||
*/
|
||||
export const putAppById = (id: string, data: AppUpdateParams) =>
|
||||
PUT(`/app/update?appId=${id}`, data);
|
||||
|
||||
/* 共享市场 */
|
||||
/**
|
||||
* 获取共享市场模型
|
||||
*/
|
||||
export const getShareModelList = (data: { searchText?: string } & RequestPaging) =>
|
||||
POST(`/app/share/getModels`, data);
|
||||
|
||||
/**
|
||||
* 收藏/取消收藏模型
|
||||
*/
|
||||
export const triggerModelCollection = (appId: string) =>
|
||||
POST<number>(`/app/share/collection?appId=${appId}`);
|
||||
|
||||
// ====================== data
|
||||
export const getAppTotalUsage = (data: { appId: string }) =>
|
||||
POST<{ date: String; total: number }[]>(`/app/data/totalUsage`, {
|
||||
...data,
|
||||
start: addDays(new Date(), -13),
|
||||
end: addDays(new Date(), 1)
|
||||
}).then((res) => (res.length === 0 ? [{ date: new Date(), total: 0 }] : res));
|
||||
76
client/src/api/chat.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { GET, POST, DELETE, PUT } from './request';
|
||||
import type { ChatHistoryItemType } from '@/types/chat';
|
||||
import type { InitChatResponse, InitShareChatResponse } from './response/chat';
|
||||
import { RequestPaging } from '../types/index';
|
||||
import type { OutLinkSchema } from '@/types/mongoSchema';
|
||||
import type { ShareChatEditType } from '@/types/app';
|
||||
import type { Props as UpdateHistoryProps } from '@/pages/api/chat/history/updateChatHistory';
|
||||
|
||||
/**
|
||||
* 获取初始化聊天内容
|
||||
*/
|
||||
export const getInitChatSiteInfo = (data: { appId: string; chatId?: string }) =>
|
||||
GET<InitChatResponse>(`/chat/init`, data);
|
||||
|
||||
/**
|
||||
* 获取历史记录
|
||||
*/
|
||||
export const getChatHistory = (data: RequestPaging & { appId?: string }) =>
|
||||
POST<ChatHistoryItemType[]>('/chat/history/getHistory', data);
|
||||
|
||||
/**
|
||||
* 删除一条历史记录
|
||||
*/
|
||||
export const delChatHistoryById = (chatId: string) => DELETE(`/chat/removeHistory`, { chatId });
|
||||
/**
|
||||
* clear all history by appid
|
||||
*/
|
||||
export const clearChatHistoryByAppId = (appId: string) => DELETE(`/chat/removeHistory`, { appId });
|
||||
|
||||
/**
|
||||
* update history quote status
|
||||
*/
|
||||
export const updateHistoryQuote = (params: {
|
||||
chatId: string;
|
||||
contentId: string;
|
||||
quoteId: string;
|
||||
sourceText: string;
|
||||
}) => PUT(`/chat/history/updateHistoryQuote`, params);
|
||||
|
||||
/**
|
||||
* 删除一句对话
|
||||
*/
|
||||
export const delChatRecordByIndex = (data: { chatId: string; contentId: string }) =>
|
||||
DELETE(`/chat/delChatRecordByContentId`, data);
|
||||
|
||||
/**
|
||||
* 修改历史记录: 标题/置顶
|
||||
*/
|
||||
export const putChatHistory = (data: UpdateHistoryProps) =>
|
||||
PUT('/chat/history/updateChatHistory', data);
|
||||
|
||||
/**
|
||||
* 初始化分享聊天
|
||||
*/
|
||||
export const initShareChatInfo = (data: { shareId: string }) =>
|
||||
GET<InitShareChatResponse>(`/chat/shareChat/init`, data);
|
||||
|
||||
/**
|
||||
* create a shareChat
|
||||
*/
|
||||
export const createShareChat = (
|
||||
data: ShareChatEditType & {
|
||||
appId: string;
|
||||
}
|
||||
) => POST<string>(`/chat/shareChat/create`, data);
|
||||
|
||||
/**
|
||||
* get shareChat
|
||||
*/
|
||||
export const getShareChatList = (appId: string) =>
|
||||
GET<OutLinkSchema[]>(`/chat/shareChat/list`, { appId });
|
||||
|
||||
/**
|
||||
* delete a shareChat
|
||||
*/
|
||||
export const delShareChatById = (id: string) => DELETE(`/chat/shareChat/delete?id=${id}`);
|
||||
115
client/src/api/fetch.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { sseResponseEventEnum, TaskResponseKeyEnum } from '@/constants/chat';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
import { parseStreamChunk, SSEParseData } from '@/utils/sse';
|
||||
import type { ChatHistoryItemResType } from '@/types/chat';
|
||||
import { StartChatFnProps } from '@/components/ChatBox';
|
||||
import { getToken } from '@/utils/user';
|
||||
|
||||
interface StreamFetchProps {
|
||||
url?: string;
|
||||
data: Record<string, any>;
|
||||
onMessage: StartChatFnProps['generatingMessage'];
|
||||
abortSignal: AbortController;
|
||||
}
|
||||
export const streamFetch = ({
|
||||
url = '/api/openapi/v1/chat/completions',
|
||||
data,
|
||||
onMessage,
|
||||
abortSignal
|
||||
}: StreamFetchProps) =>
|
||||
new Promise<{
|
||||
responseText: string;
|
||||
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType[];
|
||||
}>(async (resolve, reject) => {
|
||||
try {
|
||||
const response = await window.fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
token: getToken()
|
||||
},
|
||||
signal: abortSignal.signal,
|
||||
body: JSON.stringify({
|
||||
...data,
|
||||
detail: true,
|
||||
stream: true
|
||||
})
|
||||
});
|
||||
|
||||
if (!response?.body) {
|
||||
throw new Error('Request Error');
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader();
|
||||
|
||||
// response data
|
||||
let responseText = '';
|
||||
let errMsg = '';
|
||||
let responseData: ChatHistoryItemResType[] = [];
|
||||
|
||||
const parseData = new SSEParseData();
|
||||
|
||||
const read = async () => {
|
||||
try {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
if (response.status === 200 && !errMsg) {
|
||||
return resolve({
|
||||
responseText,
|
||||
responseData
|
||||
});
|
||||
} else {
|
||||
return reject({
|
||||
message: errMsg || '响应过程出现异常~',
|
||||
responseText
|
||||
});
|
||||
}
|
||||
}
|
||||
const chunkResponse = parseStreamChunk(value);
|
||||
|
||||
chunkResponse.forEach((item) => {
|
||||
// parse json data
|
||||
const { eventName, data } = parseData.parse(item);
|
||||
|
||||
if (!eventName || !data) return;
|
||||
|
||||
if (eventName === sseResponseEventEnum.answer && data !== '[DONE]') {
|
||||
const answer: string = data?.choices?.[0].delta.content || '';
|
||||
onMessage({ text: answer });
|
||||
responseText += answer;
|
||||
} else if (
|
||||
eventName === sseResponseEventEnum.moduleStatus &&
|
||||
data?.name &&
|
||||
data?.status
|
||||
) {
|
||||
onMessage(data);
|
||||
} else if (
|
||||
eventName === sseResponseEventEnum.appStreamResponse &&
|
||||
Array.isArray(data)
|
||||
) {
|
||||
responseData = data;
|
||||
} else if (eventName === sseResponseEventEnum.error) {
|
||||
errMsg = getErrText(data, '流响应错误');
|
||||
}
|
||||
});
|
||||
read();
|
||||
} catch (err: any) {
|
||||
if (err?.message === 'The user aborted a request.') {
|
||||
return resolve({
|
||||
responseText,
|
||||
responseData
|
||||
});
|
||||
}
|
||||
reject({
|
||||
responseText,
|
||||
message: getErrText(err, '请求异常')
|
||||
});
|
||||
}
|
||||
};
|
||||
read();
|
||||
} catch (err: any) {
|
||||
console.log(err, 'fetch error');
|
||||
|
||||
reject(getErrText(err, '请求异常'));
|
||||
}
|
||||
});
|
||||
16
client/src/api/openapi.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { GET, POST, DELETE } from './request';
|
||||
import { UserOpenApiKey } from '@/types/openapi';
|
||||
/**
|
||||
* crete a api key
|
||||
*/
|
||||
export const createAOpenApiKey = () => POST<string>('/openapi/postKey');
|
||||
|
||||
/**
|
||||
* get api keys
|
||||
*/
|
||||
export const getOpenApiKeys = () => GET<UserOpenApiKey[]>('/openapi/getKeys');
|
||||
|
||||
/**
|
||||
* delete api by id
|
||||
*/
|
||||
export const delOpenApiById = (id: string) => DELETE(`/openapi/delKey?id=${id}`);
|
||||
93
client/src/api/plugins/kb.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { GET, POST, PUT, DELETE } from '../request';
|
||||
import type { KbItemType, KbListItemType } from '@/types/plugin';
|
||||
import { RequestPaging } from '@/types/index';
|
||||
import { TrainingModeEnum } from '@/constants/plugin';
|
||||
import {
|
||||
Props as PushDataProps,
|
||||
Response as PushDateResponse
|
||||
} from '@/pages/api/openapi/kb/pushData';
|
||||
import {
|
||||
Props as SearchTestProps,
|
||||
Response as SearchTestResponse
|
||||
} from '@/pages/api/openapi/kb/searchTest';
|
||||
import { Response as KbDataItemType } from '@/pages/api/plugins/kb/data/getDataById';
|
||||
import { Props as UpdateDataProps } from '@/pages/api/openapi/kb/updateData';
|
||||
|
||||
export type KbUpdateParams = {
|
||||
id: string;
|
||||
name: string;
|
||||
tags: string;
|
||||
avatar: string;
|
||||
};
|
||||
|
||||
/* knowledge base */
|
||||
export const getKbList = () => GET<KbListItemType[]>(`/plugins/kb/list`);
|
||||
|
||||
export const getKbById = (id: string) => GET<KbItemType>(`/plugins/kb/detail?id=${id}`);
|
||||
|
||||
export const postCreateKb = (data: { name: string }) => POST<string>(`/plugins/kb/create`, data);
|
||||
|
||||
export const putKbById = (data: KbUpdateParams) => PUT(`/plugins/kb/update`, data);
|
||||
|
||||
export const delKbById = (id: string) => DELETE(`/plugins/kb/delete?id=${id}`);
|
||||
|
||||
/* kb data */
|
||||
type GetKbDataListProps = RequestPaging & {
|
||||
kbId: string;
|
||||
searchText: string;
|
||||
};
|
||||
export const getKbDataList = (data: GetKbDataListProps) =>
|
||||
POST(`/plugins/kb/data/getDataList`, data);
|
||||
|
||||
/**
|
||||
* 获取导出数据(不分页)
|
||||
*/
|
||||
export const getExportDataList = (kbId: string) =>
|
||||
GET<[string, string, string][]>(
|
||||
`/plugins/kb/data/exportModelData`,
|
||||
{ kbId },
|
||||
{
|
||||
timeout: 600000
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 获取模型正在拆分数据的数量
|
||||
*/
|
||||
export const getTrainingData = (data: { kbId: string; init: boolean }) =>
|
||||
POST<{
|
||||
qaListLen: number;
|
||||
vectorListLen: number;
|
||||
}>(`/plugins/kb/data/getTrainingData`, data);
|
||||
|
||||
export const getKbDataItemById = (dataId: string) =>
|
||||
GET<KbDataItemType>(`/plugins/kb/data/getDataById`, { dataId });
|
||||
|
||||
/**
|
||||
* 直接push数据
|
||||
*/
|
||||
export const postKbDataFromList = (data: PushDataProps) =>
|
||||
POST<PushDateResponse>(`/openapi/kb/pushData`, data);
|
||||
|
||||
/**
|
||||
* 更新一条数据
|
||||
*/
|
||||
export const putKbDataById = (data: UpdateDataProps) => PUT('/openapi/kb/updateData', data);
|
||||
/**
|
||||
* 删除一条知识库数据
|
||||
*/
|
||||
export const delOneKbDataByDataId = (dataId: string) =>
|
||||
DELETE(`/openapi/kb/delDataById?dataId=${dataId}`);
|
||||
|
||||
/**
|
||||
* 拆分数据
|
||||
*/
|
||||
export const postSplitData = (data: {
|
||||
kbId: string;
|
||||
chunks: string[];
|
||||
prompt: string;
|
||||
mode: `${TrainingModeEnum}`;
|
||||
}) => POST(`/openapi/text/pushData`, data);
|
||||
|
||||
export const searchText = (data: SearchTestProps) =>
|
||||
POST<SearchTestResponse>(`/openapi/kb/searchTest`, data);
|
||||
@@ -1,10 +1,11 @@
|
||||
import axios, { Method, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import { getToken, clearToken } from '@/utils/user';
|
||||
import { TOKEN_ERROR_CODE } from '@/constants/responseCode';
|
||||
import { clearToken, getToken } from '@/utils/user';
|
||||
import { TOKEN_ERROR_CODE } from '@/service/errorCode';
|
||||
|
||||
interface ConfigType {
|
||||
headers?: { [key: string]: string };
|
||||
hold?: boolean;
|
||||
timeout?: number;
|
||||
}
|
||||
interface ResponseDataType {
|
||||
code: number;
|
||||
@@ -17,7 +18,7 @@ interface ResponseDataType {
|
||||
*/
|
||||
function requestStart(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig {
|
||||
if (config.headers) {
|
||||
config.headers.Authorization = getToken();
|
||||
config.headers.token = getToken();
|
||||
}
|
||||
|
||||
return config;
|
||||
@@ -54,13 +55,16 @@ function responseError(err: any) {
|
||||
if (typeof err === 'string') {
|
||||
return Promise.reject({ message: err });
|
||||
}
|
||||
if (err.response) {
|
||||
// 有报错响应
|
||||
const res = err.response;
|
||||
if (res.data.code in TOKEN_ERROR_CODE) {
|
||||
clearToken();
|
||||
return Promise.reject({ message: 'token过期,重新登录' });
|
||||
}
|
||||
// 有报错响应
|
||||
if (err?.code in TOKEN_ERROR_CODE) {
|
||||
clearToken();
|
||||
window.location.replace(
|
||||
`/login?lastRoute=${encodeURIComponent(location.pathname + location.search)}`
|
||||
);
|
||||
return Promise.reject({ message: 'token过期,重新登录' });
|
||||
}
|
||||
if (err?.response?.data) {
|
||||
return Promise.reject(err?.response?.data);
|
||||
}
|
||||
return Promise.reject(err);
|
||||
}
|
||||
@@ -91,8 +95,8 @@ function request(url: string, data: any, config: ConfigType, method: Method): an
|
||||
baseURL: '/api',
|
||||
url,
|
||||
method,
|
||||
data: method === 'GET' ? null : data,
|
||||
params: method === 'GET' ? data : null, // get请求不携带data,params放在url上
|
||||
data: ['POST', 'PUT'].includes(method) ? data : null,
|
||||
params: !['POST', 'PUT'].includes(method) ? data : null,
|
||||
...config // 用户自定义配置,可以覆盖前面的配置
|
||||
})
|
||||
.then((res) => checkRes(res.data))
|
||||
@@ -118,6 +122,6 @@ export function PUT<T>(url: string, data = {}, config: ConfigType = {}): Promise
|
||||
return request(url, data, config, 'PUT');
|
||||
}
|
||||
|
||||
export function DELETE<T>(url: string, config: ConfigType = {}): Promise<T> {
|
||||
return request(url, {}, config, 'DELETE');
|
||||
export function DELETE<T>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
|
||||
return request(url, data, config, 'DELETE');
|
||||
}
|
||||
6
client/src/api/response/app.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import { AppListItemType } from '@/types/app';
|
||||
|
||||
export type AppListResponse = {
|
||||
myApps: AppListItemType[];
|
||||
myCollectionApps: AppListItemType[];
|
||||
};
|
||||
25
client/src/api/response/chat.d.ts
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { AppSchema } from '@/types/mongoSchema';
|
||||
import type { ChatItemType } from '@/types/chat';
|
||||
import { VariableItemType } from '@/types/app';
|
||||
|
||||
export interface InitChatResponse {
|
||||
chatId: string;
|
||||
appId: string;
|
||||
app: {
|
||||
variableModules?: VariableItemType[];
|
||||
welcomeText?: string;
|
||||
chatModels?: string[];
|
||||
name: string;
|
||||
avatar: string;
|
||||
intro: string;
|
||||
canUse?: boolean;
|
||||
};
|
||||
title: string;
|
||||
variables: Record<string, any>;
|
||||
history: ChatItemType[];
|
||||
}
|
||||
|
||||
export interface InitShareChatResponse {
|
||||
userAvatar: string;
|
||||
app: InitChatResponse['app'];
|
||||
}
|
||||
13
client/src/api/response/user.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { UserType } from '@/types/user';
|
||||
import type { PromotionRecordSchema } from '@/types/mongoSchema';
|
||||
export interface ResLogin {
|
||||
user: UserType;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface PromotionRecordType {
|
||||
_id: PromotionRecordSchema['_id'];
|
||||
type: PromotionRecordSchema['type'];
|
||||
createTime: PromotionRecordSchema['createTime'];
|
||||
amount: PromotionRecordSchema['amount'];
|
||||
}
|
||||
6
client/src/api/system.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { GET, POST, PUT } from './request';
|
||||
import type { InitDateResponse } from '@/pages/api/system/getInitData';
|
||||
|
||||
export const getInitData = () => GET<InitDateResponse>('/system/getInitData');
|
||||
|
||||
export const uploadImg = (base64Img: string) => POST<string>('/system/uploadImage', { base64Img });
|
||||
84
client/src/api/user.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { GET, POST, PUT } from './request';
|
||||
import { createHashPassword } from '@/utils/tools';
|
||||
import { ResLogin } from './response/user';
|
||||
import { UserAuthTypeEnum } from '@/constants/common';
|
||||
import { UserBillType, UserType, UserUpdateParams } from '@/types/user';
|
||||
import type { PagingData, RequestPaging } from '@/types';
|
||||
import { informSchema, PaySchema } from '@/types/mongoSchema';
|
||||
|
||||
export const sendAuthCode = (data: {
|
||||
username: string;
|
||||
type: `${UserAuthTypeEnum}`;
|
||||
googleToken: string;
|
||||
}) => POST('/user/sendAuthCode', data);
|
||||
|
||||
export const getTokenLogin = () => GET<UserType>('/user/account/tokenLogin');
|
||||
export const gitLogin = (code: string) => GET<ResLogin>('/user/account/gitLogin', { code });
|
||||
|
||||
export const postRegister = ({
|
||||
username,
|
||||
password,
|
||||
code,
|
||||
inviterId
|
||||
}: {
|
||||
username: string;
|
||||
code: string;
|
||||
password: string;
|
||||
inviterId: string;
|
||||
}) =>
|
||||
POST<ResLogin>('/user/account/register', {
|
||||
username,
|
||||
code,
|
||||
inviterId,
|
||||
password: createHashPassword(password)
|
||||
});
|
||||
|
||||
export const postFindPassword = ({
|
||||
username,
|
||||
code,
|
||||
password
|
||||
}: {
|
||||
username: string;
|
||||
code: string;
|
||||
password: string;
|
||||
}) =>
|
||||
POST<ResLogin>('/user/account/updatePasswordByCode', {
|
||||
username,
|
||||
code,
|
||||
password: createHashPassword(password)
|
||||
});
|
||||
|
||||
export const updatePasswordByOld = ({ oldPsw, newPsw }: { oldPsw: string; newPsw: string }) =>
|
||||
POST('/user/account/updatePasswordByOld', {
|
||||
oldPsw: createHashPassword(oldPsw),
|
||||
newPsw: createHashPassword(newPsw)
|
||||
});
|
||||
|
||||
export const postLogin = ({ username, password }: { username: string; password: string }) =>
|
||||
POST<ResLogin>('/user/account/loginByPassword', {
|
||||
username,
|
||||
password: createHashPassword(password)
|
||||
});
|
||||
|
||||
export const loginOut = () => GET('/user/account/loginout');
|
||||
|
||||
export const putUserInfo = (data: UserUpdateParams) => PUT('/user/account/update', data);
|
||||
|
||||
export const getUserBills = (data: RequestPaging) =>
|
||||
POST<PagingData<UserBillType>>(`/user/getBill`, data);
|
||||
|
||||
export const getPayOrders = () => GET<PaySchema[]>(`/user/getPayOrders`);
|
||||
|
||||
export const getPayCode = (amount: number) =>
|
||||
GET<{
|
||||
codeUrl: string;
|
||||
payId: string;
|
||||
}>(`/user/getPayCode?amount=${amount}`);
|
||||
|
||||
export const checkPayResult = (payId: string) => GET<number>(`/user/checkPayResult?payId=${payId}`);
|
||||
|
||||
export const getInforms = (data: RequestPaging) =>
|
||||
POST<PagingData<informSchema>>(`/user/inform/list`, data);
|
||||
|
||||
export const getUnreadCount = () => GET<number>(`/user/inform/countUnread`);
|
||||
export const readInform = (id: string) => GET(`/user/inform/read`, { id });
|
||||
143
client/src/components/APIKeyModal/index.tsx
Normal file
@@ -0,0 +1,143 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
ModalFooter,
|
||||
ModalBody,
|
||||
Table,
|
||||
Thead,
|
||||
Tbody,
|
||||
Tr,
|
||||
Th,
|
||||
Td,
|
||||
TableContainer,
|
||||
IconButton
|
||||
} from '@chakra-ui/react';
|
||||
import { getOpenApiKeys, createAOpenApiKey, delOpenApiById } from '@/api/openapi';
|
||||
import { useQuery, useMutation } from '@tanstack/react-query';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import dayjs from 'dayjs';
|
||||
import { AddIcon, DeleteIcon } from '@chakra-ui/icons';
|
||||
import { getErrText, useCopyData } from '@/utils/tools';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import MyIcon from '../Icon';
|
||||
import MyModal from '../MyModal';
|
||||
|
||||
const APIKeyModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const { Loading } = useLoading();
|
||||
const { toast } = useToast();
|
||||
const {
|
||||
data: apiKeys = [],
|
||||
isLoading: isGetting,
|
||||
refetch
|
||||
} = useQuery(['getOpenApiKeys'], getOpenApiKeys);
|
||||
const [apiKey, setApiKey] = useState('');
|
||||
const { copyData } = useCopyData();
|
||||
|
||||
const { mutate: onclickCreateApiKey, isLoading: isCreating } = useMutation({
|
||||
mutationFn: () => createAOpenApiKey(),
|
||||
onSuccess(res) {
|
||||
setApiKey(res);
|
||||
refetch();
|
||||
},
|
||||
onError(err) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(err)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const { mutate: onclickRemove, isLoading: isDeleting } = useMutation({
|
||||
mutationFn: async (id: string) => delOpenApiById(id),
|
||||
onSuccess() {
|
||||
refetch();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<MyModal isOpen onClose={onClose} w={'600px'}>
|
||||
<Box py={3} px={5}>
|
||||
<Box fontWeight={'bold'} fontSize={'2xl'}>
|
||||
API 秘钥管理
|
||||
</Box>
|
||||
<Box fontSize={'sm'} color={'myGray.600'}>
|
||||
如果你不想 API 秘钥被滥用,请勿将秘钥直接放置在前端使用~
|
||||
</Box>
|
||||
</Box>
|
||||
<ModalBody minH={'300px'} maxH={['70vh', '500px']} overflow={'overlay'}>
|
||||
<TableContainer mt={2} position={'relative'}>
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Api Key</Th>
|
||||
<Th>创建时间</Th>
|
||||
<Th>最后一次使用时间</Th>
|
||||
<Th />
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody fontSize={'sm'}>
|
||||
{apiKeys.map(({ id, apiKey, createTime, lastUsedTime }) => (
|
||||
<Tr key={id}>
|
||||
<Td>{apiKey}</Td>
|
||||
<Td>{dayjs(createTime).format('YYYY/MM/DD HH:mm:ss')}</Td>
|
||||
<Td>
|
||||
{lastUsedTime
|
||||
? dayjs(lastUsedTime).format('YYYY/MM/DD HH:mm:ss')
|
||||
: '没有使用过'}
|
||||
</Td>
|
||||
<Td>
|
||||
<IconButton
|
||||
icon={<DeleteIcon />}
|
||||
size={'xs'}
|
||||
aria-label={'delete'}
|
||||
variant={'base'}
|
||||
colorScheme={'gray'}
|
||||
onClick={() => onclickRemove(id)}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
variant="base"
|
||||
leftIcon={<AddIcon color={'myGray.600'} fontSize={'sm'} />}
|
||||
onClick={() => onclickCreateApiKey()}
|
||||
>
|
||||
新建秘钥
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
<Loading loading={isGetting || isCreating || isDeleting} fixed={false} />
|
||||
<MyModal isOpen={!!apiKey} w={'400px'} onClose={() => setApiKey('')}>
|
||||
<Box py={3} px={5}>
|
||||
<Box fontWeight={'bold'} fontSize={'2xl'}>
|
||||
新的 API 秘钥
|
||||
</Box>
|
||||
<Box fontSize={'sm'} color={'myGray.600'}>
|
||||
请保管好你的秘钥,秘钥不会再次展示~
|
||||
</Box>
|
||||
</Box>
|
||||
<ModalBody>
|
||||
<Flex bg={'myGray.100'} px={3} py={2} cursor={'pointer'} onClick={() => copyData(apiKey)}>
|
||||
<Box flex={1}>{apiKey}</Box>
|
||||
<MyIcon name={'copy'} w={'16px'}></MyIcon>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant="base" onClick={() => setApiKey('')}>
|
||||
好的
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default APIKeyModal;
|
||||
22
client/src/components/Avatar/index.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Image } from '@chakra-ui/react';
|
||||
import type { ImageProps } from '@chakra-ui/react';
|
||||
import { LOGO_ICON } from '@/constants/chat';
|
||||
|
||||
const Avatar = ({ w = '30px', ...props }: ImageProps) => {
|
||||
return (
|
||||
<Image
|
||||
fallbackSrc={LOGO_ICON}
|
||||
fallbackStrategy={'onError'}
|
||||
borderRadius={'50%'}
|
||||
objectFit={'cover'}
|
||||
alt=""
|
||||
w={w}
|
||||
h={w}
|
||||
p={'1px'}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Avatar;
|
||||
42
client/src/components/Badge/index.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
|
||||
const Badge = ({
|
||||
children,
|
||||
isDot = false,
|
||||
max = 99,
|
||||
count = 0
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
isDot?: boolean;
|
||||
max?: number;
|
||||
count?: number;
|
||||
}) => {
|
||||
return (
|
||||
<Box position={'relative'}>
|
||||
{children}
|
||||
{count > 0 && (
|
||||
<Box position={'absolute'} right={0} top={0} transform={'translate(70%,-50%)'}>
|
||||
{isDot ? (
|
||||
<Box w={'5px'} h={'5px'} bg={'myRead.600'} borderRadius={'20px'}></Box>
|
||||
) : (
|
||||
<Box
|
||||
color={'white'}
|
||||
bg={'myRead.600'}
|
||||
lineHeight={0.9}
|
||||
borderRadius={'100px'}
|
||||
px={'4px'}
|
||||
py={'2px'}
|
||||
fontSize={'12px'}
|
||||
border={'1px solid white'}
|
||||
>
|
||||
{count > max ? `${max}+` : count}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Badge;
|
||||
43
client/src/components/ChatBox/ContextModal.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import { ModalBody, Box, useTheme } from '@chakra-ui/react';
|
||||
import { ChatItemType } from '@/types/chat';
|
||||
import MyModal from '../MyModal';
|
||||
|
||||
const ContextModal = ({
|
||||
context = [],
|
||||
onClose
|
||||
}: {
|
||||
context: ChatItemType[];
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
title={`完整对话记录(${context.length}条)`}
|
||||
h={['90vh', '80vh']}
|
||||
minW={['90vw', '600px']}
|
||||
isCentered
|
||||
>
|
||||
<ModalBody pt={0} whiteSpace={'pre-wrap'} textAlign={'justify'} fontSize={'sm'}>
|
||||
{context.map((item, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
p={2}
|
||||
borderRadius={'lg'}
|
||||
border={theme.borders.base}
|
||||
_notLast={{ mb: 2 }}
|
||||
position={'relative'}
|
||||
>
|
||||
<Box fontWeight={'bold'}>{item.obj}</Box>
|
||||
<Box>{item.value}</Box>
|
||||
</Box>
|
||||
))}
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContextModal;
|
||||
137
client/src/components/ChatBox/QuoteModal.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { ModalBody, ModalCloseButton, ModalHeader, Box, useTheme } from '@chakra-ui/react';
|
||||
import { getKbDataItemById } from '@/api/plugins/kb';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
import { QuoteItemType } from '@/types/chat';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import InputDataModal from '@/pages/kb/detail/components/InputDataModal';
|
||||
import MyModal from '../MyModal';
|
||||
|
||||
const QuoteModal = ({
|
||||
onUpdateQuote,
|
||||
rawSearch = [],
|
||||
onClose
|
||||
}: {
|
||||
onUpdateQuote: (quoteId: string, sourceText: string) => Promise<void>;
|
||||
rawSearch: QuoteItemType[];
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const { toast } = useToast();
|
||||
const { setIsLoading, Loading } = useLoading();
|
||||
const [editDataItem, setEditDataItem] = useState<{
|
||||
kbId: string;
|
||||
dataId: string;
|
||||
a: string;
|
||||
q: string;
|
||||
}>();
|
||||
|
||||
/**
|
||||
* click edit, get new kbDataItem
|
||||
*/
|
||||
const onclickEdit = useCallback(
|
||||
async (item: QuoteItemType) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const data = (await getKbDataItemById(item.id)) as QuoteItemType;
|
||||
|
||||
if (!data) {
|
||||
onUpdateQuote(item.id, '已删除');
|
||||
throw new Error('该数据已被删除');
|
||||
}
|
||||
|
||||
setEditDataItem({
|
||||
kbId: data.kb_id,
|
||||
dataId: data.id,
|
||||
q: data.q,
|
||||
a: data.a
|
||||
});
|
||||
} catch (err) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(err)
|
||||
});
|
||||
}
|
||||
setIsLoading(false);
|
||||
},
|
||||
[setIsLoading, toast, onUpdateQuote]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MyModal
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
h={['90vh', '80vh']}
|
||||
isCentered
|
||||
minW={['90vw', '600px']}
|
||||
title={
|
||||
<>
|
||||
知识库引用({rawSearch.length}条)
|
||||
<Box fontSize={['xs', 'sm']} fontWeight={'normal'}>
|
||||
注意: 修改知识库内容成功后,此处不会显示变更情况。点击编辑后,会显示知识库最新的内容。
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<ModalBody pt={0} whiteSpace={'pre-wrap'} textAlign={'justify'} fontSize={'sm'}>
|
||||
{rawSearch.map((item) => (
|
||||
<Box
|
||||
key={item.id}
|
||||
flex={'1 0 0'}
|
||||
p={2}
|
||||
borderRadius={'lg'}
|
||||
border={theme.borders.base}
|
||||
_notLast={{ mb: 2 }}
|
||||
position={'relative'}
|
||||
_hover={{ '& .edit': { display: 'flex' } }}
|
||||
>
|
||||
{item.source && <Box color={'myGray.600'}>({item.source})</Box>}
|
||||
<Box>{item.q}</Box>
|
||||
<Box>{item.a}</Box>
|
||||
<Box
|
||||
className="edit"
|
||||
display={'none'}
|
||||
position={'absolute'}
|
||||
right={0}
|
||||
top={0}
|
||||
bottom={0}
|
||||
w={'40px'}
|
||||
bg={'rgba(255,255,255,0.9)'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
boxShadow={'-10px 0 10px rgba(255,255,255,1)'}
|
||||
>
|
||||
<MyIcon
|
||||
name={'edit'}
|
||||
w={'18px'}
|
||||
h={'18px'}
|
||||
cursor={'pointer'}
|
||||
color={'myGray.600'}
|
||||
_hover={{
|
||||
color: 'myBlue.700'
|
||||
}}
|
||||
onClick={() => onclickEdit(item)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
</ModalBody>
|
||||
<Loading fixed={false} />
|
||||
</MyModal>
|
||||
{editDataItem && (
|
||||
<InputDataModal
|
||||
onClose={() => setEditDataItem(undefined)}
|
||||
onSuccess={() => onUpdateQuote(editDataItem.dataId, '手动修改')}
|
||||
onDelete={() => onUpdateQuote(editDataItem.dataId, '已删除')}
|
||||
kbId={editDataItem.kbId}
|
||||
defaultValues={editDataItem}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default QuoteModal;
|
||||
94
client/src/components/ChatBox/ResponseDetailModal.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { ChatModuleEnum } from '@/constants/chat';
|
||||
import { ChatHistoryItemResType, ChatItemType, QuoteItemType } from '@/types/chat';
|
||||
import { Flex, BoxProps } from '@chakra-ui/react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Tag from '../Tag';
|
||||
import MyTooltip from '../MyTooltip';
|
||||
const QuoteModal = dynamic(() => import('./QuoteModal'), { ssr: false });
|
||||
const ContextModal = dynamic(() => import('./ContextModal'), { ssr: false });
|
||||
|
||||
const ResponseDetailModal = ({
|
||||
chatId,
|
||||
contentId,
|
||||
responseData = []
|
||||
}: {
|
||||
chatId?: string;
|
||||
contentId?: string;
|
||||
responseData?: ChatHistoryItemResType[];
|
||||
}) => {
|
||||
const [quoteModalData, setQuoteModalData] = useState<QuoteItemType[]>();
|
||||
const [contextModalData, setContextModalData] = useState<ChatItemType[]>();
|
||||
|
||||
const {
|
||||
tokens = 0,
|
||||
quoteList = [],
|
||||
completeMessages = []
|
||||
} = useMemo(() => {
|
||||
const chatData = responseData.find((item) => item.moduleName === ChatModuleEnum.AIChat);
|
||||
if (!chatData) return {};
|
||||
return {
|
||||
tokens: chatData.tokens,
|
||||
quoteList: chatData.quoteList,
|
||||
completeMessages: chatData.completeMessages
|
||||
};
|
||||
}, [responseData]);
|
||||
|
||||
const isEmpty = useMemo(
|
||||
() => quoteList.length === 0 && completeMessages.length === 0 && tokens === 0,
|
||||
[completeMessages.length, quoteList.length, tokens]
|
||||
);
|
||||
|
||||
const updateQuote = useCallback(async (quoteId: string, sourceText: string) => {}, []);
|
||||
|
||||
const TagStyles: BoxProps = {
|
||||
mr: 2,
|
||||
bg: 'transparent'
|
||||
};
|
||||
|
||||
return isEmpty ? null : (
|
||||
<Flex alignItems={'center'} mt={2} flexWrap={'wrap'}>
|
||||
{quoteList.length > 0 && (
|
||||
<MyTooltip label="查看引用">
|
||||
<Tag
|
||||
colorSchema="blue"
|
||||
cursor={'pointer'}
|
||||
{...TagStyles}
|
||||
onClick={() => setQuoteModalData(quoteList)}
|
||||
>
|
||||
{quoteList.length}条引用
|
||||
</Tag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
{completeMessages.length > 0 && (
|
||||
<MyTooltip label={'点击查看完整对话记录'}>
|
||||
<Tag
|
||||
colorSchema="green"
|
||||
cursor={'pointer'}
|
||||
{...TagStyles}
|
||||
onClick={() => setContextModalData(completeMessages)}
|
||||
>
|
||||
{completeMessages.length}条上下文
|
||||
</Tag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
{tokens > 0 && (
|
||||
<Tag colorSchema="gray" cursor={'default'} {...TagStyles}>
|
||||
{tokens}tokens
|
||||
</Tag>
|
||||
)}
|
||||
{!!quoteModalData && (
|
||||
<QuoteModal
|
||||
rawSearch={quoteModalData}
|
||||
onUpdateQuote={updateQuote}
|
||||
onClose={() => setQuoteModalData(undefined)}
|
||||
/>
|
||||
)}
|
||||
{!!contextModalData && (
|
||||
<ContextModal context={contextModalData} onClose={() => setContextModalData(undefined)} />
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResponseDetailModal;
|
||||
43
client/src/components/ChatBox/index.module.scss
Normal file
@@ -0,0 +1,43 @@
|
||||
.stopIcon {
|
||||
animation: zoomStopIcon 0.4s infinite alternate;
|
||||
}
|
||||
@keyframes zoomStopIcon {
|
||||
0% {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
.newChat {
|
||||
.modelListContainer {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.modelList {
|
||||
border-radius: 6px;
|
||||
}
|
||||
&:hover {
|
||||
.modelListContainer {
|
||||
height: 60vh;
|
||||
}
|
||||
.modelList {
|
||||
box-shadow: 0 0 5px rgba($color: #000000, $alpha: 0.05);
|
||||
border: 1px solid #dee0e2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.statusAnimation {
|
||||
animation: statusBox 0.8s linear infinite alternate;
|
||||
}
|
||||
@keyframes statusBox {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0.11;
|
||||
}
|
||||
}
|
||||
857
client/src/components/ChatBox/index.tsx
Normal file
@@ -0,0 +1,857 @@
|
||||
import React, {
|
||||
useCallback,
|
||||
useRef,
|
||||
useState,
|
||||
useMemo,
|
||||
forwardRef,
|
||||
useImperativeHandle,
|
||||
ForwardedRef,
|
||||
useEffect
|
||||
} from 'react';
|
||||
import { throttle } from 'lodash';
|
||||
import {
|
||||
ChatHistoryItemResType,
|
||||
ChatItemType,
|
||||
ChatSiteItemType,
|
||||
ExportChatType
|
||||
} from '@/types/chat';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import {
|
||||
useCopyData,
|
||||
voiceBroadcast,
|
||||
cancelBroadcast,
|
||||
hasVoiceApi,
|
||||
getErrText
|
||||
} from '@/utils/tools';
|
||||
import { Box, Card, Flex, Input, Textarea, Button, useTheme, BoxProps } from '@chakra-ui/react';
|
||||
import { feConfigs } from '@/store/static';
|
||||
import { Types } from 'mongoose';
|
||||
import { EventNameEnum } from '../Markdown/constant';
|
||||
|
||||
import { adaptChatItem_openAI } from '@/utils/plugin/openai';
|
||||
import { useMarkdown } from '@/hooks/useMarkdown';
|
||||
import { VariableItemType } from '@/types/app';
|
||||
import { VariableInputEnum } from '@/constants/app';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { MessageItemType } from '@/pages/api/openapi/v1/chat/completions';
|
||||
import { fileDownload } from '@/utils/file';
|
||||
import { htmlTemplate } from '@/constants/common';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { TaskResponseKeyEnum, getDefaultChatVariables } from '@/constants/chat';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import MyIcon from '@/components/Icon';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import MySelect from '@/components/Select';
|
||||
import MyTooltip from '../MyTooltip';
|
||||
import dynamic from 'next/dynamic';
|
||||
const ResponseDetailModal = dynamic(() => import('./ResponseDetailModal'));
|
||||
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const textareaMinH = '22px';
|
||||
type generatingMessageProps = { text?: string; name?: string; status?: 'running' | 'finish' };
|
||||
export type StartChatFnProps = {
|
||||
messages: MessageItemType[];
|
||||
controller: AbortController;
|
||||
variables: Record<string, any>;
|
||||
generatingMessage: (e: generatingMessageProps) => void;
|
||||
};
|
||||
|
||||
export type ComponentRef = {
|
||||
getChatHistory: () => ChatSiteItemType[];
|
||||
resetVariables: (data?: Record<string, any>) => void;
|
||||
resetHistory: (chatId: ChatSiteItemType[]) => void;
|
||||
scrollToBottom: (behavior?: 'smooth' | 'auto') => void;
|
||||
};
|
||||
|
||||
const VariableLabel = ({
|
||||
required = false,
|
||||
children
|
||||
}: {
|
||||
required?: boolean;
|
||||
children: React.ReactNode | string;
|
||||
}) => (
|
||||
<Box as={'label'} display={'inline-block'} position={'relative'} mb={1}>
|
||||
{children}
|
||||
{required && (
|
||||
<Box position={'absolute'} top={'-2px'} right={'-10px'} color={'red.500'} fontWeight={'bold'}>
|
||||
*
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
const Empty = () => {
|
||||
const { data: chatProblem } = useMarkdown({ url: '/chatProblem.md' });
|
||||
const { data: versionIntro } = useMarkdown({ url: '/versionIntro.md' });
|
||||
|
||||
return (
|
||||
<Box pt={6} w={'85%'} maxW={'600px'} m={'auto'} alignItems={'center'} justifyContent={'center'}>
|
||||
{/* version intro */}
|
||||
<Card p={4} mb={10} minH={'200px'}>
|
||||
<Markdown source={versionIntro} />
|
||||
</Card>
|
||||
<Card p={4} minH={'600px'}>
|
||||
<Markdown source={chatProblem} />
|
||||
</Card>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const ChatAvatar = ({ src, type }: { src?: string; type: 'Human' | 'AI' }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Box
|
||||
w={['28px', '34px']}
|
||||
h={['28px', '34px']}
|
||||
p={'2px'}
|
||||
borderRadius={'lg'}
|
||||
border={theme.borders.base}
|
||||
boxShadow={'0 0 5px rgba(0,0,0,0.1)'}
|
||||
bg={type === 'Human' ? 'white' : 'myBlue.100'}
|
||||
>
|
||||
<Avatar src={src} w={'100%'} h={'100%'} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const ChatBox = (
|
||||
{
|
||||
showEmptyIntro = false,
|
||||
chatId,
|
||||
appAvatar,
|
||||
userAvatar,
|
||||
variableModules,
|
||||
welcomeText,
|
||||
onUpdateVariable,
|
||||
onStartChat,
|
||||
onDelMessage
|
||||
}: {
|
||||
showEmptyIntro?: boolean;
|
||||
chatId?: string;
|
||||
appAvatar?: string;
|
||||
userAvatar?: string;
|
||||
variableModules?: VariableItemType[];
|
||||
welcomeText?: string;
|
||||
onUpdateVariable?: (e: Record<string, any>) => void;
|
||||
onStartChat: (e: StartChatFnProps) => Promise<{
|
||||
responseText: string;
|
||||
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType[];
|
||||
}>;
|
||||
onDelMessage?: (e: { contentId?: string; index: number }) => void;
|
||||
},
|
||||
ref: ForwardedRef<ComponentRef>
|
||||
) => {
|
||||
const ChatBoxRef = useRef<HTMLDivElement>(null);
|
||||
const theme = useTheme();
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const { copyData } = useCopyData();
|
||||
const { toast } = useToast();
|
||||
const { isPc } = useGlobalStore();
|
||||
const TextareaDom = useRef<HTMLTextAreaElement>(null);
|
||||
const controller = useRef(new AbortController());
|
||||
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [variables, setVariables] = useState<Record<string, any>>({});
|
||||
const [chatHistory, setChatHistory] = useState<ChatSiteItemType[]>([]);
|
||||
|
||||
const isChatting = useMemo(
|
||||
() =>
|
||||
chatHistory[chatHistory.length - 1] &&
|
||||
chatHistory[chatHistory.length - 1]?.status !== 'finish',
|
||||
[chatHistory]
|
||||
);
|
||||
const variableIsFinish = useMemo(() => {
|
||||
if (!variableModules || chatHistory.length > 0) return true;
|
||||
|
||||
for (let i = 0; i < variableModules.length; i++) {
|
||||
const item = variableModules[i];
|
||||
if (item.required && !variables[item.key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}, [chatHistory.length, variableModules, variables]);
|
||||
|
||||
const { register, reset, getValues, setValue, handleSubmit } = useForm<Record<string, any>>({
|
||||
defaultValues: variables
|
||||
});
|
||||
|
||||
// 滚动到底部
|
||||
const scrollToBottom = useCallback(
|
||||
(behavior: 'smooth' | 'auto' = 'smooth') => {
|
||||
if (!ChatBoxRef.current) return;
|
||||
ChatBoxRef.current.scrollTo({
|
||||
top: ChatBoxRef.current.scrollHeight,
|
||||
behavior
|
||||
});
|
||||
},
|
||||
[ChatBoxRef]
|
||||
);
|
||||
// 聊天信息生成中……获取当前滚动条位置,判断是否需要滚动到底部
|
||||
const generatingScroll = useCallback(
|
||||
throttle(() => {
|
||||
if (!ChatBoxRef.current) return;
|
||||
const isBottom =
|
||||
ChatBoxRef.current.scrollTop + ChatBoxRef.current.clientHeight + 150 >=
|
||||
ChatBoxRef.current.scrollHeight;
|
||||
|
||||
isBottom && scrollToBottom('auto');
|
||||
}, 100),
|
||||
[]
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const generatingMessage = useCallback(
|
||||
({ text = '', status, name }: generatingMessageProps) => {
|
||||
setChatHistory((state) =>
|
||||
state.map((item, index) => {
|
||||
if (index !== state.length - 1) return item;
|
||||
return {
|
||||
...item,
|
||||
...(text
|
||||
? {
|
||||
value: item.value + text
|
||||
}
|
||||
: {}),
|
||||
...(status && name
|
||||
? {
|
||||
status,
|
||||
moduleName: name
|
||||
}
|
||||
: {})
|
||||
};
|
||||
})
|
||||
);
|
||||
generatingScroll();
|
||||
},
|
||||
[generatingScroll, setChatHistory]
|
||||
);
|
||||
|
||||
// 复制内容
|
||||
const onclickCopy = useCallback(
|
||||
(value: string) => {
|
||||
const val = value.replace(/\n+/g, '\n');
|
||||
copyData(val);
|
||||
},
|
||||
[copyData]
|
||||
);
|
||||
|
||||
// 重置输入内容
|
||||
const resetInputVal = useCallback((val: string) => {
|
||||
if (!TextareaDom.current) return;
|
||||
|
||||
setTimeout(() => {
|
||||
/* 回到最小高度 */
|
||||
if (TextareaDom.current) {
|
||||
TextareaDom.current.value = val;
|
||||
TextareaDom.current.style.height =
|
||||
val === '' ? textareaMinH : `${TextareaDom.current.scrollHeight}px`;
|
||||
}
|
||||
}, 100);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* user confirm send prompt
|
||||
*/
|
||||
const sendPrompt = useCallback(
|
||||
async (variables: Record<string, any> = {}, inputVal = '') => {
|
||||
if (isChatting) {
|
||||
toast({
|
||||
title: '正在聊天中...请等待结束',
|
||||
status: 'warning'
|
||||
});
|
||||
return;
|
||||
}
|
||||
// get input value
|
||||
const val = inputVal.trim().replace(/\n\s*/g, '\n');
|
||||
|
||||
if (!val) {
|
||||
toast({
|
||||
title: '内容为空',
|
||||
status: 'warning'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const newChatList: ChatSiteItemType[] = [
|
||||
...chatHistory,
|
||||
{
|
||||
_id: String(new Types.ObjectId()),
|
||||
obj: 'Human',
|
||||
value: val,
|
||||
status: 'finish'
|
||||
},
|
||||
{
|
||||
_id: String(new Types.ObjectId()),
|
||||
obj: 'AI',
|
||||
value: '',
|
||||
status: 'loading'
|
||||
}
|
||||
];
|
||||
|
||||
// 插入内容
|
||||
setChatHistory(newChatList);
|
||||
|
||||
// 清空输入内容
|
||||
resetInputVal('');
|
||||
setTimeout(() => {
|
||||
scrollToBottom();
|
||||
}, 100);
|
||||
|
||||
try {
|
||||
// create abort obj
|
||||
const abortSignal = new AbortController();
|
||||
controller.current = abortSignal;
|
||||
|
||||
const messages = adaptChatItem_openAI({ messages: newChatList, reserveId: true });
|
||||
|
||||
const { responseData } = await onStartChat({
|
||||
messages,
|
||||
controller: abortSignal,
|
||||
generatingMessage,
|
||||
variables: {
|
||||
...getDefaultChatVariables(),
|
||||
...variables
|
||||
}
|
||||
});
|
||||
|
||||
// set finish status
|
||||
setChatHistory((state) =>
|
||||
state.map((item, index) => {
|
||||
if (index !== state.length - 1) return item;
|
||||
return {
|
||||
...item,
|
||||
status: 'finish',
|
||||
responseData
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
generatingScroll();
|
||||
isPc && TextareaDom.current?.focus();
|
||||
}, 100);
|
||||
} catch (err: any) {
|
||||
toast({
|
||||
title: getErrText(err, '聊天出错了~'),
|
||||
status: 'error',
|
||||
duration: 5000,
|
||||
isClosable: true
|
||||
});
|
||||
|
||||
if (!err?.responseText) {
|
||||
resetInputVal(inputVal);
|
||||
setChatHistory(newChatList.slice(0, newChatList.length - 2));
|
||||
}
|
||||
|
||||
// set finish status
|
||||
setChatHistory((state) =>
|
||||
state.map((item, index) => {
|
||||
if (index !== state.length - 1) return item;
|
||||
return {
|
||||
...item,
|
||||
status: 'finish'
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
[
|
||||
isChatting,
|
||||
chatHistory,
|
||||
resetInputVal,
|
||||
toast,
|
||||
scrollToBottom,
|
||||
onStartChat,
|
||||
generatingMessage,
|
||||
generatingScroll,
|
||||
isPc
|
||||
]
|
||||
);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getChatHistory: () => chatHistory,
|
||||
resetVariables(e) {
|
||||
const defaultVal: Record<string, any> = {};
|
||||
variableModules?.forEach((item) => {
|
||||
defaultVal[item.key] = '';
|
||||
});
|
||||
|
||||
reset(e || defaultVal);
|
||||
setVariables(e || defaultVal);
|
||||
},
|
||||
resetHistory(e) {
|
||||
setChatHistory(e);
|
||||
},
|
||||
scrollToBottom
|
||||
}));
|
||||
|
||||
const controlIconStyle = {
|
||||
w: '14px',
|
||||
cursor: 'pointer',
|
||||
p: 1,
|
||||
bg: 'white',
|
||||
borderRadius: 'lg',
|
||||
boxShadow: '0 0 5px rgba(0,0,0,0.1)',
|
||||
border: theme.borders.base,
|
||||
mr: 3
|
||||
};
|
||||
const controlContainerStyle = {
|
||||
className: 'control',
|
||||
color: 'myGray.400',
|
||||
display: ['flex', 'none'],
|
||||
pl: 1,
|
||||
mt: 2
|
||||
};
|
||||
const MessageCardStyle: BoxProps = {
|
||||
px: 4,
|
||||
py: 3,
|
||||
borderRadius: '0 8px 8px 8px',
|
||||
boxShadow: '0 0 8px rgba(0,0,0,0.15)'
|
||||
};
|
||||
|
||||
const messageCardMaxW = ['calc(100% - 25px)', 'calc(100% - 40px)'];
|
||||
|
||||
const showEmpty = useMemo(
|
||||
() =>
|
||||
feConfigs?.show_emptyChat &&
|
||||
showEmptyIntro &&
|
||||
chatHistory.length === 0 &&
|
||||
!variableModules?.length &&
|
||||
!welcomeText,
|
||||
[chatHistory.length, showEmptyIntro, variableModules, welcomeText]
|
||||
);
|
||||
const statusBoxData = useMemo(() => {
|
||||
const colorMap = {
|
||||
loading: '#67c13b',
|
||||
running: '#67c13b',
|
||||
finish: 'myBlue.600'
|
||||
};
|
||||
if (!isChatting) return;
|
||||
const chatContent = chatHistory[chatHistory.length - 1];
|
||||
if (!chatContent) return;
|
||||
|
||||
return {
|
||||
bg: colorMap[chatContent.status] || colorMap.loading,
|
||||
name: t(chatContent.moduleName || 'Running')
|
||||
};
|
||||
}, [chatHistory, isChatting, t]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
controller.current?.abort('leave');
|
||||
// close voice
|
||||
cancelBroadcast();
|
||||
};
|
||||
}, [router.query]);
|
||||
|
||||
useEffect(() => {
|
||||
const listen = () => {
|
||||
cancelBroadcast();
|
||||
};
|
||||
window.addEventListener('beforeunload', listen);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', listen);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Flex flexDirection={'column'} h={'100%'}>
|
||||
<Box ref={ChatBoxRef} flex={'1 0 0'} h={0} w={'100%'} overflow={'overlay'} px={[4, 0]} pb={3}>
|
||||
<Box maxW={['100%', '92%']} h={'100%'} mx={'auto'}>
|
||||
{showEmpty && <Empty />}
|
||||
|
||||
{!!welcomeText && (
|
||||
<Flex flexDirection={'column'} alignItems={'flex-start'} py={2}>
|
||||
{/* avatar */}
|
||||
<ChatAvatar src={appAvatar} type={'AI'} />
|
||||
{/* message */}
|
||||
<Card order={2} mt={2} {...MessageCardStyle} bg={'white'} maxW={messageCardMaxW}>
|
||||
<Markdown
|
||||
source={`~~~guide \n${welcomeText}`}
|
||||
isChatting={false}
|
||||
onClick={(e) => {
|
||||
const val = e?.data;
|
||||
if (e?.event !== EventNameEnum.guideClick || !val) return;
|
||||
handleSubmit((data) => sendPrompt(data, val))();
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</Flex>
|
||||
)}
|
||||
{/* variable input */}
|
||||
{!!variableModules?.length && (
|
||||
<Flex flexDirection={'column'} alignItems={'flex-start'} py={2}>
|
||||
{/* avatar */}
|
||||
<ChatAvatar src={appAvatar} type={'AI'} />
|
||||
{/* message */}
|
||||
<Card
|
||||
order={2}
|
||||
mt={2}
|
||||
bg={'white'}
|
||||
w={'400px'}
|
||||
maxW={messageCardMaxW}
|
||||
{...MessageCardStyle}
|
||||
>
|
||||
{variableModules.map((item) => (
|
||||
<Box key={item.id} mb={4}>
|
||||
<VariableLabel required={item.required}>{item.label}</VariableLabel>
|
||||
{item.type === VariableInputEnum.input && (
|
||||
<Input
|
||||
isDisabled={variableIsFinish}
|
||||
{...register(item.key, {
|
||||
required: item.required
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
{item.type === VariableInputEnum.select && (
|
||||
<MySelect
|
||||
width={'100%'}
|
||||
isDisabled={variableIsFinish}
|
||||
list={(item.enums || []).map((item) => ({
|
||||
label: item.value,
|
||||
value: item.value
|
||||
}))}
|
||||
value={getValues(item.key)}
|
||||
onchange={(e) => {
|
||||
setValue(item.key, e);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
{!variableIsFinish && (
|
||||
<Button
|
||||
leftIcon={<MyIcon name={'chatFill'} w={'16px'} />}
|
||||
size={'sm'}
|
||||
maxW={'100px'}
|
||||
borderRadius={'lg'}
|
||||
onClick={handleSubmit((data) => {
|
||||
onUpdateVariable?.(data);
|
||||
setVariables(data);
|
||||
})}
|
||||
>
|
||||
{'开始对话'}
|
||||
</Button>
|
||||
)}
|
||||
</Card>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{/* chat history */}
|
||||
<Box id={'history'}>
|
||||
{chatHistory.map((item, index) => (
|
||||
<Flex
|
||||
position={'relative'}
|
||||
key={item._id}
|
||||
flexDirection={'column'}
|
||||
alignItems={item.obj === 'Human' ? 'flex-end' : 'flex-start'}
|
||||
py={5}
|
||||
_hover={{
|
||||
'& .control': {
|
||||
display: item.status === 'finish' ? 'flex' : 'none'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{item.obj === 'Human' && (
|
||||
<>
|
||||
<Flex w={'100%'} alignItems={'center'} justifyContent={'flex-end'}>
|
||||
<Flex {...controlContainerStyle} justifyContent={'flex-end'} mr={3}>
|
||||
<MyTooltip label={'复制'}>
|
||||
<MyIcon
|
||||
{...controlIconStyle}
|
||||
name={'copy'}
|
||||
_hover={{ color: 'myBlue.700' }}
|
||||
onClick={() => onclickCopy(item.value)}
|
||||
/>
|
||||
</MyTooltip>
|
||||
{onDelMessage && (
|
||||
<MyTooltip label={'删除'}>
|
||||
<MyIcon
|
||||
{...controlIconStyle}
|
||||
mr={0}
|
||||
name={'delete'}
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={() => {
|
||||
setChatHistory((state) =>
|
||||
state.filter((chat) => chat._id !== item._id)
|
||||
);
|
||||
onDelMessage({
|
||||
contentId: item._id,
|
||||
index
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
)}
|
||||
</Flex>
|
||||
<ChatAvatar src={userAvatar} type={'Human'} />
|
||||
</Flex>
|
||||
<Box position={'relative'} maxW={messageCardMaxW} mt={['6px', 2]}>
|
||||
<Card
|
||||
className="markdown"
|
||||
whiteSpace={'pre-wrap'}
|
||||
{...MessageCardStyle}
|
||||
bg={'myBlue.300'}
|
||||
borderRadius={'8px 0 8px 8px'}
|
||||
>
|
||||
<Box as={'p'}>{item.value}</Box>
|
||||
</Card>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
{item.obj === 'AI' && (
|
||||
<>
|
||||
<Flex w={'100%'} alignItems={'flex-end'}>
|
||||
<ChatAvatar src={appAvatar} type={'AI'} />
|
||||
<Flex {...controlContainerStyle} ml={3}>
|
||||
<MyTooltip label={'复制'}>
|
||||
<MyIcon
|
||||
{...controlIconStyle}
|
||||
name={'copy'}
|
||||
_hover={{ color: 'myBlue.700' }}
|
||||
onClick={() => onclickCopy(item.value)}
|
||||
/>
|
||||
</MyTooltip>
|
||||
{onDelMessage && (
|
||||
<MyTooltip label={'删除'}>
|
||||
<MyIcon
|
||||
{...controlIconStyle}
|
||||
name={'delete'}
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={() => {
|
||||
setChatHistory((state) =>
|
||||
state.filter((chat) => chat._id !== item._id)
|
||||
);
|
||||
onDelMessage({
|
||||
contentId: item._id,
|
||||
index
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
)}
|
||||
{hasVoiceApi && (
|
||||
<MyTooltip label={'语音播报'}>
|
||||
<MyIcon
|
||||
{...controlIconStyle}
|
||||
name={'voice'}
|
||||
_hover={{ color: '#E74694' }}
|
||||
onClick={() => voiceBroadcast({ text: item.value })}
|
||||
/>
|
||||
</MyTooltip>
|
||||
)}
|
||||
</Flex>
|
||||
{statusBoxData && index === chatHistory.length - 1 && (
|
||||
<Flex
|
||||
ml={3}
|
||||
alignItems={'center'}
|
||||
px={3}
|
||||
py={'1px'}
|
||||
borderRadius="md"
|
||||
border={theme.borders.base}
|
||||
>
|
||||
<Box
|
||||
className={styles.statusAnimation}
|
||||
bg={statusBoxData.bg}
|
||||
w="8px"
|
||||
h="8px"
|
||||
borderRadius={'50%'}
|
||||
mt={'1px'}
|
||||
></Box>
|
||||
<Box ml={2} color={'myGray.600'}>
|
||||
{statusBoxData.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
<Box position={'relative'} maxW={messageCardMaxW} mt={['6px', 2]}>
|
||||
<Card bg={'white'} {...MessageCardStyle}>
|
||||
<Markdown
|
||||
source={item.value}
|
||||
isChatting={index === chatHistory.length - 1 && isChatting}
|
||||
/>
|
||||
<ResponseDetailModal
|
||||
chatId={chatId}
|
||||
contentId={item._id}
|
||||
responseData={item.responseData}
|
||||
/>
|
||||
</Card>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
{/* input */}
|
||||
{variableIsFinish ? (
|
||||
<Box m={['0 auto', '10px auto']} w={'100%'} maxW={['auto', 'min(750px, 100%)']} px={[0, 5]}>
|
||||
<Box
|
||||
py={'18px'}
|
||||
position={'relative'}
|
||||
boxShadow={`0 0 10px rgba(0,0,0,0.2)`}
|
||||
borderTop={['1px solid', 0]}
|
||||
borderTopColor={'myGray.200'}
|
||||
borderRadius={['none', 'md']}
|
||||
backgroundColor={'white'}
|
||||
>
|
||||
{/* 输入框 */}
|
||||
<Textarea
|
||||
ref={TextareaDom}
|
||||
py={0}
|
||||
pr={['45px', '55px']}
|
||||
border={'none'}
|
||||
_focusVisible={{
|
||||
border: 'none'
|
||||
}}
|
||||
placeholder="提问"
|
||||
resize={'none'}
|
||||
rows={1}
|
||||
height={'22px'}
|
||||
lineHeight={'22px'}
|
||||
maxHeight={'150px'}
|
||||
maxLength={-1}
|
||||
overflowY={'auto'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
wordBreak={'break-all'}
|
||||
boxShadow={'none !important'}
|
||||
color={'myGray.900'}
|
||||
onChange={(e) => {
|
||||
const textarea = e.target;
|
||||
textarea.style.height = textareaMinH;
|
||||
textarea.style.height = `${textarea.scrollHeight}px`;
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
// 触发快捷发送
|
||||
if (isPc && e.keyCode === 13 && !e.shiftKey) {
|
||||
handleSubmit((data) => sendPrompt(data, TextareaDom.current?.value))();
|
||||
e.preventDefault();
|
||||
}
|
||||
// 全选内容
|
||||
// @ts-ignore
|
||||
e.key === 'a' && e.ctrlKey && e.target?.select();
|
||||
}}
|
||||
/>
|
||||
{/* 发送和等待按键 */}
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
h={'25px'}
|
||||
w={'25px'}
|
||||
position={'absolute'}
|
||||
right={['12px', '20px']}
|
||||
bottom={'15px'}
|
||||
>
|
||||
{isChatting ? (
|
||||
<MyIcon
|
||||
className={styles.stopIcon}
|
||||
width={['22px', '25px']}
|
||||
height={['22px', '25px']}
|
||||
cursor={'pointer'}
|
||||
name={'stop'}
|
||||
color={'gray.500'}
|
||||
onClick={() => controller.current?.abort('stop')}
|
||||
/>
|
||||
) : (
|
||||
<MyIcon
|
||||
name={'chatSend'}
|
||||
width={['18px', '20px']}
|
||||
height={['18px', '20px']}
|
||||
cursor={'pointer'}
|
||||
color={'gray.500'}
|
||||
onClick={() => {
|
||||
handleSubmit((data) => sendPrompt(data, TextareaDom.current?.value))();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
</Box>
|
||||
) : null}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(forwardRef(ChatBox));
|
||||
|
||||
export const useChatBox = () => {
|
||||
const onExportChat = useCallback(
|
||||
({ type, history }: { type: ExportChatType; history: ChatItemType[] }) => {
|
||||
const getHistoryHtml = () => {
|
||||
const historyDom = document.getElementById('history');
|
||||
if (!historyDom) return;
|
||||
const dom = Array.from(historyDom.children).map((child, i) => {
|
||||
const avatar = `<img src="${
|
||||
child.querySelector<HTMLImageElement>('.avatar')?.src
|
||||
}" alt="" />`;
|
||||
|
||||
const chatContent = child.querySelector<HTMLDivElement>('.markdown');
|
||||
|
||||
if (!chatContent) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const chatContentClone = chatContent.cloneNode(true) as HTMLDivElement;
|
||||
|
||||
const codeHeader = chatContentClone.querySelectorAll('.code-header');
|
||||
codeHeader.forEach((childElement: any) => {
|
||||
childElement.remove();
|
||||
});
|
||||
|
||||
return `<div class="chat-item">
|
||||
${avatar}
|
||||
${chatContentClone.outerHTML}
|
||||
</div>`;
|
||||
});
|
||||
|
||||
const html = htmlTemplate.replace('{{CHAT_CONTENT}}', dom.join('\n'));
|
||||
return html;
|
||||
};
|
||||
|
||||
const map: Record<ExportChatType, () => void> = {
|
||||
md: () => {
|
||||
fileDownload({
|
||||
text: history.map((item) => item.value).join('\n\n'),
|
||||
type: 'text/markdown',
|
||||
filename: 'chat.md'
|
||||
});
|
||||
},
|
||||
html: () => {
|
||||
const html = getHistoryHtml();
|
||||
html &&
|
||||
fileDownload({
|
||||
text: html,
|
||||
type: 'text/html',
|
||||
filename: '聊天记录.html'
|
||||
});
|
||||
},
|
||||
pdf: () => {
|
||||
const html = getHistoryHtml();
|
||||
|
||||
html &&
|
||||
// @ts-ignore
|
||||
html2pdf(html, {
|
||||
margin: 0,
|
||||
filename: `聊天记录.pdf`
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
map[type]();
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return {
|
||||
onExportChat
|
||||
};
|
||||
};
|
||||
29
client/src/components/ChatBox/utils.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { SystemInputEnum } from '@/constants/app';
|
||||
import { FlowModuleTypeEnum } from '@/constants/flow';
|
||||
import { getChatModel } from '@/service/utils/data';
|
||||
import { AppModuleItemType, VariableItemType } from '@/types/app';
|
||||
|
||||
export const getSpecialModule = (modules: AppModuleItemType[]) => {
|
||||
const welcomeText: string =
|
||||
modules
|
||||
.find((item) => item.flowType === FlowModuleTypeEnum.userGuide)
|
||||
?.inputs?.find((item) => item.key === SystemInputEnum.welcomeText)?.value || '';
|
||||
|
||||
const variableModules: VariableItemType[] =
|
||||
modules
|
||||
.find((item) => item.flowType === FlowModuleTypeEnum.variable)
|
||||
?.inputs.find((item) => item.key === SystemInputEnum.variables)?.value || [];
|
||||
|
||||
return {
|
||||
welcomeText,
|
||||
variableModules
|
||||
};
|
||||
};
|
||||
export const getChatModelNameList = (modules: AppModuleItemType[]): string[] => {
|
||||
const chatModules = modules.filter((item) => item.flowType === FlowModuleTypeEnum.chatNode);
|
||||
return chatModules
|
||||
.map(
|
||||
(item) => getChatModel(item.inputs.find((input) => input.key === 'model')?.value)?.name || ''
|
||||
)
|
||||
.filter((item) => item);
|
||||
};
|
||||
30
client/src/components/CommunityModal/index.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import { Button, ModalFooter, ModalBody } from '@chakra-ui/react';
|
||||
import MyModal from '../MyModal';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Markdown from '../Markdown';
|
||||
|
||||
const md = `
|
||||
| 交流群 | 小助手 |
|
||||
| ----------------------- | -------------------- |
|
||||
|  |  |
|
||||
`;
|
||||
|
||||
const CommunityModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<MyModal isOpen={true} onClose={onClose} title={t('home.Community')}>
|
||||
<ModalBody textAlign={'center'}>
|
||||
<Markdown source={md} />
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button variant={'base'} onClick={onClose}>
|
||||
关闭
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CommunityModal;
|
||||
4
client/src/components/DateRangePicker/index.module.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
.datePicker {
|
||||
--rdp-background-color: #d6e8ff;
|
||||
--rdp-accent-color: #0000ff;
|
||||
}
|
||||
121
client/src/components/DateRangePicker/index.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React, { useState, useMemo, useRef } from 'react';
|
||||
import { Box, Card, Flex, useTheme, useOutsideClick, Button } from '@chakra-ui/react';
|
||||
import { addDays, format } from 'date-fns';
|
||||
import { type DateRange, DayPicker } from 'react-day-picker';
|
||||
import MyIcon from '../Icon';
|
||||
import 'react-day-picker/dist/style.css';
|
||||
import styles from './index.module.scss';
|
||||
import zhCN from 'date-fns/locale/zh-CN';
|
||||
|
||||
const DateRangePicker = ({
|
||||
onChange,
|
||||
onSuccess,
|
||||
position = 'bottom',
|
||||
defaultDate = {
|
||||
from: addDays(new Date(), -30),
|
||||
to: new Date()
|
||||
}
|
||||
}: {
|
||||
onChange?: (date: DateRange) => void;
|
||||
onSuccess?: (date: DateRange) => void;
|
||||
position?: 'bottom' | 'top';
|
||||
defaultDate?: DateRange;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const OutRangeRef = useRef(null);
|
||||
const [range, setRange] = useState<DateRange | undefined>(defaultDate);
|
||||
const [showSelected, setShowSelected] = useState(false);
|
||||
|
||||
const formatSelected = useMemo(() => {
|
||||
if (range?.from && range.to) {
|
||||
return `${format(range.from, 'y-MM-dd')} ~ ${format(range.to, 'y-MM-dd')}`;
|
||||
}
|
||||
return `${format(new Date(), 'y-MM-dd')} ~ ${format(new Date(), 'y-MM-dd')}`;
|
||||
}, [range]);
|
||||
|
||||
useOutsideClick({
|
||||
ref: OutRangeRef,
|
||||
handler: () => {
|
||||
setShowSelected(false);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Box position={'relative'} ref={OutRangeRef}>
|
||||
<Flex
|
||||
border={theme.borders.base}
|
||||
px={3}
|
||||
py={1}
|
||||
borderRadius={'sm'}
|
||||
cursor={'pointer'}
|
||||
bg={'myWhite.600'}
|
||||
fontSize={'sm'}
|
||||
onClick={() => setShowSelected(true)}
|
||||
>
|
||||
<Box>{formatSelected}</Box>
|
||||
<MyIcon ml={2} name={'date'} w={'16px'} color={'myGray.600'} />
|
||||
</Flex>
|
||||
{showSelected && (
|
||||
<Card
|
||||
position={'absolute'}
|
||||
zIndex={1}
|
||||
{...(position === 'top'
|
||||
? {
|
||||
bottom: '40px'
|
||||
}
|
||||
: {})}
|
||||
>
|
||||
<DayPicker
|
||||
locale={zhCN}
|
||||
id="test"
|
||||
mode="range"
|
||||
className={styles.datePicker}
|
||||
defaultMonth={defaultDate.to}
|
||||
selected={range}
|
||||
disabled={[
|
||||
{ from: new Date(2022, 3, 1), to: addDays(new Date(), -90) },
|
||||
{ from: addDays(new Date(), 1), to: new Date(2099, 1, 1) }
|
||||
]}
|
||||
onSelect={(date) => {
|
||||
if (date?.from === undefined) {
|
||||
date = {
|
||||
from: range?.from,
|
||||
to: range?.from
|
||||
};
|
||||
}
|
||||
if (date?.to === undefined) {
|
||||
date.to = date.from;
|
||||
}
|
||||
setRange(date);
|
||||
onChange && onChange(date);
|
||||
}}
|
||||
footer={
|
||||
<Flex justifyContent={'flex-end'}>
|
||||
<Button
|
||||
variant={'outline'}
|
||||
size={'sm'}
|
||||
mr={2}
|
||||
onClick={() => setShowSelected(false)}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
size={'sm'}
|
||||
onClick={() => {
|
||||
onSuccess && onSuccess(range || defaultDate);
|
||||
setShowSelected(false);
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
</Flex>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default DateRangePicker;
|
||||
export type DateRangeType = DateRange;
|
||||
22
client/src/components/Icon/close.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Flex, type FlexProps } from '@chakra-ui/react';
|
||||
import MyIcon from '@/components/Icon';
|
||||
|
||||
const CloseIcon = (props: FlexProps) => {
|
||||
return (
|
||||
<Flex
|
||||
cursor={'pointer'}
|
||||
w={'22px'}
|
||||
h={'22px'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
borderRadius={'50%'}
|
||||
_hover={{ bg: 'myGray.200' }}
|
||||
{...props}
|
||||
>
|
||||
<MyIcon name={'closeLight'} w={'12px'} color={'myGray.500'} />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default CloseIcon;
|
||||
25
client/src/components/Icon/delete.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import { IconProps } from '@chakra-ui/react';
|
||||
|
||||
const DeleteIcon = (props: IconProps) => {
|
||||
return (
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete' as any}
|
||||
w={'14px'}
|
||||
_hover={{ color: 'red.600' }}
|
||||
display={['block', 'none']}
|
||||
cursor={'pointer'}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteIcon;
|
||||
|
||||
export const hoverDeleteStyles = {
|
||||
'& .delete': {
|
||||
display: 'block'
|
||||
}
|
||||
};
|
||||
1
client/src/components/Icon/icons/apikey.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1686969412308" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3481" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M517.864056 487.834624c-56.774051-54.213739-58.850339-144.187937-4.6366-200.960964 54.212716-56.773028 144.187937-58.849316 200.960964-4.6366 56.775074 54.213739 58.850339 144.186913 4.6366 200.960964C664.613328 539.972075 574.639131 542.048363 517.864056 487.834624zM687.194626 452.994118c37.533848-39.308261 36.09508-101.596909-3.210112-139.128711-39.304168-37.531801-101.593839-36.094056-139.127687 3.211135-37.532825 39.307238-36.093033 101.593839 3.212158 139.125641C587.374176 493.736031 649.660778 492.302379 687.194626 452.994118zM479.104287 670.917406l-101.495602 106.289792c26.206872 25.024953 27.167756 66.540486 2.14178 92.749404-25.028023 26.209942-66.543555 27.16571-92.750427 2.140757l-58.361199 53.027727c0 0-68.750827 11.100826-100.379175-19.101033-31.630395-30.205952-37.865399-112.721271-37.865399-112.721271l246.37427-258.302951c-63.173808-117.608581-47.24707-267.162736 49.939389-368.939747 36.517705-38.242999 80.346933-65.156976 127.165238-81.040734l1.084705 46.269813c-35.443233 14.07967-68.566632 35.596729-96.618525 64.973804-80.271208 84.064604-96.099708 205.865671-49.433876 305.083393l23.075555 39.163975L146.090774 798.015106c0 0 0.593518 49.77873 17.242709 65.677838 14.888082 14.216793 61.832254 9.828856 61.832254 9.828856l60.407812-63.260789 31.631418 30.203906c8.741082 8.346085 22.570042 8.030907 30.91715-0.711198 8.347109-8.742105 8.026814-22.571065-0.713244-30.91715l-31.632441-30.207999 156.456355-163.846672 39.009456 22.481014c101.259218 42.039465 222.201731 20.61041 302.474986-63.453171 104.251366-109.178585 100.260471-282.211477-8.91709-386.464889-33.591049-32.075533-73.260537-53.829999-115.093295-65.49262l-1.030469-45.153386c53.197596 12.471033 103.945397 38.547944 146.323577 79.015611 126.645398 120.931257 131.277906 321.649698 10.344602 448.296119C748.158093 705.787588 599.500355 728.598106 479.104287 670.917406z" p-id="3482"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
1
client/src/components/Icon/icons/back.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1683436459815" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1278" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M377.04830749 512.02677874l395.88826112-382.68981248c23.33363769-22.59430969 23.33363769-59.16132466 0-81.68344804-23.33247261-22.5582171-61.18836224-22.5582171-84.52083485 0L250.30081877 471.19378659c-23.29754397 22.5582171-23.29754397 59.14385977 0 81.63105451l438.11491385 423.52280349c11.70233003 11.27968995 26.99767353 16.91837099 42.29534607 16.91837098 15.29883648 0 30.59418112-5.63984498 42.22548878-16.95446471 23.33363769-22.5582171 23.33363769-59.07283854 0-81.63105451L377.04830749 512.02677874" p-id="1279"></path></svg>
|
||||
|
After Width: | Height: | Size: 844 B |
1
client/src/components/Icon/icons/chat.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1683254586102" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="940" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M512 34.13333333c263.91893333 0 477.86666667 213.94773333 477.86666667 477.86666667S775.91893333 989.86666667 512 989.86666667H79.6448A45.51146667 45.51146667 0 0 1 34.13333333 944.3552V512C34.13333333 248.08106667 248.08106667 34.13333333 512 34.13333333zM307.2 580.26666667a22.7552 22.7552 0 0 0-22.7552 22.7552V648.53333333A22.7552 22.7552 0 0 0 307.2 671.28853333h227.5552A22.7552 22.7552 0 0 0 557.51146667 648.53333333v-45.51146666A22.7552 22.7552 0 0 0 534.7552 580.26666667H307.2z m0-182.0448a22.7552 22.7552 0 0 0-22.7552 22.75626666v45.5104A22.7552 22.7552 0 0 0 307.2 489.2448h409.6a22.7552 22.7552 0 0 0 22.7552-22.75626667v-45.5104A22.7552 22.7552 0 0 0 716.8 398.22186667H307.2z" p-id="941"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
1
client/src/components/Icon/icons/circle/add.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1690977807626" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9878" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M512 1024a512 512 0 1 1 512-512 512 512 0 0 1-512 512zM512 73.142857a438.857143 438.857143 0 1 0 438.857143 438.857143 438.857143 438.857143 0 0 0-438.857143-438.857143z" p-id="9879"></path><path d="M256 475.428571h512a36.571429 36.571429 0 0 1 36.571429 36.571429 36.571429 36.571429 0 0 1-36.571429 36.571429h-512A36.571429 36.571429 0 0 1 219.428571 512a36.571429 36.571429 0 0 1 36.571429-36.571429z" p-id="9880"></path><path d="M548.571429 256v512a36.571429 36.571429 0 0 1-73.142858 0v-512a36.571429 36.571429 0 0 1 73.142858 0z" p-id="9881"></path></svg>
|
||||
|
After Width: | Height: | Size: 870 B |
1
client/src/components/Icon/icons/closeSolid.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1683455612885" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1736" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M848.41813333 174.4896q69.90506667 69.90506667 104.31146667 157.2864t34.4064 179.13173333-34.4064 179.13173334-104.31146667 157.2864-157.2864 104.8576-179.13173333 34.95253333-179.67786667-34.95253333-157.83253333-104.8576-104.8576-157.2864-34.95253333-179.13173334 34.95253333-179.13173333 104.8576-157.2864 157.83253333-104.31146667 179.67786667-34.4064 179.13173333 34.4064 157.2864 104.31146667zM734.8224 729.36106667q22.9376-22.9376 20.20693333-52.4288t-25.66826666-52.4288l-114.688-114.688 114.688-114.688q22.9376-22.9376 25.66826666-52.4288t-20.20693333-52.4288-55.15946667-22.9376-55.15946666 22.9376l-114.688 113.59573333-111.4112-111.4112q-22.9376-21.84533333-52.4288-24.576t-52.4288 19.11466667q-22.9376 22.9376-22.9376 55.7056t22.9376 55.7056l111.4112 111.4112-111.4112 111.4112q-22.9376 22.9376-22.9376 55.15946666t22.9376 55.15946667 52.4288 20.20693333 52.4288-25.66826666l111.4112-111.4112 114.688 114.688q22.9376 22.9376 55.15946666 22.9376t55.15946667-22.9376z" p-id="1737"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
client/src/components/Icon/icons/collectionLight.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1682602070818" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2479" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M509.606998 143.114488c9.082866 0 17.327644 4.840238 20.996197 12.331863l97.262184 197.441814c5.613858 11.403724 16.663518 19.358907 29.438473 21.216207l223.738737 32.552393c8.420787 1.215688 15.604396 6.851035 18.23327 14.254655 2.520403 7.18361 0.595564 15.062044-5.084808 20.586874L730.253304 601.611947c-8.949836 8.751315-12.994965 21.171182-10.916631 33.370015l38.011732 222.060515c1.325182 7.737218-2.165316 15.426341-8.905834 19.978007-4.088108 2.741437-8.861832 4.155646-13.812587 4.155646-4.022617 0-7.999185-0.972141-11.425214-2.740414L528.149307 775.671215c-5.768377-3.006474-12.155854-4.552689-18.542308-4.552689-6.364965 0-12.727882 1.547239-18.518772 4.552689L296.254819 878.348736c-3.559059 1.855254-7.602142 2.828418-11.668761 2.828418-4.861728 0-9.723455-1.459235-13.546527-4.022617-6.961552-4.684696-10.475586-12.419867-9.127891-20.155039l38.011732-222.016513c2.078335-12.198833-1.988284-24.619724-10.939143-33.370015L125.02397 441.443038c-5.635347-5.492084-7.55814-13.348006-5.061272-20.453844 2.63092-7.481392 9.812483-13.116739 18.298761-14.332427l223.674269-32.552393c12.839423-1.857301 23.867594-9.813506 29.481452-21.216207l97.194646-197.396789C492.325403 147.965983 500.590648 143.114488 509.606998 143.114488M509.606998 104.904235c-24.043602 0-45.922912 13.226233-56.177464 33.95637L356.189863 336.302419l-223.674269 32.54216c-22.983457 3.304256-42.100864 18.718317-49.481971 39.659255-7.381108 21.048385-1.812275 44.23241 14.431687 60.033281l163.916257 160.125931-38.011732 222.016513c-3.868097 22.408359 6.03239 44.819788 25.458835 57.94676 10.69662 7.116071 23.204491 10.784624 35.757388 10.784624 10.298554 0 20.663622-2.475378 30.055526-7.337105l194.987926-102.7205L704.662463 912.072815c9.369392 4.861728 19.712971 7.337105 29.990035 7.337105 12.57541 0 25.082258-3.668553 35.778878-10.784624 19.426445-13.126972 29.305443-35.538401 25.460882-57.94676l-38.012755-222.016513 163.937746-160.125931c16.22145-15.812127 21.810748-38.984896 14.408151-60.033281-7.402597-20.940938-26.51898-36.353976-49.503461-39.659255L663.04767 336.302419l-97.240695-197.441814C555.619962 118.131491 533.695626 104.904235 509.606998 104.904235L509.606998 104.904235z" p-id="2480"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |