Compare commits
8 Commits
v4.9.1-alp
...
v4.9.1-fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a1fff74fd | ||
|
|
de87639fce | ||
|
|
f9cecfd49a | ||
|
|
70563d2bcb | ||
|
|
4ca99a6361 | ||
|
|
8f70e436cf | ||
|
|
e75d81d05a | ||
|
|
56793114d8 |
3
.github/workflows/fastgpt-preview-image.yml
vendored
@@ -1,9 +1,6 @@
|
||||
name: Preview FastGPT images
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- 'projects/app/**'
|
||||
- 'packages/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -6,4 +6,5 @@ docSite/
|
||||
*.md
|
||||
|
||||
pnpm-lock.yaml
|
||||
cl100l_base.ts
|
||||
cl100l_base.ts
|
||||
dict.json
|
||||
7
.vscode/i18n-ally-custom-framework.yml
vendored
@@ -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
|
||||
|
||||
@@ -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/)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 87 KiB |
@@ -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,无需占用本地资源,无需太关注。
|
||||
|
||||
|
||||
100
docSite/content/zh-cn/docs/development/modelConfig/ppio.md
Normal 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 代金券
|
||||
|
||||

|
||||
|
||||
(3) 生成并保存 【API 密钥】
|
||||
{{% alert context="warning" %}}
|
||||
秘钥在服务端是加密存储,请在生成时保存好秘钥;若遗失可以在控制台上删除并创建一个新的秘钥。
|
||||
{{% /alert %}}
|
||||
|
||||

|
||||

|
||||
|
||||
(4) 获取需要使用的模型 ID
|
||||
|
||||
deepseek 系列:
|
||||
|
||||
- DeepSeek R1:deepseek/deepseek-r1/community
|
||||
|
||||
- DeepSeek V3:deepseek/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,按下图在模型提供商中选择派欧云
|
||||
|
||||

|
||||
|
||||
- 测试连通性
|
||||
以 deepseek 为例,在模型中选择使用 deepseek/deepseek-r1/community,点击图中②的位置进行连通性测试,出现图中绿色的的成功显示证明连通成功,可以进行后续的配置对话了
|
||||

|
||||
|
||||
(2)不使用 OneAPI 接入 PPIO 模型
|
||||
|
||||
按照下图在模型提供商中选择派欧云
|
||||

|
||||
|
||||
- 配置模型 自定义请求地址中输入:`https://api.ppinfra.com/v3/openai/chat/completions`
|
||||

|
||||

|
||||
|
||||
- 测试连通性
|
||||

|
||||
|
||||
出现图中绿色的的成功显示证明连通成功,可以进行对话配置
|
||||
|
||||
## 4. 配置对话
|
||||
(1)新建工作台
|
||||

|
||||
(2)开始聊天
|
||||

|
||||
|
||||
## PPIO 全新福利重磅来袭 🔥
|
||||
顺利完成教程配置步骤后,您将解锁两大权益:1. 畅享 PPIO 高速通道与 FastGPT 的效能组合;2.立即激活 **「新用户邀请奖励」** ————通过专属邀请码邀好友注册,您与好友可各领 50 元代金券,硬核福利助力 AI 工具效率倍增!
|
||||
|
||||
🎁 新手专享:立即使用邀请码【VOJL20】完成注册,50 元代金券奖励即刻到账!
|
||||
@@ -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 时,存在鉴权失败清空。
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: "邀请链接说明文档"
|
||||
description: "如何使用邀请链接来邀请团队成员"
|
||||
icon: "group"
|
||||
draft: false
|
||||
toc: true
|
||||
weight: 451
|
||||
---
|
||||
|
||||
v4.9.1 团队邀请成员将开始使用「邀请链接」的模式,弃用之前输入用户名进行添加的形式。
|
||||
|
||||
在版本升级后,原收到邀请还未加入团队的成员,将自动清除邀请。请使用邀请链接重新邀请成员。
|
||||
|
||||
## 如何使用
|
||||
|
||||
1. **在团队管理页面,管理员可点击「邀请成员」按钮打开邀请成员弹窗**
|
||||
|
||||

|
||||
|
||||
2. **在邀请成员弹窗中,点击「创建邀请链接」按钮,创建邀请链接。**
|
||||
|
||||

|
||||
|
||||
3. **输入对应内容**
|
||||
|
||||

|
||||
|
||||
链接描述:建议将链接描述为使用场景或用途。链接创建后不支持修改噢。
|
||||
|
||||
有效期:30分钟,7天,1年
|
||||
|
||||
有效人数:1人,无限制
|
||||
|
||||
4. **点击复制链接,并将其发送给想要邀请的人。**
|
||||
|
||||

|
||||
|
||||
5. **用户访问链接后,如果未登录/未注册,则先跳转到登录页面进行登录。在登录后将进入团队页面,处理邀请。**
|
||||
|
||||
> 邀请链接形如:fastgpt.cn/account/team?invitelinkid=xxxx
|
||||
|
||||

