Compare commits

...

22 Commits
v4.2 ... v4.2.2

Author SHA1 Message Date
Archer
5fcdf28c5c user feedback and admin mark (#228)
* fix: csv empty data

* feat: user feedback and mark answer

* version intro

* perf: chat logs sort
2023-08-27 23:12:06 +08:00
archer
2556c19a9a issue template 2023-08-27 01:18:33 +08:00
Archer
8fd21ad5a7 Update issue templates 2023-08-27 01:13:13 +08:00
archer
efebcd6f1b docs images 2023-08-27 00:27:48 +08:00
Carson Yang
05e681f98f Fix favicon (#225)
Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>
2023-08-27 00:26:51 +08:00
archer
17c59bedfe README 2023-08-27 00:26:14 +08:00
archer
92ebd6a0b9 doc: m3e model 2023-08-26 22:31:21 +08:00
archer
0d26b1d48e fix: file sprcial char 2023-08-26 21:36:48 +08:00
archer
4973d7ad9c fix: default vector 2023-08-26 19:40:18 +08:00
archer
defc73651b glm2 docs 2023-08-26 19:24:14 +08:00
archer
eefc976d42 perf: qa prompt 2023-08-26 19:21:15 +08:00
archer
be33794a5f feat: self vector search 2023-08-26 18:25:12 +08:00
archer
13439c5183 perf: read file token error 2023-08-26 17:17:19 +08:00
archer
93030afe3e fix: limit prompt 2023-08-26 11:56:26 +08:00
archer
dfc40db835 prompt 2023-08-26 10:52:55 +08:00
MaricoHan
7f48acc42e 修正单词拼写 (#223) 2023-08-26 09:36:18 +08:00
archer
5661b11aee docs 2023-08-25 16:34:49 +08:00
archer
4b088e84c7 fix: ts 2023-08-25 15:45:24 +08:00
archer
6d93059e25 feat: config vector model and qa model 2023-08-25 15:00:51 +08:00
archer
a9970dd694 logo 2023-08-25 10:15:09 +08:00
Carson Yang
81421bccef Fix CI workflow (#220)
Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>
2023-08-25 10:06:20 +08:00
Carson Yang
f4ae980a75 Update favicon (#219)
Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>
2023-08-25 09:41:12 +08:00
130 changed files with 2127 additions and 3708 deletions

29
.github/ISSUE_TEMPLATE/bugs.md vendored Normal file
View File

@@ -0,0 +1,29 @@
---
name: 问题反馈
about: 详细清晰的描述你遇到的问题
title: ''
labels: bug
assignees: ''
---
**例行检查**
[//]: # (方框内删除已有的空格,填 x 号)
+ [ ] 我已确认目前没有类似 issue
+ [ ] 我已完整查看过项目 README以及[项目文档](https://doc.fastgpt.run/docs/intro/)
+ [ ] 我使用了自己的key并确认我的 key 是可正常使用的
+ [ ] 我理解并愿意跟进此 issue协助测试和提供反馈
+ [ ] 我理解并认可上述内容,并理解项目维护者精力有限,**不遵循规则的 issue 可能会被无视或直接关闭**
**你的版本**
+ [ ] 公有云版本
+ [ ] 私有部署版本
**问题描述**
**复现步骤**
**预期结果**
**相关截图**

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: 微信交流群
url: https://doc.fastgpt.run/wechat-fastgpt.webp
about: FastGPT 全是问题群

23
.github/ISSUE_TEMPLATE/features.md vendored Normal file
View File

@@ -0,0 +1,23 @@
---
name: 功能请求
about: 详细描述你期望的功能
title: ''
labels: enhancement
assignees: ''
---
**例行检查**
[//]: # '方框内删除已有的空格,填 x 号'
- [ ] 我已确认目前没有类似 features
- [ ] 我已确认我已升级到最新版本
- [ ] 我已完整查看过项目 README已确定现有版本无法满足需求
- [ ] 我理解并愿意跟进此 features协助测试和提供反馈
- [ ] 我理解并认可上述内容,并理解项目维护者精力有限,**不遵循规则的 features 可能会被无视或直接关闭**
**功能描述**
**应用场景**
**相关示例**

611
.github/imgs/logo.svg vendored
View File

@@ -1,599 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="256px" height="256px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve"> <image id="image0" width="256" height="256" x="0" y="0"
href="data:image/png;base64,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
MDqUEekAAAAASUVORK5CYII=" />
<svg width="32" height="32" viewBox="0 0 1041 1348" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M340.837 0.33933L681.068 0.338989V0.455643C684.032 0.378397 686.999 0.339702 689.967 0.339702C735.961 0.3397 781.504 9.62899 823.997 27.6772C866.49 45.7254 905.099 72.1791 937.622 105.528C970.144 138.877 995.942 178.467 1013.54 222.04C1031.14 265.612 1040.2 312.312 1040.2 359.474L340.836 359.474L340.836 1347.84C296.157 1347.84 251.914 1338.55 210.636 1320.49C169.357 1302.43 131.85 1275.95 100.257 1242.58C68.6636 1209.21 43.6023 1169.59 26.5041 1125.99C11.3834 1087.43 2.75216 1046.42 0.957956 1004.81H0.605869L0.605897 368.098H0.70363C0.105752 341.831 2.23741 315.443 7.14306 289.411C20.2709 219.745 52.6748 155.754 100.257 105.528C147.839 55.3017 208.462 21.0975 274.461 7.24017C296.426 2.62833 318.657 0.339101 340.837 0.33933Z" fill="url(#paint0_linear_1172_228)"/>
<path d="M633.639 904.645H513.029V576.37H635.422V576.377C678.161 576.607 720.454 585.093 759.951 601.37C799.997 617.874 836.384 642.064 867.033 672.559C897.683 703.054 921.996 739.257 938.583 779.101C955.171 818.944 963.709 861.648 963.709 904.775H633.639V904.645Z" fill="url(#paint1_linear_1172_228)"/>
<defs>
<linearGradient id="paint0_linear_1172_228" x1="520.404" y1="0.338989" x2="520.404" y2="1347.84" gradientUnits="userSpaceOnUse">
<stop stop-color="#326DFF"/>
<stop offset="1" stop-color="#8EAEFF"/>
</linearGradient>
<linearGradient id="paint1_linear_1172_228" x1="738.369" y1="576.37" x2="738.369" y2="904.775" gradientUnits="userSpaceOnUse">
<stop stop-color="#326DFF"/>
<stop offset="1" stop-color="#8EAEFF"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -54,7 +54,7 @@ jobs:
# Step 4 - Builds the site using Hugo
- name: Build
run: hugo -v --minify -s docSite
run: cd docSite && hugo mod get -u github.com/colinwilson/lotusdocs && hugo -v --minify
env:
HUGO_BASEURL: ${{ vars.BASE_URL }}

View File

@@ -77,3 +77,28 @@ jobs:
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}}
push-to-ali-hub:
needs: build-fastgpt-images
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Login to Ali Hub
uses: docker/login-action@v2
with:
registry: registry.cn-hangzhou.aliyuncs.com
username: ${{ secrets.ALI_HUB_USERNAME }}
password: ${{ secrets.ALI_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.ALI_IMAGE_NAME }}:${{env.IMAGE_TAG}}
- name: Push image to Docker Hub
run: docker push ${{ secrets.ALI_IMAGE_NAME }}:${{env.IMAGE_TAG}}

View File

@@ -85,9 +85,9 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开
## 🏘️ 社区交流群
| 交流群 | 小助手 |
| --------------------------------------------------- | ---------------------------------------------- |
| ![](https://otnvvf-imgs.oss.laf.run/wxqun300-2.jpg) | ![](https://otnvvf-imgs.oss.laf.run/wx300.jpg) |
| 交流群 | 小助手 |
| ----------------------------------------------------- | ---------------------------------------------- |
| ![](https://otnvvf-imgs.oss.laf.run/wxqun300-222.jpg) | ![](https://otnvvf-imgs.oss.laf.run/wx300.jpg) |
## 👀 其他
@@ -113,9 +113,9 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开
## 使用协议
本仓库遵循 [FstGPT Open Source License](./LICENSE) 开源协议。
本仓库遵循 [FastGPT Open Source License](./LICENSE) 开源协议。
1. 允许作为后台服务直接商用,但不允许直接使用 saas 服务商用。
2. 需保留相关版权信息。
3. 完整请查看 [FstGPT Open Source License](./LICENSE)
3. 完整请查看 [FastGPT Open Source License](./LICENSE)
4. 联系方式yujinlong@sealos.io, [点击查看定价策略](https://fael3z0zfze.feishu.cn/docx/F155dbirfo8vDDx2WgWc6extnwf)

View File

@@ -45,19 +45,19 @@
"defaultSystem": ""
}
],
"QAModels": [
{
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"maxToken": 16000,
"price": 0
}
],
"VectorModels": [
{
"model": "text-embedding-ada-002",
"name": "Embedding-2",
"price": 0
"price": 0,
"defaultToken": 500,
"maxToken": 3000
}
]
],
"QAModel": {
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"maxToken": 16000,
"price": 0
}
}

57
client/pnpm-lock.yaml generated
View File

@@ -1,8 +1,4 @@
lockfileVersion: '6.1'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
lockfileVersion: '6.0'
dependencies:
'@chakra-ui/icons':
@@ -94,7 +90,7 @@ dependencies:
version: registry.npmmirror.com/nanoid@4.0.1
next:
specifier: 13.1.6
version: registry.npmmirror.com/next@13.1.6(@babel/core@7.22.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3)
version: registry.npmmirror.com/next@13.1.6(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3)
next-i18next:
specifier: ^13.3.0
version: registry.npmmirror.com/next-i18next@13.3.0(i18next@22.5.1)(next@13.1.6)(react-i18next@12.3.1)(react@18.2.0)
@@ -234,6 +230,7 @@ packages:
dependencies:
'@jridgewell/gen-mapping': registry.npmmirror.com/@jridgewell/gen-mapping@0.3.3
'@jridgewell/trace-mapping': registry.npmmirror.com/@jridgewell/trace-mapping@0.3.18
dev: true
registry.npmmirror.com/@aws-crypto/crc32@3.0.0:
resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@aws-crypto/crc32/-/crc32-3.0.0.tgz}
@@ -1214,6 +1211,7 @@ packages:
name: '@babel/compat-data'
version: 7.22.5
engines: {node: '>=6.9.0'}
dev: true
registry.npmmirror.com/@babel/core@7.22.5:
resolution: {integrity: sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/core/-/core-7.22.5.tgz}
@@ -1238,6 +1236,7 @@ packages:
semver: registry.npmmirror.com/semver@6.3.0
transitivePeerDependencies:
- supports-color
dev: true
registry.npmmirror.com/@babel/generator@7.22.5:
resolution: {integrity: sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/generator/-/generator-7.22.5.tgz}
@@ -1249,6 +1248,7 @@ packages:
'@jridgewell/gen-mapping': registry.npmmirror.com/@jridgewell/gen-mapping@0.3.3
'@jridgewell/trace-mapping': registry.npmmirror.com/@jridgewell/trace-mapping@0.3.18
jsesc: registry.npmmirror.com/jsesc@2.5.2
dev: true
registry.npmmirror.com/@babel/helper-annotate-as-pure@7.22.5:
resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz}
@@ -1283,6 +1283,7 @@ packages:
browserslist: registry.npmmirror.com/browserslist@4.21.7
lru-cache: registry.npmmirror.com/lru-cache@5.1.1
semver: registry.npmmirror.com/semver@6.3.0
dev: true
registry.npmmirror.com/@babel/helper-create-class-features-plugin@7.22.5(@babel/core@7.22.5):
resolution: {integrity: sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz}
@@ -1346,6 +1347,7 @@ packages:
name: '@babel/helper-environment-visitor'
version: 7.22.5
engines: {node: '>=6.9.0'}
dev: true
registry.npmmirror.com/@babel/helper-function-name@7.22.5:
resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz}
@@ -1355,6 +1357,7 @@ packages:
dependencies:
'@babel/template': registry.npmmirror.com/@babel/template@7.22.5
'@babel/types': registry.npmmirror.com/@babel/types@7.22.5
dev: true
registry.npmmirror.com/@babel/helper-hoist-variables@7.22.5:
resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz}
@@ -1363,6 +1366,7 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': registry.npmmirror.com/@babel/types@7.22.5
dev: true
registry.npmmirror.com/@babel/helper-member-expression-to-functions@7.22.5:
resolution: {integrity: sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz}
@@ -1397,6 +1401,7 @@ packages:
'@babel/types': registry.npmmirror.com/@babel/types@7.22.5
transitivePeerDependencies:
- supports-color
dev: true
registry.npmmirror.com/@babel/helper-optimise-call-expression@7.22.5:
resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz}
@@ -1455,6 +1460,7 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': registry.npmmirror.com/@babel/types@7.22.5
dev: true
registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers@7.22.5:
resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz}
@@ -1472,6 +1478,7 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': registry.npmmirror.com/@babel/types@7.22.5
dev: true
registry.npmmirror.com/@babel/helper-string-parser@7.22.5:
resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz}
@@ -1490,6 +1497,7 @@ packages:
name: '@babel/helper-validator-option'
version: 7.22.5
engines: {node: '>=6.9.0'}
dev: true
registry.npmmirror.com/@babel/helper-wrap-function@7.22.5:
resolution: {integrity: sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz}
@@ -1516,6 +1524,7 @@ packages:
'@babel/types': registry.npmmirror.com/@babel/types@7.22.5
transitivePeerDependencies:
- supports-color
dev: true
registry.npmmirror.com/@babel/highlight@7.22.5:
resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/highlight/-/highlight-7.22.5.tgz}
@@ -1535,6 +1544,7 @@ packages:
hasBin: true
dependencies:
'@babel/types': registry.npmmirror.com/@babel/types@7.22.5
dev: true
registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.22.5(@babel/core@7.22.5):
resolution: {integrity: sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz}
@@ -2797,6 +2807,7 @@ packages:
'@babel/code-frame': registry.npmmirror.com/@babel/code-frame@7.22.5
'@babel/parser': registry.npmmirror.com/@babel/parser@7.22.5
'@babel/types': registry.npmmirror.com/@babel/types@7.22.5
dev: true
registry.npmmirror.com/@babel/traverse@7.22.5:
resolution: {integrity: sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/traverse/-/traverse-7.22.5.tgz}
@@ -2816,6 +2827,7 @@ packages:
globals: registry.npmmirror.com/globals@11.12.0
transitivePeerDependencies:
- supports-color
dev: true
registry.npmmirror.com/@babel/types@7.22.5:
resolution: {integrity: sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/types/-/types-7.22.5.tgz}
@@ -4409,28 +4421,33 @@ packages:
'@jridgewell/set-array': registry.npmmirror.com/@jridgewell/set-array@1.1.2
'@jridgewell/sourcemap-codec': registry.npmmirror.com/@jridgewell/sourcemap-codec@1.4.15
'@jridgewell/trace-mapping': registry.npmmirror.com/@jridgewell/trace-mapping@0.3.18
dev: true
registry.npmmirror.com/@jridgewell/resolve-uri@3.1.0:
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz}
name: '@jridgewell/resolve-uri'
version: 3.1.0
engines: {node: '>=6.0.0'}
dev: true
registry.npmmirror.com/@jridgewell/set-array@1.1.2:
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.1.2.tgz}
name: '@jridgewell/set-array'
version: 1.1.2
engines: {node: '>=6.0.0'}
dev: true
registry.npmmirror.com/@jridgewell/sourcemap-codec@1.4.14:
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz}
name: '@jridgewell/sourcemap-codec'
version: 1.4.14
dev: true
registry.npmmirror.com/@jridgewell/sourcemap-codec@1.4.15:
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz}
name: '@jridgewell/sourcemap-codec'
version: 1.4.15
dev: true
registry.npmmirror.com/@jridgewell/trace-mapping@0.3.18:
resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz}
@@ -4439,6 +4456,7 @@ packages:
dependencies:
'@jridgewell/resolve-uri': registry.npmmirror.com/@jridgewell/resolve-uri@3.1.0
'@jridgewell/sourcemap-codec': registry.npmmirror.com/@jridgewell/sourcemap-codec@1.4.14
dev: true
registry.npmmirror.com/@motionone/animation@10.15.1:
resolution: {integrity: sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@motionone/animation/-/animation-10.15.1.tgz}
@@ -6065,6 +6083,7 @@ packages:
electron-to-chromium: registry.npmmirror.com/electron-to-chromium@1.4.425
node-releases: registry.npmmirror.com/node-releases@2.0.12
update-browserslist-db: registry.npmmirror.com/update-browserslist-db@1.0.11(browserslist@4.21.7)
dev: true
registry.npmmirror.com/bson@1.1.6:
resolution: {integrity: sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/bson/-/bson-1.1.6.tgz}
@@ -7146,6 +7165,7 @@ packages:
resolution: {integrity: sha512-wv1NufHxu11zfDbY4fglYQApMswleE9FL/DSeyOyauVXDZ+Kco96JK/tPfBUaDqfRarYp2WH2hJ/5UnVywp9Jg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.425.tgz}
name: electron-to-chromium
version: 1.4.425
dev: true
registry.npmmirror.com/elkjs@0.8.2:
resolution: {integrity: sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/elkjs/-/elkjs-0.8.2.tgz}
@@ -7287,6 +7307,7 @@ packages:
name: escalade
version: 3.1.1
engines: {node: '>=6'}
dev: true
registry.npmmirror.com/escape-string-regexp@1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz}
@@ -7926,6 +7947,7 @@ packages:
name: gensync
version: 1.0.0-beta.2
engines: {node: '>=6.9.0'}
dev: true
registry.npmmirror.com/get-intrinsic@1.2.1:
resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz}
@@ -8017,6 +8039,7 @@ packages:
name: globals
version: 11.12.0
engines: {node: '>=4'}
dev: true
registry.npmmirror.com/globals@13.20.0:
resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/globals/-/globals-13.20.0.tgz}
@@ -8836,6 +8859,7 @@ packages:
version: 2.5.2
engines: {node: '>=4'}
hasBin: true
dev: true
registry.npmmirror.com/json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz}
@@ -8869,6 +8893,7 @@ packages:
version: 2.2.3
engines: {node: '>=6'}
hasBin: true
dev: true
registry.npmmirror.com/jsonwebtoken@9.0.0:
resolution: {integrity: sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz}
@@ -9106,6 +9131,7 @@ packages:
version: 5.1.1
dependencies:
yallist: registry.npmmirror.com/yallist@3.1.1
dev: true
registry.npmmirror.com/lru-cache@6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz}
@@ -9890,12 +9916,12 @@ packages:
hoist-non-react-statics: registry.npmmirror.com/hoist-non-react-statics@3.3.2
i18next: registry.npmmirror.com/i18next@22.5.1
i18next-fs-backend: registry.npmmirror.com/i18next-fs-backend@2.1.5
next: registry.npmmirror.com/next@13.1.6(@babel/core@7.22.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3)
next: registry.npmmirror.com/next@13.1.6(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3)
react: registry.npmmirror.com/react@18.2.0
react-i18next: registry.npmmirror.com/react-i18next@12.3.1(i18next@22.5.1)(react-dom@18.2.0)(react@18.2.0)
dev: false
registry.npmmirror.com/next@13.1.6(@babel/core@7.22.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3):
registry.npmmirror.com/next@13.1.6(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3):
resolution: {integrity: sha512-hHlbhKPj9pW+Cymvfzc15lvhaOZ54l+8sXDXJWm3OBNBzgrVj6hwGPmqqsXg40xO1Leq+kXpllzRPuncpC0Phw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/next/-/next-13.1.6.tgz}
id: registry.npmmirror.com/next/13.1.6
name: next
@@ -9923,7 +9949,7 @@ packages:
react: registry.npmmirror.com/react@18.2.0
react-dom: registry.npmmirror.com/react-dom@18.2.0(react@18.2.0)
sass: registry.npmmirror.com/sass@1.58.3
styled-jsx: registry.npmmirror.com/styled-jsx@5.1.1(@babel/core@7.22.5)(react@18.2.0)
styled-jsx: registry.npmmirror.com/styled-jsx@5.1.1(react@18.2.0)
optionalDependencies:
'@next/swc-android-arm-eabi': registry.npmmirror.com/@next/swc-android-arm-eabi@13.1.6
'@next/swc-android-arm64': registry.npmmirror.com/@next/swc-android-arm64@13.1.6
@@ -9952,13 +9978,14 @@ packages:
next: ^8.1.1-canary.54 || ^9.0.0 || ^10.0.0-0 || ^11.0.0 || ^12.0.0 || ^13.0.0
dependencies:
cors: registry.npmmirror.com/cors@2.8.5
next: registry.npmmirror.com/next@13.1.6(@babel/core@7.22.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3)
next: registry.npmmirror.com/next@13.1.6(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3)
dev: false
registry.npmmirror.com/node-releases@2.0.12:
resolution: {integrity: sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/node-releases/-/node-releases-2.0.12.tgz}
name: node-releases
version: 2.0.12
dev: true
registry.npmmirror.com/non-layered-tidy-tree-layout@2.0.2:
resolution: {integrity: sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz}
@@ -11145,6 +11172,7 @@ packages:
name: semver
version: 6.3.0
hasBin: true
dev: true
registry.npmmirror.com/semver@7.5.1:
resolution: {integrity: sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/semver/-/semver-7.5.1.tgz}
@@ -11421,7 +11449,7 @@ packages:
inline-style-parser: registry.npmmirror.com/inline-style-parser@0.1.1
dev: false
registry.npmmirror.com/styled-jsx@5.1.1(@babel/core@7.22.5)(react@18.2.0):
registry.npmmirror.com/styled-jsx@5.1.1(react@18.2.0):
resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/styled-jsx/-/styled-jsx-5.1.1.tgz}
id: registry.npmmirror.com/styled-jsx/5.1.1
name: styled-jsx
@@ -11437,7 +11465,6 @@ packages:
babel-plugin-macros:
optional: true
dependencies:
'@babel/core': registry.npmmirror.com/@babel/core@7.22.5
client-only: registry.npmmirror.com/client-only@0.0.1
react: registry.npmmirror.com/react@18.2.0
dev: false
@@ -11859,6 +11886,7 @@ packages:
browserslist: registry.npmmirror.com/browserslist@4.21.7
escalade: registry.npmmirror.com/escalade@3.1.1
picocolors: registry.npmmirror.com/picocolors@1.0.0
dev: true
registry.npmmirror.com/uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz}
@@ -12221,6 +12249,7 @@ packages:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz}
name: yallist
version: 3.1.1
dev: true
registry.npmmirror.com/yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz}
@@ -12273,3 +12302,7 @@ packages:
name: zwitch
version: 2.0.4
dev: false
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false

