diff --git a/README.md b/README.md index 8c8db1f9a..fc59db621 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b 1. 强大的可视化编排,轻松构建 AI 应用 - [x] 提供简易模式,无需操作编排 - - [x] 用户对话前引导, 全局字符串变量 + - [x] 用户对话前引导,全局字符串变量 - [x] 知识库搜索 - [x] 多 LLM 模型对话 - [x] 文本内容提取成结构化数据 @@ -56,12 +56,12 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b - [x] 对话下一步指引 - [ ] 对话多路线选择 - [x] 源文件引用追踪 - - [ ] 自定义文件阅读器 + - [x] 模块封装,实现多级复用 2. 丰富的知识库预处理 - [x] 多库复用,混用 - [x] chunk 记录修改和删除 - - [x] 支持 手动输入, 直接分段, QA 拆分导入 - - [x] 支持 url 读取、 CSV 批量导入 + - [x] 支持手动输入,直接分段,QA 拆分导入 + - [x] 支持 url 读取、CSV 批量导入 - [x] 支持知识库单独设置向量模型 - [x] 源文件存储 - [ ] 文件学习 Agent @@ -71,7 +71,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b - [x] 完整上下文呈现 - [x] 完整模块中间值呈现 4. OpenAPI - - [x] completions 接口(对齐 GPT 接口) + - [x] completions 接口 (对齐 GPT 接口) - [ ] 知识库 CRUD 5. 运营功能 - [x] 免登录分享窗口 @@ -80,7 +80,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b ## 👨‍💻 开发 -项目技术栈: NextJs + TS + ChakraUI + Mongo + Postgres(Vector 插件) +项目技术栈:NextJs + TS + ChakraUI + Mongo + Postgres (Vector 插件) - **⚡ 快速部署** @@ -95,7 +95,8 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b * [系统配置文件说明](https://doc.fastgpt.in/docs/development/configuration/) * [多模型配置](https://doc.fastgpt.in/docs/installation/one-api/) * [版本更新/升级介绍](https://doc.fastgpt.in/docs/installation/upgrading) -* [API 文档](https://doc.fastgpt.in/docs/development/openapi/) +* [OpenAPI API 文档](https://doc.fastgpt.in/docs/development/openapi/) +* [知识库结构详解](https://doc.fastgpt.in/docs/use-cases/datasetengine/) ## 🏘️ 社区交流群 @@ -105,10 +106,10 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b ## 💪 相关项目 -- [Laf: 3 分钟快速接入三方应用](https://github.com/labring/laf) -- [Sealos: 快速部署集群应用](https://github.com/labring/sealos) -- [One API: 多模型管理,支持 Azure、文心一言等](https://github.com/songquanpeng/one-api) -- [TuShan: 5 分钟搭建后台管理系统](https://github.com/msgbyte/tushan) +- [Laf:3 分钟快速接入三方应用](https://github.com/labring/laf) +- [Sealos:快速部署集群应用](https://github.com/labring/sealos) +- [One API:多模型管理,支持 Azure、文心一言等](https://github.com/songquanpeng/one-api) +- [TuShan:5 分钟搭建后台管理系统](https://github.com/msgbyte/tushan) ## 👀 其他 @@ -129,6 +130,6 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b 本仓库遵循 [FastGPT Open Source License](./LICENSE) 开源协议。 1. 允许作为后台服务直接商用,但不允许提供 SaaS 服务。 -2. 需保留相关版权信息。 +2. 未经商业授权,任何形式的商用服务均需保留相关版权信息。 3. 完整请查看 [FastGPT Open Source License](./LICENSE) -4. 联系方式:yujinlong@sealos.io, [点击查看定价策略](https://doc.fastgpt.run/docs/commercial) +4. 联系方式:yujinlong@sealos.io,[点击查看商业版定价策略](https://doc.fastgpt.run/docs/commercial) diff --git a/README_en.md b/README_en.md index 5a2ac71e5..037474c75 100644 --- a/README_en.md +++ b/README_en.md @@ -54,9 +54,9 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b - [x] Extend with HTTP - [ ] Embed Laf for on-the-fly HTTP module crafting - [x] Directions for the next dialogue steps - - [ ] Multiple dialogue paths selection - [x] Tracking source file references - [ ] Custom file reader + - [ ] Modules are packaged into plug-ins to achieve reuse 2. Extensive knowledge base preprocessing diff --git a/docSite/.zhlintignore b/docSite/.zhlintignore new file mode 100644 index 000000000..0b84df0f0 --- /dev/null +++ b/docSite/.zhlintignore @@ -0,0 +1 @@ +*.html \ No newline at end of file diff --git a/docSite/.zhlintrc b/docSite/.zhlintrc new file mode 100644 index 000000000..6a01ab669 --- /dev/null +++ b/docSite/.zhlintrc @@ -0,0 +1,6 @@ +{ + "preset": "default", + "rules": { + "adjustedFullWidthPunctuation": "" + } +} \ No newline at end of file diff --git a/docSite/README.md b/docSite/README.md index a3b8ba755..ea77b3e9e 100644 --- a/docSite/README.md +++ b/docSite/README.md @@ -3,7 +3,7 @@ ## 本地运行 1. 安装 go 语言环境。 -2. 安装 hugo。 [二进制下载](https://github.com/gohugoio/hugo/releases/tag/v0.117.0),注意需要安装 extended 版本。 +2. 安装 hugo。[二进制下载](https://github.com/gohugoio/hugo/releases/tag/v0.117.0),注意需要安装 extended 版本。 3. cd docSite 4. hugo serve 5. 访问 http://localhost:1313 diff --git a/docSite/assets/imgs/datasetEngine1.png b/docSite/assets/imgs/datasetEngine1.png new file mode 100644 index 000000000..7ec5c267d Binary files /dev/null and b/docSite/assets/imgs/datasetEngine1.png differ diff --git a/docSite/assets/imgs/datasetEngine10.png b/docSite/assets/imgs/datasetEngine10.png new file mode 100644 index 000000000..0fb165fa9 Binary files /dev/null and b/docSite/assets/imgs/datasetEngine10.png differ diff --git a/docSite/assets/imgs/datasetEngine11.png b/docSite/assets/imgs/datasetEngine11.png new file mode 100644 index 000000000..ba5acc88b Binary files /dev/null and b/docSite/assets/imgs/datasetEngine11.png differ diff --git a/docSite/assets/imgs/datasetEngine2.png b/docSite/assets/imgs/datasetEngine2.png new file mode 100644 index 000000000..7d07b3890 Binary files /dev/null and b/docSite/assets/imgs/datasetEngine2.png differ diff --git a/docSite/assets/imgs/datasetEngine3.png b/docSite/assets/imgs/datasetEngine3.png new file mode 100644 index 000000000..8baaae023 Binary files /dev/null and b/docSite/assets/imgs/datasetEngine3.png differ diff --git a/docSite/assets/imgs/datasetEngine4.png b/docSite/assets/imgs/datasetEngine4.png new file mode 100644 index 000000000..59c54c2f5 Binary files /dev/null and b/docSite/assets/imgs/datasetEngine4.png differ diff --git a/docSite/assets/imgs/datasetEngine5.png b/docSite/assets/imgs/datasetEngine5.png new file mode 100644 index 000000000..cc841ffcf Binary files /dev/null and b/docSite/assets/imgs/datasetEngine5.png differ diff --git a/docSite/assets/imgs/datasetEngine6.png b/docSite/assets/imgs/datasetEngine6.png new file mode 100644 index 000000000..53ebc8fe6 Binary files /dev/null and b/docSite/assets/imgs/datasetEngine6.png differ diff --git a/docSite/assets/imgs/datasetEngine7.png b/docSite/assets/imgs/datasetEngine7.png new file mode 100644 index 000000000..5754bbd9a Binary files /dev/null and b/docSite/assets/imgs/datasetEngine7.png differ diff --git a/docSite/assets/imgs/datasetEngine8.png b/docSite/assets/imgs/datasetEngine8.png new file mode 100644 index 000000000..678ac8705 Binary files /dev/null and b/docSite/assets/imgs/datasetEngine8.png differ diff --git a/docSite/assets/imgs/datasetEngine9.png b/docSite/assets/imgs/datasetEngine9.png new file mode 100644 index 000000000..ff7d3ae25 Binary files /dev/null and b/docSite/assets/imgs/datasetEngine9.png differ diff --git a/docSite/assets/imgs/datasetprompt1.png b/docSite/assets/imgs/datasetprompt1.png new file mode 100644 index 000000000..34fa65c12 Binary files /dev/null and b/docSite/assets/imgs/datasetprompt1.png differ diff --git a/docSite/assets/imgs/datasetprompt2.png b/docSite/assets/imgs/datasetprompt2.png new file mode 100644 index 000000000..7c27eb7ae Binary files /dev/null and b/docSite/assets/imgs/datasetprompt2.png differ diff --git a/docSite/assets/imgs/datasetprompt3.png b/docSite/assets/imgs/datasetprompt3.png new file mode 100644 index 000000000..60afb2c8d Binary files /dev/null and b/docSite/assets/imgs/datasetprompt3.png differ diff --git a/docSite/assets/imgs/datasetprompt4.png b/docSite/assets/imgs/datasetprompt4.png new file mode 100644 index 000000000..d44d60708 Binary files /dev/null and b/docSite/assets/imgs/datasetprompt4.png differ diff --git a/docSite/assets/imgs/datasetprompt5.png b/docSite/assets/imgs/datasetprompt5.png new file mode 100644 index 000000000..4ae712533 Binary files /dev/null and b/docSite/assets/imgs/datasetprompt5.png differ diff --git a/docSite/assets/imgs/datasetprompt6.png b/docSite/assets/imgs/datasetprompt6.png new file mode 100644 index 000000000..80d98e2aa Binary files /dev/null and b/docSite/assets/imgs/datasetprompt6.png differ diff --git a/docSite/assets/imgs/datasetprompt7.png b/docSite/assets/imgs/datasetprompt7.png new file mode 100644 index 000000000..8419580b1 Binary files /dev/null and b/docSite/assets/imgs/datasetprompt7.png differ diff --git a/docSite/assets/imgs/datasetprompt8.png b/docSite/assets/imgs/datasetprompt8.png new file mode 100644 index 000000000..bf8e0ea9d Binary files /dev/null and b/docSite/assets/imgs/datasetprompt8.png differ diff --git a/docSite/assets/imgs/datasetprompt9.png b/docSite/assets/imgs/datasetprompt9.png new file mode 100644 index 000000000..510d6b6f6 Binary files /dev/null and b/docSite/assets/imgs/datasetprompt9.png differ diff --git a/docSite/content/docs/installation/upgrading/451.md b/docSite/content/docs/installation/upgrading/451.md index 0ee7bb40f..29b448617 100644 --- a/docSite/content/docs/installation/upgrading/451.md +++ b/docSite/content/docs/installation/upgrading/451.md @@ -4,7 +4,7 @@ description: 'FastGPT V4.5.1 更新' icon: 'upgrade' draft: false toc: true -weight: 839 +weight: 838 --- ## 执行初始化 API diff --git a/docSite/content/docs/installation/upgrading/452.md b/docSite/content/docs/installation/upgrading/452.md new file mode 100644 index 000000000..5f2527c24 --- /dev/null +++ b/docSite/content/docs/installation/upgrading/452.md @@ -0,0 +1,15 @@ +--- +title: 'V4.5.2' +description: 'FastGPT V4.5.2 更新' +icon: 'upgrade' +draft: false +toc: true +weight: 837 +--- + +## 功能介绍 + +### Fast GPT V4.5.2 + +1. 新增 - 模块插件,允许自行组装插件进行模块复用。 +2. 优化 - 知识库引用提示。 \ No newline at end of file diff --git a/docSite/content/docs/use-cases/ai_settings.md b/docSite/content/docs/use-cases/ai_settings.md index e93039d10..0d96d1ce2 100644 --- a/docSite/content/docs/use-cases/ai_settings.md +++ b/docSite/content/docs/use-cases/ai_settings.md @@ -39,16 +39,18 @@ weight: 310 ``` {{% alert icon="🍅" context="success" %}} -Tips: 可以通过点击上下文按键查看完整的 +Tips: 可以通过点击上下文按键查看完整的上下文组成,便于调试。 {{% /alert %}} ## 引用模板和提示词设计 引用模板和引用提示词通常是成对出现,引用提示词依赖引用模板。 -FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变量)的格式存储,在转义成字符串时候会根据**引用模板**来进行格式化。知识库包含 3 个变量: q, a, file_id, index, source,可以通过 {{q}} {{a}} {{file_id}} {{index}} {{source}} 按需引入。下面一个模板例子: +FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变量)的格式存储,在转义成字符串时候会根据**引用模板**来进行格式化。知识库包含多个可用变量: q, a, sourceId(数据的ID), index(第n个数据), source(数据的集合名、文件名),score(距离得分,0-1) 可以通过 {{q}} {{a}} {{sourceId}} {{index}} {{source}} {{score}} 按需引入。下面一个模板例子: -**引用模板** +可以通过 [知识库结构讲解](/docs/use-cases/datasetEngine/) 了解详细的知识库的结构。 + +### 引用模板 ``` {instruction:"{{q}}",output:"{{a}}",source:"{{source}}"} @@ -62,7 +64,7 @@ FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变 {instruction:"电影《铃芽之旅》的编剧是谁?22",output:"新海诚是本片的编剧。",source:"手动输入"} ``` -**引用提示词** +### 引用提示词 引用模板需要和引用提示词一起使用,提示词中可以写引用模板的格式说明以及对话的要求等。可以使用 {{quote}} 来使用 **引用模板**,使用 {{question}} 来引入问题。例如: @@ -91,4 +93,42 @@ FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变 2. 使用背景知识回答问题。 3. 背景知识无法回答问题时,你可以礼貌的的回答用户问题。 我的问题是:"{{question}}" -``` \ No newline at end of file +``` + +### 总结 + +引用模板规定了搜索出来的内容如何组成一句话,其由 q,a,index,source 多个变量组成。 + +引用提示词由`引用模板`和`提示词`组成,提示词通常是对引用模板的一个描述,加上对模型的要求。 + +## 引用模板和提示词设计 示例 + +### 通用模板与问答模板对比 + +我们通过一组`你是谁`的手动数据,对通用模板与问答模板的效果进行对比。此处特意打了个搞笑的答案,通用模板下 GPT35 就变得不那么听话了,而问答模板下 GPT35 依然能够回答正确。这是由于结构化的提示词,在大语言模型中具有更强的引导作用。 + +{{% alert icon="🍅" context="success" %}} +Tips: 建议根据不同的场景,每种知识库仅选择1类数据类型,这样有利于充分发挥提示词的作用。 +{{% /alert %}} + +| 通用模板配置及效果 | 问答模板配置及效果 | +| --- | --- | +| ![](/imgs/datasetprompt1.png) | ![](/imgs/datasetprompt2.png) | +| ![](/imgs/datasetprompt3.png) | ![](/imgs/datasetprompt5.png) | +| ![](/imgs/datasetprompt4.png) | ![](/imgs/datasetprompt6.png) | + +### 严格模板 + +使用非严格模板,我们随便询问一个不在知识库中的内容,模型通常会根据其自身知识进行回答。 + +| 非严格模板效果 | 选择严格模板 | 严格模板效果 | +| --- | --- | --- | +| ![](/imgs/datasetprompt7.png) | ![](/imgs/datasetprompt8.png) |![](/imgs/datasetprompt9.png) | + +### 提示词设计思路 + +1. 使用序号进行不同要求描述。 +2. 使用首先、然后、最后等词语进行描述。 +3. 列举不同场景的要求时,尽量完整,不要遗漏。例如:背景知识完全可以回答、背景知识可以回答一部分、背景知识与问题无关,3种场景都说明清楚。 +4. 巧用结构化提示,例如在问答模板中,利用了`instruction`和`output`,清楚的告诉模型,`output`是一个预期的答案。 +5. 标点符号正确且完整。 diff --git a/docSite/content/docs/use-cases/datasetEngine.md b/docSite/content/docs/use-cases/datasetEngine.md new file mode 100644 index 000000000..5e643a42b --- /dev/null +++ b/docSite/content/docs/use-cases/datasetEngine.md @@ -0,0 +1,83 @@ +--- +title: "知识库结构讲解" +description: "本节会介绍 FastGPT 知识库结构设计,理解其 QA 的存储格式和检索格式,以便更好的构建知识库。这篇介绍主要以使用为主,详细原理不多介绍。" +icon: "dataset" +draft: false +toc: true +weight: 311 +--- + +# 理解向量 + +FastGPT 采用了 RAG 中的 Embedding 方案构建知识库,要使用好 FastGPT 需要简单的理解`Embedding`向量是如何工作的及其特点。 + +人类的文字、图片、视频等媒介是无法直接被计算机理解的,要想让计算机理解两段文字是否有相似性、相关性,通常需要将它们转成计算机可以理解的语言,向量是其中的一种方式。 + +向量可以简单理解为一个数字数组,两个向量之间可以通过数学公式得出一个`距离`,距离越小代表两个向量的相似度越大。从而映射到文字、图片、视频等媒介上,可以用来判断两个媒介之间的相似度。向量搜索便是利用了这个原理。 + +而由于文字是有多种类型,并且拥有成千上万种组合方式,因此在转成向量进行相似度匹配时,很难保障其精确性。在向量方案构建的知识库中,通常使用`topk`召回的方式,也就是查找前`k`个最相似的内容,丢给大模型去做更进一步的`语义判断`、`逻辑推理`和`归纳总结`,从而实现知识库问答。因此,在知识库问答中,向量搜索的环节是最为重要的。 + +影响向量搜索精度的因素非常多,主要包括:向量模型的质量、数据的质量(长度,完整性,多样性)、检索器的精度(速度与精度之间的取舍)。与数据质量对应的就是检索词的质量。 + +检索器的精度比较容易解决,向量模型的训练略复杂,因此数据和检索词质量优化成了一个重要的环节。 + +# FastGPT 中向量的结构设计 + +FastGPT 采用了 `PostgresSQL` 的 `PG Vector` 插件作为向量检索器,索引为`HNSW`。且`PostgresSQL`仅用于向量检索,`MongoDB`用于其他数据的存取。 + +在`PostgresSQL`的表中,设置一个 `index` 字段用于存储向量、一个 `q` 字段用于存储向量对应的内容,以及一个 `a` 字段用于检索映射。之所以取字段为 `qa` 是由于一些历史缘故,无需完全解为 “问答对” 的格式。在实际使用过程中,可以利用`q`和`a`的组合,对检索后的内容做进一步的声明,提高大模型的理解力(注意,这里不直接提高搜索精度)。 + +目前,提高向量搜索的精度,主要可以通过几种途径: + +1. 精简`q`的内容,减少向量内容的长度:当`q`的内容更少,更准确时,检索精度自然会提高。但与此同时,会牺牲一定的检索范围,适合答案较为严格的场景。 +2. 更好分词分段:当一段话的结构和语义是完整的,并且是单一的,精度也会提高。因此,许多系统都会优化分词器,尽可能的保障每组数据的完整性。 +3. 多样性文本:为一段内容增加关键词、摘要、相似问题等描述性信息,可以使得该内容的向量具有更大的检索覆盖范围。 +4. 优化检索词:在实际使用过程中,用户的问题通常是模糊的或是缺失的,并不一定是完整清晰的问题。因此优化用户的问题(检索词)很大程度上也可以提高精度。 +5. 微调向量模型:由于市面上直接使用的向量模型都是通用型模型,在特定领域的检索精度并不高,因此微调向量模型可以很大程度上提高专业领域的检索效果。 + +# FastGPT 构建知识库方案 + +在 FastGPT 中,整个知识库由库、集合和数据 3 部分组成。集合可以简单理解为一个`文件`。一个`库`中可以包含多个`集合`,一个`集合`中可以包含多组`数据`。最小的搜索单位是`库`,也就是说,知识库搜索时,是对整个`库`进行搜索,而集合仅是为了对数据进行分类管理,与搜索效果无关。(起码目前还是) + +| 库 | 集合 | 数据 | +| --- | --- | --- | +| ![](/imgs/datasetEngine1.png) | ![](/imgs/datasetEngine2.png) | ![](/imgs/datasetEngine3.png) | + +## 导入数据方案1 - 直接分段导入 + +选择文件导入时,可以选择直接分段方案。直接分段会利用`句子分词器`对文本进行一定长度拆分,最终分割中多组的`q`。如果使用了直接分段方案,我们建议在`应用`设置`引用提示词`时,使用`通用模板`即可,无需选择`问答模板`。 + +| 交互 | 结果 | +| --- | --- | +| ![](/imgs/datasetEngine4.png) | ![](/imgs/datasetEngine5.png) | + + +## 导入数据方案2 - QA导入 + +选择文件导入时,可以选择QA拆分方案。仍然需要使用到`句子分词器`对文本进行拆分,但长度比直接分段大很多。在导入后,会先调用`大模型`对分段进行学习,并给出一些`问题`和`答案`,最终问题和答案会一起被存储到`q`中。注意,新版的 FastGPT 为了提高搜索的范围,不再将问题和答案分别存储到 qa 中。 + +| 交互 | 结果 | +| --- | --- | +| ![](/imgs/datasetEngine6.png) | ![](/imgs/datasetEngine7.png) | + +## 导入数据方案3 - 手动录入 + +在 FastGPT 中,你可以在任何一个`集合`中点击右上角的`插入`手动录入知识点,或者使用`标注`功能手动录入。被搜索的内容为`q`,补充内容(可选)为`a`。 + +| | | | +| --- | --- | --- | +| ![](/imgs/datasetEngine8.png) | ![](/imgs/datasetEngine9.png) | ![](/imgs/datasetEngine10.png) | + +## 导入数据方案4 - CSV录入 + +有些数据较为独特,可能需要单独的进行预处理分割后再导入 FastGPT,此时可以选择 csv 导入,可批量的将处理好的数据导入。 + +![](/imgs/datasetEngine11.png) + +## 导入数据方案5 - API导入 + +参考[FastGPT OpenAPI使用](/docs/development/openapi/#知识库添加数据)。 + +# QA的组合与引用提示词构建 + +参考[引用模板与引用提示词示例](/docs/use-cases/ai_settings/#示例) diff --git a/docSite/content/docs/use-cases/openapi.md b/docSite/content/docs/use-cases/openapi.md index cb84cb90e..af22b5e95 100644 --- a/docSite/content/docs/use-cases/openapi.md +++ b/docSite/content/docs/use-cases/openapi.md @@ -4,7 +4,7 @@ description: "通过与 OpenAI 兼容的 API 对接第三方应用" icon: "model_training" draft: false toc: true -weight: 311 +weight: 312 --- ## 获取 API 秘钥 diff --git a/docSite/content/docs/workflow/examples/fixingEvidence.md b/docSite/content/docs/workflow/examples/fixingEvidence.md index ec84afa8e..a489ad88c 100644 --- a/docSite/content/docs/workflow/examples/fixingEvidence.md +++ b/docSite/content/docs/workflow/examples/fixingEvidence.md @@ -86,7 +86,7 @@ weight: 142 { "key": "history", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "type": "source", "targets": [ { @@ -210,14 +210,14 @@ weight: 142 "type": "target", "label": "引用内容", "description": "对象数组格式,结构:\n [{q:'问题',a:'回答'}]", - "valueType": "kb_quote", + "valueType": "datasetQuote", "connected": false }, { "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { diff --git a/docSite/content/docs/workflow/examples/google_search.md b/docSite/content/docs/workflow/examples/google_search.md index af896b595..7b89a884d 100644 --- a/docSite/content/docs/workflow/examples/google_search.md +++ b/docSite/content/docs/workflow/examples/google_search.md @@ -132,7 +132,7 @@ export default async function (ctx: FunctionContext) { { "key": "history", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "type": "source", "targets": [ { @@ -179,7 +179,7 @@ export default async function (ctx: FunctionContext) { "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { @@ -410,14 +410,14 @@ export default async function (ctx: FunctionContext) { "key": "quoteQA", "type": "target", "label": "引用内容", - "valueType": "kb_quote", + "valueType": "datasetQuote", "connected": false }, { "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { diff --git a/docSite/content/docs/workflow/examples/lab_appointment.md b/docSite/content/docs/workflow/examples/lab_appointment.md index 46afdea9b..41d3cacca 100644 --- a/docSite/content/docs/workflow/examples/lab_appointment.md +++ b/docSite/content/docs/workflow/examples/lab_appointment.md @@ -131,7 +131,7 @@ HTTP 模块允许你调用任意 POST 类型的 HTTP 接口,从而实验一些 { "key": "history", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "type": "source", "targets": [ { @@ -174,7 +174,7 @@ HTTP 模块允许你调用任意 POST 类型的 HTTP 接口,从而实验一些 "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { @@ -413,7 +413,7 @@ HTTP 模块允许你调用任意 POST 类型的 HTTP 接口,从而实验一些 "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { @@ -630,7 +630,7 @@ HTTP 模块允许你调用任意 POST 类型的 HTTP 接口,从而实验一些 "label": "引用内容", "description": "始终返回数组,如果希望搜索结果为空时执行额外操作,需要用到上面的两个输入以及目标模块的触发器", "type": "source", - "valueType": "kb_quote", + "valueType": "datasetQuote", "targets": [ { "moduleId": "bjfklc", @@ -729,14 +729,14 @@ HTTP 模块允许你调用任意 POST 类型的 HTTP 接口,从而实验一些 "key": "quoteQA", "type": "target", "label": "引用内容", - "valueType": "kb_quote", + "valueType": "datasetQuote", "connected": true }, { "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { @@ -831,7 +831,7 @@ HTTP 模块允许你调用任意 POST 类型的 HTTP 接口,从而实验一些 { "key": "history", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "type": "source", "targets": [ { @@ -874,7 +874,7 @@ HTTP 模块允许你调用任意 POST 类型的 HTTP 接口,从而实验一些 "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { @@ -1006,7 +1006,7 @@ HTTP 模块允许你调用任意 POST 类型的 HTTP 接口,从而实验一些 { "key": "history", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "type": "source", "targets": [ { diff --git a/docSite/content/docs/workflow/examples/op_question.md b/docSite/content/docs/workflow/examples/op_question.md index fa070b2f1..339d855db 100644 --- a/docSite/content/docs/workflow/examples/op_question.md +++ b/docSite/content/docs/workflow/examples/op_question.md @@ -83,7 +83,7 @@ weight: 144 { "key": "history", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "type": "source", "targets": [ { @@ -189,7 +189,7 @@ weight: 144 "label": "引用内容", "description": "始终返回数组,如果希望搜索结果为空时执行额外操作,需要用到上面的两个输入以及目标模块的触发器", "type": "source", - "valueType": "kb_quote", + "valueType": "datasetQuote", "targets": [ { "moduleId": "ol82hp", @@ -291,14 +291,14 @@ weight: 144 "type": "target", "label": "引用内容", "description": "对象数组格式,结构:\n [{q:'问题',a:'回答'}]", - "valueType": "kb_quote", + "valueType": "datasetQuote", "connected": true }, { "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { @@ -389,7 +389,7 @@ weight: 144 { "key": "history", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "type": "source", "targets": [ { @@ -432,7 +432,7 @@ weight: 144 "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { diff --git a/docSite/content/docs/workflow/examples/versatile_assistant.md b/docSite/content/docs/workflow/examples/versatile_assistant.md index 89a237465..24db0e34b 100644 --- a/docSite/content/docs/workflow/examples/versatile_assistant.md +++ b/docSite/content/docs/workflow/examples/versatile_assistant.md @@ -245,7 +245,7 @@ PS2:配置中的问题分类还包含着“联网搜索”,这个是另一 { "key": "history", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "type": "source", "targets": [ { @@ -300,7 +300,7 @@ PS2:配置中的问题分类还包含着“联网搜索”,这个是另一 "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { @@ -427,7 +427,7 @@ PS2:配置中的问题分类还包含着“联网搜索”,这个是另一 "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { @@ -713,14 +713,14 @@ PS2:配置中的问题分类还包含着“联网搜索”,这个是另一 "type": "custom", "label": "引用内容", "description": "对象数组格式,结构:\n [{q:'问题',a:'回答'}]", - "valueType": "kb_quote", + "valueType": "datasetQuote", "connected": false }, { "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { @@ -871,14 +871,14 @@ PS2:配置中的问题分类还包含着“联网搜索”,这个是另一 "type": "custom", "label": "引用内容", "description": "对象数组格式,结构:\n [{q:'问题',a:'回答'}]", - "valueType": "kb_quote", + "valueType": "datasetQuote", "connected": false }, { "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { @@ -1085,14 +1085,14 @@ PS2:配置中的问题分类还包含着“联网搜索”,这个是另一 "type": "custom", "label": "引用内容", "description": "对象数组格式,结构:\n [{q:'问题',a:'回答'}]", - "valueType": "kb_quote", + "valueType": "datasetQuote", "connected": false }, { "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { @@ -1162,7 +1162,7 @@ PS2:配置中的问题分类还包含着“联网搜索”,这个是另一 { "key": "history", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "type": "source", "targets": [ { @@ -1205,7 +1205,7 @@ PS2:配置中的问题分类还包含着“联网搜索”,这个是另一 "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { @@ -1452,14 +1452,14 @@ PS2:配置中的问题分类还包含着“联网搜索”,这个是另一 "type": "custom", "label": "引用内容", "description": "对象数组格式,结构:\n [{q:'问题',a:'回答'}]", - "valueType": "kb_quote", + "valueType": "datasetQuote", "connected": false }, { "key": "history", "type": "target", "label": "聊天记录", - "valueType": "chat_history", + "valueType": "chatHistory", "connected": true }, { diff --git a/package.json b/package.json index e51697579..93eb85359 100644 --- a/package.json +++ b/package.json @@ -4,18 +4,20 @@ "private": true, "scripts": { "prepare": "husky install", - "format": "prettier --config \"./.prettierrc.js\" --write \"./**/src/**/*.{ts,tsx,scss}\"" + "format-code": "prettier --config \"./.prettierrc.js\" --write \"./**/src/**/*.{ts,tsx,scss}\"", + "format-doc": "zhlint --dir ./docSite *.md --fix" }, "devDependencies": { "husky": "^8.0.3", "lint-staged": "^13.2.1", "prettier": "^3.0.3", - "i18next": "^23.2.11", - "react-i18next": "^13.0.2", - "next-i18next": "^14.0.0" + "i18next": "^22.5.1", + "react-i18next": "^12.3.1", + "next-i18next": "^13.3.0" }, "lint-staged": { - "./**/**/*.{ts,tsx,scss}": "npm run format" + "./**/**/*.{ts,tsx,scss}": "npm run format-code", + "./**/**/*.md": "npm run format-doc" }, "engines": { "node": ">=18.0.0" diff --git a/packages/global/common/file/icon.ts b/packages/global/common/file/icon.ts index 04b04bded..aeee3cca1 100644 --- a/packages/global/common/file/icon.ts +++ b/packages/global/common/file/icon.ts @@ -1,16 +1,12 @@ -import { strIsLink } from '../string/tools'; - export const fileImgs = [ { suffix: 'pdf', src: '/imgs/files/pdf.svg' }, { suffix: 'csv', src: '/imgs/files/csv.svg' }, { suffix: '(doc|docs)', src: '/imgs/files/doc.svg' }, { suffix: 'txt', src: '/imgs/files/txt.svg' }, - { suffix: 'md', src: '/imgs/files/markdown.svg' }, - { suffix: '.', src: '/imgs/files/file.svg' } + { suffix: 'md', src: '/imgs/files/markdown.svg' } + // { suffix: '.', src: '/imgs/files/file.svg' } ]; -export function getFileIcon(name = '') { - return ( - fileImgs.find((item) => new RegExp(item.suffix, 'gi').test(name))?.src || '/imgs/files/file.svg' - ); +export function getFileIcon(name = '', defaultImg = '/imgs/files/file.svg') { + return fileImgs.find((item) => new RegExp(item.suffix, 'gi').test(name))?.src || defaultImg; } diff --git a/packages/global/core/dataset/utils.ts b/packages/global/core/dataset/utils.ts index c38354ad9..17ce0034d 100644 --- a/packages/global/core/dataset/utils.ts +++ b/packages/global/core/dataset/utils.ts @@ -1,5 +1,6 @@ import { DatasetCollectionTypeEnum } from './constant'; import { getFileIcon } from '../../common/file/icon'; +import { strIsLink } from '../../common/string/tools'; export function getCollectionIcon( type: `${DatasetCollectionTypeEnum}` = DatasetCollectionTypeEnum.file, @@ -7,9 +8,11 @@ export function getCollectionIcon( ) { if (type === DatasetCollectionTypeEnum.folder) { return '/imgs/files/folder.svg'; - } else if (type === DatasetCollectionTypeEnum.link) { + } + if (type === DatasetCollectionTypeEnum.link) { return '/imgs/files/link.svg'; - } else if (type === DatasetCollectionTypeEnum.virtual) { + } + if (type === DatasetCollectionTypeEnum.virtual) { if (name === '手动录入') { return '/imgs/files/manual.svg'; } else if (name === '手动标注') { @@ -19,3 +22,25 @@ export function getCollectionIcon( } return getFileIcon(name); } +export function getSourceNameIcon({ + sourceName, + sourceId +}: { + sourceName: string; + sourceId?: string; +}) { + if (strIsLink(sourceId)) { + return '/imgs/files/link.svg'; + } + const fileIcon = getFileIcon(sourceName, ''); + if (fileIcon) { + return fileIcon; + } + + if (sourceName === '手动录入') { + return '/imgs/files/manual.svg'; + } else if (sourceName === '手动标注') { + return '/imgs/files/mark.svg'; + } + return '/imgs/files/collection.svg'; +} diff --git a/packages/global/core/module/node/constant.ts b/packages/global/core/module/node/constant.ts new file mode 100644 index 000000000..c0084479c --- /dev/null +++ b/packages/global/core/module/node/constant.ts @@ -0,0 +1,59 @@ +export enum FlowNodeInputTypeEnum { + systemInput = 'systemInput', // history, userChatInput, variableInput + input = 'input', // one line input + textarea = 'textarea', + numberInput = 'numberInput', + select = 'select', + slider = 'slider', + custom = 'custom', + target = 'target', // data input + switch = 'switch', + chatInput = 'chatInput', + selectApp = 'selectApp', + // chat special input + aiSettings = 'aiSettings', + maxToken = 'maxToken', + selectChatModel = 'selectChatModel', + // dataset special input + selectDataset = 'selectDataset', + hidden = 'hidden' +} + +export enum FlowNodeOutputTypeEnum { + answer = 'answer', + source = 'source', + hidden = 'hidden' +} + +export enum FlowNodeTypeEnum { + empty = 'empty', + variable = 'variable', + userGuide = 'userGuide', + questionInput = 'questionInput', + historyNode = 'historyNode', + chatNode = 'chatNode', + datasetSearchNode = 'datasetSearchNode', + answerNode = 'answerNode', + classifyQuestion = 'classifyQuestion', + contentExtract = 'contentExtract', + httpRequest = 'httpRequest', + runApp = 'app', + pluginModule = 'pluginModule', + pluginInput = 'pluginInput', + pluginOutput = 'pluginOutput' +} + +export enum FlowNodeSpecialInputKeyEnum { + 'answerText' = 'text', + 'agents' = 'agents', // cq agent key + 'pluginId' = 'pluginId' +} + +export enum FlowNodeValTypeEnum { + 'string' = 'string', + 'number' = 'number', + 'boolean' = 'boolean', + 'chatHistory' = 'chatHistory', + 'datasetQuote' = 'datasetQuote', + 'any' = 'any' +} diff --git a/packages/global/core/module/node/type.d.ts b/packages/global/core/module/node/type.d.ts new file mode 100644 index 000000000..999f1c3d3 --- /dev/null +++ b/packages/global/core/module/node/type.d.ts @@ -0,0 +1,57 @@ +import { + FlowNodeInputTypeEnum, + FlowNodeOutputTypeEnum, + FlowNodeValTypeEnum, + FlowNodeTypeEnum +} from './constant'; + +export type FlowNodeChangeProps = { + moduleId: string; + type: + | 'attr' // key: attr, value: new value + | 'updateInput' // key: update input key, value: new input value + | 'replaceInput' // key: old input key, value: new input value + | 'addInput' // key: null, value: new input value + | 'delInput' // key: delete input key, value: null + | 'updateOutput' // key: update output key, value: new output value + | 'replaceOutput' // key: old output key, value: new output value + | 'addOutput' // key: null, value: new output value + | 'delOutput'; // key: delete output key, value: null + key?: string; + value?: any; + index?: number; +}; + +export type FlowNodeInputItemType = { + key: string; // 字段名 + value?: any; + valueType?: `${FlowNodeValTypeEnum}`; + type: `${FlowNodeInputTypeEnum}`; + label: string; + edit?: boolean; + connected?: boolean; + description?: string; + placeholder?: string; + max?: number; + min?: number; + step?: number; + required?: boolean; + list?: { label: string; value: any }[]; + markList?: { label: string; value: any }[]; + customData?: () => any; + valueCheck?: (value: any) => boolean; +}; + +export type FlowNodeOutputTargetItemType = { + moduleId: string; + key: string; +}; +export type FlowNodeOutputItemType = { + key: string; // 字段名 + label?: string; + edit?: boolean; + description?: string; + valueType?: `${FlowNodeValTypeEnum}`; + type?: `${FlowNodeOutputTypeEnum}`; + targets: FlowNodeOutputTargetItemType[]; +}; diff --git a/packages/global/core/module/type.d.ts b/packages/global/core/module/type.d.ts new file mode 100644 index 000000000..c3d1f81fd --- /dev/null +++ b/packages/global/core/module/type.d.ts @@ -0,0 +1,44 @@ +import { FlowNodeTypeEnum } from './node/constant'; +import { FlowNodeInputItemType, FlowNodeOutputItemType } from './node/type'; + +export type FlowModuleTemplateType = { + id: string; + flowType: `${FlowNodeTypeEnum}`; // unique + logo?: string; + name: string; + description?: string; + intro?: string; + showStatus?: boolean; // chatting response step status + inputs: FlowNodeInputItemType[]; + outputs: FlowNodeOutputItemType[]; +}; +export type FlowModuleItemType = FlowModuleTemplateType & { + moduleId: string; +}; +export type SystemModuleTemplateType = { + label: string; + list: FlowModuleTemplateType[]; +}[]; + +export type ModuleItemType = { + name: string; + logo?: string; + intro?: string; + description?: string; + moduleId: string; + position?: { + x: number; + y: number; + }; + flowType: `${FlowNodeTypeEnum}`; + showStatus?: boolean; + inputs: FlowNodeInputItemType[]; + outputs: FlowNodeOutputItemType[]; +}; + +/* function type */ +export type SelectAppItemType = { + id: string; + name: string; + logo: string; +}; diff --git a/packages/global/core/module/utils.ts b/packages/global/core/module/utils.ts new file mode 100644 index 000000000..5355676ec --- /dev/null +++ b/packages/global/core/module/utils.ts @@ -0,0 +1,47 @@ +import { + FlowNodeInputTypeEnum, + FlowNodeSpecialInputKeyEnum, + FlowNodeTypeEnum +} from './node/constant'; +import { FlowNodeInputItemType, FlowNodeOutputItemType } from './node/type'; +import { ModuleItemType } from './type'; + +export function getPluginTemplatePluginIdInput(pluginId: string) { + return { + key: FlowNodeSpecialInputKeyEnum.pluginId, + type: FlowNodeInputTypeEnum.hidden, + label: 'pluginId', + value: pluginId, + connected: true + }; +} + +export function formatPluginIOModules( + pluginId: string, + modules: ModuleItemType[] +): { + inputs: FlowNodeInputItemType[]; + outputs: FlowNodeOutputItemType[]; +} { + const pluginInput = modules.find((module) => module.flowType === FlowNodeTypeEnum.pluginInput); + const customOutput = modules.find((module) => module.flowType === FlowNodeTypeEnum.pluginOutput); + + return { + inputs: pluginInput + ? [ + getPluginTemplatePluginIdInput(pluginId), + ...pluginInput.inputs.map((item) => ({ + ...item, + edit: false, + connected: false + })) + ] + : [], + outputs: customOutput + ? customOutput.outputs.map((item) => ({ + ...item, + edit: false + })) + : [] + }; +} diff --git a/packages/global/core/plugin/constants.ts b/packages/global/core/plugin/constants.ts new file mode 100644 index 000000000..f90764293 --- /dev/null +++ b/packages/global/core/plugin/constants.ts @@ -0,0 +1,28 @@ +import { ModuleItemType } from '../module/type'; + +export const defaultModules: ModuleItemType[] = [ + { + moduleId: 'fph4s3', + name: '自定义输出', + flowType: 'pluginOutput', + showStatus: false, + position: { + x: 994.1266684738011, + y: -45.87689365278443 + }, + inputs: [], + outputs: [] + }, + { + moduleId: 'w09v30', + name: '自定义输入', + flowType: 'pluginInput', + showStatus: false, + position: { + x: 457.57860319995154, + y: -44.25099042468186 + }, + inputs: [], + outputs: [] + } +]; diff --git a/packages/global/core/plugin/controller.d.ts b/packages/global/core/plugin/controller.d.ts new file mode 100644 index 000000000..4e1d7c7d2 --- /dev/null +++ b/packages/global/core/plugin/controller.d.ts @@ -0,0 +1,21 @@ +import type { ModuleItemType } from '../module/type.d'; + +export type CreateOnePluginParams = { + name: string; + avatar: string; + intro: string; + modules?: ModuleItemType[]; +}; +export type UpdatePluginParams = { + id: string; + name?: string; + avatar?: string; + intro?: string; + modules?: ModuleItemType[]; +}; +export type PluginListItemType = { + _id: string; + name: string; + avatar: string; + intro: string; +}; diff --git a/packages/global/core/plugin/type.d.ts b/packages/global/core/plugin/type.d.ts new file mode 100644 index 000000000..8a9e24e23 --- /dev/null +++ b/packages/global/core/plugin/type.d.ts @@ -0,0 +1,11 @@ +import type { ModuleItemType } from '../module/type.d'; + +export type PluginItemSchema = { + _id: string; + userId: string; + name: string; + avatar: string; + intro: string; + updateTime: Date; + modules: ModuleItemType[]; +}; diff --git a/packages/global/support/activity/type.d.ts b/packages/global/support/activity/type.d.ts new file mode 100644 index 000000000..54947771b --- /dev/null +++ b/packages/global/support/activity/type.d.ts @@ -0,0 +1,8 @@ +export type PromotionRecordSchema = { + _id: string; + userId: string; // 收益人 + objUId?: string; // 目标对象(如果是withdraw则为空) + type: 'register' | 'pay'; + createTime: Date; // 记录时间 + amount: number; +}; diff --git a/packages/global/support/user/constant.ts b/packages/global/support/user/constant.ts new file mode 100644 index 000000000..6c9adbae7 --- /dev/null +++ b/packages/global/support/user/constant.ts @@ -0,0 +1,9 @@ +export enum InformTypeEnum { + system = 'system' +} + +export const InformTypeMap = { + [InformTypeEnum.system]: { + label: '系统通知' + } +}; diff --git a/packages/global/support/user/type.d.ts b/packages/global/support/user/type.d.ts index 882db41b8..6568e0e4a 100644 --- a/packages/global/support/user/type.d.ts +++ b/packages/global/support/user/type.d.ts @@ -1,3 +1,5 @@ +import { InformTypeEnum } from './constant'; + export type UserModelSchema = { _id: string; username: string; @@ -18,3 +20,13 @@ export type UserModelSchema = { datasetMaxCount?: number; }; }; + +export type UserInformSchema = { + _id: string; + userId: string; + time: Date; + type: `${InformTypeEnum}`; + title: string; + content: string; + read: boolean; +}; diff --git a/packages/global/support/wallet/type.d.ts b/packages/global/support/wallet/type.d.ts new file mode 100644 index 000000000..77c89c264 --- /dev/null +++ b/packages/global/support/wallet/type.d.ts @@ -0,0 +1,8 @@ +export type PaySchema = { + _id: string; + userId: string; + createTime: Date; + price: number; + orderId: string; + status: 'SUCCESS' | 'REFUND' | 'NOTPAY' | 'CLOSED'; +}; diff --git a/packages/service/common/file/image/constant.ts b/packages/service/common/file/image/constant.ts new file mode 100644 index 000000000..518900c30 --- /dev/null +++ b/packages/service/common/file/image/constant.ts @@ -0,0 +1 @@ +export const imageBaseUrl = '/api/system/img/'; diff --git a/packages/service/common/file/image/controller.ts b/packages/service/common/file/image/controller.ts new file mode 100644 index 000000000..fc51430d0 --- /dev/null +++ b/packages/service/common/file/image/controller.ts @@ -0,0 +1,25 @@ +import { imageBaseUrl } from './constant'; +import { MongoImage } from './schema'; + +export function getMongoImgUrl(id: string) { + return `${imageBaseUrl}${id}`; +} + +export async function uploadMongoImg({ base64Img, userId }: { base64Img: string; userId: string }) { + const base64Data = base64Img.split(',')[1]; + + const { _id } = await MongoImage.create({ + userId, + binary: Buffer.from(base64Data, 'base64') + }); + + return getMongoImgUrl(String(_id)); +} + +export async function readMongoImg({ id }: { id: string }) { + const data = await MongoImage.findById(id); + if (!data) { + return Promise.reject('Image not found'); + } + return data?.binary; +} diff --git a/projects/app/src/service/models/image.ts b/packages/service/common/file/image/schema.ts similarity index 64% rename from projects/app/src/service/models/image.ts rename to packages/service/common/file/image/schema.ts index 598a9a565..fb7ea7e69 100644 --- a/projects/app/src/service/models/image.ts +++ b/packages/service/common/file/image/schema.ts @@ -1,4 +1,4 @@ -import { connectionMongo, type Model } from '@fastgpt/service/common/mongo'; +import { connectionMongo, type Model } from '../../mongo'; const { Schema, model, models } = connectionMongo; const ImageSchema = new Schema({ @@ -12,5 +12,5 @@ const ImageSchema = new Schema({ } }); -export const Image: Model<{ userId: string; binary: Buffer }> = +export const MongoImage: Model<{ userId: string; binary: Buffer }> = models['image'] || model('image', ImageSchema); diff --git a/packages/service/core/dataset/training/schema.ts b/packages/service/core/dataset/training/schema.ts index f8a749595..b39c9a656 100644 --- a/packages/service/core/dataset/training/schema.ts +++ b/packages/service/core/dataset/training/schema.ts @@ -63,6 +63,7 @@ const TrainingDataSchema = new Schema({ try { TrainingDataSchema.index({ lockTime: 1 }); TrainingDataSchema.index({ userId: 1 }); + TrainingDataSchema.index({ datasetCollectionId: 1 }); TrainingDataSchema.index({ expireAt: 1 }, { expireAfterSeconds: 7 * 24 * 60 }); } catch (error) { console.log(error); diff --git a/packages/service/core/plugin/controller.ts b/packages/service/core/plugin/controller.ts new file mode 100644 index 000000000..4f8deb2e2 --- /dev/null +++ b/packages/service/core/plugin/controller.ts @@ -0,0 +1,63 @@ +import { CreateOnePluginParams, UpdatePluginParams } from '@fastgpt/global/core/plugin/controller'; +import { MongoPlugin } from './schema'; +import { FlowModuleTemplateType } from '@fastgpt/global/core/module/type'; +import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; +import { formatPluginIOModules } from '@fastgpt/global/core/module/utils'; + +export async function createOnePlugin(data: CreateOnePluginParams & { userId: string }) { + const { _id } = await MongoPlugin.create(data); + return _id; +} + +export async function updateOnePlugin({ + id, + userId, + ...data +}: UpdatePluginParams & { userId: string }) { + await MongoPlugin.findOneAndUpdate({ _id: id, userId }, data); +} + +export async function deleteOnePlugin({ id, userId }: { id: string; userId: string }) { + await MongoPlugin.findOneAndDelete({ _id: id, userId }); +} +export async function getUserPlugins({ userId }: { userId: string }) { + return MongoPlugin.find({ userId }, 'name avatar intro'); +} +export async function getOnePluginDetail({ id, userId }: { userId: string; id: string }) { + return MongoPlugin.findOne({ _id: id, userId }); +} +/* plugin templates */ +export async function getUserPlugins2Templates({ + userId +}: { + userId: string; +}): Promise { + const plugins = await MongoPlugin.find({ userId }).lean(); + + return plugins.map((plugin) => ({ + id: String(plugin._id), + flowType: FlowNodeTypeEnum.pluginModule, + logo: plugin.avatar, + name: plugin.name, + description: plugin.intro, + intro: plugin.intro, + showStatus: false, + inputs: [], + outputs: [] + })); +} +/* one plugin 2 module detail */ +export async function getPluginModuleDetail({ id, userId }: { userId: string; id: string }) { + const plugin = await getOnePluginDetail({ id, userId }); + if (!plugin) return Promise.reject('plugin not found'); + return { + id: String(plugin._id), + flowType: FlowNodeTypeEnum.pluginModule, + logo: plugin.avatar, + name: plugin.name, + description: plugin.intro, + intro: plugin.intro, + showStatus: false, + ...formatPluginIOModules(String(plugin._id), plugin.modules) + }; +} diff --git a/packages/service/core/plugin/schema.ts b/packages/service/core/plugin/schema.ts new file mode 100644 index 000000000..76ab05432 --- /dev/null +++ b/packages/service/core/plugin/schema.ts @@ -0,0 +1,42 @@ +import { connectionMongo, type Model } from '../../common/mongo'; +const { Schema, model, models } = connectionMongo; +import type { PluginItemSchema } from '@fastgpt/global/core/plugin/type.d'; + +export const ModuleCollectionName = 'plugins'; + +const PluginSchema = new Schema({ + userId: { + type: Schema.Types.ObjectId, + ref: 'user', + required: true + }, + name: { + type: String, + required: true + }, + avatar: { + type: String, + default: '/icon/logo.svg' + }, + intro: { + type: String, + default: '' + }, + updateTime: { + type: Date, + default: () => new Date() + }, + modules: { + type: Array, + default: [] + } +}); + +try { + PluginSchema.index({ userId: 1 }); +} catch (error) { + console.log(error); +} + +export const MongoPlugin: Model = + models[ModuleCollectionName] || model(ModuleCollectionName, PluginSchema); diff --git a/projects/app/src/service/models/promotionRecord.ts b/packages/service/support/activity/promotion/schema.ts similarity index 70% rename from projects/app/src/service/models/promotionRecord.ts rename to packages/service/support/activity/promotion/schema.ts index a8480b420..960a1f30b 100644 --- a/projects/app/src/service/models/promotionRecord.ts +++ b/packages/service/support/activity/promotion/schema.ts @@ -1,6 +1,6 @@ -import { connectionMongo, type Model } from '@fastgpt/service/common/mongo'; +import { connectionMongo, type Model } from '../../../common/mongo'; const { Schema, model, models } = connectionMongo; -import { PromotionRecordSchema as PromotionRecordType } from '@/types/mongoSchema'; +import { PromotionRecordSchema as PromotionRecordType } from '@fastgpt/global/support/activity/type.d'; const PromotionRecordSchema = new Schema({ userId: { @@ -28,5 +28,5 @@ const PromotionRecordSchema = new Schema({ } }); -export const promotionRecord: Model = +export const MongoPromotionRecord: Model = models['promotionRecord'] || model('promotionRecord', PromotionRecordSchema); diff --git a/packages/service/support/user/inform/controller.ts b/packages/service/support/user/inform/controller.ts new file mode 100644 index 000000000..d8c0057ef --- /dev/null +++ b/packages/service/support/user/inform/controller.ts @@ -0,0 +1,45 @@ +import { MongoUserInform } from './schema'; +import { MongoUser } from '../schema'; +import { InformTypeEnum } from '@fastgpt/global/support/user/constant'; + +export type SendInformProps = { + type: `${InformTypeEnum}`; + title: string; + content: string; +}; + +export async function sendInform2AllUser({ type, title, content }: SendInformProps) { + const users = await MongoUser.find({}, '_id'); + await MongoUserInform.insertMany( + users.map(({ _id }) => ({ + type, + title, + content, + userId: _id + })) + ); +} + +export async function sendInform2OneUser({ + type, + title, + content, + userId +}: SendInformProps & { userId: string }) { + const inform = await MongoUserInform.findOne({ + type, + title, + content, + userId, + time: { $gte: new Date(Date.now() - 5 * 60 * 1000) } + }); + + if (inform) return; + + await MongoUserInform.create({ + type, + title, + content, + userId + }); +} diff --git a/projects/app/src/service/models/inform.ts b/packages/service/support/user/inform/schema.ts similarity index 64% rename from projects/app/src/service/models/inform.ts rename to packages/service/support/user/inform/schema.ts index d410185d2..fc47154fe 100644 --- a/projects/app/src/service/models/inform.ts +++ b/packages/service/support/user/inform/schema.ts @@ -1,7 +1,7 @@ -import { connectionMongo, type Model } from '@fastgpt/service/common/mongo'; +import { connectionMongo, type Model } from '../../../common/mongo'; const { Schema, model, models } = connectionMongo; -import { informSchema } from '@/types/mongoSchema'; -import { InformTypeMap } from '@/constants/user'; +import type { UserInformSchema } from '@fastgpt/global/support/user/type.d'; +import { InformTypeMap } from '@fastgpt/global/support/user/constant'; const InformSchema = new Schema({ userId: { @@ -38,4 +38,5 @@ try { console.log(error); } -export const Inform: Model = models['inform'] || model('inform', InformSchema); +export const MongoUserInform: Model = + models['inform'] || model('inform', InformSchema); diff --git a/projects/app/src/service/models/pay.ts b/packages/service/support/wallet/pay/schema.ts similarity index 67% rename from projects/app/src/service/models/pay.ts rename to packages/service/support/wallet/pay/schema.ts index 56fa5d80e..32a863cc7 100644 --- a/projects/app/src/service/models/pay.ts +++ b/packages/service/support/wallet/pay/schema.ts @@ -1,6 +1,7 @@ -import { connectionMongo, type Model } from '@fastgpt/service/common/mongo'; +import { connectionMongo, type Model } from '../../../common/mongo'; const { Schema, model, models } = connectionMongo; -import { PaySchema as PayType } from '@/types/mongoSchema'; +import { PaySchema as PayType } from '@fastgpt/global/support/wallet/type.d'; + const PaySchema = new Schema({ userId: { type: Schema.Types.ObjectId, @@ -27,4 +28,4 @@ const PaySchema = new Schema({ } }); -export const Pay: Model = models['pay'] || model('pay', PaySchema); +export const MongoPay: Model = models['pay'] || model('pay', PaySchema); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae1f60f92..5f3a08661 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,20 +12,20 @@ importers: specifier: ^8.0.3 version: registry.npmmirror.com/husky@8.0.3 i18next: - specifier: ^23.2.11 - version: registry.npmmirror.com/i18next@23.6.0 + specifier: ^22.5.1 + version: registry.npmmirror.com/i18next@22.5.1 lint-staged: specifier: ^13.2.1 version: registry.npmmirror.com/lint-staged@13.3.0 next-i18next: - specifier: ^14.0.0 - version: registry.npmmirror.com/next-i18next@14.0.3(i18next@23.6.0)(next@13.5.2)(react-i18next@13.3.1)(react@18.2.0) + specifier: ^13.3.0 + version: registry.npmmirror.com/next-i18next@13.3.0(i18next@22.5.1)(next@13.5.2)(react-i18next@12.3.1)(react@18.2.0) prettier: specifier: ^3.0.3 version: registry.npmmirror.com/prettier@3.0.3 react-i18next: - specifier: ^13.0.2 - version: registry.npmmirror.com/react-i18next@13.3.1(i18next@23.6.0)(react-dom@18.2.0)(react@18.2.0) + specifier: ^12.3.1 + version: registry.npmmirror.com/react-i18next@12.3.1(i18next@22.5.1)(react-dom@18.2.0)(react@18.2.0) packages/global: dependencies: @@ -176,8 +176,8 @@ importers: specifier: ^2.4.29 version: registry.npmmirror.com/hyperdown@2.4.29 i18next: - specifier: ^23.2.11 - version: registry.npmmirror.com/i18next@23.6.0 + specifier: ^22.5.1 + version: registry.npmmirror.com/i18next@22.5.1 immer: specifier: ^9.0.19 version: registry.npmmirror.com/immer@9.0.21 @@ -215,8 +215,8 @@ importers: specifier: 13.5.2 version: registry.npmmirror.com/next@13.5.2(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.4) next-i18next: - specifier: ^14.0.0 - version: registry.npmmirror.com/next-i18next@14.0.3(i18next@23.6.0)(next@13.5.2)(react-i18next@13.3.1)(react@18.2.0) + specifier: ^13.3.0 + version: registry.npmmirror.com/next-i18next@13.3.0(i18next@22.5.1)(next@13.5.2)(react-i18next@12.3.1)(react@18.2.0) nprogress: specifier: ^0.2.0 version: registry.npmmirror.com/nprogress@0.2.0 @@ -242,8 +242,8 @@ importers: specifier: ^7.43.1 version: registry.npmmirror.com/react-hook-form@7.47.0(react@18.2.0) react-i18next: - specifier: ^13.0.2 - version: registry.npmmirror.com/react-i18next@13.3.1(i18next@23.6.0)(react-dom@18.2.0)(react@18.2.0) + specifier: ^12.3.1 + version: registry.npmmirror.com/react-i18next@12.3.1(i18next@22.5.1)(react-dom@18.2.0)(react@18.2.0) react-markdown: specifier: ^8.0.7 version: registry.npmmirror.com/react-markdown@8.0.7(@types/react@18.0.28)(react@18.2.0) @@ -7579,10 +7579,10 @@ packages: name: i18next-fs-backend version: 2.2.0 - registry.npmmirror.com/i18next@23.6.0: - resolution: {integrity: sha512-z0Cxr0MGkt+kli306WS4nNNM++9cgt2b2VCMprY92j+AIab/oclgPxdwtTZVLP1zn5t5uo8M6uLsZmYrcjr3HA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/i18next/-/i18next-23.6.0.tgz} + registry.npmmirror.com/i18next@22.5.1: + resolution: {integrity: sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/i18next/-/i18next-22.5.1.tgz} name: i18next - version: 23.6.0 + version: 22.5.1 dependencies: '@babel/runtime': registry.npmmirror.com/@babel/runtime@7.23.2 @@ -9327,27 +9327,27 @@ packages: version: 1.4.0 dev: true - registry.npmmirror.com/next-i18next@14.0.3(i18next@23.6.0)(next@13.5.2)(react-i18next@13.3.1)(react@18.2.0): - resolution: {integrity: sha512-FtnjRMfhlamk8YyeyWqd+pndNL+3er83iMZnH4M4mhiGA93l0+vtBUvuObgOAMHDJGLLB2SS2xOOZq69oiJh7A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/next-i18next/-/next-i18next-14.0.3.tgz} - id: registry.npmmirror.com/next-i18next/14.0.3 + registry.npmmirror.com/next-i18next@13.3.0(i18next@22.5.1)(next@13.5.2)(react-i18next@12.3.1)(react@18.2.0): + resolution: {integrity: sha512-X4kgi51BCOoGdKbv87eZ8OU7ICQDg5IP+T5fNjqDY3os9ea0OKTY4YpAiVFiwcI9XimcUmSPbKO4a9jFUyYSgg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/next-i18next/-/next-i18next-13.3.0.tgz} + id: registry.npmmirror.com/next-i18next/13.3.0 name: next-i18next - version: 14.0.3 + version: 13.3.0 engines: {node: '>=14'} peerDependencies: - i18next: ^23.4.6 + i18next: ^22.0.6 next: '>= 12.0.0' react: '>= 17.0.2' - react-i18next: ^13.2.1 + react-i18next: ^12.2.0 dependencies: '@babel/runtime': registry.npmmirror.com/@babel/runtime@7.23.2 '@types/hoist-non-react-statics': registry.npmmirror.com/@types/hoist-non-react-statics@3.3.4 core-js: registry.npmmirror.com/core-js@3.33.1 hoist-non-react-statics: registry.npmmirror.com/hoist-non-react-statics@3.3.2 - i18next: registry.npmmirror.com/i18next@23.6.0 + i18next: registry.npmmirror.com/i18next@22.5.1 i18next-fs-backend: registry.npmmirror.com/i18next-fs-backend@2.2.0 next: registry.npmmirror.com/next@13.5.2(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.4) react: registry.npmmirror.com/react@18.2.0 - react-i18next: registry.npmmirror.com/react-i18next@13.3.1(i18next@23.6.0)(react-dom@18.2.0)(react@18.2.0) + react-i18next: registry.npmmirror.com/react-i18next@12.3.1(i18next@22.5.1)(react-dom@18.2.0)(react@18.2.0) registry.npmmirror.com/next@13.5.2(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.4): resolution: {integrity: sha512-vog4UhUaMYAzeqfiAAmgB/QWLW7p01/sg+2vn6bqc/CxHFYizMzLv6gjxKzl31EVFkfl/F+GbxlKizlkTE9RdA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/next/-/next-13.5.2.tgz} @@ -10150,13 +10150,13 @@ packages: react: registry.npmmirror.com/react@18.2.0 dev: false - registry.npmmirror.com/react-i18next@13.3.1(i18next@23.6.0)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-JAtYREK879JXaN9GdzfBI4yJeo/XyLeXWUsRABvYXiFUakhZJ40l+kaTo+i+A/3cKIED41kS/HAbZ5BzFtq/Og==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/react-i18next/-/react-i18next-13.3.1.tgz} - id: registry.npmmirror.com/react-i18next/13.3.1 + registry.npmmirror.com/react-i18next@12.3.1(i18next@22.5.1)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-5v8E2XjZDFzK7K87eSwC7AJcAkcLt5xYZ4+yTPDAW1i7C93oOY1dnr4BaQM7un4Hm+GmghuiPvevWwlca5PwDA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/react-i18next/-/react-i18next-12.3.1.tgz} + id: registry.npmmirror.com/react-i18next/12.3.1 name: react-i18next - version: 13.3.1 + version: 12.3.1 peerDependencies: - i18next: '>= 23.2.3' + i18next: '>= 19.0.0' react: '>= 16.8.0' react-dom: '*' react-native: '*' @@ -10168,7 +10168,7 @@ packages: dependencies: '@babel/runtime': registry.npmmirror.com/@babel/runtime@7.23.2 html-parse-stringify: registry.npmmirror.com/html-parse-stringify@3.0.1 - i18next: registry.npmmirror.com/i18next@23.6.0 + i18next: registry.npmmirror.com/i18next@22.5.1 react: registry.npmmirror.com/react@18.2.0 react-dom: registry.npmmirror.com/react-dom@18.2.0(react@18.2.0) diff --git a/projects/app/package.json b/projects/app/package.json index 9ea0641b0..96891d8ec 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -1,6 +1,6 @@ { "name": "app", - "version": "4.5.1", + "version": "4.5.2", "private": false, "scripts": { "dev": "next dev", @@ -31,7 +31,7 @@ "formidable": "^2.1.1", "framer-motion": "^9.0.6", "hyperdown": "^2.4.29", - "i18next": "^23.2.11", + "i18next": "^22.5.1", "immer": "^9.0.19", "js-cookie": "^3.0.5", "js-tiktoken": "^1.0.7", @@ -44,7 +44,7 @@ "multer": "1.4.5-lts.1", "nanoid": "^4.0.1", "next": "13.5.2", - "next-i18next": "^14.0.0", + "next-i18next": "^13.3.0", "nprogress": "^0.2.0", "papaparse": "^5.4.1", "pg": "^8.10.0", @@ -53,7 +53,7 @@ "react-day-picker": "^8.7.1", "react-dom": "18.2.0", "react-hook-form": "^7.43.1", - "react-i18next": "^13.0.2", + "react-i18next": "^12.3.1", "react-markdown": "^8.0.7", "react-syntax-highlighter": "^15.5.0", "reactflow": "^11.7.4", diff --git a/projects/app/public/docs/versionIntro.md b/projects/app/public/docs/versionIntro.md index 90a2dd47d..f84539e84 100644 --- a/projects/app/public/docs/versionIntro.md +++ b/projects/app/public/docs/versionIntro.md @@ -1,9 +1,9 @@ -### Fast GPT V4.5.1 +### Fast GPT V4.5.2 -1. 新增 - 知识库目录结构,更方便进行分类 -2. 新增 - 升级 PgVector 插件,引入 HNSW 索引,极大加快的知识库搜索速度。 -3. 新增 - AI对话模块,增加【返回AI内容】选项,可控制 AI 的内容不直接返回浏览器。 -4. 优化 - TextSplitter,采用递归拆解法。 -5. [使用文档](https://doc.fastgpt.run/docs/intro/) -6. [点击查看高级编排介绍文档](https://doc.fastgpt.run/docs/workflow) -7. [点击查看商业版](https://doc.fastgpt.run/docs/commercial/) +1. 新增 - 模块插件,允许自行组装插件进行模块复用。 +2. 优化 - 知识库引用提示。 +3. [知识库结构详解](https://doc.fastgpt.in/docs/use-cases/datasetengine/) +4. [知识库提示词详解](https://doc.fastgpt.in/docs/use-cases/ai_settings/#引用模板--引用提示词) +5. [使用文档](https://doc.fastgpt.in/docs/intro/) +6. [点击查看高级编排介绍文档](https://doc.fastgpt.in/docs/workflow) +7. [点击查看商业版](https://doc.fastgpt.in/docs/commercial/) diff --git a/projects/app/public/imgs/module/ai.svg b/projects/app/public/imgs/module/ai.svg new file mode 100644 index 000000000..2fd7b1177 --- /dev/null +++ b/projects/app/public/imgs/module/ai.svg @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/projects/app/public/imgs/module/custom.png b/projects/app/public/imgs/module/custom.png new file mode 100644 index 000000000..11eb97d28 Binary files /dev/null and b/projects/app/public/imgs/module/custom.png differ diff --git a/projects/app/public/imgs/module/input.png b/projects/app/public/imgs/module/input.png new file mode 100644 index 000000000..73b5e8bfe Binary files /dev/null and b/projects/app/public/imgs/module/input.png differ diff --git a/projects/app/public/imgs/module/output.png b/projects/app/public/imgs/module/output.png new file mode 100644 index 000000000..5bf40bc24 Binary files /dev/null and b/projects/app/public/imgs/module/output.png differ diff --git a/projects/app/public/imgs/module/plugin.svg b/projects/app/public/imgs/module/plugin.svg new file mode 100644 index 000000000..4a0d9118b --- /dev/null +++ b/projects/app/public/imgs/module/plugin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/locales/en/common.json b/projects/app/public/locales/en/common.json index 1dad6b695..4f23d11ff 100644 --- a/projects/app/public/locales/en/common.json +++ b/projects/app/public/locales/en/common.json @@ -27,6 +27,7 @@ "Connection is invalid": "Connecting is invalid", "Connection type is different": "Connection type is different", "Copy Module Config": "Copy config", + "Dataset Quote Template": "Dataset Mode", "Export Config Successful": "The configuration has been copied. Please check for important data", "Export Configs": "Export Configs", "Feedback Count": "User Feedback", @@ -46,7 +47,20 @@ "Paste Config": "Paste Config", "Variable Key Repeat Tip": "Variable Key Repeat", "module": { - "Custom Title Tip": "The title name is displayed during the conversation" + "Combine Modules": "Combine Modules", + "Custom Title Tip": "The title name is displayed during the conversation", + "My Modules": "My Custom Modules", + "No Modules": "No module", + "System Module": "System Module", + "type": "{{type}}\n{{example}}", + "valueType": { + "any": "any", + "boolean": "boolean", + "chatHistory": "Chat History", + "datasetQuote": "Dataset Quote", + "number": "number", + "string": "string" + } }, "modules": { "Title is required": "Title is required" @@ -72,6 +86,7 @@ "Mark Description Title": "Mark Description", "New Chat": "New Chat", "Question Guide Tips": "I guess what you're asking is", + "Quote": "Quote", "Read Mark Description": "Read mark description", "Read User Feedback": "Read user feedback", "Select Mark Kb": "Select Dataset", @@ -104,24 +119,32 @@ "module similarity": "Similarity", "module temperature": "Temperature", "module time": "Running Time", - "module tokens": "Tokens" + "module tokens": "Tokens", + "plugin output": "Plugin Output" }, "retry": "Retry" }, "common": { "Add": "Add", + "Back": "Back", + "Beta": "Beta", "Choose": "Choose", "Close": "Clow", "Collect": "Collect", + "Confirm Create": "Create", "Confirm Move": "Move here", + "Confirm Update": "Update", "Copy": "Copy", "Copy Successful": "Copy Successful", "Course": "", + "Create Failed": "Create Failed", + "Create Success": "Create Success", "Create Virtual File Failed": "Create Virtual File Failed", "Custom Title": "Custom Title", "Delete": "Delete", "Delete Failed": "Delete Failed", "Delete Success": "Delete Successful", + "Delete Tip": "Delete Confirm", "Delete Warning": "Warning", "Edit": "Edit", "Expired Time": "Expired", @@ -130,10 +153,13 @@ "Filed is repeated": "", "Input": "Input", "Last Step": "Last", + "Loading": "Loading", "Max credit": "Credit", "Max credit tips": "What is the maximum amount of money that can be consumed by the link? If the link is exceeded, it will be banned. -1 indicates no limit.", "Name": "Name", + "Name Can": "Name Can't Be Empty", "Name is empty": "Name is empty", + "New Create": "Create", "Next Step": "Next", "Output": "Output", "Params": "Params", @@ -143,11 +169,17 @@ "Rename Success": "Rename Success", "Request Error": "Request Error", "Search": "Search", + "Select File Failed": "Select File Failed", "Select One Folder": "Select a folder", + "Set Avatar": "Set Avatar", + "Set Name": "Make a nice name", "Status": "Status", + "Test": "Test", "Time": "Time", "Unknow": "Unknow", "Unknow Source": "UnKnow Source", + "Update Failed": "Update Failed", + "Update Success": "Update Success", "Update Successful": "Update Successful", "Update Time": "Update Time", "error": { @@ -304,11 +336,26 @@ "desc": "AI knowledge base question and answer platform based on LLM large model", "slogan": "Let the AI know more about you" }, + "module": { + "Confirm Delete Module": "Confirm to delete the custom module?", + "Confirm Sync Plugin": "Confirm the latest sync plugin information? The plug-in connection and input content will be cleared, please confirm!", + "Create Your Module": "Create You Module", + "Intro": "Module Intro", + "Load Module Failed": "Load Module Failed", + "Plugin input is not value": "User-defined input parameters cannot be null", + "Plugin input is required": "The plug setting must contain an input module", + "Plugin input must connect": "Custom input modules must all be connected", + "Preview Plugin": "Preview Plugin", + "Save Config": "Save", + "Update Your Module": "Update Module" + }, "navbar": { "Account": "Account", "Apps": "Apps", "Chat": "Chat", "Datasets": "DataSets", + "Module": "Module", + "Plugin": "Plugin", "Store": "Store", "Tools": "Tools" }, @@ -338,12 +385,26 @@ "token auth Tips": "Identity verification server address. If this value is set, the server will be specified to send a request for identity verification before each session", "token auth use cases": "Review the authentication instructions" }, + "plugin": { + "Confirm Delete": "Confirm to delete the plugin?", + "Create Your Plugin": "Create Plugin", + "Get Plugin Module Detail Failed": "Get plugin detail failed", + "Intro": "Plugin Intro", + "Load Plugin Failed": "Load Plugin Failed", + "My Plugins": "My Plugins", + "No Intro": "This plugin is not introduced", + "Plugin Module": "Plugin", + "Set Name": "Plugin Name", + "Synchronous version": "Sync Version", + "To Edit Plugin": "To Edit", + "Update Your Plugin": "Update Plugin" + }, "system": { "Help Document": "Document" }, "template": { - "Quote Content Tip": "This configuration takes effect only when reference content is passed in (knowledge base search). You can customize the structure of the reference content to better fit different scenarios. You can use {{q}}, {{a}}, {{source}} as \"search content\", \"expected content\", and \"source\", they are all optional, and here are the default values: \n{instruction:\"{{q}}\",output:\"{{a}}\"}", - "Quote Prompt Tip": "This configuration takes effect only when reference content is passed in (knowledge base search). \n You can insert references with {{quote}}, here are the default values: \n\"\"\"{{quote}}\"\"\" The three quotes are the knowledge base I gave you, they have the highest priority. instruction is a relevant introduction and output is an expected answer or supplement." + "Quote Content Tip": "This configuration takes effect only when reference content is passed in (knowledge base search).\nYou can customize the structure of the reference content to better suit different scenarios. Some variables can be used for template configuration:\n{{q}} - retrieve content, {{a}} - expected content, {{source}} - source, {{sourceId}} - source file name, {{index}} - the first n references, {{with}} - the reference points (0-1), they are optional, Here are the default values:\n{{default}}", + "Quote Prompt Tip": "This configuration takes effect only when the knowledge base is searched.\nYou can use {{quote}} to insert the reference content template and {{question}} to insert the question. Here are the default values:\n{{default}}" }, "user": { "Account": "Account", diff --git a/projects/app/public/locales/zh/common.json b/projects/app/public/locales/zh/common.json index 6b03a73cf..21ec5c4ba 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/public/locales/zh/common.json @@ -27,6 +27,7 @@ "Connection is invalid": "连接无效", "Connection type is different": "连接的类型不一致", "Copy Module Config": "复制配置", + "Dataset Quote Template": "知识库问答模式", "Export Config Successful": "已复制配置,请注意检查是否有重要数据", "Export Configs": "导出配置", "Feedback Count": "用户反馈", @@ -46,7 +47,20 @@ "Paste Config": "粘贴配置", "Variable Key Repeat Tip": "变量 key 重复", "module": { - "Custom Title Tip": "该标题名字会展示在对话过程中" + "Combine Modules": "组合模块", + "Custom Title Tip": "该标题名字会展示在对话过程中", + "My Modules": "", + "No Modules": "还没有模块~", + "System Module": "系统模块", + "type": "\"{{type}}\"类型\n{{example}}", + "valueType": { + "any": "任意", + "boolean": "布尔", + "chatHistory": "聊天记录", + "datasetQuote": "引用内容", + "number": "数字", + "string": "字符串" + } }, "modules": { "Title is required": "模块名不能为空" @@ -72,6 +86,7 @@ "Mark Description Title": "标注功能介绍", "New Chat": "新对话", "Question Guide Tips": "猜你想问", + "Quote": "引用", "Read Mark Description": "查看标注功能介绍", "Read User Feedback": "查看用户反馈", "Select Mark Kb": "选择知识库", @@ -104,24 +119,32 @@ "module similarity": "相似度", "module temperature": "温度", "module time": "运行时长", - "module tokens": "Tokens" + "module tokens": "Tokens", + "plugin output": "插件输出值" }, "retry": "重新生成" }, "common": { "Add": "添加", + "Back": "返回", + "Beta": "实验版", "Choose": "选择", "Close": "关闭", "Collect": "收藏", + "Confirm Create": "确认创建", "Confirm Move": "移动到这", + "Confirm Update": "确认更新", "Copy": "复制", "Copy Successful": "复制成功", "Course": "", + "Create Failed": "创建异常", + "Create Success": "创建成功", "Create Virtual File Failed": "创建虚拟文件失败", "Custom Title": "自定义标题", "Delete": "删除", "Delete Failed": "删除失败", "Delete Success": "删除成功", + "Delete Tip": "删除提示", "Delete Warning": "删除警告", "Edit": "编辑", "Expired Time": "过期时间", @@ -130,10 +153,13 @@ "Filed is repeated": "字段重复了", "Input": "输入", "Last Step": "上一步", + "Loading": "加载中", "Max credit": "最大金额", "Max credit tips": "该链接最大可消耗多少金额,超出后链接将被禁止使用。-1 代表无限制。", "Name": "名称", + "Name Can": "名称不能为空", "Name is empty": "名称不能为空", + "New Create": "新建", "Next Step": "下一步", "Output": "输出", "Params": "参数", @@ -143,11 +169,17 @@ "Rename Success": "重命名成功", "Request Error": "请求异常", "Search": "搜索", + "Select File Failed": "选择文件异常", "Select One Folder": "选择一个目录", + "Set Avatar": "点击设置头像", + "Set Name": "取个响亮的名字", "Status": "状态", + "Test": "测试", "Time": "时间", "Unknow": "未知", "Unknow Source": "未知来源", + "Update Failed": "更新异常", + "Update Success": "更新成功", "Update Successful": "更新成功", "Update Time": "更新时间", "error": { @@ -232,7 +264,7 @@ "data": { "Delete Tip": "确认删除该条数据?", "File import": "文件导入", - "Input Data": "导入数据", + "Input Data": "导入新数据", "Input Success Tip": "导入数据成功", "Update Data": "更新数据", "Update Success Tip": "更新数据成功" @@ -304,11 +336,26 @@ "desc": "基于 LLM 大模型的 AI 知识库问答平台", "slogan": "让 AI 更懂你的知识" }, + "module": { + "Confirm Delete Module": "确认删除该自定义模块?", + "Confirm Sync Plugin": "确认同步插件最新信息?插件的连线和输入的内容将会被清空,请确认!", + "Create Your Module": "创建自定义模块", + "Intro": "模块介绍", + "Load Module Failed": "加载模块失败", + "Plugin input is not value": "自定义输入的参数不能为空", + "Plugin input is required": "插件编排必须包含一个输入模块", + "Plugin input must connect": "自定义输入模块必须全部连接", + "Preview Plugin": "预览插件", + "Save Config": "保存配置", + "Update Your Module": "更新模块信息" + }, "navbar": { "Account": "账号", "Apps": "应用", "Chat": "聊天", "Datasets": "知识库", + "Module": "模块", + "Plugin": "插件", "Store": "应用市场", "Tools": "工具" }, @@ -338,12 +385,26 @@ "token auth Tips": "身份校验服务器地址,如填写该值,每次对话前都会想指定服务器发送一个请求,进行身份校验", "token auth use cases": "查看身份验证使用说明" }, + "plugin": { + "Confirm Delete": "确认删除该插件?", + "Create Your Plugin": "创建你的插件", + "Get Plugin Module Detail Failed": "获取插件信息异常", + "Intro": "插件介绍", + "Load Plugin Failed": "加载插件异常", + "My Plugins": "我的插件", + "No Intro": "这个插件没有介绍~", + "Plugin Module": "插件模块", + "Set Name": "给插件取个名字", + "Synchronous version": "同步版本", + "To Edit Plugin": "去编辑", + "Update Your Plugin": "更新插件" + }, "system": { "Help Document": "帮助文档" }, "template": { - "Quote Content Tip": "该配置只有传入引用内容(知识库搜索)时生效。\n可以自定义引用内容的结构,以更好的适配不同场景。可以使用一些变量来进行模板配置:\n{{q}} - 检索内容, {{a}} - 预期内容, {{source}} - 来源,{{file_id}} - 来源文件名,{{index}} - 第n个引用,他们都是可选的,下面是默认值:\n{{default}}", - "Quote Prompt Tip": "该配置只有传入引用内容(知识库搜索)时生效。\n可以用 {{quote}} 来插入引用内容,使用 {{question}} 来插入问题。下面是默认值:\n{{default}}" + "Quote Content Tip": "该配置只有传入引用内容(知识库搜索)时生效。\n可以自定义引用内容的结构,以更好的适配不同场景。可以使用一些变量来进行模板配置:\n{{q}} - 检索内容, {{a}} - 预期内容, {{source}} - 来源,{{sourceId}} - 来源文件名,{{index}} - 第n个引用,{{score}} - 该引用的得分(0-1),他们都是可选的,下面是默认值:\n{{default}}", + "Quote Prompt Tip": "该配置只在知识库搜索时生效。\n可以用 {{quote}} 来插入引用内容模板,使用 {{question}} 来插入问题。下面是默认值:\n{{default}}" }, "user": { "Account": "账号", diff --git a/projects/app/src/components/ChatBox/QuoteModal.tsx b/projects/app/src/components/ChatBox/QuoteModal.tsx index 5c1ceaf0a..50c3f0db5 100644 --- a/projects/app/src/components/ChatBox/QuoteModal.tsx +++ b/projects/app/src/components/ChatBox/QuoteModal.tsx @@ -77,13 +77,7 @@ const QuoteModal = ({ } > - + {rawSearch.map((item, i) => ( - {!isShare && ( - - - + + + + {!isShare && ( - - )} + )} + {item.q} - {item.a} + {item.a} {!isShare && ( - + {isPc && ( diff --git a/projects/app/src/components/ChatBox/ResponseTags.tsx b/projects/app/src/components/ChatBox/ResponseTags.tsx index a9af1ce32..731250487 100644 --- a/projects/app/src/components/ChatBox/ResponseTags.tsx +++ b/projects/app/src/components/ChatBox/ResponseTags.tsx @@ -1,19 +1,22 @@ import React, { useMemo, useState } from 'react'; import { ChatHistoryItemResType, ChatItemType } from '@/types/chat'; -import { Flex, BoxProps, useDisclosure } from '@chakra-ui/react'; +import { Flex, BoxProps, useDisclosure, Image, useTheme } from '@chakra-ui/react'; import { useTranslation } from 'react-i18next'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import dynamic from 'next/dynamic'; import Tag from '../Tag'; import MyTooltip from '../MyTooltip'; -import { FlowModuleTypeEnum } from '@/constants/flow'; +import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; +import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils'; +import ChatBoxDivider from '@/components/core/chat/Divider'; const QuoteModal = dynamic(() => import('./QuoteModal'), { ssr: false }); const ContextModal = dynamic(() => import('./ContextModal'), { ssr: false }); const WholeResponseModal = dynamic(() => import('./WholeResponseModal'), { ssr: false }); const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemResType[] }) => { + const theme = useTheme(); const { isPc } = useSystemStore(); const { t } = useTranslation(); const [quoteModalData, setQuoteModalData] = useState(); @@ -27,18 +30,36 @@ const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemRes const { chatAccount, quoteList = [], + sourceList = [], historyPreview = [], runningTime = 0 } = useMemo(() => { - const chatData = responseData.find((item) => item.moduleType === FlowModuleTypeEnum.chatNode); + const chatData = responseData.find((item) => item.moduleType === FlowNodeTypeEnum.chatNode); + const quoteList = responseData + .filter((item) => item.moduleType === FlowNodeTypeEnum.chatNode) + .map((item) => item.quoteList) + .flat() + .filter((item) => item) as SearchDataResponseItemType[]; + const sourceList = quoteList.reduce( + (acc: Record, cur) => { + if (!acc[cur.sourceName]) { + acc[cur.sourceName] = [cur]; + } + return acc; + }, + {} + ); + return { - chatAccount: responseData.filter((item) => item.moduleType === FlowModuleTypeEnum.chatNode) + chatAccount: responseData.filter((item) => item.moduleType === FlowNodeTypeEnum.chatNode) .length, - quoteList: responseData - .filter((item) => item.moduleType === FlowModuleTypeEnum.chatNode) - .map((item) => item.quoteList) + quoteList, + sourceList: Object.values(sourceList) .flat() - .filter((item) => item) as SearchDataResponseItemType[], + .map((item) => ({ + sourceName: item.sourceName, + icon: getSourceNameIcon({ sourceId: item.sourceId, sourceName: item.sourceName }) + })), historyPreview: chatData?.historyPreview, runningTime: +responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0).toFixed(2) }; @@ -50,64 +71,93 @@ const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemRes }; return responseData.length === 0 ? null : ( - - {quoteList.length > 0 && ( - - setQuoteModalData(quoteList)} - > - {quoteList.length}条引用 - - - )} - {chatAccount === 1 && ( + <> + {sourceList.length > 0 && ( <> - {historyPreview.length > 0 && ( - - + + {sourceList.map((item) => ( + setContextModalData(historyPreview)} + border={theme.borders.sm} + py={1} + px={2} + borderRadius={'md'} + _hover={{ + bg: 'myBlue.100' + }} + onClick={() => setQuoteModalData(quoteList)} > - {historyPreview.length}条上下文 - - - )} + {''} + {item.sourceName} + + ))} + )} - {chatAccount > 1 && ( - - 多组 AI 对话 - - )} + + {quoteList.length > 0 && ( + + setQuoteModalData(quoteList)} + > + {quoteList.length}条引用 + + + )} + {chatAccount === 1 && ( + <> + {historyPreview.length > 0 && ( + + setContextModalData(historyPreview)} + > + {historyPreview.length}条上下文 + + + )} + + )} + {chatAccount > 1 && ( + + 多组 AI 对话 + + )} - {isPc && runningTime > 0 && ( - - - {runningTime}s + {isPc && runningTime > 0 && ( + + + {runningTime}s + + + )} + + + {t('chat.Complete Response')} - )} - - - {t('chat.Complete Response')} - - - {!!quoteModalData && ( - setQuoteModalData(undefined)} /> - )} - {!!contextModalData && ( - setContextModalData(undefined)} /> - )} - {isOpenWholeModal && ( - - )} - + {!!quoteModalData && ( + setQuoteModalData(undefined)} /> + )} + {!!contextModalData && ( + setContextModalData(undefined)} /> + )} + {isOpenWholeModal && ( + + )} + + ); }; diff --git a/projects/app/src/components/ChatBox/WholeResponseModal.tsx b/projects/app/src/components/ChatBox/WholeResponseModal.tsx index 6b616b929..aa60225ce 100644 --- a/projects/app/src/components/ChatBox/WholeResponseModal.tsx +++ b/projects/app/src/components/ChatBox/WholeResponseModal.tsx @@ -32,7 +32,7 @@ function Row({ label, value }: { label: string; value?: string | number | React. ) : null; } -const ResponseModal = ({ +const WholeResponseModal = ({ response, onClose }: { @@ -50,6 +50,7 @@ const ResponseModal = ({ item.moduleType === template.flowType)?.logo } alt={''} @@ -192,10 +193,22 @@ const ResponseModal = ({ } })()} /> + + {/* plugin */} + { + try { + return JSON.stringify(activeModule?.pluginOutput, null, 2); + } catch (error) { + return ''; + } + })()} + /> ); }; -export default ResponseModal; +export default WholeResponseModal; diff --git a/projects/app/src/components/ChatBox/index.tsx b/projects/app/src/components/ChatBox/index.tsx index 0e39651ac..dbe146ac0 100644 --- a/projects/app/src/components/ChatBox/index.tsx +++ b/projects/app/src/components/ChatBox/index.tsx @@ -35,7 +35,7 @@ import { feConfigs } from '@/web/common/system/staticData'; import { eventBus } from '@/web/common/utils/eventbus'; import { adaptChat2GptMessages } from '@/utils/common/adapt/message'; import { useMarkdown } from '@/web/common/hooks/useMarkdown'; -import { AppModuleItemType } from '@/types/app'; +import { ModuleItemType } from '@fastgpt/global/core/module/type.d'; import { VariableInputEnum } from '@/constants/app'; import { useForm } from 'react-hook-form'; import type { MessageItemType } from '@/types/core/chat/type'; @@ -54,6 +54,7 @@ import Avatar from '@/components/Avatar'; import Markdown from '@/components/Markdown'; import MySelect from '@/components/Select'; import MyTooltip from '../MyTooltip'; +import ChatBoxDivider from '@/components/core/chat/Divider'; import dynamic from 'next/dynamic'; const ResponseTags = dynamic(() => import('./ResponseTags')); const FeedbackModal = dynamic(() => import('./FeedbackModal')); @@ -99,7 +100,7 @@ type Props = { showEmptyIntro?: boolean; appAvatar?: string; userAvatar?: string; - userGuideModule?: AppModuleItemType; + userGuideModule?: ModuleItemType; active?: boolean; onUpdateVariable?: (e: Record) => void; onStartChat?: (e: StartChatFnProps) => Promise<{ @@ -488,7 +489,7 @@ const ChatBox = ( return { bg: colorMap[chatContent.status] || colorMap.loading, - name: t(chatContent.moduleName || 'Running') + name: t(chatContent.moduleName || 'common.Loading') }; }, [chatHistory, isChatting, t]); /* style end */ @@ -496,6 +497,7 @@ const ChatBox = ( // page change and abort request useEffect(() => { isNewChatReplace.current = false; + setQuestionGuide([]); return () => { chatController.current?.abort('leave'); if (!isNewChatReplace.current) { @@ -750,38 +752,28 @@ const ChatBox = ( {index === chatHistory.length - 1 && !isChatting && questionGuides.length > 0 && ( - - - {t('chat.Question Guide Tips')} - - {questionGuides.map((item) => ( - - ))} - + + + + {questionGuides.map((item) => ( + + ))} + + )} {/* admin mark content */} {showMarkIcon && item.adminFeedback && ( diff --git a/projects/app/src/components/Icon/icons/common/navbar/pluginFill.svg b/projects/app/src/components/Icon/icons/common/navbar/pluginFill.svg new file mode 100644 index 000000000..516bf45ba --- /dev/null +++ b/projects/app/src/components/Icon/icons/common/navbar/pluginFill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/src/components/Icon/icons/common/navbar/pluginLight.svg b/projects/app/src/components/Icon/icons/common/navbar/pluginLight.svg new file mode 100644 index 000000000..e53209d80 --- /dev/null +++ b/projects/app/src/components/Icon/icons/common/navbar/pluginLight.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/src/components/Icon/icons/common/refreshLight.svg b/projects/app/src/components/Icon/icons/common/refreshLight.svg new file mode 100644 index 000000000..b7181a531 --- /dev/null +++ b/projects/app/src/components/Icon/icons/common/refreshLight.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/src/components/Icon/icons/core/chat/QGFill.svg b/projects/app/src/components/Icon/icons/core/chat/QGFill.svg new file mode 100644 index 000000000..1974fb398 --- /dev/null +++ b/projects/app/src/components/Icon/icons/core/chat/QGFill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/src/components/Icon/icons/core/chat/quoteFill.svg b/projects/app/src/components/Icon/icons/core/chat/quoteFill.svg new file mode 100644 index 000000000..e09f3fc29 --- /dev/null +++ b/projects/app/src/components/Icon/icons/core/chat/quoteFill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/src/components/Icon/icons/core/module/previewLight.svg b/projects/app/src/components/Icon/icons/core/module/previewLight.svg new file mode 100644 index 000000000..28480c1da --- /dev/null +++ b/projects/app/src/components/Icon/icons/core/module/previewLight.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/src/components/Icon/index.tsx b/projects/app/src/components/Icon/index.tsx index 3d6a38a72..11dc05a68 100644 --- a/projects/app/src/components/Icon/index.tsx +++ b/projects/app/src/components/Icon/index.tsx @@ -92,7 +92,13 @@ const iconPaths = { pause: () => import('./icons/common/pause.svg'), 'core/app/aiLight': () => import('./icons/core/app/aiLight.svg'), 'core/app/aiFill': () => import('./icons/core/app/aiFill.svg'), - 'common/text/t': () => import('./icons/common/text/t.svg') + 'common/text/t': () => import('./icons/common/text/t.svg'), + 'common/navbar/pluginLight': () => import('./icons/common/navbar/pluginLight.svg'), + 'common/navbar/pluginFill': () => import('./icons/common/navbar/pluginFill.svg'), + 'common/refreshLight': () => import('./icons/common/refreshLight.svg'), + 'core/module/previewLight': () => import('./icons/core/module/previewLight.svg'), + 'core/chat/quoteFill': () => import('./icons/core/chat/quoteFill.svg'), + 'core/chat/QGFill': () => import('./icons/core/chat/QGFill.svg') }; export type IconName = keyof typeof iconPaths; diff --git a/projects/app/src/components/Layout/navbar.tsx b/projects/app/src/components/Layout/navbar.tsx index cf121318a..8a227ece8 100644 --- a/projects/app/src/components/Layout/navbar.tsx +++ b/projects/app/src/components/Layout/navbar.tsx @@ -40,6 +40,13 @@ const Navbar = ({ unread }: { unread: number }) => { link: `/app/list`, activeLink: ['/app/list', '/app/detail'] }, + { + label: t('navbar.Plugin'), + icon: 'common/navbar/pluginLight', + activeIcon: 'common/navbar/pluginFill', + link: `/plugin/list`, + activeLink: ['/plugin/list', '/plugin/edit'] + }, { label: t('navbar.Datasets'), icon: 'dbLight', diff --git a/projects/app/src/components/MyModal/index.tsx b/projects/app/src/components/MyModal/index.tsx index a78cb1bf3..24dab5b79 100644 --- a/projects/app/src/components/MyModal/index.tsx +++ b/projects/app/src/components/MyModal/index.tsx @@ -43,7 +43,6 @@ const MyModal = ({ {...props} > {!!title && {title}} - {onClose && } {children} + {onClose && } ); diff --git a/projects/app/src/components/PageContainer/index.tsx b/projects/app/src/components/PageContainer/index.tsx index 68fab65fa..f74bb2325 100644 --- a/projects/app/src/components/PageContainer/index.tsx +++ b/projects/app/src/components/PageContainer/index.tsx @@ -1,10 +1,11 @@ import React from 'react'; import { Box, useTheme, type BoxProps } from '@chakra-ui/react'; +import MyBox from '../common/MyBox'; -const PageContainer = ({ children, ...props }: BoxProps) => { +const PageContainer = ({ children, ...props }: BoxProps & { isLoading?: boolean }) => { const theme = useTheme(); return ( - + { > {children} - + ); }; diff --git a/projects/app/src/components/PromptTemplate/index.tsx b/projects/app/src/components/PromptTemplate/index.tsx index 46cf770ba..82be932c8 100644 --- a/projects/app/src/components/PromptTemplate/index.tsx +++ b/projects/app/src/components/PromptTemplate/index.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import MyModal from '../MyModal'; -import { Box, Button, Grid, useTheme } from '@chakra-ui/react'; +import { Box, Button, Flex, Grid, useTheme } from '@chakra-ui/react'; import { PromptTemplateItem } from '@fastgpt/global/core/ai/type.d'; import { ModalBody, ModalFooter } from '@chakra-ui/react'; @@ -13,14 +13,14 @@ const PromptTemplate = ({ title: string; templates: PromptTemplateItem[]; onClose: () => void; - onSuccess: (e: string) => void; + onSuccess: (e: PromptTemplateItem) => void; }) => { const theme = useTheme(); const [selectTemplateTitle, setSelectTemplateTitle] = useState(); return ( - - + + {templates.map((item) => ( setSelectTemplateTitle(item)} > {item.title} + - {item.value} + {item.desc} ))} @@ -50,7 +51,7 @@ const PromptTemplate = ({ disabled={!selectTemplateTitle} onClick={() => { if (!selectTemplateTitle) return; - onSuccess(selectTemplateTitle.value); + onSuccess(selectTemplateTitle); onClose(); }} > diff --git a/projects/app/src/components/common/MyBox/index.tsx b/projects/app/src/components/common/MyBox/index.tsx new file mode 100644 index 000000000..e11ec6d26 --- /dev/null +++ b/projects/app/src/components/common/MyBox/index.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Box, BoxProps } from '@chakra-ui/react'; +import Loading from '@/components/Loading'; + +type Props = BoxProps & { + isLoading?: boolean; + text?: string; +}; + +const MyBox = ({ text, isLoading, children, ...props }: Props) => { + return ( + + {children} + {isLoading && } + + ); +}; + +export default MyBox; diff --git a/projects/app/src/components/common/ParentPaths/index.tsx b/projects/app/src/components/common/ParentPaths/index.tsx index 14db723d8..b3cd6ae69 100644 --- a/projects/app/src/components/common/ParentPaths/index.tsx +++ b/projects/app/src/components/common/ParentPaths/index.tsx @@ -30,9 +30,9 @@ const ParentPaths = (props: { {concatPaths.map((item, i) => ( {i !== concatPaths.length - 1 && ( - + )} ))} diff --git a/projects/app/src/components/core/chat/Divider/index.tsx b/projects/app/src/components/core/chat/Divider/index.tsx new file mode 100644 index 000000000..96e5e4270 --- /dev/null +++ b/projects/app/src/components/core/chat/Divider/index.tsx @@ -0,0 +1,19 @@ +import { Box, Flex } from '@chakra-ui/react'; +import React from 'react'; +import MyIcon, { type IconName } from '@/components/Icon'; + +const ChatBoxDivider = ({ icon, text }: { icon: IconName; text: string }) => { + return ( + + + + + {text} + + + + + ); +}; + +export default ChatBoxDivider; diff --git a/projects/app/src/pages/app/detail/components/AIChatSettingsModal.tsx b/projects/app/src/components/core/module/AIChatSettingsModal.tsx similarity index 91% rename from projects/app/src/pages/app/detail/components/AIChatSettingsModal.tsx rename to projects/app/src/components/core/module/AIChatSettingsModal.tsx index dcc5a3645..79910cf9e 100644 --- a/projects/app/src/pages/app/detail/components/AIChatSettingsModal.tsx +++ b/projects/app/src/components/core/module/AIChatSettingsModal.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import MyModal from '@/components/MyModal'; import { useTranslation } from 'react-i18next'; import { EditFormType } from '@/web/core/app/basicSettings'; @@ -45,7 +45,6 @@ const AIChatSettingsModal = ({ const [selectTemplateData, setSelectTemplateData] = useState<{ title: string; - key: 'quoteTemplate' | 'quotePrompt'; templates: PromptTemplateItem[]; }>(); @@ -163,8 +162,7 @@ const AIChatSettingsModal = ({ {...selectTemplateBtn} onClick={() => setSelectTemplateData({ - title: '选择引用内容模板', - key: 'quoteTemplate', + title: '选择知识库提示词模板', templates: Prompt_QuoteTemplateList }) } @@ -190,19 +188,6 @@ const AIChatSettingsModal = ({ > - - - setSelectTemplateData({ - title: '选择引用提示词模板', - key: 'quotePrompt', - templates: Prompt_QuotePromptList - }) - } - > - 选择模板 -