Compare commits

...

30 Commits

Author SHA1 Message Date
Archer
e860c56b77 perf: delete dataset (#3949)
* fix: collection list count

* fix: collection list count

* ai proxy ui

* perf: delete dataset

* perf: add dataset text index

* update doc
2025-03-03 12:49:13 +08:00
Archer
efac5312b4 fix: rerank model cannot use ai proxy (#3945)
* fix: collection list count

* fix: collection list count

* fix: rerank model cannot use ai proxy

* mongo init
2025-03-03 11:49:35 +08:00
Finley Ge
4bc7f21182 fix: add order:true to all create transactions (#3948) 2025-03-03 11:37:51 +08:00
gggaaallleee
113e8f711f add env proxypool (#3939) 2025-03-02 17:50:03 +08:00
Archer
abc6dffb41 4.8.23 dev (#3932)
* fix: collection list count

* fix: collection list count

* update doc

* perf: init log

* yml
2025-02-28 19:18:12 +08:00
gggaaallleee
f7b2a57ca3 1 (#3924) 2025-02-28 19:00:58 +08:00
Archer
cf0aaa1091 fix: invalid dataset data clear (#3927)
* fix: collection list count

* fix: collection list count

* fix: invalid dataset data clear

* update ts

* perf: cron clear invalid data

* perf: init

* perf: clear invalid code

* update init

* perf: clear invalid code

* perf: clear invalid code

* perf: init count

* batch init

* batch init

* batch init

* batch init

* add comment

* perf: init

* fix: api proxy type
2025-02-28 17:49:20 +08:00
Archer
ac4255ea0c 4.8.23 dev (#3926)
* fix: collection list count

* fix: collection list count

* fix: ts
2025-02-28 12:33:09 +08:00
Archer
df4d6f86ce fix: delete dataset field error (#3925)
* fix: collection list count

* fix: collection list count

* update doc

* perf: tts selector ui

* fix: delete dataset field error

* doc
2025-02-28 12:29:18 +08:00
heheer
e697fda82f fix: export chat log - chat detail order (#3923) 2025-02-28 11:33:46 +08:00
Archer
1aa319e7aa Update package.json (#3919) 2025-02-27 22:25:26 +08:00
Archer
fc9e614f88 4.8.23 dev (#3917)
* fix: icon refresh

* fix: aiproxy http request

* fix: collection list count

* fix: collection list count

* fix: tts selector name

* update action
2025-02-27 22:15:48 +08:00
Archer
1121ea33bd 更新 docker.md (#3913) 2025-02-27 17:02:06 +08:00
Finley Ge
9bbee60cde fix: ts error (#3911) 2025-02-27 16:31:14 +08:00
Finley Ge
9f57ad0017 fix: mongoose strictquery to false (#3906) 2025-02-27 11:25:29 +08:00
Archer
c3d3b30d7e update code positon (#3907) 2025-02-27 10:30:43 +08:00
Archer
fb0eb49196 fix: pptx encoding (#3905) 2025-02-27 10:04:59 +08:00
ZongLiang
27ebd2e8cf Update parseOffice.ts (#3901)
更新本地导入pptx文件提示文件编码错误
The argument 'windows-1252' is invalid encoding. Received 'encoding'
2025-02-27 09:57:34 +08:00
Archer
81a06718d8 feat: ai proxy v1 (#3898)
* feat: ai proxy v1

* perf: ai proxy channel crud

* feat: ai proxy logs

* feat: channel test

* doc

* update lock
2025-02-27 09:56:52 +08:00
Archer
3c382d1240 Update intro.md (#3900) 2025-02-26 22:37:56 +08:00
Finley Ge
747bb303ec chore: upgrade mongoose to v8.10.x for security (#3868)
* chore: upgrade mongoose to v8.10.x for security

* chore: remove duplicate code

* fix: ts error
2025-02-26 18:32:19 +08:00
Finley Ge
cf9c8e9f6a fix: leave team and refresh memberlist (#3893) 2025-02-26 18:29:05 +08:00
Archer
5d5bee9e41 remove markdown format;refresh username;perf: latext render (#3877)
* refresh username

* remove md format

* perf: latext render

* ignore big image

* model config
2025-02-25 16:16:30 +08:00
Archer
4f0dd96699 perf: work order tip (#3874) 2025-02-24 20:26:15 +08:00
Finley Ge
fb6dbaf2d6 feat/workorder (#3860)
* feat: workorder

* pref: workorder button

* chore: move workorder to common

* chore: format code

* pref: style
2025-02-24 19:59:06 +08:00
风沐白
ffc1520f4c Update quick-start.md (#3873) 2025-02-24 19:56:56 +08:00
Archer
255764400f feat: model config required check;feat: dataset text model default setting (#3866)
* feat: model config required check

* feat: dataset text model default setting

* perf: collection list count

* fix: ts

* remove index count
2025-02-24 19:55:49 +08:00
heheer
3bfe802c48 fix collection folder tags filter (#3853)
* fix collection folder tags filter

* add comment

* fix
2025-02-24 17:43:31 +08:00
YeYuheng
2bf17dbb87 Marker doc update (#3869)
* update-marker-doc

* marker.md

* marker.md
2025-02-24 13:13:05 +08:00
Archer
8d766372fe update doc (#3840)
* update doc

* update doc
2025-02-20 10:40:00 +08:00
278 changed files with 11794 additions and 1862 deletions

View File

@@ -68,14 +68,3 @@ jobs:
SEALOS_TYPE: 'pr_comment'
SEALOS_FILENAME: 'report.md'
SEALOS_REPLACE_TAG: 'DEFAULT_REPLACE_DEPLOY'
helm-check:
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Helm Check
run: |
helm dependency update files/helm/fastgpt
helm lint files/helm/fastgpt
helm package files/helm/fastgpt

View File

@@ -24,6 +24,6 @@ jobs:
export APP_VERSION=${{ steps.vars.outputs.tag }}
export HELM_VERSION=${{ steps.vars.outputs.tag }}
export HELM_REPO=ghcr.io/${{ github.repository_owner }}
helm dependency update files/helm/fastgpt
helm package files/helm/fastgpt --version ${HELM_VERSION}-helm --app-version ${APP_VERSION} -d bin
helm dependency update deploy/helm/fastgpt
helm package deploy/helm/fastgpt --version ${HELM_VERSION}-helm --app-version ${APP_VERSION} -d bin
helm push bin/fastgpt-${HELM_VERSION}-helm.tgz oci://${HELM_REPO}

View File

@@ -58,7 +58,7 @@
"body": [
"import '@/pages/api/__mocks__/base';",
"import { root } from '@/pages/api/__mocks__/db/init';",
"import { getTestRequest } from '@/test/utils';",
"import { getTestRequest } from '@fastgpt/service/test/utils'; ;",
"import { AppErrEnum } from '@fastgpt/global/common/error/code/app';",
"import handler from './demo';",
"",

View File

@@ -114,15 +114,15 @@ services:
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.8.21-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.21-fix # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.8.23-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.23-fix # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.21-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.21-fix # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.23-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.23-fix # 阿里云
ports:
- 3000:3000
networks:
@@ -133,7 +133,7 @@ services:
- sandbox
restart: always
environment:
# 前端访问地址: http://localhost:3000
# 前端外部可访问地址,用于自动补全文件资源路径。例如 https:fastgpt.cn不能填 localhost。这个值可以不填不填则发给模型的图片会是一个相对路径而不是全路径模型可能伪造Host。
- FE_DOMAIN=
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
- DEFAULT_ROOT_PSW=1234

View File

@@ -72,15 +72,15 @@ services:
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.8.21-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.21-fix # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.8.23-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.23-fix # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.21-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.21-fix # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.23-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.23-fix # 阿里云
ports:
- 3000:3000
networks:
@@ -91,7 +91,7 @@ services:
- sandbox
restart: always
environment:
# 前端访问地址: http://localhost:3000
# 前端外部可访问地址,用于自动补全文件资源路径。例如 https:fastgpt.cn不能填 localhost。这个值可以不填不填则发给模型的图片会是一个相对路径而不是全路径模型可能伪造Host。
- FE_DOMAIN=
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
- DEFAULT_ROOT_PSW=1234

View File

@@ -53,15 +53,15 @@ services:
wait $$!
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.8.21-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.21-fix # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.8.23-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.23-fix # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.21-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.21-fix # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.23-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.23-fix # 阿里云
ports:
- 3000:3000
networks:
@@ -71,7 +71,7 @@ services:
- sandbox
restart: always
environment:
# 前端访问地址: http://localhost:3000
# 前端外部可访问地址,用于自动补全文件资源路径。例如 https:fastgpt.cn不能填 localhost。这个值可以不填不填则发给模型的图片会是一个相对路径而不是全路径模型可能伪造Host。
- FE_DOMAIN=
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
- DEFAULT_ROOT_PSW=1234

View File

@@ -31,9 +31,9 @@ weight: 920
3 个模型代码分别为:
1. [https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-base](https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-base)
2. [https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-large](https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-large)
3. [https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-v2-m3](https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-v2-m3)
1. [https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-base](https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-base)
2. [https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-large](https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-large)
3. [https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-v2-m3](https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-v2-m3)
### 3. 安装依赖

View File

@@ -19,7 +19,7 @@ PDF 是一个相对复杂的文件格式,在 FastGPT 内置的 pdf 解析器
### 1. 按照 Marker
参考文档 [Marker 安装教程](https://github.com/labring/FastGPT/tree/main/python/pdf-marker),安装 Marker 模型。封装的 API 已经适配了 FastGPT 自定义解析服务。
参考文档 [Marker 安装教程](https://github.com/labring/FastGPT/tree/main/plugins/model/pdf-marker),安装 Marker 模型。封装的 API 已经适配了 FastGPT 自定义解析服务。
这里介绍快速 Docker 安装的方法:

View File

@@ -118,7 +118,7 @@ brew install orbstack
非 Linux 环境或无法访问外网环境,可手动创建一个目录,并下载配置文件和对应版本的`docker-compose.yml`在这个文件夹中依据下载的配置文件运行docker若作为本地开发使用推荐`docker-compose-pgvector`版本,并且自行拉取并运行`sandbox``fastgpt`并在docker配置文件中注释掉`sandbox``fastgpt`的部分
- [config.json](https://raw.githubusercontent.com/labring/FastGPT/refs/heads/main/projects/app/data/config.json)
- [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/files/docker) (注意,不同向量库版本的文件不一样)
- [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/deploy/docker) (注意,不同向量库版本的文件不一样)
{{% alert icon="🤖" context="success" %}}
@@ -134,11 +134,11 @@ cd fastgpt
curl -O https://raw.githubusercontent.com/labring/FastGPT/main/projects/app/data/config.json
# pgvector 版本(测试推荐,简单快捷)
curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/files/docker/docker-compose-pgvector.yml
curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-pgvector.yml
# milvus 版本
# curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/files/docker/docker-compose-milvus.yml
# curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-milvus.yml
# zilliz 版本
# curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/files/docker/docker-compose-zilliz.yml
# curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-zilliz.yml
```
### 2. 修改环境变量
@@ -201,6 +201,8 @@ docker restart oneapi
在OneApi中添加合适的AI模型渠道。[点击查看相关教程](/docs/development/modelconfig/one-api/)
只需要添加模型即可模板已经配置好了oneapi的连接地址和令牌无需变更。
### 5. 访问 FastGPT
目前可以通过 `ip:3000` 直接访问(注意防火墙)。登录用户名为 `root`,密码为`docker-compose.yml`环境变量里设置的 `DEFAULT_ROOT_PSW`
@@ -211,7 +213,7 @@ docker restart oneapi
### 6. 配置模型
务必先配置至少一组模型,否则系统无法正常使用。
登录FastGPT后进入模型配置页面务必先配置至少一个语言模型和一个向量模型,否则系统无法正常使用。
[点击查看模型配置教程](/docs/development/modelConfig/intro/)

View File

@@ -23,7 +23,7 @@ weight: 744
{{% alert icon=" " context="info" %}}
- [SiliconCloud(硅基流动)](https://cloud.siliconflow.cn/i/TR9Ym0c4): 提供开源模型调用的平台。
- [Sealos AIProxy](https://hzh.sealos.run/?openapp=system-aiproxy): 提供国内各家模型代理,无需逐一申请 api。
- [Sealos AIProxy](https://cloud.sealos.run/?uid=fnWRt09fZP&openapp=system-aiproxy): 提供国内各家模型代理,无需逐一申请 api。
{{% /alert %}}
在 OneAPI 配置好模型后,你就可以打开 FastGPT 页面,启用对应模型了。
@@ -467,4 +467,4 @@ OneAPI 的语言识别接口,无法正确的识别其他模型(会始终识
"charsPointsPrice": 0
}
}
```
```

View File

@@ -20,7 +20,7 @@ SANDBOX_URL=内网地址
## Docker 部署
可以拉取最新 [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/files/docker/docker-compose.yml) 文件参考
可以拉取最新 [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/deploy/docker/docker-compose.yml) 文件参考
1. 新增一个容器 `sandbox`
2. fastgpt 和 fastgpt-pro(商业版) 容器新增环境变量: `SANDBOX_URL`

View File

@@ -1,5 +1,5 @@
---
title: 'V4.8.22(进行中)'
title: 'V4.8.22(包含升级脚本)'
description: 'FastGPT V4.8.22 更新说明'
icon: 'upgrade'
draft: false
@@ -13,8 +13,8 @@ weight: 802
### 2. 更新镜像:
- 更新 fastgpt 镜像 tag: v4.8.22-alpha
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.22-alpha
- 更新 fastgpt 镜像 tag: v4.8.22
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.22
- Sandbox 镜像无需更新
### 3. 运行升级脚本

View File

@@ -0,0 +1,54 @@
---
title: 'V4.8.23'
description: 'FastGPT V4.8.23 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 802
---
## 更新指南
### 1. 做好数据库备份
### 2. 更新镜像:
- 更新 fastgpt 镜像 tag: v4.8.23-fix
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.23-fix
- Sandbox 镜像无需更新
### 3. 运行升级脚本
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`{{host}} 替换成**FastGPT 域名**。
```bash
curl --location --request POST 'https://{{host}}/api/admin/initv4823' \
--header 'rootkey: {{rootkey}}' \
--header 'Content-Type: application/json'
```
脚本会清理一些知识库脏数据,主要是多余的全文索引。
## 🚀 新增内容
1. 增加默认“知识库文本理解模型”配置
2. AI proxy V1版可替换 OneAPI使用同时提供完整模型调用日志便于排查问题。
3. 增加工单入口支持。
## ⚙️ 优化
1. 模型配置表单,增加必填项校验。
2. 集合列表数据统计方式,提高大数据量统计性能。
3. 优化数学公式,转义 Latex 格式成 Markdown 格式。
4. 解析文档图片,图片太大时,自动忽略。
5. 时间选择器当天开始时间自动设0结束设置设 23:59:59避免 UI 与实际逻辑偏差。
6. 升级 mongoose 库版本依赖。
## 🐛 修复
1. 标签过滤时,子文件夹未成功过滤。
2. 暂时移除 md 阅读优化,避免链接分割错误。
3. 离开团队时,未刷新成员列表。
4. PPTX 编码错误,导致解析失败。
5. 删除知识库单条数据时,全文索引未跟随删除。
6. 修复 Mongo Dataset text 索引在查询数据时未生效。

View File

@@ -15,7 +15,7 @@ weight: 821
## V4.8.3 更新说明
1. 新增 - 支持 Milvus 数据库, 可参考最新的 [docker-compose-milvus.yml](https://github.com/labring/FastGPT/blob/main/files/docker/docker-compose-milvus.yml).
1. 新增 - 支持 Milvus 数据库, 可参考最新的 [docker-compose-milvus.yml](https://github.com/labring/FastGPT/blob/main/deploy/docker/docker-compose-milvus.yml).
2. 新增 - 给 chat 接口 empty answer 增加 log便于排查模型问题。
3. 新增 - ifelse判断器字符串支持正则。
4. 新增 - 代码运行支持 console.log 输出调试。

View File

@@ -7,11 +7,11 @@ toc: true
weight: 102
---
更多使用技巧,[查看视教程](https://www.bilibili.com/video/BV1sH4y1T7s9)
更多使用技巧,[查看视教程](https://www.bilibili.com/video/BV1sH4y1T7s9)
## 知识库
开始前请准备一份测试电子文档WORDPDFTXTexcelmarkdown 都可以,比如公司休假制度不涉密的销售说辞产品知识等等。
开始前请准备一份测试电子文档WORDPDFTXTexcelmarkdown 都可以,比如公司休假制度不涉密的销售说辞产品知识等等。
这里使用 FastGPT 中文 README 文件为例。
@@ -31,7 +31,7 @@ weight: 102
![](/imgs/upload-data.png)
点击上传后我们需要等待数据处理完成,到我们上传的文件状态为可用。
点击上传后我们需要等待数据处理完成,到我们上传的文件状态为可用。
![](/imgs/upload-data2.png)

3
packages/README.md Normal file
View File

@@ -0,0 +1,3 @@
# 目录说明
该目录为 FastGPT 的依赖包,多端复用。

View File

@@ -4,6 +4,7 @@ import { ErrType } from '../errorCode';
/* dataset: 501000 */
export enum DatasetErrEnum {
unExist = 'unExistDataset',
unExistCollection = 'unExistCollection',
unAuthDataset = 'unAuthDataset',
unCreateCollection = 'unCreateCollection',
unAuthDatasetCollection = 'unAuthDatasetCollection',
@@ -28,6 +29,10 @@ const datasetErr = [
statusText: DatasetErrEnum.unExist,
message: 'core.dataset.error.unExistDataset'
},
{
statusText: DatasetErrEnum.unExistCollection,
message: i18nT('common:error_collection_not_exist')
},
{
statusText: DatasetErrEnum.unAuthDataset,
message: 'core.dataset.error.unAuthDataset'

View File

@@ -7,12 +7,14 @@ import { i18nT } from '../../../web/i18n/utils';
dayjs.extend(utc);
dayjs.extend(timezone);
export const formatTime2YMDHMW = (time?: Date) => dayjs(time).format('YYYY-MM-DD HH:mm:ss dddd');
export const formatTime2YMDHMS = (time?: Date) =>
export const formatTime2YMDHMW = (time?: Date | number) =>
dayjs(time).format('YYYY-MM-DD HH:mm:ss dddd');
export const formatTime2YMDHMS = (time?: Date | number) =>
time ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '';
export const formatTime2YMDHM = (time?: Date) =>
export const formatTime2YMDHM = (time?: Date | number) =>
time ? dayjs(time).format('YYYY-MM-DD HH:mm') : '';
export const formatTime2YMD = (time?: Date) => (time ? dayjs(time).format('YYYY-MM-DD') : '');
export const formatTime2YMD = (time?: Date | number) =>
time ? dayjs(time).format('YYYY-MM-DD') : '';
export const formatTime2HM = (time: Date = new Date()) => dayjs(time).format('HH:mm');
/**

View File

@@ -41,6 +41,7 @@ export type FastGPTConfigFileType = {
};
export type FastGPTFeConfigsType = {
show_workorder?: boolean;
show_emptyChat?: boolean;
register_method?: ['email' | 'phone' | 'sync'];
login_method?: ['email' | 'phone']; // Attention: login method is diffrent with oauth
@@ -53,6 +54,7 @@ export type FastGPTFeConfigsType = {
show_promotion?: boolean;
show_team_chat?: boolean;
show_compliance_copywriting?: boolean;
show_aiproxy?: boolean;
concatMd?: string;
docUrl?: string;

View File

@@ -17,6 +17,8 @@ type BaseModelItemType = {
isActive?: boolean;
isCustom?: boolean;
isDefault?: boolean;
isDefaultDatasetTextModel?: boolean;
isDefaultDatasetImageModel?: boolean;
// If has requestUrl, it will request the model directly
requestUrl?: string;

View File

@@ -192,6 +192,7 @@ export type DatasetCollectionItemType = CollectionWithDatasetType & {
sourceId?: string;
file?: DatasetFileSchema;
permission: DatasetPermission;
indexAmount: number;
};
/* ================= data ===================== */

View File

@@ -420,137 +420,3 @@ export function rewriteNodeOutputByHistories(
};
});
}
// Parse <think></think> tags to think and answer - unstream response
export const parseReasoningContent = (text: string): [string, string] => {
const regex = /<think>([\s\S]*?)<\/think>/;
const match = text.match(regex);
if (!match) {
return ['', text];
}
const thinkContent = match[1].trim();
// Add answer (remaining text after think tag)
const answerContent = text.slice(match.index! + match[0].length);
return [thinkContent, answerContent];
};
// Parse <think></think> tags to think and answer - stream response
export const parseReasoningStreamContent = () => {
let isInThinkTag: boolean | undefined;
const startTag = '<think>';
let startTagBuffer = '';
const endTag = '</think>';
let endTagBuffer = '';
/*
parseReasoning - 只控制是否主动解析 <think></think>,如果接口已经解析了,仍然会返回 think 内容。
*/
const parsePart = (
part: {
choices: {
delta: {
content?: string;
reasoning_content?: string;
};
}[];
},
parseReasoning = false
): [string, string] => {
const content = part.choices?.[0]?.delta?.content || '';
// @ts-ignore
const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || '';
if (reasoningContent || !parseReasoning) {
isInThinkTag = false;
return [reasoningContent, content];
}
if (!content) {
return ['', ''];
}
// 如果不在 think 标签中,或者有 reasoningContent(接口已解析),则返回 reasoningContent 和 content
if (isInThinkTag === false) {
return ['', content];
}
// 检测是否为 think 标签开头的数据
if (isInThinkTag === undefined) {
// Parse content think and answer
startTagBuffer += content;
// 太少内容时候,暂时不解析
if (startTagBuffer.length < startTag.length) {
return ['', ''];
}
if (startTagBuffer.startsWith(startTag)) {
isInThinkTag = true;
return [startTagBuffer.slice(startTag.length), ''];
}
// 如果未命中 think 标签,则认为不在 think 标签中,返回 buffer 内容作为 content
isInThinkTag = false;
return ['', startTagBuffer];
}
// 确认是 think 标签内容,开始返回 think 内容,并实时检测 </think>
/*
检测 </think> 方案。
存储所有疑似 </think> 的内容,直到检测到完整的 </think> 标签或超出 </think> 长度。
content 返回值包含以下几种情况:
abc - 完全未命中尾标签
abc<th - 命中一部分尾标签
abc</think> - 完全命中尾标签
abc</think>abc - 完全命中尾标签
</think>abc - 完全命中尾标签
k>abc - 命中一部分尾标签
*/
// endTagBuffer 专门用来记录疑似尾标签的内容
if (endTagBuffer) {
endTagBuffer += content;
if (endTagBuffer.includes(endTag)) {
isInThinkTag = false;
const answer = endTagBuffer.slice(endTag.length);
return ['', answer];
} else if (endTagBuffer.length >= endTag.length) {
// 缓存内容超出尾标签长度,且仍未命中 </think>,则认为本次猜测 </think> 失败,仍处于 think 阶段。
const tmp = endTagBuffer;
endTagBuffer = '';
return [tmp, ''];
}
return ['', ''];
} else if (content.includes(endTag)) {
// 返回内容,完整命中</think>,直接结束
isInThinkTag = false;
const [think, answer] = content.split(endTag);
return [think, answer];
} else {
// 无 buffer且未命中 </think>,开始疑似 </think> 检测。
for (let i = 1; i < endTag.length; i++) {
const partialEndTag = endTag.slice(0, i);
// 命中一部分尾标签
if (content.endsWith(partialEndTag)) {
const think = content.slice(0, -partialEndTag.length);
endTagBuffer += partialEndTag;
return [think, ''];
}
}
}
// 完全未命中尾标签,还是 think 阶段。
return [content, ''];
};
const getStartTagBuffer = () => startTagBuffer;
return {
parsePart,
getStartTagBuffer
};
};

View File

@@ -27,7 +27,6 @@ export type UserType = {
timezone: string;
promotionRate: UserModelSchema['promotionRate'];
team: TeamTmbItemType;
standardInfo?: standardInfoType;
notificationAccount?: string;
permission: TeamPermission;
contact?: string;

View File

@@ -18,10 +18,10 @@ export function getGFSCollection(bucket: `${BucketNameEnum}`) {
MongoDatasetFileSchema;
MongoChatFileSchema;
return connectionMongo.connection.db.collection(`${bucket}.files`);
return connectionMongo.connection.db!.collection(`${bucket}.files`);
}
export function getGridBucket(bucket: `${BucketNameEnum}`) {
return new connectionMongo.mongo.GridFSBucket(connectionMongo.connection.db, {
return new connectionMongo.mongo.GridFSBucket(connectionMongo.connection.db!, {
bucketName: bucket,
// @ts-ignore
readPreference: ReadPreference.SECONDARY_PREFERRED // Read from secondary node

View File

@@ -118,7 +118,7 @@ export async function delImgByRelatedId({
}: {
teamId: string;
relateIds: string[];
session: ClientSession;
session?: ClientSession;
}) {
if (relateIds.length === 0) return;

View File

@@ -111,15 +111,21 @@ export const readRawContentByFileBuffer = async ({
// markdown data format
if (imageList) {
await batchRun(imageList, async (item) => {
const src = await uploadMongoImg({
base64Img: `data:${item.mime};base64,${item.base64}`,
teamId,
// expiredTime: addHours(new Date(), 1),
metadata: {
...metadata,
mime: item.mime
const src = await (async () => {
try {
return await uploadMongoImg({
base64Img: `data:${item.mime};base64,${item.base64}`,
teamId,
// expiredTime: addHours(new Date(), 1),
metadata: {
...metadata,
mime: item.mime
}
});
} catch (error) {
return '';
}
});
})();
rawText = rawText.replace(item.uuid, src);
if (formatText) {
formatText = formatText.replace(item.uuid, src);

View File

@@ -38,10 +38,12 @@ const addCommonMiddleware = (schema: mongoose.Schema) => {
schema.post(op, function (this: any, result: any, next) {
if (this._startTime) {
const duration = Date.now() - this._startTime;
const warnLogData = {
query: this._query,
op,
collectionName: this.collection?.name,
op: this.op,
...(this._query && { query: this._query }),
...(this._update && { update: this._update }),
...(this._delete && { delete: this._delete }),
duration
};

View File

@@ -16,16 +16,30 @@ export async function connectMongo(): Promise<Mongoose> {
console.log('mongo start connect');
try {
connectionMongo.set('strictQuery', true);
// Remove existing listeners to prevent duplicates
connectionMongo.connection.removeAllListeners('error');
connectionMongo.connection.removeAllListeners('disconnected');
connectionMongo.set('strictQuery', false);
connectionMongo.connection.on('error', async (error) => {
console.log('mongo error', error);
await connectionMongo.disconnect();
await delay(1000);
connectMongo();
try {
if (connectionMongo.connection.readyState !== 0) {
await connectionMongo.disconnect();
await delay(1000);
await connectMongo();
}
} catch (error) {}
});
connectionMongo.connection.on('disconnected', () => {
connectionMongo.connection.on('disconnected', async () => {
console.log('mongo disconnected');
try {
if (connectionMongo.connection.readyState !== 0) {
await connectionMongo.disconnect();
await delay(1000);
await connectMongo();
}
} catch (error) {}
});
await connectionMongo.connect(process.env.MONGODB_URI as string, {

View File

@@ -1 +1,4 @@
export const FastGPTProUrl = process.env.PRO_URL ? `${process.env.PRO_URL}/api` : '';
export const isFastGPTMainService = !!process.env.PRO_URL;
// @ts-ignore
export const isFastGPTProService = () => !!global.systemConfig;

View File

@@ -21,6 +21,7 @@ export const recallFromVectorStore = Vector.embRecall;
export const getVectorDataByTime = Vector.getVectorDataByTime;
export const getVectorCountByTeamId = Vector.getVectorCountByTeamId;
export const getVectorCountByDatasetId = Vector.getVectorCountByDatasetId;
export const getVectorCountByCollectionId = Vector.getVectorCountByCollectionId;
export const insertDatasetDataVector = async ({
model,

View File

@@ -321,6 +321,23 @@ export class MilvusCtrl {
return total;
};
getVectorCountByCollectionId = async (
teamId: string,
datasetId: string,
collectionId: string
) => {
const client = await this.getClient();
const result = await client.query({
collection_name: DatasetVectorTableName,
output_fields: ['count(*)'],
filter: `(teamId == "${String(teamId)}") and (datasetId == "${String(datasetId)}") and (collectionId == "${String(collectionId)}")`
});
const total = result.data?.[0]?.['count(*)'] as number;
return total;
};
getVectorDataByTime = async (start: Date, end: Date) => {
const client = await this.getClient();

View File

@@ -240,6 +240,23 @@ export class PgVectorCtrl {
where: [['team_id', String(teamId)], 'and', ['dataset_id', String(datasetId)]]
});
return total;
};
getVectorCountByCollectionId = async (
teamId: string,
datasetId: string,
collectionId: string
) => {
const total = await PgClient.count(DatasetVectorTableName, {
where: [
['team_id', String(teamId)],
'and',
['dataset_id', String(datasetId)],
'and',
['collection_id', String(collectionId)]
]
});
return total;
};
}

View File

@@ -11,14 +11,17 @@ import { i18nT } from '../../../web/i18n/utils';
import { OpenaiAccountType } from '@fastgpt/global/support/user/team/type';
import { getLLMModel } from './model';
export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
const aiProxyBaseUrl = process.env.AIPROXY_API_ENDPOINT
? `${process.env.AIPROXY_API_ENDPOINT}/v1`
: undefined;
const openaiBaseUrl = aiProxyBaseUrl || process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
const openaiBaseKey = process.env.AIPROXY_API_TOKEN || process.env.CHAT_API_KEY || '';
export const getAIApi = (props?: { userKey?: OpenaiAccountType; timeout?: number }) => {
const { userKey, timeout } = props || {};
const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || process.env.CHAT_API_KEY || '';
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || openaiBaseKey;
return new OpenAI({
baseURL: baseUrl,
apiKey,
@@ -32,7 +35,7 @@ export const getAxiosConfig = (props?: { userKey?: OpenaiAccountType }) => {
const { userKey } = props || {};
const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || process.env.CHAT_API_KEY || '';
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || openaiBaseKey;
return {
baseUrl,
@@ -72,6 +75,7 @@ export const createChatCompletion = async ({
userKey,
timeout: formatTimeout
});
const response = await ai.chat.completions.create(body, {
...options,
...(modelConstantsData.requestUrl ? { path: modelConstantsData.requestUrl } : {}),

View File

@@ -1,4 +1,10 @@
{
"provider": "AliCloud",
"list": []
}
"list": [
{
"model": "SenseVoiceSmall",
"name": "SenseVoiceSmall",
"type": "stt"
}
]
}

View File

@@ -1,6 +1,30 @@
{
"provider": "Claude",
"list": [
{
"model": "claude-3-7-sonnet-20250219",
"name": "claude-3-7-sonnet-20250219",
"maxContext": 200000,
"maxResponse": 8000,
"quoteMaxToken": 100000,
"maxTemperature": 1,
"showTopP": true,
"showStopSign": true,
"vision": true,
"toolChoice": true,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{
"model": "claude-3-5-haiku-20241022",
"name": "claude-3-5-haiku-20241022",
@@ -10,7 +34,7 @@
"maxTemperature": 1,
"showTopP": true,
"showStopSign": true,
"vision": false,
"vision": true,
"toolChoice": true,
"functionCall": false,
"defaultSystemChatPrompt": "",
@@ -98,4 +122,4 @@
"type": "llm"
}
]
}
}

View File

@@ -1,4 +1,29 @@
{
"provider": "Grok",
"list": []
}
"list": [
{
"model": "grok-3",
"name": "grok-3",
"maxContext": 128000,
"maxResponse": 8000,
"quoteMaxToken": 128000,
"maxTemperature": 1,
"showTopP": true,
"showStopSign": true,
"vision": false,
"toolChoice": false,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
}
]
}

View File

@@ -52,6 +52,12 @@ export const loadSystemModels = async (init = false) => {
if (model.isDefault) {
global.systemDefaultModel.llm = model;
}
if (model.isDefaultDatasetTextModel) {
global.systemDefaultModel.datasetTextLLM = model;
}
if (model.isDefaultDatasetImageModel) {
global.systemDefaultModel.datasetImageLLM = model;
}
} else if (model.type === ModelTypeEnum.embedding) {
global.embeddingModelMap.set(model.model, model);
global.embeddingModelMap.set(model.name, model);
@@ -134,6 +140,16 @@ export const loadSystemModels = async (init = false) => {
if (!global.systemDefaultModel.llm) {
global.systemDefaultModel.llm = Array.from(global.llmModelMap.values())[0];
}
if (!global.systemDefaultModel.datasetTextLLM) {
global.systemDefaultModel.datasetTextLLM = Array.from(global.llmModelMap.values()).find(
(item) => item.datasetProcess
);
}
if (!global.systemDefaultModel.datasetImageLLM) {
global.systemDefaultModel.datasetImageLLM = Array.from(global.llmModelMap.values()).find(
(item) => item.vision
);
}
if (!global.systemDefaultModel.embedding) {
global.systemDefaultModel.embedding = Array.from(global.embeddingModelMap.values())[0];
}

View File

@@ -22,6 +22,9 @@ export type SystemModelItemType =
export type SystemDefaultModelType = {
[ModelTypeEnum.llm]?: LLMModelItemType;
datasetTextLLM?: LLMModelItemType;
datasetImageLLM?: LLMModelItemType;
[ModelTypeEnum.embedding]?: EmbeddingModelItemType;
[ModelTypeEnum.tts]?: TTSModelType;
[ModelTypeEnum.stt]?: STTModelType;

View File

@@ -95,11 +95,145 @@ export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
return requestBody as unknown as InferCompletionsBody<T>;
};
export const llmStreamResponseToText = async (response: StreamChatType) => {
export const llmStreamResponseToAnswerText = async (response: StreamChatType) => {
let answer = '';
for await (const part of response) {
const content = part.choices?.[0]?.delta?.content || '';
answer += content;
}
return answer;
return parseReasoningContent(answer)[1];
};
// Parse <think></think> tags to think and answer - unstream response
export const parseReasoningContent = (text: string): [string, string] => {
const regex = /<think>([\s\S]*?)<\/think>/;
const match = text.match(regex);
if (!match) {
return ['', text];
}
const thinkContent = match[1].trim();
// Add answer (remaining text after think tag)
const answerContent = text.slice(match.index! + match[0].length);
return [thinkContent, answerContent];
};
// Parse <think></think> tags to think and answer - stream response
export const parseReasoningStreamContent = () => {
let isInThinkTag: boolean | undefined;
const startTag = '<think>';
let startTagBuffer = '';
const endTag = '</think>';
let endTagBuffer = '';
/*
parseReasoning - 只控制是否主动解析 <think></think>,如果接口已经解析了,仍然会返回 think 内容。
*/
const parsePart = (
part: {
choices: {
delta: {
content?: string;
reasoning_content?: string;
};
}[];
},
parseReasoning = false
): [string, string] => {
const content = part.choices?.[0]?.delta?.content || '';
// @ts-ignore
const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || '';
if (reasoningContent || !parseReasoning) {
isInThinkTag = false;
return [reasoningContent, content];
}
if (!content) {
return ['', ''];
}
// 如果不在 think 标签中,或者有 reasoningContent(接口已解析),则返回 reasoningContent 和 content
if (isInThinkTag === false) {
return ['', content];
}
// 检测是否为 think 标签开头的数据
if (isInThinkTag === undefined) {
// Parse content think and answer
startTagBuffer += content;
// 太少内容时候,暂时不解析
if (startTagBuffer.length < startTag.length) {
return ['', ''];
}
if (startTagBuffer.startsWith(startTag)) {
isInThinkTag = true;
return [startTagBuffer.slice(startTag.length), ''];
}
// 如果未命中 think 标签,则认为不在 think 标签中,返回 buffer 内容作为 content
isInThinkTag = false;
return ['', startTagBuffer];
}
// 确认是 think 标签内容,开始返回 think 内容,并实时检测 </think>
/*
检测 </think> 方案。
存储所有疑似 </think> 的内容,直到检测到完整的 </think> 标签或超出 </think> 长度。
content 返回值包含以下几种情况:
abc - 完全未命中尾标签
abc<th - 命中一部分尾标签
abc</think> - 完全命中尾标签
abc</think>abc - 完全命中尾标签
</think>abc - 完全命中尾标签
k>abc - 命中一部分尾标签
*/
// endTagBuffer 专门用来记录疑似尾标签的内容
if (endTagBuffer) {
endTagBuffer += content;
if (endTagBuffer.includes(endTag)) {
isInThinkTag = false;
const answer = endTagBuffer.slice(endTag.length);
return ['', answer];
} else if (endTagBuffer.length >= endTag.length) {
// 缓存内容超出尾标签长度,且仍未命中 </think>,则认为本次猜测 </think> 失败,仍处于 think 阶段。
const tmp = endTagBuffer;
endTagBuffer = '';
return [tmp, ''];
}
return ['', ''];
} else if (content.includes(endTag)) {
// 返回内容,完整命中</think>,直接结束
isInThinkTag = false;
const [think, answer] = content.split(endTag);
return [think, answer];
} else {
// 无 buffer且未命中 </think>,开始疑似 </think> 检测。
for (let i = 1; i < endTag.length; i++) {
const partialEndTag = endTag.slice(0, i);
// 命中一部分尾标签
if (content.endsWith(partialEndTag)) {
const think = content.slice(0, -partialEndTag.length);
endTagBuffer += partialEndTag;
return [think, ''];
}
}
}
// 完全未命中尾标签,还是 think 阶段。
return [content, ''];
};
const getStartTagBuffer = () => startTagBuffer;
return {
parsePart,
getStartTagBuffer
};
};

View File

@@ -25,6 +25,7 @@ import { MongoImage } from '../../../common/file/image/schema';
import { hashStr } from '@fastgpt/global/common/string/tools';
import { addDays } from 'date-fns';
import { MongoDatasetDataText } from '../data/dataTextSchema';
import { delay, retryFn } from '@fastgpt/global/common/system/utils';
export const createCollectionAndInsertData = async ({
dataset,
@@ -216,7 +217,7 @@ export async function createOneCollection({
nextSyncTime
}
],
{ session }
{ session, ordered: true }
);
return collection;
@@ -227,8 +228,14 @@ export const delCollectionRelatedSource = async ({
collections,
session
}: {
collections: DatasetCollectionSchemaType[];
session: ClientSession;
collections: {
teamId: string;
fileId?: string;
metadata?: {
relatedImgId?: string;
};
}[];
session?: ClientSession;
}) => {
if (collections.length === 0) return;
@@ -259,11 +266,13 @@ export const delCollectionRelatedSource = async ({
export async function delCollection({
collections,
session,
delRelatedSource
delImg = true,
delFile = true
}: {
collections: DatasetCollectionSchemaType[];
session: ClientSession;
delRelatedSource: boolean;
delImg: boolean;
delFile: boolean;
}) {
if (collections.length === 0) return;
@@ -274,83 +283,55 @@ export async function delCollection({
const datasetIds = Array.from(new Set(collections.map((item) => String(item.datasetId))));
const collectionIds = collections.map((item) => String(item._id));
// Delete training data
await MongoDatasetTraining.deleteMany({
teamId,
datasetIds: { $in: datasetIds },
collectionId: { $in: collectionIds }
await retryFn(async () => {
await Promise.all([
// Delete training data
MongoDatasetTraining.deleteMany({
teamId,
datasetId: { $in: datasetIds },
collectionId: { $in: collectionIds }
}),
// Delete dataset_data_texts
MongoDatasetDataText.deleteMany({
teamId,
datasetId: { $in: datasetIds },
collectionId: { $in: collectionIds }
}),
// Delete dataset_datas
MongoDatasetData.deleteMany({
teamId,
datasetId: { $in: datasetIds },
collectionId: { $in: collectionIds }
}),
...(delImg
? [
delImgByRelatedId({
teamId,
relateIds: collections
.map((item) => item?.metadata?.relatedImgId || '')
.filter(Boolean)
})
]
: []),
...(delFile
? [
delFileByFileIdList({
bucketName: BucketNameEnum.dataset,
fileIdList: collections.map((item) => item?.fileId || '').filter(Boolean)
})
]
: []),
// Delete vector data
deleteDatasetDataVector({ teamId, datasetIds, collectionIds })
]);
// delete collections
await MongoDatasetCollection.deleteMany(
{
teamId,
_id: { $in: collectionIds }
},
{ session }
);
});
/* file and imgs */
if (delRelatedSource) {
await delCollectionRelatedSource({ collections, session });
}
// Delete dataset_datas
await MongoDatasetData.deleteMany(
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
{ session }
);
// Delete dataset_data_texts
await MongoDatasetDataText.deleteMany(
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
{ session }
);
// delete collections
await MongoDatasetCollection.deleteMany(
{
teamId,
_id: { $in: collectionIds }
},
{ session }
);
// no session delete: delete files, vector data
await deleteDatasetDataVector({ teamId, datasetIds, collectionIds });
}
/**
* delete delOnlyCollection
*/
export async function delOnlyCollection({
collections,
session
}: {
collections: DatasetCollectionSchemaType[];
session: ClientSession;
}) {
if (collections.length === 0) return;
const teamId = collections[0].teamId;
if (!teamId) return Promise.reject('teamId is not exist');
const datasetIds = Array.from(new Set(collections.map((item) => String(item.datasetId))));
const collectionIds = collections.map((item) => String(item._id));
// delete training data
await MongoDatasetTraining.deleteMany({
teamId,
datasetIds: { $in: datasetIds },
collectionId: { $in: collectionIds }
});
// delete dataset.datas
await MongoDatasetData.deleteMany(
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
{ session }
);
// delete collections
await MongoDatasetCollection.deleteMany(
{
teamId,
_id: { $in: collectionIds }
},
{ session }
);
// no session delete: delete files, vector data
await deleteDatasetDataVector({ teamId, datasetIds, collectionIds });
}

View File

@@ -97,7 +97,7 @@ export const createOrGetCollectionTags = async ({
datasetId,
tag: tagContent
})),
{ session }
{ session, ordered: true }
);
return [...existingTags.map((tag) => tag._id), ...newTags.map((tag) => tag._id)];
@@ -174,6 +174,14 @@ export const syncCollection = async (collection: CollectionWithDatasetType) => {
}
await mongoSessionRun(async (session) => {
// Delete old collection
await delCollection({
collections: [collection],
delImg: false,
delFile: false,
session
});
// Create new collection
await createCollectionAndInsertData({
session,
@@ -208,13 +216,6 @@ export const syncCollection = async (collection: CollectionWithDatasetType) => {
updateTime: new Date()
}
});
// Delete old collection
await delCollection({
collections: [collection],
delRelatedSource: false,
session
});
});
return DatasetCollectionSyncResultEnum.success;

View File

@@ -7,6 +7,8 @@ import { MongoDatasetTraining } from './training/schema';
import { MongoDatasetData } from './data/schema';
import { deleteDatasetDataVector } from '../../common/vectorStore/controller';
import { MongoDatasetDataText } from './data/dataTextSchema';
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
import { retryFn } from '@fastgpt/global/common/system/utils';
/* ============= dataset ========== */
/* find all datasetId by top datasetId */
@@ -54,7 +56,7 @@ export async function getCollectionWithDataset(collectionId: string) {
.populate<{ dataset: DatasetSchemaType }>('dataset')
.lean();
if (!data) {
return Promise.reject('Collection is not exist');
return Promise.reject(DatasetErrEnum.unExistCollection);
}
return data;
}
@@ -77,40 +79,39 @@ export async function delDatasetRelevantData({
const datasetIds = datasets.map((item) => item._id);
// delete training data
await MongoDatasetTraining.deleteMany({
teamId,
datasetId: { $in: datasetIds }
});
// Get _id, teamId, fileId, metadata.relatedImgId for all collections
const collections = await MongoDatasetCollection.find(
{
teamId,
datasetId: { $in: datasetIds }
},
'_id teamId datasetId fileId metadata',
{ session }
'_id teamId datasetId fileId metadata'
).lean();
// Delete Image and file
await delCollectionRelatedSource({ collections, session });
await retryFn(async () => {
await Promise.all([
// delete training data
MongoDatasetTraining.deleteMany({
teamId,
datasetId: { $in: datasetIds }
}),
//Delete dataset_data_texts
MongoDatasetDataText.deleteMany({
teamId,
datasetId: { $in: datasetIds }
}),
//delete dataset_datas
MongoDatasetData.deleteMany({ teamId, datasetId: { $in: datasetIds } }),
// Delete Image and file
delCollectionRelatedSource({ collections }),
// Delete vector data
deleteDatasetDataVector({ teamId, datasetIds })
]);
});
// delete collections
await MongoDatasetCollection.deleteMany({
teamId,
datasetId: { $in: datasetIds }
}).session(session);
// No session delete:
// Delete dataset_data_texts
await MongoDatasetDataText.deleteMany({
teamId,
datasetId: { $in: datasetIds }
});
// delete dataset_datas
await MongoDatasetData.deleteMany({ teamId, datasetId: { $in: datasetIds } });
// Delete vector data
await deleteDatasetDataVector({ teamId, datasetIds });
}

View File

@@ -1,6 +1,6 @@
import { connectionMongo, getMongoModel } from '../../../common/mongo';
const { Schema } = connectionMongo;
import { DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type.d';
import { DatasetDataTextSchemaType } from '@fastgpt/global/core/dataset/type.d';
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
import { DatasetCollectionName } from '../schema';
import { DatasetColCollectionName } from '../collection/schema';
@@ -40,12 +40,13 @@ try {
default_language: 'none'
}
);
DatasetDataTextSchema.index({ teamId: 1, datasetId: 1, collectionId: 1 });
DatasetDataTextSchema.index({ dataId: 1 }, { unique: true });
} catch (error) {
console.log(error);
}
export const MongoDatasetDataText = getMongoModel<DatasetDataSchemaType>(
export const MongoDatasetDataText = getMongoModel<DatasetDataTextSchemaType>(
DatasetDataTextCollectionName,
DatasetDataTextSchema
);

View File

@@ -200,6 +200,7 @@ export async function searchDatasetData(
forbidCollectionIdList: collections.map((item) => String(item._id))
};
};
/*
Collection metadata filter
标签过滤:
@@ -207,6 +208,63 @@ export async function searchDatasetData(
2. and 标签和 null 不能共存,否则返回空数组
*/
const filterCollectionByMetadata = async (): Promise<string[] | undefined> => {
const getAllCollectionIds = async ({
parentCollectionIds
}: {
parentCollectionIds?: string[];
}): Promise<string[] | undefined> => {
if (!parentCollectionIds) return;
if (parentCollectionIds.length === 0) {
return [];
}
const collections = await MongoDatasetCollection.find(
{
teamId,
datasetId: { $in: datasetIds },
_id: { $in: parentCollectionIds }
},
'_id type',
{
...readFromSecondary
}
).lean();
const resultIds = new Set<string>();
collections.forEach((item) => {
if (item.type !== 'folder') {
resultIds.add(String(item._id));
}
});
const folderIds = collections
.filter((item) => item.type === 'folder')
.map((item) => String(item._id));
// Get all child collection ids
if (folderIds.length) {
const childCollections = await MongoDatasetCollection.find(
{
teamId,
datasetId: { $in: datasetIds },
parentId: { $in: folderIds }
},
'_id type',
{
...readFromSecondary
}
).lean();
const childIds = await getAllCollectionIds({
parentCollectionIds: childCollections.map((item) => String(item._id))
});
childIds?.forEach((id) => resultIds.add(id));
}
return Array.from(resultIds);
};
if (!collectionFilterMatch || !global.feConfigs.isPlus) return;
let tagCollectionIdList: string[] | undefined = undefined;
@@ -326,13 +384,19 @@ export async function searchDatasetData(
}
// Concat tag and time
if (tagCollectionIdList && createTimeCollectionIdList) {
return tagCollectionIdList.filter((id) => createTimeCollectionIdList!.includes(id));
} else if (tagCollectionIdList) {
return tagCollectionIdList;
} else if (createTimeCollectionIdList) {
return createTimeCollectionIdList;
}
const collectionIds = (() => {
if (tagCollectionIdList && createTimeCollectionIdList) {
return tagCollectionIdList.filter((id) =>
(createTimeCollectionIdList as string[]).includes(id)
);
}
return tagCollectionIdList || createTimeCollectionIdList;
})();
return await getAllCollectionIds({
parentCollectionIds: collectionIds
});
} catch (error) {}
};
const embeddingRecall = async ({

View File

@@ -3,11 +3,8 @@ import { filterGPTMessageByMaxContext, loadRequestMessages } from '../../../chat
import type { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type.d';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import {
parseReasoningContent,
parseReasoningStreamContent,
textAdaptGptResponse
} from '@fastgpt/global/core/workflow/runtime/utils';
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
import { parseReasoningContent, parseReasoningStreamContent } from '../../../ai/utils';
import { createChatCompletion } from '../../../ai/config';
import type { ChatCompletionMessageParam, StreamChatType } from '@fastgpt/global/core/ai/type.d';
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';

View File

@@ -24,7 +24,7 @@
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"mammoth": "^1.6.0",
"mongoose": "^7.0.2",
"mongoose": "^8.10.1",
"multer": "1.4.5-lts.1",
"next": "14.2.5",
"nextjs-cors": "^2.2.0",

View File

@@ -178,7 +178,7 @@ export const getClbsAndGroupsWithInfo = async ({
]);
export const delResourcePermissionById = (id: string) => {
return MongoResourcePermission.findByIdAndRemove(id);
return MongoResourcePermission.findByIdAndDelete(id);
};
export const delResourcePermission = ({
session,

View File

@@ -196,7 +196,8 @@ export async function syncCollaborators({
permission: item.permission
})),
{
session
session,
ordered: true
}
);
}

View File

@@ -54,7 +54,7 @@ export async function authOutLinkCrud({
}
/* outLink exist and it app exist */
export async function authOutLinkValid<T extends OutlinkAppType = undefined>({
export async function authOutLinkValid<T extends OutlinkAppType = any>({
shareId
}: {
shareId?: string;
@@ -62,7 +62,7 @@ export async function authOutLinkValid<T extends OutlinkAppType = undefined>({
if (!shareId) {
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
}
const outLinkConfig = (await MongoOutLink.findOne({ shareId }).lean()) as OutLinkSchema<T>;
const outLinkConfig = await MongoOutLink.findOne({ shareId }).lean<OutLinkSchema<T>>();
if (!outLinkConfig) {
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
@@ -70,6 +70,6 @@ export async function authOutLinkValid<T extends OutlinkAppType = undefined>({
return {
appId: outLinkConfig.appId,
outLinkConfig
outLinkConfig: outLinkConfig
};
}

View File

@@ -64,7 +64,7 @@ export const checkTeamDatasetLimit = async (teamId: string) => {
export const checkTeamAppLimit = async (teamId: string, amount = 1) => {
const [{ standardConstants }, appCount] = await Promise.all([
getTeamStandPlan({ teamId }),
MongoApp.count({
MongoApp.countDocuments({
teamId,
type: { $in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin] }
})

View File

@@ -15,7 +15,7 @@ import { TeamDefaultPermissionVal } from '@fastgpt/global/support/permission/use
import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupSchema';
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
import { getAIApi, openaiBaseUrl } from '../../../core/ai/config';
import { getAIApi } from '../../../core/ai/config';
import { createRootOrg } from '../../permission/org/controllers';
import { refreshSourceAvatar } from '../../../common/file/image/controller';
@@ -152,7 +152,7 @@ export async function updateTeam({
// auth openai key
if (openaiAccount?.key) {
console.log('auth user openai key', openaiAccount?.key);
const baseUrl = openaiAccount?.baseUrl || openaiBaseUrl;
const baseUrl = openaiAccount?.baseUrl || 'https://api.openai.com/v1';
openaiAccount.baseUrl = baseUrl;
const ai = getAIApi({

View File

@@ -100,7 +100,7 @@ export const initTeamFreePlan = async ({
surplusPoints: freePoints
}
],
{ session }
{ session, ordered: true }
);
};

View File

@@ -8,12 +8,12 @@ import { i18nT } from '../../../../web/i18n/utils';
import { pushConcatBillTask, pushReduceTeamAiPointsTask } from './utils';
import { POST } from '../../../common/api/plusRequest';
import { FastGPTProUrl } from '../../../common/system/constants';
import { isFastGPTMainService } from '../../../common/system/constants';
export async function createUsage(data: CreateUsageProps) {
try {
// In FastGPT server
if (FastGPTProUrl) {
if (isFastGPTMainService) {
await POST('/support/wallet/usage/createUsage', data);
} else if (global.reduceAiPointsQueue) {
// In FastGPT pro server
@@ -31,7 +31,7 @@ export async function createUsage(data: CreateUsageProps) {
export async function concatUsage(data: ConcatUsageProps) {
try {
// In FastGPT server
if (FastGPTProUrl) {
if (isFastGPTMainService) {
await POST('/support/wallet/usage/concatUsage', data);
} else if (global.reduceAiPointsQueue) {
const {
@@ -160,7 +160,7 @@ export const createTrainingUsage = async ({
]
}
],
{ session }
{ session, ordered: true }
);
return { billId: String(_id) };

View File

@@ -45,11 +45,11 @@ const parsePowerPoint = async ({
// Returning an array of all the xml contents read using fs.readFileSync
const xmlContentArray = await Promise.all(
files.map((file) => {
files.map(async (file) => {
try {
return fs.promises.readFile(`${decompressPath}/${file.path}`, encoding);
return await fs.promises.readFile(`${decompressPath}/${file.path}`, encoding);
} catch (err) {
return fs.promises.readFile(`${decompressPath}/${file.path}`, 'utf-8');
return await fs.promises.readFile(`${decompressPath}/${file.path}`, 'utf-8');
}
})
);

View File

@@ -100,6 +100,13 @@ const DateRangePicker = ({
if (date?.to === undefined) {
date.to = date.from;
}
if (date?.from) {
date.from = new Date(date.from.setHours(0, 0, 0, 0));
}
if (date?.to) {
date.to = new Date(date.to.setHours(23, 59, 59, 999));
}
setRange(date);
onChange?.(date);
}}

View File

@@ -6,6 +6,7 @@ export const iconPaths = {
chatSend: () => import('./icons/chatSend.svg'),
check: () => import('./icons/check.svg'),
checkCircle: () => import('./icons/checkCircle.svg'),
close: () => import('./icons/close.svg'),
closeSolid: () => import('./icons/closeSolid.svg'),
code: () => import('./icons/code.svg'),
collectionLight: () => import('./icons/collectionLight.svg'),
@@ -32,8 +33,10 @@ export const iconPaths = {
'common/customTitleLight': () => import('./icons/common/customTitleLight.svg'),
'common/data': () => import('./icons/common/data.svg'),
'common/dingtalkFill': () => import('./icons/common/dingtalkFill.svg'),
'common/disable': () => import('./icons/common/disable.svg'),
'common/downArrowFill': () => import('./icons/common/downArrowFill.svg'),
'common/editor/resizer': () => import('./icons/common/editor/resizer.svg'),
'common/enable': () => import('./icons/common/enable.svg'),
'common/errorFill': () => import('./icons/common/errorFill.svg'),
'common/file/move': () => import('./icons/common/file/move.svg'),
'common/folderFill': () => import('./icons/common/folderFill.svg'),
@@ -161,6 +164,7 @@ export const iconPaths = {
'core/chat/chatLight': () => import('./icons/core/chat/chatLight.svg'),
'core/chat/chatModelTag': () => import('./icons/core/chat/chatModelTag.svg'),
'core/chat/chevronDown': () => import('./icons/core/chat/chevronDown.svg'),
'core/chat/chevronLeft': () => import('./icons/core/chat/chevronLeft.svg'),
'core/chat/chevronRight': () => import('./icons/core/chat/chevronRight.svg'),
'core/chat/chevronSelector': () => import('./icons/core/chat/chevronSelector.svg'),
'core/chat/chevronUp': () => import('./icons/core/chat/chevronUp.svg'),
@@ -329,6 +333,7 @@ export const iconPaths = {
edit: () => import('./icons/edit.svg'),
empty: () => import('./icons/empty.svg'),
export: () => import('./icons/export.svg'),
feedback: () => import('./icons/feedback.svg'),
'file/csv': () => import('./icons/file/csv.svg'),
'file/fill/csv': () => import('./icons/file/fill/csv.svg'),
'file/fill/doc': () => import('./icons/file/fill/doc.svg'),

View File

@@ -0,0 +1,3 @@
<svg viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.65613 2.84383C9.85139 3.0391 9.85139 3.35568 9.65613 3.55094L7.20708 5.99999L9.65617 8.44908C9.85143 8.64434 9.85143 8.96093 9.65617 9.15619C9.46091 9.35145 9.14433 9.35145 8.94906 9.15619L6.49997 6.7071L4.05088 9.15619C3.85562 9.35145 3.53904 9.35145 3.34377 9.15619C3.14851 8.96093 3.14851 8.64434 3.34377 8.44908L5.79286 5.99999L3.34382 3.55094C3.14855 3.35568 3.14855 3.0391 3.34382 2.84383C3.53908 2.64857 3.85566 2.64857 4.05092 2.84383L6.49997 5.29288L8.94902 2.84383C9.14428 2.64857 9.46087 2.64857 9.65613 2.84383Z" fill="#92A5C9"/>
</svg>

After

Width:  |  Height:  |  Size: 675 B

View File

@@ -0,0 +1 @@
<svg t="1740494996853" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2899" width="64" height="64"><path d="M512 953.6a441.6 441.6 0 1 1 0-883.2 441.6 441.6 0 0 1 0 883.2z m0-64a377.6 377.6 0 1 0 0-755.2 377.6 377.6 0 0 0 0 755.2z" p-id="2900"></path><path d="M182.1696 227.4304l45.2608-45.2608 614.4 614.4-45.2608 45.2608z" p-id="2901"></path></svg>

After

Width:  |  Height:  |  Size: 397 B

View File

@@ -0,0 +1 @@
<svg t="1740495050372" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4745" width="64" height="64"><path d="M510.2 959.7c-246.9-1-447-202.6-446-449.5s202.6-447 449.5-446 447 202.6 446 449.5-202.6 447-449.5 446z m3.3-833.7c-212.8-0.8-386.7 171.7-387.5 384.5S297.7 897.2 510.5 898 897.2 726.3 898 513.5 726.3 126.8 513.5 126z" p-id="4746"></path><path d="M465.8 712.3L291.1 537.6l43.7-43.7 131 131 262-262 43.7 43.7z" p-id="4747"></path></svg>

After

Width:  |  Height:  |  Size: 486 B

View File

@@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.4714 3.52864C10.7317 3.78899 10.7317 4.2111 10.4714 4.47145L6.94277 8.00004L10.4714 11.5286C10.7317 11.789 10.7317 12.2111 10.4714 12.4714C10.211 12.7318 9.7889 12.7318 9.52855 12.4714L5.52855 8.47144C5.26821 8.21109 5.26821 7.78898 5.52855 7.52864L9.52855 3.52864C9.7889 3.26829 10.211 3.26829 10.4714 3.52864Z" fill="#92A5C9"/>
</svg>

After

Width:  |  Height:  |  Size: 464 B

View File

@@ -0,0 +1,29 @@
<svg viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.49993 10.1261C8.49993 9.48174 9.02226 8.9594 9.6666 8.9594H14.3333C14.9776 8.9594 15.4999 9.48174 15.4999 10.1261C15.4999 10.7704 14.9776 11.2927 14.3333 11.2927H9.6666C9.02226 11.2927 8.49993 10.7704 8.49993 10.1261Z" fill="url(#paint0_linear_17422_2138)"/>
<path d="M8.49993 14.7927C8.49993 14.1484 9.02226 13.6261 9.6666 13.6261H18.4041C19.0485 13.6261 19.5708 14.1484 19.5708 14.7927C19.5708 15.4371 19.0485 15.9594 18.4041 15.9594H9.6666C9.02226 15.9594 8.49993 15.4371 8.49993 14.7927Z" fill="url(#paint1_linear_17422_2138)"/>
<path d="M17.6859 9.1641C17.3948 9.37757 17.2057 9.72212 17.2057 10.1108V11.2504C17.2057 11.2738 17.2247 11.2927 17.248 11.2927H18.3745C18.7286 11.2927 19.0462 11.1358 19.262 10.8878L25.8249 4.32494C26.2805 3.86933 26.2805 3.13063 25.8249 2.67502C25.3693 2.21941 24.6306 2.21941 24.175 2.67502L17.6859 9.1641Z" fill="url(#paint2_linear_17422_2138)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.5122 3.45903C17.5122 2.81469 16.9899 2.29236 16.3456 2.29236H7.34644C5.41344 2.29236 3.84644 3.85936 3.84644 5.79236V22.1257C3.84644 24.0587 5.41344 25.6257 7.34644 25.6257H21.6535C23.5865 25.6257 25.1535 24.0587 25.1535 22.1257V12.1788C25.1535 11.5344 24.6311 11.0121 23.9868 11.0121C23.3425 11.0121 22.8201 11.5345 22.8201 12.1788V22.1257C22.8201 22.77 22.2978 23.2924 21.6535 23.2924H7.34644C6.7021 23.2924 6.17977 22.77 6.17977 22.1257V5.79236C6.17977 5.14803 6.7021 4.62569 7.34644 4.62569H16.3456C16.9899 4.62569 17.5122 4.10336 17.5122 3.45903Z" fill="url(#paint3_linear_17422_2138)"/>
<defs>
<linearGradient id="paint0_linear_17422_2138" x1="15.0065" y1="3.04993" x2="15.0065" y2="24.8681" gradientUnits="userSpaceOnUse">
<stop stop-color="#499DFF"/>
<stop offset="0.432292" stop-color="#2770FF"/>
<stop offset="1" stop-color="#6E80FF"/>
</linearGradient>
<linearGradient id="paint1_linear_17422_2138" x1="15.0065" y1="3.04993" x2="15.0065" y2="24.8681" gradientUnits="userSpaceOnUse">
<stop stop-color="#499DFF"/>
<stop offset="0.432292" stop-color="#2770FF"/>
<stop offset="1" stop-color="#6E80FF"/>
</linearGradient>
<linearGradient id="paint2_linear_17422_2138" x1="15.0065" y1="3.04993" x2="15.0065" y2="24.8681" gradientUnits="userSpaceOnUse">
<stop stop-color="#499DFF"/>
<stop offset="0.432292" stop-color="#2770FF"/>
<stop offset="1" stop-color="#6E80FF"/>
</linearGradient>
<linearGradient id="paint3_linear_17422_2138" x1="15.0065" y1="3.04993" x2="15.0065" y2="24.8681" gradientUnits="userSpaceOnUse">
<stop stop-color="#499DFF"/>
<stop offset="0.432292" stop-color="#2770FF"/>
<stop offset="1" stop-color="#6E80FF"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import type { IconProps } from '@chakra-ui/react';
import { Box, Icon } from '@chakra-ui/react';
import { iconPaths } from './constants';
@@ -8,7 +8,7 @@ import { useRefresh } from '../../../hooks/useRefresh';
const iconCache: Record<string, any> = {};
const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconNameType } & IconProps) => {
const { refresh } = useRefresh();
const [, setUpdate] = useState(0);
useEffect(() => {
if (iconCache[name]) {
@@ -20,7 +20,7 @@ const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconNameType
const component = { as: icon.default };
// Store in cache
iconCache[name] = component;
refresh();
setUpdate((prev) => prev + 1); // force update
})
.catch((error) => console.log(error));
}, [name]);

View File

@@ -10,8 +10,9 @@ import React from 'react';
import MyIcon from '../../Icon';
import { UseFormRegister } from 'react-hook-form';
type Props = Omit<NumberInputProps, 'onChange'> & {
type Props = Omit<NumberInputProps, 'onChange' | 'onBlur'> & {
onChange?: (e?: number) => any;
onBlur?: (e?: number) => any;
placeholder?: string;
register?: UseFormRegister<any>;
name?: string;
@@ -19,11 +20,21 @@ type Props = Omit<NumberInputProps, 'onChange'> & {
};
const MyNumberInput = (props: Props) => {
const { register, name, onChange, placeholder, bg, ...restProps } = props;
const { register, name, onChange, onBlur, placeholder, bg, ...restProps } = props;
return (
<NumberInput
{...restProps}
onBlur={(e) => {
if (!onBlur) return;
const numE = Number(e.target.value);
if (isNaN(numE)) {
// @ts-ignore
onBlur('');
} else {
onBlur(numE);
}
}}
onChange={(e) => {
if (!onChange) return;
const numE = Number(e);
@@ -38,6 +49,8 @@ const MyNumberInput = (props: Props) => {
<NumberInputField
bg={bg}
placeholder={placeholder}
h={restProps.h}
defaultValue={restProps.defaultValue}
{...(register && name
? register(name, {
required: props.isRequired,

View File

@@ -98,7 +98,6 @@ const MultipleSelect = <T = any,>({
return (
<MenuItem
key={i}
{...menuItemStyles}
{...(isSelected
? {
color: 'primary.600'
@@ -114,6 +113,7 @@ const MultipleSelect = <T = any,>({
whiteSpace={'pre-wrap'}
fontSize={'sm'}
gap={2}
{...menuItemStyles}
>
<Checkbox isChecked={isSelected} />
{item.icon && <MyAvatar src={item.icon} w={'1rem'} borderRadius={'0'} />}
@@ -204,6 +204,7 @@ const MultipleSelect = <T = any,>({
}}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
onclickItem(item.value);
}}
/>
@@ -230,7 +231,6 @@ const MultipleSelect = <T = any,>({
overflowY={'auto'}
>
<MenuItem
{...menuItemStyles}
color={isSelectAll ? 'primary.600' : 'myGray.900'}
onClick={(e) => {
e.stopPropagation();
@@ -241,6 +241,7 @@ const MultipleSelect = <T = any,>({
fontSize={'sm'}
gap={2}
mb={1}
{...menuItemStyles}
>
<Checkbox isChecked={isSelectAll} />
<Box flex={'1 0 0'}>{t('common:common.All')}</Box>

View File

@@ -4,7 +4,8 @@ import React, {
useMemo,
useEffect,
useImperativeHandle,
ForwardedRef
ForwardedRef,
useState
} from 'react';
import {
Menu,
@@ -15,7 +16,8 @@ import {
MenuButton,
Box,
css,
Flex
Flex,
Input
} from '@chakra-ui/react';
import type { ButtonProps, MenuItemProps } from '@chakra-ui/react';
import MyIcon from '../Icon';
@@ -33,8 +35,10 @@ import { useScrollPagination } from '../../../hooks/useScrollPagination';
export type SelectProps<T = any> = ButtonProps & {
value?: T;
placeholder?: string;
isSearch?: boolean;
list: {
alias?: string;
icon?: string;
label: string | React.ReactNode;
description?: string;
value: T;
@@ -49,6 +53,7 @@ const MySelect = <T = any,>(
{
placeholder,
value,
isSearch = false,
width = '100%',
list = [],
onchange,
@@ -63,6 +68,7 @@ const MySelect = <T = any,>(
const ButtonRef = useRef<HTMLButtonElement>(null);
const MenuListRef = useRef<HTMLDivElement>(null);
const SelectedItemRef = useRef<HTMLDivElement>(null);
const SearchInputRef = useRef<HTMLInputElement>(null);
const menuItemStyles: MenuItemProps = {
borderRadius: 'sm',
@@ -79,6 +85,18 @@ const MySelect = <T = any,>(
const { isOpen, onOpen, onClose } = useDisclosure();
const selectItem = useMemo(() => list.find((item) => item.value === value), [list, value]);
const [search, setSearch] = useState('');
const filterList = useMemo(() => {
if (!isSearch || !search) {
return list;
}
return list.filter((item) => {
const text = `${item.label?.toString()}${item.alias}${item.value}`;
const regx = new RegExp(search, 'gi');
return regx.test(text);
});
}, [list, search, isSearch]);
useImperativeHandle(ref, () => ({
focus() {
onOpen();
@@ -90,17 +108,19 @@ const MySelect = <T = any,>(
const menu = MenuListRef.current;
const selectedItem = SelectedItemRef.current;
menu.scrollTop = selectedItem.offsetTop - menu.offsetTop - 100;
if (isSearch) {
setSearch('');
}
}
}, [isOpen]);
}, [isSearch, isOpen]);
const { runAsync: onChange, loading } = useRequest2((val: T) => onchange?.(val));
const isSelecting = loading || isLoading;
const ListRender = useMemo(() => {
return (
<>
{list.map((item, i) => (
{filterList.map((item, i) => (
<Box key={i}>
<MenuItem
{...menuItemStyles}
@@ -123,7 +143,10 @@ const MySelect = <T = any,>(
fontSize={'sm'}
display={'block'}
>
<Box>{item.label}</Box>
<Flex alignItems={'center'}>
{item.icon && <MyIcon mr={2} name={item.icon as any} w={'1rem'} />}
{item.label}
</Flex>
{item.description && (
<Box color={'myGray.500'} fontSize={'xs'}>
{item.description}
@@ -135,7 +158,9 @@ const MySelect = <T = any,>(
))}
</>
);
}, [list, value]);
}, [filterList, value]);
const isSelecting = loading || isLoading;
return (
<Box
@@ -176,8 +201,33 @@ const MySelect = <T = any,>(
{...props}
>
<Flex alignItems={'center'}>
{isSelecting && <MyIcon mr={2} name={'common/loading'} w={'16px'} />}
{selectItem?.alias || selectItem?.label || placeholder}
{isSelecting && <MyIcon mr={2} name={'common/loading'} w={'1rem'} />}
{isSearch && isOpen ? (
<Input
ref={SearchInputRef}
autoFocus
variant={'unstyled'}
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder={
selectItem?.alias ||
(typeof selectItem?.label === 'string' ? selectItem?.label : placeholder)
}
size={'sm'}
w={'100%'}
color={'myGray.700'}
onBlur={() => {
setTimeout(() => {
SearchInputRef?.current?.focus();
}, 0);
}}
/>
) : (
<>
{selectItem?.icon && <MyIcon mr={2} name={selectItem.icon as any} w={'1rem'} />}
{selectItem?.alias || selectItem?.label || placeholder}
</>
)}
</Flex>
</MenuButton>

View File

@@ -0,0 +1,47 @@
{
"api_key": "API key",
"azure": "Azure",
"base_url": "Base url",
"channel_name": "Channel",
"channel_priority": "Priority",
"channel_priority_tip": "The higher the priority channel, the easier it is to be requested",
"channel_status": "state",
"channel_status_auto_disabled": "Automatically disable",
"channel_status_disabled": "Disabled",
"channel_status_enabled": "Enable",
"channel_status_unknown": "unknown",
"channel_type": "Manufacturer",
"clear_model": "Clear the model",
"copy_model_id_success": "Copyed model id",
"create_channel": "Added channels",
"default_url": "Default address",
"detail": "Detail",
"duration": "Duration",
"edit": "edit",
"edit_channel": "Channel configuration",
"enable_channel": "Enable",
"forbid_channel": "Disabled",
"key_type": "API key format:",
"log": "Call log",
"log_detail": "Log details",
"log_request_id_search": "Search by requestId",
"log_status": "Status",
"mapping": "Model Mapping",
"mapping_tip": "A valid Json is required. \nThe model can be mapped when sending a request to the actual address. \nFor example:\n{\n \n \"gpt-4o\": \"gpt-4o-test\"\n\n}\n\nWhen FastGPT requests the gpt-4o model, the gpt-4o-test model is sent to the actual address, instead of gpt-4o.",
"model": "Model",
"model_name": "Model name",
"model_test": "Model testing",
"model_tokens": "Input/Output tokens",
"request_at": "Request time",
"request_duration": "Request duration: {{duration}}s",
"running_test": "In testing",
"search_model": "Search for models",
"select_channel": "Select a channel name",
"select_model": "Select a model",
"select_model_placeholder": "Select the model available under this channel",
"select_provider_placeholder": "Search for manufacturers",
"selected_model_empty": "Choose at least one model",
"start_test": "Start testing {{num}} models",
"test_failed": "There are {{num}} models that report errors",
"waiting_test": "Waiting for testing"
}

View File

@@ -125,7 +125,6 @@
"common.Copy Successful": "Copied Successfully",
"common.Copy_failed": "Copy Failed, Please Copy Manually",
"common.Create Failed": "Creation Failed",
"common.Create New": "Create",
"common.Create Success": "Created Successfully",
"common.Create Time": "Creation Time",
"common.Creating": "Creating",
@@ -547,7 +546,6 @@
"core.dataset.data.Main Content": "Main Content",
"core.dataset.data.Search data placeholder": "Search Related Data",
"core.dataset.data.Too Long": "Total Length Exceeded",
"core.dataset.data.Total Amount": "{{total}} Groups",
"core.dataset.data.group": "Group",
"core.dataset.data.unit": "Items",
"core.dataset.embedding model tip": "The index model can convert natural language into vectors for semantic search.\nNote that different index models cannot be used together. Once an index model is selected, it cannot be changed.",
@@ -860,7 +858,6 @@
"dataset.collections.Collection Embedding": "{{total}} Indexes",
"dataset.collections.Confirm to delete the folder": "Confirm to Delete This Folder and All Its Contents?",
"dataset.collections.Create And Import": "Create/Import",
"dataset.collections.Data Amount": "Total Data",
"dataset.collections.Select Collection": "Select File",
"dataset.collections.Select One Collection To Store": "Select a File to Store",
"dataset.data.Can not edit": "No Edit Permission",
@@ -876,6 +873,7 @@
"dataset.dataset_name": "Dataset Name",
"dataset.deleteFolderTips": "Confirm to Delete This Folder and All Its Contained Datasets? Data Cannot Be Recovered After Deletion, Please Confirm!",
"dataset.test.noResult": "No Search Results",
"dataset_text_model_tip": "Used for text processing in the knowledge base preprocessing stage, such as automatic supplementary indexing, Q&A pair extraction.",
"deep_rag_search": "In-depth search",
"delete_api": "Are you sure you want to delete this API key? \nAfter deletion, the key will become invalid immediately and the corresponding conversation log will not be deleted. Please confirm!",
"embedding_model_not_config": "No index model is detected",
@@ -890,6 +888,7 @@
"error.upload_file_error_filename": "{{name}} Upload Failed",
"error.upload_image_error": "File upload failed",
"error.username_empty": "Account cannot be empty",
"error_collection_not_exist": "The collection does not exist",
"extraction_results": "Extraction Results",
"field_name": "Field Name",
"free": "Free",
@@ -1013,6 +1012,7 @@
"plugin.go to laf": "Go to Write",
"plugin.path": "Path",
"prompt_input_placeholder": "Please enter the prompt word",
"question_feedback": "Work order",
"read_quote": "View citations",
"required": "Required",
"resume_failed": "Resume Failed",

View File

@@ -7,6 +7,7 @@
"close_auto_sync": "Are you sure you want to turn off automatic sync?",
"collection.Create update time": "Creation/Update Time",
"collection.Training type": "Training",
"collection_data_count": "Data amount",
"collection_not_support_retraining": "This collection type does not support retuning parameters",
"collection_not_support_sync": "This collection does not support synchronization",
"collection_sync": "Sync data",
@@ -20,6 +21,7 @@
"custom_data_process_params": "Custom",
"custom_data_process_params_desc": "Customize data processing rules",
"data.ideal_chunk_length": "ideal block length",
"data_amount": "{{dataAmount}} Datas, {{indexAmount}} Indexes",
"data_process_params": "Params",
"data_process_setting": "Processing config",
"dataset.Unsupported operation": "dataset.Unsupported operation",

View File

@@ -3,7 +3,7 @@
"add_default_model": "添加预设模型",
"api_key": "API 密钥",
"bills_and_invoices": "账单与发票",
"channel": "渠道",
"channel": "模型渠道",
"config_model": "模型配置",
"confirm_logout": "确认退出登录?",
"create_channel": "新增渠道",

View File

@@ -0,0 +1,47 @@
{
"api_key": "API 密钥",
"azure": "微软 Azure",
"base_url": "代理地址",
"channel_name": "渠道名",
"channel_priority": "优先级",
"channel_priority_tip": "优先级越高的渠道,越容易被请求到",
"channel_status": "状态",
"channel_status_auto_disabled": "自动禁用",
"channel_status_disabled": "禁用",
"channel_status_enabled": "启用",
"channel_status_unknown": "未知",
"channel_type": "厂商",
"clear_model": "清空模型",
"copy_model_id_success": "已复制模型id",
"create_channel": "新增渠道",
"default_url": "默认地址",
"detail": "详情",
"duration": "耗时",
"edit": "编辑",
"edit_channel": "渠道配置",
"enable_channel": "启用",
"forbid_channel": "禁用",
"key_type": "API key 格式: ",
"log": "调用日志",
"log_detail": "日志详情",
"log_request_id_search": "根据 requestId 搜索",
"log_status": "状态",
"mapping": "模型映射",
"mapping_tip": "需填写一个有效 Json。可在向实际地址发送请求时对模型进行映射。例如\n{\n \"gpt-4o\": \"gpt-4o-test\"\n}\n当 FastGPT 请求 gpt-4o 模型时,会向实际地址发送 gpt-4o-test 的模型,而不是 gpt-4o。",
"model": "模型",
"model_name": "模型名",
"model_test": "模型测试",
"model_tokens": "输入/输出 Tokens",
"request_at": "请求时间",
"request_duration": "请求时长: {{duration}}s",
"running_test": "测试中",
"search_model": "搜索模型",
"select_channel": "选择渠道名",
"select_model": "选择模型",
"select_model_placeholder": "选择该渠道下可用的模型",
"select_provider_placeholder": "搜索厂商",
"selected_model_empty": "至少选择一个模型",
"start_test": "开始测试{{num}}个模型",
"test_failed": "有{{num}}个模型报错",
"waiting_test": "等待测试"
}

View File

@@ -129,7 +129,6 @@
"common.Copy Successful": "复制成功",
"common.Copy_failed": "复制失败,请手动复制",
"common.Create Failed": "创建异常",
"common.Create New": "新建",
"common.Create Success": "创建成功",
"common.Create Time": "创建时间",
"common.Creating": "创建中",
@@ -550,7 +549,6 @@
"core.dataset.data.Main Content": "主要内容",
"core.dataset.data.Search data placeholder": "搜索相关数据",
"core.dataset.data.Too Long": "总长度超长了",
"core.dataset.data.Total Amount": "{{total}} 组",
"core.dataset.data.group": "组",
"core.dataset.data.unit": "条",
"core.dataset.embedding model tip": "索引模型可以将自然语言转成向量,用于进行语义检索。\n注意不同索引模型无法一起使用选择完索引模型后将无法修改。",
@@ -863,7 +861,6 @@
"dataset.collections.Collection Embedding": "{{total}} 组索引中",
"dataset.collections.Confirm to delete the folder": "确认删除该文件夹及里面所有内容?",
"dataset.collections.Create And Import": "新建/导入",
"dataset.collections.Data Amount": "数据总量",
"dataset.collections.Select Collection": "选择文件",
"dataset.collections.Select One Collection To Store": "选择一个文件进行存储",
"dataset.data.Can not edit": "无编辑权限",
@@ -879,6 +876,7 @@
"dataset.dataset_name": "知识库名称",
"dataset.deleteFolderTips": "确认删除该文件夹及其包含的所有知识库?删除后数据无法恢复,请确认!",
"dataset.test.noResult": "搜索结果为空",
"dataset_text_model_tip": "用于知识库预处理阶段的文本处理,例如自动补充索引、问答对提取。",
"deep_rag_search": "深度搜索",
"delete_api": "确认删除该API密钥删除后该密钥立即失效对应的对话日志不会删除请确认",
"embedding_model_not_config": "检测到没有可用的索引模型",
@@ -893,6 +891,7 @@
"error.upload_file_error_filename": "{{name}} 上传失败",
"error.upload_image_error": "上传文件失败",
"error.username_empty": "账号不能为空",
"error_collection_not_exist": "集合不存在",
"extraction_results": "提取结果",
"field_name": "字段名",
"free": "免费",
@@ -944,9 +943,9 @@
"model_moka": "Moka-AI",
"model_moonshot": "月之暗面",
"model_other": "其他",
"model_ppio": "PPIO 派欧云",
"model_qwen": "阿里千问",
"model_siliconflow": "硅基流动",
"model_ppio": "PPIO 派欧云",
"model_sparkdesk": "讯飞星火",
"model_stepfun": "阶跃星辰",
"model_yi": "零一万物",
@@ -1016,6 +1015,7 @@
"plugin.go to laf": "去编写",
"plugin.path": "路径",
"prompt_input_placeholder": "请输入提示词",
"question_feedback": "工单咨询",
"read_quote": "查看引用",
"required": "必须",
"resume_failed": "恢复失败",

View File

@@ -7,6 +7,7 @@
"close_auto_sync": "确认关闭自动同步功能?",
"collection.Create update time": "创建/更新时间",
"collection.Training type": "训练模式",
"collection_data_count": "数据量",
"collection_not_support_retraining": "该集合类型不支持重新调整参数",
"collection_not_support_sync": "该集合不支持同步",
"collection_sync": "立即同步",
@@ -20,6 +21,7 @@
"custom_data_process_params": "自定义",
"custom_data_process_params_desc": "自定义设置数据处理规则",
"data.ideal_chunk_length": "理想分块长度",
"data_amount": "{{dataAmount}} 组数据, {{indexAmount}} 组索引",
"data_process_params": "处理参数",
"data_process_setting": "数据处理配置",
"dataset.Unsupported operation": "操作不支持",

View File

@@ -3,7 +3,7 @@
"add_default_model": "新增預設模型",
"api_key": "API 金鑰",
"bills_and_invoices": "帳單與發票",
"channel": "道",
"channel": "模型渠道",
"config_model": "模型配置",
"confirm_logout": "確認登出登入?",
"create_channel": "新增頻道",

View File

@@ -0,0 +1,45 @@
{
"api_key": "API 密鑰",
"azure": "Azure",
"base_url": "代理地址",
"channel_name": "渠道名",
"channel_priority": "優先級",
"channel_priority_tip": "優先級越高的渠道,越容易被請求到",
"channel_status": "狀態",
"channel_status_auto_disabled": "自動禁用",
"channel_status_disabled": "禁用",
"channel_status_enabled": "啟用",
"channel_status_unknown": "未知",
"channel_type": "廠商",
"clear_model": "清空模型",
"copy_model_id_success": "已復制模型id",
"create_channel": "新增渠道",
"default_url": "默認地址",
"detail": "詳情",
"edit_channel": "渠道配置",
"enable_channel": "啟用",
"forbid_channel": "禁用",
"key_type": "API key 格式:",
"log": "調用日誌",
"log_detail": "日誌詳情",
"log_request_id_search": "根據 requestId 搜索",
"log_status": "狀態",
"mapping": "模型映射",
"mapping_tip": "需填寫一個有效 Json。\n可在向實際地址發送請求時對模型進行映射。\n例如\n{\n \n \"gpt-4o\": \"gpt-4o-test\"\n\n}\n\n當 FastGPT 請求 gpt-4o 模型時,會向實際地址發送 gpt-4o-test 的模型,而不是 gpt-4o。",
"model": "模型",
"model_name": "模型名",
"model_test": "模型測試",
"model_tokens": "輸入/輸出 Tokens",
"request_at": "請求時間",
"request_duration": "請求時長: {{duration}}s",
"running_test": "測試中",
"search_model": "搜索模型",
"select_channel": "選擇渠道名",
"select_model": "選擇模型",
"select_model_placeholder": "選擇該渠道下可用的模型",
"select_provider_placeholder": "搜索廠商",
"selected_model_empty": "至少選擇一個模型",
"start_test": "開始測試{{num}}個模型",
"test_failed": "有{{num}}個模型報錯",
"waiting_test": "等待測試"
}

View File

@@ -124,7 +124,6 @@
"common.Copy Successful": "複製成功",
"common.Copy_failed": "複製失敗,請手動複製",
"common.Create Failed": "建立失敗",
"common.Create New": "建立新項目",
"common.Create Success": "建立成功",
"common.Create Time": "建立時間",
"common.Creating": "建立中",
@@ -546,7 +545,6 @@
"core.dataset.data.Main Content": "主要內容",
"core.dataset.data.Search data placeholder": "搜尋相關資料",
"core.dataset.data.Too Long": "總長度超出上限",
"core.dataset.data.Total Amount": "{{total}} 組",
"core.dataset.data.group": "組",
"core.dataset.data.unit": "筆",
"core.dataset.embedding model tip": "索引模型可以將自然語言轉換成向量,用於進行語意搜尋。\n注意不同索引模型無法一起使用。選擇索引模型後就無法修改。",
@@ -860,7 +858,6 @@
"dataset.collections.Collection Embedding": "{{total}} 個索引",
"dataset.collections.Confirm to delete the folder": "確認刪除此資料夾及其所有內容?",
"dataset.collections.Create And Import": "建立或匯入",
"dataset.collections.Data Amount": "資料總量",
"dataset.collections.Select Collection": "選擇檔案",
"dataset.collections.Select One Collection To Store": "選擇一個檔案進行儲存",
"dataset.data.Can not edit": "無編輯權限",
@@ -876,6 +873,7 @@
"dataset.dataset_name": "知識庫名稱",
"dataset.deleteFolderTips": "確認刪除此資料夾及其包含的所有知識庫?刪除後資料無法復原,請確認!",
"dataset.test.noResult": "搜尋結果為空",
"dataset_text_model_tip": "用於知識庫預處理階段的文本處理,例如自動補充索引、問答對提取。",
"deep_rag_search": "深度搜索",
"delete_api": "確認刪除此 API 金鑰?\n刪除後該金鑰將立即失效對應的對話記錄不會被刪除請確認",
"embedding_model_not_config": "檢測到沒有可用的索引模型",
@@ -890,6 +888,7 @@
"error.upload_file_error_filename": "{{name}} 上傳失敗",
"error.upload_image_error": "上傳文件失敗",
"error.username_empty": "帳號不能為空",
"error_collection_not_exist": "集合不存在",
"extraction_results": "提取結果",
"field_name": "欄位名稱",
"free": "免費",
@@ -1012,6 +1011,7 @@
"plugin.go to laf": "前往編寫",
"plugin.path": "路徑",
"prompt_input_placeholder": "請輸入提示詞",
"question_feedback": "工單諮詢",
"read_quote": "查看引用",
"required": "必填",
"resume_failed": "恢復失敗",

View File

@@ -7,6 +7,7 @@
"close_auto_sync": "確認關閉自動同步功能?",
"collection.Create update time": "建立/更新時間",
"collection.Training type": "分段模式",
"collection_data_count": "數據量",
"collection_not_support_retraining": "此集合類型不支援重新調整參數",
"collection_not_support_sync": "該集合不支援同步",
"collection_sync": "立即同步",
@@ -20,6 +21,7 @@
"custom_data_process_params": "自訂",
"custom_data_process_params_desc": "自訂資料處理規則",
"data.ideal_chunk_length": "理想分塊長度",
"data_amount": "{{dataAmount}} 組數據, {{indexAmount}} 組索引",
"data_process_params": "處理參數",
"data_process_setting": "資料處理設定",
"dataset.Unsupported operation": "操作不支持",

View File

@@ -18,6 +18,7 @@ import workflow from '../i18n/zh-CN/workflow.json';
import user from '../i18n/zh-CN/user.json';
import chat from '../i18n/zh-CN/chat.json';
import login from '../i18n/zh-CN/login.json';
import account_model from '../i18n/zh-CN/account_model.json';
export interface I18nNamespaces {
common: typeof common;
@@ -39,6 +40,7 @@ export interface I18nNamespaces {
account: typeof account;
account_team: typeof account_team;
account_thirdParty: typeof account_thirdParty;
account_model: typeof account_model;
}
export type I18nNsType = (keyof I18nNamespaces)[];
@@ -73,7 +75,8 @@ declare module 'i18next' {
'account_promotion',
'account_thirdParty',
'account',
'account_team'
'account_team',
'account_model'
];
resources: I18nNamespaces;
}

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