Compare commits
35 Commits
v4.8.21
...
v4.8.23-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb0eb49196 | ||
|
|
27ebd2e8cf | ||
|
|
81a06718d8 | ||
|
|
3c382d1240 | ||
|
|
747bb303ec | ||
|
|
cf9c8e9f6a | ||
|
|
5d5bee9e41 | ||
|
|
4f0dd96699 | ||
|
|
fb6dbaf2d6 | ||
|
|
ffc1520f4c | ||
|
|
255764400f | ||
|
|
3bfe802c48 | ||
|
|
2bf17dbb87 | ||
|
|
8d766372fe | ||
|
|
ca5717936b | ||
|
|
6762723b10 | ||
|
|
8604cbd021 | ||
|
|
206325bc5f | ||
|
|
5fd520c794 | ||
|
|
09205e4666 | ||
|
|
ccf28d83b8 | ||
|
|
420aaad48e | ||
|
|
8ba2339890 | ||
|
|
e7b8934367 | ||
|
|
3e13397614 | ||
|
|
b14674cc6f | ||
|
|
4d20274a97 | ||
|
|
4447e40364 | ||
|
|
23949230ee | ||
|
|
cd7a897304 | ||
|
|
18aff8b8db | ||
|
|
d2b60ec785 | ||
|
|
1226fe42a1 | ||
|
|
abd375cdec | ||
|
|
7aacce8b0b |
BIN
docSite/assets/imgs/appid.png
Normal file
|
After Width: | Height: | Size: 332 KiB |
@@ -142,6 +142,10 @@ OneAPI 的 API Key 配置错误,需要修改`OPENAI_API_KEY`环境变量,并
|
|||||||
3. ....
|
3. ....
|
||||||
|
|
||||||
|
|
||||||
|
### Tiktoken 下载失败
|
||||||
|
|
||||||
|
由于 OneAPI 会在启动时从网络下载一个 tiktoken 的依赖,如果网络异常,就会导致启动失败。可以参考[OneAPI 离线部署](https://blog.csdn.net/wanh/article/details/139039216)解决。
|
||||||
|
|
||||||
## 四、常见模型问题
|
## 四、常见模型问题
|
||||||
|
|
||||||
### 如何检查模型可用性问题
|
### 如何检查模型可用性问题
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ weight: 705
|
|||||||
|
|
||||||
- [Git](http://git-scm.com/)
|
- [Git](http://git-scm.com/)
|
||||||
- [Docker](https://www.docker.com/)(构建镜像)
|
- [Docker](https://www.docker.com/)(构建镜像)
|
||||||
- [Node.js v18.17 / v20.x](http://nodejs.org)(版本尽量一样,可以使用nvm管理node版本)
|
- [Node.js v20.14.0](http://nodejs.org)(版本尽量一样,可以使用nvm管理node版本)
|
||||||
- [pnpm](https://pnpm.io/) 版本 8.6.0 (目前官方的开发环境)
|
- [pnpm](https://pnpm.io/) 推荐版本 9.4.0 (目前官方的开发环境)
|
||||||
- make命令: 根据不同平台,百度安装 (官方是GNU Make 4.3)
|
- make命令: 根据不同平台,百度安装 (官方是GNU Make 4.3)
|
||||||
|
|
||||||
## 开始本地开发
|
## 开始本地开发
|
||||||
@@ -77,8 +77,6 @@ Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnec
|
|||||||
可参考项目根目录下的 `dev.md`,第一次编译运行可能会有点慢,需要点耐心哦
|
可参考项目根目录下的 `dev.md`,第一次编译运行可能会有点慢,需要点耐心哦
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 给自动化脚本代码执行权限(非 linux 系统, 可以手动执行里面的 postinstall.sh 文件内容)
|
|
||||||
chmod -R +x ./scripts/
|
|
||||||
# 代码根目录下执行,会安装根 package、projects 和 packages 内所有依赖
|
# 代码根目录下执行,会安装根 package、projects 和 packages 内所有依赖
|
||||||
# 如果提示 isolate-vm 安装失败,可以参考:https://github.com/laverdet/isolated-vm?tab=readme-ov-file#requirements
|
# 如果提示 isolate-vm 安装失败,可以参考:https://github.com/laverdet/isolated-vm?tab=readme-ov-file#requirements
|
||||||
pnpm i
|
pnpm i
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ weight: 744
|
|||||||
|
|
||||||
{{% alert icon=" " context="info" %}}
|
{{% alert icon=" " context="info" %}}
|
||||||
- [SiliconCloud(硅基流动)](https://cloud.siliconflow.cn/i/TR9Ym0c4): 提供开源模型调用的平台。
|
- [SiliconCloud(硅基流动)](https://cloud.siliconflow.cn/i/TR9Ym0c4): 提供开源模型调用的平台。
|
||||||
- [Sealos AIProxy](https://hzh.sealos.run/?openapp=system-aiproxy): 提供国内各家模型代理,无需逐一申请 api。
|
- [Sealos AIProxy](https://cloud.sealos.run/?uid=fnWRt09fZP&openapp=system-aiproxy): 提供国内各家模型代理,无需逐一申请 api。
|
||||||
{{% /alert %}}
|
{{% /alert %}}
|
||||||
|
|
||||||
在 OneAPI 配置好模型后,你就可以打开 FastGPT 页面,启用对应模型了。
|
在 OneAPI 配置好模型后,你就可以打开 FastGPT 页面,启用对应模型了。
|
||||||
@@ -43,8 +43,7 @@ weight: 744
|
|||||||
{{% alert icon="🤖 " context="success" %}}
|
{{% alert icon="🤖 " context="success" %}}
|
||||||
注意:
|
注意:
|
||||||
1. 目前语音识别模型和重排模型仅会生效一个,所以配置时候,只需要配置一个即可。
|
1. 目前语音识别模型和重排模型仅会生效一个,所以配置时候,只需要配置一个即可。
|
||||||
2. 系统必须至少有一个语言模型和一个索引模型才能正常使用。
|
2. 系统至少需要一个语言模型和一个索引模型才能正常使用。
|
||||||
3. 使用知识库功能,至少要有一个语言模型,用于知识库文件处理(可以在模型配置时候打开该开关),否则知识库会报错。
|
|
||||||
{{% /alert %}}
|
{{% /alert %}}
|
||||||
|
|
||||||
#### 核心配置
|
#### 核心配置
|
||||||
@@ -468,4 +467,4 @@ OneAPI 的语言识别接口,无法正确的识别其他模型(会始终识
|
|||||||
"charsPointsPrice": 0
|
"charsPointsPrice": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ toc: true
|
|||||||
weight: 852
|
weight: 852
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# 如何获取 AppId
|
||||||
|
|
||||||
|
可在应用详情的路径里获取 AppId。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
# 发起对话
|
# 发起对话
|
||||||
|
|
||||||
{{% alert icon="🤖 " context="success" %}}
|
{{% alert icon="🤖 " context="success" %}}
|
||||||
@@ -102,8 +108,8 @@ curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
|
|||||||
{{% alert context="info" %}}
|
{{% alert context="info" %}}
|
||||||
- headers.Authorization: Bearer {{apikey}}
|
- headers.Authorization: Bearer {{apikey}}
|
||||||
- chatId: string | undefined 。
|
- chatId: string | undefined 。
|
||||||
- 为 `undefined` 时(不传入),不使用 FastGpt 提供的上下文功能,完全通过传入的 messages 构建上下文。 不会将你的记录存储到数据库中,你也无法在记录汇总中查阅到。
|
- 为 `undefined` 时(不传入),不使用 FastGpt 提供的上下文功能,完全通过传入的 messages 构建上下文。
|
||||||
- 为`非空字符串`时,意味着使用 chatId 进行对话,自动从 FastGpt 数据库取历史记录,并使用 messages 数组最后一个内容作为用户问题。请自行确保 chatId 唯一,长度小于250,通常可以是自己系统的对话框ID。
|
- 为`非空字符串`时,意味着使用 chatId 进行对话,自动从 FastGpt 数据库取历史记录,并使用 messages 数组最后一个内容作为用户问题,其余 message 会被忽略。请自行确保 chatId 唯一,长度小于250,通常可以是自己系统的对话框ID。
|
||||||
- messages: 结构与 [GPT接口](https://platform.openai.com/docs/api-reference/chat/object) chat模式一致。
|
- messages: 结构与 [GPT接口](https://platform.openai.com/docs/api-reference/chat/object) chat模式一致。
|
||||||
- responseChatItemId: string | undefined 。如果传入,则会将该值作为本次对话的响应消息的 ID,FastGPT 会自动将该 ID 存入数据库。请确保,在当前`chatId`下,`responseChatItemId`是唯一的。
|
- responseChatItemId: string | undefined 。如果传入,则会将该值作为本次对话的响应消息的 ID,FastGPT 会自动将该 ID 存入数据库。请确保,在当前`chatId`下,`responseChatItemId`是唯一的。
|
||||||
- detail: 是否返回中间值(模块状态,响应的完整结果等),`stream模式`下会通过`event`进行区分,`非stream模式`结果保存在`responseData`中。
|
- detail: 是否返回中间值(模块状态,响应的完整结果等),`stream模式`下会通过`event`进行区分,`非stream模式`结果保存在`responseData`中。
|
||||||
|
|||||||
@@ -735,7 +735,7 @@ data 为集合的 ID。
|
|||||||
|
|
||||||
**4.8.19+**
|
**4.8.19+**
|
||||||
```bash
|
```bash
|
||||||
curl --location --request POST 'http://localhost:3000/api/core/dataset/collection/listv2' \
|
curl --location --request POST 'http://localhost:3000/api/core/dataset/collection/listV2' \
|
||||||
--header 'Authorization: Bearer {{authorization}}' \
|
--header 'Authorization: Bearer {{authorization}}' \
|
||||||
--header 'Content-Type: application/json' \
|
--header 'Content-Type: application/json' \
|
||||||
--data-raw '{
|
--data-raw '{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 'Api Key 使用与鉴权'
|
title: 'OpenAPI 介绍'
|
||||||
description: 'FastGPT Api Key 使用与鉴权'
|
description: 'FastGPT OpenAPI 介绍'
|
||||||
icon: 'key'
|
icon: 'key'
|
||||||
draft: false
|
draft: false
|
||||||
toc: true
|
toc: true
|
||||||
@@ -27,6 +27,7 @@ FastGPT 的 API Key **有 2 类**,一类是全局通用的 key (无法直接
|
|||||||
| --------------------- | --------------------- |
|
| --------------------- | --------------------- |
|
||||||
|  |  |
|
|  |  |
|
||||||
|
|
||||||
|
|
||||||
## 基本配置
|
## 基本配置
|
||||||
|
|
||||||
OpenAPI 中,所有的接口都通过 Header.Authorization 进行鉴权。
|
OpenAPI 中,所有的接口都通过 Header.Authorization 进行鉴权。
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: 'V4.8.18'
|
title: 'V4.8.18(包含升级脚本)'
|
||||||
description: 'FastGPT V4.8.18 更新说明'
|
description: 'FastGPT V4.8.18 更新说明'
|
||||||
icon: 'upgrade'
|
icon: 'upgrade'
|
||||||
draft: false
|
draft: false
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
---
|
---
|
||||||
title: 'V4.8.21(进行中)'
|
title: 'V4.8.21'
|
||||||
description: 'FastGPT V4.8.21 更新说明'
|
description: 'FastGPT V4.8.21 更新说明'
|
||||||
icon: 'upgrade'
|
icon: 'upgrade'
|
||||||
draft: false
|
draft: false
|
||||||
toc: true
|
toc: true
|
||||||
weight: 804
|
weight: 803
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 更新指南
|
||||||
|
|
||||||
|
### 1. 做好数据库备份
|
||||||
|
|
||||||
|
### 2. 更新镜像:
|
||||||
|
|
||||||
|
- 更新 fastgpt 镜像 tag: v4.8.21-fix
|
||||||
|
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.21-fix
|
||||||
|
- Sandbox 镜像无需更新
|
||||||
|
|
||||||
## 完整更新内容
|
## 完整更新内容
|
||||||
|
|
||||||
|
|||||||
61
docSite/content/zh-cn/docs/development/upgrading/4822.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
---
|
||||||
|
title: 'V4.8.22(包含升级脚本)'
|
||||||
|
description: 'FastGPT V4.8.22 更新说明'
|
||||||
|
icon: 'upgrade'
|
||||||
|
draft: false
|
||||||
|
toc: true
|
||||||
|
weight: 802
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌟更新指南
|
||||||
|
|
||||||
|
### 1. 做好数据库备份
|
||||||
|
|
||||||
|
### 2. 更新镜像:
|
||||||
|
|
||||||
|
- 更新 fastgpt 镜像 tag: v4.8.22
|
||||||
|
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.22
|
||||||
|
- Sandbox 镜像无需更新
|
||||||
|
|
||||||
|
### 3. 运行升级脚本
|
||||||
|
|
||||||
|
仅商业版,并提供 Saas 服务的用户需要运行该升级脚本。
|
||||||
|
|
||||||
|
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 域名**。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location --request POST 'https://{{host}}/api/admin/initv4822' \
|
||||||
|
--header 'rootkey: {{rootkey}}' \
|
||||||
|
--header 'Content-Type: application/json'
|
||||||
|
```
|
||||||
|
|
||||||
|
会迁移联系方式到对应用户表中。
|
||||||
|
|
||||||
|
## 🚀 新增内容
|
||||||
|
|
||||||
|
1. AI 对话节点解析 `<think></think>` 标签内容作为思考链,便于各类模型进行思考链输出。需主动开启模型输出思考。
|
||||||
|
2. 对话 API 优化,无论是否传递 chatId,都会保存对话日志。未传递 chatId,则随机生成一个 chatId 来进行存储。
|
||||||
|
3. ppio 模型提供商
|
||||||
|
|
||||||
|
## ⚙️ 优化
|
||||||
|
|
||||||
|
1. 模型未配置时提示,减少冲突提示。
|
||||||
|
2. 使用记录代码。
|
||||||
|
3. 内容提取节点,字段描述过长时换行。同时修改其输出名用 key,而不是 description。
|
||||||
|
4. 团队管理交互。
|
||||||
|
5. 对话接口,非流响应,增加报错字段。
|
||||||
|
|
||||||
|
## 🐛 修复
|
||||||
|
|
||||||
|
1. 思考内容未进入到输出 Tokens.
|
||||||
|
2. 思考链流输出时,有时与正文顺序偏差。
|
||||||
|
3. API 调用工作流,如果传递的图片不支持 Head 检测时,图片会被过滤。已增加该类错误检测,避免被错误过滤。
|
||||||
|
4. 模板市场部分模板错误。
|
||||||
|
5. 免登录窗口无法正常判断语言识别是否开启。
|
||||||
|
6. 对话日志导出,未兼容 sub path。
|
||||||
|
7. 切换团队时未刷新成员列表
|
||||||
|
8. list 接口在联查 member 时,存在空指针可能性。
|
||||||
|
9. 工作流基础节点无法升级。
|
||||||
|
10. 向量检索结果未去重。
|
||||||
|
11. 用户选择节点无法正常连线。
|
||||||
|
12. 对话记录保存时,source 未正常记录。
|
||||||
30
docSite/content/zh-cn/docs/development/upgrading/4823.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
title: 'V4.8.23(进行中)'
|
||||||
|
description: 'FastGPT V4.8.23 更新说明'
|
||||||
|
icon: 'upgrade'
|
||||||
|
draft: false
|
||||||
|
toc: true
|
||||||
|
weight: 802
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## 🚀 新增内容
|
||||||
|
|
||||||
|
1. 增加默认“知识库文本理解模型”配置
|
||||||
|
2. AI proxy V1版,可替换 OneAPI使用,同时提供完整模型调用日志,便于排查问题。
|
||||||
|
|
||||||
|
## ⚙️ 优化
|
||||||
|
|
||||||
|
1. 模型配置表单,增加必填项校验。
|
||||||
|
2. 集合列表数据统计方式,提高大数据量统计性能。
|
||||||
|
3. 优化数学公式,转义 Latex 格式成 Markdown 格式。
|
||||||
|
4. 解析文档图片,图片太大时,自动忽略。
|
||||||
|
5. 时间选择器,当天开始时间自动设0,结束设置设 23:59:59,避免 UI 与实际逻辑偏差。
|
||||||
|
6. 升级 mongoose 库版本依赖。
|
||||||
|
|
||||||
|
## 🐛 修复
|
||||||
|
|
||||||
|
1. 标签过滤时,子文件夹未成功过滤。
|
||||||
|
2. 暂时移除 md 阅读优化,避免链接分割错误。
|
||||||
|
3. 离开团队时,未刷新成员列表。
|
||||||
|
4. PPTX 编码错误,导致解析失败。
|
||||||
@@ -7,11 +7,11 @@ toc: true
|
|||||||
weight: 102
|
weight: 102
|
||||||
---
|
---
|
||||||
|
|
||||||
更多使用技巧,[查看视屏教程](https://www.bilibili.com/video/BV1sH4y1T7s9)
|
更多使用技巧,[查看视频教程](https://www.bilibili.com/video/BV1sH4y1T7s9)
|
||||||
|
|
||||||
## 知识库
|
## 知识库
|
||||||
|
|
||||||
开始前,请准备一份测试电子文档,WORD,PDF,TXT,excel,markdown 都可以,比如公司休假制度,不涉密的销售说辞,产品知识等等。
|
开始前,请准备一份测试电子文档,WORD、PDF、TXT、excel、markdown 都可以,比如公司休假制度、不涉密的销售说辞、产品知识等等。
|
||||||
|
|
||||||
这里使用 FastGPT 中文 README 文件为例。
|
这里使用 FastGPT 中文 README 文件为例。
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ weight: 102
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
点击上传后我们需要等待数据处理完成,等到我们上传的文件状态为可用。
|
点击上传后我们需要等待数据处理完成,直到我们上传的文件状态为可用。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ weight: 502
|
|||||||

|

|
||||||
|
|
||||||
{{% alert icon="🍅" context="success" %}}
|
{{% alert icon="🍅" context="success" %}}
|
||||||
Tips: 安全起见,你可以设置一个额度或者过期时间,放置 key 被滥用。
|
Tips: 安全起见,你可以设置一个额度或者过期时间,防止 key 被滥用。
|
||||||
{{% /alert %}}
|
{{% /alert %}}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -114,15 +114,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.20-fix2 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.22 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.20-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.22 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.20-fix2 # git
|
image: ghcr.io/labring/fastgpt:v4.8.22 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.20-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.22 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -133,7 +133,7 @@ services:
|
|||||||
- sandbox
|
- sandbox
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
# 前端访问地址: http://localhost:3000
|
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||||
- FE_DOMAIN=
|
- FE_DOMAIN=
|
||||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||||
- DEFAULT_ROOT_PSW=1234
|
- DEFAULT_ROOT_PSW=1234
|
||||||
|
|||||||
@@ -72,15 +72,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.20-fix2 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.22 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.20-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.22 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.20-fix2 # git
|
image: ghcr.io/labring/fastgpt:v4.8.22 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.20-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.22 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -91,7 +91,7 @@ services:
|
|||||||
- sandbox
|
- sandbox
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
# 前端访问地址: http://localhost:3000
|
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||||
- FE_DOMAIN=
|
- FE_DOMAIN=
|
||||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||||
- DEFAULT_ROOT_PSW=1234
|
- DEFAULT_ROOT_PSW=1234
|
||||||
|
|||||||
@@ -53,15 +53,15 @@ services:
|
|||||||
wait $$!
|
wait $$!
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.20-fix2 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.22 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.20-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.22 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.20-fix2 # git
|
image: ghcr.io/labring/fastgpt:v4.8.22 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.20-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.22 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -71,7 +71,7 @@ services:
|
|||||||
- sandbox
|
- sandbox
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
# 前端访问地址: http://localhost:3000
|
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||||
- FE_DOMAIN=
|
- FE_DOMAIN=
|
||||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||||
- DEFAULT_ROOT_PSW=1234
|
- DEFAULT_ROOT_PSW=1234
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"format-code": "prettier --config \"./.prettierrc.js\" --write \"./**/src/**/*.{ts,tsx,scss}\"",
|
"format-code": "prettier --config \"./.prettierrc.js\" --write \"./**/src/**/*.{ts,tsx,scss}\"",
|
||||||
"format-doc": "zhlint --dir ./docSite *.md --fix",
|
"format-doc": "zhlint --dir ./docSite *.md --fix",
|
||||||
"gen:theme-typings": "chakra-cli tokens packages/web/styles/theme.ts --out node_modules/.pnpm/node_modules/@chakra-ui/styled-system/dist/theming.types.d.ts",
|
"gen:theme-typings": "chakra-cli tokens packages/web/styles/theme.ts --out node_modules/.pnpm/node_modules/@chakra-ui/styled-system/dist/theming.types.d.ts",
|
||||||
"postinstall": "sh ./scripts/postinstall.sh",
|
"postinstall": "pnpm gen:theme-typings",
|
||||||
"initIcon": "node ./scripts/icon/init.js",
|
"initIcon": "node ./scripts/icon/init.js",
|
||||||
"previewIcon": "node ./scripts/icon/index.js",
|
"previewIcon": "node ./scripts/icon/index.js",
|
||||||
"api:gen": "tsc ./scripts/openapi/index.ts && node ./scripts/openapi/index.js && npx @redocly/cli build-docs ./scripts/openapi/openapi.json -o ./projects/app/public/openapi/index.html",
|
"api:gen": "tsc ./scripts/openapi/index.ts && node ./scripts/openapi/index.js && npx @redocly/cli build-docs ./scripts/openapi/openapi.json -o ./projects/app/public/openapi/index.html",
|
||||||
|
|||||||
@@ -20,4 +20,4 @@ export const ReadFileBaseUrl = `${process.env.FILE_DOMAIN || process.env.FE_DOMA
|
|||||||
|
|
||||||
export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx';
|
export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx';
|
||||||
export const imageFileType =
|
export const imageFileType =
|
||||||
'.jpg, .jpeg, .png, .gif, .bmp, .webp, .svg, .tiff, .tif, .ico, .heic, .heif, .avif';
|
'.jpg, .jpeg, .png, .gif, .bmp, .webp, .svg, .tiff, .tif, .ico, .heic, .heif, .avif, .raw, .cr2, .nef, .arw, .dng, .psd, .ai, .eps, .emf, .wmf, .jfif, .exif, .pgm, .ppm, .pbm, .jp2, .j2k, .jpf, .jpx, .jpm, .mj2, .xbm, .pcx';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { detect } from 'jschardet';
|
import { detect } from 'jschardet';
|
||||||
import { documentFileType, imageFileType } from './constants';
|
import { documentFileType } from './constants';
|
||||||
import { ChatFileTypeEnum } from '../../core/chat/constants';
|
import { ChatFileTypeEnum } from '../../core/chat/constants';
|
||||||
import { UserChatItemValueItemType } from '../../core/chat/type';
|
import { UserChatItemValueItemType } from '../../core/chat/type';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
@@ -25,6 +25,7 @@ export const detectFileEncodingByPath = async (path: string) => {
|
|||||||
const fd = await fs.promises.open(path, 'r');
|
const fd = await fs.promises.open(path, 'r');
|
||||||
try {
|
try {
|
||||||
// Read file head
|
// Read file head
|
||||||
|
// @ts-ignore
|
||||||
const { bytesRead } = await fd.read(buffer, 0, MAX_BYTES, 0);
|
const { bytesRead } = await fd.read(buffer, 0, MAX_BYTES, 0);
|
||||||
const actualBuffer = buffer.slice(0, bytesRead);
|
const actualBuffer = buffer.slice(0, bytesRead);
|
||||||
|
|
||||||
@@ -37,40 +38,49 @@ export const detectFileEncodingByPath = async (path: string) => {
|
|||||||
// Url => user upload file type
|
// Url => user upload file type
|
||||||
export const parseUrlToFileType = (url: string): UserChatItemValueItemType['file'] | undefined => {
|
export const parseUrlToFileType = (url: string): UserChatItemValueItemType['file'] | undefined => {
|
||||||
if (typeof url !== 'string') return;
|
if (typeof url !== 'string') return;
|
||||||
const parseUrl = new URL(url, 'https://locaohost:3000');
|
|
||||||
|
|
||||||
const filename = (() => {
|
// Handle base64 image
|
||||||
// Check base64 image
|
if (url.startsWith('data:')) {
|
||||||
if (url.startsWith('data:image/')) {
|
const matches = url.match(/^data:([^;]+);base64,/);
|
||||||
const mime = url.split(',')[0].split(':')[1].split(';')[0];
|
if (!matches) return;
|
||||||
return `image.${mime.split('/')[1]}`;
|
|
||||||
}
|
|
||||||
// Old version file url: https://xxx.com/file/read?filename=xxx.pdf
|
|
||||||
const filenameQuery = parseUrl.searchParams.get('filename');
|
|
||||||
if (filenameQuery) return filenameQuery;
|
|
||||||
|
|
||||||
// Common file: https://xxx.com/xxx.pdf?xxxx=xxx
|
const mimeType = matches[1].toLowerCase();
|
||||||
const pathname = parseUrl.pathname;
|
if (!mimeType.startsWith('image/')) return;
|
||||||
if (pathname) return pathname.split('/').pop();
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (!filename) return;
|
const extension = mimeType.split('/')[1];
|
||||||
|
|
||||||
const extension = filename.split('.').pop()?.toLowerCase() || '';
|
|
||||||
|
|
||||||
if (!extension) return;
|
|
||||||
|
|
||||||
if (documentFileType.includes(extension)) {
|
|
||||||
return {
|
return {
|
||||||
type: ChatFileTypeEnum.file,
|
type: ChatFileTypeEnum.image,
|
||||||
name: filename,
|
name: `image.${extension}`,
|
||||||
url
|
url
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (imageFileType.includes(extension)) {
|
|
||||||
|
try {
|
||||||
|
const parseUrl = new URL(url, 'https://localhost:3000');
|
||||||
|
|
||||||
|
// Get filename from URL
|
||||||
|
const filename = parseUrl.searchParams.get('filename') || parseUrl.pathname.split('/').pop();
|
||||||
|
const extension = filename?.split('.').pop()?.toLowerCase() || '';
|
||||||
|
|
||||||
|
// If it's a document type, return as file, otherwise treat as image
|
||||||
|
if (extension && documentFileType.includes(extension)) {
|
||||||
|
return {
|
||||||
|
type: ChatFileTypeEnum.file,
|
||||||
|
name: filename || 'null',
|
||||||
|
url
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to image type for non-document files
|
||||||
return {
|
return {
|
||||||
type: ChatFileTypeEnum.image,
|
type: ChatFileTypeEnum.image,
|
||||||
name: filename,
|
name: filename || 'null.png',
|
||||||
|
url
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
type: ChatFileTypeEnum.image,
|
||||||
|
name: 'invalid.png',
|
||||||
url
|
url
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ import { i18nT } from '../../../web/i18n/utils';
|
|||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(timezone);
|
dayjs.extend(timezone);
|
||||||
|
|
||||||
export const formatTime2YMDHMW = (time?: Date) => dayjs(time).format('YYYY-MM-DD HH:mm:ss dddd');
|
export const formatTime2YMDHMW = (time?: Date | number) =>
|
||||||
export const formatTime2YMDHMS = (time?: Date) =>
|
dayjs(time).format('YYYY-MM-DD HH:mm:ss dddd');
|
||||||
|
export const formatTime2YMDHMS = (time?: Date | number) =>
|
||||||
time ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '';
|
time ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '';
|
||||||
export const formatTime2YMDHM = (time?: Date) =>
|
export const formatTime2YMDHM = (time?: Date | number) =>
|
||||||
time ? dayjs(time).format('YYYY-MM-DD HH:mm') : '';
|
time ? dayjs(time).format('YYYY-MM-DD HH:mm') : '';
|
||||||
export const formatTime2YMD = (time?: Date) => (time ? dayjs(time).format('YYYY-MM-DD') : '');
|
export const formatTime2YMD = (time?: Date | number) =>
|
||||||
|
time ? dayjs(time).format('YYYY-MM-DD') : '';
|
||||||
export const formatTime2HM = (time: Date = new Date()) => dayjs(time).format('HH:mm');
|
export const formatTime2HM = (time: Date = new Date()) => dayjs(time).format('HH:mm');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export type FastGPTConfigFileType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type FastGPTFeConfigsType = {
|
export type FastGPTFeConfigsType = {
|
||||||
|
show_workorder?: boolean;
|
||||||
show_emptyChat?: boolean;
|
show_emptyChat?: boolean;
|
||||||
register_method?: ['email' | 'phone' | 'sync'];
|
register_method?: ['email' | 'phone' | 'sync'];
|
||||||
login_method?: ['email' | 'phone']; // Attention: login method is diffrent with oauth
|
login_method?: ['email' | 'phone']; // Attention: login method is diffrent with oauth
|
||||||
@@ -53,6 +54,7 @@ export type FastGPTFeConfigsType = {
|
|||||||
show_promotion?: boolean;
|
show_promotion?: boolean;
|
||||||
show_team_chat?: boolean;
|
show_team_chat?: boolean;
|
||||||
show_compliance_copywriting?: boolean;
|
show_compliance_copywriting?: boolean;
|
||||||
|
show_aiproxy?: boolean;
|
||||||
concatMd?: string;
|
concatMd?: string;
|
||||||
|
|
||||||
docUrl?: string;
|
docUrl?: string;
|
||||||
|
|||||||
2
packages/global/core/ai/model.d.ts
vendored
@@ -17,6 +17,8 @@ type BaseModelItemType = {
|
|||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
isCustom?: boolean;
|
isCustom?: boolean;
|
||||||
isDefault?: boolean;
|
isDefault?: boolean;
|
||||||
|
isDefaultDatasetTextModel?: boolean;
|
||||||
|
isDefaultDatasetImageModel?: boolean;
|
||||||
|
|
||||||
// If has requestUrl, it will request the model directly
|
// If has requestUrl, it will request the model directly
|
||||||
requestUrl?: string;
|
requestUrl?: string;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export type ModelProviderIdType =
|
|||||||
| 'StepFun'
|
| 'StepFun'
|
||||||
| 'Yi'
|
| 'Yi'
|
||||||
| 'Siliconflow'
|
| 'Siliconflow'
|
||||||
|
| 'PPIO'
|
||||||
| 'Ollama'
|
| 'Ollama'
|
||||||
| 'BAAI'
|
| 'BAAI'
|
||||||
| 'FishAudio'
|
| 'FishAudio'
|
||||||
@@ -71,11 +72,6 @@ export const ModelProviderList: ModelProviderType[] = [
|
|||||||
name: 'Groq',
|
name: 'Groq',
|
||||||
avatar: 'model/groq'
|
avatar: 'model/groq'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'AliCloud',
|
|
||||||
name: i18nT('common:model_alicloud'),
|
|
||||||
avatar: 'model/alicloud'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'Qwen',
|
id: 'Qwen',
|
||||||
name: i18nT('common:model_qwen'),
|
name: i18nT('common:model_qwen'),
|
||||||
@@ -86,6 +82,11 @@ export const ModelProviderList: ModelProviderType[] = [
|
|||||||
name: i18nT('common:model_doubao'),
|
name: i18nT('common:model_doubao'),
|
||||||
avatar: 'model/doubao'
|
avatar: 'model/doubao'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'DeepSeek',
|
||||||
|
name: 'DeepSeek',
|
||||||
|
avatar: 'model/deepseek'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'ChatGLM',
|
id: 'ChatGLM',
|
||||||
name: i18nT('common:model_chatglm'),
|
name: i18nT('common:model_chatglm'),
|
||||||
@@ -96,11 +97,6 @@ export const ModelProviderList: ModelProviderType[] = [
|
|||||||
name: i18nT('common:model_ernie'),
|
name: i18nT('common:model_ernie'),
|
||||||
avatar: 'model/ernie'
|
avatar: 'model/ernie'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'DeepSeek',
|
|
||||||
name: 'DeepSeek',
|
|
||||||
avatar: 'model/deepseek'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'Moonshot',
|
id: 'Moonshot',
|
||||||
name: i18nT('common:model_moonshot'),
|
name: i18nT('common:model_moonshot'),
|
||||||
@@ -162,11 +158,21 @@ export const ModelProviderList: ModelProviderType[] = [
|
|||||||
name: i18nT('common:model_moka'),
|
name: i18nT('common:model_moka'),
|
||||||
avatar: 'model/moka'
|
avatar: 'model/moka'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'AliCloud',
|
||||||
|
name: i18nT('common:model_alicloud'),
|
||||||
|
avatar: 'model/alicloud'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'Siliconflow',
|
id: 'Siliconflow',
|
||||||
name: i18nT('common:model_siliconflow'),
|
name: i18nT('common:model_siliconflow'),
|
||||||
avatar: 'model/siliconflow'
|
avatar: 'model/siliconflow'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'PPIO',
|
||||||
|
name: i18nT('common:model_ppio'),
|
||||||
|
avatar: 'model/ppio'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'Other',
|
id: 'Other',
|
||||||
name: i18nT('common:model_other'),
|
name: i18nT('common:model_other'),
|
||||||
|
|||||||
8
packages/global/core/ai/type.d.ts
vendored
@@ -1,14 +1,12 @@
|
|||||||
import openai from 'openai';
|
import openai from 'openai';
|
||||||
import type {
|
import type {
|
||||||
ChatCompletionMessageToolCall,
|
ChatCompletionMessageToolCall,
|
||||||
ChatCompletionChunk,
|
|
||||||
ChatCompletionMessageParam as SdkChatCompletionMessageParam,
|
ChatCompletionMessageParam as SdkChatCompletionMessageParam,
|
||||||
ChatCompletionToolMessageParam,
|
ChatCompletionToolMessageParam,
|
||||||
ChatCompletionContentPart as SdkChatCompletionContentPart,
|
ChatCompletionContentPart as SdkChatCompletionContentPart,
|
||||||
ChatCompletionUserMessageParam as SdkChatCompletionUserMessageParam,
|
ChatCompletionUserMessageParam as SdkChatCompletionUserMessageParam,
|
||||||
ChatCompletionToolMessageParam as SdkChatCompletionToolMessageParam,
|
ChatCompletionToolMessageParam as SdkChatCompletionToolMessageParam,
|
||||||
ChatCompletionAssistantMessageParam as SdkChatCompletionAssistantMessageParam,
|
ChatCompletionAssistantMessageParam as SdkChatCompletionAssistantMessageParam
|
||||||
ChatCompletionContentPartText
|
|
||||||
} from 'openai/resources';
|
} from 'openai/resources';
|
||||||
import { ChatMessageTypeEnum } from './constants';
|
import { ChatMessageTypeEnum } from './constants';
|
||||||
import { WorkflowInteractiveResponseType } from '../workflow/template/system/interactive/type';
|
import { WorkflowInteractiveResponseType } from '../workflow/template/system/interactive/type';
|
||||||
@@ -48,6 +46,7 @@ export type ChatCompletionMessageParam = (
|
|||||||
| CustomChatCompletionToolMessageParam
|
| CustomChatCompletionToolMessageParam
|
||||||
| CustomChatCompletionAssistantMessageParam
|
| CustomChatCompletionAssistantMessageParam
|
||||||
) & {
|
) & {
|
||||||
|
reasoning_text?: string;
|
||||||
dataId?: string;
|
dataId?: string;
|
||||||
hideInUI?: boolean;
|
hideInUI?: boolean;
|
||||||
};
|
};
|
||||||
@@ -71,7 +70,8 @@ export type ChatCompletionMessageFunctionCall =
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Stream response
|
// Stream response
|
||||||
export type StreamChatType = Stream<ChatCompletionChunk>;
|
export type StreamChatType = Stream<openai.Chat.Completions.ChatCompletionChunk>;
|
||||||
|
export type UnStreamChatType = openai.Chat.Completions.ChatCompletion;
|
||||||
|
|
||||||
export default openai;
|
export default openai;
|
||||||
export * from 'openai';
|
export * from 'openai';
|
||||||
|
|||||||
@@ -46,7 +46,16 @@ export const chats2GPTMessages = ({
|
|||||||
|
|
||||||
messages.forEach((item) => {
|
messages.forEach((item) => {
|
||||||
const dataId = reserveId ? item.dataId : undefined;
|
const dataId = reserveId ? item.dataId : undefined;
|
||||||
if (item.obj === ChatRoleEnum.Human) {
|
if (item.obj === ChatRoleEnum.System) {
|
||||||
|
const content = item.value?.[0]?.text?.content;
|
||||||
|
if (content) {
|
||||||
|
results.push({
|
||||||
|
dataId,
|
||||||
|
role: ChatCompletionRequestMessageRoleEnum.System,
|
||||||
|
content
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (item.obj === ChatRoleEnum.Human) {
|
||||||
const value = item.value
|
const value = item.value
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
if (item.type === ChatItemValueTypeEnum.text) {
|
if (item.type === ChatItemValueTypeEnum.text) {
|
||||||
@@ -80,15 +89,6 @@ export const chats2GPTMessages = ({
|
|||||||
role: ChatCompletionRequestMessageRoleEnum.User,
|
role: ChatCompletionRequestMessageRoleEnum.User,
|
||||||
content: simpleUserContentPart(value)
|
content: simpleUserContentPart(value)
|
||||||
});
|
});
|
||||||
} else if (item.obj === ChatRoleEnum.System) {
|
|
||||||
const content = item.value?.[0]?.text?.content;
|
|
||||||
if (content) {
|
|
||||||
results.push({
|
|
||||||
dataId,
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.System,
|
|
||||||
content
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const aiResults: ChatCompletionMessageParam[] = [];
|
const aiResults: ChatCompletionMessageParam[] = [];
|
||||||
|
|
||||||
@@ -349,7 +349,7 @@ export const chatValue2RuntimePrompt = (value: ChatItemValueItemType[]): Runtime
|
|||||||
};
|
};
|
||||||
value.forEach((item) => {
|
value.forEach((item) => {
|
||||||
if (item.type === 'file' && item.file) {
|
if (item.type === 'file' && item.file) {
|
||||||
prompt.files?.push(item.file);
|
prompt.files.push(item.file);
|
||||||
} else if (item.text) {
|
} else if (item.text) {
|
||||||
prompt.text += item.text.content;
|
prompt.text += item.text.content;
|
||||||
}
|
}
|
||||||
|
|||||||
1
packages/global/core/dataset/type.d.ts
vendored
@@ -192,6 +192,7 @@ export type DatasetCollectionItemType = CollectionWithDatasetType & {
|
|||||||
sourceId?: string;
|
sourceId?: string;
|
||||||
file?: DatasetFileSchema;
|
file?: DatasetFileSchema;
|
||||||
permission: DatasetPermission;
|
permission: DatasetPermission;
|
||||||
|
indexAmount: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ================= data ===================== */
|
/* ================= data ===================== */
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
|
|||||||
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
||||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
||||||
import { replaceVariable, valToStr } from '../../../common/string/tools';
|
import { replaceVariable, valToStr } from '../../../common/string/tools';
|
||||||
|
import { ChatCompletionChunk } from 'openai/resources';
|
||||||
|
|
||||||
export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => {
|
export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => {
|
||||||
let limit = 10;
|
let limit = 10;
|
||||||
@@ -419,3 +420,137 @@ export function rewriteNodeOutputByHistories(
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse <think></think> tags to think and answer - unstream response
|
||||||
|
export const parseReasoningContent = (text: string): [string, string] => {
|
||||||
|
const regex = /<think>([\s\S]*?)<\/think>/;
|
||||||
|
const match = text.match(regex);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return ['', text];
|
||||||
|
}
|
||||||
|
|
||||||
|
const thinkContent = match[1].trim();
|
||||||
|
|
||||||
|
// Add answer (remaining text after think tag)
|
||||||
|
const answerContent = text.slice(match.index! + match[0].length);
|
||||||
|
|
||||||
|
return [thinkContent, answerContent];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse <think></think> tags to think and answer - stream response
|
||||||
|
export const parseReasoningStreamContent = () => {
|
||||||
|
let isInThinkTag: boolean | undefined;
|
||||||
|
|
||||||
|
const startTag = '<think>';
|
||||||
|
let startTagBuffer = '';
|
||||||
|
|
||||||
|
const endTag = '</think>';
|
||||||
|
let endTagBuffer = '';
|
||||||
|
|
||||||
|
/*
|
||||||
|
parseReasoning - 只控制是否主动解析 <think></think>,如果接口已经解析了,仍然会返回 think 内容。
|
||||||
|
*/
|
||||||
|
const parsePart = (
|
||||||
|
part: {
|
||||||
|
choices: {
|
||||||
|
delta: {
|
||||||
|
content?: string;
|
||||||
|
reasoning_content?: string;
|
||||||
|
};
|
||||||
|
}[];
|
||||||
|
},
|
||||||
|
parseReasoning = false
|
||||||
|
): [string, string] => {
|
||||||
|
const content = part.choices?.[0]?.delta?.content || '';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || '';
|
||||||
|
if (reasoningContent || !parseReasoning) {
|
||||||
|
isInThinkTag = false;
|
||||||
|
return [reasoningContent, content];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
return ['', ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不在 think 标签中,或者有 reasoningContent(接口已解析),则返回 reasoningContent 和 content
|
||||||
|
if (isInThinkTag === false) {
|
||||||
|
return ['', content];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测是否为 think 标签开头的数据
|
||||||
|
if (isInThinkTag === undefined) {
|
||||||
|
// Parse content think and answer
|
||||||
|
startTagBuffer += content;
|
||||||
|
// 太少内容时候,暂时不解析
|
||||||
|
if (startTagBuffer.length < startTag.length) {
|
||||||
|
return ['', ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startTagBuffer.startsWith(startTag)) {
|
||||||
|
isInThinkTag = true;
|
||||||
|
return [startTagBuffer.slice(startTag.length), ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果未命中 think 标签,则认为不在 think 标签中,返回 buffer 内容作为 content
|
||||||
|
isInThinkTag = false;
|
||||||
|
return ['', startTagBuffer];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认是 think 标签内容,开始返回 think 内容,并实时检测 </think>
|
||||||
|
/*
|
||||||
|
检测 </think> 方案。
|
||||||
|
存储所有疑似 </think> 的内容,直到检测到完整的 </think> 标签或超出 </think> 长度。
|
||||||
|
content 返回值包含以下几种情况:
|
||||||
|
abc - 完全未命中尾标签
|
||||||
|
abc<th - 命中一部分尾标签
|
||||||
|
abc</think> - 完全命中尾标签
|
||||||
|
abc</think>abc - 完全命中尾标签
|
||||||
|
</think>abc - 完全命中尾标签
|
||||||
|
k>abc - 命中一部分尾标签
|
||||||
|
*/
|
||||||
|
// endTagBuffer 专门用来记录疑似尾标签的内容
|
||||||
|
if (endTagBuffer) {
|
||||||
|
endTagBuffer += content;
|
||||||
|
if (endTagBuffer.includes(endTag)) {
|
||||||
|
isInThinkTag = false;
|
||||||
|
const answer = endTagBuffer.slice(endTag.length);
|
||||||
|
return ['', answer];
|
||||||
|
} else if (endTagBuffer.length >= endTag.length) {
|
||||||
|
// 缓存内容超出尾标签长度,且仍未命中 </think>,则认为本次猜测 </think> 失败,仍处于 think 阶段。
|
||||||
|
const tmp = endTagBuffer;
|
||||||
|
endTagBuffer = '';
|
||||||
|
return [tmp, ''];
|
||||||
|
}
|
||||||
|
return ['', ''];
|
||||||
|
} else if (content.includes(endTag)) {
|
||||||
|
// 返回内容,完整命中</think>,直接结束
|
||||||
|
isInThinkTag = false;
|
||||||
|
const [think, answer] = content.split(endTag);
|
||||||
|
return [think, answer];
|
||||||
|
} else {
|
||||||
|
// 无 buffer,且未命中 </think>,开始疑似 </think> 检测。
|
||||||
|
for (let i = 1; i < endTag.length; i++) {
|
||||||
|
const partialEndTag = endTag.slice(0, i);
|
||||||
|
// 命中一部分尾标签
|
||||||
|
if (content.endsWith(partialEndTag)) {
|
||||||
|
const think = content.slice(0, -partialEndTag.length);
|
||||||
|
endTagBuffer += partialEndTag;
|
||||||
|
return [think, ''];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 完全未命中尾标签,还是 think 阶段。
|
||||||
|
return [content, ''];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStartTagBuffer = () => startTagBuffer;
|
||||||
|
|
||||||
|
return {
|
||||||
|
parsePart,
|
||||||
|
getStartTagBuffer
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
10
packages/global/support/user/api.d.ts
vendored
@@ -1,5 +1,9 @@
|
|||||||
|
import { MemberGroupSchemaType, MemberGroupType } from 'support/permission/memberGroup/type';
|
||||||
import { OAuthEnum } from './constant';
|
import { OAuthEnum } from './constant';
|
||||||
import { TrackRegisterParams } from './login/api';
|
import { TrackRegisterParams } from './login/api';
|
||||||
|
import { TeamMemberStatusEnum } from './team/constant';
|
||||||
|
import { OrgType } from './team/org/type';
|
||||||
|
import { TeamMemberItemType } from './team/type';
|
||||||
|
|
||||||
export type PostLoginProps = {
|
export type PostLoginProps = {
|
||||||
username: string;
|
username: string;
|
||||||
@@ -21,3 +25,9 @@ export type FastLoginProps = {
|
|||||||
token: string;
|
token: string;
|
||||||
code: string;
|
code: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SearchResult = {
|
||||||
|
members: Omit<TeamMemberItemType, 'teamId' | 'permission'>[];
|
||||||
|
orgs: Omit<OrgType, 'permission' | 'members'>[];
|
||||||
|
groups: MemberGroupSchemaType[];
|
||||||
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export type CreateTeamProps = {
|
|||||||
defaultTeam?: boolean;
|
defaultTeam?: boolean;
|
||||||
memberName?: string;
|
memberName?: string;
|
||||||
memberAvatar?: string;
|
memberAvatar?: string;
|
||||||
|
notificationAccount?: string;
|
||||||
};
|
};
|
||||||
export type UpdateTeamProps = Omit<ThirdPartyAccountType, 'externalWorkflowVariable'> & {
|
export type UpdateTeamProps = Omit<ThirdPartyAccountType, 'externalWorkflowVariable'> & {
|
||||||
name?: string;
|
name?: string;
|
||||||
@@ -39,6 +40,12 @@ export type UpdateInviteProps = {
|
|||||||
tmbId: string;
|
tmbId: string;
|
||||||
status: TeamMemberSchema['status'];
|
status: TeamMemberSchema['status'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UpdateStatusProps = {
|
||||||
|
tmbId: string;
|
||||||
|
status: TeamMemberSchema['status'];
|
||||||
|
};
|
||||||
|
|
||||||
export type InviteMemberResponse = Record<
|
export type InviteMemberResponse = Record<
|
||||||
'invite' | 'inValid' | 'inTeam',
|
'invite' | 'inValid' | 'inTeam',
|
||||||
{ username: string; userId: string }[]
|
{ username: string; userId: string }[]
|
||||||
|
|||||||
5
packages/global/support/user/team/type.d.ts
vendored
@@ -34,6 +34,7 @@ export type TeamTagSchema = TeamTagItemType & {
|
|||||||
_id: string;
|
_id: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
createTime: Date;
|
createTime: Date;
|
||||||
|
updateTime?: Date;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TeamMemberSchema = {
|
export type TeamMemberSchema = {
|
||||||
@@ -41,6 +42,7 @@ export type TeamMemberSchema = {
|
|||||||
teamId: string;
|
teamId: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
createTime: Date;
|
createTime: Date;
|
||||||
|
updateTime?: Date;
|
||||||
name: string;
|
name: string;
|
||||||
role: `${TeamMemberRoleEnum}`;
|
role: `${TeamMemberRoleEnum}`;
|
||||||
status: `${TeamMemberStatusEnum}`;
|
status: `${TeamMemberStatusEnum}`;
|
||||||
@@ -79,6 +81,9 @@ export type TeamMemberItemType = {
|
|||||||
role: `${TeamMemberRoleEnum}`;
|
role: `${TeamMemberRoleEnum}`;
|
||||||
status: `${TeamMemberStatusEnum}`;
|
status: `${TeamMemberStatusEnum}`;
|
||||||
permission: TeamPermission;
|
permission: TeamPermission;
|
||||||
|
contact?: string;
|
||||||
|
createTime: Date;
|
||||||
|
updateTime?: Date;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TeamTagItemType = {
|
export type TeamTagItemType = {
|
||||||
|
|||||||
3
packages/global/support/user/type.d.ts
vendored
@@ -17,6 +17,7 @@ export type UserModelSchema = {
|
|||||||
fastgpt_sem?: {
|
fastgpt_sem?: {
|
||||||
keyword: string;
|
keyword: string;
|
||||||
};
|
};
|
||||||
|
contact?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserType = {
|
export type UserType = {
|
||||||
@@ -26,9 +27,9 @@ export type UserType = {
|
|||||||
timezone: string;
|
timezone: string;
|
||||||
promotionRate: UserModelSchema['promotionRate'];
|
promotionRate: UserModelSchema['promotionRate'];
|
||||||
team: TeamTmbItemType;
|
team: TeamTmbItemType;
|
||||||
standardInfo?: standardInfoType;
|
|
||||||
notificationAccount?: string;
|
notificationAccount?: string;
|
||||||
permission: TeamPermission;
|
permission: TeamPermission;
|
||||||
|
contact?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SourceMemberType = {
|
export type SourceMemberType = {
|
||||||
|
|||||||
4
packages/service/common/file/csv.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const generateCsv = (headers: string[], data: string[][]) => {
|
||||||
|
const csv = [headers.join(','), ...data.map((row) => row.join(','))].join('\n');
|
||||||
|
return csv;
|
||||||
|
};
|
||||||
@@ -18,10 +18,10 @@ export function getGFSCollection(bucket: `${BucketNameEnum}`) {
|
|||||||
MongoDatasetFileSchema;
|
MongoDatasetFileSchema;
|
||||||
MongoChatFileSchema;
|
MongoChatFileSchema;
|
||||||
|
|
||||||
return connectionMongo.connection.db.collection(`${bucket}.files`);
|
return connectionMongo.connection.db!.collection(`${bucket}.files`);
|
||||||
}
|
}
|
||||||
export function getGridBucket(bucket: `${BucketNameEnum}`) {
|
export function getGridBucket(bucket: `${BucketNameEnum}`) {
|
||||||
return new connectionMongo.mongo.GridFSBucket(connectionMongo.connection.db, {
|
return new connectionMongo.mongo.GridFSBucket(connectionMongo.connection.db!, {
|
||||||
bucketName: bucket,
|
bucketName: bucket,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
readPreference: ReadPreference.SECONDARY_PREFERRED // Read from secondary node
|
readPreference: ReadPreference.SECONDARY_PREFERRED // Read from secondary node
|
||||||
|
|||||||
@@ -26,15 +26,18 @@ export async function uploadMongoImg({
|
|||||||
const [base64Mime, base64Data] = base64Img.split(',');
|
const [base64Mime, base64Data] = base64Img.split(',');
|
||||||
// Check if mime type is valid
|
// Check if mime type is valid
|
||||||
if (!base64MimeRegex.test(base64Mime)) {
|
if (!base64MimeRegex.test(base64Mime)) {
|
||||||
return Promise.reject('Invalid image mime type');
|
return Promise.reject('Invalid image base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'image/jpeg'}`;
|
const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'image/jpeg'}`;
|
||||||
const binary = Buffer.from(base64Data, 'base64');
|
const binary = Buffer.from(base64Data, 'base64');
|
||||||
const extension = mime.split('/')[1];
|
let extension = mime.split('/')[1];
|
||||||
|
if (extension.startsWith('x-')) {
|
||||||
|
extension = extension.substring(2); // Remove 'x-' prefix
|
||||||
|
}
|
||||||
|
|
||||||
if (!imageFileType.includes(`.${extension}`)) {
|
if (!extension || !imageFileType.includes(`.${extension}`)) {
|
||||||
return Promise.reject('Invalid image file type');
|
return Promise.reject(`Invalid image file type: ${mime}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { _id } = await MongoImage.create({
|
const { _id } = await MongoImage.create({
|
||||||
|
|||||||
@@ -111,15 +111,21 @@ export const readRawContentByFileBuffer = async ({
|
|||||||
// markdown data format
|
// markdown data format
|
||||||
if (imageList) {
|
if (imageList) {
|
||||||
await batchRun(imageList, async (item) => {
|
await batchRun(imageList, async (item) => {
|
||||||
const src = await uploadMongoImg({
|
const src = await (async () => {
|
||||||
base64Img: `data:${item.mime};base64,${item.base64}`,
|
try {
|
||||||
teamId,
|
return await uploadMongoImg({
|
||||||
// expiredTime: addHours(new Date(), 1),
|
base64Img: `data:${item.mime};base64,${item.base64}`,
|
||||||
metadata: {
|
teamId,
|
||||||
...metadata,
|
// expiredTime: addHours(new Date(), 1),
|
||||||
mime: item.mime
|
metadata: {
|
||||||
|
...metadata,
|
||||||
|
mime: item.mime
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
});
|
})();
|
||||||
rawText = rawText.replace(item.uuid, src);
|
rawText = rawText.replace(item.uuid, src);
|
||||||
if (formatText) {
|
if (formatText) {
|
||||||
formatText = formatText.replace(item.uuid, src);
|
formatText = formatText.replace(item.uuid, src);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export const countGptMessagesTokens = async (
|
|||||||
number
|
number
|
||||||
>({
|
>({
|
||||||
name: WorkerNameEnum.countGptMessagesTokens,
|
name: WorkerNameEnum.countGptMessagesTokens,
|
||||||
maxReservedThreads: global.systemEnv?.tokenWorkers || 50
|
maxReservedThreads: global.systemEnv?.tokenWorkers || 30
|
||||||
});
|
});
|
||||||
|
|
||||||
const total = await workerController.run({ messages, tools, functionCall });
|
const total = await workerController.run({ messages, tools, functionCall });
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
export const FastGPTProUrl = process.env.PRO_URL ? `${process.env.PRO_URL}/api` : '';
|
export const FastGPTProUrl = process.env.PRO_URL ? `${process.env.PRO_URL}/api` : '';
|
||||||
|
export const isFastGPTMainService = !!process.env.PRO_URL;
|
||||||
|
// @ts-ignore
|
||||||
|
export const isFastGPTProService = () => !!global.systemConfig;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export const recallFromVectorStore = Vector.embRecall;
|
|||||||
export const getVectorDataByTime = Vector.getVectorDataByTime;
|
export const getVectorDataByTime = Vector.getVectorDataByTime;
|
||||||
export const getVectorCountByTeamId = Vector.getVectorCountByTeamId;
|
export const getVectorCountByTeamId = Vector.getVectorCountByTeamId;
|
||||||
export const getVectorCountByDatasetId = Vector.getVectorCountByDatasetId;
|
export const getVectorCountByDatasetId = Vector.getVectorCountByDatasetId;
|
||||||
|
export const getVectorCountByCollectionId = Vector.getVectorCountByCollectionId;
|
||||||
|
|
||||||
export const insertDatasetDataVector = async ({
|
export const insertDatasetDataVector = async ({
|
||||||
model,
|
model,
|
||||||
|
|||||||
@@ -321,6 +321,23 @@ export class MilvusCtrl {
|
|||||||
|
|
||||||
return total;
|
return total;
|
||||||
};
|
};
|
||||||
|
getVectorCountByCollectionId = async (
|
||||||
|
teamId: string,
|
||||||
|
datasetId: string,
|
||||||
|
collectionId: string
|
||||||
|
) => {
|
||||||
|
const client = await this.getClient();
|
||||||
|
|
||||||
|
const result = await client.query({
|
||||||
|
collection_name: DatasetVectorTableName,
|
||||||
|
output_fields: ['count(*)'],
|
||||||
|
filter: `(teamId == "${String(teamId)}") and (datasetId == "${String(datasetId)}") and (collectionId == "${String(collectionId)}")`
|
||||||
|
});
|
||||||
|
|
||||||
|
const total = result.data?.[0]?.['count(*)'] as number;
|
||||||
|
|
||||||
|
return total;
|
||||||
|
};
|
||||||
|
|
||||||
getVectorDataByTime = async (start: Date, end: Date) => {
|
getVectorDataByTime = async (start: Date, end: Date) => {
|
||||||
const client = await this.getClient();
|
const client = await this.getClient();
|
||||||
|
|||||||
@@ -240,6 +240,23 @@ export class PgVectorCtrl {
|
|||||||
where: [['team_id', String(teamId)], 'and', ['dataset_id', String(datasetId)]]
|
where: [['team_id', String(teamId)], 'and', ['dataset_id', String(datasetId)]]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return total;
|
||||||
|
};
|
||||||
|
getVectorCountByCollectionId = async (
|
||||||
|
teamId: string,
|
||||||
|
datasetId: string,
|
||||||
|
collectionId: string
|
||||||
|
) => {
|
||||||
|
const total = await PgClient.count(DatasetVectorTableName, {
|
||||||
|
where: [
|
||||||
|
['team_id', String(teamId)],
|
||||||
|
'and',
|
||||||
|
['dataset_id', String(datasetId)],
|
||||||
|
'and',
|
||||||
|
['collection_id', String(collectionId)]
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
return total;
|
return total;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import OpenAI from '@fastgpt/global/core/ai';
|
import OpenAI from '@fastgpt/global/core/ai';
|
||||||
import {
|
import {
|
||||||
ChatCompletionCreateParamsNonStreaming,
|
ChatCompletionCreateParamsNonStreaming,
|
||||||
ChatCompletionCreateParamsStreaming
|
ChatCompletionCreateParamsStreaming,
|
||||||
|
StreamChatType,
|
||||||
|
UnStreamChatType
|
||||||
} from '@fastgpt/global/core/ai/type';
|
} from '@fastgpt/global/core/ai/type';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
import { addLog } from '../../common/system/log';
|
import { addLog } from '../../common/system/log';
|
||||||
@@ -9,14 +11,17 @@ import { i18nT } from '../../../web/i18n/utils';
|
|||||||
import { OpenaiAccountType } from '@fastgpt/global/support/user/team/type';
|
import { OpenaiAccountType } from '@fastgpt/global/support/user/team/type';
|
||||||
import { getLLMModel } from './model';
|
import { getLLMModel } from './model';
|
||||||
|
|
||||||
export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
|
const aiProxyBaseUrl = process.env.AIPROXY_API_ENDPOINT
|
||||||
|
? `${process.env.AIPROXY_API_ENDPOINT}/v1`
|
||||||
|
: undefined;
|
||||||
|
const openaiBaseUrl = aiProxyBaseUrl || process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
|
||||||
|
const openaiBaseKey = process.env.AIPROXY_API_TOKEN || process.env.CHAT_API_KEY || '';
|
||||||
|
|
||||||
export const getAIApi = (props?: { userKey?: OpenaiAccountType; timeout?: number }) => {
|
export const getAIApi = (props?: { userKey?: OpenaiAccountType; timeout?: number }) => {
|
||||||
const { userKey, timeout } = props || {};
|
const { userKey, timeout } = props || {};
|
||||||
|
|
||||||
const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
|
const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
|
||||||
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || process.env.CHAT_API_KEY || '';
|
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || openaiBaseKey;
|
||||||
|
|
||||||
return new OpenAI({
|
return new OpenAI({
|
||||||
baseURL: baseUrl,
|
baseURL: baseUrl,
|
||||||
apiKey,
|
apiKey,
|
||||||
@@ -38,29 +43,30 @@ export const getAxiosConfig = (props?: { userKey?: OpenaiAccountType }) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type CompletionsBodyType =
|
export const createChatCompletion = async ({
|
||||||
| ChatCompletionCreateParamsNonStreaming
|
|
||||||
| ChatCompletionCreateParamsStreaming;
|
|
||||||
type InferResponseType<T extends CompletionsBodyType> =
|
|
||||||
T extends ChatCompletionCreateParamsStreaming
|
|
||||||
? OpenAI.Chat.Completions.ChatCompletionChunk
|
|
||||||
: OpenAI.Chat.Completions.ChatCompletion;
|
|
||||||
|
|
||||||
export const createChatCompletion = async <T extends CompletionsBodyType>({
|
|
||||||
body,
|
body,
|
||||||
userKey,
|
userKey,
|
||||||
timeout,
|
timeout,
|
||||||
options
|
options
|
||||||
}: {
|
}: {
|
||||||
body: T;
|
body: ChatCompletionCreateParamsNonStreaming | ChatCompletionCreateParamsStreaming;
|
||||||
userKey?: OpenaiAccountType;
|
userKey?: OpenaiAccountType;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
options?: OpenAI.RequestOptions;
|
options?: OpenAI.RequestOptions;
|
||||||
}): Promise<{
|
}): Promise<
|
||||||
response: InferResponseType<T>;
|
{
|
||||||
isStreamResponse: boolean;
|
getEmptyResponseTip: () => string;
|
||||||
getEmptyResponseTip: () => string;
|
} & (
|
||||||
}> => {
|
| {
|
||||||
|
response: StreamChatType;
|
||||||
|
isStreamResponse: true;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
response: UnStreamChatType;
|
||||||
|
isStreamResponse: false;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
> => {
|
||||||
try {
|
try {
|
||||||
const modelConstantsData = getLLMModel(body.model);
|
const modelConstantsData = getLLMModel(body.model);
|
||||||
|
|
||||||
@@ -69,6 +75,7 @@ export const createChatCompletion = async <T extends CompletionsBodyType>({
|
|||||||
userKey,
|
userKey,
|
||||||
timeout: formatTimeout
|
timeout: formatTimeout
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await ai.chat.completions.create(body, {
|
const response = await ai.chat.completions.create(body, {
|
||||||
...options,
|
...options,
|
||||||
...(modelConstantsData.requestUrl ? { path: modelConstantsData.requestUrl } : {}),
|
...(modelConstantsData.requestUrl ? { path: modelConstantsData.requestUrl } : {}),
|
||||||
@@ -96,9 +103,17 @@ export const createChatCompletion = async <T extends CompletionsBodyType>({
|
|||||||
return i18nT('chat:LLM_model_response_empty');
|
return i18nT('chat:LLM_model_response_empty');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isStreamResponse) {
|
||||||
|
return {
|
||||||
|
response,
|
||||||
|
isStreamResponse: true,
|
||||||
|
getEmptyResponseTip
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: response as InferResponseType<T>,
|
response,
|
||||||
isStreamResponse,
|
isStreamResponse: false,
|
||||||
getEmptyResponseTip
|
getEmptyResponseTip
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,6 +1,30 @@
|
|||||||
{
|
{
|
||||||
"provider": "Claude",
|
"provider": "Claude",
|
||||||
"list": [
|
"list": [
|
||||||
|
{
|
||||||
|
"model": "claude-3-7-sonnet-20250219",
|
||||||
|
"name": "claude-3-7-sonnet-20250219",
|
||||||
|
"maxContext": 200000,
|
||||||
|
"maxResponse": 8000,
|
||||||
|
"quoteMaxToken": 100000,
|
||||||
|
"maxTemperature": 1,
|
||||||
|
"showTopP": true,
|
||||||
|
"showStopSign": true,
|
||||||
|
"vision": true,
|
||||||
|
"toolChoice": true,
|
||||||
|
"functionCall": false,
|
||||||
|
"defaultSystemChatPrompt": "",
|
||||||
|
"datasetProcess": true,
|
||||||
|
"usedInClassify": true,
|
||||||
|
"customCQPrompt": "",
|
||||||
|
"usedInExtractFields": true,
|
||||||
|
"usedInQueryExtension": true,
|
||||||
|
"customExtractPrompt": "",
|
||||||
|
"usedInToolCall": true,
|
||||||
|
"defaultConfig": {},
|
||||||
|
"fieldMap": {},
|
||||||
|
"type": "llm"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"model": "claude-3-5-haiku-20241022",
|
"model": "claude-3-5-haiku-20241022",
|
||||||
"name": "claude-3-5-haiku-20241022",
|
"name": "claude-3-5-haiku-20241022",
|
||||||
@@ -10,7 +34,7 @@
|
|||||||
"maxTemperature": 1,
|
"maxTemperature": 1,
|
||||||
"showTopP": true,
|
"showTopP": true,
|
||||||
"showStopSign": true,
|
"showStopSign": true,
|
||||||
"vision": false,
|
"vision": true,
|
||||||
"toolChoice": true,
|
"toolChoice": true,
|
||||||
"functionCall": false,
|
"functionCall": false,
|
||||||
"defaultSystemChatPrompt": "",
|
"defaultSystemChatPrompt": "",
|
||||||
@@ -98,4 +122,4 @@
|
|||||||
"type": "llm"
|
"type": "llm"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,54 @@
|
|||||||
{
|
{
|
||||||
"provider": "Gemini",
|
"provider": "Gemini",
|
||||||
"list": [
|
"list": [
|
||||||
|
{
|
||||||
|
"model": "gemini-2.0-flash",
|
||||||
|
"name": "gemini-2.0-flash",
|
||||||
|
"maxContext": 1000000,
|
||||||
|
"maxResponse": 8000,
|
||||||
|
"quoteMaxToken": 60000,
|
||||||
|
"maxTemperature": 1,
|
||||||
|
"vision": true,
|
||||||
|
"toolChoice": true,
|
||||||
|
"functionCall": false,
|
||||||
|
"defaultSystemChatPrompt": "",
|
||||||
|
"datasetProcess": true,
|
||||||
|
"usedInClassify": true,
|
||||||
|
"customCQPrompt": "",
|
||||||
|
"usedInExtractFields": true,
|
||||||
|
"usedInQueryExtension": true,
|
||||||
|
"customExtractPrompt": "",
|
||||||
|
"usedInToolCall": true,
|
||||||
|
"defaultConfig": {},
|
||||||
|
"fieldMap": {},
|
||||||
|
"type": "llm",
|
||||||
|
"showTopP": true,
|
||||||
|
"showStopSign": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "gemini-2.0-pro-exp",
|
||||||
|
"name": "gemini-2.0-pro-exp",
|
||||||
|
"maxContext": 2000000,
|
||||||
|
"maxResponse": 8000,
|
||||||
|
"quoteMaxToken": 100000,
|
||||||
|
"maxTemperature": 1,
|
||||||
|
"vision": true,
|
||||||
|
"toolChoice": true,
|
||||||
|
"functionCall": false,
|
||||||
|
"defaultSystemChatPrompt": "",
|
||||||
|
"datasetProcess": true,
|
||||||
|
"usedInClassify": true,
|
||||||
|
"customCQPrompt": "",
|
||||||
|
"usedInExtractFields": true,
|
||||||
|
"usedInQueryExtension": true,
|
||||||
|
"customExtractPrompt": "",
|
||||||
|
"usedInToolCall": true,
|
||||||
|
"defaultConfig": {},
|
||||||
|
"fieldMap": {},
|
||||||
|
"type": "llm",
|
||||||
|
"showTopP": true,
|
||||||
|
"showStopSign": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"model": "gemini-1.5-flash",
|
"model": "gemini-1.5-flash",
|
||||||
"name": "gemini-1.5-flash",
|
"name": "gemini-1.5-flash",
|
||||||
@@ -153,4 +201,4 @@
|
|||||||
"type": "embedding"
|
"type": "embedding"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,29 @@
|
|||||||
{
|
{
|
||||||
"provider": "Grok",
|
"provider": "Grok",
|
||||||
"list": []
|
"list": [
|
||||||
}
|
{
|
||||||
|
"model": "grok-3",
|
||||||
|
"name": "grok-3",
|
||||||
|
"maxContext": 128000,
|
||||||
|
"maxResponse": 8000,
|
||||||
|
"quoteMaxToken": 128000,
|
||||||
|
"maxTemperature": 1,
|
||||||
|
"showTopP": true,
|
||||||
|
"showStopSign": true,
|
||||||
|
"vision": false,
|
||||||
|
"toolChoice": false,
|
||||||
|
"functionCall": false,
|
||||||
|
"defaultSystemChatPrompt": "",
|
||||||
|
"datasetProcess": true,
|
||||||
|
"usedInClassify": true,
|
||||||
|
"customCQPrompt": "",
|
||||||
|
"usedInExtractFields": true,
|
||||||
|
"usedInQueryExtension": true,
|
||||||
|
"customExtractPrompt": "",
|
||||||
|
"usedInToolCall": true,
|
||||||
|
"defaultConfig": {},
|
||||||
|
"fieldMap": {},
|
||||||
|
"type": "llm"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
4
packages/service/core/ai/config/provider/PPIO.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"provider": "PPIO",
|
||||||
|
"list": []
|
||||||
|
}
|
||||||
@@ -52,6 +52,12 @@ export const loadSystemModels = async (init = false) => {
|
|||||||
if (model.isDefault) {
|
if (model.isDefault) {
|
||||||
global.systemDefaultModel.llm = model;
|
global.systemDefaultModel.llm = model;
|
||||||
}
|
}
|
||||||
|
if (model.isDefaultDatasetTextModel) {
|
||||||
|
global.systemDefaultModel.datasetTextLLM = model;
|
||||||
|
}
|
||||||
|
if (model.isDefaultDatasetImageModel) {
|
||||||
|
global.systemDefaultModel.datasetImageLLM = model;
|
||||||
|
}
|
||||||
} else if (model.type === ModelTypeEnum.embedding) {
|
} else if (model.type === ModelTypeEnum.embedding) {
|
||||||
global.embeddingModelMap.set(model.model, model);
|
global.embeddingModelMap.set(model.model, model);
|
||||||
global.embeddingModelMap.set(model.name, model);
|
global.embeddingModelMap.set(model.name, model);
|
||||||
@@ -134,6 +140,16 @@ export const loadSystemModels = async (init = false) => {
|
|||||||
if (!global.systemDefaultModel.llm) {
|
if (!global.systemDefaultModel.llm) {
|
||||||
global.systemDefaultModel.llm = Array.from(global.llmModelMap.values())[0];
|
global.systemDefaultModel.llm = Array.from(global.llmModelMap.values())[0];
|
||||||
}
|
}
|
||||||
|
if (!global.systemDefaultModel.datasetTextLLM) {
|
||||||
|
global.systemDefaultModel.datasetTextLLM = Array.from(global.llmModelMap.values()).find(
|
||||||
|
(item) => item.datasetProcess
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!global.systemDefaultModel.datasetImageLLM) {
|
||||||
|
global.systemDefaultModel.datasetImageLLM = Array.from(global.llmModelMap.values()).find(
|
||||||
|
(item) => item.vision
|
||||||
|
);
|
||||||
|
}
|
||||||
if (!global.systemDefaultModel.embedding) {
|
if (!global.systemDefaultModel.embedding) {
|
||||||
global.systemDefaultModel.embedding = Array.from(global.embeddingModelMap.values())[0];
|
global.systemDefaultModel.embedding = Array.from(global.embeddingModelMap.values())[0];
|
||||||
}
|
}
|
||||||
|
|||||||
3
packages/service/core/ai/type.d.ts
vendored
@@ -22,6 +22,9 @@ export type SystemModelItemType =
|
|||||||
|
|
||||||
export type SystemDefaultModelType = {
|
export type SystemDefaultModelType = {
|
||||||
[ModelTypeEnum.llm]?: LLMModelItemType;
|
[ModelTypeEnum.llm]?: LLMModelItemType;
|
||||||
|
datasetTextLLM?: LLMModelItemType;
|
||||||
|
datasetImageLLM?: LLMModelItemType;
|
||||||
|
|
||||||
[ModelTypeEnum.embedding]?: EmbeddingModelItemType;
|
[ModelTypeEnum.embedding]?: EmbeddingModelItemType;
|
||||||
[ModelTypeEnum.tts]?: TTSModelType;
|
[ModelTypeEnum.tts]?: TTSModelType;
|
||||||
[ModelTypeEnum.stt]?: STTModelType;
|
[ModelTypeEnum.stt]?: STTModelType;
|
||||||
|
|||||||
@@ -37,25 +37,26 @@ export const computedTemperature = ({
|
|||||||
return temperature;
|
return temperature;
|
||||||
};
|
};
|
||||||
|
|
||||||
type CompletionsBodyType = (
|
type CompletionsBodyType =
|
||||||
| ChatCompletionCreateParamsNonStreaming
|
| ChatCompletionCreateParamsNonStreaming
|
||||||
| ChatCompletionCreateParamsStreaming
|
| ChatCompletionCreateParamsStreaming;
|
||||||
) & {
|
|
||||||
response_format?: any;
|
|
||||||
json_schema?: string;
|
|
||||||
stop?: string;
|
|
||||||
};
|
|
||||||
type InferCompletionsBody<T> = T extends { stream: true }
|
type InferCompletionsBody<T> = T extends { stream: true }
|
||||||
? ChatCompletionCreateParamsStreaming
|
? ChatCompletionCreateParamsStreaming
|
||||||
: ChatCompletionCreateParamsNonStreaming;
|
: T extends { stream: false }
|
||||||
|
? ChatCompletionCreateParamsNonStreaming
|
||||||
|
: ChatCompletionCreateParamsNonStreaming | ChatCompletionCreateParamsStreaming;
|
||||||
|
|
||||||
export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
|
export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
|
||||||
body: T,
|
body: T & {
|
||||||
|
response_format?: any;
|
||||||
|
json_schema?: string;
|
||||||
|
stop?: string;
|
||||||
|
},
|
||||||
model: string | LLMModelItemType
|
model: string | LLMModelItemType
|
||||||
): InferCompletionsBody<T> => {
|
): InferCompletionsBody<T> => {
|
||||||
const modelData = typeof model === 'string' ? getLLMModel(model) : model;
|
const modelData = typeof model === 'string' ? getLLMModel(model) : model;
|
||||||
if (!modelData) {
|
if (!modelData) {
|
||||||
return body as InferCompletionsBody<T>;
|
return body as unknown as InferCompletionsBody<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response_format = body.response_format;
|
const response_format = body.response_format;
|
||||||
@@ -91,9 +92,7 @@ export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(requestBody);
|
return requestBody as unknown as InferCompletionsBody<T>;
|
||||||
|
|
||||||
return requestBody as InferCompletionsBody<T>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const llmStreamResponseToText = async (response: StreamChatType) => {
|
export const llmStreamResponseToText = async (response: StreamChatType) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { connectionMongo, getMongoModel } from '../../common/mongo';
|
import { connectionMongo, getMongoModel } from '../../common/mongo';
|
||||||
const { Schema } = connectionMongo;
|
const { Schema } = connectionMongo;
|
||||||
import { ChatSchema as ChatType } from '@fastgpt/global/core/chat/type.d';
|
import { ChatSchema as ChatType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { ChatSourceMap } from '@fastgpt/global/core/chat/constants';
|
import { ChatSourceEnum, ChatSourceMap } from '@fastgpt/global/core/chat/constants';
|
||||||
import {
|
import {
|
||||||
TeamCollectionName,
|
TeamCollectionName,
|
||||||
TeamMemberCollectionName
|
TeamMemberCollectionName
|
||||||
@@ -52,8 +52,10 @@ const ChatSchema = new Schema({
|
|||||||
},
|
},
|
||||||
source: {
|
source: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
|
enum: Object.values(ChatSourceEnum)
|
||||||
},
|
},
|
||||||
|
sourceName: String,
|
||||||
shareId: {
|
shareId: {
|
||||||
type: String
|
type: String
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type.d';
|
import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { MongoApp } from '../app/schema';
|
import { MongoApp } from '../app/schema';
|
||||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
import {
|
||||||
|
ChatItemValueTypeEnum,
|
||||||
|
ChatRoleEnum,
|
||||||
|
ChatSourceEnum
|
||||||
|
} from '@fastgpt/global/core/chat/constants';
|
||||||
import { MongoChatItem } from './chatItemSchema';
|
import { MongoChatItem } from './chatItemSchema';
|
||||||
import { MongoChat } from './chatSchema';
|
import { MongoChat } from './chatSchema';
|
||||||
import { addLog } from '../../common/system/log';
|
import { addLog } from '../../common/system/log';
|
||||||
@@ -22,7 +26,8 @@ type Props = {
|
|||||||
variables?: Record<string, any>;
|
variables?: Record<string, any>;
|
||||||
isUpdateUseTime: boolean;
|
isUpdateUseTime: boolean;
|
||||||
newTitle: string;
|
newTitle: string;
|
||||||
source: string;
|
source: `${ChatSourceEnum}`;
|
||||||
|
sourceName?: string;
|
||||||
shareId?: string;
|
shareId?: string;
|
||||||
outLinkUid?: string;
|
outLinkUid?: string;
|
||||||
content: [UserChatItemType & { dataId?: string }, AIChatItemType & { dataId?: string }];
|
content: [UserChatItemType & { dataId?: string }, AIChatItemType & { dataId?: string }];
|
||||||
@@ -40,6 +45,7 @@ export async function saveChat({
|
|||||||
isUpdateUseTime,
|
isUpdateUseTime,
|
||||||
newTitle,
|
newTitle,
|
||||||
source,
|
source,
|
||||||
|
sourceName,
|
||||||
shareId,
|
shareId,
|
||||||
outLinkUid,
|
outLinkUid,
|
||||||
content,
|
content,
|
||||||
@@ -96,6 +102,7 @@ export async function saveChat({
|
|||||||
pluginInputs,
|
pluginInputs,
|
||||||
title: newTitle,
|
title: newTitle,
|
||||||
source,
|
source,
|
||||||
|
sourceName,
|
||||||
shareId,
|
shareId,
|
||||||
outLinkUid,
|
outLinkUid,
|
||||||
metadata: metadataUpdate,
|
metadata: metadataUpdate,
|
||||||
|
|||||||
@@ -197,7 +197,11 @@ export const loadRequestMessages = async ({
|
|||||||
addLog.info(`Filter invalid image: ${imgUrl}`);
|
addLog.info(`Filter invalid image: ${imgUrl}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
|
if (error?.response?.status === 405) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
addLog.warn(`Filter invalid image: ${imgUrl}`, { error });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,6 +200,7 @@ export async function searchDatasetData(
|
|||||||
forbidCollectionIdList: collections.map((item) => String(item._id))
|
forbidCollectionIdList: collections.map((item) => String(item._id))
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Collection metadata filter
|
Collection metadata filter
|
||||||
标签过滤:
|
标签过滤:
|
||||||
@@ -207,6 +208,63 @@ export async function searchDatasetData(
|
|||||||
2. and 标签和 null 不能共存,否则返回空数组
|
2. and 标签和 null 不能共存,否则返回空数组
|
||||||
*/
|
*/
|
||||||
const filterCollectionByMetadata = async (): Promise<string[] | undefined> => {
|
const filterCollectionByMetadata = async (): Promise<string[] | undefined> => {
|
||||||
|
const getAllCollectionIds = async ({
|
||||||
|
parentCollectionIds
|
||||||
|
}: {
|
||||||
|
parentCollectionIds?: string[];
|
||||||
|
}): Promise<string[] | undefined> => {
|
||||||
|
if (!parentCollectionIds) return;
|
||||||
|
if (parentCollectionIds.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const collections = await MongoDatasetCollection.find(
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
datasetId: { $in: datasetIds },
|
||||||
|
_id: { $in: parentCollectionIds }
|
||||||
|
},
|
||||||
|
'_id type',
|
||||||
|
{
|
||||||
|
...readFromSecondary
|
||||||
|
}
|
||||||
|
).lean();
|
||||||
|
|
||||||
|
const resultIds = new Set<string>();
|
||||||
|
collections.forEach((item) => {
|
||||||
|
if (item.type !== 'folder') {
|
||||||
|
resultIds.add(String(item._id));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const folderIds = collections
|
||||||
|
.filter((item) => item.type === 'folder')
|
||||||
|
.map((item) => String(item._id));
|
||||||
|
|
||||||
|
// Get all child collection ids
|
||||||
|
if (folderIds.length) {
|
||||||
|
const childCollections = await MongoDatasetCollection.find(
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
datasetId: { $in: datasetIds },
|
||||||
|
parentId: { $in: folderIds }
|
||||||
|
},
|
||||||
|
'_id type',
|
||||||
|
{
|
||||||
|
...readFromSecondary
|
||||||
|
}
|
||||||
|
).lean();
|
||||||
|
|
||||||
|
const childIds = await getAllCollectionIds({
|
||||||
|
parentCollectionIds: childCollections.map((item) => String(item._id))
|
||||||
|
});
|
||||||
|
|
||||||
|
childIds?.forEach((id) => resultIds.add(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(resultIds);
|
||||||
|
};
|
||||||
|
|
||||||
if (!collectionFilterMatch || !global.feConfigs.isPlus) return;
|
if (!collectionFilterMatch || !global.feConfigs.isPlus) return;
|
||||||
|
|
||||||
let tagCollectionIdList: string[] | undefined = undefined;
|
let tagCollectionIdList: string[] | undefined = undefined;
|
||||||
@@ -326,13 +384,19 @@ export async function searchDatasetData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Concat tag and time
|
// Concat tag and time
|
||||||
if (tagCollectionIdList && createTimeCollectionIdList) {
|
const collectionIds = (() => {
|
||||||
return tagCollectionIdList.filter((id) => createTimeCollectionIdList!.includes(id));
|
if (tagCollectionIdList && createTimeCollectionIdList) {
|
||||||
} else if (tagCollectionIdList) {
|
return tagCollectionIdList.filter((id) =>
|
||||||
return tagCollectionIdList;
|
(createTimeCollectionIdList as string[]).includes(id)
|
||||||
} else if (createTimeCollectionIdList) {
|
);
|
||||||
return createTimeCollectionIdList;
|
}
|
||||||
}
|
|
||||||
|
return tagCollectionIdList || createTimeCollectionIdList;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return await getAllCollectionIds({
|
||||||
|
parentCollectionIds: collectionIds
|
||||||
|
});
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
};
|
};
|
||||||
const embeddingRecall = async ({
|
const embeddingRecall = async ({
|
||||||
@@ -383,6 +447,7 @@ export async function searchDatasetData(
|
|||||||
).lean()
|
).lean()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const set = new Map<string, number>();
|
||||||
const formatResult = results
|
const formatResult = results
|
||||||
.map((item, index) => {
|
.map((item, index) => {
|
||||||
const collection = collections.find((col) => String(col._id) === String(item.collectionId));
|
const collection = collections.find((col) => String(col._id) === String(item.collectionId));
|
||||||
@@ -398,8 +463,6 @@ export async function searchDatasetData(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const score = item?.score || 0;
|
|
||||||
|
|
||||||
const result: SearchDataResponseItemType = {
|
const result: SearchDataResponseItemType = {
|
||||||
id: String(data._id),
|
id: String(data._id),
|
||||||
updateTime: data.updateTime,
|
updateTime: data.updateTime,
|
||||||
@@ -409,12 +472,24 @@ export async function searchDatasetData(
|
|||||||
datasetId: String(data.datasetId),
|
datasetId: String(data.datasetId),
|
||||||
collectionId: String(data.collectionId),
|
collectionId: String(data.collectionId),
|
||||||
...getCollectionSourceData(collection),
|
...getCollectionSourceData(collection),
|
||||||
score: [{ type: SearchScoreTypeEnum.embedding, value: score, index }]
|
score: [{ type: SearchScoreTypeEnum.embedding, value: item?.score || 0, index }]
|
||||||
};
|
};
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
})
|
})
|
||||||
.filter(Boolean) as SearchDataResponseItemType[];
|
.filter((item) => {
|
||||||
|
if (!item) return false;
|
||||||
|
if (set.has(item.id)) return false;
|
||||||
|
set.set(item.id, 1);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((item, index) => {
|
||||||
|
if (!item) return;
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
score: item.score.map((item) => ({ ...item, index }))
|
||||||
|
};
|
||||||
|
}) as SearchDataResponseItemType[];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
embeddingRecallResults: formatResult,
|
embeddingRecallResults: formatResult,
|
||||||
@@ -717,11 +792,12 @@ export const defaultSearchDatasetData = async ({
|
|||||||
? getLLMModel(datasetSearchExtensionModel)
|
? getLLMModel(datasetSearchExtensionModel)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const { concatQueries, rewriteQuery, aiExtensionResult } = await datasetSearchQueryExtension({
|
const { concatQueries, extensionQueries, rewriteQuery, aiExtensionResult } =
|
||||||
query,
|
await datasetSearchQueryExtension({
|
||||||
extensionModel,
|
query,
|
||||||
extensionBg: datasetSearchExtensionBg
|
extensionModel,
|
||||||
});
|
extensionBg: datasetSearchExtensionBg
|
||||||
|
});
|
||||||
|
|
||||||
const result = await searchDatasetData({
|
const result = await searchDatasetData({
|
||||||
...props,
|
...props,
|
||||||
@@ -736,7 +812,7 @@ export const defaultSearchDatasetData = async ({
|
|||||||
model: aiExtensionResult.model,
|
model: aiExtensionResult.model,
|
||||||
inputTokens: aiExtensionResult.inputTokens,
|
inputTokens: aiExtensionResult.inputTokens,
|
||||||
outputTokens: aiExtensionResult.outputTokens,
|
outputTokens: aiExtensionResult.outputTokens,
|
||||||
query: concatQueries.join('\n')
|
query: extensionQueries.join('\n')
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -72,12 +72,15 @@ Human: ${query}
|
|||||||
if (result.extensionQueries?.length === 0) return;
|
if (result.extensionQueries?.length === 0) return;
|
||||||
return result;
|
return result;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
const extensionQueries = filterSamQuery(aiExtensionResult?.extensionQueries || []);
|
||||||
if (aiExtensionResult) {
|
if (aiExtensionResult) {
|
||||||
queries = filterSamQuery(queries.concat(aiExtensionResult.extensionQueries));
|
queries = filterSamQuery(queries.concat(extensionQueries));
|
||||||
rewriteQuery = queries.join('\n');
|
rewriteQuery = queries.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
extensionQueries,
|
||||||
concatQueries: queries,
|
concatQueries: queries,
|
||||||
rewriteQuery,
|
rewriteQuery,
|
||||||
aiExtensionResult
|
aiExtensionResult
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ const getMultiInput = async ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
documentQuoteText: text,
|
documentQuoteText: text,
|
||||||
userFiles: fileLinks.map((url) => parseUrlToFileType(url))
|
userFiles: fileLinks.map((url) => parseUrlToFileType(url)).filter(Boolean)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { filterGPTMessageByMaxContext, loadRequestMessages } from '../../../chat
|
|||||||
import type { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type.d';
|
import type { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
import {
|
||||||
|
parseReasoningContent,
|
||||||
|
parseReasoningStreamContent,
|
||||||
|
textAdaptGptResponse
|
||||||
|
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { createChatCompletion } from '../../../ai/config';
|
import { createChatCompletion } from '../../../ai/config';
|
||||||
import type {
|
import type { ChatCompletionMessageParam, StreamChatType } from '@fastgpt/global/core/ai/type.d';
|
||||||
ChatCompletion,
|
|
||||||
ChatCompletionMessageParam,
|
|
||||||
StreamChatType
|
|
||||||
} from '@fastgpt/global/core/ai/type.d';
|
|
||||||
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
||||||
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||||
import { postTextCensor } from '../../../../common/api/requestPlusApi';
|
import { postTextCensor } from '../../../../common/api/requestPlusApi';
|
||||||
@@ -195,7 +195,13 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { answerText, reasoningText } = await (async () => {
|
const { answerText, reasoningText } = await (async () => {
|
||||||
if (res && isStreamResponse) {
|
if (isStreamResponse) {
|
||||||
|
if (!res) {
|
||||||
|
return {
|
||||||
|
answerText: '',
|
||||||
|
reasoningText: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
// sse response
|
// sse response
|
||||||
const { answer, reasoning } = await streamResponse({
|
const { answer, reasoning } = await streamResponse({
|
||||||
res,
|
res,
|
||||||
@@ -210,34 +216,49 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
reasoningText: reasoning
|
reasoningText: reasoning
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const unStreamResponse = response as ChatCompletion;
|
const { content, reasoningContent } = (() => {
|
||||||
const answer = unStreamResponse.choices?.[0]?.message?.content || '';
|
const content = response.choices?.[0]?.message?.content || '';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const reasoning = unStreamResponse.choices?.[0]?.message?.reasoning_content || '';
|
const reasoningContent: string = response.choices?.[0]?.message?.reasoning_content || '';
|
||||||
|
|
||||||
|
// API already parse reasoning content
|
||||||
|
if (reasoningContent || !aiChatReasoning) {
|
||||||
|
return {
|
||||||
|
content,
|
||||||
|
reasoningContent
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const [think, answer] = parseReasoningContent(content);
|
||||||
|
return {
|
||||||
|
content: answer,
|
||||||
|
reasoningContent: think
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
// Some models do not support streaming
|
// Some models do not support streaming
|
||||||
if (stream) {
|
if (stream) {
|
||||||
if (isResponseAnswerText && answer) {
|
if (aiChatReasoning && reasoningContent) {
|
||||||
workflowStreamResponse?.({
|
workflowStreamResponse?.({
|
||||||
event: SseResponseEventEnum.fastAnswer,
|
event: SseResponseEventEnum.fastAnswer,
|
||||||
data: textAdaptGptResponse({
|
data: textAdaptGptResponse({
|
||||||
text: answer
|
reasoning_content: reasoningContent
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (aiChatReasoning && reasoning) {
|
if (isResponseAnswerText && content) {
|
||||||
workflowStreamResponse?.({
|
workflowStreamResponse?.({
|
||||||
event: SseResponseEventEnum.fastAnswer,
|
event: SseResponseEventEnum.fastAnswer,
|
||||||
data: textAdaptGptResponse({
|
data: textAdaptGptResponse({
|
||||||
reasoning_content: reasoning
|
text: content
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
answerText: answer,
|
answerText: content,
|
||||||
reasoningText: reasoning
|
reasoningText: reasoningContent
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
@@ -249,7 +270,8 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
const AIMessages: ChatCompletionMessageParam[] = [
|
const AIMessages: ChatCompletionMessageParam[] = [
|
||||||
{
|
{
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||||
content: answerText
|
content: answerText,
|
||||||
|
reasoning_text: reasoningText // reasoning_text is only recorded for response, but not for request
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -267,7 +289,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
answerText,
|
answerText: answerText.trim(),
|
||||||
reasoningText,
|
reasoningText,
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||||
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
||||||
@@ -386,7 +408,7 @@ async function getMultiInput({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
documentQuoteText: text,
|
documentQuoteText: text,
|
||||||
userFiles: fileLinks.map((url) => parseUrlToFileType(url))
|
userFiles: fileLinks.map((url) => parseUrlToFileType(url)).filter(Boolean)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,26 +522,18 @@ async function streamResponse({
|
|||||||
});
|
});
|
||||||
let answer = '';
|
let answer = '';
|
||||||
let reasoning = '';
|
let reasoning = '';
|
||||||
|
const { parsePart, getStartTagBuffer } = parseReasoningStreamContent();
|
||||||
|
|
||||||
for await (const part of stream) {
|
for await (const part of stream) {
|
||||||
if (res.closed) {
|
if (res.closed) {
|
||||||
stream.controller?.abort();
|
stream.controller?.abort();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = part.choices?.[0]?.delta?.content || '';
|
const [reasoningContent, content] = parsePart(part, aiChatReasoning);
|
||||||
answer += content;
|
answer += content;
|
||||||
if (isResponseAnswerText && content) {
|
|
||||||
workflowStreamResponse?.({
|
|
||||||
write,
|
|
||||||
event: SseResponseEventEnum.answer,
|
|
||||||
data: textAdaptGptResponse({
|
|
||||||
text: content
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || '';
|
|
||||||
reasoning += reasoningContent;
|
reasoning += reasoningContent;
|
||||||
|
|
||||||
if (aiChatReasoning && reasoningContent) {
|
if (aiChatReasoning && reasoningContent) {
|
||||||
workflowStreamResponse?.({
|
workflowStreamResponse?.({
|
||||||
write,
|
write,
|
||||||
@@ -529,6 +543,21 @@ async function streamResponse({
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isResponseAnswerText && content) {
|
||||||
|
workflowStreamResponse?.({
|
||||||
|
write,
|
||||||
|
event: SseResponseEventEnum.answer,
|
||||||
|
data: textAdaptGptResponse({
|
||||||
|
text: content
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if answer is empty, try to get value from startTagBuffer. (Cause: The response content is too short to exceed the minimum parse length)
|
||||||
|
if (answer === '') {
|
||||||
|
answer = getStartTagBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
return { answer, reasoning };
|
return { answer, reasoning };
|
||||||
|
|||||||
@@ -232,9 +232,14 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
chatNodeUsages = chatNodeUsages.concat(nodeDispatchUsages);
|
chatNodeUsages = chatNodeUsages.concat(nodeDispatchUsages);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolResponses !== undefined) {
|
if (toolResponses !== undefined && toolResponses !== null) {
|
||||||
if (Array.isArray(toolResponses) && toolResponses.length === 0) return;
|
if (Array.isArray(toolResponses) && toolResponses.length === 0) return;
|
||||||
if (typeof toolResponses === 'object' && Object.keys(toolResponses).length === 0) return;
|
if (
|
||||||
|
!Array.isArray(toolResponses) &&
|
||||||
|
typeof toolResponses === 'object' &&
|
||||||
|
Object.keys(toolResponses).length === 0
|
||||||
|
)
|
||||||
|
return;
|
||||||
toolRunResponse = toolResponses;
|
toolRunResponse = toolResponses;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,12 +248,17 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
chatAssistantResponse = chatAssistantResponse.concat(assistantResponses);
|
chatAssistantResponse = chatAssistantResponse.concat(assistantResponses);
|
||||||
} else {
|
} else {
|
||||||
if (reasoningText) {
|
if (reasoningText) {
|
||||||
chatAssistantResponse.push({
|
const isResponseReasoningText = inputs.find(
|
||||||
type: ChatItemValueTypeEnum.reasoning,
|
(item) => item.key === NodeInputKeyEnum.aiChatReasoning
|
||||||
reasoning: {
|
)?.value;
|
||||||
content: reasoningText
|
if (isResponseReasoningText) {
|
||||||
}
|
chatAssistantResponse.push({
|
||||||
});
|
type: ChatItemValueTypeEnum.reasoning,
|
||||||
|
reasoning: {
|
||||||
|
content: reasoningText
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (answerText) {
|
if (answerText) {
|
||||||
// save assistant text response
|
// save assistant text response
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
|
|||||||
|
|
||||||
const userInputFiles = (() => {
|
const userInputFiles = (() => {
|
||||||
if (fileUrlList) {
|
if (fileUrlList) {
|
||||||
return fileUrlList.map((url) => parseUrlToFileType(url));
|
return fileUrlList.map((url) => parseUrlToFileType(url)).filter(Boolean);
|
||||||
}
|
}
|
||||||
// Adapt version 4.8.13 upgrade
|
// Adapt version 4.8.13 upgrade
|
||||||
return files;
|
return files;
|
||||||
|
|||||||
@@ -398,41 +398,6 @@ async function fetchData({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// function replaceVariable(text: string, obj: Record<string, any>) {
|
|
||||||
// for (const [key, value] of Object.entries(obj)) {
|
|
||||||
// if (value === undefined) {
|
|
||||||
// text = text.replace(new RegExp(`{{(${key})}}`, 'g'), UNDEFINED_SIGN);
|
|
||||||
// } else {
|
|
||||||
// const replacement = JSON.stringify(value);
|
|
||||||
// const unquotedReplacement =
|
|
||||||
// replacement.startsWith('"') && replacement.endsWith('"')
|
|
||||||
// ? replacement.slice(1, -1)
|
|
||||||
// : replacement;
|
|
||||||
// text = text.replace(new RegExp(`{{(${key})}}`, 'g'), () => unquotedReplacement);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return text || '';
|
|
||||||
// }
|
|
||||||
// function removeUndefinedSign(obj: Record<string, any>) {
|
|
||||||
// for (const key in obj) {
|
|
||||||
// if (obj[key] === UNDEFINED_SIGN) {
|
|
||||||
// obj[key] = undefined;
|
|
||||||
// } else if (Array.isArray(obj[key])) {
|
|
||||||
// obj[key] = obj[key].map((item: any) => {
|
|
||||||
// if (item === UNDEFINED_SIGN) {
|
|
||||||
// return undefined;
|
|
||||||
// } else if (typeof item === 'object') {
|
|
||||||
// removeUndefinedSign(item);
|
|
||||||
// }
|
|
||||||
// return item;
|
|
||||||
// });
|
|
||||||
// } else if (typeof obj[key] === 'object') {
|
|
||||||
// removeUndefinedSign(obj[key]);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return obj;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Replace some special response from system plugin
|
// Replace some special response from system plugin
|
||||||
async function replaceSystemPluginResponse({
|
async function replaceSystemPluginResponse({
|
||||||
response,
|
response,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mammoth": "^1.6.0",
|
"mammoth": "^1.6.0",
|
||||||
"mongoose": "^7.0.2",
|
"mongoose": "^8.10.1",
|
||||||
"multer": "1.4.5-lts.1",
|
"multer": "1.4.5-lts.1",
|
||||||
"next": "14.2.5",
|
"next": "14.2.5",
|
||||||
"nextjs-cors": "^2.2.0",
|
"nextjs-cors": "^2.2.0",
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ export const getClbsAndGroupsWithInfo = async ({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export const delResourcePermissionById = (id: string) => {
|
export const delResourcePermissionById = (id: string) => {
|
||||||
return MongoResourcePermission.findByIdAndRemove(id);
|
return MongoResourcePermission.findByIdAndDelete(id);
|
||||||
};
|
};
|
||||||
export const delResourcePermission = ({
|
export const delResourcePermission = ({
|
||||||
session,
|
session,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { AppDetailType } from '@fastgpt/global/core/app/type';
|
import { AppDetailType } from '@fastgpt/global/core/app/type';
|
||||||
import { OutlinkAppType, OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
||||||
import { parseHeaderCert } from '../controller';
|
import { parseHeaderCert } from '../controller';
|
||||||
import { MongoOutLink } from '../../outLink/schema';
|
import { MongoOutLink } from '../../outLink/schema';
|
||||||
import { OutLinkErrEnum } from '@fastgpt/global/common/error/code/outLink';
|
import { OutLinkErrEnum } from '@fastgpt/global/common/error/code/outLink';
|
||||||
@@ -54,15 +54,11 @@ export async function authOutLinkCrud({
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* outLink exist and it app exist */
|
/* outLink exist and it app exist */
|
||||||
export async function authOutLinkValid<T extends OutlinkAppType = undefined>({
|
export async function authOutLinkValid({ shareId }: { shareId?: string }) {
|
||||||
shareId
|
|
||||||
}: {
|
|
||||||
shareId?: string;
|
|
||||||
}) {
|
|
||||||
if (!shareId) {
|
if (!shareId) {
|
||||||
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||||
}
|
}
|
||||||
const outLinkConfig = (await MongoOutLink.findOne({ shareId }).lean()) as OutLinkSchema<T>;
|
const outLinkConfig = await MongoOutLink.findOne({ shareId }).lean();
|
||||||
|
|
||||||
if (!outLinkConfig) {
|
if (!outLinkConfig) {
|
||||||
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export const checkTeamDatasetLimit = async (teamId: string) => {
|
|||||||
export const checkTeamAppLimit = async (teamId: string, amount = 1) => {
|
export const checkTeamAppLimit = async (teamId: string, amount = 1) => {
|
||||||
const [{ standardConstants }, appCount] = await Promise.all([
|
const [{ standardConstants }, appCount] = await Promise.all([
|
||||||
getTeamStandPlan({ teamId }),
|
getTeamStandPlan({ teamId }),
|
||||||
MongoApp.count({
|
MongoApp.countDocuments({
|
||||||
teamId,
|
teamId,
|
||||||
type: { $in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin] }
|
type: { $in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin] }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export async function getUserDetail({
|
|||||||
promotionRate: user.promotionRate,
|
promotionRate: user.promotionRate,
|
||||||
team: tmb,
|
team: tmb,
|
||||||
notificationAccount: tmb.notificationAccount,
|
notificationAccount: tmb.notificationAccount,
|
||||||
permission: tmb.permission
|
permission: tmb.permission,
|
||||||
|
contact: user.contact
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ const UserSchema = new Schema({
|
|||||||
},
|
},
|
||||||
fastgpt_sem: Object,
|
fastgpt_sem: Object,
|
||||||
sourceDomain: String,
|
sourceDomain: String,
|
||||||
|
contact: String,
|
||||||
|
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
avatar: String
|
avatar: String
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { TeamDefaultPermissionVal } from '@fastgpt/global/support/permission/use
|
|||||||
import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupSchema';
|
import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupSchema';
|
||||||
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
|
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
|
||||||
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||||
import { getAIApi, openaiBaseUrl } from '../../../core/ai/config';
|
import { getAIApi } from '../../../core/ai/config';
|
||||||
import { createRootOrg } from '../../permission/org/controllers';
|
import { createRootOrg } from '../../permission/org/controllers';
|
||||||
import { refreshSourceAvatar } from '../../../common/file/image/controller';
|
import { refreshSourceAvatar } from '../../../common/file/image/controller';
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ export async function updateTeam({
|
|||||||
// auth openai key
|
// auth openai key
|
||||||
if (openaiAccount?.key) {
|
if (openaiAccount?.key) {
|
||||||
console.log('auth user openai key', openaiAccount?.key);
|
console.log('auth user openai key', openaiAccount?.key);
|
||||||
const baseUrl = openaiAccount?.baseUrl || openaiBaseUrl;
|
const baseUrl = openaiAccount?.baseUrl || 'https://api.openai.com/v1';
|
||||||
openaiAccount.baseUrl = baseUrl;
|
openaiAccount.baseUrl = baseUrl;
|
||||||
|
|
||||||
const ai = getAIApi({
|
const ai = getAIApi({
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ const TeamMemberSchema = new Schema({
|
|||||||
type: Date,
|
type: Date,
|
||||||
default: () => new Date()
|
default: () => new Date()
|
||||||
},
|
},
|
||||||
|
updateTime: {
|
||||||
|
type: Date
|
||||||
|
},
|
||||||
defaultTeam: {
|
defaultTeam: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
|||||||
@@ -86,9 +86,12 @@ export async function addSourceMember<T extends { tmbId: string }>({
|
|||||||
}): Promise<Array<T & { sourceMember: SourceMemberType }>> {
|
}): Promise<Array<T & { sourceMember: SourceMemberType }>> {
|
||||||
if (!Array.isArray(list)) return [];
|
if (!Array.isArray(list)) return [];
|
||||||
|
|
||||||
|
const tmbIdList = list
|
||||||
|
.map((item) => (item.tmbId ? String(item.tmbId) : undefined))
|
||||||
|
.filter(Boolean);
|
||||||
const tmbList = await MongoTeamMember.find(
|
const tmbList = await MongoTeamMember.find(
|
||||||
{
|
{
|
||||||
_id: { $in: list.map((item) => String(item.tmbId)) }
|
_id: { $in: tmbIdList }
|
||||||
},
|
},
|
||||||
'tmbId name avatar status',
|
'tmbId name avatar status',
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,114 @@
|
|||||||
import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
|
import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
|
||||||
import { MongoUsage } from './schema';
|
import { MongoUsage } from './schema';
|
||||||
import { ClientSession } from '../../../common/mongo';
|
import { ClientSession, Types } from '../../../common/mongo';
|
||||||
|
import { addLog } from '../../../common/system/log';
|
||||||
|
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||||
|
import { ConcatUsageProps, CreateUsageProps } from '@fastgpt/global/support/wallet/usage/api';
|
||||||
|
import { i18nT } from '../../../../web/i18n/utils';
|
||||||
|
import { pushConcatBillTask, pushReduceTeamAiPointsTask } from './utils';
|
||||||
|
|
||||||
|
import { POST } from '../../../common/api/plusRequest';
|
||||||
|
import { isFastGPTMainService } from '../../../common/system/constants';
|
||||||
|
|
||||||
|
export async function createUsage(data: CreateUsageProps) {
|
||||||
|
try {
|
||||||
|
// In FastGPT server
|
||||||
|
if (isFastGPTMainService) {
|
||||||
|
await POST('/support/wallet/usage/createUsage', data);
|
||||||
|
} else if (global.reduceAiPointsQueue) {
|
||||||
|
// In FastGPT pro server
|
||||||
|
await MongoUsage.create(data);
|
||||||
|
pushReduceTeamAiPointsTask({ teamId: data.teamId, totalPoints: data.totalPoints });
|
||||||
|
|
||||||
|
if (data.totalPoints === 0) {
|
||||||
|
addLog.info('0 totalPoints', data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
addLog.error('createUsage error', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function concatUsage(data: ConcatUsageProps) {
|
||||||
|
try {
|
||||||
|
// In FastGPT server
|
||||||
|
if (isFastGPTMainService) {
|
||||||
|
await POST('/support/wallet/usage/concatUsage', data);
|
||||||
|
} else if (global.reduceAiPointsQueue) {
|
||||||
|
const {
|
||||||
|
teamId,
|
||||||
|
billId,
|
||||||
|
totalPoints = 0,
|
||||||
|
listIndex,
|
||||||
|
inputTokens = 0,
|
||||||
|
outputTokens = 0
|
||||||
|
} = data;
|
||||||
|
|
||||||
|
// billId is required and valid
|
||||||
|
if (!billId || !Types.ObjectId.isValid(billId)) return;
|
||||||
|
|
||||||
|
// In FastGPT pro server
|
||||||
|
pushConcatBillTask([
|
||||||
|
{
|
||||||
|
billId,
|
||||||
|
listIndex,
|
||||||
|
inputTokens,
|
||||||
|
outputTokens,
|
||||||
|
totalPoints
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
pushReduceTeamAiPointsTask({ teamId, totalPoints });
|
||||||
|
|
||||||
|
if (data.totalPoints === 0) {
|
||||||
|
addLog.info('0 totalPoints', data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
addLog.error('concatUsage error', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createChatUsage = ({
|
||||||
|
appName,
|
||||||
|
appId,
|
||||||
|
pluginId,
|
||||||
|
teamId,
|
||||||
|
tmbId,
|
||||||
|
source,
|
||||||
|
flowUsages
|
||||||
|
}: {
|
||||||
|
appName: string;
|
||||||
|
appId?: string;
|
||||||
|
pluginId?: string;
|
||||||
|
teamId: string;
|
||||||
|
tmbId: string;
|
||||||
|
source: UsageSourceEnum;
|
||||||
|
flowUsages: ChatNodeUsageType[];
|
||||||
|
}) => {
|
||||||
|
const totalPoints = flowUsages.reduce((sum, item) => sum + (item.totalPoints || 0), 0);
|
||||||
|
|
||||||
|
createUsage({
|
||||||
|
teamId,
|
||||||
|
tmbId,
|
||||||
|
appName,
|
||||||
|
appId,
|
||||||
|
pluginId,
|
||||||
|
totalPoints,
|
||||||
|
source,
|
||||||
|
list: flowUsages.map((item) => ({
|
||||||
|
moduleName: item.moduleName,
|
||||||
|
amount: item.totalPoints || 0,
|
||||||
|
model: item.model,
|
||||||
|
inputTokens: item.inputTokens,
|
||||||
|
outputTokens: item.outputTokens
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
addLog.debug(`Create chat usage`, {
|
||||||
|
source,
|
||||||
|
teamId,
|
||||||
|
totalPoints
|
||||||
|
});
|
||||||
|
return { totalPoints };
|
||||||
|
};
|
||||||
|
|
||||||
export const createTrainingUsage = async ({
|
export const createTrainingUsage = async ({
|
||||||
teamId,
|
teamId,
|
||||||
@@ -29,21 +137,21 @@ export const createTrainingUsage = async ({
|
|||||||
totalPoints: 0,
|
totalPoints: 0,
|
||||||
list: [
|
list: [
|
||||||
{
|
{
|
||||||
moduleName: 'support.wallet.moduleName.index',
|
moduleName: i18nT('common:support.wallet.moduleName.index'),
|
||||||
model: vectorModel,
|
model: vectorModel,
|
||||||
amount: 0,
|
amount: 0,
|
||||||
inputTokens: 0,
|
inputTokens: 0,
|
||||||
outputTokens: 0
|
outputTokens: 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
moduleName: 'support.wallet.moduleName.qa',
|
moduleName: i18nT('common:support.wallet.moduleName.qa'),
|
||||||
model: agentModel,
|
model: agentModel,
|
||||||
amount: 0,
|
amount: 0,
|
||||||
inputTokens: 0,
|
inputTokens: 0,
|
||||||
outputTokens: 0
|
outputTokens: 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
moduleName: 'core.dataset.training.Auto mode',
|
moduleName: i18nT('common:core.dataset.training.Auto mode'),
|
||||||
model: agentModel,
|
model: agentModel,
|
||||||
amount: 0,
|
amount: 0,
|
||||||
inputTokens: 0,
|
inputTokens: 0,
|
||||||
|
|||||||
12
packages/service/support/wallet/usage/type.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export type ConcatBillQueueItemType = {
|
||||||
|
billId: string;
|
||||||
|
listIndex?: number;
|
||||||
|
totalPoints: number;
|
||||||
|
inputTokens: number;
|
||||||
|
outputTokens: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var reduceAiPointsQueue: { teamId: string; totalPoints: number }[];
|
||||||
|
var concatBillQueue: ConcatBillQueueItemType[];
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { findAIModel } from '../../../core/ai/model';
|
import { findAIModel } from '../../../core/ai/model';
|
||||||
import { ModelTypeEnum } from '@fastgpt/global/core/ai/model';
|
import { ModelTypeEnum } from '@fastgpt/global/core/ai/model';
|
||||||
|
import { ConcatBillQueueItemType } from './type';
|
||||||
|
|
||||||
export const formatModelChars2Points = ({
|
export const formatModelChars2Points = ({
|
||||||
model,
|
model,
|
||||||
@@ -34,3 +35,20 @@ export const formatModelChars2Points = ({
|
|||||||
totalPoints
|
totalPoints
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const pushReduceTeamAiPointsTask = ({
|
||||||
|
teamId,
|
||||||
|
totalPoints
|
||||||
|
}: {
|
||||||
|
teamId: string;
|
||||||
|
totalPoints: number;
|
||||||
|
}) => {
|
||||||
|
global.reduceAiPointsQueue.push({
|
||||||
|
teamId: String(teamId),
|
||||||
|
totalPoints
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const pushConcatBillTask = (data: ConcatBillQueueItemType[]) => {
|
||||||
|
global.concatBillQueue.push(...data);
|
||||||
|
};
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ parentPort?.on(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const total =
|
const total =
|
||||||
messages.reduce((sum, item) => {
|
messages.reduce((sum, item, index) => {
|
||||||
// Evaluates the text of toolcall and functioncall
|
// Evaluates the text of toolcall and functioncall
|
||||||
const functionCallPrompt = (() => {
|
const functionCallPrompt = (() => {
|
||||||
let prompt = '';
|
let prompt = '';
|
||||||
@@ -100,7 +100,13 @@ parentPort?.on(
|
|||||||
.join('');
|
.join('');
|
||||||
})();
|
})();
|
||||||
|
|
||||||
return sum + countPromptTokens(`${contentPrompt}${functionCallPrompt}`, item.role);
|
// Only the last message computed reasoning_text
|
||||||
|
const reasoningText = index === messages.length - 1 ? item.reasoning_text || '' : '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
sum +
|
||||||
|
countPromptTokens(`${reasoningText}${contentPrompt}${functionCallPrompt}`, item.role)
|
||||||
|
);
|
||||||
}, 0) +
|
}, 0) +
|
||||||
countToolsTokens(tools) +
|
countToolsTokens(tools) +
|
||||||
countToolsTokens(functionCall);
|
countToolsTokens(functionCall);
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ const parsePowerPoint = async ({
|
|||||||
|
|
||||||
// Returning an array of all the xml contents read using fs.readFileSync
|
// Returning an array of all the xml contents read using fs.readFileSync
|
||||||
const xmlContentArray = await Promise.all(
|
const xmlContentArray = await Promise.all(
|
||||||
files.map((file) => {
|
files.map(async (file) => {
|
||||||
try {
|
try {
|
||||||
return fs.promises.readFile(`${decompressPath}/${file.path}`, encoding);
|
return await fs.promises.readFile(`${decompressPath}/${file.path}`, encoding);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return fs.promises.readFile(`${decompressPath}/${file.path}`, 'utf-8');
|
return await fs.promises.readFile(`${decompressPath}/${file.path}`, 'utf-8');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -100,6 +100,13 @@ const DateRangePicker = ({
|
|||||||
if (date?.to === undefined) {
|
if (date?.to === undefined) {
|
||||||
date.to = date.from;
|
date.to = date.from;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (date?.from) {
|
||||||
|
date.from = new Date(date.from.setHours(0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
if (date?.to) {
|
||||||
|
date.to = new Date(date.to.setHours(23, 59, 59, 999));
|
||||||
|
}
|
||||||
setRange(date);
|
setRange(date);
|
||||||
onChange?.(date);
|
onChange?.(date);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export const iconPaths = {
|
|||||||
chatSend: () => import('./icons/chatSend.svg'),
|
chatSend: () => import('./icons/chatSend.svg'),
|
||||||
check: () => import('./icons/check.svg'),
|
check: () => import('./icons/check.svg'),
|
||||||
checkCircle: () => import('./icons/checkCircle.svg'),
|
checkCircle: () => import('./icons/checkCircle.svg'),
|
||||||
|
close: () => import('./icons/close.svg'),
|
||||||
closeSolid: () => import('./icons/closeSolid.svg'),
|
closeSolid: () => import('./icons/closeSolid.svg'),
|
||||||
code: () => import('./icons/code.svg'),
|
code: () => import('./icons/code.svg'),
|
||||||
collectionLight: () => import('./icons/collectionLight.svg'),
|
collectionLight: () => import('./icons/collectionLight.svg'),
|
||||||
@@ -26,13 +27,16 @@ export const iconPaths = {
|
|||||||
'common/closeLight': () => import('./icons/common/closeLight.svg'),
|
'common/closeLight': () => import('./icons/common/closeLight.svg'),
|
||||||
'common/confirm/commonTip': () => import('./icons/common/confirm/commonTip.svg'),
|
'common/confirm/commonTip': () => import('./icons/common/confirm/commonTip.svg'),
|
||||||
'common/confirm/deleteTip': () => import('./icons/common/confirm/deleteTip.svg'),
|
'common/confirm/deleteTip': () => import('./icons/common/confirm/deleteTip.svg'),
|
||||||
|
'common/confirm/restoreTip': () => import('./icons/common/confirm/restoreTip.svg'),
|
||||||
'common/confirm/rightTip': () => import('./icons/common/confirm/rightTip.svg'),
|
'common/confirm/rightTip': () => import('./icons/common/confirm/rightTip.svg'),
|
||||||
'common/courseLight': () => import('./icons/common/courseLight.svg'),
|
'common/courseLight': () => import('./icons/common/courseLight.svg'),
|
||||||
'common/customTitleLight': () => import('./icons/common/customTitleLight.svg'),
|
'common/customTitleLight': () => import('./icons/common/customTitleLight.svg'),
|
||||||
'common/data': () => import('./icons/common/data.svg'),
|
'common/data': () => import('./icons/common/data.svg'),
|
||||||
'common/dingtalkFill': () => import('./icons/common/dingtalkFill.svg'),
|
'common/dingtalkFill': () => import('./icons/common/dingtalkFill.svg'),
|
||||||
|
'common/disable': () => import('./icons/common/disable.svg'),
|
||||||
'common/downArrowFill': () => import('./icons/common/downArrowFill.svg'),
|
'common/downArrowFill': () => import('./icons/common/downArrowFill.svg'),
|
||||||
'common/editor/resizer': () => import('./icons/common/editor/resizer.svg'),
|
'common/editor/resizer': () => import('./icons/common/editor/resizer.svg'),
|
||||||
|
'common/enable': () => import('./icons/common/enable.svg'),
|
||||||
'common/errorFill': () => import('./icons/common/errorFill.svg'),
|
'common/errorFill': () => import('./icons/common/errorFill.svg'),
|
||||||
'common/file/move': () => import('./icons/common/file/move.svg'),
|
'common/file/move': () => import('./icons/common/file/move.svg'),
|
||||||
'common/folderFill': () => import('./icons/common/folderFill.svg'),
|
'common/folderFill': () => import('./icons/common/folderFill.svg'),
|
||||||
@@ -160,6 +164,7 @@ export const iconPaths = {
|
|||||||
'core/chat/chatLight': () => import('./icons/core/chat/chatLight.svg'),
|
'core/chat/chatLight': () => import('./icons/core/chat/chatLight.svg'),
|
||||||
'core/chat/chatModelTag': () => import('./icons/core/chat/chatModelTag.svg'),
|
'core/chat/chatModelTag': () => import('./icons/core/chat/chatModelTag.svg'),
|
||||||
'core/chat/chevronDown': () => import('./icons/core/chat/chevronDown.svg'),
|
'core/chat/chevronDown': () => import('./icons/core/chat/chevronDown.svg'),
|
||||||
|
'core/chat/chevronLeft': () => import('./icons/core/chat/chevronLeft.svg'),
|
||||||
'core/chat/chevronRight': () => import('./icons/core/chat/chevronRight.svg'),
|
'core/chat/chevronRight': () => import('./icons/core/chat/chevronRight.svg'),
|
||||||
'core/chat/chevronSelector': () => import('./icons/core/chat/chevronSelector.svg'),
|
'core/chat/chevronSelector': () => import('./icons/core/chat/chevronSelector.svg'),
|
||||||
'core/chat/chevronUp': () => import('./icons/core/chat/chevronUp.svg'),
|
'core/chat/chevronUp': () => import('./icons/core/chat/chevronUp.svg'),
|
||||||
@@ -328,6 +333,7 @@ export const iconPaths = {
|
|||||||
edit: () => import('./icons/edit.svg'),
|
edit: () => import('./icons/edit.svg'),
|
||||||
empty: () => import('./icons/empty.svg'),
|
empty: () => import('./icons/empty.svg'),
|
||||||
export: () => import('./icons/export.svg'),
|
export: () => import('./icons/export.svg'),
|
||||||
|
feedback: () => import('./icons/feedback.svg'),
|
||||||
'file/csv': () => import('./icons/file/csv.svg'),
|
'file/csv': () => import('./icons/file/csv.svg'),
|
||||||
'file/fill/csv': () => import('./icons/file/fill/csv.svg'),
|
'file/fill/csv': () => import('./icons/file/fill/csv.svg'),
|
||||||
'file/fill/doc': () => import('./icons/file/fill/doc.svg'),
|
'file/fill/doc': () => import('./icons/file/fill/doc.svg'),
|
||||||
@@ -387,6 +393,7 @@ export const iconPaths = {
|
|||||||
'model/moonshot': () => import('./icons/model/moonshot.svg'),
|
'model/moonshot': () => import('./icons/model/moonshot.svg'),
|
||||||
'model/ollama': () => import('./icons/model/ollama.svg'),
|
'model/ollama': () => import('./icons/model/ollama.svg'),
|
||||||
'model/openai': () => import('./icons/model/openai.svg'),
|
'model/openai': () => import('./icons/model/openai.svg'),
|
||||||
|
'model/ppio': () => import('./icons/model/ppio.svg'),
|
||||||
'model/qwen': () => import('./icons/model/qwen.svg'),
|
'model/qwen': () => import('./icons/model/qwen.svg'),
|
||||||
'model/siliconflow': () => import('./icons/model/siliconflow.svg'),
|
'model/siliconflow': () => import('./icons/model/siliconflow.svg'),
|
||||||
'model/sparkDesk': () => import('./icons/model/sparkDesk.svg'),
|
'model/sparkDesk': () => import('./icons/model/sparkDesk.svg'),
|
||||||
|
|||||||
3
packages/web/components/common/Icon/icons/close.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.65613 2.84383C9.85139 3.0391 9.85139 3.35568 9.65613 3.55094L7.20708 5.99999L9.65617 8.44908C9.85143 8.64434 9.85143 8.96093 9.65617 9.15619C9.46091 9.35145 9.14433 9.35145 8.94906 9.15619L6.49997 6.7071L4.05088 9.15619C3.85562 9.35145 3.53904 9.35145 3.34377 9.15619C3.14851 8.96093 3.14851 8.64434 3.34377 8.44908L5.79286 5.99999L3.34382 3.55094C3.14855 3.35568 3.14855 3.0391 3.34382 2.84383C3.53908 2.64857 3.85566 2.64857 4.05092 2.84383L6.49997 5.29288L8.94902 2.84383C9.14428 2.64857 9.46087 2.64857 9.65613 2.84383Z" fill="#92A5C9"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 675 B |
@@ -1 +1,3 @@
|
|||||||
<?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="1701403554068" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5098" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M572.074667 337.134933a57.275733 57.275733 0 1 0-114.176-6.007466h-0.170667l12.936533 272.896v0.443733a44.544 44.544 0 1 0 89.019734-1.194667l12.424533-266.1376z m-196.949334-191.214933c76.151467-130.048 199.816533-129.774933 275.797334 0l340.3776 581.085867c76.117333 130.048 15.7696 235.4176-135.236267 235.4176H169.984c-150.8352 0-211.217067-105.6768-135.202133-235.4176L375.125333 145.92z m140.049067 687.581867a57.275733 57.275733 0 1 0 0-114.517334 57.275733 57.275733 0 0 0 0 114.517334z" fill="#FB6547" p-id="5099"></path></svg>
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.77324 1.94537C9.14764 1.73458 9.57006 1.62384 9.99973 1.62384C10.4294 1.62384 10.8518 1.73458 11.2262 1.94537C11.6006 2.15617 11.9144 2.4599 12.1372 2.82727L12.1396 2.83123L19.198 14.6146L19.2047 14.6261C19.423 15.0041 19.5385 15.4327 19.5397 15.8692C19.541 16.3058 19.4279 16.735 19.2117 17.1142C18.9955 17.4935 18.6838 17.8095 18.3076 18.0309C17.9314 18.2523 17.5037 18.3713 17.0672 18.3761L17.0581 18.3762L2.93224 18.3761C2.49574 18.3713 2.0681 18.2523 1.69188 18.0309C1.31565 17.8095 1.00394 17.4935 0.787774 17.1142C0.571604 16.735 0.458504 16.3058 0.459727 15.8692C0.460949 15.4327 0.576451 15.0041 0.79474 14.6261L0.80151 14.6146L7.86222 2.82726C8.08506 2.4599 8.39883 2.15617 8.77324 1.94537ZM9.99973 3.29051C9.85651 3.29051 9.7157 3.32742 9.5909 3.39769C9.46666 3.46763 9.36246 3.56828 9.28824 3.68999L2.23531 15.4643C2.16432 15.5891 2.12679 15.7302 2.12639 15.8739C2.12598 16.0194 2.16368 16.1625 2.23574 16.2889C2.30779 16.4153 2.41169 16.5207 2.5371 16.5944C2.66141 16.6676 2.80256 16.7072 2.94673 16.7095H17.0527C17.1969 16.7072 17.338 16.6676 17.4624 16.5944C17.5878 16.5207 17.6917 16.4153 17.7637 16.2889C17.8358 16.1625 17.8735 16.0194 17.8731 15.8739C17.8727 15.7302 17.8351 15.5892 17.7642 15.4644L10.7122 3.69165C10.7119 3.6911 10.7116 3.69054 10.7112 3.68999C10.637 3.56828 10.5328 3.46763 10.4086 3.39769C10.2838 3.32742 10.143 3.29051 9.99973 3.29051ZM9.99973 6.70946C10.46 6.70946 10.8331 7.08256 10.8331 7.54279V10.8761C10.8331 11.3364 10.46 11.7095 9.99973 11.7095C9.53949 11.7095 9.1664 11.3364 9.1664 10.8761V7.54279C9.1664 7.08256 9.53949 6.70946 9.99973 6.70946ZM9.1664 14.2095C9.1664 13.7492 9.53949 13.3761 9.99973 13.3761H10.0081C10.4683 13.3761 10.8414 13.7492 10.8414 14.2095C10.8414 14.6697 10.4683 15.0428 10.0081 15.0428H9.99973C9.53949 15.0428 9.1664 14.6697 9.1664 14.2095Z" fill="#F79009"/>
|
||||||
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 869 B After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.0058 3.68803C7.89786 3.68803 5.31329 5.93506 4.79046 8.89299L5.70669 8.3668C6.10579 8.13759 6.61514 8.27532 6.84434 8.67443C7.07355 9.07353 6.93582 9.58287 6.53671 9.81208L3.95412 11.2953C3.814 11.3757 3.66029 11.411 3.50989 11.4056C3.21062 11.4162 2.91562 11.2648 2.7564 10.9869L1.29256 8.43222C1.06374 8.03289 1.20197 7.52368 1.6013 7.29487C2.00063 7.06605 2.50984 7.20428 2.73865 7.60361L3.1897 8.39078C3.93457 4.75546 7.15021 2.02136 11.0058 2.02136C15.4123 2.02136 18.9844 5.59352 18.9844 9.99999C18.9844 14.4065 15.4123 17.9786 11.0058 17.9786C9.66714 17.9786 8.30206 17.5718 7.13895 16.9284C5.98005 16.2874 4.95794 15.3757 4.35999 14.3039C4.13578 13.902 4.27984 13.3944 4.68176 13.1701C5.08369 12.9459 5.59128 13.09 5.8155 13.4919C6.2256 14.2271 6.98588 14.9391 7.94567 15.47C8.90126 15.9986 9.9912 16.312 11.0058 16.312C14.4918 16.312 17.3178 13.486 17.3178 9.99999C17.3178 6.51399 14.4918 3.68803 11.0058 3.68803Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1020 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg t="1740494996853" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2899" width="64" height="64"><path d="M512 953.6a441.6 441.6 0 1 1 0-883.2 441.6 441.6 0 0 1 0 883.2z m0-64a377.6 377.6 0 1 0 0-755.2 377.6 377.6 0 0 0 0 755.2z" p-id="2900"></path><path d="M182.1696 227.4304l45.2608-45.2608 614.4 614.4-45.2608 45.2608z" p-id="2901"></path></svg>
|
||||||
|
After Width: | Height: | Size: 397 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg t="1740495050372" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4745" width="64" height="64"><path d="M510.2 959.7c-246.9-1-447-202.6-446-449.5s202.6-447 449.5-446 447 202.6 446 449.5-202.6 447-449.5 446z m3.3-833.7c-212.8-0.8-386.7 171.7-387.5 384.5S297.7 897.2 510.5 898 897.2 726.3 898 513.5 726.3 126.8 513.5 126z" p-id="4746"></path><path d="M465.8 712.3L291.1 537.6l43.7-43.7 131 131 262-262 43.7 43.7z" p-id="4747"></path></svg>
|
||||||
|
After Width: | Height: | Size: 486 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.4714 3.52864C10.7317 3.78899 10.7317 4.2111 10.4714 4.47145L6.94277 8.00004L10.4714 11.5286C10.7317 11.789 10.7317 12.2111 10.4714 12.4714C10.211 12.7318 9.7889 12.7318 9.52855 12.4714L5.52855 8.47144C5.26821 8.21109 5.26821 7.78898 5.52855 7.52864L9.52855 3.52864C9.7889 3.26829 10.211 3.26829 10.4714 3.52864Z" fill="#92A5C9"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 464 B |
29
packages/web/components/common/Icon/icons/feedback.svg
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<svg viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8.49993 10.1261C8.49993 9.48174 9.02226 8.9594 9.6666 8.9594H14.3333C14.9776 8.9594 15.4999 9.48174 15.4999 10.1261C15.4999 10.7704 14.9776 11.2927 14.3333 11.2927H9.6666C9.02226 11.2927 8.49993 10.7704 8.49993 10.1261Z" fill="url(#paint0_linear_17422_2138)"/>
|
||||||
|
<path d="M8.49993 14.7927C8.49993 14.1484 9.02226 13.6261 9.6666 13.6261H18.4041C19.0485 13.6261 19.5708 14.1484 19.5708 14.7927C19.5708 15.4371 19.0485 15.9594 18.4041 15.9594H9.6666C9.02226 15.9594 8.49993 15.4371 8.49993 14.7927Z" fill="url(#paint1_linear_17422_2138)"/>
|
||||||
|
<path d="M17.6859 9.1641C17.3948 9.37757 17.2057 9.72212 17.2057 10.1108V11.2504C17.2057 11.2738 17.2247 11.2927 17.248 11.2927H18.3745C18.7286 11.2927 19.0462 11.1358 19.262 10.8878L25.8249 4.32494C26.2805 3.86933 26.2805 3.13063 25.8249 2.67502C25.3693 2.21941 24.6306 2.21941 24.175 2.67502L17.6859 9.1641Z" fill="url(#paint2_linear_17422_2138)"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.5122 3.45903C17.5122 2.81469 16.9899 2.29236 16.3456 2.29236H7.34644C5.41344 2.29236 3.84644 3.85936 3.84644 5.79236V22.1257C3.84644 24.0587 5.41344 25.6257 7.34644 25.6257H21.6535C23.5865 25.6257 25.1535 24.0587 25.1535 22.1257V12.1788C25.1535 11.5344 24.6311 11.0121 23.9868 11.0121C23.3425 11.0121 22.8201 11.5345 22.8201 12.1788V22.1257C22.8201 22.77 22.2978 23.2924 21.6535 23.2924H7.34644C6.7021 23.2924 6.17977 22.77 6.17977 22.1257V5.79236C6.17977 5.14803 6.7021 4.62569 7.34644 4.62569H16.3456C16.9899 4.62569 17.5122 4.10336 17.5122 3.45903Z" fill="url(#paint3_linear_17422_2138)"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_17422_2138" x1="15.0065" y1="3.04993" x2="15.0065" y2="24.8681" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#499DFF"/>
|
||||||
|
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||||
|
<stop offset="1" stop-color="#6E80FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_17422_2138" x1="15.0065" y1="3.04993" x2="15.0065" y2="24.8681" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#499DFF"/>
|
||||||
|
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||||
|
<stop offset="1" stop-color="#6E80FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint2_linear_17422_2138" x1="15.0065" y1="3.04993" x2="15.0065" y2="24.8681" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#499DFF"/>
|
||||||
|
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||||
|
<stop offset="1" stop-color="#6E80FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint3_linear_17422_2138" x1="15.0065" y1="3.04993" x2="15.0065" y2="24.8681" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#499DFF"/>
|
||||||
|
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||||
|
<stop offset="1" stop-color="#6E80FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
3
packages/web/components/common/Icon/icons/model/ppio.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M49.6479 0.359833C22.2415 0.359833 0 22.5703 0 49.9767C0 63.4861 5.41303 75.7546 14.1918 84.7039V50.0233C14.1918 40.5621 17.8832 31.6283 24.568 24.9434C31.2839 18.2275 40.1867 14.5671 49.6634 14.5671H49.9581L49.6479 14.5981C69.2372 14.5981 85.1196 30.4805 85.1196 50.0543C85.1196 51.7604 84.9955 53.4355 84.7628 55.0951L64.7238 34.9939C60.7221 30.9923 55.3556 28.7744 49.6789 28.7744C44.0022 28.7744 38.6512 30.9923 34.6341 34.9939C30.6015 39.0266 28.399 44.3621 28.399 50.0543C28.399 55.7465 30.617 61.082 34.6341 65.1146C38.6357 69.1162 44.0022 71.3342 49.6789 71.3342C55.3556 71.3342 60.7066 69.1162 64.7238 65.1146C68.4617 61.3767 70.6176 56.491 70.9123 51.2641L82.669 63.0673C77.4731 76.2199 64.6617 85.5415 49.6634 85.5415C41.8929 85.5415 34.479 83.0598 28.3835 78.4533V94.863C34.8357 97.934 42.0324 99.6402 49.6324 99.6402C77.0388 99.6402 99.2803 77.4297 99.2803 50.0233C99.3113 22.5858 77.0853 0.375343 49.6634 0.375343L49.6479 0.359833Z" fill="#0062E2"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1,17 +1,17 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import type { IconProps } from '@chakra-ui/react';
|
import type { IconProps } from '@chakra-ui/react';
|
||||||
import { Box, Icon } from '@chakra-ui/react';
|
import { Box, Icon } from '@chakra-ui/react';
|
||||||
import { iconPaths } from './constants';
|
import { iconPaths } from './constants';
|
||||||
import type { IconNameType } from './type.d';
|
import type { IconNameType } from './type.d';
|
||||||
|
import { useRefresh } from '../../../hooks/useRefresh';
|
||||||
|
|
||||||
const iconCache: Record<string, any> = {};
|
const iconCache: Record<string, any> = {};
|
||||||
|
|
||||||
const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconNameType } & IconProps) => {
|
const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconNameType } & IconProps) => {
|
||||||
const [IconComponent, setIconComponent] = useState<any>(null);
|
const { refresh } = useRefresh();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (iconCache[name]) {
|
if (iconCache[name]) {
|
||||||
setIconComponent(iconCache[name]);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,11 +20,13 @@ const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconNameType
|
|||||||
const component = { as: icon.default };
|
const component = { as: icon.default };
|
||||||
// Store in cache
|
// Store in cache
|
||||||
iconCache[name] = component;
|
iconCache[name] = component;
|
||||||
setIconComponent(component);
|
refresh();
|
||||||
})
|
})
|
||||||
.catch((error) => console.log(error));
|
.catch((error) => console.log(error));
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
|
||||||
|
const IconComponent = iconCache[name];
|
||||||
|
|
||||||
return !!IconComponent ? (
|
return !!IconComponent ? (
|
||||||
<Icon
|
<Icon
|
||||||
{...IconComponent}
|
{...IconComponent}
|
||||||
@@ -40,4 +42,4 @@ const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconNameType
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MyIcon;
|
export default React.memo(MyIcon);
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ import React from 'react';
|
|||||||
import MyIcon from '../../Icon';
|
import MyIcon from '../../Icon';
|
||||||
import { UseFormRegister } from 'react-hook-form';
|
import { UseFormRegister } from 'react-hook-form';
|
||||||
|
|
||||||
type Props = Omit<NumberInputProps, 'onChange'> & {
|
type Props = Omit<NumberInputProps, 'onChange' | 'onBlur'> & {
|
||||||
onChange?: (e?: number) => any;
|
onChange?: (e?: number) => any;
|
||||||
|
onBlur?: (e?: number) => any;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
register?: UseFormRegister<any>;
|
register?: UseFormRegister<any>;
|
||||||
name?: string;
|
name?: string;
|
||||||
@@ -19,11 +20,21 @@ type Props = Omit<NumberInputProps, 'onChange'> & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const MyNumberInput = (props: Props) => {
|
const MyNumberInput = (props: Props) => {
|
||||||
const { register, name, onChange, placeholder, bg, ...restProps } = props;
|
const { register, name, onChange, onBlur, placeholder, bg, ...restProps } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NumberInput
|
<NumberInput
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
onBlur={(e) => {
|
||||||
|
if (!onBlur) return;
|
||||||
|
const numE = Number(e.target.value);
|
||||||
|
if (isNaN(numE)) {
|
||||||
|
// @ts-ignore
|
||||||
|
onBlur('');
|
||||||
|
} else {
|
||||||
|
onBlur(numE);
|
||||||
|
}
|
||||||
|
}}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (!onChange) return;
|
if (!onChange) return;
|
||||||
const numE = Number(e);
|
const numE = Number(e);
|
||||||
@@ -38,6 +49,8 @@ const MyNumberInput = (props: Props) => {
|
|||||||
<NumberInputField
|
<NumberInputField
|
||||||
bg={bg}
|
bg={bg}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
|
h={restProps.h}
|
||||||
|
defaultValue={restProps.defaultValue}
|
||||||
{...(register && name
|
{...(register && name
|
||||||
? register(name, {
|
? register(name, {
|
||||||
required: props.isRequired,
|
required: props.isRequired,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Input, InputProps, InputGroup, InputLeftElement } from '@chakra-ui/react';
|
import { Input, InputProps, InputGroup, InputLeftElement } from '@chakra-ui/react';
|
||||||
import MyIcon from '../../Icon';
|
import MyIcon from '../../Icon';
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ const MyModal = ({
|
|||||||
objectFit={'contain'}
|
objectFit={'contain'}
|
||||||
alt=""
|
alt=""
|
||||||
src={iconSrc}
|
src={iconSrc}
|
||||||
w={'1.5rem'}
|
w={'20px'}
|
||||||
borderRadius={'sm'}
|
borderRadius={'sm'}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ const MultipleSelect = <T = any,>({
|
|||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={i}
|
key={i}
|
||||||
{...menuItemStyles}
|
|
||||||
{...(isSelected
|
{...(isSelected
|
||||||
? {
|
? {
|
||||||
color: 'primary.600'
|
color: 'primary.600'
|
||||||
@@ -114,6 +113,7 @@ const MultipleSelect = <T = any,>({
|
|||||||
whiteSpace={'pre-wrap'}
|
whiteSpace={'pre-wrap'}
|
||||||
fontSize={'sm'}
|
fontSize={'sm'}
|
||||||
gap={2}
|
gap={2}
|
||||||
|
{...menuItemStyles}
|
||||||
>
|
>
|
||||||
<Checkbox isChecked={isSelected} />
|
<Checkbox isChecked={isSelected} />
|
||||||
{item.icon && <MyAvatar src={item.icon} w={'1rem'} borderRadius={'0'} />}
|
{item.icon && <MyAvatar src={item.icon} w={'1rem'} borderRadius={'0'} />}
|
||||||
@@ -204,6 +204,7 @@ const MultipleSelect = <T = any,>({
|
|||||||
}}
|
}}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
onclickItem(item.value);
|
onclickItem(item.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -230,7 +231,6 @@ const MultipleSelect = <T = any,>({
|
|||||||
overflowY={'auto'}
|
overflowY={'auto'}
|
||||||
>
|
>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
{...menuItemStyles}
|
|
||||||
color={isSelectAll ? 'primary.600' : 'myGray.900'}
|
color={isSelectAll ? 'primary.600' : 'myGray.900'}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -241,6 +241,7 @@ const MultipleSelect = <T = any,>({
|
|||||||
fontSize={'sm'}
|
fontSize={'sm'}
|
||||||
gap={2}
|
gap={2}
|
||||||
mb={1}
|
mb={1}
|
||||||
|
{...menuItemStyles}
|
||||||
>
|
>
|
||||||
<Checkbox isChecked={isSelectAll} />
|
<Checkbox isChecked={isSelectAll} />
|
||||||
<Box flex={'1 0 0'}>{t('common:common.All')}</Box>
|
<Box flex={'1 0 0'}>{t('common:common.All')}</Box>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import React, {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useEffect,
|
useEffect,
|
||||||
useImperativeHandle,
|
useImperativeHandle,
|
||||||
ForwardedRef
|
ForwardedRef,
|
||||||
|
useState
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import {
|
import {
|
||||||
Menu,
|
Menu,
|
||||||
@@ -15,7 +16,8 @@ import {
|
|||||||
MenuButton,
|
MenuButton,
|
||||||
Box,
|
Box,
|
||||||
css,
|
css,
|
||||||
Flex
|
Flex,
|
||||||
|
Input
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import type { ButtonProps, MenuItemProps } from '@chakra-ui/react';
|
import type { ButtonProps, MenuItemProps } from '@chakra-ui/react';
|
||||||
import MyIcon from '../Icon';
|
import MyIcon from '../Icon';
|
||||||
@@ -33,8 +35,10 @@ import { useScrollPagination } from '../../../hooks/useScrollPagination';
|
|||||||
export type SelectProps<T = any> = ButtonProps & {
|
export type SelectProps<T = any> = ButtonProps & {
|
||||||
value?: T;
|
value?: T;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
isSearch?: boolean;
|
||||||
list: {
|
list: {
|
||||||
alias?: string;
|
alias?: string;
|
||||||
|
icon?: string;
|
||||||
label: string | React.ReactNode;
|
label: string | React.ReactNode;
|
||||||
description?: string;
|
description?: string;
|
||||||
value: T;
|
value: T;
|
||||||
@@ -49,6 +53,7 @@ const MySelect = <T = any,>(
|
|||||||
{
|
{
|
||||||
placeholder,
|
placeholder,
|
||||||
value,
|
value,
|
||||||
|
isSearch = false,
|
||||||
width = '100%',
|
width = '100%',
|
||||||
list = [],
|
list = [],
|
||||||
onchange,
|
onchange,
|
||||||
@@ -63,6 +68,7 @@ const MySelect = <T = any,>(
|
|||||||
const ButtonRef = useRef<HTMLButtonElement>(null);
|
const ButtonRef = useRef<HTMLButtonElement>(null);
|
||||||
const MenuListRef = useRef<HTMLDivElement>(null);
|
const MenuListRef = useRef<HTMLDivElement>(null);
|
||||||
const SelectedItemRef = useRef<HTMLDivElement>(null);
|
const SelectedItemRef = useRef<HTMLDivElement>(null);
|
||||||
|
const SearchInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const menuItemStyles: MenuItemProps = {
|
const menuItemStyles: MenuItemProps = {
|
||||||
borderRadius: 'sm',
|
borderRadius: 'sm',
|
||||||
@@ -79,6 +85,18 @@ const MySelect = <T = any,>(
|
|||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const selectItem = useMemo(() => list.find((item) => item.value === value), [list, value]);
|
const selectItem = useMemo(() => list.find((item) => item.value === value), [list, value]);
|
||||||
|
|
||||||
|
const [search, setSearch] = useState('');
|
||||||
|
const filterList = useMemo(() => {
|
||||||
|
if (!isSearch || !search) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
return list.filter((item) => {
|
||||||
|
const text = `${item.label?.toString()}${item.alias}${item.value}`;
|
||||||
|
const regx = new RegExp(search, 'gi');
|
||||||
|
return regx.test(text);
|
||||||
|
});
|
||||||
|
}, [list, search, isSearch]);
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
focus() {
|
focus() {
|
||||||
onOpen();
|
onOpen();
|
||||||
@@ -90,17 +108,19 @@ const MySelect = <T = any,>(
|
|||||||
const menu = MenuListRef.current;
|
const menu = MenuListRef.current;
|
||||||
const selectedItem = SelectedItemRef.current;
|
const selectedItem = SelectedItemRef.current;
|
||||||
menu.scrollTop = selectedItem.offsetTop - menu.offsetTop - 100;
|
menu.scrollTop = selectedItem.offsetTop - menu.offsetTop - 100;
|
||||||
|
|
||||||
|
if (isSearch) {
|
||||||
|
setSearch('');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isSearch, isOpen]);
|
||||||
|
|
||||||
const { runAsync: onChange, loading } = useRequest2((val: T) => onchange?.(val));
|
const { runAsync: onChange, loading } = useRequest2((val: T) => onchange?.(val));
|
||||||
|
|
||||||
const isSelecting = loading || isLoading;
|
|
||||||
|
|
||||||
const ListRender = useMemo(() => {
|
const ListRender = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{list.map((item, i) => (
|
{filterList.map((item, i) => (
|
||||||
<Box key={i}>
|
<Box key={i}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
{...menuItemStyles}
|
{...menuItemStyles}
|
||||||
@@ -123,7 +143,10 @@ const MySelect = <T = any,>(
|
|||||||
fontSize={'sm'}
|
fontSize={'sm'}
|
||||||
display={'block'}
|
display={'block'}
|
||||||
>
|
>
|
||||||
<Box>{item.label}</Box>
|
<Flex alignItems={'center'}>
|
||||||
|
{item.icon && <MyIcon mr={2} name={item.icon as any} w={'1rem'} />}
|
||||||
|
{item.label}
|
||||||
|
</Flex>
|
||||||
{item.description && (
|
{item.description && (
|
||||||
<Box color={'myGray.500'} fontSize={'xs'}>
|
<Box color={'myGray.500'} fontSize={'xs'}>
|
||||||
{item.description}
|
{item.description}
|
||||||
@@ -135,7 +158,9 @@ const MySelect = <T = any,>(
|
|||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}, [list, value]);
|
}, [filterList, value]);
|
||||||
|
|
||||||
|
const isSelecting = loading || isLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -176,8 +201,33 @@ const MySelect = <T = any,>(
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
{isSelecting && <MyIcon mr={2} name={'common/loading'} w={'16px'} />}
|
{isSelecting && <MyIcon mr={2} name={'common/loading'} w={'1rem'} />}
|
||||||
{selectItem?.alias || selectItem?.label || placeholder}
|
{isSearch && isOpen ? (
|
||||||
|
<Input
|
||||||
|
ref={SearchInputRef}
|
||||||
|
autoFocus
|
||||||
|
variant={'unstyled'}
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
placeholder={
|
||||||
|
selectItem?.alias ||
|
||||||
|
(typeof selectItem?.label === 'string' ? selectItem?.label : placeholder)
|
||||||
|
}
|
||||||
|
size={'sm'}
|
||||||
|
w={'100%'}
|
||||||
|
color={'myGray.700'}
|
||||||
|
onBlur={() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
SearchInputRef?.current?.focus();
|
||||||
|
}, 0);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{selectItem?.icon && <MyIcon mr={2} name={selectItem.icon as any} w={'1rem'} />}
|
||||||
|
{selectItem?.alias || selectItem?.label || placeholder}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useDisclosure, Button, ModalBody, ModalFooter } from '@chakra-ui/react';
|
import { useDisclosure, Button, ModalBody, ModalFooter, type ImageProps } from '@chakra-ui/react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import MyModal from '../components/common/MyModal';
|
import MyModal from '../components/common/MyModal';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
@@ -11,6 +11,7 @@ export const useConfirm = (props?: {
|
|||||||
showCancel?: boolean;
|
showCancel?: boolean;
|
||||||
type?: 'common' | 'delete';
|
type?: 'common' | 'delete';
|
||||||
hideFooter?: boolean;
|
hideFooter?: boolean;
|
||||||
|
iconColor?: ImageProps['color'];
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ export const useConfirm = (props?: {
|
|||||||
const {
|
const {
|
||||||
title = map?.title || t('common:Warning'),
|
title = map?.title || t('common:Warning'),
|
||||||
iconSrc = map?.iconSrc,
|
iconSrc = map?.iconSrc,
|
||||||
|
iconColor,
|
||||||
content,
|
content,
|
||||||
showCancel = true,
|
showCancel = true,
|
||||||
hideFooter = false
|
hideFooter = false
|
||||||
@@ -93,7 +95,13 @@ export const useConfirm = (props?: {
|
|||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyModal isOpen={isOpen} iconSrc={iconSrc} title={title} maxW={['90vw', '400px']}>
|
<MyModal
|
||||||
|
isOpen={isOpen}
|
||||||
|
iconSrc={iconSrc}
|
||||||
|
iconColor={iconColor}
|
||||||
|
title={title}
|
||||||
|
maxW={['90vw', '400px']}
|
||||||
|
>
|
||||||
<ModalBody pt={5} whiteSpace={'pre-wrap'} fontSize={'sm'}>
|
<ModalBody pt={5} whiteSpace={'pre-wrap'} fontSize={'sm'}>
|
||||||
{customContent}
|
{customContent}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ export function useScrollPagination<
|
|||||||
const offset = init ? 0 : data.length;
|
const offset = init ? 0 : data.length;
|
||||||
|
|
||||||
setTrue();
|
setTrue();
|
||||||
|
console.log(offset);
|
||||||
try {
|
try {
|
||||||
const res = await api({
|
const res = await api({
|
||||||
offset,
|
offset,
|
||||||
|
|||||||
46
packages/web/i18n/en/account_model.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"api_key": "API key",
|
||||||
|
"azure": "Azure",
|
||||||
|
"base_url": "Base url",
|
||||||
|
"channel_name": "Channel",
|
||||||
|
"channel_priority": "Priority",
|
||||||
|
"channel_priority_tip": "The higher the priority channel, the easier it is to be requested",
|
||||||
|
"channel_status": "state",
|
||||||
|
"channel_status_auto_disabled": "Automatically disable",
|
||||||
|
"channel_status_disabled": "Disabled",
|
||||||
|
"channel_status_enabled": "Enable",
|
||||||
|
"channel_status_unknown": "unknown",
|
||||||
|
"channel_type": "Manufacturer",
|
||||||
|
"clear_model": "Clear the model",
|
||||||
|
"copy_model_id_success": "Copyed model id",
|
||||||
|
"create_channel": "Added channels",
|
||||||
|
"default_url": "Default address",
|
||||||
|
"detail": "Detail",
|
||||||
|
"duration": "Duration",
|
||||||
|
"edit": "edit",
|
||||||
|
"edit_channel": "Channel configuration",
|
||||||
|
"enable_channel": "Enable",
|
||||||
|
"forbid_channel": "Disabled",
|
||||||
|
"key_type": "API key format:",
|
||||||
|
"log": "Call log",
|
||||||
|
"log_detail": "Log details",
|
||||||
|
"log_status": "Status",
|
||||||
|
"mapping": "Model Mapping",
|
||||||
|
"mapping_tip": "A valid Json is required. \nThe model can be mapped when sending a request to the actual address. \nFor example:\n{\n \n \"gpt-4o\": \"gpt-4o-test\"\n\n}\n\nWhen FastGPT requests the gpt-4o model, the gpt-4o-test model is sent to the actual address, instead of gpt-4o.",
|
||||||
|
"model": "Model",
|
||||||
|
"model_name": "Model name",
|
||||||
|
"model_test": "Model testing",
|
||||||
|
"model_tokens": "Input/Output tokens",
|
||||||
|
"request_at": "Request time",
|
||||||
|
"request_duration": "Request duration: {{duration}}s",
|
||||||
|
"running_test": "In testing",
|
||||||
|
"search_model": "Search for models",
|
||||||
|
"select_channel": "Select a channel name",
|
||||||
|
"select_model": "Select a model",
|
||||||
|
"select_model_placeholder": "Select the model available under this channel",
|
||||||
|
"select_provider_placeholder": "Search for manufacturers",
|
||||||
|
"selected_model_empty": "Choose at least one model",
|
||||||
|
"start_test": "Start testing {{num}} models",
|
||||||
|
"test_failed": "There are {{num}} models that report errors",
|
||||||
|
"waiting_test": "Waiting for testing"
|
||||||
|
}
|
||||||
@@ -23,7 +23,9 @@
|
|||||||
"Move": "Move",
|
"Move": "Move",
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
"None": "None",
|
"None": "None",
|
||||||
|
"Operation": "Operation",
|
||||||
"Rename": "Rename",
|
"Rename": "Rename",
|
||||||
|
"Required_input": "Required",
|
||||||
"Resume": "Resume",
|
"Resume": "Resume",
|
||||||
"Running": "Running",
|
"Running": "Running",
|
||||||
"Select_all": "Select all",
|
"Select_all": "Select all",
|
||||||
@@ -123,7 +125,6 @@
|
|||||||
"common.Copy Successful": "Copied Successfully",
|
"common.Copy Successful": "Copied Successfully",
|
||||||
"common.Copy_failed": "Copy Failed, Please Copy Manually",
|
"common.Copy_failed": "Copy Failed, Please Copy Manually",
|
||||||
"common.Create Failed": "Creation Failed",
|
"common.Create Failed": "Creation Failed",
|
||||||
"common.Create New": "Create",
|
|
||||||
"common.Create Success": "Created Successfully",
|
"common.Create Success": "Created Successfully",
|
||||||
"common.Create Time": "Creation Time",
|
"common.Create Time": "Creation Time",
|
||||||
"common.Creating": "Creating",
|
"common.Creating": "Creating",
|
||||||
@@ -165,7 +166,6 @@
|
|||||||
"common.Not open": "Not Open",
|
"common.Not open": "Not Open",
|
||||||
"common.OK": "OK",
|
"common.OK": "OK",
|
||||||
"common.Open": "Open",
|
"common.Open": "Open",
|
||||||
"Operation": "Operation",
|
|
||||||
"common.Other": "Other",
|
"common.Other": "Other",
|
||||||
"common.Output": "Output",
|
"common.Output": "Output",
|
||||||
"common.Params": "Parameters",
|
"common.Params": "Parameters",
|
||||||
@@ -179,7 +179,6 @@
|
|||||||
"common.Remove": "Remove",
|
"common.Remove": "Remove",
|
||||||
"common.Rename": "Rename",
|
"common.Rename": "Rename",
|
||||||
"common.Request Error": "Request Error",
|
"common.Request Error": "Request Error",
|
||||||
"Required_input": "Required",
|
|
||||||
"common.Reset": "Reset",
|
"common.Reset": "Reset",
|
||||||
"common.Restart": "Restart",
|
"common.Restart": "Restart",
|
||||||
"common.Role": "Permission",
|
"common.Role": "Permission",
|
||||||
@@ -369,7 +368,7 @@
|
|||||||
"core.app.tip.Add a intro to app": "Give the app an introduction",
|
"core.app.tip.Add a intro to app": "Give the app an introduction",
|
||||||
"core.app.tip.chatNodeSystemPromptTip": "Enter a prompt here",
|
"core.app.tip.chatNodeSystemPromptTip": "Enter a prompt here",
|
||||||
"core.app.tip.systemPromptTip": "Fixed guide words for the model. By adjusting this content, you can guide the model's chat direction. This content will be fixed at the beginning of the context. You can use / to insert variables.\nIf a Dataset is associated, you can also guide the model when to call the Dataset search by appropriate description. For example:\nYou are an assistant for the movie 'Interstellar'. When users ask about content related to 'Interstellar', please search the Dataset and answer based on the search results.",
|
"core.app.tip.systemPromptTip": "Fixed guide words for the model. By adjusting this content, you can guide the model's chat direction. This content will be fixed at the beginning of the context. You can use / to insert variables.\nIf a Dataset is associated, you can also guide the model when to call the Dataset search by appropriate description. For example:\nYou are an assistant for the movie 'Interstellar'. When users ask about content related to 'Interstellar', please search the Dataset and answer based on the search results.",
|
||||||
"core.app.tip.variableTip": "Before the conversation starts, you can ask the user to fill in some content as specific variables for this round of conversation. This module is located after the opening guide.\nVariables can be injected into other modules' string type inputs in the form of {{variable key}}, such as prompts, delimiters, etc.",
|
"core.app.tip.variableTip": "Before the conversation begins, users can be asked to fill in some content as specific variables for this round of conversation. \nThis module is located after the opening boot.\n\nIn the input box, you can select variables through / activation, such as: prompt words, qualifiers, etc.",
|
||||||
"core.app.tip.welcomeTextTip": "Before each conversation starts, send an initial content. Supports standard Markdown syntax. Additional tags that can be used:\n[Quick Key]: Users can directly send the question by clicking",
|
"core.app.tip.welcomeTextTip": "Before each conversation starts, send an initial content. Supports standard Markdown syntax. Additional tags that can be used:\n[Quick Key]: Users can directly send the question by clicking",
|
||||||
"core.app.tool_label.doc": "Documentation",
|
"core.app.tool_label.doc": "Documentation",
|
||||||
"core.app.tool_label.github": "GitHub Address",
|
"core.app.tool_label.github": "GitHub Address",
|
||||||
@@ -547,7 +546,6 @@
|
|||||||
"core.dataset.data.Main Content": "Main Content",
|
"core.dataset.data.Main Content": "Main Content",
|
||||||
"core.dataset.data.Search data placeholder": "Search Related Data",
|
"core.dataset.data.Search data placeholder": "Search Related Data",
|
||||||
"core.dataset.data.Too Long": "Total Length Exceeded",
|
"core.dataset.data.Too Long": "Total Length Exceeded",
|
||||||
"core.dataset.data.Total Amount": "{{total}} Groups",
|
|
||||||
"core.dataset.data.group": "Group",
|
"core.dataset.data.group": "Group",
|
||||||
"core.dataset.data.unit": "Items",
|
"core.dataset.data.unit": "Items",
|
||||||
"core.dataset.embedding model tip": "The index model can convert natural language into vectors for semantic search.\nNote that different index models cannot be used together. Once an index model is selected, it cannot be changed.",
|
"core.dataset.embedding model tip": "The index model can convert natural language into vectors for semantic search.\nNote that different index models cannot be used together. Once an index model is selected, it cannot be changed.",
|
||||||
@@ -860,7 +858,6 @@
|
|||||||
"dataset.collections.Collection Embedding": "{{total}} Indexes",
|
"dataset.collections.Collection Embedding": "{{total}} Indexes",
|
||||||
"dataset.collections.Confirm to delete the folder": "Confirm to Delete This Folder and All Its Contents?",
|
"dataset.collections.Confirm to delete the folder": "Confirm to Delete This Folder and All Its Contents?",
|
||||||
"dataset.collections.Create And Import": "Create/Import",
|
"dataset.collections.Create And Import": "Create/Import",
|
||||||
"dataset.collections.Data Amount": "Total Data",
|
|
||||||
"dataset.collections.Select Collection": "Select File",
|
"dataset.collections.Select Collection": "Select File",
|
||||||
"dataset.collections.Select One Collection To Store": "Select a File to Store",
|
"dataset.collections.Select One Collection To Store": "Select a File to Store",
|
||||||
"dataset.data.Can not edit": "No Edit Permission",
|
"dataset.data.Can not edit": "No Edit Permission",
|
||||||
@@ -876,8 +873,10 @@
|
|||||||
"dataset.dataset_name": "Dataset Name",
|
"dataset.dataset_name": "Dataset Name",
|
||||||
"dataset.deleteFolderTips": "Confirm to Delete This Folder and All Its Contained Datasets? Data Cannot Be Recovered After Deletion, Please Confirm!",
|
"dataset.deleteFolderTips": "Confirm to Delete This Folder and All Its Contained Datasets? Data Cannot Be Recovered After Deletion, Please Confirm!",
|
||||||
"dataset.test.noResult": "No Search Results",
|
"dataset.test.noResult": "No Search Results",
|
||||||
|
"dataset_text_model_tip": "Used for text processing in the knowledge base preprocessing stage, such as automatic supplementary indexing, Q&A pair extraction.",
|
||||||
"deep_rag_search": "In-depth search",
|
"deep_rag_search": "In-depth search",
|
||||||
"delete_api": "Are you sure you want to delete this API key? \nAfter deletion, the key will become invalid immediately and the corresponding conversation log will not be deleted. Please confirm!",
|
"delete_api": "Are you sure you want to delete this API key? \nAfter deletion, the key will become invalid immediately and the corresponding conversation log will not be deleted. Please confirm!",
|
||||||
|
"embedding_model_not_config": "No index model is detected",
|
||||||
"error.Create failed": "Create failed",
|
"error.Create failed": "Create failed",
|
||||||
"error.code_error": "Verification code error",
|
"error.code_error": "Verification code error",
|
||||||
"error.fileNotFound": "File not found~",
|
"error.fileNotFound": "File not found~",
|
||||||
@@ -914,6 +913,7 @@
|
|||||||
"item_name": "Field Name",
|
"item_name": "Field Name",
|
||||||
"just_now": "just",
|
"just_now": "just",
|
||||||
"key_repetition": "Key Repetition",
|
"key_repetition": "Key Repetition",
|
||||||
|
"llm_model_not_config": "No language model was detected",
|
||||||
"max_quote_tokens": "Quote cap",
|
"max_quote_tokens": "Quote cap",
|
||||||
"max_quote_tokens_tips": "The maximum number of tokens in a single search, about 1 character in Chinese = 1.7 tokens, and about 1 character in English = 1 token",
|
"max_quote_tokens_tips": "The maximum number of tokens in a single search, about 1 character in Chinese = 1.7 tokens, and about 1 character in English = 1 token",
|
||||||
"min_similarity": "lowest correlation",
|
"min_similarity": "lowest correlation",
|
||||||
@@ -939,6 +939,7 @@
|
|||||||
"model_moka": "Moka-AI",
|
"model_moka": "Moka-AI",
|
||||||
"model_moonshot": "Moonshot",
|
"model_moonshot": "Moonshot",
|
||||||
"model_other": "Other",
|
"model_other": "Other",
|
||||||
|
"model_ppio": "PPIO",
|
||||||
"model_qwen": "Qwen",
|
"model_qwen": "Qwen",
|
||||||
"model_siliconflow": "Siliconflow",
|
"model_siliconflow": "Siliconflow",
|
||||||
"model_sparkdesk": "SprkDesk",
|
"model_sparkdesk": "SprkDesk",
|
||||||
@@ -1010,6 +1011,7 @@
|
|||||||
"plugin.go to laf": "Go to Write",
|
"plugin.go to laf": "Go to Write",
|
||||||
"plugin.path": "Path",
|
"plugin.path": "Path",
|
||||||
"prompt_input_placeholder": "Please enter the prompt word",
|
"prompt_input_placeholder": "Please enter the prompt word",
|
||||||
|
"question_feedback": "Work order",
|
||||||
"read_quote": "View citations",
|
"read_quote": "View citations",
|
||||||
"required": "Required",
|
"required": "Required",
|
||||||
"resume_failed": "Resume Failed",
|
"resume_failed": "Resume Failed",
|
||||||
@@ -1179,7 +1181,6 @@
|
|||||||
"support.wallet.usage.Audio Speech": "Voice Playback",
|
"support.wallet.usage.Audio Speech": "Voice Playback",
|
||||||
"support.wallet.usage.Bill Module": "Billing Module",
|
"support.wallet.usage.Bill Module": "Billing Module",
|
||||||
"support.wallet.usage.Duration": "Duration (seconds)",
|
"support.wallet.usage.Duration": "Duration (seconds)",
|
||||||
"support.wallet.usage.Extension result": "Question Optimization Result",
|
|
||||||
"support.wallet.usage.Module name": "Module Name",
|
"support.wallet.usage.Module name": "Module Name",
|
||||||
"support.wallet.usage.Source": "Source",
|
"support.wallet.usage.Source": "Source",
|
||||||
"support.wallet.usage.Text Length": "Text Length",
|
"support.wallet.usage.Text Length": "Text Length",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"close_auto_sync": "Are you sure you want to turn off automatic sync?",
|
"close_auto_sync": "Are you sure you want to turn off automatic sync?",
|
||||||
"collection.Create update time": "Creation/Update Time",
|
"collection.Create update time": "Creation/Update Time",
|
||||||
"collection.Training type": "Training",
|
"collection.Training type": "Training",
|
||||||
|
"collection_data_count": "Data amount",
|
||||||
"collection_not_support_retraining": "This collection type does not support retuning parameters",
|
"collection_not_support_retraining": "This collection type does not support retuning parameters",
|
||||||
"collection_not_support_sync": "This collection does not support synchronization",
|
"collection_not_support_sync": "This collection does not support synchronization",
|
||||||
"collection_sync": "Sync data",
|
"collection_sync": "Sync data",
|
||||||
@@ -20,6 +21,7 @@
|
|||||||
"custom_data_process_params": "Custom",
|
"custom_data_process_params": "Custom",
|
||||||
"custom_data_process_params_desc": "Customize data processing rules",
|
"custom_data_process_params_desc": "Customize data processing rules",
|
||||||
"data.ideal_chunk_length": "ideal block length",
|
"data.ideal_chunk_length": "ideal block length",
|
||||||
|
"data_amount": "{{dataAmount}} Datas, {{indexAmount}} Indexes",
|
||||||
"data_process_params": "Params",
|
"data_process_params": "Params",
|
||||||
"data_process_setting": "Processing config",
|
"data_process_setting": "Processing config",
|
||||||
"dataset.Unsupported operation": "dataset.Unsupported operation",
|
"dataset.Unsupported operation": "dataset.Unsupported operation",
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
"forget_password": "Find Password",
|
"forget_password": "Find Password",
|
||||||
"login_failed": "Login failed",
|
"login_failed": "Login failed",
|
||||||
"login_success": "Login successful",
|
"login_success": "Login successful",
|
||||||
"model_not_config": "It is detected that the system has not configured the model, please configure the model before using it",
|
|
||||||
"no_remind": "Don't remind again",
|
"no_remind": "Don't remind again",
|
||||||
"password_condition": "Password maximum 60 characters",
|
"password_condition": "Password maximum 60 characters",
|
||||||
"password_tip": "Password must be at least 6 characters long and contain at least two combinations: numbers, letters, or special characters",
|
"password_tip": "Password must be at least 6 characters long and contain at least two combinations: numbers, letters, or special characters",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"add_default_model": "添加预设模型",
|
"add_default_model": "添加预设模型",
|
||||||
"api_key": "API 密钥",
|
"api_key": "API 密钥",
|
||||||
"bills_and_invoices": "账单与发票",
|
"bills_and_invoices": "账单与发票",
|
||||||
"channel": "渠道",
|
"channel": "模型渠道",
|
||||||
"config_model": "模型配置",
|
"config_model": "模型配置",
|
||||||
"confirm_logout": "确认退出登录?",
|
"confirm_logout": "确认退出登录?",
|
||||||
"create_channel": "新增渠道",
|
"create_channel": "新增渠道",
|
||||||
|
|||||||
@@ -71,5 +71,7 @@
|
|||||||
"user_team_team_name": "团队名",
|
"user_team_team_name": "团队名",
|
||||||
"verification_code": "验证码",
|
"verification_code": "验证码",
|
||||||
"you_can_convert": "您可以兑换",
|
"you_can_convert": "您可以兑换",
|
||||||
"yuan": "元"
|
"yuan": "元",
|
||||||
|
"contact": "联系方式",
|
||||||
|
"please_bind_contact": "请绑定联系方式"
|
||||||
}
|
}
|
||||||
|
|||||||
46
packages/web/i18n/zh-CN/account_model.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"api_key": "API 密钥",
|
||||||
|
"azure": "微软 Azure",
|
||||||
|
"base_url": "代理地址",
|
||||||
|
"channel_name": "渠道名",
|
||||||
|
"channel_priority": "优先级",
|
||||||
|
"channel_priority_tip": "优先级越高的渠道,越容易被请求到",
|
||||||
|
"channel_status": "状态",
|
||||||
|
"channel_status_auto_disabled": "自动禁用",
|
||||||
|
"channel_status_disabled": "禁用",
|
||||||
|
"channel_status_enabled": "启用",
|
||||||
|
"channel_status_unknown": "未知",
|
||||||
|
"channel_type": "厂商",
|
||||||
|
"clear_model": "清空模型",
|
||||||
|
"copy_model_id_success": "已复制模型id",
|
||||||
|
"create_channel": "新增渠道",
|
||||||
|
"default_url": "默认地址",
|
||||||
|
"detail": "详情",
|
||||||
|
"duration": "耗时",
|
||||||
|
"edit": "编辑",
|
||||||
|
"edit_channel": "渠道配置",
|
||||||
|
"enable_channel": "启用",
|
||||||
|
"forbid_channel": "禁用",
|
||||||
|
"key_type": "API key 格式: ",
|
||||||
|
"log": "调用日志",
|
||||||
|
"log_detail": "日志详情",
|
||||||
|
"log_status": "状态",
|
||||||
|
"mapping": "模型映射",
|
||||||
|
"mapping_tip": "需填写一个有效 Json。可在向实际地址发送请求时,对模型进行映射。例如:\n{\n \"gpt-4o\": \"gpt-4o-test\"\n}\n当 FastGPT 请求 gpt-4o 模型时,会向实际地址发送 gpt-4o-test 的模型,而不是 gpt-4o。",
|
||||||
|
"model": "模型",
|
||||||
|
"model_name": "模型名",
|
||||||
|
"model_test": "模型测试",
|
||||||
|
"model_tokens": "输入/输出 Tokens",
|
||||||
|
"request_at": "请求时间",
|
||||||
|
"request_duration": "请求时长: {{duration}}s",
|
||||||
|
"running_test": "测试中",
|
||||||
|
"search_model": "搜索模型",
|
||||||
|
"select_channel": "选择渠道名",
|
||||||
|
"select_model": "选择模型",
|
||||||
|
"select_model_placeholder": "选择该渠道下可用的模型",
|
||||||
|
"select_provider_placeholder": "搜索厂商",
|
||||||
|
"selected_model_empty": "至少选择一个模型",
|
||||||
|
"start_test": "开始测试{{num}}个模型",
|
||||||
|
"test_failed": "有{{num}}个模型报错",
|
||||||
|
"waiting_test": "等待测试"
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"action": "操作",
|
"action": "操作",
|
||||||
"confirm_delete_group": "确认删除群组?",
|
"confirm_delete_group": "确认删除群组?",
|
||||||
"confirm_delete_member": "确认删除成员?",
|
|
||||||
"confirm_delete_org": "确认删除该部门?",
|
"confirm_delete_org": "确认删除该部门?",
|
||||||
"confirm_leave_team": "确认离开该团队? \n退出后,您在该团队所有的资源均转让给团队所有者。",
|
"confirm_leave_team": "确认离开该团队? \n退出后,您在该团队所有的资源均转让给团队所有者。",
|
||||||
"create_group": "创建群组",
|
"create_group": "创建群组",
|
||||||
@@ -26,7 +25,9 @@
|
|||||||
"owner": "所有者",
|
"owner": "所有者",
|
||||||
"permission": "权限",
|
"permission": "权限",
|
||||||
"remark": "备注",
|
"remark": "备注",
|
||||||
"remove_tip": "确认将 {{username}} 移出团队?",
|
"remove_tip": "确认将 {{username}} 移出团队?成员将被标记为“已离职”,不删除操作数据,账号下资源自动转让给团队所有者。",
|
||||||
|
"restore_tip": "确认将 {{username}} 加入团队吗?仅恢复该成员账号可用性及相关权限,无法恢复账号下资源。",
|
||||||
|
"restore_tip_title": "恢复确认",
|
||||||
"retain_admin_permissions": "保留管理员权限",
|
"retain_admin_permissions": "保留管理员权限",
|
||||||
"search_member_group_name": "搜索成员/群组名称",
|
"search_member_group_name": "搜索成员/群组名称",
|
||||||
"total_team_members": "共 {{amount}} 名成员",
|
"total_team_members": "共 {{amount}} 名成员",
|
||||||
@@ -38,5 +39,17 @@
|
|||||||
"waiting": "待接受",
|
"waiting": "待接受",
|
||||||
"sync_immediately": "立即同步",
|
"sync_immediately": "立即同步",
|
||||||
"sync_member_failed": "同步成员失败",
|
"sync_member_failed": "同步成员失败",
|
||||||
"sync_member_success": "同步成员成功"
|
"sync_member_success": "同步成员成功",
|
||||||
|
"contact": "联系方式",
|
||||||
|
"join_update_time": "加入/更新时间",
|
||||||
|
"leave": "已离职",
|
||||||
|
"search_member": "搜索成员",
|
||||||
|
"export_members": "导出成员",
|
||||||
|
"delete_from_team": "移出团队",
|
||||||
|
"delete_from_org": "移出部门",
|
||||||
|
"confirm_delete_from_team": "确认将 {{username}} 移出团队?",
|
||||||
|
"confirm_delete_from_org": "确认将 {{username}} 移出部门?",
|
||||||
|
"search_org": "搜索部门",
|
||||||
|
"notification_recieve": "团队通知接收",
|
||||||
|
"set_name_avatar": "团队头像 & 团队名"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,9 @@
|
|||||||
"Move": "移动",
|
"Move": "移动",
|
||||||
"Name": "名称",
|
"Name": "名称",
|
||||||
"None": "无",
|
"None": "无",
|
||||||
|
"Operation": "操作",
|
||||||
"Rename": "重命名",
|
"Rename": "重命名",
|
||||||
|
"Required_input": "必填",
|
||||||
"Resume": "恢复",
|
"Resume": "恢复",
|
||||||
"Running": "运行中",
|
"Running": "运行中",
|
||||||
"Select_all": "全选",
|
"Select_all": "全选",
|
||||||
@@ -127,7 +129,6 @@
|
|||||||
"common.Copy Successful": "复制成功",
|
"common.Copy Successful": "复制成功",
|
||||||
"common.Copy_failed": "复制失败,请手动复制",
|
"common.Copy_failed": "复制失败,请手动复制",
|
||||||
"common.Create Failed": "创建异常",
|
"common.Create Failed": "创建异常",
|
||||||
"common.Create New": "新建",
|
|
||||||
"common.Create Success": "创建成功",
|
"common.Create Success": "创建成功",
|
||||||
"common.Create Time": "创建时间",
|
"common.Create Time": "创建时间",
|
||||||
"common.Creating": "创建中",
|
"common.Creating": "创建中",
|
||||||
@@ -169,7 +170,6 @@
|
|||||||
"common.Not open": "未开启",
|
"common.Not open": "未开启",
|
||||||
"common.OK": "好的",
|
"common.OK": "好的",
|
||||||
"common.Open": "打开",
|
"common.Open": "打开",
|
||||||
"Operation": "操作",
|
|
||||||
"common.Other": "其他",
|
"common.Other": "其他",
|
||||||
"common.Output": "输出",
|
"common.Output": "输出",
|
||||||
"common.Params": "参数",
|
"common.Params": "参数",
|
||||||
@@ -183,7 +183,6 @@
|
|||||||
"common.Remove": "移除",
|
"common.Remove": "移除",
|
||||||
"common.Rename": "重命名",
|
"common.Rename": "重命名",
|
||||||
"common.Request Error": "请求异常",
|
"common.Request Error": "请求异常",
|
||||||
"Required_input": "必填",
|
|
||||||
"common.Reset": "恢复默认",
|
"common.Reset": "恢复默认",
|
||||||
"common.Restart": "重新开始",
|
"common.Restart": "重新开始",
|
||||||
"common.Role": "权限",
|
"common.Role": "权限",
|
||||||
@@ -372,7 +371,7 @@
|
|||||||
"core.app.tip.Add a intro to app": "快来给应用一个介绍~",
|
"core.app.tip.Add a intro to app": "快来给应用一个介绍~",
|
||||||
"core.app.tip.chatNodeSystemPromptTip": "在此输入提示词",
|
"core.app.tip.chatNodeSystemPromptTip": "在此输入提示词",
|
||||||
"core.app.tip.systemPromptTip": "模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可通过输入 / 插入选择变量\n如果关联了知识库,你还可以通过适当的描述,来引导模型何时去调用知识库搜索。例如:\n你是电影《星际穿越》的助手,当用户询问与《星际穿越》相关的内容时,请搜索知识库并结合搜索结果进行回答。",
|
"core.app.tip.systemPromptTip": "模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可通过输入 / 插入选择变量\n如果关联了知识库,你还可以通过适当的描述,来引导模型何时去调用知识库搜索。例如:\n你是电影《星际穿越》的助手,当用户询问与《星际穿越》相关的内容时,请搜索知识库并结合搜索结果进行回答。",
|
||||||
"core.app.tip.variableTip": "可以在对话开始前,要求用户填写一些内容作为本轮对话的特定变量。该模块位于开场引导之后。\n变量可以通过 {{变量key}} 的形式注入到其他模块 string 类型的输入中,例如:提示词、限定词等",
|
"core.app.tip.variableTip": "可以在对话开始前,要求用户填写一些内容作为本轮对话的特定变量。该模块位于开场引导之后。\n输入框中,可通过 / 激活变量选择,例如:提示词、限定词等",
|
||||||
"core.app.tip.welcomeTextTip": "每次对话开始前,发送一个初始内容。支持标准 Markdown 语法,可使用的额外标记:\n[快捷按键]:用户点击后可以直接发送该问题",
|
"core.app.tip.welcomeTextTip": "每次对话开始前,发送一个初始内容。支持标准 Markdown 语法,可使用的额外标记:\n[快捷按键]:用户点击后可以直接发送该问题",
|
||||||
"core.app.tool_label.doc": "使用文档",
|
"core.app.tool_label.doc": "使用文档",
|
||||||
"core.app.tool_label.github": "GitHub地址",
|
"core.app.tool_label.github": "GitHub地址",
|
||||||
@@ -550,7 +549,6 @@
|
|||||||
"core.dataset.data.Main Content": "主要内容",
|
"core.dataset.data.Main Content": "主要内容",
|
||||||
"core.dataset.data.Search data placeholder": "搜索相关数据",
|
"core.dataset.data.Search data placeholder": "搜索相关数据",
|
||||||
"core.dataset.data.Too Long": "总长度超长了",
|
"core.dataset.data.Too Long": "总长度超长了",
|
||||||
"core.dataset.data.Total Amount": "{{total}} 组",
|
|
||||||
"core.dataset.data.group": "组",
|
"core.dataset.data.group": "组",
|
||||||
"core.dataset.data.unit": "条",
|
"core.dataset.data.unit": "条",
|
||||||
"core.dataset.embedding model tip": "索引模型可以将自然语言转成向量,用于进行语义检索。\n注意,不同索引模型无法一起使用,选择完索引模型后将无法修改。",
|
"core.dataset.embedding model tip": "索引模型可以将自然语言转成向量,用于进行语义检索。\n注意,不同索引模型无法一起使用,选择完索引模型后将无法修改。",
|
||||||
@@ -863,7 +861,6 @@
|
|||||||
"dataset.collections.Collection Embedding": "{{total}} 组索引中",
|
"dataset.collections.Collection Embedding": "{{total}} 组索引中",
|
||||||
"dataset.collections.Confirm to delete the folder": "确认删除该文件夹及里面所有内容?",
|
"dataset.collections.Confirm to delete the folder": "确认删除该文件夹及里面所有内容?",
|
||||||
"dataset.collections.Create And Import": "新建/导入",
|
"dataset.collections.Create And Import": "新建/导入",
|
||||||
"dataset.collections.Data Amount": "数据总量",
|
|
||||||
"dataset.collections.Select Collection": "选择文件",
|
"dataset.collections.Select Collection": "选择文件",
|
||||||
"dataset.collections.Select One Collection To Store": "选择一个文件进行存储",
|
"dataset.collections.Select One Collection To Store": "选择一个文件进行存储",
|
||||||
"dataset.data.Can not edit": "无编辑权限",
|
"dataset.data.Can not edit": "无编辑权限",
|
||||||
@@ -879,8 +876,10 @@
|
|||||||
"dataset.dataset_name": "知识库名称",
|
"dataset.dataset_name": "知识库名称",
|
||||||
"dataset.deleteFolderTips": "确认删除该文件夹及其包含的所有知识库?删除后数据无法恢复,请确认!",
|
"dataset.deleteFolderTips": "确认删除该文件夹及其包含的所有知识库?删除后数据无法恢复,请确认!",
|
||||||
"dataset.test.noResult": "搜索结果为空",
|
"dataset.test.noResult": "搜索结果为空",
|
||||||
|
"dataset_text_model_tip": "用于知识库预处理阶段的文本处理,例如自动补充索引、问答对提取。",
|
||||||
"deep_rag_search": "深度搜索",
|
"deep_rag_search": "深度搜索",
|
||||||
"delete_api": "确认删除该API密钥?删除后该密钥立即失效,对应的对话日志不会删除,请确认!",
|
"delete_api": "确认删除该API密钥?删除后该密钥立即失效,对应的对话日志不会删除,请确认!",
|
||||||
|
"embedding_model_not_config": "检测到没有可用的索引模型",
|
||||||
"error.Create failed": "创建失败",
|
"error.Create failed": "创建失败",
|
||||||
"error.code_error": "验证码错误",
|
"error.code_error": "验证码错误",
|
||||||
"error.fileNotFound": "文件找不到了~",
|
"error.fileNotFound": "文件找不到了~",
|
||||||
@@ -917,6 +916,7 @@
|
|||||||
"item_name": "字段名",
|
"item_name": "字段名",
|
||||||
"just_now": "刚刚",
|
"just_now": "刚刚",
|
||||||
"key_repetition": "key 重复",
|
"key_repetition": "key 重复",
|
||||||
|
"llm_model_not_config": "检测到没有可用的语言模型",
|
||||||
"max_quote_tokens": "引用上限",
|
"max_quote_tokens": "引用上限",
|
||||||
"max_quote_tokens_tips": "单次搜索最大的 token 数量,中文约 1 字=1.7 tokens,英文约 1 字=1 token",
|
"max_quote_tokens_tips": "单次搜索最大的 token 数量,中文约 1 字=1.7 tokens,英文约 1 字=1 token",
|
||||||
"min_similarity": "最低相关度",
|
"min_similarity": "最低相关度",
|
||||||
@@ -942,6 +942,7 @@
|
|||||||
"model_moka": "Moka-AI",
|
"model_moka": "Moka-AI",
|
||||||
"model_moonshot": "月之暗面",
|
"model_moonshot": "月之暗面",
|
||||||
"model_other": "其他",
|
"model_other": "其他",
|
||||||
|
"model_ppio": "PPIO 派欧云",
|
||||||
"model_qwen": "阿里千问",
|
"model_qwen": "阿里千问",
|
||||||
"model_siliconflow": "硅基流动",
|
"model_siliconflow": "硅基流动",
|
||||||
"model_sparkdesk": "讯飞星火",
|
"model_sparkdesk": "讯飞星火",
|
||||||
@@ -1013,6 +1014,7 @@
|
|||||||
"plugin.go to laf": "去编写",
|
"plugin.go to laf": "去编写",
|
||||||
"plugin.path": "路径",
|
"plugin.path": "路径",
|
||||||
"prompt_input_placeholder": "请输入提示词",
|
"prompt_input_placeholder": "请输入提示词",
|
||||||
|
"question_feedback": "工单咨询",
|
||||||
"read_quote": "查看引用",
|
"read_quote": "查看引用",
|
||||||
"required": "必须",
|
"required": "必须",
|
||||||
"resume_failed": "恢复失败",
|
"resume_failed": "恢复失败",
|
||||||
@@ -1182,7 +1184,6 @@
|
|||||||
"support.wallet.usage.Audio Speech": "语音播放",
|
"support.wallet.usage.Audio Speech": "语音播放",
|
||||||
"support.wallet.usage.Bill Module": "扣费模块",
|
"support.wallet.usage.Bill Module": "扣费模块",
|
||||||
"support.wallet.usage.Duration": "时长(秒)",
|
"support.wallet.usage.Duration": "时长(秒)",
|
||||||
"support.wallet.usage.Extension result": "问题优化结果",
|
|
||||||
"support.wallet.usage.Module name": "模块名",
|
"support.wallet.usage.Module name": "模块名",
|
||||||
"support.wallet.usage.Source": "来源",
|
"support.wallet.usage.Source": "来源",
|
||||||
"support.wallet.usage.Text Length": "文本长度",
|
"support.wallet.usage.Text Length": "文本长度",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"close_auto_sync": "确认关闭自动同步功能?",
|
"close_auto_sync": "确认关闭自动同步功能?",
|
||||||
"collection.Create update time": "创建/更新时间",
|
"collection.Create update time": "创建/更新时间",
|
||||||
"collection.Training type": "训练模式",
|
"collection.Training type": "训练模式",
|
||||||
|
"collection_data_count": "数据量",
|
||||||
"collection_not_support_retraining": "该集合类型不支持重新调整参数",
|
"collection_not_support_retraining": "该集合类型不支持重新调整参数",
|
||||||
"collection_not_support_sync": "该集合不支持同步",
|
"collection_not_support_sync": "该集合不支持同步",
|
||||||
"collection_sync": "立即同步",
|
"collection_sync": "立即同步",
|
||||||
@@ -20,6 +21,7 @@
|
|||||||
"custom_data_process_params": "自定义",
|
"custom_data_process_params": "自定义",
|
||||||
"custom_data_process_params_desc": "自定义设置数据处理规则",
|
"custom_data_process_params_desc": "自定义设置数据处理规则",
|
||||||
"data.ideal_chunk_length": "理想分块长度",
|
"data.ideal_chunk_length": "理想分块长度",
|
||||||
|
"data_amount": "{{dataAmount}} 组数据, {{indexAmount}} 组索引",
|
||||||
"data_process_params": "处理参数",
|
"data_process_params": "处理参数",
|
||||||
"data_process_setting": "数据处理配置",
|
"data_process_setting": "数据处理配置",
|
||||||
"dataset.Unsupported operation": "操作不支持",
|
"dataset.Unsupported operation": "操作不支持",
|
||||||
|
|||||||