Compare commits
39 Commits
v4.9.1-fix
...
v4.9.2-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6efd3318d | ||
|
|
95ffd710aa | ||
|
|
097bb97417 | ||
|
|
4faea8d2b8 | ||
|
|
7ecadb33d1 | ||
|
|
ce61bda223 | ||
|
|
8dba01da73 | ||
|
|
dcdad6fa39 | ||
|
|
11d080d521 | ||
|
|
2f954d2f3f | ||
|
|
a956fbca73 | ||
|
|
db7510c5eb | ||
|
|
b87cc353da | ||
|
|
ff85121546 | ||
|
|
79f9d83349 | ||
|
|
159bf17369 | ||
|
|
4512b23d4d | ||
|
|
5300ddf654 | ||
|
|
f1f0dfc691 | ||
|
|
e5acec8dc7 | ||
|
|
cb832b6305 | ||
|
|
ae9b8a2b8e | ||
|
|
d209255015 | ||
|
|
6eae841e4a | ||
|
|
75c1631670 | ||
|
|
97a182c7fd | ||
|
|
a0ad450032 | ||
|
|
74b36219e1 | ||
|
|
fc23db745c | ||
|
|
8a68de6471 | ||
|
|
1c4e0c66d5 | ||
|
|
6dcdd540b9 | ||
|
|
48233c7d55 | ||
|
|
f3ef56998d | ||
|
|
7e7269b2ba | ||
|
|
606e9505c0 | ||
|
|
1db39e8907 | ||
|
|
7f13eb4642 | ||
|
|
9a1fff74fd |
@@ -114,15 +114,15 @@ services:
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.1 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.1 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.1-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.1-fix2 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.1 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.1 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.1-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.1-fix2 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
|
||||
@@ -72,15 +72,15 @@ services:
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.1 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.1 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.1-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.1-fix2 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.1 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.1 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.1-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.1-fix2 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
|
||||
@@ -53,15 +53,15 @@ services:
|
||||
wait $$!
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.1 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.1 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.1-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.1-fix2 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.1 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.1 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.1-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.1-fix2 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
|
||||
BIN
docSite/assets/imgs/Ollama-aiproxy1.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
docSite/assets/imgs/Ollama-aiproxy2.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
docSite/assets/imgs/Ollama-aiproxy3.png
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
docSite/assets/imgs/Ollama-direct1.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
docSite/assets/imgs/Ollama-models1.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
docSite/assets/imgs/Ollama-models2.png
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
docSite/assets/imgs/Ollama-models3.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
docSite/assets/imgs/Ollama-models4.png
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
docSite/assets/imgs/Ollama-oneapi1.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
docSite/assets/imgs/Ollama-oneapi2.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
docSite/assets/imgs/Ollama-oneapi3 .png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
docSite/assets/imgs/Ollama-pull.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
184
docSite/content/zh-cn/docs/development/custom-models/ollama.md
Normal file
@@ -0,0 +1,184 @@
|
||||
---
|
||||
title: '使用 Ollama 接入本地模型 '
|
||||
description: ' 采用 Ollama 部署自己的模型'
|
||||
icon: 'api'
|
||||
draft: false
|
||||
toc: true
|
||||
weight: 950
|
||||
---
|
||||
|
||||
[Ollama](https://ollama.com/) 是一个开源的AI大模型部署工具,专注于简化大语言模型的部署和使用,支持一键下载和运行各种大模型。
|
||||
|
||||
## 安装 Ollama
|
||||
|
||||
Ollama 本身支持多种安装方式,但是推荐使用 Docker 拉取镜像部署。如果是个人设备上安装了 Ollama 后续需要解决如何让 Docker 中 FastGPT 容器访问宿主机 Ollama的问题,较为麻烦。
|
||||
|
||||
### Docker 安装(推荐)
|
||||
|
||||
你可以使用 Ollama 官方的 Docker 镜像来一键安装和启动 Ollama 服务(确保你的机器上已经安装了 Docker),命令如下:
|
||||
|
||||
```bash
|
||||
docker pull ollama/ollama
|
||||
docker run --rm -d --name ollama -p 11434:11434 ollama/ollama
|
||||
```
|
||||
|
||||
如果你的 FastGPT 是在 Docker 中进行部署的,建议在拉取 Ollama 镜像时保证和 FastGPT 镜像处于同一网络,否则可能出现 FastGPT 无法访问的问题,命令如下:
|
||||
|
||||
```bash
|
||||
docker run --rm -d --name ollama --network (你的 Fastgpt 容器所在网络) -p 11434:11434 ollama/ollama
|
||||
```
|
||||
|
||||
### 主机安装
|
||||
|
||||
如果你不想使用 Docker ,也可以采用主机安装,以下是主机安装的一些方式。
|
||||
|
||||
#### MacOS
|
||||
|
||||
如果你使用的是 macOS,且系统中已经安装了 Homebrew 包管理器,可通过以下命令来安装 Ollama:
|
||||
|
||||
```bash
|
||||
brew install ollama
|
||||
ollama serve #安装完成后,使用该命令启动服务
|
||||
```
|
||||
|
||||
#### Linux
|
||||
|
||||
在 Linux 系统上,你可以借助包管理器来安装 Ollama。以 Ubuntu 为例,在终端执行以下命令:
|
||||
|
||||
```bash
|
||||
curl https://ollama.com/install.sh | sh #此命令会从官方网站下载并执行安装脚本。
|
||||
ollama serve #安装完成后,同样启动服务
|
||||
```
|
||||
|
||||
#### Windows
|
||||
|
||||
在 Windows 系统中,你可以从 Ollama 官方网站 下载 Windows 版本的安装程序。下载完成后,运行安装程序,按照安装向导的提示完成安装。安装完成后,在命令提示符或 PowerShell 中启动服务:
|
||||
|
||||
```bash
|
||||
ollama serve #安装完成并启动服务后,你可以在浏览器中访问 http://localhost:11434 来验证 Ollama 是否安装成功。
|
||||
```
|
||||
|
||||
#### 补充说明
|
||||
|
||||
如果你是采用的主机应用 Ollama 而不是镜像,需要确保你的 Ollama 可以监听0.0.0.0。
|
||||
|
||||
##### 1. Linxu 系统
|
||||
|
||||
如果 Ollama 作为 systemd 服务运行,打开终端,编辑 Ollama 的 systemd 服务文件,使用命令sudo systemctl edit ollama.service,在[Service]部分添加Environment="OLLAMA_HOST=0.0.0.0"。保存并退出编辑器,然后执行sudo systemctl daemon - reload和sudo systemctl restart ollama使配置生效。
|
||||
|
||||
##### 2. MacOS 系统
|
||||
|
||||
打开终端,使用launchctl setenv ollama_host "0.0.0.0"命令设置环境变量,然后重启 Ollama 应用程序以使更改生效。
|
||||
|
||||
##### 3. Windows 系统
|
||||
|
||||
通过 “开始” 菜单或搜索栏打开 “编辑系统环境变量”,在 “系统属性” 窗口中点击 “环境变量”,在 “系统变量” 部分点击 “新建”,创建一个名为OLLAMA_HOST的变量,变量值设置为0.0.0.0,点击 “确定” 保存更改,最后从 “开始” 菜单重启 Ollama 应用程序。
|
||||
|
||||
### Ollama 拉取模型镜像
|
||||
|
||||
在安装后 Ollama 后,本地是没有模型镜像的,需要自己去拉取 Ollama 中的模型镜像。命令如下:
|
||||
|
||||
```bash
|
||||
# Docker 部署需要先进容器,命令为: docker exec -it < Ollama 容器名 > /bin/sh
|
||||
ollama pull <模型名>
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
### 测试通信
|
||||
|
||||
在安装完成后,需要进行检测测试,首先进入 FastGPT 所在的容器,尝试访问自己的 Ollama ,命令如下:
|
||||
|
||||
```bash
|
||||
docker exec -it < FastGPT 所在的容器名 > /bin/sh
|
||||
curl http://XXX.XXX.XXX.XXX:11434 #容器部署地址为“http://<容器名>:<端口>”,主机安装地址为"http://<主机IP>:<端口>",主机IP不可为localhost
|
||||
```
|
||||
|
||||
看到访问显示自己的 Ollama 服务以及启动,说明可以正常通信。
|
||||
|
||||
## 将 Ollama 接入 FastGPT
|
||||
|
||||
### 1. 查看 Ollama 所拥有的模型
|
||||
|
||||
首先采用下述命令查看 Ollama 中所拥有的模型,
|
||||
|
||||
```bash
|
||||
# Docker 部署 Ollama,需要此命令 docker exec -it < Ollama 容器名 > /bin/sh
|
||||
ollama ls
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 2. AI Proxy 接入
|
||||
|
||||
如果你采用的是 FastGPT 中的默认配置文件部署[这里](/docs/development/docker.md),即默认采用 AI Proxy 进行启动。
|
||||
|
||||

|
||||
|
||||
以及在确保你的 FastGPT 可以直接访问 Ollama 容器的情况下,无法访问,参考上文[点此跳转](#安装-ollama)的安装过程,检测是不是主机不能监测0.0.0.0,或者容器不在同一个网络。
|
||||
|
||||

|
||||
|
||||
在 FastGPT 中点击账号->模型提供商->模型配置->新增模型,添加自己的模型即可,添加模型时需要保证模型ID和 OneAPI 中的模型名称一致。详细参考[这里](/docs/development/modelConfig/intro.md)
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
运行 FastGPT ,在页面中选择账号->模型提供商->模型渠道->新增渠道。之后,在渠道选择中选择 Ollama ,然后加入自己拉取的模型,填入代理地址,如果是容器中安装 Ollama ,代理地址为http://地址:端口,补充:容器部署地址为“http://<容器名>:<端口>”,主机安装地址为"http://<主机IP>:<端口>",主机IP不可为localhost
|
||||
|
||||

|
||||
|
||||
在工作台中创建一个应用,选择自己之前添加的模型,此处模型名称为自己当时设置的别名。注:同一个模型无法多次添加,系统会采取最新添加时设置的别名。
|
||||
|
||||

|
||||
|
||||
### 3. OneAPI 接入
|
||||
|
||||
如果你想使用 OneAPI ,首先需要拉取 OneAPI 镜像,然后将其在 FastGPT 容器的网络中运行。具体命令如下:
|
||||
|
||||
```bash
|
||||
# 拉取 oneAPI 镜像
|
||||
docker pull intel/oneapi-hpckit
|
||||
|
||||
# 运行容器并指定自定义网络和容器名
|
||||
docker run -it --network < FastGPT 网络 > --name 容器名 intel/oneapi-hpckit /bin/bash
|
||||
```
|
||||
|
||||
进入 OneAPI 页面,添加新的渠道,类型选择 Ollama ,在模型中填入自己 Ollama 中的模型,需要保证添加的模型名称和 Ollama 中一致,再在下方填入自己的 Ollama 代理地址,默认http://地址:端口,不需要填写/v1。添加成功后在 OneAPI 进行渠道测试,测试成功则说明添加成功。此处演示采用的是 Docker 部署 Ollama 的效果,主机 Ollama需要修改代理地址为http://<主机IP>:<端口>
|
||||
|
||||

|
||||
|
||||
渠道添加成功后,点击令牌,点击添加令牌,填写名称,修改配置。
|
||||
|
||||

|
||||
|
||||
修改部署 FastGPT 的 docker-compose.yml 文件,在其中将 AI Proxy 的使用注释,在 OPENAI_BASE_URL 中加入自己的 OneAPI 开放地址,默认是http://地址:端口/v1,v1必须填写。KEY 中填写自己在 OneAPI 的令牌。
|
||||
|
||||

|
||||
|
||||
[直接跳转5](#5-模型添加和使用)添加模型,并使用。
|
||||
|
||||
### 4. 直接接入
|
||||
|
||||
如果你既不想使用 AI Proxy,也不想使用 OneAPI,也可以选择直接接入,修改部署 FastGPT 的 docker-compose.yml 文件,在其中将 AI Proxy 的使用注释,采用和 OneAPI 的类似配置。注释掉 AIProxy 相关代码,在OPENAI_BASE_URL中加入自己的 Ollama 开放地址,默认是http://地址:端口/v1,强调:v1必须填写。在KEY中随便填入,因为 Ollama 默认没有鉴权,如果开启鉴权,请自行填写。其他操作和在 OneAPI 中加入 Ollama 一致,只需在 FastGPT 中加入自己的模型即可使用。此处演示采用的是 Docker 部署 Ollama 的效果,主机 Ollama需要修改代理地址为http://<主机IP>:<端口>
|
||||
|
||||

|
||||
|
||||
完成后[点击这里](#5-模型添加和使用)进行模型添加并使用。
|
||||
|
||||
### 5. 模型添加和使用
|
||||
|
||||
在 FastGPT 中点击账号->模型提供商->模型配置->新增模型,添加自己的模型即可,添加模型时需要保证模型ID和 OneAPI 中的模型名称一致。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
在工作台中创建一个应用,选择自己之前添加的模型,此处模型名称为自己当时设置的别名。注:同一个模型无法多次添加,系统会采取最新添加时设置的别名。
|
||||
|
||||

|
||||
|
||||
### 6. 补充
|
||||
上述接入 Ollama 的代理地址中,主机安装 Ollama 的地址为“http://<主机IP>:<端口>”,容器部署 Ollama 地址为“http://<容器名>:<端口>”
|
||||
@@ -11,8 +11,6 @@ weight: 853
|
||||
| --------------------- | --------------------- |
|
||||
|  |  |
|
||||
|
||||
|
||||
|
||||
## 创建训练订单
|
||||
|
||||
{{< tabs tabTotal="2" >}}
|
||||
@@ -289,7 +287,7 @@ curl --location --request DELETE 'http://localhost:3000/api/core/dataset/delete?
|
||||
|
||||
## 集合
|
||||
|
||||
### 通用创建参数说明
|
||||
### 通用创建参数说明(必看)
|
||||
|
||||
**入参**
|
||||
|
||||
@@ -300,8 +298,11 @@ curl --location --request DELETE 'http://localhost:3000/api/core/dataset/delete?
|
||||
| trainingType | 数据处理方式。chunk: 按文本长度进行分割;qa: 问答对提取 | ✅ |
|
||||
| autoIndexes | 是否自动生成索引(仅商业版支持) | |
|
||||
| imageIndex | 是否自动生成图片索引(仅商业版支持) | |
|
||||
| chunkSize | 预估块大小 | |
|
||||
| chunkSplitter | 自定义最高优先分割符号 | |
|
||||
| chunkSettingMode | 分块参数模式。auto: 系统默认参数; custom: 手动指定参数 | |
|
||||
| chunkSplitMode | 分块拆分模式。size: 按长度拆分; char: 按字符拆分。chunkSettingMode=auto时不生效。 | |
|
||||
| chunkSize | 分块大小,默认 1500。chunkSettingMode=auto时不生效。 | |
|
||||
| indexSize | 索引大小,默认 512,必须小于索引模型最大token。chunkSettingMode=auto时不生效。 | |
|
||||
| chunkSplitter | 自定义最高优先分割符号,除非超出文件处理最大上下文,否则不会进行进一步拆分。chunkSettingMode=auto时不生效。 | |
|
||||
| qaPrompt | qa拆分提示词 | |
|
||||
| tags | 集合标签(字符串数组) | |
|
||||
| createTime | 文件创建时间(Date / String) | |
|
||||
@@ -389,9 +390,8 @@ curl --location --request POST 'http://localhost:3000/api/core/dataset/collectio
|
||||
"name":"测试训练",
|
||||
|
||||
"trainingType": "qa",
|
||||
"chunkSize":8000,
|
||||
"chunkSplitter":"",
|
||||
"qaPrompt":"11",
|
||||
"chunkSettingMode": "auto",
|
||||
"qaPrompt":"",
|
||||
|
||||
"metadata":{}
|
||||
}'
|
||||
@@ -409,10 +409,6 @@ curl --location --request POST 'http://localhost:3000/api/core/dataset/collectio
|
||||
- parentId: 父级ID,不填则默认为根目录
|
||||
- name: 集合名称(必填)
|
||||
- metadata: 元数据(暂时没啥用)
|
||||
- trainingType: 训练模式(必填)
|
||||
- chunkSize: 每个 chunk 的长度(可选). chunk模式:100~3000; qa模式: 4000~模型最大token(16k模型通常建议不超过10000)
|
||||
- chunkSplitter: 自定义最高优先分割符号(可选)
|
||||
- qaPrompt: qa拆分自定义提示词(可选)
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
@@ -462,8 +458,7 @@ curl --location --request POST 'http://localhost:3000/api/core/dataset/collectio
|
||||
"parentId": null,
|
||||
|
||||
"trainingType": "chunk",
|
||||
"chunkSize":512,
|
||||
"chunkSplitter":"",
|
||||
"chunkSettingMode": "auto",
|
||||
"qaPrompt":"",
|
||||
|
||||
"metadata":{
|
||||
@@ -483,10 +478,6 @@ curl --location --request POST 'http://localhost:3000/api/core/dataset/collectio
|
||||
- datasetId: 知识库的ID(必填)
|
||||
- parentId: 父级ID,不填则默认为根目录
|
||||
- metadata.webPageSelector: 网页选择器,用于指定网页中的哪个元素作为文本(可选)
|
||||
- trainingType:训练模式(必填)
|
||||
- chunkSize: 每个 chunk 的长度(可选). chunk模式:100~3000; qa模式: 4000~模型最大token(16k模型通常建议不超过10000)
|
||||
- chunkSplitter: 自定义最高优先分割符号(可选)
|
||||
- qaPrompt: qa拆分自定义提示词(可选)
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
@@ -545,13 +536,7 @@ curl --location --request POST 'http://localhost:3000/api/core/dataset/collectio
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- file: 文件
|
||||
- data: 知识库相关信息(json序列化后传入)
|
||||
- datasetId: 知识库的ID(必填)
|
||||
- parentId: 父级ID,不填则默认为根目录
|
||||
- trainingType:训练模式(必填)
|
||||
- chunkSize: 每个 chunk 的长度(可选). chunk模式:100~3000; qa模式: 4000~模型最大token(16k模型通常建议不超过10000)
|
||||
- chunkSplitter: 自定义最高优先分割符号(可选)
|
||||
- qaPrompt: qa拆分自定义提示词(可选)
|
||||
- data: 知识库相关信息(json序列化后传入),参数说明见上方“通用创建参数说明”
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
|
||||
@@ -13,8 +13,8 @@ weight: 799
|
||||
|
||||
### 2. 更新镜像
|
||||
|
||||
- 更新 FastGPT 镜像 tag: v4.9.1
|
||||
- 更新 FastGPT 商业版镜像 tag: v4.9.1
|
||||
- 更新 FastGPT 镜像 tag: v4.9.1-fix2
|
||||
- 更新 FastGPT 商业版镜像 tag: v4.9.1-fix2
|
||||
- Sandbox 镜像,可以不更新
|
||||
- AIProxy 镜像修改为: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.3
|
||||
|
||||
|
||||
44
docSite/content/zh-cn/docs/development/upgrading/492.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: 'V4.9.2(进行中)'
|
||||
description: 'FastGPT V4.9.2 更新说明'
|
||||
icon: 'upgrade'
|
||||
draft: false
|
||||
toc: true
|
||||
weight: 799
|
||||
---
|
||||
|
||||
## 重要提示
|
||||
|
||||
- 知识库导入数据 API 变更,增加`chunkSettingMode`,`chunkSplitMode`,`indexSize`可选参数,具体可参考 [知识库导入数据 API](/docs/development/openapi/dataset) 文档。
|
||||
|
||||
|
||||
## 🚀 新增内容
|
||||
|
||||
1. 知识库分块优化:支持单独配置分块大小和索引大小,允许进行超大分块,以更大的输入 Tokens 换取完整分块。
|
||||
2. 知识库分块增加自定义分隔符预设值,同时支持自定义换行符分割。
|
||||
3. 外部变量改名:自定义变量。 并且支持在测试时调试,在分享链接中,该变量直接隐藏。
|
||||
4. 集合同步时,支持同步修改标题。
|
||||
5. 团队成员管理重构,抽离主流 IM SSO(企微、飞书、钉钉),并支持通过自定义 SSO 接入 FastGPT。同时完善与外部系统的成员同步。
|
||||
|
||||
## ⚙️ 优化
|
||||
|
||||
1. 导出对话日志时,支持导出成员名。
|
||||
2. 邀请链接交互。
|
||||
3. 无 SSL 证书时复制失败,会提示弹窗用于手动复制。
|
||||
4. FastGPT 未内置 ai proxy 渠道时,也能正常展示其名称。
|
||||
5. 升级 nextjs 版本至 14.2.25。
|
||||
6. 工作流节点数组字符串类型,自动适配 string 输入。
|
||||
7. 工作流节点数组类型,自动进行 JSON parse 解析 string 输入。
|
||||
8. AI proxy 日志优化,去除重试失败的日志,仅保留最后一份错误日志。
|
||||
9. 分块算法小调整:
|
||||
* 跨处理符号之间连续性更强。
|
||||
* 代码块分割时,用 LLM 模型上下文作为分块大小,尽可能保证代码块完整性。
|
||||
* 表格分割时,用 LLM 模型上下文作为分块大小,尽可能保证表格完整性。
|
||||
|
||||
## 🐛 修复
|
||||
|
||||
1. 飞书和语雀知识库无法同步。
|
||||
2. 渠道测试时,如果配置了模型自定义请求地址,会走自定义请求地址,而不是渠道请求地址。
|
||||
3. 语音识别模型测试未启用的模型时,无法正常测试。
|
||||
4. 管理员配置系统插件时,如果插件包含其他系统应用,无法正常鉴权。
|
||||
5. 移除 TTS 自定义请求地址时,必须需要填 requestAuth 字段。
|
||||
@@ -124,6 +124,7 @@ curl --location --request GET '{{baseURL}}/v1/file/content?id=xx' \
|
||||
"success": true,
|
||||
"message": "",
|
||||
"data": {
|
||||
"title": "文档标题",
|
||||
"content": "FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景!\n",
|
||||
"previewUrl": "xxxx"
|
||||
}
|
||||
@@ -131,10 +132,13 @@ curl --location --request GET '{{baseURL}}/v1/file/content?id=xx' \
|
||||
```
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
二选一返回,如果同时返回则 content 优先级更高。
|
||||
|
||||
- title - 文件标题。
|
||||
- content - 文件内容,直接拿来用。
|
||||
- previewUrl - 文件链接,系统会请求该地址获取文件内容。
|
||||
|
||||
`content`和`previewUrl`二选一返回,如果同时返回则 `content` 优先级更高,返回 `previewUrl`时,则会访问该链接进行文档内容读取。
|
||||
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { defaultMaxChunkSize } from '../../core/dataset/training/utils';
|
||||
import { getErrText } from '../error/utils';
|
||||
import { replaceRegChars } from './tools';
|
||||
|
||||
export const CUSTOM_SPLIT_SIGN = '-----CUSTOM_SPLIT_SIGN-----';
|
||||
|
||||
type SplitProps = {
|
||||
text: string;
|
||||
chunkLen: number;
|
||||
chunkSize: number;
|
||||
maxSize?: number;
|
||||
overlapRatio?: number;
|
||||
customReg?: string[];
|
||||
};
|
||||
export type TextSplitProps = Omit<SplitProps, 'text' | 'chunkLen'> & {
|
||||
chunkLen?: number;
|
||||
export type TextSplitProps = Omit<SplitProps, 'text' | 'chunkSize'> & {
|
||||
chunkSize?: number;
|
||||
};
|
||||
|
||||
type SplitResponse = {
|
||||
@@ -56,7 +57,7 @@ const strIsMdTable = (str: string) => {
|
||||
return true;
|
||||
};
|
||||
const markdownTableSplit = (props: SplitProps): SplitResponse => {
|
||||
let { text = '', chunkLen } = props;
|
||||
let { text = '', chunkSize } = props;
|
||||
const splitText2Lines = text.split('\n');
|
||||
const header = splitText2Lines[0];
|
||||
const headerSize = header.split('|').length - 2;
|
||||
@@ -72,7 +73,7 @@ ${mdSplitString}
|
||||
`;
|
||||
|
||||
for (let i = 2; i < splitText2Lines.length; i++) {
|
||||
if (chunk.length + splitText2Lines[i].length > chunkLen * 1.2) {
|
||||
if (chunk.length + splitText2Lines[i].length > chunkSize * 1.2) {
|
||||
chunks.push(chunk);
|
||||
chunk = `${header}
|
||||
${mdSplitString}
|
||||
@@ -99,11 +100,17 @@ ${mdSplitString}
|
||||
5. 标点分割:重叠
|
||||
*/
|
||||
const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
let { text = '', chunkLen, overlapRatio = 0.15, customReg = [] } = props;
|
||||
let {
|
||||
text = '',
|
||||
chunkSize,
|
||||
maxSize = defaultMaxChunkSize,
|
||||
overlapRatio = 0.15,
|
||||
customReg = []
|
||||
} = props;
|
||||
|
||||
const splitMarker = 'SPLIT_HERE_SPLIT_HERE';
|
||||
const codeBlockMarker = 'CODE_BLOCK_LINE_MARKER';
|
||||
const overlapLen = Math.round(chunkLen * overlapRatio);
|
||||
const overlapLen = Math.round(chunkSize * overlapRatio);
|
||||
|
||||
// replace code block all \n to codeBlockMarker
|
||||
text = text.replace(/(```[\s\S]*?```|~~~[\s\S]*?~~~)/g, function (match) {
|
||||
@@ -115,34 +122,38 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
// The larger maxLen is, the next sentence is less likely to trigger splitting
|
||||
const markdownIndex = 4;
|
||||
const forbidOverlapIndex = 8;
|
||||
const stepReges: { reg: RegExp; maxLen: number }[] = [
|
||||
...customReg.map((text) => ({
|
||||
reg: new RegExp(`(${replaceRegChars(text)})`, 'g'),
|
||||
maxLen: chunkLen * 1.4
|
||||
})),
|
||||
{ reg: /^(#\s[^\n]+\n)/gm, maxLen: chunkLen * 1.2 },
|
||||
{ reg: /^(##\s[^\n]+\n)/gm, maxLen: chunkLen * 1.4 },
|
||||
{ reg: /^(###\s[^\n]+\n)/gm, maxLen: chunkLen * 1.6 },
|
||||
{ reg: /^(####\s[^\n]+\n)/gm, maxLen: chunkLen * 1.8 },
|
||||
{ reg: /^(#####\s[^\n]+\n)/gm, maxLen: chunkLen * 1.8 },
|
||||
|
||||
{ reg: /([\n]([`~]))/g, maxLen: chunkLen * 4 }, // code block
|
||||
{ reg: /([\n](?=\s*[0-9]+\.))/g, maxLen: chunkLen * 2 }, // 增大块,尽可能保证它是一个完整的段落。 (?![\*\-|>`0-9]): markdown special char
|
||||
{ reg: /(\n{2,})/g, maxLen: chunkLen * 1.6 },
|
||||
{ reg: /([\n])/g, maxLen: chunkLen * 1.2 },
|
||||
const stepReges: { reg: RegExp | string; maxLen: number }[] = [
|
||||
...customReg.map((text) => ({
|
||||
reg: text.replaceAll('\\n', '\n'),
|
||||
maxLen: chunkSize
|
||||
})),
|
||||
{ reg: /^(#\s[^\n]+\n)/gm, maxLen: chunkSize },
|
||||
{ reg: /^(##\s[^\n]+\n)/gm, maxLen: chunkSize },
|
||||
{ reg: /^(###\s[^\n]+\n)/gm, maxLen: chunkSize },
|
||||
{ reg: /^(####\s[^\n]+\n)/gm, maxLen: chunkSize },
|
||||
{ reg: /^(#####\s[^\n]+\n)/gm, maxLen: chunkSize },
|
||||
|
||||
{ reg: /([\n](```[\s\S]*?```|~~~[\s\S]*?~~~))/g, maxLen: maxSize }, // code block
|
||||
{
|
||||
reg: /(\n\|(?:(?:[^\n|]+\|){1,})\n\|(?:[:\-\s]+\|){1,}\n(?:\|(?:[^\n|]+\|)*\n)*)/g,
|
||||
maxLen: maxSize
|
||||
}, // Table 尽可能保证完整性
|
||||
{ reg: /(\n{2,})/g, maxLen: chunkSize },
|
||||
{ reg: /([\n])/g, maxLen: chunkSize },
|
||||
// ------ There's no overlap on the top
|
||||
{ reg: /([。]|([a-zA-Z])\.\s)/g, maxLen: chunkLen * 1.2 },
|
||||
{ reg: /([!]|!\s)/g, maxLen: chunkLen * 1.2 },
|
||||
{ reg: /([?]|\?\s)/g, maxLen: chunkLen * 1.4 },
|
||||
{ reg: /([;]|;\s)/g, maxLen: chunkLen * 1.6 },
|
||||
{ reg: /([,]|,\s)/g, maxLen: chunkLen * 2 }
|
||||
{ reg: /([。]|([a-zA-Z])\.\s)/g, maxLen: chunkSize },
|
||||
{ reg: /([!]|!\s)/g, maxLen: chunkSize },
|
||||
{ reg: /([?]|\?\s)/g, maxLen: chunkSize },
|
||||
{ reg: /([;]|;\s)/g, maxLen: chunkSize },
|
||||
{ reg: /([,]|,\s)/g, maxLen: chunkSize }
|
||||
];
|
||||
|
||||
const customRegLen = customReg.length;
|
||||
const checkIsCustomStep = (step: number) => step < customRegLen;
|
||||
const checkIsMarkdownSplit = (step: number) =>
|
||||
step >= customRegLen && step <= markdownIndex + customRegLen;
|
||||
+customReg.length;
|
||||
|
||||
const checkForbidOverlap = (step: number) => step <= forbidOverlapIndex + customRegLen;
|
||||
|
||||
// if use markdown title split, Separate record title
|
||||
@@ -151,7 +162,8 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
return [
|
||||
{
|
||||
text,
|
||||
title: ''
|
||||
title: '',
|
||||
chunkMaxSize: chunkSize
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -159,27 +171,46 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
const isCustomStep = checkIsCustomStep(step);
|
||||
const isMarkdownSplit = checkIsMarkdownSplit(step);
|
||||
|
||||
const { reg } = stepReges[step];
|
||||
const { reg, maxLen } = stepReges[step];
|
||||
|
||||
const splitTexts = text
|
||||
.replace(
|
||||
const replaceText = (() => {
|
||||
if (typeof reg === 'string') {
|
||||
let tmpText = text;
|
||||
reg.split('|').forEach((itemReg) => {
|
||||
tmpText = tmpText.replaceAll(
|
||||
itemReg,
|
||||
(() => {
|
||||
if (isCustomStep) return splitMarker;
|
||||
if (isMarkdownSplit) return `${splitMarker}$1`;
|
||||
return `$1${splitMarker}`;
|
||||
})()
|
||||
);
|
||||
});
|
||||
return tmpText;
|
||||
}
|
||||
|
||||
return text.replace(
|
||||
reg,
|
||||
(() => {
|
||||
if (isCustomStep) return splitMarker;
|
||||
if (isMarkdownSplit) return `${splitMarker}$1`;
|
||||
return `$1${splitMarker}`;
|
||||
})()
|
||||
)
|
||||
.split(`${splitMarker}`)
|
||||
.filter((part) => part.trim());
|
||||
);
|
||||
})();
|
||||
|
||||
const splitTexts = replaceText.split(splitMarker).filter((part) => part.trim());
|
||||
|
||||
return splitTexts
|
||||
.map((text) => {
|
||||
const matchTitle = isMarkdownSplit ? text.match(reg)?.[0] || '' : '';
|
||||
// 如果一个分块没有匹配到,则使用默认块大小,否则使用最大块大小
|
||||
const chunkMaxSize = text.match(reg) === null ? chunkSize : maxLen;
|
||||
|
||||
return {
|
||||
text: isMarkdownSplit ? text.replace(matchTitle, '') : text,
|
||||
title: matchTitle
|
||||
title: matchTitle,
|
||||
chunkMaxSize
|
||||
};
|
||||
})
|
||||
.filter((item) => !!item.title || !!item.text?.trim());
|
||||
@@ -188,7 +219,7 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
/* Gets the overlap at the end of a text as the beginning of the next block */
|
||||
const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => {
|
||||
const forbidOverlap = checkForbidOverlap(step);
|
||||
const maxOverlapLen = chunkLen * 0.4;
|
||||
const maxOverlapLen = chunkSize * 0.4;
|
||||
|
||||
// step >= stepReges.length: Do not overlap incomplete sentences
|
||||
if (forbidOverlap || overlapLen === 0 || step >= stepReges.length) return '';
|
||||
@@ -229,15 +260,15 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
const isCustomStep = checkIsCustomStep(step);
|
||||
const forbidConcat = isCustomStep; // forbid=true时候,lastText肯定为空
|
||||
|
||||
// oversize
|
||||
// Over step
|
||||
if (step >= stepReges.length) {
|
||||
if (text.length < chunkLen * 3) {
|
||||
if (text.length < maxSize) {
|
||||
return [text];
|
||||
}
|
||||
// use slice-chunkLen to split text
|
||||
// use slice-chunkSize to split text
|
||||
const chunks: string[] = [];
|
||||
for (let i = 0; i < text.length; i += chunkLen - overlapLen) {
|
||||
chunks.push(text.slice(i, i + chunkLen));
|
||||
for (let i = 0; i < text.length; i += chunkSize - overlapLen) {
|
||||
chunks.push(text.slice(i, i + chunkSize));
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
@@ -245,19 +276,18 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
// split text by special char
|
||||
const splitTexts = getSplitTexts({ text, step });
|
||||
|
||||
const maxLen = splitTexts.length > 1 ? stepReges[step].maxLen : chunkLen;
|
||||
const minChunkLen = chunkLen * 0.7;
|
||||
|
||||
const chunks: string[] = [];
|
||||
for (let i = 0; i < splitTexts.length; i++) {
|
||||
const item = splitTexts[i];
|
||||
|
||||
const maxLen = item.chunkMaxSize; // 当前块最大长度
|
||||
|
||||
const lastTextLen = lastText.length;
|
||||
const currentText = item.text;
|
||||
const newText = lastText + currentText;
|
||||
const newTextLen = newText.length;
|
||||
|
||||
// Markdown 模式下,会强制向下拆分最小块,并再最后一个标题时候,给小块都补充上所有标题(包含父级标题)
|
||||
// Markdown 模式下,会强制向下拆分最小块,并再最后一个标题深度,给小块都补充上所有标题(包含父级标题)
|
||||
if (isMarkdownStep) {
|
||||
// split new Text, split chunks must will greater 1 (small lastText)
|
||||
const innerChunks = splitTextRecursively({
|
||||
@@ -267,11 +297,13 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
parentTitle: parentTitle + item.title
|
||||
});
|
||||
|
||||
// 只有标题,没有内容。
|
||||
if (innerChunks.length === 0) {
|
||||
chunks.push(`${parentTitle}${item.title}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 在合并最深级标题时,需要补充标题
|
||||
chunks.push(
|
||||
...innerChunks.map(
|
||||
(chunk) =>
|
||||
@@ -282,9 +314,18 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
continue;
|
||||
}
|
||||
|
||||
// newText is too large(now, The lastText must be smaller than chunkLen)
|
||||
// newText is too large(now, The lastText must be smaller than chunkSize)
|
||||
if (newTextLen > maxLen) {
|
||||
// lastText greater minChunkLen, direct push it to chunks, not add to next chunk. (large lastText)
|
||||
const minChunkLen = maxLen * 0.8; // 当前块最小长度
|
||||
const maxChunkLen = maxLen * 1.2; // 当前块最大长度
|
||||
|
||||
// 新文本没有非常大,直接认为它是一个新的块
|
||||
if (newTextLen < maxChunkLen) {
|
||||
chunks.push(newText);
|
||||
lastText = getOneTextOverlapText({ text: newText, step }); // next chunk will start with overlayText
|
||||
continue;
|
||||
}
|
||||
// 上一个文本块已经挺大的,单独做一个块
|
||||
if (lastTextLen > minChunkLen) {
|
||||
chunks.push(lastText);
|
||||
|
||||
@@ -294,13 +335,13 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 说明是新的文本块比较大,需要进一步拆分
|
||||
// 说明是当前文本比较大,需要进一步拆分
|
||||
|
||||
// split new Text, split chunks must will greater 1 (small lastText)
|
||||
// 把新的文本块进行一个拆分,并追加到 latestText 中
|
||||
const innerChunks = splitTextRecursively({
|
||||
text: newText,
|
||||
text: currentText,
|
||||
step: step + 1,
|
||||
lastText: '',
|
||||
lastText,
|
||||
parentTitle: parentTitle + item.title
|
||||
});
|
||||
const lastChunk = innerChunks[innerChunks.length - 1];
|
||||
@@ -328,16 +369,16 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
|
||||
// Not overlap
|
||||
if (forbidConcat) {
|
||||
chunks.push(item.text);
|
||||
chunks.push(currentText);
|
||||
continue;
|
||||
}
|
||||
|
||||
lastText += item.text;
|
||||
lastText = newText;
|
||||
}
|
||||
|
||||
/* If the last chunk is independent, it needs to be push chunks. */
|
||||
if (lastText && chunks[chunks.length - 1] && !chunks[chunks.length - 1].endsWith(lastText)) {
|
||||
if (lastText.length < chunkLen * 0.4) {
|
||||
if (lastText.length < chunkSize * 0.4) {
|
||||
chunks[chunks.length - 1] = chunks[chunks.length - 1] + lastText;
|
||||
} else {
|
||||
chunks.push(lastText);
|
||||
@@ -371,9 +412,9 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
|
||||
/**
|
||||
* text split into chunks
|
||||
* chunkLen - one chunk len. max: 3500
|
||||
* chunkSize - one chunk len. max: 3500
|
||||
* overlapLen - The size of the before and after Text
|
||||
* chunkLen > overlapLen
|
||||
* chunkSize > overlapLen
|
||||
* markdown
|
||||
*/
|
||||
export const splitText2Chunks = (props: SplitProps): SplitResponse => {
|
||||
|
||||
@@ -56,7 +56,7 @@ export const replaceSensitiveText = (text: string) => {
|
||||
};
|
||||
|
||||
/* Make sure the first letter is definitely lowercase */
|
||||
export const getNanoid = (size = 12) => {
|
||||
export const getNanoid = (size = 16) => {
|
||||
const firstChar = customAlphabet('abcdefghijklmnopqrstuvwxyz', 1)();
|
||||
|
||||
if (size === 1) return firstChar;
|
||||
|
||||
@@ -84,11 +84,6 @@ export type FastGPTFeConfigsType = {
|
||||
github?: string;
|
||||
google?: string;
|
||||
wechat?: string;
|
||||
dingtalk?: string;
|
||||
wecom?: {
|
||||
corpid?: string;
|
||||
agentid?: string;
|
||||
};
|
||||
microsoft?: {
|
||||
clientId?: string;
|
||||
tenantId?: string;
|
||||
|
||||
@@ -1,54 +1,70 @@
|
||||
import { PromptTemplateItem } from '../type.d';
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
import { getPromptByVersion } from './utils';
|
||||
|
||||
export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
||||
{
|
||||
title: i18nT('app:template.standard_template'),
|
||||
desc: i18nT('app:template.standard_template_des'),
|
||||
value: `{
|
||||
value: {
|
||||
['4.9.2']: `{
|
||||
"sourceName": "{{source}}",
|
||||
"updateTime": "{{updateTime}}",
|
||||
"content": "{{q}}\n{{a}}"
|
||||
}
|
||||
`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18nT('app:template.qa_template'),
|
||||
desc: i18nT('app:template.qa_template_des'),
|
||||
value: `<Question>
|
||||
value: {
|
||||
['4.9.2']: `<Question>
|
||||
{{q}}
|
||||
</Question>
|
||||
<Answer>
|
||||
{{a}}
|
||||
</Answer>`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18nT('app:template.standard_strict'),
|
||||
desc: i18nT('app:template.standard_strict_des'),
|
||||
value: `{
|
||||
value: {
|
||||
['4.9.2']: `{
|
||||
"sourceName": "{{source}}",
|
||||
"updateTime": "{{updateTime}}",
|
||||
"content": "{{q}}\n{{a}}"
|
||||
}
|
||||
`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18nT('app:template.hard_strict'),
|
||||
desc: i18nT('app:template.hard_strict_des'),
|
||||
value: `<Question>
|
||||
value: {
|
||||
['4.9.2']: `<Question>
|
||||
{{q}}
|
||||
</Question>
|
||||
<Answer>
|
||||
{{a}}
|
||||
</Answer>`
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export const getQuoteTemplate = (version?: string) => {
|
||||
const defaultTemplate = Prompt_QuoteTemplateList[0].value;
|
||||
|
||||
return getPromptByVersion(version, defaultTemplate);
|
||||
};
|
||||
|
||||
export const Prompt_userQuotePromptList: PromptTemplateItem[] = [
|
||||
{
|
||||
title: i18nT('app:template.standard_template'),
|
||||
desc: '',
|
||||
value: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
value: {
|
||||
['4.9.2']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
|
||||
<Reference>
|
||||
{{quote}}
|
||||
@@ -62,11 +78,13 @@ export const Prompt_userQuotePromptList: PromptTemplateItem[] = [
|
||||
- 使用与问题相同的语言回答。
|
||||
|
||||
问题:"""{{question}}"""`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18nT('app:template.qa_template'),
|
||||
desc: '',
|
||||
value: `使用 <QA></QA> 标记中的问答对进行回答。
|
||||
value: {
|
||||
['4.9.2']: `使用 <QA></QA> 标记中的问答对进行回答。
|
||||
|
||||
<QA>
|
||||
{{quote}}
|
||||
@@ -79,11 +97,13 @@ export const Prompt_userQuotePromptList: PromptTemplateItem[] = [
|
||||
- 避免提及你是从 QA 获取的知识,只需要回复答案。
|
||||
|
||||
问题:"""{{question}}"""`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18nT('app:template.standard_strict'),
|
||||
desc: '',
|
||||
value: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
value: {
|
||||
['4.9.2']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
|
||||
<Reference>
|
||||
{{quote}}
|
||||
@@ -101,11 +121,13 @@ export const Prompt_userQuotePromptList: PromptTemplateItem[] = [
|
||||
- 使用与问题相同的语言回答。
|
||||
|
||||
问题:"""{{question}}"""`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18nT('app:template.hard_strict'),
|
||||
desc: '',
|
||||
value: `忘记你已有的知识,仅使用 <QA></QA> 标记中的问答对进行回答。
|
||||
value: {
|
||||
['4.9.2']: `忘记你已有的知识,仅使用 <QA></QA> 标记中的问答对进行回答。
|
||||
|
||||
<QA>
|
||||
{{quote}}
|
||||
@@ -126,6 +148,7 @@ export const Prompt_userQuotePromptList: PromptTemplateItem[] = [
|
||||
- 使用与问题相同的语言回答。
|
||||
|
||||
问题:"""{{question}}"""`
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@@ -133,7 +156,8 @@ export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
|
||||
{
|
||||
title: i18nT('app:template.standard_template'),
|
||||
desc: '',
|
||||
value: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
value: {
|
||||
['4.9.2']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
|
||||
<Reference>
|
||||
{{quote}}
|
||||
@@ -145,11 +169,13 @@ export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
|
||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
||||
- 使用 Markdown 语法优化回答格式。
|
||||
- 使用与问题相同的语言回答。`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18nT('app:template.qa_template'),
|
||||
desc: '',
|
||||
value: `使用 <QA></QA> 标记中的问答对进行回答。
|
||||
value: {
|
||||
['4.9.2']: `使用 <QA></QA> 标记中的问答对进行回答。
|
||||
|
||||
<QA>
|
||||
{{quote}}
|
||||
@@ -160,11 +186,13 @@ export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
|
||||
- 回答的内容应尽可能与 <答案></答案> 中的内容一致。
|
||||
- 如果没有相关的问答对,你需要澄清。
|
||||
- 避免提及你是从 QA 获取的知识,只需要回复答案。`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18nT('app:template.standard_strict'),
|
||||
desc: '',
|
||||
value: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
value: {
|
||||
['4.9.2']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
|
||||
<Reference>
|
||||
{{quote}}
|
||||
@@ -180,11 +208,13 @@ export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
|
||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
||||
- 使用 Markdown 语法优化回答格式。
|
||||
- 使用与问题相同的语言回答。`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18nT('app:template.hard_strict'),
|
||||
desc: '',
|
||||
value: `忘记你已有的知识,仅使用 <QA></QA> 标记中的问答对进行回答。
|
||||
value: {
|
||||
['4.9.2']: `忘记你已有的知识,仅使用 <QA></QA> 标记中的问答对进行回答。
|
||||
|
||||
<QA>
|
||||
{{quote}}
|
||||
@@ -203,12 +233,28 @@ export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
|
||||
- 避免提及你是从 QA 获取的知识,只需要回复答案。
|
||||
- 使用 Markdown 语法优化回答格式。
|
||||
- 使用与问题相同的语言回答。`
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export const getQuotePrompt = (version?: string, role: 'user' | 'system' = 'user') => {
|
||||
const quotePromptTemplates =
|
||||
role === 'user' ? Prompt_userQuotePromptList : Prompt_systemQuotePromptList;
|
||||
|
||||
const defaultTemplate = quotePromptTemplates[0].value;
|
||||
|
||||
return getPromptByVersion(version, defaultTemplate);
|
||||
};
|
||||
|
||||
// Document quote prompt
|
||||
export const Prompt_DocumentQuote = `将 <FilesContent></FilesContent> 中的内容作为本次对话的参考:
|
||||
<FilesContent>
|
||||
{{quote}}
|
||||
</FilesContent>
|
||||
`;
|
||||
export const getDocumentQuotePrompt = (version: string) => {
|
||||
const promptMap = {
|
||||
['4.9.2']: `将 <FilesContent></FilesContent> 中的内容作为本次对话的参考:
|
||||
<FilesContent>
|
||||
{{quote}}
|
||||
</FilesContent>
|
||||
`
|
||||
};
|
||||
|
||||
return getPromptByVersion(version, promptMap);
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getPromptByVersion } from './utils';
|
||||
|
||||
export const Prompt_AgentQA = {
|
||||
description: `<Context></Context> 标记中是一段文本,学习和分析它,并整理学习成果:
|
||||
- 提出问题并给出每个问题的答案。
|
||||
@@ -25,7 +27,9 @@ A2:
|
||||
`
|
||||
};
|
||||
|
||||
export const Prompt_ExtractJson = `你可以从 <对话记录></对话记录> 中提取指定 Json 信息,你仅需返回 Json 字符串,无需回答问题。
|
||||
export const getExtractJsonPrompt = (version?: string) => {
|
||||
const promptMap: Record<string, string> = {
|
||||
['4.9.2']: `你可以从 <对话记录></对话记录> 中提取指定 Json 信息,你仅需返回 Json 字符串,无需回答问题。
|
||||
<提取要求>
|
||||
{{description}}
|
||||
</提取要求>
|
||||
@@ -44,9 +48,31 @@ export const Prompt_ExtractJson = `你可以从 <对话记录></对话记录>
|
||||
{{text}}
|
||||
</对话记录>
|
||||
|
||||
提取的 json 字符串:`;
|
||||
提取的 json 字符串:`
|
||||
};
|
||||
|
||||
export const Prompt_CQJson = `请帮我执行一个“问题分类”任务,将问题分类为以下几种类型之一:
|
||||
return getPromptByVersion(version, promptMap);
|
||||
};
|
||||
|
||||
export const getExtractJsonToolPrompt = (version?: string) => {
|
||||
const promptMap: Record<string, string> = {
|
||||
['4.9.2']: `我正在执行一个函数,需要你提供一些参数,请以 JSON 字符串格式返回这些参数,要求:
|
||||
"""
|
||||
- {{description}}
|
||||
- 不是每个参数都是必须生成的,如果没有合适的参数值,不要生成该参数,或返回空字符串。
|
||||
- 需要结合前面的对话内容,一起生成合适的参数。
|
||||
"""
|
||||
|
||||
本次输入内容: """{{content}}"""
|
||||
`
|
||||
};
|
||||
|
||||
return getPromptByVersion(version, promptMap);
|
||||
};
|
||||
|
||||
export const getCQPrompt = (version?: string) => {
|
||||
const promptMap: Record<string, string> = {
|
||||
['4.9.2']: `请帮我执行一个"问题分类"任务,将问题分类为以下几种类型之一:
|
||||
|
||||
"""
|
||||
{{typeList}}
|
||||
@@ -64,9 +90,13 @@ export const Prompt_CQJson = `请帮我执行一个“问题分类”任务,
|
||||
|
||||
问题:"{{question}}"
|
||||
类型ID=
|
||||
`;
|
||||
`
|
||||
};
|
||||
|
||||
export const PROMPT_QUESTION_GUIDE = `You are an AI assistant tasked with predicting the user's next question based on the conversation history. Your goal is to generate 3 potential questions that will guide the user to continue the conversation. When generating these questions, adhere to the following rules:
|
||||
return getPromptByVersion(version, promptMap);
|
||||
};
|
||||
|
||||
export const QuestionGuidePrompt = `You are an AI assistant tasked with predicting the user's next question based on the conversation history. Your goal is to generate 3 potential questions that will guide the user to continue the conversation. When generating these questions, adhere to the following rules:
|
||||
|
||||
1. Use the same language as the user's last question in the conversation history.
|
||||
2. Keep each question under 20 characters in length.
|
||||
@@ -74,4 +104,5 @@ export const PROMPT_QUESTION_GUIDE = `You are an AI assistant tasked with predic
|
||||
Analyze the conversation history provided to you and use it as context to generate relevant and engaging follow-up questions. Your predictions should be logical extensions of the current topic or related areas that the user might be interested in exploring further.
|
||||
|
||||
Remember to maintain consistency in tone and style with the existing conversation while providing diverse options for the user to choose from. Your goal is to keep the conversation flowing naturally and help the user delve deeper into the subject matter or explore related topics.`;
|
||||
export const PROMPT_QUESTION_GUIDE_FOOTER = `Please strictly follow the format rules: \nReturn questions in JSON format: ['Question 1', 'Question 2', 'Question 3']. Your output: `;
|
||||
|
||||
export const QuestionGuideFooterPrompt = `Please strictly follow the format rules: \nReturn questions in JSON format: ['Question 1', 'Question 2', 'Question 3']. Your output: `;
|
||||
|
||||
19
packages/global/core/ai/prompt/utils.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export const getPromptByVersion = (version?: string, promptMap: Record<string, string> = {}) => {
|
||||
const versions = Object.keys(promptMap).sort((a, b) => {
|
||||
const [majorA, minorA, patchA] = a.split('.').map(Number);
|
||||
const [majorB, minorB, patchB] = b.split('.').map(Number);
|
||||
|
||||
if (majorA !== majorB) return majorB - majorA;
|
||||
if (minorA !== minorB) return minorB - minorA;
|
||||
return patchB - patchA;
|
||||
});
|
||||
|
||||
if (!version) {
|
||||
return promptMap[versions[0]];
|
||||
}
|
||||
|
||||
if (version in promptMap) {
|
||||
return promptMap[version];
|
||||
}
|
||||
return promptMap[versions[versions.length - 1]];
|
||||
};
|
||||
2
packages/global/core/ai/type.d.ts
vendored
@@ -80,5 +80,5 @@ export * from 'openai';
|
||||
export type PromptTemplateItem = {
|
||||
title: string;
|
||||
desc: string;
|
||||
value: string;
|
||||
value: Record<string, string>;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { PROMPT_QUESTION_GUIDE } from '../ai/prompt/agent';
|
||||
import {
|
||||
AppTTSConfigType,
|
||||
AppFileSelectConfigType,
|
||||
|
||||
13
packages/global/core/dataset/api.d.ts
vendored
@@ -1,5 +1,10 @@
|
||||
import { DatasetDataIndexItemType, DatasetSchemaType } from './type';
|
||||
import { DatasetCollectionTypeEnum, DatasetCollectionDataProcessModeEnum } from './constants';
|
||||
import {
|
||||
DatasetCollectionTypeEnum,
|
||||
DatasetCollectionDataProcessModeEnum,
|
||||
ChunkSettingModeEnum,
|
||||
DataChunkSplitModeEnum
|
||||
} from './constants';
|
||||
import type { LLMModelItemType } from '../ai/model.d';
|
||||
import { ParentIdType } from 'common/parentFolder/type';
|
||||
|
||||
@@ -33,7 +38,13 @@ export type DatasetCollectionChunkMetadataType = {
|
||||
trainingType?: DatasetCollectionDataProcessModeEnum;
|
||||
imageIndex?: boolean;
|
||||
autoIndexes?: boolean;
|
||||
|
||||
chunkSettingMode?: ChunkSettingModeEnum;
|
||||
chunkSplitMode?: DataChunkSplitModeEnum;
|
||||
|
||||
chunkSize?: number;
|
||||
indexSize?: number;
|
||||
|
||||
chunkSplitter?: string;
|
||||
qaPrompt?: string;
|
||||
metadata?: Record<string, any>;
|
||||
|
||||
8
packages/global/core/dataset/apiDataset.d.ts
vendored
@@ -1,3 +1,5 @@
|
||||
import { RequireOnlyOne } from '../../common/type/utils';
|
||||
|
||||
export type APIFileItem = {
|
||||
id: string;
|
||||
parentId: string | null;
|
||||
@@ -15,9 +17,9 @@ export type APIFileServer = {
|
||||
|
||||
export type APIFileListResponse = APIFileItem[];
|
||||
|
||||
export type APIFileContentResponse = {
|
||||
content?: string;
|
||||
previewUrl?: string;
|
||||
export type ApiFileReadContentResponse = {
|
||||
title?: string;
|
||||
rawText: string;
|
||||
};
|
||||
|
||||
export type APIFileReadResponse = {
|
||||
|
||||
@@ -16,3 +16,7 @@ export const getCollectionSourceData = (collection?: DatasetCollectionSchemaType
|
||||
export const checkCollectionIsFolder = (type: DatasetCollectionTypeEnum) => {
|
||||
return type === DatasetCollectionTypeEnum.folder || type === DatasetCollectionTypeEnum.virtual;
|
||||
};
|
||||
|
||||
export const collectionCanSync = (type: DatasetCollectionTypeEnum) => {
|
||||
return [DatasetCollectionTypeEnum.link, DatasetCollectionTypeEnum.apiFile].includes(type);
|
||||
};
|
||||
|
||||
@@ -13,38 +13,38 @@ export enum DatasetTypeEnum {
|
||||
export const DatasetTypeMap = {
|
||||
[DatasetTypeEnum.folder]: {
|
||||
icon: 'common/folderFill',
|
||||
label: 'folder_dataset',
|
||||
collectionLabel: 'common.Folder'
|
||||
label: i18nT('dataset:folder_dataset'),
|
||||
collectionLabel: i18nT('common:Folder')
|
||||
},
|
||||
[DatasetTypeEnum.dataset]: {
|
||||
icon: 'core/dataset/commonDatasetOutline',
|
||||
label: 'common_dataset',
|
||||
collectionLabel: 'common.File'
|
||||
label: i18nT('dataset:common_dataset'),
|
||||
collectionLabel: i18nT('common:common.File')
|
||||
},
|
||||
[DatasetTypeEnum.websiteDataset]: {
|
||||
icon: 'core/dataset/websiteDatasetOutline',
|
||||
label: 'website_dataset',
|
||||
collectionLabel: 'common.Website'
|
||||
label: i18nT('dataset:website_dataset'),
|
||||
collectionLabel: i18nT('common:common.Website')
|
||||
},
|
||||
[DatasetTypeEnum.externalFile]: {
|
||||
icon: 'core/dataset/externalDatasetOutline',
|
||||
label: 'external_file',
|
||||
collectionLabel: 'common.File'
|
||||
label: i18nT('dataset:external_file'),
|
||||
collectionLabel: i18nT('common:common.File')
|
||||
},
|
||||
[DatasetTypeEnum.apiDataset]: {
|
||||
icon: 'core/dataset/externalDatasetOutline',
|
||||
label: 'api_file',
|
||||
collectionLabel: 'common.File'
|
||||
label: i18nT('dataset:api_file'),
|
||||
collectionLabel: i18nT('common:common.File')
|
||||
},
|
||||
[DatasetTypeEnum.feishu]: {
|
||||
icon: 'core/dataset/feishuDatasetOutline',
|
||||
label: 'feishu_dataset',
|
||||
collectionLabel: 'common.File'
|
||||
label: i18nT('dataset:feishu_dataset'),
|
||||
collectionLabel: i18nT('common:common.File')
|
||||
},
|
||||
[DatasetTypeEnum.yuque]: {
|
||||
icon: 'core/dataset/yuqueDatasetOutline',
|
||||
label: 'yuque_dataset',
|
||||
collectionLabel: 'common.File'
|
||||
label: i18nT('dataset:yuque_dataset'),
|
||||
collectionLabel: i18nT('common:common.File')
|
||||
}
|
||||
};
|
||||
|
||||
@@ -129,6 +129,16 @@ export const DatasetCollectionDataProcessModeMap = {
|
||||
}
|
||||
};
|
||||
|
||||
export enum ChunkSettingModeEnum {
|
||||
auto = 'auto',
|
||||
custom = 'custom'
|
||||
}
|
||||
|
||||
export enum DataChunkSplitModeEnum {
|
||||
size = 'size',
|
||||
char = 'char'
|
||||
}
|
||||
|
||||
/* ------------ data -------------- */
|
||||
|
||||
/* ------------ training -------------- */
|
||||
|
||||
1
packages/global/core/dataset/controller.d.ts
vendored
@@ -13,6 +13,7 @@ export type CreateDatasetDataProps = {
|
||||
|
||||
export type UpdateDatasetDataProps = {
|
||||
dataId: string;
|
||||
|
||||
q?: string;
|
||||
a?: string;
|
||||
indexes?: (Omit<DatasetDataIndexItemType, 'dataId'> & {
|
||||
|
||||
@@ -15,6 +15,8 @@ export type PushDataToTrainingQueueProps = {
|
||||
vectorModel: string;
|
||||
vlmModel?: string;
|
||||
|
||||
indexSize?: number;
|
||||
|
||||
billId?: string;
|
||||
session?: ClientSession;
|
||||
};
|
||||
|
||||
136
packages/global/core/dataset/training/utils.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { EmbeddingModelItemType, LLMModelItemType } from '../../../core/ai/model.d';
|
||||
import {
|
||||
ChunkSettingModeEnum,
|
||||
DataChunkSplitModeEnum,
|
||||
DatasetCollectionDataProcessModeEnum
|
||||
} from '../constants';
|
||||
|
||||
export const minChunkSize = 64; // min index and chunk size
|
||||
|
||||
// Chunk size
|
||||
export const chunkAutoChunkSize = 1500;
|
||||
export const getMaxChunkSize = (model: LLMModelItemType) => {
|
||||
return Math.max(model.maxContext - model.maxResponse, 2000);
|
||||
};
|
||||
|
||||
// QA
|
||||
export const defaultMaxChunkSize = 8000;
|
||||
export const getLLMDefaultChunkSize = (model?: LLMModelItemType) => {
|
||||
if (!model) return defaultMaxChunkSize;
|
||||
return Math.max(Math.min(model.maxContext - model.maxResponse, defaultMaxChunkSize), 2000);
|
||||
};
|
||||
|
||||
export const getLLMMaxChunkSize = (model?: LLMModelItemType) => {
|
||||
if (!model) return 8000;
|
||||
return Math.max(model.maxContext - model.maxResponse, 2000);
|
||||
};
|
||||
|
||||
// Index size
|
||||
export const getMaxIndexSize = (model?: EmbeddingModelItemType) => {
|
||||
return model?.maxToken || 512;
|
||||
};
|
||||
export const getAutoIndexSize = (model?: EmbeddingModelItemType) => {
|
||||
return model?.defaultToken || 512;
|
||||
};
|
||||
|
||||
const indexSizeSelectList = [
|
||||
{
|
||||
label: '64',
|
||||
value: 64
|
||||
},
|
||||
{
|
||||
label: '128',
|
||||
value: 128
|
||||
},
|
||||
{
|
||||
label: '256',
|
||||
value: 256
|
||||
},
|
||||
{
|
||||
label: '512',
|
||||
value: 512
|
||||
},
|
||||
{
|
||||
label: '768',
|
||||
value: 768
|
||||
},
|
||||
{
|
||||
label: '1024',
|
||||
value: 1024
|
||||
},
|
||||
{
|
||||
label: '1536',
|
||||
value: 1536
|
||||
},
|
||||
{
|
||||
label: '2048',
|
||||
value: 2048
|
||||
},
|
||||
{
|
||||
label: '3072',
|
||||
value: 3072
|
||||
},
|
||||
{
|
||||
label: '4096',
|
||||
value: 4096
|
||||
},
|
||||
{
|
||||
label: '5120',
|
||||
value: 5120
|
||||
},
|
||||
{
|
||||
label: '6144',
|
||||
value: 6144
|
||||
},
|
||||
{
|
||||
label: '7168',
|
||||
value: 7168
|
||||
},
|
||||
{
|
||||
label: '8192',
|
||||
value: 8192
|
||||
}
|
||||
];
|
||||
export const getIndexSizeSelectList = (max = 512) => {
|
||||
return indexSizeSelectList.filter((item) => item.value <= max);
|
||||
};
|
||||
|
||||
// Compute
|
||||
export const computeChunkSize = (params: {
|
||||
trainingType: DatasetCollectionDataProcessModeEnum;
|
||||
chunkSettingMode?: ChunkSettingModeEnum;
|
||||
chunkSplitMode?: DataChunkSplitModeEnum;
|
||||
llmModel?: LLMModelItemType;
|
||||
chunkSize?: number;
|
||||
}) => {
|
||||
if (params.trainingType === DatasetCollectionDataProcessModeEnum.qa) {
|
||||
if (params.chunkSettingMode === ChunkSettingModeEnum.auto) {
|
||||
return getLLMDefaultChunkSize(params.llmModel);
|
||||
}
|
||||
} else {
|
||||
// chunk
|
||||
if (params.chunkSettingMode === ChunkSettingModeEnum.auto) {
|
||||
return chunkAutoChunkSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (params.chunkSplitMode === DataChunkSplitModeEnum.char) {
|
||||
return getLLMMaxChunkSize(params.llmModel);
|
||||
}
|
||||
|
||||
return Math.min(params.chunkSize || chunkAutoChunkSize, getLLMMaxChunkSize(params.llmModel));
|
||||
};
|
||||
|
||||
export const computeChunkSplitter = (params: {
|
||||
chunkSettingMode?: ChunkSettingModeEnum;
|
||||
chunkSplitMode?: DataChunkSplitModeEnum;
|
||||
chunkSplitter?: string;
|
||||
}) => {
|
||||
if (params.chunkSettingMode === ChunkSettingModeEnum.auto) {
|
||||
return undefined;
|
||||
}
|
||||
if (params.chunkSplitMode === DataChunkSplitModeEnum.size) {
|
||||
return undefined;
|
||||
}
|
||||
return params.chunkSplitter;
|
||||
};
|
||||
9
packages/global/core/dataset/type.d.ts
vendored
@@ -2,6 +2,7 @@ import type { LLMModelItemType, EmbeddingModelItemType } from '../../core/ai/mod
|
||||
import { PermissionTypeEnum } from '../../support/permission/constant';
|
||||
import { PushDatasetDataChunkProps } from './api';
|
||||
import {
|
||||
DataChunkSplitModeEnum,
|
||||
DatasetCollectionDataProcessModeEnum,
|
||||
DatasetCollectionTypeEnum,
|
||||
DatasetStatusEnum,
|
||||
@@ -14,6 +15,7 @@ import { Permission } from '../../support/permission/controller';
|
||||
import { APIFileServer, FeishuServer, YuqueServer } from './apiDataset';
|
||||
import { SourceMemberType } from 'support/user/type';
|
||||
import { DatasetDataIndexTypeEnum } from './data/constants';
|
||||
import { ChunkSettingModeEnum } from './constants';
|
||||
|
||||
export type DatasetSchemaType = {
|
||||
_id: string;
|
||||
@@ -88,7 +90,12 @@ export type DatasetCollectionSchemaType = {
|
||||
autoIndexes?: boolean;
|
||||
imageIndex?: boolean;
|
||||
trainingType: DatasetCollectionDataProcessModeEnum;
|
||||
chunkSize: number;
|
||||
|
||||
chunkSettingMode?: ChunkSettingModeEnum;
|
||||
chunkSplitMode?: DataChunkSplitModeEnum;
|
||||
|
||||
chunkSize?: number;
|
||||
indexSize?: number;
|
||||
chunkSplitter?: string;
|
||||
qaPrompt?: string;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { TrainingModeEnum, DatasetCollectionTypeEnum } from './constants';
|
||||
import { getFileIcon } from '../../common/file/icon';
|
||||
import { strIsLink } from '../../common/string/tools';
|
||||
import { DatasetDataIndexTypeEnum } from './data/constants';
|
||||
|
||||
export function getCollectionIcon(
|
||||
type: DatasetCollectionTypeEnum = DatasetCollectionTypeEnum.file,
|
||||
@@ -38,26 +37,6 @@ export function getSourceNameIcon({
|
||||
return 'file/fill/file';
|
||||
}
|
||||
|
||||
/* get dataset data default index */
|
||||
export function getDefaultIndex(props?: { q?: string; a?: string }) {
|
||||
const { q = '', a } = props || {};
|
||||
|
||||
return [
|
||||
{
|
||||
text: q,
|
||||
type: DatasetDataIndexTypeEnum.default
|
||||
},
|
||||
...(a
|
||||
? [
|
||||
{
|
||||
text: a,
|
||||
type: DatasetDataIndexTypeEnum.default
|
||||
}
|
||||
]
|
||||
: [])
|
||||
];
|
||||
}
|
||||
|
||||
export const predictDataLimitLength = (mode: TrainingModeEnum, data: any[]) => {
|
||||
if (mode === TrainingModeEnum.qa) return data.length * 20;
|
||||
if (mode === TrainingModeEnum.auto) return data.length * 5;
|
||||
|
||||
2
packages/global/core/plugin/type.d.ts
vendored
@@ -41,6 +41,8 @@ export type PluginTemplateType = PluginRuntimeType & {
|
||||
export type PluginRuntimeType = {
|
||||
id: string;
|
||||
teamId?: string;
|
||||
tmbId?: string;
|
||||
|
||||
name: string;
|
||||
avatar: string;
|
||||
showStatus?: boolean;
|
||||
|
||||
@@ -20,6 +20,7 @@ export enum WorkflowIOValueTypeEnum {
|
||||
number = 'number',
|
||||
boolean = 'boolean',
|
||||
object = 'object',
|
||||
|
||||
arrayString = 'arrayString',
|
||||
arrayNumber = 'arrayNumber',
|
||||
arrayBoolean = 'arrayBoolean',
|
||||
|
||||
@@ -76,16 +76,9 @@ export const Input_Template_Text_Quote: FlowNodeInputItemType = {
|
||||
valueType: WorkflowIOValueTypeEnum.string
|
||||
};
|
||||
|
||||
export const Input_Template_File_Link_Prompt: FlowNodeInputItemType = {
|
||||
key: NodeInputKeyEnum.fileUrlList,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.input],
|
||||
label: i18nT('app:file_quote_link'),
|
||||
debugLabel: i18nT('app:file_quote_link'),
|
||||
valueType: WorkflowIOValueTypeEnum.arrayString
|
||||
};
|
||||
export const Input_Template_File_Link: FlowNodeInputItemType = {
|
||||
key: NodeInputKeyEnum.fileUrlList,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.reference],
|
||||
renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.input],
|
||||
label: i18nT('app:workflow.user_file_input'),
|
||||
debugLabel: i18nT('app:workflow.user_file_input'),
|
||||
description: i18nT('app:workflow.user_file_input_desc'),
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
Input_Template_History,
|
||||
Input_Template_System_Prompt,
|
||||
Input_Template_UserChatInput,
|
||||
Input_Template_File_Link_Prompt
|
||||
Input_Template_File_Link
|
||||
} from '../../input';
|
||||
import { chatNodeSystemPromptTip, systemPromptTip } from '../../tip';
|
||||
import { getHandleConfig } from '../../utils';
|
||||
@@ -55,7 +55,7 @@ export const AiChatModule: FlowNodeTemplateType = {
|
||||
showStatus: true,
|
||||
isTool: true,
|
||||
courseUrl: '/docs/guide/workbench/workflow/ai_chat/',
|
||||
version: '490',
|
||||
version: '4.9.0',
|
||||
inputs: [
|
||||
Input_Template_SettingAiModel,
|
||||
// --- settings modal
|
||||
@@ -129,7 +129,7 @@ export const AiChatModule: FlowNodeTemplateType = {
|
||||
},
|
||||
Input_Template_History,
|
||||
Input_Template_Dataset_Quote,
|
||||
Input_Template_File_Link_Prompt,
|
||||
Input_Template_File_Link,
|
||||
{ ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') }
|
||||
],
|
||||
outputs: [
|
||||
|
||||
@@ -30,7 +30,7 @@ export const ClassifyQuestionModule: FlowNodeTemplateType = {
|
||||
name: i18nT('workflow:question_classification'),
|
||||
intro: i18nT('workflow:intro_question_classification'),
|
||||
showStatus: true,
|
||||
version: '481',
|
||||
version: '4.9.2',
|
||||
courseUrl: '/docs/guide/workbench/workflow/question_classify/',
|
||||
inputs: [
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ export const ContextExtractModule: FlowNodeTemplateType = {
|
||||
showStatus: true,
|
||||
isTool: true,
|
||||
courseUrl: '/docs/guide/workbench/workflow/content_extract/',
|
||||
version: '481',
|
||||
version: '4.9.2',
|
||||
inputs: [
|
||||
{
|
||||
...Input_Template_SelectAIModel,
|
||||
|
||||
@@ -31,7 +31,7 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
|
||||
showStatus: true,
|
||||
isTool: true,
|
||||
courseUrl: '/docs/guide/workbench/workflow/dataset_search/',
|
||||
version: '481',
|
||||
version: '4.9.2',
|
||||
inputs: [
|
||||
{
|
||||
key: NodeInputKeyEnum.datasetSelectList,
|
||||
|
||||
@@ -23,7 +23,7 @@ export const ReadFilesNode: FlowNodeTemplateType = {
|
||||
name: i18nT('app:workflow.read_files'),
|
||||
intro: i18nT('app:workflow.read_files_tip'),
|
||||
showStatus: true,
|
||||
version: '4812',
|
||||
version: '4.9.2',
|
||||
isTool: false,
|
||||
courseUrl: '/docs/guide/course/fileinput/',
|
||||
inputs: [
|
||||
|
||||
@@ -20,7 +20,7 @@ import { chatNodeSystemPromptTip, systemPromptTip } from '../tip';
|
||||
import { LLMModelTypeEnum } from '../../../ai/constants';
|
||||
import { getHandleConfig } from '../utils';
|
||||
import { i18nT } from '../../../../../web/i18n/utils';
|
||||
import { Input_Template_File_Link_Prompt } from '../input';
|
||||
import { Input_Template_File_Link } from '../input';
|
||||
|
||||
export const ToolModule: FlowNodeTemplateType = {
|
||||
id: FlowNodeTypeEnum.tools,
|
||||
@@ -33,7 +33,7 @@ export const ToolModule: FlowNodeTemplateType = {
|
||||
intro: i18nT('workflow:template.tool_call_intro'),
|
||||
showStatus: true,
|
||||
courseUrl: '/docs/guide/workbench/workflow/tool/',
|
||||
version: '4813',
|
||||
version: '4.9.2',
|
||||
inputs: [
|
||||
{
|
||||
...Input_Template_SettingAiModel,
|
||||
@@ -97,7 +97,7 @@ export const ToolModule: FlowNodeTemplateType = {
|
||||
placeholder: chatNodeSystemPromptTip
|
||||
},
|
||||
Input_Template_History,
|
||||
Input_Template_File_Link_Prompt,
|
||||
Input_Template_File_Link,
|
||||
Input_Template_UserChatInput
|
||||
],
|
||||
outputs: [
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"js-yaml": "^4.1.0",
|
||||
"jschardet": "3.1.1",
|
||||
"nanoid": "^5.1.3",
|
||||
"next": "14.2.24",
|
||||
"next": "14.2.25",
|
||||
"openai": "4.61.0",
|
||||
"openapi-types": "^12.1.3",
|
||||
"json5": "^2.2.3",
|
||||
|
||||
4
packages/global/support/permission/memberGroup/api.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export type GetGroupListBody = {
|
||||
searchKey?: string;
|
||||
withMembers?: boolean;
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import { TeamMemberItemType } from 'support/user/team/type';
|
||||
import { TeamPermission } from '../user/controller';
|
||||
import { GroupMemberRole } from './constant';
|
||||
import { Permission } from '../controller';
|
||||
|
||||
type MemberGroupSchemaType = {
|
||||
_id: string;
|
||||
@@ -16,12 +17,28 @@ type GroupMemberSchemaType = {
|
||||
role: `${GroupMemberRole}`;
|
||||
};
|
||||
|
||||
type MemberGroupType = MemberGroupSchemaType & {
|
||||
members: {
|
||||
tmbId: string;
|
||||
role: `${GroupMemberRole}`;
|
||||
}[]; // we can get tmb's info from other api. there is no need but only need to get tmb's id
|
||||
permission: TeamPermission;
|
||||
type MemberGroupListItemType<T extends boolean | undefined> = MemberGroupSchemaType & {
|
||||
members: T extends true
|
||||
? {
|
||||
tmbId: string;
|
||||
name: string;
|
||||
avatar: string;
|
||||
}[]
|
||||
: undefined;
|
||||
count: T extends true ? number : undefined;
|
||||
owner?: T extends true
|
||||
? {
|
||||
tmbId: string;
|
||||
name: string;
|
||||
avatar: string;
|
||||
}
|
||||
: undefined;
|
||||
permission: T extends true ? Permission : undefined;
|
||||
};
|
||||
|
||||
type MemberGroupListType = MemberGroupType[];
|
||||
type GroupMemberItemType = {
|
||||
tmbId: string;
|
||||
name: string;
|
||||
avatar: string;
|
||||
role: `${GroupMemberRole}`;
|
||||
};
|
||||
|
||||
7
packages/global/support/user/api.d.ts
vendored
@@ -1,4 +1,7 @@
|
||||
import { MemberGroupSchemaType, MemberGroupType } from 'support/permission/memberGroup/type';
|
||||
import {
|
||||
MemberGroupSchemaType,
|
||||
MemberGroupListItemType
|
||||
} from 'support/permission/memberGroup/type';
|
||||
import { OAuthEnum } from './constant';
|
||||
import { TrackRegisterParams } from './login/api';
|
||||
import { TeamMemberStatusEnum } from './team/constant';
|
||||
@@ -12,8 +15,8 @@ export type PostLoginProps = {
|
||||
|
||||
export type OauthLoginProps = {
|
||||
type: `${OAuthEnum}`;
|
||||
code: string;
|
||||
callbackUrl: string;
|
||||
props: Record<string, string>;
|
||||
} & TrackRegisterParams;
|
||||
|
||||
export type WxLoginProps = {
|
||||
|
||||
@@ -16,7 +16,5 @@ export enum OAuthEnum {
|
||||
google = 'google',
|
||||
wechat = 'wechat',
|
||||
microsoft = 'microsoft',
|
||||
dingtalk = 'dingtalk',
|
||||
wecom = 'wecom',
|
||||
sso = 'sso'
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
export type postCreateOrgData = {
|
||||
name: string;
|
||||
parentId: string;
|
||||
description?: string;
|
||||
avatar?: string;
|
||||
orgId?: string;
|
||||
};
|
||||
|
||||
export type putUpdateOrgMembersData = {
|
||||
orgId: string;
|
||||
orgId?: string;
|
||||
members: {
|
||||
tmbId: string;
|
||||
// role: `${OrgMemberRole}`;
|
||||
@@ -14,7 +14,7 @@ export type putUpdateOrgMembersData = {
|
||||
};
|
||||
|
||||
export type putUpdateOrgData = {
|
||||
orgId: string;
|
||||
orgId: string; // can not be undefined because can not uppdate root org
|
||||
name?: string;
|
||||
avatar?: string;
|
||||
description?: string;
|
||||
@@ -22,7 +22,7 @@ export type putUpdateOrgData = {
|
||||
|
||||
export type putMoveOrgType = {
|
||||
orgId: string;
|
||||
targetOrgId: string;
|
||||
targetOrgId?: string; // '' ===> move to root org
|
||||
};
|
||||
|
||||
// type putChnageOrgOwnerData = {
|
||||
|
||||
@@ -3,7 +3,10 @@ import { OrgSchemaType } from './type';
|
||||
export const OrgCollectionName = 'team_orgs';
|
||||
export const OrgMemberCollectionName = 'team_org_members';
|
||||
|
||||
export const getOrgChildrenPath = (org: OrgSchemaType) => `${org.path}/${org.pathId}`;
|
||||
export const getOrgChildrenPath = (org: OrgSchemaType) => {
|
||||
if (org.path === '' && org.pathId === '') return '';
|
||||
return `${org.path ?? ''}/${org.pathId}`;
|
||||
};
|
||||
|
||||
export enum SyncOrgSourceEnum {
|
||||
wecom = 'wecom'
|
||||
|
||||
13
packages/global/support/user/team/org/type.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
import type { TeamPermission } from 'support/permission/user/controller';
|
||||
import type { TeamPermission } from '../../../permission/user/controller';
|
||||
import { ResourcePermissionType } from '../type';
|
||||
import { SourceMemberType } from '../../type';
|
||||
|
||||
type OrgSchemaType = {
|
||||
_id: string;
|
||||
@@ -7,7 +8,7 @@ type OrgSchemaType = {
|
||||
pathId: string;
|
||||
path: string;
|
||||
name: string;
|
||||
avatar?: string;
|
||||
avatar: string;
|
||||
description?: string;
|
||||
updateTime: Date;
|
||||
};
|
||||
@@ -19,8 +20,14 @@ type OrgMemberSchemaType = {
|
||||
tmbId: string;
|
||||
};
|
||||
|
||||
type OrgType = Omit<OrgSchemaType, 'avatar'> & {
|
||||
export type OrgListItemType = OrgSchemaType & {
|
||||
permission?: TeamPermission;
|
||||
total: number; // members + children orgs
|
||||
};
|
||||
|
||||
export type OrgType = Omit<OrgSchemaType, 'avatar'> & {
|
||||
avatar: string;
|
||||
permission: TeamPermission;
|
||||
members: OrgMemberSchemaType[];
|
||||
total: number; // members + children orgs
|
||||
};
|
||||
|
||||
25
packages/global/support/user/team/type.d.ts
vendored
@@ -70,7 +70,13 @@ export type TeamTmbItemType = {
|
||||
permission: TeamPermission;
|
||||
} & ThirdPartyAccountType;
|
||||
|
||||
export type TeamMemberItemType = {
|
||||
export type TeamMemberItemType<
|
||||
Options extends {
|
||||
withPermission?: boolean;
|
||||
withOrgs?: boolean;
|
||||
withGroupRole?: boolean;
|
||||
} = { withPermission: true; withOrgs: true; withGroupRole: false }
|
||||
> = {
|
||||
userId: string;
|
||||
tmbId: string;
|
||||
teamId: string;
|
||||
@@ -78,11 +84,24 @@ export type TeamMemberItemType = {
|
||||
avatar: string;
|
||||
role: `${TeamMemberRoleEnum}`;
|
||||
status: `${TeamMemberStatusEnum}`;
|
||||
permission: TeamPermission;
|
||||
contact?: string;
|
||||
createTime: Date;
|
||||
updateTime?: Date;
|
||||
};
|
||||
} & (Options extends { withPermission: true }
|
||||
? {
|
||||
permission: TeamPermission;
|
||||
}
|
||||
: {}) &
|
||||
(Options extends { withOrgs: true }
|
||||
? {
|
||||
orgs?: string[]; // full path name, pattern: /teamName/orgname1/orgname2
|
||||
}
|
||||
: {}) &
|
||||
(Options extends { withGroupRole: true }
|
||||
? {
|
||||
groupRole?: `${GroupMemberRole}`;
|
||||
}
|
||||
: {});
|
||||
|
||||
export type TeamTagItemType = {
|
||||
label: string;
|
||||
|
||||
@@ -30,11 +30,11 @@ export async function text2Speech({
|
||||
response_format: 'mp3',
|
||||
speed
|
||||
},
|
||||
modelData.requestUrl && modelData.requestAuth
|
||||
modelData.requestUrl
|
||||
? {
|
||||
path: modelData.requestUrl,
|
||||
headers: {
|
||||
Authorization: `Bearer ${modelData.requestAuth}`
|
||||
...(modelData.requestAuth ? { Authorization: `Bearer ${modelData.requestAuth}` } : {})
|
||||
}
|
||||
}
|
||||
: {}
|
||||
|
||||
@@ -3,21 +3,25 @@ import { getAxiosConfig } from '../config';
|
||||
import axios from 'axios';
|
||||
import FormData from 'form-data';
|
||||
import { getSTTModel } from '../model';
|
||||
import { STTModelType } from '@fastgpt/global/core/ai/model.d';
|
||||
|
||||
export const aiTranscriptions = async ({
|
||||
model,
|
||||
model: modelData,
|
||||
fileStream,
|
||||
headers
|
||||
}: {
|
||||
model: string;
|
||||
model: STTModelType;
|
||||
fileStream: fs.ReadStream;
|
||||
headers?: Record<string, string>;
|
||||
}) => {
|
||||
if (!modelData) {
|
||||
return Promise.reject('no model');
|
||||
}
|
||||
|
||||
const data = new FormData();
|
||||
data.append('model', model);
|
||||
data.append('model', modelData.model);
|
||||
data.append('file', fileStream);
|
||||
|
||||
const modelData = getSTTModel(model);
|
||||
const aiAxiosConfig = getAxiosConfig();
|
||||
|
||||
const { data: result } = await axios<{ text: string }>({
|
||||
|
||||
@@ -4,8 +4,8 @@ import { countGptMessagesTokens, countPromptTokens } from '../../../common/strin
|
||||
import { loadRequestMessages } from '../../chat/utils';
|
||||
import { llmCompletionsBodyFormat } from '../utils';
|
||||
import {
|
||||
PROMPT_QUESTION_GUIDE,
|
||||
PROMPT_QUESTION_GUIDE_FOOTER
|
||||
QuestionGuidePrompt,
|
||||
QuestionGuideFooterPrompt
|
||||
} from '@fastgpt/global/core/ai/prompt/agent';
|
||||
import { addLog } from '../../../common/system/log';
|
||||
import json5 from 'json5';
|
||||
@@ -27,7 +27,7 @@ export async function createQuestionGuide({
|
||||
...messages,
|
||||
{
|
||||
role: 'user',
|
||||
content: `${customPrompt || PROMPT_QUESTION_GUIDE}\n${PROMPT_QUESTION_GUIDE_FOOTER}`
|
||||
content: `${customPrompt || QuestionGuidePrompt}\n${QuestionGuideFooterPrompt}`
|
||||
}
|
||||
];
|
||||
const requestMessages = await loadRequestMessages({
|
||||
|
||||
@@ -65,6 +65,7 @@ export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
|
||||
|
||||
const requestBody: T = {
|
||||
...body,
|
||||
model: modelData.model,
|
||||
temperature:
|
||||
typeof body.temperature === 'number'
|
||||
? computedTemperature({
|
||||
|
||||
@@ -37,11 +37,12 @@ export async function splitCombinePluginId(id: string) {
|
||||
return { source, pluginId: id };
|
||||
}
|
||||
|
||||
type ChildAppType = SystemPluginTemplateItemType & { teamId?: string };
|
||||
type ChildAppType = SystemPluginTemplateItemType & { teamId?: string; tmbId?: string };
|
||||
|
||||
const getSystemPluginTemplateById = async (
|
||||
pluginId: string,
|
||||
versionId?: string
|
||||
): Promise<SystemPluginTemplateItemType> => {
|
||||
): Promise<ChildAppType> => {
|
||||
const item = getSystemPluginTemplates().find((plugin) => plugin.id === pluginId);
|
||||
if (!item) return Promise.reject(PluginErrEnum.unAuth);
|
||||
|
||||
@@ -67,12 +68,17 @@ const getSystemPluginTemplateById = async (
|
||||
: await getAppLatestVersion(plugin.associatedPluginId, app);
|
||||
if (!version.versionId) return Promise.reject('App version not found');
|
||||
|
||||
plugin.workflow = {
|
||||
nodes: version.nodes,
|
||||
edges: version.edges,
|
||||
chatConfig: version.chatConfig
|
||||
return {
|
||||
...plugin,
|
||||
workflow: {
|
||||
nodes: version.nodes,
|
||||
edges: version.edges,
|
||||
chatConfig: version.chatConfig
|
||||
},
|
||||
version: versionId || String(version.versionId),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
};
|
||||
plugin.version = versionId || String(version.versionId);
|
||||
}
|
||||
return plugin;
|
||||
};
|
||||
@@ -168,6 +174,7 @@ export async function getChildAppRuntimeById(
|
||||
return {
|
||||
id: String(item._id),
|
||||
teamId: String(item.teamId),
|
||||
tmbId: String(item.tmbId),
|
||||
name: item.name,
|
||||
avatar: item.avatar,
|
||||
intro: item.intro,
|
||||
@@ -187,6 +194,7 @@ export async function getChildAppRuntimeById(
|
||||
pluginOrder: 0
|
||||
};
|
||||
} else {
|
||||
// System
|
||||
return getSystemPluginTemplateById(pluginId, versionId);
|
||||
}
|
||||
})();
|
||||
@@ -194,6 +202,7 @@ export async function getChildAppRuntimeById(
|
||||
return {
|
||||
id: app.id,
|
||||
teamId: app.teamId,
|
||||
tmbId: app.tmbId,
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
showStatus: app.showStatus,
|
||||
|
||||
@@ -131,6 +131,10 @@ export async function rewriteAppWorkflowToSimple(formatNodes: StoreNodeItemType[
|
||||
if (!val) {
|
||||
input.value = [];
|
||||
} else if (Array.isArray(val)) {
|
||||
// Not rewrite reference value
|
||||
if (val.length === 2 && val.every((item) => typeof item === 'string')) {
|
||||
return;
|
||||
}
|
||||
input.value = val
|
||||
.map((dataset: { datasetId: string }) => ({
|
||||
datasetId: dataset.datasetId
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type {
|
||||
APIFileContentResponse,
|
||||
APIFileListResponse,
|
||||
ApiFileReadContentResponse,
|
||||
APIFileReadResponse,
|
||||
APIFileServer
|
||||
} from '@fastgpt/global/core/dataset/apiDataset';
|
||||
@@ -8,6 +8,7 @@ import axios, { Method } from 'axios';
|
||||
import { addLog } from '../../../common/system/log';
|
||||
import { readFileRawTextByUrl } from '../read';
|
||||
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||
|
||||
type ResponseDataType = {
|
||||
success: boolean;
|
||||
@@ -118,17 +119,24 @@ export const useApiDatasetRequest = ({ apiServer }: { apiServer: APIFileServer }
|
||||
tmbId: string;
|
||||
apiFileId: string;
|
||||
customPdfParse?: boolean;
|
||||
}) => {
|
||||
const data = await request<APIFileContentResponse>(
|
||||
`/v1/file/content`,
|
||||
{ id: apiFileId },
|
||||
'GET'
|
||||
);
|
||||
}): Promise<ApiFileReadContentResponse> => {
|
||||
const data = await request<
|
||||
{
|
||||
title?: string;
|
||||
} & RequireOnlyOne<{
|
||||
content: string;
|
||||
previewUrl: string;
|
||||
}>
|
||||
>(`/v1/file/content`, { id: apiFileId }, 'GET');
|
||||
const title = data.title;
|
||||
const content = data.content;
|
||||
const previewUrl = data.previewUrl;
|
||||
|
||||
if (content) {
|
||||
return content;
|
||||
return {
|
||||
title,
|
||||
rawText: content
|
||||
};
|
||||
}
|
||||
if (previewUrl) {
|
||||
const rawText = await readFileRawTextByUrl({
|
||||
@@ -138,7 +146,10 @@ export const useApiDatasetRequest = ({ apiServer }: { apiServer: APIFileServer }
|
||||
relatedId: apiFileId,
|
||||
customPdfParse
|
||||
});
|
||||
return rawText;
|
||||
return {
|
||||
title,
|
||||
rawText
|
||||
};
|
||||
}
|
||||
return Promise.reject('Invalid content type: content or previewUrl is required');
|
||||
};
|
||||
|
||||
@@ -27,6 +27,11 @@ import { addDays } from 'date-fns';
|
||||
import { MongoDatasetDataText } from '../data/dataTextSchema';
|
||||
import { retryFn } from '@fastgpt/global/common/system/utils';
|
||||
import { getTrainingModeByCollection } from './utils';
|
||||
import {
|
||||
computeChunkSize,
|
||||
computeChunkSplitter,
|
||||
getLLMMaxChunkSize
|
||||
} from '@fastgpt/global/core/dataset/training/utils';
|
||||
|
||||
export const createCollectionAndInsertData = async ({
|
||||
dataset,
|
||||
@@ -54,18 +59,22 @@ export const createCollectionAndInsertData = async ({
|
||||
|
||||
const teamId = createCollectionParams.teamId;
|
||||
const tmbId = createCollectionParams.tmbId;
|
||||
// Chunk split params
|
||||
|
||||
// Set default params
|
||||
const trainingType =
|
||||
createCollectionParams.trainingType || DatasetCollectionDataProcessModeEnum.chunk;
|
||||
const chunkSize = createCollectionParams.chunkSize || 512;
|
||||
const chunkSplitter = createCollectionParams.chunkSplitter;
|
||||
const qaPrompt = createCollectionParams.qaPrompt;
|
||||
const usageName = createCollectionParams.name;
|
||||
const chunkSize = computeChunkSize({
|
||||
...createCollectionParams,
|
||||
trainingType,
|
||||
llmModel: getLLMModel(dataset.agentModel)
|
||||
});
|
||||
const chunkSplitter = computeChunkSplitter(createCollectionParams);
|
||||
|
||||
// 1. split chunks
|
||||
const chunks = rawText2Chunks({
|
||||
rawText,
|
||||
chunkLen: chunkSize,
|
||||
chunkSize,
|
||||
maxSize: getLLMMaxChunkSize(getLLMModel(dataset.agentModel)),
|
||||
overlapRatio: trainingType === DatasetCollectionDataProcessModeEnum.chunk ? 0.2 : 0,
|
||||
customReg: chunkSplitter ? [chunkSplitter] : [],
|
||||
isQAImport
|
||||
@@ -76,7 +85,7 @@ export const createCollectionAndInsertData = async ({
|
||||
teamId,
|
||||
insertLen: predictDataLimitLength(
|
||||
getTrainingModeByCollection({
|
||||
trainingType,
|
||||
trainingType: trainingType,
|
||||
autoIndexes: createCollectionParams.autoIndexes,
|
||||
imageIndex: createCollectionParams.imageIndex
|
||||
}),
|
||||
@@ -88,6 +97,9 @@ export const createCollectionAndInsertData = async ({
|
||||
// 3. create collection
|
||||
const { _id: collectionId } = await createOneCollection({
|
||||
...createCollectionParams,
|
||||
trainingType,
|
||||
chunkSize,
|
||||
chunkSplitter,
|
||||
|
||||
hashRawText: hashStr(rawText),
|
||||
rawTextLength: rawText.length,
|
||||
@@ -111,7 +123,7 @@ export const createCollectionAndInsertData = async ({
|
||||
const { billId: newBillId } = await createTrainingUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
appName: usageName,
|
||||
appName: createCollectionParams.name,
|
||||
billSource: UsageSourceEnum.training,
|
||||
vectorModel: getEmbeddingModel(dataset.vectorModel)?.name,
|
||||
agentModel: getLLMModel(dataset.agentModel)?.name,
|
||||
@@ -130,12 +142,13 @@ export const createCollectionAndInsertData = async ({
|
||||
agentModel: dataset.agentModel,
|
||||
vectorModel: dataset.vectorModel,
|
||||
vlmModel: dataset.vlmModel,
|
||||
indexSize: createCollectionParams.indexSize,
|
||||
mode: getTrainingModeByCollection({
|
||||
trainingType,
|
||||
trainingType: trainingType,
|
||||
autoIndexes: createCollectionParams.autoIndexes,
|
||||
imageIndex: createCollectionParams.imageIndex
|
||||
}),
|
||||
prompt: qaPrompt,
|
||||
prompt: createCollectionParams.qaPrompt,
|
||||
billId: traingBillId,
|
||||
data: chunks.map((item, index) => ({
|
||||
...item,
|
||||
@@ -207,11 +220,14 @@ export async function createOneCollection({
|
||||
// Parse settings
|
||||
customPdfParse,
|
||||
imageIndex,
|
||||
autoIndexes,
|
||||
|
||||
// Chunk settings
|
||||
trainingType = DatasetCollectionDataProcessModeEnum.chunk,
|
||||
autoIndexes,
|
||||
chunkSize = 512,
|
||||
trainingType,
|
||||
chunkSettingMode,
|
||||
chunkSplitMode,
|
||||
chunkSize,
|
||||
indexSize,
|
||||
chunkSplitter,
|
||||
qaPrompt,
|
||||
|
||||
@@ -249,11 +265,14 @@ export async function createOneCollection({
|
||||
// Parse settings
|
||||
customPdfParse,
|
||||
imageIndex,
|
||||
autoIndexes,
|
||||
|
||||
// Chunk settings
|
||||
trainingType,
|
||||
autoIndexes,
|
||||
chunkSettingMode,
|
||||
chunkSplitMode,
|
||||
chunkSize,
|
||||
indexSize,
|
||||
chunkSplitter,
|
||||
qaPrompt
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ const { Schema, model, models } = connectionMongo;
|
||||
import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type.d';
|
||||
import {
|
||||
DatasetCollectionTypeMap,
|
||||
DatasetCollectionDataProcessModeEnum
|
||||
DatasetCollectionDataProcessModeEnum,
|
||||
ChunkSettingModeEnum,
|
||||
DataChunkSplitModeEnum
|
||||
} from '@fastgpt/global/core/dataset/constants';
|
||||
import { DatasetCollectionName } from '../schema';
|
||||
import {
|
||||
@@ -94,11 +96,18 @@ const DatasetCollectionSchema = new Schema({
|
||||
type: String,
|
||||
enum: Object.values(DatasetCollectionDataProcessModeEnum)
|
||||
},
|
||||
chunkSize: {
|
||||
type: Number,
|
||||
required: true
|
||||
chunkSettingMode: {
|
||||
type: String,
|
||||
enum: Object.values(ChunkSettingModeEnum)
|
||||
},
|
||||
chunkSplitMode: {
|
||||
type: String,
|
||||
enum: Object.values(DataChunkSplitModeEnum)
|
||||
},
|
||||
chunkSize: Number,
|
||||
chunkSplitter: String,
|
||||
|
||||
indexSize: Number,
|
||||
qaPrompt: String
|
||||
});
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
DatasetCollectionSyncResultEnum,
|
||||
DatasetCollectionTypeEnum,
|
||||
DatasetSourceReadTypeEnum,
|
||||
DatasetTypeEnum,
|
||||
TrainingModeEnum
|
||||
} from '@fastgpt/global/core/dataset/constants';
|
||||
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
|
||||
@@ -19,6 +18,7 @@ import { readDatasetSourceRawText } from '../read';
|
||||
import { hashStr } from '@fastgpt/global/common/string/tools';
|
||||
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
|
||||
import { createCollectionAndInsertData, delCollection } from './controller';
|
||||
import { collectionCanSync } from '@fastgpt/global/core/dataset/collection/utils';
|
||||
|
||||
/**
|
||||
* get all collection by top collectionId
|
||||
@@ -137,10 +137,7 @@ export const collectionTagsToTagLabel = async ({
|
||||
export const syncCollection = async (collection: CollectionWithDatasetType) => {
|
||||
const dataset = collection.dataset;
|
||||
|
||||
if (
|
||||
collection.type !== DatasetCollectionTypeEnum.link &&
|
||||
dataset.type !== DatasetTypeEnum.apiDataset
|
||||
) {
|
||||
if (!collectionCanSync(collection.type)) {
|
||||
return Promise.reject(DatasetErrEnum.notSupportSync);
|
||||
}
|
||||
|
||||
@@ -155,15 +152,20 @@ export const syncCollection = async (collection: CollectionWithDatasetType) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (!collection.apiFileId) return Promise.reject('apiFileId is missing');
|
||||
if (!dataset.apiServer) return Promise.reject('apiServer not found');
|
||||
const sourceId = collection.apiFileId;
|
||||
|
||||
if (!sourceId) return Promise.reject('apiFileId is missing');
|
||||
|
||||
return {
|
||||
type: DatasetSourceReadTypeEnum.apiFile,
|
||||
sourceId: collection.apiFileId,
|
||||
apiServer: dataset.apiServer
|
||||
sourceId,
|
||||
apiServer: dataset.apiServer,
|
||||
feishuServer: dataset.feishuServer,
|
||||
yuqueServer: dataset.yuqueServer
|
||||
};
|
||||
})();
|
||||
const rawText = await readDatasetSourceRawText({
|
||||
|
||||
const { title, rawText } = await readDatasetSourceRawText({
|
||||
teamId: collection.teamId,
|
||||
tmbId: collection.tmbId,
|
||||
...sourceReadType
|
||||
@@ -196,7 +198,7 @@ export const syncCollection = async (collection: CollectionWithDatasetType) => {
|
||||
createCollectionParams: {
|
||||
teamId: collection.teamId,
|
||||
tmbId: collection.tmbId,
|
||||
name: collection.name,
|
||||
name: title || collection.name,
|
||||
datasetId: collection.datasetId,
|
||||
parentId: collection.parentId,
|
||||
type: collection.type,
|
||||
|
||||
@@ -79,9 +79,12 @@ export const readDatasetSourceRawText = async ({
|
||||
apiServer?: APIFileServer; // api dataset
|
||||
feishuServer?: FeishuServer; // feishu dataset
|
||||
yuqueServer?: YuqueServer; // yuque dataset
|
||||
}): Promise<string> => {
|
||||
}): Promise<{
|
||||
title?: string;
|
||||
rawText: string;
|
||||
}> => {
|
||||
if (type === DatasetSourceReadTypeEnum.fileLocal) {
|
||||
const { rawText } = await readFileContentFromMongo({
|
||||
const { filename, rawText } = await readFileContentFromMongo({
|
||||
teamId,
|
||||
tmbId,
|
||||
bucketName: BucketNameEnum.dataset,
|
||||
@@ -89,14 +92,20 @@ export const readDatasetSourceRawText = async ({
|
||||
isQAImport,
|
||||
customPdfParse
|
||||
});
|
||||
return rawText;
|
||||
return {
|
||||
title: filename,
|
||||
rawText
|
||||
};
|
||||
} else if (type === DatasetSourceReadTypeEnum.link) {
|
||||
const result = await urlsFetch({
|
||||
urlList: [sourceId],
|
||||
selector
|
||||
});
|
||||
|
||||
return result[0]?.content || '';
|
||||
return {
|
||||
title: result[0]?.title,
|
||||
rawText: result[0]?.content || ''
|
||||
};
|
||||
} else if (type === DatasetSourceReadTypeEnum.externalFile) {
|
||||
if (!externalFileId) return Promise.reject('FileId not found');
|
||||
const rawText = await readFileRawTextByUrl({
|
||||
@@ -106,9 +115,11 @@ export const readDatasetSourceRawText = async ({
|
||||
relatedId: externalFileId,
|
||||
customPdfParse
|
||||
});
|
||||
return rawText;
|
||||
return {
|
||||
rawText
|
||||
};
|
||||
} else if (type === DatasetSourceReadTypeEnum.apiFile) {
|
||||
const rawText = await readApiServerFileContent({
|
||||
const { title, rawText } = await readApiServerFileContent({
|
||||
apiServer,
|
||||
feishuServer,
|
||||
yuqueServer,
|
||||
@@ -116,9 +127,15 @@ export const readDatasetSourceRawText = async ({
|
||||
teamId,
|
||||
tmbId
|
||||
});
|
||||
return rawText;
|
||||
return {
|
||||
title,
|
||||
rawText
|
||||
};
|
||||
}
|
||||
return '';
|
||||
return {
|
||||
title: '',
|
||||
rawText: ''
|
||||
};
|
||||
};
|
||||
|
||||
export const readApiServerFileContent = async ({
|
||||
@@ -137,7 +154,10 @@ export const readApiServerFileContent = async ({
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
customPdfParse?: boolean;
|
||||
}) => {
|
||||
}): Promise<{
|
||||
title?: string;
|
||||
rawText: string;
|
||||
}> => {
|
||||
if (apiServer) {
|
||||
return useApiDatasetRequest({ apiServer }).getFileContent({
|
||||
teamId,
|
||||
@@ -148,7 +168,10 @@ export const readApiServerFileContent = async ({
|
||||
}
|
||||
|
||||
if (feishuServer || yuqueServer) {
|
||||
return POST<string>(`/core/dataset/systemApiDataset`, {
|
||||
return POST<{
|
||||
title?: string;
|
||||
rawText: string;
|
||||
}>(`/core/dataset/systemApiDataset`, {
|
||||
type: 'content',
|
||||
feishuServer,
|
||||
yuqueServer,
|
||||
@@ -162,7 +185,7 @@ export const readApiServerFileContent = async ({
|
||||
export const rawText2Chunks = ({
|
||||
rawText,
|
||||
isQAImport,
|
||||
chunkLen = 512,
|
||||
chunkSize = 512,
|
||||
...splitProps
|
||||
}: {
|
||||
rawText: string;
|
||||
@@ -175,7 +198,7 @@ export const rawText2Chunks = ({
|
||||
|
||||
const { chunks } = splitText2Chunks({
|
||||
text: rawText,
|
||||
chunkLen,
|
||||
chunkSize,
|
||||
...splitProps
|
||||
});
|
||||
|
||||
|
||||
@@ -134,12 +134,10 @@ export const filterDatasetDataByMaxTokens = async (
|
||||
let totalTokens = 0;
|
||||
|
||||
for await (const item of tokensScoreFilter) {
|
||||
results.push(item);
|
||||
|
||||
totalTokens += item.tokens;
|
||||
|
||||
if (totalTokens > maxTokens + 500) {
|
||||
break;
|
||||
}
|
||||
results.push(item);
|
||||
if (totalTokens > maxTokens) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,10 @@ import { getCollectionWithDataset } from '../controller';
|
||||
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
|
||||
import { PushDataToTrainingQueueProps } from '@fastgpt/global/core/dataset/training/type';
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
import {
|
||||
getLLMDefaultChunkSize,
|
||||
getLLMMaxChunkSize
|
||||
} from '../../../../global/core/dataset/training/utils';
|
||||
|
||||
export const lockTrainingDataByTeamId = async (teamId: string): Promise<any> => {
|
||||
try {
|
||||
@@ -55,6 +59,7 @@ export async function pushDataListToTrainingQueue({
|
||||
prompt,
|
||||
billId,
|
||||
mode = TrainingModeEnum.chunk,
|
||||
indexSize,
|
||||
session
|
||||
}: PushDataToTrainingQueueProps): Promise<PushDatasetDataResponse> {
|
||||
const getImageChunkMode = (data: PushDatasetDataChunkProps, mode: TrainingModeEnum) => {
|
||||
@@ -68,38 +73,41 @@ export async function pushDataListToTrainingQueue({
|
||||
}
|
||||
return mode;
|
||||
};
|
||||
|
||||
const vectorModelData = getEmbeddingModel(vectorModel);
|
||||
if (!vectorModelData) {
|
||||
return Promise.reject(i18nT('common:error_embedding_not_config'));
|
||||
}
|
||||
const agentModelData = getLLMModel(agentModel);
|
||||
if (!agentModelData) {
|
||||
return Promise.reject(i18nT('common:error_llm_not_config'));
|
||||
}
|
||||
if (mode === TrainingModeEnum.chunk || mode === TrainingModeEnum.auto) {
|
||||
prompt = undefined;
|
||||
}
|
||||
|
||||
const { model, maxToken, weight } = await (async () => {
|
||||
if (mode === TrainingModeEnum.chunk) {
|
||||
const vectorModelData = getEmbeddingModel(vectorModel);
|
||||
if (!vectorModelData) {
|
||||
return Promise.reject(i18nT('common:error_embedding_not_config'));
|
||||
}
|
||||
return {
|
||||
maxToken: vectorModelData.maxToken * 1.5,
|
||||
maxToken: getLLMMaxChunkSize(agentModelData),
|
||||
model: vectorModelData.model,
|
||||
weight: vectorModelData.weight
|
||||
};
|
||||
}
|
||||
|
||||
if (mode === TrainingModeEnum.qa || mode === TrainingModeEnum.auto) {
|
||||
const agentModelData = getLLMModel(agentModel);
|
||||
if (!agentModelData) {
|
||||
return Promise.reject(i18nT('common:error_llm_not_config'));
|
||||
}
|
||||
return {
|
||||
maxToken: agentModelData.maxContext * 0.8,
|
||||
maxToken: getLLMMaxChunkSize(agentModelData),
|
||||
model: agentModelData.model,
|
||||
weight: 0
|
||||
};
|
||||
}
|
||||
|
||||
if (mode === TrainingModeEnum.image) {
|
||||
const vllmModelData = getVlmModel(vlmModel);
|
||||
if (!vllmModelData) {
|
||||
return Promise.reject(i18nT('common:error_vlm_not_config'));
|
||||
}
|
||||
return {
|
||||
maxToken: vllmModelData.maxContext * 0.8,
|
||||
maxToken: getLLMMaxChunkSize(vllmModelData),
|
||||
model: vllmModelData.model,
|
||||
weight: 0
|
||||
};
|
||||
@@ -107,10 +115,6 @@ export async function pushDataListToTrainingQueue({
|
||||
|
||||
return Promise.reject(`Training mode "${mode}" is inValid`);
|
||||
})();
|
||||
// Filter redundant params
|
||||
if (mode === TrainingModeEnum.chunk || mode === TrainingModeEnum.auto) {
|
||||
prompt = undefined;
|
||||
}
|
||||
|
||||
// filter repeat or equal content
|
||||
const set = new Set();
|
||||
@@ -143,13 +147,13 @@ export async function pushDataListToTrainingQueue({
|
||||
|
||||
const text = item.q + item.a;
|
||||
|
||||
// Oversize llm tokens
|
||||
if (text.length > maxToken) {
|
||||
filterResult.overToken.push(item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (set.has(text)) {
|
||||
console.log('repeat', item);
|
||||
filterResult.repeat.push(item);
|
||||
} else {
|
||||
filterResult.success.push(item);
|
||||
@@ -182,6 +186,7 @@ export async function pushDataListToTrainingQueue({
|
||||
q: item.q,
|
||||
a: item.a,
|
||||
chunkIndex: item.chunkIndex ?? 0,
|
||||
indexSize,
|
||||
weight: weight ?? 0,
|
||||
indexes: item.indexes,
|
||||
retryCount: 5
|
||||
|
||||
@@ -76,6 +76,7 @@ const TrainingDataSchema = new Schema({
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
indexSize: Number,
|
||||
weight: {
|
||||
type: Number,
|
||||
default: 0
|
||||
|
||||
@@ -10,8 +10,7 @@ import type { ClassifyQuestionAgentItemType } from '@fastgpt/global/core/workflo
|
||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||
import { Prompt_CQJson } from '@fastgpt/global/core/ai/prompt/agent';
|
||||
import { getCQPrompt } from '@fastgpt/global/core/ai/prompt/agent';
|
||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||
import { getLLMModel } from '../../../ai/model';
|
||||
import { getHistories } from '../utils';
|
||||
@@ -23,6 +22,7 @@ import { loadRequestMessages } from '../../../chat/utils';
|
||||
import { llmCompletionsBodyFormat } from '../../../ai/utils';
|
||||
import { addLog } from '../../../../common/system/log';
|
||||
import { ModelTypeEnum } from '../../../../../global/core/ai/model';
|
||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.aiModel]: string;
|
||||
@@ -99,7 +99,8 @@ const completions = async ({
|
||||
cqModel,
|
||||
externalProvider,
|
||||
histories,
|
||||
params: { agents, systemPrompt = '', userChatInput }
|
||||
params: { agents, systemPrompt = '', userChatInput },
|
||||
node: { version }
|
||||
}: ActionProps) => {
|
||||
const messages: ChatItemType[] = [
|
||||
{
|
||||
@@ -108,7 +109,7 @@ const completions = async ({
|
||||
{
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: replaceVariable(cqModel.customCQPrompt || Prompt_CQJson, {
|
||||
content: replaceVariable(cqModel.customCQPrompt || getCQPrompt(version), {
|
||||
systemPrompt: systemPrompt || 'null',
|
||||
typeList: agents
|
||||
.map((item) => `{"类型ID":"${item.key}", "问题类型":"${item.value}"}`)
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
} from '@fastgpt/global/core/workflow/constants';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { Prompt_ExtractJson } from '@fastgpt/global/core/ai/prompt/agent';
|
||||
import { replaceVariable, sliceJsonStr } from '@fastgpt/global/common/string/tools';
|
||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||
import { getHistories } from '../utils';
|
||||
@@ -33,6 +32,10 @@ import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/ty
|
||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||
import { llmCompletionsBodyFormat } from '../../../ai/utils';
|
||||
import { ModelTypeEnum } from '../../../../../global/core/ai/model';
|
||||
import {
|
||||
getExtractJsonPrompt,
|
||||
getExtractJsonToolPrompt
|
||||
} from '@fastgpt/global/core/ai/prompt/agent';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.history]?: ChatItemType[];
|
||||
@@ -154,7 +157,8 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
|
||||
const getFunctionCallSchema = async ({
|
||||
extractModel,
|
||||
histories,
|
||||
params: { content, extractKeys, description }
|
||||
params: { content, extractKeys, description },
|
||||
node: { version }
|
||||
}: ActionProps) => {
|
||||
const messages: ChatItemType[] = [
|
||||
...histories,
|
||||
@@ -164,15 +168,10 @@ const getFunctionCallSchema = async ({
|
||||
{
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: `我正在执行一个函数,需要你提供一些参数,请以 JSON 字符串格式返回这些参数,要求:
|
||||
"""
|
||||
${description ? `- ${description}` : ''}
|
||||
- 不是每个参数都是必须生成的,如果没有合适的参数值,不要生成该参数,或返回空字符串。
|
||||
- 需要结合前面的对话内容,一起生成合适的参数。
|
||||
"""
|
||||
|
||||
本次输入内容: """${content}"""
|
||||
`
|
||||
content: replaceVariable(getExtractJsonToolPrompt(version), {
|
||||
description,
|
||||
content
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -334,7 +333,8 @@ const completions = async ({
|
||||
extractModel,
|
||||
externalProvider,
|
||||
histories,
|
||||
params: { content, extractKeys, description = 'No special requirements' }
|
||||
params: { content, extractKeys, description = 'No special requirements' },
|
||||
node: { version }
|
||||
}: ActionProps) => {
|
||||
const messages: ChatItemType[] = [
|
||||
{
|
||||
@@ -343,23 +343,26 @@ const completions = async ({
|
||||
{
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: replaceVariable(extractModel.customExtractPrompt || Prompt_ExtractJson, {
|
||||
description,
|
||||
json: extractKeys
|
||||
.map((item) => {
|
||||
const valueType = item.valueType || 'string';
|
||||
if (valueType !== 'string' && valueType !== 'number') {
|
||||
item.enum = undefined;
|
||||
}
|
||||
content: replaceVariable(
|
||||
extractModel.customExtractPrompt || getExtractJsonPrompt(version),
|
||||
{
|
||||
description,
|
||||
json: extractKeys
|
||||
.map((item) => {
|
||||
const valueType = item.valueType || 'string';
|
||||
if (valueType !== 'string' && valueType !== 'number') {
|
||||
item.enum = undefined;
|
||||
}
|
||||
|
||||
return `{"type":${item.valueType || 'string'}, "key":"${item.key}", "description":"${item.desc}" ${
|
||||
item.enum ? `, "enum":"[${item.enum.split('\n')}]"` : ''
|
||||
}}`;
|
||||
})
|
||||
.join('\n'),
|
||||
text: `${histories.map((item) => `${item.obj}:${chatValue2RuntimePrompt(item.value).text}`).join('\n')}
|
||||
return `{"type":${item.valueType || 'string'}, "key":"${item.key}", "description":"${item.desc}" ${
|
||||
item.enum ? `, "enum":"[${item.enum.split('\n')}]"` : ''
|
||||
}}`;
|
||||
})
|
||||
.join('\n'),
|
||||
text: `${histories.map((item) => `${item.obj}:${chatValue2RuntimePrompt(item.value).text}`).join('\n')}
|
||||
Human: ${content}`
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -28,10 +28,10 @@ import { filterToolResponseToPreview } from './utils';
|
||||
import { InteractiveNodeResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { getFileContentFromLinks, getHistoryFileLinks } from '../../tools/readFiles';
|
||||
import { parseUrlToFileType } from '@fastgpt/global/common/file/tools';
|
||||
import { Prompt_DocumentQuote } from '@fastgpt/global/core/ai/prompt/AIChat';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { postTextCensor } from '../../../../../common/api/requestPlusApi';
|
||||
import { ModelTypeEnum } from '@fastgpt/global/core/ai/model';
|
||||
import { getDocumentQuotePrompt } from '@fastgpt/global/core/ai/prompt/AIChat';
|
||||
|
||||
type Response = DispatchNodeResultType<{
|
||||
[NodeOutputKeyEnum.answerText]: string;
|
||||
@@ -40,7 +40,7 @@ type Response = DispatchNodeResultType<{
|
||||
|
||||
export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<Response> => {
|
||||
const {
|
||||
node: { nodeId, name, isEntry },
|
||||
node: { nodeId, name, isEntry, version },
|
||||
runtimeNodes,
|
||||
runtimeEdges,
|
||||
histories,
|
||||
@@ -118,7 +118,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
||||
toolModel.defaultSystemChatPrompt,
|
||||
systemPrompt,
|
||||
documentQuoteText
|
||||
? replaceVariable(Prompt_DocumentQuote, {
|
||||
? replaceVariable(getDocumentQuotePrompt(version), {
|
||||
quote: documentQuoteText
|
||||
})
|
||||
: ''
|
||||
|
||||
@@ -24,10 +24,9 @@ import {
|
||||
runtimePrompt2ChatsValue
|
||||
} from '@fastgpt/global/core/chat/adapt';
|
||||
import {
|
||||
Prompt_DocumentQuote,
|
||||
Prompt_userQuotePromptList,
|
||||
Prompt_QuoteTemplateList,
|
||||
Prompt_systemQuotePromptList
|
||||
getQuoteTemplate,
|
||||
getQuotePrompt,
|
||||
getDocumentQuotePrompt
|
||||
} from '@fastgpt/global/core/ai/prompt/AIChat';
|
||||
import type { AIChatNodeProps } from '@fastgpt/global/core/workflow/runtime/type.d';
|
||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||
@@ -70,7 +69,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
||||
stream = false,
|
||||
externalProvider,
|
||||
histories,
|
||||
node: { name },
|
||||
node: { name, version },
|
||||
query,
|
||||
runningUserInfo,
|
||||
workflowStreamResponse,
|
||||
@@ -115,7 +114,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
||||
filterDatasetQuote({
|
||||
quoteQA,
|
||||
model: modelConstantsData,
|
||||
quoteTemplate
|
||||
quoteTemplate: quoteTemplate || getQuoteTemplate(version)
|
||||
}),
|
||||
getMultiInput({
|
||||
histories: chatHistories,
|
||||
@@ -147,6 +146,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
||||
datasetQuoteText,
|
||||
aiChatQuoteRole,
|
||||
datasetQuotePrompt: quotePrompt,
|
||||
version,
|
||||
userChatInput,
|
||||
systemPrompt,
|
||||
userFiles,
|
||||
@@ -326,10 +326,10 @@ async function filterDatasetQuote({
|
||||
}: {
|
||||
quoteQA: ChatProps['params']['quoteQA'];
|
||||
model: LLMModelItemType;
|
||||
quoteTemplate?: string;
|
||||
quoteTemplate: string;
|
||||
}) {
|
||||
function getValue(item: SearchDataResponseItemType, index: number) {
|
||||
return replaceVariable(quoteTemplate || Prompt_QuoteTemplateList[0].value, {
|
||||
return replaceVariable(quoteTemplate, {
|
||||
id: item.id,
|
||||
q: item.q,
|
||||
a: item.a,
|
||||
@@ -425,6 +425,7 @@ async function getChatMessages({
|
||||
datasetQuotePrompt = '',
|
||||
datasetQuoteText,
|
||||
useDatasetQuote,
|
||||
version,
|
||||
histories = [],
|
||||
systemPrompt,
|
||||
userChatInput,
|
||||
@@ -437,6 +438,7 @@ async function getChatMessages({
|
||||
aiChatQuoteRole: AiChatQuoteRoleType; // user: replace user prompt; system: replace system prompt
|
||||
datasetQuotePrompt?: string;
|
||||
datasetQuoteText: string;
|
||||
version: string;
|
||||
|
||||
useDatasetQuote: boolean;
|
||||
histories: ChatItemType[];
|
||||
@@ -451,11 +453,7 @@ async function getChatMessages({
|
||||
const quoteRole =
|
||||
aiChatQuoteRole === 'user' || datasetQuotePrompt.includes('{{question}}') ? 'user' : 'system';
|
||||
|
||||
const datasetQuotePromptTemplate = datasetQuotePrompt
|
||||
? datasetQuotePrompt
|
||||
: quoteRole === 'user'
|
||||
? Prompt_userQuotePromptList[0].value
|
||||
: Prompt_systemQuotePromptList[0].value;
|
||||
const datasetQuotePromptTemplate = datasetQuotePrompt || getQuotePrompt(version, quoteRole);
|
||||
|
||||
// Reset user input, add dataset quote to user input
|
||||
const replaceInputValue =
|
||||
@@ -477,7 +475,7 @@ async function getChatMessages({
|
||||
})
|
||||
: '',
|
||||
documentQuoteText
|
||||
? replaceVariable(Prompt_DocumentQuote, {
|
||||
? replaceVariable(getDocumentQuotePrompt(version), {
|
||||
quote: documentQuoteText
|
||||
})
|
||||
: ''
|
||||
|
||||
@@ -88,9 +88,9 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
||||
: {}),
|
||||
runningAppInfo: {
|
||||
id: String(plugin.id),
|
||||
// 如果是系统插件,则使用当前团队的 teamId 和 tmbId
|
||||
// 如果系统插件有 teamId 和 tmbId,则使用系统插件的 teamId 和 tmbId(管理员指定了插件作为系统插件)
|
||||
teamId: plugin.teamId || runningAppInfo.teamId,
|
||||
tmbId: pluginData?.tmbId || runningAppInfo.tmbId
|
||||
tmbId: plugin.tmbId || runningAppInfo.tmbId
|
||||
},
|
||||
variables: runtimeVariables,
|
||||
query: getPluginRunUserQuery({
|
||||
|
||||
@@ -117,6 +117,9 @@ export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
|
||||
return Boolean(value);
|
||||
}
|
||||
try {
|
||||
if (WorkflowIOValueTypeEnum.arrayString && typeof value === 'string') {
|
||||
return [value];
|
||||
}
|
||||
if (
|
||||
type &&
|
||||
[
|
||||
@@ -124,7 +127,12 @@ export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
|
||||
WorkflowIOValueTypeEnum.chatHistory,
|
||||
WorkflowIOValueTypeEnum.datasetQuote,
|
||||
WorkflowIOValueTypeEnum.selectApp,
|
||||
WorkflowIOValueTypeEnum.selectDataset
|
||||
WorkflowIOValueTypeEnum.selectDataset,
|
||||
WorkflowIOValueTypeEnum.arrayString,
|
||||
WorkflowIOValueTypeEnum.arrayNumber,
|
||||
WorkflowIOValueTypeEnum.arrayBoolean,
|
||||
WorkflowIOValueTypeEnum.arrayObject,
|
||||
WorkflowIOValueTypeEnum.arrayAny
|
||||
].includes(type) &&
|
||||
typeof value !== 'object'
|
||||
) {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"mammoth": "^1.6.0",
|
||||
"mongoose": "^8.10.1",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"next": "14.2.24",
|
||||
"next": "14.2.25",
|
||||
"nextjs-cors": "^2.2.0",
|
||||
"node-cron": "^3.0.3",
|
||||
"node-xlsx": "^0.24.0",
|
||||
|
||||
@@ -4,14 +4,14 @@ import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
import { authUserPer } from '../user/auth';
|
||||
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
|
||||
/*
|
||||
/*
|
||||
Team manager can control org
|
||||
*/
|
||||
export const authOrgMember = async ({
|
||||
orgIds,
|
||||
...props
|
||||
}: {
|
||||
orgIds: string | string[];
|
||||
orgIds?: string | string[];
|
||||
} & AuthModeType): Promise<AuthResponseType> => {
|
||||
const result = await authUserPer({
|
||||
...props,
|
||||
|
||||
@@ -90,6 +90,6 @@ export async function createRootOrg({
|
||||
path: ''
|
||||
}
|
||||
],
|
||||
{ session }
|
||||
{ session, ordered: true }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,14 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp
|
||||
};
|
||||
}
|
||||
|
||||
export const getTeamOwner = async (teamId: string) => {
|
||||
const tmb = await MongoTeamMember.findOne({
|
||||
teamId,
|
||||
role: TeamMemberRoleEnum.owner
|
||||
}).lean();
|
||||
return tmb;
|
||||
};
|
||||
|
||||
export async function getTmbInfoByTmbId({ tmbId }: { tmbId: string }) {
|
||||
if (!tmbId) {
|
||||
return Promise.reject('tmbId or userId is required');
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import {
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import { connectionMongo, getMongoModel } from '../../../../common/mongo';
|
||||
import { InvitationSchemaType } from './type';
|
||||
import addDays from 'date-fns/esm/fp/addDays/index.js';
|
||||
const { Schema } = connectionMongo;
|
||||
|
||||
export const InvitationCollectionName = 'team_invitation_links';
|
||||
|
||||
const InvitationSchema = new Schema({
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamCollectionName,
|
||||
required: true
|
||||
},
|
||||
usedTimesLimit: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
enum: [1, -1]
|
||||
},
|
||||
forbidden: Boolean,
|
||||
expires: Date,
|
||||
description: String,
|
||||
members: {
|
||||
type: [String],
|
||||
default: []
|
||||
}
|
||||
});
|
||||
|
||||
InvitationSchema.virtual('team', {
|
||||
ref: TeamCollectionName,
|
||||
localField: 'teamId',
|
||||
foreignField: '_id',
|
||||
justOne: true
|
||||
});
|
||||
|
||||
try {
|
||||
InvitationSchema.index({ teamId: 1 });
|
||||
InvitationSchema.index({ expires: 1 }, { expireAfterSeconds: 30 * 24 * 60 * 60 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoInvitationLink = getMongoModel<InvitationSchemaType>(
|
||||
InvitationCollectionName,
|
||||
InvitationSchema
|
||||
);
|
||||
@@ -2,6 +2,7 @@ import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
|
||||
|
||||
export type InvitationSchemaType = {
|
||||
_id: string;
|
||||
linkId: string;
|
||||
teamId: string;
|
||||
usedTimesLimit?: number;
|
||||
forbidden?: boolean;
|
||||
@@ -25,11 +26,10 @@ export type InvitationLinkCreateType = {
|
||||
expires: InvitationLinkExpiresType;
|
||||
usedTimesLimit: 1 | -1;
|
||||
};
|
||||
export type InvitationLinkUpdateType = Partial<
|
||||
Omit<InvitationSchemaType, 'members' | 'teamId' | '_id'>
|
||||
> & {
|
||||
linkId: string;
|
||||
};
|
||||
|
||||
// export type InvitationLinkUpdateType = Partial<
|
||||
// Omit<InvitationSchemaType, 'members' | 'teamId' | '_id'>
|
||||
// >;
|
||||
|
||||
export type InvitationInfoType = InvitationSchemaType & {
|
||||
teamAvatar: string;
|
||||
|
||||
8
packages/web/common/zustand/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import zustandNpm from 'zustand';
|
||||
|
||||
export * from 'zustand';
|
||||
export * from 'zustand/middleware';
|
||||
export * from 'zustand/middleware/immer';
|
||||
|
||||
export * from 'zustand';
|
||||
export default zustandNpm;
|
||||
@@ -10,7 +10,16 @@ import { Box, Flex } from '@chakra-ui/react';
|
||||
* @param [groupId] - group id to make the key unique
|
||||
* @returns
|
||||
*/
|
||||
function AvatarGroup({ avatars, max = 3 }: { max?: number; avatars: string[] }) {
|
||||
function AvatarGroup({
|
||||
avatars,
|
||||
max = 3,
|
||||
total
|
||||
}: {
|
||||
max?: number;
|
||||
avatars: string[];
|
||||
total?: number;
|
||||
}) {
|
||||
const remain = (total ?? avatars.length) - max;
|
||||
return (
|
||||
<Flex position="relative">
|
||||
{avatars.slice(0, max).map((avatar, index) => (
|
||||
@@ -24,10 +33,10 @@ function AvatarGroup({ avatars, max = 3 }: { max?: number; avatars: string[] })
|
||||
borderRadius={'50%'}
|
||||
/>
|
||||
))}
|
||||
{avatars.length > max && (
|
||||
{remain > 0 && (
|
||||
<Box
|
||||
position="relative"
|
||||
left={`${(max - 1) * 15}px`}
|
||||
left={`${(max - 1) * 15 + 15}px`}
|
||||
w={'24px'}
|
||||
h={'24px'}
|
||||
borderRadius="50%"
|
||||
@@ -37,7 +46,7 @@ function AvatarGroup({ avatars, max = 3 }: { max?: number; avatars: string[] })
|
||||
fontSize="sm"
|
||||
color="myGray.500"
|
||||
>
|
||||
+{avatars.length - max}
|
||||
+{String(remain)}
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
@@ -72,7 +72,7 @@ const EditFolderModal = ({
|
||||
{...register('name', { required: true })}
|
||||
bg={'myGray.50'}
|
||||
autoFocus
|
||||
maxLength={20}
|
||||
maxLength={100}
|
||||
/>
|
||||
</Box>
|
||||
<Box mt={4}>
|
||||
|
||||
67
packages/web/components/common/Radio/RadioGroup.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import { Box, Flex, Grid, type GridProps, HStack } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import QuestionTip from '../MyTooltip/QuestionTip';
|
||||
|
||||
type Props<T> = Omit<GridProps, 'onChange'> & {
|
||||
list: {
|
||||
title: string;
|
||||
value: T;
|
||||
tooltip?: string;
|
||||
}[];
|
||||
value: T;
|
||||
defaultBg?: string;
|
||||
activeBg?: string;
|
||||
onChange: (e: T) => void;
|
||||
};
|
||||
|
||||
const RadioGroup = <T = any,>({ list, value, onChange, ...props }: Props<T>) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Flex gap={[3, 5]} fontSize={['sm', 'md']} alignItems={'center'} {...props}>
|
||||
{list.map((item) => (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
key={item.value as any}
|
||||
cursor={'pointer'}
|
||||
userSelect={'none'}
|
||||
gap={1}
|
||||
onClick={() => onChange(item.value)}
|
||||
>
|
||||
<Box
|
||||
w={'18px'}
|
||||
h={'18px'}
|
||||
borderWidth={'2.4px'}
|
||||
borderColor={value === item.value ? 'primary.015' : 'transparent'}
|
||||
borderRadius={'50%'}
|
||||
>
|
||||
<Flex
|
||||
w={'100%'}
|
||||
h={'100%'}
|
||||
borderWidth={'1px'}
|
||||
borderColor={value === item.value ? 'primary.600' : 'borderColor.high'}
|
||||
bg={value === item.value ? 'primary.1' : 'transparent'}
|
||||
borderRadius={'50%'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
>
|
||||
<Box
|
||||
w={'5px'}
|
||||
h={'5px'}
|
||||
borderRadius={'50%'}
|
||||
bg={value === item.value ? 'primary.600' : 'transparent'}
|
||||
/>
|
||||
</Flex>
|
||||
</Box>
|
||||
<HStack spacing={1} color={'myGray.900'} whiteSpace={'nowrap'} fontSize={'sm'}>
|
||||
<Box>{typeof item.title === 'string' ? t(item.title as any) : item.title}</Box>
|
||||
{!!item.tooltip && <QuestionTip label={item.tooltip} color={'myGray.600'} />}
|
||||
</HStack>
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default RadioGroup;
|
||||
@@ -3,6 +3,11 @@ import { useToast } from './useToast';
|
||||
import { useCallback } from 'react';
|
||||
import { hasHttps } from '../common/system/utils';
|
||||
import { isProduction } from '@fastgpt/global/common/system/constants';
|
||||
import MyModal from '../components/common/MyModal';
|
||||
import React from 'react';
|
||||
import { Box, ModalBody } from '@chakra-ui/react';
|
||||
import Tag from '../components/common/Tag';
|
||||
import { useCommonStore } from '../store/useCommonStore';
|
||||
|
||||
/**
|
||||
* copy text data
|
||||
@@ -10,11 +15,12 @@ import { isProduction } from '@fastgpt/global/common/system/constants';
|
||||
export const useCopyData = () => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { setCopyContent } = useCommonStore();
|
||||
|
||||
const copyData = useCallback(
|
||||
async (
|
||||
data: string,
|
||||
title: string | null = t('common:common.Copy Successful'),
|
||||
title: string | null | undefined = t('common:common.Copy Successful'),
|
||||
duration = 1000
|
||||
) => {
|
||||
data = data.trim();
|
||||
@@ -22,37 +28,18 @@ export const useCopyData = () => {
|
||||
try {
|
||||
if ((hasHttps() || !isProduction) && navigator.clipboard) {
|
||||
await navigator.clipboard.writeText(data);
|
||||
if (title) {
|
||||
toast({
|
||||
title,
|
||||
status: 'success',
|
||||
duration
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw new Error('');
|
||||
}
|
||||
} catch (error) {
|
||||
// console.log(error);
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = data;
|
||||
textarea.style.position = 'absolute';
|
||||
textarea.style.opacity = '0';
|
||||
document.body.appendChild(textarea);
|
||||
|
||||
textarea.select();
|
||||
const res = document.execCommand('copy');
|
||||
document.body.removeChild(textarea);
|
||||
|
||||
if (!res) {
|
||||
return toast({
|
||||
title: t('common:common.Copy_failed'),
|
||||
status: 'error',
|
||||
duration
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (title) {
|
||||
toast({
|
||||
title,
|
||||
status: 'success',
|
||||
duration
|
||||
});
|
||||
setCopyContent(data);
|
||||
}
|
||||
},
|
||||
[t, toast]
|
||||
@@ -62,3 +49,29 @@ export const useCopyData = () => {
|
||||
copyData
|
||||
};
|
||||
};
|
||||
|
||||
export const ManualCopyModal = () => {
|
||||
const { t } = useTranslation();
|
||||
const { copyContent, setCopyContent } = useCommonStore();
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen={!!copyContent}
|
||||
iconSrc="copy"
|
||||
iconColor="primary.600"
|
||||
title={t('common:common.Copy')}
|
||||
maxW={['90vw', '500px']}
|
||||
w={'100%'}
|
||||
onClose={() => setCopyContent(undefined)}
|
||||
>
|
||||
<ModalBody>
|
||||
<Tag w={'100%'} colorSchema="blue">
|
||||
{t('common:can_copy_content_tip')}
|
||||
</Tag>
|
||||
<Box mt={3} borderRadius={'md'} p={3} border={'base'} userSelect={'all'}>
|
||||
{copyContent}
|
||||
</Box>
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"channel_status_unknown": "unknown",
|
||||
"channel_type": "Manufacturer",
|
||||
"clear_model": "Clear the model",
|
||||
"confirm_delete_channel": "Confirm the deletion of the [{{name}}] channel?",
|
||||
"copy_model_id_success": "Copyed model id",
|
||||
"create_channel": "Added channels",
|
||||
"default_url": "Default address",
|
||||
@@ -36,6 +37,7 @@
|
||||
"model_tokens": "Input/Output tokens",
|
||||
"request_at": "Request time",
|
||||
"request_duration": "Request duration: {{duration}}s",
|
||||
"retry_times": "Number of retry times",
|
||||
"running_test": "In testing",
|
||||
"search_model": "Search for models",
|
||||
"select_channel": "Select a channel name",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"delete_org": "Delete organization",
|
||||
"edit_info": "Edit information",
|
||||
"edit_org_info": "Edit organization information",
|
||||
"expires": "Expiration",
|
||||
"expires": "Expiration time",
|
||||
"forbid_hint": "After forbidden, this invitation link will become invalid. This action is irreversible. Are you sure you want to deactivate?",
|
||||
"forbid_success": "Forbid success",
|
||||
"forbidden": "Forbidden",
|
||||
@@ -29,6 +29,7 @@
|
||||
"has_forbidden": "Forbidden",
|
||||
"has_invited": "Invited",
|
||||
"ignore": "Ignore",
|
||||
"invitation_copy_link": "[{{systemName}}] {{userName}} invites you to join the {{teamName}} team, link: {{url}}",
|
||||
"invitation_link_auto_clean_hint": "Expired links will be automatically cleaned up after 30 days",
|
||||
"invitation_link_description": "Link description",
|
||||
"invitation_link_list": "Invitation link list",
|
||||
@@ -58,6 +59,5 @@
|
||||
"user_team_invite_member": "Invite members",
|
||||
"user_team_leave_team": "Leave the team",
|
||||
"user_team_leave_team_failed": "Failure to leave the team",
|
||||
"waiting": "To be accepted",
|
||||
"invitation_copy_link": "[{{systemName}}] {{userName}} invites you to join the {{teamName}} team, link: {{url}}"
|
||||
"waiting": "To be accepted"
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
"to_dataset": "Go to the Knowledge Base",
|
||||
"unsupported_file_type": "Unsupported file types",
|
||||
"upload": "Upload",
|
||||
"variable_invisable_in_share": "Custom variables are not visible in login-free links",
|
||||
"view_citations": "View References",
|
||||
"web_site_sync": "Web Site Sync"
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"add_new_param": "Add new param",
|
||||
"app.templateMarket.templateTags.Writing": "Writing",
|
||||
"back": "Back",
|
||||
"can_copy_content_tip": "It is not possible to copy automatically using the browser, please manually copy the following content",
|
||||
"chose_condition": "Choose Condition",
|
||||
"chosen": "Chosen",
|
||||
"classification": "Classification",
|
||||
@@ -128,7 +129,6 @@
|
||||
"common.Continue_Adding": "Continue adding",
|
||||
"common.Copy": "Copy",
|
||||
"common.Copy Successful": "Copied Successfully",
|
||||
"common.Copy_failed": "Copy Failed, Please Copy Manually",
|
||||
"common.Create Failed": "Creation Failed",
|
||||
"common.Create Success": "Created Successfully",
|
||||
"common.Create Time": "Creation Time",
|
||||
@@ -569,8 +569,6 @@
|
||||
"core.dataset.import.Custom process": "Custom Rules",
|
||||
"core.dataset.import.Custom process desc": "Customize segmentation and preprocessing rules",
|
||||
"core.dataset.import.Custom prompt": "Custom Prompt",
|
||||
"core.dataset.import.Custom split char": "Custom Separator",
|
||||
"core.dataset.import.Custom split char Tips": "Allows you to segment based on custom separators. Usually used for pre-processed data, using specific separators for precise segmentation.",
|
||||
"core.dataset.import.Custom text": "Custom Text",
|
||||
"core.dataset.import.Custom text desc": "Manually enter a piece of text as a dataset",
|
||||
"core.dataset.import.Data process params": "Data Processing Parameters",
|
||||
@@ -646,11 +644,11 @@
|
||||
"core.dataset.training.Auto mode": "Auto index",
|
||||
"core.dataset.training.Auto mode Tip": "Increase the semantic richness of data blocks by generating related questions and summaries through sub-indexes and calling models, making it more conducive to retrieval. Requires more storage space and increases AI call times.",
|
||||
"core.dataset.training.Chunk mode": "Chunk",
|
||||
"core.dataset.training.Full": "Estimated Over 5 Minutes",
|
||||
"core.dataset.training.Full": "It is expected to be more than 20 minutes",
|
||||
"core.dataset.training.Leisure": "Idle",
|
||||
"core.dataset.training.QA mode": "QA",
|
||||
"core.dataset.training.Vector queue": "Index Queue",
|
||||
"core.dataset.training.Waiting": "Estimated 5 Minutes",
|
||||
"core.dataset.training.Waiting": "Estimated 20 minutes",
|
||||
"core.dataset.training.Website Sync": "Website Sync",
|
||||
"core.dataset.training.tag": "Queue Status",
|
||||
"core.dataset.website.Base Url": "Base URL",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"api_url": "API Url",
|
||||
"auto_indexes": "Automatically generate supplementary indexes",
|
||||
"auto_indexes_tips": "Additional index generation is performed through large models to improve semantic richness and improve retrieval accuracy.",
|
||||
"auto_training_queue": "Enhanced index queueing",
|
||||
"chunk_max_tokens": "max_tokens",
|
||||
"close_auto_sync": "Are you sure you want to turn off automatic sync?",
|
||||
"collection.Create update time": "Creation/Update Time",
|
||||
@@ -25,7 +26,7 @@
|
||||
"core.dataset.import.Adjust parameters": "Adjust parameters",
|
||||
"custom_data_process_params": "Custom",
|
||||
"custom_data_process_params_desc": "Customize data processing rules",
|
||||
"data.ideal_chunk_length": "ideal block length",
|
||||
"custom_split_sign_tip": "Allows you to chunk according to custom delimiters. \nUsually used for processed data, using specific separators for precise chunking. \nYou can use the | symbol to represent multiple splitters, such as: \".|.\" to represent a period in Chinese and English.\n\nTry to avoid using special symbols related to regular, such as: * () [] {}, etc.",
|
||||
"data_amount": "{{dataAmount}} Datas, {{indexAmount}} Indexes",
|
||||
"data_index_num": "Index {{index}}",
|
||||
"data_process_params": "Params",
|
||||
@@ -51,10 +52,9 @@
|
||||
"file_model_function_tip": "Enhances indexing and QA generation",
|
||||
"filename": "Filename",
|
||||
"folder_dataset": "Folder",
|
||||
"ideal_chunk_length": "ideal block length",
|
||||
"ideal_chunk_length_tips": "Segment according to the end symbol and combine multiple segments into one block. This value determines the estimated size of the block, if there is any fluctuation.",
|
||||
"image_auto_parse": "Automatic image indexing",
|
||||
"image_auto_parse_tips": "Call VLM to automatically label the pictures in the document and generate additional search indexes",
|
||||
"image_training_queue": "Queue of image processing",
|
||||
"import.Auto mode Estimated Price Tips": "The text understanding model needs to be called, which requires more points: {{price}} points/1K tokens",
|
||||
"import.Embedding Estimated Price Tips": "Only use the index model and consume a small amount of AI points: {{price}} points/1K tokens",
|
||||
"import_confirm": "Confirm upload",
|
||||
@@ -65,6 +65,8 @@
|
||||
"import_param_setting": "Parameter settings",
|
||||
"import_select_file": "Select a file",
|
||||
"import_select_link": "Enter link",
|
||||
"index_size": "Index size",
|
||||
"index_size_tips": "When vectorized, the system will automatically further segment the blocks according to this size.",
|
||||
"is_open_schedule": "Enable scheduled synchronization",
|
||||
"keep_image": "Keep the picture",
|
||||
"move.hint": "After moving, the selected knowledge base/folder will inherit the permission settings of the new folder, and the original permission settings will become invalid.",
|
||||
@@ -78,7 +80,7 @@
|
||||
"permission.des.write": "Ability to add and change knowledge base content",
|
||||
"preview_chunk": "Preview chunks",
|
||||
"preview_chunk_empty": "Unable to read the contents of the file",
|
||||
"preview_chunk_intro": "Display up to 10 pieces",
|
||||
"preview_chunk_intro": "A total of {{total}} blocks, up to 10",
|
||||
"preview_chunk_not_selected": "Click on the file on the left to preview",
|
||||
"rebuild_embedding_start_tip": "Index model switching task has started",
|
||||
"rebuilding_index_count": "Number of indexes being rebuilt: {{count}}",
|
||||
@@ -86,6 +88,16 @@
|
||||
"retain_collection": "Adjust Training Parameters",
|
||||
"retrain_task_submitted": "The retraining task has been submitted",
|
||||
"same_api_collection": "The same API set exists",
|
||||
"split_chunk_char": "Block by specified splitter",
|
||||
"split_chunk_size": "Block by length",
|
||||
"split_sign_break": "1 newline character",
|
||||
"split_sign_break2": "2 newline characters",
|
||||
"split_sign_custom": "Customize",
|
||||
"split_sign_exclamatiob": "exclamation mark",
|
||||
"split_sign_null": "Not set",
|
||||
"split_sign_period": "period",
|
||||
"split_sign_question": "question mark",
|
||||
"split_sign_semicolon": "semicolon",
|
||||
"start_sync_website_tip": "Confirm to start synchronizing data? \nThe old data will be deleted and retrieved again, please confirm!",
|
||||
"sync_collection_failed": "Synchronization collection error, please check whether the source file can be accessed normally",
|
||||
"sync_schedule": "Timing synchronization",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"channel_status_unknown": "未知",
|
||||
"channel_type": "厂商",
|
||||
"clear_model": "清空模型",
|
||||
"confirm_delete_channel": "确认删除 【{{name}}】渠道?",
|
||||
"copy_model_id_success": "已复制模型id",
|
||||
"create_channel": "新增渠道",
|
||||
"default_url": "默认地址",
|
||||
@@ -36,6 +37,7 @@
|
||||
"model_tokens": "输入/输出 Tokens",
|
||||
"request_at": "请求时间",
|
||||
"request_duration": "请求时长: {{duration}}s",
|
||||
"retry_times": "重试次数",
|
||||
"running_test": "测试中",
|
||||
"search_model": "搜索模型",
|
||||
"select_channel": "选择渠道名",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"delete_org": "删除部门",
|
||||
"edit_info": "编辑信息",
|
||||
"edit_org_info": "编辑部门信息",
|
||||
"expires": "有效期",
|
||||
"expires": "过期时间",
|
||||
"export_members": "导出成员",
|
||||
"forbid_hint": "停用后,该邀请链接将失效。 该操作不可撤销,是否确认停用?",
|
||||
"forbid_success": "停用成功",
|
||||
@@ -33,6 +33,7 @@
|
||||
"has_forbidden": "已失效",
|
||||
"has_invited": "已邀请",
|
||||
"ignore": "忽略",
|
||||
"invitation_copy_link": "【{{systemName}}】 {{userName}} 邀请您加入{{teamName}}团队,链接:{{url}}",
|
||||
"invitation_link_auto_clean_hint": "已失效链接将在30天后自动清理",
|
||||
"invitation_link_description": "链接描述",
|
||||
"invitation_link_list": "链接列表",
|
||||
@@ -53,6 +54,7 @@
|
||||
"org_name": "部门名称",
|
||||
"owner": "所有者",
|
||||
"permission": "权限",
|
||||
"please_bind_contact": "请绑定联系方式",
|
||||
"remark": "备注",
|
||||
"remove_tip": "确认将 {{username}} 移出团队?成员将被标记为“已离职”,不删除操作数据,账号下资源自动转让给团队所有者。",
|
||||
"restore_tip": "确认将 {{username}} 加入团队吗?仅恢复该成员账号可用性及相关权限,无法恢复账号下资源。",
|
||||
@@ -73,7 +75,5 @@
|
||||
"user_team_invite_member": "邀请成员",
|
||||
"user_team_leave_team": "离开团队",
|
||||
"user_team_leave_team_failed": "离开团队失败",
|
||||
"waiting": "待接受",
|
||||
"invitation_copy_link": "【{{systemName}}】 {{userName}} 邀请您加入{{teamName}}团队,链接:{{url}}",
|
||||
"please_bind_contact": "请绑定联系方式"
|
||||
"waiting": "待接受"
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
"to_dataset": "前往知识库",
|
||||
"unsupported_file_type": "不支持的文件类型",
|
||||
"upload": "上传",
|
||||
"variable_invisable_in_share": "自定义变量在免登录链接中不可见",
|
||||
"view_citations": "查看引用",
|
||||
"web_site_sync": "Web站点同步"
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"app.templateMarket.templateTags.Web_search": "联网搜索",
|
||||
"app.templateMarket.templateTags.Writing": "文本创作",
|
||||
"back": "返回",
|
||||
"can_copy_content_tip": "无法使用浏览器自动复制,请手动复制下面内容",
|
||||
"chose_condition": "选择条件",
|
||||
"chosen": "已选",
|
||||
"classification": "分类",
|
||||
@@ -132,7 +133,6 @@
|
||||
"common.Continue_Adding": "继续添加",
|
||||
"common.Copy": "复制",
|
||||
"common.Copy Successful": "复制成功",
|
||||
"common.Copy_failed": "复制失败,请手动复制",
|
||||
"common.Create Failed": "创建异常",
|
||||
"common.Create Success": "创建成功",
|
||||
"common.Create Time": "创建时间",
|
||||
@@ -335,7 +335,7 @@
|
||||
"core.app.deterministic": "严谨",
|
||||
"core.app.edit.Prompt Editor": "提示词编辑",
|
||||
"core.app.edit.Query extension background prompt": "对话背景描述",
|
||||
"core.app.edit.Query extension background tip": "描述当前对话的范围,便于 AI 为当前问题进行补全和扩展。填写的内容,通常为该助手",
|
||||
"core.app.edit.Query extension background tip": "描述当前对话的范围,便于 AI 为当前问题进行补全和扩展。填写的内容,通常为该助手所用",
|
||||
"core.app.edit_content": "应用信息编辑",
|
||||
"core.app.error.App name can not be empty": "应用名不能为空",
|
||||
"core.app.error.Get app failed": "获取应用异常",
|
||||
@@ -573,8 +573,6 @@
|
||||
"core.dataset.import.Custom process": "自定义规则",
|
||||
"core.dataset.import.Custom process desc": "自定义设置数据处理规则",
|
||||
"core.dataset.import.Custom prompt": "自定义提示词",
|
||||
"core.dataset.import.Custom split char": "自定义分隔符",
|
||||
"core.dataset.import.Custom split char Tips": "允许你根据自定义的分隔符进行分块。通常用于已处理好的数据,使用特定的分隔符来精确分块。",
|
||||
"core.dataset.import.Custom text": "自定义文本",
|
||||
"core.dataset.import.Custom text desc": "手动输入一段文本作为数据集",
|
||||
"core.dataset.import.Data process params": "数据处理参数",
|
||||
@@ -649,11 +647,11 @@
|
||||
"core.dataset.training.Auto mode": "补充索引",
|
||||
"core.dataset.training.Auto mode Tip": "通过子索引以及调用模型生成相关问题与摘要,来增加数据块的语义丰富度,更利于检索。需要消耗更多的存储空间和增加 AI 调用次数。",
|
||||
"core.dataset.training.Chunk mode": "直接分块",
|
||||
"core.dataset.training.Full": "预计 5 分钟以上",
|
||||
"core.dataset.training.Full": "预计 20 分钟以上",
|
||||
"core.dataset.training.Leisure": "空闲",
|
||||
"core.dataset.training.QA mode": "问答对提取",
|
||||
"core.dataset.training.Vector queue": "索引排队",
|
||||
"core.dataset.training.Waiting": "预计 5 分钟",
|
||||
"core.dataset.training.Waiting": "预计 20 分钟",
|
||||
"core.dataset.training.Website Sync": "Web 站点同步",
|
||||
"core.dataset.training.tag": "排队情况",
|
||||
"core.dataset.website.Base Url": "根地址",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"api_url": "接口地址",
|
||||
"auto_indexes": "自动生成补充索引",
|
||||
"auto_indexes_tips": "通过大模型进行额外索引生成,提高语义丰富度,提高检索的精度。",
|
||||
"auto_training_queue": "增强索引排队",
|
||||
"chunk_max_tokens": "分块上限",
|
||||
"close_auto_sync": "确认关闭自动同步功能?",
|
||||
"collection.Create update time": "创建/更新时间",
|
||||
@@ -25,7 +26,7 @@
|
||||
"core.dataset.import.Adjust parameters": "调整参数",
|
||||
"custom_data_process_params": "自定义",
|
||||
"custom_data_process_params_desc": "自定义设置数据处理规则",
|
||||
"data.ideal_chunk_length": "理想分块长度",
|
||||
"custom_split_sign_tip": "允许你根据自定义的分隔符进行分块。通常用于已处理好的数据,使用特定的分隔符来精确分块。可以使用 | 符号表示多个分割符,例如:“。|.” 表示中英文句号。\n尽量避免使用正则相关特殊符号,例如: * () [] {} 等。",
|
||||
"data_amount": "{{dataAmount}} 组数据, {{indexAmount}} 组索引",
|
||||
"data_index_num": "索引 {{index}}",
|
||||
"data_process_params": "处理参数",
|
||||
@@ -51,10 +52,9 @@
|
||||
"file_model_function_tip": "用于增强索引和 QA 生成",
|
||||
"filename": "文件名",
|
||||
"folder_dataset": "文件夹",
|
||||
"ideal_chunk_length": "理想分块长度",
|
||||
"ideal_chunk_length_tips": "按结束符号进行分段,并将多个分段组成一个分块,该值决定了分块的预估大小,如果会有上下浮动。",
|
||||
"image_auto_parse": "图片自动索引",
|
||||
"image_auto_parse_tips": "调用 VLM 自动标注文档里的图片,并生成额外的检索索引",
|
||||
"image_training_queue": "图片处理排队",
|
||||
"import.Auto mode Estimated Price Tips": "需调用文本理解模型,需要消耗较多AI 积分:{{price}} 积分/1K tokens",
|
||||
"import.Embedding Estimated Price Tips": "仅使用索引模型,消耗少量 AI 积分:{{price}} 积分/1K tokens",
|
||||
"import_confirm": "确认上传",
|
||||
@@ -65,6 +65,8 @@
|
||||
"import_param_setting": "参数设置",
|
||||
"import_select_file": "选择文件",
|
||||
"import_select_link": "输入链接",
|
||||
"index_size": "索引大小",
|
||||
"index_size_tips": "向量化时内容的长度,系统会自动按该大小对分块进行进一步的分割。",
|
||||
"is_open_schedule": "启用定时同步",
|
||||
"keep_image": "保留图片",
|
||||
"move.hint": "移动后,所选知识库/文件夹将继承新文件夹的权限设置,原先的权限设置失效。",
|
||||
@@ -78,7 +80,7 @@
|
||||
"permission.des.write": "可增加和变更知识库内容",
|
||||
"preview_chunk": "分块预览",
|
||||
"preview_chunk_empty": "无法读取该文件内容",
|
||||
"preview_chunk_intro": "最多展示 10 个分块",
|
||||
"preview_chunk_intro": "共 {{total}} 个分块,最多展示 10 个",
|
||||
"preview_chunk_not_selected": "点击左侧文件后进行预览",
|
||||
"rebuild_embedding_start_tip": "切换索引模型任务已开始",
|
||||
"rebuilding_index_count": "重建中索引数量:{{count}}",
|
||||
@@ -86,6 +88,16 @@
|
||||
"retain_collection": "调整训练参数",
|
||||
"retrain_task_submitted": "重新训练任务已提交",
|
||||
"same_api_collection": "存在相同的 API 集合",
|
||||
"split_chunk_char": "按指定分割符分块",
|
||||
"split_chunk_size": "按长度分块",
|
||||
"split_sign_break": "1 个换行符",
|
||||
"split_sign_break2": "2 个换行符",
|
||||
"split_sign_custom": "自定义",
|
||||
"split_sign_exclamatiob": "感叹号",
|
||||
"split_sign_null": "不设置",
|
||||
"split_sign_period": "句号",
|
||||
"split_sign_question": "问号",
|
||||
"split_sign_semicolon": "分号",
|
||||
"start_sync_website_tip": "确认开始同步数据?将会删除旧数据后重新获取,请确认!",
|
||||
"sync_collection_failed": "同步集合错误,请检查是否能正常访问源文件",
|
||||
"sync_schedule": "定时同步",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"channel_status_unknown": "未知",
|
||||
"channel_type": "廠商",
|
||||
"clear_model": "清空模型",
|
||||
"confirm_delete_channel": "確認刪除 【{{name}}】渠道?",
|
||||
"copy_model_id_success": "已復制模型id",
|
||||
"create_channel": "新增渠道",
|
||||
"default_url": "默認地址",
|
||||
@@ -34,6 +35,7 @@
|
||||
"model_tokens": "輸入/輸出 Tokens",
|
||||
"request_at": "請求時間",
|
||||
"request_duration": "請求時長: {{duration}}s",
|
||||
"retry_times": "重試次數",
|
||||
"running_test": "測試中",
|
||||
"search_model": "搜索模型",
|
||||
"select_channel": "選擇渠道名",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"delete_org": "刪除部門",
|
||||
"edit_info": "編輯訊息",
|
||||
"edit_org_info": "編輯部門資訊",
|
||||
"expires": "有效期",
|
||||
"expires": "過期時間",
|
||||
"forbid_hint": "停用後,該邀請連結將失效。 該操作不可撤銷,是否確認停用?",
|
||||
"forbid_success": "停用成功",
|
||||
"forbidden": "停用",
|
||||
@@ -29,6 +29,7 @@
|
||||
"has_forbidden": "已失效",
|
||||
"has_invited": "已邀請",
|
||||
"ignore": "忽略",
|
||||
"invitation_copy_link": "【{{systemName}}】 {{userName}} 邀請您加入{{teamName}}團隊,連結:{{url}}",
|
||||
"invitation_link_auto_clean_hint": "已失效連結將在30天後自動清理",
|
||||
"invitation_link_description": "連結描述",
|
||||
"invitation_link_list": "連結列表",
|
||||
@@ -58,6 +59,5 @@
|
||||
"user_team_invite_member": "邀請成員",
|
||||
"user_team_leave_team": "離開團隊",
|
||||
"user_team_leave_team_failed": "離開團隊失敗",
|
||||
"waiting": "待接受",
|
||||
"invitation_copy_link": "【{{systemName}}】 {{userName}} 邀請您加入{{teamName}}團隊,連結:{{url}}"
|
||||
"waiting": "待接受"
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
"to_dataset": "前往知識庫",
|
||||
"unsupported_file_type": "不支援的檔案類型",
|
||||
"upload": "上傳",
|
||||
"variable_invisable_in_share": "自定義變量在免登錄鏈接中不可見",
|
||||
"view_citations": "檢視引用",
|
||||
"web_site_sync": "網站同步"
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"add_new_param": "新增參數",
|
||||
"app.templateMarket.templateTags.Writing": "文字創作",
|
||||
"back": "返回",
|
||||
"can_copy_content_tip": "無法使用瀏覽器自動複製,請手動複製下面內容",
|
||||
"chose_condition": "選擇條件",
|
||||
"chosen": "已選擇",
|
||||
"classification": "分類",
|
||||
@@ -127,7 +128,6 @@
|
||||
"common.Continue_Adding": "繼續新增",
|
||||
"common.Copy": "複製",
|
||||
"common.Copy Successful": "複製成功",
|
||||
"common.Copy_failed": "複製失敗,請手動複製",
|
||||
"common.Create Failed": "建立失敗",
|
||||
"common.Create Success": "建立成功",
|
||||
"common.Create Time": "建立時間",
|
||||
@@ -568,8 +568,6 @@
|
||||
"core.dataset.import.Custom process": "自訂規則",
|
||||
"core.dataset.import.Custom process desc": "自訂設定資料處理規則",
|
||||
"core.dataset.import.Custom prompt": "自訂提示詞",
|
||||
"core.dataset.import.Custom split char": "自訂分隔符",
|
||||
"core.dataset.import.Custom split char Tips": "允許您根據自訂的分隔符進行分割。通常用於已處理好的資料,使用特定的分隔符來精確分割。",
|
||||
"core.dataset.import.Custom text": "自訂文字",
|
||||
"core.dataset.import.Custom text desc": "手動輸入一段文字作為資料集",
|
||||
"core.dataset.import.Data process params": "資料處理參數",
|
||||
@@ -645,11 +643,11 @@
|
||||
"core.dataset.training.Auto mode": "補充索引",
|
||||
"core.dataset.training.Auto mode Tip": "透過子索引以及呼叫模型產生相關問題與摘要,來增加資料區塊的語意豐富度,更有利於檢索。需要消耗更多的儲存空間並增加 AI 呼叫次數。",
|
||||
"core.dataset.training.Chunk mode": "直接分块",
|
||||
"core.dataset.training.Full": "預計超過 5 分鐘",
|
||||
"core.dataset.training.Full": "預計 20 分鐘以上",
|
||||
"core.dataset.training.Leisure": "閒置",
|
||||
"core.dataset.training.QA mode": "問答對提取",
|
||||
"core.dataset.training.Vector queue": "索引排隊中",
|
||||
"core.dataset.training.Waiting": "預計 5 分鐘",
|
||||
"core.dataset.training.Waiting": "預計 20 分鐘",
|
||||
"core.dataset.training.Website Sync": "網站同步",
|
||||
"core.dataset.training.tag": "排隊狀況",
|
||||
"core.dataset.website.Base Url": "根網址",
|
||||
|
||||