Compare commits

..

8 Commits

Author SHA1 Message Date
Archer
9a1fff74fd fix: nextjs 14.2.24 cannot auto create local storage (#4249) (#4250) 2025-03-20 11:40:47 +08:00
Archer
de87639fce fix: vector name (#4246) 2025-03-20 00:27:31 +08:00
Archer
f9cecfd49a fix: dataset selector load error (#4243)
* fix: dataset selector load error

* fix: path auth error

* fix: plugin scroll

* export chat log with contact (#4211)

* export chat log with contact

* fix

---------

Co-authored-by: heheer <heheer@sealos.io>
2025-03-20 00:17:10 +08:00
gggaaallleee
70563d2bcb fix DatasetSelectModal.tsx (#4241) 2025-03-19 23:08:22 +08:00
a.e.
4ca99a6361 docs: Add PPIO integration documentation (#4216)
* docs: Add PPIO integration documentation

Add new documentation for integrating models via PPIO LLM API.

* docs: copy PPIO docs
2025-03-18 17:44:49 +08:00
Finley Ge
8f70e436cf doc: invitaion link (#4217) 2025-03-18 17:11:37 +08:00
Archer
e75d81d05a V4.9.1 feature (#4206)
* fix: remove DefaultTeam (#4037)

* fix :Get application bound knowledge base information logical rewrite (#4057)

* fix :Get application bound knowledge base information logical rewrite

* fix :Get application bound knowledge base information logical rewrite

* fix :Get application bound knowledge base information logical rewrite

* fix :Get application bound knowledge base information logical rewrite

* update package

* fix: import dataset step error;perf: ai proxy avatar (#4074)

* perf: pg config params

* perf: ai proxy avatar

* fix: import dataset step error

* feat: data input ux

* perf: app dataset rewite

* fix: 文本提取不支持arrayString,arrayNumber等jsonSchema (#4079)

* update doc ;perf: model test (#4098)

* perf: extract array

* update doc

* perf: model test

* perf: model test

* perf: think tag parse (#4102)

* chat quote reader (#3912)

* init chat quote full text reader

* linked structure

* dataset data linked

* optimize code

* fix ts build

* test finish

* delete log

* fix

* fix ts

* fix ts

* remove nextId

* initial scroll

* fix

* fix

* perf: chunk read   (#4109)

* package

* perf: chunk read

* feat: api dataset support pdf parse;fix: chunk reader auth (#4117)

* feat: api dataset support pdf parse

* fix: chunk reader auth

* feat: invitation link (#3979)

* feat: invitation link schema and apis

* feat: add invitation link

* feat: member status: active, leave, forbidden

* fix: expires show hours and minutes

* feat: invalid invitation link hint

* fix: typo

* chore: fix typo & i18n

* fix

* pref: fe

* feat: add ttl index for 30-day-clean-up

* perf: invite member code (#4118)

* perf: invite member code

* fix: ts

* fix: model test channel id;fix: quote reader (#4123)

* fix: model test channel id

* fix: quote reader

* fix chat quote reader (#4125)

* perf: model test;perf: sidebar trigger (#4127)

* fix: import dataset step error;perf: ai proxy avatar (#4074)

* perf: pg config params

* perf: ai proxy avatar

* fix: import dataset step error

* feat: data input ux

* perf: app dataset rewite

* perf: model test

* perf: sidebar trigger

* lock

* update nanoid version

* fix: select component ux

* fix: ts

* fix: vitest

* remove test

* fix: prompt toolcall ui (#4139)

* load log error adapt

* fix: prompt toolcall ui

* perf: commercial function tip

* update package

* pref: copy link (#4147)

* fix(i18n): namespace (#4143)

* hiden dataset source (#4152)

* hiden dataset source

* perf: reader

* chore: move all tests into a single folder (#4160)

* fix modal close scroll (#4162)

* fix modal close scroll

* update refresh

* feat: rerank modal select and weight (#4164)

* fix loadInitData refresh (#4169)

* fix

* fix

* form input number default & api dataset max token

* feat: mix search weight (#4170)

* feat: mix search weight

* feat: svg render

* fix: avatar error remove (#4173)

* fix: avatar error remove

* fix: index

* fix: guide

* fix: auth

* update package;fix: input data model ui (#4181)

* update package

* fix: ts

* update config

* update jieba package

* add type sign

* fix: input data ui

* fix: page title refresh (#4186)

* fix: ts

* update jieba package

* fix: page title refresh

* fix: remove member length check when opening invite create modal (#4193)

* add env to check internal ip (#4187)

* fix: ts

* update jieba package

* add env to check internal ip

* package

* fix: jieba

* reset package

* update config

* fix: jieba package

* init shell

* init version

* change team reload

* update jieba package (#4200)

* update jieba package

* package

* update package

* remove invalid code

* action

* package (#4201)

* package

* update package

* remove invalid code

* package

* remove i18n tip (#4202)

* doc (#4205)

* fix: i18n (#4208)

* fix: next config (#4207)

* reset package

* i18n

* update config

* i18n

* remove log

---------

Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
Co-authored-by: gggaaallleee <91131304+gggaaallleee@users.noreply.github.com>
Co-authored-by: shilin <39396378+shilin66@users.noreply.github.com>
Co-authored-by: heheer <heheer@sealos.io>
2025-03-18 14:40:41 +08:00
Hwting
56793114d8 Update docker.md (#4176)
* Update docker.md

Fix a document error

* Update docker.md
2025-03-15 11:49:28 +08:00
176 changed files with 6184 additions and 5929 deletions

View File

@@ -1,9 +1,6 @@
name: Preview FastGPT images
on:
pull_request_target:
paths:
- 'projects/app/**'
- 'packages/**'
workflow_dispatch:
jobs:

View File

@@ -6,4 +6,5 @@ docSite/
*.md
pnpm-lock.yaml
cl100l_base.ts
cl100l_base.ts
dict.json

View File

@@ -17,15 +17,8 @@ usageMatchRegex:
# you can ignore it and use your own matching rules as well
- "[^\\w\\d]t\\(['\"`]({key})['\"`]"
- "[^\\w\\d]commonT\\(['\"`]({key})['\"`]"
# 支持 appT("your.i18n.keys")
- "[^\\w\\d]appT\\(['\"`]({key})['\"`]"
# 支持 datasetT("your.i18n.keys")
- "[^\\w\\d]datasetT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]fileT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]publishT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]workflowT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]userT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]chatT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]i18nT\\(['\"`]({key})['\"`]"
# A RegEx to set a custom scope range. This scope will be used as a prefix when detecting keys

View File

@@ -129,7 +129,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
</a>
## 🌿 第三方生态
- [PPIO 派欧云:一键调用高性价比的开源模型 API 和 GPU 容器](https://ppinfra.com/user/register?invited_by=VITYVU&utm_source=github_fastgpt)
- [AI Proxy国内模型聚合服务](https://sealos.run/aiproxy/?k=fastgpt-github/)
- [SiliconCloud (硅基流动) —— 开源模型在线体验平台](https://cloud.siliconflow.cn/i/TR9Ym0c4)
- [COW 个人微信/企微机器人](https://doc.tryfastgpt.ai/docs/use-cases/external-integration/onwechat/)

View File

@@ -114,15 +114,15 @@ services:
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.0 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.0 # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.9.1 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.1 # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.0 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.0 # 阿里云
image: ghcr.io/labring/fastgpt:v4.9.1 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.1 # 阿里云
ports:
- 3000:3000
networks:
@@ -175,7 +175,8 @@ services:
# AI Proxy
aiproxy:
image: 'ghcr.io/labring/aiproxy:latest'
image: ghcr.io/labring/aiproxy:v0.1.3
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.3 # 阿里云
container_name: aiproxy
restart: unless-stopped
depends_on:

View File

@@ -72,15 +72,15 @@ services:
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.0 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.0 # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.9.1 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.1 # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.0 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.0 # 阿里云
image: ghcr.io/labring/fastgpt:v4.9.1 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.1 # 阿里云
ports:
- 3000:3000
networks:
@@ -132,7 +132,8 @@ services:
# AI Proxy
aiproxy:
image: 'ghcr.io/labring/aiproxy:latest'
image: ghcr.io/labring/aiproxy:v0.1.3
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.3 # 阿里云
container_name: aiproxy
restart: unless-stopped
depends_on:

View File

@@ -53,15 +53,15 @@ services:
wait $$!
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.0 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.0 # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.9.1 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.1 # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.0 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.0 # 阿里云
image: ghcr.io/labring/fastgpt:v4.9.1 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.1 # 阿里云
ports:
- 3000:3000
networks:
@@ -113,7 +113,8 @@ services:
# AI Proxy
aiproxy:
image: 'ghcr.io/labring/aiproxy:latest'
image: ghcr.io/labring/aiproxy:v0.1.3
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.3 # 阿里云
container_name: aiproxy
restart: unless-stopped
depends_on:

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View File

@@ -56,7 +56,7 @@ weight: 707
### zilliz cloud版本
Milvus 的全托管服务,性能优于 Milvus 并提供 SLA点击使用 [Zilliz Cloud](https://zilliz.com.cn/)。
Zilliz Cloud 由 Milvus 原厂打造,是全托管的 SaaS 向量数据库服务,性能优于 Milvus 并提供 SLA点击使用 [Zilliz Cloud](https://zilliz.com.cn/)。
由于向量库使用了 Cloud无需占用本地资源无需太关注。

View File

@@ -0,0 +1,100 @@
---
title: '通过 PPIO LLM API 接入模型'
description: '通过 PPIO LLM API 接入模型'
icon: 'api'
draft: false
toc: true
weight: 747
---
FastGPT 还可以通过 PPIO LLM API 接入模型。
{{% alert context="warning" %}}
以下内容搬运自 [FastGPT 接入 PPIO LLM API](https://ppinfra.com/docs/third-party/fastgpt-use),可能会有更新不及时的情况。
{{% /alert %}}
FastGPT 是一个将 AI 开发、部署和使用全流程简化为可视化操作的平台。它使开发者不需要深入研究算法,
用户也不需要掌握复杂技术,通过一站式服务将人工智能技术变成易于使用的工具。
PPIO 派欧云提供简单易用的 API 接口,让开发者能够轻松调用 DeepSeek 等模型。
- 对开发者无需重构架构3 个接口完成从文本生成到决策推理的全场景接入,像搭积木一样设计 AI 工作流;
- 对生态:自动适配从中小应用到企业级系统的资源需求,让智能随业务自然生长。
下方教程提供完整接入方案(含密钥配置),帮助您快速将 FastGPT 与 PPIO API 连接起来。
## 1. 配置前置条件
(1) 获取 API 接口地址
固定为: `https://api.ppinfra.com/v3/openai/chat/completions`
(2) 获取 【API 密钥】
登录派欧云控制台 [API 秘钥管理](https://www.ppinfra.com/settings/key-management) 页面,点击创建按钮。
注册账号填写邀请码【VOJL20】得 50 代金券
![1](https://static.ppinfra.com/docs/image/llm/BKWqbzI5PoYG6qxwAPxcinQDnob.png)
(3) 生成并保存 【API 密钥】
{{% alert context="warning" %}}
秘钥在服务端是加密存储,请在生成时保存好秘钥;若遗失可以在控制台上删除并创建一个新的秘钥。
{{% /alert %}}
![2](https://static.ppinfra.com/docs/image/llm/OkUwbbWrcoCY2SxwVMIcM2aZnrs.png)
![3](https://static.ppinfra.com/docs/image/llm/GExfbvcosoJhVKxpzKVczlsdn3d.png)
(4) 获取需要使用的模型 ID
deepseek 系列:
- DeepSeek R1deepseek/deepseek-r1/community
- DeepSeek V3deepseek/deepseek-v3/community
其他模型 ID、最大上下文及价格可参考[模型列表](https://ppinfra.com/model-api/pricing)
## 2. 部署最新版 FastGPT 到本地环境
{{% alert context="warning" %}}
请使用 v4.8.22 以上版本,部署参考: https://doc.tryfastgpt.ai/docs/development/intro/
{{% /alert %}}
## 3. 模型配置(下面两种方式二选其一)
1通过 OneAPI 接入模型 PPIO 模型: 参考 OneAPI 使用文档,修改 FastGPT 的环境变量 在 One API 生成令牌后FastGPT 可以通过修改 baseurl 和 key 去请求到 One API再由 One API 去请求不同的模型。修改下面两个环境变量: 务必写上 v1。如果在同一个网络内可改成内网地址。
OPENAI_BASE_URL= http://OneAPI-IP:OneAPI-PORT/v1
下面的 key 是由 One API 提供的令牌 CHAT_API_KEY=sk-UyVQcpQWMU7ChTVl74B562C28e3c46Fe8f16E6D8AeF8736e
- 修改后重启 FastGPT按下图在模型提供商中选择派欧云
![](https://static.ppinfra.com/docs/image/llm/Fvqzb3kTroys5Uxkjlzco7kwnsb.png)
- 测试连通性
以 deepseek 为例,在模型中选择使用 deepseek/deepseek-r1/community点击图中②的位置进行连通性测试出现图中绿色的的成功显示证明连通成功可以进行后续的配置对话了
![](https://static.ppinfra.com/docs/image/llm/FzKGbGsSPoX4Eexobj2cxcaTnib.png)
2不使用 OneAPI 接入 PPIO 模型
按照下图在模型提供商中选择派欧云
![](https://static.ppinfra.com/docs/image/llm/QbcdbPqRsoAmuyx2nlycQWFanrc.png)
- 配置模型 自定义请求地址中输入:`https://api.ppinfra.com/v3/openai/chat/completions`
![](https://static.ppinfra.com/docs/image/llm/ZVyAbDIaxo7ksAxLI3HcexYYnZf.png)
![](https://static.ppinfra.com/docs/image/llm/Ha9YbggkwoQsVdx1Z4Gc9zUSnle.png)
- 测试连通性
![](https://static.ppinfra.com/docs/image/llm/V1f0b89uloab9uxxj7IcKT0rn3e.png)
出现图中绿色的的成功显示证明连通成功,可以进行对话配置
## 4. 配置对话
1新建工作台
![](https://static.ppinfra.com/docs/image/llm/ZaGpbBH6QoVubIx2TsLcwYEInfe.png)
2开始聊天
![](https://static.ppinfra.com/docs/image/llm/HzcTb4gobokVRQxTlU7cD5OunMf.png)
## PPIO 全新福利重磅来袭 🔥
顺利完成教程配置步骤后您将解锁两大权益1. 畅享 PPIO 高速通道与 FastGPT 的效能组合2.立即激活 **「新用户邀请奖励」** ————通过专属邀请码邀好友注册,您与好友可各领 50 元代金券,硬核福利助力 AI 工具效率倍增!
🎁 新手专享立即使用邀请码【VOJL20】完成注册50 元代金券奖励即刻到账!

View File

@@ -7,12 +7,39 @@ toc: true
weight: 799
---
## 更新指南
### 1. 做好数据库备份
### 2. 更新镜像
- 更新 FastGPT 镜像 tag: v4.9.1
- 更新 FastGPT 商业版镜像 tag: v4.9.1
- Sandbox 镜像,可以不更新
- AIProxy 镜像修改为: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.3
### 3. 执行升级脚本
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`{{host}} 替换成**FastGPT 域名**。
```bash
curl --location --request POST 'https://{{host}}/api/admin/initv491' \
--header 'rootkey: {{rootkey}}' \
--header 'Content-Type: application/json'
```
**脚本功能**
重新使用最新的 jieba 分词库进行分词处理。时间较长,可以从日志里查看进度。
## 🚀 新增内容
1. 商业版支持单团队模式,更好的管理内部成员。
2. 知识库分块阅读器。
3. API 知识库支持 PDF 增强解析。
4. 邀请团队成员,改为邀请链接模式。
5. 支持混合检索权重设置。
6. 支持重排模型选择和权重设置,同时调整了知识库搜索权重计算方式,改成 搜索权重 + 重排权重,而不是向量检索权重+全文检索权重+重排权重。
## ⚙️ 优化
@@ -21,6 +48,7 @@ weight: 799
3. 增加依赖包安全版本检测,并升级部分依赖包。
4. 模型测试代码。
5. 优化思考过程解析逻辑:只要配置了模型支持思考,均会解析 <think> 标签,不会因为对话时,关闭思考而不解析。
6. 载入最新 jieba 分词库,增强全文检索分词效果。
## 🐛 修复
@@ -32,3 +60,6 @@ weight: 799
6. 模型渠道测试时,实际未指定渠道测试。
7. 新增自定义模型时,会把默认模型字段也保存,导致默认模型误判。
8. 修复 promp 模式工具调用,未判空思考链,导致 UI 错误展示。
9. 编辑应用信息导致头像丢失。
10. 分享链接标题会被刷新掉。
11. 计算 parentPath 时,存在鉴权失败清空。

View File

@@ -0,0 +1,66 @@
---
title: "邀请链接说明文档"
description: "如何使用邀请链接来邀请团队成员"
icon: "group"
draft: false
toc: true
weight: 451
---
v4.9.1 团队邀请成员将开始使用「邀请链接」的模式,弃用之前输入用户名进行添加的形式。
在版本升级后,原收到邀请还未加入团队的成员,将自动清除邀请。请使用邀请链接重新邀请成员。
## 如何使用
1. **在团队管理页面,管理员可点击「邀请成员」按钮打开邀请成员弹窗**
![](/imgs/guide/team_permissions/invitation_link/image1.png)
2. **在邀请成员弹窗中,点击「创建邀请链接」按钮,创建邀请链接。**
![](/imgs/guide/team_permissions/invitation_link/image2.png)
3. **输入对应内容**
![](/imgs/guide/team_permissions/invitation_link/image3.png)
链接描述:建议将链接描述为使用场景或用途。链接创建后不支持修改噢。
有效期30分钟7天1年
有效人数1人无限制
4. **点击复制链接,并将其发送给想要邀请的人。**
![](/imgs/guide/team_permissions/invitation_link/image4.png)
5. **用户访问链接后,如果未登录/未注册,则先跳转到登录页面进行登录。在登录后将进入团队页面,处理邀请。**
> 邀请链接形如fastgpt.cn/account/team?invitelinkid=xxxx
![](/imgs/guide/team_permissions/invitation_link/image5.png)
点击接受,则用户将加入团队
点击忽略,则关闭弹窗,用户下次访问该邀请链接则还可以选择加入。
## 链接失效和自动清理
### 链接失效原因
手动停用链接
邀请链接到达有效期,自动停用
有效人数为1人的链接已有1人通过邀请链接加入团队。
停用的链接无法访问,也无法再次启用。
### 链接上限
一个用户最多可以同时存在 10 个**有效的**邀请链接。
### 链接自动清理
失效的链接将在 30 天后自动清理。

View File

@@ -12,7 +12,7 @@
"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",
"create:i18n": "node ./scripts/i18n/index.js",
"test": "vitest run --exclude './projects/app/src/test/**'",
"test": "vitest run --exclude 'test/cases/spec'",
"test:all": "vitest run",
"test:workflow": "vitest run workflow"
},
@@ -20,9 +20,9 @@
"@chakra-ui/cli": "^2.4.1",
"@vitest/coverage-v8": "^3.0.2",
"husky": "^8.0.3",
"i18next": "23.11.5",
"i18next": "23.16.8",
"lint-staged": "^13.3.0",
"next-i18next": "15.3.0",
"next-i18next": "15.4.2",
"prettier": "3.2.4",
"react-i18next": "14.1.2",
"vitest": "^3.0.2",

View File

@@ -1,3 +1,8 @@
export type GetPathProps = {
sourceId?: ParentIdType;
type: 'current' | 'parent';
};
export type ParentTreePathItemType = {
parentId: string;
parentName: string;

View File

@@ -6,7 +6,7 @@ import type {
EmbeddingModelItemType,
AudioSpeechModels,
STTModelType,
ReRankModelItemType
RerankModelItemType
} from '../../../core/ai/model.d';
import { SubTypeEnum } from '../../../support/wallet/sub/constants';
@@ -35,7 +35,7 @@ export type FastGPTConfigFileType = {
// Abandon
llmModels?: ChatModelItemType[];
vectorModels?: EmbeddingModelItemType[];
reRankModels?: ReRankModelItemType[];
reRankModels?: RerankModelItemType[];
audioSpeechModels?: TTSModelType[];
whisperModel?: STTModelType;
};

View File

@@ -72,7 +72,7 @@ export type EmbeddingModelItemType = PriceType &
queryConfig?: Record<string, any>; // Custom parameters for query
};
export type ReRankModelItemType = PriceType &
export type RerankModelItemType = PriceType &
BaseModelItemType & {
type: ModelTypeEnum.rerank;
};

View File

@@ -71,6 +71,20 @@ export type AppDetailType = AppSchema & {
permission: AppPermission;
};
export type AppDatasetSearchParamsType = {
searchMode: `${DatasetSearchModeEnum}`;
limit?: number; // limit max tokens
similarity?: number;
embeddingWeight?: number; // embedding weight, fullText weight = 1 - embeddingWeight
usingReRank?: boolean;
rerankModel?: string;
rerankWeight?: number;
datasetSearchUsingExtensionQuery?: boolean;
datasetSearchExtensionModel?: string;
datasetSearchExtensionBg?: string;
};
export type AppSimpleEditFormType = {
// templateId: string;
aiSettings: {
@@ -88,14 +102,7 @@ export type AppSimpleEditFormType = {
};
dataset: {
datasets: SelectedDatasetType;
searchMode: `${DatasetSearchModeEnum}`;
similarity?: number;
limit?: number;
usingReRank?: boolean;
datasetSearchUsingExtensionQuery?: boolean;
datasetSearchExtensionModel?: string;
datasetSearchExtensionBg?: string;
};
} & AppDatasetSearchParamsType;
selectedTools: FlowNodeTemplateType[];
chatConfig: AppChatConfigType;
};

View File

@@ -24,9 +24,11 @@ export const getDefaultAppForm = (): AppSimpleEditFormType => {
dataset: {
datasets: [],
similarity: 0.4,
limit: 1500,
limit: 3000,
searchMode: DatasetSearchModeEnum.embedding,
usingReRank: false,
rerankModel: '',
rerankWeight: 0.5,
datasetSearchUsingExtensionQuery: true,
datasetSearchExtensionBg: ''
},
@@ -106,10 +108,24 @@ export const appWorkflow2Form = ({
defaultAppForm.dataset.searchMode =
findInputValueByKey(node.inputs, NodeInputKeyEnum.datasetSearchMode) ||
DatasetSearchModeEnum.embedding;
defaultAppForm.dataset.embeddingWeight = findInputValueByKey(
node.inputs,
NodeInputKeyEnum.datasetSearchEmbeddingWeight
);
// Rerank
defaultAppForm.dataset.usingReRank = !!findInputValueByKey(
node.inputs,
NodeInputKeyEnum.datasetSearchUsingReRank
);
defaultAppForm.dataset.rerankModel = findInputValueByKey(
node.inputs,
NodeInputKeyEnum.datasetSearchRerankModel
);
defaultAppForm.dataset.rerankWeight = findInputValueByKey(
node.inputs,
NodeInputKeyEnum.datasetSearchRerankWeight
);
// Query extension
defaultAppForm.dataset.datasetSearchUsingExtensionQuery = findInputValueByKey(
node.inputs,
NodeInputKeyEnum.datasetSearchUsingExtensionQuery

View File

@@ -185,7 +185,7 @@ export enum SearchScoreTypeEnum {
}
export const SearchScoreTypeMap = {
[SearchScoreTypeEnum.embedding]: {
label: i18nT('common:core.dataset.search.score.embedding'),
label: i18nT('common:core.dataset.search.mode.embedding'),
desc: i18nT('common:core.dataset.search.score.embedding desc'),
showScore: true
},

View File

@@ -154,7 +154,12 @@ export enum NodeInputKeyEnum {
datasetSimilarity = 'similarity',
datasetMaxTokens = 'limit',
datasetSearchMode = 'searchMode',
datasetSearchEmbeddingWeight = 'embeddingWeight',
datasetSearchUsingReRank = 'usingReRank',
datasetSearchRerankWeight = 'rerankWeight',
datasetSearchRerankModel = 'rerankModel',
datasetSearchUsingExtensionQuery = 'datasetSearchUsingExtensionQuery',
datasetSearchExtensionModel = 'datasetSearchExtensionModel',
datasetSearchExtensionBg = 'datasetSearchExtensionBg',

View File

@@ -133,6 +133,9 @@ export type DispatchNodeResponseType = {
similarity?: number;
limit?: number;
searchMode?: `${DatasetSearchModeEnum}`;
embeddingWeight?: number;
rerankModel?: string;
rerankWeight?: number;
searchUsingReRank?: boolean;
queryExtensionResult?: {
model: string;

View File

@@ -64,6 +64,14 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
valueType: WorkflowIOValueTypeEnum.string,
value: DatasetSearchModeEnum.embedding
},
{
key: NodeInputKeyEnum.datasetSearchEmbeddingWeight,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.number,
value: 0.5
},
// Rerank
{
key: NodeInputKeyEnum.datasetSearchUsingReRank,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
@@ -71,6 +79,20 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
valueType: WorkflowIOValueTypeEnum.boolean,
value: false
},
{
key: NodeInputKeyEnum.datasetSearchRerankModel,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.string
},
{
key: NodeInputKeyEnum.datasetSearchRerankWeight,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.number,
value: 0.5
},
// Query Extension
{
key: NodeInputKeyEnum.datasetSearchUsingExtensionQuery,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
@@ -91,6 +113,7 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
valueType: WorkflowIOValueTypeEnum.string,
value: ''
},
{
key: NodeInputKeyEnum.authTmbId,
renderTypeList: [FlowNodeInputTypeEnum.hidden],

View File

@@ -10,7 +10,7 @@
"js-yaml": "^4.1.0",
"jschardet": "3.1.1",
"nanoid": "^5.1.3",
"next": "14.2.21",
"next": "14.2.24",
"openai": "4.61.0",
"openapi-types": "^12.1.3",
"json5": "^2.2.3",

View File

@@ -76,7 +76,7 @@ export const refreshSourceAvatar = async (
const newId = getIdFromPath(path);
const oldId = getIdFromPath(oldPath);
if (!newId) return;
if (!newId || newId === oldId) return;
await MongoImage.updateOne({ _id: newId }, { $unset: { expiredTime: 1 } }, { session });

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,13 @@
import { cut } from '@node-rs/jieba';
import { Jieba } from '@node-rs/jieba';
let jieba: Jieba | undefined;
(async () => {
const dictData = await import('./dict.json');
// @ts-ignore
const dictBuffer = Buffer.from(dictData.dict?.replace(/\\n/g, '\n'), 'utf-8');
jieba = Jieba.withDict(dictBuffer);
})();
const stopWords = new Set([
'--',
@@ -1509,8 +1518,10 @@ const stopWords = new Set([
]
]);
export function jiebaSplit({ text }: { text: string }) {
const tokens = cut(text, true);
export async function jiebaSplit({ text }: { text: string }) {
text = text.replace(/[#*`_~>[\](){}|]/g, '').replace(/\S*https?\S*/gi, '');
const tokens = (await jieba!.cutAsync(text, true)) as string[];
return (
tokens

View File

@@ -30,6 +30,8 @@ export const isInternalAddress = (url: string): boolean => {
return true;
}
if (process.env.CHECK_INTERNAL_IP !== 'true') return false;
// For IP addresses, check if they are internal
const ipv4Pattern = /^(\d{1,3}\.){3}\d{1,3}$/;
if (!ipv4Pattern.test(hostname)) {

View File

@@ -8,7 +8,7 @@ import {
EmbeddingModelItemType,
TTSModelType,
STTModelType,
ReRankModelItemType
RerankModelItemType
} from '@fastgpt/global/core/ai/model.d';
import { debounce } from 'lodash';
import {
@@ -94,7 +94,7 @@ export const loadSystemModels = async (init = false) => {
global.embeddingModelMap = new Map<string, EmbeddingModelItemType>();
global.ttsModelMap = new Map<string, TTSModelType>();
global.sttModelMap = new Map<string, STTModelType>();
global.reRankModelMap = new Map<string, ReRankModelItemType>();
global.reRankModelMap = new Map<string, RerankModelItemType>();
// @ts-ignore
global.systemDefaultModel = {};

View File

@@ -38,7 +38,7 @@ export function getSTTModel(model?: string) {
}
export const getDefaultRerankModel = () => global?.systemDefaultModel.rerank!;
export function getReRankModel(model?: string) {
export function getRerankModel(model?: string) {
if (!model) return getDefaultRerankModel();
return global.reRankModelMap.get(model) || getDefaultRerankModel();
}

View File

@@ -2,7 +2,7 @@ import { addLog } from '../../../common/system/log';
import { POST } from '../../../common/api/serverRequest';
import { getDefaultRerankModel } from '../model';
import { getAxiosConfig } from '../config';
import { ReRankModelItemType } from '@fastgpt/global/core/ai/model.d';
import { RerankModelItemType } from '@fastgpt/global/core/ai/model.d';
type PostReRankResponse = {
id: string;
@@ -19,7 +19,7 @@ export function reRankRecall({
documents,
headers
}: {
model?: ReRankModelItemType;
model?: RerankModelItemType;
query: string;
documents: { id: string; text: string }[];
headers?: Record<string, string>;

View File

@@ -1,7 +1,7 @@
import { ModelTypeEnum } from '@fastgpt/global/core/ai/model';
import {
STTModelType,
ReRankModelItemType,
RerankModelItemType,
TTSModelType,
EmbeddingModelItemType,
LLMModelItemType
@@ -18,7 +18,7 @@ export type SystemModelItemType =
| EmbeddingModelItemType
| TTSModelType
| STTModelType
| ReRankModelItemType;
| RerankModelItemType;
export type SystemDefaultModelType = {
[ModelTypeEnum.llm]?: LLMModelItemType;
@@ -28,7 +28,7 @@ export type SystemDefaultModelType = {
[ModelTypeEnum.embedding]?: EmbeddingModelItemType;
[ModelTypeEnum.tts]?: TTSModelType;
[ModelTypeEnum.stt]?: STTModelType;
[ModelTypeEnum.rerank]?: ReRankModelItemType;
[ModelTypeEnum.rerank]?: RerankModelItemType;
};
declare global {
@@ -38,7 +38,7 @@ declare global {
var embeddingModelMap: Map<string, EmbeddingModelItemType>;
var ttsModelMap: Map<string, TTSModelType>;
var sttModelMap: Map<string, STTModelType>;
var reRankModelMap: Map<string, ReRankModelItemType>;
var reRankModelMap: Map<string, RerankModelItemType>;
var systemActiveModelList: SystemModelItemType[];
var systemDefaultModel: SystemDefaultModelType;

View File

@@ -41,7 +41,7 @@ try {
}
);
DatasetDataTextSchema.index({ teamId: 1, datasetId: 1, collectionId: 1 });
DatasetDataTextSchema.index({ dataId: 1 }, { unique: true });
DatasetDataTextSchema.index({ dataId: 'hashed' });
} catch (error) {
console.log(error);
}

View File

@@ -86,7 +86,8 @@ const DatasetDataSchema = new Schema({
// Abandon
fullTextToken: String,
initFullText: Boolean
initFullText: Boolean,
initJieba: Boolean
});
try {
@@ -103,6 +104,9 @@ try {
DatasetDataSchema.index({ updateTime: 1 });
// rebuild data
DatasetDataSchema.index({ rebuilding: 1, teamId: 1, datasetId: 1 });
// 为查询 initJieba 字段不存在的数据添加索引
DatasetDataSchema.index({ initJieba: 1, updateTime: 1 });
} catch (error) {
console.log(error);
}

View File

@@ -16,7 +16,7 @@ import { reRankRecall } from '../../../core/ai/rerank';
import { countPromptTokens } from '../../../common/string/tiktoken/index';
import { datasetSearchResultConcat } from '@fastgpt/global/core/dataset/search/utils';
import { hashStr } from '@fastgpt/global/common/string/tools';
import { jiebaSplit } from '../../../common/string/jieba';
import { jiebaSplit } from '../../../common/string/jieba/index';
import { getCollectionSourceData } from '@fastgpt/global/core/dataset/collection/utils';
import { Types } from '../../../common/mongo';
import json5 from 'json5';
@@ -27,6 +27,7 @@ import { ChatItemType } from '@fastgpt/global/core/chat/type';
import { POST } from '../../../common/api/plusRequest';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { datasetSearchQueryExtension } from './utils';
import type { RerankModelItemType } from '@fastgpt/global/core/ai/model.d';
export type SearchDatasetDataProps = {
histories: ChatItemType[];
@@ -39,7 +40,11 @@ export type SearchDatasetDataProps = {
[NodeInputKeyEnum.datasetSimilarity]?: number; // min distance
[NodeInputKeyEnum.datasetMaxTokens]: number; // max Token limit
[NodeInputKeyEnum.datasetSearchMode]?: `${DatasetSearchModeEnum}`;
[NodeInputKeyEnum.datasetSearchEmbeddingWeight]?: number;
[NodeInputKeyEnum.datasetSearchUsingReRank]?: boolean;
[NodeInputKeyEnum.datasetSearchRerankModel]?: RerankModelItemType;
[NodeInputKeyEnum.datasetSearchRerankWeight]?: number;
/*
{
@@ -75,13 +80,16 @@ export type SearchDatasetDataResponse = {
};
export const datasetDataReRank = async ({
rerankModel,
data,
query
}: {
rerankModel?: RerankModelItemType;
data: SearchDataResponseItemType[];
query: string;
}): Promise<SearchDataResponseItemType[]> => {
const results = await reRankRecall({
model: rerankModel,
query,
documents: data.map((item) => ({
id: item.id,
@@ -154,7 +162,10 @@ export async function searchDatasetData(
similarity = 0,
limit: maxTokens,
searchMode = DatasetSearchModeEnum.embedding,
embeddingWeight = 0.5,
usingReRank = false,
rerankModel,
rerankWeight = 0.5,
datasetIds = [],
collectionFilterMatch
} = props;
@@ -526,7 +537,7 @@ export async function searchDatasetData(
$match: {
teamId: new Types.ObjectId(teamId),
datasetId: new Types.ObjectId(id),
$text: { $search: jiebaSplit({ text: query }) },
$text: { $search: await jiebaSplit({ text: query }) },
...(filterCollectionIdList
? {
collectionId: {
@@ -711,6 +722,7 @@ export async function searchDatasetData(
});
try {
return await datasetDataReRank({
rerankModel,
query: reRankQuery,
data: filterSameDataResults
});
@@ -721,11 +733,26 @@ export async function searchDatasetData(
})();
// embedding recall and fullText recall rrf concat
const rrfConcatResults = datasetSearchResultConcat([
{ k: 60, list: embeddingRecallResults },
{ k: 60, list: fullTextRecallResults },
{ k: 58, list: reRankResults }
const baseK = 120;
const embK = Math.round(baseK * (1 - embeddingWeight)); // 搜索结果的 k 值
const fullTextK = Math.round(baseK * embeddingWeight); // rerank 结果的 k 值
const rrfSearchResult = datasetSearchResultConcat([
{ k: embK, list: embeddingRecallResults },
{ k: fullTextK, list: fullTextRecallResults }
]);
const rrfConcatResults = (() => {
if (reRankResults.length === 0) return rrfSearchResult;
if (rerankWeight === 1) return reRankResults;
const searchK = Math.round(baseK * rerankWeight); // 搜索结果的 k 值
const rerankK = Math.round(baseK * (1 - rerankWeight)); // rerank 结果的 k 值
return datasetSearchResultConcat([
{ k: searchK, list: rrfSearchResult },
{ k: rerankK, list: reRankResults }
]);
})();
// remove same q and a data
set = new Set<string>();

View File

@@ -6,7 +6,7 @@ import { formatModelChars2Points } from '../../../../support/wallet/usage/utils'
import type { SelectedDatasetType } from '@fastgpt/global/core/workflow/api.d';
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
import { getEmbeddingModel } from '../../../ai/model';
import { getEmbeddingModel, getRerankModel } from '../../../ai/model';
import { deepRagSearch, defaultSearchDatasetData } from '../../../dataset/search/controller';
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
@@ -22,9 +22,14 @@ type DatasetSearchProps = ModuleDispatchProps<{
[NodeInputKeyEnum.datasetSelectList]: SelectedDatasetType;
[NodeInputKeyEnum.datasetSimilarity]: number;
[NodeInputKeyEnum.datasetMaxTokens]: number;
[NodeInputKeyEnum.datasetSearchMode]: `${DatasetSearchModeEnum}`;
[NodeInputKeyEnum.userChatInput]?: string;
[NodeInputKeyEnum.datasetSearchMode]: `${DatasetSearchModeEnum}`;
[NodeInputKeyEnum.datasetSearchEmbeddingWeight]?: number;
[NodeInputKeyEnum.datasetSearchUsingReRank]: boolean;
[NodeInputKeyEnum.datasetSearchRerankModel]?: string;
[NodeInputKeyEnum.datasetSearchRerankWeight]?: number;
[NodeInputKeyEnum.collectionFilterMatch]: string;
[NodeInputKeyEnum.authTmbId]?: boolean;
@@ -53,11 +58,14 @@ export async function dispatchDatasetSearch(
datasets = [],
similarity,
limit = 1500,
usingReRank,
searchMode,
userChatInput = '',
authTmbId = false,
collectionFilterMatch,
searchMode,
embeddingWeight,
usingReRank,
rerankModel,
rerankWeight,
datasetSearchUsingExtensionQuery,
datasetSearchExtensionModel,
@@ -122,7 +130,10 @@ export async function dispatchDatasetSearch(
limit,
datasetIds,
searchMode,
embeddingWeight,
usingReRank: usingReRank && (await checkTeamReRankPermission(teamId)),
rerankModel: getRerankModel(rerankModel),
rerankWeight,
collectionFilterMatch
};
const {
@@ -219,6 +230,9 @@ export async function dispatchDatasetSearch(
similarity: usingSimilarityFilter ? similarity : undefined,
limit,
searchMode,
embeddingWeight: searchMode === DatasetSearchModeEnum.mixedRecall ? embeddingWeight : undefined,
rerankModel: usingReRank ? getRerankModel(rerankModel)?.name : undefined,
rerankWeight: usingReRank ? rerankWeight : undefined,
searchUsingReRank: searchUsingReRank,
quoteList: searchRes,
queryExtensionResult,

View File

@@ -3,7 +3,7 @@
"version": "1.0.0",
"dependencies": {
"@fastgpt/global": "workspace:*",
"@node-rs/jieba": "1.10.0",
"@node-rs/jieba": "2.0.1",
"@xmldom/xmldom": "^0.8.10",
"@zilliz/milvus2-sdk-node": "2.4.2",
"axios": "^1.8.2",
@@ -26,7 +26,7 @@
"mammoth": "^1.6.0",
"mongoose": "^8.10.1",
"multer": "1.4.5-lts.1",
"next": "14.2.21",
"next": "14.2.24",
"nextjs-cors": "^2.2.0",
"node-cron": "^3.0.3",
"node-xlsx": "^0.24.0",

View File

@@ -1,7 +1,7 @@
import { FastGPTFeConfigsType, SystemEnvType } from '@fastgpt/global/common/system/types';
import {
TTSModelType,
ReRankModelItemType,
RerankModelItemType,
STTModelType,
EmbeddingModelItemType,
LLMModelItemType

View File

@@ -212,7 +212,9 @@ export const iconPaths = {
'core/dataset/manualCollection': () => import('./icons/core/dataset/manualCollection.svg'),
'core/dataset/mixedRecall': () => import('./icons/core/dataset/mixedRecall.svg'),
'core/dataset/modeEmbedding': () => import('./icons/core/dataset/modeEmbedding.svg'),
'core/dataset/questionExtension': () => import('./icons/core/dataset/questionExtension.svg'),
'core/dataset/rerank': () => import('./icons/core/dataset/rerank.svg'),
'core/dataset/searchfilter': () => import('./icons/core/dataset/searchfilter.svg'),
'core/dataset/splitLight': () => import('./icons/core/dataset/splitLight.svg'),
'core/dataset/tableCollection': () => import('./icons/core/dataset/tableCollection.svg'),
'core/dataset/tag': () => import('./icons/core/dataset/tag.svg'),
@@ -427,6 +429,7 @@ export const iconPaths = {
'price/bg': () => import('./icons/price/bg.svg'),
'price/right': () => import('./icons/price/right.svg'),
save: () => import('./icons/save.svg'),
sliderTag: () => import('./icons/sliderTag.svg'),
stop: () => import('./icons/stop.svg'),
'support/account/laf': () => import('./icons/support/account/laf.svg'),
'support/account/loginoutLight': () => import('./icons/support/account/loginoutLight.svg'),

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 16" >
<path d="M11.3317 4.63043L12.3128 5.61147L8.98815 8.9361L6.74675 6.6947C6.68372 6.63154 6.60886 6.58144 6.52644 6.54725C6.44402 6.51306 6.35567 6.49546 6.26645 6.49546C6.17722 6.49546 6.08887 6.51306 6.00646 6.54725C5.92404 6.58144 5.84918 6.63154 5.78615 6.6947L1.69849 10.7892C1.63542 10.8522 1.58538 10.9271 1.55125 11.0095C1.51711 11.0919 1.49954 11.1803 1.49954 11.2695C1.49954 11.3587 1.51711 11.447 1.55125 11.5294C1.58538 11.6118 1.63542 11.6867 1.69849 11.7498C1.76156 11.8128 1.83644 11.8629 1.91885 11.897C2.00126 11.9312 2.08959 11.9487 2.17879 11.9487C2.26799 11.9487 2.35632 11.9312 2.43873 11.897C2.52114 11.8629 2.59601 11.8128 2.65909 11.7498L6.26304 8.13901L8.50444 10.3804C8.77014 10.6461 9.19934 10.6461 9.46504 10.3804L13.2734 6.57888L14.2544 7.55992C14.302 7.6066 14.3623 7.63828 14.4277 7.65104C14.4932 7.66379 14.5609 7.65706 14.6226 7.63167C14.6842 7.60628 14.7371 7.56336 14.7746 7.50822C14.8121 7.45308 14.8326 7.38815 14.8335 7.32147V4.39198C14.8354 4.34733 14.8281 4.30276 14.812 4.26104C14.796 4.21931 14.7716 4.18132 14.7403 4.1494C14.709 4.11747 14.6716 4.0923 14.6302 4.07544C14.5888 4.05858 14.5444 4.05038 14.4997 4.05135H11.577C11.5097 4.05095 11.4439 4.07047 11.3877 4.10745C11.3315 4.14443 11.2876 4.1972 11.2613 4.25913C11.2351 4.32106 11.2278 4.38937 11.2403 4.45544C11.2529 4.52152 11.2847 4.5824 11.3317 4.63043Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 16" >
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.8911 3.61032C15.0171 5.17598 14.8761 7.37117 13.4681 8.77915L12.6427 9.60456C12.3733 9.87396 11.9365 9.87396 11.6671 9.60456L6.97652 4.91394C6.70711 4.64454 6.70711 4.20775 6.97652 3.93834L7.80192 3.11294C9.20207 1.71279 11.3807 1.56556 12.9446 2.67127L13.8718 1.74404C14.1322 1.48369 14.5543 1.48369 14.8146 1.74404C15.075 2.00439 15.075 2.4265 14.8146 2.68685L13.8911 3.61032ZM2.18142 13.4425C1.92107 13.7029 1.92107 14.125 2.18142 14.3853C2.44177 14.6457 2.86388 14.6457 3.12423 14.3853L4.04283 13.4667C5.6079 14.5859 7.79751 14.4428 9.20286 13.0374L10.0283 12.212C10.2977 11.9426 10.2977 11.5058 10.0283 11.2364L9.44649 10.6546L9.94334 10.1578C10.2037 9.89744 10.2037 9.47533 9.94334 9.21498C9.68299 8.95463 9.26088 8.95463 9.00053 9.21498L8.50368 9.71183L6.87902 8.08717L7.37233 7.59386C7.63268 7.33351 7.63268 6.9114 7.37233 6.65105C7.11198 6.3907 6.68987 6.3907 6.42952 6.65105L5.93621 7.14436L5.33765 6.5458C5.06824 6.27639 4.63145 6.27639 4.36205 6.5458L3.53665 7.3712C2.13385 8.774 1.98872 10.9582 3.10124 12.5227L2.18142 13.4425ZM4.84985 7.94362L4.47946 8.31401C3.43548 9.35799 3.43548 11.0506 4.47946 12.0946C5.52344 13.1386 7.21607 13.1386 8.26005 12.0946L8.63044 11.7242L4.84985 7.94362ZM12.5253 7.83635L12.1549 8.20674L8.37433 4.42614L8.74473 4.05575C9.78871 3.01177 11.4813 3.01177 12.5253 4.05575C13.5693 5.09973 13.5693 6.79236 12.5253 7.83635Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 11" >
<path d="M5.04123 0.144501L9.47821 4.82132C9.83075 5.19292 10.0273 5.68562 10.0273 6.19784V8.65565C10.0273 9.76022 9.13185 10.6557 8.02728 10.6557H2.05518C0.950606 10.6557 0.0551758 9.76022 0.0551758 8.65565V6.19785C0.0551758 5.68562 0.251705 5.19292 0.604247 4.82132L5.04123 0.144501Z" fill="#3370FF"/>
</svg>

After

Width:  |  Height:  |  Size: 375 B

View File

@@ -53,6 +53,7 @@ const MyModal = ({
allowPinchZoom
scrollBehavior={'inside'}
closeOnOverlayClick={closeOnOverlayClick}
returnFocusOnClose={false}
>
<ModalOverlay />
<ModalContent

View File

@@ -73,6 +73,11 @@ const LightRowTabs = <ValueType = string,>({
color: activeColor
}}
fontWeight={'medium'}
onClick={() => {
if (value === item.value) return;
onChange(item.value);
}}
{...inlineStyles}
{...(value === item.value
? {
color: activeColor,
@@ -82,11 +87,6 @@ const LightRowTabs = <ValueType = string,>({
: {
cursor: 'pointer'
})}
onClick={() => {
if (value === item.value) return;
onChange(item.value);
}}
{...inlineStyles}
>
{item.icon && (
<>

View File

@@ -44,7 +44,7 @@ export const useI18nLng = () => {
await i18n?.changeLanguage?.(lang);
if (!i18n.hasResourceBundle(lang, 'common') && prevLang !== lang) {
if (!i18n?.hasResourceBundle?.(lang, 'common') && prevLang !== lang) {
window?.location?.reload?.();
}
};

View File

@@ -1,4 +1,4 @@
import { useEffect, useRef, useState, ReactNode, useCallback } from 'react';
import { useEffect, useRef, useState, ReactNode } from 'react';
import { LinkedListResponse, LinkedPaginationProps } from '../common/fetch/type';
import { Box, BoxProps } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
@@ -34,13 +34,13 @@ export function useLinkedScroll<
bottom: null as { _id: string; index: number } | null
});
const containerRef = useRef<HTMLDivElement>(null);
const itemRefs = useRef<(HTMLElement | null)[]>([]);
const itemRefs = useRef<Map<string, HTMLElement | null>>(new Map());
const scrollToItem = async (id: string, retry = 3) => {
const itemIndex = dataList.findIndex((item) => item._id === id);
if (itemIndex === -1) return;
const element = itemRefs.current[itemIndex];
const element = itemRefs.current.get(id);
if (!element || !containerRef.current) {
if (retry > 0) {
@@ -64,11 +64,12 @@ export function useLinkedScroll<
let scroolSign = useRef(false);
const { runAsync: loadInitData } = useRequest2(
async (scrollWhenFinish = true) => {
async ({ scrollWhenFinish, refresh } = { scrollWhenFinish: true, refresh: false }) => {
console.log('loadInitData', params);
if (!currentData || isLoading) return;
const item = dataList.find((item) => item._id === currentData.id);
if (item) {
if (item && !refresh) {
scrollToItem(item._id);
return;
}

View File

@@ -82,7 +82,7 @@
"logs_chat_user": "user",
"logs_empty": "No logs yet~",
"logs_export_confirm_tip": "There are a total of {{total}} dialogue records, confirm the export?",
"logs_export_title": "Time, source, user, title, total number of messages, user feedback, custom feedback, number of labeled answers, conversation details",
"logs_export_title": "Time, source, user, contact, title, total number of messages, user good feedback, user bad feedback, custom feedback, labeled answers, conversation details",
"logs_message_total": "Total Messages",
"logs_source": "source",
"logs_title": "Title",

View File

@@ -49,6 +49,7 @@
"response.child total points": "Sub-workflow point consumption",
"response.dataset_concat_length": "Combined total",
"response.node_inputs": "Node Inputs",
"response_hybrid_weight": "Embedding : Full text = {{emb}} : {{text}}",
"select": "Select",
"select_file": "Upload File",
"select_file_img": "Upload file / image",

View File

@@ -107,6 +107,7 @@
"code_error.user_error.balance_not_enough": "Insufficient Account Balance",
"code_error.user_error.bin_visitor_guest": "You Are Currently a Guest, Unauthorized to Operate",
"code_error.user_error.un_auth_user": "User Not Found",
"commercial_function_tip": "Please Upgrade to the Commercial Version to Use This Feature: https://doc.fastgpt.cn/docs/commercial/intro/",
"common.Action": "Action",
"common.Add": "Add",
"common.Add New": "Add New",
@@ -256,7 +257,6 @@
"common.submit_success": "Submitted Successfully",
"common.submitted": "Submitted",
"common.support": "Support",
"commercial_function_tip": "Please Upgrade to the Commercial Version to Use This Feature: https://doc.fastgpt.cn/docs/commercial/intro/",
"common.system.Help Chatbot": "Help Chatbot",
"common.system.Use Helper": "Use Helper",
"common.ui.textarea.Magnifying": "Magnifying",
@@ -627,7 +627,7 @@
"core.dataset.search.score.reRank desc": "Calculate the relevance between sentences using the re-rank model, ranging from 0 to 1.",
"core.dataset.search.score.rrf": "Comprehensive Ranking",
"core.dataset.search.score.rrf desc": "Merge multiple search results using the reciprocal rank fusion method.",
"core.dataset.search.search mode": "Search Mode",
"core.dataset.search.search mode": "Search Method",
"core.dataset.status.active": "Ready",
"core.dataset.status.syncing": "Syncing",
"core.dataset.test.Batch test": "Batch Test",
@@ -1025,6 +1025,7 @@
"question_feedback": "Work order",
"read_quote": "View citations",
"required": "Required",
"rerank_weight": "Rearrange weights",
"resume_failed": "Resume Failed",
"select_reference_variable": "Select Reference Variable",
"share_link": "Share Link",

View File

@@ -82,7 +82,7 @@
"logs_chat_user": "使用者",
"logs_empty": "还没有日志噢~",
"logs_export_confirm_tip": "当前共 {{total}} 条对话记录,确认导出?",
"logs_export_title": "时间,来源,使用者,标题,消息总数,用户反馈,自定义反馈,标注答案数量,对话详情",
"logs_export_title": "时间,来源,使用者,联系方式,标题,消息总数,用户赞同反馈,用户反对反馈,自定义反馈,标注答案,对话详情",
"logs_message_total": "消息总数",
"logs_source": "来源",
"logs_title": "标题",

View File

@@ -49,6 +49,7 @@
"response.child total points": "子工作流积分消耗",
"response.dataset_concat_length": "合并后总数",
"response.node_inputs": "节点输入",
"response_hybrid_weight": "语义检索 : 全文检索 = {{emb}} : {{text}}",
"select": "选择",
"select_file": "上传文件",
"select_file_img": "上传文件/图片",

View File

@@ -111,6 +111,7 @@
"code_error.user_error.balance_not_enough": "账号余额不足~",
"code_error.user_error.bin_visitor_guest": "您当前身份为游客,无权操作",
"code_error.user_error.un_auth_user": "找不到该用户",
"commercial_function_tip": "请升级商业版后使用该功能https://doc.fastgpt.cn/docs/commercial/intro/",
"common.Action": "操作",
"common.Add": "添加",
"common.Add New": "新增",
@@ -260,7 +261,6 @@
"common.submit_success": "提交成功",
"common.submitted": "已提交",
"common.support": "支持",
"commercial_function_tip": "请升级商业版后使用该功能https://doc.fastgpt.cn/docs/commercial/intro/",
"common.system.Help Chatbot": "机器人助手",
"common.system.Use Helper": "使用帮助",
"common.ui.textarea.Magnifying": "放大",
@@ -623,7 +623,6 @@
"core.dataset.search.mode.fullTextRecall desc": "使用传统的全文检索,适合查找一些关键词和主谓语特殊的数据",
"core.dataset.search.mode.mixedRecall": "混合检索",
"core.dataset.search.mode.mixedRecall desc": "使用向量检索与全文检索的综合结果返回,使用 RRF 算法进行排序。",
"core.dataset.search.score.embedding": "语义检索",
"core.dataset.search.score.embedding desc": "通过计算向量之间的距离获取得分,范围为 0~1。",
"core.dataset.search.score.fullText": "全文检索",
"core.dataset.search.score.fullText desc": "计算相同关键词的得分,范围为 0~无穷。",
@@ -631,7 +630,7 @@
"core.dataset.search.score.reRank desc": "通过 Rerank 模型计算句子之间的关联度,范围为 0~1。",
"core.dataset.search.score.rrf": "综合排名",
"core.dataset.search.score.rrf desc": "通过倒排计算的方式,合并多个检索结果。",
"core.dataset.search.search mode": "搜索式",
"core.dataset.search.search mode": "搜索式",
"core.dataset.status.active": "已就绪",
"core.dataset.status.syncing": "同步中",
"core.dataset.test.Batch test": "批量测试",
@@ -1029,6 +1028,7 @@
"question_feedback": "工单咨询",
"read_quote": "查看引用",
"required": "必须",
"rerank_weight": "重排权重",
"resume_failed": "恢复失败",
"select_reference_variable": "选择引用变量",
"share_link": "分享链接",

View File

@@ -82,7 +82,7 @@
"logs_chat_user": "使用者",
"logs_empty": "還沒有紀錄喔~",
"logs_export_confirm_tip": "當前共 {{total}} 條對話記錄,確認導出?",
"logs_export_title": "時間,來源,使用者,標題,消息總數,用戶反饋,自定義反饋,標註答案數量,對話詳情",
"logs_export_title": "時間,來源,使用者,聯繫方式,標題,消息總數,用戶贊同反饋,用戶反對反饋,自定義反饋,標註答案,對話詳情",
"logs_message_total": "訊息總數",
"logs_source": "来源",
"logs_title": "標題",

View File

@@ -48,6 +48,7 @@
"response.child total points": "子工作流程點數消耗",
"response.dataset_concat_length": "合併總數",
"response.node_inputs": "節點輸入",
"response_hybrid_weight": "語義檢索 : 全文檢索 = {{emb}} : {{text}}",
"select": "選取",
"select_file": "上傳檔案",
"select_file_img": "上傳檔案 / 圖片",

View File

@@ -106,6 +106,7 @@
"code_error.user_error.balance_not_enough": "帳戶餘額不足",
"code_error.user_error.bin_visitor_guest": "您目前身份為訪客,無權操作",
"code_error.user_error.un_auth_user": "找不到此使用者",
"commercial_function_tip": "請升級為商業版後使用此功能https://doc.fastgpt.cn/docs/commercial/intro/",
"common.Action": "操作",
"common.Add": "新增",
"common.Add New": "新增",
@@ -255,7 +256,6 @@
"common.submit_success": "送出成功",
"common.submitted": "已送出",
"common.support": "支援",
"commercial_function_tip": "請升級為商業版後使用此功能https://doc.fastgpt.cn/docs/commercial/intro/",
"common.system.Help Chatbot": "機器人助手",
"common.system.Use Helper": "使用說明",
"common.ui.textarea.Magnifying": "放大",
@@ -626,7 +626,7 @@
"core.dataset.search.score.reRank desc": "透過重新排名模型計算句子之間的關聯度,範圍為 0 到 1。",
"core.dataset.search.score.rrf": "綜合排名",
"core.dataset.search.score.rrf desc": "使用倒數排名融合方法,合併多個搜尋結果。",
"core.dataset.search.search mode": "搜尋模式",
"core.dataset.search.search mode": "搜索方式",
"core.dataset.status.active": "已就緒",
"core.dataset.status.syncing": "同步中",
"core.dataset.test.Batch test": "批次測試",
@@ -1024,6 +1024,7 @@
"question_feedback": "工單諮詢",
"read_quote": "查看引用",
"required": "必填",
"rerank_weight": "重排權重",
"resume_failed": "恢復失敗",
"select_reference_variable": "選擇引用變數",
"share_link": "分享連結",

View File

@@ -4,8 +4,8 @@
"dependencies": {
"@chakra-ui/anatomy": "2.2.1",
"@chakra-ui/icons": "2.1.1",
"@chakra-ui/next-js": "2.1.5",
"@chakra-ui/react": "2.8.1",
"@chakra-ui/next-js": "2.4.2",
"@chakra-ui/react": "2.10.7",
"@chakra-ui/styled-system": "2.9.1",
"@chakra-ui/system": "2.6.1",
"@emotion/react": "11.11.1",
@@ -21,11 +21,11 @@
"ahooks": "^3.7.11",
"date-fns": "2.30.0",
"dayjs": "^1.11.7",
"i18next": "23.11.5",
"i18next": "23.16.8",
"js-cookie": "^3.0.5",
"lexical": "0.12.6",
"lodash": "^4.17.21",
"next-i18next": "15.3.0",
"next-i18next": "15.4.2",
"papaparse": "^5.4.1",
"react": "18.3.1",
"react-beautiful-dnd": "^13.1.1",

10258
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -52,6 +52,8 @@ USE_IP_LIMIT=false
WORKFLOW_MAX_RUN_TIMES=500
# 循环最大运行次数,避免极端的死循环情况
WORKFLOW_MAX_LOOP_TIMES=50
# 启用内网 IP 检查
CHECK_INTERNAL_IP=false
# 对话日志推送服务
# # 日志服务地址

View File

@@ -1,8 +1,6 @@
//next-i18next.config.js
/**
* @type {import('next-i18next').UserConfig}
*/
module.exports = {
i18n: {
defaultLocale: 'en',

View File

@@ -1,4 +1,4 @@
const { i18n } = require('./next-i18next.config');
const { i18n } = require('./next-i18next.config.js');
const path = require('path');
const fs = require('fs');
@@ -30,10 +30,6 @@ const nextConfig = {
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack']
},
{
test: /\.node$/,
use: [{ loader: 'nextjs-node-loader' }]
}
]),
exprContextCritical: false,
@@ -45,6 +41,7 @@ const nextConfig = {
}
if (isServer) {
config.externals.push('@node-rs/jieba');
if (nextRuntime === 'nodejs') {
const oldEntry = config.entry;
config = {
@@ -79,14 +76,15 @@ const nextConfig = {
return config;
},
transpilePackages: ['@fastgpt/*', 'ahooks'],
// 需要转译的包
transpilePackages: ['@fastgpt/global', '@fastgpt/web', 'ahooks'],
experimental: {
// 优化 Server Components 的构建和运行,避免不必要的客户端打包。
serverComponentsExternalPackages: [
'mongoose',
'pg',
'@node-rs/jieba',
'@zilliz/milvus2-sdk-node'
'@zilliz/milvus2-sdk-node',
"tiktoken",
],
outputFileTracingRoot: path.join(__dirname, '../../'),
instrumentationHook: true

View File

@@ -11,8 +11,8 @@
"dependencies": {
"@chakra-ui/anatomy": "2.2.1",
"@chakra-ui/icons": "2.1.1",
"@chakra-ui/next-js": "2.1.5",
"@chakra-ui/react": "2.8.1",
"@chakra-ui/next-js": "2.4.2",
"@chakra-ui/react": "2.10.7",
"@chakra-ui/styled-system": "2.9.1",
"@chakra-ui/system": "2.6.1",
"@emotion/react": "11.11.1",
@@ -23,9 +23,7 @@
"@fastgpt/templates": "workspace:*",
"@fastgpt/web": "workspace:*",
"@fortaine/fetch-event-source": "^3.0.6",
"@node-rs/jieba": "1.10.0",
"@tanstack/react-query": "^4.24.10",
"@types/nprogress": "^0.2.0",
"ahooks": "^3.7.11",
"axios": "^1.8.2",
"date-fns": "2.30.0",
@@ -35,7 +33,7 @@
"formidable": "^2.1.1",
"framer-motion": "9.1.7",
"hyperdown": "^2.4.29",
"i18next": "23.11.5",
"i18next": "23.16.8",
"immer": "^9.0.19",
"js-yaml": "^4.1.0",
"json5": "^2.2.3",
@@ -44,9 +42,8 @@
"lodash": "^4.17.21",
"mermaid": "^10.2.3",
"nanoid": "^5.1.3",
"next": "14.2.21",
"next-i18next": "15.3.0",
"nextjs-node-loader": "^1.1.5",
"next": "14.2.24",
"next-i18next": "15.4.2",
"nprogress": "^0.2.0",
"qrcode": "^1.5.4",
"react": "18.3.1",
@@ -67,6 +64,7 @@
"request-ip": "^3.3.0",
"sass": "^1.58.3",
"use-context-selector": "^1.4.4",
"@node-rs/jieba": "2.0.1",
"zustand": "^4.3.5"
},
"devDependencies": {
@@ -76,6 +74,7 @@
"@types/jsonwebtoken": "^9.0.3",
"@types/lodash": "^4.14.191",
"@types/node": "^20.14.2",
"@types/nprogress": "^0.2.0",
"@types/qrcode": "^1.5.5",
"@types/react": "18.3.1",
"@types/react-dom": "18.3.0",
@@ -83,7 +82,6 @@
"@types/request-ip": "^0.0.37",
"eslint": "8.56.0",
"eslint-config-next": "14.2.24",
"nextjs-node-loader": "^1.1.5",
"typescript": "^5.1.3",
"vitest": "^3.0.2"
}

View File

@@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1703041511149" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7024" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M512.512 128a384 384 0 1 1-384 384 384 384 0 0 1 384-384z m0 96a288 288 0 1 0 288 288 288 288 0 0 0-288-288z" fill="#3671FD" opacity=".2" p-id="7025"></path><path d="M512.512 64a448 448 0 1 1-302.56 117.6A32.032 32.032 0 1 1 253.312 228.8a384 384 0 1 0 298.016-98.88l-6.816-0.608v140.032a32 32 0 0 1-28.256 31.776l-3.744 0.224a32 32 0 0 1-31.776-28.288l-0.224-3.712V64z" fill="#3671FD" p-id="7026"></path><path d="M336.32 313.376l216.224 165.344a52.288 52.288 0 1 1-73.28 73.312l-165.376-216.224a16 16 0 0 1 22.4-22.4z" fill="#FE9C23" p-id="7027"></path></svg>

Before

Width:  |  Height:  |  Size: 893 B

View File

@@ -85,7 +85,7 @@ export default React.memo(Markdown);
function Code(e: any) {
const { className, codeBlock, children } = e;
const match = /language-(\w+)/.exec(className || '');
const codeType = match?.[1];
const codeType = match?.[1]?.toLowerCase();
const strChildren = String(children);
@@ -96,7 +96,7 @@ function Code(e: any) {
if (codeType === CodeClassNameEnum.guide) {
return <ChatGuide text={strChildren} />;
}
if (codeType === CodeClassNameEnum.questionGuide) {
if (codeType === CodeClassNameEnum.questionguide) {
return <QuestionGuide text={strChildren} />;
}
if (codeType === CodeClassNameEnum.echarts) {
@@ -105,7 +105,7 @@ function Code(e: any) {
if (codeType === CodeClassNameEnum.iframe) {
return <IframeCodeBlock code={strChildren} />;
}
if (codeType && codeType.toLowerCase() === CodeClassNameEnum.html) {
if (codeType === CodeClassNameEnum.html || codeType === CodeClassNameEnum.svg) {
return (
<IframeHtmlCodeBlock className={className} codeBlock={codeBlock} match={match}>
{children}

View File

@@ -1,6 +1,6 @@
export enum CodeClassNameEnum {
guide = 'guide',
questionGuide = 'questionGuide',
questionguide = 'questionguide',
mermaid = 'mermaid',
echarts = 'echarts',
quote = 'quote',
@@ -8,6 +8,7 @@ export enum CodeClassNameEnum {
latex = 'latex',
iframe = 'iframe',
html = 'html',
svg = 'svg',
video = 'video',
audio = 'audio'
}

View File

@@ -1,6 +1,6 @@
import { LOGO_ICON } from '@fastgpt/global/common/system/constants';
import Head from 'next/head';
import React, { useEffect, useMemo } from 'react';
import React, { useMemo } from 'react';
const NextHead = ({ title, icon, desc }: { title?: string; icon?: string; desc?: string }) => {
const formatIcon = useMemo(() => {
@@ -11,13 +11,6 @@ const NextHead = ({ title, icon, desc }: { title?: string; icon?: string; desc?:
return LOGO_ICON;
}, [icon]);
useEffect(() => {
// Force update document title
if (title) {
document.title = title;
}
}, [title]);
return (
<Head>
<title>{title}</title>

View File

@@ -2,13 +2,15 @@ import React, { useEffect, useMemo, useState } from 'react';
import {
Box,
Button,
Checkbox,
Divider,
Flex,
HStack,
ModalBody,
ModalFooter,
Switch,
useTheme
Slider,
SliderTrack,
SliderFilledTrack,
SliderThumb
} from '@chakra-ui/react';
import { useForm } from 'react-hook-form';
import MyModal from '@fastgpt/web/components/common/MyModal';
@@ -17,30 +19,18 @@ import { useTranslation } from 'next-i18next';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants';
import MyRadio from '@/components/common/MyRadio';
import MyIcon from '@fastgpt/web/components/common/Icon';
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useToast } from '@fastgpt/web/hooks/useToast';
import SelectAiModel from '@/components/Select/AIModelSelector';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import MyTextarea from '@/components/common/Textarea/MyTextarea';
import { defaultDatasetMaxTokens } from '@fastgpt/global/core/app/constants';
import InputSlider from '@fastgpt/web/components/common/MySlider/InputSlider';
import LeftRadio from '@fastgpt/web/components/common/Radio/LeftRadio';
import { AppDatasetSearchParamsType } from '@fastgpt/global/core/app/type';
import MyIcon from '@fastgpt/web/components/common/Icon';
export type DatasetParamsProps = {
searchMode: `${DatasetSearchModeEnum}`;
limit?: number;
similarity?: number;
usingReRank?: boolean;
datasetSearchUsingExtensionQuery?: boolean;
datasetSearchExtensionModel?: string;
datasetSearchExtensionBg?: string;
maxTokens?: number; // limit max tokens
};
enum SearchSettingTabEnum {
searchMode = 'searchMode',
limit = 'limit',
@@ -51,17 +41,22 @@ const DatasetParamsModal = ({
searchMode = DatasetSearchModeEnum.embedding,
limit,
similarity,
embeddingWeight,
usingReRank,
maxTokens = defaultDatasetMaxTokens,
rerankModel,
rerankWeight,
datasetSearchUsingExtensionQuery,
datasetSearchExtensionModel,
datasetSearchExtensionBg,
maxTokens = defaultDatasetMaxTokens,
onClose,
onSuccess
}: DatasetParamsProps & { onClose: () => void; onSuccess: (e: DatasetParamsProps) => void }) => {
}: AppDatasetSearchParamsType & {
maxTokens?: number; // limit max tokens
onClose: () => void;
onSuccess: (e: AppDatasetSearchParamsType) => void;
}) => {
const { t } = useTranslation();
const theme = useTheme();
const { toast } = useToast();
const { teamPlanStatus } = useUserStore();
const { reRankModelList, llmModelList, defaultModels } = useSystemStore();
const [refresh, setRefresh] = useState(false);
@@ -72,28 +67,41 @@ const DatasetParamsModal = ({
value: item.model,
label: item.name
})))();
const reRankModelSelectList = (() =>
reRankModelList.map((item) => ({
value: item.model,
label: item.name
})))();
const { register, setValue, getValues, handleSubmit, watch } =
useForm<AppDatasetSearchParamsType>({
defaultValues: {
searchMode,
embeddingWeight: embeddingWeight || 0.5,
usingReRank: !!usingReRank && teamPlanStatus?.standardConstants?.permissionReRank !== false,
rerankModel: rerankModel || defaultModels?.rerank?.model,
rerankWeight: rerankWeight || 0.5,
limit,
similarity,
datasetSearchUsingExtensionQuery,
datasetSearchExtensionModel: datasetSearchExtensionModel || defaultModels.llm?.model,
datasetSearchExtensionBg
}
});
const searchModeWatch = watch('searchMode');
const embeddingWeightWatch = watch('embeddingWeight');
const fullTextWeightWatch = useMemo(() => {
const val = 1 - (embeddingWeightWatch || 0.5);
return Number(val.toFixed(2));
}, [embeddingWeightWatch]);
const { register, setValue, getValues, handleSubmit, watch } = useForm<DatasetParamsProps>({
defaultValues: {
limit,
similarity,
searchMode,
usingReRank: !!usingReRank && teamPlanStatus?.standardConstants?.permissionReRank !== false,
datasetSearchUsingExtensionQuery,
datasetSearchExtensionModel: datasetSearchExtensionModel || defaultModels.llm?.model,
datasetSearchExtensionBg
}
});
const datasetSearchUsingCfrForm = watch('datasetSearchUsingExtensionQuery');
const queryExtensionModel = watch('datasetSearchExtensionModel');
const cfbBgDesc = watch('datasetSearchExtensionBg');
const usingReRankWatch = watch('usingReRank');
const searchModeWatch = watch('searchMode');
const searchModeList = useMemo(() => {
const list = Object.values(DatasetSearchModeMap);
return list;
}, []);
const usingReRankWatch = watch('usingReRank');
const reRankModelWatch = watch('rerankModel');
const rerankWeightWatch = watch('rerankWeight');
const showSimilarity = useMemo(() => {
if (similarity === undefined) return false;
@@ -134,93 +142,160 @@ const DatasetParamsModal = ({
title={t('common:core.dataset.search.Dataset Search Params')}
w={['90vw', '550px']}
>
<ModalBody flex={'auto'} overflow={'auto'}>
<ModalBody flex={'auto'} overflow={'auto'} px={[4, 10]}>
<LightRowTabs<SearchSettingTabEnum>
width={'100%'}
mb={3}
list={[
{
icon: 'modal/setting',
icon: 'common/setting',
label: t('common:core.dataset.search.search mode'),
value: SearchSettingTabEnum.searchMode
},
{
icon: 'support/outlink/apikeyFill',
icon: 'core/dataset/searchfilter',
label: t('common:core.dataset.search.Filter'),
value: SearchSettingTabEnum.limit
},
{
label: t('common:core.module.template.Query extension'),
value: SearchSettingTabEnum.queryExtension,
icon: '/imgs/workflow/cfr.svg'
icon: 'core/dataset/questionExtension'
}
]}
inlineStyles={{
borderBottomColor: 'myGray.200',
borderBottom: '1px solid'
}}
value={currentTabType}
onChange={setCurrentTabType}
/>
{currentTabType === SearchSettingTabEnum.searchMode && (
<>
<MyRadio
gridGap={2}
gridTemplateColumns={'repeat(1,1fr)'}
list={searchModeList}
value={getValues('searchMode')}
<Box mt={3}>
<LeftRadio<`${DatasetSearchModeEnum}`>
py={2.5}
gridGap={4}
list={[
{
title: t('common:core.dataset.search.mode.embedding'),
desc: t('common:core.dataset.search.mode.embedding desc'),
value: DatasetSearchModeEnum.embedding
},
{
title: t('common:core.dataset.search.mode.fullTextRecall'),
desc: t('common:core.dataset.search.mode.fullTextRecall desc'),
value: DatasetSearchModeEnum.fullTextRecall
},
{
title: t('common:core.dataset.search.mode.mixedRecall'),
desc: t('common:core.dataset.search.mode.mixedRecall desc'),
value: DatasetSearchModeEnum.mixedRecall,
children: searchModeWatch === DatasetSearchModeEnum.mixedRecall && (
<Box mt={3}>
<HStack justifyContent={'space-between'}>
<Flex alignItems={'center'}>
<Box fontSize={'sm'} color={'myGray.900'}>
{t('common:core.dataset.search.mode.embedding')}
</Box>
<Box fontSize={'xs'} color={'myGray.500'}>
{embeddingWeightWatch}
</Box>
</Flex>
<Flex alignItems={'center'}>
<Box fontSize={'sm'} color={'myGray.900'}>
{t('common:core.dataset.search.score.fullText')}
</Box>
<Box fontSize={'xs'} color={'myGray.500'}>
{fullTextWeightWatch}
</Box>
</Flex>
</HStack>
<Slider
defaultValue={embeddingWeightWatch}
min={0.1}
max={0.9}
step={0.01}
onChange={(e) => {
setValue('embeddingWeight', Number(e.toFixed(2)));
}}
>
<SliderTrack bg={'#F9518E'}>
<SliderFilledTrack bg={'#3370FF'} />
</SliderTrack>
<SliderThumb boxShadow={'none'} bg={'none'}>
<MyIcon transform={'translateY(10px)'} name={'sliderTag'} w={'1rem'} />
</SliderThumb>
</Slider>
</Box>
)
}
]}
value={searchModeWatch}
onChange={(e) => {
setValue('searchMode', e as `${DatasetSearchModeEnum}`);
setRefresh(!refresh);
setValue('searchMode', e);
}}
/>
{/* Rerank */}
<>
<Divider my={4} />
<Flex
alignItems={'center'}
cursor={'pointer'}
userSelect={'none'}
py={3}
px={4}
border={theme.borders.sm}
borderWidth={'1.5px'}
borderRadius={'md'}
position={'relative'}
{...(getValues('usingReRank')
? {
borderColor: 'primary.400'
}
: {})}
onClick={(e) => {
if (!showReRank) {
return toast({
status: 'warning',
title: t('common:core.ai.Not deploy rerank model')
});
}
if (
teamPlanStatus?.standardConstants &&
!teamPlanStatus?.standardConstants?.permissionReRank
) {
return toast({
status: 'warning',
title: t('common:support.team.limit.No permission rerank')
});
}
setValue('usingReRank', !getValues('usingReRank'));
setRefresh((state) => !state);
}}
>
<MyIcon name="core/dataset/rerank" w={'18px'} mr={'14px'} />
<Box pr={2} color={'myGray.800'} flex={'1 0 0'}>
<Box fontSize={'sm'}>{t('common:core.dataset.search.ReRank')}</Box>
<Box fontSize={'xs'} color={'myGray.500'}>
{t('common:core.dataset.search.ReRank desc')}
<HStack mt={6} justifyContent={'space-between'}>
<FormLabel>
{t('common:core.dataset.search.ReRank')}
<QuestionTip ml={0.5} label={t('common:core.dataset.search.ReRank desc')} />
</FormLabel>
{!showReRank ? (
<Box color={'myGray.500'} fontSize={'sm'}>
{t('common:core.ai.Not deploy rerank model')}
</Box>
</Box>
<Box position={'relative'} w={'18px'} h={'18px'}>
<Checkbox colorScheme="primary" isChecked={getValues('usingReRank')} size="lg" />
<Box position={'absolute'} top={0} right={0} bottom={0} left={0} zIndex={1}></Box>
</Box>
</Flex>
) : teamPlanStatus?.standardConstants &&
!teamPlanStatus?.standardConstants?.permissionReRank ? (
<Box color={'myGray.500'} fontSize={'sm'}>
{t('common:support.team.limit.No permission rerank')}
</Box>
) : (
<Switch {...register('usingReRank')} />
)}
</HStack>
{usingReRankWatch && (
<>
<HStack mt={3} justifyContent={'space-between'}>
<Box fontSize={'sm'} flex={'0 0 100px'} color={'myGray.700'}>
{t('common:rerank_weight')}
</Box>
<Box flex={'1 0 0'}>
<InputSlider
min={0.1}
max={1}
step={0.01}
value={rerankWeightWatch}
onChange={(val) => {
setValue(
NodeInputKeyEnum.datasetSearchRerankWeight,
Number(val.toFixed(2))
);
}}
/>
</Box>
</HStack>
<HStack mt={3}>
<Box fontSize={'sm'} flex={'0 0 100px'} color={'myGray.700'}>
{t('common:model.type.reRank')}
</Box>
<Box flex={'1 0 0'}>
<SelectAiModel
bg={'myGray.50'}
h={'36px'}
value={reRankModelWatch}
list={reRankModelSelectList}
onChange={(val) => {
setValue(NodeInputKeyEnum.datasetSearchRerankModel, val);
}}
/>
</Box>
</HStack>
</>
)}
</>
</>
</Box>
)}
{currentTabType === SearchSettingTabEnum.limit && (
<Box pt={5}>
@@ -262,7 +337,7 @@ const DatasetParamsModal = ({
}}
/>
) : (
<Box color={'myGray.500'}>
<Box color={'myGray.500'} fontSize={'sm'}>
{t('common:core.dataset.search.No support similarity')}
</Box>
)}

View File

@@ -40,18 +40,13 @@ export const DatasetSelectModal = ({
const { paths, setParentId, datasets, isFetching } = useDatasetSelect();
const { Loading } = useLoading();
const filterDatasets = useMemo(() => {
const filtered = {
selected: datasets.filter((item) =>
selectedDatasets.find((dataset) => dataset.datasetId === item._id)
),
unSelected: datasets.filter(
(item) => !selectedDatasets.find((dataset) => dataset.datasetId === item._id)
)
};
return filtered;
const unSelectedDatasets = useMemo(() => {
return datasets.filter(
(item) => !selectedDatasets.some((dataset) => dataset.datasetId === item._id)
);
}, [datasets, selectedDatasets]);
const activeVectorModel = defaultSelectedDatasets[0]?.vectorModel?.model;
const activeVectorModel = selectedDatasets[0]?.vectorModel?.model;
return (
<DatasetSelectContainer
@@ -71,11 +66,11 @@ export const DatasetSelectModal = ({
]}
gridGap={3}
>
{filterDatasets.selected.map((item) =>
{selectedDatasets.map((item) =>
(() => {
return (
<Card
key={item._id}
key={item.datasetId}
p={3}
border={theme.borders.base}
boxShadow={'sm'}
@@ -93,7 +88,7 @@ export const DatasetSelectModal = ({
_hover={{ color: 'red.500' }}
onClick={() => {
setSelectedDatasets((state) =>
state.filter((dataset) => dataset.datasetId !== item._id)
state.filter((dataset) => dataset.datasetId !== item.datasetId)
);
}}
/>
@@ -104,7 +99,7 @@ export const DatasetSelectModal = ({
)}
</Grid>
{filterDatasets.selected.length > 0 && <Divider my={3} />}
{selectedDatasets.length > 0 && <Divider my={3} />}
<Grid
gridTemplateColumns={[
@@ -114,7 +109,7 @@ export const DatasetSelectModal = ({
]}
gridGap={3}
>
{filterDatasets.unSelected.map((item) =>
{unSelectedDatasets.map((item) =>
(() => {
return (
<MyTooltip
@@ -174,7 +169,9 @@ export const DatasetSelectModal = ({
alignItems={'center'}
fontSize={'sm'}
color={
activeVectorModel === item.vectorModel.name ? 'primary.600' : 'myGray.500'
activeVectorModel === item.vectorModel.model
? 'primary.600'
: 'myGray.500'
}
>
{item.type === DatasetTypeEnum.folder ? (
@@ -192,21 +189,14 @@ export const DatasetSelectModal = ({
})()
)}
</Grid>
{filterDatasets.unSelected.length === 0 && (
<EmptyTip text={t('common:common.folder.empty')} />
)}
{unSelectedDatasets.length === 0 && <EmptyTip text={t('common:common.folder.empty')} />}
</ModalBody>
<ModalFooter>
<Button
onClick={() => {
// filter out the dataset that is not in the kList
const filterDatasets = selectedDatasets.filter((dataset) => {
return datasets.find((item) => item._id === dataset.datasetId);
});
onClose();
onChange(filterDatasets);
onChange(selectedDatasets);
}}
>
{t('common:common.Done')}

View File

@@ -54,7 +54,6 @@ const InputGuideConfig = ({
onChange: (e: ChatInputGuideConfigType) => void;
}) => {
const { t } = useTranslation();
const { chatT } = useI18n();
const { isOpen, onOpen, onClose } = useDisclosure();
const {
isOpen: isOpenLexiconConfig,
@@ -87,11 +86,11 @@ const InputGuideConfig = ({
<Flex alignItems={'center'}>
<MyIcon name={'core/app/inputGuides'} mr={2} w={'20px'} />
<Flex alignItems={'center'}>
<FormLabel color={'myGray.600'}>{chatT('input_guide')}</FormLabel>
<FormLabel color={'myGray.600'}>{t('chat:input_guide')}</FormLabel>
<ChatFunctionTip type={'inputGuide'} />
</Flex>
<Box flex={1} />
<MyTooltip label={chatT('config_input_guide')}>
<MyTooltip label={t('chat:config_input_guide')}>
<Button
variant={'transparentBase'}
iconSpacing={1}
@@ -104,7 +103,7 @@ const InputGuideConfig = ({
</Button>
</MyTooltip>
<MyModal
title={chatT('input_guide')}
title={t('chat:input_guide')}
iconSrc="core/app/inputGuides"
isOpen={isOpen}
onClose={onClose}
@@ -126,7 +125,7 @@ const InputGuideConfig = ({
{isOpenQuestionGuide && (
<>
<Flex mt={8} alignItems={'center'}>
<FormLabel>{chatT('input_guide_lexicon')}</FormLabel>
<FormLabel>{t('chat:input_guide_lexicon')}</FormLabel>
<Box fontSize={'xs'} px={2} bg={'myGray.100'} ml={1} rounded={'full'}>
{total}
</Box>
@@ -144,7 +143,7 @@ const InputGuideConfig = ({
</Flex>
<>
<Flex mt={8} alignItems={'center'}>
<FormLabel>{chatT('custom_input_guide_url')}</FormLabel>
<FormLabel>{t('chat:custom_input_guide_url')}</FormLabel>
<Flex
onClick={() => window.open(getDocPath('/docs/guide/course/chat_input_guide/'))}
color={'primary.700'}
@@ -181,7 +180,7 @@ const InputGuideConfig = ({
export default React.memo(InputGuideConfig);
const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () => void }) => {
const { chatT, commonT } = useI18n();
const { commonT } = useI18n();
const { t } = useTranslation();
const { toast } = useToast();
const { File, onOpen: onOpenSelectFile } = useSelectFile({
@@ -232,7 +231,7 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
if (res.insertLength < textList.length) {
toast({
status: 'warning',
title: chatT('insert_input_guide,_some_data_already_exists', { len: res.insertLength })
title: t('chat:insert_input_guide,_some_data_already_exists', { len: res.insertLength })
});
} else {
toast({
@@ -301,7 +300,7 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
return (
<MyModal
title={chatT('config_input_guide_lexicon_title')}
title={t('chat:config_input_guide_lexicon_title')}
iconSrc="core/app/inputGuides"
isOpen={true}
onClose={onClose}
@@ -338,7 +337,7 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
});
}}
>
<QuestionTip ml={-2} label={chatT('csv_input_lexicon_tip')} />
<QuestionTip ml={-2} label={t('chat:csv_input_lexicon_tip')} />
</Box>
</Flex>
<Box px={8}>
@@ -394,7 +393,7 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
<MyInput
autoFocus
rightIcon={<MyIcon name={'save'} w={'14px'} cursor={'pointer'} />}
placeholder={chatT('new_input_guide_lexicon')}
placeholder={t('chat:new_input_guide_lexicon')}
onBlur={(e) => {
createNewData([e.target.value.trim()]);
}}
@@ -411,7 +410,7 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
px={8}
flex={'1 0 0'}
fontSize={'sm'}
EmptyChildren={<EmptyTip text={chatT('chat_input_guide_lexicon_is_empty')} />}
EmptyChildren={<EmptyTip text={t('chat:chat_input_guide_lexicon_is_empty')} />}
>
{scrollDataList.map((data, index) => {
const item = data.data;

View File

@@ -22,7 +22,6 @@ export default function InputGuideBox({
onSend: (text: string) => void;
}) {
const { t } = useTranslation();
const { chatT } = useI18n();
const chatInputGuide = useContextSelector(ChatBoxContext, (v) => v.chatInputGuide);
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
@@ -65,9 +64,9 @@ export default function InputGuideBox({
>
<Flex alignItems={'center'} fontSize={'sm'} color={'myGray.600'} gap={2} mb={2} px={2}>
<MyIcon name={'union'} />
<Box>{chatT('input_guide')}</Box>
<Box>{t('chat:input_guide')}</Box>
</Flex>
{data.map((item, index) => (
{data.map((item) => (
<Flex
alignItems={'center'}
as={'li'}

View File

@@ -59,7 +59,7 @@ type Props = BasicProps & {
const RenderQuestionGuide = ({ questionGuides }: { questionGuides: string[] }) => {
return (
<Markdown
source={`\`\`\`${CodeClassNameEnum.questionGuide}
source={`\`\`\`${CodeClassNameEnum.questionguide}
${JSON.stringify(questionGuides)}`}
/>
);

View File

@@ -299,6 +299,7 @@ const RenderUserFormInteractive = React.memo(function RenderFormInput({
<MyNumberInput
min={input.min}
max={input.max}
defaultValue={input.defaultValue}
isDisabled={interactive.params.submitted}
bg={'white'}
register={register}

View File

@@ -228,10 +228,23 @@ export const WholeResponseContent = ({
{activeModule?.searchMode && (
<Row
label={t('common:core.dataset.search.search mode')}
// @ts-ignore
value={t(DatasetSearchModeMap[activeModule.searchMode]?.title)}
rawDom={
<Flex border={'base'} borderRadius={'md'} p={2}>
<Box>
{/* @ts-ignore */}
{t(DatasetSearchModeMap[activeModule.searchMode]?.title)}
</Box>
{activeModule.embeddingWeight && (
<>{`(${t('chat:response_hybrid_weight', {
emb: activeModule.embeddingWeight,
text: 1 - activeModule.embeddingWeight
})})`}</>
)}
</Flex>
}
/>
)}
<Row
label={t('common:core.chat.response.module similarity')}
value={activeModule?.similarity}
@@ -239,7 +252,19 @@ export const WholeResponseContent = ({
<Row label={t('common:core.chat.response.module limit')} value={activeModule?.limit} />
<Row
label={t('common:core.chat.response.search using reRank')}
value={`${activeModule?.searchUsingReRank}`}
rawDom={
<Box border={'base'} borderRadius={'md'} p={2}>
{activeModule?.searchUsingReRank ? (
activeModule?.rerankModel ? (
<Box>{`${activeModule.rerankModel}: ${activeModule.rerankWeight}`}</Box>
) : (
'True'
)
) : (
`False`
)}
</Box>
}
/>
{activeModule.queryExtensionResult && (
<>

View File

@@ -3,11 +3,10 @@ import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import MyIcon from '@fastgpt/web/components/common/Icon';
import React from 'react';
import { DatasetTypeMap } from '@fastgpt/global/core/dataset/constants';
import { useI18n } from '@/web/context/I18n';
import { useTranslation } from 'next-i18next';
const DatasetTypeTag = ({ type, ...props }: { type: `${DatasetTypeEnum}` } & FlexProps) => {
const { datasetT } = useI18n();
const { t } = useTranslation();
const item = DatasetTypeMap[type] || DatasetTypeMap['dataset'];
return (
@@ -24,8 +23,7 @@ const DatasetTypeTag = ({ type, ...props }: { type: `${DatasetTypeEnum}` } & Fle
{...props}
>
<MyIcon name={item.icon as any} w={'16px'} mr={2} color={'myGray.400'} />
{/* @ts-ignore */}
<Box>{datasetT(item.label)}</Box>
<Box>{t(item.label as any)}</Box>
</Flex>
);
};

View File

@@ -6,6 +6,7 @@ import { useTranslation } from 'next-i18next';
import { Box } from '@chakra-ui/react';
import ParentPaths from '@/components/common/ParentPaths';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
type PathItemType = {
parentId: string;
@@ -70,8 +71,16 @@ const DatasetSelectContainer = ({
export function useDatasetSelect() {
const [parentId, setParentId] = useState<string>('');
const { data, isFetching } = useQuery(['loadDatasetData', parentId], () =>
Promise.all([getDatasets({ parentId }), getDatasetPaths(parentId)])
const { data, loading: isFetching } = useRequest2(
() =>
Promise.all([
getDatasets({ parentId }),
getDatasetPaths({ sourceId: parentId, type: 'current' })
]),
{
manual: false,
refreshDeps: [parentId]
}
);
const paths = useMemo(() => [...(data?.[1] || [])], [data]);

View File

@@ -3,7 +3,7 @@ import type {
EmbeddingModelItemType,
AudioSpeechModels,
STTModelType,
ReRankModelItemType
RerankModelItemType
} from '@fastgpt/global/core/ai/model.d';
import type { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types/index.d';

View File

@@ -63,8 +63,13 @@ export type SearchTestProps = {
text: string;
[NodeInputKeyEnum.datasetSimilarity]?: number;
[NodeInputKeyEnum.datasetMaxTokens]?: number;
[NodeInputKeyEnum.datasetSearchMode]?: `${DatasetSearchModeEnum}`;
[NodeInputKeyEnum.datasetSearchEmbeddingWeight]?: number;
[NodeInputKeyEnum.datasetSearchUsingReRank]?: boolean;
[NodeInputKeyEnum.datasetSearchRerankModel]?: string;
[NodeInputKeyEnum.datasetSearchRerankWeight]?: number;
[NodeInputKeyEnum.datasetSearchUsingExtensionQuery]?: boolean;
[NodeInputKeyEnum.datasetSearchExtensionModel]?: string;

View File

@@ -1,14 +1,12 @@
import React, { useMemo } from 'react';
import { Box, ButtonProps, Flex } from '@chakra-ui/react';
import { Box, ButtonProps } from '@chakra-ui/react';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useTranslation } from 'next-i18next';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { getTeamList, putSwitchTeam } from '@/web/support/user/team/api';
import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import MySelect from '@fastgpt/web/components/common/MySelect';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useRouter } from 'next/router';
const TeamSelector = ({
@@ -21,7 +19,7 @@ const TeamSelector = ({
}) => {
const { t } = useTranslation();
const router = useRouter();
const { userInfo, initUserInfo } = useUserStore();
const { userInfo } = useUserStore();
const { setLoading } = useSystemStore();
const { data: myTeams = [] } = useRequest2(() => getTeamList(TeamMemberStatusEnum.active), {
@@ -33,12 +31,11 @@ const TeamSelector = ({
async (teamId: string) => {
setLoading(true);
await putSwitchTeam(teamId);
return initUserInfo();
},
{
onFinally: () => {
router.reload();
setLoading(false);
onChange?.();
},
errorToast: t('common:user.team.Switch Team Failed')
}

View File

@@ -220,13 +220,7 @@ function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: ()
{bill.metadata.payWay === 'balance' ? (
t('user:bill.not_need_invoice')
) : (
<Box>
{
(bill.metadata.payWay = bill.hasInvoice
? t('account_bill:yes')
: t('account_bill:no'))
}
</Box>
<Box>{bill.hasInvoice ? t('account_bill:yes') : t('account_bill:no')}</Box>
)}
</Flex>
)}

View File

@@ -280,6 +280,10 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
isCustom: true,
isActive: true,
isDefault: false,
isDefaultDatasetTextModel: false,
isDefaultDatasetImageModel: false,
// @ts-ignore
type
});

View File

@@ -175,22 +175,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name="common/inviteLight" w={'16px'} color={'white'} />}
onClick={() => {
if (
teamPlanStatus?.standardConstants?.maxTeamMember &&
teamPlanStatus.standardConstants.maxTeamMember <= members.length
) {
toast({
status: 'warning',
title: t('common:user.team.Over Max Member Tip', {
max: teamPlanStatus.standardConstants.maxTeamMember
})
});
setNotSufficientModalType(TeamErrEnum.teamMemberOverSize);
} else {
onOpenInvite();
}
}}
onClick={onOpenInvite}
>
{t('account_team:user_team_invite_member')}
</Button>

View File

@@ -1,12 +1,10 @@
import React, { useEffect, useState } from 'react';
import ApiKeyTable from '@/components/support/apikey/Table';
import { useTranslation } from 'next-i18next';
import { Box } from '@chakra-ui/react';
import { useI18n } from '@/web/context/I18n';
const API = ({ appId }: { appId: string }) => {
const { publishT } = useI18n();
return <ApiKeyTable tips={publishT('app_key_tips')} appId={appId} />;
const { t } = useTranslation();
return <ApiKeyTable tips={t('publish:app_key_tips')} appId={appId} />;
};
export default API;

View File

@@ -10,7 +10,6 @@ import {
HStack
} from '@chakra-ui/react';
import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d';
import type { DatasetSimpleItemType } from '@fastgpt/global/core/dataset/type.d';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
@@ -106,6 +105,7 @@ const EditForm = ({
const tokenLimit = useMemo(() => {
return selectedModel?.quoteMaxToken || 3000;
}, [selectedModel?.quoteMaxToken]);
// Force close image select when model not support vision
useEffect(() => {
if (!selectedModel.vision) {
@@ -434,8 +434,6 @@ const EditForm = ({
...e
}
}));
console.dir(e);
}}
/>
)}

View File

@@ -61,10 +61,13 @@ const Header = ({
const { lastAppListRouteType } = useSystemStore();
const { data: paths = [] } = useRequest2(() => getAppFolderPath(appId), {
manual: false,
refreshDeps: [appId]
});
const { data: paths = [] } = useRequest2(
() => getAppFolderPath({ sourceId: appId, type: 'parent' }),
{
manual: false,
refreshDeps: [appId]
}
);
const onClickRoute = useCallback(
(parentId: string) => {
router.push({

View File

@@ -112,8 +112,9 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
const { data: paths = [] } = useRequest2(
() => {
if (templateType === TemplateTypeEnum.teamPlugin) return getAppFolderPath(parentId);
return getSystemPluginPaths(parentId);
if (templateType === TemplateTypeEnum.teamPlugin)
return getAppFolderPath({ sourceId: parentId, type: 'current' });
return getSystemPluginPaths({ sourceId: parentId, type: 'current' });
},
{
manual: false,
@@ -186,7 +187,7 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
{/* route components */}
{!searchKey && parentId && (
<Flex mt={2} px={[3, 6]}>
<FolderPath paths={paths} FirstPathDom={null} onClick={() => onUpdateParentId(null)} />
<FolderPath paths={paths} FirstPathDom={null} onClick={onUpdateParentId} />
</Flex>
)}
<MyBox isLoading={isLoading} mt={2} px={[3, 6]} pb={3} flex={'1 0 0'} overflowY={'auto'}>

View File

@@ -28,8 +28,6 @@ const SimpleEdit = () => {
// Init app form
useMount(() => {
// show selected dataset
if (appDetail.version !== 'v2') {
return setAppForm(
appWorkflow2Form({

View File

@@ -144,7 +144,7 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
</Flex>
)}
<Flex flex={'1 0 0'} alignItems={'end'}>
<Flex flex={'1 0 0'} alignItems={'end'} h={'100%'}>
<Box flex={'1 0 0'} h={'100%'} overflow={'auto'}>
<ChatContainer />
</Box>

View File

@@ -276,8 +276,9 @@ const RenderHeader = React.memo(function RenderHeader({
// Get paths
const { data: paths = [] } = useRequest2(
() => {
if (templateType === TemplateTypeEnum.teamPlugin) return getAppFolderPath(parentId);
return getSystemPluginPaths(parentId);
if (templateType === TemplateTypeEnum.teamPlugin)
return getAppFolderPath({ sourceId: parentId, type: 'current' });
return getSystemPluginPaths({ sourceId: parentId, type: 'current' });
},
{
manual: false,

View File

@@ -6,13 +6,14 @@ import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import MyIcon from '@fastgpt/web/components/common/Icon';
import DatasetParamsModal, { DatasetParamsProps } from '@/components/core/app/DatasetParamsModal';
import DatasetParamsModal from '@/components/core/app/DatasetParamsModal';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import SearchParamsTip from '@/components/core/dataset/SearchParamsTip';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '@/pageComponents/app/detail/WorkflowComponents/context';
import { getWebLLMModel } from '@/web/common/system/utils';
import { defaultDatasetMaxTokens } from '@fastgpt/global/core/app/constants';
import { AppDatasetSearchParamsType } from '@fastgpt/global/core/app/type';
const SelectDatasetParam = ({ inputs = [], nodeId }: RenderInputProps) => {
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
@@ -21,11 +22,14 @@ const SelectDatasetParam = ({ inputs = [], nodeId }: RenderInputProps) => {
const { t } = useTranslation();
const { defaultModels } = useSystemStore();
const [data, setData] = useState<DatasetParamsProps>({
const [data, setData] = useState<AppDatasetSearchParamsType>({
searchMode: DatasetSearchModeEnum.embedding,
limit: 5,
embeddingWeight: 0.5,
limit: 3000,
similarity: 0.5,
usingReRank: false,
rerankModel: defaultModels.llm?.model,
rerankWeight: 0.6,
datasetSearchUsingExtensionQuery: true,
datasetSearchExtensionModel: defaultModels.llm?.model,
datasetSearchExtensionBg: ''

View File

@@ -126,7 +126,7 @@ export const useChatTest = ({
const CustomChatContainer = useMemoizedFn(() =>
appDetail.type === AppTypeEnum.plugin ? (
<Box p={5}>
<Box p={5} pb={16}>
<PluginRunBox
appId={appId}
chatId={chatId}

View File

@@ -3,31 +3,32 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { useI18n } from '@/web/context/I18n';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { Box, Flex } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
const AppTypeTag = ({ type }: { type: AppTypeEnum }) => {
const { appT } = useI18n();
const { t } = useTranslation();
const map = useRef({
[AppTypeEnum.simple]: {
label: appT('type.Simple bot'),
label: t('app:type.Simple bot'),
icon: 'core/app/type/simple',
bg: '#DBF3FF',
color: '#0884DD'
},
[AppTypeEnum.workflow]: {
label: appT('type.Workflow bot'),
label: t('app:type.Workflow bot'),
icon: 'core/app/type/workflow',
bg: '#E4E1FC',
color: '#6F5DD7'
},
[AppTypeEnum.plugin]: {
label: appT('type.Plugin'),
label: t('app:type.Plugin'),
icon: 'core/app/type/plugin',
bg: '#D0F5EE',
color: '#007E7C'
},
[AppTypeEnum.httpPlugin]: {
label: appT('type.Http plugin'),
label: t('app:type.Http plugin'),
icon: 'core/app/type/httpPlugin',
bg: '#FFE4EE',
color: '#E82F72'

View File

@@ -90,7 +90,7 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => {
);
const { data: paths = [], runAsync: refetchPaths } = useRequest2(
() => getAppFolderPath(parentId),
() => getAppFolderPath({ sourceId: parentId, type: 'current' }),
{
manual: false,
refreshDeps: [parentId]

View File

@@ -2,13 +2,12 @@ import Markdown from '@/components/Markdown';
import { Box, Flex } from '@chakra-ui/react';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { Dispatch, MutableRefObject, SetStateAction, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import InputDataModal from '@/pageComponents/dataset/detail/InputDataModal';
const CollectionQuoteItem = ({
index,
quoteRefs,
quoteIndex,
setQuoteIndex,
@@ -22,8 +21,7 @@ const CollectionQuoteItem = ({
dataId,
collectionId
}: {
index: number;
quoteRefs: MutableRefObject<(HTMLDivElement | null)[]>;
quoteRefs: MutableRefObject<Map<string, HTMLDivElement | null>>;
quoteIndex: number;
setQuoteIndex: Dispatch<SetStateAction<number>>;
refreshList: () => void;
@@ -45,7 +43,7 @@ const CollectionQuoteItem = ({
<>
<Box
ref={(el: HTMLDivElement | null) => {
quoteRefs.current[index] = el;
quoteRefs.current.set(dataId, el);
}}
p={2}
py={2}
@@ -163,9 +161,7 @@ const CollectionQuoteItem = ({
'0px 1px 2px 0px rgba(19, 51, 107, 0.05), 0px 0px 1px 0px rgba(19, 51, 107, 0.08)'
}
cursor={'pointer'}
onClick={() => {
copyData(q + '\n' + a);
}}
onClick={() => copyData(`${q}${a ? '\n' + a : ''}`)}
>
<MyIcon name="copy" w={'14px'} color={'myGray.500'} />
</Flex>

View File

@@ -3,7 +3,7 @@ import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useRouter } from 'next/router';
import { useTranslation } from 'react-i18next';
import { useTranslation } from 'next-i18next';
import DownloadButton from './DownloadButton';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { downloadFetch } from '@/web/common/system/utils';
@@ -70,8 +70,9 @@ const CollectionReader = ({
appId: metadata.appId,
...metadata.outLinkAuthData
}),
[metadata]
[chatItemDataId, collectionId, metadata.appId, metadata.chatId, metadata.outLinkAuthData]
);
const {
dataList: datasetDataList,
isLoading,
@@ -108,13 +109,22 @@ const CollectionReader = ({
url: '/api/core/dataset/collection/export',
filename: 'data.csv',
body: {
collectionId: collectionId,
chatItemDataId
appId: metadata.appId,
chatId: metadata.chatId,
chatItemDataId,
collectionId,
...metadata.outLinkAuthData
}
});
});
const handleRead = getCollectionSourceAndOpen(metadata);
const handleRead = getCollectionSourceAndOpen({
appId: metadata.appId,
chatId: metadata.chatId,
chatItemDataId,
collectionId,
...metadata.outLinkAuthData
});
return (
<MyBox display={'flex'} flexDirection={'column'} h={'full'}>
@@ -256,11 +266,10 @@ const CollectionReader = ({
{formatedDataList.map((item, index) => (
<CollectionQuoteItem
key={item._id}
index={index}
quoteRefs={itemRefs as React.MutableRefObject<(HTMLDivElement | null)[]>}
quoteRefs={itemRefs as React.MutableRefObject<Map<string, HTMLDivElement | null>>}
quoteIndex={item.quoteIndex}
setQuoteIndex={setQuoteIndex}
refreshList={() => loadInitData(false)}
refreshList={() => loadInitData({ scrollWhenFinish: false, refresh: true })}
updated={item.updated}
isCurrentSelected={item.isCurrentSelected}
q={item.q}

View File

@@ -1,5 +1,5 @@
import MyMenu from '@fastgpt/web/components/common/MyMenu';
import { useTranslation } from 'react-i18next';
import { useTranslation } from 'next-i18next';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
const DownloadButton = ({

View File

@@ -4,7 +4,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import ScoreTag from './ScoreTag';
import Markdown from '@/components/Markdown';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useTranslation } from 'react-i18next';
import { useTranslation } from 'next-i18next';
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
const QuoteItem = ({

View File

@@ -2,7 +2,7 @@ import { Box, Flex } from '@chakra-ui/react';
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { useTranslation } from 'react-i18next';
import { useTranslation } from 'next-i18next';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import QuoteItem from './QuoteItem';
import { useMemo } from 'react';

View File

@@ -2,7 +2,7 @@ import { ScoreItemType, scoreTheme } from '@/components/core/dataset/QuoteItem';
import { Box, Flex, Progress } from '@chakra-ui/react';
import { SearchScoreTypeMap } from '@fastgpt/global/core/dataset/constants';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useTranslation } from 'react-i18next';
import { useTranslation } from 'next-i18next';
const ScoreTag = (score: { primaryScore?: ScoreItemType; secondaryScore: ScoreItemType[] }) => {
const { t } = useTranslation();

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