View File

@@ -1,10 +1,11 @@
### 常见问题
- [**Git 地址**,点击查看项目地址](https://github.com/labring/FastGPT)
- [**Git 地址**点击查看项目地址](https://github.com/labring/FastGPT)
- [本地部署 FastGPT](https://doc.fastgpt.run/docs/installation)
- [API 文档](https://kjqvjse66l.feishu.cn/docx/DmLedTWtUoNGX8xui9ocdUEjnNh?pre_pathname=%2Fdrive%2Fhome%2F)
- **反馈问卷**: 如果你遇到任何使用问题或有期望的功能,可以[填写该问卷](https://www.wjx.cn/vm/rLIw1uD.aspx#)
- **问题文档**: [先看文档,再提问](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
- [点击查看商业版文档](https://fael3z0zfze.feishu.cn/docx/F155dbirfo8vDDx2WgWc6extnwf)
**价格表**
| 计费项 | 价格: 元/ 1K tokens包含上下文|

View File

@@ -1,8 +1,7 @@
### Fast GPT V4.2
### Fast GPT V4.2.2
1. 新增 - 应用日志初版,可观测到所有对话记录
2. 新增 - 好友邀请链接,[点击查看](/account?currentTab=promotion)
3. 新增 - Iframe 嵌入页面图标可拖拽
4. 优化 - 知识库搜索提示词
5. 优化 - [使用文档](https://doc.fastgpt.run/docs/intro/)
6. [点击查看高级编排介绍文档](https://doc.fastgpt.run/docs/workflow)
1. **新增 - 用户反馈和管理员标注预期答案,以不断提高模型回复准确率。** 该功能为测试版,未来交互可能会有变化,欢迎大家提出宝贵意见。
2. 优化 - 知识库搜索提示词,更适配问答场景。
3. 新增 - 好友邀请链接,[点击查看](/account?currentTab=promotion)
4. 优化 - [使用文档](https://doc.fastgpt.run/docs/intro/)
5. [点击查看高级编排介绍文档](https://doc.fastgpt.run/docs/workflow)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -1,599 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="256px" height="256px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve"> <image id="image0" width="256" height="256" x="0" y="0"
href="data:image/png;base64,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
MDqUEekAAAAASUVORK5CYII=" />
<svg width="32" height="32" viewBox="0 0 1041 1348" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M340.837 0.33933L681.068 0.338989V0.455643C684.032 0.378397 686.999 0.339702 689.967 0.339702C735.961 0.3397 781.504 9.62899 823.997 27.6772C866.49 45.7254 905.099 72.1791 937.622 105.528C970.144 138.877 995.942 178.467 1013.54 222.04C1031.14 265.612 1040.2 312.312 1040.2 359.474L340.836 359.474L340.836 1347.84C296.157 1347.84 251.914 1338.55 210.636 1320.49C169.357 1302.43 131.85 1275.95 100.257 1242.58C68.6636 1209.21 43.6023 1169.59 26.5041 1125.99C11.3834 1087.43 2.75216 1046.42 0.957956 1004.81H0.605869L0.605897 368.098H0.70363C0.105752 341.831 2.23741 315.443 7.14306 289.411C20.2709 219.745 52.6748 155.754 100.257 105.528C147.839 55.3017 208.462 21.0975 274.461 7.24017C296.426 2.62833 318.657 0.339101 340.837 0.33933Z" fill="url(#paint0_linear_1172_228)"/>
<path d="M633.639 904.645H513.029V576.37H635.422V576.377C678.161 576.607 720.454 585.093 759.951 601.37C799.997 617.874 836.384 642.064 867.033 672.559C897.683 703.054 921.996 739.257 938.583 779.101C955.171 818.944 963.709 861.648 963.709 904.775H633.639V904.645Z" fill="url(#paint1_linear_1172_228)"/>
<defs>
<linearGradient id="paint0_linear_1172_228" x1="520.404" y1="0.338989" x2="520.404" y2="1347.84" gradientUnits="userSpaceOnUse">
<stop stop-color="#326DFF"/>
<stop offset="1" stop-color="#8EAEFF"/>
</linearGradient>
<linearGradient id="paint1_linear_1172_228" x1="738.369" y1="576.37" x2="738.369" y2="904.775" gradientUnits="userSpaceOnUse">
<stop stop-color="#326DFF"/>
<stop offset="1" stop-color="#8EAEFF"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -3,8 +3,9 @@
"Cancel": "No",
"Confirm": "Yes",
"Running": "Running",
"Warning": "Warning",
"Select value is empty": "Select value is empty",
"UnKnow": "UnKnow",
"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",
@@ -17,6 +18,7 @@
"Copy Module Config": "Copy config",
"Export Config Successful": "The configuration has been copied. Please check for important data",
"Export Configs": "Export Configs",
"Feedback Count": "User Feedback",
"Import Config": "Import Config",
"Import Config Failed": "Failed to import the configuration, please ensure that the configuration is normal!",
"Import Configs": "Import Configs",
@@ -26,16 +28,33 @@
"Logs Source": "Source",
"Logs Time": "Time",
"Logs Title": "Title",
"Mark Count": "Mark Count",
"My Apps": "My Apps",
"Output Field Settings": "Output Field Settings",
"Paste Config": "Paste Config"
},
"chat": {
"Admin Mark Content": "Corrected response",
"Complete Response": "Complete Response",
"Confirm to clear history": "Confirm to clear history?",
"Exit Chat": "Exit",
"Feedback Failed": "Feedback Failed",
"Feedback Mark": "Mark",
"Feedback Modal": "Feedback",
"Feedback Modal Tip": "Enter what you find unsatisfactory",
"Feedback Close": "Close Feedback",
"Feedback Submit": "Submit",
"Feedback Success": "Feedback Success",
"Feedback Update Failed": "Feedback Update Failed",
"History": "History",
"Mark": "Mark",
"Mark Description": "The annotation feature is currently in beta. \n\n After clicking Add annotation, you need to select a knowledge base in order to store annotation data. You can use this feature to quickly annotate questions and expected answers to guide the model to the next answer. At present, the annotation function, like other data in the knowledge base, is affected by the model, which does not mean that the annotation meets 100% expectations. The \n\n annotation data is only unidirectional synchronization with the knowledge base. If the knowledge base modifies the annotation data, the annotation data displayed in the log cannot be synchronized",
"Mark Description Title": "Mark Description",
"New Chat": "New Chat",
"Read Mark Description": "Read mark description",
"Read User Feedback": "Read user feedback",
"Select Mark Kb": "Select Dataset",
"Select Mark Kb Desc": "Select a dataset to store the expected answers",
"You need to a chat app": "You need to a chat app",
"logs": {
"api": "API",
@@ -120,8 +139,8 @@
"Start Now": "Start Now",
"Visual AI orchestration": "Visual AI orchestration",
"Why FastGPT": "",
"desc": "",
"slogan": ""
"desc": "AI knowledge base question and answer platform based on LLM large model",
"slogan": "Let the AI know more about you"
},
"navbar": {
"Account": "Account",

View File

@@ -3,8 +3,9 @@
"Cancel": "取消",
"Confirm": "确认",
"Running": "运行中",
"Warning": "提示",
"Select value is empty": "选择的内容为空",
"UnKnow": "未知",
"Warning": "提示",
"app": {
"Advance App TestTip": "当前应用为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键",
"App Detail": "应用详情",
@@ -17,6 +18,7 @@
"Copy Module Config": "复制配置",
"Export Config Successful": "已复制配置,请注意检查是否有重要数据",
"Export Configs": "导出配置",
"Feedback Count": "用户反馈",
"Import Config": "导入配置",
"Import Config Failed": "导入配置失败,请确保配置正常!",
"Import Configs": "导入配置",
@@ -26,16 +28,33 @@
"Logs Source": "来源",
"Logs Time": "时间",
"Logs Title": "标题",
"Mark Count": "标注答案数量",
"My Apps": "我的应用",
"Output Field Settings": "输出字段编辑",
"Paste Config": "粘贴配置"
},
"chat": {
"Admin Mark Content": "纠正后的回复",
"Complete Response": "完整响应",
"Confirm to clear history": "确认清空该应用的聊天记录?",
"Exit Chat": "退出聊天",
"Feedback Failed": "提交反馈异常",
"Feedback Mark": "标注",
"Feedback Modal": "结果反馈",
"Feedback Modal Tip": "输入你觉得回答不满意的地方",
"Feedback Close": "关闭反馈",
"Feedback Submit": "提交反馈",
"Feedback Success": "反馈成功!",
"Feedback Update Failed": "更新反馈状态失败",
"History": "记录",
"Mark": "标注预期回答",
"Mark Description": "当前标注功能为测试版。\n\n点击添加标注后需要选择一个知识库以便存储标注数据。你可以通过该功能快速的标注问题和预期回答以便引导模型下次的回答。\n\n目前标注功能同知识库其他数据一样受模型的影响不代表标注后 100% 符合预期。\n\n标注数据仅单向与知识库同步如果知识库修改了该标注数据日志展示的标注数据无法同步",
"Mark Description Title": "标注功能介绍",
"New Chat": "新对话",
"Read Mark Description": "查看标注功能介绍",
"Read User Feedback": "查看用户反馈",
"Select Mark Kb": "选择知识库",
"Select Mark Kb Desc": "选择一个知识库存储预期答案",
"You need to a chat app": "你需要创建一个应用",
"logs": {
"api": "API 调用",

View File

@@ -53,4 +53,4 @@ export const getAppTotalUsage = (data: { appId: string }) =>
}).then((res) => (res.length === 0 ? [{ date: new Date(), total: 0 }] : res));
export const getAppChatLogs = (data: RequestPaging & { appId: string }) =>
POST(`/chat/getChatLogs`, data);
POST(`/app/getChatLogs`, data);

View File

@@ -5,6 +5,7 @@ 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';
import { AdminUpdateFeedbackParams } from './request/chat';
/**
* 获取初始化聊天内容
@@ -64,3 +65,9 @@ export const getShareChatList = (appId: string) =>
* delete a shareChat
*/
export const delShareChatById = (id: string) => DELETE(`/chat/shareChat/delete?id=${id}`);
export const userUpdateChatFeedback = (data: { chatItemId: string; userFeedback?: string }) =>
POST('/chat/feedback/userUpdate', data);
export const adminUpdateChatFeedback = (data: AdminUpdateFeedbackParams) =>
POST('/chat/feedback/adminUpdate', data);

View File

@@ -12,20 +12,14 @@ import {
} 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;
};
import type { KbUpdateParams, CreateKbParams } from '../request/kb';
/* 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 postCreateKb = (data: CreateKbParams) => POST<string>(`/plugins/kb/create`, data);
export const putKbById = (data: KbUpdateParams) => PUT(`/plugins/kb/update`, data);
@@ -72,6 +66,14 @@ export const getKbDataItemById = (dataId: string) =>
export const postKbDataFromList = (data: PushDataProps) =>
POST<PushDateResponse>(`/openapi/kb/pushData`, data);
/**
* insert one data to dataset
*/
export const insertData2Kb = (data: {
kbId: string;
data: { a: string; q: string; source?: string };
}) => POST<string>(`/plugins/kb/data/insertData`, data);
/**
* 更新一条数据
*/

6
client/src/api/request/chat.d.ts vendored Normal file
View File

@@ -0,0 +1,6 @@
export type AdminUpdateFeedbackParams = {
chatItemId: string;
kbId: string;
dataId: string;
content: string;
};

12
client/src/api/request/kb.d.ts vendored Normal file
View File

@@ -0,0 +1,12 @@
export type KbUpdateParams = {
id: string;
name: string;
tags: string;
avatar: string;
};
export type CreateKbParams = {
name: string;
tags: string[];
avatar: string;
vectorModel: string;
};

View File

@@ -25,7 +25,7 @@ export const postRegister = ({
username: string;
code: string;
password: string;
inviterId: string;
inviterId?: string;
}) =>
POST<ResLogin>(`/plusApi/user/account/register`, {
username,

View File

@@ -0,0 +1,56 @@
import React, { useRef } from 'react';
import { ModalBody, Textarea, ModalFooter, Button } from '@chakra-ui/react';
import MyModal from '../MyModal';
import { useRequest } from '@/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import { userUpdateChatFeedback } from '@/api/chat';
const FeedbackModal = ({
chatItemId,
onSuccess,
onClose
}: {
chatItemId: string;
onSuccess: (e: string) => void;
onClose: () => void;
}) => {
const ref = useRef<HTMLTextAreaElement>(null);
const { t } = useTranslation();
const { mutate, isLoading } = useRequest({
mutationFn: async () => {
const val = ref.current?.value || 'N/A';
return userUpdateChatFeedback({
chatItemId,
userFeedback: val
});
},
onSuccess() {
onSuccess(ref.current?.value || 'N/A');
},
successToast: t('chat.Feedback Success'),
errorToast: t('chat.Feedback Failed')
});
return (
<MyModal isOpen={true} onClose={onClose} title={t('chat.Feedback Modal')}>
<ModalBody>
<Textarea
ref={ref}
rows={10}
placeholder={t('chat.Feedback Modal Tip') || 'chat.Feedback Modal Tip'}
/>
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={2} onClick={onClose}>
{t('Cancel')}
</Button>
<Button isLoading={isLoading} onClick={mutate}>
{t('chat.Feedback Submit')}
</Button>
</ModalFooter>
</MyModal>
);
};
export default FeedbackModal;

View File

@@ -0,0 +1,55 @@
import React from 'react';
import { ModalBody, ModalFooter, Button } from '@chakra-ui/react';
import MyModal from '../MyModal';
import { useRequest } from '@/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import { userUpdateChatFeedback } from '@/api/chat';
const ReadFeedbackModal = ({
chatItemId,
content,
isMarked,
onMark,
onSuccess,
onClose
}: {
chatItemId: string;
content: string;
isMarked: boolean;
onMark: () => void;
onSuccess: () => void;
onClose: () => void;
}) => {
const { t } = useTranslation();
const { mutate, isLoading } = useRequest({
mutationFn: async () => {
return userUpdateChatFeedback({
chatItemId,
userFeedback: undefined
});
},
onSuccess() {
onSuccess();
},
errorToast: t('chat.Feedback Update Failed')
});
return (
<MyModal isOpen={true} onClose={onClose} title={t('chat.Feedback Modal')}>
<ModalBody>{content}</ModalBody>
<ModalFooter>
{!isMarked && (
<Button variant={'base'} mr={2} onClick={onMark}>
{t('chat.Feedback Mark')}
</Button>
)}
<Button isLoading={isLoading} onClick={mutate}>
{t('chat.Feedback Close')}
</Button>
</ModalFooter>
</MyModal>
);
};
export default React.memo(ReadFeedbackModal);

View File

@@ -0,0 +1,117 @@
import React, { useRef, useState } from 'react';
import {
ModalBody,
useTheme,
ModalFooter,
Button,
ModalHeader,
Box,
Card,
Flex
} from '@chakra-ui/react';
import MyModal from '../MyModal';
import { useTranslation } from 'next-i18next';
import { useQuery } from '@tanstack/react-query';
import { useUserStore } from '@/store/user';
import { useToast } from '@/hooks/useToast';
import Avatar from '../Avatar';
import MyIcon from '@/components/Icon';
import { useGlobalStore } from '@/store/global';
const SelectDataset = ({
onSuccess,
onClose
}: {
onSuccess: (kbId: string) => void;
onClose: () => void;
}) => {
const { t } = useTranslation();
const theme = useTheme();
const { isPc } = useGlobalStore();
const { toast } = useToast();
const { myKbList, loadKbList } = useUserStore();
const [selectedId, setSelectedId] = useState<string>();
useQuery(['loadKbList'], loadKbList);
return (
<MyModal isOpen={true} onClose={onClose} w={'100%'} maxW={['90vw', '900px']} isCentered={!isPc}>
<Flex flexDirection={'column'} h={['90vh', 'auto']}>
<ModalHeader>
<Box>{t('chat.Select Mark Kb')}</Box>
<Box fontSize={'sm'} color={'myGray.500'} fontWeight={'normal'}>
{t('chat.Select Mark Kb Desc')}
</Box>
</ModalHeader>
<ModalBody
flex={['1 0 0', '0 0 auto']}
maxH={'80vh'}
overflowY={'auto'}
display={'grid'}
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
gridGap={3}
userSelect={'none'}
>
{myKbList.map((item) =>
(() => {
const selected = selectedId === item._id;
return (
<Card
key={item._id}
p={3}
border={theme.borders.base}
boxShadow={'sm'}
h={'80px'}
cursor={'pointer'}
_hover={{
boxShadow: 'md'
}}
{...(selected
? {
bg: 'myBlue.300'
}
: {})}
onClick={() => {
setSelectedId(item._id);
}}
>
<Flex alignItems={'center'} h={'38px'}>
<Avatar src={item.avatar} w={['24px', '28px', '32px']}></Avatar>
<Box ml={3} fontWeight={'bold'} fontSize={['md', 'lg', 'xl']}>
{item.name}
</Box>
</Flex>
<Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}>
<MyIcon mr={1} name="kbTest" w={'12px'} />
<Box color={'myGray.500'}>{item.vectorModel.name}</Box>
</Flex>
</Card>
);
})()
)}
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={2} onClick={onClose}>
{t('Cancel')}
</Button>
<Button
onClick={() => {
if (!selectedId) {
return toast({
status: 'warning',
title: t('Select value is empty')
});
}
onSuccess(selectedId);
}}
>
{t('Confirm')}
</Button>
</ModalFooter>
</Flex>
</MyModal>
);
};
export default SelectDataset;

View File

@@ -25,7 +25,6 @@ import {
} 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';
@@ -41,6 +40,7 @@ import { useGlobalStore } from '@/store/global';
import { TaskResponseKeyEnum, getDefaultChatVariables } from '@/constants/chat';
import { useTranslation } from 'react-i18next';
import { customAlphabet } from 'nanoid';
import { userUpdateChatFeedback, adminUpdateChatFeedback } from '@/api/chat';
import MyIcon from '@/components/Icon';
import Avatar from '@/components/Avatar';
@@ -49,6 +49,10 @@ import MySelect from '@/components/Select';
import MyTooltip from '../MyTooltip';
import dynamic from 'next/dynamic';
const ResponseTags = dynamic(() => import('./ResponseTags'));
const FeedbackModal = dynamic(() => import('./FeedbackModal'));
const ReadFeedbackModal = dynamic(() => import('./ReadFeedbackModal'));
const SelectDataset = dynamic(() => import('./SelectDataset'));
const InputDataModal = dynamic(() => import('@/pages/kb/detail/components/InputDataModal'));
import styles from './index.module.scss';
@@ -124,6 +128,7 @@ const ChatAvatar = ({ src, type }: { src?: string; type: 'Human' | 'AI' }) => {
const ChatBox = (
{
isLogs = false,
showEmptyIntro = false,
chatId,
appAvatar,
@@ -134,6 +139,7 @@ const ChatBox = (
onStartChat,
onDelMessage
}: {
isLogs?: boolean;
showEmptyIntro?: boolean;
chatId?: string;
appAvatar?: string;
@@ -162,6 +168,19 @@ const ChatBox = (
const [refresh, setRefresh] = useState(false);
const [variables, setVariables] = useState<Record<string, any>>({});
const [chatHistory, setChatHistory] = useState<ChatSiteItemType[]>([]);
const [feedbackId, setFeedbackId] = useState<string>();
const [readFeedbackData, setReadFeedbackData] = useState<{
chatItemId: string;
content: string;
isMarked: boolean;
}>();
const [adminMarkData, setAdminMarkData] = useState<{
kbId?: string;
chatItemId: string;
dataId?: string;
q: string;
a: string;
}>();
const isChatting = useMemo(
() =>
@@ -409,7 +428,7 @@ const ChatBox = (
const controlContainerStyle = {
className: 'control',
color: 'myGray.400',
display: ['flex', 'none'],
display: isLogs ? 'flex' : ['flex', 'none'],
pl: 1,
mt: 2
};
@@ -643,7 +662,7 @@ const ChatBox = (
/>
</MyTooltip>
)}
{hasVoiceApi && (
{!isLogs && hasVoiceApi && (
<MyTooltip label={'语音播报'}>
<MyIcon
{...controlIconStyle}
@@ -653,7 +672,93 @@ const ChatBox = (
/>
</MyTooltip>
)}
{/* admin mark icon */}
{isLogs && (
<MyTooltip label={t('chat.Mark')}>
<MyIcon
{...controlIconStyle}
name={'markLight'}
_hover={{ color: '#67c13b' }}
onClick={() => {
if (!item.dataId) return;
if (item.adminFeedback) {
setAdminMarkData({
chatItemId: item.dataId,
kbId: item.adminFeedback.kbId,
dataId: item.adminFeedback.dataId,
q: chatHistory[index - 1]?.value || '',
a: item.adminFeedback.content
});
} else {
setAdminMarkData({
chatItemId: item.dataId,
q: chatHistory[index - 1]?.value || '',
a: item.value
});
}
}}
/>
</MyTooltip>
)}
{/* user feed back icon */}
{item.dataId &&
(isLogs ? (
<MyTooltip label={t('chat.Read User Feedback')}>
<MyIcon
display={item.userFeedback ? 'block' : 'none'}
{...controlIconStyle}
color={'white'}
bg={'#FC9663'}
fontWeight={'bold'}
name={'badLight'}
onClick={() =>
setReadFeedbackData({
chatItemId: item.dataId || '',
content: item.userFeedback || '',
isMarked: !!item.adminFeedback
})
}
/>
</MyTooltip>
) : (
<MyTooltip
label={
item.userFeedback
? `取消反馈。\n您当前反馈内容为:\n${item.userFeedback}`
: '反馈'
}
>
<MyIcon
{...controlIconStyle}
{...(!!item.userFeedback
? {
color: 'white',
bg: '#FC9663',
fontWeight: 'bold',
onClick: () => {
if (!item.dataId) return;
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === item.dataId
? { ...chatItem, userFeedback: undefined }
: chatItem
)
);
try {
userUpdateChatFeedback({ chatItemId: item.dataId });
} catch (error) {}
}
}
: {
_hover: { color: '#FB7C3C' },
onClick: () => setFeedbackId(item.dataId)
})}
name={'badLight'}
/>
</MyTooltip>
))}
</Flex>
{/* chatting status */}
{statusBoxData && index === chatHistory.length - 1 && (
<Flex
ml={3}
@@ -688,6 +793,19 @@ const ChatBox = (
contentId={item.dataId}
responseData={item.responseData}
/>
{/* admin mark content */}
{isLogs && item.adminFeedback && (
<Box>
<Flex alignItems={'center'} py={2}>
<MyIcon name={'markLight'} w={'14px'} color={'myGray.900'} />
<Box ml={2} color={'myGray.500'}>
{t('chat.Admin Mark Content')}
</Box>
<Box h={'1px'} bg={'myGray.300'} flex={'1'} />
</Flex>
<Box>{item.adminFeedback.content}</Box>
</Box>
)}
</Card>
</Box>
</>
@@ -782,6 +900,115 @@ const ChatBox = (
</Box>
</Box>
) : null}
{/* user feedback modal */}
{!!feedbackId && (
<FeedbackModal
chatItemId={feedbackId}
onClose={() => setFeedbackId(undefined)}
onSuccess={(content: string) => {
setChatHistory((state) =>
state.map((item) =>
item.dataId === feedbackId ? { ...item, userFeedback: content } : item
)
);
setFeedbackId(undefined);
}}
/>
)}
{/* admin read feedback modal */}
{!!readFeedbackData && (
<ReadFeedbackModal
{...readFeedbackData}
onClose={() => setReadFeedbackData(undefined)}
onMark={() => {
const index = chatHistory.findIndex(
(item) => item.dataId === readFeedbackData.chatItemId
);
if (index === -1) return setReadFeedbackData(undefined);
setAdminMarkData({
chatItemId: readFeedbackData.chatItemId,
q: chatHistory[index - 1]?.value || '',
a: chatHistory[index]?.value || ''
});
}}
onSuccess={() => {
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === readFeedbackData.chatItemId
? { ...chatItem, userFeedback: undefined }
: chatItem
)
);
setReadFeedbackData(undefined);
}}
/>
)}
{/* select one dataset to insert markData */}
{adminMarkData && !adminMarkData.kbId && (
<SelectDataset
onClose={() => setAdminMarkData(undefined)}
// @ts-ignore
onSuccess={(kbId) => setAdminMarkData((state) => ({ ...state, kbId }))}
/>
)}
{/* edit markData modal */}
{adminMarkData && adminMarkData.kbId && (
<InputDataModal
onClose={() => setAdminMarkData(undefined)}
onSuccess={async (data) => {
if (!adminMarkData.kbId || !data.dataId) {
return setAdminMarkData(undefined);
}
const adminFeedback = {
kbId: adminMarkData.kbId,
dataId: data.dataId,
content: data.a
};
// update dom
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === adminMarkData.chatItemId
? {
...chatItem,
adminFeedback
}
: chatItem
)
);
// request to update adminFeedback
try {
adminUpdateChatFeedback({
chatItemId: adminMarkData.chatItemId,
...adminFeedback
});
if (readFeedbackData) {
userUpdateChatFeedback({
chatItemId: readFeedbackData.chatItemId,
userFeedback: undefined
});
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === readFeedbackData.chatItemId
? { ...chatItem, userFeedback: undefined }
: chatItem
)
);
setReadFeedbackData(undefined);
}
} catch (error) {}
setAdminMarkData(undefined);
}}
kbId={adminMarkData.kbId}
defaultValues={{
dataId: adminMarkData.dataId,
q: adminMarkData.q,
a: adminMarkData.a
}}
/>
)}
</Flex>
);
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

View 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="1693123058703" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16328" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M875.65 912h-380a36 36 0 0 1 0-72h380a36 36 0 1 1 0 72zM812.26 284.82L285.39 811.69l-88.11 15.1L212 738l526.72-526.72 73.54 73.54m90.51-11.31L750 120.77a16 16 0 0 0-22.62 0L152.5 695.68a34.11 34.11 0 0 0-9.5 18.56l-25.95 156.23a32 32 0 0 0 37 36.78l155.38-26.62a34.2 34.2 0 0 0 18.38-9.52l575-575a16 16 0 0 0 0-22.63z" p-id="16329"></path></svg>

After

Width:  |  Height:  |  Size: 677 B

View File

@@ -77,7 +77,9 @@ const map = {
playFill: require('./icons/fill/play.svg').default,
courseLight: require('./icons/light/course.svg').default,
promotionLight: require('./icons/light/promotion.svg').default,
logsLight: require('./icons/light/logs.svg').default
logsLight: require('./icons/light/logs.svg').default,
badLight: require('./icons/light/bad.svg').default,
markLight: require('./icons/light/mark.svg').default
};
export type IconName = keyof typeof map;

View File

@@ -218,7 +218,7 @@ export const KBSearchModule: FlowModuleTemplateType = {
key: 'similarity',
type: FlowInputItemTypeEnum.slider,
label: '相似度',
value: 0.8,
value: 0.4,
min: 0,
max: 1,
step: 0.01,
@@ -845,7 +845,7 @@ export const appTemplates: (AppItemType & { avatar: string; intro: string })[] =
key: 'similarity',
type: 'slider',
label: '相似度',
value: 0.8,
value: 0.4,
min: 0,
max: 1,
step: 0.01,

View File

@@ -3,10 +3,14 @@ import type { KbItemType } from '@/types/plugin';
export const defaultKbDetail: KbItemType = {
_id: '',
userId: '',
updateTime: new Date(),
avatar: '/icon/logo.svg',
name: '',
tags: '',
totalData: 0,
model: 'text-embedding-ada-002'
vectorModel: {
model: 'text-embedding-ada-002',
name: 'Embedding-2',
price: 0.2,
defaultToken: 500,
maxToken: 3000
}
};

View File

@@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { Chat, ChatItem, connectToDatabase } from '@/service/mongo';
import { Chat, connectToDatabase } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import type { PagingData } from '@/types';
import { AppLogsListItemType } from '@/types/app';
@@ -30,25 +30,48 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const [data, total] = await Promise.all([
Chat.aggregate([
{ $match: where },
{ $sort: { updateTime: -1 } },
{ $skip: (pageNum - 1) * pageSize },
{ $limit: pageSize },
{
$lookup: {
from: 'chatitems',
localField: 'chatId',
foreignField: 'chatId',
as: 'messageCount'
as: 'chatitems'
}
},
{
$addFields: {
feedbackCount: {
$size: {
$filter: {
input: '$chatitems',
as: 'item',
cond: { $ifNull: ['$$item.userFeedback', false] }
}
}
},
markCount: {
$size: {
$filter: {
input: '$chatitems',
as: 'item',
cond: { $ifNull: ['$$item.adminFeedback', false] }
}
}
}
}
},
{ $sort: { feedbackCount: -1, updateTime: -1 } },
{ $skip: (pageNum - 1) * pageSize },
{ $limit: pageSize },
{
$project: {
id: '$chatId',
title: 1,
source: 1,
time: '$updateTime',
messageCount: { $size: '$messageCount' },
callbackCount: { $literal: 0 }
messageCount: { $size: '$chatitems' },
feedbackCount: 1,
markCount: 1
}
}
]),

View File

@@ -0,0 +1,40 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, ChatItem } from '@/service/mongo';
import { AdminUpdateFeedbackParams } from '@/api/request/chat';
import { authUser } from '@/service/utils/auth';
/* 初始化我的聊天框,需要身份验证 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { chatItemId, kbId, dataId, content = undefined } = req.body as AdminUpdateFeedbackParams;
if (!chatItemId || !kbId || !dataId || !content) {
throw new Error('missing parameter');
}
const { userId } = await authUser({ req, authToken: true });
await ChatItem.findOneAndUpdate(
{
userId,
dataId: chatItemId
},
{
adminFeedback: {
kbId,
dataId,
content
}
}
);
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -0,0 +1,34 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, ChatItem } from '@/service/mongo';
/* 初始化我的聊天框,需要身份验证 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { chatItemId, userFeedback = undefined } = req.body as {
chatItemId: string;
userFeedback?: string;
};
if (!chatItemId) {
throw new Error('chatItemId is required');
}
await ChatItem.findOneAndUpdate(
{
dataId: chatItemId
},
{
...(userFeedback ? { userFeedback } : { $unset: { userFeedback: '' } })
}
);
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -54,7 +54,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
chatId,
userId
},
`dataId obj value ${TaskResponseKeyEnum.responseData}`
`dataId obj value adminFeedback userFeedback ${TaskResponseKeyEnum.responseData}`
)
.sort({ _id: -1 })
.limit(30)

View File

@@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, TrainingData } from '@/service/mongo';
import { connectToDatabase, TrainingData, KB } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import { authKb } from '@/service/utils/auth';
import { withNextCors } from '@/service/utils/tools';
@@ -14,7 +14,6 @@ export type DateItemType = { a: string; q: string; source?: string };
export type Props = {
kbId: string;
data: DateItemType[];
model: string;
mode: `${TrainingModeEnum}`;
prompt?: string;
};
@@ -30,23 +29,12 @@ const modeMaxToken = {
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
const { kbId, data, mode, prompt, model } = req.body as Props;
const { kbId, data, mode, prompt } = req.body as Props;
if (!kbId || !Array.isArray(data) || !model) {
if (!kbId || !Array.isArray(data)) {
throw new Error('缺少参数');
}
// auth model
if (mode === TrainingModeEnum.qa && !global.qaModels.find((item) => item.model === model)) {
throw new Error('不支持的 QA 拆分模型');
}
if (
mode === TrainingModeEnum.index &&
!global.vectorModels.find((item) => item.model === model)
) {
throw new Error('不支持的向量生成模型');
}
await connectToDatabase();
// 凭证校验
@@ -58,8 +46,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
data,
userId,
mode,
prompt,
model
prompt
})
});
} catch (err) {
@@ -75,8 +62,7 @@ export async function pushDataToKb({
kbId,
data,
mode,
prompt,
model
prompt
}: { userId: string } & Props): Promise<Response> {
await authKb({
userId,
@@ -152,17 +138,24 @@ export async function pushDataToKb({
.filter((item) => item.status === 'fulfilled')
.map<DateItemType>((item: any) => item.value);
const vectorModel = await (async () => {
if (mode === TrainingModeEnum.index) {
return (await KB.findById(kbId, 'vectorModel'))?.vectorModel || global.vectorModels[0].model;
}
return global.vectorModels[0].model;
})();
// 插入记录
await TrainingData.insertMany(
insertData.map((item) => ({
q: item.q,
a: item.a,
model,
source: item.source,
userId,
kbId,
mode,
prompt
prompt,
vectorModel
}))
);

View File

@@ -6,9 +6,9 @@ import { withNextCors } from '@/service/utils/tools';
import { getVector } from '../plugin/vector';
import type { KbTestItemType } from '@/types/plugin';
import { PgTrainingTableName } from '@/constants/plugin';
import { KB } from '@/service/mongo';
export type Props = {
model: string;
kbId: string;
text: string;
};
@@ -16,21 +16,24 @@ export type Response = KbTestItemType['results'];
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
const { kbId, text, model } = req.body as Props;
const { kbId, text } = req.body as Props;
if (!kbId || !text || !model) {
if (!kbId || !text) {
throw new Error('缺少参数');
}
// 凭证校验
const { userId } = await authUser({ req });
const [{ userId }, kb] = await Promise.all([
authUser({ req }),
KB.findById(kbId, 'vectorModel')
]);
if (!userId) {
if (!userId || !kb) {
throw new Error('缺少用户ID');
}
const { vectors } = await getVector({
model,
model: kb.vectorModel,
userId,
input: [text]
});

View File

@@ -24,11 +24,11 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
await connectToDatabase();
// 凭证校验
const { userId } = await authUser({ req });
// find model
const kb = await KB.findById(kbId, 'model');
// auth user and get kb
const [{ userId }, kb] = await Promise.all([
authUser({ req }),
KB.findById(kbId, 'vectorModel')
]);
if (!kb) {
throw new Error("Can't find database");
@@ -40,7 +40,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
return getVector({
userId,
input: [q],
model: kb.model
model: kb.vectorModel
});
}
return { vectors: [[]] };

View File

@@ -2,15 +2,13 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, KB } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import type { CreateKbParams } from '@/api/request/kb';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
const { name, tags } = req.body as {
name: string;
tags: string[];
};
const { name, tags, avatar, vectorModel } = req.body as CreateKbParams;
if (!name) {
if (!name || !vectorModel) {
throw new Error('缺少参数');
}
@@ -22,7 +20,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const { _id } = await KB.create({
name,
userId,
tags
tags,
vectorModel,
avatar
});
jsonRes(res, { data: _id });

View File

@@ -0,0 +1,88 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, KB } from '@/service/mongo';
import { authKb, authUser } from '@/service/utils/auth';
import { withNextCors } from '@/service/utils/tools';
import { PgTrainingTableName } from '@/constants/plugin';
import { insertKbItem, PgClient } from '@/service/pg';
import { modelToolMap } from '@/utils/plugin';
import { getVectorModel } from '@/service/utils/data';
import { getVector } from '@/pages/api/openapi/plugin/vector';
export type Props = {
kbId: string;
data: { a: string; q: string; source?: string };
};
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
const { kbId, data = { q: '', a: '' } } = req.body as Props;
if (!kbId || !data?.q) {
throw new Error('缺少参数');
}
// 凭证校验
const { userId } = await authUser({ req });
// auth kb
const kb = await authKb({ kbId, userId });
const q = data?.q?.replace(/\\n/g, '\n').trim().replace(/'/g, '"');
const a = data?.a?.replace(/\\n/g, '\n').trim().replace(/'/g, '"');
// token check
const token = modelToolMap.countTokens({
model: 'gpt-3.5-turbo',
messages: [{ obj: 'System', value: q }]
});
if (token > getVectorModel(kb.vectorModel).maxToken) {
throw new Error('Over Tokens');
}
const { rows: existsRows } = await PgClient.query(`
SELECT COUNT(*) > 0 AS exists
FROM ${PgTrainingTableName}
WHERE md5(q)=md5('${q}') AND md5(a)=md5('${a}') AND user_id='${userId}' AND kb_id='${kbId}'
`);
const exists = existsRows[0]?.exists || false;
if (exists) {
throw new Error('已经存在完全一致的数据');
}
const { vectors } = await getVector({
model: kb.vectorModel,
input: [q],
userId
});
const response = await insertKbItem({
userId,
kbId,
data: [
{
q,
a,
source: data.source,
vector: vectors[0]
}
]
});
// @ts-ignore
const id = response?.rows?.[0]?.id || '';
jsonRes(res, {
data: id
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
});

View File

@@ -2,6 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, KB } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import { getVectorModel } from '@/service/utils/data';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
@@ -33,7 +34,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
avatar: data.avatar,
name: data.name,
userId: data.userId,
model: data.model,
vectorModel: getVectorModel(data.vectorModel),
tags: data.tags.join(' ')
}
});

View File

@@ -3,6 +3,7 @@ import { jsonRes } from '@/service/response';
import { connectToDatabase, KB } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import { KbListItemType } from '@/types/plugin';
import { getVectorModel } from '@/service/utils/data';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
@@ -15,7 +16,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
{
userId
},
'_id avatar name tags'
'_id avatar name tags vectorModel'
).sort({ updateTime: -1 });
const data = await Promise.all(
@@ -23,7 +24,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
_id: item._id,
avatar: item.avatar,
name: item.name,
tags: item.tags
tags: item.tags,
vectorModel: getVectorModel(item.vectorModel)
}))
);

View File

@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, KB } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import type { KbUpdateParams } from '@/api/plugins/kb';
import type { KbUpdateParams } from '@/api/request/kb';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {

View File

@@ -10,7 +10,7 @@ import {
export type InitDateResponse = {
chatModels: ChatModelItemType[];
qaModels: QAModelItemType[];
qaModel: QAModelItemType;
vectorModels: VectorModelItemType[];
feConfigs: FeConfigsType;
};
@@ -23,7 +23,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
data: {
feConfigs: global.feConfigs,
chatModels: global.chatModels,
qaModels: global.qaModels,
qaModel: global.qaModel,
vectorModels: global.vectorModels
}
});
@@ -69,19 +69,20 @@ const defaultChatModels = [
price: 0
}
];
const defaultQAModels = [
{
model: 'gpt-3.5-turbo-16k',
name: 'GPT35-16k',
maxToken: 16000,
price: 0
}
];
const defaultVectorModels = [
const defaultQAModel = {
model: 'gpt-3.5-turbo-16k',
name: 'GPT35-16k',
maxToken: 16000,
price: 0
};
const defaultVectorModels: VectorModelItemType[] = [
{
model: 'text-embedding-ada-002',
name: 'Embedding-2',
price: 0
price: 0,
defaultToken: 500,
maxToken: 3000
}
];
@@ -95,7 +96,7 @@ export async function getInitConfig() {
global.systemEnv = res.SystemParams || defaultSystemEnv;
global.feConfigs = res.FeConfig || defaultFeConfigs;
global.chatModels = res.ChatModels || defaultChatModels;
global.qaModels = res.QAModels || defaultQAModels;
global.qaModel = res.QAModel || defaultQAModel;
global.vectorModels = res.VectorModels || defaultVectorModels;
} catch (error) {
setDefaultData();
@@ -107,6 +108,6 @@ export function setDefaultData() {
global.systemEnv = defaultSystemEnv;
global.feConfigs = defaultFeConfigs;
global.chatModels = defaultChatModels;
global.qaModels = defaultQAModels;
global.qaModel = defaultQAModel;
global.vectorModels = defaultVectorModels;
}

View File

@@ -100,9 +100,8 @@ export async function registerUser({
username,
avatar,
password: nanoid(),
inviterId
inviterId: inviterId ? inviterId : undefined
});
console.log(response, '-=-=-=');
// 根据 id 获取用户信息
const user = await User.findById(response._id);

View File

@@ -542,7 +542,10 @@ const Settings = ({ appId }: { appId: string }) => {
{isOpenKbSelect && (
<KBSelectModal
kbList={myKbList}
activeKbs={selectedKbList.map((item) => ({ kbId: item._id }))}
activeKbs={selectedKbList.map((item) => ({
kbId: item._id,
vectorModel: item.vectorModel
}))}
onClose={onCloseKbSelect}
onChange={replaceKbList}
/>

View File

@@ -16,9 +16,11 @@ import { useForm } from 'react-hook-form';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import type { SelectedKbType } from '@/types/plugin';
import { useGlobalStore } from '@/store/global';
import { useToast } from '@/hooks/useToast';
import MySlider from '@/components/Slider';
import MyTooltip from '@/components/MyTooltip';
import MyModal from '@/components/MyModal';
import MyIcon from '@/components/Icon';
export type KbParamsType = {
searchSimilarity: number;
@@ -40,6 +42,7 @@ export const KBSelectModal = ({
const theme = useTheme();
const [selectedKbList, setSelectedKbList] = useState<SelectedKbType>(activeKbs);
const { isPc } = useGlobalStore();
const { toast } = useToast();
return (
<MyModal
@@ -50,7 +53,13 @@ export const KBSelectModal = ({
onClose={onClose}
>
<Flex flexDirection={'column'} h={['90vh', 'auto']}>
<ModalHeader>({selectedKbList.length})</ModalHeader>
<ModalHeader>
<Box>({selectedKbList.length})</Box>
<Box fontSize={'sm'} color={'myGray.500'} fontWeight={'normal'}>
</Box>
</ModalHeader>
<ModalBody
flex={['1 0 0', '0 0 auto']}
maxH={'80vh'}
@@ -58,6 +67,7 @@ export const KBSelectModal = ({
display={'grid'}
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
gridGap={3}
userSelect={'none'}
>
{kbList.map((item) =>
(() => {
@@ -84,7 +94,18 @@ export const KBSelectModal = ({
if (selected) {
setSelectedKbList((state) => state.filter((kb) => kb.kbId !== item._id));
} else {
setSelectedKbList((state) => [...state, { kbId: item._id }]);
const vectorModel = selectedKbList[0]?.vectorModel?.model;
if (vectorModel && vectorModel !== item.vectorModel.model) {
return toast({
status: 'warning',
title: '仅能选择同一个索引模型的知识库'
});
}
setSelectedKbList((state) => [
...state,
{ kbId: item._id, vectorModel: item.vectorModel }
]);
}
}}
>
@@ -94,6 +115,10 @@ export const KBSelectModal = ({
{item.name}
</Box>
</Flex>
<Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}>
<MyIcon mr={1} name="kbTest" w={'12px'} />
<Box color={'myGray.500'}>{item.vectorModel.name}</Box>
</Flex>
</Card>
);
})()
@@ -138,7 +163,10 @@ export const KbParamsModal = ({
<Box display={['block', 'flex']} py={5} pt={[0, 5]}>
<Box flex={'0 0 100px'} mb={[8, 0]}>
<MyTooltip label={'高相似度推荐0.8及以上。'} forceShow>
<MyTooltip
label={'不同索引模型的相似度有区别,请通过搜索测试来选择合适的数值'}
forceShow
>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Box>

View File

@@ -9,7 +9,9 @@ import {
Th,
Td,
Tbody,
useTheme
useTheme,
useDisclosure,
ModalBody
} from '@chakra-ui/react';
import MyIcon from '@/components/Icon';
import { useTranslation } from 'next-i18next';
@@ -24,11 +26,18 @@ import ChatBox, { type ComponentRef } from '@/components/ChatBox';
import { useQuery } from '@tanstack/react-query';
import { getInitChatSiteInfo } from '@/api/chat';
import Tag from '@/components/Tag';
import MyModal from '@/components/MyModal';
const Logs = ({ appId }: { appId: string }) => {
const { t } = useTranslation();
const { isPc } = useGlobalStore();
const {
isOpen: isOpenMarkDesc,
onOpen: onOpenMarkDesc,
onClose: onCloseMarkDesc
} = useDisclosure();
const {
data: logs,
isLoading,
@@ -54,7 +63,16 @@ const Logs = ({ appId }: { appId: string }) => {
{t('app.Chat logs')}
</Box>
<Box color={'myGray.500'} fontSize={'sm'}>
{t('app.Chat Logs Tips')}
{t('app.Chat Logs Tips')},{' '}
<Box
as={'span'}
mr={2}
textDecoration={'underline'}
cursor={'pointer'}
onClick={onOpenMarkDesc}
>
{t('chat.Read Mark Description')}
</Box>
</Box>
</>
)}
@@ -69,6 +87,8 @@ const Logs = ({ appId }: { appId: string }) => {
<Th>{t('app.Logs Time')}</Th>
<Th>{t('app.Logs Title')}</Th>
<Th>{t('app.Logs Message Total')}</Th>
<Th>{t('app.Feedback Count')}</Th>
<Th>{t('app.Mark Count')}</Th>
</Tr>
</Thead>
<Tbody>
@@ -86,6 +106,27 @@ const Logs = ({ appId }: { appId: string }) => {
{item.title}
</Td>
<Td>{item.messageCount}</Td>
<Td w={'100px'}>
{!!item?.feedbackCount ? (
<Box display={'inline-block'}>
<Flex
bg={'#FFF2EC'}
color={'#C96330'}
px={3}
py={1}
alignItems={'center'}
borderRadius={'lg'}
fontWeight={'bold'}
>
<MyIcon mr={1} name={'badLight'} color={'#C96330'} w={'14px'} />
{item.feedbackCount}
</Flex>
</Box>
) : (
<>-</>
)}
</Td>
<Td>{item.markCount}</Td>
</Tr>
))}
</Tbody>
@@ -109,6 +150,13 @@ const Logs = ({ appId }: { appId: string }) => {
onClose={() => setDetailLogsId(undefined)}
/>
)}
<MyModal
isOpen={isOpenMarkDesc}
onClose={onCloseMarkDesc}
title={t('chat.Mark Description Title')}
>
<ModalBody whiteSpace={'pre-wrap'}>{t('chat.Mark Description')}</ModalBody>
</MyModal>
</Flex>
);
};
@@ -222,6 +270,7 @@ function DetailLogsModal({
<Box pt={2} flex={'1 0 0'}>
<ChatBox
ref={ChatBoxRef}
isLogs
chatId={chatId}
appAvatar={chat?.app.avatar}
userAvatar={HUMAN_ICON}

View File

@@ -101,8 +101,8 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
<Avatar
flexShrink={0}
src={getValues('avatar')}
w={['32px', '36px']}
h={['32px', '36px']}
w={['28px', '32px']}
h={['28px', '32px']}
cursor={'pointer'}
borderRadius={'md'}
onClick={onOpenSelectFile}

View File

@@ -19,7 +19,6 @@ import { postKbDataFromList } from '@/api/plugins/kb';
import { splitText2Chunks } from '@/utils/file';
import { getErrText } from '@/utils/tools';
import { formatPrice } from '@/utils/user';
import { vectorModelList } from '@/store/static';
import MyIcon from '@/components/Icon';
import CloseIcon from '@/components/Icon/close';
import DeleteIcon, { hoverDeleteStyles } from '@/components/Icon/delete';
@@ -27,17 +26,20 @@ import MyTooltip from '@/components/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { TrainingModeEnum } from '@/constants/plugin';
import FileSelect, { type FileItemType } from './FileSelect';
import { useUserStore } from '@/store/user';
const fileExtension = '.txt, .doc, .docx, .pdf, .md';
const ChunkImport = ({ kbId }: { kbId: string }) => {
const model = vectorModelList[0]?.model || 'text-embedding-ada-002';
const unitPrice = vectorModelList[0]?.price || 0.2;
const { kbDetail } = useUserStore();
const vectorModel = kbDetail.vectorModel;
const unitPrice = vectorModel?.price || 0.2;
const theme = useTheme();
const router = useRouter();
const { toast } = useToast();
const [chunkLen, setChunkLen] = useState(500);
const [chunkLen, setChunkLen] = useState(vectorModel?.defaultToken || 300);
const [showRePreview, setShowRePreview] = useState(false);
const [files, setFiles] = useState<FileItemType[]>([]);
const [previewFile, setPreviewFile] = useState<FileItemType>();
@@ -68,7 +70,6 @@ const ChunkImport = ({ kbId }: { kbId: string }) => {
for (let i = 0; i < chunks.length; i += step) {
const { insertLen } = await postKbDataFromList({
kbId,
model,
data: chunks.slice(i, i + step),
mode: TrainingModeEnum.index
});
@@ -206,24 +207,34 @@ const ChunkImport = ({ kbId }: { kbId: string }) => {
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Box>
<NumberInput
ml={4}
<Box
flex={1}
defaultValue={chunkLen}
min={300}
max={2000}
step={10}
onChange={(e) => {
setChunkLen(+e);
setShowRePreview(true);
css={{
'& > span': {
display: 'block'
}
}}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<MyTooltip label={`范围: 100~${kbDetail.vectorModel.maxToken}`}>
<NumberInput
ml={4}
defaultValue={chunkLen}
min={100}
max={kbDetail.vectorModel.maxToken}
step={10}
onChange={(e) => {
setChunkLen(+e);
setShowRePreview(true);
}}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</MyTooltip>
</Box>
</Flex>
{/* price */}
<Flex py={5} alignItems={'center'}>

View File

@@ -11,11 +11,14 @@ import DeleteIcon, { hoverDeleteStyles } from '@/components/Icon/delete';
import { TrainingModeEnum } from '@/constants/plugin';
import FileSelect, { type FileItemType } from './FileSelect';
import { useRouter } from 'next/router';
import { useUserStore } from '@/store/user';
const fileExtension = '.csv';
const CsvImport = ({ kbId }: { kbId: string }) => {
const model = vectorModelList[0]?.model;
const { kbDetail } = useUserStore();
const maxToken = kbDetail.vectorModel?.maxToken || 2000;
const theme = useTheme();
const router = useRouter();
const { toast } = useToast();
@@ -35,16 +38,27 @@ const CsvImport = ({ kbId }: { kbId: string }) => {
const { mutate: onclickUpload, isLoading: uploading } = useMutation({
mutationFn: async () => {
const chunks = files.map((file) => file.chunks).flat();
const chunks = files
.map((file) => file.chunks)
.flat()
.filter((item) => item?.q);
const filterChunks = chunks.filter((item) => item.q.length < maxToken);
if (filterChunks.length !== chunks.length) {
toast({
title: `${chunks.length - filterChunks.length}条数据超出长度,已被过滤`,
status: 'info'
});
}
// subsection import
let success = 0;
const step = 500;
for (let i = 0; i < chunks.length; i += step) {
for (let i = 0; i < filterChunks.length; i += step) {
const { insertLen } = await postKbDataFromList({
kbId,
model,
data: chunks.slice(i, i + step),
data: filterChunks.slice(i, i + step),
mode: TrainingModeEnum.index
});

View File

@@ -1,21 +1,26 @@
import React, { useCallback, useState } from 'react';
import { Box, type BoxProps, Flex, Textarea, useTheme, Button } from '@chakra-ui/react';
import MyRadio from '@/components/Radio/index';
import React, { useState } from 'react';
import { Box, Textarea, Button, Flex } from '@chakra-ui/react';
import { useForm } from 'react-hook-form';
import { useToast } from '@/hooks/useToast';
import { useRequest } from '@/hooks/useRequest';
import { getErrText } from '@/utils/tools';
import { vectorModelList } from '@/store/static';
import { postKbDataFromList } from '@/api/plugins/kb';
import { TrainingModeEnum } from '@/constants/plugin';
import { useUserStore } from '@/store/user';
import MyTooltip from '@/components/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
type ManualFormType = { q: string; a: string };
const ManualImport = ({ kbId }: { kbId: string }) => {
const { kbDetail } = useUserStore();
const maxToken = kbDetail.vectorModel?.maxToken || 2000;
const { register, handleSubmit, reset } = useForm({
defaultValues: { q: '', a: '' }
});
const { toast } = useToast();
const [qLen, setQLen] = useState(0);
const { mutate: onImportData, isLoading } = useRequest({
mutationFn: async (e: ManualFormType) => {
@@ -35,7 +40,6 @@ const ManualImport = ({ kbId }: { kbId: string }) => {
};
const { insertLen } = await postKbDataFromList({
kbId,
model: vectorModelList[0].model,
mode: TrainingModeEnum.index,
data: [data]
});
@@ -67,19 +71,37 @@ const ManualImport = ({ kbId }: { kbId: string }) => {
return (
<Box p={[4, 8]} h={'100%'} overflow={'overlay'}>
<Box display={'flex'} flexDirection={['column', 'row']}>
<Box flex={1} mr={[0, 4]} mb={[4, 0]} h={['50%', '100%']}>
<Box h={'30px'}>{'匹配的知识点'}</Box>
<Box flex={1} mr={[0, 4]} mb={[4, 0]} h={['50%', '100%']} position={'relative'}>
<Flex>
<Box h={'30px'}>{'匹配的知识点'}</Box>
<MyTooltip label={'被向量化的部分,通常是问题,也可以是一段陈述描述'}>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Flex>
<Textarea
placeholder={'匹配的知识点。这部分内容会被搜索,请把控内容的质量。总和最多 3000 字。'}
maxLength={3000}
placeholder={`匹配的知识点。这部分内容会被搜索,请把控内容的质量。最多 ${maxToken} 字。`}
maxLength={maxToken}
h={['250px', '500px']}
{...register(`q`, {
required: true
required: true,
onChange(e) {
setQLen(e.target.value.length);
}
})}
/>
<Box position={'absolute'} color={'myGray.500'} right={5} bottom={3} zIndex={99}>
{qLen}
</Box>
</Box>
<Box flex={1} h={['50%', '100%']}>
<Box h={'30px'}></Box>
<Flex>
<Box h={'30px'}>{'补充知识'}</Box>
<MyTooltip
label={'匹配的知识点被命中后,这部分内容会随匹配知识点一起注入模型,引导模型回答'}
>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Flex>
<Textarea
placeholder={
'补充知识。这部分内容不会被搜索,但会作为"匹配的知识点"的内容补充,你可以讲一些细节的内容填写在这里。总和最多 3000 字。'

View File

@@ -7,7 +7,7 @@ import { postKbDataFromList } from '@/api/plugins/kb';
import { splitText2Chunks } from '@/utils/file';
import { getErrText } from '@/utils/tools';
import { formatPrice } from '@/utils/user';
import { qaModelList } from '@/store/static';
import { qaModel } from '@/store/static';
import MyIcon from '@/components/Icon';
import CloseIcon from '@/components/Icon/close';
import DeleteIcon, { hoverDeleteStyles } from '@/components/Icon/delete';
@@ -20,9 +20,8 @@ import { useRouter } from 'next/router';
const fileExtension = '.txt, .doc, .docx, .pdf, .md';
const QAImport = ({ kbId }: { kbId: string }) => {
const model = qaModelList[0]?.model;
const unitPrice = qaModelList[0]?.price || 3;
const chunkLen = qaModelList[0].maxToken * 0.45;
const unitPrice = qaModel.price || 3;
const chunkLen = qaModel.maxToken * 0.45;
const theme = useTheme();
const router = useRouter();
const { toast } = useToast();
@@ -58,7 +57,6 @@ const QAImport = ({ kbId }: { kbId: string }) => {
for (let i = 0; i < chunks.length; i += step) {
const { insertLen } = await postKbDataFromList({
kbId,
model,
data: chunks.slice(i, i + step),
mode: TrainingModeEnum.qa,
prompt: prompt || '下面是一段长文本'
@@ -189,20 +187,20 @@ const QAImport = ({ kbId }: { kbId: string }) => {
<Box mb={2}>
QA {' '}
<MyTooltip
label={`可输入关于文件内容的范围介绍,例如:\n1. 关于 Laf 的介绍\n2. xxx的简历`}
label={`可输入关于文件内容的范围介绍,例如:\n1. Laf 的介绍\n2. xxx的简历\n最终会补全为: 关于{输入的内容}`}
forceShow
>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Box>
<Flex alignItems={'center'} fontSize={'sm'}>
<Box mr={2}></Box>
<Box mr={2}></Box>
<Input
flex={1}
placeholder={'Laf 云函数的介绍'}
bg={'myWhite.500'}
defaultValue={prompt}
onBlur={(e) => (e.target.value ? setPrompt(`下面是"${e.target.value}"`) : '')}
onBlur={(e) => (e.target.value ? setPrompt(`关于"${e.target.value}"`) : '')}
/>
</Flex>
</Box>

View File

@@ -7,7 +7,7 @@ import React, {
ForwardedRef
} from 'react';
import { useRouter } from 'next/router';
import { Box, Flex, Button, FormControl, IconButton, Input, Card } from '@chakra-ui/react';
import { Box, Flex, Button, FormControl, IconButton, Input } from '@chakra-ui/react';
import { QuestionOutlineIcon, DeleteIcon } from '@chakra-ui/icons';
import { delKbById, putKbById } from '@/api/plugins/kb';
import { useSelectFile } from '@/hooks/useSelectFile';
@@ -17,8 +17,6 @@ import { useConfirm } from '@/hooks/useConfirm';
import { UseFormReturn } from 'react-hook-form';
import { compressImg } from '@/utils/file';
import type { KbItemType } from '@/types/plugin';
import { vectorModelList } from '@/store/static';
import MySelect from '@/components/Select';
import Avatar from '@/components/Avatar';
import Tag from '@/components/Tag';
import MyTooltip from '@/components/MyTooltip';
@@ -138,7 +136,6 @@ const Info = (
useImperativeHandle(ref, () => ({
initInput: (tags: string) => {
console.log(tags);
if (InputRef.current) {
InputRef.current.value = tags;
}
@@ -153,20 +150,27 @@ const Info = (
</Box>
<Box flex={1}>{kbDetail._id}</Box>
</Flex>
<Flex mt={8} w={'100%'} alignItems={'center'}>
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
</Box>
<Box flex={[1, '0 0 300px']}>{getValues('vectorModel').name}</Box>
</Flex>
<Flex mt={5} w={'100%'} alignItems={'center'}>
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
</Box>
<Box flex={[1, '0 0 300px']}>
<Avatar
m={'auto'}
src={getValues('avatar')}
w={['32px', '40px']}
h={['32px', '40px']}
cursor={'pointer'}
title={'点击切换头像'}
onClick={onOpenSelectFile}
/>
<MyTooltip label={'点击切换头像'}>
<Avatar
m={'auto'}
src={getValues('avatar')}
w={['32px', '40px']}
h={['32px', '40px']}
cursor={'pointer'}
onClick={onOpenSelectFile}
/>
</MyTooltip>
</Box>
</Flex>
<FormControl mt={8} w={'100%'} display={'flex'} alignItems={'center'}>
@@ -180,27 +184,9 @@ const Info = (
})}
/>
</FormControl>
<Flex mt={8} w={'100%'} alignItems={'center'}>
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
</Box>
<Box flex={[1, '0 0 300px']}>
<MySelect
w={'100%'}
value={getValues('model')}
list={vectorModelList.map((item) => ({
label: item.name,
value: item.model
}))}
onchange={(res) => {
setValue('model', res);
}}
/>
</Box>
</Flex>
<Flex mt={8} alignItems={'center'} w={'100%'} flexWrap={'wrap'}>
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
<MyTooltip label={'用空格隔开多个标签,便于搜索'} forceShow>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
@@ -208,6 +194,7 @@ const Info = (
<Input
flex={[1, '0 0 300px']}
ref={InputRef}
defaultValue={getValues('tags')}
placeholder={'标签,使用空格分割。'}
maxLength={30}
onChange={(e) => {
@@ -226,7 +213,6 @@ const Info = (
))}
</Flex>
</Flex>
<Flex mt={5} w={'100%'} alignItems={'flex-end'}>
<Box flex={['0 0 90px', '0 0 160px']} w={0}></Box>
<Button

View File

@@ -1,15 +1,17 @@
import React, { useState, useCallback } from 'react';
import { Box, Flex, Button, Textarea, IconButton } from '@chakra-ui/react';
import { useForm } from 'react-hook-form';
import { postKbDataFromList, putKbDataById, delOneKbDataByDataId } from '@/api/plugins/kb';
import { insertData2Kb, putKbDataById, delOneKbDataByDataId } from '@/api/plugins/kb';
import { useToast } from '@/hooks/useToast';
import { TrainingModeEnum } from '@/constants/plugin';
import { getErrText } from '@/utils/tools';
import { vectorModelList } from '@/store/static';
import MyIcon from '@/components/Icon';
import MyModal from '@/components/MyModal';
import MyTooltip from '@/components/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { useUserStore } from '@/store/user';
import { useQuery } from '@tanstack/react-query';
export type FormData = { dataId?: string; a: string; q: string };
export type FormData = { dataId?: string; a: string; q: string; source?: string };
const InputDataModal = ({
onClose,
@@ -30,16 +32,20 @@ const InputDataModal = ({
const [loading, setLoading] = useState(false);
const { toast } = useToast();
const { kbDetail, getKbDetail } = useUserStore();
const { register, handleSubmit, reset } = useForm<FormData>({
defaultValues
});
const maxToken = kbDetail.vectorModel?.maxToken || 2000;
/**
* 确认导入新数据
*/
const sureImportData = useCallback(
async (e: FormData) => {
if (e.a.length + e.q.length >= 3000) {
if (e.q.length >= maxToken) {
toast({
title: '总长度超长了',
status: 'warning'
@@ -50,32 +56,24 @@ const InputDataModal = ({
try {
const data = {
dataId: '',
a: e.a,
q: e.q,
source: '手动录入'
};
const { insertLen } = await postKbDataFromList({
data.dataId = await insertData2Kb({
kbId,
model: vectorModelList[0].model,
mode: TrainingModeEnum.index,
data: [data]
data
});
if (insertLen === 0) {
toast({
title: '已存在完全一致的数据',
status: 'warning'
});
} else {
toast({
title: '导入数据成功,需要一段时间训练',
status: 'success'
});
reset({
a: '',
q: ''
});
}
toast({
title: '导入数据成功,需要一段时间训练',
status: 'success'
});
reset({
a: '',
q: ''
});
onSuccess(data);
} catch (err: any) {
@@ -86,7 +84,7 @@ const InputDataModal = ({
}
setLoading(false);
},
[kbId, onSuccess, reset, toast]
[kbId, maxToken, onSuccess, reset, toast]
);
const updateData = useCallback(
@@ -122,6 +120,11 @@ const InputDataModal = ({
[defaultValues.a, defaultValues.q, kbId, onClose, onSuccess, toast]
);
useQuery(['getKbDetail'], () => {
if (kbDetail._id === kbId) return null;
return getKbDetail(kbId);
});
return (
<MyModal
isOpen={true}
@@ -143,10 +146,15 @@ const InputDataModal = ({
pb={2}
>
<Box flex={1} mr={[0, 4]} mb={[4, 0]} h={['50%', '100%']}>
<Box h={'30px'}>{'匹配的知识点'}</Box>
<Flex>
<Box h={'30px'}>{'匹配的知识点'}</Box>
<MyTooltip label={'被向量化的部分,通常是问题,也可以是一段陈述描述'}>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Flex>
<Textarea
placeholder={'匹配的知识点。这部分内容会被搜索,请把控内容的质量。总和最多 3000 字。'}
maxLength={3000}
placeholder={`匹配的知识点。这部分内容会被搜索,请把控内容的质量,最多 ${maxToken} 字。`}
maxLength={maxToken}
resize={'none'}
h={'calc(100% - 30px)'}
{...register(`q`, {
@@ -155,7 +163,14 @@ const InputDataModal = ({
/>
</Box>
<Box flex={1} h={['50%', '100%']}>
<Box h={'30px'}></Box>
<Flex>
<Box h={'30px'}>{'补充知识'}</Box>
<MyTooltip
label={'匹配的知识点被命中后,这部分内容会随匹配知识点一起注入模型,引导模型回答'}
>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Flex>
<Textarea
placeholder={
'补充知识。这部分内容不会被搜索,但会作为"匹配的知识点"的内容补充,你可以讲一些细节的内容填写在这里。总和最多 3000 字。'

View File

@@ -10,13 +10,15 @@ import InputDataModal, { type FormData } from './InputDataModal';
import { useGlobalStore } from '@/store/global';
import { getErrText } from '@/utils/tools';
import { useToast } from '@/hooks/useToast';
import { vectorModelList } from '@/store/static';
import { customAlphabet } from 'nanoid';
import MyTooltip from '@/components/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { useUserStore } from '@/store/user';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
const Test = ({ kbId }: { kbId: string }) => {
const { kbDetail } = useUserStore();
const theme = useTheme();
const { toast } = useToast();
const { setLoading } = useGlobalStore();
@@ -31,7 +33,7 @@ const Test = ({ kbId }: { kbId: string }) => {
);
const { mutate, isLoading } = useRequest({
mutationFn: () => searchText({ model: vectorModelList[0].model, kbId, text: inputText.trim() }),
mutationFn: () => searchText({ kbId, text: inputText.trim() }),
onSuccess(res) {
const testItem = {
id: nanoid(),
@@ -75,12 +77,15 @@ const Test = ({ kbId }: { kbId: string }) => {
rows={6}
resize={'none'}
variant={'unstyled'}
maxLength={1000}
maxLength={kbDetail.vectorModel.maxToken}
placeholder="输入需要测试的文本"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
/>
<Flex justifyContent={'flex-end'}>
<Flex alignItems={'center'} justifyContent={'flex-end'}>
<Box mr={3} color={'myGray.500'}>
{inputText.length}
</Box>
<Button isDisabled={inputText === ''} isLoading={isLoading} onClick={mutate}>
</Button>
@@ -177,6 +182,7 @@ const Test = ({ kbId }: { kbId: string }) => {
'repeat(1,1fr)',
'repeat(1,1fr)',
'repeat(1,1fr)',
'repeat(1,1fr)',
'repeat(2,1fr)'
]}
gridGap={4}

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useRef } from 'react';
import React, { useCallback, useRef } from 'react';
import { useRouter } from 'next/router';
import { Box, Flex, IconButton, useTheme } from '@chakra-ui/react';
import { useToast } from '@/hooks/useToast';
@@ -71,8 +71,8 @@ const Detail = ({ kbId, currentTab }: { kbId: string; currentTab: `${TabEnum}` }
useQuery([kbId], () => getKbDetail(kbId), {
onSuccess(res) {
InfoRef.current?.initInput(res.tags);
form.reset(res);
InfoRef.current?.initInput(res.tags);
},
onError(err: any) {
router.replace(`/kb/list`);
@@ -165,12 +165,14 @@ const Detail = ({ kbId, currentTab }: { kbId: string; currentTab: `${TabEnum}` }
</Box>
)}
<Box flex={'1 0 0'} h={'100%'} pb={[4, 0]}>
{currentTab === TabEnum.data && <DataCard kbId={kbId} />}
{currentTab === TabEnum.import && <ImportData kbId={kbId} />}
{currentTab === TabEnum.test && <Test kbId={kbId} />}
{currentTab === TabEnum.info && <Info ref={InfoRef} kbId={kbId} form={form} />}
</Box>
{!!kbDetail._id && (
<Box flex={'1 0 0'} h={'100%'} pb={[4, 0]}>
{currentTab === TabEnum.data && <DataCard kbId={kbId} />}
{currentTab === TabEnum.import && <ImportData kbId={kbId} />}
{currentTab === TabEnum.test && <Test kbId={kbId} />}
{currentTab === TabEnum.info && <Info ref={InfoRef} kbId={kbId} form={form} />}
</Box>
)}
</Box>
</PageContainer>
);

View File

@@ -0,0 +1,165 @@
import React, { useCallback, useState, useRef } from 'react';
import { Box, Flex, Button, ModalHeader, ModalFooter, ModalBody, Input } from '@chakra-ui/react';
import { useSelectFile } from '@/hooks/useSelectFile';
import { useForm } from 'react-hook-form';
import { compressImg } from '@/utils/file';
import { getErrText } from '@/utils/tools';
import { useToast } from '@/hooks/useToast';
import { useRouter } from 'next/router';
import { useGlobalStore } from '@/store/global';
import { useRequest } from '@/hooks/useRequest';
import Avatar from '@/components/Avatar';
import MyTooltip from '@/components/MyTooltip';
import MyModal from '@/components/MyModal';
import { postCreateKb } from '@/api/plugins/kb';
import type { CreateKbParams } from '@/api/request/kb';
import { vectorModelList } from '@/store/static';
import MySelect from '@/components/Select';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import Tag from '@/components/Tag';
const CreateModal = ({ onClose }: { onClose: () => void }) => {
const [refresh, setRefresh] = useState(false);
const { toast } = useToast();
const router = useRouter();
const { isPc } = useGlobalStore();
const { register, setValue, getValues, handleSubmit } = useForm<CreateKbParams>({
defaultValues: {
avatar: '/icon/logo.svg',
name: '',
tags: [],
vectorModel: vectorModelList[0].model
}
});
const InputRef = useRef<HTMLInputElement>(null);
const { File, onOpen: onOpenSelectFile } = useSelectFile({
fileType: '.jpg,.png',
multiple: false
});
const onSelectFile = useCallback(
async (e: File[]) => {
const file = e[0];
if (!file) return;
try {
const src = await compressImg({
file,
maxW: 100,
maxH: 100
});
setValue('avatar', src);
setRefresh((state) => !state);
} catch (err: any) {
toast({
title: getErrText(err, '头像选择异常'),
status: 'warning'
});
}
},
[setValue, toast]
);
/* create a new kb and router to it */
const { mutate: onclickCreate, isLoading: creating } = useRequest({
mutationFn: async (data: CreateKbParams) => {
const id = await postCreateKb(data);
return id;
},
successToast: '创建成功',
errorToast: '创建知识库出现意外',
onSuccess(id) {
router.push(`/kb/detail?kbId=${id}`);
}
});
return (
<MyModal isOpen onClose={onClose} isCentered={!isPc} w={'400px'}>
<ModalHeader fontSize={'2xl'}></ModalHeader>
<ModalBody>
<Box color={'myGray.800'} fontWeight={'bold'}>
</Box>
<Flex mt={3} alignItems={'center'}>
<MyTooltip label={'点击设置头像'}>
<Avatar
flexShrink={0}
src={getValues('avatar')}
w={['28px', '32px']}
h={['28px', '32px']}
cursor={'pointer'}
borderRadius={'md'}
onClick={onOpenSelectFile}
/>
</MyTooltip>
<Input
ml={3}
flex={1}
autoFocus
bg={'myWhite.600'}
{...register('name', {
required: '知识库名称不能为空~'
})}
/>
</Flex>
<Flex mt={6} alignItems={'center'}>
<Box flex={'0 0 80px'}></Box>
<Box flex={1}>
<MySelect
w={'100%'}
value={getValues('vectorModel')}
list={vectorModelList.map((item) => ({
label: item.name,
value: item.model
}))}
onchange={(e) => {
setValue('vectorModel', e);
setRefresh((state) => !state);
}}
/>
</Box>
</Flex>
<Flex mt={6} alignItems={'center'} w={'100%'}>
<Box flex={'0 0 80px'}>
<MyTooltip label={'用空格隔开多个标签,便于搜索'} forceShow>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Box>
<Input
flex={1}
ref={InputRef}
placeholder={'标签,使用空格分割。'}
maxLength={30}
onChange={(e) => {
setValue('tags', e.target.value.split(' '));
setRefresh(!refresh);
}}
/>
</Flex>
<Flex mt={2} flexWrap={'wrap'}>
{getValues('tags')
.filter((item) => item)
.map((item, i) => (
<Tag mr={2} mb={2} key={i} whiteSpace={'nowrap'}>
{item}
</Tag>
))}
</Flex>
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={3} onClick={onClose}>
</Button>
<Button isLoading={creating} onClick={handleSubmit((data) => onclickCreate(data))}>
</Button>
</ModalFooter>
<File onSelect={onSelectFile} />
</MyModal>
);
};
export default CreateModal;

View File

@@ -1,5 +1,14 @@
import React, { useCallback } from 'react';
import { Box, Card, Flex, Grid, useTheme, Button, IconButton } from '@chakra-ui/react';
import {
Box,
Card,
Flex,
Grid,
useTheme,
Button,
IconButton,
useDisclosure
} from '@chakra-ui/react';
import { useRouter } from 'next/router';
import { useUserStore } from '@/store/user';
import PageContainer from '@/components/PageContainer';
@@ -7,12 +16,14 @@ import { useConfirm } from '@/hooks/useConfirm';
import { AddIcon } from '@chakra-ui/icons';
import { useQuery } from '@tanstack/react-query';
import { useToast } from '@/hooks/useToast';
import { delKbById, postCreateKb } from '@/api/plugins/kb';
import { useRequest } from '@/hooks/useRequest';
import { delKbById } from '@/api/plugins/kb';
import Avatar from '@/components/Avatar';
import MyIcon from '@/components/Icon';
import Tag from '@/components/Tag';
import { serviceSideProps } from '@/utils/i18n';
import dynamic from 'next/dynamic';
const CreateModal = dynamic(() => import('./component/CreateModal'), { ssr: false });
const Kb = () => {
const theme = useTheme();
@@ -24,7 +35,13 @@ const Kb = () => {
});
const { myKbList, loadKbList, setKbList } = useUserStore();
useQuery(['loadKbList'], () => loadKbList());
const {
isOpen: isOpenCreateModal,
onOpen: onOpenCreateModal,
onClose: onCloseCreateModal
} = useDisclosure();
const { refetch } = useQuery(['loadKbList'], () => loadKbList());
/* 点击删除 */
const onclickDelKb = useCallback(
@@ -46,32 +63,13 @@ const Kb = () => {
[toast, setKbList, myKbList]
);
/* create a new kb and router to it */
const { mutate: onclickCreate, isLoading } = useRequest({
mutationFn: async () => {
const name = `知识库${myKbList.length + 1}`;
const id = await postCreateKb({ name });
return id;
},
successToast: '创建成功',
errorToast: '创建知识库出现意外',
onSuccess(id) {
router.push(`/kb/detail?kbId=${id}`);
}
});
return (
<PageContainer>
<Flex pt={3} px={5} alignItems={'center'}>
<Box flex={1} className="textlg" letterSpacing={1} fontSize={'24px'} fontWeight={'bold'}>
</Box>
<Button
isLoading={isLoading}
leftIcon={<AddIcon />}
variant={'base'}
onClick={onclickCreate}
>
<Button leftIcon={<AddIcon />} variant={'base'} onClick={onOpenCreateModal}>
</Button>
</Flex>
@@ -141,6 +139,10 @@ const Kb = () => {
))}
</Flex>
</Box>
<Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}>
<MyIcon mr={1} name="kbTest" w={'12px'} />
<Box color={'myGray.500'}>{kb.vectorModel.name}</Box>
</Flex>
</Card>
))}
</Grid>
@@ -153,6 +155,7 @@ const Kb = () => {
</Flex>
)}
<ConfirmModal />
{isOpenCreateModal && <CreateModal onClose={onCloseCreateModal} />}
</PageContainer>
);
};

View File

@@ -57,7 +57,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
username,
code,
password,
inviterId: localStorage.getItem('inviterId') || ''
inviterId: localStorage.getItem('inviterId') || undefined
})
);
toast({

View File

@@ -46,7 +46,7 @@ const provider = ({ code }: { code: string }) => {
if (loginStore.provider === 'git') {
return gitLogin({
code,
inviterId: localStorage.getItem('inviterId') || ''
inviterId: localStorage.getItem('inviterId') || undefined
});
}
return null;

View File

@@ -1,5 +1,5 @@
import { TrainingData } from '@/service/mongo';
import { pushSplitDataBill } from '@/service/events/pushBill';
import { pushQABill } from '@/service/events/pushBill';
import { pushDataToKb } from '@/pages/api/openapi/kb/pushData';
import { TrainingModeEnum } from '@/constants/plugin';
import { ERROR_ENUM } from '../errorCode';
@@ -60,19 +60,18 @@ export async function generateQA(): Promise<any> {
// 请求 chatgpt 获取回答
const response = await Promise.all(
[data.q].map((text) => {
const modelTokenLimit =
chatModels.find((item) => item.model === data.model)?.contextMaxToken || 16000;
const modelTokenLimit = global.qaModel.maxToken || 16000;
const messages: ChatCompletionRequestMessage[] = [
{
role: 'system',
content: `你是出题人.
${data.prompt || '我会发送一段长文本'}.
从中提取出 25 个问题和答案. 答案详细完整. 按下面格式返回:
content: `我会给你发送一段长文本,${
data.prompt ? `${data.prompt}` : ''
}请学习它,并用 markdown 格式给出 25 个问题和答案,问题可以多样化、自由扩展;答案详细、解读到位,答案包含普通文本、链接、代码、表格、公示、媒体链接等。按下面 QA 问答格式返回:
Q1:
A1:
Q2:
A2:
...`
……`
},
{
role: 'user',
@@ -88,7 +87,7 @@ A2:
return chatAPI
.createChatCompletion(
{
model: data.model,
model: global.qaModel.model,
temperature: 0.8,
messages,
stream: false,
@@ -106,12 +105,12 @@ A2:
const result = formatSplitText(answer || ''); // 格式化后的QA对
console.log(`split result length: `, result.length);
// 计费
pushSplitDataBill({
userId: data.userId,
totalTokens,
model: data.model,
appName: 'QA 拆分'
});
result.length > 0 &&
pushQABill({
userId: data.userId,
totalTokens,
appName: 'QA 拆分'
});
return {
rawContent: answer,
result
@@ -135,7 +134,6 @@ A2:
source: data.source
})),
userId,
model: global.vectorModels[0].model,
mode: TrainingModeEnum.index
});

View File

@@ -1,4 +1,3 @@
import { openaiAccountError } from '../errorCode';
import { insertKbItem } from '@/service/pg';
import { getVector } from '@/pages/api/openapi/plugin/vector';
import { TrainingData } from '../models/trainingData';
@@ -38,7 +37,7 @@ export async function generateVector(): Promise<any> {
q: 1,
a: 1,
source: 1,
model: 1
vectorModel: 1
});
// task preemption
@@ -54,14 +53,14 @@ export async function generateVector(): Promise<any> {
dataItems = [
{
q: data.q,
a: data.a
q: data.q.replace(/[\x00-\x1F]/g, ' '),
a: data.a.replace(/[\x00-\x1F]/g, ' ')
}
];
// 生成词向量
const { vectors } = await getVector({
model: data.model,
model: data.vectorModel,
input: dataItems.map((item) => item.q),
userId
});
@@ -110,28 +109,30 @@ export async function generateVector(): Promise<any> {
// err vector data
if (err?.code === 500) {
await TrainingData.findByIdAndRemove(trainingId);
await TrainingData.findByIdAndDelete(trainingId);
return generateVector();
}
// 账号余额不足,删除任务
if (userId && err === ERROR_ENUM.insufficientQuota) {
sendInform({
type: 'system',
title: '索引生成任务中止',
content:
'由于账号余额不足,索引生成任务中止,重新充值后将会继续。暂停的任务将在 7 天后被删除。',
userId
});
console.log('余额不足,暂停向量生成任务');
await TrainingData.updateMany(
{
try {
sendInform({
type: 'system',
title: '索引生成任务中止',
content:
'由于账号余额不足,索引生成任务中止,重新充值后将会继续。暂停的任务将在 7 天后被删除。',
userId
},
{
lockTime: new Date('2999/5/5')
}
);
});
console.log('余额不足,暂停向量生成任务');
await TrainingData.updateMany(
{
userId
},
{
lockTime: new Date('2999/5/5')
}
);
} catch (error) {}
return generateVector();
}

View File

@@ -76,13 +76,11 @@ export const updateShareChatBill = async ({
}
};
export const pushSplitDataBill = async ({
export const pushQABill = async ({
userId,
totalTokens,
model,
appName
}: {
model: string;
userId: string;
totalTokens: number;
appName: string;
@@ -95,7 +93,7 @@ export const pushSplitDataBill = async ({
await connectToDatabase();
// 获取模型单价格, 都是用 gpt35 拆分
const unitPrice = global.chatModels.find((item) => item.model === model)?.price || 3;
const unitPrice = global.qaModel.price || 3;
// 计算价格
const total = unitPrice * totalTokens;

View File

@@ -65,7 +65,7 @@ const AppSchema = new Schema({
},
searchSimilarity: {
type: Number,
default: 0.8
default: 0.4
},
searchLimit: {
type: Number,

View File

@@ -37,6 +37,16 @@ const ChatItemSchema = new Schema({
type: String,
default: ''
},
userFeedback: {
type: String
},
adminFeedback: {
type: {
kbId: String,
dataId: String,
content: String
}
},
[TaskResponseKeyEnum.responseData]: {
type: [
{
@@ -65,6 +75,7 @@ try {
ChatItemSchema.index({ userId: 1 });
ChatItemSchema.index({ appId: 1 });
ChatItemSchema.index({ chatId: 1 });
ChatItemSchema.index({ userFeedback: 1 });
} catch (error) {
console.log(error);
}

View File

@@ -19,7 +19,7 @@ const kbSchema = new Schema({
type: String,
required: true
},
model: {
vectorModel: {
type: String,
required: true,
default: 'text-embedding-ada-002'

View File

@@ -28,9 +28,10 @@ const TrainingDataSchema = new Schema({
enum: Object.keys(TrainingTypeMap),
required: true
},
model: {
vectorModel: {
type: String,
required: true
required: true,
default: 'text-embedding-ada-002'
},
prompt: {
// qa split prompt

View File

@@ -181,7 +181,7 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
tokens: totalTokens,
question: userChatInput,
answer: answerText,
maxToken,
maxToken: max_tokens,
quoteList: filterQuoteQA,
completeMessages
},
@@ -211,8 +211,10 @@ function filterQuote({
const quotePrompt =
filterQuoteQA.length > 0
? `"""${filterQuoteQA
.map((item) => (item.a ? `${item.q}\n${item.a}` : item.q))
.join('\n\n')}"""`
.map((item) =>
item.a ? `{instruction:"${item.q}",output:"${item.a}"}` : `{instruction:"${item.q}"}`
)
.join('\n')}"""`
: '';
return {
@@ -236,12 +238,15 @@ function getChatMessages({
model: ChatModelItemType;
}) {
const limitText = (() => {
if (limitPrompt)
return `Use the provided content delimited by triple quotes to answer questions.${limitPrompt}`;
if (quotePrompt && !limitPrompt) {
return `Use the provided content delimited by triple quotes to answer questions.Your task is to answer the question using only the provided content. If the content does not contain the information needed to answer this question then simply write: "你的问题没有在知识库中体现".`;
if (!quotePrompt) {
return limitPrompt;
}
return ``;
const defaultPrompt =
'三引号是我提供给你的专属知识它们拥有最高优先级。instruction 是相关介绍output 是预期回答,使用引用内容来回答我下面的问题。';
if (limitPrompt) {
return `${defaultPrompt}${limitPrompt}`;
}
return `${defaultPrompt}\n回答内容限制你仅回答三引号中提及的内容下面我提出的问题与引用内容无关时你可以直接回复: "你的问题没有在知识库中体现"`;
})();
const messages: ChatItemType[] = [
@@ -261,6 +266,7 @@ function getChatMessages({
}
]
: []),
...history,
...(limitText
? [
{
@@ -269,7 +275,6 @@ function getChatMessages({
}
]
: []),
...history,
{
obj: ChatRoleEnum.Human,
value: userChatInput

View File

@@ -1,5 +1,5 @@
import { PgClient } from '@/service/pg';
import type { ChatHistoryItemResType, ChatItemType } from '@/types/chat';
import type { ChatHistoryItemResType } from '@/types/chat';
import { ChatModuleEnum, TaskResponseKeyEnum } from '@/constants/chat';
import { getVector } from '@/pages/api/openapi/plugin/vector';
import { countModelPrice } from '@/service/events/pushBill';
@@ -21,7 +21,7 @@ export type KBSearchResponse = {
};
export async function dispatchKBSearch(props: Record<string, any>): Promise<KBSearchResponse> {
const { kbList = [], similarity = 0.8, limit = 5, userChatInput } = props as KBSearchProps;
const { kbList = [], similarity = 0.4, limit = 5, userChatInput } = props as KBSearchProps;
if (kbList.length === 0) {
return Promise.reject("You didn't choose the knowledge base");
@@ -32,7 +32,7 @@ export async function dispatchKBSearch(props: Record<string, any>): Promise<KBSe
}
// get vector
const vectorModel = global.vectorModels[0];
const vectorModel = kbList[0]?.vectorModel || global.vectorModels[0];
const { vectors, tokenLen } = await getVector({
model: vectorModel.model,
input: [userChatInput]

View File

@@ -142,7 +142,9 @@ class Pg {
}
const fields = props.values[0].map((item) => item.key).join(',');
const sql = `INSERT INTO ${table} (${fields}) VALUES ${this.getInsertValStr(props.values)} `;
const sql = `INSERT INTO ${table} (${fields}) VALUES ${this.getInsertValStr(
props.values
)} RETURNING id`;
const pg = await connectPg();
return pg.query(sql);
}

View File

@@ -2,13 +2,17 @@ export const getChatModel = (model?: string) => {
return global.chatModels.find((item) => item.model === model);
};
export const getVectorModel = (model?: string) => {
return global.vectorModels.find((item) => item.model === model);
};
export const getQAModel = (model?: string) => {
return global.qaModels.find((item) => item.model === model);
};
export const getModel = (model?: string) => {
return [...global.chatModels, ...global.vectorModels, ...global.qaModels].find(
(item) => item.model === model
return (
global.vectorModels.find((item) => item.model === model) || {
model: 'UnKnow',
name: 'UnKnow',
defaultToken: 500,
price: 0,
maxToken: 3000
}
);
};
export const getModel = (model?: string) => {
return [...global.chatModels, ...global.vectorModels].find((item) => item.model === model);
};

View File

@@ -92,9 +92,9 @@ export const sseResponse = ({
/* add logger */
export const addLog = {
info: (msg: string, obj?: Record<string, any>) => {
global.logger.info(msg, { meta: obj });
global.logger?.info(msg, { meta: obj });
},
error: (msg: string, obj?: Record<string, any>) => {
global.logger.error(msg, { meta: obj });
global.logger?.error(msg, { meta: obj });
}
};

View File

@@ -9,7 +9,12 @@ import { delay } from '@/utils/tools';
import { FeConfigsType } from '@/types';
export let chatModelList: ChatModelItemType[] = [];
export let qaModelList: QAModelItemType[] = [];
export let qaModel: QAModelItemType = {
model: 'gpt-3.5-turbo-16k',
name: 'GPT35-16k',
maxToken: 16000,
price: 0
};
export let vectorModelList: VectorModelItemType[] = [];
export let feConfigs: FeConfigsType = {};
@@ -20,7 +25,7 @@ export const clientInitData = async (): Promise<InitDateResponse> => {
const res = await getInitData();
chatModelList = res.chatModels;
qaModelList = res.qaModels;
qaModel = res.qaModel;
vectorModelList = res.vectorModels;
feConfigs = res.feConfigs;

View File

@@ -114,5 +114,6 @@ export type AppLogsListItemType = {
time: Date;
title: string;
messageCount: number;
callbackCount: number;
feedbackCount: number;
markCount: number;
};

View File

@@ -2,6 +2,7 @@ import { ChatRoleEnum } from '@/constants/chat';
import type { InitChatResponse, InitShareChatResponse } from '@/api/response/chat';
import { TaskResponseKeyEnum } from '@/constants/chat';
import { ClassifyQuestionAgentItemType } from './app';
import { ChatItemSchema } from './mongoSchema';
export type ExportChatType = 'md' | 'pdf' | 'html';
@@ -9,6 +10,8 @@ export type ChatItemType = {
dataId?: string;
obj: `${ChatRoleEnum}`;
value: string;
userFeedback?: string;
adminFeedback?: ChatItemSchema['adminFeedback'];
[TaskResponseKeyEnum.responseData]?: ChatHistoryItemResType[];
};

View File

@@ -51,7 +51,7 @@ declare global {
var feConfigs: FeConfigsType;
var systemEnv: SystemEnvType;
var chatModels: ChatModelItemType[];
var qaModels: QAModelItemType[];
var qaModel: QAModelItemType;
var vectorModels: VectorModelItemType[];
interface Window {

View File

@@ -17,5 +17,7 @@ export type QAModelItemType = {
export type VectorModelItemType = {
model: string;
name: string;
defaultToken: number;
price: number;
maxToken: number;
};

View File

@@ -72,7 +72,7 @@ export interface TrainingDataSchema {
kbId: string;
expireAt: Date;
lockTime: Date;
model: string;
vectorModel: string;
mode: `${TrainingModeEnum}`;
prompt: string;
q: string;
@@ -102,6 +102,12 @@ export interface ChatItemSchema extends ChatItemType {
userId: string;
appId: string;
time: Date;
userFeedback?: string;
adminFeedback?: {
kbId: string;
dataId: string;
content: string;
};
}
export type BillListItemType = {
@@ -135,7 +141,7 @@ export interface OpenApiSchema {
userId: string;
createTime: Date;
lastUsedTime?: Date;
apiKey: String;
apiKey: string;
}
export interface PromotionRecordSchema {
@@ -164,7 +170,7 @@ export interface kbSchema {
updateTime: Date;
avatar: string;
name: string;
model: string;
vectorModel: string;
tags: string[];
}

View File

@@ -1,16 +1,22 @@
import { VectorModelItemType } from './model';
import type { kbSchema } from './mongoSchema';
export type SelectedKbType = { kbId: string }[];
export type SelectedKbType = { kbId: string; vectorModel: VectorModelItemType }[];
export type KbListItemType = {
_id: string;
avatar: string;
name: string;
tags: string[];
vectorModel: VectorModelItemType;
};
/* kb type */
export interface KbItemType extends kbSchema {
totalData: number;
export interface KbItemType {
_id: string;
avatar: string;
name: string;
userId: string;
vectorModel: VectorModelItemType;
tags: string;
}

View File

@@ -49,7 +49,7 @@ export const getDefaultAppForm = (): EditFormType => {
},
kb: {
list: [],
searchSimilarity: 0.8,
searchSimilarity: 0.4,
searchLimit: 5,
searchEmptyText: ''
},

View File

@@ -148,7 +148,7 @@ export const fileDownload = ({
* maxLen > overlapLen
*/
export const splitText2Chunks = ({ text, maxLen }: { text: string; maxLen: number }) => {
const overlapLen = Math.floor(maxLen * 0.3); // Overlap length
const overlapLen = Math.floor(maxLen * 0.25); // Overlap length
try {
const splitTexts = text.split(/(?<=[。!?;.!?;])/g);
@@ -173,9 +173,16 @@ export const splitText2Chunks = ({ text, maxLen }: { text: string; maxLen: numbe
chunks.push(chunk);
}
const enc = getOpenAiEncMap();
const encodeText = enc.encode(chunks.join(''));
const tokens = encodeText.length;
const tokens = (() => {
try {
const enc = getOpenAiEncMap();
const encodeText = enc.encode(chunks.join(''));
const tokens = encodeText.length;
return tokens;
} catch (error) {
return chunks.join('').length;
}
})();
return {
chunks,
@@ -274,5 +281,8 @@ export const simpleText = (text: string) => {
text = text.replace(/([\u4e00-\u9fa5])\s+([\u4e00-\u9fa5])/g, '$1$2');
text = text.replace(/\n{2,}/g, '\n');
text = text.replace(/\s{2,}/g, ' ');
text = text.replace(/[\x00-\x1F]/g, ' ');
return text;
};

View File

@@ -1,599 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="256px" height="256px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve"> <image id="image0" width="256" height="256" x="0" y="0"
href="data:image/png;base64,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
MDqUEekAAAAASUVORK5CYII=" />
<svg width="26" height="26" viewBox="0 0 1041 1348" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M340.837 0.33933L681.068 0.338989V0.455643C684.032 0.378397 686.999 0.339702 689.967 0.339702C735.961 0.3397 781.504 9.62899 823.997 27.6772C866.49 45.7254 905.099 72.1791 937.622 105.528C970.144 138.877 995.942 178.467 1013.54 222.04C1031.14 265.612 1040.2 312.312 1040.2 359.474L340.836 359.474L340.836 1347.84C296.157 1347.84 251.914 1338.55 210.636 1320.49C169.357 1302.43 131.85 1275.95 100.257 1242.58C68.6636 1209.21 43.6023 1169.59 26.5041 1125.99C11.3834 1087.43 2.75216 1046.42 0.957956 1004.81H0.605869L0.605897 368.098H0.70363C0.105752 341.831 2.23741 315.443 7.14306 289.411C20.2709 219.745 52.6748 155.754 100.257 105.528C147.839 55.3017 208.462 21.0975 274.461 7.24017C296.426 2.62833 318.657 0.339101 340.837 0.33933Z" fill="url(#paint0_linear_1172_228)"/>
<path d="M633.639 904.645H513.029V576.37H635.422V576.377C678.161 576.607 720.454 585.093 759.951 601.37C799.997 617.874 836.384 642.064 867.033 672.559C897.683 703.054 921.996 739.257 938.583 779.101C955.171 818.944 963.709 861.648 963.709 904.775H633.639V904.645Z" fill="url(#paint1_linear_1172_228)"/>
<defs>
<linearGradient id="paint0_linear_1172_228" x1="520.404" y1="0.338989" x2="520.404" y2="1347.84" gradientUnits="userSpaceOnUse">
<stop stop-color="#326DFF"/>
<stop offset="1" stop-color="#8EAEFF"/>
</linearGradient>
<linearGradient id="paint1_linear_1172_228" x1="738.369" y1="576.37" x2="738.369" y2="904.775" gradientUnits="userSpaceOnUse">
<stop stop-color="#326DFF"/>
<stop offset="1" stop-color="#8EAEFF"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

View File

@@ -3,8 +3,8 @@
"baseUrl": ".",
"paths": {
"*": [
"../../../../.cache/hugo_cache/modules/filecache/modules/pkg/mod/github.com/gohugoio/hugo-mod-jslibs-dist/popperjs/v2@v2.21100.20000/package/dist/cjs/popper.js/*",
"../../../../.cache/hugo_cache/modules/filecache/modules/pkg/mod/github.com/twbs/bootstrap@v5.3.0+incompatible/js/*"
"../../../../../.cache/hugo_cache/modules/filecache/modules/pkg/mod/github.com/gohugoio/hugo-mod-jslibs-dist/popperjs/v2@v2.21100.20000/package/dist/cjs/popper.js/*",
"../../../../../.cache/hugo_cache/modules/filecache/modules/pkg/mod/github.com/twbs/bootstrap@v5.3.0+incompatible/js/*"
]
}
}

View File

@@ -0,0 +1,8 @@
---
weight: 0
title: '本地模型使用'
description: 'FastGPT 对接本地模型'
icon: 'model_training'
draft: false
images: []
---

View File

@@ -1,7 +1,7 @@
---
title: "接入 ChatGLM2-6B"
description: " 将 FastGPT 接入私有化模型 ChatGLM2-6B"
icon: "model_training"
title: '接入 ChatGLM2-6B'
description: ' 将 FastGPT 接入私有化模型 ChatGLM2-6B'
icon: 'model_training'
draft: false
toc: true
weight: 753
@@ -23,30 +23,31 @@ ChatGLM2-6B 是开源中英双语对话模型 ChatGLM-6B 的第二代版本,
## 推荐配置
依据官方数据,同样是生成 8192 长度,量化等级为 FP16 要占用 12.8GB 显存、int8 为 8.1GB 显存、int4 为 5.1GB 显存,量化后会稍微影响性能,但不多。
依据官方数据,同样是生成 8192 长度,量化等级为 FP16 要占用 12.8GB 显存、int8 为 8.1GB 显存、int4 为 5.1GB 显存,量化后会稍微影响性能,但不多。
因此推荐配置如下:
{{< table "table-hover table-striped" >}}
| 类型 | 内存 | 显存 | 硬盘空间 | 启动命令 |
| 类型 | 内存 | 显存 | 硬盘空间 | 启动命令 |
|------|---------|---------|----------|--------------------------|
| fp16 | >=16GB | >=16GB | >=25GB | python openai_api.py 16 |
| int8 | >=16GB | >=9GB | >=25GB | python openai_api.py 8 |
| int4 | >=16GB | >=6GB | >=25GB | python openai_api.py 4 |
| fp16 | >=16GB | >=16GB | >=25GB | python openai_api.py 16 |
| int8 | >=16GB | >=9GB | >=25GB | python openai_api.py 8 |
| int4 | >=16GB | >=6GB | >=25GB | python openai_api.py 4 |
{{< /table >}}
## 环境配置
+ Python 3.8.10
+ CUDA 11.8
+ 科学上网环境
- Python 3.8.10
- CUDA 11.8
- 科学上网环境
## 部署步骤
1. 根据上面的环境配置配置好环境,具体教程自行 GPT
2. 在命令行输入命令 `pip install -r requirments.txt`
3. 打开你需要启动的 py 文件,在代码的第 76 行配置 token这里的 token 只是加一层验证,防止接口被人盗用
4. 执行命令 `python openai_api.py 16`。这里的数字根据上面的配置进行选择。
2. 下载 [python 文件](https://github.com/labring/FastGPT/blob/main/files/models/ChatGLM2/openai_api.py)
3. 在命令行输入命令 `pip install -r requirments.txt`
4. 打开你需要启动的 py 文件,在代码的第 76 行配置 token这里的 token 只是加一层验证,防止接口被人盗用;
5. 执行命令 `python openai_api.py 16`。这里的数字根据上面的配置进行选择。
然后等待模型下载,直到模型加载完毕为止。如果出现报错先问 GPT。
@@ -60,7 +61,11 @@ ChatGLM2-6B 是开源中英双语对话模型 ChatGLM-6B 的第二代版本,
```bash
OPENAI_BASE_URL=http://127.0.0.1:6006/v1
OPENAIKEY=sk-aaabbbcccdddeeefffggghhhiiijjjkkk # 这里是你在代码中配置的 token这里的 OPENAIKEY 可以任意填写
CHAT_API_KEY=sk-aaabbbcccdddeeefffggghhhiiijjjkkk # 这里是你在代码中配置的 token这里的 OPENAIKEY 可以任意填写
```
这样就成功接入 ChatGLM2-6B 了。
## 注意
1. docker 部署时,给的推荐配置是组网模型,无法连接到本地的网络,以为这无法请求 0.0.0.0:6006。可以使用 host 模式,或者将模型发布到服务器上,并通过 oneapi 引入该模型。

View File

@@ -0,0 +1,81 @@
---
title: '接入 M3E 向量模型'
description: ' 将 FastGPT 接入私有化模型 M3E'
icon: 'model_training'
draft: false
toc: true
weight: 100
---
## 前言
FastGPT 默认使用了 openai 的 embedding 向量模型,如果你想私有部署的话,可以使用 M3E 向量模型进行替换。M3E 向量模型属于小模型资源使用不高CPU 也可以运行。下面教程是基于 “睡大觉” 同学提供的一个 CPU 版本的镜像。
## 部署镜像
镜像名: `stawky/m3e-large-api:latest`
端口号: 6008
## 接入 OneAPI
添加一个渠道,参数如下:
![](/imgs/model-m3e1.png)
## 测试
curl 例子:
```bash
curl --location --request POST 'https://domain/v1/embeddings' \
--header 'Authorization: Bearer sk-key' \
--header 'Content-Type: application/json' \
--data-raw '{
"model": "m3e",
"input": ["laf是什么"]
}'
```
Authorization 为 sk-key。model 为刚刚在 OneAPI 填写的自定义模型。
## 接入 FastGPT
修改 config.json 配置文件,在 VectorModels 中加入 M3E 模型:
```json
"VectorModels": [
{
"model": "text-embedding-ada-002",
"name": "Embedding-2",
"price": 0.2,
"defaultToken": 500,
"maxToken": 3000
},
{
"model": "m3e",
"name": "M3E测试使用",
"price": 0.1,
"defaultToken": 500,
"maxToken": 1800
}
],
```
## 测试使用
1. 创建知识库时候选择 M3E 模型。
注意,一旦选择后,知识库将无法修改向量模型。
![](/imgs/model-m3e2.png)
2. 导入数据
3. 搜索测试
![](/imgs/model-m3e3.png)
4. 应用绑定知识库
注意,应用只能绑定同一个向量模型的知识库,不能跨模型绑定。并且,需要注意调整相似度,不同向量模型的相似度(距离)会有所区别,需要自行测试实验。
![](/imgs/model-m3e4.png)

View File

@@ -162,74 +162,7 @@ docker-compose up -d
### 如何自定义配置文件?
需要在 `docker-compose.yml` 同级目录创建一个 `config.json` 文件,内容如下:
```json
{
"FeConfig": {
"show_emptyChat": true,
"show_register": false,
"show_appStore": false,
"show_userDetail": false,
"show_git": true,
"systemTitle": "FastGPT",
"authorText": "Made by FastGPT Team.",
"gitLoginKey": "",
"scripts": []
},
"SystemParams": {
"gitLoginSecret": "",
"vectorMaxProcess": 15,
"qaMaxProcess": 15,
"pgIvfflatProbe": 20
},
"plugins": {},
"ChatModels": [
{
"model": "gpt-3.5-turbo",
"name": "GPT35-4k",
"contextMaxToken": 4000,
"quoteMaxToken": 2000,
"maxTemperature": 1.2,
"price": 0,
"defaultSystem": ""
},
{
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"contextMaxToken": 16000,
"quoteMaxToken": 8000,
"maxTemperature": 1.2,
"price": 0,
"defaultSystem": ""
},
{
"model": "gpt-4",
"name": "GPT4-8k",
"contextMaxToken": 8000,
"quoteMaxToken": 4000,
"maxTemperature": 1.2,
"price": 0,
"defaultSystem": ""
}
],
"QAModels": [
{
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"maxToken": 16000,
"price": 0
}
],
"VectorModels": [
{
"model": "text-embedding-ada-002",
"name": "Embedding-2",
"price": 0
}
]
}
```
需要在 `docker-compose.yml` 同级目录创建一个 `config.json` 文件,内容参考: [配置详解](/docs/installation/reference/configuration/)
然后修改 `docker-compose.yml` 中的 `fastgpt` 容器内容,增加挂载选项即可:

View File

@@ -96,19 +96,19 @@ weight: 751
"defaultSystem": ""
}
],
"QAModels": [
{
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"maxToken": 16000,
"price": 0
}
],
"QAModel": {
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"maxToken": 16000,
"price": 0
},
"VectorModels": [
{
"model": "text-embedding-ada-002",
"name": "Embedding-2",
"price": 0
"price": 0,
"defaultToken": 500,
"maxToken": 3000
}
]
}

View File

@@ -1,7 +1,7 @@
---
title: "升级到 V4.1"
description: "FastGPT 从旧版本升级到 V4.1 操作指南"
icon: "upgrade"
title: '升级到 V4.1'
description: 'FastGPT 从旧版本升级到 V4.1 操作指南'
icon: 'upgrade'
draft: false
toc: true
weight: 762
@@ -13,6 +13,8 @@ weight: 762
V4.1 优化了 PostgreSQL 和 MongoDB 的连接变量,只需要填 1 个 URL 即可:
注意:/fastgpt 和 /postgres 是指数据库名称,需要和旧版的变量对应。
```bash
# mongo 配置,不需要改. 如果连不上,可能需要去掉 ?authSource=admin
- MONGODB_URI=mongodb://username:password@mongo:27017/fastgpt?authSource=admin
@@ -24,4 +26,4 @@ V4.1 优化了 PostgreSQL 和 MongoDB 的连接变量,只需要填 1 个 URL
部署新版项目,并发起 1 个 HTTP 请求(记得携带 `headers.rootkey`,这个值是环境变量里的)
+ https://xxxxx/api/admin/initChatItem
- https://xxxxx/api/admin/initChatItem

View File

@@ -0,0 +1,21 @@
---
title: '升级到 V4.2'
description: 'FastGPT 从旧版本升级到 V4.2 操作指南'
icon: 'upgrade'
draft: false
toc: true
weight: 763
---
99.9%用户不影响,升级 4.2 主要是修改了配置文件中 QAModel 的格式。从原先的数组改成对象:
```json
"QAModel": {
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"maxToken": 16000,
"price": 0
}
```
改动目的是,我们认为不需要留有选择余地,选择一个最合适的模型去进行任务即可。

View File

@@ -0,0 +1,24 @@
---
title: '升级到 V4.2.1'
description: 'FastGPT 从旧版本升级到 V4.2.1 操作指南'
icon: 'upgrade'
draft: false
toc: true
weight: 763
---
私有部署,如果添加了配置文件,需要在配置文件中修改 `VectorModels` 字段。增加 defaultToken 和 maxToken分别对应直接分段时的默认 token 数量和该模型支持的 token 上限(通常不建议超过 3000
```json
"VectorModels": [
{
"model": "text-embedding-ada-002",
"name": "Embedding-2",
"price": 0,
"defaultToken": 500,
"maxToken": 3000
}
]
```
改动目的是,我们认为不需要留有选择余地,选择一个最合适的模型去进行任务即可。

View File

@@ -1,44 +1,46 @@
<!DOCTYPE html>
{{ $.Scratch.Delete "social_list" }}
{{ $.Scratch.Set "pathName" (printf "%s" (.Site.Params.docs.pathName | default "docs")) }}
<!-- social_list -->
<!-- change -->
{{ $social_params := slice "github" "twitter" "instagram" "rss" "wechat" }}
{{ range $social_params }}
{{ if isset site.Params.social . }}
{{ $.Scratch.Add "social_list" (slice .) }}
{{ end }}
{{ if isset site.Params.social . }}
{{ $.Scratch.Add "social_list" (slice .) }}
{{ end }}
{{ end }}
<html lang="{{ site.LanguageCode }}">
{{- partial (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "head.html") . -}}
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "head.html") . -}}
<body>
<div class="content">
<div class="page-wrapper toggled">
{{- partial (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "sidebar.html") . -}}
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "sidebar.html") . -}}
<!-- Start Page Content -->
<main class="page-content bg-transparent">
{{- partialCached (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "top-header.html") . -}}
{{- partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "top-header.html") . -}}
<div class="container-fluid">
<div class="layout-spacing">
{{ $currentPage := . -}}
{{ if site.Params.docs.breadcrumbs | default true }}
<div class="d-md-flex justify-content-between align-items-center">
{{- partial (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "breadcrumbs.html") . -}}
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "breadcrumbs.html") . -}}
</div>
{{ end }}
<div class="row flex-xl-nowrap">
{{ if site.Params.docs.toc | default true }}
<div class="docs-toc col-xl-3 {{ if .IsNode }}visually-hidden{{ else }}{{end}} {{ if and (ne .Params.toc false) (ne .TableOfContents "<nav id=\"TableOfContents\"></nav>") }}{{ else }}visually-hidden{{ end }} {{ if site.Params.docs.toc | default true }}{{ else }}visually-hidden{{ end }} d-xl-block">
{{- partial (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "toc.html") . -}}
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "toc.html") . -}}
</div>
{{ end }}
{{ if site.Params.docs.tocMobile | default true }}
<div class="docs-toc-mobile {{ if .IsNode }}visually-hidden{{ else }}{{end}} {{ if and (ne .Params.toc false) (ne .TableOfContents "<nav id=\"TableOfContents\"></nav>") }}{{ else }}visually-hidden{{ end }} {{ if site.Params.docs.tocMobile | default true }}{{ else }}visually-hidden{{ end }} d-print-none d-xl-none">
<button id="toc-dropdown-btn" class="btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" data-bs-offset="0,0" aria-expanded="false">
Contents
Table of Contents
</button>
{{- partial (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "toc-mobile.html") . -}}
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "toc-mobile.html") . -}}
</div>
{{ end -}}
<!-- change -->
<div class="docs-content col-12 {{ if .IsNode }}{{ else }}{{ if site.Params.docs.toc | default true }}{{ if and (ne .Params.toc false) }}col-xl-9{{else}}{{end}}{{ else }}{{ end }}{{ end }} mt-0">
<div class="mb-3">
<h1 class="content-title mb-0">
@@ -60,13 +62,13 @@
{{ block "main" . }}{{ end }}
</div>
<div>
{{- partial (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "doc-nav.html") . -}}
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "doc-nav.html") . -}}
</div>
</div>
</div>
</div>
</div>
{{- partialCached (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "footer.html") . -}}
{{- partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "footer.html") . -}}
</main>
</div>
@@ -74,30 +76,30 @@
{{ if site.Params.docs.backToTop | default true }}
<!-- Back to top -->
<button onclick="topFunction()" id="back-to-top" class="back-to-top fs-5"><svg width="24" height="24"><path d="M12,10.224l-6.3,6.3L4.32,15.152,12,7.472l7.68,7.68L18.3,16.528Z" style="fill:#fff"/></svg></button>
<button onclick="topFunction()" id="back-to-top" aria-label="Back to Top Button" class="back-to-top fs-5"><svg width="24" height="24"><path d="M12,10.224l-6.3,6.3L4.32,15.152,12,7.472l7.68,7.68L18.3,16.528Z" style="fill:#fff"/></svg></button>
<!-- Back to top -->
{{ end }}
<!-- Dark Mode Switch JS -->
{{ if eq .Site.Params.docs.darkMode true -}}
{{ $darkModeSwitch := resources.Get (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "js/darkmode-switch.js") | js.Build | minify }}
{{ $darkModeSwitch := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/darkmode-switch.js") | js.Build | minify }}
<script>{{ $darkModeSwitch.Content | safeJS }}</script>
{{ end -}}
{{- partialCached (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "footer/footer-scripts.html") . -}}
{{- partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "footer/footer-scripts.html") . -}}
<!-- DocSearch Config -->
{{ if and (.Site.Params.docsearch.appID) (.Site.Params.docsearch.apiKey) -}}
{{- partialCached (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "footer/docsearch.html") . -}}
{{- partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "footer/docsearch.html") . -}}
{{ end }}
<!-- FlexSearch Config -->
{{ if or (not (isset .Site.Params.flexsearch "enabled")) (eq .Site.Params.flexsearch.enabled true) -}}
{{ if and (.Site.Params.docsearch.appID) (.Site.Params.docsearch.apiKey) -}}
{{ else }}
{{- partialCached (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "footer/flexsearch.html") . -}}
{{- partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "footer/flexsearch.html") . -}}
{{ end }}
{{ end }}
</body>
{{- partial (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "modals.html") . -}}
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "modals.html") . -}}
</html>

View File

@@ -1,3 +1,4 @@
<!-- change -->
<style>
.medium-zoom-overlay,
.medium-zoom-image--opened {
@@ -5,43 +6,43 @@
}
</style>
{{ $dayjs := resources.Get (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "js/dayjs.min.js") }}
{{ $relativeTime := resources.Get (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "js/relativeTime.min.js") }}
{{ $app := resources.Get (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "js/app.js") -}}
{{ $dayjs := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/dayjs.min.js") }}
{{ $relativeTime := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/relativeTime.min.js") }}
{{ $app := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/app.js") -}}
{{ $slice := slice $dayjs $relativeTime $app -}}
{{ if and (.Site.Params.docsearch.appID) (.Site.Params.docsearch.apiKey) -}}
{{ $docsearch := resources.Get (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "js/docsearch.min.js") }}
{{ $docsearch := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/docsearch.min.js") }}
{{ $slice = $slice | append $docsearch -}}
{{ end }}
{{ if site.Params.docs.toc | default true }}
{{ if eq .Site.Params.docs.scrollSpy true -}}
{{ $simplescrollspy := resources.Get (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "js/simple-scrollspy.min.js") }}
{{ $simplescrollspy := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/simple-scrollspy.min.js") }}
{{ $slice = $slice | append $simplescrollspy -}}
{{ end -}}
{{ if eq .Site.Params.docs.scrollSpy true -}}
{{ $scrollspyScript := resources.Get (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "js/scrollspy-script.js") }}
{{ $scrollspyScript := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/scrollspy-script.js") }}
{{ $scrollspyScript := $scrollspyScript | js.Build -}}
{{ $slice = $slice | append $scrollspyScript -}}
{{ end -}}
{{ end -}}
{{ if site.Params.docs.tocMobile | default true }}
{{ $tocmobilescrollspy := resources.Get (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "js/toc-mobile-scrollspy.js") }}
{{ $tocmobilescrollspy := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/toc-mobile-scrollspy.js") }}
{{ $slice = $slice | append $tocmobilescrollspy -}}
{{ end -}}
{{ if eq .Site.Params.docs.prism true -}}
{{ $prism := resources.Get (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "js/prism.js") }}
{{ $prism := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/prism.js") }}
{{ $prism := $prism | js.Build -}}
{{ $slice = $slice | append $prism -}}
{{ end -}}
<!-- Bootstrap JS -->
{{ $js := resources.Get (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "js/bootstrap.js") }}
{{ $js := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/bootstrap.js") }}
{{ $params := dict }}
{{ $sourceMap := cond hugo.IsProduction "" "inline" }}
{{ $opts := dict "sourceMap" $sourceMap "minify" hugo.IsProduction "target" "es2018" "params" $params }}
@@ -51,7 +52,7 @@
{{ end }}
<script src="{{ $js.RelPermalink }}" {{ if hugo.IsProduction }}integrity="{{ $js.Data.Integrity }}"{{ end -}} defer></script>
{{ $js := $slice | resources.Concat (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "js/bundle.js") -}}
{{ $js := $slice | resources.Concat (printf "/%s/%s" ($.Scratch.Get "pathName") "js/bundle.js") -}}
{{- if not .Site.IsServer }}
{{- $js := $js | minify | fingerprint "sha384" }}
@@ -60,6 +61,7 @@
<script type="text/javascript" src="{{ $js.Permalink }}" defer></script>
{{- end }}
<!-- change -->
<script
src="https://cdn.jsdelivr.us/npm/medium-zoom/dist/medium-zoom.min.js"
crossorigin="anonymous"

View File

@@ -27,17 +27,17 @@
<meta name="website" content="https://lotusdocs.dev" />
<meta name="Version" content="v0.1.0" />
<!-- favicon -->
{{ block "favicon" . }}{{ partialCached (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "head/favicon.html") . }}{{ end }}
{{ block "favicon" . }}{{ partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "head/favicon.html") . }}{{ end }}
<!-- Dark Mode -->
{{ if eq .Site.Params.docs.darkMode true -}}
{{ $darkModeInit := resources.Get (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "js/darkmode-init.js") | js.Build | minify -}}
{{ $darkModeInit := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/darkmode-init.js") | js.Build | minify -}}
<script>{{ $darkModeInit.Content | safeJS }}</script>
{{ end -}}
<!-- FlexSearch -->
{{ if or (not (isset .Site.Params.flexsearch "enabled")) (eq .Site.Params.flexsearch.enabled true) -}}
{{ if and (.Site.Params.docsearch.appID) (.Site.Params.docsearch.apiKey) -}}
{{ else }}
{{ $flexSearch := resources.Get (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "js/flexsearch.bundle.js") }}
{{ $flexSearch := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/flexsearch.bundle.js") }}
{{- if not .Site.IsServer }}
{{ $flexSearch := $flexSearch | minify | fingerprint "sha384" }}
<script type="text/javascript" src="{{ $flexSearch.Permalink }}" integrity="{{ $flexSearch.Data.Integrity }}" crossorigin="anonymous"></script>
@@ -53,8 +53,8 @@
{{- if hugo.IsProduction}}
{{- $options := dict "enableSourceMap" false "outputStyle" "compressed" }}
{{- end }}
{{- $style := resources.Get (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "scss/style.scss") }}
{{- $style = $style | resources.ExecuteAsTemplate (printf "/%s/%s" (.Site.Params.docs.pathName | default "docs") "scss/style.scss") . | resources.ToCSS $options }}
{{- $style := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "scss/style.scss") }}
{{- $style = $style | resources.ExecuteAsTemplate (printf "/%s/%s" ($.Scratch.Get "pathName") "scss/style.scss") . | resources.ToCSS $options }}
{{- if hugo.IsProduction }}
{{- $style = $style | minify | fingerprint "sha384" }}
{{- end -}}
@@ -62,7 +62,7 @@
<!-- Plausible Analytics Config -->
{{- if not .Site.IsServer }}
{{ if and (.Site.Params.plausible.scriptURL | default "https://plausible.io") (.Site.Params.plausible.dataDomain) -}}
{{- partialCached (printf "%s/%s" (.Site.Params.docs.pathName | default "docs") "head/plausible") . }}
{{- partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "head/plausible") . }}
{{- end -}}
{{- end -}}
<!-- Google Analytics v4 Config -->

Some files were not shown because too many files have changed in this diff Show More