Compare commits
10 Commits
v4.9.8-alp
...
v4.9.9-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
554b2ca8dc | ||
|
|
4e83840c14 | ||
|
|
a6c80684d1 | ||
|
|
a4db03a3b7 | ||
|
|
cba8f773fe | ||
|
|
bd93f28d6f | ||
|
|
2063cb6314 | ||
|
|
12acaf491c | ||
|
|
3688842cc7 | ||
|
|
398d131bac |
@@ -132,15 +132,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.7-fix2 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.9.8 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.7-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.8 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt-mcp-server:
|
fastgpt-mcp-server:
|
||||||
container_name: fastgpt-mcp-server
|
container_name: fastgpt-mcp-server
|
||||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.7-fix2 # git
|
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.8 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.7-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.8 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3005:3000
|
- 3005:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -150,8 +150,8 @@ services:
|
|||||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.9.7-fix2 # git
|
image: ghcr.io/labring/fastgpt:v4.9.8 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.7-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.8 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -109,15 +109,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.7-fix2 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.9.8 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.7-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.8 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt-mcp-server:
|
fastgpt-mcp-server:
|
||||||
container_name: fastgpt-mcp-server
|
container_name: fastgpt-mcp-server
|
||||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.7-fix2 # git
|
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.8 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.7-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.8 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3005:3000
|
- 3005:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -127,8 +127,8 @@ services:
|
|||||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.9.7-fix2 # git
|
image: ghcr.io/labring/fastgpt:v4.9.8 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.7-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.8 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -96,15 +96,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.7-fix2 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.9.8 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.7-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.8 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt-mcp-server:
|
fastgpt-mcp-server:
|
||||||
container_name: fastgpt-mcp-server
|
container_name: fastgpt-mcp-server
|
||||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.7-fix2 # git
|
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.8 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.7-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.8 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3005:3000
|
- 3005:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -114,8 +114,8 @@ services:
|
|||||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.9.7-fix2 # git
|
image: ghcr.io/labring/fastgpt:v4.9.8 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.7-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.8 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -72,15 +72,15 @@ services:
|
|||||||
|
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.7-fix2 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.9.8 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.7-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.8 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt-mcp-server:
|
fastgpt-mcp-server:
|
||||||
container_name: fastgpt-mcp-server
|
container_name: fastgpt-mcp-server
|
||||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.7-fix2 # git
|
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.8 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.7-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.8 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3005:3000
|
- 3005:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -90,8 +90,8 @@ services:
|
|||||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.9.7-fix2 # git
|
image: ghcr.io/labring/fastgpt:v4.9.8 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.7-fix2 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.8 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: 'V4.9.8(进行中)'
|
title: 'V4.9.8'
|
||||||
description: 'FastGPT V4.9.8 更新说明'
|
description: 'FastGPT V4.9.8 更新说明'
|
||||||
icon: 'upgrade'
|
icon: 'upgrade'
|
||||||
draft: false
|
draft: false
|
||||||
@@ -7,6 +7,17 @@ toc: true
|
|||||||
weight: 792
|
weight: 792
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 升级指南
|
||||||
|
|
||||||
|
### 1. 做好数据备份
|
||||||
|
|
||||||
|
### 2. 更新镜像 tag
|
||||||
|
|
||||||
|
- 更新 FastGPT 镜像 tag: v4.9.8
|
||||||
|
- 更新 FastGPT 商业版镜像 tag: v4.9.8
|
||||||
|
- mcp_server 无需更新
|
||||||
|
- Sandbox 无需更新
|
||||||
|
- AIProxy 无需更新
|
||||||
|
|
||||||
## 🚀 新增内容
|
## 🚀 新增内容
|
||||||
|
|
||||||
|
|||||||
25
docSite/content/zh-cn/docs/development/upgrading/499.md
Normal file
25
docSite/content/zh-cn/docs/development/upgrading/499.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
title: 'V4.9.9(进行中)'
|
||||||
|
description: 'FastGPT V4.9.9 更新说明'
|
||||||
|
icon: 'upgrade'
|
||||||
|
draft: false
|
||||||
|
toc: true
|
||||||
|
weight: 791
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## 🚀 新增内容
|
||||||
|
|
||||||
|
1. 切换 SessionId 来替代 JWT 实现登录鉴权,可控制最大登录客户端数量。
|
||||||
|
2. 新的商业版 License 管理模式。
|
||||||
|
|
||||||
|
## ⚙️ 优化
|
||||||
|
|
||||||
|
1. 优化工具调用,新工具的判断逻辑。
|
||||||
|
2. 调整 Cite 引用提示词。
|
||||||
|
|
||||||
|
## 🐛 修复
|
||||||
|
|
||||||
|
1. 无法正常获取应用历史保存/发布记录。
|
||||||
|
2. 成员创建 MCP 工具权限问题。
|
||||||
|
3. 来源引用展示,存在 ID 传递错误,导致提示无权操作该文件。
|
||||||
@@ -43,7 +43,7 @@ type ResponseType = {
|
|||||||
// 文件列表中,单项的文件类型
|
// 文件列表中,单项的文件类型
|
||||||
type FileListItem = {
|
type FileListItem = {
|
||||||
id: string;
|
id: string;
|
||||||
parentId: string | null;
|
parentId: string //也可能为 null 或者 undefined 类型;
|
||||||
name: string;
|
name: string;
|
||||||
type: 'file' | 'folder';
|
type: 'file' | 'folder';
|
||||||
updateTime: Date;
|
updateTime: Date;
|
||||||
@@ -59,7 +59,7 @@ type FileListItem = {
|
|||||||
{{< markdownify >}}
|
{{< markdownify >}}
|
||||||
|
|
||||||
{{% alert icon=" " context="success" %}}
|
{{% alert icon=" " context="success" %}}
|
||||||
- parentId - 父级 id,可选,或者 null。
|
- parentId - 父级 id,可选,或者 null | undefined。
|
||||||
- searchKey - 检索词,可选
|
- searchKey - 检索词,可选
|
||||||
{{% /alert %}}
|
{{% /alert %}}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ curl --location --request POST '{{baseURL}}/v1/file/list' \
|
|||||||
--header 'Authorization: Bearer {{authorization}}' \
|
--header 'Authorization: Bearer {{authorization}}' \
|
||||||
--header 'Content-Type: application/json' \
|
--header 'Content-Type: application/json' \
|
||||||
--data-raw '{
|
--data-raw '{
|
||||||
"parentId": null,
|
"parentId": "",
|
||||||
"searchKey": ""
|
"searchKey": ""
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|||||||
2
env.d.ts
vendored
2
env.d.ts
vendored
@@ -4,7 +4,6 @@ declare global {
|
|||||||
LOG_DEPTH: string;
|
LOG_DEPTH: string;
|
||||||
DEFAULT_ROOT_PSW: string;
|
DEFAULT_ROOT_PSW: string;
|
||||||
DB_MAX_LINK: string;
|
DB_MAX_LINK: string;
|
||||||
TOKEN_KEY: string;
|
|
||||||
FILE_TOKEN_KEY: string;
|
FILE_TOKEN_KEY: string;
|
||||||
ROOT_KEY: string;
|
ROOT_KEY: string;
|
||||||
OPENAI_BASE_URL: string;
|
OPENAI_BASE_URL: string;
|
||||||
@@ -37,6 +36,7 @@ declare global {
|
|||||||
CONFIG_JSON_PATH?: string;
|
CONFIG_JSON_PATH?: string;
|
||||||
PASSWORD_LOGIN_LOCK_SECONDS?: string;
|
PASSWORD_LOGIN_LOCK_SECONDS?: string;
|
||||||
PASSWORD_EXPIRED_MONTH?: string;
|
PASSWORD_EXPIRED_MONTH?: string;
|
||||||
|
MAX_LOGIN_SESSION?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,28 @@ import { type ErrType } from '../errorCode';
|
|||||||
import { i18nT } from '../../../../web/i18n/utils';
|
import { i18nT } from '../../../../web/i18n/utils';
|
||||||
/* dataset: 509000 */
|
/* dataset: 509000 */
|
||||||
export enum SystemErrEnum {
|
export enum SystemErrEnum {
|
||||||
communityVersionNumLimit = 'communityVersionNumLimit'
|
communityVersionNumLimit = 'communityVersionNumLimit',
|
||||||
|
licenseAppAmountLimit = 'licenseAppAmountLimit',
|
||||||
|
licenseDatasetAmountLimit = 'licenseDatasetAmountLimit',
|
||||||
|
licenseUserAmountLimit = 'licenseUserAmountLimit'
|
||||||
}
|
}
|
||||||
|
|
||||||
const systemErr = [
|
const systemErr = [
|
||||||
{
|
{
|
||||||
statusText: SystemErrEnum.communityVersionNumLimit,
|
statusText: SystemErrEnum.communityVersionNumLimit,
|
||||||
message: i18nT('common:code_error.system_error.community_version_num_limit')
|
message: i18nT('common:code_error.system_error.community_version_num_limit')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: SystemErrEnum.licenseAppAmountLimit,
|
||||||
|
message: i18nT('common:code_error.system_error.license_app_amount_limit')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: SystemErrEnum.licenseDatasetAmountLimit,
|
||||||
|
message: i18nT('common:code_error.system_error.license_dataset_amount_limit')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: SystemErrEnum.licenseUserAmountLimit,
|
||||||
|
message: i18nT('common:code_error.system_error.license_user_amount_limit')
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export const checkPasswordRule = (password: string) => {
|
|||||||
/[A-Z]/, // Contains uppercase letters
|
/[A-Z]/, // Contains uppercase letters
|
||||||
/[!@#$%^&*()_+=-]/ // Contains special characters
|
/[!@#$%^&*()_+=-]/ // Contains special characters
|
||||||
];
|
];
|
||||||
const validChars = /^[\dA-Za-z!@#$%^&*()_+=-]{6,100}$/;
|
const validChars = /^[\dA-Za-z!@#$%^&*()_+=-]{8,100}$/;
|
||||||
|
|
||||||
// Check length and valid characters
|
// Check length and valid characters
|
||||||
if (!validChars.test(password)) return false;
|
if (!validChars.test(password)) return false;
|
||||||
|
|||||||
21
packages/global/common/system/types/index.d.ts
vendored
21
packages/global/common/system/types/index.d.ts
vendored
@@ -70,6 +70,9 @@ export type FastGPTFeConfigsType = {
|
|||||||
show_publish_dingtalk?: boolean;
|
show_publish_dingtalk?: boolean;
|
||||||
show_publish_offiaccount?: boolean;
|
show_publish_offiaccount?: boolean;
|
||||||
|
|
||||||
|
show_dataset_enhance?: boolean;
|
||||||
|
show_batch_eval?: boolean;
|
||||||
|
|
||||||
concatMd?: string;
|
concatMd?: string;
|
||||||
docUrl?: string;
|
docUrl?: string;
|
||||||
openAPIDocUrl?: string;
|
openAPIDocUrl?: string;
|
||||||
@@ -142,3 +145,21 @@ export type customPdfParseType = {
|
|||||||
doc2xKey?: string;
|
doc2xKey?: string;
|
||||||
price?: number;
|
price?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type LicenseDataType = {
|
||||||
|
startTime: string;
|
||||||
|
expiredTime: string;
|
||||||
|
company: string;
|
||||||
|
description?: string; // 描述
|
||||||
|
hosts?: string[]; // 管理端有效域名
|
||||||
|
maxUsers?: number; // 最大用户数,不填默认不上限
|
||||||
|
maxApps?: number; // 最大应用数,不填默认不上限
|
||||||
|
maxDatasets?: number; // 最大数据集数,不填默认不上限
|
||||||
|
functions: {
|
||||||
|
sso: boolean;
|
||||||
|
pay: boolean;
|
||||||
|
customTemplates: boolean;
|
||||||
|
datasetEnhance: boolean;
|
||||||
|
batchEval: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -2,6 +2,248 @@ import { type PromptTemplateItem } from '../type.d';
|
|||||||
import { i18nT } from '../../../../web/i18n/utils';
|
import { i18nT } from '../../../../web/i18n/utils';
|
||||||
import { getPromptByVersion } from './utils';
|
import { getPromptByVersion } from './utils';
|
||||||
|
|
||||||
|
export const Prompt_userQuotePromptList: PromptTemplateItem[] = [
|
||||||
|
{
|
||||||
|
title: i18nT('app:template.standard_template'),
|
||||||
|
desc: '',
|
||||||
|
value: {
|
||||||
|
['4.9.7']: `## 任务描述
|
||||||
|
你是一个知识库回答助手,可以使用 <Cites></Cites> 中的内容作为你本次回答的参考。
|
||||||
|
同时,为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容。
|
||||||
|
|
||||||
|
## 追溯展示规则
|
||||||
|
|
||||||
|
- 使用 [id](CITE) 的格式来引用 <Cites></Cites> 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
|
||||||
|
- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
|
||||||
|
- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
|
||||||
|
- 不要把示例作为知识点。
|
||||||
|
- 不要伪造 id,返回的 id 必须都存在 <Cites></Cites> 中!
|
||||||
|
|
||||||
|
## 通用规则
|
||||||
|
|
||||||
|
- 如果你不清楚答案,你需要澄清。
|
||||||
|
- 避免提及你是从 <Cites></Cites> 获取的知识。
|
||||||
|
- 保持答案与 <Cites></Cites> 中描述的一致。
|
||||||
|
- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
|
||||||
|
- 使用与问题相同的语言回答。
|
||||||
|
|
||||||
|
<Cites>
|
||||||
|
{{quote}}
|
||||||
|
</Cites>
|
||||||
|
|
||||||
|
## 用户问题
|
||||||
|
|
||||||
|
{{question}}
|
||||||
|
|
||||||
|
## 回答
|
||||||
|
`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18nT('app:template.qa_template'),
|
||||||
|
desc: '',
|
||||||
|
value: {
|
||||||
|
['4.9.7']: `## 任务描述
|
||||||
|
作为一个问答助手,你会使用 <QA></QA> 标记中的提供的数据对进行内容回答。
|
||||||
|
|
||||||
|
## 回答要求
|
||||||
|
- 选择其中一个或多个问答对进行回答。
|
||||||
|
- 回答的内容应尽可能与 <Answer></Answer> 中的内容一致。
|
||||||
|
- 如果没有相关的问答对,你需要澄清。
|
||||||
|
- 避免提及你是从 <QA></QA> 获取的知识,只需要回复答案。
|
||||||
|
- 使用与问题相同的语言回答。
|
||||||
|
|
||||||
|
<QA>
|
||||||
|
{{quote}}
|
||||||
|
</QA>
|
||||||
|
|
||||||
|
## 用户问题
|
||||||
|
|
||||||
|
{{question}}
|
||||||
|
|
||||||
|
## 回答
|
||||||
|
`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18nT('app:template.standard_strict'),
|
||||||
|
desc: '',
|
||||||
|
value: {
|
||||||
|
['4.9.7']: `## 任务描述
|
||||||
|
你是一个知识库回答助手,可以使用 <Cites></Cites> 中的内容作为你本次回答的参考。
|
||||||
|
同时,为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容。
|
||||||
|
|
||||||
|
## 追溯展示规则
|
||||||
|
|
||||||
|
- 使用 [id](CITE) 的格式来引用 <Cites></Cites> 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
|
||||||
|
- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
|
||||||
|
- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
|
||||||
|
- 不要把示例作为知识点。
|
||||||
|
- 不要伪造 id,返回的 id 必须都存在 <Cites></Cites> 中!
|
||||||
|
|
||||||
|
## 通用规则
|
||||||
|
|
||||||
|
- 如果你不清楚答案,你需要澄清。
|
||||||
|
- 避免提及你是从 <Cites></Cites> 获取的知识。
|
||||||
|
- 保持答案与 <Cites></Cites> 中描述的一致。
|
||||||
|
- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
|
||||||
|
- 使用与问题相同的语言回答。
|
||||||
|
|
||||||
|
## 严格要求
|
||||||
|
|
||||||
|
你只能使用 <Cites></Cites> 标记中的内容作为参考,不能使用自身的知识,并且回答的内容需严格与 <Cites></Cites> 中的内容一致。
|
||||||
|
|
||||||
|
<Cites>
|
||||||
|
{{quote}}
|
||||||
|
</Cites>
|
||||||
|
|
||||||
|
## 用户问题
|
||||||
|
|
||||||
|
{{question}}
|
||||||
|
|
||||||
|
## 回答
|
||||||
|
`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18nT('app:template.hard_strict'),
|
||||||
|
desc: '',
|
||||||
|
value: {
|
||||||
|
['4.9.7']: `## 任务描述
|
||||||
|
作为一个问答助手,你会使用 <QA></QA> 标记中的提供的数据对进行内容回答。
|
||||||
|
|
||||||
|
## 回答要求
|
||||||
|
- 选择其中一个或多个问答对进行回答。
|
||||||
|
- 回答的内容应尽可能与 <Answer></Answer> 中的内容一致。
|
||||||
|
- 如果没有相关的问答对,你需要澄清。
|
||||||
|
- 避免提及你是从 <QA></QA> 获取的知识,只需要回复答案。
|
||||||
|
- 使用与问题相同的语言回答。
|
||||||
|
|
||||||
|
## 严格要求
|
||||||
|
|
||||||
|
你只能使用 <QA></QA> 标记中的内容作为参考,不能使用自身的知识,并且回答的内容需严格与 <QA></QA> 中的内容一致。
|
||||||
|
|
||||||
|
<QA>
|
||||||
|
{{quote}}
|
||||||
|
</QA>
|
||||||
|
|
||||||
|
## 用户问题
|
||||||
|
|
||||||
|
{{question}}
|
||||||
|
|
||||||
|
## 回答
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
|
||||||
|
{
|
||||||
|
title: i18nT('app:template.standard_template'),
|
||||||
|
desc: '',
|
||||||
|
value: {
|
||||||
|
['4.9.7']: `## 任务描述
|
||||||
|
你是一个知识库回答助手,可以使用 <Cites></Cites> 中的内容作为你本次回答的参考。
|
||||||
|
同时,为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容。
|
||||||
|
|
||||||
|
## 追溯展示规则
|
||||||
|
|
||||||
|
- 使用 [id](CITE) 的格式来引用 <Cites></Cites> 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
|
||||||
|
- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
|
||||||
|
- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
|
||||||
|
- 不要把示例作为知识点。
|
||||||
|
- 不要伪造 id,返回的 id 必须都存在 <Cites></Cites> 中!
|
||||||
|
|
||||||
|
## 通用规则
|
||||||
|
|
||||||
|
- 如果你不清楚答案,你需要澄清。
|
||||||
|
- 避免提及你是从 <Cites></Cites> 获取的知识。
|
||||||
|
- 保持答案与 <Cites></Cites> 中描述的一致。
|
||||||
|
- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
|
||||||
|
- 使用与问题相同的语言回答。
|
||||||
|
|
||||||
|
<Cites>
|
||||||
|
{{quote}}
|
||||||
|
</Cites>`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18nT('app:template.qa_template'),
|
||||||
|
desc: '',
|
||||||
|
value: {
|
||||||
|
['4.9.8']: `## 任务描述
|
||||||
|
作为一个问答助手,你会使用 <QA></QA> 标记中的提供的数据对进行内容回答。
|
||||||
|
|
||||||
|
## 回答要求
|
||||||
|
- 选择其中一个或多个问答对进行回答。
|
||||||
|
- 回答的内容应尽可能与 <Answer></Answer> 中的内容一致。
|
||||||
|
- 如果没有相关的问答对,你需要澄清。
|
||||||
|
- 避免提及你是从 <QA></QA> 获取的知识,只需要回复答案。
|
||||||
|
- 使用与问题相同的语言回答。
|
||||||
|
|
||||||
|
<QA>
|
||||||
|
{{quote}}
|
||||||
|
</QA>`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18nT('app:template.standard_strict'),
|
||||||
|
desc: '',
|
||||||
|
value: {
|
||||||
|
['4.9.7']: `## 任务描述
|
||||||
|
你是一个知识库回答助手,可以使用 <Cites></Cites> 中的内容作为你本次回答的参考。
|
||||||
|
同时,为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容。
|
||||||
|
|
||||||
|
## 追溯展示规则
|
||||||
|
|
||||||
|
- 使用 [id](CITE) 的格式来引用 <Cites></Cites> 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
|
||||||
|
- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
|
||||||
|
- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
|
||||||
|
- 不要把示例作为知识点。
|
||||||
|
- 不要伪造 id,返回的 id 必须都存在 <Cites></Cites> 中!
|
||||||
|
|
||||||
|
## 通用规则
|
||||||
|
|
||||||
|
- 如果你不清楚答案,你需要澄清。
|
||||||
|
- 避免提及你是从 <Cites></Cites> 获取的知识。
|
||||||
|
- 保持答案与 <Cites></Cites> 中描述的一致。
|
||||||
|
- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
|
||||||
|
- 使用与问题相同的语言回答。
|
||||||
|
|
||||||
|
## 严格要求
|
||||||
|
|
||||||
|
你只能使用 <Cites></Cites> 标记中的内容作为参考,不能使用自身的知识,并且回答的内容需严格与 <Cites></Cites> 中的内容一致。
|
||||||
|
|
||||||
|
<Cites>
|
||||||
|
{{quote}}
|
||||||
|
</Cites>`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18nT('app:template.hard_strict'),
|
||||||
|
desc: '',
|
||||||
|
value: {
|
||||||
|
['4.9.7']: `## 任务描述
|
||||||
|
作为一个问答助手,你会使用 <QA></QA> 标记中的提供的数据对进行内容回答。
|
||||||
|
|
||||||
|
## 回答要求
|
||||||
|
- 选择其中一个或多个问答对进行回答。
|
||||||
|
- 回答的内容应尽可能与 <Answer></Answer> 中的内容一致。
|
||||||
|
- 如果没有相关的问答对,你需要澄清。
|
||||||
|
- 避免提及你是从 <QA></QA> 获取的知识,只需要回复答案。
|
||||||
|
- 使用与问题相同的语言回答。
|
||||||
|
|
||||||
|
## 严格要求
|
||||||
|
|
||||||
|
你只能使用 <QA></QA> 标记中的内容作为参考,不能使用自身的知识,并且回答的内容需严格与 <QA></QA> 中的内容一致。
|
||||||
|
|
||||||
|
<QA>
|
||||||
|
{{quote}}
|
||||||
|
</QA>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
||||||
{
|
{
|
||||||
title: i18nT('app:template.standard_template'),
|
title: i18nT('app:template.standard_template'),
|
||||||
@@ -10,11 +252,6 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
|||||||
['4.9.7']: `{
|
['4.9.7']: `{
|
||||||
"id": "{{id}}",
|
"id": "{{id}}",
|
||||||
"sourceName": "{{source}}",
|
"sourceName": "{{source}}",
|
||||||
"content": "{{q}}\n{{a}}"
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
['4.9.2']: `{
|
|
||||||
"sourceName": "{{source}}",
|
|
||||||
"updateTime": "{{updateTime}}",
|
"updateTime": "{{updateTime}}",
|
||||||
"content": "{{q}}\n{{a}}"
|
"content": "{{q}}\n{{a}}"
|
||||||
}
|
}
|
||||||
@@ -25,7 +262,7 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
|||||||
title: i18nT('app:template.qa_template'),
|
title: i18nT('app:template.qa_template'),
|
||||||
desc: i18nT('app:template.qa_template_des'),
|
desc: i18nT('app:template.qa_template_des'),
|
||||||
value: {
|
value: {
|
||||||
['4.9.2']: `<Question>
|
['4.9.7']: `<Question>
|
||||||
{{q}}
|
{{q}}
|
||||||
</Question>
|
</Question>
|
||||||
<Answer>
|
<Answer>
|
||||||
@@ -40,11 +277,6 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
|||||||
['4.9.7']: `{
|
['4.9.7']: `{
|
||||||
"id": "{{id}}",
|
"id": "{{id}}",
|
||||||
"sourceName": "{{source}}",
|
"sourceName": "{{source}}",
|
||||||
"content": "{{q}}\n{{a}}"
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
['4.9.2']: `{
|
|
||||||
"sourceName": "{{source}}",
|
|
||||||
"updateTime": "{{updateTime}}",
|
"updateTime": "{{updateTime}}",
|
||||||
"content": "{{q}}\n{{a}}"
|
"content": "{{q}}\n{{a}}"
|
||||||
}
|
}
|
||||||
@@ -55,7 +287,7 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
|||||||
title: i18nT('app:template.hard_strict'),
|
title: i18nT('app:template.hard_strict'),
|
||||||
desc: i18nT('app:template.hard_strict_des'),
|
desc: i18nT('app:template.hard_strict_des'),
|
||||||
value: {
|
value: {
|
||||||
['4.9.2']: `<Question>
|
['4.9.7']: `<Question>
|
||||||
{{q}}
|
{{q}}
|
||||||
</Question>
|
</Question>
|
||||||
<Answer>
|
<Answer>
|
||||||
@@ -64,263 +296,12 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export const getQuoteTemplate = (version?: string) => {
|
export const getQuoteTemplate = (version?: string) => {
|
||||||
const defaultTemplate = Prompt_QuoteTemplateList[0].value;
|
const defaultTemplate = Prompt_QuoteTemplateList[0].value;
|
||||||
|
|
||||||
return getPromptByVersion(version, defaultTemplate);
|
return getPromptByVersion(version, defaultTemplate);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Prompt_userQuotePromptList: PromptTemplateItem[] = [
|
|
||||||
{
|
|
||||||
title: i18nT('app:template.standard_template'),
|
|
||||||
desc: '',
|
|
||||||
value: {
|
|
||||||
['4.9.7']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
|
||||||
|
|
||||||
<Reference>
|
|
||||||
{{quote}}
|
|
||||||
</Reference>
|
|
||||||
|
|
||||||
回答要求:
|
|
||||||
- 如果你不清楚答案,你需要澄清。
|
|
||||||
- 避免提及你是从 <Reference></Reference> 获取的知识。
|
|
||||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
|
||||||
- 使用 Markdown 语法优化回答格式。
|
|
||||||
- 使用与问题相同的语言回答。
|
|
||||||
- 使用 [id](CITE) 格式来引用<Reference></Reference>中的知识,其中 CITE 是固定常量, id 为引文中的 id。
|
|
||||||
- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
|
|
||||||
- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。`,
|
|
||||||
['4.9.2']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
|
||||||
|
|
||||||
<Reference>
|
|
||||||
{{quote}}
|
|
||||||
</Reference>
|
|
||||||
|
|
||||||
回答要求:
|
|
||||||
- 如果你不清楚答案,你需要澄清。
|
|
||||||
- 避免提及你是从 <Reference></Reference> 获取的知识。
|
|
||||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
|
||||||
- 使用 Markdown 语法优化回答格式。
|
|
||||||
- 使用与问题相同的语言回答。
|
|
||||||
|
|
||||||
问题:"""{{question}}"""`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: i18nT('app:template.qa_template'),
|
|
||||||
desc: '',
|
|
||||||
value: {
|
|
||||||
['4.9.2']: `使用 <QA></QA> 标记中的问答对进行回答。
|
|
||||||
|
|
||||||
<QA>
|
|
||||||
{{quote}}
|
|
||||||
</QA>
|
|
||||||
|
|
||||||
回答要求:
|
|
||||||
- 选择其中一个或多个问答对进行回答。
|
|
||||||
- 回答的内容应尽可能与 <答案></答案> 中的内容一致。
|
|
||||||
- 如果没有相关的问答对,你需要澄清。
|
|
||||||
- 避免提及你是从 QA 获取的知识,只需要回复答案。
|
|
||||||
|
|
||||||
问题:"""{{question}}"""`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: i18nT('app:template.standard_strict'),
|
|
||||||
desc: '',
|
|
||||||
value: {
|
|
||||||
['4.9.7']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
|
||||||
|
|
||||||
<Reference>
|
|
||||||
{{quote}}
|
|
||||||
</Reference>
|
|
||||||
|
|
||||||
思考流程:
|
|
||||||
1. 判断问题是否与 <Reference></Reference> 标记中的内容有关。
|
|
||||||
2. 如果有关,你按下面的要求回答。
|
|
||||||
3. 如果无关,你直接拒绝回答本次问题。
|
|
||||||
|
|
||||||
回答要求:
|
|
||||||
- 避免提及你是从 <Reference></Reference> 获取的知识。
|
|
||||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
|
||||||
- 使用 Markdown 语法优化回答格式。
|
|
||||||
- 使用与问题相同的语言回答。
|
|
||||||
- 使用 [id](CITE) 格式来引用<Reference></Reference>中的知识,其中 CITE 是固定常量, id 为引文中的 id。
|
|
||||||
- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
|
|
||||||
- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。
|
|
||||||
|
|
||||||
问题:"""{{question}}"""`,
|
|
||||||
['4.9.2']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
|
||||||
|
|
||||||
<Reference>
|
|
||||||
{{quote}}
|
|
||||||
</Reference>
|
|
||||||
|
|
||||||
思考流程:
|
|
||||||
1. 判断问题是否与 <Reference></Reference> 标记中的内容有关。
|
|
||||||
2. 如果有关,你按下面的要求回答。
|
|
||||||
3. 如果无关,你直接拒绝回答本次问题。
|
|
||||||
|
|
||||||
回答要求:
|
|
||||||
- 避免提及你是从 <Reference></Reference> 获取的知识。
|
|
||||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
|
||||||
- 使用 Markdown 语法优化回答格式。
|
|
||||||
- 使用与问题相同的语言回答。
|
|
||||||
|
|
||||||
问题:"""{{question}}"""`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: i18nT('app:template.hard_strict'),
|
|
||||||
desc: '',
|
|
||||||
value: {
|
|
||||||
['4.9.2']: `忘记你已有的知识,仅使用 <QA></QA> 标记中的问答对进行回答。
|
|
||||||
|
|
||||||
<QA>
|
|
||||||
{{quote}}
|
|
||||||
</QA>
|
|
||||||
|
|
||||||
思考流程:
|
|
||||||
1. 判断问题是否与 <QA></QA> 标记中的内容有关。
|
|
||||||
2. 如果无关,你直接拒绝回答本次问题。
|
|
||||||
3. 判断是否有相近或相同的问题。
|
|
||||||
4. 如果有相同的问题,直接输出对应答案。
|
|
||||||
5. 如果只有相近的问题,请把相近的问题和答案一起输出。
|
|
||||||
|
|
||||||
回答要求:
|
|
||||||
- 如果没有相关的问答对,你需要澄清。
|
|
||||||
- 回答的内容应尽可能与 <QA></QA> 标记中的内容一致。
|
|
||||||
- 避免提及你是从 QA 获取的知识,只需要回复答案。
|
|
||||||
- 使用 Markdown 语法优化回答格式。
|
|
||||||
- 使用与问题相同的语言回答。
|
|
||||||
|
|
||||||
问题:"""{{question}}"""`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
|
|
||||||
{
|
|
||||||
title: i18nT('app:template.standard_template'),
|
|
||||||
desc: '',
|
|
||||||
value: {
|
|
||||||
['4.9.7']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
|
||||||
|
|
||||||
<Reference>
|
|
||||||
{{quote}}
|
|
||||||
</Reference>
|
|
||||||
|
|
||||||
回答要求:
|
|
||||||
- 如果你不清楚答案,你需要澄清。
|
|
||||||
- 避免提及你是从 <Reference></Reference> 获取的知识。
|
|
||||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
|
||||||
- 使用 Markdown 语法优化回答格式。
|
|
||||||
- 使用与问题相同的语言回答。
|
|
||||||
- 使用 [id](CITE) 格式来引用<Reference></Reference>中的知识,其中 CITE 是固定常量, id 为引文中的 id。
|
|
||||||
- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
|
|
||||||
- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。`,
|
|
||||||
['4.9.2']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
|
||||||
|
|
||||||
<Reference>
|
|
||||||
{{quote}}
|
|
||||||
</Reference>
|
|
||||||
|
|
||||||
回答要求:
|
|
||||||
- 如果你不清楚答案,你需要澄清。
|
|
||||||
- 避免提及你是从 <Reference></Reference> 获取的知识。
|
|
||||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
|
||||||
- 使用 Markdown 语法优化回答格式。
|
|
||||||
- 使用与问题相同的语言回答。`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: i18nT('app:template.qa_template'),
|
|
||||||
desc: '',
|
|
||||||
value: {
|
|
||||||
['4.9.2']: `使用 <QA></QA> 标记中的问答对进行回答。
|
|
||||||
|
|
||||||
<QA>
|
|
||||||
{{quote}}
|
|
||||||
</QA>
|
|
||||||
|
|
||||||
回答要求:
|
|
||||||
- 选择其中一个或多个问答对进行回答。
|
|
||||||
- 回答的内容应尽可能与 <答案></答案> 中的内容一致。
|
|
||||||
- 如果没有相关的问答对,你需要澄清。
|
|
||||||
- 避免提及你是从 QA 获取的知识,只需要回复答案。`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: i18nT('app:template.standard_strict'),
|
|
||||||
desc: '',
|
|
||||||
value: {
|
|
||||||
['4.9.7']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
|
||||||
|
|
||||||
<Reference>
|
|
||||||
{{quote}}
|
|
||||||
</Reference>
|
|
||||||
|
|
||||||
思考流程:
|
|
||||||
1. 判断问题是否与 <Reference></Reference> 标记中的内容有关。
|
|
||||||
2. 如果有关,你按下面的要求回答。
|
|
||||||
3. 如果无关,你直接拒绝回答本次问题。
|
|
||||||
|
|
||||||
回答要求:
|
|
||||||
- 避免提及你是从 <Reference></Reference> 获取的知识。
|
|
||||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
|
||||||
- 使用 Markdown 语法优化回答格式。
|
|
||||||
- 使用与问题相同的语言回答。
|
|
||||||
- 使用 [id](CITE) 格式来引用<Reference></Reference>中的知识,其中 CITE 是固定常量, id 为引文中的 id。
|
|
||||||
- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
|
|
||||||
- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。
|
|
||||||
|
|
||||||
问题:"""{{question}}"""`,
|
|
||||||
['4.9.2']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
|
||||||
|
|
||||||
<Reference>
|
|
||||||
{{quote}}
|
|
||||||
</Reference>
|
|
||||||
|
|
||||||
思考流程:
|
|
||||||
1. 判断问题是否与 <Reference></Reference> 标记中的内容有关。
|
|
||||||
2. 如果有关,你按下面的要求回答。
|
|
||||||
3. 如果无关,你直接拒绝回答本次问题。
|
|
||||||
|
|
||||||
回答要求:
|
|
||||||
- 避免提及你是从 <Reference></Reference> 获取的知识。
|
|
||||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
|
||||||
- 使用 Markdown 语法优化回答格式。
|
|
||||||
- 使用与问题相同的语言回答。`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: i18nT('app:template.hard_strict'),
|
|
||||||
desc: '',
|
|
||||||
value: {
|
|
||||||
['4.9.2']: `忘记你已有的知识,仅使用 <QA></QA> 标记中的问答对进行回答。
|
|
||||||
|
|
||||||
<QA>
|
|
||||||
{{quote}}
|
|
||||||
</QA>
|
|
||||||
|
|
||||||
思考流程:
|
|
||||||
1. 判断问题是否与 <QA></QA> 标记中的内容有关。
|
|
||||||
2. 如果无关,你直接拒绝回答本次问题。
|
|
||||||
3. 判断是否有相近或相同的问题。
|
|
||||||
4. 如果有相同的问题,直接输出对应答案。
|
|
||||||
5. 如果只有相近的问题,请把相近的问题和答案一起输出。
|
|
||||||
|
|
||||||
回答要求:
|
|
||||||
- 如果没有相关的问答对,你需要澄清。
|
|
||||||
- 回答的内容应尽可能与 <QA></QA> 标记中的内容一致。
|
|
||||||
- 避免提及你是从 QA 获取的知识,只需要回复答案。
|
|
||||||
- 使用 Markdown 语法优化回答格式。
|
|
||||||
- 使用与问题相同的语言回答。`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const getQuotePrompt = (version?: string, role: 'user' | 'system' = 'user') => {
|
export const getQuotePrompt = (version?: string, role: 'user' | 'system' = 'user') => {
|
||||||
const quotePromptTemplates =
|
const quotePromptTemplates =
|
||||||
role === 'user' ? Prompt_userQuotePromptList : Prompt_systemQuotePromptList;
|
role === 'user' ? Prompt_userQuotePromptList : Prompt_systemQuotePromptList;
|
||||||
@@ -333,7 +314,7 @@ export const getQuotePrompt = (version?: string, role: 'user' | 'system' = 'user
|
|||||||
// Document quote prompt
|
// Document quote prompt
|
||||||
export const getDocumentQuotePrompt = (version?: string) => {
|
export const getDocumentQuotePrompt = (version?: string) => {
|
||||||
const promptMap = {
|
const promptMap = {
|
||||||
['4.9.2']: `将 <FilesContent></FilesContent> 中的内容作为本次对话的参考:
|
['4.9.7']: `将 <FilesContent></FilesContent> 中的内容作为本次对话的参考:
|
||||||
<FilesContent>
|
<FilesContent>
|
||||||
{{quote}}
|
{{quote}}
|
||||||
</FilesContent>
|
</FilesContent>
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
export const getDatasetSearchToolResponsePrompt = () => {
|
export const getDatasetSearchToolResponsePrompt = () => {
|
||||||
return `## Role
|
return `## Role
|
||||||
你是一个知识库回答助手,可以 "quotes" 中的内容作为本次对话的参考。为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记。
|
你是一个知识库回答助手,可以 "cites" 中的内容作为本次对话的参考。为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容。
|
||||||
|
|
||||||
## Rules
|
## 追溯展示规则
|
||||||
|
|
||||||
|
- 使用 **[id](CITE)** 格式来引用 "cites" 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
|
||||||
|
- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
|
||||||
|
- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
|
||||||
|
- 不要把示例作为知识点。
|
||||||
|
- 不要伪造 id,返回的 id 必须都存在 cites 中!
|
||||||
|
|
||||||
|
## 通用规则
|
||||||
- 如果你不清楚答案,你需要澄清。
|
- 如果你不清楚答案,你需要澄清。
|
||||||
- 避免提及你是从 "quotes" 获取的知识。
|
- 避免提及你是从 "cites" 获取的知识。
|
||||||
- 保持答案与 "quotes" 中描述的一致。
|
- 保持答案与 "cites" 中描述的一致。
|
||||||
- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
|
- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
|
||||||
- 使用与问题相同的语言回答。
|
- 使用与问题相同的语言回答。`;
|
||||||
- 使用 [id](CITE) 格式来引用 "quotes" 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
|
|
||||||
- 在每段话结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
|
|
||||||
- 每段话至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。`;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
FlowNodeTypeEnum
|
FlowNodeTypeEnum
|
||||||
} from '../../workflow/node/constant';
|
} from '../../workflow/node/constant';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { type ToolType } from '../type';
|
import { type McpToolConfigType } from '../type';
|
||||||
import { i18nT } from '../../../../web/i18n/utils';
|
import { i18nT } from '../../../../web/i18n/utils';
|
||||||
import { type RuntimeNodeItemType } from '../../workflow/runtime/type';
|
import { type RuntimeNodeItemType } from '../../workflow/runtime/type';
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ export const getMCPToolSetRuntimeNode = ({
|
|||||||
avatar
|
avatar
|
||||||
}: {
|
}: {
|
||||||
url: string;
|
url: string;
|
||||||
toolList: ToolType[];
|
toolList: McpToolConfigType[];
|
||||||
name?: string;
|
name?: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
}): RuntimeNodeItemType => {
|
}): RuntimeNodeItemType => {
|
||||||
@@ -45,7 +45,7 @@ export const getMCPToolRuntimeNode = ({
|
|||||||
url,
|
url,
|
||||||
avatar = 'core/app/type/mcpToolsFill'
|
avatar = 'core/app/type/mcpToolsFill'
|
||||||
}: {
|
}: {
|
||||||
tool: ToolType;
|
tool: McpToolConfigType;
|
||||||
url: string;
|
url: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
}): RuntimeNodeItemType => {
|
}): RuntimeNodeItemType => {
|
||||||
@@ -65,7 +65,7 @@ export const getMCPToolRuntimeNode = ({
|
|||||||
...Object.entries(tool.inputSchema?.properties || {}).map(([key, value]) => ({
|
...Object.entries(tool.inputSchema?.properties || {}).map(([key, value]) => ({
|
||||||
key,
|
key,
|
||||||
label: key,
|
label: key,
|
||||||
valueType: value.type as WorkflowIOValueTypeEnum,
|
valueType: value.type as WorkflowIOValueTypeEnum, // TODO: 这里需要做一个映射
|
||||||
description: value.description,
|
description: value.description,
|
||||||
toolDescription: value.description || key,
|
toolDescription: value.description || key,
|
||||||
required: tool.inputSchema?.required?.includes(key) || false,
|
required: tool.inputSchema?.required?.includes(key) || false,
|
||||||
|
|||||||
20
packages/global/core/app/type.d.ts
vendored
20
packages/global/core/app/type.d.ts
vendored
@@ -16,16 +16,6 @@ import { FlowNodeInputTypeEnum } from '../../core/workflow/node/constant';
|
|||||||
import type { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/type';
|
import type { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/type';
|
||||||
import type { SourceMemberType } from '../../support/user/type';
|
import type { SourceMemberType } from '../../support/user/type';
|
||||||
|
|
||||||
export type ToolType = {
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
inputSchema: {
|
|
||||||
type: string;
|
|
||||||
properties?: Record<string, { type: string; description?: string }>;
|
|
||||||
required?: string[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AppSchema = {
|
export type AppSchema = {
|
||||||
_id: string;
|
_id: string;
|
||||||
parentId?: ParentIdType;
|
parentId?: ParentIdType;
|
||||||
@@ -117,6 +107,16 @@ export type AppSimpleEditFormType = {
|
|||||||
chatConfig: AppChatConfigType;
|
chatConfig: AppChatConfigType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type McpToolConfigType = {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
inputSchema: {
|
||||||
|
type: string;
|
||||||
|
properties?: Record<string, { type: string; description?: string }>;
|
||||||
|
required?: string[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/* app chat config type */
|
/* app chat config type */
|
||||||
export type AppChatConfigType = {
|
export type AppChatConfigType = {
|
||||||
welcomeText?: string;
|
welcomeText?: string;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { type WorkflowTemplateBasicType } from '../workflow/type';
|
|||||||
import { AppTypeEnum } from './constants';
|
import { AppTypeEnum } from './constants';
|
||||||
import { AppErrEnum } from '../../common/error/code/app';
|
import { AppErrEnum } from '../../common/error/code/app';
|
||||||
import { PluginErrEnum } from '../../common/error/code/plugin';
|
import { PluginErrEnum } from '../../common/error/code/plugin';
|
||||||
|
import { i18nT } from '../../../web/i18n/utils';
|
||||||
|
|
||||||
export const getDefaultAppForm = (): AppSimpleEditFormType => {
|
export const getDefaultAppForm = (): AppSimpleEditFormType => {
|
||||||
return {
|
return {
|
||||||
@@ -189,7 +190,7 @@ export const getAppType = (config?: WorkflowTemplateBasicType | AppSimpleEditFor
|
|||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkAppUnExistError = (error?: string) => {
|
export const formatToolError = (error?: string) => {
|
||||||
const unExistError: Array<string> = [
|
const unExistError: Array<string> = [
|
||||||
AppErrEnum.unAuthApp,
|
AppErrEnum.unAuthApp,
|
||||||
AppErrEnum.unExist,
|
AppErrEnum.unExist,
|
||||||
@@ -197,9 +198,9 @@ export const checkAppUnExistError = (error?: string) => {
|
|||||||
PluginErrEnum.unExist
|
PluginErrEnum.unExist
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!!error && unExistError.includes(error)) {
|
if (error && unExistError.includes(error)) {
|
||||||
return error;
|
return i18nT('app:un_auth');
|
||||||
} else {
|
} else {
|
||||||
return undefined;
|
return error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type {
|
|||||||
} from '../../chat/type';
|
} from '../../chat/type';
|
||||||
import { NodeOutputItemType } from '../../chat/type';
|
import { NodeOutputItemType } from '../../chat/type';
|
||||||
import type { FlowNodeInputItemType, FlowNodeOutputItemType } from '../type/io.d';
|
import type { FlowNodeInputItemType, FlowNodeOutputItemType } from '../type/io.d';
|
||||||
import type { StoreNodeItemType } from '../type/node';
|
import type { NodeToolConfigType, StoreNodeItemType } from '../type/node';
|
||||||
import type { DispatchNodeResponseKeyEnum } from './constants';
|
import type { DispatchNodeResponseKeyEnum } from './constants';
|
||||||
import type { StoreEdgeItemType } from '../type/edge';
|
import type { StoreEdgeItemType } from '../type/edge';
|
||||||
import type { NodeInputKeyEnum } from '../constants';
|
import type { NodeInputKeyEnum } from '../constants';
|
||||||
@@ -102,6 +102,9 @@ export type RuntimeNodeItemType = {
|
|||||||
|
|
||||||
pluginId?: string; // workflow id / plugin id
|
pluginId?: string; // workflow id / plugin id
|
||||||
version?: string;
|
version?: string;
|
||||||
|
|
||||||
|
// tool
|
||||||
|
toolConfig?: NodeToolConfigType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RuntimeEdgeItemType = StoreEdgeItemType & {
|
export type RuntimeEdgeItemType = StoreEdgeItemType & {
|
||||||
@@ -114,7 +117,7 @@ export type DispatchNodeResponseType = {
|
|||||||
runningTime?: number;
|
runningTime?: number;
|
||||||
query?: string;
|
query?: string;
|
||||||
textOutput?: string;
|
textOutput?: string;
|
||||||
error?: Record<string, any>;
|
error?: Record<string, any> | string;
|
||||||
customInputs?: Record<string, any>;
|
customInputs?: Record<string, any>;
|
||||||
customOutputs?: Record<string, any>;
|
customOutputs?: Record<string, any>;
|
||||||
nodeInputs?: Record<string, any>;
|
nodeInputs?: Record<string, any>;
|
||||||
|
|||||||
14
packages/global/core/workflow/type/node.d.ts
vendored
14
packages/global/core/workflow/type/node.d.ts
vendored
@@ -20,11 +20,17 @@ import { RuntimeNodeItemType } from '../runtime/type';
|
|||||||
import { PluginTypeEnum } from '../../plugin/constants';
|
import { PluginTypeEnum } from '../../plugin/constants';
|
||||||
import { RuntimeEdgeItemType, StoreEdgeItemType } from './edge';
|
import { RuntimeEdgeItemType, StoreEdgeItemType } from './edge';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { AppDetailType, AppSchema } from '../../app/type';
|
import type { AppDetailType, AppSchema, McpToolConfigType } from '../../app/type';
|
||||||
import type { ParentIdType } from 'common/parentFolder/type';
|
import type { ParentIdType } from 'common/parentFolder/type';
|
||||||
import { AppTypeEnum } from 'core/app/constants';
|
import { AppTypeEnum } from '../../app/constants';
|
||||||
import type { WorkflowInteractiveResponseType } from '../template/system/interactive/type';
|
import type { WorkflowInteractiveResponseType } from '../template/system/interactive/type';
|
||||||
|
|
||||||
|
export type NodeToolConfigType = {
|
||||||
|
mcpTool?: McpToolConfigType & {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export type FlowNodeCommonType = {
|
export type FlowNodeCommonType = {
|
||||||
parentNodeId?: string;
|
parentNodeId?: string;
|
||||||
flowNodeType: FlowNodeTypeEnum; // render node card
|
flowNodeType: FlowNodeTypeEnum; // render node card
|
||||||
@@ -46,8 +52,10 @@ export type FlowNodeCommonType = {
|
|||||||
// plugin data
|
// plugin data
|
||||||
pluginId?: string;
|
pluginId?: string;
|
||||||
isFolder?: boolean;
|
isFolder?: boolean;
|
||||||
// pluginType?: AppTypeEnum;
|
|
||||||
pluginData?: PluginDataType;
|
pluginData?: PluginDataType;
|
||||||
|
|
||||||
|
// tool data
|
||||||
|
toolData?: NodeToolConfigType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PluginDataType = {
|
export type PluginDataType = {
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export const readRawContentByFileBuffer = async ({
|
|||||||
return await systemParse();
|
return await systemParse();
|
||||||
})();
|
})();
|
||||||
|
|
||||||
addLog.debug(`Parse file success, time: ${Date.now() - start}ms. Uploading file image.`);
|
addLog.debug(`Parse file success, time: ${Date.now() - start}ms. `);
|
||||||
|
|
||||||
// markdown data format
|
// markdown data format
|
||||||
if (imageList) {
|
if (imageList) {
|
||||||
@@ -185,7 +185,7 @@ export const readRawContentByFileBuffer = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addLog.debug(`Upload file image success, time: ${Date.now() - start}ms`);
|
addLog.debug(`Upload file success, time: ${Date.now() - start}ms`);
|
||||||
|
|
||||||
return { rawText, formatText, imageList };
|
return { rawText, formatText, imageList };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { getGlobalRedisCacheConnection } from './index';
|
import { getGlobalRedisConnection } from './index';
|
||||||
import { addLog } from '../system/log';
|
import { addLog } from '../system/log';
|
||||||
import { retryFn } from '@fastgpt/global/common/system/utils';
|
import { retryFn } from '@fastgpt/global/common/system/utils';
|
||||||
|
|
||||||
|
const redisPrefix = 'cache:';
|
||||||
|
const getCacheKey = (key: string) => `${redisPrefix}${key}`;
|
||||||
|
|
||||||
export enum CacheKeyEnum {
|
export enum CacheKeyEnum {
|
||||||
team_vector_count = 'team_vector_count'
|
team_vector_count = 'team_vector_count'
|
||||||
}
|
}
|
||||||
@@ -13,12 +16,12 @@ export const setRedisCache = async (
|
|||||||
) => {
|
) => {
|
||||||
return await retryFn(async () => {
|
return await retryFn(async () => {
|
||||||
try {
|
try {
|
||||||
const redis = getGlobalRedisCacheConnection();
|
const redis = getGlobalRedisConnection();
|
||||||
|
|
||||||
if (expireSeconds) {
|
if (expireSeconds) {
|
||||||
await redis.set(key, data, 'EX', expireSeconds);
|
await redis.set(getCacheKey(key), data, 'EX', expireSeconds);
|
||||||
} else {
|
} else {
|
||||||
await redis.set(key, data);
|
await redis.set(getCacheKey(key), data);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addLog.error('Set cache error:', error);
|
addLog.error('Set cache error:', error);
|
||||||
@@ -28,11 +31,11 @@ export const setRedisCache = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getRedisCache = async (key: string) => {
|
export const getRedisCache = async (key: string) => {
|
||||||
const redis = getGlobalRedisCacheConnection();
|
const redis = getGlobalRedisConnection();
|
||||||
return await retryFn(() => redis.get(key));
|
return await retryFn(() => redis.get(getCacheKey(key)));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const delRedisCache = async (key: string) => {
|
export const delRedisCache = async (key: string) => {
|
||||||
const redis = getGlobalRedisCacheConnection();
|
const redis = getGlobalRedisConnection();
|
||||||
await retryFn(() => redis.del(key));
|
await retryFn(() => redis.del(getCacheKey(key)));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,17 +27,26 @@ export const newWorkerRedisConnection = () => {
|
|||||||
return redis;
|
return redis;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getGlobalRedisCacheConnection = () => {
|
export const FASTGPT_REDIS_PREFIX = 'fastgpt:';
|
||||||
if (global.redisCache) return global.redisCache;
|
export const getGlobalRedisConnection = () => {
|
||||||
|
if (global.redisClient) return global.redisClient;
|
||||||
|
|
||||||
global.redisCache = new Redis(REDIS_URL, { keyPrefix: 'fastgpt:cache:' });
|
global.redisClient = new Redis(REDIS_URL, { keyPrefix: FASTGPT_REDIS_PREFIX });
|
||||||
|
|
||||||
global.redisCache.on('connect', () => {
|
global.redisClient.on('connect', () => {
|
||||||
addLog.info('Redis connected');
|
addLog.info('Redis connected');
|
||||||
});
|
});
|
||||||
global.redisCache.on('error', (error) => {
|
global.redisClient.on('error', (error) => {
|
||||||
addLog.error('Redis connection error', error);
|
addLog.error('Redis connection error', error);
|
||||||
});
|
});
|
||||||
|
|
||||||
return global.redisCache;
|
return global.redisClient;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllKeysByPrefix = async (key: string) => {
|
||||||
|
const redis = getGlobalRedisConnection();
|
||||||
|
const keys = (await redis.keys(`${FASTGPT_REDIS_PREFIX}${key}:*`)).map((key) =>
|
||||||
|
key.replace(FASTGPT_REDIS_PREFIX, '')
|
||||||
|
);
|
||||||
|
return keys;
|
||||||
};
|
};
|
||||||
|
|||||||
2
packages/service/common/redis/type.d.ts
vendored
2
packages/service/common/redis/type.d.ts
vendored
@@ -1,5 +1,5 @@
|
|||||||
import type Redis from 'ioredis';
|
import type Redis from 'ioredis';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
var redisCache: Redis | null;
|
var redisClient: Redis | null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,26 +2,44 @@ import { SystemConfigsTypeEnum } from '@fastgpt/global/common/system/config/cons
|
|||||||
import { MongoSystemConfigs } from './schema';
|
import { MongoSystemConfigs } from './schema';
|
||||||
import { type FastGPTConfigFileType } from '@fastgpt/global/common/system/types';
|
import { type FastGPTConfigFileType } from '@fastgpt/global/common/system/types';
|
||||||
import { FastGPTProUrl } from '../constants';
|
import { FastGPTProUrl } from '../constants';
|
||||||
|
import { type LicenseDataType } from '@fastgpt/global/common/system/types';
|
||||||
|
|
||||||
export const getFastGPTConfigFromDB = async () => {
|
export const getFastGPTConfigFromDB = async (): Promise<{
|
||||||
|
fastgptConfig: FastGPTConfigFileType;
|
||||||
|
licenseData?: LicenseDataType;
|
||||||
|
}> => {
|
||||||
if (!FastGPTProUrl) {
|
if (!FastGPTProUrl) {
|
||||||
return {
|
return {
|
||||||
config: {} as FastGPTConfigFileType
|
fastgptConfig: {} as FastGPTConfigFileType
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await MongoSystemConfigs.findOne({
|
const [fastgptConfig, licenseConfig] = await Promise.all([
|
||||||
type: SystemConfigsTypeEnum.fastgpt
|
MongoSystemConfigs.findOne({
|
||||||
}).sort({
|
type: SystemConfigsTypeEnum.fastgpt
|
||||||
createTime: -1
|
}).sort({
|
||||||
});
|
createTime: -1
|
||||||
|
}),
|
||||||
|
MongoSystemConfigs.findOne({
|
||||||
|
type: SystemConfigsTypeEnum.license
|
||||||
|
}).sort({
|
||||||
|
createTime: -1
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
const config = res?.value || {};
|
const config = fastgptConfig?.value || {};
|
||||||
|
const licenseData = licenseConfig?.value?.data as LicenseDataType | undefined;
|
||||||
|
|
||||||
|
const fastgptConfigTime = fastgptConfig?.createTime.getTime().toString();
|
||||||
|
const licenseConfigTime = licenseConfig?.createTime.getTime().toString();
|
||||||
// 利用配置文件的创建时间(更新时间)来做缓存,如果前端命中缓存,则不需要再返回配置文件
|
// 利用配置文件的创建时间(更新时间)来做缓存,如果前端命中缓存,则不需要再返回配置文件
|
||||||
global.systemInitBufferId = res ? res.createTime.getTime().toString() : undefined;
|
global.systemInitBufferId = fastgptConfigTime
|
||||||
|
? `${fastgptConfigTime}-${licenseConfigTime}`
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
config: config as FastGPTConfigFileType
|
fastgptConfig: config as FastGPTConfigFileType,
|
||||||
|
licenseData
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -135,12 +135,14 @@ export const llmStreamResponseToAnswerText = async (
|
|||||||
|
|
||||||
// Tool calls
|
// Tool calls
|
||||||
if (responseChoice?.tool_calls?.length) {
|
if (responseChoice?.tool_calls?.length) {
|
||||||
responseChoice.tool_calls.forEach((toolCall) => {
|
responseChoice.tool_calls.forEach((toolCall, i) => {
|
||||||
const index = toolCall.index;
|
const index = toolCall.index ?? i;
|
||||||
|
|
||||||
if (toolCall.id || callingTool) {
|
// Call new tool
|
||||||
// 有 id,代表新 call 工具
|
const hasNewTool = toolCall?.function?.name || callingTool;
|
||||||
if (toolCall.id) {
|
if (hasNewTool) {
|
||||||
|
// 有 function name,代表新 call 工具
|
||||||
|
if (toolCall?.function?.name) {
|
||||||
callingTool = {
|
callingTool = {
|
||||||
name: toolCall.function?.name || '',
|
name: toolCall.function?.name || '',
|
||||||
arguments: toolCall.function?.arguments || ''
|
arguments: toolCall.function?.arguments || ''
|
||||||
@@ -221,7 +223,9 @@ export const parseReasoningContent = (text: string): [string, string] => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const removeDatasetCiteText = (text: string, retainDatasetCite: boolean) => {
|
export const removeDatasetCiteText = (text: string, retainDatasetCite: boolean) => {
|
||||||
return retainDatasetCite ? text : text.replace(/\[([a-f0-9]{24})\](?:\([^\)]*\)?)?/g, '');
|
return retainDatasetCite
|
||||||
|
? text.replace(/\[id\]\(CITE\)/g, '')
|
||||||
|
: text.replace(/\[([a-f0-9]{24})\](?:\([^\)]*\)?)?/g, '').replace(/\[id\]\(CITE\)/g, '');
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse llm stream part
|
// Parse llm stream part
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||||
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
||||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
||||||
import { type ToolType } from '@fastgpt/global/core/app/type';
|
import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
|
||||||
import { addLog } from '../../common/system/log';
|
import { addLog } from '../../common/system/log';
|
||||||
import { retryFn } from '@fastgpt/global/common/system/utils';
|
import { retryFn } from '@fastgpt/global/common/system/utils';
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ export class MCPClient {
|
|||||||
* Get available tools list
|
* Get available tools list
|
||||||
* @returns List of tools
|
* @returns List of tools
|
||||||
*/
|
*/
|
||||||
public async getTools(): Promise<ToolType[]> {
|
public async getTools(): Promise<McpToolConfigType[]> {
|
||||||
try {
|
try {
|
||||||
const client = await this.getConnection();
|
const client = await this.getConnection();
|
||||||
const response = await client.listTools();
|
const response = await client.listTools();
|
||||||
|
|||||||
@@ -22,8 +22,7 @@ import {
|
|||||||
import { type PluginRuntimeType } from '@fastgpt/global/core/plugin/type';
|
import { type PluginRuntimeType } from '@fastgpt/global/core/plugin/type';
|
||||||
import { MongoSystemPlugin } from './systemPluginSchema';
|
import { MongoSystemPlugin } from './systemPluginSchema';
|
||||||
import { PluginErrEnum } from '@fastgpt/global/common/error/code/plugin';
|
import { PluginErrEnum } from '@fastgpt/global/common/error/code/plugin';
|
||||||
import { MongoAppVersion } from '../version/schema';
|
import { Types } from 'mongoose';
|
||||||
import { i18nT } from '../../../../web/i18n/utils';
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
plugin id rule:
|
plugin id rule:
|
||||||
@@ -111,19 +110,13 @@ export async function getChildAppPreviewNode({
|
|||||||
|
|
||||||
const version = await getAppVersionById({ appId, versionId, app: item });
|
const version = await getAppVersionById({ appId, versionId, app: item });
|
||||||
|
|
||||||
if (!version.versionId) return Promise.reject(i18nT('common:app_not_version'));
|
const isLatest =
|
||||||
|
version.versionId && Types.ObjectId.isValid(version.versionId)
|
||||||
const versionData = await MongoAppVersion.findById(
|
? await checkIsLatestVersion({
|
||||||
version.versionId,
|
appId,
|
||||||
'_id versionName appId time'
|
versionId: version.versionId
|
||||||
).lean();
|
})
|
||||||
|
: true;
|
||||||
const isLatest = versionData
|
|
||||||
? await checkIsLatestVersion({
|
|
||||||
appId,
|
|
||||||
versionId: versionData._id
|
|
||||||
})
|
|
||||||
: true;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: String(item._id),
|
id: String(item._id),
|
||||||
@@ -140,7 +133,7 @@ export async function getChildAppPreviewNode({
|
|||||||
templateType: FlowNodeTemplateTypeEnum.teamApp,
|
templateType: FlowNodeTemplateTypeEnum.teamApp,
|
||||||
|
|
||||||
version: version.versionId,
|
version: version.versionId,
|
||||||
versionLabel: versionData?.versionName || '',
|
versionLabel: version?.versionName || '',
|
||||||
isLatestVersion: isLatest,
|
isLatestVersion: isLatest,
|
||||||
|
|
||||||
originCost: 0,
|
originCost: 0,
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ const AppSchema = new Schema({
|
|||||||
defaultPermission: Number
|
defaultPermission: Number
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AppSchema.index({ type: 1 });
|
||||||
AppSchema.index({ teamId: 1, updateTime: -1 });
|
AppSchema.index({ teamId: 1, updateTime: -1 });
|
||||||
AppSchema.index({ teamId: 1, type: 1 });
|
AppSchema.index({ teamId: 1, type: 1 });
|
||||||
AppSchema.index(
|
AppSchema.index(
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export async function rewriteAppWorkflowToDetail({
|
|||||||
const versionIds = appNodes
|
const versionIds = appNodes
|
||||||
.filter((node) => node.version && Types.ObjectId.isValid(node.version))
|
.filter((node) => node.version && Types.ObjectId.isValid(node.version))
|
||||||
.map((node) => node.version);
|
.map((node) => node.version);
|
||||||
|
|
||||||
if (versionIds.length > 0) {
|
if (versionIds.length > 0) {
|
||||||
const versionDataList = await MongoAppVersion.find(
|
const versionDataList = await MongoAppVersion.find(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export const getAppLatestVersion = async (appId: string, app?: AppSchema) => {
|
|||||||
if (version) {
|
if (version) {
|
||||||
return {
|
return {
|
||||||
versionId: version._id,
|
versionId: version._id,
|
||||||
|
versionName: version.versionName,
|
||||||
nodes: version.nodes,
|
nodes: version.nodes,
|
||||||
edges: version.edges,
|
edges: version.edges,
|
||||||
chatConfig: version.chatConfig || app?.chatConfig || {}
|
chatConfig: version.chatConfig || app?.chatConfig || {}
|
||||||
@@ -22,6 +23,7 @@ export const getAppLatestVersion = async (appId: string, app?: AppSchema) => {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
versionId: app?.pluginData?.nodeVersion,
|
versionId: app?.pluginData?.nodeVersion,
|
||||||
|
versionName: app?.name,
|
||||||
nodes: app?.modules || [],
|
nodes: app?.modules || [],
|
||||||
edges: app?.edges || [],
|
edges: app?.edges || [],
|
||||||
chatConfig: app?.chatConfig || {}
|
chatConfig: app?.chatConfig || {}
|
||||||
@@ -47,6 +49,7 @@ export const getAppVersionById = async ({
|
|||||||
if (version) {
|
if (version) {
|
||||||
return {
|
return {
|
||||||
versionId: version._id,
|
versionId: version._id,
|
||||||
|
versionName: version.versionName,
|
||||||
nodes: version.nodes,
|
nodes: version.nodes,
|
||||||
edges: version.edges,
|
edges: version.edges,
|
||||||
chatConfig: version.chatConfig || app?.chatConfig || {}
|
chatConfig: version.chatConfig || app?.chatConfig || {}
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ const DatasetSchema = new Schema({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
DatasetSchema.index({ teamId: 1 });
|
DatasetSchema.index({ teamId: 1 });
|
||||||
|
DatasetSchema.index({ type: 1 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -474,7 +474,7 @@ export async function searchDatasetData(
|
|||||||
).lean()
|
).lean()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const set = new Map<string, number>();
|
const set = new Set<string>();
|
||||||
const formatResult = results
|
const formatResult = results
|
||||||
.map((item, index) => {
|
.map((item, index) => {
|
||||||
const collection = collections.find((col) => String(col._id) === String(item.collectionId));
|
const collection = collections.find((col) => String(col._id) === String(item.collectionId));
|
||||||
@@ -507,7 +507,7 @@ export async function searchDatasetData(
|
|||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
if (!item) return false;
|
if (!item) return false;
|
||||||
if (set.has(item.id)) return false;
|
if (set.has(item.id)) return false;
|
||||||
set.set(item.id, 1);
|
set.add(item.id);
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.map((item, index) => {
|
.map((item, index) => {
|
||||||
@@ -648,7 +648,17 @@ export async function searchDatasetData(
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter(Boolean) as SearchDataResponseItemType[],
|
.filter((item) => {
|
||||||
|
if (!item) return false;
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((item, index) => {
|
||||||
|
if (!item) return;
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
score: item.score.map((item) => ({ ...item, index }))
|
||||||
|
};
|
||||||
|
}) as SearchDataResponseItemType[],
|
||||||
tokenLen: 0
|
tokenLen: 0
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -727,9 +727,10 @@ async function streamResponse({
|
|||||||
const index = toolCall.index ?? i;
|
const index = toolCall.index ?? i;
|
||||||
|
|
||||||
// Call new tool
|
// Call new tool
|
||||||
if (toolCall.id || callingTool) {
|
const hasNewTool = toolCall?.function?.name || callingTool;
|
||||||
// 有 id,代表新 call 工具
|
if (hasNewTool) {
|
||||||
if (toolCall.id) {
|
// 有 function name,代表新 call 工具
|
||||||
|
if (toolCall?.function?.name) {
|
||||||
callingTool = {
|
callingTool = {
|
||||||
name: toolCall.function?.name || '',
|
name: toolCall.function?.name || '',
|
||||||
arguments: toolCall.function?.arguments || ''
|
arguments: toolCall.function?.arguments || ''
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ export async function dispatchDatasetSearch(
|
|||||||
nodeDispatchUsages,
|
nodeDispatchUsages,
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]: {
|
[DispatchNodeResponseKeyEnum.toolResponses]: {
|
||||||
prompt: getDatasetSearchToolResponsePrompt(),
|
prompt: getDatasetSearchToolResponsePrompt(),
|
||||||
quotes: searchRes.map((item) => ({
|
cites: searchRes.map((item) => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
sourceName: item.sourceName,
|
sourceName: item.sourceName,
|
||||||
updateTime: item.updateTime,
|
updateTime: item.updateTime,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { type MemberGroupSchemaType } from '@fastgpt/global/support/permission/m
|
|||||||
import { type TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
|
import { type TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
|
||||||
import { type OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
|
import { type OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
|
||||||
import { getOrgIdSetWithParentByTmbId } from './org/controllers';
|
import { getOrgIdSetWithParentByTmbId } from './org/controllers';
|
||||||
|
import { authUserSession } from '../user/session';
|
||||||
|
|
||||||
/** get resource permission for a team member
|
/** get resource permission for a team member
|
||||||
* If there is no permission for the team member, it will return undefined
|
* If there is no permission for the team member, it will return undefined
|
||||||
@@ -213,51 +214,6 @@ export const delResourcePermission = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* 下面代码等迁移 */
|
/* 下面代码等迁移 */
|
||||||
/* create token */
|
|
||||||
export function createJWT(user: {
|
|
||||||
_id?: string;
|
|
||||||
team?: { teamId?: string; tmbId: string };
|
|
||||||
isRoot?: boolean;
|
|
||||||
}) {
|
|
||||||
const key = process.env.TOKEN_KEY as string;
|
|
||||||
const token = jwt.sign(
|
|
||||||
{
|
|
||||||
userId: String(user._id),
|
|
||||||
teamId: String(user.team?.teamId),
|
|
||||||
tmbId: String(user.team?.tmbId),
|
|
||||||
isRoot: user.isRoot,
|
|
||||||
exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 7
|
|
||||||
},
|
|
||||||
key
|
|
||||||
);
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
// auth token
|
|
||||||
export function authJWT(token: string) {
|
|
||||||
return new Promise<{
|
|
||||||
userId: string;
|
|
||||||
teamId: string;
|
|
||||||
tmbId: string;
|
|
||||||
isRoot: boolean;
|
|
||||||
}>((resolve, reject) => {
|
|
||||||
const key = process.env.TOKEN_KEY as string;
|
|
||||||
|
|
||||||
jwt.verify(token, key, (err, decoded: any) => {
|
|
||||||
if (err || !decoded?.userId) {
|
|
||||||
reject(ERROR_ENUM.unAuthorization);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve({
|
|
||||||
userId: decoded.userId,
|
|
||||||
teamId: decoded.teamId || '',
|
|
||||||
tmbId: decoded.tmbId,
|
|
||||||
isRoot: decoded.isRoot
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function parseHeaderCert({
|
export async function parseHeaderCert({
|
||||||
req,
|
req,
|
||||||
@@ -275,7 +231,7 @@ export async function parseHeaderCert({
|
|||||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await authJWT(cookieToken);
|
return authUserSession(cookieToken);
|
||||||
}
|
}
|
||||||
// from authorization get apikey
|
// from authorization get apikey
|
||||||
async function parseAuthorization(authorization?: string) {
|
async function parseAuthorization(authorization?: string) {
|
||||||
@@ -345,6 +301,7 @@ export async function parseHeaderCert({
|
|||||||
if (authToken && (token || cookie)) {
|
if (authToken && (token || cookie)) {
|
||||||
// user token(from fastgpt web)
|
// user token(from fastgpt web)
|
||||||
const res = await authCookieToken(cookie, token);
|
const res = await authCookieToken(cookie, token);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uid: res.userId,
|
uid: res.userId,
|
||||||
teamId: res.teamId,
|
teamId: res.teamId,
|
||||||
|
|||||||
@@ -54,23 +54,50 @@ export const checkTeamDatasetLimit = async (teamId: string) => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// User check
|
||||||
if (standardConstants && datasetCount >= standardConstants.maxDatasetAmount) {
|
if (standardConstants && datasetCount >= standardConstants.maxDatasetAmount) {
|
||||||
return Promise.reject(TeamErrEnum.datasetAmountNotEnough);
|
return Promise.reject(TeamErrEnum.datasetAmountNotEnough);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// System check
|
||||||
|
if (global?.licenseData?.maxDatasets && typeof global?.licenseData?.maxDatasets === 'number') {
|
||||||
|
const totalDatasets = await MongoDataset.countDocuments({
|
||||||
|
type: { $ne: DatasetTypeEnum.folder }
|
||||||
|
});
|
||||||
|
if (totalDatasets >= global.licenseData.maxDatasets) {
|
||||||
|
return Promise.reject(SystemErrEnum.licenseDatasetAmountLimit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Open source check
|
||||||
if (!global.feConfigs.isPlus && datasetCount >= 30) {
|
if (!global.feConfigs.isPlus && datasetCount >= 30) {
|
||||||
return Promise.reject(SystemErrEnum.communityVersionNumLimit);
|
return Promise.reject(SystemErrEnum.communityVersionNumLimit);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkTeamAppLimit = async (teamId: string, amount = 1) => {
|
export const checkTeamAppLimit = async (teamId: string, amount = 1) => {
|
||||||
const [{ standardConstants }, appCount] = await Promise.all([
|
const [{ standardConstants }, appCount] = await Promise.all([
|
||||||
getTeamStandPlan({ teamId }),
|
getTeamStandPlan({ teamId }),
|
||||||
MongoApp.countDocuments({
|
MongoApp.countDocuments({
|
||||||
teamId,
|
teamId,
|
||||||
type: { $in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin] }
|
type: {
|
||||||
|
$in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool]
|
||||||
|
}
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (standardConstants && appCount + amount >= standardConstants.maxAppAmount) {
|
if (standardConstants && appCount + amount >= standardConstants.maxAppAmount) {
|
||||||
return Promise.reject(TeamErrEnum.appAmountNotEnough);
|
return Promise.reject(TeamErrEnum.appAmountNotEnough);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// System check
|
||||||
|
if (global?.licenseData?.maxApps && typeof global?.licenseData?.maxApps === 'number') {
|
||||||
|
const totalApps = await MongoApp.countDocuments({
|
||||||
|
type: {
|
||||||
|
$in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (totalApps >= global.licenseData.maxApps) {
|
||||||
|
return Promise.reject(SystemErrEnum.licenseAppAmountLimit);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
179
packages/service/support/user/session.ts
Normal file
179
packages/service/support/user/session.ts
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
import { retryFn } from '@fastgpt/global/common/system/utils';
|
||||||
|
import { getAllKeysByPrefix, getGlobalRedisConnection } from '../../common/redis';
|
||||||
|
import { addLog } from '../../common/system/log';
|
||||||
|
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||||
|
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||||
|
|
||||||
|
const redisPrefix = 'session:';
|
||||||
|
const getSessionKey = (key: string) => `${redisPrefix}${key}`;
|
||||||
|
|
||||||
|
type SessionType = {
|
||||||
|
userId: string;
|
||||||
|
teamId: string;
|
||||||
|
tmbId: string;
|
||||||
|
isRoot?: boolean;
|
||||||
|
createdAt: number;
|
||||||
|
ip?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Session manager */
|
||||||
|
const setSession = async ({
|
||||||
|
key,
|
||||||
|
data,
|
||||||
|
expireSeconds
|
||||||
|
}: {
|
||||||
|
key: string;
|
||||||
|
data: SessionType;
|
||||||
|
expireSeconds: number;
|
||||||
|
}) => {
|
||||||
|
return await retryFn(async () => {
|
||||||
|
try {
|
||||||
|
const redis = getGlobalRedisConnection();
|
||||||
|
const formatKey = getSessionKey(key);
|
||||||
|
|
||||||
|
// 使用 hmset 存储对象字段
|
||||||
|
await redis.hmset(formatKey, {
|
||||||
|
userId: data.userId,
|
||||||
|
teamId: data.teamId,
|
||||||
|
tmbId: data.tmbId,
|
||||||
|
isRoot: data.isRoot ? '1' : '0',
|
||||||
|
createdAt: data.createdAt.toString(),
|
||||||
|
ip: data.ip
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置过期时间
|
||||||
|
if (expireSeconds) {
|
||||||
|
await redis.expire(formatKey, expireSeconds);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
addLog.error('Set session error:', error);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const delSession = (key: string) => {
|
||||||
|
const redis = getGlobalRedisConnection();
|
||||||
|
retryFn(() => redis.del(getSessionKey(key)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSession = async (key: string): Promise<SessionType> => {
|
||||||
|
const formatKey = getSessionKey(key);
|
||||||
|
const redis = getGlobalRedisConnection();
|
||||||
|
|
||||||
|
// 使用 hgetall 获取所有字段
|
||||||
|
const data = await retryFn(() => redis.hgetall(formatKey));
|
||||||
|
|
||||||
|
if (!data || Object.keys(data).length === 0) {
|
||||||
|
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
userId: data.userId,
|
||||||
|
teamId: data.teamId,
|
||||||
|
tmbId: data.tmbId,
|
||||||
|
isRoot: data.isRoot === '1',
|
||||||
|
createdAt: parseInt(data.createdAt),
|
||||||
|
ip: data.ip
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
addLog.error('Parse session error:', error);
|
||||||
|
delSession(formatKey);
|
||||||
|
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const delUserAllSession = async (userId: string, whileList?: string[]) => {
|
||||||
|
const formatWhileList = whileList?.map((item) => getSessionKey(item));
|
||||||
|
const redis = getGlobalRedisConnection();
|
||||||
|
const keys = (await getAllKeysByPrefix(`${redisPrefix}${userId}`)).filter(
|
||||||
|
(item) => !formatWhileList?.includes(item)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (keys.length > 0) {
|
||||||
|
await redis.del(keys);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 会根据创建时间,删除超出客户端登录限制的 session
|
||||||
|
const delRedundantSession = async (userId: string) => {
|
||||||
|
// 至少为 1,默认为 10
|
||||||
|
let maxSession = process.env.MAX_LOGIN_SESSION ? Number(process.env.MAX_LOGIN_SESSION) : 10;
|
||||||
|
if (maxSession < 1) {
|
||||||
|
maxSession = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const redis = getGlobalRedisConnection();
|
||||||
|
const keys = await getAllKeysByPrefix(`${redisPrefix}${userId}`);
|
||||||
|
|
||||||
|
if (keys.length <= maxSession) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有会话的创建时间
|
||||||
|
const sessionList = await Promise.all(
|
||||||
|
keys.map(async (key) => {
|
||||||
|
try {
|
||||||
|
const data = await redis.hgetall(key);
|
||||||
|
if (!data || Object.keys(data).length === 0) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
createdAt: parseInt(data.createdAt)
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 过滤掉无效数据并按创建时间排序
|
||||||
|
const validSessions = sessionList.filter(Boolean) as { key: string; createdAt: number }[];
|
||||||
|
|
||||||
|
validSessions.sort((a, b) => a.createdAt - b.createdAt);
|
||||||
|
|
||||||
|
// 删除最早创建的会话
|
||||||
|
const delKeys = validSessions.slice(0, validSessions.length - maxSession).map((item) => item.key);
|
||||||
|
|
||||||
|
if (delKeys.length > 0) {
|
||||||
|
await redis.del(delKeys);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const createUserSession = async ({
|
||||||
|
userId,
|
||||||
|
teamId,
|
||||||
|
tmbId,
|
||||||
|
isRoot,
|
||||||
|
ip
|
||||||
|
}: {
|
||||||
|
userId: string;
|
||||||
|
teamId: string;
|
||||||
|
tmbId: string;
|
||||||
|
isRoot?: boolean;
|
||||||
|
ip?: string | null;
|
||||||
|
}) => {
|
||||||
|
const key = `${String(userId)}:${getNanoid(32)}`;
|
||||||
|
|
||||||
|
await setSession({
|
||||||
|
key,
|
||||||
|
data: {
|
||||||
|
userId: String(userId),
|
||||||
|
teamId: String(teamId),
|
||||||
|
tmbId: String(tmbId),
|
||||||
|
isRoot,
|
||||||
|
createdAt: new Date().getTime(),
|
||||||
|
ip
|
||||||
|
},
|
||||||
|
expireSeconds: 7 * 24 * 60 * 60
|
||||||
|
});
|
||||||
|
|
||||||
|
delRedundantSession(userId);
|
||||||
|
|
||||||
|
return key;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const authUserSession = async (key: string): Promise<SessionType> => {
|
||||||
|
const data = await getSession(key);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
7
packages/service/type.d.ts
vendored
7
packages/service/type.d.ts
vendored
@@ -1,4 +1,8 @@
|
|||||||
import type { FastGPTFeConfigsType, SystemEnvType } from '@fastgpt/global/common/system/types';
|
import type {
|
||||||
|
FastGPTFeConfigsType,
|
||||||
|
LicenseDataType,
|
||||||
|
SystemEnvType
|
||||||
|
} from '@fastgpt/global/common/system/types';
|
||||||
import {
|
import {
|
||||||
TTSModelType,
|
TTSModelType,
|
||||||
RerankModelItemType,
|
RerankModelItemType,
|
||||||
@@ -17,6 +21,7 @@ declare global {
|
|||||||
var feConfigs: FastGPTFeConfigsType;
|
var feConfigs: FastGPTFeConfigsType;
|
||||||
var systemEnv: SystemEnvType;
|
var systemEnv: SystemEnvType;
|
||||||
var subPlans: SubPlanType | undefined;
|
var subPlans: SubPlanType | undefined;
|
||||||
|
var licenseData: LicenseDataType | undefined;
|
||||||
|
|
||||||
var workerPoll: Record<WorkerNameEnum, WorkerPool>;
|
var workerPoll: Record<WorkerNameEnum, WorkerPool>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import MyTooltip from '../MyTooltip';
|
|||||||
type Props = FlexProps & {
|
type Props = FlexProps & {
|
||||||
icon: string;
|
icon: string;
|
||||||
size?: string;
|
size?: string;
|
||||||
onClick?: () => void;
|
|
||||||
hoverColor?: string;
|
hoverColor?: string;
|
||||||
hoverBg?: string;
|
hoverBg?: string;
|
||||||
hoverBorderColor?: string;
|
hoverBorderColor?: string;
|
||||||
@@ -41,9 +40,9 @@ const MyIconButton = ({
|
|||||||
color: hoverColor,
|
color: hoverColor,
|
||||||
borderColor: hoverBorderColor
|
borderColor: hoverBorderColor
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
if (isLoading) return;
|
if (isLoading) return;
|
||||||
onClick?.();
|
onClick?.(e);
|
||||||
}}
|
}}
|
||||||
sx={{ userSelect: 'none' }}
|
sx={{ userSelect: 'none' }}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { useRequest2 } from '../../../hooks/useRequest';
|
|||||||
import MyDivider from '../MyDivider';
|
import MyDivider from '../MyDivider';
|
||||||
import type { useScrollPagination } from '../../../hooks/useScrollPagination';
|
import type { useScrollPagination } from '../../../hooks/useScrollPagination';
|
||||||
import Avatar from '../Avatar';
|
import Avatar from '../Avatar';
|
||||||
|
import EmptyTip from '../EmptyTip';
|
||||||
|
|
||||||
/** 选择组件 Props 类型
|
/** 选择组件 Props 类型
|
||||||
* value: 选中的值
|
* value: 选中的值
|
||||||
@@ -141,43 +142,49 @@ const MySelect = <T = any,>(
|
|||||||
const ListRender = useMemo(() => {
|
const ListRender = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{filterList.map((item, i) => (
|
{filterList.length > 0 ? (
|
||||||
<Box key={i}>
|
filterList.map((item, i) => (
|
||||||
<MenuItem
|
<Box key={i}>
|
||||||
{...menuItemStyles}
|
<MenuItem
|
||||||
{...(value === item.value
|
{...menuItemStyles}
|
||||||
? {
|
{...(value === item.value
|
||||||
ref: SelectedItemRef,
|
? {
|
||||||
color: 'primary.700',
|
ref: SelectedItemRef,
|
||||||
bg: 'myGray.100',
|
color: 'primary.700',
|
||||||
fontWeight: '600'
|
bg: 'myGray.100',
|
||||||
|
fontWeight: '600'
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
color: 'myGray.900'
|
||||||
|
})}
|
||||||
|
onClick={() => {
|
||||||
|
if (value !== item.value) {
|
||||||
|
onClickChange(item.value);
|
||||||
}
|
}
|
||||||
: {
|
}}
|
||||||
color: 'myGray.900'
|
whiteSpace={'pre-wrap'}
|
||||||
})}
|
fontSize={'sm'}
|
||||||
onClick={() => {
|
display={'block'}
|
||||||
if (value !== item.value) {
|
mb={0.5}
|
||||||
onClickChange(item.value);
|
>
|
||||||
}
|
<Flex alignItems={'center'}>
|
||||||
}}
|
{item.icon && (
|
||||||
whiteSpace={'pre-wrap'}
|
<Avatar mr={2} src={item.icon as any} w={item.iconSize ?? '1rem'} />
|
||||||
fontSize={'sm'}
|
)}
|
||||||
display={'block'}
|
{item.label}
|
||||||
mb={0.5}
|
</Flex>
|
||||||
>
|
{item.description && (
|
||||||
<Flex alignItems={'center'}>
|
<Box color={'myGray.500'} fontSize={'xs'}>
|
||||||
{item.icon && <Avatar mr={2} src={item.icon as any} w={item.iconSize ?? '1rem'} />}
|
{item.description}
|
||||||
{item.label}
|
</Box>
|
||||||
</Flex>
|
)}
|
||||||
{item.description && (
|
</MenuItem>
|
||||||
<Box color={'myGray.500'} fontSize={'xs'}>
|
{item.showBorder && <MyDivider my={2} />}
|
||||||
{item.description}
|
</Box>
|
||||||
</Box>
|
))
|
||||||
)}
|
) : (
|
||||||
</MenuItem>
|
<EmptyTip py={0} />
|
||||||
{item.showBorder && <MyDivider my={2} />}
|
)}
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}, [filterList, onClickChange, value]);
|
}, [filterList, onClickChange, value]);
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
"package_usage_rules": "Package usage rules: The system will give priority to using more advanced packages, and the original unused packages will take effect later.",
|
"package_usage_rules": "Package usage rules: The system will give priority to using more advanced packages, and the original unused packages will take effect later.",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"password_mismatch": "Password Inconsistency: Two passwords are inconsistent",
|
"password_mismatch": "Password Inconsistency: Two passwords are inconsistent",
|
||||||
"password_tip": "Password must be at least 6 characters long and contain at least two combinations: numbers, letters, or special characters",
|
"password_tip": "Password must be at least 8 characters long and contain at least two combinations: numbers, letters, or special characters",
|
||||||
"password_update_error": "Exception when changing password",
|
"password_update_error": "Exception when changing password",
|
||||||
"password_update_success": "Password changed successfully",
|
"password_update_success": "Password changed successfully",
|
||||||
"pending_usage": "To be used",
|
"pending_usage": "To be used",
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
"owner": "owner",
|
"owner": "owner",
|
||||||
"permission": "Permissions",
|
"permission": "Permissions",
|
||||||
"permission_apikeyCreate": "Create API Key",
|
"permission_apikeyCreate": "Create API Key",
|
||||||
"permission_apikeyCreate_Tip": "Can create global APIKeys",
|
"permission_apikeyCreate_Tip": "You can create global APIKey and MCP services",
|
||||||
"permission_appCreate": "Create Application",
|
"permission_appCreate": "Create Application",
|
||||||
"permission_appCreate_tip": "Can create applications in the root directory (creation permissions in folders are controlled by the folder)",
|
"permission_appCreate_tip": "Can create applications in the root directory (creation permissions in folders are controlled by the folder)",
|
||||||
"permission_datasetCreate": "Create Knowledge Base",
|
"permission_datasetCreate": "Create Knowledge Base",
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
"app.modules.click to update": "Click to Refresh",
|
"app.modules.click to update": "Click to Refresh",
|
||||||
"app.modules.has new version": "New Version Available",
|
"app.modules.has new version": "New Version Available",
|
||||||
"app.modules.not_found": "Not Found",
|
"app.modules.not_found": "Not Found",
|
||||||
"app.modules.not_found_tips": "This component cannot be found in the system, please delete it, otherwise the process will not run normally",
|
|
||||||
"app.version_current": "Current Version",
|
"app.version_current": "Current Version",
|
||||||
"app.version_initial": "Initial Version",
|
"app.version_initial": "Initial Version",
|
||||||
"app.version_name_tips": "Version name cannot be empty",
|
"app.version_name_tips": "Version name cannot be empty",
|
||||||
@@ -193,6 +192,7 @@
|
|||||||
"type.error.Workflow data is empty": "No workflow data was obtained",
|
"type.error.Workflow data is empty": "No workflow data was obtained",
|
||||||
"type.error.workflowresponseempty": "Response content is empty",
|
"type.error.workflowresponseempty": "Response content is empty",
|
||||||
"type_not_recognized": "App type not recognized",
|
"type_not_recognized": "App type not recognized",
|
||||||
|
"un_auth": "No permission",
|
||||||
"upload_file_max_amount": "Maximum File Quantity",
|
"upload_file_max_amount": "Maximum File Quantity",
|
||||||
"upload_file_max_amount_tip": "Maximum number of files uploaded in a single round of conversation",
|
"upload_file_max_amount_tip": "Maximum number of files uploaded in a single round of conversation",
|
||||||
"variable.select type_desc": "You can define a global variable that does not need to be filled in by the user.\n\nThe value of this variable can come from the API interface, the Query of the shared link, or assigned through the [Variable Update] module.",
|
"variable.select type_desc": "You can define a global variable that does not need to be filled in by the user.\n\nThe value of this variable can come from the API interface, the Query of the shared link, or assigned through the [Variable Update] module.",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"Action": "Action",
|
"Action": "Action",
|
||||||
"Add": "Add",
|
"Add": "Add",
|
||||||
"add_success": "Added Successfully",
|
|
||||||
"Add_new_input": "Add new input",
|
"Add_new_input": "Add new input",
|
||||||
"All": "All",
|
"All": "All",
|
||||||
"App": "Application",
|
"App": "Application",
|
||||||
@@ -30,7 +29,7 @@
|
|||||||
"FAQ.ai_point_q": "What are AI points?",
|
"FAQ.ai_point_q": "What are AI points?",
|
||||||
"FAQ.check_subscription_a": "Go to Account - Personal Information - Package Details - Usage. You can view the effective and expiration dates of your subscribed packages. After the paid package expires, it will automatically switch to the free version.",
|
"FAQ.check_subscription_a": "Go to Account - Personal Information - Package Details - Usage. You can view the effective and expiration dates of your subscribed packages. After the paid package expires, it will automatically switch to the free version.",
|
||||||
"FAQ.check_subscription_q": "Where can I view my subscribed packages?",
|
"FAQ.check_subscription_q": "Where can I view my subscribed packages?",
|
||||||
"FAQ.dataset_compute_a": "1 Dataset storage equals 1 Dataset index. A piece of Dataset data can contain one or more Dataset indexes. In enhanced training, 1 piece of data generates 5 indexes.",
|
"FAQ.dataset_compute_a": "1 knowledge base storage is equal to 1 knowledge base index. \nA single chunked data usually corresponds to multiple indexes. You can see \"n group indexes\" in a single knowledge base collection.",
|
||||||
"FAQ.dataset_compute_q": "How is Dataset storage calculated?",
|
"FAQ.dataset_compute_q": "How is Dataset storage calculated?",
|
||||||
"FAQ.dataset_index_a": "No, but if the Dataset index exceeds the limit, you cannot insert or update Dataset content.",
|
"FAQ.dataset_index_a": "No, but if the Dataset index exceeds the limit, you cannot insert or update Dataset content.",
|
||||||
"FAQ.dataset_index_q": "Will the Dataset index be deleted if it exceeds the limit?",
|
"FAQ.dataset_index_q": "Will the Dataset index be deleted if it exceeds the limit?",
|
||||||
@@ -64,7 +63,6 @@
|
|||||||
"Parse": "Analysis",
|
"Parse": "Analysis",
|
||||||
"Permission": "Permission",
|
"Permission": "Permission",
|
||||||
"Permission_tip": "Individual permissions are greater than group permissions",
|
"Permission_tip": "Individual permissions are greater than group permissions",
|
||||||
"please_input_name": "Please Enter a Name",
|
|
||||||
"Preview": "Preview",
|
"Preview": "Preview",
|
||||||
"Remove": "Remove",
|
"Remove": "Remove",
|
||||||
"Rename": "Rename",
|
"Rename": "Rename",
|
||||||
@@ -94,6 +92,7 @@
|
|||||||
"action_confirm": "Confirm",
|
"action_confirm": "Confirm",
|
||||||
"add_new": "add_new",
|
"add_new": "add_new",
|
||||||
"add_new_param": "Add new param",
|
"add_new_param": "Add new param",
|
||||||
|
"add_success": "Added Successfully",
|
||||||
"all_quotes": "All quotes",
|
"all_quotes": "All quotes",
|
||||||
"all_result": "Full Results",
|
"all_result": "Full Results",
|
||||||
"app_not_version": "This application has not been published, please publish it first",
|
"app_not_version": "This application has not been published, please publish it first",
|
||||||
@@ -149,6 +148,9 @@
|
|||||||
"code_error.plugin_error.not_exist": "Plugin Does Not Exist",
|
"code_error.plugin_error.not_exist": "Plugin Does Not Exist",
|
||||||
"code_error.plugin_error.un_auth": "Unauthorized to Operate This Plugin",
|
"code_error.plugin_error.un_auth": "Unauthorized to Operate This Plugin",
|
||||||
"code_error.system_error.community_version_num_limit": "Exceeded Open Source Version Limit, Please Upgrade to Commercial Version: https://tryfastgpt.ai",
|
"code_error.system_error.community_version_num_limit": "Exceeded Open Source Version Limit, Please Upgrade to Commercial Version: https://tryfastgpt.ai",
|
||||||
|
"code_error.system_error.license_app_amount_limit": "Exceed the maximum number of applications in the system",
|
||||||
|
"code_error.system_error.license_dataset_amount_limit": "Exceed the maximum number of knowledge bases in the system",
|
||||||
|
"code_error.system_error.license_user_amount_limit": "Exceed the maximum number of users in the system",
|
||||||
"code_error.team_error.ai_points_not_enough": "Insufficient AI Points",
|
"code_error.team_error.ai_points_not_enough": "Insufficient AI Points",
|
||||||
"code_error.team_error.app_amount_not_enough": "Application Limit Reached",
|
"code_error.team_error.app_amount_not_enough": "Application Limit Reached",
|
||||||
"code_error.team_error.cannot_delete_default_group": "Cannot delete default group",
|
"code_error.team_error.cannot_delete_default_group": "Cannot delete default group",
|
||||||
@@ -752,6 +754,7 @@
|
|||||||
"data_index_image": "Image Index",
|
"data_index_image": "Image Index",
|
||||||
"data_index_question": "Inferred question index",
|
"data_index_question": "Inferred question index",
|
||||||
"data_index_summary": "Summary Index",
|
"data_index_summary": "Summary Index",
|
||||||
|
"data_not_found": "Data can't be found",
|
||||||
"dataset.Confirm move the folder": "Confirm to Move to This Directory",
|
"dataset.Confirm move the folder": "Confirm to Move to This Directory",
|
||||||
"dataset.Confirm to delete the data": "Confirm to Delete This Data?",
|
"dataset.Confirm to delete the data": "Confirm to Delete This Data?",
|
||||||
"dataset.Confirm to delete the file": "Confirm to Delete This File and All Its Data?",
|
"dataset.Confirm to delete the file": "Confirm to Delete This File and All Its Data?",
|
||||||
@@ -962,6 +965,7 @@
|
|||||||
"permission.manager": "administrator",
|
"permission.manager": "administrator",
|
||||||
"permission.read": "Read permission",
|
"permission.read": "Read permission",
|
||||||
"permission.write": "write permission",
|
"permission.write": "write permission",
|
||||||
|
"please_input_name": "Please Enter a Name",
|
||||||
"plugin.App": "Select App",
|
"plugin.App": "Select App",
|
||||||
"plugin.Currentapp": "Current App",
|
"plugin.Currentapp": "Current App",
|
||||||
"plugin.Description": "Description",
|
"plugin.Description": "Description",
|
||||||
@@ -1260,7 +1264,7 @@
|
|||||||
"user.no_usage_records": "No Usage Records",
|
"user.no_usage_records": "No Usage Records",
|
||||||
"user.old_password": "Old Password",
|
"user.old_password": "Old Password",
|
||||||
"user.password_message": "Password must be at least 4 characters and at most 60 characters",
|
"user.password_message": "Password must be at least 4 characters and at most 60 characters",
|
||||||
"user.password_tip": "Password must be at least 6 characters long and contain at least two combinations: numbers, letters, or special characters",
|
"user.password_tip": "Password must be at least 8 characters long and contain at least two combinations: numbers, letters, or special characters",
|
||||||
"user.reset_password": "Reset Password",
|
"user.reset_password": "Reset Password",
|
||||||
"user.reset_password_tip": "The initial password is not set/the password has not been modified for a long time, please reset the password",
|
"user.reset_password_tip": "The initial password is not set/the password has not been modified for a long time, please reset the password",
|
||||||
"user.team.Balance": "Team Balance",
|
"user.team.Balance": "Team Balance",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"login_success": "Login successful",
|
"login_success": "Login successful",
|
||||||
"no_remind": "Don't remind again",
|
"no_remind": "Don't remind again",
|
||||||
"password_condition": "Password maximum 60 characters",
|
"password_condition": "Password maximum 60 characters",
|
||||||
"password_tip": "Password must be at least 6 characters long and contain at least two combinations: numbers, letters, or special characters",
|
"password_tip": "Password must be at least 8 characters long and contain at least two combinations: numbers, letters, or special characters",
|
||||||
"policy_tip": "By using this service, you agree to our",
|
"policy_tip": "By using this service, you agree to our",
|
||||||
"privacy": "Privacy Policy",
|
"privacy": "Privacy Policy",
|
||||||
"privacy_policy": "Privacy Policy",
|
"privacy_policy": "Privacy Policy",
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
"package_usage_rules": "套餐使用规则:系统优先使用更高级的套餐,原未用完的套餐将延后生效",
|
"package_usage_rules": "套餐使用规则:系统优先使用更高级的套餐,原未用完的套餐将延后生效",
|
||||||
"password": "密码",
|
"password": "密码",
|
||||||
"password_mismatch": "密码不一致: 两次密码不一致",
|
"password_mismatch": "密码不一致: 两次密码不一致",
|
||||||
"password_tip": "密码至少 6 位,且至少包含两种组合:数字、字母或特殊字符",
|
"password_tip": "密码至少 8 位,且至少包含两种组合:数字、字母或特殊字符",
|
||||||
"password_update_error": "修改密码异常",
|
"password_update_error": "修改密码异常",
|
||||||
"password_update_success": "修改密码成功",
|
"password_update_success": "修改密码成功",
|
||||||
"pending_usage": "待使用",
|
"pending_usage": "待使用",
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
"owner": "所有者",
|
"owner": "所有者",
|
||||||
"permission": "权限",
|
"permission": "权限",
|
||||||
"permission_apikeyCreate": "创建 API 密钥",
|
"permission_apikeyCreate": "创建 API 密钥",
|
||||||
"permission_apikeyCreate_Tip": "可以创建全局的 APIKey",
|
"permission_apikeyCreate_Tip": "可以创建全局的 APIKey和 MCP 服务",
|
||||||
"permission_appCreate": "创建应用",
|
"permission_appCreate": "创建应用",
|
||||||
"permission_appCreate_tip": "可以在根目录创建应用,(文件夹下的创建权限由文件夹控制)",
|
"permission_appCreate_tip": "可以在根目录创建应用,(文件夹下的创建权限由文件夹控制)",
|
||||||
"permission_datasetCreate": "创建知识库",
|
"permission_datasetCreate": "创建知识库",
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
"app.modules.click to update": "点击更新",
|
"app.modules.click to update": "点击更新",
|
||||||
"app.modules.has new version": "有新版本",
|
"app.modules.has new version": "有新版本",
|
||||||
"app.modules.not_found": "组件缺失",
|
"app.modules.not_found": "组件缺失",
|
||||||
"app.modules.not_found_tips": "系统内无法查找到该组件,请删除,否则流程无法正常运行",
|
|
||||||
"app.version_current": "当前版本",
|
"app.version_current": "当前版本",
|
||||||
"app.version_initial": "初始版本",
|
"app.version_initial": "初始版本",
|
||||||
"app.version_name_tips": "版本名称不能为空",
|
"app.version_name_tips": "版本名称不能为空",
|
||||||
@@ -202,6 +201,7 @@
|
|||||||
"type.error.Workflow data is empty": "没有获取到工作流数据",
|
"type.error.Workflow data is empty": "没有获取到工作流数据",
|
||||||
"type.error.workflowresponseempty": "响应内容为空",
|
"type.error.workflowresponseempty": "响应内容为空",
|
||||||
"type_not_recognized": "未识别到应用类型",
|
"type_not_recognized": "未识别到应用类型",
|
||||||
|
"un_auth": "无权限",
|
||||||
"upload_file_max_amount": "最大文件数量",
|
"upload_file_max_amount": "最大文件数量",
|
||||||
"upload_file_max_amount_tip": "单轮对话中最大上传文件数量",
|
"upload_file_max_amount_tip": "单轮对话中最大上传文件数量",
|
||||||
"variable.select type_desc": "可以为工作流定义全局变量,常用临时缓存。赋值的方式包括:\n1. 从对话页面的 query 参数获取。\n2. 通过 API 的 variables 对象传递。\n3. 通过【变量更新】节点进行赋值。",
|
"variable.select type_desc": "可以为工作流定义全局变量,常用临时缓存。赋值的方式包括:\n1. 从对话页面的 query 参数获取。\n2. 通过 API 的 variables 对象传递。\n3. 通过【变量更新】节点进行赋值。",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"Action": "操作",
|
"Action": "操作",
|
||||||
"Add": "添加",
|
"Add": "添加",
|
||||||
"add_success": "添加成功",
|
|
||||||
"Add_new_input": "新增输入",
|
"Add_new_input": "新增输入",
|
||||||
"All": "全部",
|
"All": "全部",
|
||||||
"App": "应用",
|
"App": "应用",
|
||||||
@@ -30,7 +29,7 @@
|
|||||||
"FAQ.ai_point_q": "什么是AI积分?",
|
"FAQ.ai_point_q": "什么是AI积分?",
|
||||||
"FAQ.check_subscription_a": "账号-个人信息-套餐详情-使用情况。您可以查看所拥有套餐的生效和到期时间。当付费套餐到期后将自动切换免费版。",
|
"FAQ.check_subscription_a": "账号-个人信息-套餐详情-使用情况。您可以查看所拥有套餐的生效和到期时间。当付费套餐到期后将自动切换免费版。",
|
||||||
"FAQ.check_subscription_q": "在哪里查看已订阅的套餐?",
|
"FAQ.check_subscription_q": "在哪里查看已订阅的套餐?",
|
||||||
"FAQ.dataset_compute_a": "1条知识库存储等于1条知识库索引。一条知识库数据可以包含1条或多条知识库索引。增强训练中,1条数据会生成5条索引。",
|
"FAQ.dataset_compute_a": "1条知识库存储等于1条知识库索引。一条分块数据,通常对应多条索引,可以在单个知识库集合中看到\"n组索引\"",
|
||||||
"FAQ.dataset_compute_q": "知识库存储怎么计算?",
|
"FAQ.dataset_compute_q": "知识库存储怎么计算?",
|
||||||
"FAQ.dataset_index_a": "不会。但知识库索引超出时,无法插入和更新知识库内容。",
|
"FAQ.dataset_index_a": "不会。但知识库索引超出时,无法插入和更新知识库内容。",
|
||||||
"FAQ.dataset_index_q": "知识库索引超出会删除么?",
|
"FAQ.dataset_index_q": "知识库索引超出会删除么?",
|
||||||
@@ -64,7 +63,6 @@
|
|||||||
"Parse": "解析",
|
"Parse": "解析",
|
||||||
"Permission": "权限",
|
"Permission": "权限",
|
||||||
"Permission_tip": "个人权限大于群组权限",
|
"Permission_tip": "个人权限大于群组权限",
|
||||||
"please_input_name": "请输入名称",
|
|
||||||
"Preview": "预览",
|
"Preview": "预览",
|
||||||
"Remove": "移除",
|
"Remove": "移除",
|
||||||
"Rename": "重命名",
|
"Rename": "重命名",
|
||||||
@@ -94,6 +92,7 @@
|
|||||||
"action_confirm": "操作确认",
|
"action_confirm": "操作确认",
|
||||||
"add_new": "新增",
|
"add_new": "新增",
|
||||||
"add_new_param": "新增参数",
|
"add_new_param": "新增参数",
|
||||||
|
"add_success": "添加成功",
|
||||||
"all_quotes": "全部引用",
|
"all_quotes": "全部引用",
|
||||||
"all_result": "完整结果",
|
"all_result": "完整结果",
|
||||||
"app_not_version": " 该应用未发布过,请先发布应用",
|
"app_not_version": " 该应用未发布过,请先发布应用",
|
||||||
@@ -149,6 +148,9 @@
|
|||||||
"code_error.plugin_error.not_exist": "插件不存在",
|
"code_error.plugin_error.not_exist": "插件不存在",
|
||||||
"code_error.plugin_error.un_auth": "无权操作该插件",
|
"code_error.plugin_error.un_auth": "无权操作该插件",
|
||||||
"code_error.system_error.community_version_num_limit": "超出开源版数量限制,请升级商业版: https://fastgpt.in",
|
"code_error.system_error.community_version_num_limit": "超出开源版数量限制,请升级商业版: https://fastgpt.in",
|
||||||
|
"code_error.system_error.license_app_amount_limit": "超出系统最大应用数量",
|
||||||
|
"code_error.system_error.license_dataset_amount_limit": "超出系统最大知识库数量",
|
||||||
|
"code_error.system_error.license_user_amount_limit": "超出系统最大用户数量",
|
||||||
"code_error.team_error.ai_points_not_enough": "AI 积分不足",
|
"code_error.team_error.ai_points_not_enough": "AI 积分不足",
|
||||||
"code_error.team_error.app_amount_not_enough": "应用数量已达上限~",
|
"code_error.team_error.app_amount_not_enough": "应用数量已达上限~",
|
||||||
"code_error.team_error.cannot_delete_default_group": "不能删除默认群组",
|
"code_error.team_error.cannot_delete_default_group": "不能删除默认群组",
|
||||||
@@ -751,6 +753,7 @@
|
|||||||
"data_index_image": "图片索引",
|
"data_index_image": "图片索引",
|
||||||
"data_index_question": "推测问题索引",
|
"data_index_question": "推测问题索引",
|
||||||
"data_index_summary": "摘要索引",
|
"data_index_summary": "摘要索引",
|
||||||
|
"data_not_found": "数据找不到了",
|
||||||
"dataset.Confirm move the folder": "确认移动到该目录",
|
"dataset.Confirm move the folder": "确认移动到该目录",
|
||||||
"dataset.Confirm to delete the data": "确认删除该数据?",
|
"dataset.Confirm to delete the data": "确认删除该数据?",
|
||||||
"dataset.Confirm to delete the file": "确认删除该文件及其所有数据?",
|
"dataset.Confirm to delete the file": "确认删除该文件及其所有数据?",
|
||||||
@@ -961,6 +964,7 @@
|
|||||||
"permission.manager": "管理员",
|
"permission.manager": "管理员",
|
||||||
"permission.read": "读权限",
|
"permission.read": "读权限",
|
||||||
"permission.write": "写权限",
|
"permission.write": "写权限",
|
||||||
|
"please_input_name": "请输入名称",
|
||||||
"plugin.App": "选择应用",
|
"plugin.App": "选择应用",
|
||||||
"plugin.Currentapp": "当前应用",
|
"plugin.Currentapp": "当前应用",
|
||||||
"plugin.Description": "描述",
|
"plugin.Description": "描述",
|
||||||
@@ -1259,7 +1263,7 @@
|
|||||||
"user.no_usage_records": "暂无使用记录",
|
"user.no_usage_records": "暂无使用记录",
|
||||||
"user.old_password": "旧密码",
|
"user.old_password": "旧密码",
|
||||||
"user.password_message": "密码最少 4 位最多 60 位",
|
"user.password_message": "密码最少 4 位最多 60 位",
|
||||||
"user.password_tip": "密码至少 6 位,且至少包含两种组合:数字、字母或特殊字符",
|
"user.password_tip": "密码至少 8 位,且至少包含两种组合:数字、字母或特殊字符",
|
||||||
"user.reset_password": "重置密码",
|
"user.reset_password": "重置密码",
|
||||||
"user.reset_password_tip": "未设置初始密码/长时间未修改密码,请重置密码",
|
"user.reset_password_tip": "未设置初始密码/长时间未修改密码,请重置密码",
|
||||||
"user.team.Balance": "团队余额",
|
"user.team.Balance": "团队余额",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"login_success": "登录成功",
|
"login_success": "登录成功",
|
||||||
"no_remind": "不再提醒",
|
"no_remind": "不再提醒",
|
||||||
"password_condition": "密码最多 60 位",
|
"password_condition": "密码最多 60 位",
|
||||||
"password_tip": "密码至少 6 位,且至少包含两种组合:数字、字母或特殊字符",
|
"password_tip": "密码至少 8 位,且至少包含两种组合:数字、字母或特殊字符",
|
||||||
"policy_tip": "使用即代表你同意我们的",
|
"policy_tip": "使用即代表你同意我们的",
|
||||||
"privacy": "隐私协议",
|
"privacy": "隐私协议",
|
||||||
"privacy_policy": "隐私政策",
|
"privacy_policy": "隐私政策",
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
"package_usage_rules": "套餐使用規則:系統優先使用更進階的套餐,原未用完的套餐將延遲生效",
|
"package_usage_rules": "套餐使用規則:系統優先使用更進階的套餐,原未用完的套餐將延遲生效",
|
||||||
"password": "密碼",
|
"password": "密碼",
|
||||||
"password_mismatch": "密碼不一致:兩次密碼不一致",
|
"password_mismatch": "密碼不一致:兩次密碼不一致",
|
||||||
"password_tip": "密碼至少 6 位,且至少包含兩種組合:數字、字母或特殊字元",
|
"password_tip": "密碼至少 8 位,且至少包含兩種組合:數字、字母或特殊字元",
|
||||||
"password_update_error": "修改密碼異常",
|
"password_update_error": "修改密碼異常",
|
||||||
"password_update_success": "修改密碼成功",
|
"password_update_success": "修改密碼成功",
|
||||||
"pending_usage": "待使用",
|
"pending_usage": "待使用",
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
"owner": "擁有者",
|
"owner": "擁有者",
|
||||||
"permission": "權限",
|
"permission": "權限",
|
||||||
"permission_apikeyCreate": "建立 API 密鑰",
|
"permission_apikeyCreate": "建立 API 密鑰",
|
||||||
"permission_apikeyCreate_Tip": "可以建立全域的 APIKey",
|
"permission_apikeyCreate_Tip": "可以創建全局的 APIKey和 MCP 服務",
|
||||||
"permission_appCreate": "建立應用程式",
|
"permission_appCreate": "建立應用程式",
|
||||||
"permission_appCreate_tip": "可以在根目錄建立應用程式,(資料夾下的建立權限由資料夾控制)",
|
"permission_appCreate_tip": "可以在根目錄建立應用程式,(資料夾下的建立權限由資料夾控制)",
|
||||||
"permission_datasetCreate": "建立知識庫",
|
"permission_datasetCreate": "建立知識庫",
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
"app.modules.click to update": "點選更新",
|
"app.modules.click to update": "點選更新",
|
||||||
"app.modules.has new version": "有新版本",
|
"app.modules.has new version": "有新版本",
|
||||||
"app.modules.not_found": "元件遺失",
|
"app.modules.not_found": "元件遺失",
|
||||||
"app.modules.not_found_tips": "系統內無法查詢到該元件,請刪除,否則流程無法正常運作",
|
|
||||||
"app.version_current": "目前版本",
|
"app.version_current": "目前版本",
|
||||||
"app.version_initial": "初始版本",
|
"app.version_initial": "初始版本",
|
||||||
"app.version_name_tips": "版本名稱不能空白",
|
"app.version_name_tips": "版本名稱不能空白",
|
||||||
@@ -193,6 +192,7 @@
|
|||||||
"type.error.Workflow data is empty": "沒有獲取到工作流數據",
|
"type.error.Workflow data is empty": "沒有獲取到工作流數據",
|
||||||
"type.error.workflowresponseempty": "響應內容為空",
|
"type.error.workflowresponseempty": "響應內容為空",
|
||||||
"type_not_recognized": "未識別到應用程式類型",
|
"type_not_recognized": "未識別到應用程式類型",
|
||||||
|
"un_auth": "無權限",
|
||||||
"upload_file_max_amount": "最大檔案數量",
|
"upload_file_max_amount": "最大檔案數量",
|
||||||
"upload_file_max_amount_tip": "單輪對話中最大上傳檔案數量",
|
"upload_file_max_amount_tip": "單輪對話中最大上傳檔案數量",
|
||||||
"variable.select type_desc": "可以為工作流程定義全域變數,常用於暫存。賦值的方式包括:\n1. 從對話頁面的 query 參數取得。\n2. 透過 API 的 variables 物件傳遞。\n3. 透過【變數更新】節點進行賦值。",
|
"variable.select type_desc": "可以為工作流程定義全域變數,常用於暫存。賦值的方式包括:\n1. 從對話頁面的 query 參數取得。\n2. 透過 API 的 variables 物件傳遞。\n3. 透過【變數更新】節點進行賦值。",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"Action": "操作",
|
"Action": "操作",
|
||||||
"Add": "新增",
|
"Add": "新增",
|
||||||
"add_success": "新增成功",
|
|
||||||
"Add_new_input": "新增輸入",
|
"Add_new_input": "新增輸入",
|
||||||
"All": "全部",
|
"All": "全部",
|
||||||
"App": "應用程式",
|
"App": "應用程式",
|
||||||
@@ -30,7 +29,7 @@
|
|||||||
"FAQ.ai_point_q": "什麼是 AI 點數?",
|
"FAQ.ai_point_q": "什麼是 AI 點數?",
|
||||||
"FAQ.check_subscription_a": "帳號 - 個人資訊 - 方案詳細資訊 - 使用狀況。您可以檢視已訂閱方案的生效和到期時間。當付費方案到期後,將自動切換為免費版。",
|
"FAQ.check_subscription_a": "帳號 - 個人資訊 - 方案詳細資訊 - 使用狀況。您可以檢視已訂閱方案的生效和到期時間。當付費方案到期後,將自動切換為免費版。",
|
||||||
"FAQ.check_subscription_q": "在哪裡可以檢視已訂閱的方案?",
|
"FAQ.check_subscription_q": "在哪裡可以檢視已訂閱的方案?",
|
||||||
"FAQ.dataset_compute_a": "1 筆知識庫儲存等於 1 筆知識庫索引。一筆知識庫資料可以包含 1 筆或多筆知識庫索引。在增強訓練中,1 筆資料會產生 5 筆索引。",
|
"FAQ.dataset_compute_a": "1條知識庫存儲等於1條知識庫索引。\n一條分塊數據,通常對應多條索引,可以在單個知識庫集合中看到\"n組索引\"",
|
||||||
"FAQ.dataset_compute_q": "知識庫儲存如何計算?",
|
"FAQ.dataset_compute_q": "知識庫儲存如何計算?",
|
||||||
"FAQ.dataset_index_a": "不會,但知識庫索引超出限制時,將無法插入和更新知識庫內容。",
|
"FAQ.dataset_index_a": "不會,但知識庫索引超出限制時,將無法插入和更新知識庫內容。",
|
||||||
"FAQ.dataset_index_q": "知識庫索引超出是否會被刪除?",
|
"FAQ.dataset_index_q": "知識庫索引超出是否會被刪除?",
|
||||||
@@ -64,7 +63,6 @@
|
|||||||
"Parse": "解析",
|
"Parse": "解析",
|
||||||
"Permission": "權限",
|
"Permission": "權限",
|
||||||
"Permission_tip": "個人權限大於群組權限",
|
"Permission_tip": "個人權限大於群組權限",
|
||||||
"please_input_name": "請輸入名稱",
|
|
||||||
"Preview": "預覽",
|
"Preview": "預覽",
|
||||||
"Remove": "移除",
|
"Remove": "移除",
|
||||||
"Rename": "重新命名",
|
"Rename": "重新命名",
|
||||||
@@ -94,6 +92,7 @@
|
|||||||
"action_confirm": "確認",
|
"action_confirm": "確認",
|
||||||
"add_new": "新增",
|
"add_new": "新增",
|
||||||
"add_new_param": "新增參數",
|
"add_new_param": "新增參數",
|
||||||
|
"add_success": "新增成功",
|
||||||
"all_quotes": "全部引用",
|
"all_quotes": "全部引用",
|
||||||
"all_result": "完整結果",
|
"all_result": "完整結果",
|
||||||
"app_not_version": "該應用未發布過,請先發布應用",
|
"app_not_version": "該應用未發布過,請先發布應用",
|
||||||
@@ -148,6 +147,9 @@
|
|||||||
"code_error.plugin_error.not_exist": "外掛程式不存在",
|
"code_error.plugin_error.not_exist": "外掛程式不存在",
|
||||||
"code_error.plugin_error.un_auth": "無權操作此外掛程式",
|
"code_error.plugin_error.un_auth": "無權操作此外掛程式",
|
||||||
"code_error.system_error.community_version_num_limit": "超出開源版數量限制,請升級商業版:https://tryfastgpt.ai",
|
"code_error.system_error.community_version_num_limit": "超出開源版數量限制,請升級商業版:https://tryfastgpt.ai",
|
||||||
|
"code_error.system_error.license_app_amount_limit": "超出系統最大應用數量",
|
||||||
|
"code_error.system_error.license_dataset_amount_limit": "超出系統最大知識庫數量",
|
||||||
|
"code_error.system_error.license_user_amount_limit": "超出系統最大用戶數量",
|
||||||
"code_error.team_error.ai_points_not_enough": "AI 點數不足",
|
"code_error.team_error.ai_points_not_enough": "AI 點數不足",
|
||||||
"code_error.team_error.app_amount_not_enough": "已達應用程式數量上限",
|
"code_error.team_error.app_amount_not_enough": "已達應用程式數量上限",
|
||||||
"code_error.team_error.cannot_delete_default_group": "無法刪除預設群組",
|
"code_error.team_error.cannot_delete_default_group": "無法刪除預設群組",
|
||||||
@@ -751,6 +753,7 @@
|
|||||||
"data_index_image": "圖片索引",
|
"data_index_image": "圖片索引",
|
||||||
"data_index_question": "推測問題索引",
|
"data_index_question": "推測問題索引",
|
||||||
"data_index_summary": "摘要索引",
|
"data_index_summary": "摘要索引",
|
||||||
|
"data_not_found": "數據找不到了",
|
||||||
"dataset.Confirm move the folder": "確認移動到此目錄",
|
"dataset.Confirm move the folder": "確認移動到此目錄",
|
||||||
"dataset.Confirm to delete the data": "確認刪除此資料?",
|
"dataset.Confirm to delete the data": "確認刪除此資料?",
|
||||||
"dataset.Confirm to delete the file": "確認刪除此檔案及其所有資料?",
|
"dataset.Confirm to delete the file": "確認刪除此檔案及其所有資料?",
|
||||||
@@ -961,6 +964,7 @@
|
|||||||
"permission.manager": "管理員",
|
"permission.manager": "管理員",
|
||||||
"permission.read": "讀取權限",
|
"permission.read": "讀取權限",
|
||||||
"permission.write": "寫入權限",
|
"permission.write": "寫入權限",
|
||||||
|
"please_input_name": "請輸入名稱",
|
||||||
"plugin.App": "選擇應用程式",
|
"plugin.App": "選擇應用程式",
|
||||||
"plugin.Currentapp": "目前應用程式",
|
"plugin.Currentapp": "目前應用程式",
|
||||||
"plugin.Description": "描述",
|
"plugin.Description": "描述",
|
||||||
@@ -1259,7 +1263,7 @@
|
|||||||
"user.no_usage_records": "無使用紀錄",
|
"user.no_usage_records": "無使用紀錄",
|
||||||
"user.old_password": "舊密碼",
|
"user.old_password": "舊密碼",
|
||||||
"user.password_message": "密碼最少 4 位最多 60 位",
|
"user.password_message": "密碼最少 4 位最多 60 位",
|
||||||
"user.password_tip": "密碼至少 6 位,且至少包含兩種組合:數字、字母或特殊字元",
|
"user.password_tip": "密碼至少 8 位,且至少包含兩種組合:數字、字母或特殊字元",
|
||||||
"user.reset_password": "重置密碼",
|
"user.reset_password": "重置密碼",
|
||||||
"user.reset_password_tip": "未設置初始密碼/長時間未修改密碼,請重置密碼",
|
"user.reset_password_tip": "未設置初始密碼/長時間未修改密碼,請重置密碼",
|
||||||
"user.team.Balance": "團隊餘額",
|
"user.team.Balance": "團隊餘額",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"login_success": "登入成功",
|
"login_success": "登入成功",
|
||||||
"no_remind": "不再提醒",
|
"no_remind": "不再提醒",
|
||||||
"password_condition": "密碼最多 60 個字元",
|
"password_condition": "密碼最多 60 個字元",
|
||||||
"password_tip": "密碼至少 6 位,且至少包含兩種組合:數字、字母或特殊字元",
|
"password_tip": "密碼至少 8 位,且至少包含兩種組合:數字、字母或特殊字元",
|
||||||
"policy_tip": "使用即代表您同意我們的",
|
"policy_tip": "使用即代表您同意我們的",
|
||||||
"privacy": "隱私權政策",
|
"privacy": "隱私權政策",
|
||||||
"privacy_policy": "隱私權政策",
|
"privacy_policy": "隱私權政策",
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ LOG_DEPTH=3
|
|||||||
DEFAULT_ROOT_PSW=123456
|
DEFAULT_ROOT_PSW=123456
|
||||||
# 数据库最大连接数
|
# 数据库最大连接数
|
||||||
DB_MAX_LINK=5
|
DB_MAX_LINK=5
|
||||||
# token
|
|
||||||
TOKEN_KEY=dfdasfdas
|
|
||||||
# 文件阅读时的密钥
|
# 文件阅读时的密钥
|
||||||
FILE_TOKEN_KEY=filetokenkey
|
FILE_TOKEN_KEY=filetokenkey
|
||||||
# root key, 最高权限
|
# root key, 最高权限
|
||||||
@@ -65,6 +63,8 @@ CHECK_INTERNAL_IP=false
|
|||||||
PASSWORD_LOGIN_LOCK_SECONDS=
|
PASSWORD_LOGIN_LOCK_SECONDS=
|
||||||
# 密码过期月份,不设置则不会过期
|
# 密码过期月份,不设置则不会过期
|
||||||
PASSWORD_EXPIRED_MONTH=
|
PASSWORD_EXPIRED_MONTH=
|
||||||
|
# 最大登录客户端数量,默认为 10
|
||||||
|
MAX_LOGIN_SESSION=
|
||||||
|
|
||||||
# 特殊配置
|
# 特殊配置
|
||||||
# 自定义跨域,不配置时,默认都允许跨域(逗号分割)
|
# 自定义跨域,不配置时,默认都允许跨域(逗号分割)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "app",
|
"name": "app",
|
||||||
"version": "4.9.7",
|
"version": "4.9.8",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
|
|||||||
@@ -22,135 +22,184 @@ import { getCollectionSourceData } from '@fastgpt/global/core/dataset/collection
|
|||||||
import Markdown from '.';
|
import Markdown from '.';
|
||||||
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
|
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
|
||||||
import { Types } from 'mongoose';
|
import { Types } from 'mongoose';
|
||||||
|
import type { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||||
|
import { useCreation } from 'ahooks';
|
||||||
|
|
||||||
const A = ({ children, chatAuthData, showAnimation, ...props }: any) => {
|
export type AProps = {
|
||||||
|
chatAuthData?: {
|
||||||
|
appId: string;
|
||||||
|
chatId: string;
|
||||||
|
chatItemDataId: string;
|
||||||
|
} & OutLinkChatAuthProps;
|
||||||
|
onOpenCiteModal?: (e?: {
|
||||||
|
collectionId?: string;
|
||||||
|
sourceId?: string;
|
||||||
|
sourceName?: string;
|
||||||
|
datasetId?: string;
|
||||||
|
quoteId?: string;
|
||||||
|
}) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const EmptyHrefLink = function EmptyHrefLink({ content }: { content: string }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<MyTooltip label={t('common:core.chat.markdown.Quick Question')}>
|
||||||
|
<Button
|
||||||
|
variant={'whitePrimary'}
|
||||||
|
size={'xs'}
|
||||||
|
borderRadius={'md'}
|
||||||
|
my={1}
|
||||||
|
onClick={() => eventBus.emit(EventNameEnum.sendQuestion, { text: content })}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</Button>
|
||||||
|
</MyTooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CiteLink = React.memo(function CiteLink({
|
||||||
|
id,
|
||||||
|
chatAuthData,
|
||||||
|
onOpenCiteModal,
|
||||||
|
showAnimation
|
||||||
|
}: { id: string; showAnimation?: boolean } & AProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const content = useMemo(() => String(children), [children]);
|
|
||||||
|
if (!Types.ObjectId.isValid(id)) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: datasetCiteData,
|
||||||
|
loading,
|
||||||
|
runAsync: getQuoteDataById
|
||||||
|
} = useRequest2((id: string) => getQuoteData({ id, ...chatAuthData }), {
|
||||||
|
manual: true
|
||||||
|
});
|
||||||
|
const sourceData = useMemo(
|
||||||
|
() => getCollectionSourceData(datasetCiteData?.collection),
|
||||||
|
[datasetCiteData?.collection]
|
||||||
|
);
|
||||||
|
const icon = useMemo(
|
||||||
|
() => getSourceNameIcon({ sourceId: sourceData.sourceId, sourceName: sourceData.sourceName }),
|
||||||
|
[sourceData]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
isLazy
|
||||||
|
direction="rtl"
|
||||||
|
placement="bottom"
|
||||||
|
strategy={'fixed'}
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
onOpen={() => {
|
||||||
|
onOpen();
|
||||||
|
if (showAnimation) return;
|
||||||
|
getQuoteDataById(id);
|
||||||
|
}}
|
||||||
|
trigger={'hover'}
|
||||||
|
gutter={4}
|
||||||
|
>
|
||||||
|
<PopoverTrigger>
|
||||||
|
<Button variant={'unstyled'} minH={0} minW={0} h={'auto'}>
|
||||||
|
<MyIcon
|
||||||
|
name={'core/chat/quoteSign'}
|
||||||
|
w={'1rem'}
|
||||||
|
color={'primary.700'}
|
||||||
|
cursor={'pointer'}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent boxShadow={'lg'} w={'500px'} maxW={'90vw'} py={4}>
|
||||||
|
<MyBox isLoading={loading || showAnimation}>
|
||||||
|
<PopoverArrow />
|
||||||
|
<PopoverBody py={0} px={0} fontSize={'sm'}>
|
||||||
|
<Flex px={4} pb={1} justifyContent={'space-between'}>
|
||||||
|
<Box
|
||||||
|
alignItems={'center'}
|
||||||
|
fontSize={'xs'}
|
||||||
|
border={'sm'}
|
||||||
|
borderRadius={'sm'}
|
||||||
|
overflow={'hidden'}
|
||||||
|
display={'inline-flex'}
|
||||||
|
height={6}
|
||||||
|
mr={1}
|
||||||
|
>
|
||||||
|
<Flex px={1.5}>
|
||||||
|
<MyIcon name={icon as any} mr={1} flexShrink={0} w={'12px'} />
|
||||||
|
<Box
|
||||||
|
className={'textEllipsis'}
|
||||||
|
wordBreak={'break-all'}
|
||||||
|
flex={'1 0 0'}
|
||||||
|
fontSize={'mini'}
|
||||||
|
color={'myGray.900'}
|
||||||
|
>
|
||||||
|
{sourceData.sourceName}
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
<Button
|
||||||
|
variant={'ghost'}
|
||||||
|
color={'primary.600'}
|
||||||
|
size={'xs'}
|
||||||
|
onClick={() => {
|
||||||
|
onClose();
|
||||||
|
onOpenCiteModal?.({
|
||||||
|
quoteId: id,
|
||||||
|
sourceId: sourceData.sourceId,
|
||||||
|
sourceName: sourceData.sourceName,
|
||||||
|
datasetId: datasetCiteData?.collection.datasetId,
|
||||||
|
collectionId: datasetCiteData?.collection._id
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('common:all_quotes')}
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
<Box h={'300px'} overflow={'auto'} px={4}>
|
||||||
|
<Markdown source={datasetCiteData?.q} />
|
||||||
|
{datasetCiteData?.a && <Markdown source={datasetCiteData?.a} />}
|
||||||
|
</Box>
|
||||||
|
</PopoverBody>
|
||||||
|
</MyBox>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const A = ({
|
||||||
|
children,
|
||||||
|
chatAuthData,
|
||||||
|
onOpenCiteModal,
|
||||||
|
showAnimation,
|
||||||
|
...props
|
||||||
|
}: AProps & {
|
||||||
|
children: any;
|
||||||
|
showAnimation: boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
}) => {
|
||||||
|
const content = useCreation(() => String(children), [children]);
|
||||||
|
|
||||||
// empty href link
|
// empty href link
|
||||||
if (!props.href && typeof children?.[0] === 'string') {
|
if (!props.href && typeof children?.[0] === 'string') {
|
||||||
return (
|
return <EmptyHrefLink content={content} />;
|
||||||
<MyTooltip label={t('common:core.chat.markdown.Quick Question')}>
|
|
||||||
<Button
|
|
||||||
variant={'whitePrimary'}
|
|
||||||
size={'xs'}
|
|
||||||
borderRadius={'md'}
|
|
||||||
my={1}
|
|
||||||
onClick={() => eventBus.emit(EventNameEnum.sendQuestion, { text: content })}
|
|
||||||
>
|
|
||||||
{content}
|
|
||||||
</Button>
|
|
||||||
</MyTooltip>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cite
|
// Cite
|
||||||
if (
|
if (
|
||||||
(props.href?.startsWith('CITE') || props.href?.startsWith('QUOTE')) &&
|
(props.href?.startsWith('CITE') || props.href?.startsWith('QUOTE')) &&
|
||||||
typeof children?.[0] === 'string'
|
typeof content === 'string'
|
||||||
) {
|
) {
|
||||||
if (!Types.ObjectId.isValid(content)) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: quoteData,
|
|
||||||
loading,
|
|
||||||
runAsync: getQuoteDataById
|
|
||||||
} = useRequest2((id: string) => getQuoteData({ id, ...chatAuthData }), {
|
|
||||||
manual: true
|
|
||||||
});
|
|
||||||
const sourceData = useMemo(
|
|
||||||
() => getCollectionSourceData(quoteData?.collection),
|
|
||||||
[quoteData?.collection]
|
|
||||||
);
|
|
||||||
const icon = useMemo(
|
|
||||||
() => getSourceNameIcon({ sourceId: sourceData.sourceId, sourceName: sourceData.sourceName }),
|
|
||||||
[sourceData]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover
|
<CiteLink
|
||||||
isLazy
|
id={content}
|
||||||
direction="rtl"
|
chatAuthData={chatAuthData}
|
||||||
placement="bottom"
|
onOpenCiteModal={onOpenCiteModal}
|
||||||
strategy={'fixed'}
|
showAnimation={showAnimation}
|
||||||
isOpen={isOpen}
|
/>
|
||||||
onClose={onClose}
|
|
||||||
onOpen={() => {
|
|
||||||
onOpen();
|
|
||||||
if (showAnimation) return;
|
|
||||||
getQuoteDataById(String(children));
|
|
||||||
}}
|
|
||||||
trigger={'hover'}
|
|
||||||
gutter={4}
|
|
||||||
>
|
|
||||||
<PopoverTrigger>
|
|
||||||
<Button variant={'unstyled'} minH={0} minW={0} h={'auto'}>
|
|
||||||
<MyIcon
|
|
||||||
name={'core/chat/quoteSign'}
|
|
||||||
w={'1rem'}
|
|
||||||
color={'primary.700'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent boxShadow={'lg'} w={'500px'} maxW={'90vw'} py={4}>
|
|
||||||
<MyBox isLoading={loading || showAnimation}>
|
|
||||||
<PopoverArrow />
|
|
||||||
<PopoverBody py={0} px={0} fontSize={'sm'}>
|
|
||||||
<Flex px={4} pb={1} justifyContent={'space-between'}>
|
|
||||||
<Box
|
|
||||||
alignItems={'center'}
|
|
||||||
fontSize={'xs'}
|
|
||||||
border={'sm'}
|
|
||||||
borderRadius={'sm'}
|
|
||||||
overflow={'hidden'}
|
|
||||||
display={'inline-flex'}
|
|
||||||
height={6}
|
|
||||||
mr={1}
|
|
||||||
>
|
|
||||||
<Flex px={1.5}>
|
|
||||||
<MyIcon name={icon as any} mr={1} flexShrink={0} w={'12px'} />
|
|
||||||
<Box
|
|
||||||
className={'textEllipsis'}
|
|
||||||
wordBreak={'break-all'}
|
|
||||||
flex={'1 0 0'}
|
|
||||||
fontSize={'mini'}
|
|
||||||
color={'myGray.900'}
|
|
||||||
>
|
|
||||||
{sourceData.sourceName}
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
<Button
|
|
||||||
variant={'ghost'}
|
|
||||||
color={'primary.600'}
|
|
||||||
size={'xs'}
|
|
||||||
onClick={() => {
|
|
||||||
onClose();
|
|
||||||
eventBus.emit(EventNameEnum.openQuoteReader, {
|
|
||||||
quoteId: String(children),
|
|
||||||
sourceId: sourceData.sourceId,
|
|
||||||
sourceName: sourceData.sourceName,
|
|
||||||
datasetId: quoteData?.collection.datasetId,
|
|
||||||
collectionId: quoteData?.collection._id
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('common:all_quotes')}
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
<Box h={'300px'} overflow={'auto'} px={4}>
|
|
||||||
<Markdown source={quoteData?.q} />
|
|
||||||
{quoteData?.a && <Markdown source={quoteData?.a} />}
|
|
||||||
</Box>
|
|
||||||
</PopoverBody>
|
|
||||||
</MyBox>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import dynamic from 'next/dynamic';
|
|||||||
import { Box } from '@chakra-ui/react';
|
import { Box } from '@chakra-ui/react';
|
||||||
import { CodeClassNameEnum, mdTextFormat } from './utils';
|
import { CodeClassNameEnum, mdTextFormat } from './utils';
|
||||||
import { useCreation } from 'ahooks';
|
import { useCreation } from 'ahooks';
|
||||||
import { type OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
import type { AProps } from './A';
|
||||||
|
|
||||||
const CodeLight = dynamic(() => import('./codeBlock/CodeLight'), { ssr: false });
|
const CodeLight = dynamic(() => import('./codeBlock/CodeLight'), { ssr: false });
|
||||||
const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'), { ssr: false });
|
const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'), { ssr: false });
|
||||||
@@ -33,12 +33,7 @@ type Props = {
|
|||||||
showAnimation?: boolean;
|
showAnimation?: boolean;
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
forbidZhFormat?: boolean;
|
forbidZhFormat?: boolean;
|
||||||
chatAuthData?: {
|
} & AProps;
|
||||||
appId: string;
|
|
||||||
chatId: string;
|
|
||||||
chatItemDataId: string;
|
|
||||||
} & OutLinkChatAuthProps;
|
|
||||||
};
|
|
||||||
const Markdown = (props: Props) => {
|
const Markdown = (props: Props) => {
|
||||||
const source = props.source || '';
|
const source = props.source || '';
|
||||||
|
|
||||||
@@ -53,16 +48,25 @@ const MarkdownRender = ({
|
|||||||
showAnimation,
|
showAnimation,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
forbidZhFormat,
|
forbidZhFormat,
|
||||||
chatAuthData
|
|
||||||
|
chatAuthData,
|
||||||
|
onOpenCiteModal
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const components = useCreation(() => {
|
const components = useCreation(() => {
|
||||||
return {
|
return {
|
||||||
img: Image,
|
img: Image,
|
||||||
pre: RewritePre,
|
pre: RewritePre,
|
||||||
code: Code,
|
code: Code,
|
||||||
a: (props: any) => <A {...props} showAnimation={showAnimation} chatAuthData={chatAuthData} />
|
a: (props: any) => (
|
||||||
|
<A
|
||||||
|
{...props}
|
||||||
|
showAnimation={showAnimation}
|
||||||
|
chatAuthData={chatAuthData}
|
||||||
|
onOpenCiteModal={onOpenCiteModal}
|
||||||
|
/>
|
||||||
|
)
|
||||||
};
|
};
|
||||||
}, [chatAuthData, showAnimation]);
|
}, [chatAuthData, onOpenCiteModal, showAnimation]);
|
||||||
|
|
||||||
const formatSource = useMemo(() => {
|
const formatSource = useMemo(() => {
|
||||||
if (showAnimation || forbidZhFormat) return source;
|
if (showAnimation || forbidZhFormat) return source;
|
||||||
|
|||||||
@@ -28,10 +28,15 @@ import { isEqual } from 'lodash';
|
|||||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||||
import { formatTimeToChatItemTime } from '@fastgpt/global/common/string/time';
|
import { formatTimeToChatItemTime } from '@fastgpt/global/common/string/time';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
import {
|
||||||
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
ChatItemContext,
|
||||||
|
type OnOpenCiteModalProps
|
||||||
|
} from '@/web/core/chat/context/chatItemContext';
|
||||||
import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils';
|
import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils';
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import dynamic from 'next/dynamic';
|
||||||
|
import { useMemoizedFn } from 'ahooks';
|
||||||
|
|
||||||
|
const ResponseTags = dynamic(() => import('./ResponseTags'));
|
||||||
|
|
||||||
const colorMap = {
|
const colorMap = {
|
||||||
[ChatStatusEnum.loading]: {
|
[ChatStatusEnum.loading]: {
|
||||||
@@ -88,13 +93,15 @@ const AIContentCard = React.memo(function AIContentCard({
|
|||||||
dataId,
|
dataId,
|
||||||
isLastChild,
|
isLastChild,
|
||||||
isChatting,
|
isChatting,
|
||||||
questionGuides
|
questionGuides,
|
||||||
|
onOpenCiteModal
|
||||||
}: {
|
}: {
|
||||||
dataId: string;
|
dataId: string;
|
||||||
chatValue: ChatItemValueItemType[];
|
chatValue: ChatItemValueItemType[];
|
||||||
isLastChild: boolean;
|
isLastChild: boolean;
|
||||||
isChatting: boolean;
|
isChatting: boolean;
|
||||||
questionGuides: string[];
|
questionGuides: string[];
|
||||||
|
onOpenCiteModal: (e?: OnOpenCiteModalProps) => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Flex flexDirection={'column'} gap={2}>
|
<Flex flexDirection={'column'} gap={2}>
|
||||||
@@ -108,6 +115,7 @@ const AIContentCard = React.memo(function AIContentCard({
|
|||||||
value={value}
|
value={value}
|
||||||
isLastResponseValue={isLastChild && i === chatValue.length - 1}
|
isLastResponseValue={isLastChild && i === chatValue.length - 1}
|
||||||
isChatting={isChatting}
|
isChatting={isChatting}
|
||||||
|
onOpenCiteModal={onOpenCiteModal}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -122,7 +130,6 @@ const ChatItem = (props: Props) => {
|
|||||||
const { type, avatar, statusBoxData, children, isLastChild, questionGuides = [], chat } = props;
|
const { type, avatar, statusBoxData, children, isLastChild, questionGuides = [], chat } = props;
|
||||||
|
|
||||||
const { isPc } = useSystem();
|
const { isPc } = useSystem();
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const styleMap: BoxProps = {
|
const styleMap: BoxProps = {
|
||||||
...(type === ChatRoleEnum.Human
|
...(type === ChatRoleEnum.Human
|
||||||
@@ -150,7 +157,6 @@ const ChatItem = (props: Props) => {
|
|||||||
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
|
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
|
||||||
const showNodeStatus = useContextSelector(ChatItemContext, (v) => v.showNodeStatus);
|
const showNodeStatus = useContextSelector(ChatItemContext, (v) => v.showNodeStatus);
|
||||||
|
|
||||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
|
||||||
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
|
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
|
||||||
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
|
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
|
||||||
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
|
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
|
||||||
@@ -228,64 +234,48 @@ const ChatItem = (props: Props) => {
|
|||||||
return groupedValues;
|
return groupedValues;
|
||||||
}, [chat.obj, chat.value, isChatting]);
|
}, [chat.obj, chat.value, isChatting]);
|
||||||
|
|
||||||
const handleOpenQuoteReader = useCallback(
|
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||||
({
|
const onOpenCiteModal = useMemoizedFn(
|
||||||
collectionId,
|
(item?: {
|
||||||
sourceId,
|
|
||||||
sourceName,
|
|
||||||
datasetId,
|
|
||||||
quoteId
|
|
||||||
}: {
|
|
||||||
collectionId?: string;
|
collectionId?: string;
|
||||||
sourceId?: string;
|
sourceId?: string;
|
||||||
sourceName?: string;
|
sourceName?: string;
|
||||||
datasetId?: string;
|
datasetId?: string;
|
||||||
quoteId?: string;
|
quoteId?: string;
|
||||||
}) => {
|
}) => {
|
||||||
if (!setQuoteData) return;
|
const collectionIdList = item?.collectionId
|
||||||
|
? [item.collectionId]
|
||||||
const collectionIdList = collectionId
|
|
||||||
? [collectionId]
|
|
||||||
: [...new Set(quoteList.map((item) => item.collectionId))];
|
: [...new Set(quoteList.map((item) => item.collectionId))];
|
||||||
|
|
||||||
setQuoteData({
|
setCiteModalData({
|
||||||
rawSearch: quoteList,
|
rawSearch: quoteList,
|
||||||
metadata:
|
metadata:
|
||||||
collectionId && isShowReadRawSource
|
item?.collectionId && isShowReadRawSource
|
||||||
? {
|
? {
|
||||||
appId: appId,
|
appId: appId,
|
||||||
chatId: chatId,
|
chatId: chatId,
|
||||||
chatItemDataId: chat.dataId,
|
chatItemDataId: chat.dataId,
|
||||||
collectionId: collectionId,
|
collectionId: item.collectionId,
|
||||||
collectionIdList,
|
collectionIdList,
|
||||||
sourceId: sourceId || '',
|
sourceId: item.sourceId || '',
|
||||||
sourceName: sourceName || '',
|
sourceName: item.sourceName || '',
|
||||||
datasetId: datasetId || '',
|
datasetId: item.datasetId || '',
|
||||||
outLinkAuthData,
|
outLinkAuthData,
|
||||||
quoteId
|
quoteId: item.quoteId
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
appId: appId,
|
appId: appId,
|
||||||
chatId: chatId,
|
chatId: chatId,
|
||||||
chatItemDataId: chat.dataId,
|
chatItemDataId: chat.dataId,
|
||||||
collectionIdList,
|
collectionIdList,
|
||||||
sourceId: sourceId,
|
sourceId: item?.sourceId,
|
||||||
sourceName: sourceName,
|
sourceName: item?.sourceName,
|
||||||
outLinkAuthData
|
outLinkAuthData
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
[setQuoteData, quoteList, isShowReadRawSource, appId, chatId, chat.dataId, outLinkAuthData]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (chat.obj !== ChatRoleEnum.AI) return;
|
|
||||||
eventBus.on(EventNameEnum.openQuoteReader, handleOpenQuoteReader);
|
|
||||||
return () => {
|
|
||||||
eventBus.off(EventNameEnum.openQuoteReader);
|
|
||||||
};
|
|
||||||
}, [chat.obj, handleOpenQuoteReader]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
_hover={{
|
_hover={{
|
||||||
@@ -362,13 +352,23 @@ const ChatItem = (props: Props) => {
|
|||||||
>
|
>
|
||||||
{type === ChatRoleEnum.Human && <HumanContentCard chatValue={value} />}
|
{type === ChatRoleEnum.Human && <HumanContentCard chatValue={value} />}
|
||||||
{type === ChatRoleEnum.AI && (
|
{type === ChatRoleEnum.AI && (
|
||||||
<AIContentCard
|
<>
|
||||||
chatValue={value}
|
<AIContentCard
|
||||||
dataId={chat.dataId}
|
chatValue={value}
|
||||||
isLastChild={isLastChild && i === splitAiResponseResults.length - 1}
|
dataId={chat.dataId}
|
||||||
isChatting={isChatting}
|
isLastChild={isLastChild && i === splitAiResponseResults.length - 1}
|
||||||
questionGuides={questionGuides}
|
isChatting={isChatting}
|
||||||
/>
|
questionGuides={questionGuides}
|
||||||
|
onOpenCiteModal={onOpenCiteModal}
|
||||||
|
/>
|
||||||
|
{i === splitAiResponseResults.length - 1 && (
|
||||||
|
<ResponseTags
|
||||||
|
showTags={!isLastChild || !isChatting}
|
||||||
|
historyItem={chat}
|
||||||
|
onOpenCiteModal={onOpenCiteModal}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{/* Example: Response tags. A set of dialogs only needs to be displayed once*/}
|
{/* Example: Response tags. A set of dialogs only needs to be displayed once*/}
|
||||||
{i === splitAiResponseResults.length - 1 && <>{children}</>}
|
{i === splitAiResponseResults.length - 1 && <>{children}</>}
|
||||||
|
|||||||
@@ -14,17 +14,24 @@ import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils';
|
|||||||
import { useSize } from 'ahooks';
|
import { useSize } from 'ahooks';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { ChatBoxContext } from '../Provider';
|
import { ChatBoxContext } from '../Provider';
|
||||||
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
|
||||||
|
|
||||||
const ContextModal = dynamic(() => import('./ContextModal'));
|
const ContextModal = dynamic(() => import('./ContextModal'));
|
||||||
const WholeResponseModal = dynamic(() => import('../../../components/WholeResponseModal'));
|
const WholeResponseModal = dynamic(() => import('../../../components/WholeResponseModal'));
|
||||||
|
|
||||||
const ResponseTags = ({
|
const ResponseTags = ({
|
||||||
showTags,
|
showTags,
|
||||||
historyItem
|
historyItem,
|
||||||
|
onOpenCiteModal
|
||||||
}: {
|
}: {
|
||||||
showTags: boolean;
|
showTags: boolean;
|
||||||
historyItem: ChatSiteItemType;
|
historyItem: ChatSiteItemType;
|
||||||
|
onOpenCiteModal: (e?: {
|
||||||
|
collectionId?: string;
|
||||||
|
sourceId?: string;
|
||||||
|
sourceName?: string;
|
||||||
|
datasetId?: string;
|
||||||
|
quoteId?: string;
|
||||||
|
}) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { isPc } = useSystem();
|
const { isPc } = useSystem();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -80,15 +87,6 @@ const ResponseTags = ({
|
|||||||
}));
|
}));
|
||||||
}, [quoteList]);
|
}, [quoteList]);
|
||||||
|
|
||||||
const openQuoteReader = (item?: {
|
|
||||||
collectionId?: string;
|
|
||||||
sourceId?: string;
|
|
||||||
sourceName?: string;
|
|
||||||
datasetId?: string;
|
|
||||||
}) => {
|
|
||||||
eventBus.emit(EventNameEnum.openQuoteReader, item);
|
|
||||||
};
|
|
||||||
|
|
||||||
const notEmptyTags =
|
const notEmptyTags =
|
||||||
quoteList.length > 0 ||
|
quoteList.length > 0 ||
|
||||||
(llmModuleAccount === 1 && notSharePage) ||
|
(llmModuleAccount === 1 && notSharePage) ||
|
||||||
@@ -161,7 +159,7 @@ const ResponseTags = ({
|
|||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
openQuoteReader(item);
|
onOpenCiteModal(item);
|
||||||
}}
|
}}
|
||||||
height={6}
|
height={6}
|
||||||
>
|
>
|
||||||
@@ -216,7 +214,7 @@ const ResponseTags = ({
|
|||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
openQuoteReader();
|
onOpenCiteModal();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('chat:citations', { num: quoteList.length })}
|
{t('chat:citations', { num: quoteList.length })}
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
|
|||||||
import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
|
import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { valueTypeFormat } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { valueTypeFormat } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
|
|
||||||
const ResponseTags = dynamic(() => import('./components/ResponseTags'));
|
|
||||||
const FeedbackModal = dynamic(() => import('./components/FeedbackModal'));
|
const FeedbackModal = dynamic(() => import('./components/FeedbackModal'));
|
||||||
const ReadFeedbackModal = dynamic(() => import('./components/ReadFeedbackModal'));
|
const ReadFeedbackModal = dynamic(() => import('./components/ReadFeedbackModal'));
|
||||||
const SelectMarkCollection = dynamic(() => import('./components/SelectMarkCollection'));
|
const SelectMarkCollection = dynamic(() => import('./components/SelectMarkCollection'));
|
||||||
@@ -554,7 +553,7 @@ const ChatBox = ({
|
|||||||
const responseData = mergeChatResponseData(item.responseData || []);
|
const responseData = mergeChatResponseData(item.responseData || []);
|
||||||
if (responseData[responseData.length - 1]?.error) {
|
if (responseData[responseData.length - 1]?.error) {
|
||||||
toast({
|
toast({
|
||||||
title: t(responseData[responseData.length - 1].error?.message),
|
title: t(getErrText(responseData[responseData.length - 1].error)),
|
||||||
status: 'error'
|
status: 'error'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1014,10 +1013,6 @@ const ChatBox = ({
|
|||||||
onReadUserDislike: onReadUserDislike(item)
|
onReadUserDislike: onReadUserDislike(item)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ResponseTags
|
|
||||||
showTags={index !== chatRecords.length - 1 || !isChatting}
|
|
||||||
historyItem={item}
|
|
||||||
/>
|
|
||||||
{/* custom feedback */}
|
{/* custom feedback */}
|
||||||
{item.customFeedbacks && item.customFeedbacks.length > 0 && (
|
{item.customFeedbacks && item.customFeedbacks.length > 0 && (
|
||||||
<Box>
|
<Box>
|
||||||
@@ -1072,7 +1067,6 @@ const ChatBox = ({
|
|||||||
chatType,
|
chatType,
|
||||||
delOneMessage,
|
delOneMessage,
|
||||||
externalVariableList?.length,
|
externalVariableList?.length,
|
||||||
isChatting,
|
|
||||||
onAddUserDislike,
|
onAddUserDislike,
|
||||||
onAddUserLike,
|
onAddUserLike,
|
||||||
onCloseCustomFeedback,
|
onCloseCustomFeedback,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { ChatRecordContext } from '@/web/core/chat/context/chatRecordContext';
|
|||||||
import { type AppFileSelectConfigType } from '@fastgpt/global/core/app/type';
|
import { type AppFileSelectConfigType } from '@fastgpt/global/core/app/type';
|
||||||
import { defaultAppSelectFileConfig } from '@fastgpt/global/core/app/constants';
|
import { defaultAppSelectFileConfig } from '@fastgpt/global/core/app/constants';
|
||||||
import { mergeChatResponseData } from '@fastgpt/global/core/chat/utils';
|
import { mergeChatResponseData } from '@fastgpt/global/core/chat/utils';
|
||||||
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
|
|
||||||
type PluginRunContextType = PluginRunBoxProps & {
|
type PluginRunContextType = PluginRunBoxProps & {
|
||||||
isChatting: boolean;
|
isChatting: boolean;
|
||||||
@@ -258,7 +259,7 @@ const PluginRunContextProvider = ({
|
|||||||
const responseData = mergeChatResponseData(item.responseData || []);
|
const responseData = mergeChatResponseData(item.responseData || []);
|
||||||
if (responseData[responseData.length - 1]?.error) {
|
if (responseData[responseData.length - 1]?.error) {
|
||||||
toast({
|
toast({
|
||||||
title: t(responseData[responseData.length - 1].error?.message),
|
title: t(getErrText(responseData[responseData.length - 1].error)),
|
||||||
status: 'error'
|
status: 'error'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
|||||||
import { SelectOptionsComponent, FormInputComponent } from './Interactive/InteractiveComponents';
|
import { SelectOptionsComponent, FormInputComponent } from './Interactive/InteractiveComponents';
|
||||||
import { extractDeepestInteractive } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { extractDeepestInteractive } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
import { type OnOpenCiteModalProps } from '@/web/core/chat/context/chatItemContext';
|
||||||
import { ChatBoxContext } from '../ChatContainer/ChatBox/Provider';
|
import { ChatBoxContext } from '../ChatContainer/ChatBox/Provider';
|
||||||
import { useCreation } from 'ahooks';
|
import { useCreation } from 'ahooks';
|
||||||
|
|
||||||
@@ -90,11 +90,13 @@ const RenderResoningContent = React.memo(function RenderResoningContent({
|
|||||||
const RenderText = React.memo(function RenderText({
|
const RenderText = React.memo(function RenderText({
|
||||||
showAnimation,
|
showAnimation,
|
||||||
text,
|
text,
|
||||||
chatItemDataId
|
chatItemDataId,
|
||||||
|
onOpenCiteModal
|
||||||
}: {
|
}: {
|
||||||
showAnimation: boolean;
|
showAnimation: boolean;
|
||||||
text: string;
|
text: string;
|
||||||
chatItemDataId: string;
|
chatItemDataId: string;
|
||||||
|
onOpenCiteModal?: (e?: OnOpenCiteModalProps) => void;
|
||||||
}) {
|
}) {
|
||||||
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
|
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
|
||||||
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
|
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
|
||||||
@@ -111,7 +113,14 @@ const RenderText = React.memo(function RenderText({
|
|||||||
return { appId, chatId, chatItemDataId, ...outLinkAuthData };
|
return { appId, chatId, chatItemDataId, ...outLinkAuthData };
|
||||||
}, [appId, chatId, chatItemDataId, outLinkAuthData]);
|
}, [appId, chatId, chatItemDataId, outLinkAuthData]);
|
||||||
|
|
||||||
return <Markdown source={source} showAnimation={showAnimation} chatAuthData={chatAuthData} />;
|
return (
|
||||||
|
<Markdown
|
||||||
|
source={source}
|
||||||
|
showAnimation={showAnimation}
|
||||||
|
chatAuthData={chatAuthData}
|
||||||
|
onOpenCiteModal={onOpenCiteModal}
|
||||||
|
/>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const RenderTool = React.memo(
|
const RenderTool = React.memo(
|
||||||
@@ -240,12 +249,14 @@ const AIResponseBox = ({
|
|||||||
chatItemDataId,
|
chatItemDataId,
|
||||||
value,
|
value,
|
||||||
isLastResponseValue,
|
isLastResponseValue,
|
||||||
isChatting
|
isChatting,
|
||||||
|
onOpenCiteModal
|
||||||
}: {
|
}: {
|
||||||
chatItemDataId: string;
|
chatItemDataId: string;
|
||||||
value: UserChatItemValueItemType | AIChatItemValueItemType;
|
value: UserChatItemValueItemType | AIChatItemValueItemType;
|
||||||
isLastResponseValue: boolean;
|
isLastResponseValue: boolean;
|
||||||
isChatting: boolean;
|
isChatting: boolean;
|
||||||
|
onOpenCiteModal?: (e?: OnOpenCiteModalProps) => void;
|
||||||
}) => {
|
}) => {
|
||||||
if (value.type === ChatItemValueTypeEnum.text && value.text) {
|
if (value.type === ChatItemValueTypeEnum.text && value.text) {
|
||||||
return (
|
return (
|
||||||
@@ -253,6 +264,7 @@ const AIResponseBox = ({
|
|||||||
chatItemDataId={chatItemDataId}
|
chatItemDataId={chatItemDataId}
|
||||||
showAnimation={isChatting && isLastResponseValue}
|
showAnimation={isChatting && isLastResponseValue}
|
||||||
text={value.text.content}
|
text={value.text.content}
|
||||||
|
onOpenCiteModal={onOpenCiteModal}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ export async function register() {
|
|||||||
{ getSystemPluginCb },
|
{ getSystemPluginCb },
|
||||||
{ startMongoWatch },
|
{ startMongoWatch },
|
||||||
{ startCron },
|
{ startCron },
|
||||||
{ startTrainingQueue }
|
{ startTrainingQueue },
|
||||||
|
{ preLoadWorker },
|
||||||
|
{ loadSystemModels }
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
import('@fastgpt/service/common/mongo/init'),
|
import('@fastgpt/service/common/mongo/init'),
|
||||||
import('@fastgpt/service/common/mongo/index'),
|
import('@fastgpt/service/common/mongo/index'),
|
||||||
@@ -28,7 +30,9 @@ export async function register() {
|
|||||||
import('@/service/core/app/plugin'),
|
import('@/service/core/app/plugin'),
|
||||||
import('@/service/common/system/volumnMongoWatch'),
|
import('@/service/common/system/volumnMongoWatch'),
|
||||||
import('@/service/common/system/cron'),
|
import('@/service/common/system/cron'),
|
||||||
import('@/service/core/dataset/training/utils')
|
import('@/service/core/dataset/training/utils'),
|
||||||
|
import('@fastgpt/service/worker/preload'),
|
||||||
|
import('@fastgpt/service/core/ai/config/utils')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 执行初始化流程
|
// 执行初始化流程
|
||||||
@@ -40,8 +44,9 @@ export async function register() {
|
|||||||
connectMongo(connectionLogMongo, MONGO_LOG_URL);
|
connectMongo(connectionLogMongo, MONGO_LOG_URL);
|
||||||
|
|
||||||
//init system config;init vector database;init root user
|
//init system config;init vector database;init root user
|
||||||
await Promise.all([getInitConfig(), initVectorStore(), initRootUser()]);
|
await Promise.all([getInitConfig(), initVectorStore(), initRootUser(), loadSystemModels()]);
|
||||||
|
|
||||||
|
// 异步加载
|
||||||
initSystemPluginGroups();
|
initSystemPluginGroups();
|
||||||
initAppTemplateTypes();
|
initAppTemplateTypes();
|
||||||
getSystemPluginCb();
|
getSystemPluginCb();
|
||||||
@@ -49,6 +54,12 @@ export async function register() {
|
|||||||
startCron();
|
startCron();
|
||||||
startTrainingQueue(true);
|
startTrainingQueue(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await preLoadWorker();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Preload worker error', error);
|
||||||
|
}
|
||||||
|
|
||||||
console.log('Init system success');
|
console.log('Init system success');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
|
|||||||
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
|
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
|
||||||
const pluginRunTab = useContextSelector(ChatItemContext, (v) => v.pluginRunTab);
|
const pluginRunTab = useContextSelector(ChatItemContext, (v) => v.pluginRunTab);
|
||||||
const setPluginRunTab = useContextSelector(ChatItemContext, (v) => v.setPluginRunTab);
|
const setPluginRunTab = useContextSelector(ChatItemContext, (v) => v.setPluginRunTab);
|
||||||
const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
|
const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
|
||||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||||
|
|
||||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||||
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
|
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
|
||||||
@@ -81,7 +81,7 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
|
|||||||
right={0}
|
right={0}
|
||||||
h={['100%', '96%']}
|
h={['100%', '96%']}
|
||||||
w={'100%'}
|
w={'100%'}
|
||||||
maxW={quoteData ? ['100%', '1080px'] : ['100%', '600px']}
|
maxW={datasetCiteData ? ['100%', '1080px'] : ['100%', '600px']}
|
||||||
bg={'white'}
|
bg={'white'}
|
||||||
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
|
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
|
||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
@@ -169,7 +169,7 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{quoteData && (
|
{datasetCiteData && (
|
||||||
<Box
|
<Box
|
||||||
flex={'1 0 0'}
|
flex={'1 0 0'}
|
||||||
w={0}
|
w={0}
|
||||||
@@ -183,9 +183,9 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
|
|||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
>
|
>
|
||||||
<ChatQuoteList
|
<ChatQuoteList
|
||||||
rawSearch={quoteData.rawSearch}
|
rawSearch={datasetCiteData.rawSearch}
|
||||||
metadata={quoteData.metadata}
|
metadata={datasetCiteData.metadata}
|
||||||
onClose={() => setQuoteData(undefined)}
|
onClose={() => setCiteModalData(undefined)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import ChatRecordContextProvider from '@/web/core/chat/context/chatRecordContext
|
|||||||
import { Box, Button, Flex, Switch, Textarea } from '@chakra-ui/react';
|
import { Box, Button, Flex, Switch, Textarea } from '@chakra-ui/react';
|
||||||
import { cardStyles } from '../constants';
|
import { cardStyles } from '../constants';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { type ToolType } from '@fastgpt/global/core/app/type';
|
import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
|
||||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
@@ -19,7 +19,7 @@ import { postRunMCPTool } from '@/web/core/app/api/plugin';
|
|||||||
|
|
||||||
const JsonEditor = dynamic(() => import('@fastgpt/web/components/common/Textarea/JsonEditor'));
|
const JsonEditor = dynamic(() => import('@fastgpt/web/components/common/Textarea/JsonEditor'));
|
||||||
|
|
||||||
const ChatTest = ({ currentTool, url }: { currentTool: ToolType | null; url: string }) => {
|
const ChatTest = ({ currentTool, url }: { currentTool: McpToolConfigType | null; url: string }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [output, setOutput] = useState<string>('');
|
const [output, setOutput] = useState<string>('');
|
||||||
@@ -135,7 +135,7 @@ const ChatTest = ({ currentTool, url }: { currentTool: ToolType | null; url: str
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Render = ({ currentTool, url }: { currentTool: ToolType | null; url: string }) => {
|
const Render = ({ currentTool, url }: { currentTool: McpToolConfigType | null; url: string }) => {
|
||||||
const { chatId } = useChatStore();
|
const { chatId } = useChatStore();
|
||||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ const RenderToolInput = ({
|
|||||||
type: string;
|
type: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
};
|
};
|
||||||
toolData: ToolType | null;
|
toolData: McpToolConfigType | null;
|
||||||
value: any;
|
value: any;
|
||||||
onChange: (value: any) => void;
|
onChange: (value: any) => void;
|
||||||
isInvalid: boolean;
|
isInvalid: boolean;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import AppCard from './AppCard';
|
|||||||
import ChatTest from './ChatTest';
|
import ChatTest from './ChatTest';
|
||||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||||
import EditForm from './EditForm';
|
import EditForm from './EditForm';
|
||||||
import { type ToolType } from '@fastgpt/global/core/app/type';
|
import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
|
||||||
|
|
||||||
const Edit = ({
|
const Edit = ({
|
||||||
url,
|
url,
|
||||||
@@ -19,10 +19,10 @@ const Edit = ({
|
|||||||
}: {
|
}: {
|
||||||
url: string;
|
url: string;
|
||||||
setUrl: (url: string) => void;
|
setUrl: (url: string) => void;
|
||||||
toolList: ToolType[];
|
toolList: McpToolConfigType[];
|
||||||
setToolList: (toolList: ToolType[]) => void;
|
setToolList: (toolList: McpToolConfigType[]) => void;
|
||||||
currentTool: ToolType | null;
|
currentTool: McpToolConfigType | null;
|
||||||
setCurrentTool: (tool: ToolType) => void;
|
setCurrentTool: (tool: McpToolConfigType) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { isPc } = useSystem();
|
const { isPc } = useSystem();
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
|||||||
import { AppContext } from '../context';
|
import { AppContext } from '../context';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||||
import { type ToolType } from '@fastgpt/global/core/app/type';
|
import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||||
@@ -24,14 +24,14 @@ const EditForm = ({
|
|||||||
}: {
|
}: {
|
||||||
url: string;
|
url: string;
|
||||||
setUrl: (url: string) => void;
|
setUrl: (url: string) => void;
|
||||||
toolList: ToolType[];
|
toolList: McpToolConfigType[];
|
||||||
setToolList: (toolList: ToolType[]) => void;
|
setToolList: (toolList: McpToolConfigType[]) => void;
|
||||||
currentTool: ToolType | null;
|
currentTool: McpToolConfigType | null;
|
||||||
setCurrentTool: (tool: ToolType) => void;
|
setCurrentTool: (tool: McpToolConfigType) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [toolDetail, setToolDetail] = useState<ToolType | null>(null);
|
const [toolDetail, setToolDetail] = useState<McpToolConfigType | null>(null);
|
||||||
|
|
||||||
const { runAsync: runGetMCPTools, loading: isGettingTools } = useRequest2(
|
const { runAsync: runGetMCPTools, loading: isGettingTools } = useRequest2(
|
||||||
async (data: getMCPToolsBody) => await getMCPTools(data),
|
async (data: getMCPToolsBody) => await getMCPTools(data),
|
||||||
@@ -109,6 +109,10 @@ const EditForm = ({
|
|||||||
boxShadow:
|
boxShadow:
|
||||||
'0px 4px 4px 0px rgba(19, 51, 107, 0.05), 0px 0px 1px 0px rgba(19, 51, 107, 0.08)'
|
'0px 4px 4px 0px rgba(19, 51, 107, 0.05), 0px 0px 1px 0px rgba(19, 51, 107, 0.08)'
|
||||||
}}
|
}}
|
||||||
|
cursor={'pointer'}
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentTool(tool);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Flex alignItems={'center'} py={2} px={3}>
|
<Flex alignItems={'center'} py={2} px={3}>
|
||||||
<Box w={'20px'} fontSize={'14px'} color={'myGray.500'} fontWeight={'medium'}>
|
<Box w={'20px'} fontSize={'14px'} color={'myGray.500'} fontWeight={'medium'}>
|
||||||
@@ -157,23 +161,11 @@ const EditForm = ({
|
|||||||
hoverBg={'rgba(51, 112, 255, 0.10)'}
|
hoverBg={'rgba(51, 112, 255, 0.10)'}
|
||||||
hoverBorderColor={'primary.300'}
|
hoverBorderColor={'primary.300'}
|
||||||
tip={t('app:MCP_tools_detail')}
|
tip={t('app:MCP_tools_detail')}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
setToolDetail(tool);
|
setToolDetail(tool);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<MyIconButton
|
|
||||||
size={'16px'}
|
|
||||||
icon={'core/workflow/debug'}
|
|
||||||
p={2}
|
|
||||||
border={'1px solid'}
|
|
||||||
borderColor={'myGray.250'}
|
|
||||||
hoverBg={'rgba(51, 112, 255, 0.10)'}
|
|
||||||
hoverBorderColor={'primary.300'}
|
|
||||||
tip={t('app:MCP_tools_debug')}
|
|
||||||
onClick={() => {
|
|
||||||
setCurrentTool(tool);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</MyBox>
|
</MyBox>
|
||||||
);
|
);
|
||||||
@@ -188,7 +180,7 @@ const EditForm = ({
|
|||||||
|
|
||||||
export default React.memo(EditForm);
|
export default React.memo(EditForm);
|
||||||
|
|
||||||
const ToolDetailModal = ({ tool, onClose }: { tool: ToolType; onClose: () => void }) => {
|
const ToolDetailModal = ({ tool, onClose }: { tool: McpToolConfigType; onClose: () => void }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import { getAppFolderPath } from '@/web/core/app/api/app';
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { type ToolType } from '@fastgpt/global/core/app/type';
|
import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
|
||||||
import { postUpdateMCPTools } from '@/web/core/app/api/plugin';
|
import { postUpdateMCPTools } from '@/web/core/app/api/plugin';
|
||||||
|
|
||||||
const Header = ({ url, toolList }: { url: string; toolList: ToolType[] }) => {
|
const Header = ({ url, toolList }: { url: string; toolList: McpToolConfigType[] }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const appId = useContextSelector(AppContext, (v) => v.appId);
|
const appId = useContextSelector(AppContext, (v) => v.appId);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import Edit from './Edit';
|
|||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { AppContext } from '../context';
|
import { AppContext } from '../context';
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { type ToolType } from '@fastgpt/global/core/app/type';
|
import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
|
||||||
import { type MCPToolSetData } from '@/pageComponents/dashboard/apps/MCPToolsEditModal';
|
import { type MCPToolSetData } from '@/pageComponents/dashboard/apps/MCPToolsEditModal';
|
||||||
|
|
||||||
const MCPTools = () => {
|
const MCPTools = () => {
|
||||||
@@ -18,8 +18,10 @@ const MCPTools = () => {
|
|||||||
}, [appDetail.modules]);
|
}, [appDetail.modules]);
|
||||||
|
|
||||||
const [url, setUrl] = useState(toolSetData?.url || '');
|
const [url, setUrl] = useState(toolSetData?.url || '');
|
||||||
const [toolList, setToolList] = useState<ToolType[]>(toolSetData?.toolList || []);
|
const [toolList, setToolList] = useState<McpToolConfigType[]>(toolSetData?.toolList || []);
|
||||||
const [currentTool, setCurrentTool] = useState<ToolType | null>(toolSetData?.toolList[0] || null);
|
const [currentTool, setCurrentTool] = useState<McpToolConfigType | null>(
|
||||||
|
toolSetData?.toolList[0] || null
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex h={'100%'} flexDirection={'column'} px={[3, 0]} pr={[3, 3]}>
|
<Flex h={'100%'} flexDirection={'column'} px={[3, 0]} pr={[3, 3]}>
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||||
const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
|
const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
|
||||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||||
// form2AppWorkflow dependent allDatasets
|
// form2AppWorkflow dependent allDatasets
|
||||||
const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible);
|
const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible);
|
||||||
|
|
||||||
@@ -42,8 +42,8 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => {
|
|||||||
}, [appForm, setWorkflowData, t]);
|
}, [appForm, setWorkflowData, t]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setRenderEdit(!quoteData);
|
setRenderEdit(!datasetCiteData);
|
||||||
}, [quoteData, setRenderEdit]);
|
}, [datasetCiteData, setRenderEdit]);
|
||||||
|
|
||||||
const { ChatContainer, restartChat, loading } = useChatTest({
|
const { ChatContainer, restartChat, loading } = useChatTest({
|
||||||
...workflowData,
|
...workflowData,
|
||||||
@@ -89,12 +89,12 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => {
|
|||||||
<ChatContainer />
|
<ChatContainer />
|
||||||
</Box>
|
</Box>
|
||||||
</MyBox>
|
</MyBox>
|
||||||
{quoteData && (
|
{datasetCiteData && (
|
||||||
<Box flex={'1 0 0'} w={0} maxW={'560px'} {...cardStyles} boxShadow={'3'}>
|
<Box flex={'1 0 0'} w={0} maxW={'560px'} {...cardStyles} boxShadow={'3'}>
|
||||||
<ChatQuoteList
|
<ChatQuoteList
|
||||||
rawSearch={quoteData.rawSearch}
|
rawSearch={datasetCiteData.rawSearch}
|
||||||
metadata={quoteData.metadata}
|
metadata={datasetCiteData.metadata}
|
||||||
onClose={() => setQuoteData(undefined)}
|
onClose={() => setCiteModalData(undefined)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
|
|||||||
import ConfigToolModal from './ConfigToolModal';
|
import ConfigToolModal from './ConfigToolModal';
|
||||||
import { getWebLLMModel } from '@/web/common/system/utils';
|
import { getWebLLMModel } from '@/web/common/system/utils';
|
||||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||||
import { checkAppUnExistError } from '@fastgpt/global/core/app/utils';
|
import { formatToolError } from '@fastgpt/global/core/app/utils';
|
||||||
|
|
||||||
const ToolSelect = ({
|
const ToolSelect = ({
|
||||||
appForm,
|
appForm,
|
||||||
@@ -65,7 +65,7 @@ const ToolSelect = ({
|
|||||||
gridGap={[2, 4]}
|
gridGap={[2, 4]}
|
||||||
>
|
>
|
||||||
{appForm.selectedTools.map((item) => {
|
{appForm.selectedTools.map((item) => {
|
||||||
const hasError = checkAppUnExistError(item.pluginData?.error);
|
const toolError = formatToolError(item.pluginData?.error);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyTooltip key={item.id} label={item.intro}>
|
<MyTooltip key={item.id} label={item.intro}>
|
||||||
@@ -77,10 +77,10 @@ const ToolSelect = ({
|
|||||||
boxShadow={'0 4px 8px -2px rgba(16,24,40,.1),0 2px 4px -2px rgba(16,24,40,.06)'}
|
boxShadow={'0 4px 8px -2px rgba(16,24,40,.1),0 2px 4px -2px rgba(16,24,40,.06)'}
|
||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
border={theme.borders.base}
|
border={theme.borders.base}
|
||||||
borderColor={hasError ? 'red.600' : ''}
|
borderColor={toolError ? 'red.600' : ''}
|
||||||
_hover={{
|
_hover={{
|
||||||
...hoverDeleteStyles,
|
...hoverDeleteStyles,
|
||||||
borderColor: hasError ? 'red.600' : 'primary.300'
|
borderColor: toolError ? 'red.600' : 'primary.300'
|
||||||
}}
|
}}
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -93,7 +93,7 @@ const ToolSelect = ({
|
|||||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.selectLLMModel) ||
|
input.renderTypeList.includes(FlowNodeInputTypeEnum.selectLLMModel) ||
|
||||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)
|
input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)
|
||||||
) ||
|
) ||
|
||||||
hasError ||
|
toolError ||
|
||||||
item.flowNodeType === FlowNodeTypeEnum.tool ||
|
item.flowNodeType === FlowNodeTypeEnum.tool ||
|
||||||
item.flowNodeType === FlowNodeTypeEnum.toolSet
|
item.flowNodeType === FlowNodeTypeEnum.toolSet
|
||||||
) {
|
) {
|
||||||
@@ -113,21 +113,19 @@ const ToolSelect = ({
|
|||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</Box>
|
</Box>
|
||||||
{hasError && (
|
{toolError && (
|
||||||
<MyTooltip label={t('app:app.modules.not_found_tips')}>
|
<Flex
|
||||||
<Flex
|
bg={'red.50'}
|
||||||
bg={'red.50'}
|
alignItems={'center'}
|
||||||
alignItems={'center'}
|
h={6}
|
||||||
h={6}
|
px={2}
|
||||||
px={2}
|
rounded={'6px'}
|
||||||
rounded={'6px'}
|
fontSize={'xs'}
|
||||||
fontSize={'xs'}
|
fontWeight={'medium'}
|
||||||
fontWeight={'medium'}
|
>
|
||||||
>
|
<MyIcon name={'common/errorFill'} w={'14px'} mr={1} />
|
||||||
<MyIcon name={'common/errorFill'} w={'14px'} mr={1} />
|
<Box color={'red.600'}>{t(toolError as any)}</Box>
|
||||||
<Box color={'red.600'}>{t('app:app.modules.not_found')}</Box>
|
</Flex>
|
||||||
</Flex>
|
|
||||||
</MyTooltip>
|
|
||||||
)}
|
)}
|
||||||
<DeleteIcon
|
<DeleteIcon
|
||||||
ml={2}
|
ml={2}
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
|
|||||||
});
|
});
|
||||||
const pluginRunTab = useContextSelector(ChatItemContext, (v) => v.pluginRunTab);
|
const pluginRunTab = useContextSelector(ChatItemContext, (v) => v.pluginRunTab);
|
||||||
const setPluginRunTab = useContextSelector(ChatItemContext, (v) => v.setPluginRunTab);
|
const setPluginRunTab = useContextSelector(ChatItemContext, (v) => v.setPluginRunTab);
|
||||||
const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
|
const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
|
||||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||||
|
|
||||||
const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible);
|
const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible);
|
||||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||||
@@ -60,7 +60,7 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
|
|||||||
bottom={0}
|
bottom={0}
|
||||||
right={0}
|
right={0}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setQuoteData(undefined);
|
setCiteModalData(undefined);
|
||||||
onClose();
|
onClose();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -72,7 +72,7 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
|
|||||||
top={5}
|
top={5}
|
||||||
right={0}
|
right={0}
|
||||||
h={isOpen ? '95%' : '0'}
|
h={isOpen ? '95%' : '0'}
|
||||||
w={isOpen ? (quoteData ? ['100%', '960px'] : ['100%', '460px']) : '0'}
|
w={isOpen ? (datasetCiteData ? ['100%', '960px'] : ['100%', '460px']) : '0'}
|
||||||
bg={'white'}
|
bg={'white'}
|
||||||
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
|
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
|
||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
@@ -152,7 +152,7 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
|
|||||||
<ChatContainer />
|
<ChatContainer />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{quoteData && (
|
{datasetCiteData && (
|
||||||
<Box
|
<Box
|
||||||
flex={'1 0 0'}
|
flex={'1 0 0'}
|
||||||
w={0}
|
w={0}
|
||||||
@@ -166,9 +166,9 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
|
|||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
>
|
>
|
||||||
<ChatQuoteList
|
<ChatQuoteList
|
||||||
rawSearch={quoteData.rawSearch}
|
rawSearch={datasetCiteData.rawSearch}
|
||||||
metadata={quoteData.metadata}
|
metadata={datasetCiteData.metadata}
|
||||||
onClose={() => setQuoteData(undefined)}
|
onClose={() => setCiteModalData(undefined)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ import NodeCard from './render/NodeCard';
|
|||||||
import IOTitle from '../components/IOTitle';
|
import IOTitle from '../components/IOTitle';
|
||||||
import Container from '../components/Container';
|
import Container from '../components/Container';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { type ToolType } from '@fastgpt/global/core/app/type';
|
import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
|
||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
|
|
||||||
const NodeToolSet = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
const NodeToolSet = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { inputs } = data;
|
const { inputs } = data;
|
||||||
const toolList: ToolType[] = inputs.find((item) => item.key === 'toolSetData')?.value?.toolList;
|
const toolList: McpToolConfigType[] = inputs.find((item) => item.key === 'toolSetData')?.value
|
||||||
|
?.toolList;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeCard minW={'350px'} selected={selected} {...data}>
|
<NodeCard minW={'350px'} selected={selected} {...data}>
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
|||||||
import MyTag from '@fastgpt/web/components/common/Tag/index';
|
import MyTag from '@fastgpt/web/components/common/Tag/index';
|
||||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||||
import { useCreation } from 'ahooks';
|
import { useCreation } from 'ahooks';
|
||||||
|
import { formatToolError } from '@fastgpt/global/core/app/utils';
|
||||||
|
|
||||||
type Props = FlowNodeItemType & {
|
type Props = FlowNodeItemType & {
|
||||||
children?: React.ReactNode | React.ReactNode[] | string;
|
children?: React.ReactNode | React.ReactNode[] | string;
|
||||||
@@ -104,8 +105,8 @@ const NodeCard = (props: Props) => {
|
|||||||
const isAppNode = node && AppNodeFlowNodeTypeMap[node?.flowNodeType];
|
const isAppNode = node && AppNodeFlowNodeTypeMap[node?.flowNodeType];
|
||||||
const showVersion = useMemo(() => {
|
const showVersion = useMemo(() => {
|
||||||
if (!isAppNode || !node?.pluginId) return false;
|
if (!isAppNode || !node?.pluginId) return false;
|
||||||
const splitRes = node.pluginId.split('-');
|
if ([FlowNodeTypeEnum.tool, FlowNodeTypeEnum.toolSet].includes(node.flowNodeType)) return false;
|
||||||
if (splitRes.length > 1) {
|
if (node.pluginId.split('-').length > 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -144,6 +145,7 @@ const NodeCard = (props: Props) => {
|
|||||||
/* Node header */
|
/* Node header */
|
||||||
const Header = useMemo(() => {
|
const Header = useMemo(() => {
|
||||||
const showHeader = node?.flowNodeType !== FlowNodeTypeEnum.comment;
|
const showHeader = node?.flowNodeType !== FlowNodeTypeEnum.comment;
|
||||||
|
const error = formatToolError(node?.pluginData?.error);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box position={'relative'}>
|
<Box position={'relative'}>
|
||||||
@@ -254,23 +256,19 @@ const NodeCard = (props: Props) => {
|
|||||||
)}
|
)}
|
||||||
</UseGuideModal>
|
</UseGuideModal>
|
||||||
)}
|
)}
|
||||||
{!!node?.pluginData?.error && (
|
{!!error && (
|
||||||
<MyTooltip label={node?.pluginData?.error || t('app:app.modules.not_found_tips')}>
|
<Flex
|
||||||
<Flex
|
bg={'red.50'}
|
||||||
bg={'red.50'}
|
alignItems={'center'}
|
||||||
alignItems={'center'}
|
h={8}
|
||||||
h={8}
|
px={2}
|
||||||
px={2}
|
rounded={'6px'}
|
||||||
rounded={'6px'}
|
fontSize={'xs'}
|
||||||
fontSize={'xs'}
|
fontWeight={'medium'}
|
||||||
fontWeight={'medium'}
|
>
|
||||||
>
|
<MyIcon name={'common/errorFill'} w={'14px'} mr={1} />
|
||||||
<MyIcon name={'common/errorFill'} w={'14px'} mr={1} />
|
<Box color={'red.600'}>{t(error as any)}</Box>
|
||||||
<Box color={'red.600'}>
|
</Flex>
|
||||||
{node?.pluginData?.error || t('app:app.modules.not_found')}
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
</MyTooltip>
|
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
<NodeIntro nodeId={nodeId} intro={intro} />
|
<NodeIntro nodeId={nodeId} intro={intro} />
|
||||||
@@ -403,7 +401,9 @@ const MenuRender = React.memo(function MenuRender({
|
|||||||
outputs: node.data.outputs,
|
outputs: node.data.outputs,
|
||||||
showStatus: node.data.showStatus,
|
showStatus: node.data.showStatus,
|
||||||
pluginId: node.data.pluginId,
|
pluginId: node.data.pluginId,
|
||||||
version: node.data.version
|
version: node.data.version,
|
||||||
|
versionLabel: node.data.versionLabel,
|
||||||
|
isLatestVersion: node.data.isLatestVersion
|
||||||
};
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -423,7 +423,9 @@ const MenuRender = React.memo(function MenuRender({
|
|||||||
pluginId: template.pluginId,
|
pluginId: template.pluginId,
|
||||||
inputs: template.inputs,
|
inputs: template.inputs,
|
||||||
outputs: template.outputs,
|
outputs: template.outputs,
|
||||||
version: template.version
|
version: template.version,
|
||||||
|
versionLabel: template.versionLabel,
|
||||||
|
isLatestVersion: template.isLatestVersion
|
||||||
},
|
},
|
||||||
selected: true,
|
selected: true,
|
||||||
parentNodeId: undefined,
|
parentNodeId: undefined,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
type ReactNode,
|
type ReactNode,
|
||||||
type SetStateAction,
|
type SetStateAction,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
|
||||||
useMemo,
|
useMemo,
|
||||||
useState
|
useState
|
||||||
} from 'react';
|
} from 'react';
|
||||||
@@ -22,7 +21,6 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
|||||||
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||||
import type { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
import type { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||||
import { checkAppUnExistError } from '@fastgpt/global/core/app/utils';
|
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
|
|
||||||
const InfoModal = dynamic(() => import('./InfoModal'));
|
const InfoModal = dynamic(() => import('./InfoModal'));
|
||||||
@@ -205,16 +203,6 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
[appDetail.name, deleteApp, openConfirmDel, t]
|
[appDetail.name, deleteApp, openConfirmDel, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
// check app unExist error
|
|
||||||
useEffect(() => {
|
|
||||||
if (appDetail.modules.some((module) => checkAppUnExistError(module.pluginData?.error))) {
|
|
||||||
toast({
|
|
||||||
title: t('app:app.error.unExist_app'),
|
|
||||||
status: 'error'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [appDetail.modules, t, toast]);
|
|
||||||
|
|
||||||
const contextValue: AppContextType = useMemo(
|
const contextValue: AppContextType = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
appId,
|
appId,
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
|
|||||||
const appName = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.name);
|
const appName = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.name);
|
||||||
const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.avatar);
|
const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.avatar);
|
||||||
const showRouteToAppDetail = useContextSelector(ChatItemContext, (v) => v.showRouteToAppDetail);
|
const showRouteToAppDetail = useContextSelector(ChatItemContext, (v) => v.showRouteToAppDetail);
|
||||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||||
|
|
||||||
const concatHistory = useMemo(() => {
|
const concatHistory = useMemo(() => {
|
||||||
const formatHistories: HistoryItemType[] = histories.map((item) => {
|
const formatHistories: HistoryItemType[] = histories.map((item) => {
|
||||||
@@ -146,7 +146,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
|
|||||||
overflow={'hidden'}
|
overflow={'hidden'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onChangeChatId();
|
onChangeChatId();
|
||||||
setQuoteData(undefined);
|
setCiteModalData(undefined);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('common:core.chat.New Chat')}
|
{t('common:core.chat.New Chat')}
|
||||||
@@ -202,7 +202,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
|
|||||||
: {
|
: {
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
onChangeChatId(item.id);
|
onChangeChatId(item.id);
|
||||||
setQuoteData(undefined);
|
setCiteModalData(undefined);
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
{...(i !== concatHistory.length - 1 && {
|
{...(i !== concatHistory.length - 1 && {
|
||||||
@@ -274,7 +274,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
|
|||||||
onDelHistory(item.id);
|
onDelHistory(item.id);
|
||||||
if (item.id === activeChatId) {
|
if (item.id === activeChatId) {
|
||||||
onChangeChatId();
|
onChangeChatId();
|
||||||
setQuoteData(undefined);
|
setCiteModalData(undefined);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
type: 'danger'
|
type: 'danger'
|
||||||
|
|||||||
@@ -20,17 +20,16 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
|
|||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AppListContext } from './context';
|
import { AppListContext } from './context';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { type ToolType } from '@fastgpt/global/core/app/type';
|
import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
|
||||||
import type { getMCPToolsBody } from '@/pages/api/support/mcp/client/getTools';
|
import type { getMCPToolsBody } from '@/pages/api/support/mcp/client/getTools';
|
||||||
|
|
||||||
export type MCPToolSetData = {
|
export type MCPToolSetData = {
|
||||||
url: string;
|
url: string;
|
||||||
toolList: ToolType[];
|
toolList: McpToolConfigType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EditMCPToolsProps = {
|
export type EditMCPToolsProps = {
|
||||||
@@ -41,7 +40,6 @@ export type EditMCPToolsProps = {
|
|||||||
|
|
||||||
const MCPToolsEditModal = ({ onClose }: { onClose: () => void }) => {
|
const MCPToolsEditModal = ({ onClose }: { onClose: () => void }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const { parentId, loadMyApps } = useContextSelector(AppListContext, (v) => v);
|
const { parentId, loadMyApps } = useContextSelector(AppListContext, (v) => v);
|
||||||
|
|
||||||
@@ -81,7 +79,7 @@ const MCPToolsEditModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
const { runAsync: runGetMCPTools, loading: isGettingTools } = useRequest2(
|
const { runAsync: runGetMCPTools, loading: isGettingTools } = useRequest2(
|
||||||
(data: getMCPToolsBody) => getMCPTools(data),
|
(data: getMCPToolsBody) => getMCPTools(data),
|
||||||
{
|
{
|
||||||
onSuccess: (res: ToolType[]) => {
|
onSuccess: (res: McpToolConfigType[]) => {
|
||||||
setValue('mcpData.toolList', res);
|
setValue('mcpData.toolList', res);
|
||||||
},
|
},
|
||||||
errorToast: t('app:MCP_tools_parse_failed')
|
errorToast: t('app:MCP_tools_parse_failed')
|
||||||
|
|||||||
@@ -218,47 +218,48 @@ const CollectionChunkForm = ({ form }: { form: UseFormReturn<CollectionChunkForm
|
|||||||
gridTemplateColumns={'repeat(2, 1fr)'}
|
gridTemplateColumns={'repeat(2, 1fr)'}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
{trainingType === DatasetCollectionDataProcessModeEnum.chunk && (
|
{trainingType === DatasetCollectionDataProcessModeEnum.chunk &&
|
||||||
<Box mt={6}>
|
feConfigs?.show_dataset_enhance !== false && (
|
||||||
<Box fontSize={'sm'} mb={2} color={'myGray.600'}>
|
<Box mt={6}>
|
||||||
{t('dataset:enhanced_indexes')}
|
<Box fontSize={'sm'} mb={2} color={'myGray.600'}>
|
||||||
|
{t('dataset:enhanced_indexes')}
|
||||||
|
</Box>
|
||||||
|
<HStack gap={[3, 7]}>
|
||||||
|
<HStack flex={'1'} spacing={1}>
|
||||||
|
<MyTooltip label={!feConfigs?.isPlus ? t('common:commercial_function_tip') : ''}>
|
||||||
|
<Checkbox
|
||||||
|
isDisabled={!feConfigs?.isPlus}
|
||||||
|
isChecked={autoIndexes}
|
||||||
|
{...register('autoIndexes')}
|
||||||
|
>
|
||||||
|
<FormLabel>{t('dataset:auto_indexes')}</FormLabel>
|
||||||
|
</Checkbox>
|
||||||
|
</MyTooltip>
|
||||||
|
<QuestionTip label={t('dataset:auto_indexes_tips')} />
|
||||||
|
</HStack>
|
||||||
|
<HStack flex={'1'} spacing={1}>
|
||||||
|
<MyTooltip
|
||||||
|
label={
|
||||||
|
!feConfigs?.isPlus
|
||||||
|
? t('common:commercial_function_tip')
|
||||||
|
: !datasetDetail?.vlmModel
|
||||||
|
? t('common:error_vlm_not_config')
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
isDisabled={!feConfigs?.isPlus || !datasetDetail?.vlmModel}
|
||||||
|
isChecked={imageIndex}
|
||||||
|
{...register('imageIndex')}
|
||||||
|
>
|
||||||
|
<FormLabel>{t('dataset:image_auto_parse')}</FormLabel>
|
||||||
|
</Checkbox>
|
||||||
|
</MyTooltip>
|
||||||
|
<QuestionTip label={t('dataset:image_auto_parse_tips')} />
|
||||||
|
</HStack>
|
||||||
|
</HStack>
|
||||||
</Box>
|
</Box>
|
||||||
<HStack gap={[3, 7]}>
|
)}
|
||||||
<HStack flex={'1'} spacing={1}>
|
|
||||||
<MyTooltip label={!feConfigs?.isPlus ? t('common:commercial_function_tip') : ''}>
|
|
||||||
<Checkbox
|
|
||||||
isDisabled={!feConfigs?.isPlus}
|
|
||||||
isChecked={autoIndexes}
|
|
||||||
{...register('autoIndexes')}
|
|
||||||
>
|
|
||||||
<FormLabel>{t('dataset:auto_indexes')}</FormLabel>
|
|
||||||
</Checkbox>
|
|
||||||
</MyTooltip>
|
|
||||||
<QuestionTip label={t('dataset:auto_indexes_tips')} />
|
|
||||||
</HStack>
|
|
||||||
<HStack flex={'1'} spacing={1}>
|
|
||||||
<MyTooltip
|
|
||||||
label={
|
|
||||||
!feConfigs?.isPlus
|
|
||||||
? t('common:commercial_function_tip')
|
|
||||||
: !datasetDetail?.vlmModel
|
|
||||||
? t('common:error_vlm_not_config')
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
isDisabled={!feConfigs?.isPlus || !datasetDetail?.vlmModel}
|
|
||||||
isChecked={imageIndex}
|
|
||||||
{...register('imageIndex')}
|
|
||||||
>
|
|
||||||
<FormLabel>{t('dataset:image_auto_parse')}</FormLabel>
|
|
||||||
</Checkbox>
|
|
||||||
</MyTooltip>
|
|
||||||
<QuestionTip label={t('dataset:image_auto_parse_tips')} />
|
|
||||||
</HStack>
|
|
||||||
</HStack>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<Box mt={6}>
|
<Box mt={6}>
|
||||||
<Box fontSize={'sm'} mb={2} color={'myGray.600'}>
|
<Box fontSize={'sm'} mb={2} color={'myGray.600'}>
|
||||||
{t('dataset:params_setting')}
|
{t('dataset:params_setting')}
|
||||||
|
|||||||
@@ -81,4 +81,5 @@ function App({ Component, pageProps }: AppPropsWithLayout) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
export default appWithTranslation(App);
|
export default appWithTranslation(App);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ApiKeyTable from '@/components/support/apikey/Table';
|
import ApiKeyTable from '@/components/support/apikey/Table';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use client';
|
||||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
import { Box, Button, Flex } from '@chakra-ui/react';
|
||||||
import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs';
|
import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use client';
|
||||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use client';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Box, Flex, useTheme } from '@chakra-ui/react';
|
import { Box, Flex, useTheme } from '@chakra-ui/react';
|
||||||
import { getInforms, readInform } from '@/web/support/user/inform/api';
|
import { getInforms, readInform } from '@/web/support/user/inform/api';
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use client';
|
||||||
import { serviceSideProps } from '@/web/common/i18n/utils';
|
import { serviceSideProps } from '@/web/common/i18n/utils';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import AccountContainer from '@/pageComponents/account/AccountContainer';
|
import AccountContainer from '@/pageComponents/account/AccountContainer';
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
Grid,
|
Grid,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use client';
|
||||||
import { Box, Card, Flex } from '@chakra-ui/react';
|
import { Box, Card, Flex } from '@chakra-ui/react';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use client';
|
||||||
import { serviceSideProps } from '@/web/common/i18n/utils';
|
import { serviceSideProps } from '@/web/common/i18n/utils';
|
||||||
import AccountContainer from '@/pageComponents/account/AccountContainer';
|
import AccountContainer from '@/pageComponents/account/AccountContainer';
|
||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use client';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { Flex, Box, HStack } from '@chakra-ui/react';
|
import { Flex, Box, HStack } from '@chakra-ui/react';
|
||||||
import { UsageSourceEnum, UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants';
|
import { UsageSourceEnum, UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants';
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
await authCert({ req, authRoot: true });
|
await authCert({ req, authRoot: true });
|
||||||
|
|
||||||
// load config
|
// load config
|
||||||
const [{ config: dbConfig }, fileConfig] = await Promise.all([
|
const [{ fastgptConfig: dbConfig }, fileConfig] = await Promise.all([
|
||||||
getFastGPTConfigFromDB(),
|
getFastGPTConfigFromDB(),
|
||||||
readConfigData('config.json')
|
readConfigData('config.json')
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
|||||||
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
||||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||||
import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/user/constant';
|
import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/user/constant';
|
||||||
|
import { checkTeamAppLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||||
|
|
||||||
export type createHttpPluginQuery = {};
|
export type createHttpPluginQuery = {};
|
||||||
|
|
||||||
@@ -35,6 +36,8 @@ async function handler(
|
|||||||
? await authApp({ req, appId: parentId, per: TeamAppCreatePermissionVal, authToken: true })
|
? await authApp({ req, appId: parentId, per: TeamAppCreatePermissionVal, authToken: true })
|
||||||
: await authUserPer({ req, authToken: true, per: TeamAppCreatePermissionVal });
|
: await authUserPer({ req, authToken: true, per: TeamAppCreatePermissionVal });
|
||||||
|
|
||||||
|
await checkTeamAppLimit(teamId);
|
||||||
|
|
||||||
const httpPluginId = await mongoSessionRun(async (session) => {
|
const httpPluginId = await mongoSessionRun(async (session) => {
|
||||||
// create http plugin folder
|
// create http plugin folder
|
||||||
const httpPluginId = await onCreateApp({
|
const httpPluginId = await onCreateApp({
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/u
|
|||||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||||
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||||
import { type CreateAppBody, onCreateApp } from '../create';
|
import { type CreateAppBody, onCreateApp } from '../create';
|
||||||
import { type ToolType } from '@fastgpt/global/core/app/type';
|
import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
|
||||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
import {
|
import {
|
||||||
@@ -12,6 +12,8 @@ import {
|
|||||||
getMCPToolSetRuntimeNode
|
getMCPToolSetRuntimeNode
|
||||||
} from '@fastgpt/global/core/app/mcpTools/utils';
|
} from '@fastgpt/global/core/app/mcpTools/utils';
|
||||||
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
||||||
|
import { checkTeamAppLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||||
|
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||||
|
|
||||||
export type createMCPToolsQuery = {};
|
export type createMCPToolsQuery = {};
|
||||||
|
|
||||||
@@ -20,7 +22,7 @@ export type createMCPToolsBody = Omit<
|
|||||||
'type' | 'modules' | 'edges' | 'chatConfig'
|
'type' | 'modules' | 'edges' | 'chatConfig'
|
||||||
> & {
|
> & {
|
||||||
url: string;
|
url: string;
|
||||||
toolList: ToolType[];
|
toolList: McpToolConfigType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type createMCPToolsResponse = {};
|
export type createMCPToolsResponse = {};
|
||||||
@@ -32,9 +34,11 @@ async function handler(
|
|||||||
const { name, avatar, toolList, url, parentId } = req.body;
|
const { name, avatar, toolList, url, parentId } = req.body;
|
||||||
|
|
||||||
const { teamId, tmbId, userId } = parentId
|
const { teamId, tmbId, userId } = parentId
|
||||||
? await authApp({ req, appId: parentId, per: TeamAppCreatePermissionVal, authToken: true })
|
? await authApp({ req, appId: parentId, per: WritePermissionVal, authToken: true })
|
||||||
: await authUserPer({ req, authToken: true, per: TeamAppCreatePermissionVal });
|
: await authUserPer({ req, authToken: true, per: TeamAppCreatePermissionVal });
|
||||||
|
|
||||||
|
await checkTeamAppLimit(teamId);
|
||||||
|
|
||||||
const mcpToolsId = await mongoSessionRun(async (session) => {
|
const mcpToolsId = await mongoSessionRun(async (session) => {
|
||||||
const mcpToolsId = await onCreateApp({
|
const mcpToolsId = await onCreateApp({
|
||||||
name,
|
name,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||||
import { NextAPI } from '@/service/middleware/entry';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
import { type AppDetailType, type ToolType } from '@fastgpt/global/core/app/type';
|
import { type AppDetailType, type McpToolConfigType } from '@fastgpt/global/core/app/type';
|
||||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||||
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
|
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
@@ -24,7 +24,7 @@ export type updateMCPToolsQuery = {};
|
|||||||
export type updateMCPToolsBody = {
|
export type updateMCPToolsBody = {
|
||||||
appId: string;
|
appId: string;
|
||||||
url: string;
|
url: string;
|
||||||
toolList: ToolType[];
|
toolList: McpToolConfigType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type updateMCPToolsResponse = {};
|
export type updateMCPToolsResponse = {};
|
||||||
@@ -56,10 +56,11 @@ async function handler(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await MongoApp.findByIdAndUpdate(
|
await MongoApp.updateOne(
|
||||||
appId,
|
{ _id: appId },
|
||||||
{
|
{
|
||||||
modules: [getMCPToolSetRuntimeNode({ url, toolList, name: app.name, avatar: app.avatar })]
|
modules: [getMCPToolSetRuntimeNode({ url, toolList, name: app.name, avatar: app.avatar })],
|
||||||
|
updateTime: new Date()
|
||||||
},
|
},
|
||||||
{ session }
|
{ session }
|
||||||
);
|
);
|
||||||
@@ -97,6 +98,7 @@ const updateMCPChildrenTool = async ({
|
|||||||
teamId
|
teamId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 删除 DB 里有,新的工具列表里没有的工具
|
||||||
for await (const tool of dbTools) {
|
for await (const tool of dbTools) {
|
||||||
if (!toolSetData.toolList.find((t) => t.name === tool.name)) {
|
if (!toolSetData.toolList.find((t) => t.name === tool.name)) {
|
||||||
await onDelOneApp({
|
await onDelOneApp({
|
||||||
@@ -107,6 +109,7 @@ const updateMCPChildrenTool = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建 DB 里没有,新的工具列表里有的工具
|
||||||
for await (const tool of toolSetData.toolList) {
|
for await (const tool of toolSetData.toolList) {
|
||||||
if (!dbTools.find((t) => t.name === tool.name)) {
|
if (!dbTools.find((t) => t.name === tool.name)) {
|
||||||
await onCreateApp({
|
await onCreateApp({
|
||||||
@@ -123,11 +126,12 @@ const updateMCPChildrenTool = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新 DB 里有的工具
|
||||||
for await (const tool of toolSetData.toolList) {
|
for await (const tool of toolSetData.toolList) {
|
||||||
const dbTool = dbTools.find((t) => t.name === tool.name);
|
const dbTool = dbTools.find((t) => t.name === tool.name);
|
||||||
if (dbTool) {
|
if (dbTool) {
|
||||||
await MongoApp.findByIdAndUpdate(
|
await MongoApp.updateOne(
|
||||||
dbTool._id,
|
{ _id: dbTool._id },
|
||||||
{
|
{
|
||||||
modules: [getMCPToolRuntimeNode({ tool, url: toolSetData.url })]
|
modules: [getMCPToolRuntimeNode({ tool, url: toolSetData.url })]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ async function handler(
|
|||||||
|
|
||||||
const match = {
|
const match = {
|
||||||
appId,
|
appId,
|
||||||
isPublish
|
...(isPublish !== undefined && { isPublish })
|
||||||
};
|
};
|
||||||
|
|
||||||
const [result, total] = await Promise.all([
|
const [result, total] = await Promise.all([
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { quoteDataFieldSelector, type QuoteDataItemType } from '@/service/core/c
|
|||||||
import { processChatTimeFilter } from '@/service/core/chat/utils';
|
import { processChatTimeFilter } from '@/service/core/chat/utils';
|
||||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||||
|
|
||||||
export type GetQuoteDataProps = {
|
export type GetQuoteProps = {
|
||||||
datasetDataIdList: string[];
|
datasetDataIdList: string[];
|
||||||
|
|
||||||
collectionIdList: string[];
|
collectionIdList: string[];
|
||||||
@@ -19,9 +19,9 @@ export type GetQuoteDataProps = {
|
|||||||
teamToken?: string;
|
teamToken?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetQuoteDataRes = QuoteDataItemType[];
|
export type GetQuotesRes = QuoteDataItemType[];
|
||||||
|
|
||||||
async function handler(req: ApiRequestProps<GetQuoteDataProps>): Promise<GetQuoteDataRes> {
|
async function handler(req: ApiRequestProps<GetQuoteProps>): Promise<GetQuotesRes> {
|
||||||
const {
|
const {
|
||||||
appId,
|
appId,
|
||||||
chatId,
|
chatId,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { type OutLinkChatAuthProps } from '@fastgpt/global/support/permission/ch
|
|||||||
import { type ApiRequestProps } from '@fastgpt/service/type/next';
|
import { type ApiRequestProps } from '@fastgpt/service/type/next';
|
||||||
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||||
|
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||||
|
|
||||||
export type GetQuoteDataResponse = {
|
export type GetQuoteDataResponse = {
|
||||||
collection: DatasetCollectionSchemaType;
|
collection: DatasetCollectionSchemaType;
|
||||||
@@ -46,7 +47,7 @@ async function handler(req: ApiRequestProps<GetQuoteDataProps>): Promise<GetQuot
|
|||||||
|
|
||||||
const datasetData = await MongoDatasetData.findById(dataId);
|
const datasetData = await MongoDatasetData.findById(dataId);
|
||||||
if (!datasetData) {
|
if (!datasetData) {
|
||||||
return Promise.reject('Can not find the data');
|
return Promise.reject(i18nT('common:data_not_found'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const [collection, { responseDetail }] = await Promise.all([
|
const [collection, { responseDetail }] = await Promise.all([
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NextAPI } from '@/service/middleware/entry';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
import { type ToolType } from '@fastgpt/global/core/app/type';
|
import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
|
||||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||||
import { MCPClient } from '@fastgpt/service/core/app/mcp';
|
import { MCPClient } from '@fastgpt/service/core/app/mcp';
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ export type getMCPToolsQuery = {};
|
|||||||
|
|
||||||
export type getMCPToolsBody = { url: string };
|
export type getMCPToolsBody = { url: string };
|
||||||
|
|
||||||
export type getMCPToolsResponse = ToolType[];
|
export type getMCPToolsResponse = McpToolConfigType[];
|
||||||
|
|
||||||
async function handler(
|
async function handler(
|
||||||
req: ApiRequestProps<getMCPToolsBody, getMCPToolsQuery>,
|
req: ApiRequestProps<getMCPToolsBody, getMCPToolsQuery>,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
||||||
import { createJWT, setCookie } from '@fastgpt/service/support/permission/controller';
|
import { setCookie } from '@fastgpt/service/support/permission/controller';
|
||||||
import { getUserDetail } from '@fastgpt/service/support/user/controller';
|
import { getUserDetail } from '@fastgpt/service/support/user/controller';
|
||||||
import type { PostLoginProps } from '@fastgpt/global/support/user/api.d';
|
import type { PostLoginProps } from '@fastgpt/global/support/user/api.d';
|
||||||
import { UserStatusEnum } from '@fastgpt/global/support/user/constant';
|
import { UserStatusEnum } from '@fastgpt/global/support/user/constant';
|
||||||
@@ -13,6 +13,8 @@ import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperat
|
|||||||
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
|
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
|
||||||
import { UserAuthTypeEnum } from '@fastgpt/global/support/user/auth/constants';
|
import { UserAuthTypeEnum } from '@fastgpt/global/support/user/auth/constants';
|
||||||
import { authCode } from '@fastgpt/service/support/user/auth/controller';
|
import { authCode } from '@fastgpt/service/support/user/auth/controller';
|
||||||
|
import { createUserSession } from '@fastgpt/service/support/user/session';
|
||||||
|
import requestIp from 'request-ip';
|
||||||
|
|
||||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const { username, password, code } = req.body as PostLoginProps;
|
const { username, password, code } = req.body as PostLoginProps;
|
||||||
@@ -61,20 +63,22 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
lastLoginTmbId: userDetail.team.tmbId
|
lastLoginTmbId: userDetail.team.tmbId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const token = await createUserSession({
|
||||||
|
userId: user._id,
|
||||||
|
teamId: userDetail.team.teamId,
|
||||||
|
tmbId: userDetail.team.tmbId,
|
||||||
|
isRoot: username === 'root',
|
||||||
|
ip: requestIp.getClientIp(req)
|
||||||
|
});
|
||||||
|
|
||||||
|
setCookie(res, token);
|
||||||
|
|
||||||
pushTrack.login({
|
pushTrack.login({
|
||||||
type: 'password',
|
type: 'password',
|
||||||
uid: user._id,
|
uid: user._id,
|
||||||
teamId: userDetail.team.teamId,
|
teamId: userDetail.team.teamId,
|
||||||
tmbId: userDetail.team.tmbId
|
tmbId: userDetail.team.tmbId
|
||||||
});
|
});
|
||||||
|
|
||||||
const token = createJWT({
|
|
||||||
...userDetail,
|
|
||||||
isRoot: username === 'root'
|
|
||||||
});
|
|
||||||
|
|
||||||
setCookie(res, token);
|
|
||||||
|
|
||||||
addOperationLog({
|
addOperationLog({
|
||||||
tmbId: userDetail.team.tmbId,
|
tmbId: userDetail.team.tmbId,
|
||||||
teamId: userDetail.team.teamId,
|
teamId: userDetail.team.teamId,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use client';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Box } from '@chakra-ui/react';
|
import { Box } from '@chakra-ui/react';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react';
|
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||||
import NextHead from '@/components/common/NextHead';
|
import NextHead from '@/components/common/NextHead';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { getInitChatInfo } from '@/web/core/chat/api';
|
import { getInitChatInfo } from '@/web/core/chat/api';
|
||||||
@@ -59,8 +59,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
|||||||
const isPlugin = useContextSelector(ChatItemContext, (v) => v.isPlugin);
|
const isPlugin = useContextSelector(ChatItemContext, (v) => v.isPlugin);
|
||||||
const chatBoxData = useContextSelector(ChatItemContext, (v) => v.chatBoxData);
|
const chatBoxData = useContextSelector(ChatItemContext, (v) => v.chatBoxData);
|
||||||
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
|
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
|
||||||
const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
|
const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
|
||||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||||
|
|
||||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||||
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
|
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
|
||||||
@@ -148,7 +148,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return isPc || !appId ? (
|
return isPc || !appId ? (
|
||||||
<SideBar externalTrigger={!!quoteData}>{Children}</SideBar>
|
<SideBar externalTrigger={!!datasetCiteData}>{Children}</SideBar>
|
||||||
) : (
|
) : (
|
||||||
<Drawer
|
<Drawer
|
||||||
isOpen={isOpenSlider}
|
isOpen={isOpenSlider}
|
||||||
@@ -161,7 +161,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
|||||||
<DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent>
|
<DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
}, [t, isPc, appId, isOpenSlider, onCloseSlider, quoteData]);
|
}, [t, isPc, appId, isOpenSlider, onCloseSlider, datasetCiteData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex h={'100%'}>
|
<Flex h={'100%'}>
|
||||||
@@ -173,7 +173,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(!quoteData || isPc) && (
|
{(!datasetCiteData || isPc) && (
|
||||||
<PageContainer flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
|
<PageContainer flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
|
||||||
<Flex h={'100%'} flexDirection={['column', 'row']}>
|
<Flex h={'100%'} flexDirection={['column', 'row']}>
|
||||||
{/* pc always show history. */}
|
{/* pc always show history. */}
|
||||||
@@ -222,12 +222,12 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
|||||||
</PageContainer>
|
</PageContainer>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{quoteData && (
|
{datasetCiteData && (
|
||||||
<PageContainer flex={'1 0 0'} w={0} maxW={'560px'}>
|
<PageContainer flex={'1 0 0'} w={0} maxW={'560px'}>
|
||||||
<ChatQuoteList
|
<ChatQuoteList
|
||||||
rawSearch={quoteData.rawSearch}
|
rawSearch={datasetCiteData.rawSearch}
|
||||||
metadata={quoteData.metadata}
|
metadata={datasetCiteData.metadata}
|
||||||
onClose={() => setQuoteData(undefined)}
|
onClose={() => setCiteModalData(undefined)}
|
||||||
/>
|
/>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user