Compare commits
34 Commits
v4.8.10-al
...
v4.8.10-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
060492dbf7 | ||
|
|
9d5fd24085 | ||
|
|
903f39fe17 | ||
|
|
2ef98c24be | ||
|
|
6d00f73e91 | ||
|
|
813eaacfd0 | ||
|
|
322ca757af | ||
|
|
a177a302d4 | ||
|
|
034108c218 | ||
|
|
0632dfed80 | ||
|
|
6c16fa9166 | ||
|
|
ac4854a47b | ||
|
|
b9a6b71fe9 | ||
|
|
aba50e958e | ||
|
|
52cbfeace3 | ||
|
|
bebf565c06 | ||
|
|
c9bb39d802 | ||
|
|
454a479fd8 | ||
|
|
d057ad3a45 | ||
|
|
a206d77287 | ||
|
|
14bd1b5404 | ||
|
|
450167c951 | ||
|
|
67445b40bc | ||
|
|
d3731d221a | ||
|
|
f6e2d13e21 | ||
|
|
77e6cf4157 | ||
|
|
fd3f32d083 | ||
|
|
f7544ea47b | ||
|
|
a1a9a0b463 | ||
|
|
dbfe1fca31 | ||
|
|
94f3b7f2d6 | ||
|
|
22a0f6bcfa | ||
|
|
c1d08c0ccc | ||
|
|
a4c19fbd0a |
@@ -86,3 +86,12 @@ Verification Token 默认生成的这个 Token 用于校验来源。但我们使
|
|||||||
然后就可以在工作台里找到你的机器人啦。接下来就是把机器人拉进群组,或者单独与它对话。
|
然后就可以在工作台里找到你的机器人啦。接下来就是把机器人拉进群组,或者单独与它对话。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### 发送了消息,没响应
|
||||||
|
|
||||||
|
1. 检查飞书机器人回调地址、权限等是否正确。
|
||||||
|
2. 查看 FastGPT 对话日志,是否有对应的提问记录
|
||||||
|
3. 如果有记录,飞书没回应,则是没给机器人开权限。
|
||||||
|
4. 如果没记录,则可能是应用运行报错了,可以先试试最简单的机器人。(飞书机器人无法输入全局变量、文件、图片内容)
|
||||||
@@ -11,37 +11,74 @@ weight: 814
|
|||||||
|
|
||||||
### 1. 做好数据备份
|
### 1. 做好数据备份
|
||||||
|
|
||||||
### 2. 更新商业版环境变量
|
|
||||||
|
### 2. 商业版 —— 修改环境变量
|
||||||
|
|
||||||
1. 需要给`fastgpt-pro`镜像,增加沙盒的环境变量:`SANDBOX_URL=http://xxxxx:3000`
|
1. 需要给`fastgpt-pro`镜像,增加沙盒的环境变量:`SANDBOX_URL=http://xxxxx:3000`
|
||||||
2. 给两个镜像增加环境变量,以便更好的存储系统日志:
|
2. 给`fastgpt-pro`镜像和`fastgpt`镜像增加环境变量,以便更好的存储系统日志:
|
||||||
|
|
||||||
```
|
```
|
||||||
LOG_LEVEL=debug
|
LOG_LEVEL=debug
|
||||||
STORE_LOG_LEVEL=warn
|
STORE_LOG_LEVEL=warn
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 3. 修改镜像tag
|
||||||
|
|
||||||
|
- 更新 FastGPT 镜像 tag: v4.8.10-alpha
|
||||||
|
- 更新 FastGPT 商业版镜像 tag: v4.8.10-alpha
|
||||||
|
- Sandbox 镜像,可以不更新
|
||||||
|
|
||||||
|
## 4. 执行初始化
|
||||||
|
|
||||||
|
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 域名**。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location --request POST 'https://{{host}}/api/admin/initv4810' \
|
||||||
|
--header 'rootkey: {{rootkey}}' \
|
||||||
|
--header 'Content-Type: application/json'
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 初始化发布记录版本标记
|
||||||
|
2. 初始化开票记录
|
||||||
|
|
||||||
-------
|
-------
|
||||||
|
|
||||||
## V4.8.10 更新说明
|
## V4.8.10 更新说明
|
||||||
|
|
||||||
|
完整内容请见:[4.8.10 release](https://github.com/labring/FastGPT/releases/tag/v4.8.10-alpha)
|
||||||
|
|
||||||
1. 新增 - 模板市场
|
1. 新增 - 模板市场
|
||||||
2. 新增 - 工作流节点拖动自动对齐吸附
|
2. 新增 - 工作流节点拖动自动对齐吸附
|
||||||
3. 新增 - 用户选择节点(Debug 模式暂未支持)
|
3. 新增 - 用户选择节点(Debug 模式暂未支持)
|
||||||
4. 新增 - 工作流撤销和重做
|
4. 新增 - 工作流撤销和重做
|
||||||
5. 新增 - 工作流本次编辑记录,取代自动保存
|
5. 新增 - 工作流本次编辑记录,取代自动保存
|
||||||
6. 新增 - 工作流版本支持重命名
|
6. 新增 - 工作流版本支持重命名
|
||||||
7. 商业版新增 - 飞书机器人接入
|
7. 新增 - 应用调用迁移成单独节点,同时可以传递全局变量和用户的文件。
|
||||||
8. 商业版新增 - 公众号接入接入
|
8. 新增 - 插件增加使用说明配置。
|
||||||
9. 商业版新增 - 自助开票申请
|
9. 商业版新增 - 飞书机器人接入
|
||||||
10. 商业版新增 - SSO 定制
|
10. 商业版新增 - 公众号接入接入
|
||||||
11. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。
|
11. 商业版新增 - 自助开票申请
|
||||||
12. 优化 - 节点选择,避免切换 tab 时候,path 加载报错。
|
12. 商业版新增 - SSO 定制
|
||||||
13. 优化 - 最新 React Markdown 组件,支持 Base64 图片。
|
13. 优化 - SSE 响应优化。
|
||||||
14. 优化 - 知识库列表 UI。
|
14. 优化 - 无 SSL 证书情况下,优化复制。
|
||||||
15. 优化 - 支持无网络配置情况下运行。
|
15. 优化 - 单选框打开后自动滚动到选中的位置。
|
||||||
16. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。
|
16. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。
|
||||||
17. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
|
17. 优化 - 节点选择,避免切换 tab 时候,path 加载报错。
|
||||||
18. 修复 - 选择 Milvus 部署时,无法导出知识库。
|
18. 优化 - 最新 React Markdown 组件,支持 Base64 图片。
|
||||||
19. 修复 - 创建 APP 副本,无法复制系统配置。
|
19. 优化 - 知识库列表 UI。
|
||||||
20. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。
|
20. 优化 - 知识库详情页 UI。
|
||||||
|
21. 优化 - 支持无网络配置情况下运行。
|
||||||
|
22. 优化 - 部分全局变量,增加数据类型约束。
|
||||||
|
23. 修复 - 全局变量 key 可能重复。
|
||||||
|
24. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。
|
||||||
|
25. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
|
||||||
|
26. 修复 - 选择 Milvus 部署时,无法导出知识库。
|
||||||
|
27. 修复 - 创建 APP 副本,无法复制系统配置。
|
||||||
|
28. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。
|
||||||
|
29. 修复 - 内容提取的数据类型与输出数据类型未一致。
|
||||||
|
30. 修复 - 工作流运行时间统计错误。
|
||||||
|
31. 修复 - stream 模式下,工具调用有可能出现 undefined
|
||||||
|
32. 修复 - 全局变量在 API 中无法持久化。
|
||||||
|
33. 修复 - OpenAPI,detail=false模式下,不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题)
|
||||||
|
34. 修复 - 知识库标签重复加载。
|
||||||
|
35. 修复 - Debug 模式下,循环调用边问题。
|
||||||
|
|||||||
23
docSite/content/zh-cn/docs/development/upgrading/4811.md
Normal file
23
docSite/content/zh-cn/docs/development/upgrading/4811.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: 'V4.8.11(进行中)'
|
||||||
|
description: 'FastGPT V4.8.11 更新说明'
|
||||||
|
icon: 'upgrade'
|
||||||
|
draft: false
|
||||||
|
toc: true
|
||||||
|
weight: 813
|
||||||
|
---
|
||||||
|
|
||||||
|
## 更新指南
|
||||||
|
|
||||||
|
### 1. 做好数据备份
|
||||||
|
|
||||||
|
-------
|
||||||
|
|
||||||
|
## V4.8.11 更新预告
|
||||||
|
|
||||||
|
1.
|
||||||
|
2. 新增 - 插件自定义输入支持单选框
|
||||||
|
3. 新增 - 插件输出,支持指定某些字段为工具调用结果
|
||||||
|
4. 新增 - 插件支持配置使用引导、全局变量和文件输入
|
||||||
|
5. 优化 - SSE 响应代码。
|
||||||
|
6. 优化 - 非 HTTPS 环境下支持复制(除非 textarea 复制也不支持)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
baseURL = "https://doc.fastgpt.in"
|
baseURL = "https://doc.tryfastgpt.ai"
|
||||||
languageCode = "en-GB"
|
languageCode = "en-GB"
|
||||||
contentDir = "content"
|
contentDir = "content"
|
||||||
enableEmoji = true
|
enableEmoji = true
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"postinstall": "sh ./scripts/postinstall.sh",
|
"postinstall": "sh ./scripts/postinstall.sh",
|
||||||
"initIcon": "node ./scripts/icon/init.js",
|
"initIcon": "node ./scripts/icon/init.js",
|
||||||
"previewIcon": "node ./scripts/icon/index.js",
|
"previewIcon": "node ./scripts/icon/index.js",
|
||||||
"checkI18n": "node ./scripts/i18n/delete-unused-keys.js"
|
"i18n:delete-unused-keys": "node ./scripts/i18n/delete-unused-keys.js",
|
||||||
|
"i18n:query": "node ./scripts/i18n/query.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@chakra-ui/cli": "^2.4.1",
|
"@chakra-ui/cli": "^2.4.1",
|
||||||
@@ -30,4 +31,4 @@
|
|||||||
"node": ">=18.16.0",
|
"node": ">=18.16.0",
|
||||||
"pnpm": ">=9.0.0"
|
"pnpm": ">=9.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export const Prompt_AgentQA = {
|
|||||||
- 答案需详细完整,尽可能保留原文描述,可以适当扩展答案描述。
|
- 答案需详细完整,尽可能保留原文描述,可以适当扩展答案描述。
|
||||||
- 答案可以包含普通文字、链接、代码、表格、公示、媒体链接等 Markdown 元素。
|
- 答案可以包含普通文字、链接、代码、表格、公示、媒体链接等 Markdown 元素。
|
||||||
- 最多提出 50 个问题。
|
- 最多提出 50 个问题。
|
||||||
|
- 生成的问题和答案和源文本语言相同。
|
||||||
`,
|
`,
|
||||||
fixedText: `请按以下格式整理学习成果:
|
fixedText: `请按以下格式整理学习成果:
|
||||||
<Context>
|
<Context>
|
||||||
|
|||||||
10
packages/global/core/app/type.d.ts
vendored
10
packages/global/core/app/type.d.ts
vendored
@@ -1,7 +1,11 @@
|
|||||||
import type { FlowNodeTemplateType, StoreNodeItemType } from '../workflow/type/node';
|
import type { FlowNodeTemplateType, StoreNodeItemType } from '../workflow/type/node';
|
||||||
import { AppTypeEnum } from './constants';
|
import { AppTypeEnum } from './constants';
|
||||||
import { PermissionTypeEnum } from '../../support/permission/constant';
|
import { PermissionTypeEnum } from '../../support/permission/constant';
|
||||||
import { NodeInputKeyEnum, VariableInputEnum } from '../workflow/constants';
|
import {
|
||||||
|
NodeInputKeyEnum,
|
||||||
|
VariableInputEnum,
|
||||||
|
WorkflowIOValueTypeEnum
|
||||||
|
} from '../workflow/constants';
|
||||||
import { SelectedDatasetType } from '../workflow/api';
|
import { SelectedDatasetType } from '../workflow/api';
|
||||||
import { DatasetSearchModeEnum } from '../dataset/constants';
|
import { DatasetSearchModeEnum } from '../dataset/constants';
|
||||||
import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d';
|
import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d';
|
||||||
@@ -92,6 +96,9 @@ export type AppChatConfigType = {
|
|||||||
scheduledTriggerConfig?: AppScheduledTriggerConfigType;
|
scheduledTriggerConfig?: AppScheduledTriggerConfigType;
|
||||||
chatInputGuide?: ChatInputGuideConfigType;
|
chatInputGuide?: ChatInputGuideConfigType;
|
||||||
fileSelectConfig?: AppFileSelectConfigType;
|
fileSelectConfig?: AppFileSelectConfigType;
|
||||||
|
|
||||||
|
// plugin
|
||||||
|
instruction?: string;
|
||||||
};
|
};
|
||||||
export type SettingAIDataType = {
|
export type SettingAIDataType = {
|
||||||
model: string;
|
model: string;
|
||||||
@@ -111,6 +118,7 @@ export type VariableItemType = {
|
|||||||
required: boolean;
|
required: boolean;
|
||||||
maxLen: number;
|
maxLen: number;
|
||||||
enums: { value: string }[];
|
enums: { value: string }[];
|
||||||
|
valueType: WorkflowIOValueTypeEnum;
|
||||||
};
|
};
|
||||||
// tts
|
// tts
|
||||||
export type AppTTSConfigType = {
|
export type AppTTSConfigType = {
|
||||||
|
|||||||
1
packages/global/core/chat/type.d.ts
vendored
1
packages/global/core/chat/type.d.ts
vendored
@@ -151,6 +151,7 @@ export type ChatHistoryItemType = HistoryItemType & {
|
|||||||
/* ------- response data ------------ */
|
/* ------- response data ------------ */
|
||||||
export type ChatHistoryItemResType = DispatchNodeResponseType & {
|
export type ChatHistoryItemResType = DispatchNodeResponseType & {
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
|
id: string;
|
||||||
moduleType: FlowNodeTypeEnum;
|
moduleType: FlowNodeTypeEnum;
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ export const DatasetTypeMap = {
|
|||||||
collectionLabel: 'common.Folder'
|
collectionLabel: 'common.Folder'
|
||||||
},
|
},
|
||||||
[DatasetTypeEnum.dataset]: {
|
[DatasetTypeEnum.dataset]: {
|
||||||
icon: 'core/dataset/commonDataset',
|
icon: 'core/dataset/commonDatasetOutline',
|
||||||
label: 'common_dataset',
|
label: 'common_dataset',
|
||||||
collectionLabel: 'common.File'
|
collectionLabel: 'common.File'
|
||||||
},
|
},
|
||||||
[DatasetTypeEnum.websiteDataset]: {
|
[DatasetTypeEnum.websiteDataset]: {
|
||||||
icon: 'core/dataset/websiteDataset',
|
icon: 'core/dataset/websiteDatasetOutline',
|
||||||
label: 'website_dataset',
|
label: 'website_dataset',
|
||||||
collectionLabel: 'common.Website'
|
collectionLabel: 'common.Website'
|
||||||
},
|
},
|
||||||
[DatasetTypeEnum.externalFile]: {
|
[DatasetTypeEnum.externalFile]: {
|
||||||
icon: 'core/dataset/externalDataset',
|
icon: 'core/dataset/externalDatasetOutline',
|
||||||
label: 'external_file',
|
label: 'external_file',
|
||||||
collectionLabel: 'common.File'
|
collectionLabel: 'common.File'
|
||||||
}
|
}
|
||||||
|
|||||||
1
packages/global/core/dataset/type.d.ts
vendored
1
packages/global/core/dataset/type.d.ts
vendored
@@ -51,6 +51,7 @@ export type DatasetCollectionSchemaType = {
|
|||||||
chunkSize: number;
|
chunkSize: number;
|
||||||
chunkSplitter?: string;
|
chunkSplitter?: string;
|
||||||
qaPrompt?: string;
|
qaPrompt?: string;
|
||||||
|
ocrParse?: boolean;
|
||||||
|
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ export enum NodeInputKeyEnum {
|
|||||||
scheduleTrigger = 'scheduleTrigger',
|
scheduleTrigger = 'scheduleTrigger',
|
||||||
chatInputGuide = 'chatInputGuide',
|
chatInputGuide = 'chatInputGuide',
|
||||||
|
|
||||||
|
// plugin config
|
||||||
|
instruction = 'instruction',
|
||||||
|
|
||||||
// entry
|
// entry
|
||||||
userChatInput = 'userChatInput',
|
userChatInput = 'userChatInput',
|
||||||
inputFiles = 'inputFiles',
|
inputFiles = 'inputFiles',
|
||||||
@@ -128,6 +131,7 @@ export enum NodeInputKeyEnum {
|
|||||||
|
|
||||||
// read files
|
// read files
|
||||||
fileUrlList = 'fileUrlList',
|
fileUrlList = 'fileUrlList',
|
||||||
|
|
||||||
// user select
|
// user select
|
||||||
userSelectOptions = 'userSelectOptions'
|
userSelectOptions = 'userSelectOptions'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export enum FlowNodeInputTypeEnum { // render ui
|
|||||||
|
|
||||||
// special input
|
// special input
|
||||||
selectApp = 'selectApp',
|
selectApp = 'selectApp',
|
||||||
|
customVariable = 'customVariable',
|
||||||
|
|
||||||
// ai model select
|
// ai model select
|
||||||
selectLLMModel = 'selectLLMModel',
|
selectLLMModel = 'selectLLMModel',
|
||||||
@@ -44,7 +45,7 @@ export const FlowNodeInputMap: Record<
|
|||||||
icon: 'core/workflow/inputType/numberInput'
|
icon: 'core/workflow/inputType/numberInput'
|
||||||
},
|
},
|
||||||
[FlowNodeInputTypeEnum.select]: {
|
[FlowNodeInputTypeEnum.select]: {
|
||||||
icon: 'core/workflow/inputType/input'
|
icon: 'core/workflow/inputType/option'
|
||||||
},
|
},
|
||||||
[FlowNodeInputTypeEnum.switch]: {
|
[FlowNodeInputTypeEnum.switch]: {
|
||||||
icon: 'core/workflow/inputType/switch'
|
icon: 'core/workflow/inputType/switch'
|
||||||
@@ -79,8 +80,11 @@ export const FlowNodeInputMap: Record<
|
|||||||
[FlowNodeInputTypeEnum.hidden]: {
|
[FlowNodeInputTypeEnum.hidden]: {
|
||||||
icon: 'core/workflow/inputType/select'
|
icon: 'core/workflow/inputType/select'
|
||||||
},
|
},
|
||||||
|
[FlowNodeInputTypeEnum.customVariable]: {
|
||||||
|
icon: 'core/workflow/inputType/customVariable'
|
||||||
|
},
|
||||||
[FlowNodeInputTypeEnum.custom]: {
|
[FlowNodeInputTypeEnum.custom]: {
|
||||||
icon: 'core/workflow/inputType/select'
|
icon: 'core/workflow/inputType/custom'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -94,6 +98,7 @@ export enum FlowNodeOutputTypeEnum {
|
|||||||
export enum FlowNodeTypeEnum {
|
export enum FlowNodeTypeEnum {
|
||||||
emptyNode = 'emptyNode',
|
emptyNode = 'emptyNode',
|
||||||
systemConfig = 'userGuide',
|
systemConfig = 'userGuide',
|
||||||
|
pluginConfig = 'pluginConfig',
|
||||||
globalVariable = 'globalVariable',
|
globalVariable = 'globalVariable',
|
||||||
workflowStart = 'workflowStart',
|
workflowStart = 'workflowStart',
|
||||||
chatNode = 'chatNode',
|
chatNode = 'chatNode',
|
||||||
@@ -106,6 +111,7 @@ export enum FlowNodeTypeEnum {
|
|||||||
contentExtract = 'contentExtract',
|
contentExtract = 'contentExtract',
|
||||||
httpRequest468 = 'httpRequest468',
|
httpRequest468 = 'httpRequest468',
|
||||||
runApp = 'app',
|
runApp = 'app',
|
||||||
|
appModule = 'appModule',
|
||||||
pluginModule = 'pluginModule',
|
pluginModule = 'pluginModule',
|
||||||
pluginInput = 'pluginInput',
|
pluginInput = 'pluginInput',
|
||||||
pluginOutput = 'pluginOutput',
|
pluginOutput = 'pluginOutput',
|
||||||
|
|||||||
16
packages/global/core/workflow/runtime/type.d.ts
vendored
16
packages/global/core/workflow/runtime/type.d.ts
vendored
@@ -19,6 +19,7 @@ import { RuntimeNodeItemType } from '../runtime/type';
|
|||||||
import { RuntimeEdgeItemType } from './edge';
|
import { RuntimeEdgeItemType } from './edge';
|
||||||
import { ReadFileNodeResponse } from '../template/system/readFiles/type';
|
import { ReadFileNodeResponse } from '../template/system/readFiles/type';
|
||||||
import { UserSelectOptionType } from '../template/system/userSelect/type';
|
import { UserSelectOptionType } from '../template/system/userSelect/type';
|
||||||
|
import { WorkflowResponseType } from '../../../../service/core/workflow/dispatch/type';
|
||||||
|
|
||||||
/* workflow props */
|
/* workflow props */
|
||||||
export type ChatDispatchProps = {
|
export type ChatDispatchProps = {
|
||||||
@@ -36,9 +37,9 @@ export type ChatDispatchProps = {
|
|||||||
query: UserChatItemValueItemType[]; // trigger query
|
query: UserChatItemValueItemType[]; // trigger query
|
||||||
chatConfig: AppSchema['chatConfig'];
|
chatConfig: AppSchema['chatConfig'];
|
||||||
stream: boolean;
|
stream: boolean;
|
||||||
detail: boolean; // response detail
|
|
||||||
maxRunTimes: number;
|
maxRunTimes: number;
|
||||||
isToolCall?: boolean;
|
isToolCall?: boolean;
|
||||||
|
workflowStreamResponse?: WorkflowResponseType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ModuleDispatchProps<T> = ChatDispatchProps & {
|
export type ModuleDispatchProps<T> = ChatDispatchProps & {
|
||||||
@@ -95,6 +96,8 @@ export type DispatchNodeResponseType = {
|
|||||||
error?: Record<string, any>;
|
error?: Record<string, any>;
|
||||||
customInputs?: Record<string, any>;
|
customInputs?: Record<string, any>;
|
||||||
customOutputs?: Record<string, any>;
|
customOutputs?: Record<string, any>;
|
||||||
|
nodeInputs?: Record<string, any>;
|
||||||
|
nodeOutputs?: Record<string, any>;
|
||||||
|
|
||||||
// bill
|
// bill
|
||||||
tokens?: number;
|
tokens?: number;
|
||||||
@@ -158,15 +161,18 @@ export type DispatchNodeResponseType = {
|
|||||||
|
|
||||||
// user select
|
// user select
|
||||||
userSelectResult?: string;
|
userSelectResult?: string;
|
||||||
|
|
||||||
|
// update var
|
||||||
|
updateVarResult?: any[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DispatchNodeResultType<T> = {
|
export type DispatchNodeResultType<T> = {
|
||||||
[DispatchNodeResponseKeyEnum.skipHandleId]?: string[]; // skip some edge handle id
|
[DispatchNodeResponseKeyEnum.skipHandleId]?: string[]; // skip some edge handle id
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]?: DispatchNodeResponseType; // The node response detail
|
[DispatchNodeResponseKeyEnum.nodeResponse]?: DispatchNodeResponseType; // The node response detail
|
||||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]?: ChatNodeUsageType[]; //
|
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]?: ChatNodeUsageType[]; // Node total usage
|
||||||
[DispatchNodeResponseKeyEnum.childrenResponses]?: DispatchNodeResultType[];
|
[DispatchNodeResponseKeyEnum.childrenResponses]?: DispatchNodeResultType[]; // Children node response
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]?: ToolRunResponseItemType;
|
[DispatchNodeResponseKeyEnum.toolResponses]?: ToolRunResponseItemType; // Tool response
|
||||||
[DispatchNodeResponseKeyEnum.assistantResponses]?: ChatItemValueItemType[];
|
[DispatchNodeResponseKeyEnum.assistantResponses]?: ChatItemValueItemType[]; // Assistant response(Store to db)
|
||||||
} & T;
|
} & T;
|
||||||
|
|
||||||
/* Single node props */
|
/* Single node props */
|
||||||
|
|||||||
@@ -117,39 +117,6 @@ export const filterWorkflowEdges = (edges: RuntimeEdgeItemType[]) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
区分普通连线和递归连线
|
|
||||||
递归连线:可以通过往上查询 nodes,最终追溯到自身
|
|
||||||
*/
|
|
||||||
export const splitEdges2WorkflowEdges = ({
|
|
||||||
edges,
|
|
||||||
allEdges,
|
|
||||||
currentNode
|
|
||||||
}: {
|
|
||||||
edges: RuntimeEdgeItemType[];
|
|
||||||
allEdges: RuntimeEdgeItemType[];
|
|
||||||
currentNode: RuntimeNodeItemType;
|
|
||||||
}) => {
|
|
||||||
const commonEdges: RuntimeEdgeItemType[] = [];
|
|
||||||
const recursiveEdges: RuntimeEdgeItemType[] = [];
|
|
||||||
|
|
||||||
edges.forEach((edge) => {
|
|
||||||
const checkIsCurrentNode = (edge: RuntimeEdgeItemType): boolean => {
|
|
||||||
const sourceEdge = allEdges.find((item) => item.target === edge.source);
|
|
||||||
if (!sourceEdge) return false;
|
|
||||||
if (sourceEdge.source === currentNode.nodeId) return true;
|
|
||||||
return checkIsCurrentNode(sourceEdge);
|
|
||||||
};
|
|
||||||
if (checkIsCurrentNode(edge)) {
|
|
||||||
recursiveEdges.push(edge);
|
|
||||||
} else {
|
|
||||||
commonEdges.push(edge);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return { commonEdges, recursiveEdges };
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
1. 输入线分类:普通线和递归线(可以追溯到自身)
|
1. 输入线分类:普通线和递归线(可以追溯到自身)
|
||||||
2. 起始线全部非 waiting 执行,或递归线全部非 waiting 执行
|
2. 起始线全部非 waiting 执行,或递归线全部非 waiting 执行
|
||||||
@@ -161,31 +128,72 @@ export const checkNodeRunStatus = ({
|
|||||||
node: RuntimeNodeItemType;
|
node: RuntimeNodeItemType;
|
||||||
runtimeEdges: RuntimeEdgeItemType[];
|
runtimeEdges: RuntimeEdgeItemType[];
|
||||||
}) => {
|
}) => {
|
||||||
const workflowEdges = filterWorkflowEdges(runtimeEdges).filter(
|
/*
|
||||||
|
区分普通连线和递归连线
|
||||||
|
递归连线:可以通过往上查询 nodes,最终追溯到自身
|
||||||
|
*/
|
||||||
|
const splitEdges2WorkflowEdges = ({
|
||||||
|
sourceEdges,
|
||||||
|
allEdges,
|
||||||
|
currentNode
|
||||||
|
}: {
|
||||||
|
sourceEdges: RuntimeEdgeItemType[];
|
||||||
|
allEdges: RuntimeEdgeItemType[];
|
||||||
|
currentNode: RuntimeNodeItemType;
|
||||||
|
}) => {
|
||||||
|
const commonEdges: RuntimeEdgeItemType[] = [];
|
||||||
|
const recursiveEdges: RuntimeEdgeItemType[] = [];
|
||||||
|
|
||||||
|
const checkIsCircular = (edge: RuntimeEdgeItemType, visited: Set<string>): boolean => {
|
||||||
|
if (edge.source === currentNode.nodeId) {
|
||||||
|
return true; // 检测到环,并且环中包含当前节点
|
||||||
|
}
|
||||||
|
if (visited.has(edge.source)) {
|
||||||
|
return false; // 检测到环,但不包含当前节点(子节点成环)
|
||||||
|
}
|
||||||
|
visited.add(edge.source);
|
||||||
|
|
||||||
|
const nextEdges = allEdges.filter((item) => item.target === edge.source);
|
||||||
|
return nextEdges.some((nextEdge) => checkIsCircular(nextEdge, new Set(visited)));
|
||||||
|
};
|
||||||
|
|
||||||
|
sourceEdges.forEach((edge) => {
|
||||||
|
if (checkIsCircular(edge, new Set([currentNode.nodeId]))) {
|
||||||
|
recursiveEdges.push(edge);
|
||||||
|
} else {
|
||||||
|
commonEdges.push(edge);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { commonEdges, recursiveEdges };
|
||||||
|
};
|
||||||
|
|
||||||
|
const runtimeNodeSourceEdge = filterWorkflowEdges(runtimeEdges).filter(
|
||||||
(item) => item.target === node.nodeId
|
(item) => item.target === node.nodeId
|
||||||
);
|
);
|
||||||
|
|
||||||
// Entry
|
// Entry
|
||||||
if (workflowEdges.length === 0) {
|
if (runtimeNodeSourceEdge.length === 0) {
|
||||||
return 'run';
|
return 'run';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Classify edges
|
||||||
const { commonEdges, recursiveEdges } = splitEdges2WorkflowEdges({
|
const { commonEdges, recursiveEdges } = splitEdges2WorkflowEdges({
|
||||||
edges: workflowEdges,
|
sourceEdges: runtimeNodeSourceEdge,
|
||||||
allEdges: runtimeEdges,
|
allEdges: runtimeEdges,
|
||||||
currentNode: node
|
currentNode: node
|
||||||
});
|
});
|
||||||
|
|
||||||
// check skip
|
// check skip(其中一组边,全 skip)
|
||||||
if (commonEdges.every((item) => item.status === 'skipped')) {
|
if (commonEdges.length > 0 && commonEdges.every((item) => item.status === 'skipped')) {
|
||||||
return 'skip';
|
return 'skip';
|
||||||
}
|
}
|
||||||
if (recursiveEdges.length > 0 && recursiveEdges.every((item) => item.status === 'skipped')) {
|
if (recursiveEdges.length > 0 && recursiveEdges.every((item) => item.status === 'skipped')) {
|
||||||
return 'skip';
|
return 'skip';
|
||||||
}
|
}
|
||||||
|
|
||||||
// check active
|
// check active(有一类边,不全是 wait 即可运行)
|
||||||
if (commonEdges.every((item) => item.status !== 'waiting')) {
|
if (commonEdges.length > 0 && commonEdges.every((item) => item.status !== 'waiting')) {
|
||||||
return 'run';
|
return 'run';
|
||||||
}
|
}
|
||||||
if (recursiveEdges.length > 0 && recursiveEdges.every((item) => item.status !== 'waiting')) {
|
if (recursiveEdges.length > 0 && recursiveEdges.every((item) => item.status !== 'waiting')) {
|
||||||
@@ -236,7 +244,7 @@ export const textAdaptGptResponse = ({
|
|||||||
finish_reason?: null | 'stop';
|
finish_reason?: null | 'stop';
|
||||||
extraData?: Object;
|
extraData?: Object;
|
||||||
}) => {
|
}) => {
|
||||||
return JSON.stringify({
|
return {
|
||||||
...extraData,
|
...extraData,
|
||||||
id: '',
|
id: '',
|
||||||
object: '',
|
object: '',
|
||||||
@@ -252,7 +260,7 @@ export const textAdaptGptResponse = ({
|
|||||||
finish_reason
|
finish_reason
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Update runtimeNode's outputs with interactive data from history */
|
/* Update runtimeNode's outputs with interactive data from history */
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { SystemConfigNode } from './system/systemConfig';
|
import { SystemConfigNode } from './system/systemConfig';
|
||||||
|
import { PluginConfigNode } from './system/pluginConfig';
|
||||||
import { EmptyNode } from './system/emptyNode';
|
import { EmptyNode } from './system/emptyNode';
|
||||||
import { WorkflowStart } from './system/workflowStart';
|
import { WorkflowStart } from './system/workflowStart';
|
||||||
import { AiChatModule } from './system/aiChat';
|
import { AiChatModule } from './system/aiChat';
|
||||||
@@ -12,10 +13,11 @@ import { HttpNode468 } from './system/http468';
|
|||||||
import { ToolModule } from './system/tools';
|
import { ToolModule } from './system/tools';
|
||||||
import { StopToolNode } from './system/stopTool';
|
import { StopToolNode } from './system/stopTool';
|
||||||
|
|
||||||
import { RunAppModule } from './system/runApp/index';
|
import { RunAppModule } from './system/abandoned/runApp/index';
|
||||||
import { PluginInputModule } from './system/pluginInput';
|
import { PluginInputModule } from './system/pluginInput';
|
||||||
import { PluginOutputModule } from './system/pluginOutput';
|
import { PluginOutputModule } from './system/pluginOutput';
|
||||||
import { RunPluginModule } from './system/runPlugin';
|
import { RunPluginModule } from './system/runPlugin';
|
||||||
|
import { RunAppPluginModule } from './system/runAppPlugin';
|
||||||
import { AiQueryExtension } from './system/queryExtension';
|
import { AiQueryExtension } from './system/queryExtension';
|
||||||
|
|
||||||
import type { FlowNodeTemplateType } from '../type/node';
|
import type { FlowNodeTemplateType } from '../type/node';
|
||||||
@@ -44,8 +46,7 @@ const systemNodes: FlowNodeTemplateType[] = [
|
|||||||
LafModule,
|
LafModule,
|
||||||
IfElseNode,
|
IfElseNode,
|
||||||
VariableUpdateNode,
|
VariableUpdateNode,
|
||||||
CodeNode,
|
CodeNode
|
||||||
RunAppModule
|
|
||||||
];
|
];
|
||||||
/* app flow module templates */
|
/* app flow module templates */
|
||||||
export const appSystemModuleTemplates: FlowNodeTemplateType[] = [
|
export const appSystemModuleTemplates: FlowNodeTemplateType[] = [
|
||||||
@@ -57,6 +58,7 @@ export const appSystemModuleTemplates: FlowNodeTemplateType[] = [
|
|||||||
];
|
];
|
||||||
/* plugin flow module templates */
|
/* plugin flow module templates */
|
||||||
export const pluginSystemModuleTemplates: FlowNodeTemplateType[] = [
|
export const pluginSystemModuleTemplates: FlowNodeTemplateType[] = [
|
||||||
|
PluginConfigNode,
|
||||||
PluginInputModule,
|
PluginInputModule,
|
||||||
PluginOutputModule,
|
PluginOutputModule,
|
||||||
...systemNodes
|
...systemNodes
|
||||||
@@ -70,5 +72,7 @@ export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
EmptyNode,
|
EmptyNode,
|
||||||
RunPluginModule
|
RunPluginModule,
|
||||||
|
RunAppPluginModule,
|
||||||
|
RunAppModule
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ export const Input_Template_History: FlowNodeInputItemType = {
|
|||||||
key: NodeInputKeyEnum.history,
|
key: NodeInputKeyEnum.history,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference],
|
renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference],
|
||||||
valueType: WorkflowIOValueTypeEnum.chatHistory,
|
valueType: WorkflowIOValueTypeEnum.chatHistory,
|
||||||
label: 'core.module.input.label.chat history',
|
label: i18nT('common:core.module.input.label.chat history'),
|
||||||
description: '最多携带多少轮对话记录',
|
description: i18nT('workflow:max_dialog_rounds'),
|
||||||
|
|
||||||
required: true,
|
required: true,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 50,
|
max: 50,
|
||||||
@@ -21,7 +22,7 @@ export const Input_Template_UserChatInput: FlowNodeInputItemType = {
|
|||||||
key: NodeInputKeyEnum.userChatInput,
|
key: NodeInputKeyEnum.userChatInput,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.textarea],
|
renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.textarea],
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
label: '用户问题',
|
label: i18nT('workflow:user_question'),
|
||||||
required: true
|
required: true
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -36,14 +37,14 @@ export const Input_Template_DynamicInput: FlowNodeInputItemType = {
|
|||||||
export const Input_Template_SelectAIModel: FlowNodeInputItemType = {
|
export const Input_Template_SelectAIModel: FlowNodeInputItemType = {
|
||||||
key: NodeInputKeyEnum.aiModel,
|
key: NodeInputKeyEnum.aiModel,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.selectLLMModel, FlowNodeInputTypeEnum.reference],
|
renderTypeList: [FlowNodeInputTypeEnum.selectLLMModel, FlowNodeInputTypeEnum.reference],
|
||||||
label: 'core.module.input.label.aiModel',
|
label: i18nT('common:core.module.input.label.aiModel'),
|
||||||
required: true,
|
required: true,
|
||||||
valueType: WorkflowIOValueTypeEnum.string
|
valueType: WorkflowIOValueTypeEnum.string
|
||||||
};
|
};
|
||||||
export const Input_Template_SettingAiModel: FlowNodeInputItemType = {
|
export const Input_Template_SettingAiModel: FlowNodeInputItemType = {
|
||||||
key: NodeInputKeyEnum.aiModel,
|
key: NodeInputKeyEnum.aiModel,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.settingLLMModel, FlowNodeInputTypeEnum.reference],
|
renderTypeList: [FlowNodeInputTypeEnum.settingLLMModel, FlowNodeInputTypeEnum.reference],
|
||||||
label: 'core.module.input.label.aiModel',
|
label: i18nT('common:core.module.input.label.aiModel'),
|
||||||
valueType: WorkflowIOValueTypeEnum.string
|
valueType: WorkflowIOValueTypeEnum.string
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ export const Input_Template_System_Prompt: FlowNodeInputItemType = {
|
|||||||
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
|
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
|
||||||
max: 3000,
|
max: 3000,
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
label: 'core.ai.Prompt',
|
label: i18nT('common:core.ai.Prompt'),
|
||||||
description: chatNodeSystemPromptTip,
|
description: chatNodeSystemPromptTip,
|
||||||
placeholder: chatNodeSystemPromptTip
|
placeholder: chatNodeSystemPromptTip
|
||||||
};
|
};
|
||||||
@@ -61,7 +62,7 @@ export const Input_Template_Dataset_Quote: FlowNodeInputItemType = {
|
|||||||
key: NodeInputKeyEnum.aiChatDatasetQuote,
|
key: NodeInputKeyEnum.aiChatDatasetQuote,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.settingDatasetQuotePrompt],
|
renderTypeList: [FlowNodeInputTypeEnum.settingDatasetQuotePrompt],
|
||||||
label: '',
|
label: '',
|
||||||
debugLabel: '知识库引用',
|
debugLabel: i18nT('workflow:knowledge_base_reference'),
|
||||||
description: '',
|
description: '',
|
||||||
valueType: WorkflowIOValueTypeEnum.datasetQuote
|
valueType: WorkflowIOValueTypeEnum.datasetQuote
|
||||||
};
|
};
|
||||||
@@ -73,3 +74,12 @@ export const Input_Template_Text_Quote: FlowNodeInputItemType = {
|
|||||||
description: i18nT('app:document_quote_tip'),
|
description: i18nT('app:document_quote_tip'),
|
||||||
valueType: WorkflowIOValueTypeEnum.string
|
valueType: WorkflowIOValueTypeEnum.string
|
||||||
};
|
};
|
||||||
|
export const Input_Template_File_Link: FlowNodeInputItemType = {
|
||||||
|
key: NodeInputKeyEnum.fileUrlList,
|
||||||
|
renderTypeList: [FlowNodeInputTypeEnum.reference],
|
||||||
|
required: true,
|
||||||
|
label: i18nT('app:workflow.user_file_input'),
|
||||||
|
debugLabel: i18nT('app:workflow.user_file_input'),
|
||||||
|
description: i18nT('app:workflow.user_file_input_desc'),
|
||||||
|
valueType: WorkflowIOValueTypeEnum.arrayString
|
||||||
|
};
|
||||||
|
|||||||
@@ -3,16 +3,17 @@ import {
|
|||||||
FlowNodeInputTypeEnum,
|
FlowNodeInputTypeEnum,
|
||||||
FlowNodeOutputTypeEnum,
|
FlowNodeOutputTypeEnum,
|
||||||
FlowNodeTypeEnum
|
FlowNodeTypeEnum
|
||||||
} from '../../../node/constant';
|
} from '../../../../node/constant';
|
||||||
import { FlowNodeTemplateType } from '../../../type/node.d';
|
import { FlowNodeTemplateType } from '../../../../type/node';
|
||||||
import {
|
import {
|
||||||
WorkflowIOValueTypeEnum,
|
WorkflowIOValueTypeEnum,
|
||||||
NodeInputKeyEnum,
|
NodeInputKeyEnum,
|
||||||
NodeOutputKeyEnum,
|
NodeOutputKeyEnum,
|
||||||
FlowNodeTemplateTypeEnum
|
FlowNodeTemplateTypeEnum
|
||||||
} from '../../../constants';
|
} from '../../../../constants';
|
||||||
import { Input_Template_History, Input_Template_UserChatInput } from '../../input';
|
import { Input_Template_History, Input_Template_UserChatInput } from '../../../input';
|
||||||
import { getHandleConfig } from '../../utils';
|
import { getHandleConfig } from '../../../utils';
|
||||||
|
import { i18nT } from '../../../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export const RunAppModule: FlowNodeTemplateType = {
|
export const RunAppModule: FlowNodeTemplateType = {
|
||||||
id: FlowNodeTypeEnum.runApp,
|
id: FlowNodeTypeEnum.runApp,
|
||||||
@@ -21,8 +22,8 @@ export const RunAppModule: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(true, true, true, true),
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
avatar: 'core/workflow/template/runApp',
|
avatar: 'core/workflow/template/runApp',
|
||||||
name: '应用调用',
|
name: i18nT('workflow:application_call'),
|
||||||
intro: '可以选择一个其他应用进行调用',
|
intro: i18nT('workflow:select_another_application_to_call'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
version: '481',
|
version: '481',
|
||||||
isTool: true,
|
isTool: true,
|
||||||
@@ -31,22 +32,22 @@ export const RunAppModule: FlowNodeTemplateType = {
|
|||||||
key: NodeInputKeyEnum.runAppSelectApp,
|
key: NodeInputKeyEnum.runAppSelectApp,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.selectApp, FlowNodeInputTypeEnum.reference],
|
renderTypeList: [FlowNodeInputTypeEnum.selectApp, FlowNodeInputTypeEnum.reference],
|
||||||
valueType: WorkflowIOValueTypeEnum.selectApp,
|
valueType: WorkflowIOValueTypeEnum.selectApp,
|
||||||
label: '选择一个应用',
|
label: i18nT('workflow:select_an_application'),
|
||||||
description: '选择一个其他应用进行调用',
|
description: i18nT('workflow:choose_another_application_to_call'),
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
Input_Template_History,
|
Input_Template_History,
|
||||||
{
|
{
|
||||||
...Input_Template_UserChatInput,
|
...Input_Template_UserChatInput,
|
||||||
toolDescription: '用户问题'
|
toolDescription: i18nT('workflow:user_question')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
outputs: [
|
outputs: [
|
||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.history,
|
id: NodeOutputKeyEnum.history,
|
||||||
key: NodeOutputKeyEnum.history,
|
key: NodeOutputKeyEnum.history,
|
||||||
label: '新的上下文',
|
label: i18nT('workflow:new_context'),
|
||||||
description: '将该应用回复内容拼接到历史记录中,作为新的上下文返回',
|
description: i18nT('workflow:append_application_reply_to_history_as_new_context'),
|
||||||
valueType: WorkflowIOValueTypeEnum.chatHistory,
|
valueType: WorkflowIOValueTypeEnum.chatHistory,
|
||||||
valueDesc: chatHistoryValueDesc,
|
valueDesc: chatHistoryValueDesc,
|
||||||
required: true,
|
required: true,
|
||||||
@@ -55,8 +56,8 @@ export const RunAppModule: FlowNodeTemplateType = {
|
|||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.answerText,
|
id: NodeOutputKeyEnum.answerText,
|
||||||
key: NodeOutputKeyEnum.answerText,
|
key: NodeOutputKeyEnum.answerText,
|
||||||
label: '回复的文本',
|
label: i18nT('workflow:reply_text'),
|
||||||
description: '将在应用完全结束后触发',
|
description: i18nT('workflow:trigger_after_application_completion'),
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
}
|
}
|
||||||
@@ -81,22 +81,23 @@ export const AiChatModule: FlowNodeTemplateType = {
|
|||||||
// settings modal ---
|
// settings modal ---
|
||||||
{
|
{
|
||||||
...Input_Template_System_Prompt,
|
...Input_Template_System_Prompt,
|
||||||
label: 'core.ai.Prompt',
|
label: i18nT('common:core.ai.Prompt'),
|
||||||
description: chatNodeSystemPromptTip,
|
description: chatNodeSystemPromptTip,
|
||||||
placeholder: chatNodeSystemPromptTip
|
placeholder: chatNodeSystemPromptTip
|
||||||
},
|
},
|
||||||
Input_Template_History,
|
Input_Template_History,
|
||||||
Input_Template_Dataset_Quote,
|
Input_Template_Dataset_Quote,
|
||||||
Input_Template_Text_Quote,
|
Input_Template_Text_Quote,
|
||||||
{ ...Input_Template_UserChatInput, toolDescription: '用户问题' }
|
|
||||||
|
{ ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') }
|
||||||
],
|
],
|
||||||
outputs: [
|
outputs: [
|
||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.history,
|
id: NodeOutputKeyEnum.history,
|
||||||
key: NodeOutputKeyEnum.history,
|
key: NodeOutputKeyEnum.history,
|
||||||
required: true,
|
required: true,
|
||||||
label: 'core.module.output.label.New context',
|
label: i18nT('common:core.module.output.label.New context'),
|
||||||
description: 'core.module.output.description.New context',
|
description: i18nT('common:core.module.output.description.New context'),
|
||||||
valueType: WorkflowIOValueTypeEnum.chatHistory,
|
valueType: WorkflowIOValueTypeEnum.chatHistory,
|
||||||
valueDesc: chatHistoryValueDesc,
|
valueDesc: chatHistoryValueDesc,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
@@ -105,8 +106,8 @@ export const AiChatModule: FlowNodeTemplateType = {
|
|||||||
id: NodeOutputKeyEnum.answerText,
|
id: NodeOutputKeyEnum.answerText,
|
||||||
key: NodeOutputKeyEnum.answerText,
|
key: NodeOutputKeyEnum.answerText,
|
||||||
required: true,
|
required: true,
|
||||||
label: 'core.module.output.label.Ai response content',
|
label: i18nT('common:core.module.output.label.Ai response content'),
|
||||||
description: 'core.module.output.description.Ai response content',
|
description: i18nT('common:core.module.output.description.Ai response content'),
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
FlowNodeTemplateTypeEnum
|
FlowNodeTemplateTypeEnum
|
||||||
} from '../../constants';
|
} from '../../constants';
|
||||||
import { getHandleConfig } from '../utils';
|
import { getHandleConfig } from '../utils';
|
||||||
|
import { i18nT } from '../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export const AssignedAnswerModule: FlowNodeTemplateType = {
|
export const AssignedAnswerModule: FlowNodeTemplateType = {
|
||||||
id: FlowNodeTypeEnum.answerNode,
|
id: FlowNodeTypeEnum.answerNode,
|
||||||
@@ -14,9 +15,9 @@ export const AssignedAnswerModule: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(true, true, true, true),
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
avatar: 'core/workflow/template/reply',
|
avatar: 'core/workflow/template/reply',
|
||||||
name: '指定回复',
|
name: i18nT('workflow:assigned_reply'),
|
||||||
intro:
|
intro: i18nT('workflow:intro_assigned_reply'),
|
||||||
'该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。',
|
|
||||||
version: '481',
|
version: '481',
|
||||||
isTool: true,
|
isTool: true,
|
||||||
inputs: [
|
inputs: [
|
||||||
@@ -25,9 +26,9 @@ export const AssignedAnswerModule: FlowNodeTemplateType = {
|
|||||||
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
|
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
|
||||||
valueType: WorkflowIOValueTypeEnum.any,
|
valueType: WorkflowIOValueTypeEnum.any,
|
||||||
required: true,
|
required: true,
|
||||||
label: 'core.module.input.label.Response content',
|
label: i18nT('common:core.module.input.label.Response content'),
|
||||||
description: 'core.module.input.description.Response content',
|
description: i18nT('common:core.module.input.description.Response content'),
|
||||||
placeholder: 'core.module.input.description.Response content'
|
placeholder: i18nT('common:core.module.input.description.Response content')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
outputs: []
|
outputs: []
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
import { Input_Template_System_Prompt } from '../../input';
|
import { Input_Template_System_Prompt } from '../../input';
|
||||||
import { LLMModelTypeEnum } from '../../../../ai/constants';
|
import { LLMModelTypeEnum } from '../../../../ai/constants';
|
||||||
import { getHandleConfig } from '../../utils';
|
import { getHandleConfig } from '../../utils';
|
||||||
|
import { i18nT } from '../../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export const ClassifyQuestionModule: FlowNodeTemplateType = {
|
export const ClassifyQuestionModule: FlowNodeTemplateType = {
|
||||||
id: FlowNodeTypeEnum.classifyQuestion,
|
id: FlowNodeTypeEnum.classifyQuestion,
|
||||||
@@ -26,8 +27,8 @@ export const ClassifyQuestionModule: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(false, false, false, false),
|
sourceHandle: getHandleConfig(false, false, false, false),
|
||||||
targetHandle: getHandleConfig(true, false, true, true),
|
targetHandle: getHandleConfig(true, false, true, true),
|
||||||
avatar: 'core/workflow/template/questionClassify',
|
avatar: 'core/workflow/template/questionClassify',
|
||||||
name: '问题分类',
|
name: i18nT('workflow:question_classification'),
|
||||||
intro: `根据用户的历史记录和当前问题判断该次提问的类型。可以添加多组问题类型,下面是一个模板例子:\n类型1: 打招呼\n类型2: 关于商品“使用”问题\n类型3: 关于商品“购买”问题\n类型4: 其他问题`,
|
intro: i18nT('workflow:intro_question_classification'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [
|
inputs: [
|
||||||
@@ -50,15 +51,15 @@ export const ClassifyQuestionModule: FlowNodeTemplateType = {
|
|||||||
label: '',
|
label: '',
|
||||||
value: [
|
value: [
|
||||||
{
|
{
|
||||||
value: '打招呼',
|
value: i18nT('workflow:greeting'),
|
||||||
key: 'wqre'
|
key: 'wqre'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: '关于 xxx 的问题',
|
value: i18nT('workflow:about_xxx_question'),
|
||||||
key: 'sdfa'
|
key: 'sdfa'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: '其他问题',
|
value: i18nT('workflow:other_questions'),
|
||||||
key: 'agex'
|
key: 'agex'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -69,7 +70,7 @@ export const ClassifyQuestionModule: FlowNodeTemplateType = {
|
|||||||
id: NodeOutputKeyEnum.cqResult,
|
id: NodeOutputKeyEnum.cqResult,
|
||||||
key: NodeOutputKeyEnum.cqResult,
|
key: NodeOutputKeyEnum.cqResult,
|
||||||
required: true,
|
required: true,
|
||||||
label: '分类结果',
|
label: i18nT('workflow:classification_result'),
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
import { Input_Template_SelectAIModel, Input_Template_History } from '../../input';
|
import { Input_Template_SelectAIModel, Input_Template_History } from '../../input';
|
||||||
import { LLMModelTypeEnum } from '../../../../ai/constants';
|
import { LLMModelTypeEnum } from '../../../../ai/constants';
|
||||||
import { getHandleConfig } from '../../utils';
|
import { getHandleConfig } from '../../utils';
|
||||||
|
import { i18nT } from '../../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export const ContextExtractModule: FlowNodeTemplateType = {
|
export const ContextExtractModule: FlowNodeTemplateType = {
|
||||||
id: FlowNodeTypeEnum.contentExtract,
|
id: FlowNodeTypeEnum.contentExtract,
|
||||||
@@ -21,8 +22,8 @@ export const ContextExtractModule: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(true, true, true, true),
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
avatar: 'core/workflow/template/extractJson',
|
avatar: 'core/workflow/template/extractJson',
|
||||||
name: '文本内容提取',
|
name: i18nT('workflow:text_content_extraction'),
|
||||||
intro: '可从文本中提取指定的数据,例如:sql语句、搜索关键词、代码等',
|
intro: i18nT('workflow:intro_text_content_extraction'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
isTool: true,
|
isTool: true,
|
||||||
version: '481',
|
version: '481',
|
||||||
@@ -35,27 +36,25 @@ export const ContextExtractModule: FlowNodeTemplateType = {
|
|||||||
key: NodeInputKeyEnum.description,
|
key: NodeInputKeyEnum.description,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
|
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
label: '提取要求描述',
|
label: i18nT('workflow:extraction_requirements_description'),
|
||||||
description:
|
description: i18nT('workflow:extraction_requirements_description_detail'),
|
||||||
'给AI一些对应的背景知识或要求描述,引导AI更好的完成任务。\n该输入框可使用全局变量。',
|
placeholder: i18nT('workflow:extraction_requirements_placeholder')
|
||||||
placeholder:
|
|
||||||
'例如: \n1. 当前时间为: {{cTime}}。你是一个实验室预约助手,你的任务是帮助用户预约实验室,从文本中获取对应的预约信息。\n2. 你是谷歌搜索助手,需要从文本中提取出合适的搜索词。'
|
|
||||||
},
|
},
|
||||||
Input_Template_History,
|
Input_Template_History,
|
||||||
{
|
{
|
||||||
key: NodeInputKeyEnum.contextExtractInput,
|
key: NodeInputKeyEnum.contextExtractInput,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.textarea],
|
renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.textarea],
|
||||||
label: '需要提取的文本',
|
label: i18nT('workflow:text_to_extract'),
|
||||||
required: true,
|
required: true,
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
toolDescription: '需要检索的内容'
|
toolDescription: i18nT('workflow:content_to_retrieve')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: NodeInputKeyEnum.extractKeys,
|
key: NodeInputKeyEnum.extractKeys,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.custom],
|
renderTypeList: [FlowNodeInputTypeEnum.custom],
|
||||||
label: '',
|
label: '',
|
||||||
valueType: WorkflowIOValueTypeEnum.any,
|
valueType: WorkflowIOValueTypeEnum.any,
|
||||||
description: "由 '描述' 和 'key' 组成一个目标字段,可提取多个目标字段",
|
description: i18nT('workflow:target_fields_description'),
|
||||||
value: [] // {valueType: string; desc: string; key: string; required: boolean; enum: string[]}[]
|
value: [] // {valueType: string; desc: string; key: string; required: boolean; enum: string[]}[]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -63,18 +62,18 @@ export const ContextExtractModule: FlowNodeTemplateType = {
|
|||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.success,
|
id: NodeOutputKeyEnum.success,
|
||||||
key: NodeOutputKeyEnum.success,
|
key: NodeOutputKeyEnum.success,
|
||||||
label: '字段完全提取',
|
label: i18nT('workflow:full_field_extraction'),
|
||||||
required: true,
|
required: true,
|
||||||
description: '提取字段全部填充时返回 true (模型提取或使用默认值均属于成功)',
|
description: i18nT('workflow:full_field_extraction_description'),
|
||||||
valueType: WorkflowIOValueTypeEnum.boolean,
|
valueType: WorkflowIOValueTypeEnum.boolean,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.contextExtractFields,
|
id: NodeOutputKeyEnum.contextExtractFields,
|
||||||
key: NodeOutputKeyEnum.contextExtractFields,
|
key: NodeOutputKeyEnum.contextExtractFields,
|
||||||
label: '完整提取结果',
|
label: i18nT('workflow:complete_extraction_result'),
|
||||||
required: true,
|
required: true,
|
||||||
description: '一个 JSON 字符串,例如:{"name:":"YY","Time":"2023/7/2 18:00"}',
|
description: i18nT('workflow:complete_extraction_result_description'),
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
|
import { WorkflowIOValueTypeEnum } from '../../../constants';
|
||||||
|
|
||||||
export type ContextExtractAgentItemType = {
|
export type ContextExtractAgentItemType = {
|
||||||
valueType: 'string' | 'number' | 'boolean';
|
valueType:
|
||||||
|
| WorkflowIOValueTypeEnum.string
|
||||||
|
| WorkflowIOValueTypeEnum.number
|
||||||
|
| WorkflowIOValueTypeEnum.boolean;
|
||||||
desc: string;
|
desc: string;
|
||||||
key: string;
|
key: string;
|
||||||
required: boolean;
|
required: boolean;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
NodeInputKeyEnum
|
NodeInputKeyEnum
|
||||||
} from '../../constants';
|
} from '../../constants';
|
||||||
import { getHandleConfig } from '../utils';
|
import { getHandleConfig } from '../utils';
|
||||||
|
import { i18nT } from '../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export const CustomFeedbackNode: FlowNodeTemplateType = {
|
export const CustomFeedbackNode: FlowNodeTemplateType = {
|
||||||
id: FlowNodeTypeEnum.customFeedback,
|
id: FlowNodeTypeEnum.customFeedback,
|
||||||
@@ -14,8 +15,8 @@ export const CustomFeedbackNode: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(true, true, true, true),
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
avatar: 'core/workflow/template/customFeedback',
|
avatar: 'core/workflow/template/customFeedback',
|
||||||
name: '自定义反馈',
|
name: i18nT('workflow:custom_feedback'),
|
||||||
intro: '该模块被触发时,会给当前的对话记录增加一条反馈。可用于自动记录对话效果等。',
|
intro: i18nT('workflow:intro_custom_feedback'),
|
||||||
version: '486',
|
version: '486',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
@@ -23,7 +24,7 @@ export const CustomFeedbackNode: FlowNodeTemplateType = {
|
|||||||
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
|
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
required: true,
|
required: true,
|
||||||
label: '反馈的文本'
|
label: i18nT('workflow:feedback_text')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
outputs: []
|
outputs: []
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
import { getNanoid } from '../../../../common/string/tools';
|
import { getNanoid } from '../../../../common/string/tools';
|
||||||
import { getHandleConfig } from '../utils';
|
import { getHandleConfig } from '../utils';
|
||||||
import { FlowNodeInputItemType } from '../../type/io.d';
|
import { FlowNodeInputItemType } from '../../type/io.d';
|
||||||
|
import { i18nT } from '../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export const getOneQuoteInputTemplate = ({
|
export const getOneQuoteInputTemplate = ({
|
||||||
key = getNanoid(),
|
key = getNanoid(),
|
||||||
@@ -24,8 +25,8 @@ export const getOneQuoteInputTemplate = ({
|
|||||||
}): FlowNodeInputItemType => ({
|
}): FlowNodeInputItemType => ({
|
||||||
key,
|
key,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.reference],
|
renderTypeList: [FlowNodeInputTypeEnum.reference],
|
||||||
label: `引用${index}`,
|
label: `${i18nT('workflow:quote_num')},{ num: ${index} }`,
|
||||||
debugLabel: '知识库引用',
|
debugLabel: i18nT('workflow:knowledge_base_reference'),
|
||||||
canEdit: true,
|
canEdit: true,
|
||||||
valueType: WorkflowIOValueTypeEnum.datasetQuote
|
valueType: WorkflowIOValueTypeEnum.datasetQuote
|
||||||
});
|
});
|
||||||
@@ -37,15 +38,17 @@ export const DatasetConcatModule: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(true, true, true, true),
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
avatar: 'core/workflow/template/datasetConcat',
|
avatar: 'core/workflow/template/datasetConcat',
|
||||||
name: '知识库搜索引用合并',
|
name: i18nT('workflow:knowledge_base_search_merge'),
|
||||||
intro: '可以将多个知识库搜索结果进行合并输出。使用 RRF 的合并方式进行最终排序输出。',
|
intro: i18nT('workflow:intro_knowledge_base_search_merge'),
|
||||||
|
|
||||||
showStatus: false,
|
showStatus: false,
|
||||||
version: '486',
|
version: '486',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
key: NodeInputKeyEnum.datasetMaxTokens,
|
key: NodeInputKeyEnum.datasetMaxTokens,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.custom],
|
renderTypeList: [FlowNodeInputTypeEnum.custom],
|
||||||
label: '最大 Tokens',
|
label: i18nT('workflow:max_tokens'),
|
||||||
|
|
||||||
value: 3000,
|
value: 3000,
|
||||||
valueType: WorkflowIOValueTypeEnum.number
|
valueType: WorkflowIOValueTypeEnum.number
|
||||||
},
|
},
|
||||||
@@ -60,7 +63,7 @@ export const DatasetConcatModule: FlowNodeTemplateType = {
|
|||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.datasetQuoteQA,
|
id: NodeOutputKeyEnum.datasetQuoteQA,
|
||||||
key: NodeOutputKeyEnum.datasetQuoteQA,
|
key: NodeOutputKeyEnum.datasetQuoteQA,
|
||||||
label: 'core.module.Dataset quote.label',
|
label: i18nT('common:core.module.Dataset quote.label'),
|
||||||
type: FlowNodeOutputTypeEnum.static,
|
type: FlowNodeOutputTypeEnum.static,
|
||||||
valueType: WorkflowIOValueTypeEnum.datasetQuote,
|
valueType: WorkflowIOValueTypeEnum.datasetQuote,
|
||||||
valueDesc: datasetQuoteValueDesc
|
valueDesc: datasetQuoteValueDesc
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
|
|||||||
{
|
{
|
||||||
key: NodeInputKeyEnum.datasetSelectList,
|
key: NodeInputKeyEnum.datasetSelectList,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.selectDataset, FlowNodeInputTypeEnum.reference],
|
renderTypeList: [FlowNodeInputTypeEnum.selectDataset, FlowNodeInputTypeEnum.reference],
|
||||||
label: 'core.module.input.label.Select dataset',
|
label: i18nT('common:core.module.input.label.Select dataset'),
|
||||||
value: [],
|
value: [],
|
||||||
valueType: WorkflowIOValueTypeEnum.selectDataset,
|
valueType: WorkflowIOValueTypeEnum.selectDataset,
|
||||||
required: true
|
required: true
|
||||||
@@ -90,34 +90,24 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
...Input_Template_UserChatInput,
|
...Input_Template_UserChatInput,
|
||||||
toolDescription: '需要检索的内容'
|
toolDescription: i18nT('workflow:content_to_search')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: NodeInputKeyEnum.collectionFilterMatch,
|
key: NodeInputKeyEnum.collectionFilterMatch,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.JSONEditor, FlowNodeInputTypeEnum.reference],
|
renderTypeList: [FlowNodeInputTypeEnum.JSONEditor, FlowNodeInputTypeEnum.reference],
|
||||||
label: '集合元数据过滤',
|
label: i18nT('workflow:collection_metadata_filter'),
|
||||||
|
|
||||||
valueType: WorkflowIOValueTypeEnum.object,
|
valueType: WorkflowIOValueTypeEnum.object,
|
||||||
isPro: true,
|
isPro: true,
|
||||||
description: `目前支持标签和创建时间过滤,需按照以下格式填写:
|
description: i18nT('workflow:filter_description')
|
||||||
{
|
|
||||||
"tags": {
|
|
||||||
"$and": ["标签 1","标签 2"],
|
|
||||||
"$or": ["有 $and 标签时,and 生效,or 不生效"]
|
|
||||||
},
|
|
||||||
"createTime": {
|
|
||||||
"$gte": "YYYY-MM-DD HH:mm 格式即可,集合的创建时间大于该时间",
|
|
||||||
"$lte": "YYYY-MM-DD HH:mm 格式即可,集合的创建时间小于该时间,可和 $gte 共同使用"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
outputs: [
|
outputs: [
|
||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.datasetQuoteQA,
|
id: NodeOutputKeyEnum.datasetQuoteQA,
|
||||||
key: NodeOutputKeyEnum.datasetQuoteQA,
|
key: NodeOutputKeyEnum.datasetQuoteQA,
|
||||||
label: 'core.module.Dataset quote.label',
|
label: i18nT('common:core.module.Dataset quote.label'),
|
||||||
description: '特殊数组格式,搜索结果为空时,返回空数组。',
|
description: i18nT('workflow:special_array_format'),
|
||||||
type: FlowNodeOutputTypeEnum.static,
|
type: FlowNodeOutputTypeEnum.static,
|
||||||
valueType: WorkflowIOValueTypeEnum.datasetQuote,
|
valueType: WorkflowIOValueTypeEnum.datasetQuote,
|
||||||
valueDesc: datasetQuoteValueDesc
|
valueDesc: datasetQuoteValueDesc
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
import { Input_Template_DynamicInput } from '../input';
|
import { Input_Template_DynamicInput } from '../input';
|
||||||
import { Output_Template_AddOutput } from '../output';
|
import { Output_Template_AddOutput } from '../output';
|
||||||
import { getHandleConfig } from '../utils';
|
import { getHandleConfig } from '../utils';
|
||||||
|
import { i18nT } from '../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export const HttpNode468: FlowNodeTemplateType = {
|
export const HttpNode468: FlowNodeTemplateType = {
|
||||||
id: FlowNodeTypeEnum.httpRequest468,
|
id: FlowNodeTypeEnum.httpRequest468,
|
||||||
@@ -21,15 +22,15 @@ export const HttpNode468: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(true, true, true, true),
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
avatar: 'core/workflow/template/httpRequest',
|
avatar: 'core/workflow/template/httpRequest',
|
||||||
name: 'HTTP 请求',
|
name: i18nT('workflow:http_request'),
|
||||||
intro: '可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)',
|
intro: i18nT('workflow:intro_http_request'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
isTool: true,
|
isTool: true,
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
...Input_Template_DynamicInput,
|
...Input_Template_DynamicInput,
|
||||||
description: 'core.module.input.description.HTTP Dynamic Input',
|
description: i18nT('common:core.module.input.description.HTTP Dynamic Input'),
|
||||||
customInputConfig: {
|
customInputConfig: {
|
||||||
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
||||||
showDescription: false,
|
showDescription: false,
|
||||||
@@ -59,7 +60,7 @@ export const HttpNode468: FlowNodeTemplateType = {
|
|||||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
label: '',
|
label: '',
|
||||||
description: 'core.module.input.description.Http Request Url',
|
description: i18nT('common:core.module.input.description.Http Request Url'),
|
||||||
placeholder: 'https://api.ai.com/getInventory',
|
placeholder: 'https://api.ai.com/getInventory',
|
||||||
required: false
|
required: false
|
||||||
},
|
},
|
||||||
@@ -69,8 +70,8 @@ export const HttpNode468: FlowNodeTemplateType = {
|
|||||||
valueType: WorkflowIOValueTypeEnum.any,
|
valueType: WorkflowIOValueTypeEnum.any,
|
||||||
value: [],
|
value: [],
|
||||||
label: '',
|
label: '',
|
||||||
description: 'core.module.input.description.Http Request Header',
|
description: i18nT('common:core.module.input.description.Http Request Header'),
|
||||||
placeholder: 'core.module.input.description.Http Request Header',
|
placeholder: i18nT('common:core.module.input.description.Http Request Header'),
|
||||||
required: false
|
required: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -97,17 +98,17 @@ export const HttpNode468: FlowNodeTemplateType = {
|
|||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.error,
|
id: NodeOutputKeyEnum.error,
|
||||||
key: NodeOutputKeyEnum.error,
|
key: NodeOutputKeyEnum.error,
|
||||||
label: '请求错误',
|
label: i18nT('workflow:request_error'),
|
||||||
description: 'HTTP请求错误信息,成功时返回空',
|
description: i18nT('workflow:http_request_error_info'),
|
||||||
valueType: WorkflowIOValueTypeEnum.object,
|
valueType: WorkflowIOValueTypeEnum.object,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.httpRawResponse,
|
id: NodeOutputKeyEnum.httpRawResponse,
|
||||||
key: NodeOutputKeyEnum.httpRawResponse,
|
key: NodeOutputKeyEnum.httpRawResponse,
|
||||||
label: '原始响应',
|
|
||||||
required: true,
|
required: true,
|
||||||
description: 'HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。',
|
label: i18nT('workflow:raw_response'),
|
||||||
|
description: i18nT('workflow:http_raw_response_description'),
|
||||||
valueType: WorkflowIOValueTypeEnum.any,
|
valueType: WorkflowIOValueTypeEnum.any,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { i18nT } from '../../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export enum VariableConditionEnum {
|
export enum VariableConditionEnum {
|
||||||
equalTo = 'equalTo',
|
equalTo = 'equalTo',
|
||||||
notEqual = 'notEqual',
|
notEqual = 'notEqual',
|
||||||
@@ -29,64 +31,85 @@ export enum IfElseResultEnum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const stringConditionList = [
|
export const stringConditionList = [
|
||||||
{ label: '为空', value: VariableConditionEnum.isEmpty },
|
{ label: i18nT('workflow:is_empty'), value: VariableConditionEnum.isEmpty },
|
||||||
{ label: '不为空', value: VariableConditionEnum.isNotEmpty },
|
{ label: i18nT('workflow:is_not_empty'), value: VariableConditionEnum.isNotEmpty },
|
||||||
{ label: '等于', value: VariableConditionEnum.equalTo },
|
{ label: i18nT('workflow:is_equal_to'), value: VariableConditionEnum.equalTo },
|
||||||
{ label: '不等于', value: VariableConditionEnum.notEqual },
|
{ label: i18nT('workflow:is_not_equal'), value: VariableConditionEnum.notEqual },
|
||||||
{ label: '正则', value: VariableConditionEnum.reg },
|
{ label: i18nT('workflow:regex'), value: VariableConditionEnum.reg },
|
||||||
{ label: '包含', value: VariableConditionEnum.include },
|
{ label: i18nT('workflow:contains'), value: VariableConditionEnum.include },
|
||||||
{ label: '不包含', value: VariableConditionEnum.notInclude },
|
{ label: i18nT('workflow:not_contains'), value: VariableConditionEnum.notInclude },
|
||||||
{ label: '开始为', value: VariableConditionEnum.startWith },
|
{ label: i18nT('workflow:start_with'), value: VariableConditionEnum.startWith },
|
||||||
{ label: '结束为', value: VariableConditionEnum.endWith }
|
{ label: i18nT('workflow:end_with'), value: VariableConditionEnum.endWith }
|
||||||
];
|
];
|
||||||
export const numberConditionList = [
|
export const numberConditionList = [
|
||||||
{ label: '为空', value: VariableConditionEnum.isEmpty },
|
{ label: i18nT('workflow:is_empty'), value: VariableConditionEnum.isEmpty },
|
||||||
{ label: '不为空', value: VariableConditionEnum.isNotEmpty },
|
{ label: i18nT('workflow:is_not_empty'), value: VariableConditionEnum.isNotEmpty },
|
||||||
{ label: '等于', value: VariableConditionEnum.equalTo },
|
{ label: i18nT('workflow:is_equal_to'), value: VariableConditionEnum.equalTo },
|
||||||
{ label: '不等于', value: VariableConditionEnum.notEqual },
|
{ label: i18nT('workflow:is_not_equal'), value: VariableConditionEnum.notEqual },
|
||||||
{ label: '大于', value: VariableConditionEnum.greaterThan },
|
{ label: i18nT('workflow:greater_than'), value: VariableConditionEnum.greaterThan },
|
||||||
{ label: '大于等于', value: VariableConditionEnum.greaterThanOrEqualTo },
|
{
|
||||||
{ label: '小于', value: VariableConditionEnum.lessThan },
|
label: i18nT('workflow:greater_than_or_equal_to'),
|
||||||
{ label: '小于等于', value: VariableConditionEnum.lessThanOrEqualTo }
|
value: VariableConditionEnum.greaterThanOrEqualTo
|
||||||
|
},
|
||||||
|
{ label: i18nT('workflow:less_than'), value: VariableConditionEnum.lessThan },
|
||||||
|
{ label: i18nT('workflow:less_than_or_equal_to'), value: VariableConditionEnum.lessThanOrEqualTo }
|
||||||
];
|
];
|
||||||
export const booleanConditionList = [
|
export const booleanConditionList = [
|
||||||
{ label: '为空', value: VariableConditionEnum.isEmpty },
|
{ label: i18nT('workflow:is_empty'), value: VariableConditionEnum.isEmpty },
|
||||||
{ label: '不为空', value: VariableConditionEnum.isNotEmpty },
|
{ label: i18nT('workflow:is_not_empty'), value: VariableConditionEnum.isNotEmpty },
|
||||||
{ label: '等于', value: VariableConditionEnum.equalTo }
|
{ label: i18nT('workflow:is_equal_to'), value: VariableConditionEnum.equalTo }
|
||||||
];
|
];
|
||||||
export const arrayConditionList = [
|
export const arrayConditionList = [
|
||||||
{ label: '为空', value: VariableConditionEnum.isEmpty },
|
{ label: i18nT('workflow:is_empty'), value: VariableConditionEnum.isEmpty },
|
||||||
{ label: '不为空', value: VariableConditionEnum.isNotEmpty },
|
{ label: i18nT('workflow:is_not_empty'), value: VariableConditionEnum.isNotEmpty },
|
||||||
{ label: '包含', value: VariableConditionEnum.include },
|
{ label: i18nT('workflow:contains'), value: VariableConditionEnum.include },
|
||||||
{ label: '不包含', value: VariableConditionEnum.notInclude },
|
{ label: i18nT('workflow:not_contains'), value: VariableConditionEnum.notInclude },
|
||||||
{ label: '长度等于', value: VariableConditionEnum.lengthEqualTo },
|
{ label: i18nT('workflow:length_equal_to'), value: VariableConditionEnum.lengthEqualTo },
|
||||||
{ label: '长度不等于', value: VariableConditionEnum.lengthNotEqualTo },
|
{ label: i18nT('workflow:length_not_equal_to'), value: VariableConditionEnum.lengthNotEqualTo },
|
||||||
{ label: '长度大于', value: VariableConditionEnum.lengthGreaterThan },
|
{ label: i18nT('workflow:length_greater_than'), value: VariableConditionEnum.lengthGreaterThan },
|
||||||
{ label: '长度大于等于', value: VariableConditionEnum.lengthGreaterThanOrEqualTo },
|
{
|
||||||
{ label: '长度小于', value: VariableConditionEnum.lengthLessThan },
|
label: i18nT('workflow:length_greater_than_or_equal_to'),
|
||||||
{ label: '长度小于等于', value: VariableConditionEnum.lengthLessThanOrEqualTo }
|
value: VariableConditionEnum.lengthGreaterThanOrEqualTo
|
||||||
|
},
|
||||||
|
{ label: i18nT('workflow:length_less_than'), value: VariableConditionEnum.lengthLessThan },
|
||||||
|
{
|
||||||
|
label: i18nT('workflow:length_less_than_or_equal_to'),
|
||||||
|
value: VariableConditionEnum.lengthLessThanOrEqualTo
|
||||||
|
}
|
||||||
];
|
];
|
||||||
export const objectConditionList = [
|
export const objectConditionList = [
|
||||||
{ label: '为空', value: VariableConditionEnum.isEmpty },
|
{ label: i18nT('workflow:is_empty'), value: VariableConditionEnum.isEmpty },
|
||||||
{ label: '不为空', value: VariableConditionEnum.isNotEmpty }
|
{ label: i18nT('workflow:is_not_empty'), value: VariableConditionEnum.isNotEmpty }
|
||||||
];
|
];
|
||||||
export const allConditionList = [
|
export const allConditionList = [
|
||||||
{ label: '为空', value: VariableConditionEnum.isEmpty },
|
{ label: i18nT('workflow:is_empty'), value: VariableConditionEnum.isEmpty },
|
||||||
{ label: '不为空', value: VariableConditionEnum.isNotEmpty },
|
{ label: i18nT('workflow:is_not_empty'), value: VariableConditionEnum.isNotEmpty },
|
||||||
{ label: '等于', value: VariableConditionEnum.equalTo },
|
{ label: i18nT('workflow:is_equal_to'), value: VariableConditionEnum.equalTo },
|
||||||
{ label: '不等于', value: VariableConditionEnum.notEqual },
|
{ label: i18nT('workflow:is_not_equal'), value: VariableConditionEnum.notEqual },
|
||||||
{ label: '包含', value: VariableConditionEnum.include },
|
{ label: i18nT('workflow:contains'), value: VariableConditionEnum.include },
|
||||||
{ label: '不包含', value: VariableConditionEnum.notInclude },
|
{ label: i18nT('workflow:not_contains'), value: VariableConditionEnum.notInclude },
|
||||||
{ label: '开始为', value: VariableConditionEnum.startWith },
|
{ label: i18nT('workflow:start_with'), value: VariableConditionEnum.startWith },
|
||||||
{ label: '结束为', value: VariableConditionEnum.endWith },
|
{ label: i18nT('workflow:end_with'), value: VariableConditionEnum.endWith },
|
||||||
{ label: '大于', value: VariableConditionEnum.greaterThan },
|
{ label: i18nT('workflow:greater_than'), value: VariableConditionEnum.greaterThan },
|
||||||
{ label: '大于等于', value: VariableConditionEnum.greaterThanOrEqualTo },
|
{
|
||||||
{ label: '小于', value: VariableConditionEnum.lessThan },
|
label: i18nT('workflow:greater_than_or_equal_to'),
|
||||||
{ label: '小于等于', value: VariableConditionEnum.lessThanOrEqualTo },
|
value: VariableConditionEnum.greaterThanOrEqualTo
|
||||||
{ label: '长度等于', value: VariableConditionEnum.lengthEqualTo },
|
},
|
||||||
{ label: '长度不等于', value: VariableConditionEnum.lengthNotEqualTo },
|
{ label: i18nT('workflow:less_than'), value: VariableConditionEnum.lessThan },
|
||||||
{ label: '长度大于', value: VariableConditionEnum.lengthGreaterThan },
|
{
|
||||||
{ label: '长度大于等于', value: VariableConditionEnum.lengthGreaterThanOrEqualTo },
|
label: i18nT('workflow:less_than_or_equal_to'),
|
||||||
{ label: '长度小于', value: VariableConditionEnum.lengthLessThan },
|
value: VariableConditionEnum.lessThanOrEqualTo
|
||||||
{ label: '长度小于等于', value: VariableConditionEnum.lengthLessThanOrEqualTo }
|
},
|
||||||
|
{ label: i18nT('workflow:length_equal_to'), value: VariableConditionEnum.lengthEqualTo },
|
||||||
|
{ label: i18nT('workflow:length_not_equal_to'), value: VariableConditionEnum.lengthNotEqualTo },
|
||||||
|
{ label: i18nT('workflow:length_greater_than'), value: VariableConditionEnum.lengthGreaterThan },
|
||||||
|
{
|
||||||
|
label: i18nT('workflow:length_greater_than_or_equal_to'),
|
||||||
|
value: VariableConditionEnum.lengthGreaterThanOrEqualTo
|
||||||
|
},
|
||||||
|
{ label: i18nT('workflow:length_less_than'), value: VariableConditionEnum.lengthLessThan },
|
||||||
|
{
|
||||||
|
label: i18nT('workflow:length_less_than_or_equal_to'),
|
||||||
|
value: VariableConditionEnum.lengthLessThanOrEqualTo
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { i18nT } from '../../../../../../web/i18n/utils';
|
||||||
import {
|
import {
|
||||||
FlowNodeTemplateTypeEnum,
|
FlowNodeTemplateTypeEnum,
|
||||||
NodeInputKeyEnum,
|
NodeInputKeyEnum,
|
||||||
@@ -19,8 +20,8 @@ export const IfElseNode: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(false, false, false, false),
|
sourceHandle: getHandleConfig(false, false, false, false),
|
||||||
targetHandle: getHandleConfig(true, false, true, true),
|
targetHandle: getHandleConfig(true, false, true, true),
|
||||||
avatar: 'core/workflow/template/ifelse',
|
avatar: 'core/workflow/template/ifelse',
|
||||||
name: '判断器',
|
name: i18nT('workflow:condition_checker'),
|
||||||
intro: '根据一定的条件,执行不同的分支。',
|
intro: i18nT('workflow:execute_different_branches_based_on_conditions'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [
|
inputs: [
|
||||||
@@ -47,7 +48,7 @@ export const IfElseNode: FlowNodeTemplateType = {
|
|||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.ifElseResult,
|
id: NodeOutputKeyEnum.ifElseResult,
|
||||||
key: NodeOutputKeyEnum.ifElseResult,
|
key: NodeOutputKeyEnum.ifElseResult,
|
||||||
label: '判断结果',
|
label: i18nT('workflow:judgment_result'),
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
import { Input_Template_DynamicInput } from '../input';
|
import { Input_Template_DynamicInput } from '../input';
|
||||||
import { Output_Template_AddOutput } from '../output';
|
import { Output_Template_AddOutput } from '../output';
|
||||||
import { getHandleConfig } from '../utils';
|
import { getHandleConfig } from '../utils';
|
||||||
|
import { i18nT } from '../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export const nodeLafCustomInputConfig = {
|
export const nodeLafCustomInputConfig = {
|
||||||
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
||||||
@@ -27,15 +28,15 @@ export const LafModule: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(true, true, true, true),
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
avatar: 'core/workflow/template/lafDispatch',
|
avatar: 'core/workflow/template/lafDispatch',
|
||||||
name: 'Laf 函数调用(测试)',
|
name: i18nT('workflow:laf_function_call_test'),
|
||||||
intro: '可以调用Laf账号下的云函数。',
|
intro: i18nT('workflow:intro_laf_function_call'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
isTool: true,
|
isTool: true,
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
...Input_Template_DynamicInput,
|
...Input_Template_DynamicInput,
|
||||||
description: '接收前方节点的输出值作为变量,这些变量可以被 Laf 请求参数使用。',
|
description: i18nT('workflow:dynamic_input_description'),
|
||||||
customInputConfig: nodeLafCustomInputConfig
|
customInputConfig: nodeLafCustomInputConfig
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -52,8 +53,8 @@ export const LafModule: FlowNodeTemplateType = {
|
|||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.httpRawResponse,
|
id: NodeOutputKeyEnum.httpRawResponse,
|
||||||
key: NodeOutputKeyEnum.httpRawResponse,
|
key: NodeOutputKeyEnum.httpRawResponse,
|
||||||
label: '原始响应',
|
label: i18nT('workflow:raw_response'),
|
||||||
description: 'HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。',
|
description: i18nT('workflow:http_raw_response_description'),
|
||||||
valueType: WorkflowIOValueTypeEnum.any,
|
valueType: WorkflowIOValueTypeEnum.any,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||||
|
import { FlowNodeTemplateType } from '../../type/node.d';
|
||||||
|
import { FlowNodeTemplateTypeEnum } from '../../constants';
|
||||||
|
import { getHandleConfig } from '../utils';
|
||||||
|
import { i18nT } from '../../../../../web/i18n/utils';
|
||||||
|
|
||||||
|
export const PluginConfigNode: FlowNodeTemplateType = {
|
||||||
|
id: FlowNodeTypeEnum.pluginConfig,
|
||||||
|
templateType: FlowNodeTemplateTypeEnum.systemInput,
|
||||||
|
flowNodeType: FlowNodeTypeEnum.pluginConfig,
|
||||||
|
sourceHandle: getHandleConfig(false, false, false, false),
|
||||||
|
targetHandle: getHandleConfig(false, false, false, false),
|
||||||
|
avatar: 'core/workflow/template/systemConfig',
|
||||||
|
name: i18nT('workflow:template.system_config'),
|
||||||
|
intro: '',
|
||||||
|
unique: true,
|
||||||
|
forbidDelete: true,
|
||||||
|
version: '4811',
|
||||||
|
inputs: [],
|
||||||
|
outputs: []
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { i18nT } from '../../../../../web/i18n/utils';
|
||||||
import { FlowNodeTemplateTypeEnum } from '../../constants';
|
import { FlowNodeTemplateTypeEnum } from '../../constants';
|
||||||
import { FlowNodeTypeEnum } from '../../node/constant';
|
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||||
import { FlowNodeTemplateType } from '../../type/node';
|
import { FlowNodeTemplateType } from '../../type/node';
|
||||||
@@ -12,8 +13,8 @@ export const PluginInputModule: FlowNodeTemplateType = {
|
|||||||
unique: true,
|
unique: true,
|
||||||
forbidDelete: true,
|
forbidDelete: true,
|
||||||
avatar: 'core/workflow/template/workflowStart',
|
avatar: 'core/workflow/template/workflowStart',
|
||||||
name: '插件输入',
|
name: i18nT('workflow:plugin_input'),
|
||||||
intro: '可以配置插件需要哪些输入,利用这些输入来运行插件',
|
intro: i18nT('workflow:intro_plugin_input'),
|
||||||
showStatus: false,
|
showStatus: false,
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [],
|
inputs: [],
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { i18nT } from '../../../../../web/i18n/utils';
|
||||||
import { FlowNodeTemplateTypeEnum } from '../../constants';
|
import { FlowNodeTemplateTypeEnum } from '../../constants';
|
||||||
import { FlowNodeTypeEnum } from '../../node/constant';
|
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||||
import { FlowNodeTemplateType } from '../../type/node';
|
import { FlowNodeTemplateType } from '../../type/node';
|
||||||
@@ -12,8 +13,8 @@ export const PluginOutputModule: FlowNodeTemplateType = {
|
|||||||
unique: true,
|
unique: true,
|
||||||
forbidDelete: true,
|
forbidDelete: true,
|
||||||
avatar: 'core/workflow/template/pluginOutput',
|
avatar: 'core/workflow/template/pluginOutput',
|
||||||
name: '自定义插件输出',
|
name: i18nT('workflow:custom_plugin_output'),
|
||||||
intro: '自定义配置外部输出,使用插件时,仅暴露自定义配置的输出',
|
intro: i18nT('workflow:intro_custom_plugin_output'),
|
||||||
showStatus: false,
|
showStatus: false,
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [],
|
inputs: [],
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
} from '../input';
|
} from '../input';
|
||||||
import { LLMModelTypeEnum } from '../../../ai/constants';
|
import { LLMModelTypeEnum } from '../../../ai/constants';
|
||||||
import { getHandleConfig } from '../utils';
|
import { getHandleConfig } from '../utils';
|
||||||
|
import { i18nT } from '../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export const AiQueryExtension: FlowNodeTemplateType = {
|
export const AiQueryExtension: FlowNodeTemplateType = {
|
||||||
id: FlowNodeTypeEnum.queryExtension,
|
id: FlowNodeTypeEnum.queryExtension,
|
||||||
@@ -25,9 +26,8 @@ export const AiQueryExtension: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(true, true, true, true),
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
avatar: 'core/workflow/template/queryExtension',
|
avatar: 'core/workflow/template/queryExtension',
|
||||||
name: '问题优化',
|
name: i18nT('workflow:question_optimization'),
|
||||||
intro:
|
intro: i18nT('workflow:intro_question_optimization'),
|
||||||
'使用问题优化功能,可以提高知识库连续对话时搜索的精度。使用该功能后,会先利用 AI 根据上下文构建一个或多个新的检索词,这些检索词更利于进行知识库搜索。该模块已内置在知识库搜索模块中,如果您仅进行一次知识库搜索,可直接使用知识库内置的补全功能。',
|
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [
|
inputs: [
|
||||||
@@ -38,11 +38,11 @@ export const AiQueryExtension: FlowNodeTemplateType = {
|
|||||||
{
|
{
|
||||||
key: NodeInputKeyEnum.aiSystemPrompt,
|
key: NodeInputKeyEnum.aiSystemPrompt,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
|
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
|
||||||
label: 'core.app.edit.Query extension background prompt',
|
label: i18nT('common:core.app.edit.Query extension background prompt'),
|
||||||
max: 300,
|
max: 300,
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
description: 'core.app.edit.Query extension background tip',
|
description: i18nT('common:core.app.edit.Query extension background tip'),
|
||||||
placeholder: 'core.module.QueryExtension.placeholder'
|
placeholder: i18nT('common:core.module.QueryExtension.placeholder')
|
||||||
},
|
},
|
||||||
Input_Template_History,
|
Input_Template_History,
|
||||||
Input_Template_UserChatInput
|
Input_Template_UserChatInput
|
||||||
@@ -51,8 +51,8 @@ export const AiQueryExtension: FlowNodeTemplateType = {
|
|||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.text,
|
id: NodeOutputKeyEnum.text,
|
||||||
key: NodeOutputKeyEnum.text,
|
key: NodeOutputKeyEnum.text,
|
||||||
label: 'core.module.output.label.query extension result',
|
label: i18nT('common:core.module.output.label.query extension result'),
|
||||||
description: 'core.module.output.description.query extension result',
|
description: i18nT('common:core.module.output.description.query extension result'),
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { FlowNodeTemplateTypeEnum } from '../../constants';
|
||||||
|
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||||
|
import { FlowNodeTemplateType } from '../../type/node';
|
||||||
|
import { getHandleConfig } from '../utils';
|
||||||
|
|
||||||
|
export const RunAppPluginModule: FlowNodeTemplateType = {
|
||||||
|
id: FlowNodeTypeEnum.appModule,
|
||||||
|
templateType: FlowNodeTemplateTypeEnum.other,
|
||||||
|
flowNodeType: FlowNodeTypeEnum.appModule,
|
||||||
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
|
intro: '',
|
||||||
|
name: '',
|
||||||
|
showStatus: false,
|
||||||
|
isTool: false,
|
||||||
|
version: '481',
|
||||||
|
inputs: [], // [{key:'pluginId'},...]
|
||||||
|
outputs: []
|
||||||
|
};
|
||||||
@@ -14,6 +14,7 @@ import { getHandleConfig } from '../../utils';
|
|||||||
import { Input_Template_DynamicInput } from '../../input';
|
import { Input_Template_DynamicInput } from '../../input';
|
||||||
import { Output_Template_AddOutput } from '../../output';
|
import { Output_Template_AddOutput } from '../../output';
|
||||||
import { JS_TEMPLATE } from './constants';
|
import { JS_TEMPLATE } from './constants';
|
||||||
|
import { i18nT } from '../../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export const CodeNode: FlowNodeTemplateType = {
|
export const CodeNode: FlowNodeTemplateType = {
|
||||||
id: FlowNodeTypeEnum.code,
|
id: FlowNodeTypeEnum.code,
|
||||||
@@ -22,14 +23,14 @@ export const CodeNode: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(true, true, true, true),
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
avatar: 'core/workflow/template/codeRun',
|
avatar: 'core/workflow/template/codeRun',
|
||||||
name: '代码运行',
|
name: i18nT('workflow:code_execution'),
|
||||||
intro: '执行一段简单的脚本代码,通常用于进行复杂的数据处理。',
|
intro: i18nT('workflow:execute_a_simple_script_code_usually_for_complex_data_processing'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
version: '482',
|
version: '482',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
...Input_Template_DynamicInput,
|
...Input_Template_DynamicInput,
|
||||||
description: '这些变量会作为代码的运行的输入参数',
|
description: i18nT('workflow:these_variables_will_be_input_parameters_for_code_execution'),
|
||||||
customInputConfig: {
|
customInputConfig: {
|
||||||
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
||||||
showDescription: false,
|
showDescription: false,
|
||||||
@@ -78,20 +79,20 @@ export const CodeNode: FlowNodeTemplateType = {
|
|||||||
outputs: [
|
outputs: [
|
||||||
{
|
{
|
||||||
...Output_Template_AddOutput,
|
...Output_Template_AddOutput,
|
||||||
description: '将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key'
|
description: i18nT('workflow:pass_returned_object_as_output_to_next_nodes')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.rawResponse,
|
id: NodeOutputKeyEnum.rawResponse,
|
||||||
key: NodeOutputKeyEnum.rawResponse,
|
key: NodeOutputKeyEnum.rawResponse,
|
||||||
label: '完整响应数据',
|
label: i18nT('workflow:full_response_data'),
|
||||||
valueType: WorkflowIOValueTypeEnum.object,
|
valueType: WorkflowIOValueTypeEnum.object,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.error,
|
id: NodeOutputKeyEnum.error,
|
||||||
key: NodeOutputKeyEnum.error,
|
key: NodeOutputKeyEnum.error,
|
||||||
label: '运行错误',
|
label: i18nT('workflow:execution_error'),
|
||||||
description: '代码运行错误信息,成功时返回空',
|
description: i18nT('workflow:error_info_returns_empty_on_success'),
|
||||||
valueType: WorkflowIOValueTypeEnum.object,
|
valueType: WorkflowIOValueTypeEnum.object,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { FlowNodeTypeEnum } from '../../node/constant';
|
|||||||
import { FlowNodeTemplateType } from '../../type/node';
|
import { FlowNodeTemplateType } from '../../type/node';
|
||||||
import { FlowNodeTemplateTypeEnum } from '../../constants';
|
import { FlowNodeTemplateTypeEnum } from '../../constants';
|
||||||
import { getHandleConfig } from '../utils';
|
import { getHandleConfig } from '../utils';
|
||||||
|
import { i18nT } from '../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export const StopToolNode: FlowNodeTemplateType = {
|
export const StopToolNode: FlowNodeTemplateType = {
|
||||||
id: FlowNodeTypeEnum.stopTool,
|
id: FlowNodeTypeEnum.stopTool,
|
||||||
@@ -10,9 +11,8 @@ export const StopToolNode: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(false, false, false, false),
|
sourceHandle: getHandleConfig(false, false, false, false),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
avatar: 'core/workflow/template/stopTool',
|
avatar: 'core/workflow/template/stopTool',
|
||||||
name: '工具调用终止',
|
name: i18nT('workflow:tool_call_termination'),
|
||||||
intro:
|
intro: i18nT('workflow:intro_tool_call_termination'),
|
||||||
'该模块需配置工具调用使用。当该模块被执行时,本次工具调用将会强制结束,并且不再调用AI针对工具调用结果回答问题。',
|
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [],
|
inputs: [],
|
||||||
outputs: []
|
outputs: []
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
} from '../../constants';
|
} from '../../constants';
|
||||||
import { getHandleConfig } from '../utils';
|
import { getHandleConfig } from '../utils';
|
||||||
import { Input_Template_DynamicInput } from '../input';
|
import { Input_Template_DynamicInput } from '../input';
|
||||||
|
import { i18nT } from '../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export const TextEditorNode: FlowNodeTemplateType = {
|
export const TextEditorNode: FlowNodeTemplateType = {
|
||||||
id: FlowNodeTypeEnum.textEditor,
|
id: FlowNodeTypeEnum.textEditor,
|
||||||
@@ -20,13 +21,13 @@ export const TextEditorNode: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(true, true, true, true),
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
avatar: 'core/workflow/template/textConcat',
|
avatar: 'core/workflow/template/textConcat',
|
||||||
name: '文本拼接',
|
name: i18nT('workflow:text_concatenation'),
|
||||||
intro: '可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。',
|
intro: i18nT('workflow:intro_text_concatenation'),
|
||||||
version: '486',
|
version: '486',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
...Input_Template_DynamicInput,
|
...Input_Template_DynamicInput,
|
||||||
description: '可以引用其他节点的输出,作为文本拼接的变量,输入 / 唤起变量列表',
|
description: i18nT('workflow:dynamic_input_description_concat'),
|
||||||
customInputConfig: {
|
customInputConfig: {
|
||||||
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
||||||
showDescription: false,
|
showDescription: false,
|
||||||
@@ -38,15 +39,15 @@ export const TextEditorNode: FlowNodeTemplateType = {
|
|||||||
renderTypeList: [FlowNodeInputTypeEnum.textarea],
|
renderTypeList: [FlowNodeInputTypeEnum.textarea],
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
required: true,
|
required: true,
|
||||||
label: '拼接文本',
|
label: i18nT('workflow:concatenation_text'),
|
||||||
placeholder: '可输入 / 唤起变量列表'
|
placeholder: i18nT('workflow:input_variable_list')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
outputs: [
|
outputs: [
|
||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.text,
|
id: NodeOutputKeyEnum.text,
|
||||||
key: NodeOutputKeyEnum.text,
|
key: NodeOutputKeyEnum.text,
|
||||||
label: '拼接结果',
|
label: i18nT('workflow:concatenation_result'),
|
||||||
type: FlowNodeOutputTypeEnum.static,
|
type: FlowNodeOutputTypeEnum.static,
|
||||||
valueType: WorkflowIOValueTypeEnum.string
|
valueType: WorkflowIOValueTypeEnum.string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export const ToolModule: FlowNodeTemplateType = {
|
|||||||
|
|
||||||
{
|
{
|
||||||
...Input_Template_System_Prompt,
|
...Input_Template_System_Prompt,
|
||||||
label: 'core.ai.Prompt',
|
label: i18nT('common:core.ai.Prompt'),
|
||||||
description: chatNodeSystemPromptTip,
|
description: chatNodeSystemPromptTip,
|
||||||
placeholder: chatNodeSystemPromptTip
|
placeholder: chatNodeSystemPromptTip
|
||||||
},
|
},
|
||||||
@@ -72,8 +72,8 @@ export const ToolModule: FlowNodeTemplateType = {
|
|||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.answerText,
|
id: NodeOutputKeyEnum.answerText,
|
||||||
key: NodeOutputKeyEnum.answerText,
|
key: NodeOutputKeyEnum.answerText,
|
||||||
label: 'core.module.output.label.Ai response content',
|
label: i18nT('common:core.module.output.label.Ai response content'),
|
||||||
description: 'core.module.output.description.Ai response content',
|
description: i18nT('common:core.module.output.description.Ai response content'),
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
type: FlowNodeOutputTypeEnum.static
|
type: FlowNodeOutputTypeEnum.static
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,5 +22,3 @@ type UserSelectInteractive = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type InteractiveNodeResponseItemType = InteractiveBasicType & UserSelectInteractive;
|
export type InteractiveNodeResponseItemType = InteractiveBasicType & UserSelectInteractive;
|
||||||
|
|
||||||
export type UserInteractiveType = UserSelectInteractive;
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
WorkflowIOValueTypeEnum
|
WorkflowIOValueTypeEnum
|
||||||
} from '../../../constants';
|
} from '../../../constants';
|
||||||
import { getHandleConfig } from '../../utils';
|
import { getHandleConfig } from '../../utils';
|
||||||
|
import { i18nT } from '../../../../../../web/i18n/utils';
|
||||||
|
|
||||||
export const VariableUpdateNode: FlowNodeTemplateType = {
|
export const VariableUpdateNode: FlowNodeTemplateType = {
|
||||||
id: FlowNodeTypeEnum.variableUpdate,
|
id: FlowNodeTypeEnum.variableUpdate,
|
||||||
@@ -14,8 +15,8 @@ export const VariableUpdateNode: FlowNodeTemplateType = {
|
|||||||
sourceHandle: getHandleConfig(true, true, true, true),
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
avatar: 'core/workflow/template/variableUpdate',
|
avatar: 'core/workflow/template/variableUpdate',
|
||||||
name: '变量更新',
|
name: i18nT('workflow:variable_update'),
|
||||||
intro: '可以更新指定节点的输出值或更新全局变量',
|
intro: i18nT('workflow:update_specified_node_output_or_global_variable'),
|
||||||
showStatus: false,
|
showStatus: false,
|
||||||
isTool: false,
|
isTool: false,
|
||||||
version: '481',
|
version: '481',
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export const WorkflowStart: FlowNodeTemplateType = {
|
|||||||
forbidDelete: true,
|
forbidDelete: true,
|
||||||
unique: true,
|
unique: true,
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [{ ...Input_Template_UserChatInput, toolDescription: '用户问题' }],
|
inputs: [{ ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') }],
|
||||||
outputs: [
|
outputs: [
|
||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.userChatInput,
|
id: NodeOutputKeyEnum.userChatInput,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export type WorkflowTemplateBasicType = {
|
|||||||
};
|
};
|
||||||
export type WorkflowTemplateType = {
|
export type WorkflowTemplateType = {
|
||||||
id: string;
|
id: string;
|
||||||
parentId?: string;
|
parentId?: ParentIdType;
|
||||||
isFolder?: boolean;
|
isFolder?: boolean;
|
||||||
|
|
||||||
name: string;
|
name: string;
|
||||||
@@ -62,6 +62,8 @@ export type TemplateMarketListItemType = {
|
|||||||
|
|
||||||
// system plugin
|
// system plugin
|
||||||
export type SystemPluginTemplateItemType = WorkflowTemplateType & {
|
export type SystemPluginTemplateItemType = WorkflowTemplateType & {
|
||||||
|
customWorkflow?: string;
|
||||||
|
|
||||||
templateType: FlowNodeTemplateTypeEnum;
|
templateType: FlowNodeTemplateTypeEnum;
|
||||||
isTool?: boolean;
|
isTool?: boolean;
|
||||||
|
|
||||||
@@ -77,8 +79,6 @@ export type SystemPluginTemplateItemType = WorkflowTemplateType & {
|
|||||||
description: string;
|
description: string;
|
||||||
value?: any;
|
value?: any;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
workflow: WorkflowTemplateBasicType;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type THelperLine = {
|
export type THelperLine = {
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum, FlowNodeTypeEnum } from './node/constant';
|
import {
|
||||||
|
chatHistoryValueDesc,
|
||||||
|
FlowNodeInputTypeEnum,
|
||||||
|
FlowNodeOutputTypeEnum,
|
||||||
|
FlowNodeTypeEnum
|
||||||
|
} from './node/constant';
|
||||||
import {
|
import {
|
||||||
WorkflowIOValueTypeEnum,
|
WorkflowIOValueTypeEnum,
|
||||||
NodeInputKeyEnum,
|
NodeInputKeyEnum,
|
||||||
VariableInputEnum,
|
VariableInputEnum,
|
||||||
variableMap,
|
variableMap,
|
||||||
VARIABLE_NODE_ID
|
VARIABLE_NODE_ID,
|
||||||
|
NodeOutputKeyEnum
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import { FlowNodeInputItemType, FlowNodeOutputItemType, ReferenceValueProps } from './type/io.d';
|
import { FlowNodeInputItemType, FlowNodeOutputItemType, ReferenceValueProps } from './type/io.d';
|
||||||
import { StoreNodeItemType } from './type/node';
|
import { StoreNodeItemType } from './type/node';
|
||||||
@@ -25,6 +31,8 @@ import {
|
|||||||
import { IfElseResultEnum } from './template/system/ifElse/constant';
|
import { IfElseResultEnum } from './template/system/ifElse/constant';
|
||||||
import { RuntimeNodeItemType } from './runtime/type';
|
import { RuntimeNodeItemType } from './runtime/type';
|
||||||
import { getReferenceVariableValue } from './runtime/utils';
|
import { getReferenceVariableValue } from './runtime/utils';
|
||||||
|
import { Input_Template_History, Input_Template_UserChatInput } from './template/input';
|
||||||
|
import { i18nT } from '../../../web/i18n/utils';
|
||||||
|
|
||||||
export const getHandleId = (nodeId: string, type: 'source' | 'target', key: string) => {
|
export const getHandleId = (nodeId: string, type: 'source' | 'target', key: string) => {
|
||||||
return `${nodeId}-${type}-${key}`;
|
return `${nodeId}-${type}-${key}`;
|
||||||
@@ -72,6 +80,10 @@ export const splitGuideModule = (guideModules?: StoreNodeItemType) => {
|
|||||||
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.chatInputGuide)?.value ||
|
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.chatInputGuide)?.value ||
|
||||||
defaultChatInputGuideConfig;
|
defaultChatInputGuideConfig;
|
||||||
|
|
||||||
|
// plugin
|
||||||
|
const instruction: string =
|
||||||
|
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.instruction)?.value || '';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
welcomeText,
|
welcomeText,
|
||||||
variables,
|
variables,
|
||||||
@@ -79,7 +91,8 @@ export const splitGuideModule = (guideModules?: StoreNodeItemType) => {
|
|||||||
ttsConfig,
|
ttsConfig,
|
||||||
whisperConfig,
|
whisperConfig,
|
||||||
scheduledTriggerConfig,
|
scheduledTriggerConfig,
|
||||||
chatInputGuide
|
chatInputGuide,
|
||||||
|
instruction
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -104,7 +117,8 @@ export const getAppChatConfig = ({
|
|||||||
ttsConfig,
|
ttsConfig,
|
||||||
whisperConfig,
|
whisperConfig,
|
||||||
scheduledTriggerConfig,
|
scheduledTriggerConfig,
|
||||||
chatInputGuide
|
chatInputGuide,
|
||||||
|
instruction
|
||||||
} = splitGuideModule(systemConfigNode);
|
} = splitGuideModule(systemConfigNode);
|
||||||
|
|
||||||
const config: AppChatConfigType = {
|
const config: AppChatConfigType = {
|
||||||
@@ -113,6 +127,7 @@ export const getAppChatConfig = ({
|
|||||||
whisperConfig,
|
whisperConfig,
|
||||||
scheduledTriggerConfig,
|
scheduledTriggerConfig,
|
||||||
chatInputGuide,
|
chatInputGuide,
|
||||||
|
instruction,
|
||||||
...chatConfig,
|
...chatConfig,
|
||||||
variables: storeVariables ?? chatConfig?.variables ?? variables,
|
variables: storeVariables ?? chatConfig?.variables ?? variables,
|
||||||
welcomeText: storeWelcomeText ?? chatConfig?.welcomeText ?? welcomeText
|
welcomeText: storeWelcomeText ?? chatConfig?.welcomeText ?? welcomeText
|
||||||
@@ -147,9 +162,11 @@ export const getModuleInputUiField = (input: FlowNodeInputItemType) => {
|
|||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const pluginData2FlowNodeIO = (
|
export const pluginData2FlowNodeIO = ({
|
||||||
nodes: StoreNodeItemType[]
|
nodes
|
||||||
): {
|
}: {
|
||||||
|
nodes: StoreNodeItemType[];
|
||||||
|
}): {
|
||||||
inputs: FlowNodeInputItemType[];
|
inputs: FlowNodeInputItemType[];
|
||||||
outputs: FlowNodeOutputItemType[];
|
outputs: FlowNodeOutputItemType[];
|
||||||
} => {
|
} => {
|
||||||
@@ -157,14 +174,17 @@ export const pluginData2FlowNodeIO = (
|
|||||||
const pluginOutput = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginOutput);
|
const pluginOutput = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginOutput);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
inputs: pluginInput
|
inputs:
|
||||||
? pluginInput.inputs.map((item) => ({
|
pluginInput?.inputs.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
...getModuleInputUiField(item),
|
...getModuleInputUiField(item),
|
||||||
value: getOrInitModuleInputValue(item),
|
value: getOrInitModuleInputValue(item),
|
||||||
canEdit: false
|
canEdit: false,
|
||||||
}))
|
renderTypeList:
|
||||||
: [],
|
item.renderTypeList[0] === FlowNodeInputTypeEnum.customVariable
|
||||||
|
? [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.input]
|
||||||
|
: item.renderTypeList
|
||||||
|
})) || [],
|
||||||
outputs: pluginOutput
|
outputs: pluginOutput
|
||||||
? [
|
? [
|
||||||
...pluginOutput.inputs.map((item) => ({
|
...pluginOutput.inputs.map((item) => ({
|
||||||
@@ -180,6 +200,80 @@ export const pluginData2FlowNodeIO = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const appData2FlowNodeIO = ({
|
||||||
|
chatConfig
|
||||||
|
}: {
|
||||||
|
chatConfig?: AppChatConfigType;
|
||||||
|
}): {
|
||||||
|
inputs: FlowNodeInputItemType[];
|
||||||
|
outputs: FlowNodeOutputItemType[];
|
||||||
|
} => {
|
||||||
|
const variableInput = !chatConfig?.variables
|
||||||
|
? []
|
||||||
|
: chatConfig.variables.map((item) => {
|
||||||
|
const renderTypeMap = {
|
||||||
|
[VariableInputEnum.input]: [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.reference],
|
||||||
|
[VariableInputEnum.textarea]: [
|
||||||
|
FlowNodeInputTypeEnum.textarea,
|
||||||
|
FlowNodeInputTypeEnum.reference
|
||||||
|
],
|
||||||
|
[VariableInputEnum.select]: [FlowNodeInputTypeEnum.select],
|
||||||
|
[VariableInputEnum.custom]: [
|
||||||
|
FlowNodeInputTypeEnum.input,
|
||||||
|
FlowNodeInputTypeEnum.reference
|
||||||
|
],
|
||||||
|
default: [FlowNodeInputTypeEnum.reference]
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: item.key,
|
||||||
|
renderTypeList: renderTypeMap[item.type] || renderTypeMap.default,
|
||||||
|
label: item.label,
|
||||||
|
debugLabel: item.label,
|
||||||
|
description: '',
|
||||||
|
valueType: WorkflowIOValueTypeEnum.any,
|
||||||
|
required: item.required,
|
||||||
|
list: item.enums.map((enumItem) => ({
|
||||||
|
label: enumItem.value,
|
||||||
|
value: enumItem.value
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// const showFileLink =
|
||||||
|
// chatConfig?.fileSelectConfig?.canSelectFile || chatConfig?.fileSelectConfig?.canSelectImg;
|
||||||
|
|
||||||
|
return {
|
||||||
|
inputs: [
|
||||||
|
Input_Template_History,
|
||||||
|
Input_Template_UserChatInput,
|
||||||
|
// ...(showFileLink ? [Input_Template_File_Link] : []),
|
||||||
|
...variableInput
|
||||||
|
],
|
||||||
|
outputs: [
|
||||||
|
{
|
||||||
|
id: NodeOutputKeyEnum.history,
|
||||||
|
key: NodeOutputKeyEnum.history,
|
||||||
|
required: true,
|
||||||
|
label: i18nT('common:core.module.output.label.New context'),
|
||||||
|
description: i18nT('common:core.module.output.description.New context'),
|
||||||
|
valueType: WorkflowIOValueTypeEnum.chatHistory,
|
||||||
|
valueDesc: chatHistoryValueDesc,
|
||||||
|
type: FlowNodeOutputTypeEnum.static
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: NodeOutputKeyEnum.answerText,
|
||||||
|
key: NodeOutputKeyEnum.answerText,
|
||||||
|
required: false,
|
||||||
|
label: i18nT('common:core.module.output.label.Ai response content'),
|
||||||
|
description: i18nT('common:core.module.output.description.Ai response content'),
|
||||||
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
|
type: FlowNodeOutputTypeEnum.static
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const formatEditorVariablePickerIcon = (
|
export const formatEditorVariablePickerIcon = (
|
||||||
variables: { key: string; label: string; type?: `${VariableInputEnum}`; required?: boolean }[]
|
variables: { key: string; label: string; type?: `${VariableInputEnum}`; required?: boolean }[]
|
||||||
): EditorVariablePickerType[] => {
|
): EditorVariablePickerType[] => {
|
||||||
@@ -232,6 +326,9 @@ export const updatePluginInputByVariables = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Remove pluginInput variables from global variables
|
||||||
|
(completions api: Plugin input get value from global variables)
|
||||||
|
*/
|
||||||
export const removePluginInputVariables = (
|
export const removePluginInputVariables = (
|
||||||
variables: Record<string, any>,
|
variables: Record<string, any>,
|
||||||
nodes: RuntimeNodeItemType[]
|
nodes: RuntimeNodeItemType[]
|
||||||
@@ -250,7 +347,8 @@ export const removePluginInputVariables = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function replaceVariableLabel({
|
// replace {{$xx.xx$}} variables for text
|
||||||
|
export function replaceEditorVariable({
|
||||||
text,
|
text,
|
||||||
nodes,
|
nodes,
|
||||||
variables,
|
variables,
|
||||||
@@ -258,7 +356,7 @@ export function replaceVariableLabel({
|
|||||||
}: {
|
}: {
|
||||||
text: any;
|
text: any;
|
||||||
nodes: RuntimeNodeItemType[];
|
nodes: RuntimeNodeItemType[];
|
||||||
variables: Record<string, string | number>;
|
variables: Record<string, any>; // global variables
|
||||||
runningNode: RuntimeNodeItemType;
|
runningNode: RuntimeNodeItemType;
|
||||||
}) {
|
}) {
|
||||||
if (typeof text !== 'string') return text;
|
if (typeof text !== 'string') return text;
|
||||||
|
|||||||
29
packages/global/support/wallet/bill/api.d.ts
vendored
29
packages/global/support/wallet/bill/api.d.ts
vendored
@@ -1,16 +1,25 @@
|
|||||||
|
import { StandardSubLevelEnum, SubModeEnum } from '../sub/constants';
|
||||||
import { BillTypeEnum } from './constants';
|
import { BillTypeEnum } from './constants';
|
||||||
|
|
||||||
export type CreateBillProps = {
|
export type CreateStandPlanBill = {
|
||||||
type: BillTypeEnum;
|
type: BillTypeEnum.standSubPlan;
|
||||||
|
level: `${StandardSubLevelEnum}`;
|
||||||
// balance
|
subMode: `${SubModeEnum}`;
|
||||||
balance?: number; // read
|
|
||||||
|
|
||||||
month?: number;
|
|
||||||
// extra dataset size
|
|
||||||
extraDatasetSize?: number; // 1k
|
|
||||||
extraPoints?: number; // 100w
|
|
||||||
};
|
};
|
||||||
|
type CreateExtractPointsBill = {
|
||||||
|
type: BillTypeEnum.extraPoints;
|
||||||
|
extraPoints: number;
|
||||||
|
};
|
||||||
|
type CreateExtractDatasetBill = {
|
||||||
|
type: BillTypeEnum.extraDatasetSub;
|
||||||
|
extraDatasetSize: number;
|
||||||
|
month: number;
|
||||||
|
};
|
||||||
|
export type CreateBillProps =
|
||||||
|
| CreateStandPlanBill
|
||||||
|
| CreateExtractPointsBill
|
||||||
|
| CreateExtractDatasetBill;
|
||||||
|
|
||||||
export type CreateBillResponse = {
|
export type CreateBillResponse = {
|
||||||
billId: string;
|
billId: string;
|
||||||
codeUrl: string;
|
codeUrl: string;
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export type BillSchemaType = {
|
|||||||
month?: number;
|
month?: number;
|
||||||
datasetSize?: number;
|
datasetSize?: number;
|
||||||
extraPoints?: number;
|
extraPoints?: number;
|
||||||
invoice: boolean;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { i18nT } from '../../../../web/i18n/utils';
|
||||||
|
|
||||||
export enum SubTypeEnum {
|
export enum SubTypeEnum {
|
||||||
standard = 'standard',
|
standard = 'standard',
|
||||||
extraDatasetSize = 'extraDatasetSize',
|
extraDatasetSize = 'extraDatasetSize',
|
||||||
@@ -19,19 +21,6 @@ export const subTypeMap = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum SubStatusEnum {
|
|
||||||
active = 'active',
|
|
||||||
expired = 'expired'
|
|
||||||
}
|
|
||||||
export const subStatusMap = {
|
|
||||||
[SubStatusEnum.active]: {
|
|
||||||
label: 'support.wallet.subscription.status.active'
|
|
||||||
},
|
|
||||||
[SubStatusEnum.expired]: {
|
|
||||||
label: 'support.wallet.subscription.status.canceled'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum SubModeEnum {
|
export enum SubModeEnum {
|
||||||
month = 'month',
|
month = 'month',
|
||||||
year = 'year'
|
year = 'year'
|
||||||
@@ -39,11 +28,13 @@ export enum SubModeEnum {
|
|||||||
export const subModeMap = {
|
export const subModeMap = {
|
||||||
[SubModeEnum.month]: {
|
[SubModeEnum.month]: {
|
||||||
label: 'support.wallet.subscription.mode.Month',
|
label: 'support.wallet.subscription.mode.Month',
|
||||||
durationMonth: 1
|
durationMonth: 1,
|
||||||
|
payMonth: 1
|
||||||
},
|
},
|
||||||
[SubModeEnum.year]: {
|
[SubModeEnum.year]: {
|
||||||
label: 'support.wallet.subscription.mode.Year',
|
label: 'support.wallet.subscription.mode.Year',
|
||||||
durationMonth: 12
|
durationMonth: 12,
|
||||||
|
payMonth: 10
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -56,23 +47,28 @@ export enum StandardSubLevelEnum {
|
|||||||
}
|
}
|
||||||
export const standardSubLevelMap = {
|
export const standardSubLevelMap = {
|
||||||
[StandardSubLevelEnum.free]: {
|
[StandardSubLevelEnum.free]: {
|
||||||
label: 'support.wallet.subscription.standardSubLevel.free',
|
label: i18nT('common:support.wallet.subscription.standardSubLevel.free'),
|
||||||
desc: 'support.wallet.subscription.standardSubLevel.free desc'
|
desc: i18nT('common:support.wallet.subscription.standardSubLevel.free desc'),
|
||||||
|
weight: 1
|
||||||
},
|
},
|
||||||
[StandardSubLevelEnum.experience]: {
|
[StandardSubLevelEnum.experience]: {
|
||||||
label: 'support.wallet.subscription.standardSubLevel.experience',
|
label: i18nT('common:support.wallet.subscription.standardSubLevel.experience'),
|
||||||
desc: ''
|
desc: i18nT('common:support.wallet.subscription.standardSubLevel.experience_desc'),
|
||||||
|
weight: 2
|
||||||
},
|
},
|
||||||
[StandardSubLevelEnum.team]: {
|
[StandardSubLevelEnum.team]: {
|
||||||
label: 'support.wallet.subscription.standardSubLevel.team',
|
label: i18nT('common:support.wallet.subscription.standardSubLevel.team'),
|
||||||
desc: ''
|
desc: i18nT('common:support.wallet.subscription.standardSubLevel.team_desc'),
|
||||||
|
weight: 3
|
||||||
},
|
},
|
||||||
[StandardSubLevelEnum.enterprise]: {
|
[StandardSubLevelEnum.enterprise]: {
|
||||||
label: 'support.wallet.subscription.standardSubLevel.enterprise',
|
label: i18nT('common:support.wallet.subscription.standardSubLevel.enterprise'),
|
||||||
desc: ''
|
desc: i18nT('common:support.wallet.subscription.standardSubLevel.enterprise_desc'),
|
||||||
|
weight: 4
|
||||||
},
|
},
|
||||||
[StandardSubLevelEnum.custom]: {
|
[StandardSubLevelEnum.custom]: {
|
||||||
label: 'support.wallet.subscription.standardSubLevel.custom',
|
label: i18nT('common:support.wallet.subscription.standardSubLevel.custom'),
|
||||||
desc: ''
|
desc: '',
|
||||||
|
weight: 5
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
9
packages/global/support/wallet/sub/type.d.ts
vendored
9
packages/global/support/wallet/sub/type.d.ts
vendored
@@ -1,4 +1,4 @@
|
|||||||
import { StandardSubLevelEnum, SubModeEnum, SubStatusEnum, SubTypeEnum } from './constants';
|
import { StandardSubLevelEnum, SubModeEnum, SubTypeEnum } from './constants';
|
||||||
|
|
||||||
// Content of plan
|
// Content of plan
|
||||||
export type TeamStandardSubPlanItemType = {
|
export type TeamStandardSubPlanItemType = {
|
||||||
@@ -36,17 +36,14 @@ export type TeamSubSchema = {
|
|||||||
_id: string;
|
_id: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
type: `${SubTypeEnum}`;
|
type: `${SubTypeEnum}`;
|
||||||
status: `${SubStatusEnum}`;
|
|
||||||
startTime: Date;
|
startTime: Date;
|
||||||
expiredTime: Date;
|
expiredTime: Date;
|
||||||
price: number;
|
|
||||||
|
|
||||||
currentMode: `${SubModeEnum}`;
|
currentMode: `${SubModeEnum}`;
|
||||||
nextMode: `${SubModeEnum}`;
|
nextMode: `${SubModeEnum}`;
|
||||||
currentSubLevel: `${StandardSubLevelEnum}`;
|
currentSubLevel: StandardSubLevelEnum;
|
||||||
nextSubLevel: `${StandardSubLevelEnum}`;
|
nextSubLevel: StandardSubLevelEnum;
|
||||||
|
|
||||||
pointPrice: number;
|
|
||||||
totalPoints: number;
|
totalPoints: number;
|
||||||
surplusPoints: number;
|
surplusPoints: number;
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ import { markdownProcess } from '@fastgpt/global/common/string/markdown';
|
|||||||
import { uploadMongoImg } from '../image/controller';
|
import { uploadMongoImg } from '../image/controller';
|
||||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||||
import { addHours } from 'date-fns';
|
import { addHours } from 'date-fns';
|
||||||
|
import FormData from 'form-data';
|
||||||
|
|
||||||
import { WorkerNameEnum, runWorker } from '../../../worker/utils';
|
import { WorkerNameEnum, runWorker } from '../../../worker/utils';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { detectFileEncoding } from '@fastgpt/global/common/file/tools';
|
import { detectFileEncoding } from '@fastgpt/global/common/file/tools';
|
||||||
import type { ReadFileResponse } from '../../../worker/readFile/type';
|
import type { ReadFileResponse } from '../../../worker/readFile/type';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { addLog } from '../../system/log';
|
||||||
|
|
||||||
export type readRawTextByLocalFileParams = {
|
export type readRawTextByLocalFileParams = {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
@@ -51,15 +54,7 @@ export const readRawContentByFileBuffer = async ({
|
|||||||
metadata?: Record<string, any>;
|
metadata?: Record<string, any>;
|
||||||
}) => {
|
}) => {
|
||||||
// Upload image in markdown
|
// Upload image in markdown
|
||||||
const matchMdImgTextAndUpload = ({
|
const matchMdImgTextAndUpload = ({ teamId, md }: { md: string; teamId: string }) =>
|
||||||
teamId,
|
|
||||||
md,
|
|
||||||
metadata
|
|
||||||
}: {
|
|
||||||
md: string;
|
|
||||||
teamId: string;
|
|
||||||
metadata?: Record<string, any>;
|
|
||||||
}) =>
|
|
||||||
markdownProcess({
|
markdownProcess({
|
||||||
rawText: md,
|
rawText: md,
|
||||||
uploadImgController: (base64Img) =>
|
uploadImgController: (base64Img) =>
|
||||||
@@ -72,18 +67,63 @@ export const readRawContentByFileBuffer = async ({
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let { rawText, formatText } = await runWorker<ReadFileResponse>(WorkerNameEnum.readFile, {
|
/* If */
|
||||||
extension,
|
const customReadfileUrl = process.env.CUSTOM_READ_FILE_URL;
|
||||||
encoding,
|
const customReadFileExtension = process.env.CUSTOM_READ_FILE_EXTENSION || '';
|
||||||
buffer
|
const ocrParse = process.env.CUSTOM_READ_FILE_OCR || 'false';
|
||||||
});
|
const readFileFromCustomService = async (): Promise<ReadFileResponse | undefined> => {
|
||||||
|
if (
|
||||||
|
!customReadfileUrl ||
|
||||||
|
!customReadFileExtension ||
|
||||||
|
!customReadFileExtension.includes(extension)
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
|
const data = new FormData();
|
||||||
|
data.append('file', buffer, {
|
||||||
|
filename: `file.${extension}`
|
||||||
|
});
|
||||||
|
data.append('extension', extension);
|
||||||
|
data.append('ocr', ocrParse);
|
||||||
|
const { data: response } = await axios.post<{
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
data: {
|
||||||
|
page: number;
|
||||||
|
markdown: string;
|
||||||
|
};
|
||||||
|
}>(customReadfileUrl, data, {
|
||||||
|
timeout: 600000,
|
||||||
|
headers: {
|
||||||
|
...data.getHeaders()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addLog.info(`Use custom read file service, time: ${Date.now() - start}ms`);
|
||||||
|
|
||||||
|
const rawText = response.data.markdown;
|
||||||
|
|
||||||
|
return {
|
||||||
|
rawText,
|
||||||
|
formatText: rawText
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let { rawText, formatText } =
|
||||||
|
(await readFileFromCustomService()) ||
|
||||||
|
(await runWorker<ReadFileResponse>(WorkerNameEnum.readFile, {
|
||||||
|
extension,
|
||||||
|
encoding,
|
||||||
|
buffer
|
||||||
|
}));
|
||||||
|
|
||||||
// markdown data format
|
// markdown data format
|
||||||
if (['md', 'html', 'docx'].includes(extension)) {
|
if (['md', 'html', 'docx', ...customReadFileExtension.split(',')].includes(extension)) {
|
||||||
rawText = await matchMdImgTextAndUpload({
|
rawText = await matchMdImgTextAndUpload({
|
||||||
teamId: teamId,
|
teamId: teamId,
|
||||||
md: rawText,
|
md: rawText
|
||||||
metadata: metadata
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,9 +50,11 @@ export const getAppLatestVersion = async (appId: string, app?: AppSchema) => {
|
|||||||
const version = await MongoAppVersion.findOne({
|
const version = await MongoAppVersion.findOne({
|
||||||
appId,
|
appId,
|
||||||
isPublish: true
|
isPublish: true
|
||||||
}).sort({
|
})
|
||||||
time: -1
|
.sort({
|
||||||
});
|
time: -1
|
||||||
|
})
|
||||||
|
.lean();
|
||||||
|
|
||||||
if (version) {
|
if (version) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node.d';
|
import { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node.d';
|
||||||
import { FlowNodeTypeEnum, defaultNodeVersion } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum, defaultNodeVersion } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { pluginData2FlowNodeIO } from '@fastgpt/global/core/workflow/utils';
|
import { appData2FlowNodeIO, pluginData2FlowNodeIO } from '@fastgpt/global/core/workflow/utils';
|
||||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||||
import type { PluginRuntimeType } from '@fastgpt/global/core/workflow/runtime/type';
|
import type { PluginRuntimeType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
@@ -52,10 +52,10 @@ const getPluginTemplateById = async (
|
|||||||
showStatus: true,
|
showStatus: true,
|
||||||
workflow: {
|
workflow: {
|
||||||
nodes: item.modules,
|
nodes: item.modules,
|
||||||
edges: item.edges
|
edges: item.edges,
|
||||||
|
chatConfig: item.chatConfig
|
||||||
},
|
},
|
||||||
templateType: FlowNodeTemplateTypeEnum.teamApp,
|
templateType: FlowNodeTemplateTypeEnum.teamApp,
|
||||||
isTool: true,
|
|
||||||
version: item?.pluginData?.nodeVersion || defaultNodeVersion,
|
version: item?.pluginData?.nodeVersion || defaultNodeVersion,
|
||||||
originCost: 0,
|
originCost: 0,
|
||||||
currentCost: 0
|
currentCost: 0
|
||||||
@@ -71,22 +71,27 @@ const getPluginTemplateById = async (
|
|||||||
/* format plugin modules to plugin preview module */
|
/* format plugin modules to plugin preview module */
|
||||||
export async function getPluginPreviewNode({ id }: { id: string }): Promise<FlowNodeTemplateType> {
|
export async function getPluginPreviewNode({ id }: { id: string }): Promise<FlowNodeTemplateType> {
|
||||||
const plugin = await getPluginTemplateById(id);
|
const plugin = await getPluginTemplateById(id);
|
||||||
|
const isPlugin = !!plugin.workflow.nodes.find(
|
||||||
|
(node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: getNanoid(),
|
id: getNanoid(),
|
||||||
pluginId: plugin.id,
|
pluginId: plugin.id,
|
||||||
templateType: plugin.templateType,
|
templateType: plugin.templateType,
|
||||||
flowNodeType: FlowNodeTypeEnum.pluginModule,
|
flowNodeType: isPlugin ? FlowNodeTypeEnum.pluginModule : FlowNodeTypeEnum.appModule,
|
||||||
avatar: plugin.avatar,
|
avatar: plugin.avatar,
|
||||||
name: plugin.name,
|
name: plugin.name,
|
||||||
intro: plugin.intro,
|
intro: plugin.intro,
|
||||||
inputExplanationUrl: plugin.inputExplanationUrl,
|
inputExplanationUrl: plugin.inputExplanationUrl,
|
||||||
showStatus: plugin.showStatus,
|
showStatus: plugin.showStatus,
|
||||||
isTool: plugin.isTool,
|
isTool: isPlugin,
|
||||||
version: plugin.version,
|
version: plugin.version,
|
||||||
sourceHandle: getHandleConfig(true, true, true, true),
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
...pluginData2FlowNodeIO(plugin.workflow.nodes)
|
...(isPlugin
|
||||||
|
? pluginData2FlowNodeIO({ nodes: plugin.workflow.nodes })
|
||||||
|
: appData2FlowNodeIO({ chatConfig: plugin.workflow.chatConfig }))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ const SystemPluginSchema = new Schema({
|
|||||||
currentCost: {
|
currentCost: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
}
|
},
|
||||||
|
customConfig: Object
|
||||||
});
|
});
|
||||||
|
|
||||||
SystemPluginSchema.index({ pluginId: 1 });
|
SystemPluginSchema.index({ pluginId: 1 });
|
||||||
|
|||||||
16
packages/service/core/app/plugin/type.d.ts
vendored
16
packages/service/core/app/plugin/type.d.ts
vendored
@@ -1,4 +1,8 @@
|
|||||||
import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
|
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
|
import {
|
||||||
|
SystemPluginTemplateItemType,
|
||||||
|
WorkflowTemplateBasicType
|
||||||
|
} from '@fastgpt/global/core/workflow/type';
|
||||||
|
|
||||||
export type SystemPluginConfigSchemaType = {
|
export type SystemPluginConfigSchemaType = {
|
||||||
pluginId: string;
|
pluginId: string;
|
||||||
@@ -7,4 +11,14 @@ export type SystemPluginConfigSchemaType = {
|
|||||||
currentCost: number;
|
currentCost: number;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
inputConfig: SystemPluginTemplateItemType['inputConfig'];
|
inputConfig: SystemPluginTemplateItemType['inputConfig'];
|
||||||
|
|
||||||
|
customConfig?: {
|
||||||
|
name: string;
|
||||||
|
avatar: string;
|
||||||
|
intro?: string;
|
||||||
|
version: string;
|
||||||
|
weight?: number;
|
||||||
|
workflow: WorkflowTemplateBasicType;
|
||||||
|
templateType: FlowNodeTemplateTypeEnum;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ export const chatConfigType = {
|
|||||||
whisperConfig: Object,
|
whisperConfig: Object,
|
||||||
scheduledTriggerConfig: Object,
|
scheduledTriggerConfig: Object,
|
||||||
chatInputGuide: Object,
|
chatInputGuide: Object,
|
||||||
fileSelectConfig: Object
|
fileSelectConfig: Object,
|
||||||
|
instruction: String
|
||||||
};
|
};
|
||||||
|
|
||||||
// schema
|
// schema
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ export async function getChatItems({
|
|||||||
|
|
||||||
return { histories };
|
return { histories };
|
||||||
}
|
}
|
||||||
/* 临时适配旧的对话记录 */
|
|
||||||
|
/* Temporary adaptation for old conversation records */
|
||||||
export const adaptStringValue = (value: any): ChatItemValueItemType[] => {
|
export const adaptStringValue = (value: any): ChatItemValueItemType[] => {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ const DatasetCollectionSchema = new Schema({
|
|||||||
qaPrompt: {
|
qaPrompt: {
|
||||||
type: String
|
type: String
|
||||||
},
|
},
|
||||||
|
ocrParse: Boolean,
|
||||||
|
|
||||||
tags: {
|
tags: {
|
||||||
type: [String],
|
type: [String],
|
||||||
|
|||||||
@@ -159,7 +159,8 @@ export const reloadCollectionChunks = async ({
|
|||||||
// split data
|
// split data
|
||||||
const { chunks } = splitText2Chunks({
|
const { chunks } = splitText2Chunks({
|
||||||
text: newRawText,
|
text: newRawText,
|
||||||
chunkLen: col.chunkSize || 512
|
chunkLen: col.chunkSize || 512,
|
||||||
|
customReg: col.chunkSplitter ? [col.chunkSplitter] : [],
|
||||||
});
|
});
|
||||||
|
|
||||||
// insert to training queue
|
// insert to training queue
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
/* Abandoned */
|
||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { SelectAppItemType } from '@fastgpt/global/core/workflow/template/system/runApp/type';
|
import { SelectAppItemType } from '@fastgpt/global/core/workflow/template/system/abandoned/runApp/type';
|
||||||
import { dispatchWorkFlow } from '../index';
|
import { dispatchWorkFlow } from '../index';
|
||||||
import { responseWrite } from '../../../../common/response';
|
|
||||||
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 {
|
import {
|
||||||
@@ -31,10 +31,8 @@ type Response = DispatchNodeResultType<{
|
|||||||
|
|
||||||
export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||||
const {
|
const {
|
||||||
res,
|
|
||||||
app: workflowApp,
|
app: workflowApp,
|
||||||
stream,
|
workflowStreamResponse,
|
||||||
detail,
|
|
||||||
histories,
|
histories,
|
||||||
query,
|
query,
|
||||||
params: { userChatInput, history, app }
|
params: { userChatInput, history, app }
|
||||||
@@ -51,15 +49,12 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
|||||||
per: ReadPermissionVal
|
per: ReadPermissionVal
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res && stream) {
|
workflowStreamResponse?.({
|
||||||
responseWrite({
|
event: SseResponseEventEnum.fastAnswer,
|
||||||
res,
|
data: textAdaptGptResponse({
|
||||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
text: '\n'
|
||||||
data: textAdaptGptResponse({
|
})
|
||||||
text: '\n'
|
});
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const chatHistories = getHistories(history, histories);
|
const chatHistories = getHistories(history, histories);
|
||||||
const { files } = chatValue2RuntimePrompt(query);
|
const { files } = chatValue2RuntimePrompt(query);
|
||||||
@@ -1,85 +1,96 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { SelectAppItemType } from '@fastgpt/global/core/workflow/template/system/runApp/type';
|
import { dispatchWorkFlow } from '../index';
|
||||||
import { dispatchWorkFlowV1 } from '../index';
|
|
||||||
import { MongoApp } from '../../../../core/app/schema';
|
|
||||||
import { responseWrite } from '../../../../common/response';
|
|
||||||
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 {
|
||||||
|
getWorkflowEntryNodeIds,
|
||||||
|
initWorkflowEdgeStatus,
|
||||||
|
storeNodes2RuntimeNodes,
|
||||||
|
textAdaptGptResponse
|
||||||
|
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
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';
|
||||||
import { getHistories, setEntryEntries } from '../utils';
|
import { filterSystemVariables, getHistories } from '../utils';
|
||||||
import { chatValue2RuntimePrompt, runtimePrompt2ChatsValue } from '@fastgpt/global/core/chat/adapt';
|
import { chatValue2RuntimePrompt, runtimePrompt2ChatsValue } from '@fastgpt/global/core/chat/adapt';
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
|
import { authAppByTmbId } from '../../../../support/permission/app/auth';
|
||||||
|
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||||
|
|
||||||
type Props = ModuleDispatchProps<{
|
type Props = ModuleDispatchProps<{
|
||||||
[NodeInputKeyEnum.userChatInput]: string;
|
[NodeInputKeyEnum.userChatInput]: string;
|
||||||
[NodeInputKeyEnum.history]?: ChatItemType[] | number;
|
[NodeInputKeyEnum.history]?: ChatItemType[] | number;
|
||||||
app: SelectAppItemType;
|
[NodeInputKeyEnum.fileUrlList]?: string[];
|
||||||
}>;
|
}>;
|
||||||
type Response = DispatchNodeResultType<{
|
type Response = DispatchNodeResultType<{
|
||||||
[NodeOutputKeyEnum.answerText]: string;
|
[NodeOutputKeyEnum.answerText]: string;
|
||||||
[NodeOutputKeyEnum.history]: ChatItemType[];
|
[NodeOutputKeyEnum.history]: ChatItemType[];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
|
||||||
const {
|
const {
|
||||||
res,
|
app: workflowApp,
|
||||||
teamId,
|
|
||||||
stream,
|
|
||||||
detail,
|
|
||||||
histories,
|
histories,
|
||||||
inputFiles,
|
query,
|
||||||
params: { userChatInput, history, app }
|
node: { pluginId },
|
||||||
|
workflowStreamResponse,
|
||||||
|
params,
|
||||||
|
variables
|
||||||
} = props;
|
} = props;
|
||||||
let start = Date.now();
|
|
||||||
|
|
||||||
|
const { userChatInput, history, ...childrenAppVariables } = params;
|
||||||
if (!userChatInput) {
|
if (!userChatInput) {
|
||||||
return Promise.reject('Input is empty');
|
return Promise.reject('Input is empty');
|
||||||
}
|
}
|
||||||
|
if (!pluginId) {
|
||||||
|
return Promise.reject('pluginId is empty');
|
||||||
|
}
|
||||||
|
|
||||||
const appData = await MongoApp.findOne({
|
// Auth the app by tmbId(Not the user, but the workflow user)
|
||||||
_id: app.id,
|
const { app: appData } = await authAppByTmbId({
|
||||||
teamId
|
appId: pluginId,
|
||||||
|
tmbId: workflowApp.tmbId,
|
||||||
|
per: ReadPermissionVal
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!appData) {
|
// Auto line
|
||||||
return Promise.reject('App not found');
|
workflowStreamResponse?.({
|
||||||
}
|
event: SseResponseEventEnum.answer,
|
||||||
|
data: textAdaptGptResponse({
|
||||||
if (stream) {
|
text: '\n'
|
||||||
responseWrite({
|
})
|
||||||
res,
|
});
|
||||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
|
||||||
data: textAdaptGptResponse({
|
|
||||||
text: '\n'
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const chatHistories = getHistories(history, histories);
|
const chatHistories = getHistories(history, histories);
|
||||||
|
const { files } = chatValue2RuntimePrompt(query);
|
||||||
|
|
||||||
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlowV1({
|
// Concat variables
|
||||||
|
const systemVariables = filterSystemVariables(variables);
|
||||||
|
const childrenRunVariables = {
|
||||||
|
...systemVariables,
|
||||||
|
...childrenAppVariables
|
||||||
|
};
|
||||||
|
|
||||||
|
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
|
||||||
...props,
|
...props,
|
||||||
appId: app.id,
|
app: appData,
|
||||||
modules: setEntryEntries(appData.modules),
|
runtimeNodes: storeNodes2RuntimeNodes(
|
||||||
runtimeModules: undefined, // must reset
|
appData.modules,
|
||||||
|
getWorkflowEntryNodeIds(appData.modules)
|
||||||
|
),
|
||||||
|
runtimeEdges: initWorkflowEdgeStatus(appData.edges),
|
||||||
histories: chatHistories,
|
histories: chatHistories,
|
||||||
inputFiles,
|
query: runtimePrompt2ChatsValue({
|
||||||
startParams: {
|
files,
|
||||||
userChatInput
|
text: userChatInput
|
||||||
}
|
}),
|
||||||
|
variables: childrenRunVariables
|
||||||
});
|
});
|
||||||
|
|
||||||
const completeMessages = chatHistories.concat([
|
const completeMessages = chatHistories.concat([
|
||||||
{
|
{
|
||||||
obj: ChatRoleEnum.Human,
|
obj: ChatRoleEnum.Human,
|
||||||
value: runtimePrompt2ChatsValue({
|
value: query
|
||||||
files: inputFiles,
|
|
||||||
text: userChatInput
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
obj: ChatRoleEnum.AI,
|
obj: ChatRoleEnum.AI,
|
||||||
@@ -11,18 +11,14 @@ import {
|
|||||||
ChatCompletionAssistantMessageParam
|
ChatCompletionAssistantMessageParam
|
||||||
} from '@fastgpt/global/core/ai/type.d';
|
} from '@fastgpt/global/core/ai/type.d';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import {
|
import { responseWriteController } from '../../../../../common/response';
|
||||||
responseWrite,
|
|
||||||
responseWriteController,
|
|
||||||
responseWriteNodeStatus
|
|
||||||
} from '../../../../../common/response';
|
|
||||||
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 { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
||||||
import { dispatchWorkFlow } from '../../index';
|
import { dispatchWorkFlow } from '../../index';
|
||||||
import { DispatchToolModuleProps, RunToolResponse, ToolNodeItemType } from './type.d';
|
import { DispatchToolModuleProps, RunToolResponse, ToolNodeItemType } from './type.d';
|
||||||
import json5 from 'json5';
|
import json5 from 'json5';
|
||||||
import { DispatchFlowResponse } from '../../type';
|
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';
|
||||||
@@ -50,9 +46,9 @@ export const runToolWithFunctionCall = async (
|
|||||||
res,
|
res,
|
||||||
requestOrigin,
|
requestOrigin,
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
detail = false,
|
|
||||||
node,
|
node,
|
||||||
stream,
|
stream,
|
||||||
|
workflowStreamResponse,
|
||||||
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
||||||
} = props;
|
} = props;
|
||||||
const assistantResponses = response?.assistantResponses || [];
|
const assistantResponses = response?.assistantResponses || [];
|
||||||
@@ -143,9 +139,9 @@ export const runToolWithFunctionCall = async (
|
|||||||
if (res && stream) {
|
if (res && stream) {
|
||||||
return streamResponse({
|
return streamResponse({
|
||||||
res,
|
res,
|
||||||
detail,
|
|
||||||
toolNodes,
|
toolNodes,
|
||||||
stream: aiResponse
|
stream: aiResponse,
|
||||||
|
workflowStreamResponse
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const result = aiResponse as ChatCompletion;
|
const result = aiResponse as ChatCompletion;
|
||||||
@@ -216,21 +212,18 @@ export const runToolWithFunctionCall = async (
|
|||||||
content: stringToolResponse
|
content: stringToolResponse
|
||||||
};
|
};
|
||||||
|
|
||||||
if (stream && detail) {
|
workflowStreamResponse?.({
|
||||||
responseWrite({
|
event: SseResponseEventEnum.toolResponse,
|
||||||
res,
|
data: {
|
||||||
event: SseResponseEventEnum.toolResponse,
|
tool: {
|
||||||
data: JSON.stringify({
|
id: tool.id,
|
||||||
tool: {
|
toolName: '',
|
||||||
id: tool.id,
|
toolAvatar: '',
|
||||||
toolName: '',
|
params: '',
|
||||||
toolAvatar: '',
|
response: sliceStrStartEnd(stringToolResponse, 500, 500)
|
||||||
params: '',
|
}
|
||||||
response: sliceStrStartEnd(stringToolResponse, 500, 500)
|
}
|
||||||
}
|
});
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
toolRunResponse,
|
toolRunResponse,
|
||||||
@@ -260,10 +253,14 @@ export const runToolWithFunctionCall = async (
|
|||||||
];
|
];
|
||||||
// console.log(tokens, 'tool');
|
// console.log(tokens, 'tool');
|
||||||
|
|
||||||
if (stream && detail) {
|
// Run tool status
|
||||||
responseWriteNodeStatus({
|
if (node.showStatus) {
|
||||||
res,
|
workflowStreamResponse?.({
|
||||||
name: node.name
|
event: SseResponseEventEnum.flowNodeStatus,
|
||||||
|
data: {
|
||||||
|
status: 'running',
|
||||||
|
name: node.name
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,14 +334,14 @@ export const runToolWithFunctionCall = async (
|
|||||||
|
|
||||||
async function streamResponse({
|
async function streamResponse({
|
||||||
res,
|
res,
|
||||||
detail,
|
|
||||||
toolNodes,
|
toolNodes,
|
||||||
stream
|
stream,
|
||||||
|
workflowStreamResponse
|
||||||
}: {
|
}: {
|
||||||
res: NextApiResponse;
|
res: NextApiResponse;
|
||||||
detail: boolean;
|
|
||||||
toolNodes: ToolNodeItemType[];
|
toolNodes: ToolNodeItemType[];
|
||||||
stream: StreamChatType;
|
stream: StreamChatType;
|
||||||
|
workflowStreamResponse?: WorkflowResponseType;
|
||||||
}) {
|
}) {
|
||||||
const write = responseWriteController({
|
const write = responseWriteController({
|
||||||
res,
|
res,
|
||||||
@@ -367,9 +364,9 @@ async function streamResponse({
|
|||||||
const content = responseChoice?.content || '';
|
const content = responseChoice?.content || '';
|
||||||
textAnswer += content;
|
textAnswer += content;
|
||||||
|
|
||||||
responseWrite({
|
workflowStreamResponse?.({
|
||||||
write,
|
write,
|
||||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
event: SseResponseEventEnum.answer,
|
||||||
data: textAdaptGptResponse({
|
data: textAdaptGptResponse({
|
||||||
text: content
|
text: content
|
||||||
})
|
})
|
||||||
@@ -397,22 +394,20 @@ async function streamResponse({
|
|||||||
toolAvatar: toolNode.avatar
|
toolAvatar: toolNode.avatar
|
||||||
});
|
});
|
||||||
|
|
||||||
if (detail) {
|
workflowStreamResponse?.({
|
||||||
responseWrite({
|
write,
|
||||||
write,
|
event: SseResponseEventEnum.toolCall,
|
||||||
event: SseResponseEventEnum.toolCall,
|
data: {
|
||||||
data: JSON.stringify({
|
tool: {
|
||||||
tool: {
|
id: functionId,
|
||||||
id: functionId,
|
toolName: toolNode.name,
|
||||||
toolName: toolNode.name,
|
toolAvatar: toolNode.avatar,
|
||||||
toolAvatar: toolNode.avatar,
|
functionName: functionCall.name,
|
||||||
functionName: functionCall.name,
|
params: functionCall.arguments,
|
||||||
params: functionCall.arguments,
|
response: ''
|
||||||
response: ''
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -424,21 +419,19 @@ async function streamResponse({
|
|||||||
if (currentTool) {
|
if (currentTool) {
|
||||||
currentTool.arguments += arg;
|
currentTool.arguments += arg;
|
||||||
|
|
||||||
if (detail) {
|
workflowStreamResponse?.({
|
||||||
responseWrite({
|
write,
|
||||||
write,
|
event: SseResponseEventEnum.toolParams,
|
||||||
event: SseResponseEventEnum.toolParams,
|
data: {
|
||||||
data: JSON.stringify({
|
tool: {
|
||||||
tool: {
|
id: functionId,
|
||||||
id: functionId,
|
toolName: '',
|
||||||
toolName: '',
|
toolAvatar: '',
|
||||||
toolAvatar: '',
|
params: arg,
|
||||||
params: arg,
|
response: ''
|
||||||
response: ''
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,7 @@ import {
|
|||||||
ChatCompletionAssistantMessageParam
|
ChatCompletionAssistantMessageParam
|
||||||
} from '@fastgpt/global/core/ai/type';
|
} from '@fastgpt/global/core/ai/type';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import {
|
import { responseWriteController } from '../../../../../common/response';
|
||||||
responseWrite,
|
|
||||||
responseWriteController,
|
|
||||||
responseWriteNodeStatus
|
|
||||||
} from '../../../../../common/response';
|
|
||||||
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 { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
||||||
@@ -30,6 +26,7 @@ import { AIChatItemType } from '@fastgpt/global/core/chat/type';
|
|||||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||||
import { updateToolInputValue } from './utils';
|
import { updateToolInputValue } from './utils';
|
||||||
import { computedMaxToken, computedTemperature } from '../../../../ai/utils';
|
import { computedMaxToken, computedTemperature } from '../../../../ai/utils';
|
||||||
|
import { WorkflowResponseType } from '../../type';
|
||||||
|
|
||||||
type FunctionCallCompletion = {
|
type FunctionCallCompletion = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -56,9 +53,9 @@ export const runToolWithPromptCall = async (
|
|||||||
res,
|
res,
|
||||||
requestOrigin,
|
requestOrigin,
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
detail = false,
|
|
||||||
node,
|
node,
|
||||||
stream,
|
stream,
|
||||||
|
workflowStreamResponse,
|
||||||
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
||||||
} = props;
|
} = props;
|
||||||
const assistantResponses = response?.assistantResponses || [];
|
const assistantResponses = response?.assistantResponses || [];
|
||||||
@@ -143,9 +140,9 @@ export const runToolWithPromptCall = async (
|
|||||||
if (res && stream) {
|
if (res && stream) {
|
||||||
const { answer } = await streamResponse({
|
const { answer } = await streamResponse({
|
||||||
res,
|
res,
|
||||||
detail,
|
|
||||||
toolNodes,
|
toolNodes,
|
||||||
stream: aiResponse
|
stream: aiResponse,
|
||||||
|
workflowStreamResponse
|
||||||
});
|
});
|
||||||
|
|
||||||
return answer;
|
return answer;
|
||||||
@@ -159,9 +156,8 @@ export const runToolWithPromptCall = async (
|
|||||||
const { answer: replaceAnswer, toolJson } = parseAnswer(answer);
|
const { answer: replaceAnswer, toolJson } = parseAnswer(answer);
|
||||||
// No tools
|
// No tools
|
||||||
if (!toolJson) {
|
if (!toolJson) {
|
||||||
if (replaceAnswer === ERROR_TEXT && stream && detail) {
|
if (replaceAnswer === ERROR_TEXT) {
|
||||||
responseWrite({
|
workflowStreamResponse?.({
|
||||||
res,
|
|
||||||
event: SseResponseEventEnum.answer,
|
event: SseResponseEventEnum.answer,
|
||||||
data: textAdaptGptResponse({
|
data: textAdaptGptResponse({
|
||||||
text: replaceAnswer
|
text: replaceAnswer
|
||||||
@@ -206,22 +202,19 @@ export const runToolWithPromptCall = async (
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
// SSE response to client
|
// SSE response to client
|
||||||
if (stream && detail) {
|
workflowStreamResponse?.({
|
||||||
responseWrite({
|
event: SseResponseEventEnum.toolCall,
|
||||||
res,
|
data: {
|
||||||
event: SseResponseEventEnum.toolCall,
|
tool: {
|
||||||
data: JSON.stringify({
|
id: toolJson.id,
|
||||||
tool: {
|
toolName: toolNode.name,
|
||||||
id: toolJson.id,
|
toolAvatar: toolNode.avatar,
|
||||||
toolName: toolNode.name,
|
functionName: toolJson.name,
|
||||||
toolAvatar: toolNode.avatar,
|
params: toolJson.arguments,
|
||||||
functionName: toolJson.name,
|
response: ''
|
||||||
params: toolJson.arguments,
|
}
|
||||||
response: ''
|
}
|
||||||
}
|
});
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const moduleRunResponse = await dispatchWorkFlow({
|
const moduleRunResponse = await dispatchWorkFlow({
|
||||||
...props,
|
...props,
|
||||||
@@ -245,21 +238,18 @@ export const runToolWithPromptCall = async (
|
|||||||
return moduleRunResponse.toolResponses ? String(moduleRunResponse.toolResponses) : 'none';
|
return moduleRunResponse.toolResponses ? String(moduleRunResponse.toolResponses) : 'none';
|
||||||
})();
|
})();
|
||||||
|
|
||||||
if (stream && detail) {
|
workflowStreamResponse?.({
|
||||||
responseWrite({
|
event: SseResponseEventEnum.toolResponse,
|
||||||
res,
|
data: {
|
||||||
event: SseResponseEventEnum.toolResponse,
|
tool: {
|
||||||
data: JSON.stringify({
|
id: toolJson.id,
|
||||||
tool: {
|
toolName: '',
|
||||||
id: toolJson.id,
|
toolAvatar: '',
|
||||||
toolName: '',
|
params: '',
|
||||||
toolAvatar: '',
|
response: sliceStrStartEnd(stringToolResponse, 500, 500)
|
||||||
params: '',
|
}
|
||||||
response: sliceStrStartEnd(stringToolResponse, 500, 500)
|
}
|
||||||
}
|
});
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
moduleRunResponse,
|
moduleRunResponse,
|
||||||
@@ -267,10 +257,14 @@ export const runToolWithPromptCall = async (
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
if (stream && detail) {
|
// Run tool status
|
||||||
responseWriteNodeStatus({
|
if (node.showStatus) {
|
||||||
res,
|
workflowStreamResponse?.({
|
||||||
name: node.name
|
event: SseResponseEventEnum.flowNodeStatus,
|
||||||
|
data: {
|
||||||
|
status: 'running',
|
||||||
|
name: node.name
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,13 +334,13 @@ ANSWER: `;
|
|||||||
|
|
||||||
async function streamResponse({
|
async function streamResponse({
|
||||||
res,
|
res,
|
||||||
detail,
|
stream,
|
||||||
stream
|
workflowStreamResponse
|
||||||
}: {
|
}: {
|
||||||
res: NextApiResponse;
|
res: NextApiResponse;
|
||||||
detail: boolean;
|
|
||||||
toolNodes: ToolNodeItemType[];
|
toolNodes: ToolNodeItemType[];
|
||||||
stream: StreamChatType;
|
stream: StreamChatType;
|
||||||
|
workflowStreamResponse?: WorkflowResponseType;
|
||||||
}) {
|
}) {
|
||||||
const write = responseWriteController({
|
const write = responseWriteController({
|
||||||
res,
|
res,
|
||||||
@@ -365,14 +359,14 @@ async function streamResponse({
|
|||||||
const responseChoice = part.choices?.[0]?.delta;
|
const responseChoice = part.choices?.[0]?.delta;
|
||||||
// console.log(responseChoice, '---===');
|
// console.log(responseChoice, '---===');
|
||||||
|
|
||||||
if (responseChoice.content) {
|
if (responseChoice?.content) {
|
||||||
const content = responseChoice?.content || '';
|
const content = responseChoice?.content || '';
|
||||||
textAnswer += content;
|
textAnswer += content;
|
||||||
|
|
||||||
if (startResponseWrite) {
|
if (startResponseWrite) {
|
||||||
responseWrite({
|
workflowStreamResponse?.({
|
||||||
write,
|
write,
|
||||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
event: SseResponseEventEnum.answer,
|
||||||
data: textAdaptGptResponse({
|
data: textAdaptGptResponse({
|
||||||
text: content
|
text: content
|
||||||
})
|
})
|
||||||
@@ -384,9 +378,9 @@ async function streamResponse({
|
|||||||
// find first : index
|
// find first : index
|
||||||
const firstIndex = textAnswer.indexOf(':');
|
const firstIndex = textAnswer.indexOf(':');
|
||||||
textAnswer = textAnswer.substring(firstIndex + 1).trim();
|
textAnswer = textAnswer.substring(firstIndex + 1).trim();
|
||||||
responseWrite({
|
workflowStreamResponse?.({
|
||||||
write,
|
write,
|
||||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
event: SseResponseEventEnum.answer,
|
||||||
data: textAdaptGptResponse({
|
data: textAdaptGptResponse({
|
||||||
text: textAnswer
|
text: textAnswer
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,24 +12,21 @@ import {
|
|||||||
ChatCompletionAssistantMessageParam
|
ChatCompletionAssistantMessageParam
|
||||||
} from '@fastgpt/global/core/ai/type';
|
} from '@fastgpt/global/core/ai/type';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import {
|
import { responseWriteController } from '../../../../../common/response';
|
||||||
responseWrite,
|
|
||||||
responseWriteController,
|
|
||||||
responseWriteNodeStatus
|
|
||||||
} from '../../../../../common/response';
|
|
||||||
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 { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
||||||
import { dispatchWorkFlow } from '../../index';
|
import { dispatchWorkFlow } from '../../index';
|
||||||
import { DispatchToolModuleProps, RunToolResponse, ToolNodeItemType } from './type.d';
|
import { DispatchToolModuleProps, RunToolResponse, ToolNodeItemType } from './type.d';
|
||||||
import json5 from 'json5';
|
import json5 from 'json5';
|
||||||
import { DispatchFlowResponse } from '../../type';
|
import { DispatchFlowResponse, WorkflowResponseType } from '../../type';
|
||||||
import { countGptMessagesTokens } from '../../../../../common/string/tiktoken/index';
|
import { countGptMessagesTokens } from '../../../../../common/string/tiktoken/index';
|
||||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||||
import { AIChatItemType } from '@fastgpt/global/core/chat/type';
|
import { AIChatItemType } from '@fastgpt/global/core/chat/type';
|
||||||
import { updateToolInputValue } from './utils';
|
import { updateToolInputValue } from './utils';
|
||||||
import { computedMaxToken, computedTemperature } from '../../../../ai/utils';
|
import { computedMaxToken, computedTemperature } from '../../../../ai/utils';
|
||||||
import { sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
|
import { sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
|
||||||
|
import { addLog } from '../../../../../common/system/log';
|
||||||
|
|
||||||
type ToolRunResponseType = {
|
type ToolRunResponseType = {
|
||||||
toolRunResponse: DispatchFlowResponse;
|
toolRunResponse: DispatchFlowResponse;
|
||||||
@@ -58,9 +55,9 @@ export const runToolWithToolChoice = async (
|
|||||||
res,
|
res,
|
||||||
requestOrigin,
|
requestOrigin,
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
detail = false,
|
|
||||||
node,
|
node,
|
||||||
stream,
|
stream,
|
||||||
|
workflowStreamResponse,
|
||||||
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
||||||
} = props;
|
} = props;
|
||||||
const assistantResponses = response?.assistantResponses || [];
|
const assistantResponses = response?.assistantResponses || [];
|
||||||
@@ -145,91 +142,91 @@ export const runToolWithToolChoice = async (
|
|||||||
const ai = getAIApi({
|
const ai = getAIApi({
|
||||||
timeout: 480000
|
timeout: 480000
|
||||||
});
|
});
|
||||||
const aiResponse = await ai.chat.completions.create(requestBody, {
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json, text/plain, */*'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const { answer, toolCalls } = await (async () => {
|
try {
|
||||||
if (res && stream) {
|
const aiResponse = await ai.chat.completions.create(requestBody, {
|
||||||
return streamResponse({
|
headers: {
|
||||||
res,
|
Accept: 'application/json, text/plain, */*'
|
||||||
detail,
|
}
|
||||||
toolNodes,
|
});
|
||||||
stream: aiResponse
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const result = aiResponse as ChatCompletion;
|
|
||||||
const calls = result.choices?.[0]?.message?.tool_calls || [];
|
|
||||||
|
|
||||||
// 加上name和avatar
|
const { answer, toolCalls } = await (async () => {
|
||||||
const toolCalls = calls.map((tool) => {
|
if (res && stream) {
|
||||||
const toolNode = toolNodes.find((item) => item.nodeId === tool.function?.name);
|
return streamResponse({
|
||||||
return {
|
res,
|
||||||
...tool,
|
workflowStreamResponse,
|
||||||
toolName: toolNode?.name || '',
|
toolNodes,
|
||||||
toolAvatar: toolNode?.avatar || ''
|
stream: aiResponse
|
||||||
};
|
});
|
||||||
});
|
} else {
|
||||||
|
const result = aiResponse as ChatCompletion;
|
||||||
|
const calls = result.choices?.[0]?.message?.tool_calls || [];
|
||||||
|
|
||||||
return {
|
// 加上name和avatar
|
||||||
answer: result.choices?.[0]?.message?.content || '',
|
const toolCalls = calls.map((tool) => {
|
||||||
toolCalls: toolCalls
|
const toolNode = toolNodes.find((item) => item.nodeId === tool.function?.name);
|
||||||
};
|
return {
|
||||||
}
|
...tool,
|
||||||
})();
|
toolName: toolNode?.name || '',
|
||||||
|
toolAvatar: toolNode?.avatar || ''
|
||||||
// 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 {};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const toolRunResponse = await dispatchWorkFlow({
|
|
||||||
...props,
|
|
||||||
isToolCall: true,
|
|
||||||
runtimeNodes: runtimeNodes.map((item) =>
|
|
||||||
item.nodeId === toolNode.nodeId
|
|
||||||
? {
|
|
||||||
...item,
|
|
||||||
isEntry: true,
|
|
||||||
inputs: updateToolInputValue({ params: startParams, inputs: item.inputs })
|
|
||||||
}
|
|
||||||
: item
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const stringToolResponse = (() => {
|
return {
|
||||||
if (typeof toolRunResponse.toolResponses === 'object') {
|
answer: result.choices?.[0]?.message?.content || '',
|
||||||
return JSON.stringify(toolRunResponse.toolResponses, null, 2);
|
toolCalls: toolCalls
|
||||||
}
|
|
||||||
|
|
||||||
return toolRunResponse.toolResponses ? String(toolRunResponse.toolResponses) : 'none';
|
|
||||||
})();
|
|
||||||
|
|
||||||
const toolMsgParams: ChatCompletionToolMessageParam = {
|
|
||||||
tool_call_id: tool.id,
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Tool,
|
|
||||||
name: tool.function.name,
|
|
||||||
content: stringToolResponse
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
if (stream && detail) {
|
// Run the selected tool by LLM.
|
||||||
responseWrite({
|
const toolsRunResponse = (
|
||||||
res,
|
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 {};
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
const toolRunResponse = await dispatchWorkFlow({
|
||||||
|
...props,
|
||||||
|
isToolCall: true,
|
||||||
|
runtimeNodes: runtimeNodes.map((item) =>
|
||||||
|
item.nodeId === toolNode.nodeId
|
||||||
|
? {
|
||||||
|
...item,
|
||||||
|
isEntry: true,
|
||||||
|
inputs: updateToolInputValue({ params: startParams, inputs: item.inputs })
|
||||||
|
}
|
||||||
|
: item
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
const stringToolResponse = (() => {
|
||||||
|
if (typeof toolRunResponse.toolResponses === 'object') {
|
||||||
|
return JSON.stringify(toolRunResponse.toolResponses, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toolRunResponse.toolResponses ? String(toolRunResponse.toolResponses) : 'none';
|
||||||
|
})();
|
||||||
|
|
||||||
|
const toolMsgParams: ChatCompletionToolMessageParam = {
|
||||||
|
tool_call_id: tool.id,
|
||||||
|
role: ChatCompletionRequestMessageRoleEnum.Tool,
|
||||||
|
name: tool.function.name,
|
||||||
|
content: stringToolResponse
|
||||||
|
};
|
||||||
|
|
||||||
|
workflowStreamResponse?.({
|
||||||
event: SseResponseEventEnum.toolResponse,
|
event: SseResponseEventEnum.toolResponse,
|
||||||
data: JSON.stringify({
|
data: {
|
||||||
tool: {
|
tool: {
|
||||||
id: tool.id,
|
id: tool.id,
|
||||||
toolName: '',
|
toolName: '',
|
||||||
@@ -237,123 +234,132 @@ export const runToolWithToolChoice = async (
|
|||||||
params: '',
|
params: '',
|
||||||
response: sliceStrStartEnd(stringToolResponse, 500, 500)
|
response: sliceStrStartEnd(stringToolResponse, 500, 500)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
toolRunResponse,
|
||||||
|
toolMsgParams
|
||||||
|
};
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).filter(Boolean) as ToolRunResponseType;
|
||||||
|
|
||||||
|
const flatToolsResponseData = toolsRunResponse.map((item) => item.toolRunResponse).flat();
|
||||||
|
if (toolCalls.length > 0 && !res?.closed) {
|
||||||
|
// Run the tool, combine its results, and perform another round of AI calls
|
||||||
|
const assistantToolMsgParams: ChatCompletionAssistantToolParam = {
|
||||||
|
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||||
|
tool_calls: toolCalls
|
||||||
|
};
|
||||||
|
const concatToolMessages = [
|
||||||
|
...requestMessages,
|
||||||
|
assistantToolMsgParams
|
||||||
|
] as ChatCompletionMessageParam[];
|
||||||
|
const tokens = await countGptMessagesTokens(concatToolMessages, tools);
|
||||||
|
const completeMessages = [
|
||||||
|
...concatToolMessages,
|
||||||
|
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
||||||
|
];
|
||||||
|
|
||||||
|
// console.log(tokens, 'tool');
|
||||||
|
|
||||||
|
// Run tool status
|
||||||
|
if (node.showStatus) {
|
||||||
|
workflowStreamResponse?.({
|
||||||
|
event: SseResponseEventEnum.flowNodeStatus,
|
||||||
|
data: {
|
||||||
|
status: 'running',
|
||||||
|
name: node.name
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// tool assistant
|
||||||
|
const toolAssistants = toolsRunResponse
|
||||||
|
.map((item) => {
|
||||||
|
const assistantResponses = item.toolRunResponse.assistantResponses || [];
|
||||||
|
return assistantResponses;
|
||||||
|
})
|
||||||
|
.flat();
|
||||||
|
|
||||||
|
// tool node assistant
|
||||||
|
const adaptChatMessages = GPTMessages2Chats(completeMessages);
|
||||||
|
const toolNodeAssistant = adaptChatMessages.pop() as AIChatItemType;
|
||||||
|
|
||||||
|
const toolNodeAssistants = [
|
||||||
|
...assistantResponses,
|
||||||
|
...toolAssistants,
|
||||||
|
...toolNodeAssistant.value
|
||||||
|
];
|
||||||
|
|
||||||
|
// concat tool responses
|
||||||
|
const dispatchFlowResponse = response
|
||||||
|
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
||||||
|
: flatToolsResponseData;
|
||||||
|
|
||||||
|
/* check stop signal */
|
||||||
|
const hasStopSignal = flatToolsResponseData.some(
|
||||||
|
(item) => !!item.flowResponses?.find((item) => item.toolStop)
|
||||||
|
);
|
||||||
|
if (hasStopSignal) {
|
||||||
return {
|
return {
|
||||||
toolRunResponse,
|
dispatchFlowResponse,
|
||||||
toolMsgParams
|
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||||
|
completeMessages,
|
||||||
|
assistantResponses: toolNodeAssistants
|
||||||
};
|
};
|
||||||
})
|
}
|
||||||
)
|
|
||||||
).filter(Boolean) as ToolRunResponseType;
|
|
||||||
|
|
||||||
const flatToolsResponseData = toolsRunResponse.map((item) => item.toolRunResponse).flat();
|
return runToolWithToolChoice(
|
||||||
if (toolCalls.length > 0 && !res?.closed) {
|
{
|
||||||
// Run the tool, combine its results, and perform another round of AI calls
|
...props,
|
||||||
const assistantToolMsgParams: ChatCompletionAssistantToolParam = {
|
messages: completeMessages
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
},
|
||||||
tool_calls: toolCalls
|
{
|
||||||
};
|
dispatchFlowResponse,
|
||||||
const concatToolMessages = [
|
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||||
...requestMessages,
|
assistantResponses: toolNodeAssistants
|
||||||
assistantToolMsgParams
|
}
|
||||||
] as ChatCompletionMessageParam[];
|
);
|
||||||
const tokens = await countGptMessagesTokens(concatToolMessages, tools);
|
} else {
|
||||||
const completeMessages = [
|
// No tool is invoked, indicating that the process is over
|
||||||
...concatToolMessages,
|
const gptAssistantResponse: ChatCompletionAssistantMessageParam = {
|
||||||
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||||
];
|
content: answer
|
||||||
|
};
|
||||||
|
const completeMessages = filterMessages.concat(gptAssistantResponse);
|
||||||
|
const tokens = await countGptMessagesTokens(completeMessages, tools);
|
||||||
|
// console.log(tokens, 'response token');
|
||||||
|
|
||||||
// console.log(tokens, 'tool');
|
// concat tool assistant
|
||||||
|
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
|
||||||
|
|
||||||
if (stream && detail) {
|
|
||||||
responseWriteNodeStatus({
|
|
||||||
res,
|
|
||||||
name: node.name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// tool assistant
|
|
||||||
const toolAssistants = toolsRunResponse
|
|
||||||
.map((item) => {
|
|
||||||
const assistantResponses = item.toolRunResponse.assistantResponses || [];
|
|
||||||
return assistantResponses;
|
|
||||||
})
|
|
||||||
.flat();
|
|
||||||
|
|
||||||
// tool node assistant
|
|
||||||
const adaptChatMessages = GPTMessages2Chats(completeMessages);
|
|
||||||
const toolNodeAssistant = adaptChatMessages.pop() as AIChatItemType;
|
|
||||||
|
|
||||||
const toolNodeAssistants = [
|
|
||||||
...assistantResponses,
|
|
||||||
...toolAssistants,
|
|
||||||
...toolNodeAssistant.value
|
|
||||||
];
|
|
||||||
|
|
||||||
// concat tool responses
|
|
||||||
const dispatchFlowResponse = response
|
|
||||||
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
|
||||||
: flatToolsResponseData;
|
|
||||||
|
|
||||||
/* check stop signal */
|
|
||||||
const hasStopSignal = flatToolsResponseData.some(
|
|
||||||
(item) => !!item.flowResponses?.find((item) => item.toolStop)
|
|
||||||
);
|
|
||||||
if (hasStopSignal) {
|
|
||||||
return {
|
return {
|
||||||
dispatchFlowResponse,
|
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
||||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||||
completeMessages,
|
completeMessages,
|
||||||
assistantResponses: toolNodeAssistants
|
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
return runToolWithToolChoice(
|
addLog.warn(`LLM response error`, {
|
||||||
{
|
requestBody
|
||||||
...props,
|
});
|
||||||
messages: completeMessages
|
return Promise.reject(error);
|
||||||
},
|
|
||||||
{
|
|
||||||
dispatchFlowResponse,
|
|
||||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
|
||||||
assistantResponses: toolNodeAssistants
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} 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);
|
|
||||||
// console.log(tokens, 'response token');
|
|
||||||
|
|
||||||
// concat tool assistant
|
|
||||||
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
|
|
||||||
|
|
||||||
return {
|
|
||||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
|
||||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
|
||||||
completeMessages,
|
|
||||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value]
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async function streamResponse({
|
async function streamResponse({
|
||||||
res,
|
res,
|
||||||
detail,
|
|
||||||
toolNodes,
|
toolNodes,
|
||||||
stream
|
stream,
|
||||||
|
workflowStreamResponse
|
||||||
}: {
|
}: {
|
||||||
res: NextApiResponse;
|
res: NextApiResponse;
|
||||||
detail: boolean;
|
|
||||||
toolNodes: ToolNodeItemType[];
|
toolNodes: ToolNodeItemType[];
|
||||||
stream: StreamChatType;
|
stream: StreamChatType;
|
||||||
|
workflowStreamResponse?: WorkflowResponseType;
|
||||||
}) {
|
}) {
|
||||||
const write = responseWriteController({
|
const write = responseWriteController({
|
||||||
res,
|
res,
|
||||||
@@ -375,9 +381,9 @@ async function streamResponse({
|
|||||||
const content = responseChoice.content || '';
|
const content = responseChoice.content || '';
|
||||||
textAnswer += content;
|
textAnswer += content;
|
||||||
|
|
||||||
responseWrite({
|
workflowStreamResponse?.({
|
||||||
write,
|
write,
|
||||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
event: SseResponseEventEnum.answer,
|
||||||
data: textAdaptGptResponse({
|
data: textAdaptGptResponse({
|
||||||
text: content
|
text: content
|
||||||
})
|
})
|
||||||
@@ -405,22 +411,19 @@ async function streamResponse({
|
|||||||
toolAvatar: toolNode.avatar
|
toolAvatar: toolNode.avatar
|
||||||
});
|
});
|
||||||
|
|
||||||
if (detail) {
|
workflowStreamResponse?.({
|
||||||
responseWrite({
|
event: SseResponseEventEnum.toolCall,
|
||||||
write,
|
data: {
|
||||||
event: SseResponseEventEnum.toolCall,
|
tool: {
|
||||||
data: JSON.stringify({
|
id: toolCall.id,
|
||||||
tool: {
|
toolName: toolNode.name,
|
||||||
id: toolCall.id,
|
toolAvatar: toolNode.avatar,
|
||||||
toolName: toolNode.name,
|
functionName: toolCall.function.name,
|
||||||
toolAvatar: toolNode.avatar,
|
params: toolCall.function.arguments,
|
||||||
functionName: toolCall.function.name,
|
response: ''
|
||||||
params: toolCall.function.arguments,
|
}
|
||||||
response: ''
|
}
|
||||||
}
|
});
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -431,27 +434,25 @@ async function streamResponse({
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* arg 插入最后一个工具的参数里 */
|
/* arg 插入最后一个工具的参数里 */
|
||||||
const arg: string = toolCall?.function?.arguments;
|
const arg: string = toolCall?.function?.arguments ?? '';
|
||||||
const currentTool = toolCalls[toolCalls.length - 1];
|
const currentTool = toolCalls[toolCalls.length - 1];
|
||||||
|
|
||||||
if (currentTool) {
|
if (currentTool) {
|
||||||
currentTool.function.arguments += arg;
|
currentTool.function.arguments += arg;
|
||||||
|
|
||||||
if (detail) {
|
workflowStreamResponse?.({
|
||||||
responseWrite({
|
write,
|
||||||
write,
|
event: SseResponseEventEnum.toolParams,
|
||||||
event: SseResponseEventEnum.toolParams,
|
data: {
|
||||||
data: JSON.stringify({
|
tool: {
|
||||||
tool: {
|
id: currentTool.id,
|
||||||
id: currentTool.id,
|
toolName: '',
|
||||||
toolName: '',
|
toolAvatar: '',
|
||||||
toolAvatar: '',
|
params: arg,
|
||||||
params: arg,
|
response: ''
|
||||||
response: ''
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import {
|
|||||||
import type { AIChatNodeProps } from '@fastgpt/global/core/workflow/runtime/type.d';
|
import type { AIChatNodeProps } from '@fastgpt/global/core/workflow/runtime/type.d';
|
||||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { responseWrite, responseWriteController } from '../../../../common/response';
|
import { responseWriteController } from '../../../../common/response';
|
||||||
import { getLLMModel, ModelTypeEnum } from '../../../ai/model';
|
import { getLLMModel, ModelTypeEnum } from '../../../ai/model';
|
||||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
@@ -41,6 +41,7 @@ import { filterSearchResultsByMaxChars } from '../../utils';
|
|||||||
import { getHistoryPreview } from '@fastgpt/global/core/chat/utils';
|
import { getHistoryPreview } from '@fastgpt/global/core/chat/utils';
|
||||||
import { addLog } from '../../../../common/system/log';
|
import { addLog } from '../../../../common/system/log';
|
||||||
import { computedMaxToken, computedTemperature } from '../../../ai/utils';
|
import { computedMaxToken, computedTemperature } from '../../../ai/utils';
|
||||||
|
import { WorkflowResponseType } from '../type';
|
||||||
|
|
||||||
export type ChatProps = ModuleDispatchProps<
|
export type ChatProps = ModuleDispatchProps<
|
||||||
AIChatNodeProps & {
|
AIChatNodeProps & {
|
||||||
@@ -60,11 +61,11 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
res,
|
res,
|
||||||
requestOrigin,
|
requestOrigin,
|
||||||
stream = false,
|
stream = false,
|
||||||
detail = false,
|
|
||||||
user,
|
user,
|
||||||
histories,
|
histories,
|
||||||
node: { name },
|
node: { name },
|
||||||
query,
|
query,
|
||||||
|
workflowStreamResponse,
|
||||||
params: {
|
params: {
|
||||||
model,
|
model,
|
||||||
temperature = 0,
|
temperature = 0,
|
||||||
@@ -179,8 +180,8 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
// sse response
|
// sse response
|
||||||
const { answer } = await streamResponse({
|
const { answer } = await streamResponse({
|
||||||
res,
|
res,
|
||||||
detail,
|
stream: response,
|
||||||
stream: response
|
workflowStreamResponse
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!answer) {
|
if (!answer) {
|
||||||
@@ -340,12 +341,12 @@ async function getChatMessages({
|
|||||||
|
|
||||||
async function streamResponse({
|
async function streamResponse({
|
||||||
res,
|
res,
|
||||||
detail,
|
stream,
|
||||||
stream
|
workflowStreamResponse
|
||||||
}: {
|
}: {
|
||||||
res: NextApiResponse;
|
res: NextApiResponse;
|
||||||
detail: boolean;
|
|
||||||
stream: StreamChatType;
|
stream: StreamChatType;
|
||||||
|
workflowStreamResponse?: WorkflowResponseType;
|
||||||
}) {
|
}) {
|
||||||
const write = responseWriteController({
|
const write = responseWriteController({
|
||||||
res,
|
res,
|
||||||
@@ -360,9 +361,9 @@ async function streamResponse({
|
|||||||
const content = part.choices?.[0]?.delta?.content || '';
|
const content = part.choices?.[0]?.delta?.content || '';
|
||||||
answer += content;
|
answer += content;
|
||||||
|
|
||||||
responseWrite({
|
workflowStreamResponse?.({
|
||||||
write,
|
write,
|
||||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
event: SseResponseEventEnum.answer,
|
||||||
data: textAdaptGptResponse({
|
data: textAdaptGptResponse({
|
||||||
text: content
|
text: content
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { NextApiResponse } from 'next';
|
|
||||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import {
|
import {
|
||||||
DispatchNodeResponseKeyEnum,
|
DispatchNodeResponseKeyEnum,
|
||||||
@@ -20,10 +19,9 @@ import {
|
|||||||
FlowNodeInputTypeEnum,
|
FlowNodeInputTypeEnum,
|
||||||
FlowNodeTypeEnum
|
FlowNodeTypeEnum
|
||||||
} from '@fastgpt/global/core/workflow/node/constant';
|
} from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
import { getNanoid, replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||||
import { responseWrite, responseWriteNodeStatus } from '../../../common/response';
|
|
||||||
import { getSystemTime } from '@fastgpt/global/common/time/timezone';
|
import { getSystemTime } from '@fastgpt/global/common/time/timezone';
|
||||||
import { replaceVariableLabel } from '@fastgpt/global/core/workflow/utils';
|
import { replaceEditorVariable } from '@fastgpt/global/core/workflow/utils';
|
||||||
|
|
||||||
import { dispatchWorkflowStart } from './init/workflowStart';
|
import { dispatchWorkflowStart } from './init/workflowStart';
|
||||||
import { dispatchChatCompletion } from './chat/oneapi';
|
import { dispatchChatCompletion } from './chat/oneapi';
|
||||||
@@ -33,7 +31,7 @@ import { dispatchAnswer } from './tools/answer';
|
|||||||
import { dispatchClassifyQuestion } from './agent/classifyQuestion';
|
import { dispatchClassifyQuestion } from './agent/classifyQuestion';
|
||||||
import { dispatchContentExtract } from './agent/extract';
|
import { dispatchContentExtract } from './agent/extract';
|
||||||
import { dispatchHttp468Request } from './tools/http468';
|
import { dispatchHttp468Request } from './tools/http468';
|
||||||
import { dispatchAppRequest } from './tools/runApp';
|
import { dispatchAppRequest } from './abandoned/runApp';
|
||||||
import { dispatchQueryExtension } from './tools/queryExternsion';
|
import { dispatchQueryExtension } from './tools/queryExternsion';
|
||||||
import { dispatchRunPlugin } from './plugin/run';
|
import { dispatchRunPlugin } from './plugin/run';
|
||||||
import { dispatchPluginInput } from './plugin/runInput';
|
import { dispatchPluginInput } from './plugin/runInput';
|
||||||
@@ -41,8 +39,7 @@ import { dispatchPluginOutput } from './plugin/runOutput';
|
|||||||
import { removeSystemVariable, valueTypeFormat } from './utils';
|
import { removeSystemVariable, valueTypeFormat } from './utils';
|
||||||
import {
|
import {
|
||||||
filterWorkflowEdges,
|
filterWorkflowEdges,
|
||||||
checkNodeRunStatus,
|
checkNodeRunStatus
|
||||||
getLastInteractiveValue
|
|
||||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||||
import { dispatchRunTools } from './agent/runTool/index';
|
import { dispatchRunTools } from './agent/runTool/index';
|
||||||
@@ -62,12 +59,11 @@ import { dispatchTextEditor } from './tools/textEditor';
|
|||||||
import { dispatchCustomFeedback } from './tools/customFeedback';
|
import { dispatchCustomFeedback } from './tools/customFeedback';
|
||||||
import { dispatchReadFiles } from './tools/readFiles';
|
import { dispatchReadFiles } from './tools/readFiles';
|
||||||
import { dispatchUserSelect } from './interactive/userSelect';
|
import { dispatchUserSelect } from './interactive/userSelect';
|
||||||
import { FlowNodeOutputItemType } from '@fastgpt/global/core/workflow/type/io';
|
|
||||||
import {
|
import {
|
||||||
InteractiveNodeResponseItemType,
|
InteractiveNodeResponseItemType,
|
||||||
UserInteractiveType,
|
|
||||||
UserSelectInteractive
|
UserSelectInteractive
|
||||||
} from '@fastgpt/global/core/workflow/template/system/userSelect/type';
|
} from '@fastgpt/global/core/workflow/template/system/userSelect/type';
|
||||||
|
import { dispatchRunAppNode } from './agent/runApp';
|
||||||
|
|
||||||
const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
||||||
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
|
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
|
||||||
@@ -78,7 +74,7 @@ const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
|||||||
[FlowNodeTypeEnum.classifyQuestion]: dispatchClassifyQuestion,
|
[FlowNodeTypeEnum.classifyQuestion]: dispatchClassifyQuestion,
|
||||||
[FlowNodeTypeEnum.contentExtract]: dispatchContentExtract,
|
[FlowNodeTypeEnum.contentExtract]: dispatchContentExtract,
|
||||||
[FlowNodeTypeEnum.httpRequest468]: dispatchHttp468Request,
|
[FlowNodeTypeEnum.httpRequest468]: dispatchHttp468Request,
|
||||||
[FlowNodeTypeEnum.runApp]: dispatchAppRequest,
|
[FlowNodeTypeEnum.appModule]: dispatchRunAppNode,
|
||||||
[FlowNodeTypeEnum.pluginModule]: dispatchRunPlugin,
|
[FlowNodeTypeEnum.pluginModule]: dispatchRunPlugin,
|
||||||
[FlowNodeTypeEnum.pluginInput]: dispatchPluginInput,
|
[FlowNodeTypeEnum.pluginInput]: dispatchPluginInput,
|
||||||
[FlowNodeTypeEnum.pluginOutput]: dispatchPluginOutput,
|
[FlowNodeTypeEnum.pluginOutput]: dispatchPluginOutput,
|
||||||
@@ -96,8 +92,11 @@ const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
|||||||
|
|
||||||
// none
|
// none
|
||||||
[FlowNodeTypeEnum.systemConfig]: dispatchSystemConfig,
|
[FlowNodeTypeEnum.systemConfig]: dispatchSystemConfig,
|
||||||
|
[FlowNodeTypeEnum.pluginConfig]: () => Promise.resolve(),
|
||||||
[FlowNodeTypeEnum.emptyNode]: () => Promise.resolve(),
|
[FlowNodeTypeEnum.emptyNode]: () => Promise.resolve(),
|
||||||
[FlowNodeTypeEnum.globalVariable]: () => Promise.resolve()
|
[FlowNodeTypeEnum.globalVariable]: () => Promise.resolve(),
|
||||||
|
|
||||||
|
[FlowNodeTypeEnum.runApp]: dispatchAppRequest // abandoned
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = ChatDispatchProps & {
|
type Props = ChatDispatchProps & {
|
||||||
@@ -115,7 +114,6 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
variables = {},
|
variables = {},
|
||||||
user,
|
user,
|
||||||
stream = false,
|
stream = false,
|
||||||
detail = false,
|
|
||||||
...props
|
...props
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
@@ -261,13 +259,10 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
nodeOutputs
|
nodeOutputs
|
||||||
};
|
};
|
||||||
|
|
||||||
if (stream && res) {
|
props.workflowStreamResponse?.({
|
||||||
responseWrite({
|
event: SseResponseEventEnum.interactive,
|
||||||
res,
|
data: { interactive: interactiveResult }
|
||||||
event: SseResponseEventEnum.interactive,
|
});
|
||||||
data: JSON.stringify({ interactive: interactiveResult })
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: ChatItemValueTypeEnum.interactive,
|
type: ChatItemValueTypeEnum.interactive,
|
||||||
@@ -317,8 +312,10 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
|
|
||||||
const flat = result.flat().filter(Boolean) as unknown as {
|
const flat = result.flat().filter(Boolean) as unknown as {
|
||||||
node: RuntimeNodeItemType;
|
node: RuntimeNodeItemType;
|
||||||
|
runStatus: 'run' | 'skip';
|
||||||
result: Record<string, any>;
|
result: Record<string, any>;
|
||||||
}[];
|
}[];
|
||||||
|
// If there are no running nodes, the workflow is complete
|
||||||
if (flat.length === 0) return;
|
if (flat.length === 0) return;
|
||||||
|
|
||||||
// Update the node output at the end of the run and get the next nodes
|
// Update the node output at the end of the run and get the next nodes
|
||||||
@@ -375,7 +372,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
let value = replaceVariable(input.value, variables);
|
let value = replaceVariable(input.value, variables);
|
||||||
|
|
||||||
// replace {{$xx.xx$}} variables
|
// replace {{$xx.xx$}} variables
|
||||||
value = replaceVariableLabel({
|
value = replaceEditorVariable({
|
||||||
text: value,
|
text: value,
|
||||||
nodes: runtimeNodes,
|
nodes: runtimeNodes,
|
||||||
variables,
|
variables,
|
||||||
@@ -401,11 +398,13 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
}
|
}
|
||||||
async function nodeRunWithActive(node: RuntimeNodeItemType) {
|
async function nodeRunWithActive(node: RuntimeNodeItemType) {
|
||||||
// push run status messages
|
// push run status messages
|
||||||
if (res && stream && detail && node.showStatus) {
|
if (node.showStatus) {
|
||||||
responseStatus({
|
props.workflowStreamResponse?.({
|
||||||
res,
|
event: SseResponseEventEnum.flowNodeStatus,
|
||||||
name: node.name,
|
data: {
|
||||||
status: 'running'
|
status: 'running',
|
||||||
|
name: node.name
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
@@ -420,7 +419,6 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
histories,
|
histories,
|
||||||
user,
|
user,
|
||||||
stream,
|
stream,
|
||||||
detail,
|
|
||||||
node,
|
node,
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
runtimeEdges,
|
runtimeEdges,
|
||||||
@@ -440,6 +438,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
const formatResponseData: ChatHistoryItemResType = (() => {
|
const formatResponseData: ChatHistoryItemResType = (() => {
|
||||||
if (!dispatchRes[DispatchNodeResponseKeyEnum.nodeResponse]) return undefined;
|
if (!dispatchRes[DispatchNodeResponseKeyEnum.nodeResponse]) return undefined;
|
||||||
return {
|
return {
|
||||||
|
id: getNanoid(),
|
||||||
nodeId: node.nodeId,
|
nodeId: node.nodeId,
|
||||||
moduleName: node.name,
|
moduleName: node.name,
|
||||||
moduleType: node.flowNodeType,
|
moduleType: node.flowNodeType,
|
||||||
@@ -459,6 +458,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
node,
|
node,
|
||||||
|
runStatus: 'run',
|
||||||
result: {
|
result: {
|
||||||
...dispatchRes,
|
...dispatchRes,
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: formatResponseData
|
[DispatchNodeResponseKeyEnum.nodeResponse]: formatResponseData
|
||||||
@@ -472,6 +472,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
node,
|
node,
|
||||||
|
runStatus: 'skip',
|
||||||
result: {
|
result: {
|
||||||
[DispatchNodeResponseKeyEnum.skipHandleId]: targetEdges.map((item) => item.sourceHandle)
|
[DispatchNodeResponseKeyEnum.skipHandleId]: targetEdges.map((item) => item.sourceHandle)
|
||||||
}
|
}
|
||||||
@@ -510,23 +511,6 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sse response modules staus */
|
|
||||||
export function responseStatus({
|
|
||||||
res,
|
|
||||||
status,
|
|
||||||
name
|
|
||||||
}: {
|
|
||||||
res: NextApiResponse;
|
|
||||||
status?: 'running' | 'finish';
|
|
||||||
name?: string;
|
|
||||||
}) {
|
|
||||||
if (!name) return;
|
|
||||||
responseWriteNodeStatus({
|
|
||||||
res,
|
|
||||||
name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get system variable */
|
/* get system variable */
|
||||||
export function getSystemVariable({
|
export function getSystemVariable({
|
||||||
user,
|
user,
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
|
import type {
|
||||||
|
DispatchNodeResultType,
|
||||||
|
ModuleDispatchProps
|
||||||
|
} from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
|
|
||||||
export type UserChatInputProps = ModuleDispatchProps<{
|
export type UserChatInputProps = ModuleDispatchProps<{
|
||||||
[NodeInputKeyEnum.userChatInput]: string;
|
[NodeInputKeyEnum.userChatInput]: string;
|
||||||
}>;
|
}>;
|
||||||
type Response = {
|
type Response = DispatchNodeResultType<{
|
||||||
[NodeOutputKeyEnum.userChatInput]: string;
|
[NodeOutputKeyEnum.userChatInput]: string;
|
||||||
[NodeOutputKeyEnum.userFiles]: string[];
|
[NodeOutputKeyEnum.userFiles]: string[];
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export const dispatchWorkflowStart = (props: Record<string, any>): Response => {
|
export const dispatchWorkflowStart = (props: Record<string, any>): Response => {
|
||||||
const {
|
const {
|
||||||
@@ -19,6 +23,7 @@ export const dispatchWorkflowStart = (props: Record<string, any>): Response => {
|
|||||||
const { text, files } = chatValue2RuntimePrompt(query);
|
const { text, files } = chatValue2RuntimePrompt(query);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
[DispatchNodeResponseKeyEnum.nodeResponse]: {},
|
||||||
[NodeInputKeyEnum.userChatInput]: text || userChatInput,
|
[NodeInputKeyEnum.userChatInput]: text || userChatInput,
|
||||||
[NodeOutputKeyEnum.userFiles]: files
|
[NodeOutputKeyEnum.userFiles]: files
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import type {
|
|||||||
} from '@fastgpt/global/core/workflow/template/system/userSelect/type';
|
} from '@fastgpt/global/core/workflow/template/system/userSelect/type';
|
||||||
import { updateUserSelectedResult } from '../../../chat/controller';
|
import { updateUserSelectedResult } from '../../../chat/controller';
|
||||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { responseWrite } from '../../../../common/response';
|
|
||||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||||
|
|
||||||
type Props = ModuleDispatchProps<{
|
type Props = ModuleDispatchProps<{
|
||||||
@@ -29,10 +28,7 @@ type UserSelectResponse = DispatchNodeResultType<{
|
|||||||
|
|
||||||
export const dispatchUserSelect = async (props: Props): Promise<UserSelectResponse> => {
|
export const dispatchUserSelect = async (props: Props): Promise<UserSelectResponse> => {
|
||||||
const {
|
const {
|
||||||
res,
|
workflowStreamResponse,
|
||||||
detail,
|
|
||||||
histories,
|
|
||||||
stream,
|
|
||||||
app: { _id: appId },
|
app: { _id: appId },
|
||||||
chatId,
|
chatId,
|
||||||
node: { nodeId, isEntry },
|
node: { nodeId, isEntry },
|
||||||
@@ -43,10 +39,9 @@ export const dispatchUserSelect = async (props: Props): Promise<UserSelectRespon
|
|||||||
// Interactive node is not the entry node, return interactive result
|
// Interactive node is not the entry node, return interactive result
|
||||||
if (!isEntry) {
|
if (!isEntry) {
|
||||||
const answerText = description ? `\n${description}` : undefined;
|
const answerText = description ? `\n${description}` : undefined;
|
||||||
if (res && stream && answerText) {
|
if (answerText) {
|
||||||
responseWrite({
|
workflowStreamResponse?.({
|
||||||
res,
|
event: SseResponseEventEnum.fastAnswer,
|
||||||
event: detail ? SseResponseEventEnum.fastAnswer : undefined,
|
|
||||||
data: textAdaptGptResponse({
|
data: textAdaptGptResponse({
|
||||||
text: answerText
|
text: answerText
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import {
|
|||||||
storeNodes2RuntimeNodes
|
storeNodes2RuntimeNodes
|
||||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { updateToolInputValue } from '../agent/runTool/utils';
|
|
||||||
import { authPluginByTmbId } from '../../../../support/permission/app/auth';
|
import { authPluginByTmbId } from '../../../../support/permission/app/auth';
|
||||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||||
import { computedPluginUsage } from '../../../app/plugin/utils';
|
import { computedPluginUsage } from '../../../app/plugin/utils';
|
||||||
|
import { filterSystemVariables } from '../utils';
|
||||||
|
|
||||||
type RunPluginProps = ModuleDispatchProps<{
|
type RunPluginProps = ModuleDispatchProps<{
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
@@ -25,7 +25,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
|||||||
app: workflowApp,
|
app: workflowApp,
|
||||||
mode,
|
mode,
|
||||||
teamId,
|
teamId,
|
||||||
params: data
|
params: data // Plugin input
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
if (!pluginId) {
|
if (!pluginId) {
|
||||||
@@ -41,32 +41,31 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
|||||||
|
|
||||||
const plugin = await getPluginRuntimeById(pluginId);
|
const plugin = await getPluginRuntimeById(pluginId);
|
||||||
|
|
||||||
// concat dynamic inputs
|
const runtimeNodes = storeNodes2RuntimeNodes(
|
||||||
const inputModule = plugin.nodes.find(
|
plugin.nodes,
|
||||||
(item) => item.flowNodeType === FlowNodeTypeEnum.pluginInput
|
getWorkflowEntryNodeIds(plugin.nodes)
|
||||||
);
|
).map((node) => {
|
||||||
if (!inputModule) return Promise.reject('Plugin error, It has no set input.');
|
// Update plugin input value
|
||||||
|
if (node.flowNodeType === FlowNodeTypeEnum.pluginInput) {
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
showStatus: false,
|
||||||
|
inputs: node.inputs.map((input) => ({
|
||||||
|
...input,
|
||||||
|
value: data[input.key] ?? input.value
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
showStatus: false
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
|
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
|
||||||
...props,
|
...props,
|
||||||
runtimeNodes: storeNodes2RuntimeNodes(plugin.nodes, getWorkflowEntryNodeIds(plugin.nodes)).map(
|
variables: filterSystemVariables(props.variables),
|
||||||
(node) => {
|
runtimeNodes,
|
||||||
if (node.flowNodeType === FlowNodeTypeEnum.pluginInput) {
|
|
||||||
return {
|
|
||||||
...node,
|
|
||||||
showStatus: false,
|
|
||||||
inputs: updateToolInputValue({
|
|
||||||
inputs: node.inputs,
|
|
||||||
params: data
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...node,
|
|
||||||
showStatus: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
),
|
|
||||||
runtimeEdges: initWorkflowEdgeStatus(plugin.edges)
|
runtimeEdges: initWorkflowEdgeStatus(plugin.edges)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
DispatchNodeResponseKeyEnum,
|
DispatchNodeResponseKeyEnum,
|
||||||
SseResponseEventEnum
|
SseResponseEventEnum
|
||||||
} from '@fastgpt/global/core/workflow/runtime/constants';
|
} from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import { responseWrite } from '../../../../common/response';
|
|
||||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
@@ -16,24 +15,19 @@ export type AnswerResponse = DispatchNodeResultType<{
|
|||||||
|
|
||||||
export const dispatchAnswer = (props: Record<string, any>): AnswerResponse => {
|
export const dispatchAnswer = (props: Record<string, any>): AnswerResponse => {
|
||||||
const {
|
const {
|
||||||
res,
|
workflowStreamResponse,
|
||||||
detail,
|
|
||||||
stream,
|
|
||||||
params: { text = '' }
|
params: { text = '' }
|
||||||
} = props as AnswerProps;
|
} = props as AnswerProps;
|
||||||
|
|
||||||
const formatText = typeof text === 'string' ? text : JSON.stringify(text, null, 2);
|
const formatText = typeof text === 'string' ? text : JSON.stringify(text, null, 2);
|
||||||
const responseText = `\n${formatText}`;
|
const responseText = `\n${formatText}`;
|
||||||
|
|
||||||
if (res && stream) {
|
workflowStreamResponse?.({
|
||||||
responseWrite({
|
event: SseResponseEventEnum.fastAnswer,
|
||||||
res,
|
data: textAdaptGptResponse({
|
||||||
event: detail ? SseResponseEventEnum.fastAnswer : undefined,
|
text: responseText
|
||||||
data: textAdaptGptResponse({
|
})
|
||||||
text: responseText
|
});
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[NodeOutputKeyEnum.answerText]: responseText,
|
[NodeOutputKeyEnum.answerText]: responseText,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/
|
|||||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { addCustomFeedbacks } from '../../../chat/controller';
|
import { addCustomFeedbacks } from '../../../chat/controller';
|
||||||
import { responseWrite } from '../../../../common/response';
|
|
||||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
|
|
||||||
type Props = ModuleDispatchProps<{
|
type Props = ModuleDispatchProps<{
|
||||||
@@ -16,12 +15,11 @@ type Response = DispatchNodeResultType<{}>;
|
|||||||
|
|
||||||
export const dispatchCustomFeedback = (props: Record<string, any>): Response => {
|
export const dispatchCustomFeedback = (props: Record<string, any>): Response => {
|
||||||
const {
|
const {
|
||||||
res,
|
|
||||||
app: { _id: appId },
|
app: { _id: appId },
|
||||||
chatId,
|
chatId,
|
||||||
responseChatItemId: chatItemId,
|
responseChatItemId: chatItemId,
|
||||||
stream,
|
stream,
|
||||||
detail,
|
workflowStreamResponse,
|
||||||
params: { system_textareaInput: feedbackText = '' }
|
params: { system_textareaInput: feedbackText = '' }
|
||||||
} = props as Props;
|
} = props as Props;
|
||||||
|
|
||||||
@@ -36,9 +34,8 @@ export const dispatchCustomFeedback = (props: Record<string, any>): Response =>
|
|||||||
|
|
||||||
if (stream) {
|
if (stream) {
|
||||||
if (!chatId || !chatItemId) {
|
if (!chatId || !chatItemId) {
|
||||||
responseWrite({
|
workflowStreamResponse?.({
|
||||||
res,
|
event: SseResponseEventEnum.fastAnswer,
|
||||||
event: detail ? SseResponseEventEnum.fastAnswer : undefined,
|
|
||||||
data: textAdaptGptResponse({
|
data: textAdaptGptResponse({
|
||||||
text: `\n\n**自定义反馈成功: (仅调试模式下展示该内容)**: "${feedbackText}"\n\n`
|
text: `\n\n**自定义反馈成功: (仅调试模式下展示该内容)**: "${feedbackText}"\n\n`
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { SERVICE_LOCAL_HOST } from '../../../../common/system/tools';
|
|||||||
import { addLog } from '../../../../common/system/log';
|
import { addLog } from '../../../../common/system/log';
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
import { responseWrite } from '../../../../common/response';
|
|
||||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { getSystemPluginCb } from '../../../../../plugins/register';
|
import { getSystemPluginCb } from '../../../../../plugins/register';
|
||||||
|
|
||||||
@@ -43,15 +42,13 @@ const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
|
|||||||
|
|
||||||
export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<HttpResponse> => {
|
export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<HttpResponse> => {
|
||||||
let {
|
let {
|
||||||
res,
|
|
||||||
detail,
|
|
||||||
app: { _id: appId },
|
app: { _id: appId },
|
||||||
chatId,
|
chatId,
|
||||||
stream,
|
|
||||||
responseChatItemId,
|
responseChatItemId,
|
||||||
variables,
|
variables,
|
||||||
node: { outputs },
|
node: { outputs },
|
||||||
histories,
|
histories,
|
||||||
|
workflowStreamResponse,
|
||||||
params: {
|
params: {
|
||||||
system_httpMethod: httpMethod = 'POST',
|
system_httpMethod: httpMethod = 'POST',
|
||||||
system_httpReqUrl: httpReqUrl,
|
system_httpReqUrl: httpReqUrl,
|
||||||
@@ -158,10 +155,9 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
|||||||
results[key] = valueTypeFormat(formatResponse[key], output.valueType);
|
results[key] = valueTypeFormat(formatResponse[key], output.valueType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stream && typeof formatResponse[NodeOutputKeyEnum.answerText] === 'string') {
|
if (typeof formatResponse[NodeOutputKeyEnum.answerText] === 'string') {
|
||||||
responseWrite({
|
workflowStreamResponse?.({
|
||||||
res,
|
event: SseResponseEventEnum.fastAnswer,
|
||||||
event: detail ? SseResponseEventEnum.fastAnswer : undefined,
|
|
||||||
data: textAdaptGptResponse({
|
data: textAdaptGptResponse({
|
||||||
text: formatResponse[NodeOutputKeyEnum.answerText]
|
text: formatResponse[NodeOutputKeyEnum.answerText]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -73,25 +73,35 @@ export const dispatchReadFiles = async (props: Props): Promise<Response> => {
|
|||||||
// Concat fileUrlList and filesFromHistories; remove not supported files
|
// Concat fileUrlList and filesFromHistories; remove not supported files
|
||||||
const parseUrlList = [...fileUrlList, ...filesFromHistories]
|
const parseUrlList = [...fileUrlList, ...filesFromHistories]
|
||||||
.map((url) => {
|
.map((url) => {
|
||||||
// System file
|
try {
|
||||||
if (url.startsWith('/') || (requestOrigin && url.startsWith(requestOrigin))) {
|
// Avoid "/api/xxx" file error.
|
||||||
// Parse url, get filename query. Keep only documents that can be parsed
|
const origin = requestOrigin ?? 'http://localhost:3000';
|
||||||
const parseUrl = new URL(url);
|
|
||||||
const filenameQuery = parseUrl.searchParams.get('filename');
|
// Check is system upload file
|
||||||
if (filenameQuery) {
|
if (url.startsWith('/') || (requestOrigin && url.startsWith(requestOrigin))) {
|
||||||
const extensionQuery = filenameQuery.split('.').pop()?.toLowerCase() || '';
|
// Parse url, get filename query. Keep only documents that can be parsed
|
||||||
if (!documentFileType.includes(extensionQuery)) {
|
const parseUrl = new URL(url, origin);
|
||||||
return '';
|
const filenameQuery = parseUrl.searchParams.get('filename');
|
||||||
|
|
||||||
|
// Not document
|
||||||
|
if (filenameQuery) {
|
||||||
|
const extensionQuery = filenameQuery.split('.').pop()?.toLowerCase() || '';
|
||||||
|
if (!documentFileType.includes(extensionQuery)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the origin(Make intranet requests directly)
|
||||||
|
if (requestOrigin && url.startsWith(requestOrigin)) {
|
||||||
|
url = url.replace(requestOrigin, '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the origin(Make intranet requests directly)
|
return url;
|
||||||
if (requestOrigin && url.startsWith(requestOrigin)) {
|
} catch (error) {
|
||||||
url = url.replace(requestOrigin, '');
|
console.log(error);
|
||||||
}
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return url;
|
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.slice(0, maxFiles);
|
.slice(0, maxFiles);
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { getElseIFLabel, getHandleId } from '@fastgpt/global/core/workflow/utils';
|
import { getElseIFLabel, getHandleId } from '@fastgpt/global/core/workflow/utils';
|
||||||
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
|
|
||||||
|
|
||||||
type Props = ModuleDispatchProps<{
|
type Props = ModuleDispatchProps<{
|
||||||
[NodeInputKeyEnum.condition]: IfElseConditionType;
|
[NodeInputKeyEnum.condition]: IfElseConditionType;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime
|
|||||||
import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type';
|
import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type';
|
||||||
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { removeSystemVariable, valueTypeFormat } from '../utils';
|
import { removeSystemVariable, valueTypeFormat } from '../utils';
|
||||||
import { responseWrite } from '../../../../common/response';
|
import { replaceEditorVariable } from '@fastgpt/global/core/workflow/utils';
|
||||||
|
|
||||||
type Props = ModuleDispatchProps<{
|
type Props = ModuleDispatchProps<{
|
||||||
[NodeInputKeyEnum.updateList]: TUpdateListItem[];
|
[NodeInputKeyEnum.updateList]: TUpdateListItem[];
|
||||||
@@ -16,20 +16,29 @@ type Props = ModuleDispatchProps<{
|
|||||||
type Response = DispatchNodeResultType<{}>;
|
type Response = DispatchNodeResultType<{}>;
|
||||||
|
|
||||||
export const dispatchUpdateVariable = async (props: Props): Promise<Response> => {
|
export const dispatchUpdateVariable = async (props: Props): Promise<Response> => {
|
||||||
const { res, detail, stream, params, variables, runtimeNodes } = props;
|
const { params, variables, runtimeNodes, workflowStreamResponse, node } = props;
|
||||||
|
|
||||||
const { updateList } = params;
|
const { updateList } = params;
|
||||||
updateList.forEach((item) => {
|
const result = updateList.map((item) => {
|
||||||
const varNodeId = item.variable?.[0];
|
const varNodeId = item.variable?.[0];
|
||||||
const varKey = item.variable?.[1];
|
const varKey = item.variable?.[1];
|
||||||
|
|
||||||
if (!varNodeId || !varKey) {
|
if (!varNodeId || !varKey) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = (() => {
|
const value = (() => {
|
||||||
if (!item.value?.[0]) {
|
if (!item.value?.[0]) {
|
||||||
return valueTypeFormat(item.value?.[1], item.valueType);
|
const formatValue = valueTypeFormat(item.value?.[1], item.valueType);
|
||||||
|
|
||||||
|
return typeof formatValue === 'string'
|
||||||
|
? replaceEditorVariable({
|
||||||
|
text: formatValue,
|
||||||
|
nodes: runtimeNodes,
|
||||||
|
variables,
|
||||||
|
runningNode: node
|
||||||
|
})
|
||||||
|
: formatValue;
|
||||||
} else {
|
} else {
|
||||||
return getReferenceVariableValue({
|
return getReferenceVariableValue({
|
||||||
value: item.value,
|
value: item.value,
|
||||||
@@ -39,10 +48,11 @@ export const dispatchUpdateVariable = async (props: Props): Promise<Response> =>
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
// Global variable
|
||||||
if (varNodeId === VARIABLE_NODE_ID) {
|
if (varNodeId === VARIABLE_NODE_ID) {
|
||||||
// update global variable
|
|
||||||
variables[varKey] = value;
|
variables[varKey] = value;
|
||||||
} else {
|
} else {
|
||||||
|
// Other nodes
|
||||||
runtimeNodes
|
runtimeNodes
|
||||||
.find((node) => node.nodeId === varNodeId)
|
.find((node) => node.nodeId === varNodeId)
|
||||||
?.outputs?.find((output) => {
|
?.outputs?.find((output) => {
|
||||||
@@ -52,19 +62,18 @@ export const dispatchUpdateVariable = async (props: Props): Promise<Response> =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (detail && stream) {
|
workflowStreamResponse?.({
|
||||||
responseWrite({
|
event: SseResponseEventEnum.updateVariables,
|
||||||
res,
|
data: removeSystemVariable(variables)
|
||||||
event: SseResponseEventEnum.updateVariables,
|
});
|
||||||
data: JSON.stringify(removeSystemVariable(variables))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||||
totalPoints: 0
|
updateVarResult: result
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import {
|
|||||||
ChatItemValueItemType,
|
ChatItemValueItemType,
|
||||||
ToolRunResponseItemType
|
ToolRunResponseItemType
|
||||||
} from '@fastgpt/global/core/chat/type';
|
} from '@fastgpt/global/core/chat/type';
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import {
|
||||||
|
DispatchNodeResponseKeyEnum,
|
||||||
|
SseResponseEventEnum
|
||||||
|
} from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
|
import { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||||
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||||
@@ -21,3 +24,15 @@ export type DispatchFlowResponse = {
|
|||||||
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
|
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
|
||||||
newVariables: Record<string, string>;
|
newVariables: Record<string, string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type WorkflowResponseType = ({
|
||||||
|
write,
|
||||||
|
event,
|
||||||
|
data,
|
||||||
|
stream
|
||||||
|
}: {
|
||||||
|
write?: ((text: string) => void) | undefined;
|
||||||
|
event: SseResponseEventEnum;
|
||||||
|
data: Record<string, any>;
|
||||||
|
stream?: boolean | undefined;
|
||||||
|
}) => void;
|
||||||
|
|||||||
@@ -6,6 +6,56 @@ import {
|
|||||||
NodeOutputKeyEnum
|
NodeOutputKeyEnum
|
||||||
} from '@fastgpt/global/core/workflow/constants';
|
} from '@fastgpt/global/core/workflow/constants';
|
||||||
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/runtime/type';
|
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
|
import { responseWrite } from '../../../common/response';
|
||||||
|
import { NextApiResponse } from 'next';
|
||||||
|
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
|
|
||||||
|
export const getWorkflowResponseWrite = ({
|
||||||
|
res,
|
||||||
|
detail,
|
||||||
|
streamResponse,
|
||||||
|
id
|
||||||
|
}: {
|
||||||
|
res?: NextApiResponse;
|
||||||
|
detail: boolean;
|
||||||
|
streamResponse: boolean;
|
||||||
|
id: string;
|
||||||
|
}) => {
|
||||||
|
return ({
|
||||||
|
write,
|
||||||
|
event,
|
||||||
|
data,
|
||||||
|
stream
|
||||||
|
}: {
|
||||||
|
write?: (text: string) => void;
|
||||||
|
event: SseResponseEventEnum;
|
||||||
|
data: Record<string, any>;
|
||||||
|
stream?: boolean; // Focus set stream response
|
||||||
|
}) => {
|
||||||
|
const useStreamResponse = stream ?? streamResponse;
|
||||||
|
|
||||||
|
if (!res || res.closed || !useStreamResponse) return;
|
||||||
|
|
||||||
|
const detailEvent = [
|
||||||
|
SseResponseEventEnum.error,
|
||||||
|
SseResponseEventEnum.flowNodeStatus,
|
||||||
|
SseResponseEventEnum.flowResponses,
|
||||||
|
SseResponseEventEnum.interactive,
|
||||||
|
SseResponseEventEnum.toolCall,
|
||||||
|
SseResponseEventEnum.toolParams,
|
||||||
|
SseResponseEventEnum.toolResponse,
|
||||||
|
SseResponseEventEnum.updateVariables
|
||||||
|
];
|
||||||
|
if (!detail && detailEvent.includes(event)) return;
|
||||||
|
|
||||||
|
responseWrite({
|
||||||
|
res,
|
||||||
|
write,
|
||||||
|
event: detail ? event : undefined,
|
||||||
|
data: JSON.stringify(data)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const filterToolNodeIdByEdges = ({
|
export const filterToolNodeIdByEdges = ({
|
||||||
nodeId,
|
nodeId,
|
||||||
@@ -94,6 +144,15 @@ export const removeSystemVariable = (variables: Record<string, any>) => {
|
|||||||
|
|
||||||
return copyVariables;
|
return copyVariables;
|
||||||
};
|
};
|
||||||
|
export const filterSystemVariables = (variables: Record<string, any>) => {
|
||||||
|
return {
|
||||||
|
appId: variables.appId,
|
||||||
|
chatId: variables.chatId,
|
||||||
|
responseChatItemId: variables.responseChatItemId,
|
||||||
|
histories: variables.histories,
|
||||||
|
cTime: variables.cTime
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const formatHttpError = (error: any) => {
|
export const formatHttpError = (error: any) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,321 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt';
|
|
||||||
import { filterGPTMessageByMaxTokens } from '../../../chat/utils';
|
|
||||||
|
|
||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
|
||||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
|
||||||
import { getAIApi } from '../../../ai/config';
|
|
||||||
import type { ClassifyQuestionAgentItemType } from '@fastgpt/global/core/workflow/template/system/classifyQuestion/type';
|
|
||||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
|
||||||
import { Prompt_CQJson } from '@fastgpt/global/core/ai/prompt/agent';
|
|
||||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
|
||||||
import { ModelTypeEnum, getLLMModel } from '../../../ai/model';
|
|
||||||
import { getHistories } from '../utils';
|
|
||||||
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
|
||||||
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
|
||||||
import {
|
|
||||||
ChatCompletionCreateParams,
|
|
||||||
ChatCompletionMessageParam,
|
|
||||||
ChatCompletionTool
|
|
||||||
} from '@fastgpt/global/core/ai/type';
|
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
|
||||||
import {
|
|
||||||
countMessagesTokens,
|
|
||||||
countGptMessagesTokens
|
|
||||||
} from '../../../../common/string/tiktoken/index';
|
|
||||||
|
|
||||||
type Props = ModuleDispatchProps<{
|
|
||||||
[NodeInputKeyEnum.aiModel]: string;
|
|
||||||
[NodeInputKeyEnum.aiSystemPrompt]?: string;
|
|
||||||
[NodeInputKeyEnum.history]?: ChatItemType[] | number;
|
|
||||||
[NodeInputKeyEnum.userChatInput]: string;
|
|
||||||
[NodeInputKeyEnum.agents]: ClassifyQuestionAgentItemType[];
|
|
||||||
}>;
|
|
||||||
type CQResponse = DispatchNodeResultType<{
|
|
||||||
[key: string]: any;
|
|
||||||
}>;
|
|
||||||
type ActionProps = Props & { cqModel: LLMModelItemType };
|
|
||||||
|
|
||||||
const agentFunName = 'classify_question';
|
|
||||||
|
|
||||||
/* request openai chat */
|
|
||||||
export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse> => {
|
|
||||||
const {
|
|
||||||
user,
|
|
||||||
module: { name },
|
|
||||||
histories,
|
|
||||||
params: { model, history = 6, agents, userChatInput }
|
|
||||||
} = props as Props;
|
|
||||||
|
|
||||||
if (!userChatInput) {
|
|
||||||
return Promise.reject('Input is empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
const cqModel = getLLMModel(model);
|
|
||||||
|
|
||||||
const chatHistories = getHistories(history, histories);
|
|
||||||
|
|
||||||
const { arg, tokens } = await (async () => {
|
|
||||||
if (cqModel.toolChoice) {
|
|
||||||
return toolChoice({
|
|
||||||
...props,
|
|
||||||
histories: chatHistories,
|
|
||||||
cqModel
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (cqModel.functionCall) {
|
|
||||||
return functionCall({
|
|
||||||
...props,
|
|
||||||
histories: chatHistories,
|
|
||||||
cqModel
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return completions({
|
|
||||||
...props,
|
|
||||||
histories: chatHistories,
|
|
||||||
cqModel
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
const result = agents.find((item) => item.key === arg?.type) || agents[agents.length - 1];
|
|
||||||
|
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
|
||||||
model: cqModel.model,
|
|
||||||
tokens,
|
|
||||||
modelType: ModelTypeEnum.llm
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
[result.key]: true,
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
|
||||||
model: modelName,
|
|
||||||
query: userChatInput,
|
|
||||||
tokens,
|
|
||||||
cqList: agents,
|
|
||||||
cqResult: result.value,
|
|
||||||
contextTotalLen: chatHistories.length + 2
|
|
||||||
},
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
|
||||||
{
|
|
||||||
moduleName: name,
|
|
||||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
|
||||||
model: modelName,
|
|
||||||
tokens
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getFunctionCallSchema = async ({
|
|
||||||
cqModel,
|
|
||||||
histories,
|
|
||||||
params: { agents, systemPrompt, userChatInput }
|
|
||||||
}: ActionProps) => {
|
|
||||||
const messages: ChatItemType[] = [
|
|
||||||
...histories,
|
|
||||||
{
|
|
||||||
obj: ChatRoleEnum.Human,
|
|
||||||
value: [
|
|
||||||
{
|
|
||||||
type: ChatItemValueTypeEnum.text,
|
|
||||||
text: {
|
|
||||||
content: systemPrompt
|
|
||||||
? `<背景知识>
|
|
||||||
${systemPrompt}
|
|
||||||
</背景知识>
|
|
||||||
|
|
||||||
问题: "${userChatInput}"
|
|
||||||
`
|
|
||||||
: userChatInput
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const adaptMessages = chats2GPTMessages({ messages, reserveId: false });
|
|
||||||
const filterMessages = await filterGPTMessageByMaxTokens({
|
|
||||||
messages: adaptMessages,
|
|
||||||
maxTokens: cqModel.maxContext
|
|
||||||
});
|
|
||||||
|
|
||||||
// function body
|
|
||||||
const agentFunction = {
|
|
||||||
name: agentFunName,
|
|
||||||
description: '结合对话记录及背景知识,对问题进行分类,并返回对应的类型字段',
|
|
||||||
parameters: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
type: {
|
|
||||||
type: 'string',
|
|
||||||
description: `问题类型。下面是几种可选的问题类型: ${agents
|
|
||||||
.map((item) => `${item.value},返回:'${item.key}'`)
|
|
||||||
.join(';')}`,
|
|
||||||
enum: agents.map((item) => item.key)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
required: ['type']
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
agentFunction,
|
|
||||||
filterMessages
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const toolChoice = async (props: ActionProps) => {
|
|
||||||
const { user, cqModel } = props;
|
|
||||||
|
|
||||||
const { agentFunction, filterMessages } = await getFunctionCallSchema(props);
|
|
||||||
// function body
|
|
||||||
const tools: ChatCompletionTool[] = [
|
|
||||||
{
|
|
||||||
type: 'function',
|
|
||||||
function: agentFunction
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const ai = getAIApi({
|
|
||||||
userKey: user.openaiAccount,
|
|
||||||
timeout: 480000
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await ai.chat.completions.create({
|
|
||||||
model: cqModel.model,
|
|
||||||
temperature: 0.01,
|
|
||||||
messages: filterMessages,
|
|
||||||
tools,
|
|
||||||
tool_choice: { type: 'function', function: { name: agentFunName } }
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const arg = JSON.parse(
|
|
||||||
response?.choices?.[0]?.message?.tool_calls?.[0]?.function?.arguments || ''
|
|
||||||
);
|
|
||||||
const completeMessages: ChatCompletionMessageParam[] = [
|
|
||||||
...filterMessages,
|
|
||||||
{
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
|
||||||
tool_calls: response.choices?.[0]?.message?.tool_calls
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
|
||||||
arg,
|
|
||||||
tokens: await countGptMessagesTokens(completeMessages, tools)
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.log(response.choices?.[0]?.message);
|
|
||||||
|
|
||||||
console.log('Your model may not support toll_call', error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
arg: {},
|
|
||||||
tokens: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const functionCall = async (props: ActionProps) => {
|
|
||||||
const { user, cqModel } = props;
|
|
||||||
|
|
||||||
const { agentFunction, filterMessages } = await getFunctionCallSchema(props);
|
|
||||||
const functions: ChatCompletionCreateParams.Function[] = [agentFunction];
|
|
||||||
|
|
||||||
const ai = getAIApi({
|
|
||||||
userKey: user.openaiAccount,
|
|
||||||
timeout: 480000
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await ai.chat.completions.create({
|
|
||||||
model: cqModel.model,
|
|
||||||
temperature: 0.01,
|
|
||||||
messages: filterMessages,
|
|
||||||
function_call: {
|
|
||||||
name: agentFunName
|
|
||||||
},
|
|
||||||
functions
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const arg = JSON.parse(response?.choices?.[0]?.message?.function_call?.arguments || '');
|
|
||||||
const completeMessages: ChatCompletionMessageParam[] = [
|
|
||||||
...filterMessages,
|
|
||||||
{
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
|
||||||
function_call: response.choices?.[0]?.message?.function_call
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
|
||||||
arg,
|
|
||||||
tokens: await countGptMessagesTokens(completeMessages, undefined, functions)
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.log(response.choices?.[0]?.message);
|
|
||||||
|
|
||||||
console.log('Your model may not support toll_call', error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
arg: {},
|
|
||||||
tokens: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const completions = async ({
|
|
||||||
cqModel,
|
|
||||||
user,
|
|
||||||
histories,
|
|
||||||
params: { agents, systemPrompt = '', userChatInput }
|
|
||||||
}: ActionProps) => {
|
|
||||||
const messages: ChatItemType[] = [
|
|
||||||
{
|
|
||||||
obj: ChatRoleEnum.Human,
|
|
||||||
value: [
|
|
||||||
{
|
|
||||||
type: ChatItemValueTypeEnum.text,
|
|
||||||
text: {
|
|
||||||
content: replaceVariable(cqModel.customCQPrompt || Prompt_CQJson, {
|
|
||||||
systemPrompt: systemPrompt || 'null',
|
|
||||||
typeList: agents
|
|
||||||
.map((item) => `{"questionType": "${item.value}", "typeId": "${item.key}"}`)
|
|
||||||
.join('\n'),
|
|
||||||
history: histories
|
|
||||||
.map((item) => `${item.obj}:${chatValue2RuntimePrompt(item.value).text}`)
|
|
||||||
.join('\n'),
|
|
||||||
question: userChatInput
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const ai = getAIApi({
|
|
||||||
userKey: user.openaiAccount,
|
|
||||||
timeout: 480000
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await ai.chat.completions.create({
|
|
||||||
model: cqModel.model,
|
|
||||||
temperature: 0.01,
|
|
||||||
messages: chats2GPTMessages({ messages, reserveId: false }),
|
|
||||||
stream: false
|
|
||||||
});
|
|
||||||
const answer = data.choices?.[0].message?.content || '';
|
|
||||||
|
|
||||||
const id =
|
|
||||||
agents.find((item) => answer.includes(item.key) || answer.includes(item.value))?.key || '';
|
|
||||||
|
|
||||||
return {
|
|
||||||
tokens: await countMessagesTokens(messages),
|
|
||||||
arg: { type: id }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,384 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt';
|
|
||||||
import { filterGPTMessageByMaxTokens } from '../../../chat/utils';
|
|
||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
|
||||||
import {
|
|
||||||
countMessagesTokens,
|
|
||||||
countGptMessagesTokens
|
|
||||||
} from '../../../../common/string/tiktoken/index';
|
|
||||||
|
|
||||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
|
||||||
import { getAIApi } from '../../../ai/config';
|
|
||||||
import type { ContextExtractAgentItemType } from '@fastgpt/global/core/workflow/template/system/contextExtract/type';
|
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { Prompt_ExtractJson } from '@fastgpt/global/core/ai/prompt/agent';
|
|
||||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
|
||||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
|
||||||
import { getHistories } from '../utils';
|
|
||||||
import { ModelTypeEnum, getLLMModel } from '../../../ai/model';
|
|
||||||
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
|
||||||
import json5 from 'json5';
|
|
||||||
import {
|
|
||||||
ChatCompletionCreateParams,
|
|
||||||
ChatCompletionMessageParam,
|
|
||||||
ChatCompletionTool
|
|
||||||
} from '@fastgpt/global/core/ai/type';
|
|
||||||
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
|
||||||
|
|
||||||
type Props = ModuleDispatchProps<{
|
|
||||||
[NodeInputKeyEnum.history]?: ChatItemType[];
|
|
||||||
[NodeInputKeyEnum.contextExtractInput]: string;
|
|
||||||
[NodeInputKeyEnum.extractKeys]: ContextExtractAgentItemType[];
|
|
||||||
[NodeInputKeyEnum.description]: string;
|
|
||||||
[NodeInputKeyEnum.aiModel]: string;
|
|
||||||
}>;
|
|
||||||
type Response = DispatchNodeResultType<{
|
|
||||||
[NodeOutputKeyEnum.success]?: boolean;
|
|
||||||
[NodeOutputKeyEnum.failed]?: boolean;
|
|
||||||
[NodeOutputKeyEnum.contextExtractFields]: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
type ActionProps = Props & { extractModel: LLMModelItemType };
|
|
||||||
|
|
||||||
const agentFunName = 'request_function';
|
|
||||||
|
|
||||||
export async function dispatchContentExtract(props: Props): Promise<Response> {
|
|
||||||
const {
|
|
||||||
user,
|
|
||||||
module: { name },
|
|
||||||
histories,
|
|
||||||
params: { content, history = 6, model, description, extractKeys }
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
if (!content) {
|
|
||||||
return Promise.reject('Input is empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
const extractModel = getLLMModel(model);
|
|
||||||
const chatHistories = getHistories(history, histories);
|
|
||||||
|
|
||||||
const { arg, tokens } = await (async () => {
|
|
||||||
if (extractModel.toolChoice) {
|
|
||||||
return toolChoice({
|
|
||||||
...props,
|
|
||||||
histories: chatHistories,
|
|
||||||
extractModel
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (extractModel.functionCall) {
|
|
||||||
return functionCall({
|
|
||||||
...props,
|
|
||||||
histories: chatHistories,
|
|
||||||
extractModel
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return completions({
|
|
||||||
...props,
|
|
||||||
histories: chatHistories,
|
|
||||||
extractModel
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
// remove invalid key
|
|
||||||
for (let key in arg) {
|
|
||||||
const item = extractKeys.find((item) => item.key === key);
|
|
||||||
if (!item) {
|
|
||||||
delete arg[key];
|
|
||||||
}
|
|
||||||
if (arg[key] === '') {
|
|
||||||
delete arg[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// auto fill required fields
|
|
||||||
extractKeys.forEach((item) => {
|
|
||||||
if (item.required && !arg[item.key]) {
|
|
||||||
arg[item.key] = item.defaultValue || '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// auth fields
|
|
||||||
let success = !extractKeys.find((item) => !(item.key in arg));
|
|
||||||
// auth empty value
|
|
||||||
if (success) {
|
|
||||||
for (const key in arg) {
|
|
||||||
const item = extractKeys.find((item) => item.key === key);
|
|
||||||
if (!item) {
|
|
||||||
success = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
|
||||||
model: extractModel.model,
|
|
||||||
tokens,
|
|
||||||
modelType: ModelTypeEnum.llm
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
[NodeOutputKeyEnum.success]: success ? true : undefined,
|
|
||||||
[NodeOutputKeyEnum.failed]: success ? undefined : true,
|
|
||||||
[NodeOutputKeyEnum.contextExtractFields]: JSON.stringify(arg),
|
|
||||||
...arg,
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
|
||||||
model: modelName,
|
|
||||||
query: content,
|
|
||||||
tokens,
|
|
||||||
extractDescription: description,
|
|
||||||
extractResult: arg,
|
|
||||||
contextTotalLen: chatHistories.length + 2
|
|
||||||
},
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
|
||||||
{
|
|
||||||
moduleName: name,
|
|
||||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
|
||||||
model: modelName,
|
|
||||||
tokens
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const getFunctionCallSchema = async ({
|
|
||||||
extractModel,
|
|
||||||
histories,
|
|
||||||
params: { content, extractKeys, description }
|
|
||||||
}: ActionProps) => {
|
|
||||||
const messages: ChatItemType[] = [
|
|
||||||
...histories,
|
|
||||||
{
|
|
||||||
obj: ChatRoleEnum.Human,
|
|
||||||
value: [
|
|
||||||
{
|
|
||||||
type: ChatItemValueTypeEnum.text,
|
|
||||||
text: {
|
|
||||||
content: `我正在执行一个函数,需要你提供一些参数,请以 JSON 字符串格式返回这些参数,要求:
|
|
||||||
"""
|
|
||||||
${description ? `- ${description}` : ''}
|
|
||||||
- 不是每个参数都是必须生成的,如果没有合适的参数值,不要生成该参数,或返回空字符串。
|
|
||||||
- 需要结合前面的对话内容,一起生成合适的参数。
|
|
||||||
"""
|
|
||||||
|
|
||||||
本次输入内容: ${content}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
const adaptMessages = chats2GPTMessages({ messages, reserveId: false });
|
|
||||||
const filterMessages = await filterGPTMessageByMaxTokens({
|
|
||||||
messages: adaptMessages,
|
|
||||||
maxTokens: extractModel.maxContext
|
|
||||||
});
|
|
||||||
|
|
||||||
const properties: Record<
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
type: string;
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
> = {};
|
|
||||||
extractKeys.forEach((item) => {
|
|
||||||
properties[item.key] = {
|
|
||||||
type: 'string',
|
|
||||||
description: item.desc,
|
|
||||||
...(item.enum ? { enum: item.enum.split('\n') } : {})
|
|
||||||
};
|
|
||||||
});
|
|
||||||
// function body
|
|
||||||
const agentFunction = {
|
|
||||||
name: agentFunName,
|
|
||||||
description: '需要执行的函数',
|
|
||||||
parameters: {
|
|
||||||
type: 'object',
|
|
||||||
properties
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
filterMessages,
|
|
||||||
agentFunction
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const toolChoice = async (props: ActionProps) => {
|
|
||||||
const { user, extractModel } = props;
|
|
||||||
|
|
||||||
const { filterMessages, agentFunction } = await getFunctionCallSchema(props);
|
|
||||||
|
|
||||||
const tools: ChatCompletionTool[] = [
|
|
||||||
{
|
|
||||||
type: 'function',
|
|
||||||
function: agentFunction
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const ai = getAIApi({
|
|
||||||
userKey: user.openaiAccount,
|
|
||||||
timeout: 480000
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await ai.chat.completions.create({
|
|
||||||
model: extractModel.model,
|
|
||||||
temperature: 0,
|
|
||||||
messages: filterMessages,
|
|
||||||
tools,
|
|
||||||
tool_choice: { type: 'function', function: { name: agentFunName } }
|
|
||||||
});
|
|
||||||
|
|
||||||
const arg: Record<string, any> = (() => {
|
|
||||||
try {
|
|
||||||
return json5.parse(
|
|
||||||
response?.choices?.[0]?.message?.tool_calls?.[0]?.function?.arguments || '{}'
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(agentFunction.parameters);
|
|
||||||
console.log(response.choices?.[0]?.message?.tool_calls?.[0]?.function);
|
|
||||||
console.log('Your model may not support tool_call', error);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const completeMessages: ChatCompletionMessageParam[] = [
|
|
||||||
...filterMessages,
|
|
||||||
{
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
|
||||||
tool_calls: response.choices?.[0]?.message?.tool_calls
|
|
||||||
}
|
|
||||||
];
|
|
||||||
return {
|
|
||||||
tokens: await countGptMessagesTokens(completeMessages, tools),
|
|
||||||
arg
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const functionCall = async (props: ActionProps) => {
|
|
||||||
const { user, extractModel } = props;
|
|
||||||
|
|
||||||
const { agentFunction, filterMessages } = await getFunctionCallSchema(props);
|
|
||||||
const functions: ChatCompletionCreateParams.Function[] = [agentFunction];
|
|
||||||
|
|
||||||
const ai = getAIApi({
|
|
||||||
userKey: user.openaiAccount,
|
|
||||||
timeout: 480000
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await ai.chat.completions.create({
|
|
||||||
model: extractModel.model,
|
|
||||||
temperature: 0,
|
|
||||||
messages: filterMessages,
|
|
||||||
function_call: {
|
|
||||||
name: agentFunName
|
|
||||||
},
|
|
||||||
functions
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const arg = JSON.parse(response?.choices?.[0]?.message?.function_call?.arguments || '');
|
|
||||||
const completeMessages: ChatCompletionMessageParam[] = [
|
|
||||||
...filterMessages,
|
|
||||||
{
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
|
||||||
function_call: response.choices?.[0]?.message?.function_call
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
|
||||||
arg,
|
|
||||||
tokens: await countGptMessagesTokens(completeMessages, undefined, functions)
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.log(response.choices?.[0]?.message);
|
|
||||||
|
|
||||||
console.log('Your model may not support toll_call', error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
arg: {},
|
|
||||||
tokens: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const completions = async ({
|
|
||||||
extractModel,
|
|
||||||
user,
|
|
||||||
histories,
|
|
||||||
params: { content, extractKeys, description }
|
|
||||||
}: ActionProps) => {
|
|
||||||
const messages: ChatItemType[] = [
|
|
||||||
{
|
|
||||||
obj: ChatRoleEnum.Human,
|
|
||||||
value: [
|
|
||||||
{
|
|
||||||
type: ChatItemValueTypeEnum.text,
|
|
||||||
text: {
|
|
||||||
content: replaceVariable(extractModel.customExtractPrompt || Prompt_ExtractJson, {
|
|
||||||
description,
|
|
||||||
json: extractKeys
|
|
||||||
.map(
|
|
||||||
(item) =>
|
|
||||||
`{"key":"${item.key}", "description":"${item.desc}"${
|
|
||||||
item.enum ? `, "enum":"[${item.enum.split('\n')}]"` : ''
|
|
||||||
}}`
|
|
||||||
)
|
|
||||||
.join('\n'),
|
|
||||||
text: `${histories.map((item) => `${item.obj}:${chatValue2RuntimePrompt(item.value).text}`).join('\n')}
|
|
||||||
Human: ${content}`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const ai = getAIApi({
|
|
||||||
userKey: user.openaiAccount,
|
|
||||||
timeout: 480000
|
|
||||||
});
|
|
||||||
const data = await ai.chat.completions.create({
|
|
||||||
model: extractModel.model,
|
|
||||||
temperature: 0.01,
|
|
||||||
messages: chats2GPTMessages({ messages, reserveId: false }),
|
|
||||||
stream: false
|
|
||||||
});
|
|
||||||
const answer = data.choices?.[0].message?.content || '';
|
|
||||||
|
|
||||||
// parse response
|
|
||||||
const start = answer.indexOf('{');
|
|
||||||
const end = answer.lastIndexOf('}');
|
|
||||||
|
|
||||||
if (start === -1 || end === -1) {
|
|
||||||
return {
|
|
||||||
rawResponse: answer,
|
|
||||||
tokens: await countMessagesTokens(messages),
|
|
||||||
arg: {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const jsonStr = answer
|
|
||||||
.substring(start, end + 1)
|
|
||||||
.replace(/(\\n|\\)/g, '')
|
|
||||||
.replace(/ /g, '');
|
|
||||||
|
|
||||||
try {
|
|
||||||
return {
|
|
||||||
rawResponse: answer,
|
|
||||||
tokens: await countMessagesTokens(messages),
|
|
||||||
arg: json5.parse(jsonStr) as Record<string, any>
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return {
|
|
||||||
rawResponse: answer,
|
|
||||||
tokens: await countMessagesTokens(messages),
|
|
||||||
arg: {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
export const Prompt_Tool_Call = `<Instruction>
|
|
||||||
你是一个智能机器人,除了可以回答用户问题外,你还掌握工具的使用能力。有时候,你可以依赖工具的运行结果,来更准确的回答用户。
|
|
||||||
|
|
||||||
工具使用了 JSON Schema 的格式声明,其中 toolId 是工具的 description 是工具的描述,parameters 是工具的参数,包括参数的类型和描述,required 是必填参数的列表。
|
|
||||||
|
|
||||||
请你根据工具描述,决定回答问题或是使用工具。在完成任务过程中,USER代表用户的输入,TOOL_RESPONSE代表工具运行结果。ASSISTANT 代表你的输出。
|
|
||||||
你的每次输出都必须以0,1开头,代表是否需要调用工具:
|
|
||||||
0: 不使用工具,直接回答内容。
|
|
||||||
1: 使用工具,返回工具调用的参数。
|
|
||||||
|
|
||||||
例如:
|
|
||||||
|
|
||||||
USER: 你好呀
|
|
||||||
ANSWER: 0: 你好,有什么可以帮助你的么?
|
|
||||||
USER: 今天杭州的天气如何
|
|
||||||
ANSWER: 1: {"toolId":"testToolId",arguments:{"city": "杭州"}}
|
|
||||||
TOOL_RESPONSE: """
|
|
||||||
晴天......
|
|
||||||
"""
|
|
||||||
ANSWER: 0: 今天杭州是晴天。
|
|
||||||
USER: 今天杭州的天气适合去哪里玩?
|
|
||||||
ANSWER: 1: {"toolId":"testToolId2",arguments:{"query": "杭州 天气 去哪里玩"}}
|
|
||||||
TOOL_RESPONSE: """
|
|
||||||
晴天. 西湖、灵隐寺、千岛湖……
|
|
||||||
"""
|
|
||||||
ANSWER: 0: 今天杭州是晴天,适合去西湖、灵隐寺、千岛湖等地玩。
|
|
||||||
</Instruction>
|
|
||||||
|
|
||||||
现在,我们开始吧!下面是你本次可以使用的工具:
|
|
||||||
|
|
||||||
"""
|
|
||||||
{{toolsPrompt}}
|
|
||||||
"""
|
|
||||||
|
|
||||||
下面是正式的对话内容:
|
|
||||||
|
|
||||||
USER: {{question}}
|
|
||||||
ANSWER:
|
|
||||||
`;
|
|
||||||
@@ -1,410 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
|
||||||
import { getAIApi } from '../../../../ai/config';
|
|
||||||
import { filterGPTMessageByMaxTokens } from '../../../../chat/utils';
|
|
||||||
import {
|
|
||||||
ChatCompletion,
|
|
||||||
StreamChatType,
|
|
||||||
ChatCompletionMessageParam,
|
|
||||||
ChatCompletionCreateParams,
|
|
||||||
ChatCompletionMessageFunctionCall,
|
|
||||||
ChatCompletionFunctionMessageParam,
|
|
||||||
ChatCompletionAssistantMessageParam
|
|
||||||
} from '@fastgpt/global/core/ai/type';
|
|
||||||
import { NextApiResponse } from 'next';
|
|
||||||
import {
|
|
||||||
responseWrite,
|
|
||||||
responseWriteController,
|
|
||||||
responseWriteNodeStatus
|
|
||||||
} from '../../../../../common/response';
|
|
||||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
|
||||||
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
|
||||||
import { dispatchWorkFlowV1 } from '../../index';
|
|
||||||
import { DispatchToolModuleProps, RunToolResponse, ToolModuleItemType } from './type.d';
|
|
||||||
import json5 from 'json5';
|
|
||||||
import { DispatchFlowResponse } from '../../type';
|
|
||||||
import { countGptMessagesTokens } from '../../../../../common/string/tiktoken';
|
|
||||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
|
||||||
import { AIChatItemType, AIChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
|
||||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
|
||||||
|
|
||||||
type FunctionRunResponseType = {
|
|
||||||
moduleRunResponse: DispatchFlowResponse;
|
|
||||||
functionCallMsg: ChatCompletionFunctionMessageParam;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
export const runToolWithFunctionCall = async (
|
|
||||||
props: DispatchToolModuleProps & {
|
|
||||||
messages: ChatCompletionMessageParam[];
|
|
||||||
toolModules: ToolModuleItemType[];
|
|
||||||
toolModel: LLMModelItemType;
|
|
||||||
},
|
|
||||||
response?: RunToolResponse
|
|
||||||
): Promise<RunToolResponse> => {
|
|
||||||
const {
|
|
||||||
toolModel,
|
|
||||||
toolModules,
|
|
||||||
messages,
|
|
||||||
res,
|
|
||||||
runtimeModules,
|
|
||||||
detail = false,
|
|
||||||
module,
|
|
||||||
stream
|
|
||||||
} = props;
|
|
||||||
const assistantResponses = response?.assistantResponses || [];
|
|
||||||
|
|
||||||
const functions: ChatCompletionCreateParams.Function[] = toolModules.map((module) => {
|
|
||||||
const properties: Record<
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
type: string;
|
|
||||||
description: string;
|
|
||||||
required?: boolean;
|
|
||||||
}
|
|
||||||
> = {};
|
|
||||||
module.toolParams.forEach((item) => {
|
|
||||||
properties[item.key] = {
|
|
||||||
type: 'string',
|
|
||||||
description: item.toolDescription || ''
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: module.moduleId,
|
|
||||||
description: module.intro,
|
|
||||||
parameters: {
|
|
||||||
type: 'object',
|
|
||||||
properties,
|
|
||||||
required: module.toolParams.filter((item) => item.required).map((item) => item.key)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const filterMessages = await filterGPTMessageByMaxTokens({
|
|
||||||
messages,
|
|
||||||
maxTokens: toolModel.maxContext - 500 // filter token. not response maxToken
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Run llm */
|
|
||||||
const ai = getAIApi({
|
|
||||||
timeout: 480000
|
|
||||||
});
|
|
||||||
const aiResponse = await ai.chat.completions.create(
|
|
||||||
{
|
|
||||||
...toolModel?.defaultConfig,
|
|
||||||
model: toolModel.model,
|
|
||||||
temperature: 0,
|
|
||||||
stream,
|
|
||||||
messages: filterMessages,
|
|
||||||
functions,
|
|
||||||
function_call: 'auto'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json, text/plain, */*'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const { answer, functionCalls } = await (async () => {
|
|
||||||
if (stream) {
|
|
||||||
return streamResponse({
|
|
||||||
res,
|
|
||||||
detail,
|
|
||||||
toolModules,
|
|
||||||
stream: aiResponse
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const result = aiResponse as ChatCompletion;
|
|
||||||
const function_call = result.choices?.[0]?.message?.function_call;
|
|
||||||
const toolModule = toolModules.find((module) => module.moduleId === function_call?.name);
|
|
||||||
|
|
||||||
const toolCalls = function_call
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
...function_call,
|
|
||||||
id: getNanoid(),
|
|
||||||
toolName: toolModule?.name,
|
|
||||||
toolAvatar: toolModule?.avatar
|
|
||||||
}
|
|
||||||
]
|
|
||||||
: [];
|
|
||||||
|
|
||||||
return {
|
|
||||||
answer: result.choices?.[0]?.message?.content || '',
|
|
||||||
functionCalls: toolCalls
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
// Run the selected tool.
|
|
||||||
const toolsRunResponse = (
|
|
||||||
await Promise.all(
|
|
||||||
functionCalls.map(async (tool) => {
|
|
||||||
if (!tool) return;
|
|
||||||
|
|
||||||
const toolModule = toolModules.find((module) => module.moduleId === tool.name);
|
|
||||||
|
|
||||||
if (!toolModule) return;
|
|
||||||
|
|
||||||
const startParams = (() => {
|
|
||||||
try {
|
|
||||||
return json5.parse(tool.arguments);
|
|
||||||
} catch (error) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const moduleRunResponse = await dispatchWorkFlowV1({
|
|
||||||
...props,
|
|
||||||
runtimeModules: runtimeModules.map((module) => ({
|
|
||||||
...module,
|
|
||||||
isEntry: module.moduleId === toolModule.moduleId
|
|
||||||
})),
|
|
||||||
startParams
|
|
||||||
});
|
|
||||||
|
|
||||||
const stringToolResponse = (() => {
|
|
||||||
if (typeof moduleRunResponse.toolResponses === 'object') {
|
|
||||||
return JSON.stringify(moduleRunResponse.toolResponses, null, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return moduleRunResponse.toolResponses ? String(moduleRunResponse.toolResponses) : 'none';
|
|
||||||
})();
|
|
||||||
|
|
||||||
const functionCallMsg: ChatCompletionFunctionMessageParam = {
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Function,
|
|
||||||
name: tool.name,
|
|
||||||
content: stringToolResponse
|
|
||||||
};
|
|
||||||
|
|
||||||
if (stream && detail) {
|
|
||||||
responseWrite({
|
|
||||||
res,
|
|
||||||
event: SseResponseEventEnum.toolResponse,
|
|
||||||
data: JSON.stringify({
|
|
||||||
tool: {
|
|
||||||
id: tool.id,
|
|
||||||
toolName: '',
|
|
||||||
toolAvatar: '',
|
|
||||||
params: '',
|
|
||||||
response: stringToolResponse
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
moduleRunResponse,
|
|
||||||
functionCallMsg
|
|
||||||
};
|
|
||||||
})
|
|
||||||
)
|
|
||||||
).filter(Boolean) as FunctionRunResponseType;
|
|
||||||
|
|
||||||
const flatToolsResponseData = toolsRunResponse.map((item) => item.moduleRunResponse).flat();
|
|
||||||
|
|
||||||
const functionCall = functionCalls[0];
|
|
||||||
if (functionCall && !res.closed) {
|
|
||||||
// Run the tool, combine its results, and perform another round of AI calls
|
|
||||||
const assistantToolMsgParams: ChatCompletionAssistantMessageParam = {
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
|
||||||
function_call: functionCall
|
|
||||||
};
|
|
||||||
const concatToolMessages = [
|
|
||||||
...filterMessages,
|
|
||||||
assistantToolMsgParams
|
|
||||||
] as ChatCompletionMessageParam[];
|
|
||||||
const tokens = await countGptMessagesTokens(concatToolMessages, undefined, functions);
|
|
||||||
const completeMessages = [
|
|
||||||
...concatToolMessages,
|
|
||||||
...toolsRunResponse.map((item) => item?.functionCallMsg)
|
|
||||||
];
|
|
||||||
// console.log(tokens, 'tool');
|
|
||||||
|
|
||||||
if (stream && detail) {
|
|
||||||
responseWriteNodeStatus({
|
|
||||||
res,
|
|
||||||
name: module.name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// tool assistant
|
|
||||||
const toolAssistants = toolsRunResponse
|
|
||||||
.map((item) => {
|
|
||||||
const assistantResponses = item.moduleRunResponse.assistantResponses || [];
|
|
||||||
return assistantResponses;
|
|
||||||
})
|
|
||||||
.flat();
|
|
||||||
// tool node assistant
|
|
||||||
const adaptChatMessages = GPTMessages2Chats(completeMessages);
|
|
||||||
const toolNodeAssistant = adaptChatMessages.pop() as AIChatItemType;
|
|
||||||
|
|
||||||
const toolNodeAssistants = [
|
|
||||||
...assistantResponses,
|
|
||||||
...toolAssistants,
|
|
||||||
...toolNodeAssistant.value
|
|
||||||
];
|
|
||||||
|
|
||||||
// concat tool responses
|
|
||||||
const dispatchFlowResponse = response
|
|
||||||
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
|
||||||
: flatToolsResponseData;
|
|
||||||
|
|
||||||
/* check stop signal */
|
|
||||||
const hasStopSignal = flatToolsResponseData.some(
|
|
||||||
(item) => !!item.flowResponses?.find((item) => item.toolStop)
|
|
||||||
);
|
|
||||||
if (hasStopSignal) {
|
|
||||||
return {
|
|
||||||
dispatchFlowResponse,
|
|
||||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
|
||||||
completeMessages: filterMessages,
|
|
||||||
assistantResponses: toolNodeAssistants
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return runToolWithFunctionCall(
|
|
||||||
{
|
|
||||||
...props,
|
|
||||||
messages: completeMessages
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dispatchFlowResponse,
|
|
||||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
|
||||||
assistantResponses: toolNodeAssistants
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} 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, undefined, functions);
|
|
||||||
// console.log(tokens, 'response token');
|
|
||||||
|
|
||||||
// concat tool assistant
|
|
||||||
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
|
|
||||||
|
|
||||||
return {
|
|
||||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
|
||||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
|
||||||
completeMessages,
|
|
||||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async function streamResponse({
|
|
||||||
res,
|
|
||||||
detail,
|
|
||||||
toolModules,
|
|
||||||
stream
|
|
||||||
}: {
|
|
||||||
res: NextApiResponse;
|
|
||||||
detail: boolean;
|
|
||||||
toolModules: ToolModuleItemType[];
|
|
||||||
stream: StreamChatType;
|
|
||||||
}) {
|
|
||||||
const write = responseWriteController({
|
|
||||||
res,
|
|
||||||
readStream: stream
|
|
||||||
});
|
|
||||||
|
|
||||||
let textAnswer = '';
|
|
||||||
let functionCalls: ChatCompletionMessageFunctionCall[] = [];
|
|
||||||
let functionId = getNanoid();
|
|
||||||
|
|
||||||
for await (const part of stream) {
|
|
||||||
if (res.closed) {
|
|
||||||
stream.controller?.abort();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseChoice = part.choices?.[0]?.delta;
|
|
||||||
if (responseChoice.content) {
|
|
||||||
const content = responseChoice?.content || '';
|
|
||||||
textAnswer += content;
|
|
||||||
|
|
||||||
responseWrite({
|
|
||||||
write,
|
|
||||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
|
||||||
data: textAdaptGptResponse({
|
|
||||||
text: content
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} else if (responseChoice.function_call) {
|
|
||||||
const functionCall: {
|
|
||||||
arguments: string;
|
|
||||||
name?: string;
|
|
||||||
} = responseChoice.function_call;
|
|
||||||
|
|
||||||
// 流响应中,每次只会返回一个函数,如果带了name,说明触发某个函数
|
|
||||||
if (functionCall?.name) {
|
|
||||||
functionId = getNanoid();
|
|
||||||
const toolModule = toolModules.find((module) => module.moduleId === functionCall?.name);
|
|
||||||
|
|
||||||
if (toolModule) {
|
|
||||||
if (functionCall?.arguments === undefined) {
|
|
||||||
functionCall.arguments = '';
|
|
||||||
}
|
|
||||||
functionCalls.push({
|
|
||||||
...functionCall,
|
|
||||||
id: functionId,
|
|
||||||
name: functionCall.name,
|
|
||||||
toolName: toolModule.name,
|
|
||||||
toolAvatar: toolModule.avatar
|
|
||||||
});
|
|
||||||
|
|
||||||
if (detail) {
|
|
||||||
responseWrite({
|
|
||||||
write,
|
|
||||||
event: SseResponseEventEnum.toolCall,
|
|
||||||
data: JSON.stringify({
|
|
||||||
tool: {
|
|
||||||
id: functionId,
|
|
||||||
toolName: toolModule.name,
|
|
||||||
toolAvatar: toolModule.avatar,
|
|
||||||
functionName: functionCall.name,
|
|
||||||
params: functionCall.arguments,
|
|
||||||
response: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* arg 插入最后一个工具的参数里 */
|
|
||||||
const arg: string = functionCall?.arguments || '';
|
|
||||||
const currentTool = functionCalls[functionCalls.length - 1];
|
|
||||||
if (currentTool) {
|
|
||||||
currentTool.arguments += arg;
|
|
||||||
|
|
||||||
if (detail) {
|
|
||||||
responseWrite({
|
|
||||||
write,
|
|
||||||
event: SseResponseEventEnum.toolParams,
|
|
||||||
data: JSON.stringify({
|
|
||||||
tool: {
|
|
||||||
id: functionId,
|
|
||||||
toolName: '',
|
|
||||||
toolAvatar: '',
|
|
||||||
params: arg,
|
|
||||||
response: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!textAnswer && functionCalls.length === 0) {
|
|
||||||
return Promise.reject('LLM api response empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
return { answer: textAnswer, functionCalls };
|
|
||||||
}
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import type {
|
|
||||||
DispatchNodeResultType,
|
|
||||||
RuntimeNodeItemType
|
|
||||||
} from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { ModelTypeEnum, getLLMModel } from '../../../../ai/model';
|
|
||||||
import { getHistories } from '../../utils';
|
|
||||||
import { runToolWithToolChoice } from './toolChoice';
|
|
||||||
import { DispatchToolModuleProps, ToolModuleItemType } from './type.d';
|
|
||||||
import { ChatItemType } from '@fastgpt/global/core/chat/type';
|
|
||||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
|
||||||
import {
|
|
||||||
GPTMessages2Chats,
|
|
||||||
chats2GPTMessages,
|
|
||||||
getSystemPrompt,
|
|
||||||
runtimePrompt2ChatsValue
|
|
||||||
} from '@fastgpt/global/core/chat/adapt';
|
|
||||||
import { formatModelChars2Points } from '../../../../../support/wallet/usage/utils';
|
|
||||||
import { getHistoryPreview } from '@fastgpt/global/core/chat/utils';
|
|
||||||
import { runToolWithFunctionCall } from './functionCall';
|
|
||||||
import { runToolWithPromptCall } from './promptCall';
|
|
||||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
|
||||||
import { Prompt_Tool_Call } from './constants';
|
|
||||||
|
|
||||||
type Response = DispatchNodeResultType<{}>;
|
|
||||||
|
|
||||||
export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<Response> => {
|
|
||||||
const {
|
|
||||||
module: { name, outputs },
|
|
||||||
runtimeModules,
|
|
||||||
histories,
|
|
||||||
params: { model, systemPrompt, userChatInput, history = 6 }
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const toolModel = getLLMModel(model);
|
|
||||||
const chatHistories = getHistories(history, histories);
|
|
||||||
|
|
||||||
/* get tool params */
|
|
||||||
|
|
||||||
// get tool output targets
|
|
||||||
const toolOutput = outputs.find((output) => output.key === NodeOutputKeyEnum.selectedTools);
|
|
||||||
|
|
||||||
if (!toolOutput) {
|
|
||||||
return Promise.reject('No tool output found');
|
|
||||||
}
|
|
||||||
|
|
||||||
const targets = toolOutput.targets;
|
|
||||||
|
|
||||||
// Gets the module to which the tool is connected
|
|
||||||
const toolModules = targets
|
|
||||||
.map((item) => {
|
|
||||||
const tool = runtimeModules.find((module) => module.moduleId === item.moduleId);
|
|
||||||
return tool;
|
|
||||||
})
|
|
||||||
.filter(Boolean)
|
|
||||||
.map<ToolModuleItemType>((tool) => {
|
|
||||||
const toolParams = tool?.inputs.filter((input) => !!input.toolDescription) || [];
|
|
||||||
return {
|
|
||||||
...(tool as RuntimeNodeItemType),
|
|
||||||
toolParams
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const messages: ChatItemType[] = [
|
|
||||||
...getSystemPrompt(systemPrompt),
|
|
||||||
...chatHistories,
|
|
||||||
{
|
|
||||||
obj: ChatRoleEnum.Human,
|
|
||||||
value: runtimePrompt2ChatsValue({
|
|
||||||
text: userChatInput,
|
|
||||||
files: []
|
|
||||||
})
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const {
|
|
||||||
dispatchFlowResponse, // tool flow response
|
|
||||||
totalTokens,
|
|
||||||
completeMessages = [], // The actual message sent to AI(just save text)
|
|
||||||
assistantResponses = [] // FastGPT system store assistant.value response
|
|
||||||
} = await (async () => {
|
|
||||||
const adaptMessages = chats2GPTMessages({ messages, reserveId: false });
|
|
||||||
|
|
||||||
if (toolModel.toolChoice) {
|
|
||||||
return runToolWithToolChoice({
|
|
||||||
...props,
|
|
||||||
toolModules,
|
|
||||||
toolModel,
|
|
||||||
messages: adaptMessages
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (toolModel.functionCall) {
|
|
||||||
return runToolWithFunctionCall({
|
|
||||||
...props,
|
|
||||||
toolModules,
|
|
||||||
toolModel,
|
|
||||||
messages: adaptMessages
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastMessage = adaptMessages[adaptMessages.length - 1];
|
|
||||||
if (typeof lastMessage.content !== 'string') {
|
|
||||||
return Promise.reject('暂时只支持纯文本');
|
|
||||||
}
|
|
||||||
|
|
||||||
lastMessage.content = replaceVariable(Prompt_Tool_Call, {
|
|
||||||
question: userChatInput
|
|
||||||
});
|
|
||||||
|
|
||||||
return runToolWithPromptCall({
|
|
||||||
...props,
|
|
||||||
toolModules,
|
|
||||||
toolModel,
|
|
||||||
messages: adaptMessages
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
|
||||||
model,
|
|
||||||
tokens: totalTokens,
|
|
||||||
modelType: ModelTypeEnum.llm
|
|
||||||
});
|
|
||||||
|
|
||||||
// flat child tool response
|
|
||||||
const childToolResponse = dispatchFlowResponse.map((item) => item.flowResponses).flat();
|
|
||||||
|
|
||||||
// concat tool usage
|
|
||||||
const totalPointsUsage =
|
|
||||||
totalPoints +
|
|
||||||
dispatchFlowResponse.reduce((sum, item) => {
|
|
||||||
const childrenTotal = item.flowUsages.reduce((sum, item) => sum + item.totalPoints, 0);
|
|
||||||
return sum + childrenTotal;
|
|
||||||
}, 0);
|
|
||||||
const flatUsages = dispatchFlowResponse.map((item) => item.flowUsages).flat();
|
|
||||||
|
|
||||||
return {
|
|
||||||
[DispatchNodeResponseKeyEnum.assistantResponses]: assistantResponses,
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
totalPoints: totalPointsUsage,
|
|
||||||
toolCallTokens: totalTokens,
|
|
||||||
model: modelName,
|
|
||||||
query: userChatInput,
|
|
||||||
historyPreview: getHistoryPreview(GPTMessages2Chats(completeMessages, false)),
|
|
||||||
toolDetail: childToolResponse
|
|
||||||
},
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
|
||||||
{
|
|
||||||
moduleName: name,
|
|
||||||
totalPoints,
|
|
||||||
model: modelName,
|
|
||||||
tokens: totalTokens
|
|
||||||
},
|
|
||||||
...flatUsages
|
|
||||||
]
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,388 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
|
||||||
import { getAIApi } from '../../../../ai/config';
|
|
||||||
import { filterGPTMessageByMaxTokens } from '../../../../chat/utils';
|
|
||||||
import {
|
|
||||||
ChatCompletion,
|
|
||||||
StreamChatType,
|
|
||||||
ChatCompletionMessageParam,
|
|
||||||
ChatCompletionAssistantMessageParam
|
|
||||||
} from '@fastgpt/global/core/ai/type';
|
|
||||||
import { NextApiResponse } from 'next';
|
|
||||||
import {
|
|
||||||
responseWrite,
|
|
||||||
responseWriteController,
|
|
||||||
responseWriteNodeStatus
|
|
||||||
} from '../../../../../common/response';
|
|
||||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
|
||||||
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
|
||||||
import { dispatchWorkFlowV1 } from '../../index';
|
|
||||||
import { DispatchToolModuleProps, RunToolResponse, ToolModuleItemType } from './type.d';
|
|
||||||
import json5 from 'json5';
|
|
||||||
import { countGptMessagesTokens } from '../../../../../common/string/tiktoken';
|
|
||||||
import { getNanoid, replaceVariable } from '@fastgpt/global/common/string/tools';
|
|
||||||
import { AIChatItemType } from '@fastgpt/global/core/chat/type';
|
|
||||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
|
||||||
|
|
||||||
type FunctionCallCompletion = {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
arguments: string;
|
|
||||||
toolName?: string;
|
|
||||||
toolAvatar?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const runToolWithPromptCall = async (
|
|
||||||
props: DispatchToolModuleProps & {
|
|
||||||
messages: ChatCompletionMessageParam[];
|
|
||||||
toolModules: ToolModuleItemType[];
|
|
||||||
toolModel: LLMModelItemType;
|
|
||||||
},
|
|
||||||
response?: RunToolResponse
|
|
||||||
): Promise<RunToolResponse> => {
|
|
||||||
const {
|
|
||||||
toolModel,
|
|
||||||
toolModules,
|
|
||||||
messages,
|
|
||||||
res,
|
|
||||||
runtimeModules,
|
|
||||||
detail = false,
|
|
||||||
module,
|
|
||||||
stream
|
|
||||||
} = props;
|
|
||||||
const assistantResponses = response?.assistantResponses || [];
|
|
||||||
|
|
||||||
const toolsPrompt = JSON.stringify(
|
|
||||||
toolModules.map((module) => {
|
|
||||||
const properties: Record<
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
type: string;
|
|
||||||
description: string;
|
|
||||||
required?: boolean;
|
|
||||||
}
|
|
||||||
> = {};
|
|
||||||
module.toolParams.forEach((item) => {
|
|
||||||
properties[item.key] = {
|
|
||||||
type: 'string',
|
|
||||||
description: item.toolDescription || ''
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
toolId: module.moduleId,
|
|
||||||
description: module.intro,
|
|
||||||
parameters: {
|
|
||||||
type: 'object',
|
|
||||||
properties,
|
|
||||||
required: module.toolParams.filter((item) => item.required).map((item) => item.key)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const lastMessage = messages[messages.length - 1];
|
|
||||||
if (typeof lastMessage.content !== 'string') {
|
|
||||||
return Promise.reject('暂时只支持纯文本');
|
|
||||||
}
|
|
||||||
lastMessage.content = replaceVariable(lastMessage.content, {
|
|
||||||
toolsPrompt
|
|
||||||
});
|
|
||||||
|
|
||||||
const filterMessages = await filterGPTMessageByMaxTokens({
|
|
||||||
messages,
|
|
||||||
maxTokens: toolModel.maxContext - 500 // filter token. not response maxToken
|
|
||||||
});
|
|
||||||
// console.log(JSON.stringify(filterMessages, null, 2));
|
|
||||||
/* Run llm */
|
|
||||||
const ai = getAIApi({
|
|
||||||
timeout: 480000
|
|
||||||
});
|
|
||||||
const aiResponse = await ai.chat.completions.create(
|
|
||||||
{
|
|
||||||
...toolModel?.defaultConfig,
|
|
||||||
model: toolModel.model,
|
|
||||||
temperature: 0,
|
|
||||||
stream,
|
|
||||||
messages: filterMessages
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json, text/plain, */*'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const answer = await (async () => {
|
|
||||||
if (stream) {
|
|
||||||
const { answer } = await streamResponse({
|
|
||||||
res,
|
|
||||||
detail,
|
|
||||||
toolModules,
|
|
||||||
stream: aiResponse
|
|
||||||
});
|
|
||||||
|
|
||||||
return answer;
|
|
||||||
} else {
|
|
||||||
const result = aiResponse as ChatCompletion;
|
|
||||||
|
|
||||||
return result.choices?.[0]?.message?.content || '';
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const parseAnswerResult = parseAnswer(answer);
|
|
||||||
// console.log(parseAnswer, '==11==');
|
|
||||||
// No tools
|
|
||||||
if (typeof parseAnswerResult === 'string') {
|
|
||||||
// No tool is invoked, indicating that the process is over
|
|
||||||
const gptAssistantResponse: ChatCompletionAssistantMessageParam = {
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
|
||||||
content: parseAnswerResult
|
|
||||||
};
|
|
||||||
const completeMessages = filterMessages.concat(gptAssistantResponse);
|
|
||||||
const tokens = await countGptMessagesTokens(completeMessages, undefined);
|
|
||||||
// console.log(tokens, 'response token');
|
|
||||||
|
|
||||||
// concat tool assistant
|
|
||||||
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
|
|
||||||
|
|
||||||
return {
|
|
||||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
|
||||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
|
||||||
completeMessages,
|
|
||||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the selected tool.
|
|
||||||
const toolsRunResponse = await (async () => {
|
|
||||||
if (!parseAnswerResult) return Promise.reject('tool run error');
|
|
||||||
|
|
||||||
const toolModule = toolModules.find((module) => module.moduleId === parseAnswerResult.name);
|
|
||||||
if (!toolModule) return Promise.reject('tool not found');
|
|
||||||
|
|
||||||
parseAnswerResult.toolName = toolModule.name;
|
|
||||||
parseAnswerResult.toolAvatar = toolModule.avatar;
|
|
||||||
|
|
||||||
// run tool flow
|
|
||||||
const startParams = (() => {
|
|
||||||
try {
|
|
||||||
return json5.parse(parseAnswerResult.arguments);
|
|
||||||
} catch (error) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
// SSE response to client
|
|
||||||
if (stream && detail) {
|
|
||||||
responseWrite({
|
|
||||||
res,
|
|
||||||
event: SseResponseEventEnum.toolCall,
|
|
||||||
data: JSON.stringify({
|
|
||||||
tool: {
|
|
||||||
id: parseAnswerResult.id,
|
|
||||||
toolName: toolModule.name,
|
|
||||||
toolAvatar: toolModule.avatar,
|
|
||||||
functionName: parseAnswerResult.name,
|
|
||||||
params: parseAnswerResult.arguments,
|
|
||||||
response: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const moduleRunResponse = await dispatchWorkFlowV1({
|
|
||||||
...props,
|
|
||||||
runtimeModules: runtimeModules.map((module) => ({
|
|
||||||
...module,
|
|
||||||
isEntry: module.moduleId === toolModule.moduleId
|
|
||||||
})),
|
|
||||||
startParams
|
|
||||||
});
|
|
||||||
|
|
||||||
const stringToolResponse = (() => {
|
|
||||||
if (typeof moduleRunResponse.toolResponses === 'object') {
|
|
||||||
return JSON.stringify(moduleRunResponse.toolResponses, null, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return moduleRunResponse.toolResponses ? String(moduleRunResponse.toolResponses) : 'none';
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (stream && detail) {
|
|
||||||
responseWrite({
|
|
||||||
res,
|
|
||||||
event: SseResponseEventEnum.toolResponse,
|
|
||||||
data: JSON.stringify({
|
|
||||||
tool: {
|
|
||||||
id: parseAnswerResult.id,
|
|
||||||
toolName: '',
|
|
||||||
toolAvatar: '',
|
|
||||||
params: '',
|
|
||||||
response: stringToolResponse
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
moduleRunResponse,
|
|
||||||
toolResponsePrompt: stringToolResponse
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (stream && detail) {
|
|
||||||
responseWriteNodeStatus({
|
|
||||||
res,
|
|
||||||
name: module.name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 合并工具调用的结果,使用 functionCall 格式存储。
|
|
||||||
const assistantToolMsgParams: ChatCompletionAssistantMessageParam = {
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
|
||||||
function_call: parseAnswerResult
|
|
||||||
};
|
|
||||||
const concatToolMessages = [
|
|
||||||
...filterMessages,
|
|
||||||
assistantToolMsgParams
|
|
||||||
] as ChatCompletionMessageParam[];
|
|
||||||
const tokens = await countGptMessagesTokens(concatToolMessages, undefined);
|
|
||||||
const completeMessages: ChatCompletionMessageParam[] = [
|
|
||||||
...concatToolMessages,
|
|
||||||
{
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Function,
|
|
||||||
name: parseAnswerResult.name,
|
|
||||||
content: toolsRunResponse.toolResponsePrompt
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// tool assistant
|
|
||||||
const toolAssistants = toolsRunResponse.moduleRunResponse.assistantResponses || [];
|
|
||||||
// tool node assistant
|
|
||||||
const adaptChatMessages = GPTMessages2Chats(completeMessages);
|
|
||||||
const toolNodeAssistant = adaptChatMessages.pop() as AIChatItemType;
|
|
||||||
|
|
||||||
const toolNodeAssistants = [...assistantResponses, ...toolAssistants, ...toolNodeAssistant.value];
|
|
||||||
|
|
||||||
const dispatchFlowResponse = response
|
|
||||||
? response.dispatchFlowResponse.concat(toolsRunResponse.moduleRunResponse)
|
|
||||||
: [toolsRunResponse.moduleRunResponse];
|
|
||||||
|
|
||||||
// get the next user prompt
|
|
||||||
lastMessage.content += `${answer}
|
|
||||||
TOOL_RESPONSE: """
|
|
||||||
${toolsRunResponse.toolResponsePrompt}
|
|
||||||
"""
|
|
||||||
ANSWER: `;
|
|
||||||
|
|
||||||
/* check stop signal */
|
|
||||||
const hasStopSignal = toolsRunResponse.moduleRunResponse.flowResponses.some(
|
|
||||||
(item) => !!item.toolStop
|
|
||||||
);
|
|
||||||
if (hasStopSignal) {
|
|
||||||
return {
|
|
||||||
dispatchFlowResponse,
|
|
||||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
|
||||||
completeMessages: filterMessages,
|
|
||||||
assistantResponses: toolNodeAssistants
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return runToolWithPromptCall(
|
|
||||||
{
|
|
||||||
...props,
|
|
||||||
messages
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dispatchFlowResponse,
|
|
||||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
|
||||||
assistantResponses: toolNodeAssistants
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function streamResponse({
|
|
||||||
res,
|
|
||||||
detail,
|
|
||||||
stream
|
|
||||||
}: {
|
|
||||||
res: NextApiResponse;
|
|
||||||
detail: boolean;
|
|
||||||
toolModules: ToolModuleItemType[];
|
|
||||||
stream: StreamChatType;
|
|
||||||
}) {
|
|
||||||
const write = responseWriteController({
|
|
||||||
res,
|
|
||||||
readStream: stream
|
|
||||||
});
|
|
||||||
|
|
||||||
let startResponseWrite = false;
|
|
||||||
let textAnswer = '';
|
|
||||||
|
|
||||||
for await (const part of stream) {
|
|
||||||
if (res.closed) {
|
|
||||||
stream.controller?.abort();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseChoice = part.choices?.[0]?.delta;
|
|
||||||
if (responseChoice.content) {
|
|
||||||
const content = responseChoice?.content || '';
|
|
||||||
textAnswer += content;
|
|
||||||
|
|
||||||
if (startResponseWrite) {
|
|
||||||
responseWrite({
|
|
||||||
write,
|
|
||||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
|
||||||
data: textAdaptGptResponse({
|
|
||||||
text: content
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} else if (textAnswer.length >= 3) {
|
|
||||||
textAnswer = textAnswer.trim();
|
|
||||||
if (textAnswer.startsWith('0')) {
|
|
||||||
startResponseWrite = true;
|
|
||||||
// find first : index
|
|
||||||
const firstIndex = textAnswer.indexOf(':');
|
|
||||||
textAnswer = textAnswer.substring(firstIndex + 1).trim();
|
|
||||||
responseWrite({
|
|
||||||
write,
|
|
||||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
|
||||||
data: textAdaptGptResponse({
|
|
||||||
text: textAnswer
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!textAnswer) {
|
|
||||||
return Promise.reject('LLM api response empty');
|
|
||||||
}
|
|
||||||
// console.log(textAnswer, '---===');
|
|
||||||
return { answer: textAnswer.trim() };
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseAnswer = (str: string): FunctionCallCompletion | string => {
|
|
||||||
// 首先,使用正则表达式提取TOOL_ID和TOOL_ARGUMENTS
|
|
||||||
const prefix = '1:';
|
|
||||||
str = str.trim();
|
|
||||||
if (str.startsWith(prefix)) {
|
|
||||||
const toolString = str.substring(prefix.length).trim();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const toolCall = json5.parse(toolString);
|
|
||||||
return {
|
|
||||||
id: getNanoid(),
|
|
||||||
name: toolCall.toolId,
|
|
||||||
arguments: JSON.stringify(toolCall.arguments || toolCall.parameters)
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
|
|
||||||
export type AnswerProps = ModuleDispatchProps<{}>;
|
|
||||||
export type AnswerResponse = DispatchNodeResultType<{}>;
|
|
||||||
|
|
||||||
export const dispatchStopToolCall = (props: Record<string, any>): AnswerResponse => {
|
|
||||||
return {
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
toolStop: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,413 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
|
||||||
import { getAIApi } from '../../../../ai/config';
|
|
||||||
import { filterGPTMessageByMaxTokens } from '../../../../chat/utils';
|
|
||||||
import {
|
|
||||||
ChatCompletion,
|
|
||||||
ChatCompletionMessageToolCall,
|
|
||||||
StreamChatType,
|
|
||||||
ChatCompletionToolMessageParam,
|
|
||||||
ChatCompletionAssistantToolParam,
|
|
||||||
ChatCompletionMessageParam,
|
|
||||||
ChatCompletionTool,
|
|
||||||
ChatCompletionAssistantMessageParam
|
|
||||||
} from '@fastgpt/global/core/ai/type';
|
|
||||||
import { NextApiResponse } from 'next';
|
|
||||||
import {
|
|
||||||
responseWrite,
|
|
||||||
responseWriteController,
|
|
||||||
responseWriteNodeStatus
|
|
||||||
} from '../../../../../common/response';
|
|
||||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
|
||||||
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
|
||||||
import { dispatchWorkFlowV1 } from '../../index';
|
|
||||||
import { DispatchToolModuleProps, RunToolResponse, ToolModuleItemType } from './type.d';
|
|
||||||
import json5 from 'json5';
|
|
||||||
import { DispatchFlowResponse } from '../../type';
|
|
||||||
import { countGptMessagesTokens } from '../../../../../common/string/tiktoken';
|
|
||||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
|
||||||
import { AIChatItemType } from '@fastgpt/global/core/chat/type';
|
|
||||||
|
|
||||||
type ToolRunResponseType = {
|
|
||||||
moduleRunResponse: DispatchFlowResponse;
|
|
||||||
toolMsgParams: ChatCompletionToolMessageParam;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
/*
|
|
||||||
调用思路
|
|
||||||
1. messages 接收发送给AI的消息
|
|
||||||
2. response 记录递归运行结果(累计计算 dispatchFlowResponse, totalTokens和assistantResponses)
|
|
||||||
3. 如果运行工具的话,则需要把工具中的结果累计加到dispatchFlowResponse中。 本次消耗的 token 加到 totalTokens, assistantResponses 记录当前工具运行的内容。
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const runToolWithToolChoice = async (
|
|
||||||
props: DispatchToolModuleProps & {
|
|
||||||
messages: ChatCompletionMessageParam[];
|
|
||||||
toolModules: ToolModuleItemType[];
|
|
||||||
toolModel: LLMModelItemType;
|
|
||||||
},
|
|
||||||
response?: RunToolResponse
|
|
||||||
): Promise<RunToolResponse> => {
|
|
||||||
const {
|
|
||||||
toolModel,
|
|
||||||
toolModules,
|
|
||||||
messages,
|
|
||||||
res,
|
|
||||||
runtimeModules,
|
|
||||||
detail = false,
|
|
||||||
module,
|
|
||||||
stream
|
|
||||||
} = props;
|
|
||||||
const assistantResponses = response?.assistantResponses || [];
|
|
||||||
|
|
||||||
const tools: ChatCompletionTool[] = toolModules.map((module) => {
|
|
||||||
const properties: Record<
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
type: string;
|
|
||||||
description: string;
|
|
||||||
required?: boolean;
|
|
||||||
}
|
|
||||||
> = {};
|
|
||||||
module.toolParams.forEach((item) => {
|
|
||||||
properties[item.key] = {
|
|
||||||
type: 'string',
|
|
||||||
description: item.toolDescription || ''
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'function',
|
|
||||||
function: {
|
|
||||||
name: module.moduleId,
|
|
||||||
description: module.intro,
|
|
||||||
parameters: {
|
|
||||||
type: 'object',
|
|
||||||
properties,
|
|
||||||
required: module.toolParams.filter((item) => item.required).map((item) => item.key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const filterMessages = await filterGPTMessageByMaxTokens({
|
|
||||||
messages,
|
|
||||||
maxTokens: toolModel.maxContext - 300 // filter token. not response maxToken
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Run llm */
|
|
||||||
const ai = getAIApi({
|
|
||||||
timeout: 480000
|
|
||||||
});
|
|
||||||
const aiResponse = await ai.chat.completions.create(
|
|
||||||
{
|
|
||||||
...toolModel?.defaultConfig,
|
|
||||||
model: toolModel.model,
|
|
||||||
temperature: 0,
|
|
||||||
stream,
|
|
||||||
messages: filterMessages,
|
|
||||||
tools,
|
|
||||||
tool_choice: 'auto'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json, text/plain, */*'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const { answer, toolCalls } = await (async () => {
|
|
||||||
if (stream) {
|
|
||||||
return streamResponse({
|
|
||||||
res,
|
|
||||||
detail,
|
|
||||||
toolModules,
|
|
||||||
stream: aiResponse
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const result = aiResponse as ChatCompletion;
|
|
||||||
const calls = result.choices?.[0]?.message?.tool_calls || [];
|
|
||||||
|
|
||||||
// 加上name和avatar
|
|
||||||
const toolCalls = calls.map((tool) => {
|
|
||||||
const toolModule = toolModules.find((module) => module.moduleId === tool.function?.name);
|
|
||||||
return {
|
|
||||||
...tool,
|
|
||||||
toolName: toolModule?.name || '',
|
|
||||||
toolAvatar: toolModule?.avatar || ''
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
answer: result.choices?.[0]?.message?.content || '',
|
|
||||||
toolCalls: toolCalls
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
// Run the selected tool.
|
|
||||||
const toolsRunResponse = (
|
|
||||||
await Promise.all(
|
|
||||||
toolCalls.map(async (tool) => {
|
|
||||||
const toolModule = toolModules.find((module) => module.moduleId === tool.function?.name);
|
|
||||||
|
|
||||||
if (!toolModule) return;
|
|
||||||
|
|
||||||
const startParams = (() => {
|
|
||||||
try {
|
|
||||||
return json5.parse(tool.function.arguments);
|
|
||||||
} catch (error) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const moduleRunResponse = await dispatchWorkFlowV1({
|
|
||||||
...props,
|
|
||||||
runtimeModules: runtimeModules.map((module) => ({
|
|
||||||
...module,
|
|
||||||
isEntry: module.moduleId === toolModule.moduleId
|
|
||||||
})),
|
|
||||||
startParams
|
|
||||||
});
|
|
||||||
|
|
||||||
const stringToolResponse = (() => {
|
|
||||||
if (typeof moduleRunResponse.toolResponses === 'object') {
|
|
||||||
return JSON.stringify(moduleRunResponse.toolResponses, null, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return moduleRunResponse.toolResponses ? String(moduleRunResponse.toolResponses) : 'none';
|
|
||||||
})();
|
|
||||||
|
|
||||||
const toolMsgParams: ChatCompletionToolMessageParam = {
|
|
||||||
tool_call_id: tool.id,
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Tool,
|
|
||||||
name: tool.function.name,
|
|
||||||
content: stringToolResponse
|
|
||||||
};
|
|
||||||
|
|
||||||
if (stream && detail) {
|
|
||||||
responseWrite({
|
|
||||||
res,
|
|
||||||
event: SseResponseEventEnum.toolResponse,
|
|
||||||
data: JSON.stringify({
|
|
||||||
tool: {
|
|
||||||
id: tool.id,
|
|
||||||
toolName: '',
|
|
||||||
toolAvatar: '',
|
|
||||||
params: '',
|
|
||||||
response: stringToolResponse
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
moduleRunResponse,
|
|
||||||
toolMsgParams
|
|
||||||
};
|
|
||||||
})
|
|
||||||
)
|
|
||||||
).filter(Boolean) as ToolRunResponseType;
|
|
||||||
|
|
||||||
const flatToolsResponseData = toolsRunResponse.map((item) => item.moduleRunResponse).flat();
|
|
||||||
if (toolCalls.length > 0 && !res.closed) {
|
|
||||||
// Run the tool, combine its results, and perform another round of AI calls
|
|
||||||
const assistantToolMsgParams: ChatCompletionAssistantToolParam = {
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
|
||||||
tool_calls: toolCalls
|
|
||||||
};
|
|
||||||
const concatToolMessages = [
|
|
||||||
...filterMessages,
|
|
||||||
assistantToolMsgParams
|
|
||||||
] as ChatCompletionMessageParam[];
|
|
||||||
const tokens = await countGptMessagesTokens(concatToolMessages, tools);
|
|
||||||
const completeMessages = [
|
|
||||||
...concatToolMessages,
|
|
||||||
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
|
||||||
];
|
|
||||||
|
|
||||||
// console.log(tokens, 'tool');
|
|
||||||
|
|
||||||
if (stream && detail) {
|
|
||||||
responseWriteNodeStatus({
|
|
||||||
res,
|
|
||||||
name: module.name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// tool assistant
|
|
||||||
const toolAssistants = toolsRunResponse
|
|
||||||
.map((item) => {
|
|
||||||
const assistantResponses = item.moduleRunResponse.assistantResponses || [];
|
|
||||||
return assistantResponses;
|
|
||||||
})
|
|
||||||
.flat();
|
|
||||||
|
|
||||||
// tool node assistant
|
|
||||||
const adaptChatMessages = GPTMessages2Chats(completeMessages);
|
|
||||||
const toolNodeAssistant = adaptChatMessages.pop() as AIChatItemType;
|
|
||||||
|
|
||||||
const toolNodeAssistants = [
|
|
||||||
...assistantResponses,
|
|
||||||
...toolAssistants,
|
|
||||||
...toolNodeAssistant.value
|
|
||||||
];
|
|
||||||
|
|
||||||
// concat tool responses
|
|
||||||
const dispatchFlowResponse = response
|
|
||||||
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
|
||||||
: flatToolsResponseData;
|
|
||||||
|
|
||||||
/* check stop signal */
|
|
||||||
const hasStopSignal = flatToolsResponseData.some(
|
|
||||||
(item) => !!item.flowResponses?.find((item) => item.toolStop)
|
|
||||||
);
|
|
||||||
if (hasStopSignal) {
|
|
||||||
return {
|
|
||||||
dispatchFlowResponse,
|
|
||||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
|
||||||
completeMessages,
|
|
||||||
assistantResponses: toolNodeAssistants
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return runToolWithToolChoice(
|
|
||||||
{
|
|
||||||
...props,
|
|
||||||
messages: completeMessages
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dispatchFlowResponse,
|
|
||||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
|
||||||
assistantResponses: toolNodeAssistants
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} 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);
|
|
||||||
// console.log(tokens, 'response token');
|
|
||||||
|
|
||||||
// concat tool assistant
|
|
||||||
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
|
|
||||||
|
|
||||||
return {
|
|
||||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
|
||||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
|
||||||
completeMessages,
|
|
||||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async function streamResponse({
|
|
||||||
res,
|
|
||||||
detail,
|
|
||||||
toolModules,
|
|
||||||
stream
|
|
||||||
}: {
|
|
||||||
res: NextApiResponse;
|
|
||||||
detail: boolean;
|
|
||||||
toolModules: ToolModuleItemType[];
|
|
||||||
stream: StreamChatType;
|
|
||||||
}) {
|
|
||||||
const write = responseWriteController({
|
|
||||||
res,
|
|
||||||
readStream: stream
|
|
||||||
});
|
|
||||||
|
|
||||||
let textAnswer = '';
|
|
||||||
let toolCalls: ChatCompletionMessageToolCall[] = [];
|
|
||||||
|
|
||||||
for await (const part of stream) {
|
|
||||||
if (res.closed) {
|
|
||||||
stream.controller?.abort();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseChoice = part.choices?.[0]?.delta;
|
|
||||||
// console.log(JSON.stringify(responseChoice, null, 2));
|
|
||||||
if (responseChoice?.content) {
|
|
||||||
const content = responseChoice.content || '';
|
|
||||||
textAnswer += content;
|
|
||||||
|
|
||||||
responseWrite({
|
|
||||||
write,
|
|
||||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
|
||||||
data: textAdaptGptResponse({
|
|
||||||
text: content
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} else if (responseChoice?.tool_calls?.[0]) {
|
|
||||||
const toolCall: ChatCompletionMessageToolCall = responseChoice.tool_calls[0];
|
|
||||||
|
|
||||||
// 流响应中,每次只会返回一个工具. 如果带了 id,说明是执行一个工具
|
|
||||||
if (toolCall.id) {
|
|
||||||
const toolModule = toolModules.find(
|
|
||||||
(module) => module.moduleId === toolCall.function?.name
|
|
||||||
);
|
|
||||||
|
|
||||||
if (toolModule) {
|
|
||||||
if (toolCall.function?.arguments === undefined) {
|
|
||||||
toolCall.function.arguments = '';
|
|
||||||
}
|
|
||||||
toolCalls.push({
|
|
||||||
...toolCall,
|
|
||||||
toolName: toolModule.name,
|
|
||||||
toolAvatar: toolModule.avatar
|
|
||||||
});
|
|
||||||
|
|
||||||
if (detail) {
|
|
||||||
responseWrite({
|
|
||||||
write,
|
|
||||||
event: SseResponseEventEnum.toolCall,
|
|
||||||
data: JSON.stringify({
|
|
||||||
tool: {
|
|
||||||
id: toolCall.id,
|
|
||||||
toolName: toolModule.name,
|
|
||||||
toolAvatar: toolModule.avatar,
|
|
||||||
functionName: toolCall.function.name,
|
|
||||||
params: toolCall.function.arguments,
|
|
||||||
response: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* arg 插入最后一个工具的参数里 */
|
|
||||||
const arg: string = responseChoice.tool_calls?.[0]?.function?.arguments;
|
|
||||||
const currentTool = toolCalls[toolCalls.length - 1];
|
|
||||||
if (currentTool) {
|
|
||||||
currentTool.function.arguments += arg;
|
|
||||||
|
|
||||||
if (detail) {
|
|
||||||
responseWrite({
|
|
||||||
write,
|
|
||||||
event: SseResponseEventEnum.toolParams,
|
|
||||||
data: JSON.stringify({
|
|
||||||
tool: {
|
|
||||||
id: currentTool.id,
|
|
||||||
toolName: '',
|
|
||||||
toolAvatar: '',
|
|
||||||
params: arg,
|
|
||||||
response: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!textAnswer && toolCalls.length === 0) {
|
|
||||||
return Promise.reject('LLM api response empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
return { answer: textAnswer, toolCalls };
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
|
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/node/type';
|
|
||||||
import type {
|
|
||||||
ModuleDispatchProps,
|
|
||||||
DispatchNodeResponseType
|
|
||||||
} from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
|
||||||
import type { DispatchFlowResponse } from '../../type.d';
|
|
||||||
import { AIChatItemValueItemType, ChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
|
||||||
|
|
||||||
export type DispatchToolModuleProps = ModuleDispatchProps<{
|
|
||||||
[NodeInputKeyEnum.history]?: ChatItemType[];
|
|
||||||
[NodeInputKeyEnum.aiModel]: string;
|
|
||||||
[NodeInputKeyEnum.aiSystemPrompt]: string;
|
|
||||||
[NodeInputKeyEnum.userChatInput]: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export type RunToolResponse = {
|
|
||||||
dispatchFlowResponse: DispatchFlowResponse[];
|
|
||||||
totalTokens: number;
|
|
||||||
completeMessages?: ChatCompletionMessageParam[];
|
|
||||||
assistantResponses?: AIChatItemValueItemType[];
|
|
||||||
};
|
|
||||||
export type ToolModuleItemType = RuntimeNodeItemType & {
|
|
||||||
toolParams: RuntimeNodeItemType['inputs'];
|
|
||||||
};
|
|
||||||
@@ -1,396 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import type { NextApiResponse } from 'next';
|
|
||||||
import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../chat/utils';
|
|
||||||
import type { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type.d';
|
|
||||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
|
||||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
|
||||||
import { getAIApi } from '../../../ai/config';
|
|
||||||
import type {
|
|
||||||
ChatCompletion,
|
|
||||||
ChatCompletionMessageParam,
|
|
||||||
StreamChatType
|
|
||||||
} from '@fastgpt/global/core/ai/type.d';
|
|
||||||
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
|
||||||
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
|
||||||
import { postTextCensor } from '../../../../common/api/requestPlusApi';
|
|
||||||
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
|
||||||
import type { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
|
||||||
import type { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import {
|
|
||||||
countMessagesTokens,
|
|
||||||
countGptMessagesTokens
|
|
||||||
} from '../../../../common/string/tiktoken/index';
|
|
||||||
import {
|
|
||||||
chats2GPTMessages,
|
|
||||||
getSystemPrompt,
|
|
||||||
GPTMessages2Chats,
|
|
||||||
runtimePrompt2ChatsValue
|
|
||||||
} from '@fastgpt/global/core/chat/adapt';
|
|
||||||
import {
|
|
||||||
Prompt_QuotePromptList,
|
|
||||||
Prompt_QuoteTemplateList
|
|
||||||
} from '@fastgpt/global/core/ai/prompt/AIChat';
|
|
||||||
import type { AIChatNodeProps } from '@fastgpt/global/core/workflow/runtime/type.d';
|
|
||||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { responseWrite, responseWriteController } from '../../../../common/response';
|
|
||||||
import { getLLMModel, ModelTypeEnum } from '../../../ai/model';
|
|
||||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import { getHistories } from '../utils';
|
|
||||||
import { filterSearchResultsByMaxChars } from '../../utils';
|
|
||||||
import { getHistoryPreview } from '@fastgpt/global/core/chat/utils';
|
|
||||||
|
|
||||||
export type ChatProps = ModuleDispatchProps<
|
|
||||||
AIChatNodeProps & {
|
|
||||||
[NodeInputKeyEnum.userChatInput]: string;
|
|
||||||
[NodeInputKeyEnum.history]?: ChatItemType[] | number;
|
|
||||||
[NodeInputKeyEnum.aiChatDatasetQuote]?: SearchDataResponseItemType[];
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
export type ChatResponse = DispatchNodeResultType<{
|
|
||||||
[NodeOutputKeyEnum.answerText]: string;
|
|
||||||
[NodeOutputKeyEnum.history]: ChatItemType[];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/* request openai chat */
|
|
||||||
export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResponse> => {
|
|
||||||
let {
|
|
||||||
res,
|
|
||||||
stream = false,
|
|
||||||
detail = false,
|
|
||||||
user,
|
|
||||||
histories,
|
|
||||||
module: { name, outputs },
|
|
||||||
inputFiles = [],
|
|
||||||
params: {
|
|
||||||
model,
|
|
||||||
temperature = 0,
|
|
||||||
maxToken = 4000,
|
|
||||||
history = 6,
|
|
||||||
quoteQA,
|
|
||||||
userChatInput,
|
|
||||||
isResponseAnswerText = true,
|
|
||||||
systemPrompt = '',
|
|
||||||
quoteTemplate,
|
|
||||||
quotePrompt
|
|
||||||
}
|
|
||||||
} = props;
|
|
||||||
if (!userChatInput && inputFiles.length === 0) {
|
|
||||||
return Promise.reject('Question is empty');
|
|
||||||
}
|
|
||||||
stream = stream && isResponseAnswerText;
|
|
||||||
|
|
||||||
const chatHistories = getHistories(history, histories);
|
|
||||||
|
|
||||||
// temperature adapt
|
|
||||||
const modelConstantsData = getLLMModel(model);
|
|
||||||
|
|
||||||
if (!modelConstantsData) {
|
|
||||||
return Promise.reject('The chat model is undefined, you need to select a chat model.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { quoteText } = await filterQuote({
|
|
||||||
quoteQA,
|
|
||||||
model: modelConstantsData,
|
|
||||||
quoteTemplate
|
|
||||||
});
|
|
||||||
|
|
||||||
// censor model and system key
|
|
||||||
if (modelConstantsData.censor && !user.openaiAccount?.key) {
|
|
||||||
await postTextCensor({
|
|
||||||
text: `${systemPrompt}
|
|
||||||
${quoteText}
|
|
||||||
${userChatInput}
|
|
||||||
`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { filterMessages } = await getChatMessages({
|
|
||||||
model: modelConstantsData,
|
|
||||||
histories: chatHistories,
|
|
||||||
quoteQA,
|
|
||||||
quoteText,
|
|
||||||
quotePrompt,
|
|
||||||
userChatInput,
|
|
||||||
inputFiles,
|
|
||||||
systemPrompt
|
|
||||||
});
|
|
||||||
|
|
||||||
const { max_tokens } = await getMaxTokens({
|
|
||||||
model: modelConstantsData,
|
|
||||||
maxToken,
|
|
||||||
filterMessages
|
|
||||||
});
|
|
||||||
|
|
||||||
// FastGPT temperature range: 1~10
|
|
||||||
temperature = +(modelConstantsData.maxTemperature * (temperature / 10)).toFixed(2);
|
|
||||||
temperature = Math.max(temperature, 0.01);
|
|
||||||
const ai = getAIApi({
|
|
||||||
userKey: user.openaiAccount,
|
|
||||||
timeout: 480000
|
|
||||||
});
|
|
||||||
|
|
||||||
const concatMessages = [
|
|
||||||
...(modelConstantsData.defaultSystemChatPrompt
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.System,
|
|
||||||
content: modelConstantsData.defaultSystemChatPrompt
|
|
||||||
}
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...filterMessages
|
|
||||||
] as ChatCompletionMessageParam[];
|
|
||||||
|
|
||||||
if (concatMessages.length === 0) {
|
|
||||||
return Promise.reject('core.chat.error.Messages empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadMessages = await loadRequestMessages({
|
|
||||||
messages: concatMessages,
|
|
||||||
useVision: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await ai.chat.completions.create(
|
|
||||||
{
|
|
||||||
...modelConstantsData?.defaultConfig,
|
|
||||||
model: modelConstantsData.model,
|
|
||||||
temperature,
|
|
||||||
max_tokens,
|
|
||||||
stream,
|
|
||||||
messages: loadMessages
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json, text/plain, */*'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const { answerText } = await (async () => {
|
|
||||||
if (res && stream) {
|
|
||||||
// sse response
|
|
||||||
const { answer } = await streamResponse({
|
|
||||||
res,
|
|
||||||
detail,
|
|
||||||
stream: response
|
|
||||||
});
|
|
||||||
|
|
||||||
targetResponse({ res, detail, outputs });
|
|
||||||
|
|
||||||
return {
|
|
||||||
answerText: answer
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const unStreamResponse = response as ChatCompletion;
|
|
||||||
const answer = unStreamResponse.choices?.[0]?.message?.content || '';
|
|
||||||
|
|
||||||
return {
|
|
||||||
answerText: answer
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const completeMessages = filterMessages.concat({
|
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
|
||||||
content: answerText
|
|
||||||
});
|
|
||||||
const chatCompleteMessages = GPTMessages2Chats(completeMessages);
|
|
||||||
|
|
||||||
const tokens = await countMessagesTokens(chatCompleteMessages);
|
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
|
||||||
model,
|
|
||||||
tokens,
|
|
||||||
modelType: ModelTypeEnum.llm
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
answerText,
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
|
||||||
model: modelName,
|
|
||||||
tokens,
|
|
||||||
query: `${userChatInput}`,
|
|
||||||
maxToken: max_tokens,
|
|
||||||
historyPreview: getHistoryPreview(chatCompleteMessages),
|
|
||||||
contextTotalLen: completeMessages.length
|
|
||||||
},
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
|
||||||
{
|
|
||||||
moduleName: name,
|
|
||||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
|
||||||
model: modelName,
|
|
||||||
tokens
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]: answerText,
|
|
||||||
history: chatCompleteMessages
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
async function filterQuote({
|
|
||||||
quoteQA = [],
|
|
||||||
model,
|
|
||||||
quoteTemplate
|
|
||||||
}: {
|
|
||||||
quoteQA: ChatProps['params']['quoteQA'];
|
|
||||||
model: LLMModelItemType;
|
|
||||||
quoteTemplate?: string;
|
|
||||||
}) {
|
|
||||||
function getValue(item: SearchDataResponseItemType, index: number) {
|
|
||||||
return replaceVariable(quoteTemplate || Prompt_QuoteTemplateList[0].value, {
|
|
||||||
q: item.q,
|
|
||||||
a: item.a,
|
|
||||||
source: item.sourceName,
|
|
||||||
sourceId: String(item.sourceId || 'UnKnow'),
|
|
||||||
index: index + 1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// slice filterSearch
|
|
||||||
const filterQuoteQA = await filterSearchResultsByMaxChars(quoteQA, model.quoteMaxToken);
|
|
||||||
|
|
||||||
const quoteText =
|
|
||||||
filterQuoteQA.length > 0
|
|
||||||
? `${filterQuoteQA.map((item, index) => getValue(item, index).trim()).join('\n------\n')}`
|
|
||||||
: '';
|
|
||||||
|
|
||||||
return {
|
|
||||||
quoteText
|
|
||||||
};
|
|
||||||
}
|
|
||||||
async function getChatMessages({
|
|
||||||
quotePrompt,
|
|
||||||
quoteText,
|
|
||||||
quoteQA,
|
|
||||||
histories = [],
|
|
||||||
systemPrompt,
|
|
||||||
userChatInput,
|
|
||||||
inputFiles,
|
|
||||||
model
|
|
||||||
}: {
|
|
||||||
quotePrompt?: string;
|
|
||||||
quoteText: string;
|
|
||||||
quoteQA: ChatProps['params']['quoteQA'];
|
|
||||||
histories: ChatItemType[];
|
|
||||||
systemPrompt: string;
|
|
||||||
userChatInput: string;
|
|
||||||
inputFiles: UserChatItemValueItemType['file'][];
|
|
||||||
model: LLMModelItemType;
|
|
||||||
}) {
|
|
||||||
const replaceInputValue =
|
|
||||||
quoteQA !== undefined
|
|
||||||
? replaceVariable(quotePrompt || Prompt_QuotePromptList[0].value, {
|
|
||||||
quote: quoteText,
|
|
||||||
question: userChatInput
|
|
||||||
})
|
|
||||||
: userChatInput;
|
|
||||||
|
|
||||||
const messages: ChatItemType[] = [
|
|
||||||
...getSystemPrompt(systemPrompt),
|
|
||||||
...histories,
|
|
||||||
{
|
|
||||||
obj: ChatRoleEnum.Human,
|
|
||||||
value: runtimePrompt2ChatsValue({
|
|
||||||
files: inputFiles,
|
|
||||||
text: replaceInputValue
|
|
||||||
})
|
|
||||||
}
|
|
||||||
];
|
|
||||||
const adaptMessages = chats2GPTMessages({ messages, reserveId: false });
|
|
||||||
|
|
||||||
const filterMessages = await filterGPTMessageByMaxTokens({
|
|
||||||
messages: adaptMessages,
|
|
||||||
maxTokens: model.maxContext - 300 // filter token. not response maxToken
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
filterMessages
|
|
||||||
};
|
|
||||||
}
|
|
||||||
async function getMaxTokens({
|
|
||||||
maxToken,
|
|
||||||
model,
|
|
||||||
filterMessages = []
|
|
||||||
}: {
|
|
||||||
maxToken: number;
|
|
||||||
model: LLMModelItemType;
|
|
||||||
filterMessages: ChatCompletionMessageParam[];
|
|
||||||
}) {
|
|
||||||
maxToken = Math.min(maxToken, model.maxResponse);
|
|
||||||
const tokensLimit = model.maxContext;
|
|
||||||
|
|
||||||
/* count response max token */
|
|
||||||
const promptsToken = await countGptMessagesTokens(filterMessages);
|
|
||||||
maxToken = promptsToken + maxToken > tokensLimit ? tokensLimit - promptsToken : maxToken;
|
|
||||||
|
|
||||||
if (maxToken <= 0) {
|
|
||||||
maxToken = 200;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
max_tokens: maxToken
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function targetResponse({
|
|
||||||
res,
|
|
||||||
outputs,
|
|
||||||
detail
|
|
||||||
}: {
|
|
||||||
res: NextApiResponse;
|
|
||||||
outputs: FlowNodeItemType['outputs'];
|
|
||||||
detail: boolean;
|
|
||||||
}) {
|
|
||||||
const targets =
|
|
||||||
outputs.find((output) => output.key === NodeOutputKeyEnum.answerText)?.targets || [];
|
|
||||||
|
|
||||||
if (targets.length === 0) return;
|
|
||||||
responseWrite({
|
|
||||||
res,
|
|
||||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
|
||||||
data: textAdaptGptResponse({
|
|
||||||
text: '\n'
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function streamResponse({
|
|
||||||
res,
|
|
||||||
detail,
|
|
||||||
stream
|
|
||||||
}: {
|
|
||||||
res: NextApiResponse;
|
|
||||||
detail: boolean;
|
|
||||||
stream: StreamChatType;
|
|
||||||
}) {
|
|
||||||
const write = responseWriteController({
|
|
||||||
res,
|
|
||||||
readStream: stream
|
|
||||||
});
|
|
||||||
let answer = '';
|
|
||||||
for await (const part of stream) {
|
|
||||||
if (res.closed) {
|
|
||||||
stream.controller?.abort();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const content = part.choices?.[0]?.delta?.content || '';
|
|
||||||
answer += content;
|
|
||||||
|
|
||||||
responseWrite({
|
|
||||||
write,
|
|
||||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
|
||||||
data: textAdaptGptResponse({
|
|
||||||
text: content
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!answer) {
|
|
||||||
return Promise.reject('core.chat.Chat API is error or undefined');
|
|
||||||
}
|
|
||||||
|
|
||||||
return { answer };
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { datasetSearchResultConcat } from '@fastgpt/global/core/dataset/search/utils';
|
|
||||||
import { filterSearchResultsByMaxChars } from '../../utils';
|
|
||||||
|
|
||||||
type DatasetConcatProps = ModuleDispatchProps<
|
|
||||||
{
|
|
||||||
[NodeInputKeyEnum.datasetMaxTokens]: number;
|
|
||||||
} & { [key: string]: SearchDataResponseItemType[] }
|
|
||||||
>;
|
|
||||||
type DatasetConcatResponse = {
|
|
||||||
[NodeOutputKeyEnum.datasetQuoteQA]: SearchDataResponseItemType[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function dispatchDatasetConcat(
|
|
||||||
props: DatasetConcatProps
|
|
||||||
): Promise<DatasetConcatResponse> {
|
|
||||||
const {
|
|
||||||
params: { limit = 1500, ...quoteMap }
|
|
||||||
} = props as DatasetConcatProps;
|
|
||||||
|
|
||||||
const quoteList = Object.values(quoteMap).filter((list) => Array.isArray(list));
|
|
||||||
|
|
||||||
const rrfConcatResults = datasetSearchResultConcat(
|
|
||||||
quoteList.map((list) => ({
|
|
||||||
k: 60,
|
|
||||||
list
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
[NodeOutputKeyEnum.datasetQuoteQA]: await filterSearchResultsByMaxChars(rrfConcatResults, limit)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import {
|
|
||||||
DispatchNodeResponseType,
|
|
||||||
DispatchNodeResultType
|
|
||||||
} from '@fastgpt/global/core/workflow/runtime/type.d';
|
|
||||||
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
|
||||||
import type { SelectedDatasetType } from '@fastgpt/global/core/workflow/api.d';
|
|
||||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { ModelTypeEnum, getLLMModel, getVectorModel } from '../../../ai/model';
|
|
||||||
import { searchDatasetData } from '../../../dataset/search/controller';
|
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
|
|
||||||
import { getHistories } from '../utils';
|
|
||||||
import { datasetSearchQueryExtension } from '../../../dataset/search/utils';
|
|
||||||
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
|
||||||
import { checkTeamReRankPermission } from '../../../../support/permission/teamLimit';
|
|
||||||
|
|
||||||
type DatasetSearchProps = ModuleDispatchProps<{
|
|
||||||
[NodeInputKeyEnum.datasetSelectList]: SelectedDatasetType;
|
|
||||||
[NodeInputKeyEnum.datasetSimilarity]: number;
|
|
||||||
[NodeInputKeyEnum.datasetMaxTokens]: number;
|
|
||||||
[NodeInputKeyEnum.datasetSearchMode]: `${DatasetSearchModeEnum}`;
|
|
||||||
[NodeInputKeyEnum.userChatInput]: string;
|
|
||||||
[NodeInputKeyEnum.datasetSearchUsingReRank]: boolean;
|
|
||||||
[NodeInputKeyEnum.datasetSearchUsingExtensionQuery]: boolean;
|
|
||||||
[NodeInputKeyEnum.datasetSearchExtensionModel]: string;
|
|
||||||
[NodeInputKeyEnum.datasetSearchExtensionBg]: string;
|
|
||||||
}>;
|
|
||||||
export type DatasetSearchResponse = DispatchNodeResultType<{
|
|
||||||
isEmpty?: boolean;
|
|
||||||
unEmpty?: boolean;
|
|
||||||
[NodeOutputKeyEnum.datasetQuoteQA]: SearchDataResponseItemType[];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export async function dispatchDatasetSearch(
|
|
||||||
props: DatasetSearchProps
|
|
||||||
): Promise<DatasetSearchResponse> {
|
|
||||||
const {
|
|
||||||
teamId,
|
|
||||||
histories,
|
|
||||||
module,
|
|
||||||
params: {
|
|
||||||
datasets = [],
|
|
||||||
similarity,
|
|
||||||
limit = 1500,
|
|
||||||
usingReRank,
|
|
||||||
searchMode,
|
|
||||||
userChatInput,
|
|
||||||
|
|
||||||
datasetSearchUsingExtensionQuery,
|
|
||||||
datasetSearchExtensionModel,
|
|
||||||
datasetSearchExtensionBg
|
|
||||||
}
|
|
||||||
} = props as DatasetSearchProps;
|
|
||||||
|
|
||||||
if (!Array.isArray(datasets)) {
|
|
||||||
return Promise.reject('Quote type error');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (datasets.length === 0) {
|
|
||||||
return Promise.reject('core.chat.error.Select dataset empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!userChatInput) {
|
|
||||||
return Promise.reject('core.chat.error.User input empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
// query extension
|
|
||||||
const extensionModel =
|
|
||||||
datasetSearchUsingExtensionQuery && datasetSearchExtensionModel
|
|
||||||
? getLLMModel(datasetSearchExtensionModel)
|
|
||||||
: undefined;
|
|
||||||
const { concatQueries, rewriteQuery, aiExtensionResult } = await datasetSearchQueryExtension({
|
|
||||||
query: userChatInput,
|
|
||||||
extensionModel,
|
|
||||||
extensionBg: datasetSearchExtensionBg,
|
|
||||||
histories: getHistories(6, histories)
|
|
||||||
});
|
|
||||||
|
|
||||||
// console.log(concatQueries, rewriteQuery, aiExtensionResult);
|
|
||||||
|
|
||||||
// get vector
|
|
||||||
const vectorModel = getVectorModel(datasets[0]?.vectorModel?.model);
|
|
||||||
|
|
||||||
// start search
|
|
||||||
const {
|
|
||||||
searchRes,
|
|
||||||
tokens,
|
|
||||||
usingSimilarityFilter,
|
|
||||||
usingReRank: searchUsingReRank
|
|
||||||
} = await searchDatasetData({
|
|
||||||
teamId,
|
|
||||||
reRankQuery: `${rewriteQuery}`,
|
|
||||||
queries: concatQueries,
|
|
||||||
model: vectorModel.model,
|
|
||||||
similarity,
|
|
||||||
limit,
|
|
||||||
datasetIds: datasets.map((item) => item.datasetId),
|
|
||||||
searchMode,
|
|
||||||
usingReRank: usingReRank && (await checkTeamReRankPermission(teamId))
|
|
||||||
});
|
|
||||||
|
|
||||||
// count bill results
|
|
||||||
// vector
|
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
|
||||||
model: vectorModel.model,
|
|
||||||
tokens,
|
|
||||||
modelType: ModelTypeEnum.vector
|
|
||||||
});
|
|
||||||
const responseData: DispatchNodeResponseType & { totalPoints: number } = {
|
|
||||||
totalPoints,
|
|
||||||
query: concatQueries.join('\n'),
|
|
||||||
model: modelName,
|
|
||||||
tokens,
|
|
||||||
similarity: usingSimilarityFilter ? similarity : undefined,
|
|
||||||
limit,
|
|
||||||
searchMode,
|
|
||||||
searchUsingReRank: searchUsingReRank,
|
|
||||||
quoteList: searchRes
|
|
||||||
};
|
|
||||||
const nodeDispatchUsages: ChatNodeUsageType[] = [
|
|
||||||
{
|
|
||||||
totalPoints,
|
|
||||||
moduleName: module.name,
|
|
||||||
model: modelName,
|
|
||||||
tokens
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
if (aiExtensionResult) {
|
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
|
||||||
model: aiExtensionResult.model,
|
|
||||||
tokens: aiExtensionResult.tokens,
|
|
||||||
modelType: ModelTypeEnum.llm
|
|
||||||
});
|
|
||||||
|
|
||||||
responseData.totalPoints += totalPoints;
|
|
||||||
responseData.tokens = aiExtensionResult.tokens;
|
|
||||||
responseData.extensionModel = modelName;
|
|
||||||
responseData.extensionResult =
|
|
||||||
aiExtensionResult.extensionQueries?.join('\n') ||
|
|
||||||
JSON.stringify(aiExtensionResult.extensionQueries);
|
|
||||||
|
|
||||||
nodeDispatchUsages.push({
|
|
||||||
totalPoints,
|
|
||||||
moduleName: 'core.module.template.Query extension',
|
|
||||||
model: modelName,
|
|
||||||
tokens: aiExtensionResult.tokens
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
isEmpty: searchRes.length === 0 ? true : undefined,
|
|
||||||
unEmpty: searchRes.length > 0 ? true : undefined,
|
|
||||||
quoteQA: searchRes,
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: responseData,
|
|
||||||
nodeDispatchUsages,
|
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]: searchRes.map((item) => ({
|
|
||||||
id: item.id,
|
|
||||||
text: `${item.q}\n${item.a}`.trim()
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,430 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import { NextApiResponse } from 'next';
|
|
||||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import type { ChatDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type.d';
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import type {
|
|
||||||
AIChatItemValueItemType,
|
|
||||||
ChatHistoryItemResType,
|
|
||||||
ToolRunResponseItemType
|
|
||||||
} from '@fastgpt/global/core/chat/type.d';
|
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
|
||||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
|
||||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
|
||||||
import { responseWriteNodeStatus } from '../../../common/response';
|
|
||||||
import { getSystemTime } from '@fastgpt/global/common/time/timezone';
|
|
||||||
|
|
||||||
import { dispatchChatInput } from './init/userChatInput';
|
|
||||||
import { dispatchChatCompletion } from './chat/oneapi';
|
|
||||||
import { dispatchDatasetSearch } from './dataset/search';
|
|
||||||
import { dispatchDatasetConcat } from './dataset/concat';
|
|
||||||
import { dispatchAnswer } from './tools/answer';
|
|
||||||
import { dispatchClassifyQuestion } from './agent/classifyQuestion';
|
|
||||||
import { dispatchContentExtract } from './agent/extract';
|
|
||||||
import { dispatchHttpRequest } from './tools/http';
|
|
||||||
import { dispatchHttp468Request } from './tools/http468';
|
|
||||||
import { dispatchAppRequest } from './tools/runApp';
|
|
||||||
import { dispatchQueryExtension } from './tools/queryExternsion';
|
|
||||||
import { dispatchRunPlugin } from './plugin/run';
|
|
||||||
import { dispatchPluginInput } from './plugin/runInput';
|
|
||||||
import { dispatchPluginOutput } from './plugin/runOutput';
|
|
||||||
import { checkTheModuleConnectedByTool, valueTypeFormat } from './utils';
|
|
||||||
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
|
||||||
import { dispatchRunTools } from './agent/runTool/index';
|
|
||||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
|
||||||
import { DispatchFlowResponse } from './type';
|
|
||||||
import { dispatchStopToolCall } from './agent/runTool/stopTool';
|
|
||||||
import { dispatchLafRequest } from './tools/runLaf';
|
|
||||||
|
|
||||||
const callbackMap: Record<string, Function> = {
|
|
||||||
questionInput: dispatchChatInput,
|
|
||||||
[FlowNodeTypeEnum.answerNode]: dispatchAnswer,
|
|
||||||
[FlowNodeTypeEnum.chatNode]: dispatchChatCompletion,
|
|
||||||
[FlowNodeTypeEnum.datasetSearchNode]: dispatchDatasetSearch,
|
|
||||||
[FlowNodeTypeEnum.datasetConcatNode]: dispatchDatasetConcat,
|
|
||||||
[FlowNodeTypeEnum.classifyQuestion]: dispatchClassifyQuestion,
|
|
||||||
[FlowNodeTypeEnum.contentExtract]: dispatchContentExtract,
|
|
||||||
[FlowNodeTypeEnum.httpRequest468]: dispatchHttp468Request,
|
|
||||||
[FlowNodeTypeEnum.runApp]: dispatchAppRequest,
|
|
||||||
[FlowNodeTypeEnum.pluginModule]: dispatchRunPlugin,
|
|
||||||
[FlowNodeTypeEnum.pluginInput]: dispatchPluginInput,
|
|
||||||
[FlowNodeTypeEnum.pluginOutput]: dispatchPluginOutput,
|
|
||||||
[FlowNodeTypeEnum.queryExtension]: dispatchQueryExtension,
|
|
||||||
[FlowNodeTypeEnum.tools]: dispatchRunTools,
|
|
||||||
[FlowNodeTypeEnum.stopTool]: dispatchStopToolCall,
|
|
||||||
[FlowNodeTypeEnum.lafModule]: dispatchLafRequest
|
|
||||||
};
|
|
||||||
|
|
||||||
/* running */
|
|
||||||
export async function dispatchWorkFlowV1({
|
|
||||||
res,
|
|
||||||
modules = [],
|
|
||||||
runtimeModules,
|
|
||||||
startParams = {},
|
|
||||||
histories = [],
|
|
||||||
variables = {},
|
|
||||||
user,
|
|
||||||
stream = false,
|
|
||||||
detail = false,
|
|
||||||
...props
|
|
||||||
}: ChatDispatchProps & {
|
|
||||||
modules?: FlowNodeItemType[]; // app modules
|
|
||||||
runtimeModules?: RuntimeNodeItemType[];
|
|
||||||
startParams?: Record<string, any>; // entry module params
|
|
||||||
}): Promise<DispatchFlowResponse> {
|
|
||||||
// set sse response headers
|
|
||||||
if (res && stream) {
|
|
||||||
res.setHeader('Content-Type', 'text/event-stream;charset=utf-8');
|
|
||||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
||||||
res.setHeader('X-Accel-Buffering', 'no');
|
|
||||||
res.setHeader('Cache-Control', 'no-cache, no-transform');
|
|
||||||
}
|
|
||||||
|
|
||||||
variables = {
|
|
||||||
...getSystemVariable({ timezone: user.timezone }),
|
|
||||||
...variables
|
|
||||||
};
|
|
||||||
const runningModules = runtimeModules ? runtimeModules : loadModules(modules, variables);
|
|
||||||
|
|
||||||
let chatResponses: ChatHistoryItemResType[] = []; // response request and save to database
|
|
||||||
let chatAssistantResponse: AIChatItemValueItemType[] = []; // The value will be returned to the user
|
|
||||||
let chatNodeUsages: ChatNodeUsageType[] = [];
|
|
||||||
let toolRunResponse: ToolRunResponseItemType;
|
|
||||||
let runningTime = Date.now();
|
|
||||||
|
|
||||||
/* Store special response field */
|
|
||||||
function pushStore(
|
|
||||||
{ inputs = [] }: RuntimeNodeItemType,
|
|
||||||
{
|
|
||||||
answerText = '',
|
|
||||||
responseData,
|
|
||||||
nodeDispatchUsages,
|
|
||||||
toolResponses,
|
|
||||||
assistantResponses
|
|
||||||
}: {
|
|
||||||
[NodeOutputKeyEnum.answerText]?: string;
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]?: ChatHistoryItemResType;
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]?: ChatNodeUsageType[];
|
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]?: ToolRunResponseItemType;
|
|
||||||
[DispatchNodeResponseKeyEnum.assistantResponses]?: AIChatItemValueItemType[]; // tool module, save the response value
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
const time = Date.now();
|
|
||||||
|
|
||||||
if (responseData) {
|
|
||||||
chatResponses.push({
|
|
||||||
...responseData,
|
|
||||||
runningTime: +((time - runningTime) / 1000).toFixed(2)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (nodeDispatchUsages) {
|
|
||||||
chatNodeUsages = chatNodeUsages.concat(nodeDispatchUsages);
|
|
||||||
props.maxRunTimes -= nodeDispatchUsages.length;
|
|
||||||
}
|
|
||||||
if (toolResponses !== undefined) {
|
|
||||||
if (Array.isArray(toolResponses) && toolResponses.length === 0) return;
|
|
||||||
if (typeof toolResponses === 'object' && Object.keys(toolResponses).length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
toolRunResponse = toolResponses;
|
|
||||||
}
|
|
||||||
if (assistantResponses) {
|
|
||||||
chatAssistantResponse = chatAssistantResponse.concat(assistantResponses);
|
|
||||||
}
|
|
||||||
|
|
||||||
// save assistant text response
|
|
||||||
if (answerText) {
|
|
||||||
const isResponseAnswerText =
|
|
||||||
inputs.find((item) => item.key === NodeInputKeyEnum.aiChatIsResponseText)?.value ?? true;
|
|
||||||
if (isResponseAnswerText) {
|
|
||||||
chatAssistantResponse.push({
|
|
||||||
type: ChatItemValueTypeEnum.text,
|
|
||||||
text: {
|
|
||||||
content: answerText
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runningTime = time;
|
|
||||||
}
|
|
||||||
/* Inject data into module input */
|
|
||||||
function moduleInput(module: RuntimeNodeItemType, data: Record<string, any> = {}) {
|
|
||||||
const updateInputValue = (key: string, value: any) => {
|
|
||||||
const index = module.inputs.findIndex((item: any) => item.key === key);
|
|
||||||
if (index === -1) return;
|
|
||||||
module.inputs[index].value = value;
|
|
||||||
};
|
|
||||||
Object.entries(data).map(([key, val]: any) => {
|
|
||||||
updateInputValue(key, val);
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Pass the output of the module to the next stage */
|
|
||||||
function moduleOutput(
|
|
||||||
module: RuntimeNodeItemType,
|
|
||||||
result: Record<string, any> = {}
|
|
||||||
): Promise<any> {
|
|
||||||
pushStore(module, result);
|
|
||||||
|
|
||||||
const nextRunModules: RuntimeNodeItemType[] = [];
|
|
||||||
|
|
||||||
// Assign the output value to the next module
|
|
||||||
module.outputs.map((outputItem) => {
|
|
||||||
if (result[outputItem.key] === undefined) return;
|
|
||||||
/* update output value */
|
|
||||||
outputItem.value = result[outputItem.key];
|
|
||||||
|
|
||||||
/* update target */
|
|
||||||
outputItem.targets.map((target: any) => {
|
|
||||||
// find module
|
|
||||||
const targetModule = runningModules.find((item) => item.moduleId === target.moduleId);
|
|
||||||
if (!targetModule) return;
|
|
||||||
|
|
||||||
// push to running queue
|
|
||||||
nextRunModules.push(targetModule);
|
|
||||||
|
|
||||||
// update input
|
|
||||||
moduleInput(targetModule, { [target.key]: outputItem.value });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ensure the uniqueness of running modules
|
|
||||||
const set = new Set<string>();
|
|
||||||
const filterModules = nextRunModules.filter((module) => {
|
|
||||||
if (set.has(module.moduleId)) return false;
|
|
||||||
set.add(module.moduleId);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return checkModulesCanRun(filterModules);
|
|
||||||
}
|
|
||||||
function checkModulesCanRun(modules: RuntimeNodeItemType[] = []) {
|
|
||||||
return Promise.all(
|
|
||||||
modules.map((module) => {
|
|
||||||
if (!module.inputs.find((item: any) => item.value === undefined)) {
|
|
||||||
// remove switch
|
|
||||||
moduleInput(module, { [NodeInputKeyEnum.switch]: undefined });
|
|
||||||
return moduleRun(module);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
async function moduleRun(module: RuntimeNodeItemType): Promise<any> {
|
|
||||||
if (res?.closed || props.maxRunTimes <= 0) return Promise.resolve();
|
|
||||||
|
|
||||||
if (res && stream && detail && module.showStatus) {
|
|
||||||
responseStatus({
|
|
||||||
res,
|
|
||||||
name: module.name,
|
|
||||||
status: 'running'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// get module running params
|
|
||||||
const params: Record<string, any> = {};
|
|
||||||
module.inputs.forEach((item) => {
|
|
||||||
params[item.key] = valueTypeFormat(item.value, item.valueType);
|
|
||||||
});
|
|
||||||
|
|
||||||
const dispatchData: ModuleDispatchProps<Record<string, any>> = {
|
|
||||||
...props,
|
|
||||||
res,
|
|
||||||
variables,
|
|
||||||
histories,
|
|
||||||
user,
|
|
||||||
stream,
|
|
||||||
detail,
|
|
||||||
module,
|
|
||||||
runtimeModules: runningModules,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
|
|
||||||
// run module
|
|
||||||
const dispatchRes: Record<string, any> = await (async () => {
|
|
||||||
if (callbackMap[module.flowType]) {
|
|
||||||
return callbackMap[module.flowType](dispatchData);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
})();
|
|
||||||
|
|
||||||
// format response data. Add modulename and module type
|
|
||||||
const formatResponseData: ChatHistoryItemResType = (() => {
|
|
||||||
if (!dispatchRes[DispatchNodeResponseKeyEnum.nodeResponse]) return undefined;
|
|
||||||
return {
|
|
||||||
moduleName: module.name,
|
|
||||||
moduleType: module.flowType,
|
|
||||||
...dispatchRes[DispatchNodeResponseKeyEnum.nodeResponse]
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
// Add output default value
|
|
||||||
module.outputs.forEach((item) => {
|
|
||||||
if (!item.required) return;
|
|
||||||
if (dispatchRes[item.key] !== undefined) return;
|
|
||||||
dispatchRes[item.key] = valueTypeFormat(item.defaultValue, item.valueType);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Pass userChatInput
|
|
||||||
const hasUserChatInputTarget = !!module.outputs.find(
|
|
||||||
(item) => item.key === NodeOutputKeyEnum.userChatInput
|
|
||||||
)?.targets?.length;
|
|
||||||
|
|
||||||
return moduleOutput(module, {
|
|
||||||
finish: true,
|
|
||||||
[NodeOutputKeyEnum.userChatInput]: hasUserChatInputTarget
|
|
||||||
? params[NodeOutputKeyEnum.userChatInput]
|
|
||||||
: undefined,
|
|
||||||
...dispatchRes,
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: formatResponseData,
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]:
|
|
||||||
dispatchRes[DispatchNodeResponseKeyEnum.nodeDispatchUsages]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// start process width initInput
|
|
||||||
const initModules = runningModules.filter((item) => item.isEntry);
|
|
||||||
// reset entry
|
|
||||||
modules.forEach((item) => {
|
|
||||||
item.isEntry = false;
|
|
||||||
});
|
|
||||||
// console.log(JSON.stringify(runningModules, null, 2));
|
|
||||||
initModules.map((module) =>
|
|
||||||
moduleInput(module, {
|
|
||||||
...startParams,
|
|
||||||
history: [] // abandon history field. History module will get histories from other fields.
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
await checkModulesCanRun(initModules);
|
|
||||||
|
|
||||||
// focus try to run pluginOutput
|
|
||||||
const pluginOutputModule = runningModules.find(
|
|
||||||
(item) => item.flowType === FlowNodeTypeEnum.pluginOutput
|
|
||||||
);
|
|
||||||
if (pluginOutputModule) {
|
|
||||||
await moduleRun(pluginOutputModule);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
flowResponses: chatResponses,
|
|
||||||
flowUsages: chatNodeUsages,
|
|
||||||
[DispatchNodeResponseKeyEnum.assistantResponses]:
|
|
||||||
concatAssistantResponseAnswerText(chatAssistantResponse),
|
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* init store modules to running modules */
|
|
||||||
function loadModules(
|
|
||||||
modules: FlowNodeItemType[],
|
|
||||||
variables: Record<string, any>
|
|
||||||
): RuntimeNodeItemType[] {
|
|
||||||
return modules
|
|
||||||
.filter((item) => {
|
|
||||||
return ![FlowNodeTypeEnum.userGuide].includes(item.moduleId as any);
|
|
||||||
})
|
|
||||||
.map<RuntimeNodeItemType>((module) => {
|
|
||||||
return {
|
|
||||||
moduleId: module.moduleId,
|
|
||||||
name: module.name,
|
|
||||||
avatar: module.avatar,
|
|
||||||
intro: module.intro,
|
|
||||||
flowType: module.flowType,
|
|
||||||
showStatus: module.showStatus,
|
|
||||||
isEntry: module.isEntry,
|
|
||||||
inputs: module.inputs
|
|
||||||
.filter(
|
|
||||||
/*
|
|
||||||
1. system input must be save
|
|
||||||
2. connected by source handle
|
|
||||||
3. manual input value or have default value
|
|
||||||
4. For the module connected by the tool, leave the toolDescription input
|
|
||||||
*/
|
|
||||||
(item) => {
|
|
||||||
const isTool = checkTheModuleConnectedByTool(modules, module);
|
|
||||||
|
|
||||||
if (isTool && item.toolDescription) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return item.type === 'systemInput' || item.connected || item.value !== undefined;
|
|
||||||
}
|
|
||||||
) // filter unconnected target input
|
|
||||||
.map((item) => {
|
|
||||||
const replace = ['string'].includes(typeof item.value);
|
|
||||||
|
|
||||||
return {
|
|
||||||
key: item.key,
|
|
||||||
// variables replace
|
|
||||||
value: replace ? replaceVariable(item.value, variables) : item.value,
|
|
||||||
valueType: item.valueType,
|
|
||||||
required: item.required,
|
|
||||||
toolDescription: item.toolDescription
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
outputs: module.outputs
|
|
||||||
.map((item) => ({
|
|
||||||
key: item.key,
|
|
||||||
required: item.required,
|
|
||||||
defaultValue: item.defaultValue,
|
|
||||||
answer: item.key === NodeOutputKeyEnum.answerText,
|
|
||||||
value: undefined,
|
|
||||||
valueType: item.valueType,
|
|
||||||
targets: item.targets
|
|
||||||
}))
|
|
||||||
.sort((a, b) => {
|
|
||||||
// finish output always at last
|
|
||||||
if (a.key === NodeOutputKeyEnum.finish) return 1;
|
|
||||||
if (b.key === NodeOutputKeyEnum.finish) return -1;
|
|
||||||
return 0;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* sse response modules staus */
|
|
||||||
export function responseStatus({
|
|
||||||
res,
|
|
||||||
status,
|
|
||||||
name
|
|
||||||
}: {
|
|
||||||
res: NextApiResponse;
|
|
||||||
status?: 'running' | 'finish';
|
|
||||||
name?: string;
|
|
||||||
}) {
|
|
||||||
if (!name) return;
|
|
||||||
responseWriteNodeStatus({
|
|
||||||
res,
|
|
||||||
name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get system variable */
|
|
||||||
export function getSystemVariable({ timezone }: { timezone: string }) {
|
|
||||||
return {
|
|
||||||
cTime: getSystemTime(timezone)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const concatAssistantResponseAnswerText = (response: AIChatItemValueItemType[]) => {
|
|
||||||
const result: AIChatItemValueItemType[] = [];
|
|
||||||
// 合并连续的text
|
|
||||||
for (let i = 0; i < response.length; i++) {
|
|
||||||
const item = response[i];
|
|
||||||
if (item.type === ChatItemValueTypeEnum.text) {
|
|
||||||
let text = item.text?.content || '';
|
|
||||||
const lastItem = result[result.length - 1];
|
|
||||||
if (lastItem && lastItem.type === ChatItemValueTypeEnum.text && lastItem.text?.content) {
|
|
||||||
lastItem.text.content += text;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { getHistories } from '../utils';
|
|
||||||
export type HistoryProps = ModuleDispatchProps<{
|
|
||||||
maxContext?: number;
|
|
||||||
[NodeInputKeyEnum.history]: ChatItemType[];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export const dispatchHistory = (props: Record<string, any>) => {
|
|
||||||
const {
|
|
||||||
histories,
|
|
||||||
params: { maxContext }
|
|
||||||
} = props as HistoryProps;
|
|
||||||
|
|
||||||
return {
|
|
||||||
history: getHistories(maxContext, histories)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
export type UserChatInputProps = ModuleDispatchProps<{
|
|
||||||
[NodeInputKeyEnum.userChatInput]: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export const dispatchChatInput = (props: Record<string, any>) => {
|
|
||||||
const {
|
|
||||||
params: { userChatInput }
|
|
||||||
} = props as UserChatInputProps;
|
|
||||||
return {
|
|
||||||
userChatInput
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { dispatchWorkFlowV1 } from '../index';
|
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
|
||||||
import {
|
|
||||||
FlowNodeTemplateTypeEnum,
|
|
||||||
NodeInputKeyEnum
|
|
||||||
} from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import { splitCombinePluginId } from '../../../app/plugin/controller';
|
|
||||||
import { setEntryEntries, DYNAMIC_INPUT_KEY } from '../utils';
|
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { PluginRuntimeType, PluginTemplateType } from '@fastgpt/global/core/plugin/type';
|
|
||||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
|
||||||
import { MongoPlugin } from '../../../plugin/schema';
|
|
||||||
|
|
||||||
type RunPluginProps = ModuleDispatchProps<{
|
|
||||||
[NodeInputKeyEnum.pluginId]: string;
|
|
||||||
[key: string]: any;
|
|
||||||
}>;
|
|
||||||
type RunPluginResponse = DispatchNodeResultType<{}>;
|
|
||||||
|
|
||||||
const getPluginTemplateById = async (id: string): Promise<PluginTemplateType> => {
|
|
||||||
const { source, pluginId } = await splitCombinePluginId(id);
|
|
||||||
if (source === PluginSourceEnum.community) {
|
|
||||||
const item = global.communityPluginsV1?.find((plugin) => plugin.id === pluginId);
|
|
||||||
|
|
||||||
if (!item) return Promise.reject('plugin not found');
|
|
||||||
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
if (source === PluginSourceEnum.personal) {
|
|
||||||
const item = await MongoPlugin.findById(id).lean();
|
|
||||||
if (!item) return Promise.reject('plugin not found');
|
|
||||||
return {
|
|
||||||
id: String(item._id),
|
|
||||||
teamId: String(item.teamId),
|
|
||||||
name: item.name,
|
|
||||||
avatar: item.avatar,
|
|
||||||
intro: item.intro,
|
|
||||||
showStatus: true,
|
|
||||||
source: PluginSourceEnum.personal,
|
|
||||||
modules: item.modules,
|
|
||||||
templateType: FlowNodeTemplateTypeEnum.teamApp
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return Promise.reject('plugin not found');
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPluginRuntimeById = async (id: string): Promise<PluginRuntimeType> => {
|
|
||||||
const plugin = await getPluginTemplateById(id);
|
|
||||||
|
|
||||||
return {
|
|
||||||
teamId: plugin.teamId,
|
|
||||||
name: plugin.name,
|
|
||||||
avatar: plugin.avatar,
|
|
||||||
showStatus: plugin.showStatus,
|
|
||||||
modules: plugin.modules
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPluginResponse> => {
|
|
||||||
const {
|
|
||||||
mode,
|
|
||||||
teamId,
|
|
||||||
tmbId,
|
|
||||||
params: { pluginId, ...data }
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
if (!pluginId) {
|
|
||||||
return Promise.reject('pluginId can not find');
|
|
||||||
}
|
|
||||||
|
|
||||||
const plugin = await getPluginRuntimeById(pluginId);
|
|
||||||
if (plugin.teamId && plugin.teamId !== teamId) {
|
|
||||||
return Promise.reject('plugin not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// concat dynamic inputs
|
|
||||||
const inputModule = plugin.modules.find((item) => item.flowType === FlowNodeTypeEnum.pluginInput);
|
|
||||||
if (!inputModule) return Promise.reject('Plugin error, It has no set input.');
|
|
||||||
const hasDynamicInput = inputModule.inputs.find((input) => input.key === DYNAMIC_INPUT_KEY);
|
|
||||||
|
|
||||||
const startParams: Record<string, any> = (() => {
|
|
||||||
if (!hasDynamicInput) return data;
|
|
||||||
|
|
||||||
const params: Record<string, any> = {
|
|
||||||
[DYNAMIC_INPUT_KEY]: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const key in data) {
|
|
||||||
const input = inputModule.inputs.find((input) => input.key === key);
|
|
||||||
if (input) {
|
|
||||||
params[key] = data[key];
|
|
||||||
} else {
|
|
||||||
params[DYNAMIC_INPUT_KEY][key] = data[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return params;
|
|
||||||
})();
|
|
||||||
|
|
||||||
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlowV1({
|
|
||||||
...props,
|
|
||||||
modules: setEntryEntries(plugin.modules).map((module) => ({
|
|
||||||
...module,
|
|
||||||
showStatus: false
|
|
||||||
})),
|
|
||||||
runtimeModules: undefined, // must reset
|
|
||||||
startParams
|
|
||||||
});
|
|
||||||
|
|
||||||
const output = flowResponses.find((item) => item.moduleType === FlowNodeTypeEnum.pluginOutput);
|
|
||||||
|
|
||||||
if (output) {
|
|
||||||
output.moduleLogo = plugin.avatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
assistantResponses,
|
|
||||||
// responseData, // debug
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
moduleLogo: plugin.avatar,
|
|
||||||
totalPoints: flowResponses.reduce((sum, item) => sum + (item.totalPoints || 0), 0),
|
|
||||||
pluginOutput: output?.pluginOutput,
|
|
||||||
pluginDetail:
|
|
||||||
mode === 'test' && plugin.teamId === teamId
|
|
||||||
? flowResponses.filter((item) => {
|
|
||||||
const filterArr = [FlowNodeTypeEnum.pluginOutput];
|
|
||||||
return !filterArr.includes(item.moduleType as any);
|
|
||||||
})
|
|
||||||
: undefined
|
|
||||||
},
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
|
||||||
{
|
|
||||||
moduleName: plugin.name,
|
|
||||||
totalPoints: flowUsages.reduce((sum, item) => sum + (item.totalPoints || 0), 0),
|
|
||||||
model: plugin.name,
|
|
||||||
tokens: 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]: output?.pluginOutput ? output.pluginOutput : {},
|
|
||||||
...(output ? output.pluginOutput : {})
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
|
|
||||||
export type PluginInputProps = ModuleDispatchProps<{
|
|
||||||
[key: string]: any;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export const dispatchPluginInput = (props: PluginInputProps) => {
|
|
||||||
const { params } = props;
|
|
||||||
|
|
||||||
return params;
|
|
||||||
};
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type.d';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
|
|
||||||
export type PluginOutputProps = ModuleDispatchProps<{
|
|
||||||
[key: string]: any;
|
|
||||||
}>;
|
|
||||||
export type PluginOutputResponse = DispatchNodeResultType<{}>;
|
|
||||||
|
|
||||||
export const dispatchPluginOutput = (props: PluginOutputProps): PluginOutputResponse => {
|
|
||||||
const { params } = props;
|
|
||||||
|
|
||||||
return {
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
totalPoints: 0,
|
|
||||||
pluginOutput: params
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import { responseWrite } from '../../../../common/response';
|
|
||||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
export type AnswerProps = ModuleDispatchProps<{
|
|
||||||
text: string;
|
|
||||||
}>;
|
|
||||||
export type AnswerResponse = DispatchNodeResultType<{
|
|
||||||
[NodeOutputKeyEnum.answerText]: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export const dispatchAnswer = (props: Record<string, any>): AnswerResponse => {
|
|
||||||
const {
|
|
||||||
res,
|
|
||||||
detail,
|
|
||||||
stream,
|
|
||||||
params: { text = '' }
|
|
||||||
} = props as AnswerProps;
|
|
||||||
|
|
||||||
const formatText = typeof text === 'string' ? text : JSON.stringify(text, null, 2);
|
|
||||||
|
|
||||||
if (stream) {
|
|
||||||
responseWrite({
|
|
||||||
res,
|
|
||||||
event: detail ? SseResponseEventEnum.fastAnswer : undefined,
|
|
||||||
data: textAdaptGptResponse({
|
|
||||||
text: `\n${formatText}`
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
[NodeOutputKeyEnum.answerText]: `\n${formatText}`
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { valueTypeFormat } from '../utils';
|
|
||||||
import { SERVICE_LOCAL_HOST } from '../../../../common/system/tools';
|
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { DYNAMIC_INPUT_KEY } from '../utils';
|
|
||||||
|
|
||||||
type HttpRequestProps = ModuleDispatchProps<{
|
|
||||||
[NodeInputKeyEnum.abandon_httpUrl]: string;
|
|
||||||
[NodeInputKeyEnum.httpMethod]: string;
|
|
||||||
[NodeInputKeyEnum.httpReqUrl]: string;
|
|
||||||
[NodeInputKeyEnum.httpHeaders]: string;
|
|
||||||
[key: string]: any;
|
|
||||||
}>;
|
|
||||||
type HttpResponse = DispatchNodeResultType<{
|
|
||||||
[NodeOutputKeyEnum.failed]?: boolean;
|
|
||||||
[key: string]: any;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
const flatDynamicParams = (params: Record<string, any>) => {
|
|
||||||
const dynamicParams = params[DYNAMIC_INPUT_KEY];
|
|
||||||
if (!dynamicParams) return params;
|
|
||||||
return {
|
|
||||||
...params,
|
|
||||||
...dynamicParams,
|
|
||||||
[DYNAMIC_INPUT_KEY]: undefined
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const dispatchHttpRequest = async (props: HttpRequestProps): Promise<HttpResponse> => {
|
|
||||||
let {
|
|
||||||
appId,
|
|
||||||
chatId,
|
|
||||||
responseChatItemId,
|
|
||||||
variables,
|
|
||||||
module: { outputs },
|
|
||||||
params: {
|
|
||||||
system_httpMethod: httpMethod = 'POST',
|
|
||||||
system_httpReqUrl: httpReqUrl,
|
|
||||||
system_httpHeader: httpHeader,
|
|
||||||
...body
|
|
||||||
}
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
if (!httpReqUrl) {
|
|
||||||
return Promise.reject('Http url is empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
body = flatDynamicParams(body);
|
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
appId,
|
|
||||||
chatId,
|
|
||||||
responseChatItemId,
|
|
||||||
variables,
|
|
||||||
data: body
|
|
||||||
};
|
|
||||||
const requestQuery = {
|
|
||||||
appId,
|
|
||||||
chatId,
|
|
||||||
...variables,
|
|
||||||
...body
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatBody = transformFlatJson({ ...requestBody });
|
|
||||||
|
|
||||||
// parse header
|
|
||||||
const headers = await (() => {
|
|
||||||
try {
|
|
||||||
if (!httpHeader) return {};
|
|
||||||
return JSON.parse(httpHeader);
|
|
||||||
} catch (error) {
|
|
||||||
return Promise.reject('Header 为非法 JSON 格式');
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetchData({
|
|
||||||
method: httpMethod,
|
|
||||||
url: httpReqUrl,
|
|
||||||
headers,
|
|
||||||
body: formatBody,
|
|
||||||
query: requestQuery
|
|
||||||
});
|
|
||||||
|
|
||||||
// format output value type
|
|
||||||
const results: Record<string, any> = {};
|
|
||||||
for (const key in response) {
|
|
||||||
const output = outputs.find((item) => item.key === key);
|
|
||||||
if (!output) continue;
|
|
||||||
results[key] = valueTypeFormat(response[key], output.valueType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
totalPoints: 0,
|
|
||||||
body: formatBody,
|
|
||||||
httpResult: response
|
|
||||||
},
|
|
||||||
...results
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
[NodeOutputKeyEnum.failed]: true,
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
totalPoints: 0,
|
|
||||||
body: formatBody,
|
|
||||||
httpResult: { error }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async function fetchData({
|
|
||||||
method,
|
|
||||||
url,
|
|
||||||
headers,
|
|
||||||
body,
|
|
||||||
query
|
|
||||||
}: {
|
|
||||||
method: string;
|
|
||||||
url: string;
|
|
||||||
headers: Record<string, any>;
|
|
||||||
body: Record<string, any>;
|
|
||||||
query: Record<string, any>;
|
|
||||||
}): Promise<Record<string, any>> {
|
|
||||||
const { data: response } = await axios<Record<string, any>>({
|
|
||||||
method,
|
|
||||||
baseURL: `http://${SERVICE_LOCAL_HOST}`,
|
|
||||||
url,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
...headers
|
|
||||||
},
|
|
||||||
timeout: 360000,
|
|
||||||
params: method === 'GET' ? query : {},
|
|
||||||
data: method === 'POST' ? body : {}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
parse the json:
|
|
||||||
{
|
|
||||||
user: {
|
|
||||||
name: 'xxx',
|
|
||||||
age: 12
|
|
||||||
},
|
|
||||||
list: [
|
|
||||||
{
|
|
||||||
name: 'xxx',
|
|
||||||
age: 50
|
|
||||||
},
|
|
||||||
[{ test: 22 }]
|
|
||||||
],
|
|
||||||
psw: 'xxx'
|
|
||||||
}
|
|
||||||
|
|
||||||
result: {
|
|
||||||
'user': { name: 'xxx', age: 12 },
|
|
||||||
'user.name': 'xxx',
|
|
||||||
'user.age': 12,
|
|
||||||
'list': [ { name: 'xxx', age: 50 }, [ [Object] ] ],
|
|
||||||
'list[0]': { name: 'xxx', age: 50 },
|
|
||||||
'list[0].name': 'xxx',
|
|
||||||
'list[0].age': 50,
|
|
||||||
'list[1]': [ { test: 22 } ],
|
|
||||||
'list[1][0]': { test: 22 },
|
|
||||||
'list[1][0].test': 22,
|
|
||||||
'psw': 'xxx'
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const parseJson = (obj: Record<string, any>, prefix = '') => {
|
|
||||||
let result: Record<string, any> = {};
|
|
||||||
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
for (let i = 0; i < obj.length; i++) {
|
|
||||||
result[`${prefix}[${i}]`] = obj[i];
|
|
||||||
|
|
||||||
if (Array.isArray(obj[i])) {
|
|
||||||
result = {
|
|
||||||
...result,
|
|
||||||
...parseJson(obj[i], `${prefix}[${i}]`)
|
|
||||||
};
|
|
||||||
} else if (typeof obj[i] === 'object') {
|
|
||||||
result = {
|
|
||||||
...result,
|
|
||||||
...parseJson(obj[i], `${prefix}[${i}].`)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (typeof obj == 'object') {
|
|
||||||
for (const key in obj) {
|
|
||||||
result[`${prefix}${key}`] = obj[key];
|
|
||||||
|
|
||||||
if (Array.isArray(obj[key])) {
|
|
||||||
result = {
|
|
||||||
...result,
|
|
||||||
...parseJson(obj[key], `${prefix}${key}`)
|
|
||||||
};
|
|
||||||
} else if (typeof obj[key] === 'object') {
|
|
||||||
result = {
|
|
||||||
...result,
|
|
||||||
...parseJson(obj[key], `${prefix}${key}.`)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
return parseJson(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
function transformFlatJson(obj: Record<string, any>) {
|
|
||||||
for (let key in obj) {
|
|
||||||
if (typeof obj[key] === 'object') {
|
|
||||||
transformFlatJson(obj[key]);
|
|
||||||
}
|
|
||||||
if (key.includes('.')) {
|
|
||||||
let parts = key.split('.');
|
|
||||||
if (parts.length <= 1) continue;
|
|
||||||
|
|
||||||
const firstKey = parts.shift();
|
|
||||||
|
|
||||||
if (!firstKey) continue;
|
|
||||||
|
|
||||||
const lastKey = parts.join('.');
|
|
||||||
|
|
||||||
if (obj[firstKey]) {
|
|
||||||
obj[firstKey] = {
|
|
||||||
...obj[firstKey],
|
|
||||||
[lastKey]: obj[key]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
obj[firstKey] = { [lastKey]: obj[key] };
|
|
||||||
}
|
|
||||||
|
|
||||||
transformFlatJson(obj[firstKey]);
|
|
||||||
|
|
||||||
delete obj[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
@@ -1,294 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { DYNAMIC_INPUT_KEY, valueTypeFormat } from '../utils';
|
|
||||||
import { SERVICE_LOCAL_HOST } from '../../../../common/system/tools';
|
|
||||||
import { addLog } from '../../../../common/system/log';
|
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
|
||||||
|
|
||||||
type PropsArrType = {
|
|
||||||
key: string;
|
|
||||||
type: string;
|
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
type HttpRequestProps = ModuleDispatchProps<{
|
|
||||||
[NodeInputKeyEnum.abandon_httpUrl]: string;
|
|
||||||
[NodeInputKeyEnum.httpMethod]: string;
|
|
||||||
[NodeInputKeyEnum.httpReqUrl]: string;
|
|
||||||
[NodeInputKeyEnum.httpHeaders]: PropsArrType[];
|
|
||||||
[NodeInputKeyEnum.httpParams]: PropsArrType[];
|
|
||||||
[NodeInputKeyEnum.httpJsonBody]: string;
|
|
||||||
[DYNAMIC_INPUT_KEY]: Record<string, any>;
|
|
||||||
[key: string]: any;
|
|
||||||
}>;
|
|
||||||
type HttpResponse = DispatchNodeResultType<{
|
|
||||||
[NodeOutputKeyEnum.failed]?: boolean;
|
|
||||||
[key: string]: any;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
|
|
||||||
|
|
||||||
export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<HttpResponse> => {
|
|
||||||
let {
|
|
||||||
appId,
|
|
||||||
chatId,
|
|
||||||
responseChatItemId,
|
|
||||||
variables,
|
|
||||||
module: { outputs },
|
|
||||||
histories,
|
|
||||||
params: {
|
|
||||||
system_httpMethod: httpMethod = 'POST',
|
|
||||||
system_httpReqUrl: httpReqUrl,
|
|
||||||
system_httpHeader: httpHeader,
|
|
||||||
system_httpParams: httpParams = [],
|
|
||||||
system_httpJsonBody: httpJsonBody,
|
|
||||||
[DYNAMIC_INPUT_KEY]: dynamicInput,
|
|
||||||
...body
|
|
||||||
}
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
if (!httpReqUrl) {
|
|
||||||
return Promise.reject('Http url is empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
const concatVariables = {
|
|
||||||
appId,
|
|
||||||
chatId,
|
|
||||||
responseChatItemId,
|
|
||||||
...variables,
|
|
||||||
histories: histories.slice(-10),
|
|
||||||
...body
|
|
||||||
};
|
|
||||||
|
|
||||||
httpReqUrl = replaceVariable(httpReqUrl, concatVariables);
|
|
||||||
// parse header
|
|
||||||
const headers = await (() => {
|
|
||||||
try {
|
|
||||||
if (!httpHeader || httpHeader.length === 0) return {};
|
|
||||||
// array
|
|
||||||
return httpHeader.reduce((acc: Record<string, string>, item) => {
|
|
||||||
const key = replaceVariable(item.key, concatVariables);
|
|
||||||
const value = replaceVariable(item.value, concatVariables);
|
|
||||||
acc[key] = valueTypeFormat(value, 'string');
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
} catch (error) {
|
|
||||||
return Promise.reject('Header 为非法 JSON 格式');
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
const params = httpParams.reduce((acc: Record<string, string>, item) => {
|
|
||||||
const key = replaceVariable(item.key, concatVariables);
|
|
||||||
const value = replaceVariable(item.value, concatVariables);
|
|
||||||
acc[key] = valueTypeFormat(value, 'string');
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
const requestBody = await (() => {
|
|
||||||
if (!httpJsonBody) return { [DYNAMIC_INPUT_KEY]: dynamicInput };
|
|
||||||
httpJsonBody = replaceVariable(httpJsonBody, concatVariables);
|
|
||||||
try {
|
|
||||||
const jsonParse = JSON.parse(httpJsonBody);
|
|
||||||
const removeSignJson = removeUndefinedSign(jsonParse);
|
|
||||||
return { [DYNAMIC_INPUT_KEY]: dynamicInput, ...removeSignJson };
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return Promise.reject(`Invalid JSON body: ${httpJsonBody}`);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { formatResponse, rawResponse } = await fetchData({
|
|
||||||
method: httpMethod,
|
|
||||||
url: httpReqUrl,
|
|
||||||
headers,
|
|
||||||
body: requestBody,
|
|
||||||
params
|
|
||||||
});
|
|
||||||
|
|
||||||
// format output value type
|
|
||||||
const results: Record<string, any> = {};
|
|
||||||
for (const key in formatResponse) {
|
|
||||||
const output = outputs.find((item) => item.key === key);
|
|
||||||
if (!output) continue;
|
|
||||||
results[key] = valueTypeFormat(formatResponse[key], output.valueType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
totalPoints: 0,
|
|
||||||
params: Object.keys(params).length > 0 ? params : undefined,
|
|
||||||
body: Object.keys(requestBody).length > 0 ? requestBody : undefined,
|
|
||||||
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
||||||
httpResult: rawResponse
|
|
||||||
},
|
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]: results,
|
|
||||||
[NodeOutputKeyEnum.httpRawResponse]: rawResponse,
|
|
||||||
...results
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
addLog.error('Http request error', error);
|
|
||||||
return {
|
|
||||||
[NodeOutputKeyEnum.failed]: true,
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
totalPoints: 0,
|
|
||||||
params: Object.keys(params).length > 0 ? params : undefined,
|
|
||||||
body: Object.keys(requestBody).length > 0 ? requestBody : undefined,
|
|
||||||
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
||||||
httpResult: { error: formatHttpError(error) }
|
|
||||||
},
|
|
||||||
[NodeOutputKeyEnum.httpRawResponse]: getErrText(error)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async function fetchData({
|
|
||||||
method,
|
|
||||||
url,
|
|
||||||
headers,
|
|
||||||
body,
|
|
||||||
params
|
|
||||||
}: {
|
|
||||||
method: string;
|
|
||||||
url: string;
|
|
||||||
headers: Record<string, any>;
|
|
||||||
body: Record<string, any>;
|
|
||||||
params: Record<string, any>;
|
|
||||||
}): Promise<Record<string, any>> {
|
|
||||||
const { data: response } = await axios({
|
|
||||||
method,
|
|
||||||
baseURL: `http://${SERVICE_LOCAL_HOST}`,
|
|
||||||
url,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
...headers
|
|
||||||
},
|
|
||||||
timeout: 120000,
|
|
||||||
params: params,
|
|
||||||
data: ['POST', 'PUT', 'PATCH'].includes(method) ? body : undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
parse the json:
|
|
||||||
{
|
|
||||||
user: {
|
|
||||||
name: 'xxx',
|
|
||||||
age: 12
|
|
||||||
},
|
|
||||||
list: [
|
|
||||||
{
|
|
||||||
name: 'xxx',
|
|
||||||
age: 50
|
|
||||||
},
|
|
||||||
[{ test: 22 }]
|
|
||||||
],
|
|
||||||
psw: 'xxx'
|
|
||||||
}
|
|
||||||
|
|
||||||
result: {
|
|
||||||
'user': { name: 'xxx', age: 12 },
|
|
||||||
'user.name': 'xxx',
|
|
||||||
'user.age': 12,
|
|
||||||
'list': [ { name: 'xxx', age: 50 }, [ [Object] ] ],
|
|
||||||
'list[0]': { name: 'xxx', age: 50 },
|
|
||||||
'list[0].name': 'xxx',
|
|
||||||
'list[0].age': 50,
|
|
||||||
'list[1]': [ { test: 22 } ],
|
|
||||||
'list[1][0]': { test: 22 },
|
|
||||||
'list[1][0].test': 22,
|
|
||||||
'psw': 'xxx'
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const parseJson = (obj: Record<string, any>, prefix = '') => {
|
|
||||||
let result: Record<string, any> = {};
|
|
||||||
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
for (let i = 0; i < obj.length; i++) {
|
|
||||||
result[`${prefix}[${i}]`] = obj[i];
|
|
||||||
|
|
||||||
if (Array.isArray(obj[i])) {
|
|
||||||
result = {
|
|
||||||
...result,
|
|
||||||
...parseJson(obj[i], `${prefix}[${i}]`)
|
|
||||||
};
|
|
||||||
} else if (typeof obj[i] === 'object') {
|
|
||||||
result = {
|
|
||||||
...result,
|
|
||||||
...parseJson(obj[i], `${prefix}[${i}].`)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (typeof obj == 'object') {
|
|
||||||
for (const key in obj) {
|
|
||||||
result[`${prefix}${key}`] = obj[key];
|
|
||||||
|
|
||||||
if (Array.isArray(obj[key])) {
|
|
||||||
result = {
|
|
||||||
...result,
|
|
||||||
...parseJson(obj[key], `${prefix}${key}`)
|
|
||||||
};
|
|
||||||
} else if (typeof obj[key] === 'object') {
|
|
||||||
result = {
|
|
||||||
...result,
|
|
||||||
...parseJson(obj[key], `${prefix}${key}.`)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
formatResponse:
|
|
||||||
typeof response === 'object' && !Array.isArray(response) ? parseJson(response) : {},
|
|
||||||
rawResponse: response
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceVariable(text: string, obj: Record<string, any>) {
|
|
||||||
for (const [key, value] of Object.entries(obj)) {
|
|
||||||
if (value === undefined) {
|
|
||||||
text = text.replace(new RegExp(`{{${key}}}`, 'g'), UNDEFINED_SIGN);
|
|
||||||
} else {
|
|
||||||
const replacement = JSON.stringify(value);
|
|
||||||
const unquotedReplacement =
|
|
||||||
replacement.startsWith('"') && replacement.endsWith('"')
|
|
||||||
? replacement.slice(1, -1)
|
|
||||||
: replacement;
|
|
||||||
text = text.replace(new RegExp(`{{${key}}}`, 'g'), unquotedReplacement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return text || '';
|
|
||||||
}
|
|
||||||
function removeUndefinedSign(obj: Record<string, any>) {
|
|
||||||
for (const key in obj) {
|
|
||||||
if (obj[key] === UNDEFINED_SIGN) {
|
|
||||||
obj[key] = undefined;
|
|
||||||
} else if (Array.isArray(obj[key])) {
|
|
||||||
obj[key] = obj[key].map((item: any) => {
|
|
||||||
if (item === UNDEFINED_SIGN) {
|
|
||||||
return undefined;
|
|
||||||
} else if (typeof item === 'object') {
|
|
||||||
removeUndefinedSign(item);
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
} else if (typeof obj[key] === 'object') {
|
|
||||||
removeUndefinedSign(obj[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
function formatHttpError(error: any) {
|
|
||||||
return {
|
|
||||||
message: error?.message,
|
|
||||||
name: error?.name,
|
|
||||||
method: error?.config?.method,
|
|
||||||
baseURL: error?.config?.baseURL,
|
|
||||||
url: error?.config?.url,
|
|
||||||
code: error?.code,
|
|
||||||
status: error?.status
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import { ModelTypeEnum, getLLMModel } from '../../../../core/ai/model';
|
|
||||||
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
|
||||||
import { queryExtension } from '../../../../core/ai/functions/queryExtension';
|
|
||||||
import { getHistories } from '../utils';
|
|
||||||
import { hashStr } from '@fastgpt/global/common/string/tools';
|
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
|
|
||||||
type Props = ModuleDispatchProps<{
|
|
||||||
[NodeInputKeyEnum.aiModel]: string;
|
|
||||||
[NodeInputKeyEnum.aiSystemPrompt]?: string;
|
|
||||||
[NodeInputKeyEnum.history]?: ChatItemType[] | number;
|
|
||||||
[NodeInputKeyEnum.userChatInput]: string;
|
|
||||||
}>;
|
|
||||||
type Response = DispatchNodeResultType<{
|
|
||||||
[NodeOutputKeyEnum.text]: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export const dispatchQueryExtension = async ({
|
|
||||||
histories,
|
|
||||||
module,
|
|
||||||
params: { model, systemPrompt, history, userChatInput }
|
|
||||||
}: Props): Promise<Response> => {
|
|
||||||
if (!userChatInput) {
|
|
||||||
return Promise.reject('Question is empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryExtensionModel = getLLMModel(model);
|
|
||||||
const chatHistories = getHistories(history, histories);
|
|
||||||
|
|
||||||
const { extensionQueries, tokens } = await queryExtension({
|
|
||||||
chatBg: systemPrompt,
|
|
||||||
query: userChatInput,
|
|
||||||
histories: chatHistories,
|
|
||||||
model: queryExtensionModel.model
|
|
||||||
});
|
|
||||||
|
|
||||||
extensionQueries.unshift(userChatInput);
|
|
||||||
|
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
|
||||||
model: queryExtensionModel.model,
|
|
||||||
tokens,
|
|
||||||
modelType: ModelTypeEnum.llm
|
|
||||||
});
|
|
||||||
|
|
||||||
const set = new Set<string>();
|
|
||||||
const filterSameQueries = extensionQueries.filter((item) => {
|
|
||||||
// 删除所有的标点符号与空格等,只对文本进行比较
|
|
||||||
const str = hashStr(item.replace(/[^\p{L}\p{N}]/gu, ''));
|
|
||||||
if (set.has(str)) return false;
|
|
||||||
set.add(str);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
totalPoints,
|
|
||||||
model: modelName,
|
|
||||||
tokens,
|
|
||||||
query: userChatInput,
|
|
||||||
textOutput: JSON.stringify(filterSameQueries)
|
|
||||||
},
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
|
||||||
{
|
|
||||||
moduleName: module.name,
|
|
||||||
totalPoints,
|
|
||||||
model: modelName,
|
|
||||||
tokens
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[NodeOutputKeyEnum.text]: JSON.stringify(filterSameQueries)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { DYNAMIC_INPUT_KEY, valueTypeFormat } from '../utils';
|
|
||||||
import { SERVICE_LOCAL_HOST } from '../../../../common/system/tools';
|
|
||||||
import { addLog } from '../../../../common/system/log';
|
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
|
||||||
|
|
||||||
type LafRequestProps = ModuleDispatchProps<{
|
|
||||||
[NodeInputKeyEnum.httpReqUrl]: string;
|
|
||||||
[DYNAMIC_INPUT_KEY]: Record<string, any>;
|
|
||||||
[key: string]: any;
|
|
||||||
}>;
|
|
||||||
type LafResponse = DispatchNodeResultType<{
|
|
||||||
[NodeOutputKeyEnum.failed]?: boolean;
|
|
||||||
[key: string]: any;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
|
|
||||||
|
|
||||||
export const dispatchLafRequest = async (props: LafRequestProps): Promise<LafResponse> => {
|
|
||||||
let {
|
|
||||||
appId,
|
|
||||||
chatId,
|
|
||||||
responseChatItemId,
|
|
||||||
variables,
|
|
||||||
module: { outputs },
|
|
||||||
histories,
|
|
||||||
params: { system_httpReqUrl: httpReqUrl, [DYNAMIC_INPUT_KEY]: dynamicInput, ...body }
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
if (!httpReqUrl) {
|
|
||||||
return Promise.reject('Http url is empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
const concatVariables = {
|
|
||||||
appId,
|
|
||||||
chatId,
|
|
||||||
responseChatItemId,
|
|
||||||
...variables,
|
|
||||||
...body
|
|
||||||
};
|
|
||||||
|
|
||||||
httpReqUrl = replaceVariable(httpReqUrl, concatVariables);
|
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
systemParams: {
|
|
||||||
appId,
|
|
||||||
chatId,
|
|
||||||
responseChatItemId,
|
|
||||||
histories: histories.slice(0, 10)
|
|
||||||
},
|
|
||||||
variables,
|
|
||||||
...dynamicInput,
|
|
||||||
...body
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { formatResponse, rawResponse } = await fetchData({
|
|
||||||
method: 'POST',
|
|
||||||
url: httpReqUrl,
|
|
||||||
body: requestBody
|
|
||||||
});
|
|
||||||
|
|
||||||
// format output value type
|
|
||||||
const results: Record<string, any> = {};
|
|
||||||
for (const key in formatResponse) {
|
|
||||||
const output = outputs.find((item) => item.key === key);
|
|
||||||
if (!output) continue;
|
|
||||||
results[key] = valueTypeFormat(formatResponse[key], output.valueType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
assistantResponses: [],
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
totalPoints: 0,
|
|
||||||
body: Object.keys(requestBody).length > 0 ? requestBody : undefined,
|
|
||||||
httpResult: rawResponse
|
|
||||||
},
|
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]: rawResponse,
|
|
||||||
[NodeOutputKeyEnum.httpRawResponse]: rawResponse,
|
|
||||||
...results
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
addLog.error('Http request error', error);
|
|
||||||
return {
|
|
||||||
[NodeOutputKeyEnum.failed]: true,
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
totalPoints: 0,
|
|
||||||
body: Object.keys(requestBody).length > 0 ? requestBody : undefined,
|
|
||||||
httpResult: { error: formatHttpError(error) }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async function fetchData({
|
|
||||||
method,
|
|
||||||
url,
|
|
||||||
body
|
|
||||||
}: {
|
|
||||||
method: string;
|
|
||||||
url: string;
|
|
||||||
body: Record<string, any>;
|
|
||||||
}): Promise<Record<string, any>> {
|
|
||||||
const { data: response } = await axios({
|
|
||||||
method,
|
|
||||||
baseURL: `http://${SERVICE_LOCAL_HOST}`,
|
|
||||||
url,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
data: body
|
|
||||||
});
|
|
||||||
|
|
||||||
const parseJson = (obj: Record<string, any>, prefix = '') => {
|
|
||||||
let result: Record<string, any> = {};
|
|
||||||
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
for (let i = 0; i < obj.length; i++) {
|
|
||||||
result[`${prefix}[${i}]`] = obj[i];
|
|
||||||
|
|
||||||
if (Array.isArray(obj[i])) {
|
|
||||||
result = {
|
|
||||||
...result,
|
|
||||||
...parseJson(obj[i], `${prefix}[${i}]`)
|
|
||||||
};
|
|
||||||
} else if (typeof obj[i] === 'object') {
|
|
||||||
result = {
|
|
||||||
...result,
|
|
||||||
...parseJson(obj[i], `${prefix}[${i}].`)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (typeof obj == 'object') {
|
|
||||||
for (const key in obj) {
|
|
||||||
result[`${prefix}${key}`] = obj[key];
|
|
||||||
|
|
||||||
if (Array.isArray(obj[key])) {
|
|
||||||
result = {
|
|
||||||
...result,
|
|
||||||
...parseJson(obj[key], `${prefix}${key}`)
|
|
||||||
};
|
|
||||||
} else if (typeof obj[key] === 'object') {
|
|
||||||
result = {
|
|
||||||
...result,
|
|
||||||
...parseJson(obj[key], `${prefix}${key}.`)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
formatResponse:
|
|
||||||
typeof response === 'object' && !Array.isArray(response) ? parseJson(response) : {},
|
|
||||||
rawResponse: response
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceVariable(text: string, obj: Record<string, any>) {
|
|
||||||
for (const [key, value] of Object.entries(obj)) {
|
|
||||||
if (value === undefined) {
|
|
||||||
text = text.replace(new RegExp(`{{${key}}}`, 'g'), UNDEFINED_SIGN);
|
|
||||||
} else {
|
|
||||||
const replacement = JSON.stringify(value);
|
|
||||||
const unquotedReplacement =
|
|
||||||
replacement.startsWith('"') && replacement.endsWith('"')
|
|
||||||
? replacement.slice(1, -1)
|
|
||||||
: replacement;
|
|
||||||
text = text.replace(new RegExp(`{{${key}}}`, 'g'), unquotedReplacement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return text || '';
|
|
||||||
}
|
|
||||||
function removeUndefinedSign(obj: Record<string, any>) {
|
|
||||||
for (const key in obj) {
|
|
||||||
if (obj[key] === UNDEFINED_SIGN) {
|
|
||||||
obj[key] = undefined;
|
|
||||||
} else if (Array.isArray(obj[key])) {
|
|
||||||
obj[key] = obj[key].map((item: any) => {
|
|
||||||
if (item === UNDEFINED_SIGN) {
|
|
||||||
return undefined;
|
|
||||||
} else if (typeof item === 'object') {
|
|
||||||
removeUndefinedSign(item);
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
} else if (typeof obj[key] === 'object') {
|
|
||||||
removeUndefinedSign(obj[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
function formatHttpError(error: any) {
|
|
||||||
return {
|
|
||||||
message: error?.message,
|
|
||||||
name: error?.name,
|
|
||||||
method: error?.config?.method,
|
|
||||||
baseURL: error?.config?.baseURL,
|
|
||||||
url: error?.config?.url,
|
|
||||||
code: error?.code,
|
|
||||||
status: error?.status
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import {
|
|
||||||
AIChatItemValueItemType,
|
|
||||||
ChatHistoryItemResType,
|
|
||||||
ChatItemValueItemType,
|
|
||||||
ToolRunResponseItemType
|
|
||||||
} from '@fastgpt/global/core/chat/type';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
|
||||||
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
|
||||||
|
|
||||||
export type DispatchFlowResponse = {
|
|
||||||
flowResponses: ChatHistoryItemResType[];
|
|
||||||
flowUsages: ChatNodeUsageType[];
|
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]: ToolRunResponseItemType;
|
|
||||||
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
|
|
||||||
newVariables: Record<string, string>;
|
|
||||||
};
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
|
||||||
import {
|
|
||||||
WorkflowIOValueTypeEnum,
|
|
||||||
NodeOutputKeyEnum
|
|
||||||
} from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
|
||||||
import { FlowNodeItemType, StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
|
||||||
export const DYNAMIC_INPUT_KEY = 'DYNAMIC_INPUT_KEY';
|
|
||||||
|
|
||||||
export const setEntryEntries = (modules: StoreNodeItemType[]) => {
|
|
||||||
const initRunningModuleType: Record<string, boolean> = {
|
|
||||||
questionInput: true,
|
|
||||||
[FlowNodeTypeEnum.pluginInput]: true
|
|
||||||
};
|
|
||||||
|
|
||||||
modules.forEach((item) => {
|
|
||||||
if (initRunningModuleType[item.flowType]) {
|
|
||||||
item.isEntry = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return modules;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const checkTheModuleConnectedByTool = (
|
|
||||||
modules: FlowNodeItemType[],
|
|
||||||
module: FlowNodeItemType
|
|
||||||
) => {
|
|
||||||
let sign = false;
|
|
||||||
const toolModules = modules.filter((item) => item.flowType === FlowNodeTypeEnum.tools);
|
|
||||||
|
|
||||||
toolModules.forEach((item) => {
|
|
||||||
const toolOutput = item.outputs.find(
|
|
||||||
(output) => output.key === NodeOutputKeyEnum.selectedTools
|
|
||||||
);
|
|
||||||
toolOutput?.targets.forEach((target) => {
|
|
||||||
if (target.moduleId === module.moduleId) {
|
|
||||||
sign = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return sign;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getHistories = (history?: ChatItemType[] | number, histories: ChatItemType[] = []) => {
|
|
||||||
if (!history) return [];
|
|
||||||
if (typeof history === 'number') return histories.slice(-history);
|
|
||||||
if (Array.isArray(history)) return history;
|
|
||||||
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
|
|
||||||
/* value type format */
|
|
||||||
export const valueTypeFormat = (value: any, type?: `${WorkflowIOValueTypeEnum}`) => {
|
|
||||||
if (value === undefined) return;
|
|
||||||
|
|
||||||
if (type === 'string') {
|
|
||||||
if (typeof value !== 'object') return String(value);
|
|
||||||
return JSON.stringify(value);
|
|
||||||
}
|
|
||||||
if (type === 'number') return Number(value);
|
|
||||||
if (type === 'boolean') return Boolean(value);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user