Compare commits
41 Commits
v4.8.13
...
v4.8.13-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2423a096c5 | ||
|
|
f68ae33cd8 | ||
|
|
211061d122 | ||
|
|
dc8aeefa2a | ||
|
|
cdce94a202 | ||
|
|
5e273341dd | ||
|
|
73d28d1fc3 | ||
|
|
5892ded567 | ||
|
|
f4e0dfc9bd | ||
|
|
d758ddb47c | ||
|
|
acdf6a0dd9 | ||
|
|
0201e63cfd | ||
|
|
e9f0d5dad5 | ||
|
|
11134f39e4 | ||
|
|
a5765ae32f | ||
|
|
72836402be | ||
|
|
8bd0749afe | ||
|
|
49aaf9b77e | ||
|
|
6c6c964b8a | ||
|
|
c5022654ca | ||
|
|
0a238845ab | ||
|
|
8ede7add01 | ||
|
|
91645cc420 | ||
|
|
4d6f736be3 | ||
|
|
f1fb85ead0 | ||
|
|
34fbd5a223 | ||
|
|
45aa2e7374 | ||
|
|
f58dba2eda | ||
|
|
78ef74f902 | ||
|
|
8303933ec2 | ||
|
|
2388652858 | ||
|
|
ba61c9e2e6 | ||
|
|
b0a14c585d | ||
|
|
0d494fde45 | ||
|
|
5a76b6f76d | ||
|
|
e78fa26ca7 | ||
|
|
16280e5d94 | ||
|
|
e96f09ca9f | ||
|
|
788d87a26f | ||
|
|
dc1119ca90 | ||
|
|
9e8138e55f |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 338 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 174 KiB |
@@ -32,7 +32,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv464' \
|
|||||||
4. 优化 - 历史记录模块。弃用旧的历史记录模块,直接在对应地方填写数值即可。
|
4. 优化 - 历史记录模块。弃用旧的历史记录模块,直接在对应地方填写数值即可。
|
||||||
5. 调整 - 知识库搜索模块 topk 逻辑,采用 MaxToken 计算,兼容不同长度的文本块
|
5. 调整 - 知识库搜索模块 topk 逻辑,采用 MaxToken 计算,兼容不同长度的文本块
|
||||||
6. 调整鉴权顺序,提高 apikey 的优先级,避免cookie抢占 apikey 的鉴权。
|
6. 调整鉴权顺序,提高 apikey 的优先级,避免cookie抢占 apikey 的鉴权。
|
||||||
7. 链接读取支持多选择器。参考[Web 站点同步用法](/docs/guide/knowledge_base/websync/)
|
7. 链接读取支持多选择器。参考[Web 站点同步用法](/docs/course/websync)
|
||||||
8. 修复 - 分享链接图片上传鉴权问题
|
8. 修复 - 分享链接图片上传鉴权问题
|
||||||
9. 修复 - Mongo 连接池未释放问题。
|
9. 修复 - Mongo 连接池未释放问题。
|
||||||
10. 修复 - Dataset Intro 无法更新
|
10. 修复 - Dataset Intro 无法更新
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ weight: 831
|
|||||||
|
|
||||||
## V4.6.5 功能介绍
|
## V4.6.5 功能介绍
|
||||||
|
|
||||||
1. 新增 - [问题优化模块](/docs/guide/workbench/workflow/coreferenceresolution/)
|
1. 新增 - [问题优化模块](/docs/workflow/modules/coreferenceresolution/)
|
||||||
2. 新增 - [文本编辑模块](/docs/guide/workbench/workflow/text_editor/)
|
2. 新增 - [文本编辑模块](/docs/workflow/modules/text_editor/)
|
||||||
3. 新增 - [判断器模块](/docs/guide/workbench/workflow/tfswitch//)
|
3. 新增 - [判断器模块](/docs/workflow/modules/tfswitch/)
|
||||||
4. 新增 - [自定义反馈模块](/docs/guide/workbench/workflow/custom_feedback/)
|
4. 新增 - [自定义反馈模块](/docs/workflow/modules/custom_feedback/)
|
||||||
5. 新增 - 【内容提取】模块支持选择模型,以及字段枚举
|
5. 新增 - 【内容提取】模块支持选择模型,以及字段枚举
|
||||||
6. 优化 - docx读取,兼容表格(表格转markdown)
|
6. 优化 - docx读取,兼容表格(表格转markdown)
|
||||||
7. 优化 - 高级编排连接线交互
|
7. 优化 - 高级编排连接线交互
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ weight: 830
|
|||||||
|
|
||||||
1. 查看 [FastGPT 2024 RoadMap](https://github.com/labring/FastGPT?tab=readme-ov-file#-%E5%9C%A8%E7%BA%BF%E4%BD%BF%E7%94%A8)
|
1. 查看 [FastGPT 2024 RoadMap](https://github.com/labring/FastGPT?tab=readme-ov-file#-%E5%9C%A8%E7%BA%BF%E4%BD%BF%E7%94%A8)
|
||||||
2. 新增 - Http 模块请求头支持 Json 编辑器。
|
2. 新增 - Http 模块请求头支持 Json 编辑器。
|
||||||
3. 新增 - [ReRank模型部署](/docs/development/custom-models/bge-rerank/)
|
3. 新增 - [ReRank模型部署](/docs/development/custom-models/reranker/)
|
||||||
4. 新增 - 搜索方式:分离向量语义检索,全文检索和重排,通过 RRF 进行排序合并。
|
4. 新增 - 搜索方式:分离向量语义检索,全文检索和重排,通过 RRF 进行排序合并。
|
||||||
5. 优化 - 问题分类提示词,id引导。测试国产商用 api 模型(百度阿里智谱讯飞)使用 Prompt 模式均可分类。
|
5. 优化 - 问题分类提示词,id引导。测试国产商用 api 模型(百度阿里智谱讯飞)使用 Prompt 模式均可分类。
|
||||||
6. UI 优化,未来将逐步替换新的UI设计。
|
6. UI 优化,未来将逐步替换新的UI设计。
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ curl --location --request POST 'https://{{host}}/api/init/v468' \
|
|||||||
|
|
||||||
1. 新增 - 知识库搜索合并模块。
|
1. 新增 - 知识库搜索合并模块。
|
||||||
2. 新增 - 新的 Http 模块,支持更加灵活的参数传入。同时支持了输入输出自动数据类型转化,例如:接口输出的 JSON 类型会自动转成字符串类型,直接给其他模块使用。此外,还补充了一些例子,可在文档中查看。
|
2. 新增 - 新的 Http 模块,支持更加灵活的参数传入。同时支持了输入输出自动数据类型转化,例如:接口输出的 JSON 类型会自动转成字符串类型,直接给其他模块使用。此外,还补充了一些例子,可在文档中查看。
|
||||||
3. 优化 - 内容补全。将内容补全内置到【知识库搜索】中,并实现了一次内容补全,即可完成“指代消除”和“问题扩展”。FastGPT知识库搜索详细流程可查看:[知识库搜索介绍](/docs/guide/workbench/workflow/dataset_search/)
|
3. 优化 - 内容补全。将内容补全内置到【知识库搜索】中,并实现了一次内容补全,即可完成“指代消除”和“问题扩展”。FastGPT知识库搜索详细流程可查看:[知识库搜索介绍](/docs/course/data_search/)
|
||||||
4. 优化 - LLM 模型配置,不再区分对话、分类、提取模型。同时支持模型的默认参数,避免不同模型参数冲突,可通过`defaultConfig`传入默认的配置。
|
4. 优化 - LLM 模型配置,不再区分对话、分类、提取模型。同时支持模型的默认参数,避免不同模型参数冲突,可通过`defaultConfig`传入默认的配置。
|
||||||
5. 优化 - 流响应,参考了`ChatNextWeb`的流,更加丝滑。此外,之前提到的乱码、中断,刷新后又正常了,可能会修复)
|
5. 优化 - 流响应,参考了`ChatNextWeb`的流,更加丝滑。此外,之前提到的乱码、中断,刷新后又正常了,可能会修复)
|
||||||
6. 修复 - 语音输入文件无法上传。
|
6. 修复 - 语音输入文件无法上传。
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ weight: 811
|
|||||||
|
|
||||||
### 2. 修改镜像
|
### 2. 修改镜像
|
||||||
|
|
||||||
- 更新 FastGPT 镜像 tag: v4.8.13-beta
|
- 更新 FastGPT 镜像 tag: v4.8.13-alpha
|
||||||
- 更新 FastGPT 商业版镜像 tag: v4.8.13-beta (fastgpt-pro镜像)
|
- 更新 FastGPT 管理端镜像 tag: v4.8.13-alpha (fastgpt-pro镜像)
|
||||||
- Sandbox 镜像,可以不更新
|
- Sandbox 镜像,可以不更新
|
||||||
|
|
||||||
### 3. 调整文件上传编排
|
### 3. 调整文件上传编排
|
||||||
@@ -39,7 +39,6 @@ weight: 811
|
|||||||
14. 优化 - Markdown 组件自动空格,避免分割 url 中的中文。
|
14. 优化 - Markdown 组件自动空格,避免分割 url 中的中文。
|
||||||
15. 优化 - 工作流上下文拆分,性能优化。
|
15. 优化 - 工作流上下文拆分,性能优化。
|
||||||
16. 优化 - 语音播报,不支持 mediaSource 的浏览器可等待完全生成语音后输出。
|
16. 优化 - 语音播报,不支持 mediaSource 的浏览器可等待完全生成语音后输出。
|
||||||
17. 优化 - 对话引导 csv 读取,自动识别编码。
|
17. 修复 - Dockerfile pnpm install 支持代理。
|
||||||
18. 修复 - Dockerfile pnpm install 支持代理。
|
18. 修复 - BI 图表生成无法写入文件。同时优化其解析,支持数字类型数组。
|
||||||
19. 修复 - BI 图表生成无法写入文件。同时优化其解析,支持数字类型数组。
|
19. 修复 - 分享链接首次加载时,标题显示不正确。
|
||||||
20. 修复 - 分享链接首次加载时,标题显示不正确。
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ Tips: 可以通过点击上下文按键查看完整的上下文组成,便于
|
|||||||
|
|
||||||
FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变量)的格式存储,在转义成字符串时候会根据**引用模板**来进行格式化。知识库包含多个可用变量: q, a, sourceId(数据的ID), index(第n个数据), source(数据的集合名、文件名),score(距离得分,0-1) 可以通过 {{q}} {{a}} {{sourceId}} {{index}} {{source}} {{score}} 按需引入。下面一个模板例子:
|
FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变量)的格式存储,在转义成字符串时候会根据**引用模板**来进行格式化。知识库包含多个可用变量: q, a, sourceId(数据的ID), index(第n个数据), source(数据的集合名、文件名),score(距离得分,0-1) 可以通过 {{q}} {{a}} {{sourceId}} {{index}} {{source}} {{score}} 按需引入。下面一个模板例子:
|
||||||
|
|
||||||
可以通过 [知识库结构讲解](/docs/guide/knowledge_base/dataset_engine/) 了解详细的知识库的结构。
|
可以通过 [知识库结构讲解](/docs/course/dataset_engine/) 了解详细的知识库的结构。
|
||||||
|
|
||||||
#### 引用模板
|
#### 引用模板
|
||||||
|
|
||||||
|
|||||||
@@ -30,5 +30,5 @@ weight: 232
|
|||||||
|
|
||||||
|
|
||||||
{{% alert icon="🍅" context="success" %}}
|
{{% alert icon="🍅" context="success" %}}
|
||||||
具体配置参数介绍可以参考: [AI参数配置说明](/docs/guide/course/ai_settings/)
|
具体配置参数介绍可以参考: [AI参数配置说明](/docs/course/ai_settings)
|
||||||
{{% /alert %}}
|
{{% /alert %}}
|
||||||
@@ -36,4 +36,4 @@ weight: 264
|
|||||||
|
|
||||||
## 示例
|
## 示例
|
||||||
|
|
||||||
- [接入谷歌搜索](/docs/use-cases/app-cases/google_search/)
|
- [接入谷歌搜索](/docs/workflow/examples/google_search/)
|
||||||
@@ -5,28 +5,4 @@ icon: "form_input"
|
|||||||
draft: false
|
draft: false
|
||||||
toc: true
|
toc: true
|
||||||
weight: 244
|
weight: 244
|
||||||
---
|
---
|
||||||
|
|
||||||
## 特点
|
|
||||||
|
|
||||||
- 用户交互
|
|
||||||
- 可重复添加
|
|
||||||
- 触发执行
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## 功能
|
|
||||||
|
|
||||||
「表单输入」节点属于用户交互节点,当触发这个节点时,对话会进入“交互”状态,会记录工作流的状态,等用户完成交互后,继续向下执行工作流
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
比如上图中的例子,当触发表单输入节点时,对话框隐藏,对话进入“交互状态”
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
当用户填完必填的信息并点击提交后,节点能够收集用户填写的表单信息,传递到后续的节点中使用
|
|
||||||
|
|
||||||
## 作用
|
|
||||||
|
|
||||||
能够精准收集需要的用户信息,再根据用户信息进行后续操作
|
|
||||||
@@ -250,6 +250,6 @@ export default async function (ctx: FunctionContext) {
|
|||||||
|
|
||||||
## 相关示例
|
## 相关示例
|
||||||
|
|
||||||
- [谷歌搜索](/docs/use-cases/app-cases/google_search/)
|
- [谷歌搜索](/docs/workflow/examples/google_search/)
|
||||||
- [发送飞书webhook](/docs/use-cases/app-cases/feishu_webhook/)
|
- [发送飞书webhook](/docs/workflow/examples/feishu_webhook/)
|
||||||
- [实验室预约(操作数据库)](/docs/use-cases/app-cases/lab_appointment/)
|
- [实验室预约(操作数据库)](/docs/workflow/examples/lab_appointment/)
|
||||||
|
|||||||
@@ -29,4 +29,4 @@ weight: 246
|
|||||||
|
|
||||||
## 示例
|
## 示例
|
||||||
|
|
||||||
- [接入谷歌搜索](/docs/use-cases/app-cases/google_search/)
|
- [接入谷歌搜索](/docs/workflow/examples/google_search/)
|
||||||
@@ -7,21 +7,20 @@ toc: true
|
|||||||
weight: 236
|
weight: 236
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### **什么是工具**
|
## 什么是工具
|
||||||
|
|
||||||
工具可以是一个系统模块,例如:AI对话、知识库搜索、HTTP模块等。也可以是一个插件。
|
工具可以是一个系统模块,例如:AI对话、知识库搜索、HTTP模块等。也可以是一个插件。
|
||||||
|
|
||||||
工具调用可以让 LLM 更动态的决策流程,而不都是固定的流程。(当然,缺点就是费tokens)
|
工具调用可以让 LLM 更动态的决策流程,而不都是固定的流程。(当然,缺点就是费tokens)
|
||||||
|
|
||||||
### **工具的组成**
|
## 工具的组成
|
||||||
|
|
||||||
1. 工具介绍。通常是模块的介绍或插件的介绍,这个介绍会告诉LLM,这个工具的作用是什么。
|
1. 工具介绍。通常是模块的介绍或插件的介绍,这个介绍会告诉LLM,这个工具的作用是什么。
|
||||||
2. 工具参数。对于系统模块来说,工具参数已经是固定的,无需额外配置。对于插件来说,工具参数是一个可配置项。
|
2. 工具参数。对于系统模块来说,工具参数已经是固定的,无需额外配置。对于插件来说,工具参数是一个可配置项。
|
||||||
|
|
||||||
### **工具是如何运行的**
|
## 工具是如何运行的
|
||||||
|
|
||||||
要了解工具如何运行的,首先需要知道它的运行条件。
|
要了解工具如何运行的,首先需要知道它的运行条件。
|
||||||
|
|
||||||
@@ -30,57 +29,43 @@ weight: 236
|
|||||||
|
|
||||||
结合工具的介绍、参数介绍和参数是否必须,LLM会决定是否调用这个工具。有以下几种情况:
|
结合工具的介绍、参数介绍和参数是否必须,LLM会决定是否调用这个工具。有以下几种情况:
|
||||||
|
|
||||||
|
|
||||||
1. 无参数的工具:直接根据工具介绍,决定是否需要执行。例如:获取当前时间。
|
1. 无参数的工具:直接根据工具介绍,决定是否需要执行。例如:获取当前时间。
|
||||||
2. 有参数的工具:
|
2. 有参数的工具:
|
||||||
1. 无必须的参数:尽管上下文中,没有适合的参数,也可以调用该工具。但有时候,LLM会自己伪造一个参数。
|
1. 无必须的参数:尽管上下文中,没有适合的参数,也可以调用该工具。但有时候,LLM会自己伪造一个参数。
|
||||||
2. 有必须的参数:如果没有适合的参数,LLM可能不会调用该工具。可以通过提示词,引导用户提供参数。
|
2. 有必须的参数:如果没有适合的参数,LLM可能不会调用该工具。可以通过提示词,引导用户提供参数。
|
||||||
|
|
||||||
#### **工具调用逻辑**
|
|
||||||
|
|
||||||
在支持`函数调用`的模型中,可以一次性调用多个工具,调用逻辑如下:
|
在支持`函数调用`的模型中,可以一次性调用多个工具,调用逻辑如下:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### **怎么用**
|
## 怎么用
|
||||||
|
|
||||||
<div style="display: flex; gap: 10px;">
|
| 有工具调用模块 | 无工具调用模块 |
|
||||||
<img src="/imgs/flow-tool3.png" alt="工具调用模块示例 3" width="40%" />
|
| --- | --- |
|
||||||
<img src="/imgs/flow-tool4.png" alt="工具调用模块示例 4" width="60%" />
|
|  |  |
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ! -->
|
高级编排中,托动工具调用的连接点,可用的工具头部会出现一个菱形,可以将它与工具调用模块底部的菱形相连接。
|
||||||
|
|
||||||
高级编排中,拖动工具调用的连接点,可用的工具头部会出现一个菱形,可以将它与工具调用模块底部的菱形相连接。
|
被连接的工具,会自动分离工具输入与普通的输入,并且可以编辑`描述`,可以通过调整介绍,使得该工具调用时机更加精确。对于一些内置的节点,务必修改`描述`才能让模型正常调用。
|
||||||
|
|
||||||
被连接的工具,会自动分离工具输入与普通的输入,并且可以编辑`介绍`,可以通过调整介绍,使得该工具调用时机更加精确。
|
|
||||||
|
|
||||||
关于工具调用,如何调试仍然是一个玄学,所以建议,不要一次性增加太多工具,选择少量工具调优后再进一步尝试。
|
关于工具调用,如何调试仍然是一个玄学,所以建议,不要一次性增加太多工具,选择少量工具调优后再进一步尝试。
|
||||||
|
|
||||||
#### 用途
|
## 组合节点
|
||||||
|
|
||||||
默认清空下,工具调用节点,在决定调用工具后,会将工具运行的结果,返回给AI,让 AI 对工具运行的结果进行总结输出。有时候,如果你不需要 AI 进行进一步的总结输出,可以使用该节点,将其接入对于工具流程的末尾。
|
### 工具调用终止
|
||||||
|
|
||||||
如下图,在执行知识库搜索后,发送给了 HTTP 请求,搜索将不会返回搜索的结果给工具调用进行 AI 总结。
|
工具调用默认会把子流程运行的结果作为`工具结果`,返回给模型进行回答。有时候,你可能不希望模型做回答,你可以给对应子流程的末尾增加上一个`工具调用终止`节点,这样,子流程的结果就不会被返回给模型。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### 附加节点
|
|
||||||
|
|
||||||
当您使用了工具调用节点,同时就会出现工具调用终止节点和自定义变量节点,能够进一步提升工具调用的使用体验。
|
|
||||||
|
|
||||||
#### 工具调用终止
|
|
||||||
|
|
||||||
工具调用终止可用于结束本次调用,即可以接在某个工具后面,当工作流执行到这个节点时,便会强制结束本次工具调用,不再调用其他工具,也不会再调用 AI 针对工具调用结果回答问题。
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 自定义工具变量
|
### 自定义工具变量
|
||||||
|
|
||||||
自定义变量可以扩展工具的变量输入,即对于一些未被视作工具参数或无法工具调用的节点,可以自定义工具变量,填上对应的参数描述,那么工具调用便会相对应的调用这个节点,进而调用其之后的工作流。
|
工具调用的子流程运行,有时候会依赖`AI`生成的一些变量,为了简化交互流程,我们给系统内置的节点都指定了`工具变量`。然而,有些时候,你需要的变量不仅是目标流程的`首个节点`的变量,而是需要更复杂的变量,此时你可以使用`自定义工具变量`。它允许你完全自定义该`工具流程`的变量。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### **相关示例**
|
## 相关示例
|
||||||
|
|
||||||
- [谷歌搜索](https://doc.fastgpt.in/docs/use-cases/app-cases/google_search/)
|
- [谷歌搜索](/docs/workflow/examples/google_search/)
|
||||||
- [发送飞书webhook](https://doc.fastgpt.in/docs/use-cases/app-cases/feishu_webhook/)
|
- [发送飞书webhook](/docs/workflow/examples/feishu_webhook/)
|
||||||
@@ -11,7 +11,7 @@ weight: 509
|
|||||||
|
|
||||||
[chatgpt-on-wechat GitHub 地址](https://github.com/zhayujie/chatgpt-on-wechat)
|
[chatgpt-on-wechat GitHub 地址](https://github.com/zhayujie/chatgpt-on-wechat)
|
||||||
|
|
||||||
由于 FastGPT 的 API 接口和 OpenAI 的规范一致,可以无需变更原来的应用即可使用 FastGPT 上编排好的应用。API 使用可参考 [这篇文章](/docs/use-cases/external-integration/openapi/)。编排示例,可参考 [高级编排介绍](/docs/workflow/intro)
|
由于 FastGPT 的 API 接口和 OpenAI 的规范一致,可以无需变更原来的应用即可使用 FastGPT 上编排好的应用。API 使用可参考 [这篇文章](/docs/course/openapi/)。编排示例,可参考 [高级编排介绍](/docs/workflow/intro)
|
||||||
|
|
||||||
## 1. 获取 OpenAPI 密钥
|
## 1. 获取 OpenAPI 密钥
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const bucketNameMap = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ReadFileBaseUrl = `${process.env.FE_DOMAIN || ''}${process.env.NEXT_PUBLIC_BASE_URL || ''}/api/common/file/read`;
|
export const ReadFileBaseUrl = `${process.env.FE_DOMAIN || ''}${process.env.NEXT_PUBLIC_BASE_URL}/api/common/file/read`;
|
||||||
|
|
||||||
export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx';
|
export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx';
|
||||||
export const imageFileType =
|
export const imageFileType =
|
||||||
|
|||||||
@@ -78,15 +78,11 @@ export const getHistoryPreview = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const filterPublicNodeResponseData = ({
|
export const filterPublicNodeResponseData = ({
|
||||||
flowResponses = [],
|
flowResponses = []
|
||||||
responseDetail = false
|
|
||||||
}: {
|
}: {
|
||||||
flowResponses?: ChatHistoryItemResType[];
|
flowResponses?: ChatHistoryItemResType[];
|
||||||
responseDetail?: boolean;
|
|
||||||
}) => {
|
}) => {
|
||||||
const filedList = responseDetail
|
const filedList = ['quoteList', 'moduleType', 'pluginOutput', 'runningTime'];
|
||||||
? ['quoteList', 'moduleType', 'pluginOutput', 'runningTime']
|
|
||||||
: ['moduleType', 'pluginOutput', 'runningTime'];
|
|
||||||
const filterModuleTypeList: any[] = [
|
const filterModuleTypeList: any[] = [
|
||||||
FlowNodeTypeEnum.pluginModule,
|
FlowNodeTypeEnum.pluginModule,
|
||||||
FlowNodeTypeEnum.datasetSearchNode,
|
FlowNodeTypeEnum.datasetSearchNode,
|
||||||
|
|||||||
@@ -95,10 +95,10 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: NodeInputKeyEnum.collectionFilterMatch,
|
key: NodeInputKeyEnum.collectionFilterMatch,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
|
renderTypeList: [FlowNodeInputTypeEnum.JSONEditor, FlowNodeInputTypeEnum.reference],
|
||||||
label: i18nT('workflow:collection_metadata_filter'),
|
label: i18nT('workflow:collection_metadata_filter'),
|
||||||
|
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.object,
|
||||||
isPro: true,
|
isPro: true,
|
||||||
description: i18nT('workflow:filter_description')
|
description: i18nT('workflow:filter_description')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
|
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
|
||||||
import OpenAI from '@fastgpt/global/core/ai';
|
import OpenAI from '@fastgpt/global/core/ai';
|
||||||
import {
|
|
||||||
ChatCompletionCreateParamsNonStreaming,
|
|
||||||
ChatCompletionCreateParamsStreaming
|
|
||||||
} from '@fastgpt/global/core/ai/type';
|
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
|
||||||
import { addLog } from '../../common/system/log';
|
|
||||||
|
|
||||||
export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
|
export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
|
||||||
|
|
||||||
@@ -40,55 +34,3 @@ export const getAxiosConfig = (props?: { userKey?: UserModelSchema['openaiAccoun
|
|||||||
authorization: `Bearer ${apiKey}`
|
authorization: `Bearer ${apiKey}`
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type CompletionsBodyType =
|
|
||||||
| ChatCompletionCreateParamsNonStreaming
|
|
||||||
| ChatCompletionCreateParamsStreaming;
|
|
||||||
type InferResponseType<T extends CompletionsBodyType> =
|
|
||||||
T extends ChatCompletionCreateParamsStreaming
|
|
||||||
? OpenAI.Chat.Completions.ChatCompletionChunk
|
|
||||||
: OpenAI.Chat.Completions.ChatCompletion;
|
|
||||||
|
|
||||||
export const createChatCompletion = async <T extends CompletionsBodyType>({
|
|
||||||
body,
|
|
||||||
userKey,
|
|
||||||
timeout,
|
|
||||||
options
|
|
||||||
}: {
|
|
||||||
body: T;
|
|
||||||
userKey?: UserModelSchema['openaiAccount'];
|
|
||||||
timeout?: number;
|
|
||||||
options?: OpenAI.RequestOptions;
|
|
||||||
}): Promise<{
|
|
||||||
response: InferResponseType<T>;
|
|
||||||
isStreamResponse: boolean;
|
|
||||||
}> => {
|
|
||||||
try {
|
|
||||||
const formatTimeout = timeout ? timeout : body.stream ? 60000 : 600000;
|
|
||||||
const ai = getAIApi({
|
|
||||||
userKey,
|
|
||||||
timeout: formatTimeout
|
|
||||||
});
|
|
||||||
const response = await ai.chat.completions.create(body, options);
|
|
||||||
|
|
||||||
const isStreamResponse =
|
|
||||||
typeof response === 'object' &&
|
|
||||||
response !== null &&
|
|
||||||
('iterator' in response || 'controller' in response);
|
|
||||||
|
|
||||||
return {
|
|
||||||
response: response as InferResponseType<T>,
|
|
||||||
isStreamResponse
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
addLog.error(`LLM response error`, error);
|
|
||||||
addLog.warn(`LLM response error`, {
|
|
||||||
baseUrl: userKey?.baseUrl,
|
|
||||||
requestBody: body
|
|
||||||
});
|
|
||||||
if (userKey?.baseUrl) {
|
|
||||||
return Promise.reject(`您的 OpenAI key 出错了: ${getErrText(error)}`);
|
|
||||||
}
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export async function getVectorsByText({ model, input, type }: GetVectorProps) {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addLog.error(`Embedding Error`, error);
|
console.log(`Embedding Error`, error);
|
||||||
|
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type.d';
|
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type.d';
|
||||||
import { createChatCompletion } from '../config';
|
import { getAIApi } from '../config';
|
||||||
import { countGptMessagesTokens } from '../../../common/string/tiktoken/index';
|
import { countGptMessagesTokens } from '../../../common/string/tiktoken/index';
|
||||||
import { loadRequestMessages } from '../../chat/utils';
|
import { loadRequestMessages } from '../../chat/utils';
|
||||||
import { llmCompletionsBodyFormat } from '../utils';
|
import { llmCompletionsBodyFormat } from '../utils';
|
||||||
@@ -29,8 +29,11 @@ export async function createQuestionGuide({
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const { response: data } = await createChatCompletion({
|
const ai = getAIApi({
|
||||||
body: llmCompletionsBodyFormat(
|
timeout: 480000
|
||||||
|
});
|
||||||
|
const data = await ai.chat.completions.create(
|
||||||
|
llmCompletionsBodyFormat(
|
||||||
{
|
{
|
||||||
model,
|
model,
|
||||||
temperature: 0.1,
|
temperature: 0.1,
|
||||||
@@ -43,7 +46,7 @@ export async function createQuestionGuide({
|
|||||||
},
|
},
|
||||||
model
|
model
|
||||||
)
|
)
|
||||||
});
|
);
|
||||||
|
|
||||||
const answer = data.choices?.[0]?.message?.content || '';
|
const answer = data.choices?.[0]?.message?.content || '';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||||
import { createChatCompletion } from '../config';
|
import { getAIApi } from '../config';
|
||||||
import { ChatItemType } from '@fastgpt/global/core/chat/type';
|
import { ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||||
import { countGptMessagesTokens } from '../../../common/string/tiktoken/index';
|
import { countGptMessagesTokens } from '../../../common/string/tiktoken/index';
|
||||||
|
import { ChatCompletion, ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
|
||||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||||
import { getLLMModel } from '../model';
|
import { getLLMModel } from '../model';
|
||||||
import { llmCompletionsBodyFormat } from '../utils';
|
import { llmCompletionsBodyFormat } from '../utils';
|
||||||
@@ -137,6 +138,10 @@ A: ${chatBg}
|
|||||||
|
|
||||||
const modelData = getLLMModel(model);
|
const modelData = getLLMModel(model);
|
||||||
|
|
||||||
|
const ai = getAIApi({
|
||||||
|
timeout: 480000
|
||||||
|
});
|
||||||
|
|
||||||
const messages = [
|
const messages = [
|
||||||
{
|
{
|
||||||
role: 'user',
|
role: 'user',
|
||||||
@@ -145,19 +150,20 @@ A: ${chatBg}
|
|||||||
histories: concatFewShot
|
histories: concatFewShot
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
] as any;
|
] as ChatCompletionMessageParam[];
|
||||||
|
|
||||||
const { response: result } = await createChatCompletion({
|
const result = (await ai.chat.completions.create(
|
||||||
body: llmCompletionsBodyFormat(
|
llmCompletionsBodyFormat(
|
||||||
{
|
{
|
||||||
stream: false,
|
stream: false,
|
||||||
model: modelData.model,
|
model: modelData.model,
|
||||||
temperature: 0.01,
|
temperature: 0.01,
|
||||||
|
// @ts-ignore
|
||||||
messages
|
messages
|
||||||
},
|
},
|
||||||
modelData
|
modelData
|
||||||
)
|
)
|
||||||
});
|
)) as ChatCompletion;
|
||||||
|
|
||||||
let answer = result.choices?.[0]?.message?.content || '';
|
let answer = result.choices?.[0]?.message?.content || '';
|
||||||
if (!answer) {
|
if (!answer) {
|
||||||
|
|||||||
@@ -48,17 +48,14 @@ export const computedTemperature = ({
|
|||||||
type CompletionsBodyType =
|
type CompletionsBodyType =
|
||||||
| ChatCompletionCreateParamsNonStreaming
|
| ChatCompletionCreateParamsNonStreaming
|
||||||
| ChatCompletionCreateParamsStreaming;
|
| ChatCompletionCreateParamsStreaming;
|
||||||
type InferCompletionsBody<T> = T extends { stream: true }
|
|
||||||
? ChatCompletionCreateParamsStreaming
|
|
||||||
: ChatCompletionCreateParamsNonStreaming;
|
|
||||||
|
|
||||||
export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
|
export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
|
||||||
body: T,
|
body: T,
|
||||||
model: string | LLMModelItemType
|
model: string | LLMModelItemType
|
||||||
): InferCompletionsBody<T> => {
|
) => {
|
||||||
const modelData = typeof model === 'string' ? getLLMModel(model) : model;
|
const modelData = typeof model === 'string' ? getLLMModel(model) : model;
|
||||||
if (!modelData) {
|
if (!modelData) {
|
||||||
return body as InferCompletionsBody<T>;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestBody: T = {
|
const requestBody: T = {
|
||||||
@@ -84,5 +81,5 @@ export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
|
|||||||
|
|
||||||
// console.log(requestBody);
|
// console.log(requestBody);
|
||||||
|
|
||||||
return requestBody as InferCompletionsBody<T>;
|
return requestBody;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -118,10 +118,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
|||||||
let createTimeCollectionIdList: string[] | undefined = undefined;
|
let createTimeCollectionIdList: string[] | undefined = undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const jsonMatch =
|
const jsonMatch = json5.parse(collectionFilterMatch);
|
||||||
typeof collectionFilterMatch === 'object'
|
|
||||||
? collectionFilterMatch
|
|
||||||
: json5.parse(collectionFilterMatch);
|
|
||||||
|
|
||||||
// Tag
|
// Tag
|
||||||
let andTags = jsonMatch?.tags?.$and as (string | null)[] | undefined;
|
let andTags = jsonMatch?.tags?.$and as (string | null)[] | undefined;
|
||||||
@@ -350,7 +347,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
|||||||
teamId: new Types.ObjectId(teamId),
|
teamId: new Types.ObjectId(teamId),
|
||||||
datasetId: new Types.ObjectId(id),
|
datasetId: new Types.ObjectId(id),
|
||||||
$text: { $search: jiebaSplit({ text: query }) },
|
$text: { $search: jiebaSplit({ text: query }) },
|
||||||
...(filterCollectionIdList
|
...(filterCollectionIdList && filterCollectionIdList.length > 0
|
||||||
? {
|
? {
|
||||||
collectionId: {
|
collectionId: {
|
||||||
$in: filterCollectionIdList.map((id) => new Types.ObjectId(id))
|
$in: filterCollectionIdList.map((id) => new Types.ObjectId(id))
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt';
|
|||||||
import { countMessagesTokens } from '../../../../common/string/tiktoken/index';
|
import { countMessagesTokens } from '../../../../common/string/tiktoken/index';
|
||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { createChatCompletion } from '../../../ai/config';
|
import { getAIApi } from '../../../ai/config';
|
||||||
import type { ClassifyQuestionAgentItemType } from '@fastgpt/global/core/workflow/template/system/classifyQuestion/type';
|
import type { ClassifyQuestionAgentItemType } from '@fastgpt/global/core/workflow/template/system/classifyQuestion/type';
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
@@ -120,8 +120,13 @@ const completions = async ({
|
|||||||
useVision: false
|
useVision: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const { response: data } = await createChatCompletion({
|
const ai = getAIApi({
|
||||||
body: llmCompletionsBodyFormat(
|
userKey: user.openaiAccount,
|
||||||
|
timeout: 480000
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await ai.chat.completions.create(
|
||||||
|
llmCompletionsBodyFormat(
|
||||||
{
|
{
|
||||||
model: cqModel.model,
|
model: cqModel.model,
|
||||||
temperature: 0.01,
|
temperature: 0.01,
|
||||||
@@ -129,9 +134,8 @@ const completions = async ({
|
|||||||
stream: false
|
stream: false
|
||||||
},
|
},
|
||||||
cqModel
|
cqModel
|
||||||
),
|
)
|
||||||
userKey: user.openaiAccount
|
);
|
||||||
});
|
|
||||||
const answer = data.choices?.[0].message?.content || '';
|
const answer = data.choices?.[0].message?.content || '';
|
||||||
|
|
||||||
// console.log(JSON.stringify(chats2GPTMessages({ messages, reserveId: false }), null, 2));
|
// console.log(JSON.stringify(chats2GPTMessages({ messages, reserveId: false }), null, 2));
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
countGptMessagesTokens
|
countGptMessagesTokens
|
||||||
} from '../../../../common/string/tiktoken/index';
|
} from '../../../../common/string/tiktoken/index';
|
||||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { createChatCompletion } from '../../../ai/config';
|
import { getAIApi } from '../../../ai/config';
|
||||||
import type { ContextExtractAgentItemType } from '@fastgpt/global/core/workflow/template/system/contextExtract/type';
|
import type { ContextExtractAgentItemType } from '@fastgpt/global/core/workflow/template/system/contextExtract/type';
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
@@ -222,8 +222,13 @@ const toolChoice = async (props: ActionProps) => {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const { response } = await createChatCompletion({
|
const ai = getAIApi({
|
||||||
body: llmCompletionsBodyFormat(
|
userKey: user.openaiAccount,
|
||||||
|
timeout: 480000
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await ai.chat.completions.create(
|
||||||
|
llmCompletionsBodyFormat(
|
||||||
{
|
{
|
||||||
model: extractModel.model,
|
model: extractModel.model,
|
||||||
temperature: 0.01,
|
temperature: 0.01,
|
||||||
@@ -232,9 +237,8 @@ const toolChoice = async (props: ActionProps) => {
|
|||||||
tool_choice: { type: 'function', function: { name: agentFunName } }
|
tool_choice: { type: 'function', function: { name: agentFunName } }
|
||||||
},
|
},
|
||||||
extractModel
|
extractModel
|
||||||
),
|
)
|
||||||
userKey: user.openaiAccount
|
);
|
||||||
});
|
|
||||||
|
|
||||||
const arg: Record<string, any> = (() => {
|
const arg: Record<string, any> = (() => {
|
||||||
try {
|
try {
|
||||||
@@ -268,8 +272,13 @@ const functionCall = async (props: ActionProps) => {
|
|||||||
const { agentFunction, filterMessages } = await getFunctionCallSchema(props);
|
const { agentFunction, filterMessages } = await getFunctionCallSchema(props);
|
||||||
const functions: ChatCompletionCreateParams.Function[] = [agentFunction];
|
const functions: ChatCompletionCreateParams.Function[] = [agentFunction];
|
||||||
|
|
||||||
const { response } = await createChatCompletion({
|
const ai = getAIApi({
|
||||||
body: llmCompletionsBodyFormat(
|
userKey: user.openaiAccount,
|
||||||
|
timeout: 480000
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await ai.chat.completions.create(
|
||||||
|
llmCompletionsBodyFormat(
|
||||||
{
|
{
|
||||||
model: extractModel.model,
|
model: extractModel.model,
|
||||||
temperature: 0.01,
|
temperature: 0.01,
|
||||||
@@ -280,9 +289,8 @@ const functionCall = async (props: ActionProps) => {
|
|||||||
functions
|
functions
|
||||||
},
|
},
|
||||||
extractModel
|
extractModel
|
||||||
),
|
)
|
||||||
userKey: user.openaiAccount
|
);
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const arg = JSON.parse(response?.choices?.[0]?.message?.function_call?.arguments || '');
|
const arg = JSON.parse(response?.choices?.[0]?.message?.function_call?.arguments || '');
|
||||||
@@ -350,8 +358,12 @@ Human: ${content}`
|
|||||||
useVision: false
|
useVision: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const { response: data } = await createChatCompletion({
|
const ai = getAIApi({
|
||||||
body: llmCompletionsBodyFormat(
|
userKey: user.openaiAccount,
|
||||||
|
timeout: 480000
|
||||||
|
});
|
||||||
|
const data = await ai.chat.completions.create(
|
||||||
|
llmCompletionsBodyFormat(
|
||||||
{
|
{
|
||||||
model: extractModel.model,
|
model: extractModel.model,
|
||||||
temperature: 0.01,
|
temperature: 0.01,
|
||||||
@@ -359,9 +371,8 @@ Human: ${content}`
|
|||||||
stream: false
|
stream: false
|
||||||
},
|
},
|
||||||
extractModel
|
extractModel
|
||||||
),
|
)
|
||||||
userKey: user.openaiAccount
|
);
|
||||||
});
|
|
||||||
const answer = data.choices?.[0].message?.content || '';
|
const answer = data.choices?.[0].message?.content || '';
|
||||||
|
|
||||||
// parse response
|
// parse response
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { createChatCompletion } from '../../../../ai/config';
|
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||||
|
import { getAIApi } from '../../../../ai/config';
|
||||||
import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../../chat/utils';
|
import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../../chat/utils';
|
||||||
import {
|
import {
|
||||||
ChatCompletion,
|
ChatCompletion,
|
||||||
@@ -21,12 +22,12 @@ import { DispatchFlowResponse, WorkflowResponseType } from '../../type';
|
|||||||
import { countGptMessagesTokens } from '../../../../../common/string/tiktoken/index';
|
import { countGptMessagesTokens } from '../../../../../common/string/tiktoken/index';
|
||||||
import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
|
import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
|
||||||
import { AIChatItemType } from '@fastgpt/global/core/chat/type';
|
import { AIChatItemType } from '@fastgpt/global/core/chat/type';
|
||||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
import { chats2GPTMessages, GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||||
import { formatToolResponse, initToolCallEdges, initToolNodes } from './utils';
|
import { formatToolResponse, initToolCallEdges, initToolNodes } from './utils';
|
||||||
import { computedMaxToken, llmCompletionsBodyFormat } from '../../../../ai/utils';
|
import { computedMaxToken, llmCompletionsBodyFormat } from '../../../../ai/utils';
|
||||||
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
|
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { i18nT } from '../../../../../../web/i18n/utils';
|
import { i18nT } from '../../../../../../web/i18n/utils';
|
||||||
|
|
||||||
type FunctionRunResponseType = {
|
type FunctionRunResponseType = {
|
||||||
@@ -44,7 +45,7 @@ export const runToolWithFunctionCall = async (
|
|||||||
requestOrigin,
|
requestOrigin,
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
runtimeEdges,
|
runtimeEdges,
|
||||||
user,
|
node,
|
||||||
stream,
|
stream,
|
||||||
workflowStreamResponse,
|
workflowStreamResponse,
|
||||||
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
||||||
@@ -216,18 +217,17 @@ export const runToolWithFunctionCall = async (
|
|||||||
|
|
||||||
// console.log(JSON.stringify(requestMessages, null, 2));
|
// console.log(JSON.stringify(requestMessages, null, 2));
|
||||||
/* Run llm */
|
/* Run llm */
|
||||||
const { response: aiResponse, isStreamResponse } = await createChatCompletion({
|
const ai = getAIApi({
|
||||||
body: requestBody,
|
timeout: 480000
|
||||||
userKey: user.openaiAccount,
|
});
|
||||||
options: {
|
const aiResponse = await ai.chat.completions.create(requestBody, {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json, text/plain, */*'
|
Accept: 'application/json, text/plain, */*'
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const { answer, functionCalls } = await (async () => {
|
const { answer, functionCalls } = await (async () => {
|
||||||
if (res && isStreamResponse) {
|
if (res && stream) {
|
||||||
return streamResponse({
|
return streamResponse({
|
||||||
res,
|
res,
|
||||||
toolNodes,
|
toolNodes,
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import { getFileContentFromLinks, getHistoryFileLinks } from '../../tools/readFi
|
|||||||
import { parseUrlToFileType } from '@fastgpt/global/common/file/tools';
|
import { parseUrlToFileType } from '@fastgpt/global/common/file/tools';
|
||||||
import { Prompt_DocumentQuote } from '@fastgpt/global/core/ai/prompt/AIChat';
|
import { Prompt_DocumentQuote } from '@fastgpt/global/core/ai/prompt/AIChat';
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { postTextCensor } from '../../../../../common/api/requestPlusApi';
|
|
||||||
|
|
||||||
type Response = DispatchNodeResultType<{
|
type Response = DispatchNodeResultType<{
|
||||||
[NodeOutputKeyEnum.answerText]: string;
|
[NodeOutputKeyEnum.answerText]: string;
|
||||||
@@ -46,7 +45,6 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
|||||||
requestOrigin,
|
requestOrigin,
|
||||||
chatConfig,
|
chatConfig,
|
||||||
runningAppInfo: { teamId },
|
runningAppInfo: { teamId },
|
||||||
user,
|
|
||||||
params: {
|
params: {
|
||||||
model,
|
model,
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
@@ -152,15 +150,6 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
|||||||
return value;
|
return value;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// censor model and system key
|
|
||||||
if (toolModel.censor && !user.openaiAccount?.key) {
|
|
||||||
await postTextCensor({
|
|
||||||
text: `${systemPrompt}
|
|
||||||
${userChatInput}
|
|
||||||
`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
toolWorkflowInteractiveResponse,
|
toolWorkflowInteractiveResponse,
|
||||||
dispatchFlowResponse, // tool flow response
|
dispatchFlowResponse, // tool flow response
|
||||||
@@ -228,14 +217,13 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
|||||||
tokens: toolNodeTokens,
|
tokens: toolNodeTokens,
|
||||||
modelType: ModelTypeEnum.llm
|
modelType: ModelTypeEnum.llm
|
||||||
});
|
});
|
||||||
const toolAIUsage = user.openaiAccount?.key ? 0 : totalPoints;
|
|
||||||
|
|
||||||
// flat child tool response
|
// flat child tool response
|
||||||
const childToolResponse = dispatchFlowResponse.map((item) => item.flowResponses).flat();
|
const childToolResponse = dispatchFlowResponse.map((item) => item.flowResponses).flat();
|
||||||
|
|
||||||
// concat tool usage
|
// concat tool usage
|
||||||
const totalPointsUsage =
|
const totalPointsUsage =
|
||||||
toolAIUsage +
|
totalPoints +
|
||||||
dispatchFlowResponse.reduce((sum, item) => {
|
dispatchFlowResponse.reduce((sum, item) => {
|
||||||
const childrenTotal = item.flowUsages.reduce((sum, item) => sum + item.totalPoints, 0);
|
const childrenTotal = item.flowUsages.reduce((sum, item) => sum + item.totalPoints, 0);
|
||||||
return sum + childrenTotal;
|
return sum + childrenTotal;
|
||||||
@@ -252,7 +240,6 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
|||||||
.join(''),
|
.join(''),
|
||||||
[DispatchNodeResponseKeyEnum.assistantResponses]: previewAssistantResponses,
|
[DispatchNodeResponseKeyEnum.assistantResponses]: previewAssistantResponses,
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||||
// 展示的积分消耗
|
|
||||||
totalPoints: totalPointsUsage,
|
totalPoints: totalPointsUsage,
|
||||||
toolCallTokens: toolNodeTokens,
|
toolCallTokens: toolNodeTokens,
|
||||||
childTotalPoints: flatUsages.reduce((sum, item) => sum + item.totalPoints, 0),
|
childTotalPoints: flatUsages.reduce((sum, item) => sum + item.totalPoints, 0),
|
||||||
@@ -267,14 +254,12 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
|||||||
mergeSignId: nodeId
|
mergeSignId: nodeId
|
||||||
},
|
},
|
||||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
||||||
// 工具调用本身的积分消耗
|
|
||||||
{
|
{
|
||||||
moduleName: name,
|
moduleName: name,
|
||||||
totalPoints: toolAIUsage,
|
totalPoints,
|
||||||
model: modelName,
|
model: modelName,
|
||||||
tokens: toolNodeTokens
|
tokens: toolNodeTokens
|
||||||
},
|
},
|
||||||
// 工具的消耗
|
|
||||||
...flatUsages
|
...flatUsages
|
||||||
],
|
],
|
||||||
[DispatchNodeResponseKeyEnum.interactive]: toolWorkflowInteractiveResponse
|
[DispatchNodeResponseKeyEnum.interactive]: toolWorkflowInteractiveResponse
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createChatCompletion } from '../../../../ai/config';
|
import { getAIApi } from '../../../../ai/config';
|
||||||
import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../../chat/utils';
|
import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../../chat/utils';
|
||||||
import {
|
import {
|
||||||
ChatCompletion,
|
ChatCompletion,
|
||||||
@@ -52,7 +52,7 @@ export const runToolWithPromptCall = async (
|
|||||||
requestOrigin,
|
requestOrigin,
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
runtimeEdges,
|
runtimeEdges,
|
||||||
user,
|
node,
|
||||||
stream,
|
stream,
|
||||||
workflowStreamResponse,
|
workflowStreamResponse,
|
||||||
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
||||||
@@ -225,15 +225,18 @@ export const runToolWithPromptCall = async (
|
|||||||
|
|
||||||
// console.log(JSON.stringify(requestMessages, null, 2));
|
// console.log(JSON.stringify(requestMessages, null, 2));
|
||||||
/* Run llm */
|
/* Run llm */
|
||||||
const { response: aiResponse, isStreamResponse } = await createChatCompletion({
|
const ai = getAIApi({
|
||||||
body: requestBody,
|
timeout: 480000
|
||||||
userKey: user.openaiAccount,
|
});
|
||||||
options: {
|
const aiResponse = await ai.chat.completions.create(requestBody, {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json, text/plain, */*'
|
Accept: 'application/json, text/plain, */*'
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const isStreamResponse =
|
||||||
|
typeof aiResponse === 'object' &&
|
||||||
|
aiResponse !== null &&
|
||||||
|
('iterator' in aiResponse || 'controller' in aiResponse);
|
||||||
|
|
||||||
const answer = await (async () => {
|
const answer = await (async () => {
|
||||||
if (res && isStreamResponse) {
|
if (res && isStreamResponse) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createChatCompletion } from '../../../../ai/config';
|
import { getAIApi } from '../../../../ai/config';
|
||||||
import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../../chat/utils';
|
import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../../chat/utils';
|
||||||
import {
|
import {
|
||||||
ChatCompletion,
|
ChatCompletion,
|
||||||
@@ -92,7 +92,6 @@ export const runToolWithToolChoice = async (
|
|||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
runtimeEdges,
|
runtimeEdges,
|
||||||
stream,
|
stream,
|
||||||
user,
|
|
||||||
workflowStreamResponse,
|
workflowStreamResponse,
|
||||||
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
||||||
} = workflowProps;
|
} = workflowProps;
|
||||||
@@ -272,265 +271,277 @@ export const runToolWithToolChoice = async (
|
|||||||
);
|
);
|
||||||
// console.log(JSON.stringify(requestBody, null, 2), '==requestBody');
|
// console.log(JSON.stringify(requestBody, null, 2), '==requestBody');
|
||||||
/* Run llm */
|
/* Run llm */
|
||||||
const { response: aiResponse, isStreamResponse } = await createChatCompletion({
|
const ai = getAIApi({
|
||||||
body: requestBody,
|
timeout: 480000
|
||||||
userKey: user.openaiAccount,
|
});
|
||||||
options: {
|
|
||||||
|
try {
|
||||||
|
const aiResponse = await ai.chat.completions.create(requestBody, {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json, text/plain, */*'
|
Accept: 'application/json, text/plain, */*'
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
const isStreamResponse =
|
||||||
|
typeof aiResponse === 'object' &&
|
||||||
|
aiResponse !== null &&
|
||||||
|
('iterator' in aiResponse || 'controller' in aiResponse);
|
||||||
|
|
||||||
const { answer, toolCalls } = await (async () => {
|
const { answer, toolCalls } = await (async () => {
|
||||||
if (res && isStreamResponse) {
|
if (res && isStreamResponse) {
|
||||||
return streamResponse({
|
return streamResponse({
|
||||||
res,
|
res,
|
||||||
workflowStreamResponse,
|
workflowStreamResponse,
|
||||||
toolNodes,
|
toolNodes,
|
||||||
stream: aiResponse
|
stream: aiResponse
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const result = aiResponse as ChatCompletion;
|
const result = aiResponse as ChatCompletion;
|
||||||
const calls = result.choices?.[0]?.message?.tool_calls || [];
|
const calls = result.choices?.[0]?.message?.tool_calls || [];
|
||||||
const answer = result.choices?.[0]?.message?.content || '';
|
const answer = result.choices?.[0]?.message?.content || '';
|
||||||
|
|
||||||
// 加上name和avatar
|
// 加上name和avatar
|
||||||
const toolCalls = calls.map((tool) => {
|
const toolCalls = calls.map((tool) => {
|
||||||
const toolNode = toolNodes.find((item) => item.nodeId === tool.function?.name);
|
const toolNode = toolNodes.find((item) => item.nodeId === tool.function?.name);
|
||||||
return {
|
return {
|
||||||
...tool,
|
...tool,
|
||||||
toolName: toolNode?.name || '',
|
toolName: toolNode?.name || '',
|
||||||
toolAvatar: toolNode?.avatar || ''
|
toolAvatar: toolNode?.avatar || ''
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// 不支持 stream 模式的模型的流失响应
|
// 不支持 stream 模式的模型的流失响应
|
||||||
toolCalls.forEach((tool) => {
|
toolCalls.forEach((tool) => {
|
||||||
workflowStreamResponse?.({
|
workflowStreamResponse?.({
|
||||||
event: SseResponseEventEnum.toolCall,
|
event: SseResponseEventEnum.toolCall,
|
||||||
data: {
|
data: {
|
||||||
tool: {
|
tool: {
|
||||||
id: tool.id,
|
id: tool.id,
|
||||||
toolName: tool.toolName,
|
toolName: tool.toolName,
|
||||||
toolAvatar: tool.toolAvatar,
|
toolAvatar: tool.toolAvatar,
|
||||||
functionName: tool.function.name,
|
functionName: tool.function.name,
|
||||||
params: tool.function?.arguments ?? '',
|
params: tool.function?.arguments ?? '',
|
||||||
response: ''
|
response: ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
if (answer) {
|
|
||||||
workflowStreamResponse?.({
|
|
||||||
event: SseResponseEventEnum.fastAnswer,
|
|
||||||
data: textAdaptGptResponse({
|
|
||||||
text: answer
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
answer,
|
|
||||||
toolCalls: toolCalls
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
// Run the selected tool by LLM.
|
|
||||||
const toolsRunResponse = (
|
|
||||||
await Promise.all(
|
|
||||||
toolCalls.map(async (tool) => {
|
|
||||||
const toolNode = toolNodes.find((item) => item.nodeId === tool.function?.name);
|
|
||||||
|
|
||||||
if (!toolNode) return;
|
|
||||||
|
|
||||||
const startParams = (() => {
|
|
||||||
try {
|
|
||||||
return json5.parse(tool.function.arguments);
|
|
||||||
} catch (error) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
initToolNodes(runtimeNodes, [toolNode.nodeId], startParams);
|
|
||||||
const toolRunResponse = await dispatchWorkFlow({
|
|
||||||
...workflowProps,
|
|
||||||
isToolCall: true
|
|
||||||
});
|
|
||||||
|
|
||||||
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
|
|
||||||
|
|
||||||
const toolMsgParams: ChatCompletionToolMessageParam = {
|
|
||||||
tool_call_id: tool.id,
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Tool,
|
|
||||||
name: tool.function.name,
|
|
||||||
content: stringToolResponse
|
|
||||||
};
|
|
||||||
|
|
||||||
workflowStreamResponse?.({
|
|
||||||
event: SseResponseEventEnum.toolResponse,
|
|
||||||
data: {
|
|
||||||
tool: {
|
|
||||||
id: tool.id,
|
|
||||||
toolName: '',
|
|
||||||
toolAvatar: '',
|
|
||||||
params: '',
|
|
||||||
response: sliceStrStartEnd(stringToolResponse, 5000, 5000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
if (answer) {
|
||||||
|
workflowStreamResponse?.({
|
||||||
|
event: SseResponseEventEnum.fastAnswer,
|
||||||
|
data: textAdaptGptResponse({
|
||||||
|
text: answer
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
toolRunResponse,
|
answer,
|
||||||
toolMsgParams
|
toolCalls: toolCalls
|
||||||
};
|
};
|
||||||
})
|
|
||||||
)
|
|
||||||
).filter(Boolean) as ToolRunResponseType;
|
|
||||||
|
|
||||||
const flatToolsResponseData = toolsRunResponse.map((item) => item.toolRunResponse).flat();
|
|
||||||
// concat tool responses
|
|
||||||
const dispatchFlowResponse = response
|
|
||||||
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
|
||||||
: flatToolsResponseData;
|
|
||||||
|
|
||||||
if (toolCalls.length > 0 && !res?.closed) {
|
|
||||||
// Run the tool, combine its results, and perform another round of AI calls
|
|
||||||
const assistantToolMsgParams: ChatCompletionAssistantMessageParam[] = [
|
|
||||||
...(answer
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant as 'assistant',
|
|
||||||
content: answer
|
|
||||||
}
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
{
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
|
||||||
tool_calls: toolCalls
|
|
||||||
}
|
}
|
||||||
];
|
})();
|
||||||
|
|
||||||
/*
|
// Run the selected tool by LLM.
|
||||||
|
const toolsRunResponse = (
|
||||||
|
await Promise.all(
|
||||||
|
toolCalls.map(async (tool) => {
|
||||||
|
const toolNode = toolNodes.find((item) => item.nodeId === tool.function?.name);
|
||||||
|
|
||||||
|
if (!toolNode) return;
|
||||||
|
|
||||||
|
const startParams = (() => {
|
||||||
|
try {
|
||||||
|
return json5.parse(tool.function.arguments);
|
||||||
|
} catch (error) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
initToolNodes(runtimeNodes, [toolNode.nodeId], startParams);
|
||||||
|
const toolRunResponse = await dispatchWorkFlow({
|
||||||
|
...workflowProps,
|
||||||
|
isToolCall: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
|
||||||
|
|
||||||
|
const toolMsgParams: ChatCompletionToolMessageParam = {
|
||||||
|
tool_call_id: tool.id,
|
||||||
|
role: ChatCompletionRequestMessageRoleEnum.Tool,
|
||||||
|
name: tool.function.name,
|
||||||
|
content: stringToolResponse
|
||||||
|
};
|
||||||
|
|
||||||
|
workflowStreamResponse?.({
|
||||||
|
event: SseResponseEventEnum.toolResponse,
|
||||||
|
data: {
|
||||||
|
tool: {
|
||||||
|
id: tool.id,
|
||||||
|
toolName: '',
|
||||||
|
toolAvatar: '',
|
||||||
|
params: '',
|
||||||
|
response: sliceStrStartEnd(stringToolResponse, 5000, 5000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
toolRunResponse,
|
||||||
|
toolMsgParams
|
||||||
|
};
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).filter(Boolean) as ToolRunResponseType;
|
||||||
|
|
||||||
|
const flatToolsResponseData = toolsRunResponse.map((item) => item.toolRunResponse).flat();
|
||||||
|
// concat tool responses
|
||||||
|
const dispatchFlowResponse = response
|
||||||
|
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
||||||
|
: flatToolsResponseData;
|
||||||
|
|
||||||
|
if (toolCalls.length > 0 && !res?.closed) {
|
||||||
|
// Run the tool, combine its results, and perform another round of AI calls
|
||||||
|
const assistantToolMsgParams: ChatCompletionAssistantMessageParam[] = [
|
||||||
|
...(answer
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
role: ChatCompletionRequestMessageRoleEnum.Assistant as 'assistant',
|
||||||
|
content: answer
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
{
|
||||||
|
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||||
|
tool_calls: toolCalls
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/*
|
||||||
...
|
...
|
||||||
user
|
user
|
||||||
assistant: tool data
|
assistant: tool data
|
||||||
*/
|
*/
|
||||||
const concatToolMessages = [
|
const concatToolMessages = [
|
||||||
...requestMessages,
|
...requestMessages,
|
||||||
...assistantToolMsgParams
|
...assistantToolMsgParams
|
||||||
] as ChatCompletionMessageParam[];
|
] as ChatCompletionMessageParam[];
|
||||||
|
|
||||||
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply
|
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply
|
||||||
const tokens = await countGptMessagesTokens(concatToolMessages, tools);
|
const tokens = await countGptMessagesTokens(concatToolMessages, tools);
|
||||||
/*
|
/*
|
||||||
...
|
...
|
||||||
user
|
user
|
||||||
assistant: tool data
|
assistant: tool data
|
||||||
tool: tool response
|
tool: tool response
|
||||||
*/
|
*/
|
||||||
const completeMessages = [
|
const completeMessages = [
|
||||||
...concatToolMessages,
|
...concatToolMessages,
|
||||||
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
||||||
];
|
];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get tool node assistant response
|
Get tool node assistant response
|
||||||
history assistant
|
history assistant
|
||||||
current tool assistant
|
current tool assistant
|
||||||
tool child assistant
|
tool child assistant
|
||||||
*/
|
*/
|
||||||
const toolNodeAssistant = GPTMessages2Chats([
|
const toolNodeAssistant = GPTMessages2Chats([
|
||||||
...assistantToolMsgParams,
|
...assistantToolMsgParams,
|
||||||
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
||||||
])[0] as AIChatItemType;
|
])[0] as AIChatItemType;
|
||||||
const toolChildAssistants = flatToolsResponseData
|
const toolChildAssistants = flatToolsResponseData
|
||||||
.map((item) => item.assistantResponses)
|
.map((item) => item.assistantResponses)
|
||||||
.flat()
|
.flat()
|
||||||
.filter((item) => item.type !== ChatItemValueTypeEnum.interactive); // 交互节点留着下次记录
|
.filter((item) => item.type !== ChatItemValueTypeEnum.interactive); // 交互节点留着下次记录
|
||||||
const toolNodeAssistants = [
|
const toolNodeAssistants = [
|
||||||
...assistantResponses,
|
...assistantResponses,
|
||||||
...toolNodeAssistant.value,
|
...toolNodeAssistant.value,
|
||||||
...toolChildAssistants
|
...toolChildAssistants
|
||||||
];
|
];
|
||||||
|
|
||||||
const runTimes =
|
const runTimes =
|
||||||
(response?.runTimes || 0) +
|
(response?.runTimes || 0) +
|
||||||
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0);
|
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0);
|
||||||
const toolNodeTokens = response ? response.toolNodeTokens + tokens : tokens;
|
const toolNodeTokens = response ? response.toolNodeTokens + tokens : tokens;
|
||||||
|
|
||||||
// Check stop signal
|
// Check stop signal
|
||||||
const hasStopSignal = flatToolsResponseData.some(
|
const hasStopSignal = flatToolsResponseData.some(
|
||||||
(item) => !!item.flowResponses?.find((item) => item.toolStop)
|
(item) => !!item.flowResponses?.find((item) => item.toolStop)
|
||||||
);
|
);
|
||||||
// Check interactive response(Only 1 interaction is reserved)
|
// Check interactive response(Only 1 interaction is reserved)
|
||||||
const workflowInteractiveResponseItem = toolsRunResponse.find(
|
const workflowInteractiveResponseItem = toolsRunResponse.find(
|
||||||
(item) => item.toolRunResponse.workflowInteractiveResponse
|
(item) => item.toolRunResponse.workflowInteractiveResponse
|
||||||
);
|
);
|
||||||
if (hasStopSignal || workflowInteractiveResponseItem) {
|
if (hasStopSignal || workflowInteractiveResponseItem) {
|
||||||
// Get interactive tool data
|
// Get interactive tool data
|
||||||
const workflowInteractiveResponse =
|
const workflowInteractiveResponse =
|
||||||
workflowInteractiveResponseItem?.toolRunResponse.workflowInteractiveResponse;
|
workflowInteractiveResponseItem?.toolRunResponse.workflowInteractiveResponse;
|
||||||
|
|
||||||
// Flashback traverses completeMessages, intercepting messages that know the first user
|
// Flashback traverses completeMessages, intercepting messages that know the first user
|
||||||
const firstUserIndex = completeMessages.findLastIndex((item) => item.role === 'user');
|
const firstUserIndex = completeMessages.findLastIndex((item) => item.role === 'user');
|
||||||
const newMessages = completeMessages.slice(firstUserIndex + 1);
|
const newMessages = completeMessages.slice(firstUserIndex + 1);
|
||||||
|
|
||||||
const toolWorkflowInteractiveResponse: WorkflowInteractiveResponseType | undefined =
|
const toolWorkflowInteractiveResponse: WorkflowInteractiveResponseType | undefined =
|
||||||
workflowInteractiveResponse
|
workflowInteractiveResponse
|
||||||
? {
|
? {
|
||||||
...workflowInteractiveResponse,
|
...workflowInteractiveResponse,
|
||||||
toolParams: {
|
toolParams: {
|
||||||
entryNodeIds: workflowInteractiveResponse.entryNodeIds,
|
entryNodeIds: workflowInteractiveResponse.entryNodeIds,
|
||||||
toolCallId: workflowInteractiveResponseItem?.toolMsgParams.tool_call_id,
|
toolCallId: workflowInteractiveResponseItem?.toolMsgParams.tool_call_id,
|
||||||
memoryMessages: newMessages
|
memoryMessages: newMessages
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
: undefined;
|
||||||
: undefined;
|
|
||||||
|
return {
|
||||||
|
dispatchFlowResponse,
|
||||||
|
toolNodeTokens,
|
||||||
|
completeMessages,
|
||||||
|
assistantResponses: toolNodeAssistants,
|
||||||
|
runTimes,
|
||||||
|
toolWorkflowInteractiveResponse
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return runToolWithToolChoice(
|
||||||
|
{
|
||||||
|
...props,
|
||||||
|
maxRunToolTimes: maxRunToolTimes - 1,
|
||||||
|
messages: completeMessages
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dispatchFlowResponse,
|
||||||
|
toolNodeTokens,
|
||||||
|
assistantResponses: toolNodeAssistants,
|
||||||
|
runTimes
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// No tool is invoked, indicating that the process is over
|
||||||
|
const gptAssistantResponse: ChatCompletionAssistantMessageParam = {
|
||||||
|
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||||
|
content: answer
|
||||||
|
};
|
||||||
|
const completeMessages = filterMessages.concat(gptAssistantResponse);
|
||||||
|
const tokens = await countGptMessagesTokens(completeMessages, tools);
|
||||||
|
|
||||||
|
// concat tool assistant
|
||||||
|
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dispatchFlowResponse,
|
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
||||||
toolNodeTokens,
|
toolNodeTokens: response ? response.toolNodeTokens + tokens : tokens,
|
||||||
completeMessages,
|
completeMessages,
|
||||||
assistantResponses: toolNodeAssistants,
|
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
||||||
runTimes,
|
runTimes: (response?.runTimes || 0) + 1
|
||||||
toolWorkflowInteractiveResponse
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
return runToolWithToolChoice(
|
console.log(error);
|
||||||
{
|
addLog.warn(`LLM response error`, {
|
||||||
...props,
|
requestBody
|
||||||
maxRunToolTimes: maxRunToolTimes - 1,
|
});
|
||||||
messages: completeMessages
|
return Promise.reject(error);
|
||||||
},
|
|
||||||
{
|
|
||||||
dispatchFlowResponse,
|
|
||||||
toolNodeTokens,
|
|
||||||
assistantResponses: toolNodeAssistants,
|
|
||||||
runTimes
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// No tool is invoked, indicating that the process is over
|
|
||||||
const gptAssistantResponse: ChatCompletionAssistantMessageParam = {
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
|
||||||
content: answer
|
|
||||||
};
|
|
||||||
const completeMessages = filterMessages.concat(gptAssistantResponse);
|
|
||||||
const tokens = await countGptMessagesTokens(completeMessages, tools);
|
|
||||||
|
|
||||||
// concat tool assistant
|
|
||||||
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
|
|
||||||
|
|
||||||
return {
|
|
||||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
|
||||||
toolNodeTokens: response ? response.toolNodeTokens + tokens : tokens,
|
|
||||||
completeMessages,
|
|
||||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
|
||||||
runTimes: (response?.runTimes || 0) + 1
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/co
|
|||||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { createChatCompletion } from '../../../ai/config';
|
import { getAIApi } from '../../../ai/config';
|
||||||
import type { ChatCompletion, StreamChatType } from '@fastgpt/global/core/ai/type.d';
|
import type { ChatCompletion, StreamChatType } from '@fastgpt/global/core/ai/type.d';
|
||||||
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
||||||
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||||
@@ -138,6 +138,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
if (modelConstantsData.censor && !user.openaiAccount?.key) {
|
if (modelConstantsData.censor && !user.openaiAccount?.key) {
|
||||||
return postTextCensor({
|
return postTextCensor({
|
||||||
text: `${systemPrompt}
|
text: `${systemPrompt}
|
||||||
|
${datasetQuoteText}
|
||||||
${userChatInput}
|
${userChatInput}
|
||||||
`
|
`
|
||||||
});
|
});
|
||||||
@@ -170,16 +171,21 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
);
|
);
|
||||||
// console.log(JSON.stringify(requestBody, null, 2), '===');
|
// console.log(JSON.stringify(requestBody, null, 2), '===');
|
||||||
try {
|
try {
|
||||||
const { response, isStreamResponse } = await createChatCompletion({
|
const ai = getAIApi({
|
||||||
body: requestBody,
|
|
||||||
userKey: user.openaiAccount,
|
userKey: user.openaiAccount,
|
||||||
options: {
|
timeout: 480000
|
||||||
headers: {
|
});
|
||||||
Accept: 'application/json, text/plain, */*'
|
const response = await ai.chat.completions.create(requestBody, {
|
||||||
}
|
headers: {
|
||||||
|
Accept: 'application/json, text/plain, */*'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isStreamResponse =
|
||||||
|
typeof response === 'object' &&
|
||||||
|
response !== null &&
|
||||||
|
('iterator' in response || 'controller' in response);
|
||||||
|
|
||||||
const { answerText } = await (async () => {
|
const { answerText } = await (async () => {
|
||||||
if (res && isStreamResponse) {
|
if (res && isStreamResponse) {
|
||||||
// sse response
|
// sse response
|
||||||
|
|||||||
@@ -65,17 +65,7 @@ export async function dispatchDatasetSearch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!userChatInput) {
|
if (!userChatInput) {
|
||||||
return {
|
return Promise.reject(i18nT('common:core.chat.error.User input empty'));
|
||||||
quoteQA: [],
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
totalPoints: 0,
|
|
||||||
query: '',
|
|
||||||
limit,
|
|
||||||
searchMode
|
|
||||||
},
|
|
||||||
nodeDispatchUsages: [],
|
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]: []
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// query extension
|
// query extension
|
||||||
|
|||||||
@@ -61,14 +61,7 @@ export const readFileRawText = ({
|
|||||||
|
|
||||||
reject(getErrText(err, 'Load file error'));
|
reject(getErrText(err, 'Load file error'));
|
||||||
};
|
};
|
||||||
detectFileEncoding(file).then((encoding) => {
|
reader.readAsText(file);
|
||||||
console.log(encoding);
|
|
||||||
|
|
||||||
reader.readAsText(
|
|
||||||
file,
|
|
||||||
['iso-8859-1', 'windows-1252'].includes(encoding) ? 'gb2312' : 'utf-8'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
reject('The browser does not support file content reading');
|
reject('The browser does not support file content reading');
|
||||||
}
|
}
|
||||||
@@ -78,24 +71,6 @@ export const readFileRawText = ({
|
|||||||
export const readCsvRawText = async ({ file }: { file: File }) => {
|
export const readCsvRawText = async ({ file }: { file: File }) => {
|
||||||
const rawText = await readFileRawText({ file });
|
const rawText = await readFileRawText({ file });
|
||||||
const csvArr = Papa.parse(rawText).data as string[][];
|
const csvArr = Papa.parse(rawText).data as string[][];
|
||||||
|
|
||||||
return csvArr;
|
return csvArr;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function detectFileEncoding(file: File): Promise<string> {
|
|
||||||
const buffer = await loadFile2Buffer({ file });
|
|
||||||
const encoding = (() => {
|
|
||||||
const encodings = ['utf-8', 'iso-8859-1', 'windows-1252'];
|
|
||||||
for (let encoding of encodings) {
|
|
||||||
try {
|
|
||||||
const decoder = new TextDecoder(encoding, { fatal: true });
|
|
||||||
decoder.decode(buffer);
|
|
||||||
return encoding; // 如果解码成功,返回当前编码
|
|
||||||
} catch (e) {
|
|
||||||
// continue to try next encoding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null; // 如果没有编码匹配,返回null
|
|
||||||
})();
|
|
||||||
|
|
||||||
return encoding || 'utf-8';
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -245,7 +245,13 @@ export const MultipleRowArraySelect = ({
|
|||||||
onClick={() => handleSelect(item)}
|
onClick={() => handleSelect(item)}
|
||||||
{...(isSelected ? { color: 'primary.600' } : {})}
|
{...(isSelected ? { color: 'primary.600' } : {})}
|
||||||
>
|
>
|
||||||
{showCheckbox && <Checkbox isChecked={isChecked} mr={1} />}
|
{showCheckbox && (
|
||||||
|
<Checkbox
|
||||||
|
isChecked={isChecked}
|
||||||
|
icon={<MyIcon name={'common/check'} w={'12px'} />}
|
||||||
|
mr={1}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Box>{item.label}</Box>
|
<Box>{item.label}</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type EditorVariablePickerType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Props = Omit<BoxProps, 'resize' | 'onChange'> & {
|
export type Props = Omit<BoxProps, 'resize' | 'onChange'> & {
|
||||||
|
height?: number;
|
||||||
resize?: boolean;
|
resize?: boolean;
|
||||||
defaultValue?: string;
|
defaultValue?: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
@@ -110,7 +111,7 @@ const MyEditor = ({
|
|||||||
borderWidth={'1px'}
|
borderWidth={'1px'}
|
||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
borderColor={'myGray.200'}
|
borderColor={'myGray.200'}
|
||||||
py={1}
|
py={2}
|
||||||
height={height}
|
height={height}
|
||||||
position={'relative'}
|
position={'relative'}
|
||||||
pl={2}
|
pl={2}
|
||||||
@@ -131,8 +132,8 @@ const MyEditor = ({
|
|||||||
{resize && (
|
{resize && (
|
||||||
<Box
|
<Box
|
||||||
position={'absolute'}
|
position={'absolute'}
|
||||||
right={'-2.5'}
|
right={'-1'}
|
||||||
bottom={'-3.5'}
|
bottom={'-1'}
|
||||||
zIndex={10}
|
zIndex={10}
|
||||||
cursor={'ns-resize'}
|
cursor={'ns-resize'}
|
||||||
px={'4px'}
|
px={'4px'}
|
||||||
|
|||||||
@@ -19,11 +19,9 @@ const CodeEditor = (props: Props) => {
|
|||||||
iconSrc="modal/edit"
|
iconSrc="modal/edit"
|
||||||
title={t('common:code_editor')}
|
title={t('common:code_editor')}
|
||||||
w={'full'}
|
w={'full'}
|
||||||
h={'85vh'}
|
|
||||||
isCentered
|
|
||||||
>
|
>
|
||||||
<ModalBody flex={'1 0 0'} overflow={'auto'}>
|
<ModalBody>
|
||||||
<MyEditor {...props} bg={'myGray.50'} height={'100%'} />
|
<MyEditor {...props} bg={'myGray.50'} defaultHeight={600} />
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button mr={2} onClick={onClose} px={6}>
|
<Button mr={2} onClick={onClose} px={6}>
|
||||||
|
|||||||
@@ -12,27 +12,25 @@ const LANG_KEY = 'NEXT_LOCALE';
|
|||||||
|
|
||||||
export const useI18nLng = () => {
|
export const useI18nLng = () => {
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const languageMap: Record<string, string> = {
|
|
||||||
zh: 'zh',
|
|
||||||
'zh-CN': 'zh',
|
|
||||||
'zh-Hans': 'zh',
|
|
||||||
en: 'en',
|
|
||||||
'en-US': 'en'
|
|
||||||
};
|
|
||||||
|
|
||||||
const onChangeLng = (lng: string) => {
|
const onChangeLng = (lng: string) => {
|
||||||
const lang = languageMap[lng] || 'en';
|
setCookie(LANG_KEY, lng, {
|
||||||
|
expires: 30,
|
||||||
setCookie(LANG_KEY, lang, {
|
sameSite: 'None',
|
||||||
expires: 30
|
secure: true
|
||||||
});
|
});
|
||||||
i18n?.changeLanguage(lang);
|
i18n?.changeLanguage(lng);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setUserDefaultLng = () => {
|
const setUserDefaultLng = () => {
|
||||||
if (!navigator || !localStorage) return;
|
if (!navigator || !localStorage) return;
|
||||||
if (getCookie(LANG_KEY)) return onChangeLng(getCookie(LANG_KEY) as string);
|
if (getCookie(LANG_KEY)) return onChangeLng(getCookie(LANG_KEY) as string);
|
||||||
|
|
||||||
|
const languageMap: Record<string, string> = {
|
||||||
|
zh: 'zh',
|
||||||
|
'zh-CN': 'zh'
|
||||||
|
};
|
||||||
|
|
||||||
const lang = languageMap[navigator.language] || 'en';
|
const lang = languageMap[navigator.language] || 'en';
|
||||||
|
|
||||||
// currentLng not in userLang
|
// currentLng not in userLang
|
||||||
|
|||||||
@@ -11,14 +11,11 @@ export const useWidthVariable = <T = any>({
|
|||||||
}) => {
|
}) => {
|
||||||
const value = useMemo(() => {
|
const value = useMemo(() => {
|
||||||
// 根据 width 计算,找到第一个大于 width 的值
|
// 根据 width 计算,找到第一个大于 width 的值
|
||||||
const reversedWidthList = [...widthList].reverse();
|
const index = widthList.findLastIndex((item) => width > item);
|
||||||
const reversedList = [...list].reverse();
|
|
||||||
const index = reversedWidthList.findIndex((item) => width > item);
|
|
||||||
|
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
return reversedList[0];
|
return list[0];
|
||||||
}
|
}
|
||||||
return reversedList[index];
|
return list[index];
|
||||||
}, [list, width, widthList]);
|
}, [list, width, widthList]);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -206,7 +206,12 @@ const DatasetParamsModal = ({
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box position={'relative'} w={'18px'} h={'18px'}>
|
<Box position={'relative'} w={'18px'} h={'18px'}>
|
||||||
<Checkbox colorScheme="primary" isChecked={getValues('usingReRank')} size="lg" />
|
<Checkbox
|
||||||
|
colorScheme="primary"
|
||||||
|
isChecked={getValues('usingReRank')}
|
||||||
|
size="lg"
|
||||||
|
icon={<MyIcon name={'common/check'} w={'12px'} />}
|
||||||
|
/>
|
||||||
<Box position={'absolute'} top={0} right={0} bottom={0} left={0} zIndex={1}></Box>
|
<Box position={'absolute'} top={0} right={0} bottom={0} left={0} zIndex={1}></Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ const ResponseTags = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const quoteListRef = React.useRef<HTMLDivElement>(null);
|
const quoteListRef = React.useRef<HTMLDivElement>(null);
|
||||||
const dataId = historyItem.dataId;
|
const dataId = historyItem.dataId;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
totalQuoteList: quoteList = [],
|
totalQuoteList: quoteList = [],
|
||||||
llmModuleAccount = 0,
|
llmModuleAccount = 0,
|
||||||
|
|||||||
@@ -117,12 +117,11 @@ function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) {
|
|||||||
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
|
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
|
||||||
{filterGroups.map((group) => {
|
{filterGroups.map((group) => {
|
||||||
const onChange = () => {
|
const onChange = () => {
|
||||||
setSelectedGroupIdList((state) => {
|
if (selectedGroupIdList.includes(group._id)) {
|
||||||
if (state.includes(group._id)) {
|
setSelectedGroupIdList(selectedGroupIdList.filter((v) => v !== group._id));
|
||||||
return state.filter((v) => v !== group._id);
|
} else {
|
||||||
}
|
setSelectedGroupIdList([...selectedGroupIdList, group._id]);
|
||||||
return [...state, group._id];
|
}
|
||||||
});
|
|
||||||
};
|
};
|
||||||
const collaborator = collaboratorList.find((v) => v.groupId === group._id);
|
const collaborator = collaboratorList.find((v) => v.groupId === group._id);
|
||||||
return (
|
return (
|
||||||
@@ -142,7 +141,10 @@ function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) {
|
|||||||
}}
|
}}
|
||||||
onClick={onChange}
|
onClick={onChange}
|
||||||
>
|
>
|
||||||
<Checkbox isChecked={selectedGroupIdList.includes(group._id)} />
|
<Checkbox
|
||||||
|
isChecked={selectedGroupIdList.includes(group._id)}
|
||||||
|
icon={<MyIcon name={'common/check'} w={'12px'} />}
|
||||||
|
/>
|
||||||
<MyAvatar src={group.avatar} w="1.5rem" borderRadius={'50%'} />
|
<MyAvatar src={group.avatar} w="1.5rem" borderRadius={'50%'} />
|
||||||
<Box ml="2" w="full">
|
<Box ml="2" w="full">
|
||||||
{group.name === DefaultGroupName ? userInfo?.team.teamName : group.name}
|
{group.name === DefaultGroupName ? userInfo?.team.teamName : group.name}
|
||||||
@@ -155,12 +157,11 @@ function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) {
|
|||||||
})}
|
})}
|
||||||
{filterMembers.map((member) => {
|
{filterMembers.map((member) => {
|
||||||
const onChange = () => {
|
const onChange = () => {
|
||||||
setSelectedMembers((state) => {
|
if (selectedMemberIdList.includes(member.tmbId)) {
|
||||||
if (state.includes(member.tmbId)) {
|
setSelectedMembers(selectedMemberIdList.filter((v) => v !== member.tmbId));
|
||||||
return state.filter((v) => v !== member.tmbId);
|
} else {
|
||||||
}
|
setSelectedMembers([...selectedMemberIdList, member.tmbId]);
|
||||||
return [...state, member.tmbId];
|
}
|
||||||
});
|
|
||||||
};
|
};
|
||||||
const collaborator = collaboratorList.find((v) => v.tmbId === member.tmbId);
|
const collaborator = collaboratorList.find((v) => v.tmbId === member.tmbId);
|
||||||
return (
|
return (
|
||||||
@@ -204,12 +205,11 @@ function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) {
|
|||||||
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
|
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
|
||||||
{selectedGroupIdList.map((groupId) => {
|
{selectedGroupIdList.map((groupId) => {
|
||||||
const onChange = () => {
|
const onChange = () => {
|
||||||
setSelectedGroupIdList((state) => {
|
if (selectedGroupIdList.includes(groupId)) {
|
||||||
if (state.includes(groupId)) {
|
setSelectedGroupIdList(selectedGroupIdList.filter((v) => v !== groupId));
|
||||||
return state.filter((v) => v !== groupId);
|
} else {
|
||||||
}
|
setSelectedGroupIdList([...selectedGroupIdList, groupId]);
|
||||||
return [...state, groupId];
|
}
|
||||||
});
|
|
||||||
};
|
};
|
||||||
const group = groups.find((v) => String(v._id) === groupId);
|
const group = groups.find((v) => String(v._id) === groupId);
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -6,15 +6,11 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
|||||||
const isLLMNode = (item: ChatHistoryItemResType) =>
|
const isLLMNode = (item: ChatHistoryItemResType) =>
|
||||||
item.moduleType === FlowNodeTypeEnum.chatNode || item.moduleType === FlowNodeTypeEnum.tools;
|
item.moduleType === FlowNodeTypeEnum.chatNode || item.moduleType === FlowNodeTypeEnum.tools;
|
||||||
|
|
||||||
export function transformPreviewHistories(
|
export function transformPreviewHistories(histories: ChatItemType[]): ChatItemType[] {
|
||||||
histories: ChatItemType[],
|
|
||||||
responseDetail: boolean
|
|
||||||
): ChatItemType[] {
|
|
||||||
return histories.map((item) => {
|
return histories.map((item) => {
|
||||||
return {
|
return {
|
||||||
...addStatisticalDataToHistoryItem(item),
|
...addStatisticalDataToHistoryItem(item),
|
||||||
responseData: undefined,
|
responseData: undefined
|
||||||
...(responseDetail ? {} : { totalQuoteList: undefined })
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -22,7 +18,6 @@ export function transformPreviewHistories(
|
|||||||
export function addStatisticalDataToHistoryItem(historyItem: ChatItemType) {
|
export function addStatisticalDataToHistoryItem(historyItem: ChatItemType) {
|
||||||
if (historyItem.obj !== ChatRoleEnum.AI) return historyItem;
|
if (historyItem.obj !== ChatRoleEnum.AI) return historyItem;
|
||||||
if (historyItem.totalQuoteList !== undefined) return historyItem;
|
if (historyItem.totalQuoteList !== undefined) return historyItem;
|
||||||
if (!historyItem.responseData) return historyItem;
|
|
||||||
|
|
||||||
// Flat children
|
// Flat children
|
||||||
const flatResData: ChatHistoryItemResType[] =
|
const flatResData: ChatHistoryItemResType[] =
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
|||||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||||
import MyImage from '@fastgpt/web/components/common/Image/MyImage';
|
import MyImage from '@fastgpt/web/components/common/Image/MyImage';
|
||||||
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
|
|
||||||
|
|
||||||
const StandDetailModal = dynamic(() => import('./standardDetailModal'));
|
const StandDetailModal = dynamic(() => import('./standardDetailModal'));
|
||||||
const TeamMenu = dynamic(() => import('@/components/support/user/team/TeamMenu'));
|
const TeamMenu = dynamic(() => import('@/components/support/user/team/TeamMenu'));
|
||||||
@@ -495,7 +494,7 @@ const PlanUsage = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Link
|
<Link
|
||||||
href={getWebReqUrl(EXTRA_PLAN_CARD_ROUTE)}
|
href={EXTRA_PLAN_CARD_ROUTE}
|
||||||
transform={'translateX(15px)'}
|
transform={'translateX(15px)'}
|
||||||
display={'flex'}
|
display={'flex'}
|
||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
|
|||||||
@@ -82,16 +82,11 @@ async function handler(
|
|||||||
limit: pageSize
|
limit: pageSize
|
||||||
});
|
});
|
||||||
|
|
||||||
const responseDetail = !shareChat || shareChat.responseDetail;
|
|
||||||
|
|
||||||
// Remove important information
|
// Remove important information
|
||||||
if (shareChat && app.type !== AppTypeEnum.plugin) {
|
if (shareChat && app.type !== AppTypeEnum.plugin) {
|
||||||
histories.forEach((item) => {
|
histories.forEach((item) => {
|
||||||
if (item.obj === ChatRoleEnum.AI) {
|
if (item.obj === ChatRoleEnum.AI) {
|
||||||
item.responseData = filterPublicNodeResponseData({
|
item.responseData = filterPublicNodeResponseData({ flowResponses: item.responseData });
|
||||||
flowResponses: item.responseData,
|
|
||||||
responseDetail
|
|
||||||
});
|
|
||||||
|
|
||||||
if (shareChat.showNodeStatus === false) {
|
if (shareChat.showNodeStatus === false) {
|
||||||
item.value = item.value.filter((v) => v.type !== ChatItemValueTypeEnum.tool);
|
item.value = item.value.filter((v) => v.type !== ChatItemValueTypeEnum.tool);
|
||||||
@@ -101,7 +96,7 @@ async function handler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
list: isPlugin ? histories : transformPreviewHistories(histories, responseDetail),
|
list: isPlugin ? histories : transformPreviewHistories(histories),
|
||||||
total
|
total
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
|
|||||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||||
import { filterPublicNodeResponseData } from '@fastgpt/global/core/chat/utils';
|
import { filterPublicNodeResponseData } from '@fastgpt/global/core/chat/utils';
|
||||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
|
||||||
|
|
||||||
export type getResDataQuery = OutLinkChatAuthProps & {
|
export type getResDataQuery = OutLinkChatAuthProps & {
|
||||||
chatId?: string;
|
chatId?: string;
|
||||||
@@ -27,57 +26,44 @@ async function handler(
|
|||||||
req: ApiRequestProps<getResDataBody, getResDataQuery>,
|
req: ApiRequestProps<getResDataBody, getResDataQuery>,
|
||||||
res: ApiResponseType<any>
|
res: ApiResponseType<any>
|
||||||
): Promise<getResDataResponse> {
|
): Promise<getResDataResponse> {
|
||||||
const { appId, chatId, dataId, shareId } = req.query;
|
const { appId, chatId, dataId } = req.query;
|
||||||
if (!appId || !chatId || !dataId) {
|
if (!appId || !chatId || !dataId) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Un login api: share chat, team chat
|
// 1. Un login api: share chat, team chat
|
||||||
// 2. Login api: account chat, chat log
|
// 2. Login api: account chat, chat log
|
||||||
const authData = await (() => {
|
try {
|
||||||
try {
|
await authChatCrud({
|
||||||
return authChatCrud({
|
req,
|
||||||
req,
|
authToken: true,
|
||||||
authToken: true,
|
authApiKey: true,
|
||||||
authApiKey: true,
|
...req.query,
|
||||||
...req.query,
|
per: ReadPermissionVal
|
||||||
per: ReadPermissionVal
|
});
|
||||||
});
|
} catch (error) {
|
||||||
} catch (error) {
|
await authApp({
|
||||||
return authApp({
|
req,
|
||||||
req,
|
authToken: true,
|
||||||
authToken: true,
|
authApiKey: true,
|
||||||
authApiKey: true,
|
appId,
|
||||||
appId,
|
per: ManagePermissionVal
|
||||||
per: ManagePermissionVal
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const [chatData] = await Promise.all([
|
|
||||||
MongoChatItem.findOne(
|
|
||||||
{
|
|
||||||
appId,
|
|
||||||
chatId,
|
|
||||||
dataId
|
|
||||||
},
|
|
||||||
'obj responseData'
|
|
||||||
).lean(),
|
|
||||||
shareId ? MongoOutLink.findOne({ shareId }).lean() : Promise.resolve(null)
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (chatData?.obj !== ChatRoleEnum.AI) {
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const flowResponses = chatData.responseData ?? {};
|
const chatData = await MongoChatItem.findOne(
|
||||||
return req.query.shareId
|
{
|
||||||
? filterPublicNodeResponseData({
|
appId,
|
||||||
// @ts-ignore
|
chatId,
|
||||||
responseDetail: authData.responseDetail,
|
dataId
|
||||||
flowResponses: chatData.responseData
|
},
|
||||||
})
|
'obj responseData'
|
||||||
: flowResponses;
|
).lean();
|
||||||
|
|
||||||
|
if (chatData?.obj === ChatRoleEnum.AI) {
|
||||||
|
const data = chatData.responseData || {};
|
||||||
|
return req.query.shareId ? filterPublicNodeResponseData(data) : data;
|
||||||
|
} else return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NextAPI(handler);
|
export default NextAPI(handler);
|
||||||
|
|||||||
@@ -363,7 +363,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
/* select fe response field */
|
/* select fe response field */
|
||||||
const feResponseData = canWrite
|
const feResponseData = canWrite
|
||||||
? flowResponses
|
? flowResponses
|
||||||
: filterPublicNodeResponseData({ flowResponses, responseDetail });
|
: filterPublicNodeResponseData({ flowResponses });
|
||||||
|
|
||||||
if (stream) {
|
if (stream) {
|
||||||
workflowResponseWrite({
|
workflowResponseWrite({
|
||||||
@@ -380,10 +380,12 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (detail) {
|
if (detail) {
|
||||||
workflowResponseWrite({
|
if (responseDetail || isPlugin) {
|
||||||
event: SseResponseEventEnum.flowResponses,
|
workflowResponseWrite({
|
||||||
data: feResponseData
|
event: SseResponseEventEnum.flowResponses,
|
||||||
});
|
data: feResponseData
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.end();
|
res.end();
|
||||||
|
|||||||
@@ -104,10 +104,7 @@ const EditForm = ({
|
|||||||
const formatVariables = useMemo(
|
const formatVariables = useMemo(
|
||||||
() =>
|
() =>
|
||||||
formatEditorVariablePickerIcon([
|
formatEditorVariablePickerIcon([
|
||||||
...workflowSystemVariables.filter(
|
...workflowSystemVariables,
|
||||||
(variable) =>
|
|
||||||
!['appId', 'chatId', 'responseChatItemId', 'histories'].includes(variable.key)
|
|
||||||
),
|
|
||||||
...(appForm.chatConfig.variables || [])
|
...(appForm.chatConfig.variables || [])
|
||||||
]).map((item) => ({
|
]).map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ const ExtraPlan = () => {
|
|||||||
>
|
>
|
||||||
<MyNumberInput
|
<MyNumberInput
|
||||||
name="points"
|
name="points"
|
||||||
register={registerExtraPoints}
|
register={registerDatasetSize}
|
||||||
min={0}
|
min={0}
|
||||||
max={10000}
|
max={10000}
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
||||||
import { pushQAUsage } from '@/service/support/wallet/usage/push';
|
import { pushQAUsage } from '@/service/support/wallet/usage/push';
|
||||||
import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constants';
|
import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||||
import { createChatCompletion } from '@fastgpt/service/core/ai/config';
|
import { getAIApi } from '@fastgpt/service/core/ai/config';
|
||||||
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type.d';
|
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type.d';
|
||||||
import { addLog } from '@fastgpt/service/common/system/log';
|
import { addLog } from '@fastgpt/service/common/system/log';
|
||||||
import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter';
|
import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter';
|
||||||
@@ -109,8 +109,11 @@ ${replaceVariable(Prompt_AgentQA.fixedText, { text })}`;
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const { response: chatResponse } = await createChatCompletion({
|
const ai = getAIApi({
|
||||||
body: llmCompletionsBodyFormat(
|
timeout: 600000
|
||||||
|
});
|
||||||
|
const chatResponse = await ai.chat.completions.create(
|
||||||
|
llmCompletionsBodyFormat(
|
||||||
{
|
{
|
||||||
model: modelData.model,
|
model: modelData.model,
|
||||||
temperature: 0.3,
|
temperature: 0.3,
|
||||||
@@ -119,7 +122,7 @@ ${replaceVariable(Prompt_AgentQA.fixedText, { text })}`;
|
|||||||
},
|
},
|
||||||
modelData
|
modelData
|
||||||
)
|
)
|
||||||
});
|
);
|
||||||
const answer = chatResponse.choices?.[0].message?.content || '';
|
const answer = chatResponse.choices?.[0].message?.content || '';
|
||||||
|
|
||||||
const qaArr = formatSplitText(answer, text); // 格式化后的QA对
|
const qaArr = formatSplitText(answer, text); // 格式化后的QA对
|
||||||
@@ -162,7 +165,6 @@ ${replaceVariable(Prompt_AgentQA.fixedText, { text })}`;
|
|||||||
reduceQueue();
|
reduceQueue();
|
||||||
generateQA();
|
generateQA();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
addLog.error(`[QA Queue] Error`);
|
|
||||||
reduceQueue();
|
reduceQueue();
|
||||||
|
|
||||||
if (await checkInvalidChunkAndLock({ err, data, errText: 'QA模型调用失败' })) {
|
if (await checkInvalidChunkAndLock({ err, data, errText: 'QA模型调用失败' })) {
|
||||||
|
|||||||
@@ -121,7 +121,6 @@ export async function generateVector(): Promise<any> {
|
|||||||
reduceQueue();
|
reduceQueue();
|
||||||
generateVector();
|
generateVector();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
addLog.error(`[Vector Queue] Error`, err);
|
|
||||||
reduceQueue();
|
reduceQueue();
|
||||||
|
|
||||||
if (await checkInvalidChunkAndLock({ err, data, errText: '向量模型调用失败' })) {
|
if (await checkInvalidChunkAndLock({ err, data, errText: '向量模型调用失败' })) {
|
||||||
|
|||||||
@@ -42,19 +42,18 @@ export async function authChatCrud({
|
|||||||
chat?: ChatSchema;
|
chat?: ChatSchema;
|
||||||
isOutLink: boolean;
|
isOutLink: boolean;
|
||||||
uid?: string;
|
uid?: string;
|
||||||
responseDetail: boolean;
|
|
||||||
}> {
|
}> {
|
||||||
const isOutLink = Boolean((shareId || spaceTeamId) && outLinkUid);
|
const isOutLink = Boolean((shareId || spaceTeamId) && outLinkUid);
|
||||||
if (!chatId) return { isOutLink, uid: outLinkUid, responseDetail: true };
|
if (!chatId) return { isOutLink, uid: outLinkUid };
|
||||||
|
|
||||||
const chat = await MongoChat.findOne({ appId, chatId }).lean();
|
const chat = await MongoChat.findOne({ appId, chatId }).lean();
|
||||||
|
|
||||||
const { uid, responseDetail } = await (async () => {
|
const { uid } = await (async () => {
|
||||||
// outLink Auth
|
// outLink Auth
|
||||||
if (shareId && outLinkUid) {
|
if (shareId && outLinkUid) {
|
||||||
const { uid, shareChat } = await authOutLink({ shareId, outLinkUid });
|
const { uid } = await authOutLink({ shareId, outLinkUid });
|
||||||
if (!chat || (chat.shareId === shareId && chat.outLinkUid === uid)) {
|
if (!chat || (chat.shareId === shareId && chat.outLinkUid === uid)) {
|
||||||
return { uid, responseDetail: shareChat.responseDetail };
|
return { uid };
|
||||||
}
|
}
|
||||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||||
}
|
}
|
||||||
@@ -63,12 +62,12 @@ export async function authChatCrud({
|
|||||||
const { uid } = await authTeamSpaceToken({ teamId: spaceTeamId, teamToken });
|
const { uid } = await authTeamSpaceToken({ teamId: spaceTeamId, teamToken });
|
||||||
addLog.debug('Auth team token', { uid, spaceTeamId, teamToken, chatUid: chat?.outLinkUid });
|
addLog.debug('Auth team token', { uid, spaceTeamId, teamToken, chatUid: chat?.outLinkUid });
|
||||||
if (!chat || (String(chat.teamId) === String(spaceTeamId) && chat.outLinkUid === uid)) {
|
if (!chat || (String(chat.teamId) === String(spaceTeamId) && chat.outLinkUid === uid)) {
|
||||||
return { uid, responseDetail: true };
|
return { uid };
|
||||||
}
|
}
|
||||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!chat) return { id: outLinkUid, responseDetail: true };
|
if (!chat) return { id: outLinkUid };
|
||||||
|
|
||||||
// auth req
|
// auth req
|
||||||
const { teamId, tmbId, permission } = await authApp({
|
const { teamId, tmbId, permission } = await authApp({
|
||||||
@@ -81,19 +80,18 @@ export async function authChatCrud({
|
|||||||
|
|
||||||
if (String(teamId) !== String(chat.teamId)) return Promise.reject(ChatErrEnum.unAuthChat);
|
if (String(teamId) !== String(chat.teamId)) return Promise.reject(ChatErrEnum.unAuthChat);
|
||||||
|
|
||||||
if (permission.hasManagePer) return { uid: outLinkUid, responseDetail: true };
|
if (permission.hasManagePer) return { uid: outLinkUid };
|
||||||
if (String(tmbId) === String(chat.tmbId)) return { uid: outLinkUid, responseDetail: true };
|
if (String(tmbId) === String(chat.tmbId)) return { uid: outLinkUid };
|
||||||
|
|
||||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
if (!chat) return { isOutLink, uid, responseDetail };
|
if (!chat) return { isOutLink, uid };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
chat,
|
chat,
|
||||||
isOutLink,
|
isOutLink,
|
||||||
uid,
|
uid
|
||||||
responseDetail
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||