diff --git a/docSite/content/zh-cn/docs/development/upgrading/499.md b/docSite/content/zh-cn/docs/development/upgrading/499.md
index e19f29f62..6e92abb52 100644
--- a/docSite/content/zh-cn/docs/development/upgrading/499.md
+++ b/docSite/content/zh-cn/docs/development/upgrading/499.md
@@ -11,11 +11,15 @@ weight: 791
## 🚀 新增内容
1. 切换 SessionId 来替代 JWT 实现登录鉴权,可控制最大登录客户端数量。
+2. 新的商业版 License 管理模式。
## ⚙️ 优化
-
+1. 优化工具调用,新工具的判断逻辑。
+2. 调整 Cite 引用提示词。
## 🐛 修复
-
+1. 无法正常获取应用历史保存/发布记录。
+2. 成员创建 MCP 工具权限问题。
+3. 来源引用展示,存在 ID 传递错误,导致提示无权操作该文件。
diff --git a/packages/global/core/ai/prompt/AIChat.ts b/packages/global/core/ai/prompt/AIChat.ts
index 2c1c1a13e..b3869ad42 100644
--- a/packages/global/core/ai/prompt/AIChat.ts
+++ b/packages/global/core/ai/prompt/AIChat.ts
@@ -2,6 +2,248 @@ import { type PromptTemplateItem } from '../type.d';
import { i18nT } from '../../../../web/i18n/utils';
import { getPromptByVersion } from './utils';
+export const Prompt_userQuotePromptList: PromptTemplateItem[] = [
+ {
+ title: i18nT('app:template.standard_template'),
+ desc: '',
+ value: {
+ ['4.9.7']: `## 任务描述
+你是一个知识库回答助手,可以使用 中的内容作为你本次回答的参考。
+同时,为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容。
+
+## 追溯展示规则
+
+- 使用 [id](CITE) 的格式来引用 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
+- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
+- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
+- 不要把示例作为知识点。
+- 不要伪造 id,返回的 id 必须都存在 中!
+
+## 通用规则
+
+- 如果你不清楚答案,你需要澄清。
+- 避免提及你是从 获取的知识。
+- 保持答案与 中描述的一致。
+- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
+- 使用与问题相同的语言回答。
+
+
+{{quote}}
+
+
+## 用户问题
+
+{{question}}
+
+## 回答
+`
+ }
+ },
+ {
+ title: i18nT('app:template.qa_template'),
+ desc: '',
+ value: {
+ ['4.9.7']: `## 任务描述
+作为一个问答助手,你会使用 标记中的提供的数据对进行内容回答。
+
+## 回答要求
+- 选择其中一个或多个问答对进行回答。
+- 回答的内容应尽可能与 中的内容一致。
+- 如果没有相关的问答对,你需要澄清。
+- 避免提及你是从 获取的知识,只需要回复答案。
+- 使用与问题相同的语言回答。
+
+
+{{quote}}
+
+
+## 用户问题
+
+{{question}}
+
+## 回答
+`
+ }
+ },
+ {
+ title: i18nT('app:template.standard_strict'),
+ desc: '',
+ value: {
+ ['4.9.7']: `## 任务描述
+你是一个知识库回答助手,可以使用 中的内容作为你本次回答的参考。
+同时,为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容。
+
+## 追溯展示规则
+
+- 使用 [id](CITE) 的格式来引用 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
+- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
+- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
+- 不要把示例作为知识点。
+- 不要伪造 id,返回的 id 必须都存在 中!
+
+## 通用规则
+
+- 如果你不清楚答案,你需要澄清。
+- 避免提及你是从 获取的知识。
+- 保持答案与 中描述的一致。
+- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
+- 使用与问题相同的语言回答。
+
+## 严格要求
+
+你只能使用 标记中的内容作为参考,不能使用自身的知识,并且回答的内容需严格与 中的内容一致。
+
+
+{{quote}}
+
+
+## 用户问题
+
+{{question}}
+
+## 回答
+`
+ }
+ },
+ {
+ title: i18nT('app:template.hard_strict'),
+ desc: '',
+ value: {
+ ['4.9.7']: `## 任务描述
+作为一个问答助手,你会使用 标记中的提供的数据对进行内容回答。
+
+## 回答要求
+- 选择其中一个或多个问答对进行回答。
+- 回答的内容应尽可能与 中的内容一致。
+- 如果没有相关的问答对,你需要澄清。
+- 避免提及你是从 获取的知识,只需要回复答案。
+- 使用与问题相同的语言回答。
+
+## 严格要求
+
+你只能使用 标记中的内容作为参考,不能使用自身的知识,并且回答的内容需严格与 中的内容一致。
+
+
+{{quote}}
+
+
+## 用户问题
+
+{{question}}
+
+## 回答
+`
+ }
+ }
+];
+
+export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
+ {
+ title: i18nT('app:template.standard_template'),
+ desc: '',
+ value: {
+ ['4.9.7']: `## 任务描述
+你是一个知识库回答助手,可以使用 中的内容作为你本次回答的参考。
+同时,为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容。
+
+## 追溯展示规则
+
+- 使用 [id](CITE) 的格式来引用 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
+- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
+- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
+- 不要把示例作为知识点。
+- 不要伪造 id,返回的 id 必须都存在 中!
+
+## 通用规则
+
+- 如果你不清楚答案,你需要澄清。
+- 避免提及你是从 获取的知识。
+- 保持答案与 中描述的一致。
+- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
+- 使用与问题相同的语言回答。
+
+
+{{quote}}
+`
+ }
+ },
+ {
+ title: i18nT('app:template.qa_template'),
+ desc: '',
+ value: {
+ ['4.9.8']: `## 任务描述
+作为一个问答助手,你会使用 标记中的提供的数据对进行内容回答。
+
+## 回答要求
+- 选择其中一个或多个问答对进行回答。
+- 回答的内容应尽可能与 中的内容一致。
+- 如果没有相关的问答对,你需要澄清。
+- 避免提及你是从 获取的知识,只需要回复答案。
+- 使用与问题相同的语言回答。
+
+
+{{quote}}
+`
+ }
+ },
+ {
+ title: i18nT('app:template.standard_strict'),
+ desc: '',
+ value: {
+ ['4.9.7']: `## 任务描述
+你是一个知识库回答助手,可以使用 中的内容作为你本次回答的参考。
+同时,为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容。
+
+## 追溯展示规则
+
+- 使用 [id](CITE) 的格式来引用 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
+- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
+- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
+- 不要把示例作为知识点。
+- 不要伪造 id,返回的 id 必须都存在 中!
+
+## 通用规则
+
+- 如果你不清楚答案,你需要澄清。
+- 避免提及你是从 获取的知识。
+- 保持答案与 中描述的一致。
+- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
+- 使用与问题相同的语言回答。
+
+## 严格要求
+
+你只能使用 标记中的内容作为参考,不能使用自身的知识,并且回答的内容需严格与 中的内容一致。
+
+
+{{quote}}
+`
+ }
+ },
+ {
+ title: i18nT('app:template.hard_strict'),
+ desc: '',
+ value: {
+ ['4.9.7']: `## 任务描述
+作为一个问答助手,你会使用 标记中的提供的数据对进行内容回答。
+
+## 回答要求
+- 选择其中一个或多个问答对进行回答。
+- 回答的内容应尽可能与 中的内容一致。
+- 如果没有相关的问答对,你需要澄清。
+- 避免提及你是从 获取的知识,只需要回复答案。
+- 使用与问题相同的语言回答。
+
+## 严格要求
+
+你只能使用 标记中的内容作为参考,不能使用自身的知识,并且回答的内容需严格与 中的内容一致。
+
+
+{{quote}}
+`
+ }
+ }
+];
+
export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
{
title: i18nT('app:template.standard_template'),
@@ -10,11 +252,6 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
['4.9.7']: `{
"id": "{{id}}",
"sourceName": "{{source}}",
- "content": "{{q}}\n{{a}}"
-}
-`,
- ['4.9.2']: `{
- "sourceName": "{{source}}",
"updateTime": "{{updateTime}}",
"content": "{{q}}\n{{a}}"
}
@@ -25,7 +262,7 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
title: i18nT('app:template.qa_template'),
desc: i18nT('app:template.qa_template_des'),
value: {
- ['4.9.2']: `
+ ['4.9.7']: `
{{q}}
@@ -40,11 +277,6 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
['4.9.7']: `{
"id": "{{id}}",
"sourceName": "{{source}}",
- "content": "{{q}}\n{{a}}"
-}
-`,
- ['4.9.2']: `{
- "sourceName": "{{source}}",
"updateTime": "{{updateTime}}",
"content": "{{q}}\n{{a}}"
}
@@ -55,7 +287,7 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
title: i18nT('app:template.hard_strict'),
desc: i18nT('app:template.hard_strict_des'),
value: {
- ['4.9.2']: `
+ ['4.9.7']: `
{{q}}
@@ -64,263 +296,12 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
}
}
];
-
export const getQuoteTemplate = (version?: string) => {
const defaultTemplate = Prompt_QuoteTemplateList[0].value;
return getPromptByVersion(version, defaultTemplate);
};
-export const Prompt_userQuotePromptList: PromptTemplateItem[] = [
- {
- title: i18nT('app:template.standard_template'),
- desc: '',
- value: {
- ['4.9.7']: `使用 标记中的内容作为本次对话的参考:
-
-
-{{quote}}
-
-
-回答要求:
-- 如果你不清楚答案,你需要澄清。
-- 避免提及你是从 获取的知识。
-- 保持答案与 中描述的一致。
-- 使用 Markdown 语法优化回答格式。
-- 使用与问题相同的语言回答。
-- 使用 [id](CITE) 格式来引用中的知识,其中 CITE 是固定常量, id 为引文中的 id。
-- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
-- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。`,
- ['4.9.2']: `使用 标记中的内容作为本次对话的参考:
-
-
-{{quote}}
-
-
-回答要求:
-- 如果你不清楚答案,你需要澄清。
-- 避免提及你是从 获取的知识。
-- 保持答案与 中描述的一致。
-- 使用 Markdown 语法优化回答格式。
-- 使用与问题相同的语言回答。
-
-问题:"""{{question}}"""`
- }
- },
- {
- title: i18nT('app:template.qa_template'),
- desc: '',
- value: {
- ['4.9.2']: `使用 标记中的问答对进行回答。
-
-
-{{quote}}
-
-
-回答要求:
-- 选择其中一个或多个问答对进行回答。
-- 回答的内容应尽可能与 <答案>答案> 中的内容一致。
-- 如果没有相关的问答对,你需要澄清。
-- 避免提及你是从 QA 获取的知识,只需要回复答案。
-
-问题:"""{{question}}"""`
- }
- },
- {
- title: i18nT('app:template.standard_strict'),
- desc: '',
- value: {
- ['4.9.7']: `忘记你已有的知识,仅使用 标记中的内容作为本次对话的参考:
-
-
-{{quote}}
-
-
-思考流程:
-1. 判断问题是否与 标记中的内容有关。
-2. 如果有关,你按下面的要求回答。
-3. 如果无关,你直接拒绝回答本次问题。
-
-回答要求:
-- 避免提及你是从 获取的知识。
-- 保持答案与 中描述的一致。
-- 使用 Markdown 语法优化回答格式。
-- 使用与问题相同的语言回答。
-- 使用 [id](CITE) 格式来引用中的知识,其中 CITE 是固定常量, id 为引文中的 id。
-- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
-- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。
-
-问题:"""{{question}}"""`,
- ['4.9.2']: `忘记你已有的知识,仅使用 标记中的内容作为本次对话的参考:
-
-
-{{quote}}
-
-
-思考流程:
-1. 判断问题是否与 标记中的内容有关。
-2. 如果有关,你按下面的要求回答。
-3. 如果无关,你直接拒绝回答本次问题。
-
-回答要求:
-- 避免提及你是从 获取的知识。
-- 保持答案与 中描述的一致。
-- 使用 Markdown 语法优化回答格式。
-- 使用与问题相同的语言回答。
-
-问题:"""{{question}}"""`
- }
- },
- {
- title: i18nT('app:template.hard_strict'),
- desc: '',
- value: {
- ['4.9.2']: `忘记你已有的知识,仅使用 标记中的问答对进行回答。
-
-
-{{quote}}
-
-
-思考流程:
-1. 判断问题是否与 标记中的内容有关。
-2. 如果无关,你直接拒绝回答本次问题。
-3. 判断是否有相近或相同的问题。
-4. 如果有相同的问题,直接输出对应答案。
-5. 如果只有相近的问题,请把相近的问题和答案一起输出。
-
-回答要求:
-- 如果没有相关的问答对,你需要澄清。
-- 回答的内容应尽可能与 标记中的内容一致。
-- 避免提及你是从 QA 获取的知识,只需要回复答案。
-- 使用 Markdown 语法优化回答格式。
-- 使用与问题相同的语言回答。
-
-问题:"""{{question}}"""`
- }
- }
-];
-
-export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
- {
- title: i18nT('app:template.standard_template'),
- desc: '',
- value: {
- ['4.9.7']: `使用 标记中的内容作为本次对话的参考:
-
-
-{{quote}}
-
-
-回答要求:
-- 如果你不清楚答案,你需要澄清。
-- 避免提及你是从 获取的知识。
-- 保持答案与 中描述的一致。
-- 使用 Markdown 语法优化回答格式。
-- 使用与问题相同的语言回答。
-- 使用 [id](CITE) 格式来引用中的知识,其中 CITE 是固定常量, id 为引文中的 id。
-- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
-- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。`,
- ['4.9.2']: `使用 标记中的内容作为本次对话的参考:
-
-
-{{quote}}
-
-
-回答要求:
-- 如果你不清楚答案,你需要澄清。
-- 避免提及你是从 获取的知识。
-- 保持答案与 中描述的一致。
-- 使用 Markdown 语法优化回答格式。
-- 使用与问题相同的语言回答。`
- }
- },
- {
- title: i18nT('app:template.qa_template'),
- desc: '',
- value: {
- ['4.9.2']: `使用 标记中的问答对进行回答。
-
-
-{{quote}}
-
-
-回答要求:
-- 选择其中一个或多个问答对进行回答。
-- 回答的内容应尽可能与 <答案>答案> 中的内容一致。
-- 如果没有相关的问答对,你需要澄清。
-- 避免提及你是从 QA 获取的知识,只需要回复答案。`
- }
- },
- {
- title: i18nT('app:template.standard_strict'),
- desc: '',
- value: {
- ['4.9.7']: `忘记你已有的知识,仅使用 标记中的内容作为本次对话的参考:
-
-
-{{quote}}
-
-
-思考流程:
-1. 判断问题是否与 标记中的内容有关。
-2. 如果有关,你按下面的要求回答。
-3. 如果无关,你直接拒绝回答本次问题。
-
-回答要求:
-- 避免提及你是从 获取的知识。
-- 保持答案与 中描述的一致。
-- 使用 Markdown 语法优化回答格式。
-- 使用与问题相同的语言回答。
-- 使用 [id](CITE) 格式来引用中的知识,其中 CITE 是固定常量, id 为引文中的 id。
-- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
-- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。
-
-问题:"""{{question}}"""`,
- ['4.9.2']: `忘记你已有的知识,仅使用 标记中的内容作为本次对话的参考:
-
-
-{{quote}}
-
-
-思考流程:
-1. 判断问题是否与 标记中的内容有关。
-2. 如果有关,你按下面的要求回答。
-3. 如果无关,你直接拒绝回答本次问题。
-
-回答要求:
-- 避免提及你是从 获取的知识。
-- 保持答案与 中描述的一致。
-- 使用 Markdown 语法优化回答格式。
-- 使用与问题相同的语言回答。`
- }
- },
- {
- title: i18nT('app:template.hard_strict'),
- desc: '',
- value: {
- ['4.9.2']: `忘记你已有的知识,仅使用 标记中的问答对进行回答。
-
-
-{{quote}}
-
-
-思考流程:
-1. 判断问题是否与 标记中的内容有关。
-2. 如果无关,你直接拒绝回答本次问题。
-3. 判断是否有相近或相同的问题。
-4. 如果有相同的问题,直接输出对应答案。
-5. 如果只有相近的问题,请把相近的问题和答案一起输出。
-
-回答要求:
-- 如果没有相关的问答对,你需要澄清。
-- 回答的内容应尽可能与 标记中的内容一致。
-- 避免提及你是从 QA 获取的知识,只需要回复答案。
-- 使用 Markdown 语法优化回答格式。
-- 使用与问题相同的语言回答。`
- }
- }
-];
-
export const getQuotePrompt = (version?: string, role: 'user' | 'system' = 'user') => {
const quotePromptTemplates =
role === 'user' ? Prompt_userQuotePromptList : Prompt_systemQuotePromptList;
@@ -333,7 +314,7 @@ export const getQuotePrompt = (version?: string, role: 'user' | 'system' = 'user
// Document quote prompt
export const getDocumentQuotePrompt = (version?: string) => {
const promptMap = {
- ['4.9.2']: `将 中的内容作为本次对话的参考:
+ ['4.9.7']: `将 中的内容作为本次对话的参考:
{{quote}}
diff --git a/packages/global/core/ai/prompt/dataset.ts b/packages/global/core/ai/prompt/dataset.ts
index aec3cfa5c..c40187834 100644
--- a/packages/global/core/ai/prompt/dataset.ts
+++ b/packages/global/core/ai/prompt/dataset.ts
@@ -1,14 +1,19 @@
export const getDatasetSearchToolResponsePrompt = () => {
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" 获取的知识。
-- 保持答案与 "quotes" 中描述的一致。
+- 避免提及你是从 "cites" 获取的知识。
+- 保持答案与 "cites" 中描述的一致。
- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
-- 使用与问题相同的语言回答。
-- 使用 [id](CITE) 格式来引用 "quotes" 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
-- 在每段话结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
-- 每段话至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。`;
+- 使用与问题相同的语言回答。`;
};
diff --git a/packages/global/core/app/utils.ts b/packages/global/core/app/utils.ts
index 30c21e9cd..f2b3a8895 100644
--- a/packages/global/core/app/utils.ts
+++ b/packages/global/core/app/utils.ts
@@ -9,6 +9,7 @@ import { type WorkflowTemplateBasicType } from '../workflow/type';
import { AppTypeEnum } from './constants';
import { AppErrEnum } from '../../common/error/code/app';
import { PluginErrEnum } from '../../common/error/code/plugin';
+import { i18nT } from '../../../web/i18n/utils';
export const getDefaultAppForm = (): AppSimpleEditFormType => {
return {
@@ -189,7 +190,7 @@ export const getAppType = (config?: WorkflowTemplateBasicType | AppSimpleEditFor
return '';
};
-export const checkAppUnExistError = (error?: string) => {
+export const formatToolError = (error?: string) => {
const unExistError: Array = [
AppErrEnum.unAuthApp,
AppErrEnum.unExist,
@@ -197,9 +198,9 @@ export const checkAppUnExistError = (error?: string) => {
PluginErrEnum.unExist
];
- if (!!error && unExistError.includes(error)) {
- return error;
+ if (error && unExistError.includes(error)) {
+ return i18nT('app:un_auth');
} else {
- return undefined;
+ return error;
}
};
diff --git a/packages/service/common/file/read/utils.ts b/packages/service/common/file/read/utils.ts
index 1e83ba351..3b263879a 100644
--- a/packages/service/common/file/read/utils.ts
+++ b/packages/service/common/file/read/utils.ts
@@ -149,7 +149,7 @@ export const readRawContentByFileBuffer = async ({
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
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 };
};
diff --git a/packages/service/core/ai/utils.ts b/packages/service/core/ai/utils.ts
index 449160ea8..d52713244 100644
--- a/packages/service/core/ai/utils.ts
+++ b/packages/service/core/ai/utils.ts
@@ -138,9 +138,11 @@ export const llmStreamResponseToAnswerText = async (
responseChoice.tool_calls.forEach((toolCall, i) => {
const index = toolCall.index ?? i;
- if (toolCall.id || callingTool) {
- // 有 id,代表新 call 工具
- if (toolCall.id) {
+ // Call new tool
+ const hasNewTool = toolCall?.function?.name || callingTool;
+ if (hasNewTool) {
+ // 有 function name,代表新 call 工具
+ if (toolCall?.function?.name) {
callingTool = {
name: toolCall.function?.name || '',
arguments: toolCall.function?.arguments || ''
@@ -221,7 +223,9 @@ export const parseReasoningContent = (text: string): [string, string] => {
};
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
diff --git a/packages/service/core/app/utils.ts b/packages/service/core/app/utils.ts
index 91ce6404b..23dab1b1b 100644
--- a/packages/service/core/app/utils.ts
+++ b/packages/service/core/app/utils.ts
@@ -46,6 +46,7 @@ export async function rewriteAppWorkflowToDetail({
const versionIds = appNodes
.filter((node) => node.version && Types.ObjectId.isValid(node.version))
.map((node) => node.version);
+
if (versionIds.length > 0) {
const versionDataList = await MongoAppVersion.find(
{
diff --git a/packages/service/core/dataset/search/controller.ts b/packages/service/core/dataset/search/controller.ts
index 4279b4ee8..fbfddd9f4 100644
--- a/packages/service/core/dataset/search/controller.ts
+++ b/packages/service/core/dataset/search/controller.ts
@@ -474,7 +474,7 @@ export async function searchDatasetData(
).lean()
]);
- const set = new Map();
+ const set = new Set();
const formatResult = results
.map((item, index) => {
const collection = collections.find((col) => String(col._id) === String(item.collectionId));
@@ -507,7 +507,7 @@ export async function searchDatasetData(
.filter((item) => {
if (!item) return false;
if (set.has(item.id)) return false;
- set.set(item.id, 1);
+ set.add(item.id);
return true;
})
.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
};
};
diff --git a/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts b/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts
index f85246c9f..5f55ecf91 100644
--- a/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts
+++ b/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts
@@ -727,9 +727,10 @@ async function streamResponse({
const index = toolCall.index ?? i;
// Call new tool
- if (toolCall.id || callingTool) {
- // 有 id,代表新 call 工具
- if (toolCall.id) {
+ const hasNewTool = toolCall?.function?.name || callingTool;
+ if (hasNewTool) {
+ // 有 function name,代表新 call 工具
+ if (toolCall?.function?.name) {
callingTool = {
name: toolCall.function?.name || '',
arguments: toolCall.function?.arguments || ''
diff --git a/packages/service/core/workflow/dispatch/dataset/search.ts b/packages/service/core/workflow/dispatch/dataset/search.ts
index a3d8982b4..d7ca421cf 100644
--- a/packages/service/core/workflow/dispatch/dataset/search.ts
+++ b/packages/service/core/workflow/dispatch/dataset/search.ts
@@ -268,7 +268,7 @@ export async function dispatchDatasetSearch(
nodeDispatchUsages,
[DispatchNodeResponseKeyEnum.toolResponses]: {
prompt: getDatasetSearchToolResponsePrompt(),
- quotes: searchRes.map((item) => ({
+ cites: searchRes.map((item) => ({
id: item.id,
sourceName: item.sourceName,
updateTime: item.updateTime,
diff --git a/packages/web/components/common/Icon/button.tsx b/packages/web/components/common/Icon/button.tsx
index 06fb76903..800740f91 100644
--- a/packages/web/components/common/Icon/button.tsx
+++ b/packages/web/components/common/Icon/button.tsx
@@ -6,7 +6,6 @@ import MyTooltip from '../MyTooltip';
type Props = FlexProps & {
icon: string;
size?: string;
- onClick?: () => void;
hoverColor?: string;
hoverBg?: string;
hoverBorderColor?: string;
@@ -41,9 +40,9 @@ const MyIconButton = ({
color: hoverColor,
borderColor: hoverBorderColor
}}
- onClick={() => {
+ onClick={(e) => {
if (isLoading) return;
- onClick?.();
+ onClick?.(e);
}}
sx={{ userSelect: 'none' }}
{...props}
diff --git a/packages/web/i18n/en/account_team.json b/packages/web/i18n/en/account_team.json
index bddc21b32..c58ded16f 100644
--- a/packages/web/i18n/en/account_team.json
+++ b/packages/web/i18n/en/account_team.json
@@ -77,7 +77,7 @@
"owner": "owner",
"permission": "Permissions",
"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_tip": "Can create applications in the root directory (creation permissions in folders are controlled by the folder)",
"permission_datasetCreate": "Create Knowledge Base",
diff --git a/packages/web/i18n/en/app.json b/packages/web/i18n/en/app.json
index b00bc635e..413871f56 100644
--- a/packages/web/i18n/en/app.json
+++ b/packages/web/i18n/en/app.json
@@ -18,7 +18,6 @@
"app.modules.click to update": "Click to Refresh",
"app.modules.has new version": "New Version Available",
"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_initial": "Initial Version",
"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.workflowresponseempty": "Response content is empty",
"type_not_recognized": "App type not recognized",
+ "un_auth": "No permission",
"upload_file_max_amount": "Maximum File Quantity",
"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.",
diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json
index b356bf847..faa8a639f 100644
--- a/packages/web/i18n/en/common.json
+++ b/packages/web/i18n/en/common.json
@@ -754,6 +754,7 @@
"data_index_image": "Image Index",
"data_index_question": "Inferred question 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 to delete the data": "Confirm to Delete This Data?",
"dataset.Confirm to delete the file": "Confirm to Delete This File and All Its Data?",
diff --git a/packages/web/i18n/zh-CN/account_team.json b/packages/web/i18n/zh-CN/account_team.json
index fe522fb9f..df1f91e0b 100644
--- a/packages/web/i18n/zh-CN/account_team.json
+++ b/packages/web/i18n/zh-CN/account_team.json
@@ -84,7 +84,7 @@
"owner": "所有者",
"permission": "权限",
"permission_apikeyCreate": "创建 API 密钥",
- "permission_apikeyCreate_Tip": "可以创建全局的 APIKey",
+ "permission_apikeyCreate_Tip": "可以创建全局的 APIKey和 MCP 服务",
"permission_appCreate": "创建应用",
"permission_appCreate_tip": "可以在根目录创建应用,(文件夹下的创建权限由文件夹控制)",
"permission_datasetCreate": "创建知识库",
diff --git a/packages/web/i18n/zh-CN/app.json b/packages/web/i18n/zh-CN/app.json
index 7b1fb99f9..1d49f4963 100644
--- a/packages/web/i18n/zh-CN/app.json
+++ b/packages/web/i18n/zh-CN/app.json
@@ -22,7 +22,6 @@
"app.modules.click to update": "点击更新",
"app.modules.has new version": "有新版本",
"app.modules.not_found": "组件缺失",
- "app.modules.not_found_tips": "系统内无法查找到该组件,请删除,否则流程无法正常运行",
"app.version_current": "当前版本",
"app.version_initial": "初始版本",
"app.version_name_tips": "版本名称不能为空",
@@ -202,6 +201,7 @@
"type.error.Workflow data is empty": "没有获取到工作流数据",
"type.error.workflowresponseempty": "响应内容为空",
"type_not_recognized": "未识别到应用类型",
+ "un_auth": "无权限",
"upload_file_max_amount": "最大文件数量",
"upload_file_max_amount_tip": "单轮对话中最大上传文件数量",
"variable.select type_desc": "可以为工作流定义全局变量,常用临时缓存。赋值的方式包括:\n1. 从对话页面的 query 参数获取。\n2. 通过 API 的 variables 对象传递。\n3. 通过【变量更新】节点进行赋值。",
diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json
index c26c029f4..b351f9440 100644
--- a/packages/web/i18n/zh-CN/common.json
+++ b/packages/web/i18n/zh-CN/common.json
@@ -753,6 +753,7 @@
"data_index_image": "图片索引",
"data_index_question": "推测问题索引",
"data_index_summary": "摘要索引",
+ "data_not_found": "数据找不到了",
"dataset.Confirm move the folder": "确认移动到该目录",
"dataset.Confirm to delete the data": "确认删除该数据?",
"dataset.Confirm to delete the file": "确认删除该文件及其所有数据?",
diff --git a/packages/web/i18n/zh-Hant/account_team.json b/packages/web/i18n/zh-Hant/account_team.json
index ef22aa482..9c31f6320 100644
--- a/packages/web/i18n/zh-Hant/account_team.json
+++ b/packages/web/i18n/zh-Hant/account_team.json
@@ -77,7 +77,7 @@
"owner": "擁有者",
"permission": "權限",
"permission_apikeyCreate": "建立 API 密鑰",
- "permission_apikeyCreate_Tip": "可以建立全域的 APIKey",
+ "permission_apikeyCreate_Tip": "可以創建全局的 APIKey和 MCP 服務",
"permission_appCreate": "建立應用程式",
"permission_appCreate_tip": "可以在根目錄建立應用程式,(資料夾下的建立權限由資料夾控制)",
"permission_datasetCreate": "建立知識庫",
diff --git a/packages/web/i18n/zh-Hant/app.json b/packages/web/i18n/zh-Hant/app.json
index 37f168035..aed7c1053 100644
--- a/packages/web/i18n/zh-Hant/app.json
+++ b/packages/web/i18n/zh-Hant/app.json
@@ -18,7 +18,6 @@
"app.modules.click to update": "點選更新",
"app.modules.has new version": "有新版本",
"app.modules.not_found": "元件遺失",
- "app.modules.not_found_tips": "系統內無法查詢到該元件,請刪除,否則流程無法正常運作",
"app.version_current": "目前版本",
"app.version_initial": "初始版本",
"app.version_name_tips": "版本名稱不能空白",
@@ -193,6 +192,7 @@
"type.error.Workflow data is empty": "沒有獲取到工作流數據",
"type.error.workflowresponseempty": "響應內容為空",
"type_not_recognized": "未識別到應用程式類型",
+ "un_auth": "無權限",
"upload_file_max_amount": "最大檔案數量",
"upload_file_max_amount_tip": "單輪對話中最大上傳檔案數量",
"variable.select type_desc": "可以為工作流程定義全域變數,常用於暫存。賦值的方式包括:\n1. 從對話頁面的 query 參數取得。\n2. 透過 API 的 variables 物件傳遞。\n3. 透過【變數更新】節點進行賦值。",
diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json
index 6b482499a..a12acdbcd 100644
--- a/packages/web/i18n/zh-Hant/common.json
+++ b/packages/web/i18n/zh-Hant/common.json
@@ -753,6 +753,7 @@
"data_index_image": "圖片索引",
"data_index_question": "推測問題索引",
"data_index_summary": "摘要索引",
+ "data_not_found": "數據找不到了",
"dataset.Confirm move the folder": "確認移動到此目錄",
"dataset.Confirm to delete the data": "確認刪除此資料?",
"dataset.Confirm to delete the file": "確認刪除此檔案及其所有資料?",
diff --git a/projects/app/src/components/Markdown/A.tsx b/projects/app/src/components/Markdown/A.tsx
index 750112cdb..99c304ee1 100644
--- a/projects/app/src/components/Markdown/A.tsx
+++ b/projects/app/src/components/Markdown/A.tsx
@@ -22,135 +22,184 @@ import { getCollectionSourceData } from '@fastgpt/global/core/dataset/collection
import Markdown from '.';
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
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 (
+
+
+
+ );
+};
+
+const CiteLink = React.memo(function CiteLink({
+ id,
+ chatAuthData,
+ onOpenCiteModal,
+ showAnimation
+}: { id: string; showAnimation?: boolean } & AProps) {
const { t } = useTranslation();
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 (
+ {
+ onOpen();
+ if (showAnimation) return;
+ getQuoteDataById(id);
+ }}
+ trigger={'hover'}
+ gutter={4}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ {sourceData.sourceName}
+
+
+
+
+
+
+
+ {datasetCiteData?.a && }
+
+
+
+
+
+ );
+});
+
+const A = ({
+ children,
+ chatAuthData,
+ onOpenCiteModal,
+ showAnimation,
+ ...props
+}: AProps & {
+ children: any;
+ showAnimation: boolean;
+ [key: string]: any;
+}) => {
+ const content = useCreation(() => String(children), [children]);
// empty href link
if (!props.href && typeof children?.[0] === 'string') {
- return (
-
-
-
- );
+ return ;
}
// Cite
if (
(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 (
- {
- onOpen();
- if (showAnimation) return;
- getQuoteDataById(String(children));
- }}
- trigger={'hover'}
- gutter={4}
- >
-
-
-
-
-
-
-
-
-
-
-
-
- {sourceData.sourceName}
-
-
-
-
-
-
-
- {quoteData?.a && }
-
-
-
-
-
+
);
}
diff --git a/projects/app/src/components/Markdown/index.tsx b/projects/app/src/components/Markdown/index.tsx
index 9768db1fd..5445a8168 100644
--- a/projects/app/src/components/Markdown/index.tsx
+++ b/projects/app/src/components/Markdown/index.tsx
@@ -13,7 +13,7 @@ import dynamic from 'next/dynamic';
import { Box } from '@chakra-ui/react';
import { CodeClassNameEnum, mdTextFormat } from './utils';
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 MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'), { ssr: false });
@@ -33,12 +33,7 @@ type Props = {
showAnimation?: boolean;
isDisabled?: boolean;
forbidZhFormat?: boolean;
- chatAuthData?: {
- appId: string;
- chatId: string;
- chatItemDataId: string;
- } & OutLinkChatAuthProps;
-};
+} & AProps;
const Markdown = (props: Props) => {
const source = props.source || '';
@@ -53,16 +48,25 @@ const MarkdownRender = ({
showAnimation,
isDisabled,
forbidZhFormat,
- chatAuthData
+
+ chatAuthData,
+ onOpenCiteModal
}: Props) => {
const components = useCreation(() => {
return {
img: Image,
pre: RewritePre,
code: Code,
- a: (props: any) =>
+ a: (props: any) => (
+
+ )
};
- }, [chatAuthData, showAnimation]);
+ }, [chatAuthData, onOpenCiteModal, showAnimation]);
const formatSource = useMemo(() => {
if (showAnimation || forbidZhFormat) return source;
diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx
index 31767a2cc..84936d373 100644
--- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx
+++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx
@@ -28,10 +28,15 @@ import { isEqual } from 'lodash';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { formatTimeToChatItemTime } from '@fastgpt/global/common/string/time';
import dayjs from 'dayjs';
-import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
-import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
+import {
+ ChatItemContext,
+ type OnOpenCiteModalProps
+} from '@/web/core/chat/context/chatItemContext';
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 = {
[ChatStatusEnum.loading]: {
@@ -88,13 +93,15 @@ const AIContentCard = React.memo(function AIContentCard({
dataId,
isLastChild,
isChatting,
- questionGuides
+ questionGuides,
+ onOpenCiteModal
}: {
dataId: string;
chatValue: ChatItemValueItemType[];
isLastChild: boolean;
isChatting: boolean;
questionGuides: string[];
+ onOpenCiteModal: (e?: OnOpenCiteModalProps) => void;
}) {
return (
@@ -108,6 +115,7 @@ const AIContentCard = React.memo(function AIContentCard({
value={value}
isLastResponseValue={isLastChild && i === chatValue.length - 1}
isChatting={isChatting}
+ onOpenCiteModal={onOpenCiteModal}
/>
);
})}
@@ -122,7 +130,6 @@ const ChatItem = (props: Props) => {
const { type, avatar, statusBoxData, children, isLastChild, questionGuides = [], chat } = props;
const { isPc } = useSystem();
- const { toast } = useToast();
const styleMap: BoxProps = {
...(type === ChatRoleEnum.Human
@@ -150,7 +157,6 @@ const ChatItem = (props: Props) => {
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
const showNodeStatus = useContextSelector(ChatItemContext, (v) => v.showNodeStatus);
- const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
@@ -228,64 +234,48 @@ const ChatItem = (props: Props) => {
return groupedValues;
}, [chat.obj, chat.value, isChatting]);
- const handleOpenQuoteReader = useCallback(
- ({
- collectionId,
- sourceId,
- sourceName,
- datasetId,
- quoteId
- }: {
+ const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
+ const onOpenCiteModal = useMemoizedFn(
+ (item?: {
collectionId?: string;
sourceId?: string;
sourceName?: string;
datasetId?: string;
quoteId?: string;
}) => {
- if (!setQuoteData) return;
-
- const collectionIdList = collectionId
- ? [collectionId]
+ const collectionIdList = item?.collectionId
+ ? [item.collectionId]
: [...new Set(quoteList.map((item) => item.collectionId))];
- setQuoteData({
+ setCiteModalData({
rawSearch: quoteList,
metadata:
- collectionId && isShowReadRawSource
+ item?.collectionId && isShowReadRawSource
? {
appId: appId,
chatId: chatId,
chatItemDataId: chat.dataId,
- collectionId: collectionId,
+ collectionId: item.collectionId,
collectionIdList,
- sourceId: sourceId || '',
- sourceName: sourceName || '',
- datasetId: datasetId || '',
+ sourceId: item.sourceId || '',
+ sourceName: item.sourceName || '',
+ datasetId: item.datasetId || '',
outLinkAuthData,
- quoteId
+ quoteId: item.quoteId
}
: {
appId: appId,
chatId: chatId,
chatItemDataId: chat.dataId,
collectionIdList,
- sourceId: sourceId,
- sourceName: sourceName,
+ sourceId: item?.sourceId,
+ sourceName: item?.sourceName,
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 (
{
>
{type === ChatRoleEnum.Human && }
{type === ChatRoleEnum.AI && (
-
+ <>
+
+ {i === splitAiResponseResults.length - 1 && (
+
+ )}
+ >
)}
{/* Example: Response tags. A set of dialogs only needs to be displayed once*/}
{i === splitAiResponseResults.length - 1 && <>{children}>}
diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ResponseTags.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ResponseTags.tsx
index 60fea36aa..65f3580f8 100644
--- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ResponseTags.tsx
+++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ResponseTags.tsx
@@ -14,17 +14,24 @@ import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils';
import { useSize } from 'ahooks';
import { useContextSelector } from 'use-context-selector';
import { ChatBoxContext } from '../Provider';
-import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
const ContextModal = dynamic(() => import('./ContextModal'));
const WholeResponseModal = dynamic(() => import('../../../components/WholeResponseModal'));
const ResponseTags = ({
showTags,
- historyItem
+ historyItem,
+ onOpenCiteModal
}: {
showTags: boolean;
historyItem: ChatSiteItemType;
+ onOpenCiteModal: (e?: {
+ collectionId?: string;
+ sourceId?: string;
+ sourceName?: string;
+ datasetId?: string;
+ quoteId?: string;
+ }) => void;
}) => {
const { isPc } = useSystem();
const { t } = useTranslation();
@@ -80,15 +87,6 @@ const ResponseTags = ({
}));
}, [quoteList]);
- const openQuoteReader = (item?: {
- collectionId?: string;
- sourceId?: string;
- sourceName?: string;
- datasetId?: string;
- }) => {
- eventBus.emit(EventNameEnum.openQuoteReader, item);
- };
-
const notEmptyTags =
quoteList.length > 0 ||
(llmModuleAccount === 1 && notSharePage) ||
@@ -161,7 +159,7 @@ const ResponseTags = ({
cursor={'pointer'}
onClick={(e) => {
e.stopPropagation();
- openQuoteReader(item);
+ onOpenCiteModal(item);
}}
height={6}
>
@@ -216,7 +214,7 @@ const ResponseTags = ({
cursor={'pointer'}
onClick={(e) => {
e.stopPropagation();
- openQuoteReader();
+ onOpenCiteModal();
}}
>
{t('chat:citations', { num: quoteList.length })}
diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx
index 40da37b6f..e0ba4f01f 100644
--- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx
+++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx
@@ -68,7 +68,6 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
import { valueTypeFormat } from '@fastgpt/global/core/workflow/runtime/utils';
-const ResponseTags = dynamic(() => import('./components/ResponseTags'));
const FeedbackModal = dynamic(() => import('./components/FeedbackModal'));
const ReadFeedbackModal = dynamic(() => import('./components/ReadFeedbackModal'));
const SelectMarkCollection = dynamic(() => import('./components/SelectMarkCollection'));
@@ -1014,10 +1013,6 @@ const ChatBox = ({
onReadUserDislike: onReadUserDislike(item)
}}
>
-
{/* custom feedback */}
{item.customFeedbacks && item.customFeedbacks.length > 0 && (
@@ -1072,7 +1067,6 @@ const ChatBox = ({
chatType,
delOneMessage,
externalVariableList?.length,
- isChatting,
onAddUserDislike,
onAddUserLike,
onCloseCustomFeedback,
diff --git a/projects/app/src/components/core/chat/components/AIResponseBox.tsx b/projects/app/src/components/core/chat/components/AIResponseBox.tsx
index d5ca6aa43..685548e40 100644
--- a/projects/app/src/components/core/chat/components/AIResponseBox.tsx
+++ b/projects/app/src/components/core/chat/components/AIResponseBox.tsx
@@ -30,7 +30,7 @@ import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
import { SelectOptionsComponent, FormInputComponent } from './Interactive/InteractiveComponents';
import { extractDeepestInteractive } from '@fastgpt/global/core/workflow/runtime/utils';
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 { useCreation } from 'ahooks';
@@ -90,11 +90,13 @@ const RenderResoningContent = React.memo(function RenderResoningContent({
const RenderText = React.memo(function RenderText({
showAnimation,
text,
- chatItemDataId
+ chatItemDataId,
+ onOpenCiteModal
}: {
showAnimation: boolean;
text: string;
chatItemDataId: string;
+ onOpenCiteModal?: (e?: OnOpenCiteModalProps) => void;
}) {
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
@@ -111,7 +113,14 @@ const RenderText = React.memo(function RenderText({
return { appId, chatId, chatItemDataId, ...outLinkAuthData };
}, [appId, chatId, chatItemDataId, outLinkAuthData]);
- return ;
+ return (
+
+ );
});
const RenderTool = React.memo(
@@ -240,12 +249,14 @@ const AIResponseBox = ({
chatItemDataId,
value,
isLastResponseValue,
- isChatting
+ isChatting,
+ onOpenCiteModal
}: {
chatItemDataId: string;
value: UserChatItemValueItemType | AIChatItemValueItemType;
isLastResponseValue: boolean;
isChatting: boolean;
+ onOpenCiteModal?: (e?: OnOpenCiteModalProps) => void;
}) => {
if (value.type === ChatItemValueTypeEnum.text && value.text) {
return (
@@ -253,6 +264,7 @@ const AIResponseBox = ({
chatItemDataId={chatItemDataId}
showAnimation={isChatting && isLastResponseValue}
text={value.text.content}
+ onOpenCiteModal={onOpenCiteModal}
/>
);
}
diff --git a/projects/app/src/pageComponents/app/detail/Logs/DetailLogsModal.tsx b/projects/app/src/pageComponents/app/detail/Logs/DetailLogsModal.tsx
index 94134cf65..16c4bcbc6 100644
--- a/projects/app/src/pageComponents/app/detail/Logs/DetailLogsModal.tsx
+++ b/projects/app/src/pageComponents/app/detail/Logs/DetailLogsModal.tsx
@@ -38,8 +38,8 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
const pluginRunTab = useContextSelector(ChatItemContext, (v) => v.pluginRunTab);
const setPluginRunTab = useContextSelector(ChatItemContext, (v) => v.setPluginRunTab);
- const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
- const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
+ const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
+ const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
@@ -81,7 +81,7 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
right={0}
h={['100%', '96%']}
w={'100%'}
- maxW={quoteData ? ['100%', '1080px'] : ['100%', '600px']}
+ maxW={datasetCiteData ? ['100%', '1080px'] : ['100%', '600px']}
bg={'white'}
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
borderRadius={'md'}
@@ -169,7 +169,7 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
)}
- {quoteData && (
+ {datasetCiteData && (
{
borderRadius={'md'}
>
setQuoteData(undefined)}
+ rawSearch={datasetCiteData.rawSearch}
+ metadata={datasetCiteData.metadata}
+ onClose={() => setCiteModalData(undefined)}
/>
)}
diff --git a/projects/app/src/pageComponents/app/detail/MCPTools/EditForm.tsx b/projects/app/src/pageComponents/app/detail/MCPTools/EditForm.tsx
index 97c5872d9..e9c9e72bb 100644
--- a/projects/app/src/pageComponents/app/detail/MCPTools/EditForm.tsx
+++ b/projects/app/src/pageComponents/app/detail/MCPTools/EditForm.tsx
@@ -109,6 +109,10 @@ const EditForm = ({
boxShadow:
'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);
+ }}
>
@@ -157,23 +161,11 @@ const EditForm = ({
hoverBg={'rgba(51, 112, 255, 0.10)'}
hoverBorderColor={'primary.300'}
tip={t('app:MCP_tools_detail')}
- onClick={() => {
+ onClick={(e) => {
+ e.stopPropagation();
setToolDetail(tool);
}}
/>
- {
- setCurrentTool(tool);
- }}
- />
);
diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx b/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx
index 918edb210..f68c419a2 100644
--- a/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx
+++ b/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx
@@ -26,8 +26,8 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => {
const { t } = useTranslation();
const { appDetail } = useContextSelector(AppContext, (v) => v);
- const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
- const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
+ const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
+ const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
// form2AppWorkflow dependent allDatasets
const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible);
@@ -42,8 +42,8 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => {
}, [appForm, setWorkflowData, t]);
useEffect(() => {
- setRenderEdit(!quoteData);
- }, [quoteData, setRenderEdit]);
+ setRenderEdit(!datasetCiteData);
+ }, [datasetCiteData, setRenderEdit]);
const { ChatContainer, restartChat, loading } = useChatTest({
...workflowData,
@@ -89,12 +89,12 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => {
- {quoteData && (
+ {datasetCiteData && (
setQuoteData(undefined)}
+ rawSearch={datasetCiteData.rawSearch}
+ metadata={datasetCiteData.metadata}
+ onClose={() => setCiteModalData(undefined)}
/>
)}
diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelect.tsx b/projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelect.tsx
index f4ee0ea41..61c1b7432 100644
--- a/projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelect.tsx
+++ b/projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelect.tsx
@@ -17,7 +17,7 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
import ConfigToolModal from './ConfigToolModal';
import { getWebLLMModel } from '@/web/common/system/utils';
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 = ({
appForm,
@@ -65,7 +65,7 @@ const ToolSelect = ({
gridGap={[2, 4]}
>
{appForm.selectedTools.map((item) => {
- const hasError = checkAppUnExistError(item.pluginData?.error);
+ const toolError = formatToolError(item.pluginData?.error);
return (
@@ -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)'}
borderRadius={'md'}
border={theme.borders.base}
- borderColor={hasError ? 'red.600' : ''}
+ borderColor={toolError ? 'red.600' : ''}
_hover={{
...hoverDeleteStyles,
- borderColor: hasError ? 'red.600' : 'primary.300'
+ borderColor: toolError ? 'red.600' : 'primary.300'
}}
cursor={'pointer'}
onClick={() => {
@@ -93,7 +93,7 @@ const ToolSelect = ({
input.renderTypeList.includes(FlowNodeInputTypeEnum.selectLLMModel) ||
input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)
) ||
- hasError ||
+ toolError ||
item.flowNodeType === FlowNodeTypeEnum.tool ||
item.flowNodeType === FlowNodeTypeEnum.toolSet
) {
@@ -113,21 +113,19 @@ const ToolSelect = ({
>
{item.name}
- {hasError && (
-
-
-
- {t('app:app.modules.not_found')}
-
-
+ {toolError && (
+
+
+ {t(toolError as any)}
+
)}
{
});
const pluginRunTab = useContextSelector(ChatItemContext, (v) => v.pluginRunTab);
const setPluginRunTab = useContextSelector(ChatItemContext, (v) => v.setPluginRunTab);
- const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
- const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
+ const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
+ const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible);
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
@@ -60,7 +60,7 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
bottom={0}
right={0}
onClick={() => {
- setQuoteData(undefined);
+ setCiteModalData(undefined);
onClose();
}}
/>
@@ -72,7 +72,7 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
top={5}
right={0}
h={isOpen ? '95%' : '0'}
- w={isOpen ? (quoteData ? ['100%', '960px'] : ['100%', '460px']) : '0'}
+ w={isOpen ? (datasetCiteData ? ['100%', '960px'] : ['100%', '460px']) : '0'}
bg={'white'}
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
borderRadius={'md'}
@@ -152,7 +152,7 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
- {quoteData && (
+ {datasetCiteData && (
{
borderRadius={'md'}
>
setQuoteData(undefined)}
+ rawSearch={datasetCiteData.rawSearch}
+ metadata={datasetCiteData.metadata}
+ onClose={() => setCiteModalData(undefined)}
/>
)}
diff --git a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/NodeCard.tsx b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/NodeCard.tsx
index a4d880e68..70e7363af 100644
--- a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/NodeCard.tsx
+++ b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/NodeCard.tsx
@@ -35,6 +35,7 @@ import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import MySelect from '@fastgpt/web/components/common/MySelect';
import { useCreation } from 'ahooks';
+import { formatToolError } from '@fastgpt/global/core/app/utils';
type Props = FlowNodeItemType & {
children?: React.ReactNode | React.ReactNode[] | string;
@@ -144,6 +145,7 @@ const NodeCard = (props: Props) => {
/* Node header */
const Header = useMemo(() => {
const showHeader = node?.flowNodeType !== FlowNodeTypeEnum.comment;
+ const error = formatToolError(node?.pluginData?.error);
return (
@@ -254,23 +256,19 @@ const NodeCard = (props: Props) => {
)}
)}
- {!!node?.pluginData?.error && (
-
-
-
-
- {node?.pluginData?.error || t('app:app.modules.not_found')}
-
-
-
+ {!!error && (
+
+
+ {t(error as any)}
+
)}
diff --git a/projects/app/src/pageComponents/app/detail/context.tsx b/projects/app/src/pageComponents/app/detail/context.tsx
index e2203e84c..4bbe40671 100644
--- a/projects/app/src/pageComponents/app/detail/context.tsx
+++ b/projects/app/src/pageComponents/app/detail/context.tsx
@@ -3,7 +3,6 @@ import {
type ReactNode,
type SetStateAction,
useCallback,
- useEffect,
useMemo,
useState
} 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 { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
-import { checkAppUnExistError } from '@fastgpt/global/core/app/utils';
import { useToast } from '@fastgpt/web/hooks/useToast';
const InfoModal = dynamic(() => import('./InfoModal'));
@@ -205,16 +203,6 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
[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(
() => ({
appId,
diff --git a/projects/app/src/pageComponents/chat/ChatHistorySlider.tsx b/projects/app/src/pageComponents/chat/ChatHistorySlider.tsx
index d056f1a6d..52fd4686b 100644
--- a/projects/app/src/pageComponents/chat/ChatHistorySlider.tsx
+++ b/projects/app/src/pageComponents/chat/ChatHistorySlider.tsx
@@ -46,7 +46,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
const appName = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.name);
const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.avatar);
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 formatHistories: HistoryItemType[] = histories.map((item) => {
@@ -146,7 +146,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
overflow={'hidden'}
onClick={() => {
onChangeChatId();
- setQuoteData(undefined);
+ setCiteModalData(undefined);
}}
>
{t('common:core.chat.New Chat')}
@@ -202,7 +202,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
: {
onClick: () => {
onChangeChatId(item.id);
- setQuoteData(undefined);
+ setCiteModalData(undefined);
}
})}
{...(i !== concatHistory.length - 1 && {
@@ -274,7 +274,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
onDelHistory(item.id);
if (item.id === activeChatId) {
onChangeChatId();
- setQuoteData(undefined);
+ setCiteModalData(undefined);
}
},
type: 'danger'
diff --git a/projects/app/src/pages/_app.tsx b/projects/app/src/pages/_app.tsx
index ae15629d8..48792ca65 100644
--- a/projects/app/src/pages/_app.tsx
+++ b/projects/app/src/pages/_app.tsx
@@ -81,4 +81,5 @@ function App({ Component, pageProps }: AppPropsWithLayout) {
);
}
+// @ts-ignore
export default appWithTranslation(App);
diff --git a/projects/app/src/pages/api/core/app/mcpTools/create.ts b/projects/app/src/pages/api/core/app/mcpTools/create.ts
index e349d59ed..d4a8c8099 100644
--- a/projects/app/src/pages/api/core/app/mcpTools/create.ts
+++ b/projects/app/src/pages/api/core/app/mcpTools/create.ts
@@ -13,6 +13,7 @@ import {
} from '@fastgpt/global/core/app/mcpTools/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 = {};
@@ -33,7 +34,7 @@ async function handler(
const { name, avatar, toolList, url, parentId } = req.body;
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 checkTeamAppLimit(teamId);
diff --git a/projects/app/src/pages/api/core/app/mcpTools/update.ts b/projects/app/src/pages/api/core/app/mcpTools/update.ts
index c7f76e259..e335844ca 100644
--- a/projects/app/src/pages/api/core/app/mcpTools/update.ts
+++ b/projects/app/src/pages/api/core/app/mcpTools/update.ts
@@ -56,10 +56,11 @@ async function handler(
});
}
- await MongoApp.findByIdAndUpdate(
- appId,
+ await MongoApp.updateOne(
+ { _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 }
);
@@ -97,6 +98,7 @@ const updateMCPChildrenTool = async ({
teamId
});
+ // 删除 DB 里有,新的工具列表里没有的工具
for await (const tool of dbTools) {
if (!toolSetData.toolList.find((t) => t.name === tool.name)) {
await onDelOneApp({
@@ -107,6 +109,7 @@ const updateMCPChildrenTool = async ({
}
}
+ // 创建 DB 里没有,新的工具列表里有的工具
for await (const tool of toolSetData.toolList) {
if (!dbTools.find((t) => t.name === tool.name)) {
await onCreateApp({
@@ -123,11 +126,12 @@ const updateMCPChildrenTool = async ({
}
}
+ // 更新 DB 里有的工具
for await (const tool of toolSetData.toolList) {
const dbTool = dbTools.find((t) => t.name === tool.name);
if (dbTool) {
- await MongoApp.findByIdAndUpdate(
- dbTool._id,
+ await MongoApp.updateOne(
+ { _id: dbTool._id },
{
modules: [getMCPToolRuntimeNode({ tool, url: toolSetData.url })]
},
diff --git a/projects/app/src/pages/api/core/chat/quote/getQuote.ts b/projects/app/src/pages/api/core/chat/quote/getQuote.ts
index 6873d2842..56c9f0c22 100644
--- a/projects/app/src/pages/api/core/chat/quote/getQuote.ts
+++ b/projects/app/src/pages/api/core/chat/quote/getQuote.ts
@@ -6,7 +6,7 @@ import { quoteDataFieldSelector, type QuoteDataItemType } from '@/service/core/c
import { processChatTimeFilter } from '@/service/core/chat/utils';
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
-export type GetQuoteDataProps = {
+export type GetQuoteProps = {
datasetDataIdList: string[];
collectionIdList: string[];
@@ -19,9 +19,9 @@ export type GetQuoteDataProps = {
teamToken?: string;
};
-export type GetQuoteDataRes = QuoteDataItemType[];
+export type GetQuotesRes = QuoteDataItemType[];
-async function handler(req: ApiRequestProps): Promise {
+async function handler(req: ApiRequestProps): Promise {
const {
appId,
chatId,
diff --git a/projects/app/src/pages/api/core/dataset/data/getQuoteData.ts b/projects/app/src/pages/api/core/dataset/data/getQuoteData.ts
index 5c8c3a2df..2bf4d03ec 100644
--- a/projects/app/src/pages/api/core/dataset/data/getQuoteData.ts
+++ b/projects/app/src/pages/api/core/dataset/data/getQuoteData.ts
@@ -8,6 +8,7 @@ import { type OutLinkChatAuthProps } from '@fastgpt/global/support/permission/ch
import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
+import { i18nT } from '@fastgpt/web/i18n/utils';
export type GetQuoteDataResponse = {
collection: DatasetCollectionSchemaType;
@@ -46,7 +47,7 @@ async function handler(req: ApiRequestProps): Promise {
const isPlugin = useContextSelector(ChatItemContext, (v) => v.isPlugin);
const chatBoxData = useContextSelector(ChatItemContext, (v) => v.chatBoxData);
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
- const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
- const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
+ const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
+ const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
@@ -148,7 +148,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
);
return isPc || !appId ? (
- {Children}
+ {Children}
) : (
{
{Children}
);
- }, [t, isPc, appId, isOpenSlider, onCloseSlider, quoteData]);
+ }, [t, isPc, appId, isOpenSlider, onCloseSlider, datasetCiteData]);
return (
@@ -173,7 +173,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
)}
- {(!quoteData || isPc) && (
+ {(!datasetCiteData || isPc) && (
{/* pc always show history. */}
@@ -222,12 +222,12 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
)}
- {quoteData && (
+ {datasetCiteData && (
setQuoteData(undefined)}
+ rawSearch={datasetCiteData.rawSearch}
+ metadata={datasetCiteData.metadata}
+ onClose={() => setCiteModalData(undefined)}
/>
)}
diff --git a/projects/app/src/pages/chat/share.tsx b/projects/app/src/pages/chat/share.tsx
index 172048169..bf71617a1 100644
--- a/projects/app/src/pages/chat/share.tsx
+++ b/projects/app/src/pages/chat/share.tsx
@@ -85,8 +85,8 @@ const OutLink = (props: Props) => {
const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables);
const isPlugin = useContextSelector(ChatItemContext, (v) => v.isPlugin);
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
- const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
- const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
+ const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
+ const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
const isResponseDetail = useContextSelector(ChatItemContext, (v) => v.isResponseDetail);
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
@@ -226,7 +226,7 @@ const OutLink = (props: Props) => {
if (showHistory !== '1') return null;
return isPc ? (
- {Children}
+ {Children}
) : (
{
);
- }, [isOpenSlider, isPc, onCloseSlider, quoteData, showHistory, t]);
+ }, [isOpenSlider, isPc, onCloseSlider, datasetCiteData, showHistory, t]);
return (
<>
@@ -255,7 +255,7 @@ const OutLink = (props: Props) => {
gap={4}
{...(isEmbed ? { p: '0 !important', borderRadius: '0', boxShadow: 'none' } : { p: [0, 5] })}
>
- {(!quoteData || isPc) && (
+ {(!datasetCiteData || isPc) && (
{RenderHistoryList}
@@ -303,12 +303,12 @@ const OutLink = (props: Props) => {
)}
- {quoteData && (
+ {datasetCiteData && (
setQuoteData(undefined)}
+ rawSearch={datasetCiteData.rawSearch}
+ metadata={datasetCiteData.metadata}
+ onClose={() => setCiteModalData(undefined)}
/>
)}
diff --git a/projects/app/src/pages/chat/team.tsx b/projects/app/src/pages/chat/team.tsx
index b01767379..9501c0151 100644
--- a/projects/app/src/pages/chat/team.tsx
+++ b/projects/app/src/pages/chat/team.tsx
@@ -64,8 +64,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables);
const chatBoxData = useContextSelector(ChatItemContext, (v) => v.chatBoxData);
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
- const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
- const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
+ const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
+ const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
@@ -166,7 +166,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
);
return isPc || !appId ? (
- {Children}
+ {Children}
) : (
{
{Children}
);
- }, [appId, isOpenSlider, isPc, onCloseSlider, quoteData, t]);
+ }, [appId, isOpenSlider, isPc, onCloseSlider, datasetCiteData, t]);
return (
@@ -191,7 +191,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
)}
- {(!quoteData || isPc) && (
+ {(!datasetCiteData || isPc) && (
{RenderHistoryList}
@@ -236,12 +236,12 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
)}
- {quoteData && (
+ {datasetCiteData && (
setQuoteData(undefined)}
+ rawSearch={datasetCiteData.rawSearch}
+ metadata={datasetCiteData.metadata}
+ onClose={() => setCiteModalData(undefined)}
/>
)}
diff --git a/projects/app/src/pages/dashboard/mcpServer/index.tsx b/projects/app/src/pages/dashboard/mcpServer/index.tsx
index eecacdc43..612cf948c 100644
--- a/projects/app/src/pages/dashboard/mcpServer/index.tsx
+++ b/projects/app/src/pages/dashboard/mcpServer/index.tsx
@@ -30,6 +30,7 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import dynamic from 'next/dynamic';
import { type McpKeyType } from '@fastgpt/global/support/mcp/type';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
+import { useUserStore } from '@/web/support/user/useUserStore';
const UsageWay = dynamic(() => import('@/pageComponents/dashboard/mcp/usageWay'), {
ssr: false
@@ -38,6 +39,7 @@ const UsageWay = dynamic(() => import('@/pageComponents/dashboard/mcp/usageWay')
const McpServer = () => {
const { t } = useTranslation();
const { isPc } = useSystem();
+ const { userInfo } = useUserStore();
const {
data: mcpServerList = [],
@@ -78,7 +80,10 @@ const McpServer = () => {
{t('dashboard_mcp:mcp_server_description')}
-
@@ -94,7 +99,10 @@ const McpServer = () => {
{t('dashboard_mcp:mcp_server_description')}
- setEditMcp(defaultForm)}>
+ setEditMcp(defaultForm)}
+ >
{t('dashboard_mcp:create_mcp_server')}
diff --git a/projects/app/src/service/core/app/utils.ts b/projects/app/src/service/core/app/utils.ts
index a808ece26..0377f96fe 100644
--- a/projects/app/src/service/core/app/utils.ts
+++ b/projects/app/src/service/core/app/utils.ts
@@ -29,10 +29,8 @@ import {
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
import { authAppByTmbId } from '@fastgpt/service/support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
-import {
- type PluginDataType,
- type StoreNodeItemType
-} from '@fastgpt/global/core/workflow/type/node';
+import { type StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
+import { getErrText } from '@fastgpt/global/common/error/utils';
export const getScheduleTriggerApp = async () => {
// 1. Find all the app
@@ -152,6 +150,7 @@ export const checkNode = async ({
try {
const { source } = await splitCombinePluginId(pluginId);
+
if (source === PluginSourceEnum.personal) {
await authAppByTmbId({
tmbId: ownerTmbId,
@@ -176,8 +175,8 @@ export const checkNode = async ({
return {
...node,
pluginData: {
- error
- } as PluginDataType
+ error: getErrText(error)
+ }
};
}
};
diff --git a/projects/app/src/web/core/chat/api.ts b/projects/app/src/web/core/chat/api.ts
index a7577f66e..80e2b9bbb 100644
--- a/projects/app/src/web/core/chat/api.ts
+++ b/projects/app/src/web/core/chat/api.ts
@@ -25,7 +25,7 @@ import type {
getPaginationRecordsBody,
getPaginationRecordsResponse
} from '@/pages/api/core/chat/getPaginationRecords';
-import type { GetQuoteDataProps, GetQuoteDataRes } from '@/pages/api/core/chat/quote/getQuote';
+import type { GetQuoteProps, GetQuotesRes } from '@/pages/api/core/chat/quote/getQuote';
import type {
GetCollectionQuoteProps,
GetCollectionQuoteRes
@@ -101,8 +101,8 @@ export const getMyTokensApps = (data: AuthTeamTagTokenProps) =>
export const getinitTeamChat = (data: { teamId: string; authToken: string; appId: string }) =>
GET(`/proApi/core/chat/initTeamChat`, data);
-export const getQuoteDataList = (data: GetQuoteDataProps) =>
- POST(`/core/chat/quote/getQuote`, data);
+export const getQuoteDataList = (data: GetQuoteProps) =>
+ POST(`/core/chat/quote/getQuote`, data);
export const getCollectionQuote = (data: GetCollectionQuoteProps) =>
POST(`/core/chat/quote/getCollectionQuote`, data);
diff --git a/projects/app/src/web/core/chat/context/chatItemContext.tsx b/projects/app/src/web/core/chat/context/chatItemContext.tsx
index 47cceb306..e3453d19d 100644
--- a/projects/app/src/web/core/chat/context/chatItemContext.tsx
+++ b/projects/app/src/web/core/chat/context/chatItemContext.tsx
@@ -34,13 +34,13 @@ type ChatBoxDataType = {
};
};
+// 知识库引用相关 type
export type GetQuoteDataBasicProps = {
appId: string;
chatId: string;
chatItemDataId: string;
outLinkAuthData?: OutLinkChatAuthProps;
};
-// 获取单个集合引用
export type GetCollectionQuoteDataProps = GetQuoteDataBasicProps & {
quoteId?: string;
collectionId: string;
@@ -54,11 +54,17 @@ export type GetAllQuoteDataProps = GetQuoteDataBasicProps & {
sourceName?: string;
};
export type GetQuoteProps = GetAllQuoteDataProps | GetCollectionQuoteDataProps;
-
export type QuoteDataType = {
rawSearch: SearchDataResponseItemType[];
metadata: GetQuoteProps;
};
+export type OnOpenCiteModalProps = {
+ collectionId?: string;
+ sourceId?: string;
+ sourceName?: string;
+ datasetId?: string;
+ quoteId?: string;
+};
type ChatItemContextType = {
ChatBoxRef: React.RefObject | null;
@@ -74,8 +80,8 @@ type ChatItemContextType = {
setChatBoxData: React.Dispatch>;
isPlugin: boolean;
- quoteData?: QuoteDataType;
- setQuoteData: React.Dispatch>;
+ datasetCiteData?: QuoteDataType;
+ setCiteModalData: React.Dispatch>;
isVariableVisible: boolean;
setIsVariableVisible: React.Dispatch>;
} & ContextProps;
@@ -98,8 +104,8 @@ export const ChatItemContext = createContext({
throw new Error('Function not implemented.');
},
- quoteData: undefined,
- setQuoteData: function (value: React.SetStateAction): void {
+ datasetCiteData: undefined,
+ setCiteModalData: function (value: React.SetStateAction): void {
throw new Error('Function not implemented.');
},
isVariableVisible: true,
@@ -124,7 +130,6 @@ const ChatItemContextProvider = ({
} & ContextProps) => {
const ChatBoxRef = useRef(null);
const variablesForm = useForm();
- const [quoteData, setQuoteData] = useState();
const [isVariableVisible, setIsVariableVisible] = useState(true);
const [chatBoxData, setChatBoxData] = useState({
@@ -162,6 +167,8 @@ const ChatItemContextProvider = ({
ChatBoxRef.current?.restartChat?.();
}, [variablesForm]);
+ const [datasetCiteData, setCiteModalData] = useState();
+
const contextValue = useMemo(() => {
return {
chatBoxData,
@@ -180,8 +187,8 @@ const ChatItemContextProvider = ({
// isShowFullText,
showNodeStatus,
- quoteData,
- setQuoteData,
+ datasetCiteData,
+ setCiteModalData,
isVariableVisible,
setIsVariableVisible
};
@@ -198,8 +205,8 @@ const ChatItemContextProvider = ({
isResponseDetail,
// isShowFullText,
showNodeStatus,
- quoteData,
- setQuoteData,
+ datasetCiteData,
+ setCiteModalData,
isVariableVisible,
setIsVariableVisible
]);
diff --git a/projects/app/src/web/core/dataset/api.ts b/projects/app/src/web/core/dataset/api.ts
index 9b1457b05..2959d1d91 100644
--- a/projects/app/src/web/core/dataset/api.ts
+++ b/projects/app/src/web/core/dataset/api.ts
@@ -72,7 +72,7 @@ import type {
getTrainingErrorResponse
} from '@/pages/api/core/dataset/training/getTrainingError';
import type { APIFileItem } from '@fastgpt/global/core/dataset/apiDataset';
-import type { GetQuoteDataProps } from '@/pages/api/core/chat/quote/getQuote';
+import type { GetQuoteDataProps } from '@/pages/api/core/dataset/data/getQuoteData';
import type {
GetApiDatasetCataLogResponse,
GetApiDatasetCataLogProps
diff --git a/test/cases/function/packages/service/core/ai/parseStreamResponse.test.ts b/test/cases/function/packages/service/core/ai/parseStreamResponse.test.ts
index 1e1268d5a..2599025c5 100644
--- a/test/cases/function/packages/service/core/ai/parseStreamResponse.test.ts
+++ b/test/cases/function/packages/service/core/ai/parseStreamResponse.test.ts
@@ -384,6 +384,22 @@ describe('Parse dataset cite content test', async () => {
content: '知识库问答系统[67e517e7476861](CITE)[67e517e74767063e882d6861](CITE)',
responseContent: '知识库问答系统[67e517e7476861](CITE)'
}
+ },
+ {
+ // [id](CITE)
+ data: [
+ { content: '知识库' },
+ { content: '问答系统' },
+ { content: '[i' },
+ { content: 'd](CITE)' },
+ { content: '[67e517e747' },
+ { content: '67063e882d' },
+ { content: '6861](CITE)' }
+ ],
+ correct: {
+ content: '知识库问答系统[id](CITE)[67e517e74767063e882d6861](CITE)',
+ responseContent: '知识库问答系统'
+ }
}
];