|
||||
|
||||
点击接受,则用户将加入团队
|
||||
|
||||
点击忽略,则关闭弹窗,用户下次访问该邀请链接则还可以选择加入。
|
||||
|
||||
## 链接失效和自动清理
|
||||
|
||||
### 链接失效原因
|
||||
|
||||
手动停用链接
|
||||
|
||||
邀请链接到达有效期,自动停用
|
||||
|
||||
有效人数为1人的链接,已有1人通过邀请链接加入团队。
|
||||
|
||||
停用的链接无法访问,也无法再次启用。
|
||||
|
||||
### 链接上限
|
||||
|
||||
一个用户最多可以同时存在 10 个**有效的**邀请链接。
|
||||
|
||||
### 链接自动清理
|
||||
|
||||
失效的链接将在 30 天后自动清理。
|
||||
@@ -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",
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
export type GetPathProps = {
|
||||
sourceId?: ParentIdType;
|
||||
type: 'current' | 'parent';
|
||||
};
|
||||
|
||||
export type ParentTreePathItemType = {
|
||||
parentId: string;
|
||||
parentName: string;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
2
packages/global/core/ai/model.d.ts
vendored
@@ -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;
|
||||
};
|
||||
|
||||
23
packages/global/core/app/type.d.ts
vendored
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -133,6 +133,9 @@ export type DispatchNodeResponseType = {
|
||||
similarity?: number;
|
||||
limit?: number;
|
||||
searchMode?: `${DatasetSearchModeEnum}`;
|
||||
embeddingWeight?: number;
|
||||
rerankModel?: string;
|
||||
rerankWeight?: number;
|
||||
searchUsingReRank?: boolean;
|
||||
queryExtensionResult?: {
|
||||
model: string;
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 });
|
||||
|
||||
|
||||
3
packages/service/common/string/jieba/dict.json
Normal 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
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
8
packages/service/core/ai/type.d.ts
vendored
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
2
packages/service/type.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
import { FastGPTFeConfigsType, SystemEnvType } from '@fastgpt/global/common/system/types';
|
||||
import {
|
||||
TTSModelType,
|
||||
ReRankModelItemType,
|
||||
RerankModelItemType,
|
||||
STTModelType,
|
||||
EmbeddingModelItemType,
|
||||
LLMModelItemType
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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 |
@@ -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 |
3
packages/web/components/common/Icon/icons/sliderTag.svg
Normal 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 |
@@ -53,6 +53,7 @@ const MyModal = ({
|
||||
allowPinchZoom
|
||||
scrollBehavior={'inside'}
|
||||
closeOnOverlayClick={closeOnOverlayClick}
|
||||
returnFocusOnClose={false}
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent
|
||||
|
||||
@@ -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 && (
|
||||
<>
|
||||
|
||||
@@ -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?.();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "标题",
|
||||
|
||||
@@ -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": "上传文件/图片",
|
||||
|
||||
@@ -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": "分享链接",
|
||||
|
||||
@@ -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": "標題",
|
||||
|
||||
@@ -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": "上傳檔案 / 圖片",
|
||||
|
||||
@@ -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": "分享連結",
|
||||
|
||||
@@ -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
@@ -52,6 +52,8 @@ USE_IP_LIMIT=false
|
||||
WORKFLOW_MAX_RUN_TIMES=500
|
||||
# 循环最大运行次数,避免极端的死循环情况
|
||||
WORKFLOW_MAX_LOOP_TIMES=50
|
||||
# 启用内网 IP 检查
|
||||
CHECK_INTERNAL_IP=false
|
||||
|
||||
# 对话日志推送服务
|
||||
# # 日志服务地址
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
//next-i18next.config.js
|
||||
/**
|
||||
* @type {import('next-i18next').UserConfig}
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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 |
@@ -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}
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'}
|
||||
|
||||
@@ -59,7 +59,7 @@ type Props = BasicProps & {
|
||||
const RenderQuestionGuide = ({ questionGuides }: { questionGuides: string[] }) => {
|
||||
return (
|
||||
<Markdown
|
||||
source={`\`\`\`${CodeClassNameEnum.questionGuide}
|
||||
source={`\`\`\`${CodeClassNameEnum.questionguide}
|
||||
${JSON.stringify(questionGuides)}`}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 && (
|
||||
<>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -280,6 +280,10 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
|
||||
|
||||
isCustom: true,
|
||||
isActive: true,
|
||||
|
||||
isDefault: false,
|
||||
isDefaultDatasetTextModel: false,
|
||||
isDefaultDatasetImageModel: false,
|
||||
// @ts-ignore
|
||||
type
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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'}>
|
||||
|
||||
@@ -28,8 +28,6 @@ const SimpleEdit = () => {
|
||||
|
||||
// Init app form
|
||||
useMount(() => {
|
||||
// show selected dataset
|
||||
|
||||
if (appDetail.version !== 'v2') {
|
||||
return setAppForm(
|
||||
appWorkflow2Form({
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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: ''
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 = ({
|
||||
|
||||
@@ -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 = ({
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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();
|
||||
|
||||