Compare commits
47 Commits
v4.8.21-fi
...
v4.8.23-fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e860c56b77 | ||
|
|
efac5312b4 | ||
|
|
4bc7f21182 | ||
|
|
113e8f711f | ||
|
|
abc6dffb41 | ||
|
|
f7b2a57ca3 | ||
|
|
cf0aaa1091 | ||
|
|
ac4255ea0c | ||
|
|
df4d6f86ce | ||
|
|
e697fda82f | ||
|
|
1aa319e7aa | ||
|
|
fc9e614f88 | ||
|
|
1121ea33bd | ||
|
|
9bbee60cde | ||
|
|
9f57ad0017 | ||
|
|
c3d3b30d7e | ||
|
|
fb0eb49196 | ||
|
|
27ebd2e8cf | ||
|
|
81a06718d8 | ||
|
|
3c382d1240 | ||
|
|
747bb303ec | ||
|
|
cf9c8e9f6a | ||
|
|
5d5bee9e41 | ||
|
|
4f0dd96699 | ||
|
|
fb6dbaf2d6 | ||
|
|
ffc1520f4c | ||
|
|
255764400f | ||
|
|
3bfe802c48 | ||
|
|
2bf17dbb87 | ||
|
|
8d766372fe | ||
|
|
ca5717936b | ||
|
|
6762723b10 | ||
|
|
8604cbd021 | ||
|
|
206325bc5f | ||
|
|
5fd520c794 | ||
|
|
09205e4666 | ||
|
|
ccf28d83b8 | ||
|
|
420aaad48e | ||
|
|
8ba2339890 | ||
|
|
e7b8934367 | ||
|
|
3e13397614 | ||
|
|
b14674cc6f | ||
|
|
4d20274a97 | ||
|
|
4447e40364 | ||
|
|
23949230ee | ||
|
|
cd7a897304 | ||
|
|
18aff8b8db |
@@ -68,14 +68,3 @@ jobs:
|
|||||||
SEALOS_TYPE: 'pr_comment'
|
SEALOS_TYPE: 'pr_comment'
|
||||||
SEALOS_FILENAME: 'report.md'
|
SEALOS_FILENAME: 'report.md'
|
||||||
SEALOS_REPLACE_TAG: 'DEFAULT_REPLACE_DEPLOY'
|
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
|
|
||||||
4
.github/workflows/helm-release.yaml
vendored
4
.github/workflows/helm-release.yaml
vendored
@@ -24,6 +24,6 @@ jobs:
|
|||||||
export APP_VERSION=${{ steps.vars.outputs.tag }}
|
export APP_VERSION=${{ steps.vars.outputs.tag }}
|
||||||
export HELM_VERSION=${{ steps.vars.outputs.tag }}
|
export HELM_VERSION=${{ steps.vars.outputs.tag }}
|
||||||
export HELM_REPO=ghcr.io/${{ github.repository_owner }}
|
export HELM_REPO=ghcr.io/${{ github.repository_owner }}
|
||||||
helm dependency update files/helm/fastgpt
|
helm dependency update deploy/helm/fastgpt
|
||||||
helm package files/helm/fastgpt --version ${HELM_VERSION}-helm --app-version ${APP_VERSION} -d bin
|
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}
|
helm push bin/fastgpt-${HELM_VERSION}-helm.tgz oci://${HELM_REPO}
|
||||||
|
|||||||
2
.vscode/nextapi.code-snippets
vendored
2
.vscode/nextapi.code-snippets
vendored
@@ -58,7 +58,7 @@
|
|||||||
"body": [
|
"body": [
|
||||||
"import '@/pages/api/__mocks__/base';",
|
"import '@/pages/api/__mocks__/base';",
|
||||||
"import { root } from '@/pages/api/__mocks__/db/init';",
|
"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 { AppErrEnum } from '@fastgpt/global/common/error/code/app';",
|
||||||
"import handler from './demo';",
|
"import handler from './demo';",
|
||||||
"",
|
"",
|
||||||
|
|||||||
@@ -114,15 +114,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.20-fix2 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.23-fix # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.20-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.23-fix # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.20-fix2 # git
|
image: ghcr.io/labring/fastgpt:v4.8.23-fix # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.20-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.23-fix # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -133,7 +133,7 @@ services:
|
|||||||
- sandbox
|
- sandbox
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
# 前端访问地址: http://localhost:3000
|
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||||
- FE_DOMAIN=
|
- FE_DOMAIN=
|
||||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||||
- DEFAULT_ROOT_PSW=1234
|
- DEFAULT_ROOT_PSW=1234
|
||||||
@@ -72,15 +72,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.21 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.23-fix # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.21 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.23-fix # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.21 # git
|
image: ghcr.io/labring/fastgpt:v4.8.23-fix # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.21 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.23-fix # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -91,7 +91,7 @@ services:
|
|||||||
- sandbox
|
- sandbox
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
# 前端访问地址: http://localhost:3000
|
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||||
- FE_DOMAIN=
|
- FE_DOMAIN=
|
||||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||||
- DEFAULT_ROOT_PSW=1234
|
- DEFAULT_ROOT_PSW=1234
|
||||||
@@ -53,15 +53,15 @@ services:
|
|||||||
wait $$!
|
wait $$!
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.20-fix2 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.23-fix # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.20-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.23-fix # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.20-fix2 # git
|
image: ghcr.io/labring/fastgpt:v4.8.23-fix # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.20-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.23-fix # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -71,7 +71,7 @@ services:
|
|||||||
- sandbox
|
- sandbox
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
# 前端访问地址: http://localhost:3000
|
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||||
- FE_DOMAIN=
|
- FE_DOMAIN=
|
||||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||||
- DEFAULT_ROOT_PSW=1234
|
- DEFAULT_ROOT_PSW=1234
|
||||||
BIN
docSite/assets/imgs/appid.png
Normal file
BIN
docSite/assets/imgs/appid.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 332 KiB |
@@ -31,9 +31,9 @@ weight: 920
|
|||||||
|
|
||||||
3 个模型代码分别为:
|
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)
|
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/python/bge-rerank/bge-reranker-large](https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-large)
|
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/python/bge-rerank/bge-reranker-v2-m3](https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-v2-m3)
|
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. 安装依赖
|
### 3. 安装依赖
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ PDF 是一个相对复杂的文件格式,在 FastGPT 内置的 pdf 解析器
|
|||||||
|
|
||||||
### 1. 按照 Marker
|
### 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 安装的方法:
|
这里介绍快速 Docker 安装的方法:
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ brew install orbstack
|
|||||||
非 Linux 环境或无法访问外网环境,可手动创建一个目录,并下载配置文件和对应版本的`docker-compose.yml`,在这个文件夹中依据下载的配置文件运行docker,若作为本地开发使用推荐`docker-compose-pgvector`版本,并且自行拉取并运行`sandbox`和`fastgpt`,并在docker配置文件中注释掉`sandbox`和`fastgpt`的部分
|
非 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)
|
- [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" %}}
|
{{% alert icon="🤖" context="success" %}}
|
||||||
|
|
||||||
@@ -134,11 +134,11 @@ cd fastgpt
|
|||||||
curl -O https://raw.githubusercontent.com/labring/FastGPT/main/projects/app/data/config.json
|
curl -O https://raw.githubusercontent.com/labring/FastGPT/main/projects/app/data/config.json
|
||||||
|
|
||||||
# pgvector 版本(测试推荐,简单快捷)
|
# 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 版本
|
# 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 版本
|
# 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. 修改环境变量
|
### 2. 修改环境变量
|
||||||
@@ -201,6 +201,8 @@ docker restart oneapi
|
|||||||
|
|
||||||
在OneApi中添加合适的AI模型渠道。[点击查看相关教程](/docs/development/modelconfig/one-api/)
|
在OneApi中添加合适的AI模型渠道。[点击查看相关教程](/docs/development/modelconfig/one-api/)
|
||||||
|
|
||||||
|
只需要添加模型即可,模板已经配置好了oneapi的连接地址和令牌,无需变更。
|
||||||
|
|
||||||
### 5. 访问 FastGPT
|
### 5. 访问 FastGPT
|
||||||
|
|
||||||
目前可以通过 `ip:3000` 直接访问(注意防火墙)。登录用户名为 `root`,密码为`docker-compose.yml`环境变量里设置的 `DEFAULT_ROOT_PSW`。
|
目前可以通过 `ip:3000` 直接访问(注意防火墙)。登录用户名为 `root`,密码为`docker-compose.yml`环境变量里设置的 `DEFAULT_ROOT_PSW`。
|
||||||
@@ -211,7 +213,7 @@ docker restart oneapi
|
|||||||
|
|
||||||
### 6. 配置模型
|
### 6. 配置模型
|
||||||
|
|
||||||
务必先配置至少一组模型,否则系统无法正常使用。
|
登录FastGPT后,进入模型配置页面,务必先配置至少一个语言模型和一个向量模型,否则系统无法正常使用。
|
||||||
|
|
||||||
[点击查看模型配置教程](/docs/development/modelConfig/intro/)
|
[点击查看模型配置教程](/docs/development/modelConfig/intro/)
|
||||||
|
|
||||||
|
|||||||
@@ -142,6 +142,10 @@ OneAPI 的 API Key 配置错误,需要修改`OPENAI_API_KEY`环境变量,并
|
|||||||
3. ....
|
3. ....
|
||||||
|
|
||||||
|
|
||||||
|
### Tiktoken 下载失败
|
||||||
|
|
||||||
|
由于 OneAPI 会在启动时从网络下载一个 tiktoken 的依赖,如果网络异常,就会导致启动失败。可以参考[OneAPI 离线部署](https://blog.csdn.net/wanh/article/details/139039216)解决。
|
||||||
|
|
||||||
## 四、常见模型问题
|
## 四、常见模型问题
|
||||||
|
|
||||||
### 如何检查模型可用性问题
|
### 如何检查模型可用性问题
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ weight: 705
|
|||||||
|
|
||||||
- [Git](http://git-scm.com/)
|
- [Git](http://git-scm.com/)
|
||||||
- [Docker](https://www.docker.com/)(构建镜像)
|
- [Docker](https://www.docker.com/)(构建镜像)
|
||||||
- [Node.js v18.17 / v20.x](http://nodejs.org)(版本尽量一样,可以使用nvm管理node版本)
|
- [Node.js v20.14.0](http://nodejs.org)(版本尽量一样,可以使用nvm管理node版本)
|
||||||
- [pnpm](https://pnpm.io/) 版本 8.6.0 (目前官方的开发环境)
|
- [pnpm](https://pnpm.io/) 推荐版本 9.4.0 (目前官方的开发环境)
|
||||||
- make命令: 根据不同平台,百度安装 (官方是GNU Make 4.3)
|
- make命令: 根据不同平台,百度安装 (官方是GNU Make 4.3)
|
||||||
|
|
||||||
## 开始本地开发
|
## 开始本地开发
|
||||||
@@ -77,8 +77,6 @@ Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnec
|
|||||||
可参考项目根目录下的 `dev.md`,第一次编译运行可能会有点慢,需要点耐心哦
|
可参考项目根目录下的 `dev.md`,第一次编译运行可能会有点慢,需要点耐心哦
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 给自动化脚本代码执行权限(非 linux 系统, 可以手动执行里面的 postinstall.sh 文件内容)
|
|
||||||
chmod -R +x ./scripts/
|
|
||||||
# 代码根目录下执行,会安装根 package、projects 和 packages 内所有依赖
|
# 代码根目录下执行,会安装根 package、projects 和 packages 内所有依赖
|
||||||
# 如果提示 isolate-vm 安装失败,可以参考:https://github.com/laverdet/isolated-vm?tab=readme-ov-file#requirements
|
# 如果提示 isolate-vm 安装失败,可以参考:https://github.com/laverdet/isolated-vm?tab=readme-ov-file#requirements
|
||||||
pnpm i
|
pnpm i
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ weight: 744
|
|||||||
|
|
||||||
{{% alert icon=" " context="info" %}}
|
{{% alert icon=" " context="info" %}}
|
||||||
- [SiliconCloud(硅基流动)](https://cloud.siliconflow.cn/i/TR9Ym0c4): 提供开源模型调用的平台。
|
- [SiliconCloud(硅基流动)](https://cloud.siliconflow.cn/i/TR9Ym0c4): 提供开源模型调用的平台。
|
||||||
- [Sealos AIProxy](https://hzh.sealos.run/?openapp=system-aiproxy): 提供国内各家模型代理,无需逐一申请 api。
|
- [Sealos AIProxy](https://cloud.sealos.run/?uid=fnWRt09fZP&openapp=system-aiproxy): 提供国内各家模型代理,无需逐一申请 api。
|
||||||
{{% /alert %}}
|
{{% /alert %}}
|
||||||
|
|
||||||
在 OneAPI 配置好模型后,你就可以打开 FastGPT 页面,启用对应模型了。
|
在 OneAPI 配置好模型后,你就可以打开 FastGPT 页面,启用对应模型了。
|
||||||
@@ -467,4 +467,4 @@ OneAPI 的语言识别接口,无法正确的识别其他模型(会始终识
|
|||||||
"charsPointsPrice": 0
|
"charsPointsPrice": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ toc: true
|
|||||||
weight: 852
|
weight: 852
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# 如何获取 AppId
|
||||||
|
|
||||||
|
可在应用详情的路径里获取 AppId。
|
||||||
|
|
||||||
|

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

|

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

|

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

|

|
||||||
|
|
||||||
{{% alert icon="🍅" context="success" %}}
|
{{% alert icon="🍅" context="success" %}}
|
||||||
Tips: 安全起见,你可以设置一个额度或者过期时间,放置 key 被滥用。
|
Tips: 安全起见,你可以设置一个额度或者过期时间,防止 key 被滥用。
|
||||||
{{% /alert %}}
|
{{% /alert %}}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"format-code": "prettier --config \"./.prettierrc.js\" --write \"./**/src/**/*.{ts,tsx,scss}\"",
|
"format-code": "prettier --config \"./.prettierrc.js\" --write \"./**/src/**/*.{ts,tsx,scss}\"",
|
||||||
"format-doc": "zhlint --dir ./docSite *.md --fix",
|
"format-doc": "zhlint --dir ./docSite *.md --fix",
|
||||||
"gen:theme-typings": "chakra-cli tokens packages/web/styles/theme.ts --out node_modules/.pnpm/node_modules/@chakra-ui/styled-system/dist/theming.types.d.ts",
|
"gen:theme-typings": "chakra-cli tokens packages/web/styles/theme.ts --out node_modules/.pnpm/node_modules/@chakra-ui/styled-system/dist/theming.types.d.ts",
|
||||||
"postinstall": "sh ./scripts/postinstall.sh",
|
"postinstall": "pnpm gen:theme-typings",
|
||||||
"initIcon": "node ./scripts/icon/init.js",
|
"initIcon": "node ./scripts/icon/init.js",
|
||||||
"previewIcon": "node ./scripts/icon/index.js",
|
"previewIcon": "node ./scripts/icon/index.js",
|
||||||
"api:gen": "tsc ./scripts/openapi/index.ts && node ./scripts/openapi/index.js && npx @redocly/cli build-docs ./scripts/openapi/openapi.json -o ./projects/app/public/openapi/index.html",
|
"api:gen": "tsc ./scripts/openapi/index.ts && node ./scripts/openapi/index.js && npx @redocly/cli build-docs ./scripts/openapi/openapi.json -o ./projects/app/public/openapi/index.html",
|
||||||
|
|||||||
3
packages/README.md
Normal file
3
packages/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# 目录说明
|
||||||
|
|
||||||
|
该目录为 FastGPT 的依赖包,多端复用。
|
||||||
@@ -4,6 +4,7 @@ import { ErrType } from '../errorCode';
|
|||||||
/* dataset: 501000 */
|
/* dataset: 501000 */
|
||||||
export enum DatasetErrEnum {
|
export enum DatasetErrEnum {
|
||||||
unExist = 'unExistDataset',
|
unExist = 'unExistDataset',
|
||||||
|
unExistCollection = 'unExistCollection',
|
||||||
unAuthDataset = 'unAuthDataset',
|
unAuthDataset = 'unAuthDataset',
|
||||||
unCreateCollection = 'unCreateCollection',
|
unCreateCollection = 'unCreateCollection',
|
||||||
unAuthDatasetCollection = 'unAuthDatasetCollection',
|
unAuthDatasetCollection = 'unAuthDatasetCollection',
|
||||||
@@ -28,6 +29,10 @@ const datasetErr = [
|
|||||||
statusText: DatasetErrEnum.unExist,
|
statusText: DatasetErrEnum.unExist,
|
||||||
message: 'core.dataset.error.unExistDataset'
|
message: 'core.dataset.error.unExistDataset'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
statusText: DatasetErrEnum.unExistCollection,
|
||||||
|
message: i18nT('common:error_collection_not_exist')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
statusText: DatasetErrEnum.unAuthDataset,
|
statusText: DatasetErrEnum.unAuthDataset,
|
||||||
message: 'core.dataset.error.unAuthDataset'
|
message: 'core.dataset.error.unAuthDataset'
|
||||||
|
|||||||
@@ -20,4 +20,4 @@ export const ReadFileBaseUrl = `${process.env.FILE_DOMAIN || process.env.FE_DOMA
|
|||||||
|
|
||||||
export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx';
|
export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx';
|
||||||
export const imageFileType =
|
export const imageFileType =
|
||||||
'.jpg, .jpeg, .png, .gif, .bmp, .webp, .svg, .tiff, .tif, .ico, .heic, .heif, .avif';
|
'.jpg, .jpeg, .png, .gif, .bmp, .webp, .svg, .tiff, .tif, .ico, .heic, .heif, .avif, .raw, .cr2, .nef, .arw, .dng, .psd, .ai, .eps, .emf, .wmf, .jfif, .exif, .pgm, .ppm, .pbm, .jp2, .j2k, .jpf, .jpx, .jpm, .mj2, .xbm, .pcx';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { detect } from 'jschardet';
|
import { detect } from 'jschardet';
|
||||||
import { documentFileType, imageFileType } from './constants';
|
import { documentFileType } from './constants';
|
||||||
import { ChatFileTypeEnum } from '../../core/chat/constants';
|
import { ChatFileTypeEnum } from '../../core/chat/constants';
|
||||||
import { UserChatItemValueItemType } from '../../core/chat/type';
|
import { UserChatItemValueItemType } from '../../core/chat/type';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
@@ -25,6 +25,7 @@ export const detectFileEncodingByPath = async (path: string) => {
|
|||||||
const fd = await fs.promises.open(path, 'r');
|
const fd = await fs.promises.open(path, 'r');
|
||||||
try {
|
try {
|
||||||
// Read file head
|
// Read file head
|
||||||
|
// @ts-ignore
|
||||||
const { bytesRead } = await fd.read(buffer, 0, MAX_BYTES, 0);
|
const { bytesRead } = await fd.read(buffer, 0, MAX_BYTES, 0);
|
||||||
const actualBuffer = buffer.slice(0, bytesRead);
|
const actualBuffer = buffer.slice(0, bytesRead);
|
||||||
|
|
||||||
@@ -37,40 +38,49 @@ export const detectFileEncodingByPath = async (path: string) => {
|
|||||||
// Url => user upload file type
|
// Url => user upload file type
|
||||||
export const parseUrlToFileType = (url: string): UserChatItemValueItemType['file'] | undefined => {
|
export const parseUrlToFileType = (url: string): UserChatItemValueItemType['file'] | undefined => {
|
||||||
if (typeof url !== 'string') return;
|
if (typeof url !== 'string') return;
|
||||||
const parseUrl = new URL(url, 'https://locaohost:3000');
|
|
||||||
|
|
||||||
const filename = (() => {
|
// Handle base64 image
|
||||||
// Check base64 image
|
if (url.startsWith('data:')) {
|
||||||
if (url.startsWith('data:image/')) {
|
const matches = url.match(/^data:([^;]+);base64,/);
|
||||||
const mime = url.split(',')[0].split(':')[1].split(';')[0];
|
if (!matches) return;
|
||||||
return `image.${mime.split('/')[1]}`;
|
|
||||||
}
|
|
||||||
// Old version file url: https://xxx.com/file/read?filename=xxx.pdf
|
|
||||||
const filenameQuery = parseUrl.searchParams.get('filename');
|
|
||||||
if (filenameQuery) return filenameQuery;
|
|
||||||
|
|
||||||
// Common file: https://xxx.com/xxx.pdf?xxxx=xxx
|
const mimeType = matches[1].toLowerCase();
|
||||||
const pathname = parseUrl.pathname;
|
if (!mimeType.startsWith('image/')) return;
|
||||||
if (pathname) return pathname.split('/').pop();
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (!filename) return;
|
const extension = mimeType.split('/')[1];
|
||||||
|
|
||||||
const extension = filename.split('.').pop()?.toLowerCase() || '';
|
|
||||||
|
|
||||||
if (!extension) return;
|
|
||||||
|
|
||||||
if (documentFileType.includes(extension)) {
|
|
||||||
return {
|
return {
|
||||||
type: ChatFileTypeEnum.file,
|
type: ChatFileTypeEnum.image,
|
||||||
name: filename,
|
name: `image.${extension}`,
|
||||||
url
|
url
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (imageFileType.includes(extension)) {
|
|
||||||
|
try {
|
||||||
|
const parseUrl = new URL(url, 'https://localhost:3000');
|
||||||
|
|
||||||
|
// Get filename from URL
|
||||||
|
const filename = parseUrl.searchParams.get('filename') || parseUrl.pathname.split('/').pop();
|
||||||
|
const extension = filename?.split('.').pop()?.toLowerCase() || '';
|
||||||
|
|
||||||
|
// If it's a document type, return as file, otherwise treat as image
|
||||||
|
if (extension && documentFileType.includes(extension)) {
|
||||||
|
return {
|
||||||
|
type: ChatFileTypeEnum.file,
|
||||||
|
name: filename || 'null',
|
||||||
|
url
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to image type for non-document files
|
||||||
return {
|
return {
|
||||||
type: ChatFileTypeEnum.image,
|
type: ChatFileTypeEnum.image,
|
||||||
name: filename,
|
name: filename || 'null.png',
|
||||||
|
url
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
type: ChatFileTypeEnum.image,
|
||||||
|
name: 'invalid.png',
|
||||||
url
|
url
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ import { i18nT } from '../../../web/i18n/utils';
|
|||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(timezone);
|
dayjs.extend(timezone);
|
||||||
|
|
||||||
export const formatTime2YMDHMW = (time?: Date) => dayjs(time).format('YYYY-MM-DD HH:mm:ss dddd');
|
export const formatTime2YMDHMW = (time?: Date | number) =>
|
||||||
export const formatTime2YMDHMS = (time?: Date) =>
|
dayjs(time).format('YYYY-MM-DD HH:mm:ss dddd');
|
||||||
|
export const formatTime2YMDHMS = (time?: Date | number) =>
|
||||||
time ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '';
|
time ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '';
|
||||||
export const formatTime2YMDHM = (time?: Date) =>
|
export const formatTime2YMDHM = (time?: Date | number) =>
|
||||||
time ? dayjs(time).format('YYYY-MM-DD HH:mm') : '';
|
time ? dayjs(time).format('YYYY-MM-DD HH:mm') : '';
|
||||||
export const formatTime2YMD = (time?: Date) => (time ? dayjs(time).format('YYYY-MM-DD') : '');
|
export const formatTime2YMD = (time?: Date | number) =>
|
||||||
|
time ? dayjs(time).format('YYYY-MM-DD') : '';
|
||||||
export const formatTime2HM = (time: Date = new Date()) => dayjs(time).format('HH:mm');
|
export const formatTime2HM = (time: Date = new Date()) => dayjs(time).format('HH:mm');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export type FastGPTConfigFileType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type FastGPTFeConfigsType = {
|
export type FastGPTFeConfigsType = {
|
||||||
|
show_workorder?: boolean;
|
||||||
show_emptyChat?: boolean;
|
show_emptyChat?: boolean;
|
||||||
register_method?: ['email' | 'phone' | 'sync'];
|
register_method?: ['email' | 'phone' | 'sync'];
|
||||||
login_method?: ['email' | 'phone']; // Attention: login method is diffrent with oauth
|
login_method?: ['email' | 'phone']; // Attention: login method is diffrent with oauth
|
||||||
@@ -53,6 +54,7 @@ export type FastGPTFeConfigsType = {
|
|||||||
show_promotion?: boolean;
|
show_promotion?: boolean;
|
||||||
show_team_chat?: boolean;
|
show_team_chat?: boolean;
|
||||||
show_compliance_copywriting?: boolean;
|
show_compliance_copywriting?: boolean;
|
||||||
|
show_aiproxy?: boolean;
|
||||||
concatMd?: string;
|
concatMd?: string;
|
||||||
|
|
||||||
docUrl?: string;
|
docUrl?: string;
|
||||||
|
|||||||
2
packages/global/core/ai/model.d.ts
vendored
2
packages/global/core/ai/model.d.ts
vendored
@@ -17,6 +17,8 @@ type BaseModelItemType = {
|
|||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
isCustom?: boolean;
|
isCustom?: boolean;
|
||||||
isDefault?: boolean;
|
isDefault?: boolean;
|
||||||
|
isDefaultDatasetTextModel?: boolean;
|
||||||
|
isDefaultDatasetImageModel?: boolean;
|
||||||
|
|
||||||
// If has requestUrl, it will request the model directly
|
// If has requestUrl, it will request the model directly
|
||||||
requestUrl?: string;
|
requestUrl?: string;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export type ModelProviderIdType =
|
|||||||
| 'StepFun'
|
| 'StepFun'
|
||||||
| 'Yi'
|
| 'Yi'
|
||||||
| 'Siliconflow'
|
| 'Siliconflow'
|
||||||
|
| 'PPIO'
|
||||||
| 'Ollama'
|
| 'Ollama'
|
||||||
| 'BAAI'
|
| 'BAAI'
|
||||||
| 'FishAudio'
|
| 'FishAudio'
|
||||||
@@ -71,11 +72,6 @@ export const ModelProviderList: ModelProviderType[] = [
|
|||||||
name: 'Groq',
|
name: 'Groq',
|
||||||
avatar: 'model/groq'
|
avatar: 'model/groq'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'AliCloud',
|
|
||||||
name: i18nT('common:model_alicloud'),
|
|
||||||
avatar: 'model/alicloud'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'Qwen',
|
id: 'Qwen',
|
||||||
name: i18nT('common:model_qwen'),
|
name: i18nT('common:model_qwen'),
|
||||||
@@ -86,6 +82,11 @@ export const ModelProviderList: ModelProviderType[] = [
|
|||||||
name: i18nT('common:model_doubao'),
|
name: i18nT('common:model_doubao'),
|
||||||
avatar: 'model/doubao'
|
avatar: 'model/doubao'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'DeepSeek',
|
||||||
|
name: 'DeepSeek',
|
||||||
|
avatar: 'model/deepseek'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'ChatGLM',
|
id: 'ChatGLM',
|
||||||
name: i18nT('common:model_chatglm'),
|
name: i18nT('common:model_chatglm'),
|
||||||
@@ -96,11 +97,6 @@ export const ModelProviderList: ModelProviderType[] = [
|
|||||||
name: i18nT('common:model_ernie'),
|
name: i18nT('common:model_ernie'),
|
||||||
avatar: 'model/ernie'
|
avatar: 'model/ernie'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'DeepSeek',
|
|
||||||
name: 'DeepSeek',
|
|
||||||
avatar: 'model/deepseek'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'Moonshot',
|
id: 'Moonshot',
|
||||||
name: i18nT('common:model_moonshot'),
|
name: i18nT('common:model_moonshot'),
|
||||||
@@ -162,11 +158,21 @@ export const ModelProviderList: ModelProviderType[] = [
|
|||||||
name: i18nT('common:model_moka'),
|
name: i18nT('common:model_moka'),
|
||||||
avatar: 'model/moka'
|
avatar: 'model/moka'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'AliCloud',
|
||||||
|
name: i18nT('common:model_alicloud'),
|
||||||
|
avatar: 'model/alicloud'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'Siliconflow',
|
id: 'Siliconflow',
|
||||||
name: i18nT('common:model_siliconflow'),
|
name: i18nT('common:model_siliconflow'),
|
||||||
avatar: 'model/siliconflow'
|
avatar: 'model/siliconflow'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'PPIO',
|
||||||
|
name: i18nT('common:model_ppio'),
|
||||||
|
avatar: 'model/ppio'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'Other',
|
id: 'Other',
|
||||||
name: i18nT('common:model_other'),
|
name: i18nT('common:model_other'),
|
||||||
|
|||||||
8
packages/global/core/ai/type.d.ts
vendored
8
packages/global/core/ai/type.d.ts
vendored
@@ -1,14 +1,12 @@
|
|||||||
import openai from 'openai';
|
import openai from 'openai';
|
||||||
import type {
|
import type {
|
||||||
ChatCompletionMessageToolCall,
|
ChatCompletionMessageToolCall,
|
||||||
ChatCompletionChunk,
|
|
||||||
ChatCompletionMessageParam as SdkChatCompletionMessageParam,
|
ChatCompletionMessageParam as SdkChatCompletionMessageParam,
|
||||||
ChatCompletionToolMessageParam,
|
ChatCompletionToolMessageParam,
|
||||||
ChatCompletionContentPart as SdkChatCompletionContentPart,
|
ChatCompletionContentPart as SdkChatCompletionContentPart,
|
||||||
ChatCompletionUserMessageParam as SdkChatCompletionUserMessageParam,
|
ChatCompletionUserMessageParam as SdkChatCompletionUserMessageParam,
|
||||||
ChatCompletionToolMessageParam as SdkChatCompletionToolMessageParam,
|
ChatCompletionToolMessageParam as SdkChatCompletionToolMessageParam,
|
||||||
ChatCompletionAssistantMessageParam as SdkChatCompletionAssistantMessageParam,
|
ChatCompletionAssistantMessageParam as SdkChatCompletionAssistantMessageParam
|
||||||
ChatCompletionContentPartText
|
|
||||||
} from 'openai/resources';
|
} from 'openai/resources';
|
||||||
import { ChatMessageTypeEnum } from './constants';
|
import { ChatMessageTypeEnum } from './constants';
|
||||||
import { WorkflowInteractiveResponseType } from '../workflow/template/system/interactive/type';
|
import { WorkflowInteractiveResponseType } from '../workflow/template/system/interactive/type';
|
||||||
@@ -48,6 +46,7 @@ export type ChatCompletionMessageParam = (
|
|||||||
| CustomChatCompletionToolMessageParam
|
| CustomChatCompletionToolMessageParam
|
||||||
| CustomChatCompletionAssistantMessageParam
|
| CustomChatCompletionAssistantMessageParam
|
||||||
) & {
|
) & {
|
||||||
|
reasoning_text?: string;
|
||||||
dataId?: string;
|
dataId?: string;
|
||||||
hideInUI?: boolean;
|
hideInUI?: boolean;
|
||||||
};
|
};
|
||||||
@@ -71,7 +70,8 @@ export type ChatCompletionMessageFunctionCall =
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Stream response
|
// Stream response
|
||||||
export type StreamChatType = Stream<ChatCompletionChunk>;
|
export type StreamChatType = Stream<openai.Chat.Completions.ChatCompletionChunk>;
|
||||||
|
export type UnStreamChatType = openai.Chat.Completions.ChatCompletion;
|
||||||
|
|
||||||
export default openai;
|
export default openai;
|
||||||
export * from 'openai';
|
export * from 'openai';
|
||||||
|
|||||||
@@ -46,7 +46,16 @@ export const chats2GPTMessages = ({
|
|||||||
|
|
||||||
messages.forEach((item) => {
|
messages.forEach((item) => {
|
||||||
const dataId = reserveId ? item.dataId : undefined;
|
const dataId = reserveId ? item.dataId : undefined;
|
||||||
if (item.obj === ChatRoleEnum.Human) {
|
if (item.obj === ChatRoleEnum.System) {
|
||||||
|
const content = item.value?.[0]?.text?.content;
|
||||||
|
if (content) {
|
||||||
|
results.push({
|
||||||
|
dataId,
|
||||||
|
role: ChatCompletionRequestMessageRoleEnum.System,
|
||||||
|
content
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (item.obj === ChatRoleEnum.Human) {
|
||||||
const value = item.value
|
const value = item.value
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
if (item.type === ChatItemValueTypeEnum.text) {
|
if (item.type === ChatItemValueTypeEnum.text) {
|
||||||
@@ -80,15 +89,6 @@ export const chats2GPTMessages = ({
|
|||||||
role: ChatCompletionRequestMessageRoleEnum.User,
|
role: ChatCompletionRequestMessageRoleEnum.User,
|
||||||
content: simpleUserContentPart(value)
|
content: simpleUserContentPart(value)
|
||||||
});
|
});
|
||||||
} else if (item.obj === ChatRoleEnum.System) {
|
|
||||||
const content = item.value?.[0]?.text?.content;
|
|
||||||
if (content) {
|
|
||||||
results.push({
|
|
||||||
dataId,
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.System,
|
|
||||||
content
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const aiResults: ChatCompletionMessageParam[] = [];
|
const aiResults: ChatCompletionMessageParam[] = [];
|
||||||
|
|
||||||
@@ -349,7 +349,7 @@ export const chatValue2RuntimePrompt = (value: ChatItemValueItemType[]): Runtime
|
|||||||
};
|
};
|
||||||
value.forEach((item) => {
|
value.forEach((item) => {
|
||||||
if (item.type === 'file' && item.file) {
|
if (item.type === 'file' && item.file) {
|
||||||
prompt.files?.push(item.file);
|
prompt.files.push(item.file);
|
||||||
} else if (item.text) {
|
} else if (item.text) {
|
||||||
prompt.text += item.text.content;
|
prompt.text += item.text.content;
|
||||||
}
|
}
|
||||||
|
|||||||
1
packages/global/core/dataset/type.d.ts
vendored
1
packages/global/core/dataset/type.d.ts
vendored
@@ -192,6 +192,7 @@ export type DatasetCollectionItemType = CollectionWithDatasetType & {
|
|||||||
sourceId?: string;
|
sourceId?: string;
|
||||||
file?: DatasetFileSchema;
|
file?: DatasetFileSchema;
|
||||||
permission: DatasetPermission;
|
permission: DatasetPermission;
|
||||||
|
indexAmount: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ================= data ===================== */
|
/* ================= data ===================== */
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
|
|||||||
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
||||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
||||||
import { replaceVariable, valToStr } from '../../../common/string/tools';
|
import { replaceVariable, valToStr } from '../../../common/string/tools';
|
||||||
|
import { ChatCompletionChunk } from 'openai/resources';
|
||||||
|
|
||||||
export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => {
|
export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => {
|
||||||
let limit = 10;
|
let limit = 10;
|
||||||
|
|||||||
10
packages/global/support/user/api.d.ts
vendored
10
packages/global/support/user/api.d.ts
vendored
@@ -1,5 +1,9 @@
|
|||||||
|
import { MemberGroupSchemaType, MemberGroupType } from 'support/permission/memberGroup/type';
|
||||||
import { OAuthEnum } from './constant';
|
import { OAuthEnum } from './constant';
|
||||||
import { TrackRegisterParams } from './login/api';
|
import { TrackRegisterParams } from './login/api';
|
||||||
|
import { TeamMemberStatusEnum } from './team/constant';
|
||||||
|
import { OrgType } from './team/org/type';
|
||||||
|
import { TeamMemberItemType } from './team/type';
|
||||||
|
|
||||||
export type PostLoginProps = {
|
export type PostLoginProps = {
|
||||||
username: string;
|
username: string;
|
||||||
@@ -21,3 +25,9 @@ export type FastLoginProps = {
|
|||||||
token: string;
|
token: string;
|
||||||
code: string;
|
code: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SearchResult = {
|
||||||
|
members: Omit<TeamMemberItemType, 'teamId' | 'permission'>[];
|
||||||
|
orgs: Omit<OrgType, 'permission' | 'members'>[];
|
||||||
|
groups: MemberGroupSchemaType[];
|
||||||
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export type CreateTeamProps = {
|
|||||||
defaultTeam?: boolean;
|
defaultTeam?: boolean;
|
||||||
memberName?: string;
|
memberName?: string;
|
||||||
memberAvatar?: string;
|
memberAvatar?: string;
|
||||||
|
notificationAccount?: string;
|
||||||
};
|
};
|
||||||
export type UpdateTeamProps = Omit<ThirdPartyAccountType, 'externalWorkflowVariable'> & {
|
export type UpdateTeamProps = Omit<ThirdPartyAccountType, 'externalWorkflowVariable'> & {
|
||||||
name?: string;
|
name?: string;
|
||||||
@@ -39,6 +40,12 @@ export type UpdateInviteProps = {
|
|||||||
tmbId: string;
|
tmbId: string;
|
||||||
status: TeamMemberSchema['status'];
|
status: TeamMemberSchema['status'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UpdateStatusProps = {
|
||||||
|
tmbId: string;
|
||||||
|
status: TeamMemberSchema['status'];
|
||||||
|
};
|
||||||
|
|
||||||
export type InviteMemberResponse = Record<
|
export type InviteMemberResponse = Record<
|
||||||
'invite' | 'inValid' | 'inTeam',
|
'invite' | 'inValid' | 'inTeam',
|
||||||
{ username: string; userId: string }[]
|
{ username: string; userId: string }[]
|
||||||
|
|||||||
5
packages/global/support/user/team/type.d.ts
vendored
5
packages/global/support/user/team/type.d.ts
vendored
@@ -34,6 +34,7 @@ export type TeamTagSchema = TeamTagItemType & {
|
|||||||
_id: string;
|
_id: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
createTime: Date;
|
createTime: Date;
|
||||||
|
updateTime?: Date;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TeamMemberSchema = {
|
export type TeamMemberSchema = {
|
||||||
@@ -41,6 +42,7 @@ export type TeamMemberSchema = {
|
|||||||
teamId: string;
|
teamId: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
createTime: Date;
|
createTime: Date;
|
||||||
|
updateTime?: Date;
|
||||||
name: string;
|
name: string;
|
||||||
role: `${TeamMemberRoleEnum}`;
|
role: `${TeamMemberRoleEnum}`;
|
||||||
status: `${TeamMemberStatusEnum}`;
|
status: `${TeamMemberStatusEnum}`;
|
||||||
@@ -79,6 +81,9 @@ export type TeamMemberItemType = {
|
|||||||
role: `${TeamMemberRoleEnum}`;
|
role: `${TeamMemberRoleEnum}`;
|
||||||
status: `${TeamMemberStatusEnum}`;
|
status: `${TeamMemberStatusEnum}`;
|
||||||
permission: TeamPermission;
|
permission: TeamPermission;
|
||||||
|
contact?: string;
|
||||||
|
createTime: Date;
|
||||||
|
updateTime?: Date;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TeamTagItemType = {
|
export type TeamTagItemType = {
|
||||||
|
|||||||
3
packages/global/support/user/type.d.ts
vendored
3
packages/global/support/user/type.d.ts
vendored
@@ -17,6 +17,7 @@ export type UserModelSchema = {
|
|||||||
fastgpt_sem?: {
|
fastgpt_sem?: {
|
||||||
keyword: string;
|
keyword: string;
|
||||||
};
|
};
|
||||||
|
contact?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserType = {
|
export type UserType = {
|
||||||
@@ -26,9 +27,9 @@ export type UserType = {
|
|||||||
timezone: string;
|
timezone: string;
|
||||||
promotionRate: UserModelSchema['promotionRate'];
|
promotionRate: UserModelSchema['promotionRate'];
|
||||||
team: TeamTmbItemType;
|
team: TeamTmbItemType;
|
||||||
standardInfo?: standardInfoType;
|
|
||||||
notificationAccount?: string;
|
notificationAccount?: string;
|
||||||
permission: TeamPermission;
|
permission: TeamPermission;
|
||||||
|
contact?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SourceMemberType = {
|
export type SourceMemberType = {
|
||||||
|
|||||||
4
packages/service/common/file/csv.ts
Normal file
4
packages/service/common/file/csv.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const generateCsv = (headers: string[], data: string[][]) => {
|
||||||
|
const csv = [headers.join(','), ...data.map((row) => row.join(','))].join('\n');
|
||||||
|
return csv;
|
||||||
|
};
|
||||||
@@ -18,10 +18,10 @@ export function getGFSCollection(bucket: `${BucketNameEnum}`) {
|
|||||||
MongoDatasetFileSchema;
|
MongoDatasetFileSchema;
|
||||||
MongoChatFileSchema;
|
MongoChatFileSchema;
|
||||||
|
|
||||||
return connectionMongo.connection.db.collection(`${bucket}.files`);
|
return connectionMongo.connection.db!.collection(`${bucket}.files`);
|
||||||
}
|
}
|
||||||
export function getGridBucket(bucket: `${BucketNameEnum}`) {
|
export function getGridBucket(bucket: `${BucketNameEnum}`) {
|
||||||
return new connectionMongo.mongo.GridFSBucket(connectionMongo.connection.db, {
|
return new connectionMongo.mongo.GridFSBucket(connectionMongo.connection.db!, {
|
||||||
bucketName: bucket,
|
bucketName: bucket,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
readPreference: ReadPreference.SECONDARY_PREFERRED // Read from secondary node
|
readPreference: ReadPreference.SECONDARY_PREFERRED // Read from secondary node
|
||||||
|
|||||||
@@ -26,15 +26,18 @@ export async function uploadMongoImg({
|
|||||||
const [base64Mime, base64Data] = base64Img.split(',');
|
const [base64Mime, base64Data] = base64Img.split(',');
|
||||||
// Check if mime type is valid
|
// Check if mime type is valid
|
||||||
if (!base64MimeRegex.test(base64Mime)) {
|
if (!base64MimeRegex.test(base64Mime)) {
|
||||||
return Promise.reject('Invalid image mime type');
|
return Promise.reject('Invalid image base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'image/jpeg'}`;
|
const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'image/jpeg'}`;
|
||||||
const binary = Buffer.from(base64Data, 'base64');
|
const binary = Buffer.from(base64Data, 'base64');
|
||||||
const extension = mime.split('/')[1];
|
let extension = mime.split('/')[1];
|
||||||
|
if (extension.startsWith('x-')) {
|
||||||
|
extension = extension.substring(2); // Remove 'x-' prefix
|
||||||
|
}
|
||||||
|
|
||||||
if (!imageFileType.includes(`.${extension}`)) {
|
if (!extension || !imageFileType.includes(`.${extension}`)) {
|
||||||
return Promise.reject('Invalid image file type');
|
return Promise.reject(`Invalid image file type: ${mime}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { _id } = await MongoImage.create({
|
const { _id } = await MongoImage.create({
|
||||||
@@ -115,7 +118,7 @@ export async function delImgByRelatedId({
|
|||||||
}: {
|
}: {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
relateIds: string[];
|
relateIds: string[];
|
||||||
session: ClientSession;
|
session?: ClientSession;
|
||||||
}) {
|
}) {
|
||||||
if (relateIds.length === 0) return;
|
if (relateIds.length === 0) return;
|
||||||
|
|
||||||
|
|||||||
@@ -111,15 +111,21 @@ export const readRawContentByFileBuffer = async ({
|
|||||||
// markdown data format
|
// markdown data format
|
||||||
if (imageList) {
|
if (imageList) {
|
||||||
await batchRun(imageList, async (item) => {
|
await batchRun(imageList, async (item) => {
|
||||||
const src = await uploadMongoImg({
|
const src = await (async () => {
|
||||||
base64Img: `data:${item.mime};base64,${item.base64}`,
|
try {
|
||||||
teamId,
|
return await uploadMongoImg({
|
||||||
// expiredTime: addHours(new Date(), 1),
|
base64Img: `data:${item.mime};base64,${item.base64}`,
|
||||||
metadata: {
|
teamId,
|
||||||
...metadata,
|
// expiredTime: addHours(new Date(), 1),
|
||||||
mime: item.mime
|
metadata: {
|
||||||
|
...metadata,
|
||||||
|
mime: item.mime
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
});
|
})();
|
||||||
rawText = rawText.replace(item.uuid, src);
|
rawText = rawText.replace(item.uuid, src);
|
||||||
if (formatText) {
|
if (formatText) {
|
||||||
formatText = formatText.replace(item.uuid, src);
|
formatText = formatText.replace(item.uuid, src);
|
||||||
|
|||||||
@@ -38,10 +38,12 @@ const addCommonMiddleware = (schema: mongoose.Schema) => {
|
|||||||
schema.post(op, function (this: any, result: any, next) {
|
schema.post(op, function (this: any, result: any, next) {
|
||||||
if (this._startTime) {
|
if (this._startTime) {
|
||||||
const duration = Date.now() - this._startTime;
|
const duration = Date.now() - this._startTime;
|
||||||
|
|
||||||
const warnLogData = {
|
const warnLogData = {
|
||||||
query: this._query,
|
collectionName: this.collection?.name,
|
||||||
op,
|
op: this.op,
|
||||||
|
...(this._query && { query: this._query }),
|
||||||
|
...(this._update && { update: this._update }),
|
||||||
|
...(this._delete && { delete: this._delete }),
|
||||||
duration
|
duration
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,16 +16,30 @@ export async function connectMongo(): Promise<Mongoose> {
|
|||||||
|
|
||||||
console.log('mongo start connect');
|
console.log('mongo start connect');
|
||||||
try {
|
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) => {
|
connectionMongo.connection.on('error', async (error) => {
|
||||||
console.log('mongo error', error);
|
console.log('mongo error', error);
|
||||||
await connectionMongo.disconnect();
|
try {
|
||||||
await delay(1000);
|
if (connectionMongo.connection.readyState !== 0) {
|
||||||
connectMongo();
|
await connectionMongo.disconnect();
|
||||||
|
await delay(1000);
|
||||||
|
await connectMongo();
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
});
|
});
|
||||||
connectionMongo.connection.on('disconnected', () => {
|
connectionMongo.connection.on('disconnected', async () => {
|
||||||
console.log('mongo disconnected');
|
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, {
|
await connectionMongo.connect(process.env.MONGODB_URI as string, {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export const countGptMessagesTokens = async (
|
|||||||
number
|
number
|
||||||
>({
|
>({
|
||||||
name: WorkerNameEnum.countGptMessagesTokens,
|
name: WorkerNameEnum.countGptMessagesTokens,
|
||||||
maxReservedThreads: global.systemEnv?.tokenWorkers || 50
|
maxReservedThreads: global.systemEnv?.tokenWorkers || 30
|
||||||
});
|
});
|
||||||
|
|
||||||
const total = await workerController.run({ messages, tools, functionCall });
|
const total = await workerController.run({ messages, tools, functionCall });
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
export const FastGPTProUrl = process.env.PRO_URL ? `${process.env.PRO_URL}/api` : '';
|
export const FastGPTProUrl = process.env.PRO_URL ? `${process.env.PRO_URL}/api` : '';
|
||||||
|
export const isFastGPTMainService = !!process.env.PRO_URL;
|
||||||
|
// @ts-ignore
|
||||||
|
export const isFastGPTProService = () => !!global.systemConfig;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export const recallFromVectorStore = Vector.embRecall;
|
|||||||
export const getVectorDataByTime = Vector.getVectorDataByTime;
|
export const getVectorDataByTime = Vector.getVectorDataByTime;
|
||||||
export const getVectorCountByTeamId = Vector.getVectorCountByTeamId;
|
export const getVectorCountByTeamId = Vector.getVectorCountByTeamId;
|
||||||
export const getVectorCountByDatasetId = Vector.getVectorCountByDatasetId;
|
export const getVectorCountByDatasetId = Vector.getVectorCountByDatasetId;
|
||||||
|
export const getVectorCountByCollectionId = Vector.getVectorCountByCollectionId;
|
||||||
|
|
||||||
export const insertDatasetDataVector = async ({
|
export const insertDatasetDataVector = async ({
|
||||||
model,
|
model,
|
||||||
|
|||||||
@@ -321,6 +321,23 @@ export class MilvusCtrl {
|
|||||||
|
|
||||||
return total;
|
return total;
|
||||||
};
|
};
|
||||||
|
getVectorCountByCollectionId = async (
|
||||||
|
teamId: string,
|
||||||
|
datasetId: string,
|
||||||
|
collectionId: string
|
||||||
|
) => {
|
||||||
|
const client = await this.getClient();
|
||||||
|
|
||||||
|
const result = await client.query({
|
||||||
|
collection_name: DatasetVectorTableName,
|
||||||
|
output_fields: ['count(*)'],
|
||||||
|
filter: `(teamId == "${String(teamId)}") and (datasetId == "${String(datasetId)}") and (collectionId == "${String(collectionId)}")`
|
||||||
|
});
|
||||||
|
|
||||||
|
const total = result.data?.[0]?.['count(*)'] as number;
|
||||||
|
|
||||||
|
return total;
|
||||||
|
};
|
||||||
|
|
||||||
getVectorDataByTime = async (start: Date, end: Date) => {
|
getVectorDataByTime = async (start: Date, end: Date) => {
|
||||||
const client = await this.getClient();
|
const client = await this.getClient();
|
||||||
|
|||||||
@@ -240,6 +240,23 @@ export class PgVectorCtrl {
|
|||||||
where: [['team_id', String(teamId)], 'and', ['dataset_id', String(datasetId)]]
|
where: [['team_id', String(teamId)], 'and', ['dataset_id', String(datasetId)]]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return total;
|
||||||
|
};
|
||||||
|
getVectorCountByCollectionId = async (
|
||||||
|
teamId: string,
|
||||||
|
datasetId: string,
|
||||||
|
collectionId: string
|
||||||
|
) => {
|
||||||
|
const total = await PgClient.count(DatasetVectorTableName, {
|
||||||
|
where: [
|
||||||
|
['team_id', String(teamId)],
|
||||||
|
'and',
|
||||||
|
['dataset_id', String(datasetId)],
|
||||||
|
'and',
|
||||||
|
['collection_id', String(collectionId)]
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
return total;
|
return total;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import OpenAI from '@fastgpt/global/core/ai';
|
import OpenAI from '@fastgpt/global/core/ai';
|
||||||
import {
|
import {
|
||||||
ChatCompletionCreateParamsNonStreaming,
|
ChatCompletionCreateParamsNonStreaming,
|
||||||
ChatCompletionCreateParamsStreaming
|
ChatCompletionCreateParamsStreaming,
|
||||||
|
StreamChatType,
|
||||||
|
UnStreamChatType
|
||||||
} from '@fastgpt/global/core/ai/type';
|
} from '@fastgpt/global/core/ai/type';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
import { addLog } from '../../common/system/log';
|
import { addLog } from '../../common/system/log';
|
||||||
@@ -9,14 +11,17 @@ import { i18nT } from '../../../web/i18n/utils';
|
|||||||
import { OpenaiAccountType } from '@fastgpt/global/support/user/team/type';
|
import { OpenaiAccountType } from '@fastgpt/global/support/user/team/type';
|
||||||
import { getLLMModel } from './model';
|
import { getLLMModel } from './model';
|
||||||
|
|
||||||
export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
|
const aiProxyBaseUrl = process.env.AIPROXY_API_ENDPOINT
|
||||||
|
? `${process.env.AIPROXY_API_ENDPOINT}/v1`
|
||||||
|
: undefined;
|
||||||
|
const openaiBaseUrl = aiProxyBaseUrl || process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
|
||||||
|
const openaiBaseKey = process.env.AIPROXY_API_TOKEN || process.env.CHAT_API_KEY || '';
|
||||||
|
|
||||||
export const getAIApi = (props?: { userKey?: OpenaiAccountType; timeout?: number }) => {
|
export const getAIApi = (props?: { userKey?: OpenaiAccountType; timeout?: number }) => {
|
||||||
const { userKey, timeout } = props || {};
|
const { userKey, timeout } = props || {};
|
||||||
|
|
||||||
const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
|
const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
|
||||||
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || process.env.CHAT_API_KEY || '';
|
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || openaiBaseKey;
|
||||||
|
|
||||||
return new OpenAI({
|
return new OpenAI({
|
||||||
baseURL: baseUrl,
|
baseURL: baseUrl,
|
||||||
apiKey,
|
apiKey,
|
||||||
@@ -30,7 +35,7 @@ export const getAxiosConfig = (props?: { userKey?: OpenaiAccountType }) => {
|
|||||||
const { userKey } = props || {};
|
const { userKey } = props || {};
|
||||||
|
|
||||||
const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
|
const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
|
||||||
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || process.env.CHAT_API_KEY || '';
|
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || openaiBaseKey;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
baseUrl,
|
baseUrl,
|
||||||
@@ -38,29 +43,30 @@ export const getAxiosConfig = (props?: { userKey?: OpenaiAccountType }) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type CompletionsBodyType =
|
export const createChatCompletion = async ({
|
||||||
| ChatCompletionCreateParamsNonStreaming
|
|
||||||
| ChatCompletionCreateParamsStreaming;
|
|
||||||
type InferResponseType<T extends CompletionsBodyType> =
|
|
||||||
T extends ChatCompletionCreateParamsStreaming
|
|
||||||
? OpenAI.Chat.Completions.ChatCompletionChunk
|
|
||||||
: OpenAI.Chat.Completions.ChatCompletion;
|
|
||||||
|
|
||||||
export const createChatCompletion = async <T extends CompletionsBodyType>({
|
|
||||||
body,
|
body,
|
||||||
userKey,
|
userKey,
|
||||||
timeout,
|
timeout,
|
||||||
options
|
options
|
||||||
}: {
|
}: {
|
||||||
body: T;
|
body: ChatCompletionCreateParamsNonStreaming | ChatCompletionCreateParamsStreaming;
|
||||||
userKey?: OpenaiAccountType;
|
userKey?: OpenaiAccountType;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
options?: OpenAI.RequestOptions;
|
options?: OpenAI.RequestOptions;
|
||||||
}): Promise<{
|
}): Promise<
|
||||||
response: InferResponseType<T>;
|
{
|
||||||
isStreamResponse: boolean;
|
getEmptyResponseTip: () => string;
|
||||||
getEmptyResponseTip: () => string;
|
} & (
|
||||||
}> => {
|
| {
|
||||||
|
response: StreamChatType;
|
||||||
|
isStreamResponse: true;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
response: UnStreamChatType;
|
||||||
|
isStreamResponse: false;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
> => {
|
||||||
try {
|
try {
|
||||||
const modelConstantsData = getLLMModel(body.model);
|
const modelConstantsData = getLLMModel(body.model);
|
||||||
|
|
||||||
@@ -69,6 +75,7 @@ export const createChatCompletion = async <T extends CompletionsBodyType>({
|
|||||||
userKey,
|
userKey,
|
||||||
timeout: formatTimeout
|
timeout: formatTimeout
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await ai.chat.completions.create(body, {
|
const response = await ai.chat.completions.create(body, {
|
||||||
...options,
|
...options,
|
||||||
...(modelConstantsData.requestUrl ? { path: modelConstantsData.requestUrl } : {}),
|
...(modelConstantsData.requestUrl ? { path: modelConstantsData.requestUrl } : {}),
|
||||||
@@ -96,9 +103,17 @@ export const createChatCompletion = async <T extends CompletionsBodyType>({
|
|||||||
return i18nT('chat:LLM_model_response_empty');
|
return i18nT('chat:LLM_model_response_empty');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isStreamResponse) {
|
||||||
|
return {
|
||||||
|
response,
|
||||||
|
isStreamResponse: true,
|
||||||
|
getEmptyResponseTip
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: response as InferResponseType<T>,
|
response,
|
||||||
isStreamResponse,
|
isStreamResponse: false,
|
||||||
getEmptyResponseTip
|
getEmptyResponseTip
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
{
|
{
|
||||||
"provider": "AliCloud",
|
"provider": "AliCloud",
|
||||||
"list": []
|
"list": [
|
||||||
}
|
{
|
||||||
|
"model": "SenseVoiceSmall",
|
||||||
|
"name": "SenseVoiceSmall",
|
||||||
|
"type": "stt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,30 @@
|
|||||||
{
|
{
|
||||||
"provider": "Claude",
|
"provider": "Claude",
|
||||||
"list": [
|
"list": [
|
||||||
|
{
|
||||||
|
"model": "claude-3-7-sonnet-20250219",
|
||||||
|
"name": "claude-3-7-sonnet-20250219",
|
||||||
|
"maxContext": 200000,
|
||||||
|
"maxResponse": 8000,
|
||||||
|
"quoteMaxToken": 100000,
|
||||||
|
"maxTemperature": 1,
|
||||||
|
"showTopP": true,
|
||||||
|
"showStopSign": true,
|
||||||
|
"vision": true,
|
||||||
|
"toolChoice": true,
|
||||||
|
"functionCall": false,
|
||||||
|
"defaultSystemChatPrompt": "",
|
||||||
|
"datasetProcess": true,
|
||||||
|
"usedInClassify": true,
|
||||||
|
"customCQPrompt": "",
|
||||||
|
"usedInExtractFields": true,
|
||||||
|
"usedInQueryExtension": true,
|
||||||
|
"customExtractPrompt": "",
|
||||||
|
"usedInToolCall": true,
|
||||||
|
"defaultConfig": {},
|
||||||
|
"fieldMap": {},
|
||||||
|
"type": "llm"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"model": "claude-3-5-haiku-20241022",
|
"model": "claude-3-5-haiku-20241022",
|
||||||
"name": "claude-3-5-haiku-20241022",
|
"name": "claude-3-5-haiku-20241022",
|
||||||
@@ -10,7 +34,7 @@
|
|||||||
"maxTemperature": 1,
|
"maxTemperature": 1,
|
||||||
"showTopP": true,
|
"showTopP": true,
|
||||||
"showStopSign": true,
|
"showStopSign": true,
|
||||||
"vision": false,
|
"vision": true,
|
||||||
"toolChoice": true,
|
"toolChoice": true,
|
||||||
"functionCall": false,
|
"functionCall": false,
|
||||||
"defaultSystemChatPrompt": "",
|
"defaultSystemChatPrompt": "",
|
||||||
@@ -98,4 +122,4 @@
|
|||||||
"type": "llm"
|
"type": "llm"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,54 @@
|
|||||||
{
|
{
|
||||||
"provider": "Gemini",
|
"provider": "Gemini",
|
||||||
"list": [
|
"list": [
|
||||||
|
{
|
||||||
|
"model": "gemini-2.0-flash",
|
||||||
|
"name": "gemini-2.0-flash",
|
||||||
|
"maxContext": 1000000,
|
||||||
|
"maxResponse": 8000,
|
||||||
|
"quoteMaxToken": 60000,
|
||||||
|
"maxTemperature": 1,
|
||||||
|
"vision": true,
|
||||||
|
"toolChoice": true,
|
||||||
|
"functionCall": false,
|
||||||
|
"defaultSystemChatPrompt": "",
|
||||||
|
"datasetProcess": true,
|
||||||
|
"usedInClassify": true,
|
||||||
|
"customCQPrompt": "",
|
||||||
|
"usedInExtractFields": true,
|
||||||
|
"usedInQueryExtension": true,
|
||||||
|
"customExtractPrompt": "",
|
||||||
|
"usedInToolCall": true,
|
||||||
|
"defaultConfig": {},
|
||||||
|
"fieldMap": {},
|
||||||
|
"type": "llm",
|
||||||
|
"showTopP": true,
|
||||||
|
"showStopSign": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "gemini-2.0-pro-exp",
|
||||||
|
"name": "gemini-2.0-pro-exp",
|
||||||
|
"maxContext": 2000000,
|
||||||
|
"maxResponse": 8000,
|
||||||
|
"quoteMaxToken": 100000,
|
||||||
|
"maxTemperature": 1,
|
||||||
|
"vision": true,
|
||||||
|
"toolChoice": true,
|
||||||
|
"functionCall": false,
|
||||||
|
"defaultSystemChatPrompt": "",
|
||||||
|
"datasetProcess": true,
|
||||||
|
"usedInClassify": true,
|
||||||
|
"customCQPrompt": "",
|
||||||
|
"usedInExtractFields": true,
|
||||||
|
"usedInQueryExtension": true,
|
||||||
|
"customExtractPrompt": "",
|
||||||
|
"usedInToolCall": true,
|
||||||
|
"defaultConfig": {},
|
||||||
|
"fieldMap": {},
|
||||||
|
"type": "llm",
|
||||||
|
"showTopP": true,
|
||||||
|
"showStopSign": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"model": "gemini-1.5-flash",
|
"model": "gemini-1.5-flash",
|
||||||
"name": "gemini-1.5-flash",
|
"name": "gemini-1.5-flash",
|
||||||
@@ -153,4 +201,4 @@
|
|||||||
"type": "embedding"
|
"type": "embedding"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,29 @@
|
|||||||
{
|
{
|
||||||
"provider": "Grok",
|
"provider": "Grok",
|
||||||
"list": []
|
"list": [
|
||||||
}
|
{
|
||||||
|
"model": "grok-3",
|
||||||
|
"name": "grok-3",
|
||||||
|
"maxContext": 128000,
|
||||||
|
"maxResponse": 8000,
|
||||||
|
"quoteMaxToken": 128000,
|
||||||
|
"maxTemperature": 1,
|
||||||
|
"showTopP": true,
|
||||||
|
"showStopSign": true,
|
||||||
|
"vision": false,
|
||||||
|
"toolChoice": false,
|
||||||
|
"functionCall": false,
|
||||||
|
"defaultSystemChatPrompt": "",
|
||||||
|
"datasetProcess": true,
|
||||||
|
"usedInClassify": true,
|
||||||
|
"customCQPrompt": "",
|
||||||
|
"usedInExtractFields": true,
|
||||||
|
"usedInQueryExtension": true,
|
||||||
|
"customExtractPrompt": "",
|
||||||
|
"usedInToolCall": true,
|
||||||
|
"defaultConfig": {},
|
||||||
|
"fieldMap": {},
|
||||||
|
"type": "llm"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
4
packages/service/core/ai/config/provider/PPIO.json
Normal file
4
packages/service/core/ai/config/provider/PPIO.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"provider": "PPIO",
|
||||||
|
"list": []
|
||||||
|
}
|
||||||
@@ -52,6 +52,12 @@ export const loadSystemModels = async (init = false) => {
|
|||||||
if (model.isDefault) {
|
if (model.isDefault) {
|
||||||
global.systemDefaultModel.llm = model;
|
global.systemDefaultModel.llm = model;
|
||||||
}
|
}
|
||||||
|
if (model.isDefaultDatasetTextModel) {
|
||||||
|
global.systemDefaultModel.datasetTextLLM = model;
|
||||||
|
}
|
||||||
|
if (model.isDefaultDatasetImageModel) {
|
||||||
|
global.systemDefaultModel.datasetImageLLM = model;
|
||||||
|
}
|
||||||
} else if (model.type === ModelTypeEnum.embedding) {
|
} else if (model.type === ModelTypeEnum.embedding) {
|
||||||
global.embeddingModelMap.set(model.model, model);
|
global.embeddingModelMap.set(model.model, model);
|
||||||
global.embeddingModelMap.set(model.name, model);
|
global.embeddingModelMap.set(model.name, model);
|
||||||
@@ -134,6 +140,16 @@ export const loadSystemModels = async (init = false) => {
|
|||||||
if (!global.systemDefaultModel.llm) {
|
if (!global.systemDefaultModel.llm) {
|
||||||
global.systemDefaultModel.llm = Array.from(global.llmModelMap.values())[0];
|
global.systemDefaultModel.llm = Array.from(global.llmModelMap.values())[0];
|
||||||
}
|
}
|
||||||
|
if (!global.systemDefaultModel.datasetTextLLM) {
|
||||||
|
global.systemDefaultModel.datasetTextLLM = Array.from(global.llmModelMap.values()).find(
|
||||||
|
(item) => item.datasetProcess
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!global.systemDefaultModel.datasetImageLLM) {
|
||||||
|
global.systemDefaultModel.datasetImageLLM = Array.from(global.llmModelMap.values()).find(
|
||||||
|
(item) => item.vision
|
||||||
|
);
|
||||||
|
}
|
||||||
if (!global.systemDefaultModel.embedding) {
|
if (!global.systemDefaultModel.embedding) {
|
||||||
global.systemDefaultModel.embedding = Array.from(global.embeddingModelMap.values())[0];
|
global.systemDefaultModel.embedding = Array.from(global.embeddingModelMap.values())[0];
|
||||||
}
|
}
|
||||||
|
|||||||
3
packages/service/core/ai/type.d.ts
vendored
3
packages/service/core/ai/type.d.ts
vendored
@@ -22,6 +22,9 @@ export type SystemModelItemType =
|
|||||||
|
|
||||||
export type SystemDefaultModelType = {
|
export type SystemDefaultModelType = {
|
||||||
[ModelTypeEnum.llm]?: LLMModelItemType;
|
[ModelTypeEnum.llm]?: LLMModelItemType;
|
||||||
|
datasetTextLLM?: LLMModelItemType;
|
||||||
|
datasetImageLLM?: LLMModelItemType;
|
||||||
|
|
||||||
[ModelTypeEnum.embedding]?: EmbeddingModelItemType;
|
[ModelTypeEnum.embedding]?: EmbeddingModelItemType;
|
||||||
[ModelTypeEnum.tts]?: TTSModelType;
|
[ModelTypeEnum.tts]?: TTSModelType;
|
||||||
[ModelTypeEnum.stt]?: STTModelType;
|
[ModelTypeEnum.stt]?: STTModelType;
|
||||||
|
|||||||
@@ -37,25 +37,26 @@ export const computedTemperature = ({
|
|||||||
return temperature;
|
return temperature;
|
||||||
};
|
};
|
||||||
|
|
||||||
type CompletionsBodyType = (
|
type CompletionsBodyType =
|
||||||
| ChatCompletionCreateParamsNonStreaming
|
| ChatCompletionCreateParamsNonStreaming
|
||||||
| ChatCompletionCreateParamsStreaming
|
| ChatCompletionCreateParamsStreaming;
|
||||||
) & {
|
|
||||||
response_format?: any;
|
|
||||||
json_schema?: string;
|
|
||||||
stop?: string;
|
|
||||||
};
|
|
||||||
type InferCompletionsBody<T> = T extends { stream: true }
|
type InferCompletionsBody<T> = T extends { stream: true }
|
||||||
? ChatCompletionCreateParamsStreaming
|
? ChatCompletionCreateParamsStreaming
|
||||||
: ChatCompletionCreateParamsNonStreaming;
|
: T extends { stream: false }
|
||||||
|
? ChatCompletionCreateParamsNonStreaming
|
||||||
|
: ChatCompletionCreateParamsNonStreaming | ChatCompletionCreateParamsStreaming;
|
||||||
|
|
||||||
export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
|
export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
|
||||||
body: T,
|
body: T & {
|
||||||
|
response_format?: any;
|
||||||
|
json_schema?: string;
|
||||||
|
stop?: string;
|
||||||
|
},
|
||||||
model: string | LLMModelItemType
|
model: string | LLMModelItemType
|
||||||
): InferCompletionsBody<T> => {
|
): InferCompletionsBody<T> => {
|
||||||
const modelData = typeof model === 'string' ? getLLMModel(model) : model;
|
const modelData = typeof model === 'string' ? getLLMModel(model) : model;
|
||||||
if (!modelData) {
|
if (!modelData) {
|
||||||
return body as InferCompletionsBody<T>;
|
return body as unknown as InferCompletionsBody<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response_format = body.response_format;
|
const response_format = body.response_format;
|
||||||
@@ -91,16 +92,148 @@ export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(requestBody);
|
return requestBody as unknown as InferCompletionsBody<T>;
|
||||||
|
|
||||||
return requestBody as InferCompletionsBody<T>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const llmStreamResponseToText = async (response: StreamChatType) => {
|
export const llmStreamResponseToAnswerText = async (response: StreamChatType) => {
|
||||||
let answer = '';
|
let answer = '';
|
||||||
for await (const part of response) {
|
for await (const part of response) {
|
||||||
const content = part.choices?.[0]?.delta?.content || '';
|
const content = part.choices?.[0]?.delta?.content || '';
|
||||||
answer += 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
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { connectionMongo, getMongoModel } from '../../common/mongo';
|
import { connectionMongo, getMongoModel } from '../../common/mongo';
|
||||||
const { Schema } = connectionMongo;
|
const { Schema } = connectionMongo;
|
||||||
import { ChatSchema as ChatType } from '@fastgpt/global/core/chat/type.d';
|
import { ChatSchema as ChatType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { ChatSourceMap } from '@fastgpt/global/core/chat/constants';
|
import { ChatSourceEnum, ChatSourceMap } from '@fastgpt/global/core/chat/constants';
|
||||||
import {
|
import {
|
||||||
TeamCollectionName,
|
TeamCollectionName,
|
||||||
TeamMemberCollectionName
|
TeamMemberCollectionName
|
||||||
@@ -52,8 +52,10 @@ const ChatSchema = new Schema({
|
|||||||
},
|
},
|
||||||
source: {
|
source: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
|
enum: Object.values(ChatSourceEnum)
|
||||||
},
|
},
|
||||||
|
sourceName: String,
|
||||||
shareId: {
|
shareId: {
|
||||||
type: String
|
type: String
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type.d';
|
import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { MongoApp } from '../app/schema';
|
import { MongoApp } from '../app/schema';
|
||||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
import {
|
||||||
|
ChatItemValueTypeEnum,
|
||||||
|
ChatRoleEnum,
|
||||||
|
ChatSourceEnum
|
||||||
|
} from '@fastgpt/global/core/chat/constants';
|
||||||
import { MongoChatItem } from './chatItemSchema';
|
import { MongoChatItem } from './chatItemSchema';
|
||||||
import { MongoChat } from './chatSchema';
|
import { MongoChat } from './chatSchema';
|
||||||
import { addLog } from '../../common/system/log';
|
import { addLog } from '../../common/system/log';
|
||||||
@@ -22,7 +26,8 @@ type Props = {
|
|||||||
variables?: Record<string, any>;
|
variables?: Record<string, any>;
|
||||||
isUpdateUseTime: boolean;
|
isUpdateUseTime: boolean;
|
||||||
newTitle: string;
|
newTitle: string;
|
||||||
source: string;
|
source: `${ChatSourceEnum}`;
|
||||||
|
sourceName?: string;
|
||||||
shareId?: string;
|
shareId?: string;
|
||||||
outLinkUid?: string;
|
outLinkUid?: string;
|
||||||
content: [UserChatItemType & { dataId?: string }, AIChatItemType & { dataId?: string }];
|
content: [UserChatItemType & { dataId?: string }, AIChatItemType & { dataId?: string }];
|
||||||
@@ -40,6 +45,7 @@ export async function saveChat({
|
|||||||
isUpdateUseTime,
|
isUpdateUseTime,
|
||||||
newTitle,
|
newTitle,
|
||||||
source,
|
source,
|
||||||
|
sourceName,
|
||||||
shareId,
|
shareId,
|
||||||
outLinkUid,
|
outLinkUid,
|
||||||
content,
|
content,
|
||||||
@@ -96,6 +102,7 @@ export async function saveChat({
|
|||||||
pluginInputs,
|
pluginInputs,
|
||||||
title: newTitle,
|
title: newTitle,
|
||||||
source,
|
source,
|
||||||
|
sourceName,
|
||||||
shareId,
|
shareId,
|
||||||
outLinkUid,
|
outLinkUid,
|
||||||
metadata: metadataUpdate,
|
metadata: metadataUpdate,
|
||||||
|
|||||||
@@ -197,7 +197,11 @@ export const loadRequestMessages = async ({
|
|||||||
addLog.info(`Filter invalid image: ${imgUrl}`);
|
addLog.info(`Filter invalid image: ${imgUrl}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
|
if (error?.response?.status === 405) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
addLog.warn(`Filter invalid image: ${imgUrl}`, { error });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { MongoImage } from '../../../common/file/image/schema';
|
|||||||
import { hashStr } from '@fastgpt/global/common/string/tools';
|
import { hashStr } from '@fastgpt/global/common/string/tools';
|
||||||
import { addDays } from 'date-fns';
|
import { addDays } from 'date-fns';
|
||||||
import { MongoDatasetDataText } from '../data/dataTextSchema';
|
import { MongoDatasetDataText } from '../data/dataTextSchema';
|
||||||
|
import { delay, retryFn } from '@fastgpt/global/common/system/utils';
|
||||||
|
|
||||||
export const createCollectionAndInsertData = async ({
|
export const createCollectionAndInsertData = async ({
|
||||||
dataset,
|
dataset,
|
||||||
@@ -216,7 +217,7 @@ export async function createOneCollection({
|
|||||||
nextSyncTime
|
nextSyncTime
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
{ session }
|
{ session, ordered: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
return collection;
|
return collection;
|
||||||
@@ -227,8 +228,14 @@ export const delCollectionRelatedSource = async ({
|
|||||||
collections,
|
collections,
|
||||||
session
|
session
|
||||||
}: {
|
}: {
|
||||||
collections: DatasetCollectionSchemaType[];
|
collections: {
|
||||||
session: ClientSession;
|
teamId: string;
|
||||||
|
fileId?: string;
|
||||||
|
metadata?: {
|
||||||
|
relatedImgId?: string;
|
||||||
|
};
|
||||||
|
}[];
|
||||||
|
session?: ClientSession;
|
||||||
}) => {
|
}) => {
|
||||||
if (collections.length === 0) return;
|
if (collections.length === 0) return;
|
||||||
|
|
||||||
@@ -259,11 +266,13 @@ export const delCollectionRelatedSource = async ({
|
|||||||
export async function delCollection({
|
export async function delCollection({
|
||||||
collections,
|
collections,
|
||||||
session,
|
session,
|
||||||
delRelatedSource
|
delImg = true,
|
||||||
|
delFile = true
|
||||||
}: {
|
}: {
|
||||||
collections: DatasetCollectionSchemaType[];
|
collections: DatasetCollectionSchemaType[];
|
||||||
session: ClientSession;
|
session: ClientSession;
|
||||||
delRelatedSource: boolean;
|
delImg: boolean;
|
||||||
|
delFile: boolean;
|
||||||
}) {
|
}) {
|
||||||
if (collections.length === 0) return;
|
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 datasetIds = Array.from(new Set(collections.map((item) => String(item.datasetId))));
|
||||||
const collectionIds = collections.map((item) => String(item._id));
|
const collectionIds = collections.map((item) => String(item._id));
|
||||||
|
|
||||||
// Delete training data
|
await retryFn(async () => {
|
||||||
await MongoDatasetTraining.deleteMany({
|
await Promise.all([
|
||||||
teamId,
|
// Delete training data
|
||||||
datasetIds: { $in: datasetIds },
|
MongoDatasetTraining.deleteMany({
|
||||||
collectionId: { $in: collectionIds }
|
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 });
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export const createOrGetCollectionTags = async ({
|
|||||||
datasetId,
|
datasetId,
|
||||||
tag: tagContent
|
tag: tagContent
|
||||||
})),
|
})),
|
||||||
{ session }
|
{ session, ordered: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
return [...existingTags.map((tag) => tag._id), ...newTags.map((tag) => tag._id)];
|
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) => {
|
await mongoSessionRun(async (session) => {
|
||||||
|
// Delete old collection
|
||||||
|
await delCollection({
|
||||||
|
collections: [collection],
|
||||||
|
delImg: false,
|
||||||
|
delFile: false,
|
||||||
|
session
|
||||||
|
});
|
||||||
|
|
||||||
// Create new collection
|
// Create new collection
|
||||||
await createCollectionAndInsertData({
|
await createCollectionAndInsertData({
|
||||||
session,
|
session,
|
||||||
@@ -208,13 +216,6 @@ export const syncCollection = async (collection: CollectionWithDatasetType) => {
|
|||||||
updateTime: new Date()
|
updateTime: new Date()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Delete old collection
|
|
||||||
await delCollection({
|
|
||||||
collections: [collection],
|
|
||||||
delRelatedSource: false,
|
|
||||||
session
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return DatasetCollectionSyncResultEnum.success;
|
return DatasetCollectionSyncResultEnum.success;
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { MongoDatasetTraining } from './training/schema';
|
|||||||
import { MongoDatasetData } from './data/schema';
|
import { MongoDatasetData } from './data/schema';
|
||||||
import { deleteDatasetDataVector } from '../../common/vectorStore/controller';
|
import { deleteDatasetDataVector } from '../../common/vectorStore/controller';
|
||||||
import { MongoDatasetDataText } from './data/dataTextSchema';
|
import { MongoDatasetDataText } from './data/dataTextSchema';
|
||||||
|
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
|
||||||
|
import { retryFn } from '@fastgpt/global/common/system/utils';
|
||||||
|
|
||||||
/* ============= dataset ========== */
|
/* ============= dataset ========== */
|
||||||
/* find all datasetId by top datasetId */
|
/* find all datasetId by top datasetId */
|
||||||
@@ -54,7 +56,7 @@ export async function getCollectionWithDataset(collectionId: string) {
|
|||||||
.populate<{ dataset: DatasetSchemaType }>('dataset')
|
.populate<{ dataset: DatasetSchemaType }>('dataset')
|
||||||
.lean();
|
.lean();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return Promise.reject('Collection is not exist');
|
return Promise.reject(DatasetErrEnum.unExistCollection);
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -77,40 +79,39 @@ export async function delDatasetRelevantData({
|
|||||||
|
|
||||||
const datasetIds = datasets.map((item) => item._id);
|
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
|
// Get _id, teamId, fileId, metadata.relatedImgId for all collections
|
||||||
const collections = await MongoDatasetCollection.find(
|
const collections = await MongoDatasetCollection.find(
|
||||||
{
|
{
|
||||||
teamId,
|
teamId,
|
||||||
datasetId: { $in: datasetIds }
|
datasetId: { $in: datasetIds }
|
||||||
},
|
},
|
||||||
'_id teamId datasetId fileId metadata',
|
'_id teamId datasetId fileId metadata'
|
||||||
{ session }
|
|
||||||
).lean();
|
).lean();
|
||||||
|
|
||||||
// Delete Image and file
|
await retryFn(async () => {
|
||||||
await delCollectionRelatedSource({ collections, session });
|
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
|
// delete collections
|
||||||
await MongoDatasetCollection.deleteMany({
|
await MongoDatasetCollection.deleteMany({
|
||||||
teamId,
|
teamId,
|
||||||
datasetId: { $in: datasetIds }
|
datasetId: { $in: datasetIds }
|
||||||
}).session(session);
|
}).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 });
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { connectionMongo, getMongoModel } from '../../../common/mongo';
|
import { connectionMongo, getMongoModel } from '../../../common/mongo';
|
||||||
const { Schema } = connectionMongo;
|
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 { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||||
import { DatasetCollectionName } from '../schema';
|
import { DatasetCollectionName } from '../schema';
|
||||||
import { DatasetColCollectionName } from '../collection/schema';
|
import { DatasetColCollectionName } from '../collection/schema';
|
||||||
@@ -40,12 +40,13 @@ try {
|
|||||||
default_language: 'none'
|
default_language: 'none'
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
DatasetDataTextSchema.index({ teamId: 1, datasetId: 1, collectionId: 1 });
|
||||||
DatasetDataTextSchema.index({ dataId: 1 }, { unique: true });
|
DatasetDataTextSchema.index({ dataId: 1 }, { unique: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MongoDatasetDataText = getMongoModel<DatasetDataSchemaType>(
|
export const MongoDatasetDataText = getMongoModel<DatasetDataTextSchemaType>(
|
||||||
DatasetDataTextCollectionName,
|
DatasetDataTextCollectionName,
|
||||||
DatasetDataTextSchema
|
DatasetDataTextSchema
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -200,6 +200,7 @@ export async function searchDatasetData(
|
|||||||
forbidCollectionIdList: collections.map((item) => String(item._id))
|
forbidCollectionIdList: collections.map((item) => String(item._id))
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Collection metadata filter
|
Collection metadata filter
|
||||||
标签过滤:
|
标签过滤:
|
||||||
@@ -207,6 +208,63 @@ export async function searchDatasetData(
|
|||||||
2. and 标签和 null 不能共存,否则返回空数组
|
2. and 标签和 null 不能共存,否则返回空数组
|
||||||
*/
|
*/
|
||||||
const filterCollectionByMetadata = async (): Promise<string[] | undefined> => {
|
const filterCollectionByMetadata = async (): Promise<string[] | undefined> => {
|
||||||
|
const getAllCollectionIds = async ({
|
||||||
|
parentCollectionIds
|
||||||
|
}: {
|
||||||
|
parentCollectionIds?: string[];
|
||||||
|
}): Promise<string[] | undefined> => {
|
||||||
|
if (!parentCollectionIds) return;
|
||||||
|
if (parentCollectionIds.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const collections = await MongoDatasetCollection.find(
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
datasetId: { $in: datasetIds },
|
||||||
|
_id: { $in: parentCollectionIds }
|
||||||
|
},
|
||||||
|
'_id type',
|
||||||
|
{
|
||||||
|
...readFromSecondary
|
||||||
|
}
|
||||||
|
).lean();
|
||||||
|
|
||||||
|
const resultIds = new Set<string>();
|
||||||
|
collections.forEach((item) => {
|
||||||
|
if (item.type !== 'folder') {
|
||||||
|
resultIds.add(String(item._id));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const folderIds = collections
|
||||||
|
.filter((item) => item.type === 'folder')
|
||||||
|
.map((item) => String(item._id));
|
||||||
|
|
||||||
|
// Get all child collection ids
|
||||||
|
if (folderIds.length) {
|
||||||
|
const childCollections = await MongoDatasetCollection.find(
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
datasetId: { $in: datasetIds },
|
||||||
|
parentId: { $in: folderIds }
|
||||||
|
},
|
||||||
|
'_id type',
|
||||||
|
{
|
||||||
|
...readFromSecondary
|
||||||
|
}
|
||||||
|
).lean();
|
||||||
|
|
||||||
|
const childIds = await getAllCollectionIds({
|
||||||
|
parentCollectionIds: childCollections.map((item) => String(item._id))
|
||||||
|
});
|
||||||
|
|
||||||
|
childIds?.forEach((id) => resultIds.add(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(resultIds);
|
||||||
|
};
|
||||||
|
|
||||||
if (!collectionFilterMatch || !global.feConfigs.isPlus) return;
|
if (!collectionFilterMatch || !global.feConfigs.isPlus) return;
|
||||||
|
|
||||||
let tagCollectionIdList: string[] | undefined = undefined;
|
let tagCollectionIdList: string[] | undefined = undefined;
|
||||||
@@ -326,13 +384,19 @@ export async function searchDatasetData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Concat tag and time
|
// Concat tag and time
|
||||||
if (tagCollectionIdList && createTimeCollectionIdList) {
|
const collectionIds = (() => {
|
||||||
return tagCollectionIdList.filter((id) => createTimeCollectionIdList!.includes(id));
|
if (tagCollectionIdList && createTimeCollectionIdList) {
|
||||||
} else if (tagCollectionIdList) {
|
return tagCollectionIdList.filter((id) =>
|
||||||
return tagCollectionIdList;
|
(createTimeCollectionIdList as string[]).includes(id)
|
||||||
} else if (createTimeCollectionIdList) {
|
);
|
||||||
return createTimeCollectionIdList;
|
}
|
||||||
}
|
|
||||||
|
return tagCollectionIdList || createTimeCollectionIdList;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return await getAllCollectionIds({
|
||||||
|
parentCollectionIds: collectionIds
|
||||||
|
});
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
};
|
};
|
||||||
const embeddingRecall = async ({
|
const embeddingRecall = async ({
|
||||||
@@ -383,6 +447,7 @@ export async function searchDatasetData(
|
|||||||
).lean()
|
).lean()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const set = new Map<string, number>();
|
||||||
const formatResult = results
|
const formatResult = results
|
||||||
.map((item, index) => {
|
.map((item, index) => {
|
||||||
const collection = collections.find((col) => String(col._id) === String(item.collectionId));
|
const collection = collections.find((col) => String(col._id) === String(item.collectionId));
|
||||||
@@ -398,8 +463,6 @@ export async function searchDatasetData(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const score = item?.score || 0;
|
|
||||||
|
|
||||||
const result: SearchDataResponseItemType = {
|
const result: SearchDataResponseItemType = {
|
||||||
id: String(data._id),
|
id: String(data._id),
|
||||||
updateTime: data.updateTime,
|
updateTime: data.updateTime,
|
||||||
@@ -409,12 +472,24 @@ export async function searchDatasetData(
|
|||||||
datasetId: String(data.datasetId),
|
datasetId: String(data.datasetId),
|
||||||
collectionId: String(data.collectionId),
|
collectionId: String(data.collectionId),
|
||||||
...getCollectionSourceData(collection),
|
...getCollectionSourceData(collection),
|
||||||
score: [{ type: SearchScoreTypeEnum.embedding, value: score, index }]
|
score: [{ type: SearchScoreTypeEnum.embedding, value: item?.score || 0, index }]
|
||||||
};
|
};
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
})
|
})
|
||||||
.filter(Boolean) as SearchDataResponseItemType[];
|
.filter((item) => {
|
||||||
|
if (!item) return false;
|
||||||
|
if (set.has(item.id)) return false;
|
||||||
|
set.set(item.id, 1);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((item, index) => {
|
||||||
|
if (!item) return;
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
score: item.score.map((item) => ({ ...item, index }))
|
||||||
|
};
|
||||||
|
}) as SearchDataResponseItemType[];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
embeddingRecallResults: formatResult,
|
embeddingRecallResults: formatResult,
|
||||||
@@ -717,11 +792,12 @@ export const defaultSearchDatasetData = async ({
|
|||||||
? getLLMModel(datasetSearchExtensionModel)
|
? getLLMModel(datasetSearchExtensionModel)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const { concatQueries, rewriteQuery, aiExtensionResult } = await datasetSearchQueryExtension({
|
const { concatQueries, extensionQueries, rewriteQuery, aiExtensionResult } =
|
||||||
query,
|
await datasetSearchQueryExtension({
|
||||||
extensionModel,
|
query,
|
||||||
extensionBg: datasetSearchExtensionBg
|
extensionModel,
|
||||||
});
|
extensionBg: datasetSearchExtensionBg
|
||||||
|
});
|
||||||
|
|
||||||
const result = await searchDatasetData({
|
const result = await searchDatasetData({
|
||||||
...props,
|
...props,
|
||||||
@@ -736,7 +812,7 @@ export const defaultSearchDatasetData = async ({
|
|||||||
model: aiExtensionResult.model,
|
model: aiExtensionResult.model,
|
||||||
inputTokens: aiExtensionResult.inputTokens,
|
inputTokens: aiExtensionResult.inputTokens,
|
||||||
outputTokens: aiExtensionResult.outputTokens,
|
outputTokens: aiExtensionResult.outputTokens,
|
||||||
query: concatQueries.join('\n')
|
query: extensionQueries.join('\n')
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -72,12 +72,15 @@ Human: ${query}
|
|||||||
if (result.extensionQueries?.length === 0) return;
|
if (result.extensionQueries?.length === 0) return;
|
||||||
return result;
|
return result;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
const extensionQueries = filterSamQuery(aiExtensionResult?.extensionQueries || []);
|
||||||
if (aiExtensionResult) {
|
if (aiExtensionResult) {
|
||||||
queries = filterSamQuery(queries.concat(aiExtensionResult.extensionQueries));
|
queries = filterSamQuery(queries.concat(extensionQueries));
|
||||||
rewriteQuery = queries.join('\n');
|
rewriteQuery = queries.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
extensionQueries,
|
||||||
concatQueries: queries,
|
concatQueries: queries,
|
||||||
rewriteQuery,
|
rewriteQuery,
|
||||||
aiExtensionResult
|
aiExtensionResult
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ const getMultiInput = async ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
documentQuoteText: text,
|
documentQuoteText: text,
|
||||||
userFiles: fileLinks.map((url) => parseUrlToFileType(url))
|
userFiles: fileLinks.map((url) => parseUrlToFileType(url)).filter(Boolean)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,9 @@ import type { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/co
|
|||||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
|
import { parseReasoningContent, parseReasoningStreamContent } from '../../../ai/utils';
|
||||||
import { createChatCompletion } from '../../../ai/config';
|
import { createChatCompletion } from '../../../ai/config';
|
||||||
import type {
|
import type { ChatCompletionMessageParam, StreamChatType } from '@fastgpt/global/core/ai/type.d';
|
||||||
ChatCompletion,
|
|
||||||
ChatCompletionMessageParam,
|
|
||||||
StreamChatType
|
|
||||||
} from '@fastgpt/global/core/ai/type.d';
|
|
||||||
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
||||||
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||||
import { postTextCensor } from '../../../../common/api/requestPlusApi';
|
import { postTextCensor } from '../../../../common/api/requestPlusApi';
|
||||||
@@ -195,7 +192,13 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { answerText, reasoningText } = await (async () => {
|
const { answerText, reasoningText } = await (async () => {
|
||||||
if (res && isStreamResponse) {
|
if (isStreamResponse) {
|
||||||
|
if (!res) {
|
||||||
|
return {
|
||||||
|
answerText: '',
|
||||||
|
reasoningText: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
// sse response
|
// sse response
|
||||||
const { answer, reasoning } = await streamResponse({
|
const { answer, reasoning } = await streamResponse({
|
||||||
res,
|
res,
|
||||||
@@ -210,34 +213,49 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
reasoningText: reasoning
|
reasoningText: reasoning
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const unStreamResponse = response as ChatCompletion;
|
const { content, reasoningContent } = (() => {
|
||||||
const answer = unStreamResponse.choices?.[0]?.message?.content || '';
|
const content = response.choices?.[0]?.message?.content || '';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const reasoning = unStreamResponse.choices?.[0]?.message?.reasoning_content || '';
|
const reasoningContent: string = response.choices?.[0]?.message?.reasoning_content || '';
|
||||||
|
|
||||||
|
// API already parse reasoning content
|
||||||
|
if (reasoningContent || !aiChatReasoning) {
|
||||||
|
return {
|
||||||
|
content,
|
||||||
|
reasoningContent
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const [think, answer] = parseReasoningContent(content);
|
||||||
|
return {
|
||||||
|
content: answer,
|
||||||
|
reasoningContent: think
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
// Some models do not support streaming
|
// Some models do not support streaming
|
||||||
if (stream) {
|
if (stream) {
|
||||||
if (isResponseAnswerText && answer) {
|
if (aiChatReasoning && reasoningContent) {
|
||||||
workflowStreamResponse?.({
|
workflowStreamResponse?.({
|
||||||
event: SseResponseEventEnum.fastAnswer,
|
event: SseResponseEventEnum.fastAnswer,
|
||||||
data: textAdaptGptResponse({
|
data: textAdaptGptResponse({
|
||||||
text: answer
|
reasoning_content: reasoningContent
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (aiChatReasoning && reasoning) {
|
if (isResponseAnswerText && content) {
|
||||||
workflowStreamResponse?.({
|
workflowStreamResponse?.({
|
||||||
event: SseResponseEventEnum.fastAnswer,
|
event: SseResponseEventEnum.fastAnswer,
|
||||||
data: textAdaptGptResponse({
|
data: textAdaptGptResponse({
|
||||||
reasoning_content: reasoning
|
text: content
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
answerText: answer,
|
answerText: content,
|
||||||
reasoningText: reasoning
|
reasoningText: reasoningContent
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
@@ -249,7 +267,8 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
const AIMessages: ChatCompletionMessageParam[] = [
|
const AIMessages: ChatCompletionMessageParam[] = [
|
||||||
{
|
{
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||||
content: answerText
|
content: answerText,
|
||||||
|
reasoning_text: reasoningText // reasoning_text is only recorded for response, but not for request
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -267,7 +286,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
answerText,
|
answerText: answerText.trim(),
|
||||||
reasoningText,
|
reasoningText,
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||||
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
||||||
@@ -386,7 +405,7 @@ async function getMultiInput({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
documentQuoteText: text,
|
documentQuoteText: text,
|
||||||
userFiles: fileLinks.map((url) => parseUrlToFileType(url))
|
userFiles: fileLinks.map((url) => parseUrlToFileType(url)).filter(Boolean)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,26 +519,18 @@ async function streamResponse({
|
|||||||
});
|
});
|
||||||
let answer = '';
|
let answer = '';
|
||||||
let reasoning = '';
|
let reasoning = '';
|
||||||
|
const { parsePart, getStartTagBuffer } = parseReasoningStreamContent();
|
||||||
|
|
||||||
for await (const part of stream) {
|
for await (const part of stream) {
|
||||||
if (res.closed) {
|
if (res.closed) {
|
||||||
stream.controller?.abort();
|
stream.controller?.abort();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = part.choices?.[0]?.delta?.content || '';
|
const [reasoningContent, content] = parsePart(part, aiChatReasoning);
|
||||||
answer += content;
|
answer += content;
|
||||||
if (isResponseAnswerText && content) {
|
|
||||||
workflowStreamResponse?.({
|
|
||||||
write,
|
|
||||||
event: SseResponseEventEnum.answer,
|
|
||||||
data: textAdaptGptResponse({
|
|
||||||
text: content
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || '';
|
|
||||||
reasoning += reasoningContent;
|
reasoning += reasoningContent;
|
||||||
|
|
||||||
if (aiChatReasoning && reasoningContent) {
|
if (aiChatReasoning && reasoningContent) {
|
||||||
workflowStreamResponse?.({
|
workflowStreamResponse?.({
|
||||||
write,
|
write,
|
||||||
@@ -529,6 +540,21 @@ async function streamResponse({
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isResponseAnswerText && content) {
|
||||||
|
workflowStreamResponse?.({
|
||||||
|
write,
|
||||||
|
event: SseResponseEventEnum.answer,
|
||||||
|
data: textAdaptGptResponse({
|
||||||
|
text: content
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if answer is empty, try to get value from startTagBuffer. (Cause: The response content is too short to exceed the minimum parse length)
|
||||||
|
if (answer === '') {
|
||||||
|
answer = getStartTagBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
return { answer, reasoning };
|
return { answer, reasoning };
|
||||||
|
|||||||
@@ -232,9 +232,14 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
chatNodeUsages = chatNodeUsages.concat(nodeDispatchUsages);
|
chatNodeUsages = chatNodeUsages.concat(nodeDispatchUsages);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolResponses !== undefined) {
|
if (toolResponses !== undefined && toolResponses !== null) {
|
||||||
if (Array.isArray(toolResponses) && toolResponses.length === 0) return;
|
if (Array.isArray(toolResponses) && toolResponses.length === 0) return;
|
||||||
if (typeof toolResponses === 'object' && Object.keys(toolResponses).length === 0) return;
|
if (
|
||||||
|
!Array.isArray(toolResponses) &&
|
||||||
|
typeof toolResponses === 'object' &&
|
||||||
|
Object.keys(toolResponses).length === 0
|
||||||
|
)
|
||||||
|
return;
|
||||||
toolRunResponse = toolResponses;
|
toolRunResponse = toolResponses;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,12 +248,17 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
chatAssistantResponse = chatAssistantResponse.concat(assistantResponses);
|
chatAssistantResponse = chatAssistantResponse.concat(assistantResponses);
|
||||||
} else {
|
} else {
|
||||||
if (reasoningText) {
|
if (reasoningText) {
|
||||||
chatAssistantResponse.push({
|
const isResponseReasoningText = inputs.find(
|
||||||
type: ChatItemValueTypeEnum.reasoning,
|
(item) => item.key === NodeInputKeyEnum.aiChatReasoning
|
||||||
reasoning: {
|
)?.value;
|
||||||
content: reasoningText
|
if (isResponseReasoningText) {
|
||||||
}
|
chatAssistantResponse.push({
|
||||||
});
|
type: ChatItemValueTypeEnum.reasoning,
|
||||||
|
reasoning: {
|
||||||
|
content: reasoningText
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (answerText) {
|
if (answerText) {
|
||||||
// save assistant text response
|
// save assistant text response
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
|
|||||||
|
|
||||||
const userInputFiles = (() => {
|
const userInputFiles = (() => {
|
||||||
if (fileUrlList) {
|
if (fileUrlList) {
|
||||||
return fileUrlList.map((url) => parseUrlToFileType(url));
|
return fileUrlList.map((url) => parseUrlToFileType(url)).filter(Boolean);
|
||||||
}
|
}
|
||||||
// Adapt version 4.8.13 upgrade
|
// Adapt version 4.8.13 upgrade
|
||||||
return files;
|
return files;
|
||||||
|
|||||||
@@ -398,41 +398,6 @@ async function fetchData({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// function replaceVariable(text: string, obj: Record<string, any>) {
|
|
||||||
// for (const [key, value] of Object.entries(obj)) {
|
|
||||||
// if (value === undefined) {
|
|
||||||
// text = text.replace(new RegExp(`{{(${key})}}`, 'g'), UNDEFINED_SIGN);
|
|
||||||
// } else {
|
|
||||||
// const replacement = JSON.stringify(value);
|
|
||||||
// const unquotedReplacement =
|
|
||||||
// replacement.startsWith('"') && replacement.endsWith('"')
|
|
||||||
// ? replacement.slice(1, -1)
|
|
||||||
// : replacement;
|
|
||||||
// text = text.replace(new RegExp(`{{(${key})}}`, 'g'), () => unquotedReplacement);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return text || '';
|
|
||||||
// }
|
|
||||||
// function removeUndefinedSign(obj: Record<string, any>) {
|
|
||||||
// for (const key in obj) {
|
|
||||||
// if (obj[key] === UNDEFINED_SIGN) {
|
|
||||||
// obj[key] = undefined;
|
|
||||||
// } else if (Array.isArray(obj[key])) {
|
|
||||||
// obj[key] = obj[key].map((item: any) => {
|
|
||||||
// if (item === UNDEFINED_SIGN) {
|
|
||||||
// return undefined;
|
|
||||||
// } else if (typeof item === 'object') {
|
|
||||||
// removeUndefinedSign(item);
|
|
||||||
// }
|
|
||||||
// return item;
|
|
||||||
// });
|
|
||||||
// } else if (typeof obj[key] === 'object') {
|
|
||||||
// removeUndefinedSign(obj[key]);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return obj;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Replace some special response from system plugin
|
// Replace some special response from system plugin
|
||||||
async function replaceSystemPluginResponse({
|
async function replaceSystemPluginResponse({
|
||||||
response,
|
response,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mammoth": "^1.6.0",
|
"mammoth": "^1.6.0",
|
||||||
"mongoose": "^7.0.2",
|
"mongoose": "^8.10.1",
|
||||||
"multer": "1.4.5-lts.1",
|
"multer": "1.4.5-lts.1",
|
||||||
"next": "14.2.5",
|
"next": "14.2.5",
|
||||||
"nextjs-cors": "^2.2.0",
|
"nextjs-cors": "^2.2.0",
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ export const getClbsAndGroupsWithInfo = async ({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export const delResourcePermissionById = (id: string) => {
|
export const delResourcePermissionById = (id: string) => {
|
||||||
return MongoResourcePermission.findByIdAndRemove(id);
|
return MongoResourcePermission.findByIdAndDelete(id);
|
||||||
};
|
};
|
||||||
export const delResourcePermission = ({
|
export const delResourcePermission = ({
|
||||||
session,
|
session,
|
||||||
|
|||||||
@@ -196,7 +196,8 @@ export async function syncCollaborators({
|
|||||||
permission: item.permission
|
permission: item.permission
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
session
|
session,
|
||||||
|
ordered: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user