Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5fe671ffe | ||
|
|
44e772f0fd | ||
|
|
a3c6d6800b | ||
|
|
19b1ff5a8d | ||
|
|
8d55587cf4 | ||
|
|
a754ceaf3b | ||
|
|
086ea83fac | ||
|
|
1ace8fb9a3 | ||
|
|
e0b23a26f2 | ||
|
|
7c16d08ec0 | ||
|
|
5157e62fed | ||
|
|
1fe2c49204 | ||
|
|
b9b50a0f5a | ||
|
|
23cc2f81e9 | ||
|
|
68cdf50cb6 | ||
|
|
2ae8d43216 | ||
|
|
0ea464f30f | ||
|
|
7cb035ba24 | ||
|
|
7231a847f7 | ||
|
|
4f0f950dd4 | ||
|
|
c1f4785392 | ||
|
|
b22c878cf9 | ||
|
|
3420f677b6 | ||
|
|
baee8cfe82 | ||
|
|
0b0570fa54 | ||
|
|
299409aa7b | ||
|
|
5284312eb3 | ||
|
|
86a0e7ce23 | ||
|
|
e0de04dddb | ||
|
|
19d7edb585 | ||
|
|
fbb75c97d0 | ||
|
|
7e9cac3478 | ||
|
|
be937956af | ||
|
|
c5c3826714 | ||
|
|
42fec3a95c | ||
|
|
64b9367ca1 | ||
|
|
5fcdf28c5c | ||
|
|
2556c19a9a | ||
|
|
8fd21ad5a7 |
29
.github/ISSUE_TEMPLATE/bugs.md
vendored
Normal file
29
.github/ISSUE_TEMPLATE/bugs.md
vendored
Normal 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
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal 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
23
.github/ISSUE_TEMPLATE/features.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: 功能请求
|
||||
about: 详细描述你期望的功能
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**例行检查**
|
||||
|
||||
[//]: # '方框内删除已有的空格,填 x 号'
|
||||
|
||||
- [ ] 我已确认目前没有类似 features
|
||||
- [ ] 我已确认我已升级到最新版本
|
||||
- [ ] 我已完整查看过项目 README,已确定现有版本无法满足需求
|
||||
- [ ] 我理解并愿意跟进此 features,协助测试和提供反馈
|
||||
- [ ] 我理解并认可上述内容,并理解项目维护者精力有限,**不遵循规则的 features 可能会被无视或直接关闭**
|
||||
|
||||
**功能描述**
|
||||
|
||||
**应用场景**
|
||||
|
||||
**相关示例**
|
||||
27
README.md
27
README.md
@@ -37,10 +37,11 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开
|
||||
- [x] 多 LLM 模型对话
|
||||
- [x] 文本内容提取成结构化数据
|
||||
- [x] HTTP 扩展
|
||||
- [ ] 沙盒 JS 运行模块
|
||||
- [ ] 嵌入 Laf,实现在线编写 HTTP 模块
|
||||
- [ ] 连续对话引导
|
||||
- [ ] 对话多路线选择
|
||||
- [ ] 源文件引用追踪
|
||||
- [x] 源文件引用追踪
|
||||
- [ ] 自定义文件阅读器
|
||||
2. 丰富的知识库预处理
|
||||
- [x] 多库复用,混用
|
||||
- [x] chunk 记录修改和删除
|
||||
@@ -49,8 +50,8 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开
|
||||
- [x] 支持手动输入内容
|
||||
- [x] 支持 url 读取导入
|
||||
- [x] 支持 CSV 批量导入问答对
|
||||
- [ ] 支持知识库单独设置向量模型
|
||||
- [ ] 源文件存储
|
||||
- [x] 支持知识库单独设置向量模型
|
||||
- [x] 源文件存储
|
||||
3. 多种效果测试渠道
|
||||
- [x] 知识库单点搜索测试
|
||||
- [x] 对话时反馈引用并可修改与删除
|
||||
@@ -62,7 +63,7 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开
|
||||
5. 运营功能
|
||||
- [x] 免登录分享窗口
|
||||
- [x] Iframe 一键嵌入
|
||||
- [ ] 统一查阅对话记录
|
||||
- [x] 统一查阅对话记录,并对数据进行标注
|
||||
|
||||
## 👨💻 开发
|
||||
|
||||
@@ -76,25 +77,25 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开
|
||||
|
||||
由于需要部署数据库,部署完后需要等待 2~4 分钟才能正常访问。默认用了最低配置,首次访问时会有些慢。
|
||||
|
||||
* [快开始本地开发](https://doc.fastgpt.run/docs/development)
|
||||
* [快开始本地开发](https://doc.fastgpt.run/docs/development/intro/)
|
||||
* [部署 FastGPT](https://doc.fastgpt.run/docs/installation)
|
||||
* [系统配置文件说明](https://doc.fastgpt.run/docs/installation/reference)
|
||||
* [多模型配置](https://doc.fastgpt.run/docs/installation/reference/models)
|
||||
* [系统配置文件说明](https://doc.fastgpt.run/docs/development/configuration/)
|
||||
* [多模型配置](https://doc.fastgpt.run/docs/installation/one-api/)
|
||||
* [版本升级](https://doc.fastgpt.run/docs/installation/upgrading)
|
||||
* [API 文档](https://kjqvjse66l.feishu.cn/docx/DmLedTWtUoNGX8xui9ocdUEjnNh?pre_pathname=%2Fdrive%2Fhome%2F)
|
||||
|
||||
## 🏘️ 社区交流群
|
||||
|
||||
| 交流群 | 小助手 |
|
||||
| ----------------------------------------------------- | ---------------------------------------------- |
|
||||
|  |  |
|
||||
添加 wx 小助手加入:
|
||||
|
||||

|
||||
|
||||
## 👀 其他
|
||||
|
||||
- [FastGpt 常见问题](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||
- [FastGPT 常见问题](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||
- [docker 部署教程视频](https://www.bilibili.com/video/BV1jo4y147fT/)
|
||||
- [公众号接入视频教程](https://www.bilibili.com/video/BV1xh4y1t7fy/)
|
||||
- [FastGpt 知识库演示](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
|
||||
- [FastGPT 知识库演示](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
|
||||
|
||||
## 💪 相关项目
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
# FastGPT
|
||||
|
||||
FastGPT is a knowledge-based question answering system based on the LLM language model, providing out-of-the-box capabilities for data processing, model invocation, and more. It also allows for complex question answering scenarios through visual workflow orchestration using Flow!
|
||||
FastGPT is a knowledge-based question answering system built on the LLM. It offers out-of-the-box data processing and model invocation capabilities. Moreover, it allows for workflow orchestration through Flow visualization, thereby enabling complex question and answer scenarios!
|
||||
|
||||
</div>
|
||||
|
||||
@@ -84,7 +84,7 @@ Project tech stack: NextJs + TS + ChakraUI + Mongo + Postgres (Vector plugin)
|
||||
- [V3 Upgrade V4 Initialization](https://doc.fastgpt.run/docs/installation/upgrading)
|
||||
|
||||
<!-- ## :point_right: RoadMap
|
||||
- [FastGpt RoadMap](https://kjqvjse66l.feishu.cn/docx/RVUxdqE2WolDYyxEKATcM0XXnte) -->
|
||||
- [FastGPT RoadMap](https://kjqvjse66l.feishu.cn/docx/RVUxdqE2WolDYyxEKATcM0XXnte) -->
|
||||
|
||||
<!-- ## 🏘️ Community
|
||||
|
||||
@@ -94,10 +94,10 @@ Project tech stack: NextJs + TS + ChakraUI + Mongo + Postgres (Vector plugin)
|
||||
|
||||
## 👀 Others
|
||||
|
||||
- [FastGpt FAQ](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||
- [FastGPT FAQ](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||
- [Docker Deployment Tutorial Video](https://www.bilibili.com/video/BV1jo4y147fT/)
|
||||
- [Official Account Integration Video Tutorial](https://www.bilibili.com/video/BV1xh4y1t7fy/)
|
||||
- [FastGpt Knowledge Base Demo](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
|
||||
- [FastGPT Knowledge Base Demo](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
|
||||
|
||||
## 💪 Related Projects
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
# 默认用户密码,用户名为 root,每次重启时会自动更新。
|
||||
DEFAULT_ROOT_PSW=123456
|
||||
# 代理
|
||||
# AXIOS_PROXY_HOST=127.0.0.1
|
||||
# AXIOS_PROXY_PORT=7890
|
||||
# 数据库最大连接数
|
||||
DB_MAX_LINK=5
|
||||
# token
|
||||
TOKEN_KEY=dfdasfdas
|
||||
# 文件阅读时的秘钥
|
||||
FILE_TOKEN_KEY=filetokenkey
|
||||
# root key, 最高权限
|
||||
ROOT_KEY=fdafasd
|
||||
# openai 基本地址,可用作中转。
|
||||
@@ -17,5 +16,7 @@ OPENAI_BASE_URL=https://api.openai.com/v1
|
||||
# 此处逻辑:优先走 ONEAPI_URL,如果填写了 ONEAPI_URL,key 也需要是 ONEAPI 的 key
|
||||
CHAT_API_KEY=sk-xxxx
|
||||
# db
|
||||
MONGODB_URI=mongodb://username:password@0.0.0.0:27017/fastgpt
|
||||
PG_URL=postgresql://username:password@host:port/postgres
|
||||
MONGODB_URI=mongodb://username:password@0.0.0.0:27017/fastgpt?authSource=admin
|
||||
PG_URL=postgresql://username:password@host:port/postgres
|
||||
# 首页路径
|
||||
HOME_URL=/
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
"show_register": false,
|
||||
"show_appStore": false,
|
||||
"show_userDetail": false,
|
||||
"show_contact": true,
|
||||
"show_git": true,
|
||||
"show_doc": true,
|
||||
"systemTitle": "FastGPT",
|
||||
"authorText": "Made by FastGPT Team.",
|
||||
"gitLoginKey": "",
|
||||
"scripts": []
|
||||
},
|
||||
"SystemParams": {
|
||||
"gitLoginSecret": "",
|
||||
"vectorMaxProcess": 15,
|
||||
"qaMaxProcess": 15,
|
||||
"pgIvfflatProbe": 20
|
||||
|
||||
@@ -31,12 +31,14 @@
|
||||
"i18next": "^22.5.1",
|
||||
"immer": "^9.0.19",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jschardet": "^3.0.0",
|
||||
"jsdom": "^22.1.0",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mammoth": "^1.5.1",
|
||||
"mermaid": "^10.2.3",
|
||||
"mongoose": "^6.10.0",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"nanoid": "^4.0.1",
|
||||
"next": "13.1.6",
|
||||
"next-i18next": "^13.3.0",
|
||||
@@ -72,6 +74,7 @@
|
||||
"@types/jsdom": "^21.1.1",
|
||||
"@types/jsonwebtoken": "^9.0.1",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "18.14.0",
|
||||
"@types/papaparse": "^5.3.7",
|
||||
"@types/pg": "^8.6.6",
|
||||
|
||||
257
client/pnpm-lock.yaml
generated
257
client/pnpm-lock.yaml
generated
@@ -1,4 +1,8 @@
|
||||
lockfileVersion: '6.0'
|
||||
lockfileVersion: '6.1'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@chakra-ui/icons':
|
||||
@@ -67,6 +71,9 @@ dependencies:
|
||||
js-cookie:
|
||||
specifier: ^3.0.5
|
||||
version: registry.npmmirror.com/js-cookie@3.0.5
|
||||
jschardet:
|
||||
specifier: ^3.0.0
|
||||
version: registry.npmmirror.com/jschardet@3.0.0
|
||||
jsdom:
|
||||
specifier: ^22.1.0
|
||||
version: registry.npmmirror.com/jsdom@22.1.0
|
||||
@@ -85,12 +92,15 @@ dependencies:
|
||||
mongoose:
|
||||
specifier: ^6.10.0
|
||||
version: registry.npmmirror.com/mongoose@6.10.0
|
||||
multer:
|
||||
specifier: 1.4.5-lts.1
|
||||
version: registry.npmmirror.com/multer@1.4.5-lts.1
|
||||
nanoid:
|
||||
specifier: ^4.0.1
|
||||
version: registry.npmmirror.com/nanoid@4.0.1
|
||||
next:
|
||||
specifier: 13.1.6
|
||||
version: registry.npmmirror.com/next@13.1.6(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3)
|
||||
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)
|
||||
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)
|
||||
@@ -186,6 +196,9 @@ devDependencies:
|
||||
'@types/lodash':
|
||||
specifier: ^4.14.191
|
||||
version: registry.npmmirror.com/@types/lodash@4.14.191
|
||||
'@types/multer':
|
||||
specifier: ^1.4.7
|
||||
version: registry.npmmirror.com/@types/multer@1.4.7
|
||||
'@types/node':
|
||||
specifier: 18.14.0
|
||||
version: registry.npmmirror.com/@types/node@18.14.0
|
||||
@@ -230,7 +243,6 @@ 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}
|
||||
@@ -1211,7 +1223,6 @@ 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}
|
||||
@@ -1236,7 +1247,6 @@ 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}
|
||||
@@ -1248,7 +1258,6 @@ 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,7 +1292,6 @@ 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}
|
||||
@@ -1347,7 +1355,6 @@ 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}
|
||||
@@ -1357,7 +1364,6 @@ 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}
|
||||
@@ -1366,7 +1372,6 @@ 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}
|
||||
@@ -1401,7 +1406,6 @@ 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}
|
||||
@@ -1460,7 +1464,6 @@ 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}
|
||||
@@ -1478,7 +1481,6 @@ 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}
|
||||
@@ -1497,7 +1499,6 @@ 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}
|
||||
@@ -1524,7 +1525,6 @@ 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}
|
||||
@@ -1544,7 +1544,6 @@ 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}
|
||||
@@ -2807,7 +2806,6 @@ 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}
|
||||
@@ -2827,7 +2825,6 @@ 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}
|
||||
@@ -4421,33 +4418,28 @@ 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}
|
||||
@@ -4456,7 +4448,6 @@ 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}
|
||||
@@ -5126,6 +5117,23 @@ packages:
|
||||
engines: {node: '>=10.13.0'}
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@types/body-parser@1.19.2:
|
||||
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.2.tgz}
|
||||
name: '@types/body-parser'
|
||||
version: 1.19.2
|
||||
dependencies:
|
||||
'@types/connect': registry.npmmirror.com/@types/connect@3.4.35
|
||||
'@types/node': registry.npmmirror.com/@types/node@18.14.0
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@types/connect@3.4.35:
|
||||
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/connect/-/connect-3.4.35.tgz}
|
||||
name: '@types/connect'
|
||||
version: 3.4.35
|
||||
dependencies:
|
||||
'@types/node': registry.npmmirror.com/@types/node@18.14.0
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@types/cookie@0.5.1:
|
||||
resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/cookie/-/cookie-0.5.1.tgz}
|
||||
name: '@types/cookie'
|
||||
@@ -5381,6 +5389,28 @@ packages:
|
||||
'@types/ms': registry.npmmirror.com/@types/ms@0.7.31
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/@types/express-serve-static-core@4.17.36:
|
||||
resolution: {integrity: sha512-zbivROJ0ZqLAtMzgzIUC4oNqDG9iF0lSsAqpOD9kbs5xcIM3dTiyuHvBc7R8MtWBp3AAWGaovJa+wzWPjLYW7Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.36.tgz}
|
||||
name: '@types/express-serve-static-core'
|
||||
version: 4.17.36
|
||||
dependencies:
|
||||
'@types/node': registry.npmmirror.com/@types/node@18.14.0
|
||||
'@types/qs': registry.npmmirror.com/@types/qs@6.9.8
|
||||
'@types/range-parser': registry.npmmirror.com/@types/range-parser@1.2.4
|
||||
'@types/send': registry.npmmirror.com/@types/send@0.17.1
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@types/express@4.17.17:
|
||||
resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/express/-/express-4.17.17.tgz}
|
||||
name: '@types/express'
|
||||
version: 4.17.17
|
||||
dependencies:
|
||||
'@types/body-parser': registry.npmmirror.com/@types/body-parser@1.19.2
|
||||
'@types/express-serve-static-core': registry.npmmirror.com/@types/express-serve-static-core@4.17.36
|
||||
'@types/qs': registry.npmmirror.com/@types/qs@6.9.8
|
||||
'@types/serve-static': registry.npmmirror.com/@types/serve-static@1.15.2
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@types/formidable@2.0.5:
|
||||
resolution: {integrity: sha512-uvMcdn/KK3maPOaVUAc3HEYbCEhjaGFwww4EsX6IJfWIJ1tzHtDHczuImH3GKdusPnAAmzB07St90uabZeCKPA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/formidable/-/formidable-2.0.5.tgz}
|
||||
name: '@types/formidable'
|
||||
@@ -5412,6 +5442,12 @@ packages:
|
||||
hoist-non-react-statics: registry.npmmirror.com/hoist-non-react-statics@3.3.2
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/@types/http-errors@2.0.1:
|
||||
resolution: {integrity: sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/http-errors/-/http-errors-2.0.1.tgz}
|
||||
name: '@types/http-errors'
|
||||
version: 2.0.1
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@types/js-cookie@3.0.3:
|
||||
resolution: {integrity: sha512-Xe7IImK09HP1sv2M/aI+48a20VX+TdRJucfq4vfRVy6nWN8PYPOEnlMRSgxJAgYQIXJVL8dZ4/ilAM7dWNaOww==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/js-cookie/-/js-cookie-3.0.3.tgz}
|
||||
name: '@types/js-cookie'
|
||||
@@ -5475,12 +5511,32 @@ packages:
|
||||
'@types/unist': registry.npmmirror.com/@types/unist@2.0.6
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/@types/mime@1.3.2:
|
||||
resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/mime/-/mime-1.3.2.tgz}
|
||||
name: '@types/mime'
|
||||
version: 1.3.2
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@types/mime@3.0.1:
|
||||
resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/mime/-/mime-3.0.1.tgz}
|
||||
name: '@types/mime'
|
||||
version: 3.0.1
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@types/ms@0.7.31:
|
||||
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/ms/-/ms-0.7.31.tgz}
|
||||
name: '@types/ms'
|
||||
version: 0.7.31
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/@types/multer@1.4.7:
|
||||
resolution: {integrity: sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/multer/-/multer-1.4.7.tgz}
|
||||
name: '@types/multer'
|
||||
version: 1.4.7
|
||||
dependencies:
|
||||
'@types/express': registry.npmmirror.com/@types/express@4.17.17
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@types/node@18.14.0:
|
||||
resolution: {integrity: sha512-5EWrvLmglK+imbCJY0+INViFWUHg1AHel1sq4ZVSfdcNqGy9Edv3UB9IIzzg+xPaUcAgZYcfVs2fBcwDeZzU0A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/node/-/node-18.14.0.tgz}
|
||||
name: '@types/node'
|
||||
@@ -5520,6 +5576,18 @@ packages:
|
||||
name: '@types/prop-types'
|
||||
version: 15.7.5
|
||||
|
||||
registry.npmmirror.com/@types/qs@6.9.8:
|
||||
resolution: {integrity: sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/qs/-/qs-6.9.8.tgz}
|
||||
name: '@types/qs'
|
||||
version: 6.9.8
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@types/range-parser@1.2.4:
|
||||
resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.4.tgz}
|
||||
name: '@types/range-parser'
|
||||
version: 1.2.4
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@types/react-dom@18.0.11:
|
||||
resolution: {integrity: sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.0.11.tgz}
|
||||
name: '@types/react-dom'
|
||||
@@ -5558,6 +5626,25 @@ packages:
|
||||
name: '@types/scheduler'
|
||||
version: 0.16.3
|
||||
|
||||
registry.npmmirror.com/@types/send@0.17.1:
|
||||
resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/send/-/send-0.17.1.tgz}
|
||||
name: '@types/send'
|
||||
version: 0.17.1
|
||||
dependencies:
|
||||
'@types/mime': registry.npmmirror.com/@types/mime@1.3.2
|
||||
'@types/node': registry.npmmirror.com/@types/node@18.14.0
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@types/serve-static@1.15.2:
|
||||
resolution: {integrity: sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/serve-static/-/serve-static-1.15.2.tgz}
|
||||
name: '@types/serve-static'
|
||||
version: 1.15.2
|
||||
dependencies:
|
||||
'@types/http-errors': registry.npmmirror.com/@types/http-errors@2.0.1
|
||||
'@types/mime': registry.npmmirror.com/@types/mime@3.0.1
|
||||
'@types/node': registry.npmmirror.com/@types/node@18.14.0
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@types/tough-cookie@4.0.2:
|
||||
resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz}
|
||||
name: '@types/tough-cookie'
|
||||
@@ -5766,6 +5853,12 @@ packages:
|
||||
picomatch: registry.npmmirror.com/picomatch@2.3.1
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/append-field@1.0.0:
|
||||
resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/append-field/-/append-field-1.0.0.tgz}
|
||||
name: append-field
|
||||
version: 1.0.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/argparse@1.0.10:
|
||||
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz}
|
||||
name: argparse
|
||||
@@ -6083,7 +6176,6 @@ 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}
|
||||
@@ -6107,6 +6199,12 @@ packages:
|
||||
version: 1.0.1
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz}
|
||||
name: buffer-from
|
||||
version: 1.1.2
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/buffer-writer@2.0.0:
|
||||
resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/buffer-writer/-/buffer-writer-2.0.0.tgz}
|
||||
name: buffer-writer
|
||||
@@ -6132,6 +6230,15 @@ packages:
|
||||
run-applescript: registry.npmmirror.com/run-applescript@5.0.0
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/busboy@1.6.0:
|
||||
resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz}
|
||||
name: busboy
|
||||
version: 1.6.0
|
||||
engines: {node: '>=10.16.0'}
|
||||
dependencies:
|
||||
streamsearch: registry.npmmirror.com/streamsearch@1.1.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/call-bind@1.0.2:
|
||||
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/call-bind/-/call-bind-1.0.2.tgz}
|
||||
name: call-bind
|
||||
@@ -6342,6 +6449,18 @@ packages:
|
||||
version: 0.0.1
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/concat-stream@1.6.2:
|
||||
resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/concat-stream/-/concat-stream-1.6.2.tgz}
|
||||
name: concat-stream
|
||||
version: 1.6.2
|
||||
engines: {'0': node >= 0.8}
|
||||
dependencies:
|
||||
buffer-from: registry.npmmirror.com/buffer-from@1.1.2
|
||||
inherits: registry.npmmirror.com/inherits@2.0.4
|
||||
readable-stream: registry.npmmirror.com/readable-stream@2.3.8
|
||||
typedarray: registry.npmmirror.com/typedarray@0.0.6
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/convert-source-map@1.9.0:
|
||||
resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/convert-source-map/-/convert-source-map-1.9.0.tgz}
|
||||
name: convert-source-map
|
||||
@@ -7165,7 +7284,6 @@ 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}
|
||||
@@ -7307,7 +7425,6 @@ 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}
|
||||
@@ -7947,7 +8064,6 @@ 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}
|
||||
@@ -8039,7 +8155,6 @@ 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}
|
||||
@@ -8806,6 +8921,13 @@ packages:
|
||||
argparse: registry.npmmirror.com/argparse@2.0.1
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/jschardet@3.0.0:
|
||||
resolution: {integrity: sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/jschardet/-/jschardet-3.0.0.tgz}
|
||||
name: jschardet
|
||||
version: 3.0.0
|
||||
engines: {node: '>=0.1.90'}
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/jsdom@22.1.0:
|
||||
resolution: {integrity: sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/jsdom/-/jsdom-22.1.0.tgz}
|
||||
name: jsdom
|
||||
@@ -8859,7 +8981,6 @@ 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}
|
||||
@@ -8893,7 +9014,6 @@ 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}
|
||||
@@ -9131,7 +9251,6 @@ 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}
|
||||
@@ -9346,6 +9465,13 @@ packages:
|
||||
version: 2.0.14
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/media-typer@0.3.0:
|
||||
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz}
|
||||
name: media-typer
|
||||
version: 0.3.0
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/memory-pager@1.5.0:
|
||||
resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/memory-pager/-/memory-pager-1.5.0.tgz}
|
||||
name: memory-pager
|
||||
@@ -9761,7 +9887,15 @@ packages:
|
||||
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz}
|
||||
name: minimist
|
||||
version: 1.2.8
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/mkdirp@0.5.6:
|
||||
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz}
|
||||
name: mkdirp
|
||||
version: 0.5.6
|
||||
hasBin: true
|
||||
dependencies:
|
||||
minimist: registry.npmmirror.com/minimist@1.2.8
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/mongodb-connection-string-url@2.6.0:
|
||||
resolution: {integrity: sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz}
|
||||
@@ -9876,6 +10010,21 @@ packages:
|
||||
name: ms
|
||||
version: 2.1.3
|
||||
|
||||
registry.npmmirror.com/multer@1.4.5-lts.1:
|
||||
resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/multer/-/multer-1.4.5-lts.1.tgz}
|
||||
name: multer
|
||||
version: 1.4.5-lts.1
|
||||
engines: {node: '>= 6.0.0'}
|
||||
dependencies:
|
||||
append-field: registry.npmmirror.com/append-field@1.0.0
|
||||
busboy: registry.npmmirror.com/busboy@1.6.0
|
||||
concat-stream: registry.npmmirror.com/concat-stream@1.6.2
|
||||
mkdirp: registry.npmmirror.com/mkdirp@0.5.6
|
||||
object-assign: registry.npmmirror.com/object-assign@4.1.1
|
||||
type-is: registry.npmmirror.com/type-is@1.6.18
|
||||
xtend: registry.npmmirror.com/xtend@4.0.2
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/nanoid@3.3.6:
|
||||
resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/nanoid/-/nanoid-3.3.6.tgz}
|
||||
name: nanoid
|
||||
@@ -9916,12 +10065,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(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3)
|
||||
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)
|
||||
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(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3):
|
||||
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):
|
||||
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
|
||||
@@ -9949,7 +10098,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(react@18.2.0)
|
||||
styled-jsx: registry.npmmirror.com/styled-jsx@5.1.1(@babel/core@7.22.5)(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
|
||||
@@ -9978,14 +10127,13 @@ 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(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3)
|
||||
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)
|
||||
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}
|
||||
@@ -11172,7 +11320,6 @@ 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}
|
||||
@@ -11343,6 +11490,13 @@ packages:
|
||||
internal-slot: registry.npmmirror.com/internal-slot@1.0.5
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/streamsearch@1.1.0:
|
||||
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz}
|
||||
name: streamsearch
|
||||
version: 1.1.0
|
||||
engines: {node: '>=10.0.0'}
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/string.prototype.matchall@4.0.8:
|
||||
resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz}
|
||||
name: string.prototype.matchall
|
||||
@@ -11449,7 +11603,7 @@ packages:
|
||||
inline-style-parser: registry.npmmirror.com/inline-style-parser@0.1.1
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/styled-jsx@5.1.1(react@18.2.0):
|
||||
registry.npmmirror.com/styled-jsx@5.1.1(@babel/core@7.22.5)(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
|
||||
@@ -11465,6 +11619,7 @@ 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
|
||||
@@ -11713,6 +11868,16 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/type-is@1.6.18:
|
||||
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz}
|
||||
name: type-is
|
||||
version: 1.6.18
|
||||
engines: {node: '>= 0.6'}
|
||||
dependencies:
|
||||
media-typer: registry.npmmirror.com/media-typer@0.3.0
|
||||
mime-types: registry.npmmirror.com/mime-types@2.1.35
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/typed-array-length@1.0.4:
|
||||
resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.4.tgz}
|
||||
name: typed-array-length
|
||||
@@ -11723,6 +11888,12 @@ packages:
|
||||
is-typed-array: registry.npmmirror.com/is-typed-array@1.1.10
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/typedarray@0.0.6:
|
||||
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/typedarray/-/typedarray-0.0.6.tgz}
|
||||
name: typedarray
|
||||
version: 0.0.6
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/typescript@4.9.5:
|
||||
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/typescript/-/typescript-4.9.5.tgz}
|
||||
name: typescript
|
||||
@@ -11886,7 +12057,6 @@ 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}
|
||||
@@ -12249,7 +12419,6 @@ 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}
|
||||
@@ -12302,7 +12471,3 @@ packages:
|
||||
name: zwitch
|
||||
version: 2.0.4
|
||||
dev: false
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
@@ -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(包含上下文)|
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
### Fast GPT V4.2
|
||||
### Fast GPT V4.3
|
||||
|
||||
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. 优化 - [使用文档](https://doc.fastgpt.run/docs/intro/)
|
||||
4. [点击查看高级编排介绍文档](https://doc.fastgpt.run/docs/workflow)
|
||||
5. [点击查看商业版](https://fael3z0zfze.feishu.cn/docx/F155dbirfo8vDDx2WgWc6extnwf)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 1041 1348" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="1041" height="1348" 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>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
1
client/public/imgs/home/icon_0.svg
Normal file
1
client/public/imgs/home/icon_0.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1693533362715" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4021" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M166.32 808.98l-99.28 99.28c-15.31 15.32-39.71 15.32-55.03 0-15.32-15.31-15.32-39.71 0-55.03l98.71-99.28c15.32-15.31 39.71-15.31 55.03 0 15.32 15.32 15.32 39.72 0.57 55.03z m48.22 48.79l-99.28 99.27c-15.31 15.32-15.31 39.71 0 55.03 15.32 15.32 39.71 15.32 55.03 0l99.28-99.28c15.32-15.32 15.32-39.71 0-55.03-15.32-15.31-39.72-15.31-55.03 0.01z m625.14-545.16c0 71.47-57.86 129.34-129.34 129.34S581 384.08 581 312.61c0-71.48 57.86-129.34 129.34-129.34 72.05 0 129.91 57.86 129.34 129.34z m-77.71 0.57c0-28.36-23.26-51.63-51.62-51.63-28.36 0-51.62 23.26-51.62 51.63 0 28.36 23.26 51.62 51.62 51.62 28.93 0 52.19-23.26 51.62-51.62z m236.56 68.07c-27.8 104.39-83.39 203.65-164.51 285.35h-3.97l-5.11 5.67c10.21 38.57 11.35 78.28 2.84 116.86-9.07 46.52-32.33 90.2-66.94 124.8l-2.27 2.27c-38.57 36.87-85.09 59.57-134.45 67.51-51.62 7.94-105.51 0-152.6-23.26-17.58-10.21-25.53-32.9-16.45-51.63 1.7-2.84 3.41-5.67 5.67-8.5 14.18-15.32 24.96-32.9 32.9-51.63l3.4-7.94c-10.21 1.7-20.42 4.53-30.63 5.1-29.5 4.54-60.14 6.24-90.2 5.67-10.21 0-19.86-3.97-26.1-11.35L181.63 671.14c-8.51-7.95-11.35-18.72-10.78-29.5 0-28.37 2.27-58.43 5.67-86.79 1.7-10.78 2.84-20.99 5.1-31.2l-8.51 3.4c-17.58 7.95-35.17 18.72-51.06 33.47-14.75 14.18-39.14 12.48-53.32-2.27-2.27-2.84-3.97-7.37-6.24-10.21-23.26-47.09-30.63-99.28-22.69-150.9 8.22-51.8 32.65-99.66 69.78-136.71 34.61-34.61 78.85-57.86 123.67-66.94 39.14-8.51 79.42-7.94 116.86 2.27l7.95-7.95C439.75 106.69 538.45 51.66 642.83 23.3c108.35-28.36 222.38-28.36 329.59 0 13.62 4.54 23.82 15.32 27.23 27.23l0.57 0.57v0.56c28.37 107.78 28.37 221.81-1.69 329.59zM936.12 92.51c-89.06-19.86-182.67-18.16-270.03 5.1-91.34 24.96-178.7 73.18-250.74 145.22l-10.78 10.78-10.21 11.35c-10.21 11.35-26.66 16.45-42.55 10.78-31.2-12.48-65.81-14.75-98.71-7.38-31.2 6.81-61.27 22.13-86.23 46.52-26.66 27.8-43.12 60.7-48.79 95.3-3.4 20.99-3.4 42.55 0.57 62.97 9.64-5.67 18.72-10.78 28.93-14.18 26.66-11.34 55.6-18.15 84.53-18.72 3.97-0.57 7.94 0 11.91 0.56 19.86 5.67 31.77 26.66 26.1 46.52-7.95 26.1-13.05 52.2-16.45 78.29-1.7 22.12-3.4 43.12-3.97 64.67l146.36 146.93c22.12-1.14 43.11-2.27 64.67-5.11 27.23-3.97 52.76-8.51 78.29-15.89 3.97-0.56 7.94-1.13 11.91-1.13 20.42 0.57 36.31 18.72 36.31 39.14-1.7 29.5-7.38 58.43-19.29 85.66-3.97 9.08-9.07 19.29-14.18 28.36 20.42 3.97 41.98 3.97 62.4 1.13 33.47-5.1 66.94-20.99 93.03-47.09l1.13-1.13c24.39-24.96 39.71-55.6 47.09-86.79 6.81-32.34 3.97-66.94-7.94-99.28-4.54-14.18-1.7-30.64 10.21-41.41l11.91-11.35 8.51-8.51 2.27-1.13c72.04-72.61 119.7-159.41 145.79-250.17 23.27-89.05 25.54-180.95 7.95-270.01z" p-id="4022" fill="#515151"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -27,7 +27,7 @@ async function embedChatbot() {
|
||||
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.allow = 'fullscreen;microphone';
|
||||
iframe.title = 'FastGpt Chat Window';
|
||||
iframe.title = 'FastGPT Chat Window';
|
||||
iframe.id = chatWindowId;
|
||||
iframe.src = botSrc;
|
||||
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
"Cancel": "No",
|
||||
"Confirm": "Yes",
|
||||
"Running": "Running",
|
||||
"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",
|
||||
"Chat Logs Tips": "Logs record the app's online, shared, and API conversations",
|
||||
"Chat Logs Tips": "Logs record the app's online, shared, and API(chatId is existing) conversations",
|
||||
"Chat logs": "Chat Logs",
|
||||
"Confirm Del App Tip": "Confirm to delete the app and all its chats",
|
||||
"Confirm Save App Tip": "The application may be in advanced orchestration mode, and the advanced orchestration configuration will be overwritten after saving, please confirm!",
|
||||
@@ -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,34 @@
|
||||
"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"
|
||||
"Paste Config": "Paste Config",
|
||||
"Variable Key Repeat Tip": "Variable Key Repeat"
|
||||
},
|
||||
"chat": {
|
||||
"Admin Mark Content": "Corrected response",
|
||||
"Complete Response": "Complete Response",
|
||||
"Confirm to clear history": "Confirm to clear history?",
|
||||
"Exit Chat": "Exit",
|
||||
"Feedback Close": "Close Feedback",
|
||||
"Feedback Failed": "Feedback Failed",
|
||||
"Feedback Mark": "Mark",
|
||||
"Feedback Modal": "Feedback",
|
||||
"Feedback Modal Tip": "Enter what you find unsatisfactory",
|
||||
"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",
|
||||
@@ -68,13 +88,16 @@
|
||||
},
|
||||
"file": {
|
||||
"Click to download CSV template": "Click to download CSV template",
|
||||
"Click to view file": "Click to view file",
|
||||
"Create File": "Create File",
|
||||
"Create file": "Create file",
|
||||
"Drag and drop": "Drag and drop files here",
|
||||
"Fetch Url": "Fetch Url",
|
||||
"If the imported file is garbled, please convert CSV to UTF-8 encoding format": "If the imported file is garbled, please convert CSV to UTF-8 encoding format",
|
||||
"Parse": "{{name}} Parsing...",
|
||||
"Release the mouse to upload the file": "Release the mouse to upload the file",
|
||||
"Select a maximum of 10 files": "Select a maximum of 10 files",
|
||||
"Uploading": "Uploading: {{name}}, Progress: {{percent}}%",
|
||||
"max 10": "Max 10 files",
|
||||
"select a document": "select a document",
|
||||
"support": "support {{fileExtension}} file",
|
||||
@@ -90,10 +113,12 @@
|
||||
"Choice Desc": "FastGPT follows the Apache License 2.0 open source protocol",
|
||||
"Choice Extension": "Infinite Extension",
|
||||
"Choice Extension Desc": "HTTP based extension, easy to achieve custom functions",
|
||||
"Choice Fast": "Fast",
|
||||
"Choice Fast Desc": "{{title}} provides out-of-the-box visual actions to build AI applications point-by-point",
|
||||
"Choice Models": "Multiple Models",
|
||||
"Choice Models Desc": "",
|
||||
"Choice Models Desc": "Supports multiple models such as GPT, Claude, Spark, and ChatGLM",
|
||||
"Choice Open": "Open",
|
||||
"Choice Open Desc": "",
|
||||
"Choice Open Desc": "{{title}} follows the Apache License 2.0 open source protocol",
|
||||
"Choice QA": "QA Struceture",
|
||||
"Choice QA Desc": "The index is constructed with the structure of QA pairs, and ADAPTS to various scenarios such as Q&A and reading",
|
||||
"Choice Visual": "Visual workflow",
|
||||
@@ -102,12 +127,12 @@
|
||||
"Dateset": "",
|
||||
"Dateset Desc": "",
|
||||
"Docs": "Docs",
|
||||
"FastGPT Ability": "FastGPT Ability",
|
||||
"FastGPT Desc": "FastGPT is a knowledgebase question answering system based on LLM large language model, which provides out-of-the-box data processing, model invocation and other capabilities. At the same time, workflow orchestration can be performed through Flow visualization to achieve complex Q&A scenarios!",
|
||||
"FastGPT Ability": "{{title}} Ability",
|
||||
"FastGPT Desc": "{{title}} is a knowledgebase question answering system based on LLM large language model, which provides out-of-the-box data processing, model invocation and other capabilities. At the same time, workflow orchestration can be performed through Flow visualization to achieve complex Q&A scenarios!",
|
||||
"Features": "Features",
|
||||
"Footer Developer": "Developer",
|
||||
"Footer Docs": "Docs",
|
||||
"Footer FastGPT Cloud": "FastGPT Cloud",
|
||||
"Footer FastGPT Cloud": "{{title}} Cloud",
|
||||
"Footer Feedback": "Feedback",
|
||||
"Footer Git": "Code",
|
||||
"Footer Product": "Product",
|
||||
@@ -119,7 +144,7 @@
|
||||
"Quickly build AI question and answer library": "Quickly build AI question and answer library",
|
||||
"Start Now": "Start Now",
|
||||
"Visual AI orchestration": "Visual AI orchestration",
|
||||
"Why FastGPT": "",
|
||||
"Why FastGPT": "Why {{title}}",
|
||||
"desc": "AI knowledge base question and answer platform based on LLM large model",
|
||||
"slogan": "Let the AI know more about you"
|
||||
},
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
"Cancel": "取消",
|
||||
"Confirm": "确认",
|
||||
"Running": "运行中",
|
||||
"Warning": "提示",
|
||||
"Select value is empty": "选择的内容为空",
|
||||
"UnKnow": "未知",
|
||||
"Warning": "提示",
|
||||
"app": {
|
||||
"Advance App TestTip": "当前应用为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键",
|
||||
"App Detail": "应用详情",
|
||||
"Chat Logs Tips": "日志会记录该应用的在线、分享和 API 对话记录",
|
||||
"Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录",
|
||||
"Chat logs": "对话日志",
|
||||
"Confirm Del App Tip": "确认删除该应用及其所有聊天记录?",
|
||||
"Confirm Save App Tip": "该应用可能为高级编排模式,保存后将会覆盖高级编排配置,请确认!",
|
||||
@@ -17,6 +18,7 @@
|
||||
"Copy Module Config": "复制配置",
|
||||
"Export Config Successful": "已复制配置,请注意检查是否有重要数据",
|
||||
"Export Configs": "导出配置",
|
||||
"Feedback Count": "用户反馈",
|
||||
"Import Config": "导入配置",
|
||||
"Import Config Failed": "导入配置失败,请确保配置正常!",
|
||||
"Import Configs": "导入配置",
|
||||
@@ -26,16 +28,34 @@
|
||||
"Logs Source": "来源",
|
||||
"Logs Time": "时间",
|
||||
"Logs Title": "标题",
|
||||
"Mark Count": "标注答案数量",
|
||||
"My Apps": "我的应用",
|
||||
"Output Field Settings": "输出字段编辑",
|
||||
"Paste Config": "粘贴配置"
|
||||
"Paste Config": "粘贴配置",
|
||||
"Variable Key Repeat Tip": "变量 key 重复"
|
||||
},
|
||||
"chat": {
|
||||
"Admin Mark Content": "纠正后的回复",
|
||||
"Complete Response": "完整响应",
|
||||
"Confirm to clear history": "确认清空该应用的聊天记录?",
|
||||
"Confirm to clear history": "确认清空该应用的在线聊天记录?分享和 API 调用的记录不会被清空。",
|
||||
"Exit Chat": "退出聊天",
|
||||
"Feedback Close": "关闭反馈",
|
||||
"Feedback Failed": "提交反馈异常",
|
||||
"Feedback Mark": "标注",
|
||||
"Feedback Modal": "结果反馈",
|
||||
"Feedback Modal Tip": "输入你觉得回答不满意的地方",
|
||||
"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 调用",
|
||||
@@ -63,18 +83,21 @@
|
||||
},
|
||||
"dataset": {
|
||||
"Confirm to delete the data": "确认删除该数据?",
|
||||
"Queue Desc": "该数据是指整个系统当前待训练的数量。FastGPT 采用排队训练的方式,如果待训练的数据过多,可能需要等待一段时间",
|
||||
"Queue Desc": "该数据是指整个系统当前待训练的数量。{{title}} 采用排队训练的方式,如果待训练的数据过多,可能需要等待一段时间",
|
||||
"System Data Queue": "排队长度"
|
||||
},
|
||||
"file": {
|
||||
"Click to download CSV template": "点击下载 CSV 模板",
|
||||
"Click to view file": "点击查看原始文件",
|
||||
"Create File": "创建新文件",
|
||||
"Create file": "创建文件",
|
||||
"Drag and drop": "拖拽文件至此",
|
||||
"Fetch Url": "链接读取",
|
||||
"If the imported file is garbled, please convert CSV to UTF-8 encoding format": "如果导入文件乱码,请将 CSV 转成 UTF-8 编码格式",
|
||||
"Parse": "{{name}} 解析中...",
|
||||
"Release the mouse to upload the file": "松开鼠标上传文件",
|
||||
"Select a maximum of 10 files": "最多选择10个文件",
|
||||
"Uploading": "正在上传 {{name}},进度: {{percent}}%",
|
||||
"max 10": "最多选择 10 个文件",
|
||||
"select a document": "选择文件",
|
||||
"support": "支持 {{fileExtension}} 文件",
|
||||
@@ -90,10 +113,12 @@
|
||||
"Choice Desc": "",
|
||||
"Choice Extension": "无限扩展",
|
||||
"Choice Extension Desc": "基于 HTTP 实现扩展,轻松实现定制功能",
|
||||
"Choice Fast": "开箱即用",
|
||||
"Choice Fast Desc": "{{title}} 提供开箱即用的可视化操作,点点点即可构建 AI 应用",
|
||||
"Choice Models": "支持多种模型",
|
||||
"Choice Models Desc": "支持 GPT、Claude、文心一言等多模型",
|
||||
"Choice Models Desc": "支持 GPT、Claude、Spark、ChatGLM等多模型",
|
||||
"Choice Open": "更开放",
|
||||
"Choice Open Desc": "FastGPT 遵循 Apache License 2.0 开源协议",
|
||||
"Choice Open Desc": "{{title}} 遵循 Apache License 2.0 开源协议",
|
||||
"Choice QA": "独特的 QA 结构",
|
||||
"Choice QA Desc": "采用 QA 对的结构构建索引,适应问答、阅读等多种场景",
|
||||
"Choice Visual": "可视化工作流",
|
||||
@@ -102,12 +127,12 @@
|
||||
"Dateset": "自动数据预处理",
|
||||
"Dateset Desc": "提供手动输入、直接分段、LLM 自动处理和 CSV 等多种数据导入途径",
|
||||
"Docs": "文档",
|
||||
"FastGPT Ability": "FastGPT 能力",
|
||||
"FastGPT Desc": "FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景!",
|
||||
"FastGPT Ability": "{{title}} 能力",
|
||||
"FastGPT Desc": "{{title}} 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景!",
|
||||
"Features": "特点",
|
||||
"Footer Developer": "开发者",
|
||||
"Footer Docs": "文档",
|
||||
"Footer FastGPT Cloud": "FastGPT 线上服务",
|
||||
"Footer FastGPT Cloud": "{{title}} 线上服务",
|
||||
"Footer Feedback": "反馈",
|
||||
"Footer Git": "源码",
|
||||
"Footer Product": "产品",
|
||||
@@ -119,7 +144,7 @@
|
||||
"Quickly build AI question and answer library": "快速搭建 AI 问答系统",
|
||||
"Start Now": "立即开始",
|
||||
"Visual AI orchestration": "可视化 AI 编排",
|
||||
"Why FastGPT": "为什么选择 FastGPT",
|
||||
"Why FastGPT": "为什么选择 {{title}}",
|
||||
"desc": "基于 LLM 大模型的 AI 知识库问答平台",
|
||||
"slogan": "让 AI 更懂你的知识"
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { AppListItemType, AppUpdateParams } from '@/types/app';
|
||||
import { RequestPaging } from '../types/index';
|
||||
import type { Props as CreateAppProps } from '@/pages/api/app/create';
|
||||
import { addDays } from 'date-fns';
|
||||
import { GetAppChatLogsParams } from './request/app';
|
||||
|
||||
/**
|
||||
* 获取模型列表
|
||||
@@ -52,5 +53,4 @@ export const getAppTotalUsage = (data: { appId: string }) =>
|
||||
end: addDays(new Date(), 1)
|
||||
}).then((res) => (res.length === 0 ? [{ date: new Date(), total: 0 }] : res));
|
||||
|
||||
export const getAppChatLogs = (data: RequestPaging & { appId: string }) =>
|
||||
POST(`/app/getChatLogs`, data);
|
||||
export const getAppChatLogs = (data: GetAppChatLogsParams) => POST(`/app/getChatLogs`, data);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GET, POST, PUT, DELETE } from '../request';
|
||||
import type { KbItemType, KbListItemType } from '@/types/plugin';
|
||||
import type { DatasetItemType, KbItemType, KbListItemType } from '@/types/plugin';
|
||||
import { RequestPaging } from '@/types/index';
|
||||
import { TrainingModeEnum } from '@/constants/plugin';
|
||||
import {
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import { Response as KbDataItemType } from '@/pages/api/plugins/kb/data/getDataById';
|
||||
import { Props as UpdateDataProps } from '@/pages/api/openapi/kb/updateData';
|
||||
import type { KbUpdateParams, CreateKbParams } from '../request/kb';
|
||||
import { QuoteItemType } from '@/types/chat';
|
||||
|
||||
/* knowledge base */
|
||||
export const getKbList = () => GET<KbListItemType[]>(`/plugins/kb/list`);
|
||||
@@ -58,7 +59,7 @@ export const getTrainingData = (data: { kbId: string; init: boolean }) =>
|
||||
export const getTrainingQueueLen = () => GET<number>(`/plugins/kb/data/getQueueLen`);
|
||||
|
||||
export const getKbDataItemById = (dataId: string) =>
|
||||
GET<KbDataItemType>(`/plugins/kb/data/getDataById`, { dataId });
|
||||
GET<QuoteItemType>(`/plugins/kb/data/getDataById`, { dataId });
|
||||
|
||||
/**
|
||||
* 直接push数据
|
||||
@@ -66,6 +67,12 @@ 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: DatasetItemType }) =>
|
||||
POST<string>(`/plugins/kb/data/insertData`, data);
|
||||
|
||||
/**
|
||||
* 更新一条数据
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import axios, { Method, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import axios, {
|
||||
Method,
|
||||
InternalAxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
AxiosProgressEvent
|
||||
} from 'axios';
|
||||
import { clearToken, getToken } from '@/utils/user';
|
||||
import { TOKEN_ERROR_CODE } from '@/service/errorCode';
|
||||
|
||||
@@ -6,6 +11,7 @@ interface ConfigType {
|
||||
headers?: { [key: string]: string };
|
||||
hold?: boolean;
|
||||
timeout?: number;
|
||||
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
|
||||
}
|
||||
interface ResponseDataType {
|
||||
code: number;
|
||||
|
||||
7
client/src/api/request/app.d.ts
vendored
Normal file
7
client/src/api/request/app.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { RequestPaging } from '@/types';
|
||||
|
||||
export type GetAppChatLogsParams = RequestPaging & {
|
||||
appId: string;
|
||||
dateStart: Date;
|
||||
dateEnd: Date;
|
||||
};
|
||||
6
client/src/api/request/chat.d.ts
vendored
Normal file
6
client/src/api/request/chat.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
export type AdminUpdateFeedbackParams = {
|
||||
chatItemId: string;
|
||||
kbId: string;
|
||||
dataId: string;
|
||||
content: string;
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios, { Method, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import { baseUrl } from '../../service/ai/openai';
|
||||
import { baseUrl } from '../../service/lib/openai';
|
||||
|
||||
interface ConfigType {
|
||||
headers?: { [key: string]: string };
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
import { GET, POST, PUT } from './request';
|
||||
import type { InitDateResponse } from '@/pages/api/system/getInitData';
|
||||
import { AxiosProgressEvent } from 'axios';
|
||||
|
||||
export const getInitData = () => GET<InitDateResponse>('/system/getInitData');
|
||||
|
||||
export const uploadImg = (base64Img: string) => POST<string>('/system/uploadImage', { base64Img });
|
||||
|
||||
export const postUploadFiles = (
|
||||
data: FormData,
|
||||
onUploadProgress: (progressEvent: AxiosProgressEvent) => void
|
||||
) =>
|
||||
POST<string[]>('/plugins/file/upload', data, {
|
||||
onUploadProgress,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data; charset=utf-8'
|
||||
}
|
||||
});
|
||||
|
||||
export const getFileViewUrl = (fileId: string) => GET<string>('/plugins/file/readUrl', { fileId });
|
||||
|
||||
@@ -14,7 +14,7 @@ export const sendAuthCode = (data: {
|
||||
|
||||
export const getTokenLogin = () => GET<UserType>('/user/account/tokenLogin');
|
||||
export const gitLogin = (params: { code: string; inviterId?: string }) =>
|
||||
GET<ResLogin>('/user/account/gitLogin', params);
|
||||
GET<ResLogin>('/plusApi/user/account/gitLogin', params);
|
||||
|
||||
export const postRegister = ({
|
||||
username,
|
||||
|
||||
@@ -8,8 +8,8 @@ const Avatar = ({ w = '30px', ...props }: ImageProps) => {
|
||||
<Image
|
||||
fallbackSrc={LOGO_ICON}
|
||||
fallbackStrategy={'onError'}
|
||||
borderRadius={'50%'}
|
||||
objectFit={'cover'}
|
||||
borderRadius={'md'}
|
||||
objectFit={'contain'}
|
||||
alt=""
|
||||
w={w}
|
||||
h={w}
|
||||
|
||||
@@ -21,7 +21,13 @@ const ContextModal = ({
|
||||
minW={['90vw', '600px']}
|
||||
isCentered
|
||||
>
|
||||
<ModalBody pt={0} whiteSpace={'pre-wrap'} textAlign={'justify'} fontSize={'sm'}>
|
||||
<ModalBody
|
||||
pt={0}
|
||||
whiteSpace={'pre-wrap'}
|
||||
textAlign={'justify'}
|
||||
wordBreak={'break-all'}
|
||||
fontSize={'sm'}
|
||||
>
|
||||
{context.map((item, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
|
||||
56
client/src/components/ChatBox/FeedbackModal.tsx
Normal file
56
client/src/components/ChatBox/FeedbackModal.tsx
Normal 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;
|
||||
@@ -6,15 +6,12 @@ import { useToast } from '@/hooks/useToast';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
import { QuoteItemType } from '@/types/chat';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import InputDataModal from '@/pages/kb/detail/components/InputDataModal';
|
||||
import InputDataModal, { RawFileText } from '@/pages/kb/detail/components/InputDataModal';
|
||||
import MyModal from '../MyModal';
|
||||
import { KbDataItemType } from '@/types/plugin';
|
||||
|
||||
type SearchType = {
|
||||
type SearchType = KbDataItemType & {
|
||||
kb_id?: string;
|
||||
id?: string;
|
||||
q: string;
|
||||
a?: string;
|
||||
source?: string | undefined;
|
||||
};
|
||||
|
||||
const QuoteModal = ({
|
||||
@@ -29,12 +26,7 @@ const QuoteModal = ({
|
||||
const theme = useTheme();
|
||||
const { toast } = useToast();
|
||||
const { setIsLoading, Loading } = useLoading();
|
||||
const [editDataItem, setEditDataItem] = useState<{
|
||||
kbId: string;
|
||||
dataId: string;
|
||||
a: string;
|
||||
q: string;
|
||||
}>();
|
||||
const [editDataItem, setEditDataItem] = useState<QuoteItemType>();
|
||||
|
||||
/**
|
||||
* click edit, get new kbDataItem
|
||||
@@ -44,19 +36,14 @@ const QuoteModal = ({
|
||||
if (!item.id) return;
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const data = (await getKbDataItemById(item.id)) as QuoteItemType;
|
||||
const data = await getKbDataItemById(item.id);
|
||||
|
||||
if (!data) {
|
||||
onUpdateQuote(item.id, '已删除');
|
||||
throw new Error('该数据已被删除');
|
||||
}
|
||||
|
||||
setEditDataItem({
|
||||
kbId: data.kb_id,
|
||||
dataId: data.id,
|
||||
q: data.q,
|
||||
a: data.a
|
||||
});
|
||||
setEditDataItem(data);
|
||||
} catch (err) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
@@ -85,7 +72,13 @@ const QuoteModal = ({
|
||||
</>
|
||||
}
|
||||
>
|
||||
<ModalBody pt={0} whiteSpace={'pre-wrap'} textAlign={'justify'} fontSize={'sm'}>
|
||||
<ModalBody
|
||||
pt={0}
|
||||
whiteSpace={'pre-wrap'}
|
||||
textAlign={'justify'}
|
||||
wordBreak={'break-all'}
|
||||
fontSize={'sm'}
|
||||
>
|
||||
{rawSearch.map((item, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
@@ -98,7 +91,7 @@ const QuoteModal = ({
|
||||
_hover={{ '& .edit': { display: 'flex' } }}
|
||||
overflow={'hidden'}
|
||||
>
|
||||
{item.source && <Box color={'myGray.600'}>({item.source})</Box>}
|
||||
{item.source && <RawFileText filename={item.source} fileId={item.file_id} />}
|
||||
<Box>{item.q}</Box>
|
||||
<Box>{item.a}</Box>
|
||||
{item.id && (
|
||||
@@ -136,10 +129,13 @@ const QuoteModal = ({
|
||||
{editDataItem && (
|
||||
<InputDataModal
|
||||
onClose={() => setEditDataItem(undefined)}
|
||||
onSuccess={() => onUpdateQuote(editDataItem.dataId, '手动修改')}
|
||||
onDelete={() => onUpdateQuote(editDataItem.dataId, '已删除')}
|
||||
kbId={editDataItem.kbId}
|
||||
defaultValues={editDataItem}
|
||||
onSuccess={() => onUpdateQuote(editDataItem.id, '手动修改')}
|
||||
onDelete={() => onUpdateQuote(editDataItem.id, '已删除')}
|
||||
kbId={editDataItem.kb_id}
|
||||
defaultValues={{
|
||||
...editDataItem,
|
||||
dataId: editDataItem.id
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
55
client/src/components/ChatBox/ReadFeedbackModal.tsx
Normal file
55
client/src/components/ChatBox/ReadFeedbackModal.tsx
Normal 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);
|
||||
117
client/src/components/ChatBox/SelectDataset.tsx
Normal file
117
client/src/components/ChatBox/SelectDataset.tsx
Normal 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;
|
||||
@@ -25,8 +25,7 @@ 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 { event } from '@/utils/plugin/eventbus';
|
||||
|
||||
import { adaptChatItem_openAI } from '@/utils/plugin/openai';
|
||||
import { useMarkdown } from '@/hooks/useMarkdown';
|
||||
@@ -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';
|
||||
|
||||
@@ -71,6 +75,12 @@ export type ComponentRef = {
|
||||
scrollToBottom: (behavior?: 'smooth' | 'auto') => void;
|
||||
};
|
||||
|
||||
enum FeedbackTypeEnum {
|
||||
user = 'user',
|
||||
admin = 'admin',
|
||||
hidden = 'hidden'
|
||||
}
|
||||
|
||||
const VariableLabel = ({
|
||||
required = false,
|
||||
children
|
||||
@@ -124,6 +134,9 @@ const ChatAvatar = ({ src, type }: { src?: string; type: 'Human' | 'AI' }) => {
|
||||
|
||||
const ChatBox = (
|
||||
{
|
||||
feedbackType = FeedbackTypeEnum.hidden,
|
||||
showMarkIcon = false,
|
||||
showVoiceIcon = true,
|
||||
showEmptyIntro = false,
|
||||
chatId,
|
||||
appAvatar,
|
||||
@@ -134,6 +147,9 @@ const ChatBox = (
|
||||
onStartChat,
|
||||
onDelMessage
|
||||
}: {
|
||||
feedbackType?: `${FeedbackTypeEnum}`;
|
||||
showMarkIcon?: boolean;
|
||||
showVoiceIcon?: boolean;
|
||||
showEmptyIntro?: boolean;
|
||||
chatId?: string;
|
||||
appAvatar?: string;
|
||||
@@ -162,6 +178,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(
|
||||
() =>
|
||||
@@ -169,8 +198,10 @@ const ChatBox = (
|
||||
chatHistory[chatHistory.length - 1]?.status !== 'finish',
|
||||
[chatHistory]
|
||||
);
|
||||
// compute variable input is finish.
|
||||
const [variableInputFinish, setVariableInputFinish] = useState(false);
|
||||
const variableIsFinish = useMemo(() => {
|
||||
if (!variableModules || chatHistory.length > 0) return true;
|
||||
if (!variableModules || variableModules.length === 0 || chatHistory.length > 0) return true;
|
||||
|
||||
for (let i = 0; i < variableModules.length; i++) {
|
||||
const item = variableModules[i];
|
||||
@@ -179,8 +210,8 @@ const ChatBox = (
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}, [chatHistory.length, variableModules, variables]);
|
||||
return variableInputFinish;
|
||||
}, [chatHistory.length, variableInputFinish, variableModules, variables]);
|
||||
|
||||
const { register, reset, getValues, setValue, handleSubmit } = useForm<Record<string, any>>({
|
||||
defaultValues: variables
|
||||
@@ -379,6 +410,7 @@ const ChatBox = (
|
||||
]
|
||||
);
|
||||
|
||||
// output data
|
||||
useImperativeHandle(ref, () => ({
|
||||
getChatHistory: () => chatHistory,
|
||||
resetVariables(e) {
|
||||
@@ -391,6 +423,7 @@ const ChatBox = (
|
||||
setVariables(e || defaultVal);
|
||||
},
|
||||
resetHistory(e) {
|
||||
setVariableInputFinish(!!e.length);
|
||||
setChatHistory(e);
|
||||
},
|
||||
scrollToBottom
|
||||
@@ -406,13 +439,20 @@ const ChatBox = (
|
||||
border: theme.borders.base,
|
||||
mr: 3
|
||||
};
|
||||
const controlContainerStyle = {
|
||||
className: 'control',
|
||||
color: 'myGray.400',
|
||||
display: ['flex', 'none'],
|
||||
pl: 1,
|
||||
mt: 2
|
||||
};
|
||||
const controlContainerStyle = useCallback((status: ChatSiteItemType['status']) => {
|
||||
return {
|
||||
className: 'control',
|
||||
color: 'myGray.400',
|
||||
display:
|
||||
status === 'finish'
|
||||
? feedbackType === FeedbackTypeEnum.admin
|
||||
? 'flex'
|
||||
: ['flex', 'none']
|
||||
: 'none',
|
||||
pl: 1,
|
||||
mt: 2
|
||||
};
|
||||
}, []);
|
||||
const MessageCardStyle: BoxProps = {
|
||||
px: 4,
|
||||
py: 3,
|
||||
@@ -455,6 +495,16 @@ const ChatBox = (
|
||||
};
|
||||
}, [router.query]);
|
||||
|
||||
useEffect(() => {
|
||||
event.on('guideClick', ({ text }: { text: string }) => {
|
||||
if (!text) return;
|
||||
handleSubmit((data) => sendPrompt(data, text))();
|
||||
});
|
||||
|
||||
return () => {
|
||||
event.off('guideClick');
|
||||
};
|
||||
}, [handleSubmit, sendPrompt]);
|
||||
useEffect(() => {
|
||||
const listen = () => {
|
||||
cancelBroadcast();
|
||||
@@ -478,15 +528,7 @@ const ChatBox = (
|
||||
<ChatAvatar src={appAvatar} type={'AI'} />
|
||||
{/* message */}
|
||||
<Card order={2} mt={2} {...MessageCardStyle} bg={'white'} maxW={messageCardMaxW}>
|
||||
<Markdown
|
||||
source={`~~~guide \n${welcomeText}`}
|
||||
isChatting={false}
|
||||
onClick={(e) => {
|
||||
const val = e?.data;
|
||||
if (e?.event !== EventNameEnum.guideClick || !val) return;
|
||||
handleSubmit((data) => sendPrompt(data, val))();
|
||||
}}
|
||||
/>
|
||||
<Markdown source={`~~~guide \n${welcomeText}`} isChatting={false} />
|
||||
</Card>
|
||||
</Flex>
|
||||
)}
|
||||
@@ -541,6 +583,7 @@ const ChatBox = (
|
||||
onClick={handleSubmit((data) => {
|
||||
onUpdateVariable?.(data);
|
||||
setVariables(data);
|
||||
setVariableInputFinish(true);
|
||||
})}
|
||||
>
|
||||
{'开始对话'}
|
||||
@@ -568,7 +611,11 @@ const ChatBox = (
|
||||
{item.obj === 'Human' && (
|
||||
<>
|
||||
<Flex w={'100%'} alignItems={'center'} justifyContent={'flex-end'}>
|
||||
<Flex {...controlContainerStyle} justifyContent={'flex-end'} mr={3}>
|
||||
<Flex
|
||||
{...controlContainerStyle(item.status)}
|
||||
justifyContent={'flex-end'}
|
||||
mr={3}
|
||||
>
|
||||
<MyTooltip label={'复制'}>
|
||||
<MyIcon
|
||||
{...controlIconStyle}
|
||||
@@ -616,7 +663,7 @@ const ChatBox = (
|
||||
<>
|
||||
<Flex w={'100%'} alignItems={'flex-end'}>
|
||||
<ChatAvatar src={appAvatar} type={'AI'} />
|
||||
<Flex {...controlContainerStyle} ml={3}>
|
||||
<Flex {...controlContainerStyle(item.status)} ml={3}>
|
||||
<MyTooltip label={'复制'}>
|
||||
<MyIcon
|
||||
{...controlIconStyle}
|
||||
@@ -643,7 +690,7 @@ const ChatBox = (
|
||||
/>
|
||||
</MyTooltip>
|
||||
)}
|
||||
{hasVoiceApi && (
|
||||
{showVoiceIcon && hasVoiceApi && (
|
||||
<MyTooltip label={'语音播报'}>
|
||||
<MyIcon
|
||||
{...controlIconStyle}
|
||||
@@ -653,7 +700,92 @@ const ChatBox = (
|
||||
/>
|
||||
</MyTooltip>
|
||||
)}
|
||||
{/* admin mark icon */}
|
||||
{showMarkIcon && (
|
||||
<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>
|
||||
)}
|
||||
{feedbackType === FeedbackTypeEnum.admin && (
|
||||
<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>
|
||||
)}
|
||||
{feedbackType === FeedbackTypeEnum.user && (
|
||||
<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 +820,19 @@ const ChatBox = (
|
||||
contentId={item.dataId}
|
||||
responseData={item.responseData}
|
||||
/>
|
||||
{/* admin mark content */}
|
||||
{showMarkIcon && 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 +927,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>
|
||||
);
|
||||
};
|
||||
|
||||
8
client/src/components/Icon/icons/light/bad.svg
Normal file
8
client/src/components/Icon/icons/light/bad.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 6.1 KiB |
1
client/src/components/Icon/icons/light/mark.svg
Normal file
1
client/src/components/Icon/icons/light/mark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="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 |
@@ -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;
|
||||
|
||||
@@ -106,7 +106,13 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
cursor={'pointer'}
|
||||
onClick={() => router.push('/account')}
|
||||
>
|
||||
<Avatar w={'36px'} h={'36px'} src={userInfo?.avatar} fallbackSrc={HUMAN_ICON} />
|
||||
<Avatar
|
||||
w={'36px'}
|
||||
h={'36px'}
|
||||
borderRadius={'none'}
|
||||
src={userInfo?.avatar}
|
||||
fallbackSrc={HUMAN_ICON}
|
||||
/>
|
||||
</Box>
|
||||
{/* 导航列表 */}
|
||||
<Box flex={1}>
|
||||
@@ -161,20 +167,20 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
</Link>
|
||||
</Box>
|
||||
)}
|
||||
<MyTooltip label={t('home.Docs')} placement={'right-end'}>
|
||||
<Box
|
||||
{...itemStyles}
|
||||
mb={0}
|
||||
color={'#9096a5'}
|
||||
onClick={() => {
|
||||
window.open(`https://doc.fastgpt.run/docs/intro`);
|
||||
}}
|
||||
>
|
||||
<Badge count={unread}>
|
||||
{feConfigs?.show_doc && (
|
||||
<MyTooltip label={t('home.Docs')} placement={'right-end'}>
|
||||
<Box
|
||||
{...itemStyles}
|
||||
mb={0}
|
||||
color={'#9096a5'}
|
||||
onClick={() => {
|
||||
window.open(`https://doc.fastgpt.run/docs/intro`);
|
||||
}}
|
||||
>
|
||||
<MyIcon name={'courseLight'} width={'26px'} height={'26px'} />
|
||||
</Badge>
|
||||
</Box>
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
</MyTooltip>
|
||||
)}
|
||||
<Language {...itemStyles} />
|
||||
{feConfigs?.show_git && (
|
||||
<MyTooltip label={`Git Star: ${gitStar}`} placement={'right-end'}>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Spinner, Flex } from '@chakra-ui/react';
|
||||
import { Spinner, Flex, Box } from '@chakra-ui/react';
|
||||
|
||||
const Loading = ({ fixed = true }: { fixed?: boolean }) => {
|
||||
const Loading = ({ fixed = true, text = '' }: { fixed?: boolean; text?: string }) => {
|
||||
return (
|
||||
<Flex
|
||||
position={fixed ? 'fixed' : 'absolute'}
|
||||
@@ -13,8 +13,14 @@ const Loading = ({ fixed = true }: { fixed?: boolean }) => {
|
||||
bottom={0}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
flexDirection={'column'}
|
||||
>
|
||||
<Spinner thickness="4px" speed="0.65s" emptyColor="myGray.100" color="myBlue.600" size="xl" />
|
||||
{text && (
|
||||
<Box mt={2} color="myBlue.700" fontWeight={'bold'}>
|
||||
{text}
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,13 +4,37 @@ import ReactMarkdown from 'react-markdown';
|
||||
import RemarkGfm from 'remark-gfm';
|
||||
import RemarkMath from 'remark-math';
|
||||
import RehypeKatex from 'rehype-katex';
|
||||
import { event } from '@/utils/plugin/eventbus';
|
||||
|
||||
import 'katex/dist/katex.min.css';
|
||||
import styles from '../index.module.scss';
|
||||
import { EventNameEnum } from '../constant';
|
||||
import Image from '../img/Image';
|
||||
|
||||
const Guide = ({ text, onClick }: { text: string; onClick?: (e: any) => void }) => {
|
||||
const formatText = useMemo(() => text.replace(/\[(.*?)\]/g, '[$1]()'), [text]);
|
||||
function Link(e: any) {
|
||||
const href = e.href;
|
||||
const text = String(e.children);
|
||||
return (
|
||||
<Box as={'li'} py={1} m={0}>
|
||||
<Box
|
||||
as={'span'}
|
||||
color={'blue.600'}
|
||||
textDecoration={'underline'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
if (href) {
|
||||
return window.open(href, '_blank');
|
||||
}
|
||||
event.emit('guideClick', { text });
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const Guide = ({ text }: { text: string }) => {
|
||||
const formatText = useMemo(() => text.replace(/\[(.*?)\]($|\n)/g, '[$1]()\n'), [text]);
|
||||
|
||||
return (
|
||||
<ReactMarkdown
|
||||
@@ -18,27 +42,8 @@ const Guide = ({ text, onClick }: { text: string; onClick?: (e: any) => void })
|
||||
remarkPlugins={[RemarkGfm, RemarkMath]}
|
||||
rehypePlugins={[RehypeKatex]}
|
||||
components={{
|
||||
a({ children }: any) {
|
||||
return (
|
||||
<Box as={'li'} py={1} m={0}>
|
||||
<Box
|
||||
as={'span'}
|
||||
color={'blue.600'}
|
||||
textDecoration={'underline'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
if (!onClick) return;
|
||||
onClick({
|
||||
event: EventNameEnum.guideClick,
|
||||
data: String(children)
|
||||
});
|
||||
}}
|
||||
>
|
||||
{String(children)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
a: Link,
|
||||
img: Image
|
||||
}}
|
||||
>
|
||||
{formatText}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export enum EventNameEnum {
|
||||
guideClick = 'guideClick'
|
||||
}
|
||||
@@ -22,6 +22,7 @@ const MdImage = ({ src }: { src?: string }) => {
|
||||
fallbackStrategy={'onError'}
|
||||
cursor={succeed ? 'pointer' : 'default'}
|
||||
loading="eager"
|
||||
objectFit={'contain'}
|
||||
onLoad={() => {
|
||||
setIsLoading(false);
|
||||
setSucceed(true);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import RemarkGfm from 'remark-gfm';
|
||||
import RemarkMath from 'remark-math';
|
||||
@@ -15,7 +15,7 @@ const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'));
|
||||
const MdImage = dynamic(() => import('./img/Image'));
|
||||
const ChatGuide = dynamic(() => import('./chat/Guide'));
|
||||
|
||||
function Code({ inline, className, children, onClick }: any) {
|
||||
function Code({ inline, className, children }: any) {
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
const codeType = match?.[1];
|
||||
|
||||
@@ -24,7 +24,7 @@ function Code({ inline, className, children, onClick }: any) {
|
||||
}
|
||||
|
||||
if (codeType === 'guide') {
|
||||
return <ChatGuide text={String(children)} onClick={onClick} />;
|
||||
return <ChatGuide text={String(children)} />;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -33,28 +33,19 @@ function Code({ inline, className, children, onClick }: any) {
|
||||
</CodeLight>
|
||||
);
|
||||
}
|
||||
|
||||
function Image({ src }: { src?: string }) {
|
||||
return <MdImage src={src} />;
|
||||
}
|
||||
|
||||
const Markdown = ({
|
||||
source,
|
||||
isChatting = false,
|
||||
onClick
|
||||
}: {
|
||||
source: string;
|
||||
isChatting?: boolean;
|
||||
onClick?: (e: any) => void;
|
||||
}) => {
|
||||
const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: boolean }) => {
|
||||
const components = useMemo(
|
||||
() => ({
|
||||
img: Image,
|
||||
pre: 'div',
|
||||
p: 'div',
|
||||
code: (props: any) => <Code {...props} onClick={onClick} />
|
||||
code: Code
|
||||
}),
|
||||
[onClick]
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,20 +6,26 @@ export enum UserAuthTypeEnum {
|
||||
export const PRICE_SCALE = 100000;
|
||||
|
||||
export const fileImgs = [
|
||||
{ reg: /pdf/gi, src: '/imgs/files/pdf.svg' },
|
||||
{ reg: /csv/gi, src: '/imgs/files/csv.svg' },
|
||||
{ reg: /(doc|docs)/gi, src: '/imgs/files/doc.svg' },
|
||||
{ reg: /txt/gi, src: '/imgs/files/txt.svg' },
|
||||
{ reg: /md/gi, src: '/imgs/files/markdown.svg' }
|
||||
{ suffix: 'pdf', src: '/imgs/files/pdf.svg' },
|
||||
{ suffix: 'csv', src: '/imgs/files/csv.svg' },
|
||||
{ suffix: '(doc|docs)', src: '/imgs/files/doc.svg' },
|
||||
{ suffix: 'txt', src: '/imgs/files/txt.svg' },
|
||||
{ suffix: 'md', src: '/imgs/files/markdown.svg' }
|
||||
];
|
||||
|
||||
export enum TrackEventName {
|
||||
windowError = 'windowError',
|
||||
pageError = 'pageError',
|
||||
wordReadError = 'wordReadError'
|
||||
}
|
||||
|
||||
export const htmlTemplate = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width initial-scale=1.0" />
|
||||
<title>FastGpt</title>
|
||||
<title>FastGPT</title>
|
||||
</head>
|
||||
<style>
|
||||
.markdown > :first-child {
|
||||
|
||||
@@ -11,6 +11,6 @@ export const defaultKbDetail: KbItemType = {
|
||||
name: 'Embedding-2',
|
||||
price: 0.2,
|
||||
defaultToken: 500,
|
||||
maxToken: 8000
|
||||
maxToken: 3000
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import type { ShareChatEditType } from '@/types/app';
|
||||
import type { AppSchema } from '@/types/mongoSchema';
|
||||
|
||||
export enum OpenAiChatEnum {
|
||||
'GPT35' = 'gpt-3.5-turbo',
|
||||
'GPT3516k' = 'gpt-3.5-turbo-16k',
|
||||
'FastAI-Plus' = 'gpt-4',
|
||||
'FastAI-Plus32k' = 'gpt-4-32k'
|
||||
}
|
||||
|
||||
export const defaultApp: AppSchema = {
|
||||
_id: '',
|
||||
userId: 'userId',
|
||||
|
||||
@@ -10,7 +10,7 @@ export enum PageTypeEnum {
|
||||
}
|
||||
|
||||
export const BillSourceMap: Record<`${BillSourceEnum}`, string> = {
|
||||
[BillSourceEnum.fastgpt]: 'FastGpt 平台',
|
||||
[BillSourceEnum.fastgpt]: 'FastGPT 平台',
|
||||
[BillSourceEnum.api]: 'Api',
|
||||
[BillSourceEnum.shareLink]: '免登录链接'
|
||||
};
|
||||
|
||||
@@ -5,8 +5,16 @@ export const useLoading = (props?: { defaultLoading: boolean }) => {
|
||||
const [isLoading, setIsLoading] = useState(props?.defaultLoading || false);
|
||||
|
||||
const Loading = useCallback(
|
||||
({ loading, fixed = true }: { loading?: boolean; fixed?: boolean }): JSX.Element | null => {
|
||||
return isLoading || loading ? <LoadingComponent fixed={fixed} /> : null;
|
||||
({
|
||||
loading,
|
||||
fixed = true,
|
||||
text = ''
|
||||
}: {
|
||||
loading?: boolean;
|
||||
fixed?: boolean;
|
||||
text?: string;
|
||||
}): JSX.Element | null => {
|
||||
return isLoading || loading ? <LoadingComponent fixed={fixed} text={text} /> : null;
|
||||
},
|
||||
[isLoading]
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@ import { clientInitData, feConfigs } from '@/store/static';
|
||||
import { appWithTranslation, useTranslation } from 'next-i18next';
|
||||
import { getLangStore, setLangStore } from '@/utils/i18n';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
|
||||
import 'nprogress/nprogress.css';
|
||||
import '@/styles/reset.scss';
|
||||
@@ -37,11 +38,13 @@ function App({ Component, pageProps }: AppProps) {
|
||||
const router = useRouter();
|
||||
const { hiId } = router.query as { hiId?: string };
|
||||
const { i18n } = useTranslation();
|
||||
const { setLastRoute } = useGlobalStore();
|
||||
|
||||
const [scripts, setScripts] = useState<FeConfigsType['scripts']>([]);
|
||||
const [googleClientVerKey, setGoogleVerKey] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
// get init data
|
||||
(async () => {
|
||||
const {
|
||||
feConfigs: { scripts, googleClientVerKey }
|
||||
@@ -49,6 +52,21 @@ function App({ Component, pageProps }: AppProps) {
|
||||
setScripts(scripts || []);
|
||||
setGoogleVerKey(googleClientVerKey);
|
||||
})();
|
||||
// add window error track
|
||||
window.onerror = function (msg, url) {
|
||||
window.umami?.track('windowError', {
|
||||
device: {
|
||||
userAgent: navigator.userAgent,
|
||||
platform: navigator.platform,
|
||||
appName: navigator.appName
|
||||
},
|
||||
msg,
|
||||
url
|
||||
});
|
||||
};
|
||||
return () => {
|
||||
window.onerror = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -59,12 +77,16 @@ function App({ Component, pageProps }: AppProps) {
|
||||
const lang = getLangStore() || 'zh';
|
||||
i18n?.changeLanguage?.(lang);
|
||||
setLangStore(lang);
|
||||
|
||||
return () => {
|
||||
setLastRoute(router.asPath);
|
||||
};
|
||||
}, [router.asPath]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{feConfigs?.systemTitle || 'FastGPT'}</title>
|
||||
<title>{feConfigs?.systemTitle || 'AI'}</title>
|
||||
<meta name="description" content="Embedding + LLM, Build AI knowledge base" />
|
||||
<meta
|
||||
name="viewport"
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { serviceSideProps } from '@/utils/i18n';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { addLog } from '@/service/utils/tools';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
|
||||
function Error() {
|
||||
const router = useRouter();
|
||||
const { lastRoute } = useGlobalStore();
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
router.replace('/app/list');
|
||||
window.umami?.track('pageError', {
|
||||
userAgent: navigator.userAgent,
|
||||
platform: navigator.platform,
|
||||
appName: navigator.appName,
|
||||
lastRoute,
|
||||
route: router.asPath
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
setTimeout(() => {
|
||||
router.back();
|
||||
}, 2000);
|
||||
}, []);
|
||||
|
||||
@@ -16,4 +33,14 @@ function Error() {
|
||||
);
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
console.log('[render error]: ', context);
|
||||
|
||||
addLog.error(getErrText(context?.res));
|
||||
|
||||
return {
|
||||
props: { ...(await serviceSideProps(context)) }
|
||||
};
|
||||
}
|
||||
|
||||
export default Error;
|
||||
|
||||
@@ -43,7 +43,7 @@ const OpenAIAccountModal = ({
|
||||
<Input
|
||||
flex={1}
|
||||
{...register('baseUrl')}
|
||||
placeholder={'中转地址,未自动补全 "v1"'}
|
||||
placeholder={'请求地址,默认为 openai 官方。可填中转地址,未自动补全 "v1"'}
|
||||
></Input>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
|
||||
35
client/src/pages/api/admin/initv43.ts
Normal file
35
client/src/pages/api/admin/initv43.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { PgClient } from '@/service/pg';
|
||||
import { PgTrainingTableName } from '@/constants/plugin';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await authUser({ req, authRoot: true });
|
||||
|
||||
const { rowCount } = await PgClient.query(`SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = '${PgTrainingTableName}'
|
||||
AND column_name = 'file_id'`);
|
||||
|
||||
if (rowCount > 0) {
|
||||
return jsonRes(res, {
|
||||
data: '已经存在file_id字段'
|
||||
});
|
||||
}
|
||||
|
||||
jsonRes(res, {
|
||||
data: await PgClient.query(
|
||||
`ALTER TABLE ${PgTrainingTableName} ADD COLUMN file_id VARCHAR(100)`
|
||||
)
|
||||
});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,22 @@
|
||||
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';
|
||||
import { Types } from 'mongoose';
|
||||
import { addDays } from 'date-fns';
|
||||
import { GetAppChatLogsParams } from '@/api/request/app';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const {
|
||||
pageNum = 1,
|
||||
pageSize = 20,
|
||||
appId
|
||||
} = req.body as { pageNum: number; pageSize: number; appId: string };
|
||||
appId,
|
||||
dateStart = addDays(new Date(), -7),
|
||||
dateEnd = new Date()
|
||||
} = req.body as GetAppChatLogsParams;
|
||||
|
||||
if (!appId) {
|
||||
throw new Error('缺少参数');
|
||||
@@ -24,31 +28,58 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
|
||||
const where = {
|
||||
appId: new Types.ObjectId(appId),
|
||||
userId: new Types.ObjectId(userId)
|
||||
userId: new Types.ObjectId(userId),
|
||||
updateTime: {
|
||||
$gte: new Date(dateStart),
|
||||
$lte: new Date(dateEnd)
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
]),
|
||||
|
||||
40
client/src/pages/api/chat/feedback/adminUpdate.ts
Normal file
40
client/src/pages/api/chat/feedback/adminUpdate.ts
Normal 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
|
||||
});
|
||||
}
|
||||
}
|
||||
34
client/src/pages/api/chat/feedback/userUpdate.ts
Normal file
34
client/src/pages/api/chat/feedback/userUpdate.ts
Normal 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
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Chat, ChatItem } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { ChatSourceEnum } from '@/constants/chat';
|
||||
|
||||
type Props = {
|
||||
chatId?: string;
|
||||
@@ -29,14 +30,19 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
]);
|
||||
}
|
||||
if (appId) {
|
||||
const chats = await Chat.find({
|
||||
appId,
|
||||
userId,
|
||||
source: ChatSourceEnum.online
|
||||
}).select('_id');
|
||||
const chatIds = chats.map((chat) => chat._id);
|
||||
|
||||
await Promise.all([
|
||||
Chat.deleteMany({
|
||||
appId,
|
||||
userId
|
||||
_id: { $in: chatIds }
|
||||
}),
|
||||
ChatItem.deleteMany({
|
||||
userId,
|
||||
appId
|
||||
chatId: { $in: chatIds }
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -8,12 +8,12 @@ import { PgTrainingTableName, TrainingModeEnum } from '@/constants/plugin';
|
||||
import { startQueue } from '@/service/utils/tools';
|
||||
import { PgClient } from '@/service/pg';
|
||||
import { modelToolMap } from '@/utils/plugin';
|
||||
|
||||
export type DateItemType = { a: string; q: string; source?: string };
|
||||
import { getVectorModel } from '@/service/utils/data';
|
||||
import { DatasetItemType } from '@/types/plugin';
|
||||
|
||||
export type Props = {
|
||||
kbId: string;
|
||||
data: DateItemType[];
|
||||
data: DatasetItemType[];
|
||||
mode: `${TrainingModeEnum}`;
|
||||
prompt?: string;
|
||||
};
|
||||
@@ -22,17 +22,25 @@ export type Response = {
|
||||
insertLen: number;
|
||||
};
|
||||
|
||||
const modeMaxToken = {
|
||||
[TrainingModeEnum.index]: 6000,
|
||||
[TrainingModeEnum.qa]: 12000
|
||||
const modeMap = {
|
||||
[TrainingModeEnum.index]: true,
|
||||
[TrainingModeEnum.qa]: true
|
||||
};
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const { kbId, data, mode, prompt } = req.body as Props;
|
||||
const { kbId, data, mode = TrainingModeEnum.index, prompt } = req.body as Props;
|
||||
|
||||
if (!kbId || !Array.isArray(data)) {
|
||||
throw new Error('缺少参数');
|
||||
throw new Error('KbId or data is empty');
|
||||
}
|
||||
|
||||
if (modeMap[mode] === undefined) {
|
||||
throw new Error('Mode is error');
|
||||
}
|
||||
|
||||
if (data.length > 500) {
|
||||
throw new Error('Data is too long, max 500');
|
||||
}
|
||||
|
||||
await connectToDatabase();
|
||||
@@ -64,25 +72,41 @@ export async function pushDataToKb({
|
||||
mode,
|
||||
prompt
|
||||
}: { userId: string } & Props): Promise<Response> {
|
||||
await authKb({
|
||||
userId,
|
||||
kbId
|
||||
});
|
||||
const [kb, vectorModel] = await Promise.all([
|
||||
authKb({
|
||||
userId,
|
||||
kbId
|
||||
}),
|
||||
(async () => {
|
||||
if (mode === TrainingModeEnum.index) {
|
||||
const vectorModel = (await KB.findById(kbId, 'vectorModel'))?.vectorModel;
|
||||
|
||||
return getVectorModel(vectorModel || global.vectorModels[0].model);
|
||||
}
|
||||
return global.vectorModels[0];
|
||||
})()
|
||||
]);
|
||||
|
||||
const modeMaxToken = {
|
||||
[TrainingModeEnum.index]: vectorModel.maxToken,
|
||||
[TrainingModeEnum.qa]: global.qaModel.maxToken * 0.8
|
||||
};
|
||||
|
||||
// 过滤重复的 qa 内容
|
||||
const set = new Set();
|
||||
const filterData: DateItemType[] = [];
|
||||
const filterData: DatasetItemType[] = [];
|
||||
|
||||
data.forEach((item) => {
|
||||
if (!item.q) return;
|
||||
|
||||
const text = item.q + item.a;
|
||||
|
||||
// count token
|
||||
// count q token
|
||||
const token = modelToolMap.countTokens({
|
||||
model: 'gpt-3.5-turbo',
|
||||
messages: [{ obj: 'System', value: item.q }]
|
||||
});
|
||||
|
||||
if (token > modeMaxToken[TrainingModeEnum.qa]) {
|
||||
if (token > modeMaxToken[mode]) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -95,13 +119,10 @@ export async function pushDataToKb({
|
||||
// 数据库去重
|
||||
const insertData = (
|
||||
await Promise.allSettled(
|
||||
filterData.map(async ({ q, a = '', source }) => {
|
||||
filterData.map(async (data) => {
|
||||
let { q, a } = data;
|
||||
if (mode !== TrainingModeEnum.index) {
|
||||
return Promise.resolve({
|
||||
q,
|
||||
a,
|
||||
source
|
||||
});
|
||||
return Promise.resolve(data);
|
||||
}
|
||||
|
||||
if (!q) {
|
||||
@@ -127,49 +148,36 @@ export async function pushDataToKb({
|
||||
console.log(error);
|
||||
error;
|
||||
}
|
||||
return Promise.resolve({
|
||||
q,
|
||||
a,
|
||||
source
|
||||
});
|
||||
return Promise.resolve(data);
|
||||
})
|
||||
)
|
||||
)
|
||||
.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;
|
||||
})();
|
||||
.map<DatasetItemType>((item: any) => item.value);
|
||||
|
||||
// 插入记录
|
||||
await TrainingData.insertMany(
|
||||
const insertRes = await TrainingData.insertMany(
|
||||
insertData.map((item) => ({
|
||||
q: item.q,
|
||||
a: item.a,
|
||||
source: item.source,
|
||||
...item,
|
||||
userId,
|
||||
kbId,
|
||||
mode,
|
||||
prompt,
|
||||
vectorModel
|
||||
vectorModel: vectorModel.model
|
||||
}))
|
||||
);
|
||||
|
||||
insertData.length > 0 && startQueue();
|
||||
insertRes.length > 0 && startQueue();
|
||||
|
||||
return {
|
||||
insertLen: insertData.length
|
||||
insertLen: insertRes.length
|
||||
};
|
||||
}
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: {
|
||||
sizeLimit: '20mb'
|
||||
sizeLimit: '12mb'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
const response: any = await PgClient.query(
|
||||
`BEGIN;
|
||||
SET LOCAL ivfflat.probes = ${global.systemEnv.pgIvfflatProbe || 10};
|
||||
select id,q,a,source,(vector <#> '[${
|
||||
select id, q, a, source, file_id, (vector <#> '[${
|
||||
vectors[0]
|
||||
}]') * -1 AS score from ${PgTrainingTableName} where kb_id='${kbId}' AND user_id='${userId}' order by vector <#> '[${
|
||||
vectors[0]
|
||||
@@ -49,7 +49,9 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
COMMIT;`
|
||||
);
|
||||
|
||||
jsonRes<Response>(res, { data: response?.[2]?.rows || [] });
|
||||
jsonRes<Response>(res, {
|
||||
data: response?.[2]?.rows || []
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
jsonRes(res, {
|
||||
|
||||
@@ -25,7 +25,10 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
await connectToDatabase();
|
||||
|
||||
// auth user and get kb
|
||||
const [{ userId }, kb] = await Promise.all([authUser({ req }), KB.findById(kbId, 'model')]);
|
||||
const [{ userId }, kb] = await Promise.all([
|
||||
authUser({ req }),
|
||||
KB.findById(kbId, 'vectorModel')
|
||||
]);
|
||||
|
||||
if (!kb) {
|
||||
throw new Error("Can't find database");
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authBalanceByUid, authUser } from '@/service/utils/auth';
|
||||
import { withNextCors } from '@/service/utils/tools';
|
||||
import { getAIChatApi, axiosConfig } from '@/service/ai/openai';
|
||||
import { getAIChatApi, axiosConfig } from '@/service/lib/openai';
|
||||
import { pushGenerateVectorBill } from '@/service/events/pushBill';
|
||||
|
||||
type Props = {
|
||||
@@ -66,14 +66,14 @@ export async function getVector({
|
||||
...axiosConfig()
|
||||
}
|
||||
)
|
||||
.then((res) => {
|
||||
.then(async (res) => {
|
||||
if (!res.data?.data?.[0]?.embedding) {
|
||||
// @ts-ignore
|
||||
return Promise.reject(res.data?.error?.message || 'Embedding Error');
|
||||
return Promise.reject(res.data?.error?.message || 'Embedding API Error');
|
||||
}
|
||||
return {
|
||||
tokenLen: res.data.usage.total_tokens || 0,
|
||||
vectors: res.data.data.map((item) => unityDimensional(item.embedding))
|
||||
vectors: await Promise.all(res.data.data.map((item) => unityDimensional(item.embedding)))
|
||||
};
|
||||
});
|
||||
|
||||
@@ -88,6 +88,7 @@ export async function getVector({
|
||||
}
|
||||
|
||||
function unityDimensional(vector: number[]) {
|
||||
if (vector.length > 1536) return Promise.reject('向量维度不能超过 1536');
|
||||
let resultVector = vector;
|
||||
const vectorLen = vector.length;
|
||||
|
||||
|
||||
@@ -4,13 +4,10 @@ import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import type { ChatItemType } from '@/types/chat';
|
||||
import { countOpenAIToken } from '@/utils/plugin/openai';
|
||||
import { OpenAiChatEnum } from '@/constants/model';
|
||||
|
||||
type ModelType = `${OpenAiChatEnum}`;
|
||||
|
||||
type Props = {
|
||||
messages: ChatItemType[];
|
||||
model: ModelType;
|
||||
model: string;
|
||||
maxLen: number;
|
||||
};
|
||||
type Response = ChatItemType[];
|
||||
@@ -28,7 +25,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
return jsonRes<Response>(res, {
|
||||
data: gpt_chatItemTokenSlice({
|
||||
messages,
|
||||
model,
|
||||
maxToken: maxLen
|
||||
})
|
||||
});
|
||||
@@ -42,11 +38,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
|
||||
export function gpt_chatItemTokenSlice({
|
||||
messages,
|
||||
model = 'gpt-3.5-turbo',
|
||||
maxToken
|
||||
}: {
|
||||
messages: ChatItemType[];
|
||||
model?: string;
|
||||
maxToken: number;
|
||||
}) {
|
||||
let result: ChatItemType[] = [];
|
||||
@@ -54,7 +48,7 @@ export function gpt_chatItemTokenSlice({
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const msgs = [...result, messages[i]];
|
||||
|
||||
const tokens = countOpenAIToken({ messages: msgs, model });
|
||||
const tokens = countOpenAIToken({ messages: msgs });
|
||||
|
||||
if (tokens < maxToken) {
|
||||
result = msgs;
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import axios from 'axios';
|
||||
import { axiosConfig } from '@/service/ai/openai';
|
||||
|
||||
export type Props = {
|
||||
input: string;
|
||||
};
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await authUser({ req });
|
||||
|
||||
const result = await sensitiveCheck(req.body);
|
||||
|
||||
jsonRes(res, {
|
||||
data: result,
|
||||
message: result
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function sensitiveCheck({ input }: Props) {
|
||||
const response = await axios({
|
||||
...axiosConfig(),
|
||||
method: 'POST',
|
||||
url: `/moderations`,
|
||||
data: {
|
||||
input
|
||||
}
|
||||
});
|
||||
|
||||
const data = (response.data.results?.[0]?.category_scores as Record<string, number>) || {};
|
||||
|
||||
const values = Object.values(data);
|
||||
|
||||
for (const val of values) {
|
||||
if (val > 0.2) {
|
||||
return Promise.reject('您的内容不合规');
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
30
client/src/pages/api/plugins/file/delete.ts
Normal file
30
client/src/pages/api/plugins/file/delete.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { GridFSStorage } from '@/service/lib/gridfs';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
|
||||
const { fileId } = req.query as { fileId: string };
|
||||
|
||||
if (!fileId) {
|
||||
throw new Error('fileId is empty');
|
||||
}
|
||||
|
||||
const { userId } = await authUser({ req });
|
||||
|
||||
const gridFs = new GridFSStorage('dataset', userId);
|
||||
|
||||
await gridFs.delete(fileId);
|
||||
|
||||
jsonRes(res);
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
40
client/src/pages/api/plugins/file/read.ts
Normal file
40
client/src/pages/api/plugins/file/read.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { GridFSStorage } from '@/service/lib/gridfs';
|
||||
import { authFileToken } from './readUrl';
|
||||
import jschardet from 'jschardet';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
|
||||
const { token } = req.query as { token: string };
|
||||
|
||||
const { fileId, userId } = await authFileToken(token);
|
||||
|
||||
if (!fileId) {
|
||||
throw new Error('fileId is empty');
|
||||
}
|
||||
|
||||
const gridFs = new GridFSStorage('dataset', userId);
|
||||
|
||||
const [file, buffer] = await Promise.all([
|
||||
gridFs.findAndAuthFile(fileId),
|
||||
gridFs.download(fileId)
|
||||
]);
|
||||
|
||||
const encoding = jschardet.detect(buffer)?.encoding;
|
||||
|
||||
res.setHeader('Content-Type', `${file.contentType}; charset=${encoding}`);
|
||||
res.setHeader('Cache-Control', 'public, max-age=3600');
|
||||
res.setHeader('Content-Disposition', `inline; filename="${encodeURIComponent(file.filename)}"`);
|
||||
|
||||
res.end(buffer);
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
70
client/src/pages/api/plugins/file/readUrl.ts
Normal file
70
client/src/pages/api/plugins/file/readUrl.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { ERROR_ENUM } from '@/service/errorCode';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
|
||||
const { fileId } = req.query as { fileId: string };
|
||||
|
||||
if (!fileId) {
|
||||
throw new Error('fileId is empty');
|
||||
}
|
||||
|
||||
const { userId } = await authUser({ req });
|
||||
|
||||
const token = await createFileToken({
|
||||
userId,
|
||||
fileId
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
data: `/api/plugins/file/read?token=${token}`
|
||||
});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const createFileToken = (data: { userId: string; fileId: string }) => {
|
||||
if (!process.env.FILE_TOKEN_KEY) {
|
||||
return Promise.reject('System unset FILE_TOKEN_KEY');
|
||||
}
|
||||
const expiredTime = Math.floor(Date.now() / 1000) + 60 * 30;
|
||||
|
||||
const key = process.env.FILE_TOKEN_KEY as string;
|
||||
const token = jwt.sign(
|
||||
{
|
||||
...data,
|
||||
exp: expiredTime
|
||||
},
|
||||
key
|
||||
);
|
||||
return Promise.resolve(token);
|
||||
};
|
||||
|
||||
export const authFileToken = (token?: string) =>
|
||||
new Promise<{ userId: string; fileId: string }>((resolve, reject) => {
|
||||
if (!token) {
|
||||
return reject(ERROR_ENUM.unAuthFile);
|
||||
}
|
||||
const key = process.env.FILE_TOKEN_KEY as string;
|
||||
|
||||
jwt.verify(token, key, function (err, decoded: any) {
|
||||
if (err || !decoded?.userId || !decoded?.fileId) {
|
||||
reject(ERROR_ENUM.unAuthFile);
|
||||
return;
|
||||
}
|
||||
resolve({
|
||||
userId: decoded.userId,
|
||||
fileId: decoded.fileId
|
||||
});
|
||||
});
|
||||
});
|
||||
111
client/src/pages/api/plugins/file/upload.ts
Normal file
111
client/src/pages/api/plugins/file/upload.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { GridFSStorage } from '@/service/lib/gridfs';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import multer from 'multer';
|
||||
import path from 'path';
|
||||
|
||||
const nanoid = customAlphabet('1234567890abcdef', 12);
|
||||
|
||||
type FileType = {
|
||||
fieldname: string;
|
||||
originalname: string;
|
||||
encoding: string;
|
||||
mimetype: string;
|
||||
filename: string;
|
||||
path: string;
|
||||
size: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the multer uploader
|
||||
*/
|
||||
const maxSize = 50 * 1024 * 1024;
|
||||
class UploadModel {
|
||||
uploader = multer({
|
||||
limits: {
|
||||
fieldSize: maxSize
|
||||
},
|
||||
preservePath: true,
|
||||
storage: multer.diskStorage({
|
||||
filename: (_req, file, cb) => {
|
||||
const { ext } = path.parse(decodeURIComponent(file.originalname));
|
||||
cb(null, nanoid() + ext);
|
||||
}
|
||||
})
|
||||
}).any();
|
||||
|
||||
async doUpload(req: NextApiRequest, res: NextApiResponse) {
|
||||
return new Promise<{ files: FileType[]; metadata: Record<string, any> }>((resolve, reject) => {
|
||||
// @ts-ignore
|
||||
this.uploader(req, res, (error) => {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
resolve({
|
||||
files:
|
||||
// @ts-ignore
|
||||
req.files?.map((file) => ({
|
||||
...file,
|
||||
originalname: decodeURIComponent(file.originalname)
|
||||
})) || [],
|
||||
metadata: (() => {
|
||||
if (!req.body?.metadata) return {};
|
||||
try {
|
||||
return JSON.parse(req.body.metadata);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
return {};
|
||||
}
|
||||
})()
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const upload = new UploadModel();
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
const { files, metadata } = await upload.doUpload(req, res);
|
||||
|
||||
const gridFs = new GridFSStorage('dataset', userId);
|
||||
|
||||
const upLoadResults = await Promise.all(
|
||||
files.map((file) =>
|
||||
gridFs.save({
|
||||
path: file.path,
|
||||
filename: file.originalname,
|
||||
metadata: {
|
||||
...metadata,
|
||||
contentType: file.mimetype,
|
||||
userId
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
jsonRes(res, {
|
||||
data: upLoadResults
|
||||
});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: false
|
||||
}
|
||||
};
|
||||
@@ -30,7 +30,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
const where: any = [['user_id', userId], 'AND', ['id', dataId]];
|
||||
|
||||
const searchRes = await PgClient.select<KbDataItemType>(PgTrainingTableName, {
|
||||
fields: ['kb_id', 'id', 'q', 'a', 'source'],
|
||||
fields: ['kb_id', 'id', 'q', 'a', 'source', 'file_id'],
|
||||
where,
|
||||
limit: 1
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
|
||||
const [searchRes, total] = await Promise.all([
|
||||
PgClient.select<KbDataItemType>(PgTrainingTableName, {
|
||||
fields: ['id', 'q', 'a', 'source'],
|
||||
fields: ['id', 'q', 'a', 'source', 'file_id'],
|
||||
where,
|
||||
order: [{ field: 'id', mode: 'DESC' }],
|
||||
limit: pageSize,
|
||||
|
||||
88
client/src/pages/api/plugins/kb/data/insertData.ts
Normal file
88
client/src/pages/api/plugins/kb/data/insertData.ts
Normal 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';
|
||||
import { DatasetItemType } from '@/types/plugin';
|
||||
|
||||
export type Props = {
|
||||
kbId: string;
|
||||
data: DatasetItemType;
|
||||
};
|
||||
|
||||
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({
|
||||
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
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -5,6 +5,7 @@ import { authUser } from '@/service/utils/auth';
|
||||
import { PgClient } from '@/service/pg';
|
||||
import { Types } from 'mongoose';
|
||||
import { PgTrainingTableName } from '@/constants/plugin';
|
||||
import { GridFSStorage } from '@/service/lib/gridfs';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
@@ -21,24 +22,20 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
// delete all pg data
|
||||
await PgClient.delete(PgTrainingTableName, {
|
||||
where: [['user_id', userId], 'AND', ['kb_id', id]]
|
||||
});
|
||||
|
||||
// delete training data
|
||||
await TrainingData.deleteMany({
|
||||
userId,
|
||||
kbId: id
|
||||
});
|
||||
|
||||
// delete related app
|
||||
await App.updateMany(
|
||||
{
|
||||
userId
|
||||
},
|
||||
{ $pull: { 'chat.relatedKbs': new Types.ObjectId(id) } }
|
||||
);
|
||||
// delete all pg data
|
||||
await PgClient.delete(PgTrainingTableName, {
|
||||
where: [['user_id', userId], 'AND', ['kb_id', id]]
|
||||
});
|
||||
|
||||
// delete related files
|
||||
const gridFs = new GridFSStorage('dataset', userId);
|
||||
await gridFs.deleteFilesByKbId(id);
|
||||
|
||||
// delete kb data
|
||||
await KB.findOneAndDelete({
|
||||
|
||||
@@ -88,6 +88,8 @@ const defaultVectorModels: VectorModelItemType[] = [
|
||||
|
||||
export async function getInitConfig() {
|
||||
try {
|
||||
if (global.feConfigs) return;
|
||||
|
||||
const filename =
|
||||
process.env.NODE_ENV === 'development' ? 'data/config.local.json' : '/app/data/config.json';
|
||||
const res = JSON.parse(readFileSync(filename, 'utf-8'));
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { User } from '@/service/models/user';
|
||||
import { generateToken, setCookie } from '@/service/utils/tools';
|
||||
import axios from 'axios';
|
||||
import { parseQueryString } from '@/utils/tools';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 8);
|
||||
|
||||
type GithubAccessTokenType = {
|
||||
access_token: string;
|
||||
expires_in: number;
|
||||
refresh_token: string;
|
||||
refresh_token_expires_in: number;
|
||||
token_type: 'bearer';
|
||||
scope: string;
|
||||
};
|
||||
type GithubUserType = {
|
||||
login: string;
|
||||
email: string;
|
||||
avatar_url: string;
|
||||
};
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const { code, inviterId } = req.query as { code: string; inviterId?: string };
|
||||
|
||||
const { data: gitAccessToken } = await axios.post<string>(
|
||||
`https://github.com/login/oauth/access_token?client_id=${global.feConfigs.gitLoginKey}&client_secret=${global.systemEnv.gitLoginSecret}&code=${code}`
|
||||
);
|
||||
const jsonGitAccessToken = parseQueryString(gitAccessToken) as GithubAccessTokenType;
|
||||
|
||||
const access_token = jsonGitAccessToken?.access_token;
|
||||
if (!access_token) {
|
||||
throw new Error('access_token is null');
|
||||
}
|
||||
|
||||
const { data } = await axios.get<GithubUserType>('https://api.github.com/user', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${access_token}`
|
||||
}
|
||||
});
|
||||
const { login, avatar_url } = data;
|
||||
const username = `git-${login}`;
|
||||
|
||||
try {
|
||||
jsonRes(res, {
|
||||
data: await loginByUsername({ username, res })
|
||||
});
|
||||
} catch (err: any) {
|
||||
if (err?.code === 500) {
|
||||
jsonRes(res, {
|
||||
data: await registerUser({ username, avatar: avatar_url, res, inviterId })
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw new Error(err);
|
||||
}
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function loginByUsername({
|
||||
username,
|
||||
res
|
||||
}: {
|
||||
username: string;
|
||||
res: NextApiResponse;
|
||||
}) {
|
||||
const user = await User.findOne({ username });
|
||||
|
||||
if (!user) {
|
||||
return Promise.reject({
|
||||
code: 500
|
||||
});
|
||||
}
|
||||
|
||||
const token = generateToken(user._id);
|
||||
setCookie(res, token);
|
||||
return { user, token };
|
||||
}
|
||||
|
||||
export async function registerUser({
|
||||
username,
|
||||
avatar,
|
||||
inviterId,
|
||||
res
|
||||
}: {
|
||||
username: string;
|
||||
avatar?: string;
|
||||
inviterId?: string;
|
||||
res: NextApiResponse;
|
||||
}) {
|
||||
const response = await User.create({
|
||||
username,
|
||||
avatar,
|
||||
password: nanoid(),
|
||||
inviterId: inviterId ? inviterId : undefined
|
||||
});
|
||||
|
||||
// 根据 id 获取用户信息
|
||||
const user = await User.findById(response._id);
|
||||
|
||||
if (!user) {
|
||||
return Promise.reject('获取用户信息异常');
|
||||
}
|
||||
|
||||
const token = generateToken(user._id);
|
||||
setCookie(res, token);
|
||||
|
||||
return {
|
||||
user,
|
||||
token
|
||||
};
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { User } from '@/service/models/user';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { UserUpdateParams } from '@/types/user';
|
||||
import { axiosConfig, getAIChatApi, openaiBaseUrl } from '@/service/ai/openai';
|
||||
import { axiosConfig, getAIChatApi, openaiBaseUrl } from '@/service/lib/openai';
|
||||
|
||||
/* update user info */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
|
||||
@@ -113,6 +113,7 @@ const ChatTest = (
|
||||
ref={ChatBoxRef}
|
||||
appAvatar={app.avatar}
|
||||
userAvatar={userInfo?.avatar}
|
||||
showMarkIcon
|
||||
{...getSpecialModule(modules)}
|
||||
onStartChat={startChat}
|
||||
onDelMessage={() => {}}
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { VariableItemType } from '@/types/app';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||
import VariableEditModal from '../../../VariableEditModal';
|
||||
import VariableEditModal, { addVariable } from '../../../VariableEditModal';
|
||||
|
||||
export const defaultVariable: VariableItemType = {
|
||||
id: nanoid(),
|
||||
@@ -105,7 +105,7 @@ const NodeUserGuide = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
variant={'base'}
|
||||
leftIcon={<AddIcon fontSize={'10px'} />}
|
||||
onClick={() => {
|
||||
const newVariable = { ...defaultVariable, id: nanoid() };
|
||||
const newVariable = addVariable();
|
||||
updateVariables(variables.concat(newVariable));
|
||||
setEditVariable(newVariable);
|
||||
}}
|
||||
|
||||
@@ -532,6 +532,13 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
variables.map((item) => (item.id === variable.id ? variable : item))
|
||||
);
|
||||
} else {
|
||||
// auth same key
|
||||
if (variables.find((item) => item.key === variable.key)) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: t('app.Variable Key Repeat Tip')
|
||||
});
|
||||
}
|
||||
appendVariable(variable);
|
||||
}
|
||||
|
||||
@@ -639,6 +646,7 @@ const ChatTest = ({ appId }: { appId: string }) => {
|
||||
ref={ChatBoxRef}
|
||||
appAvatar={appDetail.avatar}
|
||||
userAvatar={userInfo?.avatar}
|
||||
showMarkIcon
|
||||
{...getSpecialModule(modules)}
|
||||
onStartChat={startChat}
|
||||
onDelMessage={() => {}}
|
||||
|
||||
@@ -214,7 +214,7 @@ export const KbParamsModal = ({
|
||||
rows={5}
|
||||
maxLength={500}
|
||||
placeholder={
|
||||
'若填写该内容,没有搜索到对应内容时,将直接回复填写的内容。\n为了连贯上下文,FastGpt 会取部分上一个聊天的搜索记录作为补充,因此在连续对话时,该功能可能会失效。'
|
||||
'若填写该内容,没有搜索到对应内容时,将直接回复填写的内容。\n为了连贯上下文,FastGPT 会取部分上一个聊天的搜索记录作为补充,因此在连续对话时,该功能可能会失效。'
|
||||
}
|
||||
{...register('searchEmptyText')}
|
||||
></Textarea>
|
||||
|
||||
@@ -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';
|
||||
@@ -19,16 +21,29 @@ import dayjs from 'dayjs';
|
||||
import { ChatSourceMap, HUMAN_ICON } from '@/constants/chat';
|
||||
import { AppLogsListItemType } from '@/types/app';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
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';
|
||||
import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker';
|
||||
import { addDays } from 'date-fns';
|
||||
|
||||
const Logs = ({ appId }: { appId: string }) => {
|
||||
const { t } = useTranslation();
|
||||
const { isPc } = useGlobalStore();
|
||||
|
||||
const [dateRange, setDateRange] = useState<DateRangeType>({
|
||||
from: addDays(new Date(), -7),
|
||||
to: new Date()
|
||||
});
|
||||
|
||||
const {
|
||||
isOpen: isOpenMarkDesc,
|
||||
onOpen: onOpenMarkDesc,
|
||||
onClose: onCloseMarkDesc
|
||||
} = useDisclosure();
|
||||
|
||||
const {
|
||||
data: logs,
|
||||
isLoading,
|
||||
@@ -39,7 +54,9 @@ const Logs = ({ appId }: { appId: string }) => {
|
||||
api: getAppChatLogs,
|
||||
pageSize: 20,
|
||||
params: {
|
||||
appId
|
||||
appId,
|
||||
dateStart: dateRange.from || new Date(),
|
||||
dateEnd: addDays(dateRange.to || new Date(), 1)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -54,7 +71,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 +95,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,14 +114,32 @@ 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>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<Box p={4}>
|
||||
<Pagination />
|
||||
</Box>
|
||||
{logs.length === 0 && !isLoading && (
|
||||
<Flex h={'100%'} flexDirection={'column'} alignItems={'center'} pt={'10vh'}>
|
||||
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
|
||||
@@ -102,6 +148,18 @@ const Logs = ({ appId }: { appId: string }) => {
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
<Flex w={'100%'} p={4} alignItems={'center'} justifyContent={'flex-end'}>
|
||||
<DateRangePicker
|
||||
defaultDate={dateRange}
|
||||
position="top"
|
||||
onChange={setDateRange}
|
||||
onSuccess={() => getData(1)}
|
||||
/>
|
||||
<Box ml={3}>
|
||||
<Pagination />
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
{!!detailLogsId && (
|
||||
<DetailLogsModal
|
||||
appId={appId}
|
||||
@@ -109,6 +167,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>
|
||||
);
|
||||
};
|
||||
@@ -225,9 +290,11 @@ function DetailLogsModal({
|
||||
chatId={chatId}
|
||||
appAvatar={chat?.app.avatar}
|
||||
userAvatar={HUMAN_ICON}
|
||||
feedbackType={'admin'}
|
||||
showMarkIcon
|
||||
showVoiceIcon={false}
|
||||
variableModules={chat?.app.variableModules}
|
||||
welcomeText={chat?.app.welcomeText}
|
||||
onUpdateVariable={(e) => {}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
@@ -204,6 +204,6 @@ export const defaultVariable: VariableItemType = {
|
||||
enums: [{ value: '' }]
|
||||
};
|
||||
export const addVariable = () => {
|
||||
const newVariable = { ...defaultVariable, id: nanoid() };
|
||||
const newVariable = { ...defaultVariable, key: nanoid(), id: nanoid() };
|
||||
return newVariable;
|
||||
};
|
||||
|
||||
@@ -15,29 +15,31 @@ const SliderApps = ({ appId }: { appId: string }) => {
|
||||
useQuery(['loadModels'], () => loadMyApps(false));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
py={2}
|
||||
px={3}
|
||||
borderRadius={'md'}
|
||||
_hover={{ bg: 'myGray.200' }}
|
||||
onClick={() => router.push('/app/list')}
|
||||
>
|
||||
<IconButton
|
||||
mr={3}
|
||||
icon={<MyIcon name={'backFill'} w={'18px'} color={'myBlue.600'} />}
|
||||
bg={'white'}
|
||||
boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'}
|
||||
h={'28px'}
|
||||
size={'sm'}
|
||||
borderRadius={'50%'}
|
||||
aria-label={''}
|
||||
/>
|
||||
{t('chat.Exit Chat')}
|
||||
</Flex>
|
||||
<Box mt={5}>
|
||||
<Flex flexDirection={'column'} h={'100%'}>
|
||||
<Box px={5} py={4}>
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
py={2}
|
||||
px={3}
|
||||
borderRadius={'md'}
|
||||
_hover={{ bg: 'myGray.200' }}
|
||||
onClick={() => router.push('/app/list')}
|
||||
>
|
||||
<IconButton
|
||||
mr={3}
|
||||
icon={<MyIcon name={'backFill'} w={'18px'} color={'myBlue.600'} />}
|
||||
bg={'white'}
|
||||
boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'}
|
||||
h={'28px'}
|
||||
size={'sm'}
|
||||
borderRadius={'50%'}
|
||||
aria-label={''}
|
||||
/>
|
||||
{t('chat.Exit Chat')}
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box flex={'1 0 0'} h={0} px={5} overflow={'overlay'}>
|
||||
{myApps.map((item) => (
|
||||
<Flex
|
||||
key={item._id}
|
||||
@@ -72,7 +74,7 @@ const SliderApps = ({ appId }: { appId: string }) => {
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -252,7 +252,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
</Head>
|
||||
{/* pc show myself apps */}
|
||||
{isPc && (
|
||||
<Box p={5} borderRight={theme.borders.base} w={'220px'} flexShrink={0}>
|
||||
<Box borderRight={theme.borders.base} w={'220px'} flexShrink={0}>
|
||||
<SliderApps appId={appId} />
|
||||
</Box>
|
||||
)}
|
||||
@@ -362,6 +362,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
appAvatar={chatData.app.avatar}
|
||||
userAvatar={userInfo?.avatar}
|
||||
variableModules={chatData.app.variableModules}
|
||||
feedbackType={'user'}
|
||||
welcomeText={chatData.app.welcomeText}
|
||||
onUpdateVariable={(e) => {}}
|
||||
onStartChat={startChat}
|
||||
|
||||
@@ -226,6 +226,7 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
|
||||
userAvatar={shareChatData.userAvatar}
|
||||
variableModules={shareChatData.app.variableModules}
|
||||
welcomeText={shareChatData.app.welcomeText}
|
||||
feedbackType={'user'}
|
||||
onUpdateVariable={(e) => {
|
||||
setShareChatData((state) => ({
|
||||
...state,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Box, Image, BoxProps, Grid, useTheme } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { feConfigs } from '@/store/static';
|
||||
|
||||
const Ability = () => {
|
||||
const theme = useTheme();
|
||||
@@ -34,7 +35,7 @@ const Ability = () => {
|
||||
fontSize={['22px', '30px']}
|
||||
fontWeight={'bold'}
|
||||
>
|
||||
{t('home.FastGPT Ability')}
|
||||
{t('home.FastGPT Ability', { title: feConfigs.systemTitle })}
|
||||
</Box>
|
||||
<Grid px={[5, 0]} minH={'400px'} gridTemplateColumns={['1fr', `500px 1fr`]} gridGap={6}>
|
||||
<Box
|
||||
|
||||
@@ -1,50 +1,55 @@
|
||||
import { Box, Image, Flex, Grid, useTheme } from '@chakra-ui/react';
|
||||
import React, { useRef } from 'react';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import { feConfigs } from '@/store/static';
|
||||
|
||||
const Choice = () => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const list = [
|
||||
{
|
||||
icon: '/imgs/home/icon_1.svg',
|
||||
title: t('home.Choice Open'),
|
||||
desc: t('home.Choice Open Desc'),
|
||||
tooltip: '前往 GitHub',
|
||||
onClick: () => window.open('https://github.com/labring/FastGPT', '_blank')
|
||||
},
|
||||
...(feConfigs?.show_git
|
||||
? [
|
||||
{
|
||||
icon: '/imgs/home/icon_1.svg',
|
||||
title: t('home.Choice Open'),
|
||||
desc: t('home.Choice Open Desc', { title: feConfigs?.systemTitle }),
|
||||
tooltip: '前往 GitHub',
|
||||
onClick: () => window.open('https://github.com/labring/FastGPT', '_blank')
|
||||
}
|
||||
]
|
||||
: [
|
||||
{
|
||||
icon: '/imgs/home/icon_0.svg',
|
||||
title: t('home.Choice Fast'),
|
||||
desc: t('home.Choice Fast Desc', { title: feConfigs?.systemTitle })
|
||||
}
|
||||
]),
|
||||
{
|
||||
icon: '/imgs/home/icon_2.svg',
|
||||
title: t('home.Choice QA'),
|
||||
desc: t('home.Choice QA Desc'),
|
||||
onClick: () => {}
|
||||
desc: t('home.Choice QA Desc')
|
||||
},
|
||||
{
|
||||
icon: '/imgs/home/icon_3.svg',
|
||||
title: t('home.Choice Visual'),
|
||||
desc: t('home.Choice Visual Desc'),
|
||||
onClick: () => {}
|
||||
desc: t('home.Choice Visual Desc')
|
||||
},
|
||||
{
|
||||
icon: '/imgs/home/icon_4.svg',
|
||||
title: t('home.Choice Extension'),
|
||||
desc: t('home.Choice Extension Desc'),
|
||||
onClick: () => {}
|
||||
desc: t('home.Choice Extension Desc')
|
||||
},
|
||||
{
|
||||
icon: '/imgs/home/icon_5.svg',
|
||||
title: t('home.Choice Debug'),
|
||||
desc: t('home.Choice Debug Desc'),
|
||||
onClick: () => {}
|
||||
desc: t('home.Choice Debug Desc')
|
||||
},
|
||||
{
|
||||
icon: '/imgs/home/icon_6.svg',
|
||||
title: t('home.Choice Models'),
|
||||
desc: t('home.Choice Models Desc'),
|
||||
onClick: () => {}
|
||||
desc: t('home.Choice Models Desc')
|
||||
}
|
||||
];
|
||||
|
||||
@@ -57,7 +62,7 @@ const Choice = () => {
|
||||
fontSize={['22px', '30px']}
|
||||
fontWeight={'bold'}
|
||||
>
|
||||
{t('home.Why FastGPT')}
|
||||
{t('home.Why FastGPT', { title: feConfigs?.systemTitle })}
|
||||
</Box>
|
||||
<Grid px={[5, 0]} gridTemplateColumns={['1fr', `1fr 1fr`, 'repeat(3,1fr)']} gridGap={6}>
|
||||
{list.map((item) => (
|
||||
@@ -72,7 +77,7 @@ const Choice = () => {
|
||||
_hover={{
|
||||
bg: 'rgba(255,255,255,0.8)'
|
||||
}}
|
||||
onClick={item.onClick}
|
||||
onClick={() => item.onClick?.()}
|
||||
>
|
||||
<Flex
|
||||
flex={'0 0 48px'}
|
||||
|
||||
@@ -16,7 +16,7 @@ const Footer = () => {
|
||||
label: t('home.Footer Product'),
|
||||
child: [
|
||||
{
|
||||
label: t('home.Footer FastGPT Cloud'),
|
||||
label: t('home.Footer FastGPT Cloud', { title: feConfigs.systemTitle }),
|
||||
onClick: () => {
|
||||
router.push('/app/list');
|
||||
}
|
||||
@@ -96,7 +96,7 @@ const Footer = () => {
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box mt={5} fontSize={'sm'} color={'myGray.600'} maxW={'380px'} textAlign={'justify'}>
|
||||
{t('home.FastGPT Desc')}
|
||||
{t('home.FastGPT Desc', { title: feConfigs.systemTitle })}
|
||||
</Box>
|
||||
</Box>
|
||||
{list.map((item) => (
|
||||
|
||||
@@ -19,26 +19,30 @@ const Navbar = () => {
|
||||
} = useDisclosure();
|
||||
const { isOpen: isOpenMenu, onOpen: onOpenMenu, onClose: onCloseMenu } = useDisclosure();
|
||||
const { isPc } = useGlobalStore();
|
||||
const menuList = useMemo(
|
||||
() => [
|
||||
// { label: t('home.Features'), key: 'features', onClick: () => {} },
|
||||
{
|
||||
label: t('home.Community'),
|
||||
key: 'community',
|
||||
onClick: () => {
|
||||
onOpenCommunity();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('home.Docs'),
|
||||
key: 'docs',
|
||||
onClick: () => {
|
||||
window.open('https://doc.fastgpt.run/docs/intro');
|
||||
}
|
||||
}
|
||||
],
|
||||
[onOpenCommunity, t]
|
||||
);
|
||||
const menuList = [
|
||||
...(feConfigs?.show_contact
|
||||
? [
|
||||
{
|
||||
label: t('home.Community'),
|
||||
key: 'community',
|
||||
onClick: () => {
|
||||
onOpenCommunity();
|
||||
}
|
||||
}
|
||||
]
|
||||
: []),
|
||||
...(feConfigs?.show_doc
|
||||
? [
|
||||
{
|
||||
label: t('home.Docs'),
|
||||
key: 'docs',
|
||||
onClick: () => {
|
||||
window.open('https://doc.fastgpt.run/docs/intro');
|
||||
}
|
||||
}
|
||||
]
|
||||
: [])
|
||||
];
|
||||
const bgOpacity = useMemo(() => {
|
||||
const rate = scrollTop / 120;
|
||||
if (rate > 0.7) {
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
|
||||
import { feConfigs } from '@/store/static';
|
||||
import { serviceSideProps } from '@/utils/i18n';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import Navbar from './components/Navbar';
|
||||
import Hero from './components/Hero';
|
||||
import Ability from './components/Ability';
|
||||
import Choice from './components/Choice';
|
||||
import Footer from './components/Footer';
|
||||
import Loading from '@/components/Loading';
|
||||
|
||||
const Home = () => {
|
||||
return (
|
||||
const Home = ({ homeUrl = '/' }: { homeUrl: string }) => {
|
||||
const router = useRouter();
|
||||
|
||||
if (homeUrl !== '/') {
|
||||
router.replace(homeUrl);
|
||||
}
|
||||
|
||||
return homeUrl === '/' ? (
|
||||
<Box id="home" bg={'myWhite.600'} h={'100vh'} overflowY={'auto'} overflowX={'hidden'}>
|
||||
<Box position={'fixed'} zIndex={10} top={0} left={0} right={0}>
|
||||
<Navbar />
|
||||
@@ -22,17 +30,22 @@ const Home = () => {
|
||||
<Choice />
|
||||
</Box>
|
||||
</Box>
|
||||
<Box bg={'white'}>
|
||||
<Footer />
|
||||
</Box>
|
||||
{feConfigs?.show_git && (
|
||||
<Box bg={'white'}>
|
||||
<Footer />
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
) : (
|
||||
<Loading />
|
||||
);
|
||||
};
|
||||
|
||||
export async function getServerSideProps(content: any) {
|
||||
return {
|
||||
props: {
|
||||
...(await serviceSideProps(content))
|
||||
...(await serviceSideProps(content)),
|
||||
homeUrl: process.env.HOME_URL || '/'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ const DataCard = ({ kbId }: { kbId: string }) => {
|
||||
maxW={['60%', '300px']}
|
||||
size={'sm'}
|
||||
value={searchText}
|
||||
placeholder="根据匹配知识,补充知识和来源进行搜索"
|
||||
placeholder="根据匹配知识,预期答案和来源进行搜索"
|
||||
onChange={(e) => {
|
||||
setSearchText(e.target.value);
|
||||
getFirstData();
|
||||
@@ -198,8 +198,7 @@ const DataCard = ({ kbId }: { kbId: string }) => {
|
||||
onClick={() =>
|
||||
setEditInputData({
|
||||
dataId: item.id,
|
||||
q: item.q,
|
||||
a: item.a
|
||||
...item
|
||||
})
|
||||
}
|
||||
>
|
||||
|
||||
@@ -66,7 +66,7 @@ const ChunkImport = ({ kbId }: { kbId: string }) => {
|
||||
|
||||
// subsection import
|
||||
let success = 0;
|
||||
const step = 500;
|
||||
const step = 300;
|
||||
for (let i = 0; i < chunks.length; i += step) {
|
||||
const { insertLen } = await postKbDataFromList({
|
||||
kbId,
|
||||
@@ -109,10 +109,9 @@ const ChunkImport = ({ kbId }: { kbId: string }) => {
|
||||
return {
|
||||
...file,
|
||||
tokens: splitRes.tokens,
|
||||
chunks: splitRes.chunks.map((chunk) => ({
|
||||
q: chunk,
|
||||
a: '',
|
||||
source: file.filename
|
||||
chunks: file.chunks.map((chunk, i) => ({
|
||||
...chunk,
|
||||
q: splitRes.chunks[i]
|
||||
}))
|
||||
};
|
||||
})
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { Box, Flex, Button, useTheme, Image } from '@chakra-ui/react';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useConfirm } from '@/hooks/useConfirm';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { postKbDataFromList } from '@/api/plugins/kb';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
import { vectorModelList } from '@/store/static';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import DeleteIcon, { hoverDeleteStyles } from '@/components/Icon/delete';
|
||||
import { TrainingModeEnum } from '@/constants/plugin';
|
||||
@@ -38,7 +37,10 @@ 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);
|
||||
|
||||
@@ -51,7 +53,7 @@ const CsvImport = ({ kbId }: { kbId: string }) => {
|
||||
|
||||
// subsection import
|
||||
let success = 0;
|
||||
const step = 500;
|
||||
const step = 300;
|
||||
for (let i = 0; i < filterChunks.length; i += step) {
|
||||
const { insertLen } = await postKbDataFromList({
|
||||
kbId,
|
||||
|
||||
@@ -2,7 +2,13 @@ import MyIcon from '@/components/Icon';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { useSelectFile } from '@/hooks/useSelectFile';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { fileDownload, readCsvContent, simpleText, splitText2Chunks } from '@/utils/file';
|
||||
import {
|
||||
fileDownload,
|
||||
readCsvContent,
|
||||
simpleText,
|
||||
splitText2Chunks,
|
||||
uploadFiles
|
||||
} from '@/utils/file';
|
||||
import { Box, Flex, useDisclosure, type BoxProps } from '@chakra-ui/react';
|
||||
import { fileImgs } from '@/constants/common';
|
||||
import { DragEvent, useCallback, useState } from 'react';
|
||||
@@ -11,7 +17,9 @@ import { readTxtContent, readPdfContent, readDocContent } from '@/utils/file';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import dynamic from 'next/dynamic';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { FetchResultItem } from '@/types/plugin';
|
||||
import { FetchResultItem, DatasetItemType } from '@/types/plugin';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
import { useUserStore } from '@/store/user';
|
||||
|
||||
const UrlFetchModal = dynamic(() => import('./UrlFetchModal'));
|
||||
const CreateFileModal = dynamic(() => import('./CreateFileModal'));
|
||||
@@ -22,7 +30,7 @@ const csvTemplate = `question,answer,source\n"什么是 laf","laf 是一个云
|
||||
export type FileItemType = {
|
||||
id: string;
|
||||
filename: string;
|
||||
chunks: { q: string; a: string; source?: string }[];
|
||||
chunks: DatasetItemType[];
|
||||
text: string;
|
||||
icon: string;
|
||||
tokens: number;
|
||||
@@ -47,6 +55,7 @@ const FileSelect = ({
|
||||
showCreateFile = true,
|
||||
...props
|
||||
}: Props) => {
|
||||
const { kbDetail } = useUserStore();
|
||||
const { Loading: FileSelectLoading } = useLoading();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -58,7 +67,7 @@ const FileSelect = ({
|
||||
});
|
||||
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [selecting, setSelecting] = useState(false);
|
||||
const [selectingText, setSelectingText] = useState<string>();
|
||||
|
||||
const {
|
||||
isOpen: isOpenUrlFetch,
|
||||
@@ -73,17 +82,23 @@ const FileSelect = ({
|
||||
|
||||
const onSelectFile = useCallback(
|
||||
async (files: File[]) => {
|
||||
setSelecting(true);
|
||||
try {
|
||||
// Parse file by file
|
||||
let promise = Promise.resolve<FileItemType[]>([]);
|
||||
files.forEach((file) => {
|
||||
promise = promise.then(async (result) => {
|
||||
const extension = file?.name?.split('.')?.pop()?.toLowerCase();
|
||||
const chunkFiles: FileItemType[] = [];
|
||||
|
||||
/* text file */
|
||||
const icon = fileImgs.find((item) => new RegExp(item.reg).test(file.name))?.src;
|
||||
let text = await (async () => {
|
||||
for await (let file of files) {
|
||||
const extension = file?.name?.split('.')?.pop()?.toLowerCase();
|
||||
|
||||
/* text file */
|
||||
const icon = fileImgs.find((item) => new RegExp(item.suffix, 'gi').test(file.name))?.src;
|
||||
|
||||
if (!icon) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse and upload files
|
||||
let [text, filesId] = await Promise.all([
|
||||
(async () => {
|
||||
switch (extension) {
|
||||
case 'txt':
|
||||
case 'md':
|
||||
@@ -95,68 +110,75 @@ const FileSelect = ({
|
||||
return readDocContent(file);
|
||||
}
|
||||
return '';
|
||||
})();
|
||||
|
||||
if (!icon) return result;
|
||||
|
||||
if (text) {
|
||||
text = simpleText(text);
|
||||
const splitRes = splitText2Chunks({
|
||||
text,
|
||||
maxLen: chunkLen
|
||||
});
|
||||
const fileItem: FileItemType = {
|
||||
id: nanoid(),
|
||||
filename: file.name,
|
||||
icon,
|
||||
text,
|
||||
tokens: splitRes.tokens,
|
||||
chunks: splitRes.chunks.map((chunk) => ({
|
||||
q: chunk,
|
||||
a: '',
|
||||
source: file.name
|
||||
}))
|
||||
};
|
||||
return [fileItem].concat(result);
|
||||
}
|
||||
|
||||
/* csv file */
|
||||
if (extension === 'csv') {
|
||||
const { header, data } = await readCsvContent(file);
|
||||
if (header[0] !== 'question' || header[1] !== 'answer') {
|
||||
throw new Error('csv 文件格式有误,请确保 question 和 answer 两列');
|
||||
})(),
|
||||
uploadFiles([file], { kbId: kbDetail._id }, (percent) => {
|
||||
if (percent < 100) {
|
||||
setSelectingText(
|
||||
t('file.Uploading', { name: file.name.slice(0, 20), percent }) || ''
|
||||
);
|
||||
} else {
|
||||
setSelectingText(t('file.Parse', { name: file.name.slice(0, 20) }) || '');
|
||||
}
|
||||
const fileItem: FileItemType = {
|
||||
id: nanoid(),
|
||||
filename: file.name,
|
||||
icon,
|
||||
tokens: 0,
|
||||
text: '',
|
||||
chunks: data.map((item) => ({
|
||||
q: item[0],
|
||||
a: item[1],
|
||||
source: item[2] || file.name
|
||||
}))
|
||||
};
|
||||
return [fileItem].concat(result);
|
||||
})
|
||||
]);
|
||||
|
||||
if (text) {
|
||||
text = simpleText(text);
|
||||
const splitRes = splitText2Chunks({
|
||||
text,
|
||||
maxLen: chunkLen
|
||||
});
|
||||
const fileItem: FileItemType = {
|
||||
id: nanoid(),
|
||||
filename: file.name,
|
||||
icon,
|
||||
text,
|
||||
tokens: splitRes.tokens,
|
||||
chunks: splitRes.chunks.map((chunk) => ({
|
||||
q: chunk,
|
||||
a: '',
|
||||
source: file.name,
|
||||
file_id: filesId[0]
|
||||
}))
|
||||
};
|
||||
chunkFiles.unshift(fileItem);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* csv file */
|
||||
if (extension === 'csv') {
|
||||
const { header, data } = await readCsvContent(file);
|
||||
if (header[0] !== 'question' || header[1] !== 'answer') {
|
||||
throw new Error('csv 文件格式有误,请确保 question 和 answer 两列');
|
||||
}
|
||||
return result;
|
||||
});
|
||||
});
|
||||
|
||||
const chunkFiles = await promise;
|
||||
const fileItem: FileItemType = {
|
||||
id: nanoid(),
|
||||
filename: file.name,
|
||||
icon,
|
||||
tokens: 0,
|
||||
text: '',
|
||||
chunks: data.map((item) => ({
|
||||
q: item[0],
|
||||
a: item[1],
|
||||
source: item[2] || file.name,
|
||||
file_id: filesId[0]
|
||||
}))
|
||||
};
|
||||
|
||||
chunkFiles.unshift(fileItem);
|
||||
}
|
||||
}
|
||||
onPushFiles(chunkFiles);
|
||||
} catch (error: any) {
|
||||
console.log(error);
|
||||
toast({
|
||||
title: typeof error === 'string' ? error : '解析文件失败',
|
||||
title: getErrText(error, '解析文件失败'),
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
setSelecting(false);
|
||||
setSelectingText(undefined);
|
||||
},
|
||||
[chunkLen, onPushFiles, toast]
|
||||
[chunkLen, onPushFiles, t, toast]
|
||||
);
|
||||
const onUrlFetch = useCallback(
|
||||
(e: FetchResultItem[]) => {
|
||||
@@ -353,7 +375,9 @@ const FileSelect = ({
|
||||
{t('file.Click to download CSV template')}
|
||||
</Box>
|
||||
)}
|
||||
<FileSelectLoading loading={selecting} fixed={false} />
|
||||
{selectingText !== undefined && (
|
||||
<FileSelectLoading loading text={selectingText} fixed={false} />
|
||||
)}
|
||||
<File onSelect={onSelectFile} />
|
||||
{isOpenUrlFetch && <UrlFetchModal onClose={onCloseUrlFetch} onSuccess={onUrlFetch} />}
|
||||
{isOpenCreateFile && <CreateFileModal onClose={onCloseCreateFile} onSuccess={onCreateFile} />}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Box, Textarea, Button } from '@chakra-ui/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';
|
||||
@@ -7,6 +7,8 @@ import { getErrText } from '@/utils/tools';
|
||||
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 };
|
||||
|
||||
@@ -70,7 +72,12 @@ const ManualImport = ({ kbId }: { kbId: string }) => {
|
||||
<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%']} position={'relative'}>
|
||||
<Box h={'30px'}>{'匹配的知识点'}</Box>
|
||||
<Flex>
|
||||
<Box h={'30px'}>{'匹配的知识点'}</Box>
|
||||
<MyTooltip label={'被向量化的部分,通常是问题,也可以是一段陈述描述'}>
|
||||
<QuestionOutlineIcon ml={1} />
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
<Textarea
|
||||
placeholder={`匹配的知识点。这部分内容会被搜索,请把控内容的质量。最多 ${maxToken} 字。`}
|
||||
maxLength={maxToken}
|
||||
@@ -87,10 +94,17 @@ const ManualImport = ({ kbId }: { kbId: string }) => {
|
||||
</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 字。'
|
||||
'预期答案。这部分内容不会被搜索,但会作为"匹配的知识点"的内容补充,通常是问题的答案。总和最多 3000 字。'
|
||||
}
|
||||
h={['250px', '500px']}
|
||||
maxLength={3000}
|
||||
|
||||
@@ -53,7 +53,7 @@ const QAImport = ({ kbId }: { kbId: string }) => {
|
||||
|
||||
// subsection import
|
||||
let success = 0;
|
||||
const step = 300;
|
||||
const step = 200;
|
||||
for (let i = 0; i < chunks.length; i += step) {
|
||||
const { insertLen } = await postKbDataFromList({
|
||||
kbId,
|
||||
@@ -97,10 +97,9 @@ const QAImport = ({ kbId }: { kbId: string }) => {
|
||||
return {
|
||||
...file,
|
||||
tokens: splitRes.tokens,
|
||||
chunks: splitRes.chunks.map((chunk) => ({
|
||||
q: chunk,
|
||||
a: '',
|
||||
source: file.filename
|
||||
chunks: file.chunks.map((chunk, i) => ({
|
||||
...chunk,
|
||||
q: splitRes.chunks[i]
|
||||
}))
|
||||
};
|
||||
})
|
||||
|
||||
@@ -156,6 +156,12 @@ const Info = (
|
||||
</Box>
|
||||
<Box flex={[1, '0 0 300px']}>{getValues('vectorModel').name}</Box>
|
||||
</Flex>
|
||||
<Flex mt={8} w={'100%'} alignItems={'center'}>
|
||||
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
|
||||
MaxTokens
|
||||
</Box>
|
||||
<Box flex={[1, '0 0 300px']}>{getValues('vectorModel').maxToken}</Box>
|
||||
</Flex>
|
||||
<Flex mt={5} w={'100%'} alignItems={'center'}>
|
||||
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
|
||||
知识库头像
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { Box, Flex, Button, Textarea, IconButton } from '@chakra-ui/react';
|
||||
import { Box, Flex, Button, Textarea, IconButton, BoxProps } 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 { getFileViewUrl } from '@/api/system';
|
||||
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';
|
||||
import { DatasetItemType } from '@/types/plugin';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export type FormData = { dataId?: string; a: string; q: string };
|
||||
export type FormData = { dataId?: string } & DatasetItemType;
|
||||
|
||||
const InputDataModal = ({
|
||||
onClose,
|
||||
@@ -27,19 +32,24 @@ const InputDataModal = ({
|
||||
kbId: string;
|
||||
defaultValues?: FormData;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { toast } = useToast();
|
||||
|
||||
const { register, handleSubmit, reset } = useForm<FormData>({
|
||||
const { kbDetail, getKbDetail } = useUserStore();
|
||||
|
||||
const { getValues, 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,31 +60,24 @@ const InputDataModal = ({
|
||||
|
||||
try {
|
||||
const data = {
|
||||
dataId: '',
|
||||
a: e.a,
|
||||
q: e.q,
|
||||
source: '手动录入'
|
||||
};
|
||||
const { insertLen } = await postKbDataFromList({
|
||||
data.dataId = await insertData2Kb({
|
||||
kbId,
|
||||
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) {
|
||||
@@ -85,7 +88,7 @@ const InputDataModal = ({
|
||||
}
|
||||
setLoading(false);
|
||||
},
|
||||
[kbId, onSuccess, reset, toast]
|
||||
[kbId, maxToken, onSuccess, reset, toast]
|
||||
);
|
||||
|
||||
const updateData = useCallback(
|
||||
@@ -121,6 +124,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}
|
||||
@@ -142,10 +150,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`, {
|
||||
@@ -154,10 +167,17 @@ 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 字。'
|
||||
'预期答案。这部分内容不会被搜索,但会作为"匹配的知识点"的内容补充,通常是问题的答案。总和最多 3000 字。'
|
||||
}
|
||||
maxLength={3000}
|
||||
resize={'none'}
|
||||
@@ -167,7 +187,16 @@ const InputDataModal = ({
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Flex px={6} pt={2} pb={4} alignItems={'center'}>
|
||||
<Flex px={6} pt={['34px', 2]} pb={4} alignItems={'center'} position={'relative'}>
|
||||
<RawFileText
|
||||
fileId={getValues('file_id')}
|
||||
filename={getValues('source')}
|
||||
position={'absolute'}
|
||||
left={'50%'}
|
||||
top={['16px', '50%']}
|
||||
transform={'translate(-50%,-50%)'}
|
||||
/>
|
||||
|
||||
<Box flex={1}>
|
||||
{defaultValues.dataId && onDelete && (
|
||||
<IconButton
|
||||
@@ -201,15 +230,17 @@ const InputDataModal = ({
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<Button variant={'base'} mr={3} isLoading={loading} onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
isLoading={loading}
|
||||
onClick={handleSubmit(defaultValues.dataId ? updateData : sureImportData)}
|
||||
>
|
||||
{defaultValues.dataId ? '确认变更' : '确认导入'}
|
||||
</Button>
|
||||
<Box>
|
||||
<Button variant={'base'} mr={3} isLoading={loading} onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
isLoading={loading}
|
||||
onClick={handleSubmit(defaultValues.dataId ? updateData : sureImportData)}
|
||||
>
|
||||
{defaultValues.dataId ? '确认变更' : '确认导入'}
|
||||
</Button>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</MyModal>
|
||||
@@ -217,3 +248,44 @@ const InputDataModal = ({
|
||||
};
|
||||
|
||||
export default InputDataModal;
|
||||
|
||||
interface RawFileTextProps extends BoxProps {
|
||||
filename?: string;
|
||||
fileId?: string;
|
||||
}
|
||||
export function RawFileText({ fileId, filename = '', ...props }: RawFileTextProps) {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
return (
|
||||
<MyTooltip label={fileId ? t('file.Click to view file') || '' : ''} shouldWrapChildren={false}>
|
||||
<Box
|
||||
color={'myGray.600'}
|
||||
display={'inline-block'}
|
||||
{...(!!fileId
|
||||
? {
|
||||
cursor: 'pointer',
|
||||
textDecoration: ['underline', 'none'],
|
||||
_hover: {
|
||||
textDecoration: 'underline'
|
||||
},
|
||||
onClick: async () => {
|
||||
try {
|
||||
const url = await getFileViewUrl(fileId);
|
||||
const asPath = `${location.origin}${url}`;
|
||||
window.open(asPath, '_blank');
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: getErrText(error, '获取文件地址失败'),
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
: {})}
|
||||
{...props}
|
||||
>
|
||||
{filename}
|
||||
</Box>
|
||||
</MyTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -207,8 +207,7 @@ const Test = ({ kbId }: { kbId: string }) => {
|
||||
|
||||
setEditData({
|
||||
dataId: data.id,
|
||||
q: data.q,
|
||||
a: data.a
|
||||
...data
|
||||
});
|
||||
} catch (err) {
|
||||
toast({
|
||||
|
||||
@@ -22,6 +22,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { getTrainingQueueLen } from '@/api/plugins/kb';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import { feConfigs } from '@/store/static';
|
||||
|
||||
const ImportData = dynamic(() => import('./components/Import'), {
|
||||
ssr: false
|
||||
@@ -119,7 +120,10 @@ const Detail = ({ kbId, currentTab }: { kbId: string; currentTab: `${TabEnum}` }
|
||||
<Flex justifyContent={'center'} alignItems={'center'}>
|
||||
<MyIcon mr={1} name="overviewLight" w={'16px'} color={'green.500'} />
|
||||
<Box>{t('dataset.System Data Queue')}</Box>
|
||||
<MyTooltip label={t('dataset.Queue Desc')} placement={'top'}>
|
||||
<MyTooltip
|
||||
label={t('dataset.Queue Desc', { title: feConfigs?.systemTitle })}
|
||||
placement={'top'}
|
||||
>
|
||||
<QuestionOutlineIcon ml={1} w={'16px'} />
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
|
||||
@@ -31,7 +31,7 @@ const Kb = () => {
|
||||
const { toast } = useToast();
|
||||
const { openConfirm, ConfirmModal } = useConfirm({
|
||||
title: '删除提示',
|
||||
content: '确认删除该知识库?'
|
||||
content: '确认删除该知识库?知识库相关的文件、记录将永久删除,无法恢复!'
|
||||
});
|
||||
const { myKbList, loadKbList, setKbList } = useUserStore();
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ const Login = () => {
|
||||
>
|
||||
<DynamicComponent type={pageType} />
|
||||
|
||||
{feConfigs?.show_register && (
|
||||
{feConfigs?.show_contact && (
|
||||
<Box
|
||||
fontSize={'sm'}
|
||||
color={'myGray.600'}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { ResLogin } from '@/api/response/user';
|
||||
@@ -10,6 +10,7 @@ import { useToast } from '@/hooks/useToast';
|
||||
import Loading from '@/components/Loading';
|
||||
import { serviceSideProps } from '@/utils/i18n';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
|
||||
const provider = ({ code }: { code: string }) => {
|
||||
const { loginStore } = useGlobalStore();
|
||||
@@ -56,15 +57,19 @@ const provider = ({ code }: { code: string }) => {
|
||||
status: 'warning',
|
||||
title: '登录异常'
|
||||
});
|
||||
return router.replace('/login');
|
||||
return setTimeout(() => {
|
||||
router.replace('/login');
|
||||
}, 1000);
|
||||
}
|
||||
loginSuccess(res);
|
||||
} catch (error) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: '登录异常'
|
||||
title: getErrText(error, '登录异常')
|
||||
});
|
||||
router.replace('/login');
|
||||
setTimeout(() => {
|
||||
router.replace('/login');
|
||||
}, 1000);
|
||||
}
|
||||
}, [code, loginStore, loginSuccess]);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user